mirror of
https://github.com/jorenn92/Maintainerr.git
synced 2026-08-31 07:08:35 +02:00
ci: sync release commit back to development
This commit is contained in:
@@ -15,6 +15,7 @@ Promotes the current `development` branch state to `main` for release.
|
||||
- If checks or branch protection still block the merge, release automation comments on this PR with the blocker.
|
||||
- If the flow succeeds, release automation posts a final summary comment with the merge, sync-back, and build results.
|
||||
- The manual release workflow starts only after the post-merge sync-back is complete.
|
||||
- `Release 5 - Publish` finishes with a second sync-back so the semantic-release commit on `main` is merged back into `development`.
|
||||
- Trigger the final release from `main` with `REF=main ./release.sh release`.
|
||||
|
||||
## Test Plan
|
||||
@@ -24,3 +25,4 @@ Promotes the current `development` branch state to `main` for release.
|
||||
- [ ] Approve this PR to trigger `Release 2 - Queue Push PR To Main` and `Release 2.5 - Execute Push PR To Main`
|
||||
- [ ] Confirm the PR was squash-merged into `main`, sync-back completed, and `Release 4 - Build Main` finished
|
||||
- [ ] Run `REF=main ./release.sh release`
|
||||
- [ ] Confirm `Release 5 - Publish` synced the release commit from `main` back into `development`
|
||||
|
||||
@@ -3,6 +3,13 @@ name: "Release 3 - Sync Back"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
mode:
|
||||
type: choice
|
||||
description: Sync strategy to run
|
||||
options:
|
||||
- reconcile
|
||||
- post-release
|
||||
default: reconcile
|
||||
dry_run:
|
||||
type: boolean
|
||||
description: Preview sync-back without pushing changes
|
||||
@@ -13,6 +20,10 @@ on:
|
||||
type: string
|
||||
required: false
|
||||
default: development
|
||||
mode:
|
||||
type: string
|
||||
required: false
|
||||
default: reconcile
|
||||
dry_run:
|
||||
type: boolean
|
||||
required: false
|
||||
@@ -30,6 +41,7 @@ env:
|
||||
MAIN_BRANCH: main
|
||||
DEVELOPMENT_BRANCH: development
|
||||
RECONCILE_COMMIT_MESSAGE: "chore: reconcile with main after squash-merge"
|
||||
POST_RELEASE_COMMIT_MESSAGE: "chore: sync release changes back from main"
|
||||
|
||||
jobs:
|
||||
guard_manual_run:
|
||||
@@ -80,65 +92,145 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
git fetch "$REMOTE"
|
||||
|
||||
shortstat="$(git diff --shortstat "$REMOTE/$MAIN_BRANCH" "$REMOTE/$DEVELOPMENT_BRANCH")"
|
||||
if [ -n "$shortstat" ]; then
|
||||
echo "$shortstat" >&2
|
||||
echo "Branches do not have identical content. Squash-merge the PR first." >&2
|
||||
exit 1
|
||||
sync_mode='${{ inputs.mode }}'
|
||||
if [ -z "$sync_mode" ]; then
|
||||
sync_mode='${{ github.event.inputs.mode }}'
|
||||
fi
|
||||
|
||||
merge_base="$(git merge-base "$REMOTE/$MAIN_BRANCH" "$REMOTE/$DEVELOPMENT_BRANCH")"
|
||||
preview="$(git merge-tree "$merge_base" "$REMOTE/$MAIN_BRANCH" "$REMOTE/$DEVELOPMENT_BRANCH")"
|
||||
if [ -n "$preview" ]; then
|
||||
printf '%s\n' "$preview" >&2
|
||||
echo "Reconciliation preview is not clean. Resolve branch differences before syncing back." >&2
|
||||
exit 1
|
||||
if [ -z "$sync_mode" ]; then
|
||||
sync_mode='reconcile'
|
||||
fi
|
||||
|
||||
if [ "$DRY_RUN" = "true" ]; then
|
||||
{
|
||||
echo "## Sync Back"
|
||||
echo
|
||||
echo "- Mode: dry run"
|
||||
echo "- Status: branches have identical content and the merge preview is clean"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git checkout "$DEVELOPMENT_BRANCH"
|
||||
git pull --ff-only "$REMOTE" "$DEVELOPMENT_BRANCH"
|
||||
|
||||
if git show-ref --verify --quiet "refs/heads/$MAIN_BRANCH"; then
|
||||
git checkout "$MAIN_BRANCH"
|
||||
else
|
||||
git checkout -b "$MAIN_BRANCH" "$REMOTE/$MAIN_BRANCH"
|
||||
fi
|
||||
git pull --ff-only "$REMOTE" "$MAIN_BRANCH"
|
||||
|
||||
git checkout "$DEVELOPMENT_BRANCH"
|
||||
git merge "$MAIN_BRANCH" --no-ff -m "$RECONCILE_COMMIT_MESSAGE"
|
||||
git push "$REMOTE" "$DEVELOPMENT_BRANCH"
|
||||
|
||||
git checkout "$MAIN_BRANCH"
|
||||
git merge "$DEVELOPMENT_BRANCH" --ff-only
|
||||
git push "$REMOTE" "$MAIN_BRANCH"
|
||||
|
||||
git fetch "$REMOTE"
|
||||
|
||||
ahead_dev="$(git rev-list --count "$REMOTE/$MAIN_BRANCH..$REMOTE/$DEVELOPMENT_BRANCH")"
|
||||
ahead_main="$(git rev-list --count "$REMOTE/$DEVELOPMENT_BRANCH..$REMOTE/$MAIN_BRANCH")"
|
||||
case "$sync_mode" in
|
||||
reconcile)
|
||||
shortstat="$(git diff --shortstat "$REMOTE/$MAIN_BRANCH" "$REMOTE/$DEVELOPMENT_BRANCH")"
|
||||
if [ -n "$shortstat" ]; then
|
||||
echo "$shortstat" >&2
|
||||
echo "Branches do not have identical content. Squash-merge the PR first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$ahead_dev" != "0" ] || [ "$ahead_main" != "0" ]; then
|
||||
echo "Branches are still diverged after sync-back." >&2
|
||||
exit 1
|
||||
fi
|
||||
merge_base="$(git merge-base "$REMOTE/$MAIN_BRANCH" "$REMOTE/$DEVELOPMENT_BRANCH")"
|
||||
preview="$(git merge-tree "$merge_base" "$REMOTE/$MAIN_BRANCH" "$REMOTE/$DEVELOPMENT_BRANCH")"
|
||||
if [ -n "$preview" ]; then
|
||||
printf '%s\n' "$preview" >&2
|
||||
echo "Reconciliation preview is not clean. Resolve branch differences before syncing back." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
{
|
||||
echo "## Sync Back"
|
||||
echo
|
||||
echo "- Mode: apply"
|
||||
echo "- development_ahead_of_main=$ahead_dev"
|
||||
echo "- main_ahead_of_development=$ahead_main"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
if [ "$DRY_RUN" = "true" ]; then
|
||||
{
|
||||
echo "## Sync Back"
|
||||
echo
|
||||
echo "- Strategy: reconcile"
|
||||
echo "- Mode: dry run"
|
||||
echo "- Status: branches have identical content and the merge preview is clean"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git checkout "$DEVELOPMENT_BRANCH"
|
||||
git pull --ff-only "$REMOTE" "$DEVELOPMENT_BRANCH"
|
||||
|
||||
if git show-ref --verify --quiet "refs/heads/$MAIN_BRANCH"; then
|
||||
git checkout "$MAIN_BRANCH"
|
||||
else
|
||||
git checkout -b "$MAIN_BRANCH" "$REMOTE/$MAIN_BRANCH"
|
||||
fi
|
||||
git pull --ff-only "$REMOTE" "$MAIN_BRANCH"
|
||||
|
||||
git checkout "$DEVELOPMENT_BRANCH"
|
||||
git merge "$MAIN_BRANCH" --no-ff -m "$RECONCILE_COMMIT_MESSAGE"
|
||||
git push "$REMOTE" "$DEVELOPMENT_BRANCH"
|
||||
|
||||
git checkout "$MAIN_BRANCH"
|
||||
git merge "$DEVELOPMENT_BRANCH" --ff-only
|
||||
git push "$REMOTE" "$MAIN_BRANCH"
|
||||
|
||||
git fetch "$REMOTE"
|
||||
|
||||
ahead_dev="$(git rev-list --count "$REMOTE/$MAIN_BRANCH..$REMOTE/$DEVELOPMENT_BRANCH")"
|
||||
ahead_main="$(git rev-list --count "$REMOTE/$DEVELOPMENT_BRANCH..$REMOTE/$MAIN_BRANCH")"
|
||||
|
||||
if [ "$ahead_dev" != "0" ] || [ "$ahead_main" != "0" ]; then
|
||||
echo "Branches are still diverged after sync-back." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
{
|
||||
echo "## Sync Back"
|
||||
echo
|
||||
echo "- Strategy: reconcile"
|
||||
echo "- Mode: apply"
|
||||
echo "- development_ahead_of_main=$ahead_dev"
|
||||
echo "- main_ahead_of_development=$ahead_main"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
;;
|
||||
post-release)
|
||||
ahead_dev_before="$(git rev-list --count "$REMOTE/$MAIN_BRANCH..$REMOTE/$DEVELOPMENT_BRANCH")"
|
||||
ahead_main_before="$(git rev-list --count "$REMOTE/$DEVELOPMENT_BRANCH..$REMOTE/$MAIN_BRANCH")"
|
||||
|
||||
git checkout "$DEVELOPMENT_BRANCH"
|
||||
git pull --ff-only "$REMOTE" "$DEVELOPMENT_BRANCH"
|
||||
|
||||
if [ "$ahead_main_before" = "0" ]; then
|
||||
{
|
||||
echo "## Sync Back"
|
||||
echo
|
||||
echo "- Strategy: post-release"
|
||||
echo "- Mode: $([ "$DRY_RUN" = "true" ] && echo dry\ run || echo apply)"
|
||||
echo "- Status: development already contains main"
|
||||
echo "- development_ahead_of_main=$ahead_dev_before"
|
||||
echo "- main_ahead_of_development=$ahead_main_before"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! git merge --no-commit --no-ff "$REMOTE/$MAIN_BRANCH"; then
|
||||
git merge --abort || true
|
||||
echo "Post-release sync-back hit merge conflicts. Resolve the branch drift before retrying." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$DRY_RUN" = "true" ]; then
|
||||
git merge --abort
|
||||
{
|
||||
echo "## Sync Back"
|
||||
echo
|
||||
echo "- Strategy: post-release"
|
||||
echo "- Mode: dry run"
|
||||
echo "- Status: main can be merged back into development cleanly"
|
||||
echo "- development_ahead_of_main=$ahead_dev_before"
|
||||
echo "- main_ahead_of_development=$ahead_main_before"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git commit -m "$POST_RELEASE_COMMIT_MESSAGE"
|
||||
git push "$REMOTE" "$DEVELOPMENT_BRANCH"
|
||||
|
||||
git fetch "$REMOTE"
|
||||
|
||||
ahead_dev_after="$(git rev-list --count "$REMOTE/$MAIN_BRANCH..$REMOTE/$DEVELOPMENT_BRANCH")"
|
||||
ahead_main_after="$(git rev-list --count "$REMOTE/$DEVELOPMENT_BRANCH..$REMOTE/$MAIN_BRANCH")"
|
||||
|
||||
if [ "$ahead_main_after" != "0" ]; then
|
||||
echo "main is still ahead of development after post-release sync-back." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
{
|
||||
echo "## Sync Back"
|
||||
echo
|
||||
echo "- Strategy: post-release"
|
||||
echo "- Mode: apply"
|
||||
echo "- development_ahead_of_main=$ahead_dev_after"
|
||||
echo "- main_ahead_of_development=$ahead_main_after"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown sync mode: $sync_mode" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -88,6 +88,19 @@ jobs:
|
||||
npx semantic-release
|
||||
fi
|
||||
|
||||
sync_back_release_commit:
|
||||
name: Sync release commit back to development
|
||||
needs: semantic-release
|
||||
if: ${{ needs.semantic-release.result == 'success' && github.event.inputs.dry-run-semantic-release != 'true' }}
|
||||
uses: ./.github/workflows/release_3_sync_back.yml
|
||||
permissions:
|
||||
contents: write
|
||||
secrets: inherit
|
||||
with:
|
||||
ref: development
|
||||
mode: post-release
|
||||
dry_run: false
|
||||
|
||||
build-docker-image:
|
||||
if: github.event.inputs.with-docker-images == 'true'
|
||||
name: Build ${{ matrix.platform }}
|
||||
|
||||
+4
-1
@@ -168,13 +168,14 @@ When adding new UI text, please try to adhere to the following guidelines:
|
||||
| `development` | Default branch. All PRs target here. Granular commit history. |
|
||||
| `main` | Stable release branch. Updated via squash-merge PRs from `development`. |
|
||||
|
||||
Maintainers should promote `development` to `main` through the release PR workflow. After the release PR is approved, automation will squash-merge it into `main`, sync the branches, and run `Release 4 - Build Main`.
|
||||
Maintainers should promote `development` to `main` through the release PR workflow. After the release PR is approved, automation will squash-merge it into `main`, sync the branches, and run `Release 4 - Build Main`. After `Release 5 - Publish` creates the release commit on `main`, automation runs a second sync-back so `development` does not stay behind `main`.
|
||||
|
||||
```bash
|
||||
./release.sh prepare-pr
|
||||
# Approve the release PR to trigger Release 2 and Release 2.5
|
||||
# Wait for the PR summary comment confirming merge, sync-back, and Build Main
|
||||
REF=main ./release.sh release
|
||||
# Release 5 finishes with a post-publish sync-back into development
|
||||
```
|
||||
|
||||
If branch sync needs to be rerun manually, use:
|
||||
@@ -182,6 +183,8 @@ If branch sync needs to be rerun manually, use:
|
||||
```bash
|
||||
./release.sh sync-back --dry-run
|
||||
./release.sh sync-back
|
||||
./release.sh sync-back --post-release --dry-run
|
||||
./release.sh sync-back --post-release
|
||||
```
|
||||
|
||||
The workflow-only fallback is [release_3_sync_back.yml](.github/workflows/release_3_sync_back.yml).
|
||||
|
||||
+22
-7
@@ -8,7 +8,7 @@ usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
./release.sh prepare-pr
|
||||
./release.sh sync-back [--dry-run]
|
||||
./release.sh sync-back [--post-release] [--dry-run]
|
||||
./release.sh release [--dry-run] [--with-docker-images]
|
||||
|
||||
Environment:
|
||||
@@ -28,12 +28,27 @@ run_prepare_pr() {
|
||||
}
|
||||
|
||||
run_sync_back() {
|
||||
if [[ "${1:-}" == "--dry-run" ]]; then
|
||||
gh workflow run release_3_sync_back.yml --ref "$REF" -f dry_run=true
|
||||
return
|
||||
fi
|
||||
local mode="reconcile"
|
||||
local dry_run="false"
|
||||
|
||||
gh workflow run release_3_sync_back.yml --ref "$REF"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--dry-run)
|
||||
dry_run="true"
|
||||
;;
|
||||
--post-release)
|
||||
mode="post-release"
|
||||
;;
|
||||
*)
|
||||
usage >&2
|
||||
echo "Error: unknown sync-back option: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
gh workflow run release_3_sync_back.yml --ref "$REF" -f mode="$mode" -f dry_run="$dry_run"
|
||||
}
|
||||
|
||||
run_release() {
|
||||
@@ -69,7 +84,7 @@ main() {
|
||||
;;
|
||||
sync-back)
|
||||
shift
|
||||
run_sync_back "${1:-}"
|
||||
run_sync_back "$@"
|
||||
;;
|
||||
release)
|
||||
shift
|
||||
|
||||
Reference in New Issue
Block a user