fix: PR base branch

This commit is contained in:
Tienson Qin
2026-03-01 10:32:09 +08:00
parent bbecb0935f
commit d3bd12c8ed
3 changed files with 107 additions and 29 deletions

View File

@@ -776,39 +776,52 @@
(defn- <handle-pr-after-push!
[^js self current-session body user-id repo-url head-branch force? create-pr?]
(p/let [requested-base-branch (source-control/sanitize-branch-name (:base-branch body))
default-base (source-control/sanitize-branch-name (default-base-branch))
detected-base-branch (when (nil? requested-base-branch)
(source-control/<default-branch! (.-env self)
nil
repo-url))
detected-base-branch (source-control/sanitize-branch-name detected-base-branch)
base-branch (or requested-base-branch
detected-base-branch
default-base)
base-branch (if (= base-branch head-branch)
(or (some (fn [candidate]
(let [candidate (source-control/sanitize-branch-name candidate)]
(when (and (string? candidate)
(not= candidate head-branch))
candidate)))
[default-base "main" "master"])
base-branch)
base-branch)]
(let [requested-base-branch (source-control/sanitize-branch-name (:base-branch body))
task-base-branch (some-> (get-in current-session [:task :project :base-branch])
source-control/sanitize-branch-name)
default-base (source-control/sanitize-branch-name (default-base-branch))
choose-base-branch (fn [candidates]
(let [valid-candidates (->> candidates
(map source-control/sanitize-branch-name)
(remove nil?)
vec)
non-head-candidate (some (fn [candidate]
(when (not= candidate head-branch)
candidate))
valid-candidates)
fallback-non-head (some (fn [candidate]
(let [candidate (source-control/sanitize-branch-name candidate)]
(when (and (string? candidate)
(not= candidate head-branch))
candidate)))
["main" "master"])]
(or non-head-candidate
fallback-non-head
(first valid-candidates))))]
(cond
(false? create-pr?)
(http/json-response :sessions/pr
(cond-> {:status "pushed"
:head-branch head-branch
:message "branch pushed"}
(string? base-branch) (assoc :base-branch base-branch)
(some? force?) (assoc :force force?)))
(let [base-branch (choose-base-branch [requested-base-branch
task-base-branch
default-base])]
(http/json-response :sessions/pr
(cond-> {:status "pushed"
:head-branch head-branch
:message "branch pushed"}
(string? base-branch) (assoc :base-branch base-branch)
(some? force?) (assoc :force force?))))
(not (pr-enabled? current-session))
(http/forbidden)
:else
(p/let [pr-token (source-control/<pr-token! (.-env self) repo-url)]
(p/let [pr-token (source-control/<pr-token! (.-env self) repo-url)
detected-base-branch (source-control/<default-branch! (.-env self)
pr-token
repo-url)
base-branch (choose-base-branch [detected-base-branch
requested-base-branch
task-base-branch
default-base])]
(let [description (or (some-> (:body body) str string/trim not-empty)
"Automated changes from agent session.")
title (or (some-> (:title body) str string/trim not-empty)

View File

@@ -871,6 +871,68 @@
(is false (str "unexpected error: " error))
(done))))))))
(deftest pr-endpoint-prefers-detected-default-base-branch-for-pr-create-test
(testing "session publish endpoint uses detected default branch when creating PR"
(async done
(let [create-pr-calls (atom [])
env #js {"AGENT_RUNTIME_PROVIDER" "local-dev"}
self (make-self env)
headers {"content-type" "application/json"
"x-user-id" "user-1"}]
(-> (.put (.-storage self)
"session"
(clj->js {:id "sess-pr-default-base"
:status "running"
:task {:project {:repo-url "https://github.com/example/repo"
:base-branch "main"}}
:runtime {:provider "local-dev"
:session-id "sess-pr-default-base"}
:audit {}
:created-at 0
:updated-at 0}))
(.then (fn [_]
(with-redefs [runtime-provider/<push-branch!
(fn [_provider _runtime opts]
(js/Promise.resolve
{:head-branch (:head-branch opts)
:repo-url (:repo-url opts)
:force (:force opts)
:remote "origin"}))
source-control/<pr-token!
(fn [_env _repo-url]
(js/Promise.resolve "pr-token"))
source-control/<default-branch!
(fn [_env _token _repo-url]
(js/Promise.resolve "develop"))
source-control/<create-pull-request!
(fn [_env _token _repo-url opts]
(swap! create-pr-calls conj opts)
(js/Promise.resolve {:url "https://github.com/example/repo/pull/99"
:id 99
:base-branch (:base-branch opts)}))
agent-do/<terminate-runtime!
(fn [_self _runtime]
(js/Promise.resolve nil))]
(agent-do/handle-fetch self
(json-request "http://db-sync.local/__session__/pr"
"POST"
{:create-pr true
:head-branch "feature/improve-base-detection"
:base-branch "main"}
headers)))))
(.then (fn [resp]
(is (= 200 (.-status resp)))
(.then (<json resp)
(fn [body]
(is (= "pr-created" (:status body)))
(is (= "develop" (:base-branch body)))
(is (= 1 (count @create-pr-calls)))
(is (= "develop" (:base-branch (first @create-pr-calls))))
(done)))))
(.catch (fn [error]
(is false (str "unexpected error: " error))
(done))))))))
(deftest pr-endpoint-pr-created-terminates-runtime-test
(testing "session publish endpoint terminates runtime and clears sandbox runtime when PR is created"
(async done
@@ -901,6 +963,9 @@
source-control/<pr-token!
(fn [_env _repo-url]
(js/Promise.resolve "pr-token"))
source-control/<default-branch!
(fn [_env _token _repo-url]
(js/Promise.resolve "main"))
source-control/<create-pull-request!
(fn [_env _token _repo-url _opts]
(js/Promise.resolve {:url "https://github.com/example/repo/pull/88"

View File

@@ -670,11 +670,11 @@
(string? (blank->nil base-branch)) (assoc :base-branch (blank->nil base-branch))))
(defn- maybe-insert-pr-sibling-blocks!
[block-uuid resp summary]
[block-uuid summary]
(when-let [summary (blank->nil summary)]
(editor-handler/api-insert-new-block! (str "PR Summary: " summary)
{:block-uuid block-uuid
:sibling? true})))
:sibling? false})))
(defn- publish-status-message
[resp]
@@ -735,7 +735,7 @@
(when (= "pr-created" status)
(maybe-update-task-pr-url! block-uuid (:pr-url resp)))
(maybe-update-task-status! block-uuid status))
(maybe-insert-pr-sibling-blocks! block-uuid resp (:body raw-body))
(maybe-insert-pr-sibling-blocks! block-uuid (:body raw-body))
(notification/show! (publish-status-message resp)
(if (= "manual-pr-required" (:status resp))
:warning