Conversation
changelog/bundle/action.yml
Outdated
| args+=(--owner "$OWNER") | ||
| fi | ||
|
|
||
| docs-builder changelog bundle "${args[@]}" |
There was a problem hiding this comment.
We should run this in docker similar to
I think we can also add --network none in this case.
There was a problem hiding this comment.
Done in 4de3e84. We get the file via curl and send it to docs-builder inside docker with --network none. It goes to code that only tries to regex parse it.
changelog/bundle/action.yml
Outdated
| git config user.name "github-actions[bot]" | ||
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | ||
| git add "$OUTPUT" | ||
|
|
||
| if git diff --cached --quiet; then | ||
| echo "No changes to commit" | ||
| echo "committed=false" >> "$GITHUB_OUTPUT" | ||
| else | ||
| git commit -m "Update changelog bundle" | ||
| git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GIT_REPOSITORY}.git" | ||
| git push | ||
| git remote set-url origin "https://github.com/${GIT_REPOSITORY}.git" | ||
| echo "committed=true" >> "$GITHUB_OUTPUT" | ||
| fi |
There was a problem hiding this comment.
is this needed if you are already doing elastic/docs-actions/docs-builder/setup@v1?
Also, if this is supposed to run on the main branch, we must create a PR instead of committing directly.
There was a problem hiding this comment.
Oof, yeah. 4de3e84 Puts a get artifact -> submit PR action after getting the artifact from the bundle gen action.
changelog/bundle/action.yml
Outdated
| value: ${{ steps.commit.outputs.committed || 'false' }} | ||
|
|
||
| runs: | ||
| using: composite |
There was a problem hiding this comment.
We should split this whole thing into two jobs.
one that creates the bundle file and uploads the artifact and has only contents: write permissions
and one that downloads the artifact and has permissions to create a PR.
There was a problem hiding this comment.
Done in 4de3e84. We have bundle-create for the first step (contents: read) and bundle-pr for the rest (contents/pull-request: write). contents: write would be needed because the PR branch needs to be created there. Unless there's a different way?
| @@ -0,0 +1,45 @@ | |||
| name: Changelog bundle | |||
There was a problem hiding this comment.
is a reusable workflow necessary if it has only one step?
it would be necessary, if we split it into 2 jobs (https://github.com/elastic/docs-actions/pull/42/changes#r2997494919)
There was a problem hiding this comment.
Makes sense. I've split them with their specific permissions now.
changelog/README.md
Outdated
| directory: docs/changelog | ||
| repo: my-repo | ||
| owner: elastic |
There was a problem hiding this comment.
All of these are optional right?
I don't think we should document hardcoding the repo and owner, for most this would be equal to the one holding changelog.yml ?
There was a problem hiding this comment.
Yeah, that was by convention but they shouldn't be exposed willy-nilly. Removed
|
|
||
| on: | ||
| schedule: | ||
| - cron: '0 8 * * 1-5' |
There was a problem hiding this comment.
| - cron: '0 8 * * 1-5' | |
| # At 08:00 AM, Monday through Friday | |
| - cron: '0 8 * * 1-5' |
I can never read cron syntax :D
Mpdreamz
left a comment
There was a problem hiding this comment.
Security & logic review
Compared against the patterns established in changelog-validate.yml, changelog-submit.yml, changelog/submit/action.yml, and docs-deploy.yml.
Seven issues found — two are high severity and block merge; the rest are medium/low or best-practice gaps.
🔴 High
1. Missing top-level permissions: {} on the reusable workflow — .github/workflows/changelog-bundle.yml
Both sibling workflows (changelog-validate.yml:11, changelog-submit.yml:15) open with permissions: {} to deny all permissions at workflow scope, then grant only what is needed per job. This file has no top-level permissions block, so it inherits the repository default token permissions (typically includes contents: write, packages: read, etc.). The generate job silently runs with write access it does not need.
Fix: add permissions: {} between the on: block and jobs:.
2. Missing concurrency block — .github/workflows/changelog-bundle.yml
Both changelog-validate.yml (line 13) and changelog-submit.yml (lines 17–19) define concurrency groups. Without one here, two simultaneous calls for the same output path will race: both generate jobs upload an artifact named changelog-bundle (last write wins), and both create-pr jobs will race on the same branch with a force-push.
Fix: add something like:
concurrency:
group: changelog-bundle-${{ inputs.output }}-${{ github.run_id }}
cancel-in-progress: false🟡 Medium
3. Force-push without branch-name validation — changelog/bundle-pr/action.yml lines 39–58
BUNDLE_NAME=$(basename "$OUTPUT" .yaml) is then used directly as a branch name component and in a git push -f. basename strips the directory prefix but does not validate characters. Compare changelog/submit/action.yml lines 66–77, which has an explicit ref-name guard: ^[a-zA-Z0-9._/+-]+$.
The force-push (-f) is also destructive and inconsistent with the pattern in changelog/submit/action.yml which uses a plain git push. If a team member has pushed additional commits to the bundle branch, a force-push will silently discard them.
Fix: validate $BUNDLE_NAME against ^[a-zA-Z0-9._+-]+$ before constructing $BRANCH; replace -f with a regular push (or justify explicitly in a comment).
4. Unvalidated $OUTPUT used as cp destination — changelog/bundle-pr/action.yml lines 42–43
mkdir -p "$(dirname "$OUTPUT")"
cp ".bundle-artifact/$(basename "$OUTPUT")" "$OUTPUT"$OUTPUT is a caller-controlled string used directly as the copy destination. The action description says "Must match the path used by bundle-create" but there is no enforcement. A value containing ../ sequences or an absolute path outside the workspace would be silently honoured.
Fix: validate that $OUTPUT is a relative path with no .. components and that it ends in .yml or .yaml (as the docs promise but the code does not check).
5. Unvalidated URL passed to curl — changelog/bundle-create/action.yml lines 78–80
curl -fsSL "$REPORT" -o .bundle-report.html$REPORT is a caller-controlled URL fetched on the runner before --network none is applied to the Docker container. A misconfigured (or malicious) caller could pass an internal endpoint (e.g. the EC2 instance metadata service at http://169.254.169.254/...) and the runner would fetch it. The downloaded content then becomes input to the Docker container.
The attack surface is limited to workflow_call authors in the same org, but the risk should be documented or mitigated (e.g. allowlist known Buildkite URL prefixes).
🟠 Logic
6. No --base branch on gh pr create — changelog/bundle-pr/action.yml line 65–68
The PR is created without --base, so it targets the repo default branch at call time. If the workflow is ever used to produce bundles for a non-default release branch, this will silently target the wrong base. Making it explicit is a one-word fix and matches the defensive posture of the rest of the repo.
Fix: add --base "$GITHUB_REF_NAME" or a dedicated base-branch input.
7. Hardcoded artifact name changelog-bundle — changelog/bundle-create/action.yml line 91 / changelog/bundle-pr/action.yml line 29
If a consuming workflow ever calls bundle-create twice in parallel (e.g. matrix), the second upload will overwrite the first and bundle-pr will silently process the wrong file. Consider parameterising the artifact name or including the output basename.
Summary
| File | Line(s) | Severity | Finding |
|---|---|---|---|
changelog-bundle.yml |
after line 30 | 🔴 | Missing permissions: {} |
changelog-bundle.yml |
after line 30 | 🔴 | Missing concurrency block |
bundle-pr/action.yml |
39–58 | 🟡 | Force-push + no branch-name validation |
bundle-pr/action.yml |
42–43 | 🟡 | Unvalidated $OUTPUT as cp destination |
bundle-create/action.yml |
78–80 | 🟡 | Unvalidated URL to curl (SSRF potential) |
bundle-pr/action.yml |
65–68 | 🟠 | gh pr create missing --base |
bundle-create/action.yml |
91 | 🟠 | Hardcoded artifact name risks collision |
| description: 'GitHub repository owner; falls back to bundle.owner in changelog.yml' | ||
| type: string | ||
|
|
||
| jobs: |
There was a problem hiding this comment.
Missing permissions: {} at workflow scope. Both changelog-validate.yml (line 11) and changelog-submit.yml (line 15) deny all permissions at the top level and then grant only what each job needs. Without this, the workflow inherits the repository default token permissions — the generate job silently runs with write access it does not need.
Add before jobs::
permissions: {}There was a problem hiding this comment.
Changes from this report:
.github/workflows/changelog-bundle.yml
permissions: {}added at workflow scope- concurrency block added, keyed on
inputs.outputwithcancel-in-progress: false. Two calls for the same output path will now queue instead of racing. base-branchinput propagated tobundle-praction.- report description updated to specify HTTPS.
changelog/bundle-create/action.yml
- HTTPS-only for report URLs — http:// URLs are now rejected with an error.
artifact-nameinput added (defaultchangelog-bundle) — the upload step uses it instead of a hardcoded string, preventing collisions in matrix scenarios.
changelog/bundle-pr/action.yml
- Output path validation step added: rejects absolute paths, .. traversal, and files not ending in .yml/.yaml.
- Branch name validation using
^[a-zA-Z0-9._+-]+$— matches the guard inchangelog/submit/action.ymllines 72-77. - basename strips both .yaml and .yml extensions to handle either.
--baseflag ongh pr createwhenbase-branchinput is provided.artifact-nameinput added to match thebundle-createparameterization.- Force-push has been changed to
--force-with-lease.
| | `config` | Path to changelog.yml configuration file | `false` | `docs/changelog.yml` | | ||
| | `release-version` | GitHub release tag used as the PR filter source (e.g. v9.2.0). Mutually exclusive with report. Requires repo to be set in changelog.yml or via repo input. | `false` | | | ||
| | `report` | Buildkite promotion report URL or local file path used as the PR filter source. Mutually exclusive with release-version. Local paths must be relative to repo root. | `false` | | | ||
| | `output` | Output file path for the bundle, relative to the repo root (e.g. docs/releases/v9.2.0.yaml). Must end in .yml or .yaml. | `true` | | |
There was a problem hiding this comment.
It might be nice (in follow-up PR?) to make the github action support profiles. Then more of these options could be picked up automatically from the config file (e.g. output, output_products, etc)
This pull request introduces an action and reusable workflow for bundling changelog entries, allowing teams to generate a fully-resolved changelog bundle file filtered by release version or promotion report. The changes add a reusable GitHub Actions workflow, a composite action, and comprehensive documentation for integrating this process into various release pipelines.
Changelog bundling infrastructure:
.github/workflows/changelog-bundle.yml, a reusable workflow that generates a changelog bundle file by invoking the new composite action. This workflow supports filtering by GitHub release version or Buildkite promotion report, and outputs the bundled changelog to a specified path.changelog/bundle/action.yml, a composite GitHub Action that runsdocs-builder changelog bundlewith flexible input options, commits the result to the repository, and handles filtering, configuration, and output management.