Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/version-propagation.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Pre-releases (`v*-beta.N` produced by `develop`/`release-candidate`) are filtere
| Bump is `major` | PR opened (never direct push) |
| Auto-merge enabled for that bump level | `gh pr merge --auto --squash` on the fallback PR |
| Target already on `new_tag` | Skipped, no change |
| Older unmerged bump PR still open on the same `target_branch` | Closed right after the direct push succeeds or the replacement PR is opened, with a comment noting it was superseded. Only PRs authored by the automation's own identity are closed. |

The `target_branch` default is `develop` (gitflow-friendly: the bump flows through `develop → release-candidate → main` via the normal promotion pipeline). Repos without `develop` declare `target_branch: main` in their target entry.

Expand Down
70 changes: 4 additions & 66 deletions src/changelog/gptchangelog/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -547,72 +547,10 @@ runs:
echo "❌ Failed to push after $max_attempts attempts"
exit 1

- name: Backmerge default branch into develop
if: steps.generate.outputs.apps_updated != ''
shell: bash
env:
GH_TOKEN: ${{ inputs.github-token }}
run: |
DEFAULT_BRANCH=$(gh repo view --json defaultBranchRef -q '.defaultBranchRef.name')
TARGET_BRANCH="develop"

if ! git ls-remote --heads origin develop | grep -q develop; then
echo "⚠️ No develop branch found — skipping backmerge"
exit 0
fi

echo "📌 Backmerging $DEFAULT_BRANCH → $TARGET_BRANCH"

git fetch origin "$DEFAULT_BRANCH" "$TARGET_BRANCH"
git checkout "$TARGET_BRANCH"
git reset --hard "origin/$TARGET_BRANCH"

if git merge-base --is-ancestor "origin/$DEFAULT_BRANCH" "HEAD"; then
echo "✅ $TARGET_BRANCH already contains $DEFAULT_BRANCH — nothing to backmerge"
exit 0
fi

# Try clean merge first — push directly if no conflicts
set +e
git merge --no-ff -S \
-m "chore(changelog): backmerge $DEFAULT_BRANCH into $TARGET_BRANCH [skip ci]" \
"origin/$DEFAULT_BRANCH"
MERGE_EXIT=$?
set -e

if [ $MERGE_EXIT -eq 0 ]; then
if git push origin "$TARGET_BRANCH"; then
echo "✅ Backmerged $DEFAULT_BRANCH into $TARGET_BRANCH (direct push)"
exit 0
fi
echo "⚠️ Direct push to $TARGET_BRANCH rejected — falling back to PR"
elif [ $MERGE_EXIT -eq 1 ]; then
echo "⚠️ Merge conflict between $DEFAULT_BRANCH and $TARGET_BRANCH — falling back to PR for manual resolution"
git merge --abort 2>/dev/null || true
else
echo "⚠️ Merge failed (exit $MERGE_EXIT) — falling back to PR"
git merge --abort 2>/dev/null || true
fi

# Fallback: open a backmerge PR for manual resolution
BACKMERGE_BRANCH="release/backmerge-${DEFAULT_BRANCH}-into-${TARGET_BRANCH}"
echo "📌 Creating backmerge branch: $BACKMERGE_BRANCH"
# Fetch the backmerge branch (if it exists) so --force-with-lease
# compares against the current remote state, not a stale tracking ref.
git fetch origin "$BACKMERGE_BRANCH" 2>/dev/null || true
git checkout -B "$BACKMERGE_BRANCH" "origin/$DEFAULT_BRANCH"
git push --force-with-lease origin "$BACKMERGE_BRANCH"

if [[ "$(gh pr list --head "$BACKMERGE_BRANCH" --base "$TARGET_BRANCH" --state open --json number --jq 'length')" == "0" ]]; then
gh pr create \
--title "chore(changelog): backmerge $DEFAULT_BRANCH into $TARGET_BRANCH" \
--body "Automated backmerge from \`$DEFAULT_BRANCH\` into \`$TARGET_BRANCH\` could not be completed automatically. Please review and merge manually." \
--base "$TARGET_BRANCH" \
--head "$BACKMERGE_BRANCH"
echo "✅ Backmerge PR created"
else
echo "⚠️ Backmerge PR already exists"
fi
# Backmerging default branch into develop is NOT done here. The caller
# (release.yml) runs a dedicated `backmerge` job after this composite,
# which already carries the changelog commit just pushed above since it
# fetches the source branch fresh. A second backmerge here was redundant.

# ----------------- Cleanup -----------------
- name: Cleanup temporary files
Expand Down
54 changes: 53 additions & 1 deletion src/config/version-propagation/scripts/push-or-pr.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#!/usr/bin/env bash
# Commits staged changes and either pushes directly to the target branch or
# opens a PR. Falls back to PR if direct push fails. Forces PR mode for major
# bumps (caller sets FORCE_PR=1).
# bumps (caller sets FORCE_PR=1). After a successful direct push or fresh
# replacement PR, closes any still-open PRs this automation opened for older
# bumps against the same target branch, so consumer repos don't accumulate
# stale bump PRs.
#
# Required env:
# REPO owner/name
Expand Down Expand Up @@ -81,5 +84,54 @@ Generated by [version-propagation](https://github.com/LerianStudio/github-action
fi
fi

# The direct push succeeded or the replacement PR was just opened above —
# close any older bump PRs this automation left open against the same target
# branch, so consumers don't accumulate one unmerged PR per skipped release.
bot_login_err=""
stale_prs_err=""
cleanup_tmp_files() {
[[ -z "$bot_login_err" ]] || rm -f -- "$bot_login_err"
[[ -z "$stale_prs_err" ]] || rm -f -- "$stale_prs_err"
}
trap cleanup_tmp_files EXIT

bot_login=""
if bot_login_err="$(mktemp)" && stale_prs_err="$(mktemp)"; then
bot_login=$(gh api user --jq '.login' 2>"$bot_login_err") || {
echo "::warning::could not resolve automation identity, skipping stale PR cleanup:" >&2
cat "$bot_login_err" >&2
bot_login=""
}
else
echo "::warning::could not create temporary files for stale PR cleanup; skipping cleanup" >&2
fi

current_branch="chore/bump-shared-workflows-${NEW_TAG}"
stale_prs=""
if [[ -n "$bot_login" ]]; then
if ! stale_prs=$(gh pr list \
--repo "$REPO" \
--base "$TARGET_BRANCH" \
--state open \
--limit 200 \
--json number,headRefName,author \
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
--jq ".[] | select(.headRefName != \"$current_branch\" and (.headRefName | startswith(\"chore/bump-shared-workflows-\")) and .author.login == \"$bot_login\") | .number" \
2>"$stale_prs_err"); then
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
echo "::warning::gh pr list failed while looking for stale bump PRs:" >&2
cat "$stale_prs_err" >&2
stale_prs=""
fi
fi

if [[ -n "$stale_prs" ]]; then
while IFS= read -r pr_number; do
[[ -z "$pr_number" ]] && continue
echo "closing stale bump PR #${pr_number} (superseded by ${NEW_TAG})" >&2
gh pr close "$pr_number" --repo "$REPO" \
--comment "Superseded by \`${NEW_TAG}\`, propagated in this run. Closing to avoid stale bump PRs piling up." \
|| echo "failed to close stale PR #${pr_number}" >&2
done <<< "$stale_prs"
fi

jq -n --arg repo "$REPO" --arg action "$action" --arg url "$url" \
'{repo:$repo, action:$action, url:$url}' >&3
Loading