Skip to content

feat: check-tool-versions reads adopter-owned pins from an optional scripts/tool-pins.extra - #112

Merged
TzuH-Hsu merged 3 commits into
mainfrom
chore/107-extra-tool-pins
Sep 29, 2026
Merged

TzuH-Hsu merged 3 commits into
mainfrom
chore/107-extra-tool-pins

Conversation

@TzuH-Hsu

Copy link
Copy Markdown
Owner

Summary

scripts/check-tool-versions.sh watched only the five pins in scripts/install-ci-tools.sh. An adopter that pins another tool (two downstream repositories pin syft, one also uv and pnpm) had two options: edit both kit files, which creates local changes every upstream sync must preserve, or leave the pin unwatched.

The check now also reads an optional, adopter-owned plain-data file, scripts/tool-pins.extra:

# tool   version   source
syft     1.52.0    github:anchore/syft
uv       0.12.19   pypi:uv
pnpm     12.6.0    npm:pnpm
  • The template ships no such file, and upstream syncs never touch it, so adding a tool edits neither kit script. When the file is absent, the check behaves exactly as today.
  • Supported sources are github:<owner>/<repo> (any non-numeric tag prefix is stripped), npm:<package> and pypi:<package>.
  • A malformed row or an unknown source is an ERROR, never a skip, matching the script's own rule. hold_reason applies to these rows as it does to kit tools.
  • Installing those tools stays with the adopter: their install step reads the version from the same file (for example with awk '$1 == "syft" { print $2 }').
  • The JSON fetch timeout goes from 30 s to 90 s. PyPI's JSON for a large project is about 5 MB (uv's is 4.9 MB), and cold fetches from here took 45–63 s. This is a weekly job, and a timeout is still reported as an ERROR.

Docs: skills/github-actions-hygiene rule 10 and the docs/template/architecture.md ledger row mention the file.

Related issue

Closes #107

Validation

Pick validation depth by blast radius. A level that applies but could not run becomes a RISK: line below. See AGENTS.md for the validation ladder.

  • L0 static — make lint; bash -n and shellcheck on the script
  • L1 unit — make check
  • L2 integration — live runs against upstream:
    • rows for syft (GitHub), pip-licenses (PyPI) and pnpm (npm), with comments and blank lines: all ok, exit 0;
    • a stale pin → DRIFT; a two-field row → ERROR (… expected '<tool> <version> <source>'); gitlab:x/y → ERROR (lookup failed), exit 1;
    • no extra file → unchanged output.
  • L3 e2e / preview — the next weekly Maintenance run exercises it in CI

Risk / rollback

RISK: L3 not run — the uv row itself hit the old 30 s timeout on this machine's network; pip-licenses exercised the same pypi path to completion
Rollback: revert this PR; the extra file is ignored again and the timeout returns to 30 s

Checklist

Before merging:

  • Conventional Commit PR title (<type>: <description>)
  • Linked issue using "Closes #N"
  • No secrets, no *.local.md files committed
  • Documentation updated where affected

…cripts/tool-pins.extra

Adopters that pin tools the template does not ship (syft, uv, pnpm, ...)
had to edit install-ci-tools.sh and check-tool-versions.sh — local changes
every upstream sync must preserve — or leave the pin unwatched. The drift
check now also reads an optional, adopter-owned plain-data file, one row
per tool: `<tool> <version> <source>`, with github:<owner>/<repo>,
npm:<pkg> or pypi:<pkg> as the source. The template ships no such file.
Malformed rows and unknown sources are errors, never skips; holds work
the same as for kit tools. The JSON fetch timeout goes from 30 s to 90 s:
PyPI's JSON for a large project is ~5 MB and a cold fetch took 45-63 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@TzuH-Hsu TzuH-Hsu added the area:ci CI workflows and automation label Sep 28, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-28T16:45:11.015102Z ddf9460 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 65529ad723

ℹ️ 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".

Comment thread scripts/check-tool-versions.sh
…in the script's report, not a cancel

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 35bd951237

ℹ️ 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".

Comment thread scripts/check-tool-versions.sh
…icated, instead of gh api

`gh api` has no request timeout, so a stalled call could outlast the job
before the script reported its lookup error. Every GitHub lookup now uses
the same bounded curl path as the registries, with GH_TOKEN, GITHUB_TOKEN
or a logged-in gh's token as a bearer header (passed on a file descriptor,
not in argv), keeping the authenticated rate limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@TzuH-Hsu TzuH-Hsu added this to the v0.6.0 milestone Sep 29, 2026
@TzuH-Hsu
TzuH-Hsu merged commit 5e37f0b into main Sep 29, 2026
1 check passed
@TzuH-Hsu
TzuH-Hsu deleted the chore/107-extra-tool-pins branch September 29, 2026 06:57
@github-actions github-actions Bot mentioned this pull request Sep 29, 2026
TzuH-Hsu added a commit that referenced this pull request Sep 29, 2026
… Markdown (#113)

<!-- markdownlint-disable-next-line MD041 -->
## Summary

`scripts/pr-lint.js` decided which issues a PR closes by re-implementing
CommonMark block parsing. Nine review rounds on #96 still left it
disagreeing with GitHub whenever containers nest, and three more simple
cases turned up after v0.5.7. GitHub already exposes the ground truth:
the pull request's `closingIssuesReferences` field, read with
`excludeUserLinked: true` so it lists exactly the issues the body
closes, with keywords in prose as GitHub renders them.

- `run()` reads that field over GraphQL. `lint()` checks the result:
exactly one issue, in this repository, the branch's, and still open.
Branch grammar and the bot exemptions are unchanged.
- The Markdown scanner and its tests are gone (−260 lines net).
- **The field lags a body edit.** I measured this by editing a PR body
and polling: 1–9 s. The lint therefore reads until two reads 5 s apart
agree, at most five times. Typical cost is one extra read and a 5 s
wait.
- Because the lint reads live state, "Re-run jobs" now re-checks the
current body. The `ci.yml` comment that said it replays the old payload
is corrected.
- Messages follow GitHub's semantics. A number that is a pull request,
or a deleted issue, now shows up as "closes no issue", because GitHub
closes nothing for it.

**Checking GitHub against CommonMark.** I put the three cases deferred
to this issue into an open PR's body and read the field:

| Body | GitHub | CommonMark | v0.5.7 scanner |
| --- | --- | --- | --- |
| `Heading\n=======\n Closes #104` | not closed | not closed | closed |
| `- ```\n  Closes #104\n  ```` | not closed | not closed | closed |
| `- item\n > quote\n # heading\n\n Closes #107` | closed | closed | not
closed |

**Sidebar links do not count.** Adding or removing an issue link in the
PR's sidebar fires no `pull_request` event, so accepting it would let a
green check go stale; the lint reads only what the body closes, and
AGENTS.md step 4, `skills/pr-authoring` rule 5 and the architecture
ledger say so. Before the first read, the lint also waits until the
triggering edit is 15 s old, so two agreeing reads cannot both be stale.

**Downstream:** the kit's `pr-lint.js` and `pr-lint.test.js` change
shape completely; adopters replace both files whole. The workflow step
is unchanged, and so are the job permissions: `issues: read` and
`pull-requests: read` were already there.

## Related issue

Closes #104

## Validation

Pick validation depth by blast radius. A level that applies but could
not run becomes a `RISK:` line below. See AGENTS.md for the validation
ladder.

- [x] L0 static — `make lint`
- [x] L1 unit — `make check` (`scripts/pr-lint.test.js`, 14 pass: lint
rules, settle logic with a lagging and a flapping field, `run()` with a
GraphQL stub, 403 and other failures)
- [x] L2 integration — the real query through `closingRefs()` against
live PRs here: #111 → [105], #112 → [107], #91 → [88, CLOSED], release
PR #94 → []
- [x] L3 e2e / preview — this PR's own `ci` run executes the new lint
against this PR

## Risk / rollback

```text
RISK: L2 not run on a private repository — GraphQL under the ci job's issues/pull-requests read scopes is expected to work as the REST read did, but is untested; the first private adopter run will show it, and a refusal fails the check with the scopes named
Rollback: revert this PR; the Markdown scanner returns
```

## Checklist

Before merging:

- [x] Conventional Commit PR title (`<type>: <description>`)
- [x] Linked issue using "Closes #N"
- [x] No secrets, no `*.local.md` files committed
- [x] Documentation updated where affected

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
TzuH-Hsu pushed a commit that referenced this pull request Sep 29, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.6.0](v0.5.8...v0.6.0)
(2026-09-29)


### Features

* check-tool-versions reads adopter-owned pins from an optional
scripts/tool-pins.extra
([#112](#112))
([5e37f0b](5e37f0b))
* pr-lint asks GitHub which issues a PR closes instead of parsing
Markdown
([#113](#113))
([02b4138](02b4138))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ci CI workflows and automation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Task: check-tool-versions reads adopter-local tool pins, so local tools get drift detection without editing kit files

1 participant