This repository was archived by the owner on Aug 25, 2026. It is now read-only.
TML-2501: declare Supabase roles via a standalone PSL role block (#957) #1208
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 to npm | |
| # Source-of-truth model: | |
| # The version comes from the root `package.json` `version` field. | |
| # Maintainers advance it via `pnpm bump-minor` (see | |
| # docs/oss/versioning.md). This workflow can never publish a version | |
| # other than what is committed at HEAD. | |
| # | |
| # Trigger model: | |
| # - push to `main` with the root `version` unchanged → publish | |
| # `<base>-dev.N` under dist-tag `dev`. | |
| # - push to `main` with the root `version` changed → publish `<base>` | |
| # under dist-tag `latest` and create a GitHub Release. This is how a | |
| # merged `chore(release): ...` PR auto-ships. | |
| # - workflow_dispatch → publish `<base>` | |
| # under the chosen dist-tag (default `latest`); also the dry-run path. | |
| on: | |
| push: | |
| branches: [main] | |
| tags: ["!**"] | |
| workflow_dispatch: | |
| inputs: | |
| dist-tag: | |
| description: "npm dist-tag (e.g., latest, dev, beta)" | |
| required: true | |
| default: "latest" | |
| type: string | |
| dry-run: | |
| description: "Dry-run only (build + pack + lint, no npm publish, no GitHub Release)." | |
| required: false | |
| default: true | |
| type: boolean | |
| concurrency: | |
| group: npm-publish | |
| cancel-in-progress: false | |
| jobs: | |
| publish: | |
| name: Publish packages to npm | |
| runs-on: ubuntu-latest | |
| # Only `main` may produce a real publish. A dry-run dispatch is permitted | |
| # from any branch so maintainers can validate the pipeline (build + pack + | |
| # publish-dep gate + `pnpm publish --dry-run`) before merging changes that | |
| # touch publishing. The dry-run path performs no registry writes and skips | |
| # the GitHub Release step, so non-main runs cannot affect production state. | |
| if: ${{ github.ref == 'refs/heads/main' || (github.event_name == 'workflow_dispatch' && github.event.inputs.dry-run == 'true') }} | |
| permissions: | |
| contents: write # Required to create the GitHub Release + tag for stable publishes | |
| id-token: write # Required for npm OIDC Trusted Publishing | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| # Need history reaching `github.event.before` so `determine-version.ts` | |
| # can compare the root `package.json` version at that ref to HEAD and | |
| # decide whether this push is a release bump (publish `latest`) or a | |
| # routine commit (publish `dev`). A multi-commit push can place | |
| # `before` arbitrarily far back, so fetch the full history. | |
| fetch-depth: 0 | |
| - name: Setup mise | |
| uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0 | |
| with: | |
| cache: true | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 | |
| - name: Configure npm | |
| run: pnpm config set registry https://registry.npmjs.org | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Determine version | |
| id: version | |
| env: | |
| GITHUB_EVENT_NAME: ${{ github.event_name }} | |
| INPUT_DIST_TAG: ${{ github.event.inputs.dist-tag }} | |
| # `before` is the ref `main` pointed at before this push. | |
| # `determine-version.ts` reads the root `package.json` at that ref | |
| # to detect release bumps. Empty for `workflow_dispatch`, which | |
| # the script also handles. | |
| PUSH_BEFORE_SHA: ${{ github.event.before }} | |
| run: node scripts/determine-version.ts | |
| - name: Set package versions | |
| run: node scripts/set-version.ts "${{ steps.version.outputs.version }}" | |
| - name: Build packages | |
| run: pnpm build | |
| # Publish-time gate: packs every publishable workspace package and | |
| # verifies the resolved package.json contains no `workspace:*` or | |
| # `catalog:` dependency specifiers. `pnpm publish` rewrites these on | |
| # its own, but `npm publish` (and some CI flows) don't — and a single | |
| # leaked specifier breaks downstream installs. This gate fails the | |
| # publish before anything reaches the registry. | |
| - name: Check publish dependency specifiers | |
| run: pnpm check:publish-deps | |
| # Publish-time gate: ensures every substrate change since the previous | |
| # `v[0-9]*` tag is accompanied by a matching upgrade-instructions | |
| # directory in the user-skill / extension-upgrade-skill packages, and | |
| # that any newly added entries land in the in-flight transition | |
| # directory. See `scripts/check-upgrade-coverage.mjs`. | |
| - name: Check upgrade-instruction coverage | |
| run: pnpm check:upgrade-coverage --mode publish | |
| # Both publish paths fan out across `PUBLISH_CONCURRENCY` workers | |
| # (default 8) via `scripts/publish-packages.mjs`. `pnpm -r publish` is | |
| # intentionally serialized (it rejects `--workspace-concurrency` | |
| # outright), and the per-package npm upload + Sigstore signing | |
| # round-trips dominate wall-clock time, so serial publish of ~60 | |
| # packages takes 5–10 minutes on CI. Fanning out brings it down by | |
| # close to the concurrency factor while staying well below npm's | |
| # informal abuse thresholds. | |
| # Dry-run path: exercises the full publish pipeline (pack, validate | |
| # tarball contents, dependency rewriting) without touching the npm | |
| # registry. Use from any branch via `workflow_dispatch` to validate | |
| # changes that affect publishing before merging. | |
| - name: Publish packages (dry-run) | |
| if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.dry-run == 'true' }} | |
| run: node scripts/publish-packages.mjs --tag "${{ steps.version.outputs.tag }}" --dry-run | |
| # NODE_AUTH_TOKEN is intentionally NOT set. npm detects the OIDC environment | |
| # (id-token: write) and authenticates via Trusted Publishing automatically. | |
| # Setting NODE_AUTH_TOKEN to any value -- even empty string -- would block OIDC. | |
| # | |
| # Enable npm provenance attestations for each published package. | |
| # This requires a public source repository (npm rejects provenance from private repos). | |
| - name: Publish packages | |
| if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.dry-run != 'true' }} | |
| env: | |
| NPM_CONFIG_PROVENANCE: "true" | |
| run: node scripts/publish-packages.mjs --tag "${{ steps.version.outputs.tag }}" | |
| # Lightweight git tag for non-stable publishes (dev / beta / …). Stable | |
| # publishes get a tag implicitly via `gh release create` below; dev-channel | |
| # builds need one too because `prisma-next init` resolves its skill-cluster | |
| # URL via `#v<cliVersion>` (see packages/1-framework/3-tooling/cli/src/ | |
| # commands/init/skill-install.ts), which 404s without a matching git ref. | |
| # Tag-only is intentional — a full Release per dev build would drown out | |
| # the changelog signal. (TML-2696) | |
| # | |
| # Idempotent on workflow rerun: if `v$VERSION` already exists, skip. | |
| - name: Create lightweight git tag for non-stable publishes | |
| if: ${{ steps.version.outputs.tag != 'latest' && (github.event_name != 'workflow_dispatch' || github.event.inputs.dry-run != 'true') }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| if gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/v$VERSION" >/dev/null 2>&1; then | |
| echo "Tag v$VERSION already exists; skipping" | |
| else | |
| gh api "repos/$GITHUB_REPOSITORY/git/refs" \ | |
| -X POST \ | |
| -f ref="refs/tags/v$VERSION" \ | |
| -f "sha=$GITHUB_SHA" | |
| fi | |
| # Publish-time gate (stable only): a `latest` Release publishes the | |
| # committed notes file as its body, so that file must exist before the | |
| # Release step runs. There is no auto-generated fallback — a release | |
| # bump without docs/releases/v$VERSION.md fails here rather than | |
| # shipping flat notes. Dev/beta builds create no Release, so the gate is | |
| # scoped to `latest`, matching the Release step's own condition. | |
| - name: Check release notes | |
| if: ${{ steps.version.outputs.tag == 'latest' && (github.event_name != 'workflow_dispatch' || github.event.inputs.dry-run != 'true') }} | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: pnpm check:release-notes --mode publish --version "$VERSION" | |
| # Emit a GitHub Release for stable publishes only (dist-tag `latest`). | |
| # Dev / PR / beta builds publish to npm but do not produce a Release — | |
| # those would drown out the changelog signal. The Release is created at | |
| # $GITHUB_SHA so the tag points at the same commit the publish ran from. | |
| # The body is the committed `docs/releases/v$VERSION.md` (its presence is | |
| # enforced by the "Check release notes" gate above); there is no | |
| # auto-generated fallback. | |
| # | |
| # Idempotent on workflow rerun: if a Release for `v$VERSION` already exists | |
| # (e.g. a previous run published to npm but failed before this step), edit | |
| # it in place rather than re-creating it. The edit sets title/target only | |
| # and preserves the already-published body rather than re-pushing the | |
| # file — the right trade-off versus clobbering a hand-edited body or | |
| # failing the rerun outright. | |
| - name: Create GitHub Release for stable publishes | |
| if: ${{ steps.version.outputs.tag == 'latest' && (github.event_name != 'workflow_dispatch' || github.event.inputs.dry-run != 'true') }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| if gh release view "v$VERSION" >/dev/null 2>&1; then | |
| gh release edit "v$VERSION" \ | |
| --target "$GITHUB_SHA" \ | |
| --title "v$VERSION" | |
| else | |
| gh release create "v$VERSION" \ | |
| --target "$GITHUB_SHA" \ | |
| --title "v$VERSION" \ | |
| --notes-file "docs/releases/v$VERSION.md" | |
| fi |