From 8eb8990c7b2e30e3bb965d2a851634d11a76ca86 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Mon, 22 Jun 2026 20:43:24 +0200 Subject: [PATCH 1/2] Buildkite: Skip PR builds for non-legacy branches Make the docs-build-pr check exit 0 early (reporting success on the still-required check) when a PR targets a branch that no longer carries legacy AsciiDoc docs. A new Perl helper derives each repo's legacy branch set from conf.yaml so the guard stays correct as products migrate to docs-builder, without hardcoded version patterns. Supersedes #3233, which used a single regex that broke non-stack-versioned doc sets (e.g. ECE's ms-* branches, ECK 3.x). Co-Authored-By: Claude Opus 4.8 (1M context) --- .buildkite/scripts/build_pr.sh | 12 ++++ .buildkite/scripts/legacy_branches.pl | 81 +++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100755 .buildkite/scripts/legacy_branches.pl diff --git a/.buildkite/scripts/build_pr.sh b/.buildkite/scripts/build_pr.sh index 506a5a6d0ee11..9fc51e6b5e2ac 100755 --- a/.buildkite/scripts/build_pr.sh +++ b/.buildkite/scripts/build_pr.sh @@ -41,6 +41,18 @@ buildkite-agent \ if [[ "${GITHUB_PR_BASE_REPO}" != 'docs' ]]; then + # Skip the build but report success when the PR targets a branch that no longer carries + # legacy AsciiDoc docs (per conf.yaml). docs-build-pr is a required check, so we exit 0 + # to keep it green rather than failing or leaving it pending. + # The helper exits non-zero on a parse error or unknown repo, which short-circuits the + # condition below so we build as today — i.e. fail open, never block a real build. + if legacy_branches=$(perl "$(dirname "$0")/legacy_branches.pl" "$GITHUB_PR_BASE_REPO") \ + && [[ -n "${legacy_branches}" ]] \ + && ! grep -qxF "${GITHUB_PR_TARGET_BRANCH}" <<< "${legacy_branches}"; then + echo "Target branch '${GITHUB_PR_TARGET_BRANCH}' has no legacy AsciiDoc docs in conf.yaml for ${GITHUB_PR_BASE_REPO} — skipping build (reporting success)." + exit 0 + fi + # Buildkite PR bot for repositories other than the `elastic/docs` repo are configured to # always checkout the master branch of the `elastic/docs` repo (where the build logic resides). # We first need to checkout the product repo / branch in a sub directory, that we'll reference diff --git a/.buildkite/scripts/legacy_branches.pl b/.buildkite/scripts/legacy_branches.pl new file mode 100755 index 0000000000000..6b5a000759e3b --- /dev/null +++ b/.buildkite/scripts/legacy_branches.pl @@ -0,0 +1,81 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use YAML qw(LoadFile); + +# Usage: legacy_branches.pl +# Prints one legacy (AsciiDoc) branch per line that the given repo still carries in conf.yaml. +# +# Exit 0: repo found; list may be empty if fully migrated to docs-builder. +# Exit 2: repo not found in conf.yaml — caller should build as today (fail open). +# Exit 1: YAML parse / load error — caller should build as today (fail open). + +my ($github_repo) = @ARGV + or die "Usage: $0 \n"; + +# conf.yaml lives in the repo root, which is also the working directory when +# build_pr.sh runs — same assumption used by build_docs.pl (see build_docs.pl:964). +my $conf = eval { LoadFile('conf.yaml') }; +if ($@) { + warn "Failed to load conf.yaml: $@\n"; + exit 1; +} + +# Build a map: GitHub repo name (URL basename minus .git) -> conf repo key. +# Sources in conf.yaml reference the conf key (e.g. "esf"), not the GitHub name +# (e.g. "elastic-serverless-forwarder"), so we need this translation. +my %github_to_key; +while ( my ( $key, $url ) = each %{ $conf->{repos} } ) { + ( my $name = $url ) =~ s{.*/|\.git$}{}g; # URL -> repo name (strip path and .git) + $github_to_key{$name} = $key; +} + +my $conf_key = $github_to_key{$github_repo}; +unless ( defined $conf_key ) { + exit 2; # repo not in conf — caller builds as today +} + +# Walk conf.yaml contents and collect every git branch for which this repo still +# appears as a source, using the same branch-resolution rules as the build itself +# (ES::Book:104-109 + ES::BranchTracker:63): +# - git branch = LHS of each branches entry (scalar or single-key hash) +# - map_branches{book_branch} applied to get the actual git branch in the source repo +# - branches in exclude_branches skipped +my %branches; +walk_entries( $conf->{contents}, $conf_key, \%branches ); + +print "$_\n" for sort keys %branches; +exit 0; + + +sub walk_entries { + my ( $entries, $conf_key, $branches ) = @_; + for my $entry (@$entries) { + if ( $entry->{sections} ) { + walk_entries( $entry->{sections}, $conf_key, $branches ); + } else { + collect_book_branches( $entry, $conf_key, $branches ); + } + } +} + +sub collect_book_branches { + my ( $book, $conf_key, $branches ) = @_; + my $branch_list = $book->{branches} or return; + my $sources = $book->{sources} or return; + + my @matching = grep { ( $_->{repo} // '' ) eq $conf_key } @$sources; + return unless @matching; + + for my $source (@matching) { + my $map = $source->{map_branches} // {}; + my %excl = map { $_ => 1 } @{ $source->{exclude_branches} // [] }; + + for my $entry (@$branch_list) { + # Each entry is a scalar or a single-key hash { book_branch => display_title } + my ($branch) = ref $entry eq 'HASH' ? keys %$entry : ($entry); + next if $excl{$branch}; + $branches->{ $map->{$branch} // $branch } = 1; + } + } +} From 5d1b163eddb718d5a3bfc5ff95fa00543c7ba4d4 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 3 Jul 2026 10:34:08 +0200 Subject: [PATCH 2/2] fix: build only when conf.yaml has a legacy branch match (per review by @Mpdreamz) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main/master are hardcoded to always skip since they no longer carry legacy AsciiDoc docs. For every other branch, flip from fail-open (build unless proven migrated) to positive-match (build only if conf.yaml lists the branch as legacy) — an empty or missing conf.yaml entry now means skip. Genuine parse failures (e.g. missing YAML module) still fail open and build, since we can't tell in that case. legacy_branches.pl resolves a GitHub repo name to its conf.yaml key by trying a direct key match before falling back to URL-basename translation, since both forms occur (e.g. "esf" is used directly, while "elastic-serverless-forwarder" only matches via its source URL). The basename-only version silently returned no legacy branches for "esf", "kibana-cn", and "swiftype", which would have always skipped their builds. Added legacy_branches.t as a regression check. --- .buildkite/scripts/build_pr.sh | 21 ++++++++++++------ .buildkite/scripts/legacy_branches.pl | 31 ++++++++++++++++----------- .buildkite/scripts/legacy_branches.t | 31 +++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 19 deletions(-) create mode 100755 .buildkite/scripts/legacy_branches.t diff --git a/.buildkite/scripts/build_pr.sh b/.buildkite/scripts/build_pr.sh index 9fc51e6b5e2ac..50781309a672d 100755 --- a/.buildkite/scripts/build_pr.sh +++ b/.buildkite/scripts/build_pr.sh @@ -41,15 +41,22 @@ buildkite-agent \ if [[ "${GITHUB_PR_BASE_REPO}" != 'docs' ]]; then - # Skip the build but report success when the PR targets a branch that no longer carries - # legacy AsciiDoc docs (per conf.yaml). docs-build-pr is a required check, so we exit 0 - # to keep it green rather than failing or leaving it pending. - # The helper exits non-zero on a parse error or unknown repo, which short-circuits the - # condition below so we build as today — i.e. fail open, never block a real build. + # main/master are hardcoded to always skip the build, without needing conf.yaml/YAML at all: + # we no longer build legacy AsciiDoc docs against these branches. + if [[ "${GITHUB_PR_TARGET_BRANCH}" == "main" || "${GITHUB_PR_TARGET_BRANCH}" == "master" ]]; then + echo "Target branch '${GITHUB_PR_TARGET_BRANCH}' is main/master — skipping build (reporting success)." + exit 0 + fi + + # Build only if conf.yaml lists the target branch as a legacy AsciiDoc branch + # for this repo — otherwise skip but report success, since docs-build-pr is a + # required check and we want it green rather than failing or left pending. + # The helper exits non-zero only when conf.yaml itself couldn't be read (e.g. + # the perl YAML module is missing), which short-circuits the condition below + # so we fail open and build as today, rather than silently going green. if legacy_branches=$(perl "$(dirname "$0")/legacy_branches.pl" "$GITHUB_PR_BASE_REPO") \ - && [[ -n "${legacy_branches}" ]] \ && ! grep -qxF "${GITHUB_PR_TARGET_BRANCH}" <<< "${legacy_branches}"; then - echo "Target branch '${GITHUB_PR_TARGET_BRANCH}' has no legacy AsciiDoc docs in conf.yaml for ${GITHUB_PR_BASE_REPO} — skipping build (reporting success)." + echo "Target branch '${GITHUB_PR_TARGET_BRANCH}' is not a legacy AsciiDoc branch in conf.yaml for ${GITHUB_PR_BASE_REPO} — skipping build (reporting success)." exit 0 fi diff --git a/.buildkite/scripts/legacy_branches.pl b/.buildkite/scripts/legacy_branches.pl index 6b5a000759e3b..e89723462f223 100755 --- a/.buildkite/scripts/legacy_branches.pl +++ b/.buildkite/scripts/legacy_branches.pl @@ -6,9 +6,11 @@ # Usage: legacy_branches.pl # Prints one legacy (AsciiDoc) branch per line that the given repo still carries in conf.yaml. # -# Exit 0: repo found; list may be empty if fully migrated to docs-builder. -# Exit 2: repo not found in conf.yaml — caller should build as today (fail open). -# Exit 1: YAML parse / load error — caller should build as today (fail open). +# Exit 0: conf.yaml was read successfully. The list is empty if the repo is +# fully migrated to docs-builder or isn't in conf.yaml at all — either +# way it has no legacy branches, so the caller should skip the build. +# Exit 1: conf.yaml couldn't be loaded (e.g. missing YAML module, parse error) +# — caller should build as today (fail open), since we can't tell. my ($github_repo) = @ARGV or die "Usage: $0 \n"; @@ -21,18 +23,23 @@ exit 1; } -# Build a map: GitHub repo name (URL basename minus .git) -> conf repo key. -# Sources in conf.yaml reference the conf key (e.g. "esf"), not the GitHub name -# (e.g. "elastic-serverless-forwarder"), so we need this translation. -my %github_to_key; -while ( my ( $key, $url ) = each %{ $conf->{repos} } ) { - ( my $name = $url ) =~ s{.*/|\.git$}{}g; # URL -> repo name (strip path and .git) - $github_to_key{$name} = $key; +# Sources in conf.yaml reference the conf key (e.g. "esf"), which for most repos +# is already the GitHub repo name but for some (e.g. "elastic-serverless-forwarder") +# differs from it. Try the GitHub name as a conf key directly first, then fall back +# to matching it against each repo's URL basename. +my $conf_key = exists $conf->{repos}{$github_repo} ? $github_repo : undef; +unless ( defined $conf_key ) { + while ( my ( $key, $url ) = each %{ $conf->{repos} } ) { + ( my $name = $url ) =~ s{.*/|\.git$}{}g; # URL -> repo name (strip path and .git) + if ( $name eq $github_repo ) { + $conf_key = $key; + last; + } + } } -my $conf_key = $github_to_key{$github_repo}; unless ( defined $conf_key ) { - exit 2; # repo not in conf — caller builds as today + exit 0; # repo not in conf — no legacy branches, caller should skip } # Walk conf.yaml contents and collect every git branch for which this repo still diff --git a/.buildkite/scripts/legacy_branches.t b/.buildkite/scripts/legacy_branches.t new file mode 100755 index 0000000000000..8905f5faa7742 --- /dev/null +++ b/.buildkite/scripts/legacy_branches.t @@ -0,0 +1,31 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use Test::More; +use FindBin qw($RealBin); + +# Regression check for legacy_branches.pl's repo-name resolution: it must +# handle both a GitHub repo name that already matches a conf.yaml key +# directly (e.g. "esf" itself) and one that only matches via a source repo's +# URL basename (e.g. "elastic-serverless-forwarder" -> "esf"). Run from the +# repo root since legacy_branches.pl reads ./conf.yaml relative to cwd. + +chdir "$RealBin/../.." or die "Can't chdir to repo root: $!"; + +sub run { + my ($repo) = @_; + my $out = `perl .buildkite/scripts/legacy_branches.pl @{[quotemeta $repo]} 2>/dev/null`; + return ( $? >> 8, $out ); +} + +for my $repo (qw(kibana-cn swiftype esf elastic-serverless-forwarder)) { + my ( $exit, $out ) = run($repo); + is( $exit, 0, "$repo: exits 0" ); + isnt( $out, '', "$repo: has at least one legacy branch (conf key resolved)" ); +} + +my ( $exit, $out ) = run('some-repo-not-in-conf-yaml'); +is( $exit, 0, 'unknown repo: exits 0' ); +is( $out, '', 'unknown repo: no legacy branches' ); + +done_testing();