Skip to content

Commit 975a98d

Browse files
ci: introduce towncrier for fragment-based changelog management (#16)
* introduce towncrier for fragment-based changelog management Replace manual CHANGELOG.md editing with towncrier fragments. Each PR adds a small file under <package>/.changelog/ instead of editing CHANGELOG.md directly, eliminating merge conflicts on backports and concurrent PRs. Every publishable package in this repo releases independently, so each gets its own [tool.towncrier] config and .changelog/ directory. Changes: - Add [tool.towncrier] block to each package's pyproject.toml (7 instrumentations + util-genai) - Add .changelog/.gitignore stub per package - Insert do-not-edit comment and <!-- changelog start --> marker above the Unreleased header in each CHANGELOG.md - Add shared scripts/changelog_template.j2 - Add .github/workflows/changelog.yml: blocks direct CHANGELOG.md edits, requires a fragment, previews the rendered changelog per package. Skipped via the "Skip Changelog" label - Add tox -e changelog-preview for local previewing - Add towncrier to root dev dependencies - Update CONTRIBUTING.md with fragment authoring instructions Assisted-by: Claude Opus 4.7 (1M context) * fix CI: drop changelog-preview from envlist; refresh uv.lock - Remove changelog-preview from tox envlist so the workflow generator doesn't create a CI job for it. The env stays available for local use and the changelog.yml workflow already runs the preview in CI. - Regenerate uv.lock with the index URL form the uv-lock pre-commit hook uses (trailing slash), matching upstream. Assisted-by: Claude Opus 4.7 (1M context) * add condensed changelog guidance to AGENTS.md Addresses review feedback on PR #16 to mirror the new CONTRIBUTING.md guidance for AI-assisted contributions in a more concise form. Assisted-by: Claude Opus 4.7 (1M context) * address review feedback on changelog setup - Re-trigger the changelog workflow on labeled/unlabeled events so adding the "Skip Changelog" label without pushing a new commit is enough to clear a previously failed run. - Validate fragment basenames against <PR_NUMBER>.<type> in the workflow, catching typos in either the PR number or the fragment type before they ship a misleading changelog link. - Note in each CHANGELOG.md do-not-edit comment that the existing static "## Unreleased" entries must be folded into the first towncrier-built release manually. Assisted-by: Claude Opus 4.7 (1M context) * address review feedback on AGENTS.md changelog section - Drop the Skip Changelog label bullet: most contributors don't have write access, so steering agents toward applying labels is the wrong default. - Drop the trailing link to CONTRIBUTING.md — it's already linked at the top of the file. Assisted-by: Claude Opus 4.7 (1M context)
1 parent 58b3c46 commit 975a98d

31 files changed

Lines changed: 669 additions & 3 deletions

File tree

.github/workflows/changelog.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# This action requires that any PR targeting the main branch should add a
2+
# changelog fragment file in a .changelog/ directory under the affected
3+
# package. If a changelog entry is not required, add the "Skip Changelog"
4+
# label to disable this action.
5+
6+
name: changelog
7+
8+
on:
9+
pull_request:
10+
types: [opened, synchronize, reopened, labeled, unlabeled]
11+
branches:
12+
- main
13+
14+
permissions:
15+
contents: read
16+
17+
jobs:
18+
changelog:
19+
runs-on: ubuntu-latest
20+
if: ${{ !contains(github.event.pull_request.labels.*.name, 'Skip Changelog') }}
21+
22+
steps:
23+
- uses: actions/checkout@v4
24+
with:
25+
fetch-depth: 0
26+
27+
- name: Fetch base branch
28+
run: git fetch origin ${{ github.base_ref }} --depth=1
29+
30+
- name: Ensure no direct changes to CHANGELOG.md
31+
run: |
32+
if [[ $(git diff --name-only FETCH_HEAD -- '**/CHANGELOG.md') ]]
33+
then
34+
echo "CHANGELOG.md files should not be directly modified."
35+
echo "Please add a changelog fragment file to the affected package's .changelog/ directory instead."
36+
echo "See CONTRIBUTING.md for details."
37+
echo ""
38+
echo "Or add the \"Skip Changelog\" label if this job should be skipped."
39+
false
40+
fi
41+
42+
- name: Check for changelog fragment
43+
env:
44+
PR_NUMBER: ${{ github.event.pull_request.number }}
45+
run: |
46+
fragments=$(git diff --diff-filter=A --name-only FETCH_HEAD -- '**/.changelog/*' | grep -v '/\.gitignore$' || true)
47+
if [[ -z "$fragments" ]]; then
48+
echo "No changelog fragment found for this PR."
49+
echo ""
50+
echo "Add a file named <package>/.changelog/${PR_NUMBER}.<type>"
51+
echo "where <type> is one of: added, changed, deprecated, removed, fixed"
52+
echo ""
53+
echo "See CONTRIBUTING.md for details."
54+
echo ""
55+
echo "Or add the \"Skip Changelog\" label if this job should be skipped."
56+
exit 1
57+
fi
58+
invalid=()
59+
while IFS= read -r f; do
60+
base=$(basename "$f")
61+
if [[ ! "$base" =~ ^([0-9]+)\.(added|changed|deprecated|removed|fixed)$ ]]; then
62+
invalid+=("$f (expected <PR_NUMBER>.<type>; type one of added, changed, deprecated, removed, fixed)")
63+
continue
64+
fi
65+
if [[ "${BASH_REMATCH[1]}" != "${PR_NUMBER}" ]]; then
66+
invalid+=("$f (PR number ${BASH_REMATCH[1]} does not match this PR's number ${PR_NUMBER})")
67+
fi
68+
done <<< "$fragments"
69+
if (( ${#invalid[@]} > 0 )); then
70+
echo "Invalid changelog fragment(s):"
71+
for msg in "${invalid[@]}"; do
72+
echo " $msg"
73+
done
74+
exit 1
75+
fi
76+
echo "Found changelog fragment(s):"
77+
echo "$fragments"
78+
79+
- name: Set up Python
80+
uses: actions/setup-python@v5
81+
with:
82+
python-version: "3.14"
83+
84+
- name: Install towncrier
85+
run: pip install towncrier==25.8.0
86+
87+
- name: Preview changelogs
88+
run: |
89+
set -eu
90+
for pkg in instrumentation/* util/opentelemetry-util-genai; do
91+
[ -f "$pkg/pyproject.toml" ] || continue
92+
grep -q "tool.towncrier" "$pkg/pyproject.toml" || continue
93+
echo "=== $pkg ==="
94+
(cd "$pkg" && towncrier build --draft --version Unreleased)
95+
done

AGENTS.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ uv run tox -e typecheck
7070
`typing.cast(...)`, unless the referenced type is imported at runtime.
7171
- Whenever applicable, all code changes should have tests that actually validate the changes.
7272

73+
## Changelog
74+
75+
This repo uses [towncrier](https://towncrier.readthedocs.io/) to manage changelogs.
76+
77+
- Do not edit `CHANGELOG.md` directly — the `changelog` workflow rejects PRs that do.
78+
- For changes with user-visible impact, add a fragment at `<package>/.changelog/<PR_NUMBER>.<type>`
79+
containing a one-line description. Types: `added`, `changed`, `deprecated`, `removed`, `fixed`.
80+
- Don't include the PR number in the body — towncrier appends it from the filename.
81+
- Preview locally with `uv run tox -e changelog-preview`.
82+
7383
## Instrumentation rules
7484

7585
Apply to packages under `instrumentation/`.

CONTRIBUTING.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,33 @@ uv run tox -e typecheck
6868

6969
### 4. Update the changelog
7070

71-
Add an entry to the affected package's `CHANGELOG.md` under the `Unreleased`
72-
section for any change with user-visible impact. Pure docs and tooling
73-
changes don't need an entry.
71+
This repo uses [towncrier](https://towncrier.readthedocs.io/) to manage
72+
changelogs. Each PR with user-visible impact must add a changelog fragment
73+
under the affected package's `.changelog/` directory rather than editing
74+
`CHANGELOG.md` directly.
75+
76+
**Fragment path:** `<package>/.changelog/<PR_NUMBER>.<TYPE>`
77+
78+
**Types:** `added`, `changed`, `deprecated`, `removed`, `fixed`.
79+
80+
The file contains a one-line description. For example,
81+
`instrumentation/opentelemetry-instrumentation-anthropic/.changelog/123.fixed`:
82+
83+
```
84+
fix request hook not being called when stream=True
85+
```
86+
87+
Don't include the PR number in the body — towncrier appends it from the
88+
filename.
89+
90+
Preview the rendered changelogs locally:
91+
92+
```sh
93+
uv run tox -e changelog-preview
94+
```
95+
96+
If your change doesn't need an entry (pure docs/tooling), add the
97+
`Skip Changelog` label to the PR.
7498

7599
## Keep PRs small
76100

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!.gitignore

instrumentation/opentelemetry-instrumentation-anthropic/CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
<!--
9+
Do *NOT* add changelog entries here!
10+
11+
This changelog is managed by towncrier and is compiled at release time.
12+
13+
The static "## Unreleased" section below pre-dates towncrier; its entries
14+
must be folded into the first towncrier-generated release manually.
15+
16+
See https://github.com/open-telemetry/opentelemetry-python-genai/blob/main/CONTRIBUTING.md#changelog for details.
17+
-->
18+
19+
<!-- changelog start -->
20+
821
## Unreleased
922

1023
- Update `opentelemetry-util-genai` dependency range to `>= 0.4b0.dev, <0.5b0`

instrumentation/opentelemetry-instrumentation-anthropic/pyproject.toml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,37 @@ packages = ["src/opentelemetry"]
5252

5353
[tool.pytest.ini_options]
5454
testpaths = ["tests"]
55+
56+
[tool.towncrier]
57+
directory = ".changelog"
58+
filename = "CHANGELOG.md"
59+
start_string = "<!-- changelog start -->\n"
60+
template = "../../scripts/changelog_template.j2"
61+
issue_format = "[#{issue}](https://github.com/open-telemetry/opentelemetry-python-genai/pull/{issue})"
62+
wrap = true
63+
issue_pattern = "^(\\d+)"
64+
65+
[[tool.towncrier.type]]
66+
directory = "added"
67+
name = "Added"
68+
showcontent = true
69+
70+
[[tool.towncrier.type]]
71+
directory = "changed"
72+
name = "Changed"
73+
showcontent = true
74+
75+
[[tool.towncrier.type]]
76+
directory = "deprecated"
77+
name = "Deprecated"
78+
showcontent = true
79+
80+
[[tool.towncrier.type]]
81+
directory = "removed"
82+
name = "Removed"
83+
showcontent = true
84+
85+
[[tool.towncrier.type]]
86+
directory = "fixed"
87+
name = "Fixed"
88+
showcontent = true
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!.gitignore

instrumentation/opentelemetry-instrumentation-claude-agent-sdk/CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
<!--
9+
Do *NOT* add changelog entries here!
10+
11+
This changelog is managed by towncrier and is compiled at release time.
12+
13+
The static "## Unreleased" section below pre-dates towncrier; its entries
14+
must be folded into the first towncrier-generated release manually.
15+
16+
See https://github.com/open-telemetry/opentelemetry-python-genai/blob/main/CONTRIBUTING.md#changelog for details.
17+
-->
18+
19+
<!-- changelog start -->
20+
821
## Unreleased
922

1023
- Update `opentelemetry-util-genai` dependency range to `>= 0.4b0.dev, <0.5b0`

instrumentation/opentelemetry-instrumentation-claude-agent-sdk/pyproject.toml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,37 @@ packages = ["src/opentelemetry"]
5151

5252
[tool.pytest.ini_options]
5353
testpaths = ["tests"]
54+
55+
[tool.towncrier]
56+
directory = ".changelog"
57+
filename = "CHANGELOG.md"
58+
start_string = "<!-- changelog start -->\n"
59+
template = "../../scripts/changelog_template.j2"
60+
issue_format = "[#{issue}](https://github.com/open-telemetry/opentelemetry-python-genai/pull/{issue})"
61+
wrap = true
62+
issue_pattern = "^(\\d+)"
63+
64+
[[tool.towncrier.type]]
65+
directory = "added"
66+
name = "Added"
67+
showcontent = true
68+
69+
[[tool.towncrier.type]]
70+
directory = "changed"
71+
name = "Changed"
72+
showcontent = true
73+
74+
[[tool.towncrier.type]]
75+
directory = "deprecated"
76+
name = "Deprecated"
77+
showcontent = true
78+
79+
[[tool.towncrier.type]]
80+
directory = "removed"
81+
name = "Removed"
82+
showcontent = true
83+
84+
[[tool.towncrier.type]]
85+
directory = "fixed"
86+
name = "Fixed"
87+
showcontent = true
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!.gitignore

0 commit comments

Comments
 (0)