Repository navigation
ci(pins): attach release notes to the template pins PR - #70
Conversation
The PR body was a hard-wrapped YAML block, which GitHub rendered as ragged lines, and carried no release notes to review the bumps against. A new script reads the pin changes from `git diff` and writes the body, one line per paragraph, with the notes of every release in each bumped range in a collapsed <details>. The notes are quoted as Dependabot does: links go through redirect.github.com, bare #123 points upstream, and @mentions sit in code spans, so upstream gets no pings or backlinks. Also correct the dispatch-step comment: a PR opened with GITHUB_TOKEN does get `pull_request` runs, but they are held for approval. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GyMoD8tx4oz1bpJ1tEL845
There was a problem hiding this comment.
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path=".github/scripts/pins_pr_body.py" line_range="109" />
<code_context>
+ release = json.loads(
+ gh("release", "view", tag, "-R", repo, "--json", "body,url")
+ )
+ notes = release["body"].strip() or "_No release notes._"
+ if len(notes) > MAX_NOTES:
+ notes = (
</code_context>
<issue_to_address>
**issue (bug_risk):** `release["body"].strip()` raises `AttributeError` when GitHub returns a null release body, so the script fails instead of using `_No release notes._`; the exception is not caught by the `CalledProcessError` handler.
**Triggers:** When a stable release has no release-body text.
**Suggested fix:** Use `(release.get("body") or "").strip()` before applying the empty-notes fallback.
```suggestion
notes = (release.get("body") or "").strip() or "_No release notes._"
```
</issue_to_address>
### Comment 2
<location path=".github/scripts/pins_pr_body.py" line_range="84-97" />
<code_context>
+ "-R",
+ repo,
+ "-L",
+ "100",
+ "--exclude-drafts",
+ "--exclude-pre-releases",
+ "--json",
+ "tagName",
+ )
+ )
+ lo, hi = version_key(old), version_key(new)
+ tags = sorted(
+ (r["tagName"] for r in releases if lo < version_key(r["tagName"]) <= hi),
+ key=version_key,
+ reverse=True,
</code_context>
<issue_to_address>
**issue (bug_risk):** The script lists at most 100 releases, so releases older than the newest 100 are omitted even when they fall between the old and new pin versions; the generated PR body then falsely claims to contain every release's notes.
**Triggers:** When an action has more than 100 releases and the pin range extends beyond the newest 100 releases.
**Suggested fix:** Paginate the release list or request enough releases to cover the old tag through the new tag instead of imposing `-L 100`.
```suggestion
releases = [
{"tagName": release["tag_name"]}
for page in json.loads(
gh(
"api",
"--paginate",
"--slurp",
f"repos/{repo}/releases",
)
)
for release in page
if not release["draft"] and not release["prerelease"]
]
```
</issue_to_address>Sourcery assessment
Approval pending. 2 findings to address first.
Blocking findings: .github/scripts/pins_pr_body.py:109, .github/scripts/pins_pr_body.py:97
| release = json.loads( | ||
| gh("release", "view", tag, "-R", repo, "--json", "body,url") | ||
| ) | ||
| notes = release["body"].strip() or "_No release notes._" |
There was a problem hiding this comment.
issue (bug_risk): release["body"].strip() raises AttributeError when GitHub returns a null release body, so the script fails instead of using _No release notes._; the exception is not caught by the CalledProcessError handler.
Triggers: When a stable release has no release-body text.
Suggested fix: Use (release.get("body") or "").strip() before applying the empty-notes fallback.
| notes = release["body"].strip() or "_No release notes._" | |
| notes = (release.get("body") or "").strip() or "_No release notes._" |
| releases = json.loads( | ||
| gh( | ||
| "release", | ||
| "list", | ||
| "-R", | ||
| repo, | ||
| "-L", | ||
| "100", | ||
| "--exclude-drafts", | ||
| "--exclude-pre-releases", | ||
| "--json", | ||
| "tagName", | ||
| ) | ||
| ) |
There was a problem hiding this comment.
issue (bug_risk): The script lists at most 100 releases, so releases older than the newest 100 are omitted even when they fall between the old and new pin versions; the generated PR body then falsely claims to contain every release's notes.
Triggers: When an action has more than 100 releases and the pin range extends beyond the newest 100 releases.
Suggested fix: Paginate the release list or request enough releases to cover the old tag through the new tag instead of imposing -L 100.
| releases = json.loads( | |
| gh( | |
| "release", | |
| "list", | |
| "-R", | |
| repo, | |
| "-L", | |
| "100", | |
| "--exclude-drafts", | |
| "--exclude-pre-releases", | |
| "--json", | |
| "tagName", | |
| ) | |
| ) | |
| releases = [ | |
| {"tagName": release["tag_name"]} | |
| for page in json.loads( | |
| gh( | |
| "api", | |
| "--paginate", | |
| "--slurp", | |
| f"repos/{repo}/releases", | |
| ) | |
| ) | |
| for release in page | |
| if not release["draft"] and not release["prerelease"] | |
| ] |
Follow-up to #67, from reviewing #69, the first PR the pins workflow opened.
Changes
|block, which GitHub rendered with ragged line breaks..github/scripts/pins_pr_body.pyreads the pin changes fromgit diff(owner/repo, old tag → new tag), fetches every stable release in that range withgh release view, and writes one collapsed<details>per action with a compare link.create-pull-requestnow takes the body frombody-path.redirect.github.com(no "mentioned this" backlinks upstream), bare#123is rewritten to the upstream issue, and@useris put in a code span so no one is pinged. Each release's notes are blockquoted so their##headings stay contained.GITHUB_TOKENdoes getpull_requestruns, but they are held for approval (seen on ⬆️ Update action pins in the template #69), so the dispatch step is what runs the tests.Verification
@mentions, and nogithub.com/.../issues|pull/links.🤖 Generated with Claude Code
https://claude.ai/code/session_01GyMoD8tx4oz1bpJ1tEL845