Prepare and execute a release to pub.dev. Bumps versions, validates changelogs, checks pub.dev status, and guides through the tiered publishing process.
Prepare and execute a dart_node release. This is a multi-step process that publishes packages in tiers due to interdependencies.
Every version: in packages/*/pubspec.yaml is 0.0.0-dev at all times, on every branch. The real
version is stamped from the tag in the CI runner's working tree only at publish time and is never
committed, pushed, branched, or PR'd back. A release therefore produces ZERO churn on tracked source.
version: in source. Do NOT merge a "release" PR that changes a placeholder to a real version
— there is no such PR anymore, and per the spec it must be rejected in review.path: deps in source; tools/prepare_publish.dart <version> switches
them to ^<version> in-runner at publish time.$ARGUMENTS = version to release (e.g., 0.12.0-beta, 1.0.0)
If no version provided, show current versions and prompt for the target version.
git status
git branch --show-current
main branchpackages/*/pubspec.yaml version: must read 0.0.0-dev (placeholder). If any shows a real
version, that is churn — reset it to 0.0.0-dev before tagging.grep -h "^version:" packages/*/pubspec.yaml | head -20
dart run tools/prepare_publish.dart 2>&1 || true
This shows any packages missing from tools/lib/packages.dart.
Internal deps stay as local path: deps in source. Each tier workflow runs
dart run tools/prepare_publish.dart <version> in the runner working tree before publishing, which
stamps 0.0.0-dev → <version> and rewrites path: deps to ^<version>. Nothing is committed — the
runner is discarded after publish. You do not switch deps by hand and there is no release branch.
Every publishable package must have a ## $ARGUMENTS entry in its CHANGELOG.md.
Publishable packages (must have changelog entries):
| Tier | Package | |------|---------| | 1 | dart_logging | | 1 | dart_node_core | | 2 | reflux | | 2 | dart_node_express | | 2 | dart_node_ws | | 2 | dart_node_better_sqlite3 | | 2 | dart_node_sql_js | | 2 | dart_node_mcp | | 3 | dart_node_react | | 3 | dart_node_react_native |
Check each changelog:
VERSION="$ARGUMENTS"
for pkg in dart_logging dart_node_core reflux dart_node_express dart_node_ws dart_node_better_sqlite3 dart_node_sql_js dart_node_mcp dart_node_react dart_node_react_native; do
if grep -q "^## $VERSION" "packages/$pkg/CHANGELOG.md" 2>/dev/null; then
echo "✅ $pkg"
else
echo "❌ $pkg - missing ## $VERSION entry"
fi
done
If any are missing, stop and update the changelogs before proceeding.
Each package should have a README.md. Quick sanity check:
for pkg in dart_logging dart_node_core reflux dart_node_express dart_node_ws dart_node_better_sqlite3 dart_node_sql_js dart_node_mcp dart_node_react dart_node_react_native; do
if [[ -f "packages/$pkg/README.md" ]]; then
LINES=$(wc -l < "packages/$pkg/README.md")
echo "✅ $pkg - $LINES lines"
else
echo "❌ $pkg - missing README.md"
fi
done
Verify which versions are currently published:
for pkg in dart_logging dart_node_core reflux dart_node_express dart_node_ws dart_node_better_sqlite3 dart_node_sql_js dart_node_mcp dart_node_react dart_node_react_native; do
LATEST=$(curl -s "https://pub.dev/api/packages/$pkg" | grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4)
echo "$pkg: $LATEST"
done
Before releasing, ensure all tests pass:
./tools/test.sh
Or run specific tiers:
./tools/test.sh --tier 1
./tools/test.sh --tier 2
./tools/test.sh --tier 3
To see what the in-runner stamp will produce, run it locally then discard the changes (never commit):
dart run tools/prepare_publish.dart $ARGUMENTS
git restore packages # throw the stamp away — source must stay 0.0.0-dev
It stamps 0.0.0-dev → $ARGUMENTS and rewrites path: deps to ^$ARGUMENTS. CI does exactly this in the
runner working tree at publish time.
The release is triggered by pushing a tag:
git tag "Release/$ARGUMENTS"
git push origin "Release/$ARGUMENTS"
This triggers the publish-tier1 workflow which, in a single throwaway runner:
main and changelogs have ## $ARGUMENTSNo release branch, no commit, no PR — zero churn.
After tier 1 succeeds and packages are live on pub.dev (wait ~5 minutes for indexing):
git tag "Release-Tier2/$ARGUMENTS"
git push origin "Release-Tier2/$ARGUMENTS"
Publishes: reflux, dart_node_express, dart_node_ws, dart_node_better_sqlite3, dart_node_sql_js, dart_node_mcp
After tier 2 succeeds:
git tag "Release-Tier3/$ARGUMENTS"
git push origin "Release-Tier3/$ARGUMENTS"
Publishes: dart_node_react, dart_node_react_native
Each tier stamps in its own runner and publishes — nothing is committed, so there is no release branch to merge and no dependency "switch back" step. The release is complete once tier 3's packages are live.
After release, verify all packages are available:
VERSION="$ARGUMENTS"
for pkg in dart_logging dart_node_core reflux dart_node_express dart_node_ws dart_node_better_sqlite3 dart_node_sql_js dart_node_mcp dart_node_react dart_node_react_native; do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://pub.dev/api/packages/$pkg/versions/$VERSION")
if [[ "$HTTP_CODE" == "200" ]]; then
echo "✅ $pkg@$VERSION published"
else
echo "❌ $pkg@$VERSION not found (HTTP $HTTP_CODE)"
fi
done
If a package version already exists, the publish script skips it. This is safe.
Tier 2/3 packages depend on tier 1. Wait for tier 1 to be fully indexed on pub.dev before starting tier 2.
Add the missing ## X.Y.Z header to the package's CHANGELOG.md with release notes.
packages/*/pubspec.yaml version: is 0.0.0-dev (placeholder)path: deps (CI switches them in-runner)## $ARGUMENTS entriesRelease/$ARGUMENTS tag pushed (triggers tier 1)Release-Tier2/$ARGUMENTS tag pushedRelease-Tier3/$ARGUMENTS tag pushedversion: still 0.0.0-dev, no release branch/PR createdSearch for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer