Skip to content

[backports] Add .backports.yml inventory, validator, and dry-run trigger (#19210)#19362

Open
mrodm wants to merge 56 commits into
elastic:mainfrom
mrodm:initial-backports-file
Open

[backports] Add .backports.yml inventory, validator, and dry-run trigger (#19210)#19362
mrodm wants to merge 56 commits into
elastic:mainfrom
mrodm:initial-backports-file

Conversation

@mrodm

@mrodm mrodm commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

TL;DR

Implements the backport branch inventory described in issue #19210:
a .backports.yml file that tracks all active backport-* branches,
a Go package with validator and active-branch checker that runs in CI,
and a pipeline step that dry-runs integrations-backport
for 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:

- package: aws
  branch: backport-aws-3.17
  base_version: "3.17.0"
  base_commit: "5b593f6681"
  maintained_until: null
  archived: false

A branch is considered inactive if archived: true OR
maintained_until is set to a date in the past.

Supporting tooling added alongside the inventory:

  • dev/backports/inventory.go — Go implementation that provides:
    • ValidateInventory (run via mage validateBackportsInventory):
      checks every entry for required fields, valid semver base_version,
      hex base_commit, that the referenced package exists in packages/,
      and detects duplicate branch names and package/version pairs.
    • CheckActive (run via mage checkBackportBranchActive):
      evaluates whether a single branch is active, covering both the
      archived: true flag and an expired maintained_until date.
  • .buildkite/scripts/check_backports_inventory.sh — CI wrapper that
    runs the Go validator and blocks the pipeline on failure.
  • .buildkite/scripts/trigger_backport_dryrun.sh — for each entry
    that is new in the PR (absent from the base branch), uploads a
    trigger step that runs integrations-backport in DRY_RUN=true
    mode to validate base_commit and base_version before the entry
    lands on main.

New mage targets in magefile.go:

Target Description
ValidateBackportsInventory Validates .backports.yml schema (required fields, semver, hex SHA, package existence, duplicates)
CheckBackportBranchActive <branch> [-asjson] Reports whether a branch is active; exit 0 = active, 1 = inactive, 2 = error

CI wiring in .buildkite/pipeline.yml:

  • check-backports-inventory step runs the Go validator on every PR
    that touches .backports.yml or the validator source.
  • trigger-backport-dryrun step runs the dry-run trigger, gated on
    check-backports-inventory passing.

.buildkite/pipeline.backport.yml is updated so triggered dry-run
builds skip the human-input gate (input-variables step).

dev/scripts/README.md added to document that dev/scripts/ scripts
are 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 the
    validator (ValidateInventory) and the active-branch check
    (CheckActive), including duplicate-detection cases and the known
    security_detection_engine@8.17.7 exception.
  • run_dev_scripts_tests.sh refactored to delegate to per-script test
    files via direct bash calls (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

  • Ensure all debugging changes are removed before merging.
  • Verify base_commit values for backport-security_detection_engine-* branches (noted as approximations in the file header).
  • Test triggering integrations-backport pipeline https://buildkite.com/elastic/integrations/builds/44100
  • Check archived values for the entries in .backports.yml

How to test this PR locally

# Run the Go inventory validator
mage validateBackportsInventory

# Check whether a specific branch is active
dev/scripts/backport_check_active.sh --branch backport-aws-3.17
dev/scripts/backport_check_active.sh --branch backport-aws-3.17 --asjson

# Run the test suite
mage -v check
# or 
go test ./dev/backports/...

The trigger-backport-dryrun step can be observed in the Buildkite
build 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/44100

Related issues


This PR was generated with the assistance of Claude (claude-sonnet-4-6).

mrodm and others added 30 commits May 29, 2026 13:29
…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>
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>
@mrodm mrodm force-pushed the initial-backports-file branch from 505a87e to 9ca6828 Compare June 3, 2026 17:32
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@mrodm mrodm force-pushed the initial-backports-file branch from f1bac57 to aeb87cb Compare June 3, 2026 18:14
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

TL;DR

The failing Buildkite job is the backport-branch creation step, and it fails because the configured BASE_COMMIT (65b2a04d98) is not found in the branch being checked (HEAD in this run). Update the backport inputs/branch selection so commit validation runs against the intended source branch and a valid commit SHA.

Remediation

  • Ensure SOURCE_BRANCH resolves to the intended branch (typically main) before commitExists runs in .buildkite/scripts/backport_branch.sh (SOURCE_BRANCH assignment near line 43; validation at lines 294-297).
  • Provide a valid BASE_COMMIT from that branch via Buildkite metadata/input step before running .buildkite/scripts/backport_branch.sh.
  • Re-run the integrations-backport build after updating branch/metadata values.
Investigation details

Root Cause

create-backport-branch exits in the base-commit validation path:

  • Script logic validates BASE_COMMIT with commitExists "$BASE_COMMIT" "$SOURCE_BRANCH" at .buildkite/scripts/backport_branch.sh line 295.
  • In this failing run, logs show SOURCE_BRANCH: HEAD, and then validation fails with The commit 65b2a04d98 doesn't exist in the branch HEAD.

This is a configuration/pipeline input mismatch (invalid commit/branch context for backport validation), not a package test failure.

Evidence

Verification

  • Not run (detective workflow is read-only; analysis based on provided Buildkite logs and repository script logic).

Follow-up

If 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 items

The following items were blocked because they don't meet the GitHub integrity level.

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

What is this? | From workflow: PR Buildkite Detective

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

github-actions Bot and others added 4 commits June 4, 2026 11:46
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>
@mrodm mrodm force-pushed the initial-backports-file branch from a414959 to dcbbab2 Compare June 4, 2026 14:54
Remove entry for .backports.yml file in CODEOWNERS file. By default, all
files are owned by the ecosystem team.

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.

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 ?

github-actions Bot and others added 7 commits June 4, 2026 18:04
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$"

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 removed before merging.
Keep it for now in case it is needed to push more changes.

@mrodm mrodm Jun 5, 2026

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.

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"

@elasticmachine

Copy link
Copy Markdown

💚 Build Succeeded

History

cc @mrodm

Comment thread magefile.go
// 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 {

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.

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 ?

@mrodm mrodm added the ci label Jun 5, 2026
@mrodm mrodm marked this pull request as ready for review June 5, 2026 14:28
@mrodm mrodm requested a review from a team as a code owner June 5, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci documentation Improvements or additions to documentation. Applied to PRs that modify *.md files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

backport: introduce .backports.yml inventory and active-branch check script

3 participants