Repository navigation
fix: declare the supertool dependency as supertool-cli (#1806) #1137
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: changelog | |
| on: | |
| pull_request: | |
| # One run per pull request at a time; a push supersedes the run before it (#962). This | |
| # workflow is `pull_request`-only, so unlike tests.yml there is no default-branch push run | |
| # to protect and the flag is unconditional -- if a `push:` trigger is ever added here, this | |
| # must become the same expression tests.yml uses, or a commit on the default branch will be | |
| # left with a cancelled run reporting neither pass nor fail. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| # Declared at workflow level rather than per job: every job here only reads the pull | |
| # request and reports, and a job added later should inherit read-only rather than fall | |
| # back to the repository default, which is read/write until somebody changes it. This | |
| # workflow runs scripts/assemble_changelog.py from the pull request's own checkout, so | |
| # a branch pull request in this repository runs contributor Python with that token (#32). | |
| permissions: | |
| contents: read | |
| # The actions used below are pinned to major tags rather than commit SHAs, for this | |
| # repository's own CI. Decided, not missed: .github/dependabot.yml watches github-actions | |
| # and raises the bumps in the open, and SHA pins here would have to be carried by hand | |
| # through every action release. This reasoning is NOT what the scaffolded workflow | |
| # (scripts/scaffold.py's CHANGELOG_WORKFLOW) uses any more -- #1462 pinned that template | |
| # to a SHA instead, because a repo the plugin scaffolds is not one whose own maintainer | |
| # necessarily notices a retagged action the way a repo with eyes on its own CI might. | |
| # The tool was vendored long before this gate existed, so the policy was written | |
| # down in a command doc and enforced by nobody. A convention with no check is a | |
| # convention until the first busy afternoon. | |
| jobs: | |
| fragment: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: "3.12" | |
| # The checker parses each fragment with markdown-it-py and refuses to fall | |
| # back to text scanning when it is absent -- it reports `skipped` and exits | |
| # non-zero rather than claiming anything. So the job has to install it; the | |
| # gate was added without this step and its first real run failed on the | |
| # parser rather than on a fragment. | |
| - name: Install the fragment parser | |
| run: python3 -m pip install --disable-pip-version-check markdown-it-py | |
| # No --dir/--changelog: the script finds the repo root by walking up | |
| # for .git (#20), the same default a human gets running the bare | |
| # command from the README. Passing the flags here would let this step | |
| # stay green through a regression in that default while every local | |
| # invocation broke -- which is how the previous bug went unnoticed. | |
| - name: Fragments parse and name a real section | |
| run: | | |
| python3 scripts/assemble_changelog.py --check | |
| # The gate that did not exist. `--check-links` has been implemented the | |
| # whole time and no leg ever called it, so its findings were printed only | |
| # when somebody typed the command -- and a check nothing calls reports | |
| # exactly what a clean file reports. Two releases shipped with every | |
| # `## [x.y.z]` heading rendering as literal bracketed text (#93). | |
| # | |
| # It also catches the failure a "does a definition exist" check cannot: | |
| # an `[Unreleased]` left comparing from a tag two releases back, which | |
| # resolves, returns a real diff, and shows shipped work as pending. | |
| # | |
| # `--untagged 0.1.0`: that section was never tagged and has no release | |
| # page, so a `releases/tag/v0.1.0` link would be a 404 that reads as a | |
| # working one. Declared here rather than in the script, which is vendored | |
| # into repositories whose release history is not ours. Remove it if | |
| # v0.1.0 is ever tagged; leaving it stale is caught by the audit itself, | |
| # which reports a declaration with no matching section. | |
| # | |
| # No --dir/--changelog, for the reason the step above gives. | |
| - name: Every release heading links somewhere true | |
| run: | | |
| python3 scripts/assemble_changelog.py --check-links --untagged 0.1.0 | |
| # A PR that changes what the plugin DOES must say so where users read it. | |
| # Docs, tests and CI changes are exempt by construction: the paths below are | |
| # the ones a user can observe from outside. | |
| # | |
| # The obvious next step -- reuse this same trigger to demand a docs_targets | |
| # diff -- was measured against this repository's last thirty merged pull | |
| # requests and REJECTED on the numbers (#164). It works here because the | |
| # requirement is unconditional over the trigger: every user-visible change | |
| # needs a fragment. It is not unconditional for the README, which most | |
| # product changes correctly leave alone. The counts live in | |
| # agents/developer.md section 5 and tests/test_docs_duty.py, in one place | |
| # each, deliberately not repeated here. That duty is observed in the agent's | |
| # report instead, as a per-path survey with a third state. | |
| - name: A user-visible change carries a fragment | |
| if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-changelog') }} | |
| # Passed through env, never interpolated into the script body: a `${{ }}` | |
| # expansion is textual substitution, so anything it carries becomes shell | |
| # source. A ref is attacker-influenced on a fork PR. | |
| env: | |
| BASE_REF: ${{ github.event.pull_request.base.ref }} | |
| run: | | |
| changed=$(git diff --name-only "origin/$BASE_REF"...HEAD) | |
| product=$(printf '%s\n' "$changed" | grep -E '^(scripts/|skills/|agents/|commands/|bin/|\.claude-plugin/)' || true) | |
| fragments=$(printf '%s\n' "$changed" | grep -E '^changelog\.d/[0-9]+\..+\.md$' || true) | |
| if [ -z "$product" ]; then | |
| echo "No user-visible paths changed — no fragment required." | |
| exit 0 | |
| fi | |
| if [ -n "$fragments" ]; then | |
| echo "Fragment present:" | |
| printf '%s\n' "$fragments" | |
| exit 0 | |
| fi | |
| echo "This PR changes what the plugin does:" >&2 | |
| printf '%s\n' "$product" >&2 | |
| echo >&2 | |
| echo "Add changelog.d/<issue>.<section>[.<slug>].md, or label the PR 'no-changelog' if" >&2 | |
| echo "the change is genuinely invisible to users. Sections are the Keep a" >&2 | |
| echo "Changelog headings, lowercased: added, changed, deprecated, removed," >&2 | |
| echo "fixed, security." >&2 | |
| exit 1 |