Skip to content

[CI] Add /sync-changelog comment trigger to retry failed changelog sync PRs - #20578

Draft
mrodm wants to merge 44 commits into
elastic:mainfrom
mrodm:add-sync-changelog-comment
Draft

[CI] Add /sync-changelog comment trigger to retry failed changelog sync PRs#20578
mrodm wants to merge 44 commits into
elastic:mainfrom
mrodm:add-sync-changelog-comment

Conversation

@mrodm

@mrodm mrodm commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Proposed commit message

[CI] Add /sync-changelog comment trigger to retry failed changelog sync PRs

Add an issue_comment trigger so maintainers can retry a failed changelog
sync by commenting /sync-changelog on the originating merged backport PR,
without needing to push a dummy commit.

- New retry-changelog job: guards on merged PR, write/admin permission,
  and backport-* base branch; derives before/after SHA from merge commit
- Shared steps extracted to .github/actions/sync-backport-changelog/
  composite action (reused by both push and comment jobs)
- Use git fetch + --force-with-lease to overwrite stale working branches
- Only open sync PRs block retries; merged/closed are now transparent
- New already_exists outcome and /sync-changelog retry hint in failure comments
- Write mage outputs before returning CreateSyncPR error so comment always fires

WHAT:

  • New retry-changelog job in sync-backport-changelog.yml triggered by issue_comment events. Guards: PR must be merged, commenter must have write/admin permission, base must be a backport-* branch.
  • New composite action at .github/actions/sync-backport-changelog/ holding the shared setup steps (Go, mage, git, sync, comment) reused by both the push and comment jobs.
  • sync-changelog job simplified to a job-level if, removing the zero-SHA guard step and all per-step proceed conditions.
  • existingSyncPR in collect.go now returns the PR URL (was bool) and only checks open PRs — merged/closed sync PRs no longer block future retries.
  • Collect accepts an optional knownPRNumber to skip the merge-commit→PR association retry/backoff on the retry path.
  • CreateSyncPR in sync.go uses git fetch + --force-with-lease to safely overwrite a stale working branch left by a previous failed run.
  • New already_exists outcome surfaces the URL of an existing open sync PR instead of the misleading "versions already present on main" message.
  • Failure comments now include a /sync-changelog retry hint so maintainers can discover the command where they need it.
  • SyncBackportChangelog mage target writes outputs before returning a CreateSyncPR error, ensuring the comment step always fires.
  • .github/actions/ added to non_package_patterns.txt and pull-requests.json so changes to composite actions don't trigger package tests.

WHY: When gh pr create fails after the working branch has already been pushed (transient API error, rate limit), there is no self-service recovery path — the only option was pushing a dummy commit to the backport branch to re-trigger the workflow. The /sync-changelog command provides a clean retry path directly from the PR where the failure is reported.

Author's Checklist

  • Composite action tested on fork by merging branch to fork main first (the issue_comment trigger only activates once the workflow is on the default branch)
  • Verified retry succeeds when working branch already exists on remote (--force-with-lease path)
  • Verified unauthorized commenter exits silently with no side effects
  • Validate that all required files and folders are copied into the backport branches created (backport_branch.sh)

How to test this PR locally

The issue_comment trigger only fires when the workflow is on the default branch. To test on a fork:

  1. Merge this branch to main on your fork.
  2. Create a backport-* branch, push a changelog change, let the push workflow run (or simulate a failure).
  3. On the resulting backport PR (merged), comment /sync-changelog.
  4. Verify the retry-changelog job runs and posts a success/skipped comment.

Edge cases to verify:

  • Comment on an unmerged PR → job exits silently, no comment posted
  • Comment from an account without write access → silent skip at actor-check step
  • Sync PR already open → already_exists comment with PR link
  • Stale working branch on remote → --force-with-lease overwrites it cleanly

This PR has been tested on a fork:

Related issues


This PR was generated with the assistance of Claude (claude-opus-5).

mrodm and others added 30 commits August 4, 2026 17:58
Replaces all mage backport targets with a self-contained Go sub-module
at cmd/backport/ that builds a `backport` binary. The sub-module has its
own go.mod and cmd/backport/.go-version (pinned to main's Go version) so
the tool always compiles with a modern toolchain even on backport branches
that carry an older root .go-version (e.g. 1.19/1.20).

Key changes:
- cmd/backport/main.go: CLI binary with subcommands validate-inventory,
  check-active, validate-branch-name, check-owners, detect-packages,
  render/parse/update-checklist, sync-changelog, post-comment, apply,
  check-changelog-versions, add-entry
- cmd/backport/go.mod: standalone sub-module; mage pinned via `tool`
  directive (Go 1.24+); no replace directive — fully self-contained
- Library packages moved from dev/backports/ into the sub-module;
  dev/citools, dev/codeowners, dev/gitutil copied (originals kept for
  root module use); dead code pruned from the copies
- Binary output: build/backport (gitignored, matching elastic-package
  convention); BACKPORT_BIN exported in common.sh
- with_backport() builds in a subshell so the Go version switch does not
  leak into the caller (fixes go mod tidy in backport_branch.sh)
- All Buildkite scripts and GitHub Actions workflows updated; new
  check-backport-tool Buildkite step runs cmd/backport tests using
  cmd/backport/.go-version

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- non_package_patterns.txt: replace ^dev/backports/ (deleted) with
  ^cmd/backport/; add check_backport_tool.sh; sort entries alphabetically
- pull-requests.json: add check_backport_tool.sh to skip_ci_on_only_changed;
  cmd/backport/ is intentionally not skipped so tool changes run full CI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- dev/gitutil: remove Git.RunToStderr (no callers in root module;
  cmd/backport/gitutil keeps its own copy used by apply and sync)
- dev/citools: make parsePackageManifest private (only called
  internally by ReadPackageManifest in the same file)
- dev/codeowners: remove ParseOwners (no callers outside tests;
  cmd/backport/codeowners keeps its own copy used by compare.go)
- dev/codeowners test: drop TestParseOwners; replace remaining
  ParseOwners calls with a mustParseOwners helper that calls
  scanGithubOwners directly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move ReportFailedTests and RequiresUpdate (the two mage targets that
import go-gh/v2 via dev/testsreporter and dev/requiresupdate) into a
new magefile_main_only.go. These targets are only invoked from
main-branch pipelines; backport branches never call them.

Update backport_branch.sh to exclude this file and the two dev packages
when creating a backport branch, so go mod tidy drops go-gh/v2. This
allows backport branches to retain their original Go version — .go-version
is no longer copied from main.

go mod tidy now runs in a subshell that installs Go from the backport
branch's own .go-version via gvm; main's Go version is restored in the
parent shell afterwards.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds Format (go-licenser + goimports) and Check (Build → Format → Tidy → Test)
targets to the cmd/backport magefile, matching the root magefile pattern.
Tool deps pinned via tools.go (//go:build tools) consistent with the root module.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Change the build output path from $GITHUB_WORKSPACE/backport to
$GITHUB_WORKSPACE/build/backport in all four backport workflows and
process-checked-branches.sh, consistent with the Buildkite BACKPORT_BIN
path and the magefile Build target.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The run: value started with a double-quoted string, causing YAML to treat
it as a quoted scalar and leave the subcommand as invalid trailing content.
Convert to a block scalar (run: |) to match the surrounding steps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Go's flag package stops parsing at the first non-flag argument, so
--json must come before the positional SHA arguments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…onflicts

When a commit on main touches multiple packages, cherry-picking it onto a
backport branch that doesn't have all the same packages previously reported
a spurious conflict instead of succeeding.

Add resetNonPackageChanges() which iterates git status --porcelain after
the cherry-pick and resets each file outside the target package dir to HEAD:
- git checkout HEAD -- file (handles clean staged changes and UU conflicts)
- git rm --force -- file fallback (handles DU modify/delete conflicts where
  HEAD has the file deleted, e.g. packages absent from the backport branch)

A note is printed to stderr when files outside the package are reset.

Add three integration tests covering all three cases:
- modify/delete conflict (file absent from backport branch)
- regular UU conflict (file exists on both branches, different edits)
- clean apply to other package (file exists, no conflict, still discarded)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace `go test` with `mage check` so the CI step exercises Build,
Format, Tidy, and Test together. Add check_git_diff at the end to catch
any files left dirty by format or tidy. Use add_bin_path + with_mage
(from common.sh) to set up Go and install mage from inside cmd/backport/,
matching the version pinned in cmd/backport/.go-version.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Changes to the main-only mage targets file should not trigger package
tests. Also fixes a stale test path (dev/backports/ no longer exists)
replaced with the correct cmd/backport/ path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update all references in backport_branch.sh, non_package_patterns.txt,
and test_non_package_patterns.sh.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
It is dev tooling (duplicate-name checker), not package content.
Consistent with dev/codeowners, dev/gitutil, and similar patterns.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backport branches must use the backport implementation from
cmd/backport.
- Add `already_exists` outcome to `buildCommentBody` with a message
  linking the existing sync PR (falls back to branch name when no URL).
- Append a `/sync-changelog` retry hint to all failure comments so
  maintainers can discover the retry command where they need it.
- Thread `existingSyncPRURL` through `PostComment` and `buildCommentBody`.
- Cover new behaviour with table-driven tests (TDD).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fetch the remote-tracking ref before pushing so --force-with-lease has
something to compare against, then overwrite the branch. This allows a
retry to cleanly replace a stale working branch left by a previous
failed run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Rename syncPRExists → existingSyncPR returning the PR URL so callers
  can surface it in comments rather than just a boolean.
- Add ExistingSyncPRURL to CollectResult; populate it on the early-return
  path when a sync PR already exists.
- Add knownPRNumber param to Collect to skip the merge-commit→PR
  association retry/backoff when the PR number is already known.
- Wire BACKPORT_PR_NUMBER and EXISTING_SYNC_PR_URL through the mage
  targets; emit already_exists outcome when a sync PR already exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add an issue_comment trigger so maintainers can retry a failed changelog
sync by commenting /sync-changelog on the originating backport PR.

- Split into two jobs with job-level ifs: sync-changelog (push) and
  retry-changelog (issue_comment), eliminating the zero-SHA guard step
  and all per-step proceed conditions.
- retry-changelog gates execution on four sequential checks: PR is merged,
  actor has write/admin permission, base branch is backport-*, and the
  merge commit can be resolved to a before/after SHA pair.
- Extract the shared setup steps (Go, mage, git, sync, comment) into a
  new composite action at .github/actions/sync-backport-changelog so
  both jobs reuse them without duplication.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mrodm mrodm self-assigned this Aug 6, 2026
mrodm and others added 2 commits August 6, 2026 17:04
Explains that citools, codeowners, and gitutil are local copies of their
dev/ counterparts, and why a replace directive is not used.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@elastic-vault-github-plugin-prod

Copy link
Copy Markdown
Contributor

🚀 Benchmarks report

To see the full report comment with /test benchmark fullreport

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# list all directories that are packages from the root of the repository
list_all_directories() {
mage -d "${WORKSPACE}" listPackages
mage -d "${WORKSPACE}" listPackages |grep -E '^packages/(nginx|elastic_package_registry)$'

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be reverted before merging.

mrodm and others added 2 commits August 7, 2026 12:32
Merge separate_backport_tool into add-sync-changelog-comment.

Conflict resolutions:
- magefile.go: remove backport mage targets; they now live in
  cmd/backport/magefile.go. Daily-job targets moved to magefile_daily_jobs.go.
- backport_branch.sh: merge both diff lines — include cmd/backport/ from
  separate_backport_tool and .github/actions/ + .github/workflows/ from HEAD.
- sync-backport-changelog.yml: keep the two-job structure (sync-changelog on
  push, retry-changelog on issue_comment) from HEAD.

Additional fixes needed after the merge:
- action.yml: replace mage syncBackportChangelog/postBackportComment with the
  cmd/backport binary (go build -C cmd/backport, then backport sync-changelog /
  backport post-comment). Uses cmd/backport/.go-version.
- cmd/backport/main.go: align runSyncChangelog and runPostComment with the
  updated signatures (4-arg Collect, 7-arg PostComment), add already_exists
  outcome, existing_sync_pr_url outputs, and pre-error output writes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Conflict resolutions:
- backport_branch.sh: removed .go-version from the diagnostic diff (root
  .go-version is not updated by the script; cmd/backport/.go-version is
  covered by the cmd/backport/ entry already copied from main); kept
  .github/actions/ and .github/workflows/ from our branch.
- cmd/backport/main.go: kept all sync-changelog enhancements from our
  branch (4-arg Collect with BACKPORT_PR_NUMBER, already_exists outcome,
  existing_sync_pr_url outputs, pre-error output writes, 7-arg PostComment
  with EXISTING_SYNC_PR_URL).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

💚 Build Succeeded

History

cc @mrodm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CI] Add /sync-changelog comment trigger to retry failed changelog sync PRs

1 participant