fix(release): make just release idempotent for re-runs (#948)

This commit is contained in:
Will Pfleger
2026-06-10 12:50:18 -04:00
committed by GitHub
parent e9cd1c3926
commit 5c0af0bc93
2 changed files with 41 additions and 11 deletions

View File

@@ -18,11 +18,13 @@ just release 1.0.0
This creates a `version-bump/<version>` PR that bumps all version manifests, regenerates lockfiles, and appends a changelog entry. Merge the PR to trigger the build automatically. This creates a `version-bump/<version>` PR that bumps all version manifests, regenerates lockfiles, and appends a changelog entry. Merge the PR to trigger the build automatically.
Re-running `just release` with the same version is safe — it detects the existing branch and PR, resets to current `main`, regenerates the changelog with any new commits, and updates the PR in place.
--- ---
## How It Works ## How It Works
1. **`just release`** runs locally on `main` — computes the next version, creates a `version-bump/<version>` branch, bumps versions in all manifests, regenerates lockfiles, generates a changelog entry, commits, pushes, and opens a PR. 1. **`just release`** runs locally on `main` — computes the next version, creates (or reuses) a `version-bump/<version>` branch, bumps versions in all manifests, regenerates lockfiles, generates a changelog entry, commits, pushes, and opens (or updates) a PR.
2. **Merge the PR** — the `auto-tag-on-release-pr-merge` workflow detects the `version-bump/*` branch merge and pushes a `v<version>` tag. 2. **Merge the PR** — the `auto-tag-on-release-pr-merge` workflow detects the `version-bump/*` branch merge and pushes a `v<version>` tag.
@@ -120,6 +122,9 @@ Switch to `main` and pull latest before running `just release`.
### `just release` fails with "working tree is dirty" ### `just release` fails with "working tree is dirty"
Commit or stash your changes before running `just release`. Commit or stash your changes before running `just release`.
### New commits merged after creating the release PR
Re-run `just release` from an up-to-date `main`. It resets the branch to current `main`, regenerates the changelog and PR body to include the new commits, and force-pushes the updated branch.
### Build fails at "Validate version" ### Build fails at "Validate version"
The version string must be valid semver: `MAJOR.MINOR.PATCH` with an optional pre-release suffix. Do not include a `v` prefix. The version string must be valid semver: `MAJOR.MINOR.PATCH` with an optional pre-release suffix. Do not include a `v` prefix.

View File

@@ -445,7 +445,7 @@ bump-version version:
(unset GIT_DIR GIT_WORK_TREE; cd mobile && flutter pub get) (unset GIT_DIR GIT_WORK_TREE; cd mobile && flutter pub get)
echo "Bumped all manifests to {{ version }} and regenerated lockfiles" echo "Bumped all manifests to {{ version }} and regenerated lockfiles"
# Create a release PR that bumps version and generates changelog # Create or update a release PR that bumps version and generates changelog
release *ARGS: release *ARGS:
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
@@ -479,9 +479,19 @@ release *ARGS:
echo "Error: working tree is dirty. Commit or stash changes first." echo "Error: working tree is dirty. Commit or stash changes first."
exit 1 exit 1
fi fi
# Create version-bump branch # Switch to version-bump branch (create if needed, reset to main if it exists)
BRANCH="version-bump/${VERSION}" BRANCH="version-bump/${VERSION}"
git switch -c "$BRANCH" if git rev-parse --verify "refs/heads/$BRANCH" >/dev/null 2>&1; then
echo "Branch '$BRANCH' already exists — resetting to origin/main..."
git switch "$BRANCH"
git reset --hard origin/main
elif git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
echo "Branch '$BRANCH' exists on remote — checking out and resetting to origin/main..."
git switch -c "$BRANCH" --track "origin/$BRANCH"
git reset --hard origin/main
else
git switch -c "$BRANCH"
fi
# Bump versions and lockfiles # Bump versions and lockfiles
just bump-version "$VERSION" just bump-version "$VERSION"
# Generate changelog # Generate changelog
@@ -513,9 +523,14 @@ release *ARGS:
mobile/pubspec.lock \ mobile/pubspec.lock \
pnpm-lock.yaml \ pnpm-lock.yaml \
CHANGELOG.md CHANGELOG.md
git commit -m "chore(release): release version ${VERSION}" RELEASE_MSG="chore(release): release version ${VERSION}"
if [[ "$(git log -1 --format='%s' 2>/dev/null)" == "$RELEASE_MSG" ]]; then
git commit --amend --no-edit
else
git commit -m "$RELEASE_MSG"
fi
# Push and open PR # Push and open PR
git push -u origin "$BRANCH" git push --force-with-lease -u origin "$BRANCH"
# Build PR body # Build PR body
PR_BODY="## Release v${VERSION}"$'\n\n' PR_BODY="## Release v${VERSION}"$'\n\n'
if [[ -n "$LAST_TAG" ]]; then if [[ -n "$LAST_TAG" ]]; then
@@ -525,11 +540,21 @@ release *ARGS:
PR_BODY+="Initial release."$'\n\n' PR_BODY+="Initial release."$'\n\n'
fi fi
PR_BODY+="**To release:** merge this PR. The tag and build will happen automatically." PR_BODY+="**To release:** merge this PR. The tag and build will happen automatically."
PR_URL=$(gh pr create \ EXISTING_PR=$(gh pr list --head "$BRANCH" --json url --jq '.[0].url' 2>/dev/null || true)
--title "chore(release): release version ${VERSION}" \ if [[ -n "$EXISTING_PR" ]]; then
--body "$PR_BODY") gh pr edit "$BRANCH" \
echo "" --title "chore(release): release version ${VERSION}" \
echo "Release PR opened: ${PR_URL}" --body "$PR_BODY"
PR_URL="$EXISTING_PR"
echo ""
echo "Updated existing release PR: ${PR_URL}"
else
PR_URL=$(gh pr create \
--title "chore(release): release version ${VERSION}" \
--body "$PR_BODY")
echo ""
echo "Release PR opened: ${PR_URL}"
fi
echo "Merge it to trigger the release build." echo "Merge it to trigger the release build."
# ─── Agent Harness ──────────────────────────────────────────────────────────── # ─── Agent Harness ────────────────────────────────────────────────────────────