From 205a3b6979c1e3dae8514183d89807bcd344a8b7 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 24 Jun 2026 13:28:59 -0300 Subject: [PATCH 1/3] changelog: wire --repo/--owner and document artifact-root S3 layout Pass --repo/--owner to `docs-builder changelog upload` so changelog entries are keyed by the authoring repo (changelog/{repo}/...), and update the changelog READMEs for the new key contracts (bundle/{product}/..., changelog/{repo}/...) and the {repo}-{dateOrVersion}.yaml bundle filename convention. Refs elastic/docs-eng-team#413 Co-authored-by: Cursor --- changelog/README.md | 10 ++++++---- changelog/bundle-fetch/README.md | 4 +++- changelog/bundle-upload/README.md | 4 +++- changelog/upload/action.yml | 6 +++++- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/changelog/README.md b/changelog/README.md index c88d08f..6823045 100644 --- a/changelog/README.md +++ b/changelog/README.md @@ -189,7 +189,7 @@ Each PR produces a file at `docs/changelog/{filename}.yaml` on the PR branch (wh ## Uploading to S3 -Changelog files on the default branch can be uploaded to S3. Files land in a **private bucket** (`elastic-docs-v3-changelog-bundles-private`), which is the internal source of truth. A scrubber Lambda automatically mirrors sanitized copies (with private repository references removed) to the **public bucket** served via CloudFront CDN. Changelogs are uploaded under `{product}/changelog/{filename}.yaml`. +Changelog files on the default branch can be uploaded to S3. Files land in a **private bucket** (`elastic-docs-v3-changelog-bundles-private`), which is the internal source of truth. A scrubber Lambda automatically mirrors sanitized copies (with private repository references removed) to the **public bucket** served via CloudFront CDN. Changelog entries are uploaded once per authoring repository under `changelog/{repo}/{filename}.yaml` (the repo is resolved from `--repo`, `bundle.repo` in `changelog.yml`, or the git remote origin). ### 1. Add the upload workflow @@ -270,7 +270,7 @@ Setting up bundling comes down to two choices: **how the bundle is delivered** a | Your goal | Workflow | What you get | | --- | --- | --- | -| Make the bundle available to docs rendering | [`changelog-bundle.yml`](#setup-1) | Uploads the bundle to S3 (`{product}/bundle/{file}`) | +| Make the bundle available to docs rendering | [`changelog-bundle.yml`](#setup-1) | Uploads the bundle to S3 (`bundle/{product}/{file}`) | | Commit the bundle into your repository | [`changelog-bundle-pr.yml`](#bundle-pr-workflow-opt-in) | Opens a PR with the scrubbed bundle fetched from the CDN | | Both | run `changelog-bundle.yml`, then `changelog-bundle-pr.yml` | Upload first, then open the PR | @@ -294,7 +294,7 @@ Each mode has a complete, copy-pasteable workflow file under [Setup](#setup-1). By default, the bundle command sources the individual changelog entries from the **public CDN**, scoped to the bundle's product(s), rather than from the local `bundle.directory`. This means a bundle reflects the same sanitized entries that have been published to S3, and a repository can produce a bundle without keeping every entry file checked out locally. -CDN sourcing requires a resolvable product (from a profile's `products`/`output_products`, or `--input-products`) so the per-product registry (`{product}/changelog/registry.json`) can be located. When no product can be resolved (e.g. an option-mode PR/issue-only filter), the command automatically falls back to local sourcing. +CDN sourcing locates each authoring repo's entry registry (`changelog/{repo}/registry.json`), resolved from `--repo`, `bundle.repo` in `changelog.yml`, or the git remote origin, to discover that repo's published entries. When the source repo can't be resolved (e.g. an option-mode PR/issue-only filter), the command automatically falls back to local sourcing. To always source entries from the local `bundle.directory` instead, set `use_local_changelogs: true` in the `bundle` section of your `docs/changelog.yml`. Passing an explicit `--directory`/`output` also forces local sourcing. @@ -467,7 +467,9 @@ If your changelog configuration is not at `docs/changelog.yml`, pass the path ex ### Output -The primary workflow (`changelog-bundle.yml`) uploads the bundle to the `elastic-docs-v3-changelog-bundles` S3 bucket under `{product}/bundle/{filename}`. The bundle is available to downstream rendering workflows immediately after upload. +The primary workflow (`changelog-bundle.yml`) uploads the bundle to the `elastic-docs-v3-changelog-bundles` S3 bucket under `bundle/{product}/{filename}`. The bundle is available to downstream rendering workflows immediately after upload. + +> **Note:** Bundles are keyed by product, so a shared product (e.g. `cloud-serverless`) bundled by more than one repository shares the `bundle/{product}/` prefix. To avoid one repo's bundle overwriting another's, give each bundle a repo-qualified filename such as `{repo}-{dateOrVersion}.yaml` (e.g. `my-repo-2026-03.yaml`). ### Bundle PR workflow (opt-in) diff --git a/changelog/bundle-fetch/README.md b/changelog/bundle-fetch/README.md index e2d9330..0e6950d 100644 --- a/changelog/bundle-fetch/README.md +++ b/changelog/bundle-fetch/README.md @@ -9,10 +9,12 @@ sanitized bundle that the `changelog-bundle` workflow previously uploaded to S3 Lambda mirrored to the public CDN), so the resulting PR carries the scrubbed copy rather than a raw, locally-generated one. Pair it with [`bundle-pr`](../bundle-pr) which opens the pull request. -Fetching from the CDN requires a resolvable product to scope the URL (`{base}/{product}/bundle/{file}`), +Fetching from the CDN requires a resolvable product to scope the URL (`{base}/bundle/{product}/{file}`), so use a profile with `products`/`output_products`. PR/issue-only option-mode bundles cannot be located on the CDN — use the [`changelog-bundle`](../README.md) workflow for those. +> **Note:** Bundles are keyed by product, so a shared product (e.g. `cloud-serverless`) is published by more than one repository under the same `bundle/{product}/` prefix. To avoid collisions, bundles should use a repo-qualified filename such as `{repo}-{dateOrVersion}.yaml` (e.g. `my-repo-2026-03.yaml`); fetch the specific file that matches the bundle you want. + ## Inputs | Name | Description | Required | Default | diff --git a/changelog/bundle-upload/README.md b/changelog/bundle-upload/README.md index ce0bee8..6444b6f 100644 --- a/changelog/bundle-upload/README.md +++ b/changelog/bundle-upload/README.md @@ -26,4 +26,6 @@ steps: output: docs/releases/v9.2.0.yaml ``` -This action is typically used as the second job in the `changelog-bundle.yml` reusable workflow, after `bundle-create` generates the artifact. The S3 key for each bundle is `{product}/bundle/{filename}`, where the product is read from the bundle's YAML `products` array. +This action is typically used as the second job in the `changelog-bundle.yml` reusable workflow, after `bundle-create` generates the artifact. The S3 key for each bundle is `bundle/{product}/{filename}`, where the product is read from the bundle's YAML `products` array. + +> **Note:** Bundles are keyed by product, so a shared product (e.g. `cloud-serverless`) published by more than one repository shares the `bundle/{product}/` prefix. To avoid collisions, give each bundle a repo-qualified filename such as `{repo}-{dateOrVersion}.yaml` (e.g. `my-repo-2026-03.yaml`). diff --git a/changelog/upload/action.yml b/changelog/upload/action.yml index b212a01..59d9957 100644 --- a/changelog/upload/action.yml +++ b/changelog/upload/action.yml @@ -131,9 +131,13 @@ runs: shell: bash env: CONFIG: ${{ inputs.config }} + REPO_NAME: ${{ github.event.repository.name }} + OWNER: ${{ github.repository_owner }} run: | docs-builder changelog upload \ --artifact-type changelog \ --target s3 \ --s3-bucket-name elastic-docs-v3-changelog-bundles-private \ - --config "$CONFIG" + --config "$CONFIG" \ + --repo "$REPO_NAME" \ + --owner "$OWNER" From c55a6e9baf03258f4194e73a5998c0a52a3a649f Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Tue, 30 Jun 2026 11:14:15 -0300 Subject: [PATCH 2/3] changelog: pass --branch and allow non-default-branch uploads Adds a `branch` input (defaults to github.ref_name) and forwards it to `docs-builder changelog upload` as `--branch`, so entries are uploaded under the per-branch pool (changelog/{org}/{repo}/{branch}/...). Relaxes the upload gate from a hard main/master check to any branch push (rejecting tags and detached refs) and validates the branch input. --- changelog/upload/action.yml | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/changelog/upload/action.yml b/changelog/upload/action.yml index 59d9957..a8ef8be 100644 --- a/changelog/upload/action.yml +++ b/changelog/upload/action.yml @@ -6,14 +6,18 @@ description: > each merged fork PR, regenerates the changelog entry from the live PR record using `docs-builder changelog add --prs `, so fork PRs (which can't commit to their own branch) still produce an entry on merge. A scrubber Lambda - mirrors sanitized copies to the public CDN bucket. Intended to run on push - to the default branch (main/master). Only files whose content has changed + mirrors sanitized copies to the public CDN bucket. Entries are scoped per + branch (changelog/{org}/{repo}/{branch}/...), so this can run on a push to any + branch (main/master or a release branch). Only files whose content has changed are transferred. inputs: config: description: 'Path to changelog.yml configuration file (repo-relative, no ".." or absolute paths)' default: 'docs/changelog.yml' + branch: + description: 'Branch the changelog entries are published under (changelog/{org}/{repo}/{branch}/...). Defaults to the pushed branch.' + default: '${{ github.ref_name }}' github-token: description: 'GitHub token (used by docs-builder setup and the GitHub API lookups). Use the default GITHUB_TOKEN; do not substitute a broader PAT.' default: '${{ github.token }}' @@ -24,20 +28,21 @@ inputs: runs: using: composite steps: - - name: Verify push to default branch + - name: Verify branch push shell: bash env: REF: ${{ github.ref }} run: | - if [[ "$REF" != "refs/heads/main" && "$REF" != "refs/heads/master" ]]; then - echo "::error::changelog upload must run on a push to main or master (got $REF)" + if [[ "$REF" != refs/heads/* ]]; then + echo "::error::changelog upload must run on a branch push (got $REF)" exit 1 fi - - name: Validate config path + - name: Validate inputs shell: bash env: CONFIG: ${{ inputs.config }} + BRANCH: ${{ inputs.branch }} run: | if [[ "$CONFIG" == /* ]]; then echo "::error::config path must be relative, not absolute: ${CONFIG}" @@ -51,6 +56,14 @@ runs: echo "::error::config path must not contain newlines" exit 1 fi + if [[ -z "$BRANCH" ]]; then + echo "::error::branch must not be empty" + exit 1 + fi + if [[ "$BRANCH" == *$'\n'* || "$BRANCH" == *$'\r'* ]]; then + echo "::error::branch must not contain newlines" + exit 1 + fi - name: Checkout uses: actions/checkout@v6 @@ -133,6 +146,7 @@ runs: CONFIG: ${{ inputs.config }} REPO_NAME: ${{ github.event.repository.name }} OWNER: ${{ github.repository_owner }} + BRANCH: ${{ inputs.branch }} run: | docs-builder changelog upload \ --artifact-type changelog \ @@ -140,4 +154,5 @@ runs: --s3-bucket-name elastic-docs-v3-changelog-bundles-private \ --config "$CONFIG" \ --repo "$REPO_NAME" \ - --owner "$OWNER" + --owner "$OWNER" \ + --branch "$BRANCH" From 94b2aef09bf7b794ab2cda5cc2f01ca7d7dd3049 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Tue, 30 Jun 2026 11:18:57 -0300 Subject: [PATCH 3/3] changelog: update README for {org}/{repo}/{branch} entry pool Reflects the org/branch nesting in the upload + CDN-sourcing docs: changelog/{org}/{repo}/{branch}/{file} and registry.json, branch resolution, and that uploads can now run from any branch (not just the default). --- changelog/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog/README.md b/changelog/README.md index 6823045..d2a13a8 100644 --- a/changelog/README.md +++ b/changelog/README.md @@ -189,7 +189,7 @@ Each PR produces a file at `docs/changelog/{filename}.yaml` on the PR branch (wh ## Uploading to S3 -Changelog files on the default branch can be uploaded to S3. Files land in a **private bucket** (`elastic-docs-v3-changelog-bundles-private`), which is the internal source of truth. A scrubber Lambda automatically mirrors sanitized copies (with private repository references removed) to the **public bucket** served via CloudFront CDN. Changelog entries are uploaded once per authoring repository under `changelog/{repo}/{filename}.yaml` (the repo is resolved from `--repo`, `bundle.repo` in `changelog.yml`, or the git remote origin). +Changelog files can be uploaded to S3 from a push to any branch. Files land in a **private bucket** (`elastic-docs-v3-changelog-bundles-private`), which is the internal source of truth. A scrubber Lambda automatically mirrors sanitized copies (with private repository references removed) to the **public bucket** served via CloudFront CDN. Changelog entries are uploaded once per authoring org/repo/branch under `changelog/{org}/{repo}/{branch}/{filename}.yaml` (the owner and repo are resolved from `--owner`/`--repo`, `bundle.owner`/`bundle.repo` in `changelog.yml`, or the git remote origin; the branch from `--branch`, defaulting to the pushed branch). The branch is stored verbatim, so a branch name with `/` (e.g. `feature/foo`) becomes additional key segments. ### 1. Add the upload workflow @@ -294,7 +294,7 @@ Each mode has a complete, copy-pasteable workflow file under [Setup](#setup-1). By default, the bundle command sources the individual changelog entries from the **public CDN**, scoped to the bundle's product(s), rather than from the local `bundle.directory`. This means a bundle reflects the same sanitized entries that have been published to S3, and a repository can produce a bundle without keeping every entry file checked out locally. -CDN sourcing locates each authoring repo's entry registry (`changelog/{repo}/registry.json`), resolved from `--repo`, `bundle.repo` in `changelog.yml`, or the git remote origin, to discover that repo's published entries. When the source repo can't be resolved (e.g. an option-mode PR/issue-only filter), the command automatically falls back to local sourcing. +CDN sourcing locates each authoring pool's entry registry (`changelog/{org}/{repo}/{branch}/registry.json`) — repo from `--repo`/`bundle.repo`/git remote, org from `--owner`/`bundle.owner` (default `elastic`), branch from `--branch`/`bundle.branch` (default `main`) — to discover that pool's published entries. When the source repo can't be resolved (e.g. an option-mode PR/issue-only filter), the command automatically falls back to local sourcing. To always source entries from the local `bundle.directory` instead, set `use_local_changelogs: true` in the `bundle` section of your `docs/changelog.yml`. Passing an explicit `--directory`/`output` also forces local sourcing.