diff --git a/cli/src/commands/sync/sync.ts b/cli/src/commands/sync/sync.ts index d4aa3d818c..f9c648eef0 100644 --- a/cli/src/commands/sync/sync.ts +++ b/cli/src/commands/sync/sync.ts @@ -2503,8 +2503,14 @@ export async function pull( } if (opts.onlyCreateBranch) { - // Branch is checked out locally; the caller pushes it. Symmetric with - // the non-onlyCreateBranch path: CLI does branch + pull, never push. + gitSyncDeployPush({ + items: deployItems, + authorName: process.env["WM_USERNAME"] || "windmill", + authorEmail: process.env["WM_EMAIL"] || "windmill@windmill.dev", + committerName: opts.gitCommitterName, + committerEmail: opts.gitCommitterEmail, + onlyCreateBranch: true, + }); return; } } @@ -2976,11 +2982,19 @@ export async function pull( log.warn(`Failed to pull shared UI folder: ${e}`); } - // Git-sync deployment-callback mode stops here: branch checkout + pull have - // happened, but commit + push are the caller's job. The hub script does - // them in-process with `set_gpg_signing_secret` so the agent's pre-warmed - // passphrase cache is still warm at sign time (WIN-1974). `gitSyncDeployPush` - // stays exported for callers that want the same commit/push behavior. + // Git-sync deployment-callback mode: commit the pulled files and push the + // current branch (the wm_deploy/fork branch checked out above, or the base + // branch in workspace-wide mode). + if (opts.gitDeployItems !== undefined && !opts.onlyCreateBranch) { + const deployItems: GitSyncDeployItem[] = JSON.parse(opts.gitDeployItems); + gitSyncDeployPush({ + items: deployItems, + authorName: process.env["WM_USERNAME"] || "windmill", + authorEmail: process.env["WM_EMAIL"] || "windmill@windmill.dev", + committerName: opts.gitCommitterName, + committerEmail: opts.gitCommitterEmail, + }); + } } // Internal git-sync deployment-callback entrypoint. Invoked only by the diff --git a/cli/test/gitsync_promotion.test.ts b/cli/test/gitsync_promotion.test.ts index 5b73e8f8e1..f809d2032b 100644 --- a/cli/test/gitsync_promotion.test.ts +++ b/cli/test/gitsync_promotion.test.ts @@ -6,11 +6,10 @@ * `use_individual_branch` is set — NOT straight to the cloned base branch * (e.g. a protected `main`, which fails with GH006). * - * Contract: `wmill sync git-deploy` does branch checkout + pull only. Commit - * + push are the caller's job — the hub script does them in the same process - * as `set_gpg_signing_secret` so the GPG agent's passphrase cache is still - * warm at sign time (WIN-1974). This test replicates the caller half (git - * add + commit + push) inline so the full promotion regression stays caught. + * The CLI's `wmill sync pull --git-deploy-items ...` now owns that branch + * checkout + commit + push (previously hub-script-only, hence untestable). + * This drives it against a real local bare repo so the regression is caught + * deterministically, with no network and no GitHub. */ import { expect, test } from "bun:test"; @@ -122,23 +121,6 @@ test.skipIf(shouldSkipOnCI())( { path_type: "script", path: "f/promo/foo", commit_msg: "deploy foo" }, ]); - // Caller-half: stage anything the CLI's pull dropped, commit on the - // current branch (which the CLI just checked out), and push. Mirrors - // what the hub script does in production after `wmill sync git-deploy`. - const commitAndPush = (work: string) => { - git(work, "config", "user.email", "test@windmill.dev"); - git(work, "config", "user.name", "test"); - git(work, "add", "-A"); - try { - git(work, "diff", "--cached", "--quiet"); - // Exit 0 = nothing staged; nothing to commit. Still push the - // (possibly new) branch ref so the assertions see it. - } catch { - git(work, "commit", "-m", "deploy foo"); - } - git(work, "push", "--porcelain", "-u", "origin", "HEAD"); - }; - // --- Case A: use_individual_branch=true -> wm_deploy branch, main untouched --- const workA = await mkdtemp(join(tmpdir(), "wmill_promo_a_")); git(workA, "clone", `file://${bareDir}`, "."); @@ -159,7 +141,6 @@ test.skipIf(shouldSkipOnCI())( workA, ); expect(resA.code).toBe(0); - commitAndPush(workA); const branchesA = remoteBranches(bareDir); const expectedBranch = `refs/heads/wm_deploy/${ws}/script/f__promo__foo`; @@ -186,8 +167,6 @@ test.skipIf(shouldSkipOnCI())( workB, ); expect(resB.code).toBe(0); - commitAndPush(workB); - expect(remoteHead(bareDir, "main")).not.toBe(seedMain); expect( remoteBranches(bareDir).filter((b) => b.includes("wm_deploy")).length,