pgserve 1.1.9 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,138 +1,253 @@
1
- name: Unified Release
1
+ name: Release
2
+
3
+ # Single-branch release pipeline modeled on khal-os/desktop.
4
+ #
5
+ # Two trigger paths into the same workflow:
6
+ #
7
+ # 1. push to main with a hand-bumped package.json (no [skip ci] marker)
8
+ # -> auto-detect path: prepare reads package.json, checks if v${version}
9
+ # tag exists, builds + publishes + creates GitHub Release if not.
10
+ #
11
+ # 2. workflow_dispatch with bump=patch|minor|major
12
+ # -> bump job runs `npm version`, commits with [skip ci], tags, pushes.
13
+ # prepare/build/release continue inline in the same workflow run.
14
+ #
15
+ # Bot-loop guard: bump commits carry [skip ci]. Push of those commits is
16
+ # filtered out by the prepare gate, so the bot's own push does not retrigger.
17
+ #
18
+ # Auth: npm OIDC Trusted Publishing (configured via build-all-platforms.yml).
2
19
 
3
20
  on:
4
- pull_request:
5
- types: [closed]
21
+ push:
6
22
  branches: [main]
7
23
  workflow_dispatch:
8
24
  inputs:
9
- action:
10
- description: 'Release action'
25
+ bump:
26
+ description: "Version bump type"
11
27
  required: true
12
28
  type: choice
13
29
  options:
14
- - bump-rc
15
- - promote
30
+ - patch
31
+ - minor
32
+ - major
16
33
 
17
34
  concurrency:
18
- group: unified-release
35
+ group: release-${{ github.ref }}
19
36
  cancel-in-progress: false
20
37
 
21
38
  permissions:
22
39
  contents: write
23
- pull-requests: read
24
- id-token: write
40
+ id-token: write # required so the reusable `version.yml` workflow can mint
41
+ # the OIDC token for npm Trusted Publishing — without this,
42
+ # GH rejects the workflow at parse time (startup_failure)
43
+ # because the called job's `id-token: write` permission
44
+ # exceeds what the caller has granted.
25
45
 
26
46
  jobs:
27
- # Gate: Skip bot commits, detect action from PR labels
28
- gate:
29
- name: Release Gate
30
- runs-on: ubuntu-latest
31
- if: |
32
- github.event_name == 'workflow_dispatch' ||
33
- (github.event.pull_request.merged == true &&
34
- (contains(github.event.pull_request.labels.*.name, 'rc') ||
35
- contains(github.event.pull_request.labels.*.name, 'stable')))
36
- outputs:
37
- should_run: ${{ steps.check.outputs.should_run }}
38
- action: ${{ steps.detect.outputs.action }}
39
-
40
- steps:
41
- - name: Check for bot commits
42
- id: check
43
- run: |
44
- # Skip if this is a bot commit (prevents infinite loops)
45
- if [[ "${{ github.actor }}" == "github-actions[bot]" ]]; then
46
- echo "Skipping: bot commit detected"
47
- echo "should_run=false" >> $GITHUB_OUTPUT
48
- else
49
- echo "should_run=true" >> $GITHUB_OUTPUT
50
- fi
51
-
52
- - name: Detect action
53
- id: detect
54
- if: steps.check.outputs.should_run == 'true'
55
- run: |
56
- if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
57
- echo "action=${{ inputs.action }}" >> $GITHUB_OUTPUT
58
- echo "Action from dispatch: ${{ inputs.action }}"
59
- elif [[ "${{ contains(github.event.pull_request.labels.*.name, 'stable') }}" == "true" ]]; then
60
- echo "action=promote" >> $GITHUB_OUTPUT
61
- echo "Action from label: promote (stable)"
62
- elif [[ "${{ contains(github.event.pull_request.labels.*.name, 'rc') }}" == "true" ]]; then
63
- echo "action=bump-rc" >> $GITHUB_OUTPUT
64
- echo "Action from label: bump-rc"
65
- else
66
- echo "No release label found"
67
- echo "action=" >> $GITHUB_OUTPUT
68
- fi
69
-
70
- # Version: Bump version and create tag
71
- version:
72
- name: Bump Version
73
- needs: gate
74
- if: needs.gate.outputs.should_run == 'true' && needs.gate.outputs.action != ''
47
+ # ---------------------------------------------------------------------------
48
+ # Bump (workflow_dispatch only): npm version, commit [skip ci], tag, push.
49
+ # ---------------------------------------------------------------------------
50
+ bump:
51
+ name: Bump version
52
+ if: github.event_name == 'workflow_dispatch'
75
53
  runs-on: ubuntu-latest
54
+ timeout-minutes: 5
76
55
  outputs:
77
56
  version: ${{ steps.bump.outputs.version }}
78
57
  tag: ${{ steps.bump.outputs.tag }}
79
- npm_tag: ${{ steps.bump.outputs.npm_tag }}
80
- is_promote: ${{ steps.bump.outputs.is_promote }}
81
-
82
58
  steps:
83
- - name: Checkout
84
- uses: actions/checkout@v4
59
+ - uses: actions/checkout@v4
85
60
  with:
61
+ ref: main
86
62
  fetch-depth: 0
87
63
  token: ${{ secrets.GITHUB_TOKEN }}
88
64
 
89
- - name: Setup Node.js
90
- uses: actions/setup-node@v4
65
+ - uses: actions/setup-node@v4
91
66
  with:
92
- node-version: '20'
67
+ node-version: "22"
93
68
 
94
- - name: Configure Git
69
+ - name: Configure git
95
70
  run: |
96
71
  git config user.name "github-actions[bot]"
97
72
  git config user.email "github-actions[bot]@users.noreply.github.com"
98
73
 
99
- - name: Run release script
74
+ - name: Bump version
100
75
  id: bump
101
- run: node scripts/release.cjs --action ${{ needs.gate.outputs.action }}
76
+ run: |
77
+ npm version "${{ inputs.bump }}" --no-git-tag-version
78
+ VERSION=$(node -p "require('./package.json').version")
79
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
80
+ echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
81
+ echo "Bumped to ${VERSION}"
82
+
83
+ - name: Commit, tag, push
84
+ run: |
85
+ VERSION="${{ steps.bump.outputs.version }}"
86
+ TAG="${{ steps.bump.outputs.tag }}"
87
+ git add package.json
88
+ git commit -m "[skip ci] release ${TAG}"
89
+ git tag "${TAG}"
90
+ git push origin HEAD --follow-tags
91
+
92
+ # ---------------------------------------------------------------------------
93
+ # Prepare: resolve version, skip if tag already exists, build changelog.
94
+ #
95
+ # Gate handles both event types:
96
+ # - push: bump is skipped; the !cancelled() && !failure() guard lets
97
+ # this job run regardless. The [skip ci] check filters the
98
+ # bot's own bump-commit push so it does not retrigger.
99
+ # - dispatch: bump succeeded; the workflow_dispatch branch of the OR
100
+ # short-circuits the [skip ci] check (the dispatch event
101
+ # itself does not carry the bump's commit message).
102
+ # ---------------------------------------------------------------------------
103
+ prepare:
104
+ name: Prepare release
105
+ needs: bump
106
+ if: |
107
+ !cancelled() && !failure() &&
108
+ (github.event_name == 'workflow_dispatch' ||
109
+ (github.event_name == 'push' &&
110
+ !startsWith(github.event.head_commit.message, '[skip ci]')))
111
+ runs-on: ubuntu-latest
112
+ timeout-minutes: 5
113
+ outputs:
114
+ version: ${{ steps.ver.outputs.version }}
115
+ tag: ${{ steps.ver.outputs.tag }}
116
+ skip: ${{ steps.ver.outputs.skip }}
117
+ changelog: ${{ steps.changelog.outputs.notes }}
118
+ # Surface the resolved checkout-ref so downstream jobs (build, release)
119
+ # can use it. They cannot reference `needs.bump.outputs.*` directly
120
+ # because they only have `needs: prepare` (or [prepare, build]) — not
121
+ # `bump` — in their needs context.
122
+ ref: ${{ needs.bump.outputs.tag || github.sha }}
123
+ steps:
124
+ - uses: actions/checkout@v4
125
+ with:
126
+ # On dispatch, check out the freshly-pushed tag; on push, the
127
+ # triggering SHA already has the bumped package.json.
128
+ # (Prepare cannot reference its own `outputs.ref` here — that's
129
+ # only available to downstream jobs.)
130
+ ref: ${{ needs.bump.outputs.tag || github.sha }}
131
+ fetch-depth: 0
132
+
133
+ - name: Resolve version
134
+ id: ver
135
+ env:
136
+ GH_TOKEN: ${{ github.token }}
137
+ run: |
138
+ VERSION=$(node -p "require('./package.json').version")
139
+ TAG="v${VERSION}"
140
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
141
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
142
+
143
+ if gh release view "${TAG}" > /dev/null 2>&1; then
144
+ echo "Release ${TAG} already exists — skipping"
145
+ echo "skip=true" >> "$GITHUB_OUTPUT"
146
+ else
147
+ echo "skip=false" >> "$GITHUB_OUTPUT"
148
+ fi
149
+
150
+ - name: Find previous release tag
151
+ id: prev
152
+ if: steps.ver.outputs.skip == 'false'
153
+ env:
154
+ GH_TOKEN: ${{ github.token }}
155
+ run: |
156
+ TAG="${{ steps.ver.outputs.tag }}"
157
+ PREV=$(gh release list --limit 50 --json tagName -q '.[].tagName' | grep -v "^${TAG}$" | head -1)
158
+ if [ -n "$PREV" ] && git merge-base --is-ancestor "$PREV" HEAD 2>/dev/null; then
159
+ echo "tag=${PREV}" >> "$GITHUB_OUTPUT"
160
+ echo "Previous tag: ${PREV}"
161
+ else
162
+ echo "tag=" >> "$GITHUB_OUTPUT"
163
+ echo "No previous tag reachable from HEAD"
164
+ fi
165
+
166
+ - name: Generate changelog
167
+ id: changelog
168
+ if: steps.ver.outputs.skip == 'false' && steps.prev.outputs.tag != ''
169
+ run: |
170
+ PREV="${{ steps.prev.outputs.tag }}"
171
+ NOTES=$(git log --oneline "${PREV}..HEAD" --pretty="- %s" | head -50)
172
+ {
173
+ echo "notes<<EOF"
174
+ echo "$NOTES"
175
+ echo "EOF"
176
+ } >> "$GITHUB_OUTPUT"
102
177
 
103
- - name: Push changes
178
+ # Echo the resolved outputs so downstream skip/no-skip decisions are
179
+ # debuggable from the run log without re-running with step debug.
180
+ - name: Debug resolved outputs
104
181
  run: |
105
- git push origin HEAD:${{ github.ref_name }}
106
- git push origin --tags
182
+ echo "version=${{ steps.ver.outputs.version }}"
183
+ echo "tag=${{ steps.ver.outputs.tag }}"
184
+ echo "skip=${{ steps.ver.outputs.skip }}"
185
+ echo "prev=${{ steps.prev.outputs.tag }}"
107
186
 
108
- # Build & Publish: Always build and publish (npm_tag comes from version job)
187
+ # ---------------------------------------------------------------------------
188
+ # Build & Publish: matrix build of platform binaries + npm publish via OIDC.
189
+ #
190
+ # The reusable workflow filename is `version.yml` because npm Trusted
191
+ # Publisher is configured against that exact path. Renaming requires
192
+ # updating the npmjs.com Trusted Publisher entry first.
193
+ #
194
+ # The `if:` uses `always() && needs.prepare.result == 'success' &&
195
+ # needs.prepare.outputs.skip != 'true'`. This is the bulletproof pattern
196
+ # for reusable-workflow callers when any upstream job in the `needs:`
197
+ # chain was SKIPPED. With the simpler `needs.prepare.outputs.skip != 'true'`
198
+ # alone, GH Actions silently evaluated the gate as false even though the
199
+ # debug step in `prepare` proved the output was actually `'false'` — a
200
+ # known GH Actions quirk: when a job's transitive `needs:` chain includes
201
+ # a skipped job (here, `bump` is skipped on push events), the reusable-
202
+ # workflow caller's expression evaluator treats `needs.<job>.outputs.<x>`
203
+ # as null/missing regardless of the actual value.
204
+ #
205
+ # `always()` opts out of the implicit success() filter; the explicit
206
+ # `result == 'success'` reinstates it correctly; the outputs check then
207
+ # works as intended.
208
+ # ---------------------------------------------------------------------------
109
209
  build:
110
210
  name: Build & Publish
111
- needs: version
112
- uses: ./.github/workflows/build-all-platforms.yml
211
+ needs: prepare
212
+ if: |
213
+ always() &&
214
+ needs.prepare.result == 'success' &&
215
+ needs.prepare.outputs.skip != 'true'
216
+ uses: ./.github/workflows/version.yml
113
217
  with:
114
- version: ${{ needs.version.outputs.version }}
115
- npm_tag: ${{ needs.version.outputs.npm_tag }}
116
- ref: ${{ needs.version.outputs.tag }}
218
+ version: ${{ needs.prepare.outputs.version }}
219
+ npm_tag: latest
220
+ # Use prepare.outputs.ref (which resolves to the bump-job tag on
221
+ # dispatch, or `github.sha` on push). Build cannot reference
222
+ # `needs.bump.*` directly — only `prepare` is in its `needs:` chain.
223
+ # On the push path nobody creates the tag before this runs; the
224
+ # SHA-based checkout works because the merge commit already has the
225
+ # bumped package.json. The release job creates the tag at the end
226
+ # via `gh release create`.
227
+ ref: ${{ needs.prepare.outputs.ref }}
117
228
  secrets: inherit
118
229
 
119
- # GitHub Release: Create release with changelog
120
- github-release:
230
+ # ---------------------------------------------------------------------------
231
+ # Release: download artifacts, create GitHub Release with cliff-free notes.
232
+ # ---------------------------------------------------------------------------
233
+ release:
121
234
  name: Create GitHub Release
122
- needs: [version, build]
123
- if: always() && needs.version.result == 'success' && needs.build.result == 'success'
235
+ needs: [prepare, build]
236
+ if: |
237
+ always() &&
238
+ needs.prepare.result == 'success' &&
239
+ needs.build.result == 'success' &&
240
+ needs.prepare.outputs.skip != 'true'
124
241
  runs-on: ubuntu-latest
125
- permissions:
126
- contents: write
127
-
242
+ timeout-minutes: 10
128
243
  steps:
129
- - name: Checkout
130
- uses: actions/checkout@v4
244
+ - uses: actions/checkout@v4
131
245
  with:
132
- ref: ${{ needs.version.outputs.tag }}
246
+ # Same as the build job: prefer the bump job's tag (dispatch
247
+ # path) but fall back to the SHA (push path, no tag exists yet).
248
+ ref: ${{ needs.prepare.outputs.ref }}
133
249
 
134
- - name: Download artifacts
135
- if: needs.build.result == 'success'
250
+ - name: Download binaries
136
251
  uses: actions/download-artifact@v4
137
252
  with:
138
253
  path: dist/
@@ -140,28 +255,35 @@ jobs:
140
255
  merge-multiple: true
141
256
 
142
257
  - name: List artifacts
258
+ run: ls -la dist/
259
+
260
+ - name: Create release
261
+ env:
262
+ GH_TOKEN: ${{ github.token }}
263
+ RELEASE_NOTES: ${{ needs.prepare.outputs.changelog }}
143
264
  run: |
144
- if [ -d "dist" ]; then
145
- echo "Release artifacts:"
146
- ls -la dist/
147
- else
148
- echo "No artifacts (promote release)"
265
+ TAG="${{ needs.prepare.outputs.tag }}"
266
+ VERSION="${{ needs.prepare.outputs.version }}"
267
+
268
+ if [ -z "$RELEASE_NOTES" ]; then
269
+ RELEASE_NOTES="Release ${TAG}"
149
270
  fi
150
271
 
151
- - name: Create GitHub Release
152
- uses: softprops/action-gh-release@v2
153
- with:
154
- tag_name: ${{ needs.version.outputs.tag }}
155
- name: ${{ needs.version.outputs.tag }}
156
- generate_release_notes: true
157
- body: |
158
- ## Install
159
-
160
- ```bash
161
- npm install pgserve@${{ needs.version.outputs.npm_tag }}
162
- bunx pgserve@${{ needs.version.outputs.npm_tag }}
163
- ```
164
- prerelease: ${{ contains(needs.version.outputs.version, '-rc.') }}
165
- files: |
272
+ {
273
+ echo "$RELEASE_NOTES"
274
+ echo ""
275
+ echo "## Install"
276
+ echo ""
277
+ echo '```bash'
278
+ echo "npm install pgserve@${VERSION}"
279
+ echo "bunx pgserve@${VERSION}"
280
+ echo '```'
281
+ } > /tmp/release-notes.md
282
+
283
+ # The tag already exists (created by bump job on dispatch, or by the
284
+ # human commit on push). gh release create resolves --target via the
285
+ # tag automatically when omitted.
286
+ gh release create "${TAG}" \
287
+ --title "${TAG}" \
288
+ --notes-file /tmp/release-notes.md \
166
289
  dist/*
167
- fail_on_unmatched_files: false
@@ -112,7 +112,12 @@ jobs:
112
112
  needs: build
113
113
  runs-on: ubuntu-latest
114
114
  if: inputs.version != ''
115
- environment: npm-publish
115
+ # Note: `environment: npm-publish` was removed because the npmjs.com
116
+ # Trusted Publisher entry for `pgserve` does not declare an environment
117
+ # name. With the env gate present here, the OIDC token's environment
118
+ # claim did not match the registry's expectation and `npm publish`
119
+ # returned 404. Re-add this line if/when the Trusted Publisher entry
120
+ # has its Environment Name field set to `npm-publish`.
116
121
  permissions:
117
122
  id-token: write
118
123
  contents: read
@@ -123,10 +128,14 @@ jobs:
123
128
  with:
124
129
  ref: ${{ inputs.ref || github.ref }}
125
130
 
131
+ # Node 24 ships npm 11.5+ which has built-in OIDC trusted-publisher
132
+ # support. Avoids the `npm install -g npm@latest` self-upgrade bug
133
+ # (Arborist clobbering its own promise-retry dep mid-install) that
134
+ # broke rlmx's OIDC publish on Node 22.
126
135
  - name: Setup Node.js
127
136
  uses: actions/setup-node@v4
128
137
  with:
129
- node-version: '20'
138
+ node-version: '24'
130
139
  registry-url: 'https://registry.npmjs.org'
131
140
 
132
141
  - name: Setup Bun
@@ -137,6 +146,16 @@ jobs:
137
146
  - name: Install dependencies
138
147
  run: bun install
139
148
 
149
+ - name: Verify npm version supports OIDC trusted publishing
150
+ run: |
151
+ NPM_VERSION=$(npm --version)
152
+ echo "npm version: ${NPM_VERSION}"
153
+ MAJOR=$(echo "${NPM_VERSION}" | cut -d. -f1)
154
+ if [ "${MAJOR}" -lt 11 ]; then
155
+ echo "::error::npm ${NPM_VERSION} too old — OIDC requires >= 11.5.1. Bump node-version above."
156
+ exit 1
157
+ fi
158
+
140
159
  - name: Download all artifacts
141
160
  uses: actions/download-artifact@v4
142
161
  with:
@@ -179,13 +198,18 @@ jobs:
179
198
  echo "published=false" >> $GITHUB_OUTPUT
180
199
  fi
181
200
 
182
- - name: Publish to npm
201
+ - name: Publish to npm via OIDC
183
202
  if: steps.check.outputs.published == 'false'
203
+ env:
204
+ HUSKY: "0"
205
+ # npm auto-enables provenance in any CI env with `id-token: write`,
206
+ # regardless of the --provenance CLI flag. On some runners the
207
+ # server-side sigstore check fails with 422; disable explicitly.
208
+ # OIDC token exchange still happens.
209
+ NPM_CONFIG_PROVENANCE: "false"
184
210
  run: |
185
- echo "Publishing version ${{ inputs.version }} with tag ${{ inputs.npm_tag }}"
211
+ echo "Publishing version ${{ inputs.version }} with tag ${{ inputs.npm_tag }} via OIDC"
186
212
  npm publish --access public --tag ${{ inputs.npm_tag }}
187
- env:
188
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
189
213
 
190
214
  - name: Verify publish
191
215
  if: steps.check.outputs.published == 'false'
package/AGENTS.md CHANGED
@@ -1,3 +1,5 @@
1
+ > **Shared rules in `~/.claude/rules/agent-bible.md`. Read it.**
2
+
1
3
  # Genie Agent Framework
2
4
 
3
5
  ## Core Identity
@@ -188,16 +190,18 @@ Before editing ANY implementation file, Base Genie must check:
188
190
 
189
191
  **Protocol:** `@.genie/spells/orchestration-boundary-protocol.md`
190
192
 
191
- **Release Workflow Protocol:**
192
- - Never manually trigger `workflow_dispatch` for releases
193
- - Never manually bump version in package.json
194
- - ✅ Always use PR with `rc` or `stable` label - release.yml auto-triggers on merge
195
- - Version bumping is automated by scripts/release.cjs
193
+ **Release Workflow Protocol (push-to-main, single tier):**
194
+ - Manual path: bump locally with `npm version patch|minor|major`, commit, PR to main. Merge → `release.yml` fires automatically.
195
+ - Bot path: `gh workflow run release.yml -f bump=patch` (or `minor`/`major`). Bot bumps, tags, builds binaries, publishes to npm via OIDC.
196
+ - ✅ Skip: any commit message starting with `[skip ci]` is filtered by the prepare gate.
197
+ - No `rc`/`stable` PR labels (legacy — removed). No `scripts/release.cjs` (deleted).
198
+ - ❌ Don't edit `package.json` `version` directly on `main` outside the `npm version` flow above.
199
+ - ⚙️ npm publish runs from `.github/workflows/version.yml` (the file npmjs.com Trusted Publisher is bound to).
196
200
 
197
- **Documented Violations:**
201
+ **Documented Violations (history):**
198
202
  - Bug #168, task b51db539, 2025-10-21 (duplicate implementation)
199
203
  - 2025-10-26 (claimed release implementation steps without investigating automation)
200
- - 2025-12-08 (manually set version to 1.1.0 + triggered workflow_dispatch → version jumped to 1.1.1-rc.1)
204
+ - 2025-12-08 (manually set version to 1.1.0 + triggered workflow_dispatch → version jumped to 1.1.1-rc.1; this class of error is no longer possible — the bump path goes through `gh workflow run`, not direct package.json edits)
201
205
 
202
206
  ### 4. Task State Optimization - Live State, Not Documentation 🔴 CRITICAL
203
207
  **Rule:** Task state is ephemeral runtime data, not permanent documentation
package/Makefile CHANGED
@@ -28,15 +28,13 @@ help: ## Show this help
28
28
  @echo "$(CYAN)Embedded PostgreSQL server with multi-tenant support$(RESET)"
29
29
  @echo ""
30
30
  @echo "$(BOLD)Quick Commands:$(RESET)"
31
- @echo " $(PURPLE)release-rc$(RESET) Create RC release locally"
32
- @echo " $(PURPLE)release-stable$(RESET) Promote RC to stable"
33
31
  @echo " $(PURPLE)test-local$(RESET) Test server locally"
34
32
  @echo " $(PURPLE)pm2-start$(RESET) Start server with PM2"
35
33
  @echo ""
36
- @echo "$(BOLD)CI/CD Workflow:$(RESET)"
37
- @echo " 1. Create PR with changes"
38
- @echo " 2. Add 'rc' label → auto-publishes to npm @next"
39
- @echo " 3. Add 'stable' label promotes to npm @latest"
34
+ @echo "$(BOLD)Releasing:$(RESET)"
35
+ @echo " Manual: bump locally with 'npm version patch|minor|major', PR to main."
36
+ @echo " Bot: 'gh workflow run release.yml -f bump=patch' (or minor/major)."
37
+ @echo " Skip: any commit message starting with [skip ci] is ignored."
40
38
  @echo ""
41
39
  @echo "$(BOLD)Build Executables:$(RESET)"
42
40
  @echo " $(PURPLE)build$(RESET) Build for current platform"
@@ -211,35 +209,19 @@ clean-dist: ## Clean build artifacts
211
209
  @echo "$(GREEN)✅ Dist cleaned!$(RESET)"
212
210
 
213
211
  # ==========================================
214
- # 🚀 CI/CD Release (Automated)
212
+ # 🚀 Releasing
215
213
  # ==========================================
216
- # Releases are triggered by GitHub Actions when PRs are merged with labels:
217
- # - 'rc' label → Creates RC release (1.0.8 → 1.0.9-rc.1)
218
- # - 'stable' label → Promotes RC to stable (1.0.9-rc.1 → 1.0.9)
214
+ # Releases are driven by .github/workflows/release.yml on push to main.
219
215
  #
220
- # See .github/workflows/release.yml for full automation.
216
+ # Manual: bump locally with `npm version patch|minor|major`, commit, PR
217
+ # to main. Merge -> release fires automatically.
218
+ # Bot: `gh workflow run release.yml -f bump=patch` (or minor/major).
219
+ # The bot bumps, tags, builds binaries, publishes to npm via OIDC.
220
+ # Skip: any commit message starting with [skip ci] is ignored.
221
+ #
222
+ # There are no Make targets for releases — versioning is intentionally
223
+ # centralized in CI to keep the local-vs-prod workflow paths identical.
221
224
  # ==========================================
222
- .PHONY: release-rc release-stable release-dry
223
-
224
- release-rc: ## Create RC release locally (for testing)
225
- @echo "$(CYAN)🔢 Creating RC release...$(RESET)"
226
- @node scripts/release.cjs --action bump-rc
227
- @echo ""
228
- @echo "$(GREEN)✅ RC release created!$(RESET)"
229
- @echo "$(YELLOW)Push with: git push && git push --tags$(RESET)"
230
-
231
- release-stable: ## Promote RC to stable locally (for testing)
232
- @echo "$(CYAN)🎉 Promoting to stable...$(RESET)"
233
- @node scripts/release.cjs --action promote
234
- @echo ""
235
- @echo "$(GREEN)✅ Stable release created!$(RESET)"
236
- @echo "$(YELLOW)Push with: git push && git push --tags$(RESET)"
237
-
238
- release-dry: ## Dry-run release (no changes)
239
- @echo "$(CYAN)🔍 Dry-run release...$(RESET)"
240
- @node scripts/release.cjs --action bump-rc --dry-run
241
- @echo ""
242
- @echo "$(GREEN)✅ Dry-run complete (no changes made)$(RESET)"
243
225
 
244
226
  # ==========================================
245
227
  # 📦 Manual Publish (Deprecated)
@@ -254,19 +236,14 @@ publish-dry: pre-publish ## Dry-run publish (test without actually publishing)
254
236
  @echo "$(GREEN)✅ Dry-run successful!$(RESET)"
255
237
  @echo "$(YELLOW)To actually publish, run: make publish$(RESET)"
256
238
 
257
- publish: ## ⚠️ [DEPRECATED] Use PR labels instead
239
+ publish: ## ⚠️ [DEPRECATED] Releases run from CI on push to main
258
240
  @echo ""
259
241
  @echo "$(YELLOW)$(BOLD)╔═══════════════════════════════════════════════════════════════╗$(RESET)"
260
242
  @echo "$(YELLOW)$(BOLD)║ ⚠️ Manual publish is DEPRECATED ║$(RESET)"
261
243
  @echo "$(YELLOW)$(BOLD)║ ║$(RESET)"
262
- @echo "$(YELLOW)$(BOLD)║ Use PR labels for automated releases: ║$(RESET)"
263
- @echo "$(YELLOW)$(BOLD)║ Add 'rc' label RC release (npm @next) ║$(RESET)"
264
- @echo "$(YELLOW)$(BOLD)║ Add 'stable' label Promote to stable (npm @latest) ║$(RESET)"
265
- @echo "$(YELLOW)$(BOLD)║ ║$(RESET)"
266
- @echo "$(YELLOW)$(BOLD)║ Local testing: ║$(RESET)"
267
- @echo "$(YELLOW)$(BOLD)║ make release-rc Create RC locally ║$(RESET)"
268
- @echo "$(YELLOW)$(BOLD)║ make release-stable Promote locally ║$(RESET)"
269
- @echo "$(YELLOW)$(BOLD)║ make release-dry Dry-run (no changes) ║$(RESET)"
244
+ @echo "$(YELLOW)$(BOLD)║ Releases are driven by .github/workflows/release.yml: ║$(RESET)"
245
+ @echo "$(YELLOW)$(BOLD)║ Manual: npm version patch|minor|major, commit, PR to main ║$(RESET)"
246
+ @echo "$(YELLOW)$(BOLD)║ Bot: gh workflow run release.yml -f bump=patch ║$(RESET)"
270
247
  @echo "$(YELLOW)$(BOLD)╚═══════════════════════════════════════════════════════════════╝$(RESET)"
271
248
  @echo ""
272
249