Skip to content

fix: declare the supertool dependency as supertool-cli (#1806) #1137

fix: declare the supertool dependency as supertool-cli (#1806)

fix: declare the supertool dependency as supertool-cli (#1806) #1137

Workflow file for this run

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