From 3b48b2ca5b694f610dd032ea47612d69b5539c6c Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Mon, 8 Jun 2026 09:57:00 +1000 Subject: [PATCH] Use 0.0.0-dev placeholder versions; stamp at publish (zero release churn) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enforce the Shipwright [SWR-VERSION-BUILD-STAMPING] contract: source version fields are placeholders and the real version is stamped from the tag in the CI runner working tree only — never committed, pushed, branched, or PR'd back. - Set every packages/*/pubspec.yaml version: to 0.0.0-dev (was 0.11.0-beta, and dart_node_sql_js was hard-coded at 0.13.0-beta). - Rewrite publish-tier1/2/3 workflows to stamp in-runner via prepare_publish and publish directly. Removes the release branch, the bot "prepare"/"switch to local" commits, and the "Create PR to main" step that bumped placeholders on main. Drop now-unneeded contents:write from tier1/tier3 (least privilege). - Document the placeholder contract in CLAUDE.md and the release skill; remove the stale "merge the release PR" / manual switch_deps steps. Releases now produce ZERO churn on tracked source. Inter-package deps stay as local path: deps in source and are rewritten to ^ in-runner at publish. --- .claude/skills/release/SKILL.md | 90 ++++++++----------- .github/workflows/publish-tier1.yml | 68 +++----------- .github/workflows/publish-tier2.yml | 11 +-- .github/workflows/publish-tier3.yml | 29 ++---- CLAUDE.md | 8 ++ packages/dart_jsx/pubspec.yaml | 2 +- packages/dart_logging/pubspec.yaml | 2 +- .../dart_node_better_sqlite3/pubspec.yaml | 2 +- packages/dart_node_core/pubspec.yaml | 2 +- packages/dart_node_coverage/pubspec.yaml | 2 +- packages/dart_node_express/pubspec.yaml | 2 +- packages/dart_node_mcp/pubspec.yaml | 2 +- packages/dart_node_react/pubspec.yaml | 2 +- packages/dart_node_react_native/pubspec.yaml | 2 +- packages/dart_node_sql_js/pubspec.yaml | 2 +- packages/dart_node_vsix/pubspec.yaml | 2 +- packages/dart_node_ws/pubspec.yaml | 2 +- packages/reflux/pubspec.yaml | 2 +- 18 files changed, 81 insertions(+), 151 deletions(-) diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index a1a716d..7f62519 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -1,6 +1,6 @@ --- name: release -description: Prepare and execute a release to pub.dev. Bumps versions, validates changelogs, checks pub.dev status, and guides through the tiered publishing process. +description: Prepare and execute a release to pub.dev. Stamps placeholder versions from the tag in-runner (zero source churn), validates changelogs, checks pub.dev status, and guides through the tiered publishing process. argument-hint: "" disable-model-invocation: true allowed-tools: Bash, Read, Grep, Glob, WebFetch @@ -10,6 +10,18 @@ allowed-tools: Bash, Read, Grep, Glob, WebFetch Prepare and execute a dart_node release. This is a multi-step process that publishes packages in tiers due to interdependencies. +## Version model — placeholders only ([SWR-VERSION-BUILD-STAMPING]) + +**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. + +- Do NOT bump `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. +- Inter-package deps stay as local `path:` deps in source; `tools/prepare_publish.dart ` switches + them to `^` in-runner at publish time. +- The only things committed for a release are **CHANGELOG** entries (release notes) — never version fields. + ## Arguments `$ARGUMENTS` = version to release (e.g., `0.12.0-beta`, `1.0.0`) @@ -25,8 +37,10 @@ git status git branch --show-current ``` -- Must be on `main` branch (or create a release branch) +- Must be on `main` branch - Working directory should be clean +- Every `packages/*/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. ### 1.2 Check current versions @@ -42,34 +56,12 @@ dart run tools/prepare_publish.dart 2>&1 || true This shows any packages missing from `tools/lib/packages.dart`. -## Step 2: Switch dependencies to pub.dev versions - -**CRITICAL**: Before publishing, all internal dependencies must point to pub.dev versions, NOT local paths! - -Use `tools/switch_deps.dart` to switch: - -```bash -# Switch from local paths to pub.dev versioned dependencies -dart run tools/switch_deps.dart release -``` - -This converts: -```yaml -# FROM (local development): -dart_node_core: - path: ../dart_node_core - -# TO (release): -dart_node_core: ^0.11.0-beta -``` - -To switch back to local for development: - -```bash -dart run tools/switch_deps.dart local -``` +## Step 2: Dependency switching is automatic (no action) -**Note**: The CI workflow handles this automatically on the release branch. +Internal deps stay as local `path:` deps in source. Each tier workflow runs +`dart run tools/prepare_publish.dart ` **in the runner working tree** before publishing, which +stamps `0.0.0-dev` → `` and rewrites `path:` deps to `^`. Nothing is committed — the +runner is discarded after publish. You do not switch deps by hand and there is no release branch. ## Step 3: Validate changelogs @@ -147,20 +139,17 @@ Or run specific tiers: ./tools/test.sh --tier 3 ``` -## Step 7: Bump versions (dry run) +## Step 7: Preview the stamp (optional, local dry run) -Preview what changes the prepare script will make: +To see what the in-runner stamp will produce, run it locally then **discard the changes** (never commit): ```bash dart run tools/prepare_publish.dart $ARGUMENTS +git restore packages # throw the stamp away — source must stay 0.0.0-dev ``` -This updates: -- `version:` in all pubspec.yaml files -- Interdependencies to use `^$ARGUMENTS` (pub.dev versions) -- Removes `publish_to: none` - -**Do NOT commit these changes yet** - they are made on a release branch by CI. +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. ## Step 8: Create release tag @@ -171,11 +160,12 @@ git tag "Release/$ARGUMENTS" git push origin "Release/$ARGUMENTS" ``` -This triggers the **publish-tier1** workflow which: -1. Creates a `release/$ARGUMENTS` branch -2. Runs `prepare_publish.dart` -3. Creates a PR to main -4. Publishes dart_logging and dart_node_core +This triggers the **publish-tier1** workflow which, in a single throwaway runner: +1. Validates the tag is on `main` and changelogs have `## $ARGUMENTS` +2. Stamps versions/deps in the working tree (NOT committed) +3. Publishes dart_logging and dart_node_core + +No release branch, no commit, no PR — zero churn. ## Step 9: Tier 2 publishing @@ -199,15 +189,8 @@ git push origin "Release-Tier3/$ARGUMENTS" Publishes: dart_node_react, dart_node_react_native -After tier 3 completes, it switches dependencies back to local and pushes to the release branch. - -## Step 11: Merge the release PR - -After all tiers complete successfully: - -1. Go to the PR created by tier 1 -2. Verify all packages are published on pub.dev -3. Merge the PR to main +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. ## Verification @@ -239,7 +222,8 @@ Add the missing `## X.Y.Z` header to the package's CHANGELOG.md with release not ## Checklist summary - [ ] On main branch, clean working directory -- [ ] **No local path dependencies** (all point to pub.dev versions) +- [ ] Every `packages/*/pubspec.yaml` `version:` is `0.0.0-dev` (placeholder) +- [ ] Internal deps are local `path:` deps (CI switches them in-runner) - [ ] All changelogs have `## $ARGUMENTS` entries - [ ] All READMEs are present and up-to-date - [ ] All tests pass @@ -249,4 +233,4 @@ Add the missing `## X.Y.Z` header to the package's CHANGELOG.md with release not - [ ] Tier 2 complete, packages live on pub.dev - [ ] `Release-Tier3/$ARGUMENTS` tag pushed - [ ] Tier 3 complete, packages live on pub.dev -- [ ] Release PR merged to main +- [ ] Source unchanged — every `version:` still `0.0.0-dev`, no release branch/PR created diff --git a/.github/workflows/publish-tier1.yml b/.github/workflows/publish-tier1.yml index ed0d47d..6204ef3 100644 --- a/.github/workflows/publish-tier1.yml +++ b/.github/workflows/publish-tier1.yml @@ -12,22 +12,19 @@ on: type: string permissions: - contents: write - pull-requests: write + contents: read id-token: write jobs: - prepare: + publish: runs-on: ubuntu-latest timeout-minutes: 10 - outputs: - version: ${{ steps.version.outputs.VERSION }} + environment: pub.dev-tier1 steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - name: Validate tag is on main (tag push only) if: github.event_name == 'push' @@ -89,59 +86,14 @@ jobs: with: dart-version: ${{ vars.DART_VERSION }} - - name: Prepare for publishing (switch to pub.dev deps) - run: dart run tools/prepare_publish.dart ${{ steps.version.outputs.VERSION }} - - - name: Create release branch and commit - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git checkout -b release/${{ steps.version.outputs.VERSION }} - git add -A - git commit -m "chore: prepare release ${{ steps.version.outputs.VERSION }} with pub.dev dependencies" - git push origin release/${{ steps.version.outputs.VERSION }} - - - name: Create PR to main - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh pr create \ - --title "Release ${{ steps.version.outputs.VERSION }}" \ - --body "## Release ${{ steps.version.outputs.VERSION }} - - This PR is auto-created from the release tag. - - **Do not merge yet!** Publishing happens in tier workflows (tier1 → tier2 → tier3). - - After tier3 completes, dependencies will be switched back to local and this PR will be updated. - - ### Publishing Tiers - - Tier 1: dart_logging, dart_node_core (this workflow) - - Tier 2: reflux, dart_node_express, dart_node_ws, dart_node_better_sqlite3, dart_node_mcp - - Tier 3: dart_node_react, dart_node_react_native" \ - --base main \ - --head release/${{ steps.version.outputs.VERSION }} - - publish: - needs: prepare - runs-on: ubuntu-latest - timeout-minutes: 10 - environment: pub.dev-tier1 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Checkout release branch - run: | - git fetch origin "release/${{ needs.prepare.outputs.version }}" - git checkout "release/${{ needs.prepare.outputs.version }}" - - - uses: ./.github/actions/setup - with: - dart-version: ${{ vars.DART_VERSION }} + # [SWR-VERSION-BUILD-STAMPING] Stamp the version from the tag in the + # runner working tree ONLY. Source stays 0.0.0-dev; nothing is committed, + # pushed, branched, or PR'd. The runner is discarded after publish, so a + # release produces ZERO churn on tracked source. + - name: Stamp versions in the working tree (not committed) + run: dart run tools/prepare_publish.dart "${{ steps.version.outputs.VERSION }}" - name: Publish packages run: | chmod +x .github/scripts/publish-packages.sh - .github/scripts/publish-packages.sh "${{ needs.prepare.outputs.version }}" dart_logging dart_node_core + .github/scripts/publish-packages.sh "${{ steps.version.outputs.VERSION }}" dart_logging dart_node_core diff --git a/.github/workflows/publish-tier2.yml b/.github/workflows/publish-tier2.yml index 8b6668c..dc17747 100644 --- a/.github/workflows/publish-tier2.yml +++ b/.github/workflows/publish-tier2.yml @@ -23,15 +23,16 @@ jobs: with: fetch-depth: 0 - - name: Checkout release branch - run: | - git fetch origin "release/${{ steps.version.outputs.VERSION }}" - git checkout "release/${{ steps.version.outputs.VERSION }}" - - uses: ./.github/actions/setup with: dart-version: ${{ vars.DART_VERSION }} + # [SWR-VERSION-BUILD-STAMPING] Stamp the version from the tag in the + # runner working tree ONLY. Source stays 0.0.0-dev; nothing is committed, + # pushed, or branched. The runner is discarded after publish. + - name: Stamp versions in the working tree (not committed) + run: dart run tools/prepare_publish.dart "${{ steps.version.outputs.VERSION }}" + - name: Publish packages run: | chmod +x .github/scripts/publish-packages.sh diff --git a/.github/workflows/publish-tier3.yml b/.github/workflows/publish-tier3.yml index 389aaa9..9ac0837 100644 --- a/.github/workflows/publish-tier3.yml +++ b/.github/workflows/publish-tier3.yml @@ -6,7 +6,7 @@ on: - 'Release-Tier3/[0-9]+.[0-9]+.[0-9]+*' permissions: - contents: write + contents: read id-token: write jobs: @@ -22,33 +22,18 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Checkout release branch - run: | - git fetch origin "release/${{ steps.version.outputs.VERSION }}" - git checkout "release/${{ steps.version.outputs.VERSION }}" - uses: ./.github/actions/setup with: dart-version: ${{ vars.DART_VERSION }} + # [SWR-VERSION-BUILD-STAMPING] Stamp the version from the tag in the + # runner working tree ONLY. Source stays 0.0.0-dev; nothing is committed, + # pushed, or branched. The runner is discarded after publish. + - name: Stamp versions in the working tree (not committed) + run: dart run tools/prepare_publish.dart "${{ steps.version.outputs.VERSION }}" + - name: Publish packages run: | chmod +x .github/scripts/publish-packages.sh .github/scripts/publish-packages.sh "${{ steps.version.outputs.VERSION }}" dart_node_react dart_node_react_native - - - name: Switch dependencies to local - run: dart run tools/switch_deps.dart local - - - name: Commit local dependencies to release branch - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --staged --quiet; then - echo "No changes to commit" - else - git commit -m "chore: switch to local dependencies after publish" - git push origin release/${{ steps.version.outputs.VERSION }} - fi diff --git a/CLAUDE.md b/CLAUDE.md index af519a5..b1bcb5c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -40,6 +40,14 @@ Dart packages for building Node.js apps. Strongly Typed Dart layer over JS inter - Use the template: .github/PULL_REQUEST_TEMPLATE.md - Only use git diff with main. Ignore commit messages +**Releases (Shipwright `[SWR-VERSION-BUILD-STAMPING]`)** +- Every `version:` in `packages/*/pubspec.yaml` MUST stay `0.0.0-dev` on every branch. The real version is + stamped from the git tag in the CI runner working tree at publish time — NEVER committed, pushed, branched, + or PR'd back. Releases produce ZERO source churn. +- ⛔️ ILLEGAL: bumping a `version:` in source, or a PR that turns a `0.0.0-dev` placeholder into a real version. +- Internal deps stay as local `path:` deps in source; `tools/prepare_publish.dart ` rewrites them + in-runner. Only CHANGELOG entries are committed for a release. + # Web & Translation - Optimize for AI Search and SEO diff --git a/packages/dart_jsx/pubspec.yaml b/packages/dart_jsx/pubspec.yaml index 9aa017e..6546865 100644 --- a/packages/dart_jsx/pubspec.yaml +++ b/packages/dart_jsx/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_jsx description: JSX transpiler for Dart - transforms JSX syntax to dart_node_react calls -version: 0.1.0 +version: 0.0.0-dev publish_to: none repository: https://github.com/user/dart_node/tree/main/packages/dart_jsx diff --git a/packages/dart_logging/pubspec.yaml b/packages/dart_logging/pubspec.yaml index d417a36..b38af80 100644 --- a/packages/dart_logging/pubspec.yaml +++ b/packages/dart_logging/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_logging description: A logging framework with structured logging, child loggers, and console output. -version: 0.11.0-beta +version: 0.0.0-dev repository: https://github.com/MelbourneDeveloper/dart_node environment: diff --git a/packages/dart_node_better_sqlite3/pubspec.yaml b/packages/dart_node_better_sqlite3/pubspec.yaml index 921f43b..0995225 100644 --- a/packages/dart_node_better_sqlite3/pubspec.yaml +++ b/packages/dart_node_better_sqlite3/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_node_better_sqlite3 description: Typed Dart bindings for better-sqlite3 npm package -version: 0.11.0-beta +version: 0.0.0-dev repository: https://github.com/MelbourneDeveloper/dart_node environment: diff --git a/packages/dart_node_core/pubspec.yaml b/packages/dart_node_core/pubspec.yaml index 2b5043b..d8eccc4 100644 --- a/packages/dart_node_core/pubspec.yaml +++ b/packages/dart_node_core/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_node_core description: Core JS interop utilities for dart_node packages -version: 0.11.0-beta +version: 0.0.0-dev repository: https://github.com/MelbourneDeveloper/dart_node environment: diff --git a/packages/dart_node_coverage/pubspec.yaml b/packages/dart_node_coverage/pubspec.yaml index 3b95537..1aeae09 100644 --- a/packages/dart_node_coverage/pubspec.yaml +++ b/packages/dart_node_coverage/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_node_coverage description: Coverage collection for Dart tests running on Node.js (dart2js) -version: 0.9.0-beta +version: 0.0.0-dev publish_to: none repository: https://github.com/MelbourneDeveloper/dart_node diff --git a/packages/dart_node_express/pubspec.yaml b/packages/dart_node_express/pubspec.yaml index 61f02a1..339e073 100644 --- a/packages/dart_node_express/pubspec.yaml +++ b/packages/dart_node_express/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_node_express description: Express.js bindings for Dart -version: 0.11.0-beta +version: 0.0.0-dev repository: https://github.com/MelbourneDeveloper/dart_node environment: diff --git a/packages/dart_node_mcp/pubspec.yaml b/packages/dart_node_mcp/pubspec.yaml index 5d2cb8c..0620d1c 100644 --- a/packages/dart_node_mcp/pubspec.yaml +++ b/packages/dart_node_mcp/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_node_mcp description: Typed Dart bindings for @modelcontextprotocol/sdk -version: 0.11.0-beta +version: 0.0.0-dev repository: https://github.com/MelbourneDeveloper/dart_node environment: diff --git a/packages/dart_node_react/pubspec.yaml b/packages/dart_node_react/pubspec.yaml index 552cbbd..d82d9e3 100644 --- a/packages/dart_node_react/pubspec.yaml +++ b/packages/dart_node_react/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_node_react description: React bindings for Dart -version: 0.11.0-beta +version: 0.0.0-dev repository: https://github.com/MelbourneDeveloper/dart_node environment: diff --git a/packages/dart_node_react_native/pubspec.yaml b/packages/dart_node_react_native/pubspec.yaml index f71ba96..85c17bc 100644 --- a/packages/dart_node_react_native/pubspec.yaml +++ b/packages/dart_node_react_native/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_node_react_native description: React Native bindings for Dart -version: 0.11.0-beta +version: 0.0.0-dev repository: https://github.com/MelbourneDeveloper/dart_node environment: diff --git a/packages/dart_node_sql_js/pubspec.yaml b/packages/dart_node_sql_js/pubspec.yaml index 1595404..6a82fb2 100644 --- a/packages/dart_node_sql_js/pubspec.yaml +++ b/packages/dart_node_sql_js/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_node_sql_js description: Typed Dart bindings for sql.js (SQLite compiled to WebAssembly) -version: 0.13.0-beta +version: 0.0.0-dev repository: https://github.com/MelbourneDeveloper/dart_node environment: diff --git a/packages/dart_node_vsix/pubspec.yaml b/packages/dart_node_vsix/pubspec.yaml index 5f6bb25..6c4eac1 100644 --- a/packages/dart_node_vsix/pubspec.yaml +++ b/packages/dart_node_vsix/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_node_vsix description: VSCode extension API bindings for Dart via dart:js_interop -version: 0.1.0 +version: 0.0.0-dev publish_to: none environment: diff --git a/packages/dart_node_ws/pubspec.yaml b/packages/dart_node_ws/pubspec.yaml index 56f6ca4..a5588d9 100644 --- a/packages/dart_node_ws/pubspec.yaml +++ b/packages/dart_node_ws/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_node_ws description: WebSocket bindings for Dart on Node.js -version: 0.11.0-beta +version: 0.0.0-dev repository: https://github.com/MelbourneDeveloper/dart_node environment: diff --git a/packages/reflux/pubspec.yaml b/packages/reflux/pubspec.yaml index 467dee7..9319f90 100644 --- a/packages/reflux/pubspec.yaml +++ b/packages/reflux/pubspec.yaml @@ -1,6 +1,6 @@ name: reflux description: Predictable state container for Dart apps - inspired by Redux patterns -version: 0.11.0-beta +version: 0.0.0-dev repository: https://github.com/MelbourneDeveloper/dart_node environment: