Skip to content

馃懛 Require a changelog entry when a package's lib changes - #2596

Open
dylanpulver wants to merge 2 commits into
cfug:mainfrom
dylanpulver:ci/require-changelog-entry
Open

馃懛 Require a changelog entry when a package's lib changes#2596
dylanpulver wants to merge 2 commits into
cfug:mainfrom
dylanpulver:ci/require-changelog-entry

Conversation

@dylanpulver

@dylanpulver dylanpulver commented Aug 20, 2026

Copy link
Copy Markdown

Closes #1662

The only guard today is the manual checkbox from #1679, which that PR called a temporary solution. It misses things: of the last 200 commits on main, 14 changed a package's lib without adding a line to that package's CHANGELOG.md, including #2464 and #2534.

This adds a Check changelog entry workflow and scripts/check_changelog_entry.sh. The script diffs the PR against its base, takes every changed path under <package>/lib/, and requires <package>/CHANGELOG.md to have gained at least one line; a changelog edit that only removes lines does not count. The package root comes from the path rather than a hardcoded list, so a new package is covered the day it is created, and a package with no CHANGELOG.md is exempt, which keeps dio_test and the example apps out of the way. Only paths under lib/ trigger it, so tests, docs, workflows and pubspec.yaml never do.

The escape hatch is a skip-changelog label rather than a magic string in the commit message, because only someone with write access can apply one, so the waiver stays a maintainer decision. The workflow listens to labeled/unlabeled, so applying it re-runs the check without a new push. The label does not exist in this repo yet; its name lives only in SKIP_CHANGELOG_LABEL.

Trigger is pull_request, not pull_request_target, so a fork PR runs in the fork's context with a read-only token: no secret is read, the job requests contents: read and nothing else, and nothing is posted back. No paths filter, on purpose, so it always reports a result and can be made a required check without blocking docs-only PRs.

I ran the script over the last 200 commits of main, treating each as a PR, against an independently written Python implementation of the same rule. The two agreed on all 200: 186 pass, 14 fail. #2591 (changed dio/lib, added a changelog line) passes; #2464 (changed dio/lib/src/options.dart, added only tests) fails; #2534, which changed both dio/lib and plugins/web_adapter/lib but updated only the web adapter changelog, fails naming dio alone. I reproduced a GitHub merge ref locally with git merge --no-ff and confirmed it fails on a missing entry, passes once added, passes with the label in the payload, and still fails when the changelog is touched but only loses a line. A label payload with shell metacharacters is parsed as JSON and stays inert. shellcheck clean; zizmor 1.29.0 with the repo's config and --persona=pedantic reports nothing for the new workflow or for .github as a whole.

Unverified: the workflow has never executed on GitHub Actions. Only a real run exercises the event payload, the merge commit actions/checkout leaves at HEAD, the sparse checkout of scripts, and the re-run on a label change; I emulated each locally. The first real evidence is this PR, which changes no package lib and should pass by finding nothing to require. I did not run melos run test or melos run analyze, since no Dart source is touched.

No CHANGELOG.md here, since AGENTS.md scopes that to packages the change touches and this touches none, matching workflow-only PRs such as #2587.

Claude (Opus) was used to design and implement this change and to run the local verification described above. I reviewed the result and own it.

Closes cfug#1662

Co-Authored-By: Claude <noreply@anthropic.com>
@dylanpulver
dylanpulver requested a review from a team as a code owner August 20, 2026 16:32
Comment on lines +8 to +10
on:
pull_request:
types: [ opened, reopened, synchronize, labeled, unlabeled ]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would a path glob filter be more effective here?

A pull request that changes no package `lib/` can never need an entry, so
filtering on paths keeps it from starting a job at all. Across the last 45
merged pull requests this skips 30 of them.

`paths` is matched against the whole pull request rather than the newest
push, so a pull request that touches `lib/` stays in scope while its
changelog entry is added and the check re-runs to pass.

Co-Authored-By: Claude <noreply@anthropic.com>
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.

Add CI step to ensure a changelog entry is added

2 participants