From eceda86a7591bd8b91747b874b7554503c8a8540 Mon Sep 17 00:00:00 2001 From: DevBot Date: Thu, 9 Jul 2026 17:08:07 +0800 Subject: [PATCH] refactor(autoflow): simplify release branch/tag dance, drop force-push (#320) - Remove `--force` from the tag push in `release.ts`. A tag that already exists at a different commit is now left untouched; the guarded local `git tag -f` (only when an existing tag points at a different commit, with a warning) is preserved. - Consolidate the local/manual release path: instead of bouncing dev -> main -> dev with redundant `pull --ff-only` / `pull --rebase` / duplicate `push main` + `push dev` round-trips, do a single fast-forward `main` from `dev`, publish/deploy/record evidence once, push `main`, then fast-forward `dev` from `main` and push `dev`. Fewer branch transitions, lower risk of a half-released state. - Updated the local-plan assertion in policy.test.ts to the new step name. The release flow itself is not executed by CI (it needs publish/release credentials), so this needs a human eye before the next real release. Closes #320 --- tools/autoflow/__tests__/policy.test.ts | 2 +- tools/autoflow/release.ts | 32 ++++++++++--------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/tools/autoflow/__tests__/policy.test.ts b/tools/autoflow/__tests__/policy.test.ts index bdd418eb..811759b7 100644 --- a/tools/autoflow/__tests__/policy.test.ts +++ b/tools/autoflow/__tests__/policy.test.ts @@ -183,7 +183,7 @@ Deno.test('release: local plan includes publish, smoke, gates, and GitHub releas assert(commands.some(([name]) => name === 'run release gates after bump')); assert(commands.some(([name]) => name === 'package artifact gate')); assert(commands.some(([name]) => name === 'push dev')); - assert(commands.some(([name]) => name === 'sync dev to main')); + assert(commands.some(([name]) => name === 'sync main from dev (fast-forward)')); assert( commands.some(([, command]) => command.includes('deno task publish:npm')), ); diff --git a/tools/autoflow/release.ts b/tools/autoflow/release.ts index 3eceb131..36801846 100644 --- a/tools/autoflow/release.ts +++ b/tools/autoflow/release.ts @@ -185,7 +185,10 @@ export function createReleasePlan( }, { name: 'push tag', - command: ['git', 'push', '--force', 'origin', tag], + // No force-push: a tag that already exists at a different commit is left + // untouched (the local `git tag -f` above is guarded and only runs when + // the existing tag points at a different commit, with a warning). + command: ['git', 'push', 'origin', tag], }, ...(canCreateGitHubRelease() ? [ @@ -278,7 +281,10 @@ export function createReleasePlan( ]; } - // Local/manual release: work on dev, then fast-forward main from dev. + // Local/manual release: bump on dev, fast-forward main from dev in a single + // transition, then keep dev in sync with main. This removes the redundant + // pull/push round-trips that previously bounced between dev and main and + // reduces the chance of a half-released state. return [ ...baseSteps, { @@ -294,21 +300,9 @@ export function createReleasePlan( command: ['git', 'checkout', 'main'], }, { - name: 'refresh main', - command: ['git', 'pull', '--ff-only', 'origin', 'main'], - }, - { - name: 'sync dev to main', + name: 'sync main from dev (fast-forward)', command: ['git', 'merge', '--ff-only', 'dev'], }, - { - name: 'pull main (before publish)', - command: ['git', 'pull', '--rebase', 'origin', 'main'], - }, - { - name: 'push main (before publish)', - command: ['git', 'push', 'origin', 'main'], - }, ...publishSteps, { name: 'deploy:pages', @@ -320,22 +314,22 @@ export function createReleasePlan( }, ...evidenceSteps, { - name: 'push main evidence', + name: 'push main (release + evidence)', command: ['git', 'push', 'origin', 'main'], }, + ...tagSteps, { name: 'checkout dev', command: ['git', 'checkout', 'dev'], }, { - name: 'sync main evidence to dev', + name: 'sync dev from main (fast-forward)', command: ['git', 'merge', '--ff-only', 'main'], }, { - name: 'push dev evidence', + name: 'push dev', command: ['git', 'push', 'origin', 'dev'], }, - ...tagSteps, ]; }