[chore] Implement chloggen fragment automation for releases (#2787)#2837
[chore] Implement chloggen fragment automation for releases (#2787)#2837harshitt13 wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a two-stage chloggen-based changelog automation: a per-PR workflow that uses GitHub Models to generate a .chloggen/pr-<num>.yaml fragment (committing to same-repo branches, commenting instructions for forks) and validates it, plus a manual workflow that compiles all fragments into CHANGELOG.md and opens a release PR via opentelemetrybot. Standardizes fragments with .chloggen/config.yaml and .chloggen/TEMPLATE.yaml.
Changes:
- Add
changelog-fragment.ymlworkflow: AI-generated fragment per PR, with skip rules ([chore],Skip Changeloglabel) and avalidate-fragmentjob. - Add
draft-release-changelog.ymlmanual workflow that runschloggen update --version <v>and opens a release PR. - Add chloggen
config.yamlandTEMPLATE.yamlwith the supported change_type enum.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
.github/workflows/changelog-fragment.yml |
Per-PR AI fragment generation, fork-fallback comment, and validation job. |
.github/workflows/draft-release-changelog.yml |
Manual release workflow compiling fragments into CHANGELOG.md and opening a PR. |
.chloggen/config.yaml |
chloggen configuration: allowed change_types, entries dir, changelog target. |
.chloggen/TEMPLATE.yaml |
Fragment template documenting valid change_type values. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2f4e62c0f0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@jaydeluca, could you please review the PR whenever you have a moment? |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.
Comments suppressed due to low confidence (1)
.github/workflows/changelog-fragment.yml:223
- Same
$GITHUB_PATHtiming issue as in thegenerate-fragmentjob:go installputschloggenin$GOPATH/bin, but theecho >> $GITHUB_PATHon line 222 only takes effect for the next step, so the immediately-followingchloggen validatecall on line 223 will not find the binary. Either split this into a separate install step, prepend$(go env GOPATH)/bintoPATHviaexportin the same shell, or call chloggen by absolute path.
go install go.opentelemetry.io/build-tools/chloggen@v0.15.0
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
chloggen validate --config .chloggen/config.yaml
Signed-off-by: Harshit Kushwaha <find.harshitkushwaha@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (6)
.github/workflows/changelog-fragment.yml:188
- The
generate-fragmentjob's steps are guarded bygithub.event.pull_request.head.repo.full_name == github.repository, so for PRs opened from forks no fragment is ever produced. However the downstreamvalidate-fragmentjob (intended to become a required status check) does not include the same fork guard in its skip logic and unconditionally errors out withmissing changelog fragment: ...on line 183. This means every fork PR will permanently fail the required status check with no way to recover, which would effectively block all outside contributions once the status check is enforced. The PR description states fork PRs should instead receive a comment containing the generated YAML, but no such comment-posting step exists in the diff — either that fallback needs to be implemented, orvalidate-fragmentneeds to treat fork PRs as a skip case (and the maintainer must add the fragment after merging the PR to a same-repo branch).
- name: Validate fragment
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
fragment=".chloggen/pr-${PR_NUMBER}.yaml"
if [[ ! -f "$fragment" ]]; then
echo "missing changelog fragment: $fragment"
exit 1
fi
go install go.opentelemetry.io/build-tools/chloggen@v0.15.0
"$(go env GOPATH)/bin/chloggen" validate --config .chloggen/config.yaml
.github/workflows/changelog-fragment.yml:135
- The PR description states that for fork PRs the workflow "posts a shell-safe generated comment with the YAML for fork PRs," but the diff contains no
gh pr comment/actions/github-script/peter-evans/create-or-update-commentstep or any other comment-posting step. The fork branch is silently skipped instead. Either the PR description should be corrected, or the fallback fork-PR comment step should be added.
- name: Generate fragment
if: ${{ steps.skip.outputs.skip != 'true' && steps.fragment.outputs.exists != 'true' && github.event.pull_request.head.repo.full_name == github.repository }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_BODY: ${{ github.event.pull_request.body }}
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
changed_files="$(git diff --name-only "origin/${BASE_REF}"...HEAD)"
request_payload="$(jq -n \
--arg title "$PR_TITLE" \
--arg body "$PR_BODY" \
--arg changed_files "$changed_files" \
'{
model: "gpt-4o-mini",
response_format: {type: "json_object"},
messages: [
{
role: "system",
content: "Generate a single JSON object for a changelog fragment. Return only valid JSON with these keys: change_type, component, note, issues, subtext, change_logs. change_type must be one of breaking, deprecation, new_component, enhancement, bug_fix. component must be blank if the PR does not clearly map to one component. note must be a brief end-user facing summary. issues must be an array of issue numbers without #. subtext is optional and may be null or omitted. change_logs must be [\"user\"]. Do not add markdown or extra keys."
},
{
role: "user",
content: "PR title: \($title)\n\nPR body:\n\($body)\n\nChanged files:\n\($changed_files)"
}
]
}')"
response="$(curl --fail-with-body --silent --show-error https://models.github.ai/inference/chat/completions \
-H "Accept: application/vnd.github+json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-d "$request_payload")"
jq -r '.choices[0].message.content | fromjson' <<<"$response" | "$(go env GOPATH)/bin/yq" -P - > ".chloggen/pr-${PR_NUMBER}.yaml"
- name: Validate AI Output
if: ${{ steps.skip.outputs.skip != 'true' && steps.fragment.outputs.exists != 'true' && github.event.pull_request.head.repo.full_name == github.repository }}
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
fragment=".chloggen/pr-${PR_NUMBER}.yaml"
if ! "$(go env GOPATH)/bin/yq" -e 'has("change_type") and has("component") and has("note")' "$fragment" >/dev/null; then
echo "missing required keys: change_type, component, note"
rm -f "$fragment"
exit 1
fi
- name: Commit
if: ${{ steps.skip.outputs.skip != 'true' && steps.fragment.outputs.exists != 'true' && github.event.pull_request.head.repo.full_name == github.repository }}
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_BRANCH: ${{ github.head_ref }}
run: |
git config user.name opentelemetrybot
git config user.email 107717825+opentelemetrybot@users.noreply.github.com
git add ".chloggen/pr-${PR_NUMBER}.yaml"
git commit -m "Add changelog fragment for PR #${PR_NUMBER}"
git push origin HEAD:"${PR_BRANCH}"
.github/workflows/changelog-fragment.yml:170
- The skip-condition logic (dependabot/renovate bot user,
[chore]prefix,Skip Changeloglabel) is duplicated verbatim between thegenerate-fragmentandvalidate-fragmentjobs (lines 33–54 vs. 149–170). Any future tweak (e.g. adding another skip label or another bot account) must be made in both places and will silently desync. Consider extracting the check into its own small job whose output is consumed vianeeds.<job>.outputs.skip, or factor it into a reusable composite action.
- name: Check skip conditions
id: skip
env:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }}
PR_USER_LOGIN: ${{ github.event.pull_request.user.login }}
run: |
skip=false
if [[ "$PR_USER_LOGIN" == "dependabot[bot]" || "$PR_USER_LOGIN" == "renovate[bot]" ]]; then
skip=true
fi
if [[ "$PR_TITLE" == \[chore\]* ]]; then
skip=true
fi
if jq -e 'index("Skip Changelog")' <<<"$PR_LABELS" >/dev/null; then
skip=true
fi
echo "skip=$skip" >> "$GITHUB_OUTPUT"
.github/workflows/changelog-fragment.yml:188
chloggenis installed inside the validation step on every PR run viago install ...@v0.15.0, which downloads and compiles the binary from source on each invocation. This adds latency to a status check that will run on the majority of PRs. Consider caching the Go build cache (e.g. viaactions/setup-gowith caching enabled) or downloading a pre-built release artifact instead.
go install go.opentelemetry.io/build-tools/chloggen@v0.15.0
"$(go env GOPATH)/bin/chloggen" validate --config .chloggen/config.yaml
.github/workflows/changelog-fragment.yml:31
yqis installed viago installon every PR run (line 31). Sinceyqis a popular tool with prebuilt binaries and the runner already has Go available, this still adds noticeable build time on each invocation. Consider downloading a release binary (curl -L https://github.com/mikefarah/yq/releases/download/v4.44.3/yq_linux_amd64 -o ...) or pinning asetup-goaction with module caching.
- name: Install yq
run: |
go install github.com/mikefarah/yq/v4@v4.44.3
.github/workflows/draft-release-changelog.yml:40
- Third-party action
peter-evans/create-pull-request@v6is referenced by a mutable major-version tag. Other workflows in this repository (and the OpenTelemetry OSSF scorecard configuration this repo uses) generally prefer pinning third-party actions to a full commit SHA with the version as a comment, to avoid supply-chain risk if the tag is re-pointed. Consider pinning to a specific commit SHA.
uses: peter-evans/create-pull-request@v6
…ror messaging Signed-off-by: Harshit Kushwaha <find.harshitkushwaha@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (1)
.github/workflows/draft-release-changelog.yml:52
- Trailing whitespace at the end of this line.
| - name: Commit | ||
| if: ${{ steps.skip.outputs.skip != 'true' && steps.fragment.outputs.exists != 'true' && github.event.pull_request.head.repo.full_name == github.repository }} | ||
| env: | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| PR_BRANCH: ${{ github.head_ref }} | ||
| run: | | ||
| git config user.name opentelemetrybot | ||
| git config user.email 107717825+opentelemetrybot@users.noreply.github.com | ||
| git add ".chloggen/pr-${PR_NUMBER}.yaml" | ||
| git commit -m "Add changelog fragment for PR #${PR_NUMBER}" | ||
| git push origin HEAD:"${PR_BRANCH}" |
| validate-fragment: | ||
| needs: generate-fragment | ||
| if: always() | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| repository: ${{ github.event.pull_request.head.repo.full_name }} | ||
| ref: ${{ github.head_ref }} | ||
| token: ${{ github.token }} | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Check skip conditions | ||
| id: skip | ||
| env: | ||
| PR_TITLE: ${{ github.event.pull_request.title }} | ||
| PR_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }} | ||
| PR_USER_LOGIN: ${{ github.event.pull_request.user.login }} | ||
| run: | | ||
| skip=false | ||
|
|
||
| if [[ "$PR_USER_LOGIN" == "dependabot[bot]" || "$PR_USER_LOGIN" == "renovate[bot]" ]]; then | ||
| skip=true | ||
| fi | ||
|
|
||
| if [[ "$PR_TITLE" == \[chore\]* ]]; then | ||
| skip=true | ||
| fi | ||
|
|
||
| if jq -e 'index("Skip Changelog")' <<<"$PR_LABELS" >/dev/null; then | ||
| skip=true | ||
| fi | ||
|
|
||
| echo "skip=$skip" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Exit if skipped | ||
| if: ${{ steps.skip.outputs.skip == 'true' }} | ||
| run: exit 0 | ||
|
|
||
| - name: Validate fragment | ||
| env: | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| run: | | ||
| fragment=".chloggen/pr-${PR_NUMBER}.yaml" | ||
|
|
||
| if [[ ! -f "$fragment" ]]; then | ||
| echo "❌ Missing changelog fragment: $fragment" | ||
| echo "If you are contributing from a fork, you must create this file manually." | ||
| echo "Copy the contents of .chloggen/TEMPLATE.yaml into a new file named $fragment, fill it out, and commit it to your branch." | ||
| exit 1 | ||
| fi |
| - name: Check skip conditions | ||
| id: skip | ||
| env: | ||
| PR_TITLE: ${{ github.event.pull_request.title }} | ||
| PR_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }} | ||
| PR_USER_LOGIN: ${{ github.event.pull_request.user.login }} | ||
| run: | | ||
| skip=false | ||
|
|
||
| if [[ "$PR_USER_LOGIN" == "dependabot[bot]" || "$PR_USER_LOGIN" == "renovate[bot]" ]]; then | ||
| skip=true | ||
| fi | ||
|
|
||
| if [[ "$PR_TITLE" == \[chore\]* ]]; then | ||
| skip=true | ||
| fi | ||
|
|
||
| if jq -e 'index("Skip Changelog")' <<<"$PR_LABELS" >/dev/null; then | ||
| skip=true | ||
| fi | ||
|
|
||
| echo "skip=$skip" >> "$GITHUB_OUTPUT" |
| - uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
| change_type: enhancement | ||
| component: | ||
| note: Automate changelog generation using fragment pattern | ||
| issues: [2787] | ||
| subtext: | ||
| change_logs: [user] |
breedx-splk
left a comment
There was a problem hiding this comment.
I hate chloggen. It's unnecessarily complicated and makes the process of submitting PRs worse.
Who is asking for this? I don't think the maintainers want this.
|
For things like this, please open an issue to gather consensus from the codeowners (approvers + maintainers) before going and opening a PR. |
Description:
Feature addition - This PR implements a robust, two-part changelog automation system utilizing the standard OpenTelemetry
chloggenfragment pattern. It shifts changelog generation left to the PR level and uses deterministic compilation at release time, preventing release-day bottlenecks, merge conflicts, and AI hallucinations..github/workflows/changelog-fragment.yml)pull_request.gpt-4o-mini) via the built-inGITHUB_TOKENto generate a.chloggen/pr-{number}.yamlfragment based on the PR title, body, and changed files.opentelemetrybotaccount (or posts a shell-safe generated comment with the YAML for fork PRs).validate-fragmentrunningchloggen validate) to ensure fragment correctness..github/workflows/draft-release-changelog.yml)workflow_dispatchtrigger for the Release Captain.chloggenbinary, deterministically compiles all fragments intoCHANGELOG.md, clears the.chloggendirectory, and opens a PR using theopentelemetrybotaccount to ensure EasyCLA compliance..chloggen/config.yamland.chloggen/TEMPLATE.yamlto standardize the entries.Existing Issue(s):
Resolves #2787
Testing:
.chloggenconfigurations and workflow files using Pythonyaml.safe_load.git diff --check.jqandyqavailable on theubuntu-latestrunner.opentelemetrybotpermissions and API payload execution behave as expected in the live environment.Documentation:
.chloggen/TEMPLATE.yamlwhich acts as inline documentation for contributors regarding validchange_typeenums and required fields.[chore]or theSkip Changeloglabel).Outstanding items:
validate-fragmentas a Required Status Check formainto enforce the workflow.CONTRIBUTING.mdin a future PR explaining how to use theSkip Changeloglabel or[chore]prefix to intentionally bypass generation for internal tasks.