Skip to content

Commit 4a45489

Browse files
committed
ci(release): add main/tag guard for non-dry publish; unify publish step
- Enforce ref before real publish when dry_run is false (!inputs.dry_run runs guard on tag pushes) - Single pnpm publish step: --dry-run or --provenance via FLAGS - workflow_dispatch: clearer dry_run description, required input; token note July 2026 - CONTRIBUTING: document ref guard
1 parent 88e27e6 commit 4a45489

2 files changed

Lines changed: 47 additions & 24 deletions

File tree

.github/workflows/rc-tag-release.yml

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,29 @@
1-
# Release workflow for @loop-engine/* OSS packages (under packages/* and packages/adapters/*)
1+
# Release workflow for @loop-engine/* OSS packages
22
#
3-
# Trigger:
4-
# - Push a semver tag: git tag v0.2.0 && git push origin v0.2.0
5-
# - Or Actions → "RC tag release" → Run workflow (default: publish --dry-run only; uncheck dry_run to test a real publish)
3+
# Normal publish trigger:
4+
# git tag v0.2.0 && git push --tags
5+
# workflow fires automatically, no manual dispatch needed
66
#
7-
# Prerequisites before tagging:
8-
# 1. Run `pnpm build && pnpm validate:publish` locally — confirm no workspace:* refs in packed manifests
9-
# 2. Bump versions in package.json files (or use changesets) so the tag matches what npm will publish
10-
# 3. Update CHANGELOG / draft a GitHub Release as needed
7+
# Pipeline test (dry run):
8+
# GitHub → Actions → RC tag release → Run workflow
9+
# → leave dry_run CHECKED → run
10+
# → exercises install, build, validate:publish, and pnpm publish --dry-run
1111
#
12-
# Auth: setup-node with registry-url configures ~/.npmrc; the env var MUST be NODE_AUTH_TOKEN
13-
# (map the GitHub Actions repository secret named NPM_TOKEN to NODE_AUTH_TOKEN in the publish steps below).
12+
# Manual dispatch with dry_run UNCHECKED:
13+
# Only allowed from main or a version tag — workflow will hard-fail otherwise.
14+
# Use only as an emergency escape hatch, not as a routine publish path.
1415
#
15-
# Provenance: id-token: write lets npm attach provenance linking the tarball to this GitHub Actions run.
16+
# Tag pushes: inputs.dry_run is undefined; guard step uses if: ${{ !inputs.dry_run }} so it still
17+
# runs, and startsWith(github.ref, 'refs/tags/') passes immediately — do not change that behavior.
1618
#
1719
# Token rotation:
18-
# NPM_TOKEN secret expires ~April 2027.
19-
# Rotate at: npmjs.com → Account Settings → Access Tokens
20-
# Update at: GitHub → Settings → Secrets and variables → Actions → NPM_TOKEN
20+
# NPM_TOKEN secret expires July 15, 2026.
21+
# Rotate at: npmjs.com → Account Settings → Access Tokens → Automation token
22+
# Update at: GitHub → loopengine/loop-engine → Settings → Secrets → NPM_TOKEN
23+
# Set calendar reminder for July 1, 2026.
24+
#
25+
# Auth: setup-node with registry-url configures ~/.npmrc; the env var MUST be NODE_AUTH_TOKEN
26+
# (map the GitHub Actions repository secret named NPM_TOKEN to NODE_AUTH_TOKEN below).
2127

2228
name: RC tag release
2329

@@ -28,9 +34,10 @@ on:
2834
workflow_dispatch:
2935
inputs:
3036
dry_run:
31-
description: "If true, run pnpm publish --dry-run only (no registry upload)"
37+
description: "Dry run only — does not publish to npm. Uncheck only from main with a version tag pushed."
3238
type: boolean
3339
default: true
40+
required: true
3441

3542
env:
3643
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
@@ -68,15 +75,31 @@ jobs:
6875
- name: Validate no workspace:* in publish targets
6976
run: pnpm validate:publish
7077

71-
- name: Publish to npm
72-
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false')
73-
run: pnpm publish -r --access public --no-git-checks --provenance
74-
env:
75-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
78+
- name: Enforce main or tag for real publish
79+
if: ${{ !inputs.dry_run }}
80+
run: |
81+
IS_TAG=${{ startsWith(github.ref, 'refs/tags/') }}
82+
IS_MAIN=${{ github.ref_name == 'main' }}
83+
84+
if [ "$IS_TAG" != "true" ] && [ "$IS_MAIN" != "true" ]; then
85+
echo "❌ Real publish is only allowed from main or a version tag."
86+
echo " Current ref: ${{ github.ref }}"
87+
echo " To test the pipeline safely, re-run with dry_run checked."
88+
exit 1
89+
fi
90+
91+
echo "✅ Ref check passed: ${{ github.ref }}"
7692
77-
- name: Publish dry run
78-
if: github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'true'
79-
run: pnpm publish -r --access public --no-git-checks --dry-run
93+
- name: Publish to npm
94+
run: |
95+
FLAGS="--access public --no-git-checks"
96+
if [ "${{ inputs.dry_run }}" = "true" ]; then
97+
FLAGS="$FLAGS --dry-run"
98+
echo "ℹ️ Dry run mode — no packages will be published"
99+
else
100+
FLAGS="$FLAGS --provenance"
101+
fi
102+
pnpm publish -r $FLAGS
80103
env:
81104
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
82105

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pnpm check-boundary
4545

4646
1. **Local gate**`pnpm build && pnpm validate:publish`. This is the tarball check (not `npm publish`). It fails if any packed public package still has `workspace:` in its manifest.
4747
2. **Ship changes via PR** — packaging fixes (e.g. `package.json`, `.npmrc`, `scripts/check-no-workspace-refs.mjs`, workflow updates) go through CI like any other change.
48-
3. **CI publish**[.github/workflows/rc-tag-release.yml](.github/workflows/rc-tag-release.yml) publishes from GitHub Actions only. It runs on **semver tag push** (`v*.*.*`): install → build → `validate:publish``pnpm publish -r` with **`NODE_AUTH_TOKEN`** set from the **`NPM_TOKEN`** repository secret (the env name must be `NODE_AUTH_TOKEN` for `setup-node`’s `.npmrc`). Provenance is enabled (`id-token: write`). To **dry-run** auth and packing without uploading, use **Actions → RC tag release → Run workflow** (default is dry-run; uncheck to publish).
48+
3. **CI publish**[.github/workflows/rc-tag-release.yml](.github/workflows/rc-tag-release.yml) publishes from GitHub Actions only. It runs on **semver tag push** (`v*.*.*`): install → build → `validate:publish``pnpm publish -r` with **`NODE_AUTH_TOKEN`** set from the **`NPM_TOKEN`** repository secret (the env name must be `NODE_AUTH_TOKEN` for `setup-node`’s `.npmrc`). Provenance is enabled (`id-token: write`) except in dry-run mode. To **dry-run** auth and packing without uploading, use **Actions → RC tag release → Run workflow** (default is dry-run). Unchecking dry run triggers a **ref guard**: real publish is allowed only from **`main`** or a **`refs/tags/`** ref — other branches fail fast.
4949
4. **Post-publish** — the tag workflow runs a clean `npm install @loop-engine/sdk@<version>` smoke test. Repeat manually from a scratch directory before announcing if you want extra assurance.
5050

5151
Local one-off releases can still use `pnpm release` (`validate:publish` + `changeset publish`) from a trusted machine; prefer the tag workflow for production npm publishes.

0 commit comments

Comments
 (0)