diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2e168299..c0b71c74 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -26,9 +26,14 @@ is switched off, keeping any names it adds (one for each rejection added), or why it cannot be given. See "Before a pull request is reviewed" in CONTRIBUTING.md. Otherwise write "Not a code or schema fix". --> +## Changelog entry + + + ## Checklist - [ ] DCO sign-off on all commits (`git commit -s`) -- [ ] `CHANGELOG.md` updated (for any normative change) - [ ] Breaking changes marked with `` in spec text - [ ] Backward compatibility statement included (for breaking changes) diff --git a/.github/workflows/changelog-guard.yml b/.github/workflows/changelog-guard.yml new file mode 100644 index 00000000..54d693e0 --- /dev/null +++ b/.github/workflows/changelog-guard.yml @@ -0,0 +1,53 @@ +name: Changelog guard + +# CHANGELOG.md is written on main at merge by the merging Maintainer, from the +# entry the pull request proposes in its description (CONTRIBUTING.md, step 5 of +# a normative contribution). A pull request that edits the file conflicts with +# every other open pull request that does, so this check fails when the diff +# against the base touches it, unless a Maintainer has put the `changelog` label +# on the pull request (a release, or a correction to the file itself). It reads +# the diff and writes nothing, so it needs no write token and runs on +# pull_request, not pull_request_target. +on: + pull_request: + types: [opened, synchronize, reopened, labeled, unlabeled] + +permissions: + contents: read + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" + +concurrency: + group: changelog-guard-${{ github.ref }} + cancel-in-progress: true + +jobs: + guard: + name: Changelog guard + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + # Full history, so the merge base with the base branch is present. + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Fail if CHANGELOG.md is in the diff and the pull request is not labelled changelog + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + LABELLED: ${{ contains(github.event.pull_request.labels.*.name, 'changelog') }} + run: | + base="$(git merge-base "$BASE_SHA" "$HEAD_SHA")" + if git diff --name-only "$base" "$HEAD_SHA" | grep -qx 'CHANGELOG.md'; then + if [ "$LABELLED" = "true" ]; then + echo "CHANGELOG.md is in the diff and the pull request carries the changelog label." + else + echo "::error::CHANGELOG.md is in the diff. Put the entry in the description under '## Changelog entry' and leave the file to the merging Maintainer, or ask a Maintainer for the changelog label. See CONTRIBUTING.md, step 5 of a normative contribution." + exit 1 + fi + else + echo "CHANGELOG.md is not in the diff." + fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 10037bc3..5fbc3cdf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -99,7 +99,7 @@ review. "Before a pull request is reviewed" says what is. for two tables, the required flags. A wrong description stays green unless it omits a digest algorithm its field's pattern admits. For the rest, the README of the example set you touched, the docs page that describes the - behaviour, `CHANGELOG.md` and the description, assume nothing checks them + behaviour, the changelog entry and the description, assume nothing checks them unless you find the test, and read each after the last edit, counts and names first. @@ -180,7 +180,7 @@ Steps for a normative contribution: 2. Allow a minimum 5 business days for comment. Breaking changes, including anything touching wire format, cryptographic algorithms, or Trust Record required fields, carry a minimum 30-day comment period. See [Backward compatibility](GOVERNANCE.md#backward-compatibility) for the conditions under which a breaking change is considered at all. 3. Name the sponsoring organization in the PR. If a proposal is accepted without a sponsor, a Maintainer may sponsor and carry the PR, and the proposer is credited in the `CHANGELOG.md` entry. A normative PR opened without a sponsor is not rejected on that basis: reviewers will say so on the PR and either identify a sponsor or convert it to an informative change. 4. Submit the PR. Mark changed normative text with an HTML comment: ``. -5. Update `CHANGELOG.md`. +5. Put the `CHANGELOG.md` entry you propose in the pull request description under `## Changelog entry`, one bullet in the file's style, and do not edit `CHANGELOG.md` itself. The merging Maintainer writes the entry under `[Unreleased]` on `main` at merge, crediting the author, so open pull requests never conflict on the one file they would all touch. A pull request that has to carry the file, a release for instance, is labelled `changelog` by a Maintainer, and the `Changelog guard` check passes it on that label. 6. Breaking changes require Project Lead approval and an explicit backward-compatibility statement naming what breaks and what implementers must do. **No sponsor is required for** editorial changes, examples, conformance tests, tooling, schema changes tracking an already-merged spec change, and informative additions such as crosswalks and mappings to external schemas. Informative text carries no RFC 2119 keywords and binds no implementation, so it is the right home for a mapping that is still settling. Most contributions are in this set.