publish-npm #1
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
| name: publish-npm | |
| # Publishes the @metaobjectsdev/* npm packages (the full lockstep set) to npm, | |
| # in tier order (a dependency never lands after its dependent). Mirrors | |
| # publish-csharp.yml / publish-python.yml: tag-triggered or manual. | |
| # | |
| # WHICH packages, and in WHAT ORDER, is NOT decided here — `scripts/publish-set.mjs` | |
| # is the single source of truth, shared with the local `bun run release` path. This | |
| # workflow used to carry its own hardcoded list of 13 directories, which drifted from | |
| # that derivation: @metaobjectsdev/docs-site is a runtime dependency of | |
| # @metaobjectsdev/cli but was never in the list, so a release cut through here would | |
| # have shipped a cli pinning a docs-site version nobody published — an uninstallable | |
| # tarball, discoverable only by an external `npm install`. A list that is derived | |
| # cannot drift from the derivation. | |
| # | |
| # This publishes the versions ALREADY COMMITTED in each package.json at the tagged | |
| # commit — bump + commit them first (locally: `bun run release <ver>` does the | |
| # bump/build/verify/publish in one shot WITH a confirm gate; this workflow is the | |
| # hands-off CI alternative for the common tag-and-go case). The pre-publish | |
| # pack-verify here catches the stale-lockfile sibling-pinning bug before anything | |
| # ships. npm versions are immutable. | |
| # | |
| # One-time setup (repo Settings -> Secrets and variables -> Actions): | |
| # NPM_TOKEN = a granular automation token from npmjs.com with publish rights to | |
| # the @metaobjectsdev scope. (OIDC trusted publishing for npm can't drive | |
| # dist-tags yet, so a token is still the pragmatic choice for a lockstep set.) | |
| # | |
| # Triggers: a `npm-v*` tag (e.g. `npm-v0.12.6`), or manual workflow_dispatch. | |
| on: | |
| workflow_dispatch: | |
| push: | |
| tags: | |
| - 'npm-v*' | |
| jobs: | |
| publish: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Bun | |
| uses: oven-sh/setup-bun@v2 | |
| - name: Install + build | |
| run: | | |
| bun install --frozen-lockfile | |
| bun run build | |
| - name: Derive + verify the publish set | |
| run: node scripts/publish-set.mjs --check | |
| - name: Pack-verify the cli tarball pins siblings (no workspace:*) | |
| run: | | |
| set -euo pipefail | |
| VER=$(node -e "console.log(require('./server/typescript/packages/cli/package.json').version)") | |
| echo "publishing version: $VER" | |
| cd server/typescript/packages/cli && bun pm pack --destination /tmp/p | |
| PACKED=$(tar -xzOf /tmp/p/*.tgz package/package.json) | |
| echo "$PACKED" | node -e ' | |
| const p = JSON.parse(require("fs").readFileSync(0, "utf8")); | |
| const ver = p.version; | |
| const bad = Object.entries(p.dependencies || {}) | |
| .filter(([k, v]) => k.startsWith("@metaobjectsdev/") && v !== ver); | |
| if (bad.length) { console.error("stale/workspace sibling deps:", bad); process.exit(1); } | |
| console.log("packed deps OK: all @metaobjectsdev/* pinned to", ver); | |
| ' | |
| - name: Publish in tier order | |
| env: | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc | |
| trap 'rm -f ~/.npmrc' EXIT | |
| pub() { ( cd "$1" && bun publish --access public ) && echo " ✓ $1"; } | |
| # The set and its tier order (deps before dependents) come from the shared | |
| # derivation, never from a list maintained here. Written to a file first so a | |
| # non-zero exit fails the step instead of yielding a silently short list. | |
| node scripts/publish-set.mjs > "${RUNNER_TEMP}/publish-set.txt" | |
| mapfile -t DIRS < "${RUNNER_TEMP}/publish-set.txt" | |
| [ "${#DIRS[@]}" -gt 0 ] || { echo "::error::derived publish set is empty"; exit 1; } | |
| echo "publishing ${#DIRS[@]} packages in tier order" | |
| for d in "${DIRS[@]}"; do | |
| pub "$d" | |
| done |