Repository navigation
[backport] Auto-create backport PRs, fix changelog links, and sync changelogs to main - #20197
Conversation
Inject a sentinel URL (https://github.com/<repo>/pull/REPLACE_ME) into every changelog link field of a backport entry, instead of copying the original PR link. The sentinel is replaced with the actual backport PR URL in a follow-up commit once the PR is opened. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ersion Adds UpdateEntryLinks(path, version, newURL) which replaces every link: value in a given changelog version entry with newURL, leaving all other entries untouched. Used to replace the sentinel URL with the real backport PR URL once the PR is opened. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add fixChangelogLink to apply.go, which runs after maybeOpenPR() returns the real PR URL. It calls UpdateEntryLinks to replace the sentinel, then commits "Fix changelog link to backport PR" and pushes as a second commit on the working branch. Failures are non-fatal: a warning is logged to stderr and Apply() still returns success with the PRURL populated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BuildComment now renders `- [ ] \`branch\`` / `- [x] \`branch\`` using the checked map instead of plain list items. Intro text updated to: "PRs will be created automatically on merge, or when you update this checklist after merge." Tests updated to cover unchecked/checked rendering and the new sentence. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New ChecklistItem struct (Package, Branch, Checked, Processed) and ParseChecklistItems(body string) []ChecklistItem in checklist.go. Parses **package** section headers and checkbox branch lines; sets Processed=true when a ✅ or⚠️ status suffix is present. 10 test cases covering: empty/marker-free body, checked/unchecked, success/conflict status markers, maintained_until suffix combinations, and multiple packages with multiple branches. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New pure function UpdateBranchStatus(body, branch, status string) string in checklist.go. Appends " — <status>" to the matching checkbox branch line; no-ops if the line already carries a ✅ or⚠️ suffix (defence-in- depth against workflow self-trigger loops). 7 test cases covering: success/conflict status appended, no-op on existing ✅/⚠️ suffix, branch not found, only matching line updated, and maintained_until suffix preserved. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… mage targets ParseBackportChecklist reads a checklist comment body from a file and prints a JSON array of ChecklistItem to stdout. UpdateChecklistBranchStatus reads a body from a file, appends a status suffix to the given branch line, and prints the updated body to stdout. Both are used by the upcoming auto-backport.yml workflow. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Triggers on push to main and issue_comment edited events. For each merged PR that has a backport checklist, creates backport PRs for every checked, unprocessed branch. Updates the checklist comment with ✅ #PR or⚠️ conflict status after each branch. Key behaviours: - Job-level actor guard breaks the self-trigger loop from bot comment edits - Permission check (write/admin) gates issue_comment runs before checkout - backport:sync-changelog PRs are silently skipped - Dedup via working-branch name prevents duplicate PRs on re-runs - Checklist patched per-branch so partial progress survives cancellation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a lightweight pr-check step (just gh pr view --json state) that runs before checkout for issue_comment events. Checkout, Go setup, mage install, and resolve now gate on pr-check.outputs.merged so unmerged-PR comment edits exit without spinning up the full toolchain. The redundant state check inside the resolve step is removed since pr-check already guarantees the PR is merged at that point. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolve merge SHA and PR metadata now runs before checkout, Go setup, and mage install. All three gate on steps.resolve.outputs.pr_number so push events with no matching PR or a backport:sync-changelog label exit after a single fast API call without cloning the repository. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After a successful non-dry-run Apply(), the working tree was left on the auto-backport working branch (based on the target backport branch), not on the branch that was active when Apply() started. The auto-backport workflow calls mage targets (updateChecklistBranchStatus) after backport_apply.sh returns; those targets only exist on the calling branch, so the "Unknown target specified" errors seen in CI were caused by this missing restore. The error path already ran `git checkout -` via the deferred cleanup — this commit adds the same restore to the success path, just before the `success = true` that skips the defer. Also adds TestApplyIntegration_RestoresBranchAfterSuccess to cover this. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ut clean
gitutil.Git.Run() wires cmd.Stdout to os.Stdout. When backport_apply.sh
calls `exec mage ApplyBackport --json`, mage's os.Stdout is captured by
the shell's $() subshell into $RESULT. Every a.git.Run("commit", ...)
inside Apply() therefore mixed git commit summaries into $RESULT, making
it invalid JSON. jq then failed to parse the status field, causing the
workflow to always treat a successful backport as a conflict (⚠️ instead
of ✅).
Fix: add gitutil.Git.RunToStderr(), which redirects both stdout and
stderr to os.Stderr. Switch all 16 a.git.Run() calls in apply.go to
RunToStderr() so that git informational output never reaches the JSON
capture buffer. The original Run() is untouched and still used by other
callers (sync.go, etc.) where stdout contamination is not a concern.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Backport mage *string parameters require -name=value flag syntax, not positional arguments. backport_apply.sh was passing remote, repository and packagesDir as positional args after the bool flags, so mage never set them (they stayed nil) and treated the leftover tokens as extra targets to run — producing "Unknown target specified: """ errors and causing repository to default to "elastic/integrations" regardless of the actual repo. Fix: add all three *string params to the flags array using -name=value format, only when non-empty (omitting the flag leaves the param nil in mage, which Apply() handles as "use default"). Also update the ApplyBackport doc comment to document all optional flags with their correct format. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switch post-backport-checklist.yml from PATCH (edit in place) to DELETE + POST so the checklist always floats to the bottom of the PR thread and notifies subscribers on each update. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The commits/{sha}/pulls API may not immediately associate a merge
commit with its PR after a push event fires. Add a retry loop with
delays of 0s, 5s, 15s, and 30s before giving up so transient API
propagation lag no longer causes the sync-changelog step to skip.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a PR check that fails when a backport PR introduces a changelog version that already exists in origin/main, catching duplicate entries before merge. Only runs on PRs targeting backport-* branches where **/changelog.yml changed. - CheckVersionsAgainstMain in collect.go: diffs the PR's changes against its base branch and checks each new version against main - CheckChangelogVersionsInMain mage target: takes the base branch as a parameter (passed from $BUILDKITE_PULL_REQUEST_BASE_BRANCH) - check_changelog_versions_in_main.sh: Buildkite script wrapper - pipeline.yml: new step with PR + backport-* base branch guards - collect_test.go: 3 integration tests (no conflict, conflict, no changelog changes) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exit code 1 (hard fail) is now reserved for changelog entries containing the REPLACE_ME placeholder, which indicates the auto-fix step in Apply() failed to push the corrected link. Regular link mismatches (wrong but real PR URL) exit with code 2 (soft fail). The pipeline step is updated to only soft-fail on exit 2, so a REPLACE_ME link blocks the backport PR from merging instead of just annotating it. Generated with Claude Sonnet 4.6
When a push event and an issue_comment event fire at nearly the same time (e.g. a checklist item is checked just before the PR is merged), both runs pass the dedup guard simultaneously and race to push the working branch. The losing run's push is rejected, which previously surfaced as a false⚠️ conflict on the checklist. The failure path now does a recovery lookup: if a PR already exists for the working branch, the run posts ✅ with that PR number instead of⚠️ . Generated with Claude Sonnet 4.6
Moves the inline shell script from the "Process checked branches" step in auto-backport.yml into .github/scripts/backport/process-checked-branches.sh. The workflow step is reduced to a single run: call. No behaviour change. Also adds ^\.github/scripts/ to non_package_patterns.txt so changes to this new directory do not trigger package tests. Generated with Claude Sonnet 4.6
The dedup check was extracting the version with grep -oP '\d+\.\d+$',
which fails for branches like backport-kubernetes-1.x (VERSION becomes
empty, producing auto-backport/kubernetes--<sha>).
Now mirrors workingBranchName in apply.go exactly:
strings.TrimPrefix(branchName, "backport-"+pkg+"-")
using bash prefix stripping: ${BRANCH#backport-${PKG}-}
This correctly handles both numeric (6.14) and non-numeric (1.x) suffixes.
Generated with Claude Sonnet 4.6
Generated with Claude Sonnet 4.6
…ly changes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Latest tests run in my own fork:
The default branch has been changed in my own fork for debugging purposes. |
|
Hi @elastic/observablt-ci , adding you as a reviewers in case there is anything that should be changed or improved regarding the new Github Action workflows introduced here (permissions, steps, concurrency groups...) to automate backport processes or if you find anything else. Thanks in advance! |
| - step: "check-buildkite-scripts" | ||
| allow_failure: false | ||
| if: | | ||
| build.branch == "nonexisting" |
There was a problem hiding this comment.
I guess this is a leftover?
There was a problem hiding this comment.
Yes, I'll remove it before merging to avoid triggering a new step for each package defined in the repo (460 or more steps).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Tick the box to add this pull request to the merge queue (same as
|
⏳ Build in-progress, with failures
Failed CI StepsHistory
cc @mrodm |
🚀 Benchmarks reportPackage
|
| Data stream | Previous EPS | New EPS | Diff (%) | Result |
|---|---|---|---|---|
item_usages |
15873.02 | 7874.02 | -7999 (-50.39%) | 💔 |
Package akamai 👍(0) 💚(0) 💔(1)
Expand to view
| Data stream | Previous EPS | New EPS | Diff (%) | Result |
|---|---|---|---|---|
siem |
1277.14 | 940.73 | -336.41 (-26.34%) | 💔 |
Package atlassian_jira 👍(0) 💚(0) 💔(1)
Expand to view
| Data stream | Previous EPS | New EPS | Diff (%) | Result |
|---|---|---|---|---|
audit |
3401.36 | 2617.8 | -783.56 (-23.04%) | 💔 |
Package mimecast 👍(4) 💚(3) 💔(4)
Expand to view
| Data stream | Previous EPS | New EPS | Diff (%) | Result |
|---|---|---|---|---|
ttp_ip_logs |
16393.44 | 12048.19 | -4345.25 (-26.51%) | 💔 |
dlp_logs |
8620.69 | 5494.51 | -3126.18 (-36.26%) | 💔 |
siem_logs |
3759.4 | 2941.18 | -818.22 (-21.76%) | 💔 |
threat_intel_malware_grid |
7407.41 | 6211.18 | -1196.23 (-16.15%) | 💔 |
To see the full report comment with /test benchmark fullreport
…ckages Update docs to reflect the now-merged auto-backport automation (PR elastic#20197) and upcoming skip_checklist_packages field (PR elastic#20384): - Replace "Coming soon" note with description of auto-backport.yml: on merge, the workflow reads checked branches from the checklist and creates backport PRs automatically; post-merge checklist edits also trigger it. - Update checklist comment section: describe checkboxes and auto-backport trigger, update example to show [ ] checkbox format and new intro text, and note that the comment is now recreated (delete+POST) on each push. - Add "Suppressing a package from the checklist" block documenting the new top-level skip_checklist_packages field (elastic#20384). - ci_pipelines.md: remove stale "coming soon" qualifier from the check-changelog-versions-in-main step — it shipped with elastic#20197. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Proposed commit message
This PR implements the automation described in #19214 (and related meta-issue #19016). It adds three tightly-coupled features:
1. Auto-backport PR creation (
auto-backport.yml)A new
auto-backport.ymlworkflow fires onpushtomainand onissue_comment: edited. When a PR merges intomain:- [x] backport-*), it callsmage applyBackportto cherry-pick the fix onto a working branch and open a backport PR.2. Changelog link sentinel + post-creation fix (
apply.go,update.go)Cherry-picked changelog entries previously copied the source PR's link verbatim, pointing readers at the wrong PR. This is fixed by:
https://github.com/<repo>/pull/REPLACE_ME) for everylink:field whenApply()writes the changelog entry.fixChangelogLink()replaces the sentinel with the real backport PR URL, stages and commits the change as a second commit ("Fix changelog link to backport PR"), and pushes — no force-push required.changelog.UpdateEntryLinks(path, version, newURL)helper handles the line-level rewrite.3. Sync-changelog-to-main workflow (
sync-backport-changelog.yml)When a backport PR is merged into a
backport-*branch, a newsync-backport-changelog.ymlworkflow fires:mage collectChangelog.mainadding that entry, so package changelogs on the main branch stay up to date.4. Buildkite check for duplicate changelog versions (
check_changelog_versions_in_main.sh)A new Buildkite step runs on PRs targeting
backport-*branches and fails if a changelog version introduced by the PR already exists inmain. This catches sync collisions early. Implemented via:changelog.CheckVersionsAgainstMain(git, before, after)incollect.gomage checkChangelogVersionsInMain <baseBranch>target inmagefile.go5. Backport checklist improvements (
checklist.go)BuildCommentnow renders- [ ]/- [x]checkboxes (not plain list items) and updated intro text explaining that PRs are created automatically.ParseChecklistItemsandUpdateBranchStatushelpers maintain per-branch status without disturbing the rest of the comment body.post-backport-checklist.ymlrecreates (delete + POST) the checklist comment on each push, ensuring authors always receive a notification.Other fixes:
Apply()redirects git stdout to stderr so the JSON result stays clean.Apply()succeeds.mage ApplyBackport*stringparams now accept positional args correctly.backport_apply.shis now executable.Merge commit message
Author's Checklist
9b3c252501("Skip trigger integration tests - to be removed") which disables the integration test trigger step in.buildkite/pipeline.ymlad8e1cf7d6("Tests for debugging in fork") which adds a test branch trigger toauto-backport.ymland changes thesync.gocheckout base toadd_workflows_backport_prs-testinstead ofmain9d376f8131("Add backport entry") which adds a temporary entry to.backports.ymlused for testingauto-backport.ymlfires correctly on bothpushandissue_comment editedeventssync-backport-changelog.ymlopens a PR tomainafter a backport branch mergecheck-changelog-versions-in-mainstep blocks on duplicate versionsHow to test this PR locally
The full end-to-end flow has been validated in a personal fork. The following three PRs demonstrate each stage of the automation:
PR adding a change to the default branch (triggers
auto-backport.ymlon merge):Test kubernets package update - including owners script mrodm/integrations#53
Backport PR created automatically after merging the above (created by
auto-backport.yml, changelog link fixed to point at this PR):[kubernetes] Backport Test backport (1.83.4) mrodm/integrations#54
Sync-changelog PR updating the default branch changelog (created automatically by
sync-backport-changelog.ymlafter the backport PR was merged):changelog: kubernetes 1.83.4 (backport sync from PR #54) mrodm/integrations#55
Related issues