Publish from a tag instead of from a laptop - #8
Open
sovanken wants to merge 3 commits into
Open
Conversation
Releasing currently means running `flutter pub publish --force` on the
maintainer's machine against a long-lived refresh token in
%APPDATA%/dart/pub-credentials.json. That token is the only thing standing
between a laptop and the package's users, the release is not reproducible,
and there is no record of what was verified before it went out. It also
means a release can only happen where that credential lives.
.github/workflows/release.yml replaces it. Pushing a vX.Y.Z tag:
1. verifies the tag matches pubspec.yaml — a mismatch would publish the
wrong version under the right name, which pub.dev cannot undo;
2. verifies CHANGELOG.md has a section for it, so release notes are the
changelog rather than a second copy that drifts (v1.1.0 previously sat
tagged-but-unreleased for twelve days because that step was manual);
3. runs format, analyze, tests, `pub publish --dry-run` and pana;
4. publishes with a short-lived OIDC token (id-token: write), so no
credential exists outside the run;
5. creates the GitHub Release from the changelog section.
ci.yml gains a "Package & pub.dev score" job so packaging and score
problems surface on the PR rather than at tag time, when the version is
already public.
tool/assert_pana_score.py exists because `pana --exit-code-threshold 0` is
not trustworthy on its own: running it locally, pana aborted with a Windows
sandbox path error and still exited 0. The script reads pana's JSON, treats
a missing score as failure, and names the section that lost points. Both
workflows also install the webp tools first — pana needs them to process
the three screenshots pubspec declares, and without them it logs SEVERE and
can lose points pub.dev would grant.
The gate is set at full marks because the package currently scores 160/160.
Verified: tag guard accepts v1.2.0 and rejects v9.9.9; the changelog
extractor returns the 1.2.0 section and exits non-zero for a missing
version; the score asserter passes at 160/160, fails at 150/160 naming the
section, and fails on a crashed run that reported no score. The pana run
itself is verified by this PR's own CI, since Windows cannot run it.
Requires one pub.dev setting before the next release: Admin -> Automated
publishing -> repository sovanken/solar_iconkit, tag pattern v{{version}}.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Not for merge — identifies why a fresh Linux checkout reports analysis_options.yaml as modified when the local tree is clean.
The new packaging gate failed on its first run, and not on the score: `pub publish --dry-run` exits 65 because a fresh Linux checkout reports analysis_options.yaml and example/analysis_options.yaml as modified. Cause: Flutter rewrites both files during `pub get`. 3.47.2 (current stable, used by the unpinned job) adds `- build/**` to the root analyzer exclude and prepends a whole `analyzer: exclude: [build/**, android/**]` block to the example's. Flutter 3.41.4 locally does not, which is why the tree looked clean on my machine and why this never showed up before. Fixed by committing exactly what Flutter writes, byte-for-byte, so it has nothing left to add and the tree stays clean on any version. Excluding build/ and android/ from analysis is correct regardless. Worth noting this would have broken the next release rather than just this PR: release.yml runs the same --dry-run step. It went unnoticed until now only because publishing was done by hand with --force, which skips exactly this validation. Also removes the temporary diagnostic step that found it.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Releasing today means running
flutter pub publish --forceon the maintainer's machine against a long-lived refresh token in%APPDATA%/dart/pub-credentials.json. That token is the only thing between a laptop and the package's users, the release isn't reproducible, and there's no record of what was verified before it shipped.This makes the tag the trigger.
The flow
Step 1 exists because a mismatched tag would publish the wrong version under the right name, which pub.dev cannot undo. Step 2 exists because
v1.1.0sat tagged-but-unreleased for twelve days when that step was manual.ci.ymlalso gains a Package & pub.dev score job, so packaging and score problems surface on the PR instead of at tag time when the version is already public.Why
assert_pana_score.pyrather than--exit-code-threshold 0I tried to validate the gate locally first. pana aborted — its sandbox runner rejects Windows drive-letter paths, and
webpinfowas missing — and still exited 0. A gate that passes when the analysis crashed is worse than no gate, and would have looked identical to a working one.So the score is asserted from pana's JSON instead: a missing score is a failure, and the section that lost points is named in the log. Both workflows install the
webptools first, since pana needs them to process the three screenshotspubspec.yamldeclares — without them it logs SEVERE and can lose points pub.dev itself would grant.The gate is set at full marks because the package currently scores 160/160.
Verified
v1.2.0accepted,v9.9.9rejectedpub publish --dry-runThat last row is the point of opening this as a PR: the new
packagejob runs pana in the environment that actually matters, so if 160/160 doesn't hold on Ubuntu, this PR goes red before it merges rather than breaking your next release.One-time setup needed before the next release
pub.dev → Admin → Automated publishing → repository
sovanken/solar_iconkit, tag patternv{{version}}. Until that's set the publish step will fail; every verification step before it works regardless. If you also enable "Require GitHub Actions environment" there, addenvironment: pub.devto the job to match.Type of change
No packaged code changes.
🤖 Generated with Claude Code