Skip to content

ci(pins): attach release notes to the template pins PR - #70

Merged
tsvikas merged 1 commit into
mainfrom
pins-pr-body
Sep 27, 2026
Merged

tsvikas merged 1 commit into
mainfrom
pins-pr-body

Conversation

@tsvikas

@tsvikas tsvikas commented Sep 27, 2026

Copy link
Copy Markdown
Owner

Follow-up to #67, from reviewing #69, the first PR the pins workflow opened.

Changes

  • One line per paragraph: the PR body was a hard-wrapped YAML | block, which GitHub rendered with ragged line breaks.
  • Release notes: new .github/scripts/pins_pr_body.py reads the pin changes from git diff (owner/repo, old tag → new tag), fetches every stable release in that range with gh release view, and writes one collapsed <details> per action with a compare link. create-pull-request now takes the body from body-path.
  • Upstream notes are quoted the way Dependabot does it: links go through redirect.github.com (no "mentioned this" backlinks upstream), bare #123 is rewritten to the upstream issue, and @user is put in a code span so no one is pinged. Each release's notes are blockquoted so their ## headings stay contained.
  • Size limits: notes are truncated at 5,000 characters per release, and the body stays under GitHub's 65,536-character limit.
  • Comment fix in both PR-creating workflows: a PR opened with GITHUB_TOKEN does get pull_request runs, 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

  • Ran the script against ⬆️ Update action pins in the template #69's real diff (setup-uv v10.0.1 → v10.1.0, codecov-action v7.0.0 → v7.1.1, alls-green v1.2.2 → v1.3.0). It produced 6.4 KB: three sections, codecov covering both v7.1.0 and v7.1.1, no unquoted @mentions, and no github.com/.../issues|pull/ links.
  • prek passes (ruff, black, actionlint, zizmor).
  • After merging, re-running the workflow updates ⬆️ Update action pins in the template #69's body in place.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GyMoD8tx4oz1bpJ1tEL845

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

release = json.loads(
gh("release", "view", tag, "-R", repo, "--json", "body,url")
)
notes = release["body"].strip() or "_No release notes._"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
notes = release["body"].strip() or "_No release notes._"
notes = (release.get("body") or "").strip() or "_No release notes._"

Comment on lines +84 to +97
releases = json.loads(
gh(
"release",
"list",
"-R",
repo,
"-L",
"100",
"--exclude-drafts",
"--exclude-pre-releases",
"--json",
"tagName",
)
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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"]
]

@tsvikas
tsvikas merged commit 3b666e5 into main Sep 27, 2026
5 checks passed
@tsvikas
tsvikas deleted the pins-pr-body branch September 27, 2026 12:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant