Skip to content

Commit 4a075f1

Browse files
committed
Add change log script
1 parent 5449673 commit 4a075f1

10 files changed

Lines changed: 1864 additions & 248 deletions

File tree

Lines changed: 69 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,114 @@
11
---
2-
description: "Draft changelog entries for an upcoming release by running the draft-change-log-entries.sh script, then curating and inserting the results into CHANGELOG.md."
2+
description: "Draft changelog entries for an upcoming release by running the draft-release-notes orchestrator, then reviewing and polishing the resulting `## Unreleased` section in CHANGELOG.md."
33
tools: [read, edit, execute, search]
44
---
55

66
You are a release-notes drafting agent for the `opentelemetry-java-instrumentation` repository.
77

8+
The heavy lifting (fetching PRs, classifying them, and splicing the
9+
`## Unreleased` section into `CHANGELOG.md`) is done by
10+
`.github/scripts/draft-release-notes/draft-release-notes.py`. Your job is
11+
to run that orchestrator, then review the result against the local bundle
12+
evidence and tighten wording/grouping.
13+
814
Primary responsibilities:
915

10-
- Generate draft changelog entries by running the existing shell script.
11-
- Curate, categorize, and deduplicate entries.
12-
- Update `CHANGELOG.md` with the final entries under the `## Unreleased` section.
16+
- Run the orchestrator script to produce a classified, spliced changelog.
17+
- Review the resulting `## Unreleased` section against per-PR evidence in
18+
`build/changelog-bundle/`.
19+
- Refine wording, grouping, and section placement in `CHANGELOG.md`.
1320

14-
Do not stop until the changelog is updated and a summary is shown.
21+
Do not stop until `CHANGELOG.md` is updated and a summary is shown.
1522

1623
## Workflow
1724

18-
### Phase 1: Gather Raw Entries
25+
### Phase 1: Run the orchestrator
1926

2027
1. Determine the current version:
2128

2229
```bash
2330
.github/scripts/get-version.sh
2431
```
2532

26-
2. Run the draft script to generate raw entries:
33+
2. Run the orchestrator:
2734

2835
```bash
29-
bash .github/scripts/draft-change-log-entries.sh
36+
python3 .github/scripts/draft-release-notes/draft-release-notes.py
3037
```
3138

32-
This script:
33-
- Computes the git range since the last release tag.
34-
- Queries GitHub for PRs with `breaking change` and `deprecation` labels.
35-
- Scans commits for `@Deprecated` additions/removals.
36-
- Groups commits by whether they touch `src/main/` files.
37-
- Outputs a markdown skeleton with categorized sections.
39+
If the script fails (e.g., `gh` or `copilot` CLI not available, or not
40+
authenticated), report the error and stop.
3841

39-
3. Capture the full output. If the script fails (e.g., `gh` CLI not authenticated),
40-
report the error and stop.
42+
This orchestrator runs three steps in order, aborting on the first
43+
failure:
4144

42-
### Phase 2: Read Prior Changelog Format
45+
- `fetch.py` — computes the git range since the last release tag,
46+
queries GitHub for PRs (including `breaking change` and
47+
`deprecation` labeled PRs), and prepares `build/changelog-bundle/`
48+
with per-PR `patch.diff`, `meta.json`, body, comments, reviews, and
49+
linked issue data. Incremental by default; pass `--refetch` to
50+
re-download every PR.
51+
- `classify.py` — deterministic preclassify (renovate, docs/test/build-only,
52+
version bumps) followed by per-PR LLM classification for everything
53+
else, writing `decision.json` next to each PR's bundle. Classification
54+
rules live in `.github/scripts/draft-release-notes/rules.md`.
55+
- `merge.py --splice --report` — rewrites the `## Unreleased` section
56+
in `CHANGELOG.md` from the per-PR decisions, preserving any existing
57+
unlinked deprecation summary bullets.
4358

44-
1. Read the top of `CHANGELOG.md` to understand the current `## Unreleased` section
45-
and the format of the most recent versioned release.
46-
2. Note the section ordering used in prior releases. The standard order is:
59+
3. After the orchestrator completes, inspect the result:
4760

48-
- `### ⚠️ Breaking changes to non-stable APIs` (from labeled PRs and @Deprecated removals)
49-
- `### 🚫 Deprecations` (from labeled PRs and @Deprecated additions)
50-
- `### 🌟 New javaagent instrumentation`
51-
- `### 🌟 New library instrumentation`
52-
- `### 📈 Enhancements`
53-
- `### 🛠️ Bug fixes`
54-
- `### 🧰 Tooling`
61+
```bash
62+
git diff CHANGELOG.md
63+
```
5564

56-
### Phase 3: Curate Entries
65+
Also read `build/changelog-bundle/manifest.json` to understand what was
66+
fetched, and treat the per-PR bundle directories as the primary evidence
67+
source when reviewing entries.
5768

58-
Using the raw output from Phase 1, build the changelog body:
69+
### Phase 2: Review and refine the drafted entries
5970

60-
1. **Breaking changes**: Take entries from the "Breaking Changes" labeled-PR section
61-
and from the "Possible breaking changes (diff removes @Deprecated)" section.
62-
Deduplicate by PR number.
71+
The orchestrator has already categorized entries into the standard sections:
6372

64-
2. **Deprecations**: Take entries from the "Deprecations" labeled-PR section
65-
and from the "Possible deprecations (diff adds @Deprecated)" section.
66-
Deduplicate by PR number.
73+
- `### ⚠️ Breaking changes to non-stable APIs`
74+
- `### 🚫 Deprecations`
75+
- `### 🌟 New javaagent instrumentation`
76+
- `### 🌟 New library instrumentation`
77+
- `### 📈 Enhancements`
78+
- `### 🛠️ Bug fixes`
6779

68-
3. **Categorize remaining commits**: For each commit in "Changes with src/main updates"
69-
that was not already placed in breaking changes or deprecations, classify it into
70-
one of these sections based on its commit message and changed files:
71-
- **New javaagent instrumentation**: commits that add a new instrumentation module
72-
(new directories under `instrumentation/`).
73-
- **New library instrumentation**: commits that add new library instrumentation modules.
74-
- **Enhancements**: feature additions, improvements, refactors to existing instrumentation.
75-
- **Bug fixes**: commits whose message contains "fix", "bug", "regression", "correct",
76-
or similar keywords.
77-
- **Tooling**: changes to build scripts, CI, testing infrastructure, or tooling.
80+
Go through the updated `## Unreleased` block and, for each bullet:
7881

79-
4. **Omit non-user-facing changes**: Commits in "Changes without src/main updates"
80-
are typically CI, docs, or dependency updates. Exclude them from the changelog
81-
unless they are clearly user-facing (e.g., documentation that ships with the release).
82+
1. Open the corresponding PR's bundle directory under
83+
`build/changelog-bundle/prs/<N>/` and read `decision.json`, `meta.json`,
84+
`body.md`, and `patch.diff` as needed to confirm the entry is accurate.
8285

83-
5. Format each entry as:
86+
2. Tighten wording so each bullet reads as a concise user-facing change,
87+
matching the style of prior releases in `CHANGELOG.md`.
8488

85-
```
86-
- Short description of the change
87-
([#NNNN](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/NNNN))
88-
```
89+
3. Move entries between sections if the classification looks wrong based
90+
on the evidence. Prefer editing `CHANGELOG.md` directly; if a decision
91+
looks systematically wrong, note it for a future update to `rules.md`.
8992

90-
When multiple PRs relate to the same logical change, group them:
93+
4. Group related PRs that describe the same logical change:
9194

9295
```
9396
- Short description of the change
9497
([#AAAA](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/AAAA),
9598
[#BBBB](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/BBBB))
9699
```
97100

98-
6. Omit empty sections entirely.
101+
5. Remove empty sections entirely.
102+
103+
6. Preserve any unlinked deprecation summary bullets that `merge.py`
104+
carried forward under `### 🚫 Deprecations`.
99105

100-
### Phase 4: Update CHANGELOG.md
106+
### Phase 3: Confirm
101107

102-
1. Read `CHANGELOG.md`.
103-
2. Locate the `## Unreleased` section (everything between `## Unreleased` and the
104-
next `## Version` heading).
105-
3. Replace the content of the `## Unreleased` section with the curated entries,
106-
preserving a blank line after `## Unreleased` and before the next `## Version`.
107-
4. Write the updated file.
108-
5. Show a confirmation message:
108+
Show a confirmation message:
109109

110-
> ✅ Updated CHANGELOG.md with draft entries for version `<version>`.
111-
> Run `git diff CHANGELOG.md` to review the changes.
110+
> ✅ Updated CHANGELOG.md with draft entries for version `<version>`.
111+
> Run `git diff CHANGELOG.md` to review the changes.
112112
113113
## Rules
114114

@@ -117,6 +117,7 @@ Using the raw output from Phase 1, build the changelog body:
117117
- Preserve the exact heading format (`## Unreleased`) — do not add a date or version number.
118118
The version heading is set later during the release process.
119119
- Use the same markdown formatting conventions as prior releases in the file.
120-
- If `gh` CLI is unavailable or not authenticated, fall back to git-only analysis
121-
(skip labeled-PR extraction) and warn the user that breaking-change and deprecation
122-
labels could not be checked.
120+
- Do not re-run `fetch.py`, `classify.py`, or `merge.py` directly; drive
121+
them through `draft-release-notes.py` so the three steps stay in sync.
122+
- If a PR's classification looks wrong, fix the bullet in `CHANGELOG.md`
123+
rather than hand-editing `decision.json`.
Lines changed: 16 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#!/bin/bash -e
22

3+
# this has mostly been replaced by .github/scripts/draft-release-notes/draft-release-notes.py
4+
# keeping this around as a backup since that script relies on Copilot CLI
5+
36
version=$("$(dirname "$0")/get-version.sh")
47

58
if [[ $version =~ ([0-9]+)\.([0-9]+)\.0 ]]; then
@@ -23,115 +26,29 @@ else
2326
range="v$major.$((minor - 1)).0..HEAD"
2427
fi
2528

26-
echo "# Changelog"
27-
echo
2829
echo "## Unreleased"
2930
echo
30-
31-
"$(dirname "$0")/extract-labeled-prs.sh" "$range"
32-
31+
echo "### Migration notes"
32+
echo
33+
echo
3334
echo "### 🌟 New javaagent instrumentation"
3435
echo
36+
echo
3537
echo "### 🌟 New library instrumentation"
3638
echo
39+
echo
3740
echo "### 📈 Enhancements"
3841
echo
42+
echo
3943
echo "### 🛠️ Bug fixes"
4044
echo
45+
echo
4146
echo "### 🧰 Tooling"
4247
echo
4348

44-
# Group commits by file type and @Deprecated detection
45-
declare -A src_main_commits
46-
declare -A no_src_main_commits
47-
declare -A deprecated_added_commits
48-
declare -A deprecated_removed_commits
49-
50-
format_commit_msg() {
51-
git log --format=%s -n 1 "$1" | sed -E 's, *\(#([0-9]+)\)$,\n ([#\1](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/\1)),'
52-
}
53-
54-
while IFS= read -r commit_hash; do
55-
files=$(git diff-tree --no-commit-id --name-only -r "$commit_hash")
56-
57-
has_src_main=false
58-
59-
while IFS= read -r file; do
60-
if [[ $file =~ /src/main/ ]] && [[ ! $file =~ ^smoke-tests/ ]] && [[ ! $file =~ ^smoke-tests-otel-starter/ ]] && [[ ! $file =~ /testing/ ]] && [[ ! $file =~ -testing/ ]] && [[ ! $file =~ ^instrumentation-docs/ ]]; then
61-
has_src_main=true
62-
break
63-
fi
64-
done <<< "$files"
65-
66-
commit_msg=$(git log --format=%s -n 1 "$commit_hash" | sed -E 's, *\(#([0-9]+)\)$,\n ([#\1](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/\1)),')
67-
68-
# Check diff for @Deprecated additions/removals in src/main Java files
69-
# Count added vs removed to distinguish real changes from moves/refactors
70-
diff_output=$(git diff-tree -p "$commit_hash" -- '*/src/main/**/*.java' 2>/dev/null || true)
71-
72-
added_count=$(echo "$diff_output" | grep -cP '^\+(?!\+).*@Deprecated' || true)
73-
removed_count=$(echo "$diff_output" | grep -cP '^-(?!-).*@Deprecated' || true)
74-
75-
# Net new @Deprecated annotations = new deprecation
76-
if [[ $added_count -gt $removed_count ]]; then
77-
deprecated_added_commits["$commit_hash"]=1
78-
fi
79-
# Net removed @Deprecated annotations = removed deprecated API (breaking)
80-
if [[ $removed_count -gt $added_count ]]; then
81-
deprecated_removed_commits["$commit_hash"]=1
82-
fi
83-
84-
# Categorize commit
85-
if [[ $has_src_main == true ]]; then
86-
src_main_commits["$commit_hash"]="$commit_msg"
87-
else
88-
no_src_main_commits["$commit_hash"]="$commit_msg"
89-
fi
90-
done < <(git log --reverse --perl-regexp --author='^(?!renovate\[bot\] )' --pretty=format:"%H" "$range")
91-
92-
# Output @Deprecated-based breaking changes (removed @Deprecated = removed deprecated API)
93-
if [[ ${#deprecated_removed_commits[@]} -gt 0 ]]; then
94-
echo "#### Possible breaking changes (diff removes @Deprecated)"
95-
echo
96-
for commit_hash in $(git log --reverse --perl-regexp --author='^(?!renovate\[bot\] )' --pretty=format:"%H" "$range"); do
97-
if [[ -n ${deprecated_removed_commits[$commit_hash]} ]]; then
98-
echo "- $(format_commit_msg "$commit_hash")"
99-
fi
100-
done
101-
echo
102-
fi
103-
104-
# Output @Deprecated-based deprecations (added @Deprecated = new deprecation)
105-
if [[ ${#deprecated_added_commits[@]} -gt 0 ]]; then
106-
echo "#### Possible deprecations (diff adds @Deprecated)"
107-
echo
108-
for commit_hash in $(git log --reverse --perl-regexp --author='^(?!renovate\[bot\] )' --pretty=format:"%H" "$range"); do
109-
if [[ -n ${deprecated_added_commits[$commit_hash]} ]]; then
110-
echo "- $(format_commit_msg "$commit_hash")"
111-
fi
112-
done
113-
echo
114-
fi
115-
116-
# Output grouped commits
117-
if [[ ${#src_main_commits[@]} -gt 0 ]]; then
118-
echo "#### Changes with src/main updates"
119-
echo
120-
for commit_hash in $(git log --reverse --perl-regexp --author='^(?!renovate\[bot\] )' --pretty=format:"%H" "$range"); do
121-
if [[ -n ${src_main_commits[$commit_hash]} ]] && [[ -z ${deprecated_added_commits[$commit_hash]} ]] && [[ -z ${deprecated_removed_commits[$commit_hash]} ]]; then
122-
echo "- $(format_commit_msg "$commit_hash")"
123-
fi
124-
done
125-
echo
126-
fi
127-
128-
if [[ ${#no_src_main_commits[@]} -gt 0 ]]; then
129-
echo "#### Changes without src/main updates"
130-
echo
131-
for commit_hash in $(git log --reverse --perl-regexp --author='^(?!renovate\[bot\] )' --pretty=format:"%H" "$range"); do
132-
if [[ -n ${no_src_main_commits[$commit_hash]} ]] && [[ -z ${deprecated_added_commits[$commit_hash]} ]] && [[ -z ${deprecated_removed_commits[$commit_hash]} ]]; then
133-
echo "- $(format_commit_msg "$commit_hash")"
134-
fi
135-
done
136-
echo
137-
fi
49+
git log --reverse \
50+
--perl-regexp \
51+
--author='^(?!renovate\[bot\] )' \
52+
--pretty=format:"- %s" \
53+
"$range" \
54+
| sed -E 's,\(#([0-9]+)\)$,\n ([#\1](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/\1)),'

0 commit comments

Comments
 (0)