[backports] Add .backports.yml inventory, validator, and dry-run trigger (#19210)#19362
[backports] Add .backports.yml inventory, validator, and dry-run trigger (#19210)#19362mrodm wants to merge 56 commits into
Conversation
…c#19210) Introduce the single source of truth for backport branch inventory. - `.backports.yml` — lists all 58 existing backport-* branches with their package name, branch, base_version, base_commit (git merge-base with main), maintained_until (null — fill in known EOL dates in a review pass), and archived status (true for branches with last commit >1 year ago). - `dev/scripts/backport_bootstrap_inventory.sh` — one-time bootstrap script kept for auditability. Seeds .backports.yml from remote backport-* branches: resolves package manifests, determines base_commit via git merge-base, and marks stale branches as archived. Do not re-run; update the yml directly. Active branch logic (used by subsequent tooling in this issue): inactive if archived == true OR (maintained_until != null AND maintained_until < today) Closes part of elastic#19210 (foundation: inventory file + bootstrap). maintained_until dates to be filled in during review. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e inventory The original git merge-base approach returns a stale ancestor for old branches whose BASE_COMMIT is no longer in upstream/main's history. Two improvements: 1. CI-sync-parent heuristic: the backport_branch.sh script always commits a "Update/Add .buildkite from main" commit on top of BASE_COMMIT when creating a branch. The parent of the first such commit IS the BASE_COMMIT. This correctly recovers base_commit for branches like backport-aws-1.51 (88ad4b8 / 1.51.2) and backport-security_detection_engine-8.16 (550932f / 8.16.2). 2. Version-family check: if the resolved base_version doesn't belong to the branch name's major.minor family, the result is discarded before falling back to the next strategy. Remaining stale (base_commit: 77c5d81): backport-aws-7.15.0 and backport-cloud_security_posture-1.9 — both archived: true and their history predates the CI-sync commit pattern. Correct manually if needed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Revised base_commit discovery to use three complementary strategies and
pick the OLDEST (by commit timestamp) candidate whose version matches the
branch's major.minor family:
A) git merge-base — fast; works when BASE_COMMIT is still in main's history
B) Walk from HEAD, skip CI-automation and backport cherry-picks — finds the
first "normal" commit; skips [backport], "backporting", CI-sync messages,
and double-PR pattern "(#XXXXX) (#YYYYY)" common to backport cherry-picks
C) Parent of oldest CI-sync commit — reliable when the CI files were added
right after branch creation
"Oldest wins" resolves the class of branches where the CI-sync was re-run after
cherry-picks were applied (e.g. backport-cloud_security_posture-1.1):
- Walk (B) finds 6291fa2 (Dec 2022, version 1.1.1) as the first non-backport commit
- CI-sync parent (C) finds 9a1c96b (Feb 2023, version 1.1.3) — a cherry-pick
- Oldest wins → 6291fa2 ✓
Known remaining limitation: SDE backport branches (backport-security_detection_engine-*)
predate the CI-sync commit convention and their version-update commits carry a single
PR number with no backport markers, making it impossible to distinguish BASE_COMMIT from
later cherry-picks without a GitHub API check. Their base_commit values should be
verified manually via `gh pr view <N> --json baseRefName`.
One stale base_commit remains: backport-aws-7.15.0 (archived: true, low priority).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces dev/scripts/backport_check_active.sh — evaluates whether a
backport branch is active per the .backports.yml inventory.
Active logic:
inactive if: archived == true
OR (maintained_until != null AND maintained_until < today)
Flags:
--branch <name> branch to evaluate (required)
--json machine-readable JSON output
Exit codes: 0 = active, 1 = inactive, 2 = error
Uses pure awk for YAML parsing (no yq dependency) so it runs in any
environment without tool setup. Supports BACKPORTS_INVENTORY env var
override to point at a different inventory file (used by tests).
Tests added to .buildkite/scripts/run_dev_scripts_tests.sh covering:
active, archived → inactive, expired maintained_until → inactive,
future maintained_until → active, archived beats future date,
--json shape for all cases, unknown branch, missing flag, unknown flag.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the awk-based YAML parser in backport_check_active.sh with direct yq calls — the script now assumes yq is installed (consistent with other backport tooling in this repo). Also consolidate the test cleanup: DUMMY_INVENTORY is now declared at the top of run_dev_scripts_tests.sh alongside DUMMY_REPO and removed by the shared EXIT trap, ensuring the temp file is always deleted even when a test failure causes early exit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…stic#19210) Introduces dev/backports/inventory.go which validates .backports.yml schema: - required string fields: package, branch, base_version, base_commit - archived must be present (bool) - maintained_until may be null; when set must be a valid YYYY-MM-DD date - all violations across all entries are collected and reported together Adds mage target ValidateBackportsInventory() that runs the validator against the repo-root .backports.yml. Tests in dev/backports/inventory_test.go are auto-picked up by goTest's ./dev/... glob in the existing check step. Adds a dedicated check-backports-inventory step in .buildkite/pipeline.yml that runs mage validateBackportsInventory on every build. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Include dev/backports/ alongside the other dev/ mage helper packages (citools, testsreporter, coverage, codeowners, packagenames) that are synced from main when a backport branch is created or updated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lastic#19210) Changes to .backports.yml and dev/backports/ are CI/tooling files and should not trigger the full package test matrix. dev/scripts/ already covered the backport shell scripts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…elastic#19210) Replace the inline mage command in the pipeline with a dedicated script that mirrors the setup pattern of check_sources.sh: sources common.sh, then calls add_bin_path / with_mage / with_yq to install dependencies before running mage validateBackportsInventory. Switch the step to a GCP VM agent (IMAGE_UBUNTU_X86_64) so that Go, mage, and yq are reliably available via the common.sh helpers rather than assuming they are pre-installed on the runner. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces os.CreateTemp + manual t.Cleanup with t.TempDir(), which automatically removes the directory when the test ends. Simplifies writeTemp to a single os.WriteFile call. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…#19210) The catch-all rule already covers it, but an explicit entry makes the ownership intent visible for the backport inventory file. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…yml changes (elastic#19210) When a PR edits .backports.yml, trigger_backport_dryrun.sh compares the old and new inventory, finds entries that are new or have a changed base_commit (skipping archived ones), and dynamically uploads one trigger step per changed entry: trigger: integrations-backport build.env: DRY_RUN=true, PACKAGE_NAME, PACKAGE_VERSION, BASE_COMMIT The trigger step runs from the main pipeline via if_changed: .backports.yml. pipeline.backport.yml changes: - check-ui: also allows build.source == 'trigger_job' (was UI-only) - input-variables: skipped for triggered builds so the pipeline does not block waiting for human input; backport_branch.sh already falls back to env vars set by the trigger step Also adds trigger_backport_dryrun.sh to non_package_patterns so that editing it does not trigger the full package test matrix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dryrun.sh Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
trigger_backport_dryrun.sh: add cleanup() + trap EXIT so OLD_INVENTORY and PIPELINE_FILE are always removed even if yq fails inside the loop or buildkite-agent pipeline upload fails. Variables are initialised to "" before the trap is registered so cleanup() never calls rm -f "". backport_bootstrap_inventory.sh: remove unused `local all_manifests` declaration in find_manifest_path() — the variable was declared but never assigned; the loop reads directly into `candidate`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d package existence
Skip branches that already exist in the base-branch inventory; only newly added entries warrant a dry-run of integrations-backport. Also add skip-reason echo messages for both the archived and already-present cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Validate both OLD_INVENTORY and NEW_INVENTORY with yq before the loop, replacing the silent || true / 2>/dev/null error suppression with a fast-fail check that prints a clear error message. - Move the OLD_INVENTORY existence check above the three field extractions to avoid wasted yq forks for pre-existing entries. - Update stale file-header comment and log banner to reflect that only new entries (not base_commit changes) trigger a dry-run. - Expand inline comment to explicitly cover the re-activation case (archived:true → false): branch already exists, no dry-run needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move the backport_check_active.sh tests from the inline runner into dev/scripts/test_backport_check_active.sh, following the same pattern as test_get_release_commit.sh. The runner now delegates to both test files via run_tests_if_exists. Also add dev/scripts/README.md documenting that scripts here are not involved in package testing, which is why the directory is listed in non_package_patterns, and noting what to update if that assumption ever changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
505a87e to
9ca6828
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
f1bac57 to
aeb87cb
Compare
TL;DRThe failing Buildkite job is the backport-branch creation step, and it fails because the configured Remediation
Investigation detailsRoot Cause
This is a configuration/pipeline input mismatch (invalid commit/branch context for backport validation), not a package test failure. Evidence
Verification
Follow-upIf this run was intended as a PR-only dry-run, gate the branch-creation step so it does not execute commit validation without explicit UI input metadata. Note 🔒 Integrity filter blocked 2 itemsThe following items were blocked because they don't meet the GitHub integrity level.
To allow these resources, lower tools:
github:
min-integrity: approved # merged | approved | unapproved | noneWhat is this? | From workflow: PR Buildkite Detective Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not. |
Add duplicate detection to the Go inventory validator:
- Flag entries sharing the same branch name.
- Flag entries sharing the same package + base_version combination,
using map[string]struct{} sets for zero-value-allocation membership
checks.
An exception map (duplicatePackageVersionExceptions) allows known
legitimate duplicates to be explicitly listed with a comment. The first
entry is security_detection_engine@8.17.7, shared by
backport-security_detection_engine-8.17 and -8.18, which were both cut
from the same release tag.
Six new test cases cover: duplicate branch, duplicate package/version,
valid same-package-different-versions, valid same-version-different-packages,
the known exception, and both duplicate kinds reported together.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
a414959 to
dcbbab2
Compare
Remove entry for .backports.yml file in CODEOWNERS file. By default, all files are owned by the ecosystem team.
There was a problem hiding this comment.
This file was added to the Pull Request for reference on how the initial .backports.yml was built.
But this was for a one-shot.
I think it can be removed. WDYT ?
Replace the shell-based backport_check_active.sh and its bash test suite with a Go implementation in dev/backports/inventory.go (CheckActive function + ActiveResult type) and a new mage target CheckBackportBranchActive. The Go implementation covers both archived and maintained_until expiry checks (the shell script only checked archived). trigger_backport_dryrun.sh now uses the mage target instead of yq for the active-branch check, adding proper error distinction between inactive (exit 1) and error (exit 2). Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This reverts commit dcbbab2.
| # list all directories that are packages from the root of the repository | ||
| list_all_directories() { | ||
| mage -d "${WORKSPACE}" listPackages | ||
| mage -d "${WORKSPACE}" listPackages |grep "^packages/elastic_package_registry$" |
There was a problem hiding this comment.
To be removed before merging.
Keep it for now in case it is needed to push more changes.
There was a problem hiding this comment.
This will trigger a DRY_RUN build of the integrations-backport for each new entry detected.
For this first time, it has been set that it will not trigger any build. This is detected when in the main branch there is no .backports.yml file.
It has also been set in the .buildkite/pipeline.yml file that this step is just triggered in Pull Requests targeting main brnach.
if_changed:
- ".backports.yml"
if: |
build.env('BUILDKITE_PULL_REQUEST') != "false" &&
build.env('BUILDKITE_PIPELINE_SLUG') == "integrations" &&
build.env('BUILDKITE_PULL_REQUEST_BASE_BRANCH') == "main"
💚 Build Succeeded
History
cc @mrodm |
| // Prints "<branch>: active" or "<branch>: inactive (<reason>)". | ||
| // Pass -json for JSON output: mage CheckBackportBranchActive <branch> -json | ||
| // Exit codes: 0 = active, 1 = inactive, 2 = error (branch not found, parse error, etc.). | ||
| func CheckBackportBranchActive(branch string, asJSON *bool) error { |
There was a problem hiding this comment.
Since mage 1.16.0, optional parameters are supported.
The optional parameter have the name of the argument. So here it would be
mage checkBackportBranchActive -asjson
Do you prefer another name for this argument/parameter?
Some other options:
-tojson-json
If we want to use -json , it would be needed to do some renamings in the imports too and other changes
--- magefile.go
+++ magefile.go
@@ -8,7 +8,7 @@ package main
import (
"context"
- "encoding/json"
+ stdjson "encoding/json"
"fmt"
"io"
"os"Probably it is better (or expected by the users) to use -json , WDYT ?
TL;DR
Implements the backport branch inventory described in issue #19210:
a
.backports.ymlfile that tracks all activebackport-*branches,a Go package with validator and active-branch checker that runs in CI,
and a pipeline step that dry-runs
integrations-backportfor each newly added inventory entry.
Proposed commit message
Add .backports.yml inventory, validator, and dry-run trigger
WHAT
Introduces a structured inventory of all active backport branches in
.backports.yml(59 entries at time of writing). Each entry records:A branch is considered inactive if
archived: trueORmaintained_untilis set to a date in the past.Supporting tooling added alongside the inventory:
dev/backports/inventory.go— Go implementation that provides:ValidateInventory(run viamage validateBackportsInventory):checks every entry for required fields, valid semver
base_version,hex
base_commit, that the referenced package exists inpackages/,and detects duplicate branch names and package/version pairs.
CheckActive(run viamage checkBackportBranchActive):evaluates whether a single branch is active, covering both the
archived: trueflag and an expiredmaintained_untildate..buildkite/scripts/check_backports_inventory.sh— CI wrapper thatruns the Go validator and blocks the pipeline on failure.
.buildkite/scripts/trigger_backport_dryrun.sh— for each entrythat is new in the PR (absent from the base branch), uploads a
trigger step that runs
integrations-backportinDRY_RUN=truemode to validate
base_commitandbase_versionbefore the entrylands on
main.New
magetargets inmagefile.go:ValidateBackportsInventory.backports.ymlschema (required fields, semver, hex SHA, package existence, duplicates)CheckBackportBranchActive <branch> [-asjson]CI wiring in
.buildkite/pipeline.yml:check-backports-inventorystep runs the Go validator on every PRthat touches
.backports.ymlor the validator source.trigger-backport-dryrunstep runs the dry-run trigger, gated oncheck-backports-inventorypassing..buildkite/pipeline.backport.ymlis updated so triggered dry-runbuilds skip the human-input gate (
input-variablesstep).dev/scripts/README.mdadded to document thatdev/scripts/scriptsare not involved in package testing and are therefore excluded from
non_package_patterns.Test coverage:
dev/backports/inventory_test.go— Go unit tests for both thevalidator (
ValidateInventory) and the active-branch check(
CheckActive), including duplicate-detection cases and the knownsecurity_detection_engine@8.17.7exception.run_dev_scripts_tests.shrefactored to delegate to per-script testfiles via direct
bashcalls (missing file now fails the build).WHY
Without a structured inventory, automation cannot reliably determine
which
backport-*branches are active, maintained, or stale.This inventory is the foundation for the backport tooling described
in the parent epic (#19016).
Author's Checklist
base_commitvalues forbackport-security_detection_engine-*branches (noted as approximations in the file header).integrations-backportpipeline https://buildkite.com/elastic/integrations/builds/44100archivedvalues for the entries in.backports.ymlHow to test this PR locally
The
trigger-backport-dryrunstep can be observed in the Buildkitebuild for this PR — it should queue a dry-run for any entry added or
absent from
main's.backports.yml. Example: https://buildkite.com/elastic/integrations/builds/44100Related issues