release-pub Skill
This skill is a specialized release workflow for Dart and Flutter packages published to pub.dev (including Dart CLI tools). It relies on a local helper script (release_helper) to safely manipulate pubspec.yaml and CHANGELOG.md.
Official Documentation
For detailed information on automated publishing, refer to the official Dart documentation: Automated publishing of packages to pub.dev
[!CAUTION] Immutable Tags: NEVER delete, modify, or re-push a Git tag that has already been published to pub.dev. Pub.dev entries are immutable. If a mistake is found after publishing, you must publish a new version (e.g., a patch release) instead of attempting to overwrite the existing tag.
Workflow Overview
Follow these steps precisely:
0. Initial Setup Verification (One-time only)
If this is the first time the package is being published via GitHub Actions, ensure the user has configured OIDC on pub.dev and added the workflow file:
-
Configure pub.dev:
- Advise the user to access
https://pub.dev/packages/<package_name>/admin. - Find the Automated publishing section and click Enable publishing from GitHub Actions.
- Recommend setting the Repository to the current repository (
owner/repo). - Set the Tag pattern:
- For single package repositories:
v{{version}}(recommended) or{{version}}. - For monorepos:
package_name-v{{version}}.
- For single package repositories:
- Security Hardening (Optional but recommended): Mention the Require GitHub Actions environment option to restrict publishing to specific users/approvers via GitHub Deployment Environments.
- Advise the user to access
-
Add GitHub Actions Workflow:
- Ensure
.github/workflows/publish.ymlexists with the following content (adjust the tag pattern if necessary):
- Ensure
name: Publish to pub.dev
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+*' # Align with the tag-pattern on pub.dev (e.g., 'v{{version}}')
jobs:
publish:
permissions:
id-token: write # Essential for OIDC authentication
uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1
# with:
# working-directory: path/to/package # Required if the package is not in the root
Wait for the user to confirm the setup is complete before proceeding with the first automated release.
1. Pre-release Checks
- Check if there are any uncommitted changes:
git status -s. If there are, tell the user to commit or stash them before proceeding. - README / Docs Update Check: Scan the recent changes. If there are new features or changed options, remind the user to check if
README.mdor other documentation needs updating before releasing. - CRITICAL: Run the appropriate formatter, analyzer, and tests based on the project type:
- For Dart packages:
dart format .,dart analyze, anddart test - For Flutter packages:
flutter format .(ordart format .),flutter analyze, andflutter test- [MANDATORY]: Resolve ALL analyzer issues (errors, warnings, and info level lints) before proceeding. Do not ignore "info" level issues unless they are explicitly documented as unavoidable.
- If there are unresolved issues, report them and ask the user to fix them (or offer to fix them if they are straightforward).
- For Dart packages:
- CRITICAL: Run the pre-publish dry-run:
- For Dart packages:
dart pub publish --dry-run - For Flutter packages:
flutter pub publish --dry-runIf warnings or errors appear (other than expected ones that can be ignored), report them and ask for user confirmation to proceed.
- For Dart packages:
2. Analyze Changes & Plan Release
- Find the last tag:
LAST_TAG=$(git tag --sort=-v:refname | head -1) - Analyze the commits since the last tag:
git log ${LAST_TAG}..HEAD --oneline - Determine if a tag prefix is used:
- Check the format of the
$LAST_TAG. If it starts withv(e.g.,v1.2.3), usevas the prefix for the new tag. - If no tags exist (first release), default to using the
vprefix (TAG_PREFIX="v"). - Otherwise, follow the existing pattern (no prefix if
$LAST_TAGis just a version string).
- Check the format of the
- Determine the bump type (
major,minor,patch) based on Conventional Commits:BREAKING CHANGE:or<type>!:-> major (or minor if version is< 1.0.0but follow the user's lead on pre-1.0.0 breaking changes).feat:-> minorfix:,docs:,chore:,refactor:,perf:etc. -> patch
- Generate markdown for
CHANGELOG.mdnotes describing the changes. Generate the notes entirely in English. DO NOT include the## [version] - [date]title header in the notes as the script adds that automatically. - Generate Preview (Dry-Run): Present a clear preview of the upcoming release to the user before making any file changes.
- Show the version bump recommendation (e.g.,
1.2.3 -> 1.3.0 (Recommended: feat=minor)) and offer other valid SemVer alternatives (e.g.1.2.4,2.0.0) in case they want a different bump. - List the categorized commits that will be included in the release.
- Show the preview text of the upcoming
CHANGELOG.mdentry.
- Show the version bump recommendation (e.g.,
3. Execution using Helper Script
Use the bundled Dart CLI script to apply changes safely. The script is located at .agents/skills/release-pub/scripts/release_helper.
-
Prepare Release (Version Bump & Changelog): Determine the bump type (
major,minor,patch) and the notes as analyzed in Step 2.dart run .agents/skills/release-pub/scripts/release_helper/bin/release_helper.dart prepare <type> --notes " ### Features - ... ### Bug Fixes - ... " -
Extract New Version: Read the
pubspec.yamlto find the newly updated version string (e.g.1.2.3). Let's call this$NEW_VERSION.
4. User Confirmation
Ask the user to confirm the prepared release based on the generated preview in Step 2:
- First present the version change options (e.g., "1.3.0 (Recommended)", "1.2.4", "2.0.0").
- Once the user chooses the version, proceed to update the files locally via Step 3.
- After running the execution helpers, show the user the Git diff (
git diff) and ask: "Ready to create release commit and tag (v$NEW_VERSION)?" Wait for explicit confirmation.
5. Git & GitHub Operations
Once the user confirms:
- Stage the files:
git add pubspec.yaml CHANGELOG.md - Commit:
Note: Do NOT add agit commit -m "chore: release v$NEW_VERSION"Co-Authored-Byline. This is a release commit, not a code contribution. - Tag:
git tag ${TAG_PREFIX}$NEW_VERSION - Push:
git push origin main git push origin ${TAG_PREFIX}$NEW_VERSION - Create GitHub Release:
Save the notes to a temporary file, e.g.,
/tmp/release_notes.md, then:gh release create ${TAG_PREFIX}$NEW_VERSION --title "${TAG_PREFIX}$NEW_VERSION" --notes-file /tmp/release_notes.md env -u GITHUB_TOKEN -u GH_TOKEN gh release create ${TAG_PREFIX}$NEW_VERSION --title "${TAG_PREFIX}$NEW_VERSION" --notes-file /tmp/release_notes.md rm /tmp/release_notes.md
Finally, report that the release is complete and that GitHub Actions will automatically handle pushing to pub.dev.
6. Monitor & Verify pub.dev Publishing
Do not stop after pushing the release. Actively monitor the automated publishing process until it completes, and promptly inform the user of the result:
-
Locate Workflow Run: After pushing the tag, GitHub Actions takes a few seconds to register the run. Query GitHub CLI to locate the run ID triggered for the release commit:
COMMIT_SHA=$(git rev-parse HEAD) RUN_ID="" for i in {1..10}; do RUN_ID=$(env -u GITHUB_TOKEN -u GH_TOKEN gh run list --workflow=publish.yml -c "$COMMIT_SHA" --json databaseId -q '.[0].databaseId') if [ -n "$RUN_ID" ]; then break fi sleep 3 done(If
--workflow=publish.ymldoes not yield results because a different workflow name or file is used, query without--workflow:env -u GITHUB_TOKEN -u GH_TOKEN gh run list -c "$COMMIT_SHA" --json databaseId,workflowName -q '.[0].databaseId'.) -
Watch Workflow Progress: Track the workflow run until it finishes:
env -u GITHUB_TOKEN -u GH_TOKEN gh run watch "$RUN_ID" --exit-status- If the workflow fails: Immediately fetch the error logs and alert the user:
Explain what failed (e.g., pub.dev authentication/OIDC, package verification error, or environment protection rules) and suggest how to resolve it.env -u GITHUB_TOKEN -u GH_TOKEN gh run view "$RUN_ID" --log-failed
- If the workflow fails: Immediately fetch the error logs and alert the user:
-
Verify Package on pub.dev: Once the workflow succeeds, verify that pub.dev is serving the newly released version:
PACKAGE_NAME=$(grep '^name:' pubspec.yaml | awk '{print $2}') for i in {1..10}; do STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pub.dev/packages/$PACKAGE_NAME/versions/$NEW_VERSION") if [ "$STATUS" = "200" ]; then break fi sleep 3 done -
Notify the User: As soon as the release is verified on pub.dev, report the successful completion to the user along with the direct pub.dev link: "🎉 Successfully published
$PACKAGE_NAMEv$NEW_VERSIONto pub.dev: https://pub.dev/packages/$PACKAGE_NAME/versions/$NEW_VERSION" -
Check pub.dev Score (Pana Analysis): Pub.dev automatically runs pana analysis upon publishing. Check the score and wait for the analysis of the new version to finish (typically 1–3 minutes). Poll the metrics API: Use the helper script to monitor pana analysis and verify the score:
PACKAGE_NAME=$(grep '^name:' pubspec.yaml | awk '{print $2}') # Poll until the newly released version has been analyzed by pub.dev for i in {1..30}; do ANALYZED_VERSION=$(curl -s "https://pub.dev/api/packages/$PACKAGE_NAME/metrics" | jq -r '.scorecard.packageVersion // empty') if [ "$ANALYZED_VERSION" = "$NEW_VERSION" ]; then break fi sleep 10 done dart run .agents/skills/release-pub/scripts/release_helper/bin/release_helper.dart check-scoreOnce analyzed, fetch the detailed score:
SCORE_JSON=$(curl -s "https://pub.dev/api/packages/$PACKAGE_NAME/metrics") GRANTED_POINTS=$(echo "$SCORE_JSON" | jq -r '.score.grantedPoints') MAX_POINTS=$(echo "$SCORE_JSON" | jq -r '.score.maxPoints')(Or explicitly specify the target version:
dart run .agents/skills/release-pub/scripts/release_helper/bin/release_helper.dart check-score $NEW_VERSION)- If full score (
$GRANTED_POINTS == $MAX_POINTS): Confirm all sections passed and tags (such asis:wasm-ready) are in place. - If score is deducted (
$GRANTED_POINTS < $MAX_POINTS): Extract and display the deducting sections and summaries:
Alert the user with the exact deductions so they can be addressed promptly. The command polls pub.dev metrics until the analysis ofecho "$SCORE_JSON" | jq -r '.scorecard.panaReport.report.sections[] | select(.grantedPoints < .maxPoints) | "### \(.title) (\(.grantedPoints)/\(.maxPoints))\n\(.summary)\n"'$NEW_VERSIONfinishes. It outputs: - Pub score (e.g.
160 / 160) - WASM compatibility status (
is:wasm-ready) - Platform support tags
- Exact deduction details and summaries (if points are deducted)
- Non-zero exit code (1) if score is deducted, or 0 on full points.
- If full score (
-
Notify the User: Report the successful publication along with the pub.dev score: Report the publication result to the user along with the pub.dev links:
- Pub.dev Version:
https://pub.dev/packages/$PACKAGE_NAME/versions/$NEW_VERSION - Pub.dev Score:
https://pub.dev/packages/$PACKAGE_NAME/score - Score Result:
- Full score: e.g. "🎉 Pub Score: $GRANTED_POINTS / $MAX_POINTS (Full points!)"
- Deducted score: e.g. "⚠️ Pub Score: $GRANTED_POINTS / $MAX_POINTS" along with the detailed causes of deduction.
- Deducted score: e.g. "⚠️ Pub Score: $GRANTED_POINTS / $MAX_POINTS" along with the detailed causes of deduction printed by
check-score.
- Pub.dev Version: