Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/changelog-bundle-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,18 @@ concurrency:
cancel-in-progress: false

jobs:
generate:
fetch:
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
outputs:
output: ${{ steps.bundle.outputs.output }}
steps:
# Resolve the plan and download the already generated and scrubbed bundle from the public CDN
# (it must have been uploaded first by the changelog-bundle workflow). No regeneration, no S3 auth.
- id: bundle
uses: elastic/docs-actions/changelog/bundle-create@v1
uses: elastic/docs-actions/changelog/bundle-fetch@v1
with:
config: ${{ inputs.config }}
profile: ${{ inputs.profile }}
Expand All @@ -84,14 +86,14 @@ jobs:
github-token: ${{ github.token }}

create-pr:
needs: generate
needs: fetch
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: elastic/docs-actions/changelog/bundle-pr@v1
with:
output: ${{ needs.generate.outputs.output }}
output: ${{ needs.fetch.outputs.output }}
base-branch: ${{ inputs.base-branch }}
github-token: ${{ github.token }}
60 changes: 50 additions & 10 deletions changelog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,14 +262,48 @@ The entry is always regenerated from the *current* PR state at merge time, so an

## Bundling changelogs

Individual changelog files accumulate on the default branch as PRs merge. The bundle action generates a fully-resolved YAML file containing only the changelog entries that match a given filter, then uploads it to the `elastic-docs-v3-changelog-bundles` S3 bucket.
As PRs merge, individual changelog entries pile up on your default branch (and, once uploaded, in S3). A **bundle** is a single, fully-resolved YAML file that collects the entries matching a filter — a release version, a promotion report, or an explicit list of PRs — with each entry's full content inlined. Downstream tooling renders a release changelog from the bundle alone, without needing the original entry files.

Two reusable workflows are available:
Setting up bundling comes down to two choices: **how the bundle is delivered** and **how its entries are selected**. Pick one row from each table below, then follow the matching recipe.

- **`changelog-bundle.yml`** (primary) — generates a bundle and uploads it to S3. Used for release-triggered workflows.
- **`changelog-bundle-pr.yml`** (opt-in) — generates a bundle and opens a pull request. Used for teams that need a committed bundle before a tag exists.
### 1. Choose how the bundle is delivered

The bundle always includes the full content of each matching entry, so downstream consumers can render changelogs without access to the original files.
| 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}`) |
| 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 |

The PR workflow is **fetch-only**: it downloads the already-uploaded, scrubbed bundle instead of regenerating it, so the committed file is exactly what was published (private references removed). It therefore requires `changelog-bundle.yml` to have uploaded the bundle first.

### 2. Choose how entries are selected

Pick exactly one — `profile`, `release-version`, `report`, and `prs` are mutually exclusive.

| Situation | Mode | Key inputs |
| --- | --- | --- |
| You accumulate entry files and tag releases (most teams) | **Profile** _(recommended)_ | `profile` + `version` |
| You build changelogs from a GitHub release's notes rather than entry files | **gh-release** | `mode: gh-release` + `repo` + `version` |
| You want everything in a given release tag | **Option** | `release-version` (+ `output`) |
| You want everything in a Buildkite promotion report | **Option** | `report` (+ `output`) |
| You want a specific set of PRs | **Option** | `prs` (+ `output`) |

Each mode has a complete, copy-pasteable workflow file under [Setup](#setup-1).

### Where bundle entries come from

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.

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.

```yaml
bundle:
directory: docs/changelog
output_directory: docs/releases
use_local_changelogs: true # opt out of CDN sourcing; use local entry files
```

### Prerequisites

Expand All @@ -287,6 +321,8 @@ Your repository must also be listed in the `elastic-docs-v3-changelog-bundles` i

### Setup

Each recipe below is a complete `changelog-bundle.yml` caller for one selection mode (S3-upload delivery). To also commit the bundle to your repo, add the [Bundle PR workflow](#bundle-pr-workflow-opt-in) afterwards.

#### Profile-based bundling with S3 upload (`on: release`)

The recommended setup for stack and product releases. The caller triggers on `release`, passes a profile and version, and the bundle is uploaded to S3 automatically.
Expand Down Expand Up @@ -435,7 +471,9 @@ The primary workflow (`changelog-bundle.yml`) uploads the bundle to the `elastic

### Bundle PR workflow (opt-in)

For teams that need a committed bundle file before a tag exists (e.g. feature-freeze branches), use the PR workflow instead. This generates the bundle and opens a pull request.
For teams that need the bundle committed into the repository, use the PR workflow. Unlike the primary workflow, it does **not** generate the bundle: it downloads the already-uploaded, scrubbed copy from the public CDN and opens a pull request with it. This guarantees the committed file matches what was published to S3 (private references removed), rather than a freshly-regenerated local copy.

Because it is fetch-only, `changelog-bundle.yml` must have run and uploaded the bundle first. The fetch step polls the CDN with exponential backoff (up to ~10 minutes) to absorb scrubbing and CloudFront propagation latency, failing the job if the bundle never appears.

**`.github/workflows/changelog-bundle-pr.yml`**

Expand All @@ -452,17 +490,19 @@ on:
permissions: {}

jobs:
bundle:
bundle-pr:
permissions:
contents: write # commit the bundle file and push the branch
pull-requests: write # open or update the bundle PR
packages: read # pull the docs-builder image from GHCR
packages: read # pull the docs-builder image from GHCR (plan step)
uses: elastic/docs-actions/.github/workflows/changelog-bundle-pr.yml@v1
with:
profile: my-release
version: ${{ inputs.version }}
```

The PR workflow opens a pull request on a branch named `changelog-bundle/<bundle-name>` (e.g. `changelog-bundle/v9.2.0`). If a PR already exists for that branch, the bundle is updated in place. If the generated bundle is identical to what's already in the repository, no commit or PR is created.
The PR workflow opens a pull request on a branch named `changelog-bundle/<bundle-name>` (e.g. `changelog-bundle/v9.2.0`). If a PR already exists for that branch, the bundle is updated in place. If the fetched bundle is identical to what's already in the repository, no commit or PR is created.

> **Note:** Locating the bundle on the CDN requires a resolvable product, so the PR workflow only supports profile (or product-scoped option) mode — the same constraint as CDN entry sourcing above. PR/issue-only filters that resolve no product cannot be fetched from the CDN.

> **Note:** The PR workflow does not upload to S3. If you need both S3 upload and a PR, run both workflows or use the composite actions (`bundle-create`, `bundle-upload`, `bundle-pr`) directly.
> **Note:** The PR workflow does not upload to S3; it consumes what `changelog-bundle.yml` already uploaded. If you need both S3 upload and a PR, run the primary workflow first, then this one. To compose the steps yourself, use the composite actions (`bundle-fetch` then `bundle-pr`) directly.
8 changes: 7 additions & 1 deletion changelog/bundle-create/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ Checks out the repository, runs docs-builder in Docker to generate a fully-resol

## Modes

- **`bundle`** (default) — runs `docs-builder changelog bundle` with profile or option-based filtering. Requires changelogs to already exist in the repository.
- **`bundle`** (default) — runs `docs-builder changelog bundle` with profile or option-based filtering.
- **`gh-release`** — runs `docs-builder changelog gh-release` to create changelogs directly from a GitHub release's notes. Requires `repo` and optionally `version` (defaults to `latest`).

## Entry sourcing

In `bundle` mode, the individual changelog entries are sourced from the **public CDN** by default (scoped to the bundle's product), so the local entry files don't need to be checked out. The Docker run enables network access only when the `--plan` step reports `needs_network: true`; otherwise it runs with `--network none`.

CDN sourcing requires a resolvable product. When none can be resolved (e.g. an option-mode PR/issue-only filter) the command falls back to the local `bundle.directory`, which must then contain the entries. To force local sourcing, set `use_local_changelogs: true` in `changelog.yml` or pass an explicit `output` path.

## Inputs

| Name | Description | Required | Default |
Expand Down
51 changes: 51 additions & 0 deletions changelog/bundle-fetch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!-- Generated by https://github.com/reakaleek/gh-action-readme -->
# <!--name-->Changelog bundle fetch<!--/name-->
<!--description-->
Resolves the bundle plan with docs-builder, then downloads the already generated and scrubbed bundle from the public CDN (rather than regenerating it locally) and uploads it as an artifact for the bundle-pr action. Polls the CDN with exponential backoff to absorb scrubbing/CloudFront propagation latency. The bundle must already have been uploaded by the changelog-bundle workflow; this action never contacts the private S3 bucket.
<!--/description-->

This action is the read-only first half of the `changelog-bundle-pr` workflow: it fetches the
sanitized bundle that the `changelog-bundle` workflow previously uploaded to S3 (and the scrubber
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}`),
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.

## Inputs
<!--inputs-->
| Name | Description | Required | Default |
|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|-----------------------|
| `config` | Path to changelog.yml configuration file | `false` | `docs/changelog.yml` |
| `profile` | Bundle profile name from bundle.profiles in changelog.yml. Mutually exclusive with release-version and prs.<br> | `false` | ` ` |
| `version` | Version string for profile mode (e.g. 9.2.0, 2026-03). Used for {version} substitution in profile patterns.<br> | `false` | ` ` |
| `release-version` | GitHub release tag used as the PR filter source (e.g. v9.2.0). Mutually exclusive with profile, report, and prs.<br> | `false` | ` ` |
| `report` | Buildkite promotion report URL or local file path used as the PR filter source. Local paths must be relative to the repo root.<br> | `false` | ` ` |
| `prs` | Comma-separated PR URLs or numbers, or a path to a newline-delimited file. Mutually exclusive with profile, release-version, and report.<br> | `false` | ` ` |
| `output` | Output file path for the bundle, relative to the repo root. Optional; when omitted the path is resolved from the config (bundle.output_directory) by the plan.<br> | `false` | ` ` |
| `repo` | GitHub repository name. Falls back to bundle.repo in changelog.yml. | `false` | ` ` |
| `owner` | GitHub repository owner. Falls back to bundle.owner in changelog.yml, then elastic. | `false` | ` ` |
| `docs-builder-version` | docs-builder version to use (e.g. 0.1.100, latest, edge). Non-edge versions are attestation-verified by the setup action.<br> | `false` | `edge` |
| `artifact-name` | Name for the uploaded artifact (must match bundle-pr artifact-name) | `false` | `changelog-bundle` |
| `cdn-base-url` | Override the public changelog CDN base URL (for staging/testing). When set, it is passed to docs-builder via DOCS_BUILDER_CHANGELOG_CDN so the resolved cdn_url and the poll target stay in sync. Must be an absolute HTTPS URL.<br> | `false` | ` ` |
| `github-token` | GitHub token (used by docs-builder setup). | `false` | `${{ github.token }}` |
<!--/inputs-->

## Outputs
<!--outputs-->
| Name | Description |
|----------|----------------------------------------------------------|
| `output` | Resolved output file path for the bundle (repo-relative) |
<!--/outputs-->

## Usage
<!--usage action="elastic/docs-actions/changelog/bundle-fetch" version="v1"-->
```yaml
steps:
- uses: elastic/docs-actions/changelog/bundle-fetch@v1
with:
profile: elasticsearch-release
version: 9.2.0
```
<!--/usage-->
Loading
Loading