codebyplan 1.5.1 → 1.9.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.
- package/dist/cli.js +4462 -748
- package/package.json +5 -1
- package/templates/.gitkeep +0 -0
- package/templates/README.md +20 -0
- package/templates/agents/cbp-cc-executor.md +213 -0
- package/templates/agents/cbp-database-agent.md +229 -0
- package/templates/agents/cbp-improve-claude.md +245 -0
- package/templates/agents/cbp-improve-round.md +284 -0
- package/templates/agents/cbp-mechanical-edits.md +111 -0
- package/templates/agents/cbp-research.md +282 -0
- package/templates/agents/cbp-round-executor.md +604 -0
- package/templates/agents/cbp-security-agent.md +134 -0
- package/templates/agents/cbp-task-check.md +213 -0
- package/templates/agents/cbp-task-planner.md +582 -0
- package/templates/agents/cbp-test-e2e-agent.md +363 -0
- package/templates/agents/cbp-testing-qa-agent.md +400 -0
- package/templates/context/mcp-docs.md +139 -0
- package/templates/hooks/README.md +236 -0
- package/templates/hooks/cbp-auto-test-hooks.sh +44 -0
- package/templates/hooks/cbp-lint-format-on-edit.sh +159 -0
- package/templates/hooks/cbp-maestro-yaml-validate.sh +100 -0
- package/templates/hooks/cbp-mcp-migration-guard.sh +32 -0
- package/templates/hooks/cbp-mcp-round-sync.sh +79 -0
- package/templates/hooks/cbp-mcp-worktree-inject.sh +76 -0
- package/templates/hooks/cbp-notify.sh +68 -0
- package/templates/hooks/cbp-plugin-dispatch.sh +29 -0
- package/templates/hooks/cbp-pre-commit-quality-gate.sh +204 -0
- package/templates/hooks/cbp-statusline.sh +347 -0
- package/templates/hooks/cbp-subagent-statusline.sh +182 -0
- package/templates/hooks/cbp-test-coverage-gate.sh +144 -0
- package/templates/hooks/cbp-test-hooks.sh +320 -0
- package/templates/hooks/hooks.json +85 -0
- package/templates/hooks/validate-context-usage.sh +59 -0
- package/templates/hooks/validate-git-commit.sh +78 -0
- package/templates/hooks/validate-git-stash-deny.sh +32 -0
- package/templates/hooks/validate-structure-lengths.sh +57 -0
- package/templates/hooks/validate-structure-lib.sh +104 -0
- package/templates/hooks/validate-structure-patterns.sh +54 -0
- package/templates/hooks/validate-structure-scope.sh +33 -0
- package/templates/hooks/validate-structure-smoke.sh +95 -0
- package/templates/hooks/validate-structure-templates.sh +34 -0
- package/templates/hooks/validate-structure.sh +69 -0
- package/templates/rules/.gitkeep +0 -0
- package/templates/rules/README.md +47 -0
- package/templates/rules/context-file-loading.md +52 -0
- package/templates/rules/scope-vocabulary.md +64 -0
- package/templates/rules/todo-backend.md +109 -0
- package/templates/settings.project.base.json +55 -0
- package/templates/settings.user.base.json +25 -0
- package/templates/skills/cbp-build-cc-agent/SKILL.md +139 -0
- package/templates/skills/cbp-build-cc-agent/examples/read-only-reviewer.md +32 -0
- package/templates/skills/cbp-build-cc-agent/examples/with-hooks.md +41 -0
- package/templates/skills/cbp-build-cc-agent/examples/with-skills-preload.md +25 -0
- package/templates/skills/cbp-build-cc-agent/reference/cbp-quality.md +153 -0
- package/templates/skills/cbp-build-cc-agent/reference/frontmatter-fields.md +37 -0
- package/templates/skills/cbp-build-cc-agent/reference/permission-modes.md +18 -0
- package/templates/skills/cbp-build-cc-agent/scripts/validate-agent.sh +67 -0
- package/templates/skills/cbp-build-cc-agent/templates/agent.md +66 -0
- package/templates/skills/cbp-build-cc-claude-file/SKILL.md +178 -0
- package/templates/skills/cbp-build-cc-claude-file/examples/minimal-project.md +33 -0
- package/templates/skills/cbp-build-cc-claude-file/examples/monorepo-with-imports.md +39 -0
- package/templates/skills/cbp-build-cc-claude-file/reference/imports.md +72 -0
- package/templates/skills/cbp-build-cc-claude-file/reference/what-belongs.md +39 -0
- package/templates/skills/cbp-build-cc-claude-file/templates/project-claude-md.md +48 -0
- package/templates/skills/cbp-build-cc-claude-file/templates/user-claude-md.md +22 -0
- package/templates/skills/cbp-build-cc-memory/SKILL.md +201 -0
- package/templates/skills/cbp-build-cc-memory/examples/feedback-memory.md +11 -0
- package/templates/skills/cbp-build-cc-memory/examples/project-memory.md +11 -0
- package/templates/skills/cbp-build-cc-memory/examples/reference-memory.md +13 -0
- package/templates/skills/cbp-build-cc-memory/examples/user-memory.md +14 -0
- package/templates/skills/cbp-build-cc-memory/reference/memory-types.md +59 -0
- package/templates/skills/cbp-build-cc-memory/reference/when-to-save.md +62 -0
- package/templates/skills/cbp-build-cc-memory/templates/MEMORY-index.md +4 -0
- package/templates/skills/cbp-build-cc-memory/templates/memory-entry.md +15 -0
- package/templates/skills/cbp-build-cc-mode/SKILL.md +99 -0
- package/templates/skills/cbp-build-cc-rule/SKILL.md +176 -0
- package/templates/skills/cbp-build-cc-rule/examples/global-rule.md +19 -0
- package/templates/skills/cbp-build-cc-rule/examples/scoped-rule.md +41 -0
- package/templates/skills/cbp-build-cc-rule/reference/paths-patterns.md +48 -0
- package/templates/skills/cbp-build-cc-rule/templates/rule.md +32 -0
- package/templates/skills/cbp-build-cc-settings/SKILL.md +220 -0
- package/templates/skills/cbp-build-cc-settings/examples/hooks-config.json +64 -0
- package/templates/skills/cbp-build-cc-settings/examples/permissions-config.json +34 -0
- package/templates/skills/cbp-build-cc-settings/examples/sandbox-config.json +42 -0
- package/templates/skills/cbp-build-cc-settings/reference/cbp-conventions.md +104 -0
- package/templates/skills/cbp-build-cc-settings/reference/permission-rules.md +61 -0
- package/templates/skills/cbp-build-cc-settings/reference/scope-precedence.md +73 -0
- package/templates/skills/cbp-build-cc-settings/reference/settings-fields.md +166 -0
- package/templates/skills/cbp-build-cc-settings/templates/settings.json +23 -0
- package/templates/skills/cbp-build-cc-settings/templates/settings.local.json +10 -0
- package/templates/skills/cbp-build-cc-skill/SKILL.md +154 -0
- package/templates/skills/cbp-build-cc-skill/examples/dynamic-context.md +31 -0
- package/templates/skills/cbp-build-cc-skill/examples/fork-skill.md +22 -0
- package/templates/skills/cbp-build-cc-skill/examples/knowledge-skill.md +25 -0
- package/templates/skills/cbp-build-cc-skill/examples/task-skill.md +29 -0
- package/templates/skills/cbp-build-cc-skill/reference/cbp-quality.md +157 -0
- package/templates/skills/cbp-build-cc-skill/reference/frontmatter-fields.md +35 -0
- package/templates/skills/cbp-build-cc-skill/reference/string-substitutions.md +60 -0
- package/templates/skills/cbp-build-cc-skill/scripts/validate-skill.sh +90 -0
- package/templates/skills/cbp-build-cc-skill/templates/skill.md +51 -0
- package/templates/skills/cbp-checkpoint-check/SKILL.md +156 -0
- package/templates/skills/cbp-checkpoint-complete/SKILL.md +109 -0
- package/templates/skills/cbp-checkpoint-create/SKILL.md +116 -0
- package/templates/skills/cbp-checkpoint-end/SKILL.md +241 -0
- package/templates/skills/cbp-checkpoint-plan/SKILL.md +137 -0
- package/templates/skills/cbp-checkpoint-plan/reference/alternative-comparison-template.md +54 -0
- package/templates/skills/cbp-checkpoint-plan/reference/dep-decision-rubric.md +50 -0
- package/templates/skills/cbp-checkpoint-plan/reference/e2e-discovery-probe.md +57 -0
- package/templates/skills/cbp-checkpoint-plan/reference/gap-analysis-playbook.md +47 -0
- package/templates/skills/cbp-checkpoint-start/SKILL.md +84 -0
- package/templates/skills/cbp-checkpoint-update/SKILL.md +115 -0
- package/templates/skills/cbp-frontend-a11y/SKILL.md +109 -0
- package/templates/skills/cbp-frontend-a11y/reference/aria-roles-states.md +130 -0
- package/templates/skills/cbp-frontend-a11y/reference/contrast-visual.md +122 -0
- package/templates/skills/cbp-frontend-a11y/reference/keyboard-patterns.md +154 -0
- package/templates/skills/cbp-frontend-a11y/reference/semantic-html.md +111 -0
- package/templates/skills/cbp-frontend-design/SKILL.md +145 -0
- package/templates/skills/cbp-frontend-design/reference/nextjs-scss.md +118 -0
- package/templates/skills/cbp-frontend-design/reference/rn-expo.md +101 -0
- package/templates/skills/cbp-frontend-design/reference/tauri-react.md +82 -0
- package/templates/skills/cbp-frontend-ui/SKILL.md +262 -0
- package/templates/skills/cbp-frontend-ui/reference/ui-label-maps.md +42 -0
- package/templates/skills/cbp-frontend-ui/reference/ui-layout-patterns.md +105 -0
- package/templates/skills/cbp-frontend-ui/reference/variant-defaults.md +149 -0
- package/templates/skills/cbp-frontend-ux/SKILL.md +181 -0
- package/templates/skills/cbp-git-branch-feat-create/SKILL.md +115 -0
- package/templates/skills/cbp-git-commit/SKILL.md +278 -0
- package/templates/skills/cbp-git-worktree-create/SKILL.md +226 -0
- package/templates/skills/cbp-git-worktree-remove/SKILL.md +145 -0
- package/templates/skills/cbp-merge-main/SKILL.md +228 -0
- package/templates/skills/cbp-round-check/SKILL.md +104 -0
- package/templates/skills/cbp-round-end/SKILL.md +183 -0
- package/templates/skills/cbp-round-end/reference/findings-presentation.md +44 -0
- package/templates/skills/cbp-round-end/reference/inline-fallback.md +35 -0
- package/templates/skills/cbp-round-execute/SKILL.md +211 -0
- package/templates/skills/cbp-round-execute/reference/inline-fallback.md +59 -0
- package/templates/skills/cbp-round-input/SKILL.md +165 -0
- package/templates/skills/cbp-round-start/SKILL.md +222 -0
- package/templates/skills/cbp-round-update/SKILL.md +163 -0
- package/templates/skills/cbp-session-end/SKILL.md +187 -0
- package/templates/skills/cbp-session-start/SKILL.md +155 -0
- package/templates/skills/cbp-ship/SKILL.md +332 -0
- package/templates/skills/cbp-ship/reference/changesets-overview.md +120 -0
- package/templates/skills/cbp-ship/reference/eas-cli-overview.md +60 -0
- package/templates/skills/cbp-ship/reference/gh-cli-overview.md +135 -0
- package/templates/skills/cbp-ship/reference/gh-cli-shipment-commands.md +283 -0
- package/templates/skills/cbp-ship/reference/npm-publish-monorepo.md +252 -0
- package/templates/skills/cbp-ship/reference/npm-publish-oidc-trusted.md +157 -0
- package/templates/skills/cbp-ship/reference/npm-publish-overview.md +171 -0
- package/templates/skills/cbp-ship/reference/preflight-checklist.md +88 -0
- package/templates/skills/cbp-ship/reference/railway-nestjs-deployment.md +169 -0
- package/templates/skills/cbp-ship/reference/railway-overview.md +120 -0
- package/templates/skills/cbp-ship/reference/railway-troubleshooting.md +168 -0
- package/templates/skills/cbp-ship/reference/release-please-overview.md +99 -0
- package/templates/skills/cbp-ship/reference/surface-expo-eas.md +155 -0
- package/templates/skills/cbp-ship/reference/surface-npm.md +180 -0
- package/templates/skills/cbp-ship/reference/surface-railway.md +152 -0
- package/templates/skills/cbp-ship/reference/surface-supabase.md +178 -0
- package/templates/skills/cbp-ship/reference/surface-tauri.md +138 -0
- package/templates/skills/cbp-ship/reference/surface-vercel.md +124 -0
- package/templates/skills/cbp-ship/reference/surface-vscode-ext.md +144 -0
- package/templates/skills/cbp-ship/reference/surfaces.md +60 -0
- package/templates/skills/cbp-ship/reference/testflight-automation.md +215 -0
- package/templates/skills/cbp-ship/reference/testflight-internal-vs-external.md +69 -0
- package/templates/skills/cbp-ship/reference/testflight-overview.md +98 -0
- package/templates/skills/cbp-ship/reference/versioning.md +116 -0
- package/templates/skills/cbp-ship/scripts/detect-surfaces.sh +217 -0
- package/templates/skills/cbp-ship/scripts/verify-expo-eas.sh +35 -0
- package/templates/skills/cbp-ship/scripts/verify-npm.sh +21 -0
- package/templates/skills/cbp-ship/scripts/verify-railway.sh +41 -0
- package/templates/skills/cbp-ship/scripts/verify-supabase.sh +19 -0
- package/templates/skills/cbp-ship/scripts/verify-tauri.sh +24 -0
- package/templates/skills/cbp-ship/scripts/verify-vercel.sh +32 -0
- package/templates/skills/cbp-ship/scripts/verify-vscode-ext.sh +25 -0
- package/templates/skills/cbp-ship/templates/eas.json +66 -0
- package/templates/skills/cbp-ship/templates/railway.toml +15 -0
- package/templates/skills/cbp-ship/templates/release-please-config.json +17 -0
- package/templates/skills/cbp-ship/templates/vercel.json +19 -0
- package/templates/skills/cbp-ship/templates/vscodeignore +21 -0
- package/templates/skills/cbp-ship/templates/workflow-changesets.yml +41 -0
- package/templates/skills/cbp-ship/templates/workflow-eas-submit.yml +53 -0
- package/templates/skills/cbp-ship/templates/workflow-npm-publish.yml +36 -0
- package/templates/skills/cbp-ship/templates/workflow-release-please.yml +21 -0
- package/templates/skills/cbp-ship/templates/workflow-tauri-release.yml +69 -0
- package/templates/skills/cbp-ship/templates/workflow-vsce-publish.yml +31 -0
- package/templates/skills/cbp-ship-configure/SKILL.md +296 -0
- package/templates/skills/cbp-ship-configure/reference/expo-mobile.md +204 -0
- package/templates/skills/cbp-ship-configure/reference/npm-package.md +165 -0
- package/templates/skills/cbp-ship-configure/reference/railway-backend.md +199 -0
- package/templates/skills/cbp-ship-configure/reference/supabase.md +200 -0
- package/templates/skills/cbp-ship-configure/reference/tauri-desktop.md +181 -0
- package/templates/skills/cbp-ship-configure/reference/vercel.md +117 -0
- package/templates/skills/cbp-ship-configure/reference/vscode-ext.md +155 -0
- package/templates/skills/cbp-ship-main/SKILL.md +65 -0
- package/templates/skills/cbp-supabase-branch-check/SKILL.md +337 -0
- package/templates/skills/cbp-supabase-branch-check/reference/dag-steps.md +29 -0
- package/templates/skills/cbp-supabase-migrate/SKILL.md +314 -0
- package/templates/skills/cbp-supabase-migrate/reference/advisor-triage.md +70 -0
- package/templates/skills/cbp-supabase-migrate/reference/cli-fallback.md +87 -0
- package/templates/skills/cbp-supabase-migrate/reference/preflight-dry-run.md +58 -0
- package/templates/skills/cbp-supabase-setup/SKILL.md +239 -0
- package/templates/skills/cbp-supabase-setup/reference/branching-setup.md +121 -0
- package/templates/skills/cbp-supabase-setup/reference/cli-fallback.md +109 -0
- package/templates/skills/cbp-task-check/SKILL.md +166 -0
- package/templates/skills/cbp-task-complete/SKILL.md +206 -0
- package/templates/skills/cbp-task-complete/reference/checkpoint-done-branching.md +48 -0
- package/templates/skills/cbp-task-complete/reference/next-step-heuristic.md +56 -0
- package/templates/skills/cbp-task-create/SKILL.md +167 -0
- package/templates/skills/cbp-task-start/SKILL.md +239 -0
- package/templates/skills/cbp-task-testing/SKILL.md +277 -0
- package/templates/skills/cbp-todo/SKILL.md +111 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# GitHub CLI — Shipment Commands
|
|
2
|
+
|
|
3
|
+
Commands `/cbp-ship` actually runs, organised by surface. Authoritative reference: <https://cli.github.com/manual>. Each section: exact command, flags we use, JSON shape (where applicable), common errors.
|
|
4
|
+
|
|
5
|
+
## 1. Pull Requests
|
|
6
|
+
|
|
7
|
+
### `gh pr create` — open a PR
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
gh pr create --base main --head "$BRANCH" \
|
|
11
|
+
--title "$TITLE" --body-file .claude/tmp/pr-body.md --draft
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Flag | Purpose |
|
|
15
|
+
|------|---------|
|
|
16
|
+
| `--base <branch>` | Target. CBP ships `feat/* -> main`; always set explicitly. |
|
|
17
|
+
| `--head <branch>` | Source. Defaults to current; pass explicitly with `--repo`. |
|
|
18
|
+
| `--title` | One-line title. Obey `rules/git-workflow.md` (no Claude attribution). |
|
|
19
|
+
| `--body-file <path>` | Multiline body. Preferred over `--body`. |
|
|
20
|
+
| `--draft` | Open in draft. CBP default for non-trivial PRs. |
|
|
21
|
+
| `--reviewer @me,user,team/slug` | Optional. |
|
|
22
|
+
| `--label`, `--assignee`, `--milestone`, `--project` | Optional metadata. |
|
|
23
|
+
| `--fill` | Use commit subject + body as title/body. |
|
|
24
|
+
| `--repo OWNER/REPO` | Required outside the checkout. |
|
|
25
|
+
|
|
26
|
+
Output: prints the URL on stdout (no JSON). Capture the PR number with `gh pr view --json number`.
|
|
27
|
+
|
|
28
|
+
Common errors:
|
|
29
|
+
|
|
30
|
+
- `A pull request already exists for ...` — branch has an open PR; use `gh pr view`.
|
|
31
|
+
- `must provide --title and --body (or --fill)` — non-TTY without flags.
|
|
32
|
+
- `Head sha can't be blank` — branch not pushed; `git push -u origin <branch>` first.
|
|
33
|
+
|
|
34
|
+
### `gh pr view` — read PR state
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
gh pr view "$PR_NUMBER" \
|
|
38
|
+
--json number,state,mergeable,mergeStateStatus,headRefOid,baseRefName,reviewDecision,statusCheckRollup,url \
|
|
39
|
+
--jq .
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
| Field | Type | Use |
|
|
43
|
+
|-------|------|-----|
|
|
44
|
+
| `state` | `OPEN`/`CLOSED`/`MERGED` | Gate further actions |
|
|
45
|
+
| `mergeable` | `MERGEABLE`/`CONFLICTING`/`UNKNOWN` | Pre-merge gate |
|
|
46
|
+
| `mergeStateStatus` | `CLEAN`/`BLOCKED`/`BEHIND`/`DIRTY`/`UNSTABLE`/`HAS_HOOKS` | Why merge is blocked |
|
|
47
|
+
| `headRefOid` | sha | Pin a `--match-head-commit` merge |
|
|
48
|
+
| `reviewDecision` | `APPROVED`/`CHANGES_REQUESTED`/`REVIEW_REQUIRED` | Review gate |
|
|
49
|
+
| `statusCheckRollup` | array | Per-check status; filter with `--jq` |
|
|
50
|
+
|
|
51
|
+
Pass `--json` with no value to discover the full field list.
|
|
52
|
+
|
|
53
|
+
### `gh pr merge` — merge a PR
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
gh pr merge "$PR_NUMBER" --merge --delete-branch --match-head-commit "$HEAD_OID"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Flag | Purpose |
|
|
60
|
+
|------|---------|
|
|
61
|
+
| `--merge`/`--squash`/`--rebase` | Strategy. CBP default is `--merge`. `--rebase` often blocked by branch protection. |
|
|
62
|
+
| `--auto` | Auto-merge once required checks pass. |
|
|
63
|
+
| `--delete-branch` (`-d`) | Delete head after merge. Always set for `feat/*`. |
|
|
64
|
+
| `--admin` | Bypass branch protection. Reserved for emergency hotfixes; never default. |
|
|
65
|
+
| `--match-head-commit <oid>` | Refuse merge if HEAD has moved. Use `headRefOid` from `gh pr view`. |
|
|
66
|
+
| `--subject`, `--body` | Override merge commit message. |
|
|
67
|
+
|
|
68
|
+
Common errors:
|
|
69
|
+
|
|
70
|
+
- `Pull request is not mergeable: <reason>` — inspect `mergeStateStatus`. `BLOCKED` = required checks/reviews missing.
|
|
71
|
+
- `auto-merge is not allowed for this repository` — repo disables it; remove `--auto`.
|
|
72
|
+
- HTTP 405 on protected branches without `--admin` — expected; do NOT auto-escalate.
|
|
73
|
+
|
|
74
|
+
## 2. Releases
|
|
75
|
+
|
|
76
|
+
### `gh release create`
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
gh release create "$TAG" --title "$TITLE" --notes-file CHANGELOG-fragment.md \
|
|
80
|
+
--target "$COMMIT_SHA" --latest ./dist/*.tar.gz ./dist/*.zip
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Argument order: `gh release create <tag> [<asset>...]`. Tag is positional + required.
|
|
84
|
+
|
|
85
|
+
| Flag | Purpose |
|
|
86
|
+
|------|---------|
|
|
87
|
+
| `--title` (`-t`) | Title. Defaults to tag. |
|
|
88
|
+
| `--notes-file` (`-F`) | Notes from file. `-` for stdin. |
|
|
89
|
+
| `--generate-notes` | Auto-generate via API; combinable with `--notes-file` (file content goes above generated). |
|
|
90
|
+
| `--target <branch-or-sha>` | Tag-target if tag does not exist. |
|
|
91
|
+
| `--draft` (`-d`) | Save as draft. CBP default for human review. |
|
|
92
|
+
| `--prerelease` (`-p`) | Prerelease flag. |
|
|
93
|
+
| `--latest` / `--latest=false` | Mark/unmark as Latest. |
|
|
94
|
+
| `--verify-tag` | Refuse if tag does not exist remotely. |
|
|
95
|
+
| `--discussion-category <name>` | Open a discussion. |
|
|
96
|
+
| `--fail-on-no-commits` | Abort if no new commits since last release. |
|
|
97
|
+
|
|
98
|
+
Asset upload: paths after the tag. Custom labels via `'/path/to/file.zip#Display Label'`.
|
|
99
|
+
|
|
100
|
+
Common errors:
|
|
101
|
+
|
|
102
|
+
- `tag already exists` (without `--target`) — local tag points to different sha; `git push --tags` or `git tag -d` + recreate.
|
|
103
|
+
- `validation failed: name has already been taken` — use `gh release edit` or `gh release delete` first.
|
|
104
|
+
- `could not find tag` with `--verify-tag` — push the tag first.
|
|
105
|
+
|
|
106
|
+
### `gh release view`
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
gh release view "$TAG" --json tagName,name,isDraft,isLatest,isPrerelease,publishedAt,url,assets --jq .
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
JSON fields: `tagName`, `name`, `body`, `isDraft`, `isLatest`, `isPrerelease`, `publishedAt`, `url`, `tarballUrl`, `zipballUrl`, `assets[].{name,size,downloadCount,url}`.
|
|
113
|
+
|
|
114
|
+
### `gh release delete`
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
gh release delete "$TAG" --yes --cleanup-tag
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`--yes` skips confirmation (required non-TTY). `--cleanup-tag` also deletes the underlying git tag. `release not found` is a soft failure — treat as already-clean.
|
|
121
|
+
|
|
122
|
+
## 3. Secrets
|
|
123
|
+
|
|
124
|
+
### `gh secret list`
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
gh secret list --repo "$OWNER/$REPO"
|
|
128
|
+
gh secret list --org "$ORG"
|
|
129
|
+
gh secret list --env "$ENV" --repo "$OWNER/$REPO"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Output is tab-separated `NAME UPDATED`. No `--json`. Secret values are never returned.
|
|
133
|
+
|
|
134
|
+
### `gh secret set`
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
echo -n "$VALUE" | gh secret set MY_SECRET # repo-level
|
|
138
|
+
gh secret set --env-file .env.production # bulk from dotenv
|
|
139
|
+
echo -n "$VALUE" | gh secret set MY_SECRET --env production # environment
|
|
140
|
+
echo -n "$VALUE" | gh secret set MY_SECRET --org myorg --visibility selected --repos repo1,repo2
|
|
141
|
+
echo -n "$VALUE" | gh secret set MY_SECRET --app dependabot # dependabot
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Scope matrix:
|
|
145
|
+
|
|
146
|
+
| Flag combo | Scope | Token scope |
|
|
147
|
+
|------------|-------|-------------|
|
|
148
|
+
| (none) | Repository | `repo` |
|
|
149
|
+
| `--env <name>` | Repository environment (env must exist) | `repo` |
|
|
150
|
+
| `--org <name> --visibility all`/`private` | Org-wide | `admin:org` |
|
|
151
|
+
| `--org <name> --visibility selected --repos a,b` | Org allow-list | `admin:org` |
|
|
152
|
+
| `--app actions` (default) / `dependabot` / `codespaces` | Per-application surface | as above |
|
|
153
|
+
|
|
154
|
+
Input precedence: `--body <value>` > stdin > interactive prompt. Prefer stdin to keep values out of process listings and shell history.
|
|
155
|
+
|
|
156
|
+
Common errors:
|
|
157
|
+
|
|
158
|
+
- `HTTP 404` with `--env` — environment must exist (`gh api repos/{o}/{r}/environments/{name} --method PUT`).
|
|
159
|
+
- `HTTP 422: visibility 'selected' requires --repos` — supply allow-list.
|
|
160
|
+
- `HTTP 403` against `--org` — `gh auth refresh --scopes admin:org`.
|
|
161
|
+
|
|
162
|
+
## 4. Workflow Runs
|
|
163
|
+
|
|
164
|
+
### `gh run list`
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
gh run list --workflow ci.yml --branch "$BRANCH" --limit 10 \
|
|
168
|
+
--json databaseId,status,conclusion,headSha,event,createdAt,workflowName,url \
|
|
169
|
+
--jq '.[] | select(.conclusion != "success")'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Flags: `--workflow` (filename/ID/name), `--branch`, `--user`, `--status`, `--event`, `--limit` (default 20), `--created`, `--json`, `-R/--repo`.
|
|
173
|
+
|
|
174
|
+
JSON fields: `attempt`, `conclusion`, `createdAt`, `databaseId`, `displayTitle`, `event`, `headBranch`, `headSha`, `name`, `number`, `startedAt`, `status`, `updatedAt`, `url`, `workflowDatabaseId`, `workflowName`.
|
|
175
|
+
|
|
176
|
+
`status`: `queued`, `in_progress`, `completed`, `requested`, `waiting`. `conclusion` (when `status=completed`): `success`, `failure`, `cancelled`, `skipped`, `timed_out`, `action_required`, `neutral`, `stale`.
|
|
177
|
+
|
|
178
|
+
### `gh run watch`
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
gh run watch "$RUN_ID" --exit-status --interval 5 --compact
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
| Flag | Purpose |
|
|
185
|
+
|------|---------|
|
|
186
|
+
| `--exit-status` | Non-zero on failure. Required for `set -e`. |
|
|
187
|
+
| `--interval <sec>` (`-i`) | Poll interval (default 3). Bump to 5-10 for long runs. |
|
|
188
|
+
| `--compact` | Show only failed/relevant steps. |
|
|
189
|
+
| `-R/--repo` | Override repo. |
|
|
190
|
+
|
|
191
|
+
Never omit `RUN_ID` in `/cbp-ship` — without it the command prompts (no TTY).
|
|
192
|
+
|
|
193
|
+
### `gh run view`
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
gh run view "$RUN_ID" --json status,conclusion,jobs,workflowName,url --jq . # summary
|
|
197
|
+
gh run view "$RUN_ID" --log-failed # failed-step logs
|
|
198
|
+
gh run view --job "$JOB_ID" --log # one job
|
|
199
|
+
gh run view "$RUN_ID" --exit-status # script-friendly
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
JSON fields: same as `run list` plus `jobs`. `jobs[].{databaseId,name,status,conclusion,steps[].{name,conclusion,number}}` — canonical drill-down from a failed run to the responsible step.
|
|
203
|
+
|
|
204
|
+
Common errors:
|
|
205
|
+
|
|
206
|
+
- `could not find any workflow run with ID <x>` — wrong repo or expired; verify with `gh run list -R <repo>`.
|
|
207
|
+
- `--log-failed` empty while in progress — pair with `gh run watch` first.
|
|
208
|
+
- Logs older than 90 days unavailable (GitHub-side retention).
|
|
209
|
+
|
|
210
|
+
## 5. Deployments API (Non-Vercel)
|
|
211
|
+
|
|
212
|
+
For non-Vercel deploy targets, `/cbp-ship` records deploys via the GitHub Deployments API using `gh api`. There is no `gh deployment` subcommand.
|
|
213
|
+
|
|
214
|
+
### Create a deployment
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
gh api "repos/$OWNER/$REPO/deployments" --method POST \
|
|
218
|
+
-f ref="$COMMIT_SHA" -f environment=production -f description="ship: $TAG" \
|
|
219
|
+
-F auto_merge=false -f required_contexts='[]'
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- `-f key=value` — string field.
|
|
223
|
+
- `-F key=value` — typed field (`true`/`false`/`null`/integer auto-converted).
|
|
224
|
+
- `required_contexts='[]'` — empty JSON array as a string to skip status-check gating.
|
|
225
|
+
- `--input <file>` — for complex payloads, pass JSON from file.
|
|
226
|
+
|
|
227
|
+
Response (excerpt):
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"id": 12345678,
|
|
232
|
+
"url": "https://api.github.com/repos/.../deployments/12345678",
|
|
233
|
+
"sha": "abc123...",
|
|
234
|
+
"ref": "main",
|
|
235
|
+
"environment": "production",
|
|
236
|
+
"statuses_url": "https://api.github.com/repos/.../deployments/12345678/statuses"
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Capture `id` for status follow-ups.
|
|
241
|
+
|
|
242
|
+
### Update deployment status
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
gh api "repos/$OWNER/$REPO/deployments/$DEPLOYMENT_ID/statuses" --method POST \
|
|
246
|
+
-f state=success -f environment_url="https://app.example.com" \
|
|
247
|
+
-f log_url="https://github.com/.../actions/runs/$RUN_ID" -f description="Live"
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
`state` values: `error`, `failure`, `inactive`, `in_progress`, `queued`, `pending`, `success`. Only `success` and `inactive` are terminal in the UI.
|
|
251
|
+
|
|
252
|
+
### List deployments
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
gh api "repos/$OWNER/$REPO/deployments?environment=production&per_page=10" \
|
|
256
|
+
--jq '.[] | {id, sha, created_at, ref}'
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Combine `--paginate` for full history, but bound it — Deployments grow unbounded.
|
|
260
|
+
|
|
261
|
+
Common errors:
|
|
262
|
+
|
|
263
|
+
- `HTTP 422: required_contexts is invalid` — pass `'[]'` literal with `-f`.
|
|
264
|
+
- `HTTP 422: Conflict merging <ref>` — `auto_merge` defaulted to true; pass `-F auto_merge=false`.
|
|
265
|
+
- `HTTP 404` — ref does not exist remotely, or token lacks `repo_deployment` (subset of `repo`).
|
|
266
|
+
|
|
267
|
+
## Quick-Reference: What `/cbp-ship` Calls
|
|
268
|
+
|
|
269
|
+
| Phase | Command |
|
|
270
|
+
|-------|---------|
|
|
271
|
+
| Open PR | `gh pr create --base ... --head ... --title ... --body-file ... --draft` |
|
|
272
|
+
| Wait for checks | `gh run watch $RUN_ID --exit-status --interval 5` |
|
|
273
|
+
| Inspect failure | `gh run view $RUN_ID --log-failed` |
|
|
274
|
+
| Pre-merge gate | `gh pr view $PR --json mergeable,mergeStateStatus,reviewDecision,headRefOid` |
|
|
275
|
+
| Merge | `gh pr merge $PR --merge --delete-branch --match-head-commit $OID` |
|
|
276
|
+
| Cut release | `gh release create $TAG --title ... --notes-file ... --target $SHA --latest` |
|
|
277
|
+
| Record deploy (non-Vercel) | `gh api repos/.../deployments --method POST -f ref=$SHA -f environment=production` |
|
|
278
|
+
|
|
279
|
+
## Pairs With
|
|
280
|
+
|
|
281
|
+
- [gh-cli-overview.md](./gh-cli-overview.md) — install, auth, config locations
|
|
282
|
+
- `/cbp-ship` skill — orchestrator that sequences these calls
|
|
283
|
+
- `.claude/rules/git-workflow.md` — branch/commit conventions enforced before any of these commands run
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# Monorepo Publishing — pnpm Workspaces
|
|
2
|
+
|
|
3
|
+
Publishing one or more packages from a pnpm workspace. Workspace layout, `pnpm publish` vs `npm publish`, per-package `publishConfig`, version-management tools (changesets vs release-please), public/private separation, pre-publish gates.
|
|
4
|
+
|
|
5
|
+
## Workspace Layout
|
|
6
|
+
|
|
7
|
+
A typical pnpm monorepo with mixed published and internal packages:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
repo/
|
|
11
|
+
├── pnpm-workspace.yaml
|
|
12
|
+
├── package.json # private: true; root tooling only
|
|
13
|
+
├── packages/
|
|
14
|
+
│ ├── published/
|
|
15
|
+
│ │ ├── cli/ # @codebyplan/cli — published
|
|
16
|
+
│ │ ├── design-tokens/
|
|
17
|
+
│ │ └── auth/
|
|
18
|
+
│ └── internal/
|
|
19
|
+
│ ├── eslint-config/ # @codebyplan/eslint-config — private
|
|
20
|
+
│ └── tsconfig/
|
|
21
|
+
└── apps/
|
|
22
|
+
├── web/ # not published
|
|
23
|
+
└── backend/
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`pnpm-workspace.yaml`:
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
packages:
|
|
30
|
+
- "packages/*/*"
|
|
31
|
+
- "apps/*"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`packages/published/*` vs `packages/internal/*` is **organisational**. What keeps internal packages off the registry is `"private": true` in their `package.json` — pnpm refuses to publish private packages and skips them silently in `pnpm -r publish`.
|
|
35
|
+
|
|
36
|
+
## Root `package.json`
|
|
37
|
+
|
|
38
|
+
Always private at the root — never accidentally publish the workspace meta-package:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"name": "codebyplan-monorepo",
|
|
43
|
+
"private": true,
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "turbo run build",
|
|
46
|
+
"test": "turbo run test",
|
|
47
|
+
"lint:packages": "turbo run lint:package",
|
|
48
|
+
"release": "changeset publish"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Per-Package `package.json`
|
|
54
|
+
|
|
55
|
+
A published workspace package needs `publishConfig`:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"name": "@codebyplan/cli",
|
|
60
|
+
"version": "1.4.2",
|
|
61
|
+
"type": "module",
|
|
62
|
+
"exports": {
|
|
63
|
+
".": {
|
|
64
|
+
"types": "./dist/index.d.ts",
|
|
65
|
+
"default": "./dist/index.js"
|
|
66
|
+
},
|
|
67
|
+
"./package.json": "./package.json"
|
|
68
|
+
},
|
|
69
|
+
"bin": { "codebyplan": "./dist/cli.js" },
|
|
70
|
+
"files": ["dist", "README.md"],
|
|
71
|
+
"engines": { "node": ">=20" },
|
|
72
|
+
"publishConfig": {
|
|
73
|
+
"access": "public",
|
|
74
|
+
"provenance": true
|
|
75
|
+
},
|
|
76
|
+
"repository": {
|
|
77
|
+
"type": "git",
|
|
78
|
+
"url": "git+https://github.com/codebyplan/codebyplan.git",
|
|
79
|
+
"directory": "packages/published/cli"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Notes:
|
|
85
|
+
|
|
86
|
+
- `repository.directory` tells the registry which subfolder this package lives in. Required for provenance attestations to validate.
|
|
87
|
+
- `publishConfig.access: "public"` is required for scoped packages (`@codebyplan/*`). Without it the first publish fails with `402 Payment Required`.
|
|
88
|
+
- `publishConfig.provenance: true` is the package.json equivalent of `--provenance` on the CLI.
|
|
89
|
+
|
|
90
|
+
An internal package just sets `"private": true`:
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"name": "@codebyplan/eslint-config",
|
|
95
|
+
"version": "0.0.0",
|
|
96
|
+
"private": true,
|
|
97
|
+
"main": "./index.js"
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## `pnpm publish` vs `npm publish`
|
|
102
|
+
|
|
103
|
+
| Capability | `pnpm publish` | `npm publish` |
|
|
104
|
+
|------------|----------------|----------------|
|
|
105
|
+
| Recursive (`-r`) — publish all changed packages | Yes | No |
|
|
106
|
+
| `--filter` to scope to a subset | Yes | No |
|
|
107
|
+
| Auto-includes workspace root `LICENSE` | Yes | No |
|
|
108
|
+
| Rewrites `workspace:*` deps to actual versions in tarball | Yes — automatic | No |
|
|
109
|
+
| `--no-git-checks` | Yes | No |
|
|
110
|
+
| OIDC trusted publishing | Yes (npm CLI underneath) | Yes |
|
|
111
|
+
|
|
112
|
+
For a pnpm monorepo, **always use `pnpm publish`** — `npm publish` will leak `workspace:*` protocol strings into the published tarball and consumers will get install errors.
|
|
113
|
+
|
|
114
|
+
`pnpm publish -r` only publishes packages whose `version` is not already on the registry — safe to run repeatedly.
|
|
115
|
+
|
|
116
|
+
## Version Management — Changesets vs release-please
|
|
117
|
+
|
|
118
|
+
Two dominant tools. Both work with pnpm + GitHub Actions OIDC.
|
|
119
|
+
|
|
120
|
+
### Changesets
|
|
121
|
+
|
|
122
|
+
Maintainer-friendly — every PR adds a `.changeset/*.md` file declaring intent:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
.changeset/funky-tigers-dance.md
|
|
126
|
+
---
|
|
127
|
+
"@codebyplan/cli": minor
|
|
128
|
+
"@codebyplan/auth": patch
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
Added --provenance flag to publish command. Auth payload now includes scope.
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Workflow:
|
|
135
|
+
|
|
136
|
+
1. `pnpm changeset` while authoring a PR. Pick affected packages and bump types.
|
|
137
|
+
2. PR merges to main. Changesets GitHub Action opens (or updates) a "Version Packages" PR that consumes pending changesets, bumps versions, regenerates CHANGELOGs.
|
|
138
|
+
3. Merging the Version Packages PR triggers `pnpm changeset publish` → calls `pnpm publish -r` for every bumped package.
|
|
139
|
+
|
|
140
|
+
`.changeset/config.json` essentials:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"changelog": "@changesets/cli/changelog",
|
|
145
|
+
"commit": false,
|
|
146
|
+
"fixed": [],
|
|
147
|
+
"linked": [],
|
|
148
|
+
"access": "public",
|
|
149
|
+
"baseBranch": "main",
|
|
150
|
+
"updateInternalDependencies": "patch",
|
|
151
|
+
"ignore": ["@codebyplan/web", "@codebyplan/backend"]
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`ignore` excludes apps that are deployed but never published.
|
|
156
|
+
|
|
157
|
+
Strengths: explicit intent per change; supports linked/fixed package groups; rich CHANGELOG output. Weaknesses: every PR must remember to add a changeset (the bot enforces this).
|
|
158
|
+
|
|
159
|
+
### release-please
|
|
160
|
+
|
|
161
|
+
Conventional-commit-driven — version is inferred from commit messages (`feat:` → minor, `fix:` → patch, `feat!:` or `BREAKING CHANGE:` → major):
|
|
162
|
+
|
|
163
|
+
1. Developer writes conventional-commit messages.
|
|
164
|
+
2. release-please GitHub Action watches main, opens a release PR per package summarising commits since the last tag.
|
|
165
|
+
3. Merging the release PR creates a git tag, GitHub release, and triggers a separate publish workflow.
|
|
166
|
+
|
|
167
|
+
`release-please-config.json`:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"packages": {
|
|
172
|
+
"packages/published/cli": { "release-type": "node" },
|
|
173
|
+
"packages/published/auth": { "release-type": "node" }
|
|
174
|
+
},
|
|
175
|
+
"plugins": ["node-workspace"]
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The `node-workspace` plugin keeps `workspace:*` deps in sync across the bump.
|
|
180
|
+
|
|
181
|
+
Strengths: zero per-PR ceremony when commit hygiene is enforced. Weaknesses: monorepo config is more involved; granular bump control is harder.
|
|
182
|
+
|
|
183
|
+
### Picking one
|
|
184
|
+
|
|
185
|
+
- Conventional commits enforced → release-please.
|
|
186
|
+
- Want explicit per-PR intent and rich CHANGELOGs → Changesets.
|
|
187
|
+
- OSS / mixed contributors → Changesets (the bot reminds people).
|
|
188
|
+
|
|
189
|
+
CodeByPlan uses Changesets for `packages/published/*`.
|
|
190
|
+
|
|
191
|
+
## Pre-Publish Gates
|
|
192
|
+
|
|
193
|
+
Run these in CI before `pnpm publish -r`:
|
|
194
|
+
|
|
195
|
+
| # | Gate | Command |
|
|
196
|
+
|---|------|---------|
|
|
197
|
+
| 1 | Build | `pnpm -r --filter "./packages/published/*" build` |
|
|
198
|
+
| 2 | Test | `pnpm -r --filter "./packages/published/*" test` |
|
|
199
|
+
| 3 | publint | `pnpm -r --filter "./packages/published/*" exec publint` |
|
|
200
|
+
| 4 | attw | `pnpm -r --filter "./packages/published/*" exec attw --pack .` |
|
|
201
|
+
| 5 | Dry-run pack | `pnpm -r --filter "./packages/published/*" publish --dry-run` |
|
|
202
|
+
|
|
203
|
+
[publint](https://publint.dev) catches missing `exports`, broken `main` pointers, type-definition exclusion, ESM/CJS misalignment. [Are The Types Wrong](https://arethetypeswrong.github.io/) validates that the published tarball resolves correctly under both CJS and ESM consumers across conditional-export combinations.
|
|
204
|
+
|
|
205
|
+
The dry-run output lists every file that would be in the tarball plus total size. Compare against `files` in `package.json` — anything outside `files` means the glob is wrong (or `.npmignore` is interfering).
|
|
206
|
+
|
|
207
|
+
`pnpm publish -r` skips already-published versions automatically, but for clarity:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
pnpm -r --filter "./packages/published/*" exec sh -c \
|
|
211
|
+
'npm view "$npm_package_name@$npm_package_version" version 2>/dev/null \
|
|
212
|
+
&& echo "ALREADY PUBLISHED" && exit 1 || true'
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Wire gates 3-4 as a turbo task in each package's `package.json`:
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"scripts": {
|
|
220
|
+
"lint:package": "publint && attw --pack ."
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Then in CI before publish: `pnpm turbo run lint:package`.
|
|
226
|
+
|
|
227
|
+
## Public / Private Separation Recap
|
|
228
|
+
|
|
229
|
+
| Layer | Mechanism | Purpose |
|
|
230
|
+
|-------|-----------|---------|
|
|
231
|
+
| Folder convention | `packages/published/*` vs `packages/internal/*` | Human signal |
|
|
232
|
+
| `private: true` | `package.json` per internal package | Hard block — pnpm refuses to publish |
|
|
233
|
+
| Changesets `ignore` | `.changeset/config.json` | Keeps deployed apps off the version PR |
|
|
234
|
+
| Workspace dep protocol | `"@codebyplan/foo": "workspace:*"` | Internal deps stay internal; pnpm rewrites at publish time |
|
|
235
|
+
|
|
236
|
+
When an internal package is published by mistake:
|
|
237
|
+
|
|
238
|
+
1. `npm unpublish @scope/pkg@x.y.z` within 72 hours, OR
|
|
239
|
+
2. `npm deprecate @scope/pkg@"<= x.y.z" "Internal package — do not use"` after that.
|
|
240
|
+
|
|
241
|
+
Then add `"private": true` and ship.
|
|
242
|
+
|
|
243
|
+
## Sources
|
|
244
|
+
|
|
245
|
+
- [pnpm — publish](https://pnpm.io/cli/publish)
|
|
246
|
+
- [pnpm — workspaces](https://pnpm.io/workspaces)
|
|
247
|
+
- [npm Docs — package.json](https://docs.npmjs.com/cli/v10/configuring-npm/package-json)
|
|
248
|
+
- [Changesets](https://github.com/changesets/changesets)
|
|
249
|
+
- [release-please](https://github.com/googleapis/release-please)
|
|
250
|
+
- [publint](https://publint.dev/)
|
|
251
|
+
- [Are The Types Wrong](https://arethetypeswrong.github.io/)
|
|
252
|
+
- [npm CLI source](https://github.com/npm/cli)
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# npm OIDC Trusted Publishing
|
|
2
|
+
|
|
3
|
+
Modern publish flow: no NPM_TOKEN, no leak risk. npm trusts a short-lived OIDC token issued by your CI provider. Provenance attestations are generated automatically and recorded in a public transparency ledger (Sigstore).
|
|
4
|
+
|
|
5
|
+
This is the **default publish path for `/cbp-ship`** when the npm-package surface is in play — it's the publish path the orchestrator should reach for first.
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
1. CI job starts. The CI provider (GitHub Actions, GitLab CI, CircleCI) mints a short-lived OIDC ID token signed with its public key.
|
|
10
|
+
2. `npm publish` reads the OIDC token from the runner and exchanges it with the npm registry for a one-shot publish credential.
|
|
11
|
+
3. npm validates the OIDC token's claims — `repository`, `workflow`, `ref`, `aud` — against the trusted-publisher rule registered for the package.
|
|
12
|
+
4. If the claims match, the publish is accepted. If not, the publish is rejected with `403`.
|
|
13
|
+
5. (GitHub Actions / GitLab only) npm automatically generates a Sigstore provenance attestation linking the published tarball to the exact commit, workflow file, and runner image that built it. The attestation is logged to the [Rekor transparency ledger](https://search.sigstore.dev/).
|
|
14
|
+
|
|
15
|
+
The OIDC token's lifetime is measured in minutes; it is bound to the workflow run; it cannot be replayed. There is nothing for an attacker to exfiltrate from the runner that survives the job.
|
|
16
|
+
|
|
17
|
+
## Supported Providers
|
|
18
|
+
|
|
19
|
+
| Provider | OIDC | Auto-provenance |
|
|
20
|
+
|----------|------|-----------------|
|
|
21
|
+
| GitHub Actions (GitHub-hosted runners) | Yes | Yes |
|
|
22
|
+
| GitLab CI/CD (GitLab.com shared runners) | Yes | Yes |
|
|
23
|
+
| CircleCI cloud | Yes | No (must pass `--provenance` manually with extra setup) |
|
|
24
|
+
| Self-hosted runners (any provider) | Not supported | Not supported |
|
|
25
|
+
|
|
26
|
+
Self-hosted runners cannot use trusted publishing today — the npm registry does not yet validate signing certificates from non-cloud OIDC issuers.
|
|
27
|
+
|
|
28
|
+
## Setup — GitHub Actions
|
|
29
|
+
|
|
30
|
+
### Step 1 — Register the trusted publisher on npmjs.com
|
|
31
|
+
|
|
32
|
+
1. Sign in at https://www.npmjs.com.
|
|
33
|
+
2. Navigate to your package: `npmjs.com/package/<your-pkg>`.
|
|
34
|
+
3. Settings → **Trusted Publishers** → **Add publisher**.
|
|
35
|
+
4. Select **GitHub Actions**, then fill in:
|
|
36
|
+
- **Organization or user**: e.g. `codebyplan`
|
|
37
|
+
- **Repository**: e.g. `codebyplan`
|
|
38
|
+
- **Workflow filename**: e.g. `release.yml` (just the filename, not the full path)
|
|
39
|
+
- **Environment** (optional): e.g. `production` — restricts to runs scoped to a GitHub Actions environment
|
|
40
|
+
|
|
41
|
+
If the package does not yet exist on the registry, you must publish it once with a classic token before you can register a trusted publisher (npm requires the package to exist to attach a trust rule).
|
|
42
|
+
|
|
43
|
+
### Step 2 — Update the workflow
|
|
44
|
+
|
|
45
|
+
Add `id-token: write` to the job's permissions and use a recent npm CLI (≥9.5.0; npm 10+ is recommended).
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
name: Release
|
|
49
|
+
|
|
50
|
+
on:
|
|
51
|
+
push:
|
|
52
|
+
tags:
|
|
53
|
+
- "v*"
|
|
54
|
+
|
|
55
|
+
jobs:
|
|
56
|
+
publish:
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
|
|
59
|
+
permissions:
|
|
60
|
+
contents: read # checkout
|
|
61
|
+
id-token: write # OIDC — required for trusted publishing
|
|
62
|
+
attestations: write # required for provenance attestations
|
|
63
|
+
|
|
64
|
+
steps:
|
|
65
|
+
- uses: actions/checkout@v4
|
|
66
|
+
|
|
67
|
+
- uses: actions/setup-node@v4
|
|
68
|
+
with:
|
|
69
|
+
node-version: "20"
|
|
70
|
+
registry-url: "https://registry.npmjs.org"
|
|
71
|
+
|
|
72
|
+
- uses: pnpm/action-setup@v4
|
|
73
|
+
with:
|
|
74
|
+
version: 10
|
|
75
|
+
|
|
76
|
+
- run: pnpm install --frozen-lockfile
|
|
77
|
+
|
|
78
|
+
- run: pnpm build
|
|
79
|
+
|
|
80
|
+
- run: pnpm test
|
|
81
|
+
|
|
82
|
+
- name: Verify publish (dry run)
|
|
83
|
+
run: pnpm publish --dry-run --access public
|
|
84
|
+
|
|
85
|
+
- name: Lint package shape
|
|
86
|
+
run: |
|
|
87
|
+
pnpm dlx publint
|
|
88
|
+
pnpm dlx @arethetypeswrong/cli --pack
|
|
89
|
+
|
|
90
|
+
- name: Publish to npm
|
|
91
|
+
run: pnpm publish --access public --provenance --no-git-checks
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Notes:
|
|
95
|
+
|
|
96
|
+
- **No `NODE_AUTH_TOKEN` env var.** That's the entire point — there is no token to leak. The `setup-node` action wires up `.npmrc` to the registry URL; OIDC handshake happens inside `npm publish`.
|
|
97
|
+
- `--provenance` is **automatic** on GitHub Actions OIDC publishes (npm enables it implicitly), but passing the flag is harmless and makes intent explicit. `publishConfig.provenance: true` in `package.json` is the third equivalent.
|
|
98
|
+
- `--no-git-checks` (pnpm-only) bypasses the "current branch must be the publish branch, working tree must be clean" check. Safe in CI where the runner has a fresh clone at a tag.
|
|
99
|
+
|
|
100
|
+
## Provenance Attestations
|
|
101
|
+
|
|
102
|
+
A provenance attestation is a JSON document signed by Sigstore that describes:
|
|
103
|
+
|
|
104
|
+
- The source repository URL and the exact commit SHA that was built.
|
|
105
|
+
- The workflow file path and the run ID.
|
|
106
|
+
- The builder identity (e.g. `https://github.com/actions/runner`).
|
|
107
|
+
- The npm package name, version, and tarball SHA.
|
|
108
|
+
|
|
109
|
+
After publish, the npm registry shows a "Provenance" badge on the package page linking to the Sigstore record.
|
|
110
|
+
|
|
111
|
+
### Verifying attestations as a consumer
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm audit signatures
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
This walks every dependency in the current project, checks the registry signature on each tarball, and validates any provenance attestations. A failure means either the package was tampered with after publish or its signing identity changed unexpectedly.
|
|
118
|
+
|
|
119
|
+
For a single package without installing it:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npm view <pkg> --json | jq '.dist.attestations'
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Returns the URL of the attestation bundle on the registry; the bundle is a Sigstore-format JSON that can be verified with `cosign` or the `sigstore-js` library.
|
|
126
|
+
|
|
127
|
+
## Comparison vs Classic NPM_TOKEN
|
|
128
|
+
|
|
129
|
+
| Aspect | Classic NPM_TOKEN | OIDC Trusted Publishing |
|
|
130
|
+
|--------|-------------------|--------------------------|
|
|
131
|
+
| Secret stored in CI | Yes — long-lived token | No |
|
|
132
|
+
| Rotation burden | Manual; forgotten tokens are common | None — every publish mints a fresh credential |
|
|
133
|
+
| Leak blast radius | Token works from anywhere until revoked | Credential expires in minutes; bound to one workflow run |
|
|
134
|
+
| Provenance | Manual `--provenance` + extra setup | Automatic on GitHub Actions / GitLab |
|
|
135
|
+
| Setup complexity | Generate token, paste into CI secrets | One-time npm web UI registration |
|
|
136
|
+
| Works on self-hosted runners | Yes | No (today) |
|
|
137
|
+
| Works locally | Yes | No (no OIDC issuer) |
|
|
138
|
+
|
|
139
|
+
Classic tokens still have a place: local release machines, self-hosted runners, providers npm doesn't trust yet. For everything else, OIDC is the default in 2026.
|
|
140
|
+
|
|
141
|
+
## Failure Modes
|
|
142
|
+
|
|
143
|
+
| Symptom | Cause | Fix |
|
|
144
|
+
|---------|-------|-----|
|
|
145
|
+
| `403 Forbidden — OIDC publish requires id-token: write` | Workflow missing the permission | Add `permissions.id-token: write` to the job |
|
|
146
|
+
| `403 — trusted publisher mismatch` | Workflow filename, repo, or org doesn't match the rule | Re-check the trusted-publisher rule on npmjs.com matches `${{ github.repository }}` and `${{ github.workflow_ref }}` exactly |
|
|
147
|
+
| `Provenance generation failed: repository field mismatch` | `package.json` `repository.url` doesn't match the GitHub repo URL | Set `"repository": { "type": "git", "url": "git+https://github.com/<org>/<repo>.git" }` (case-sensitive) |
|
|
148
|
+
| Attestations missing on the registry page | Used CircleCI without manual provenance setup | Either accept (CircleCI limitation) or migrate to GitHub Actions / GitLab |
|
|
149
|
+
| `OIDC token unavailable` from a self-hosted runner | Trusted publishing not supported there | Fall back to a granular token, or move the publish job to a GitHub-hosted runner |
|
|
150
|
+
|
|
151
|
+
## Sources
|
|
152
|
+
|
|
153
|
+
- [npm Docs — Trusted publishers](https://docs.npmjs.com/trusted-publishers)
|
|
154
|
+
- [npm Docs — Generating provenance statements](https://docs.npmjs.com/generating-provenance-statements)
|
|
155
|
+
- [npm Docs — npm audit signatures](https://docs.npmjs.com/cli/v10/commands/npm-audit)
|
|
156
|
+
- [Sigstore](https://www.sigstore.dev/) — signing infrastructure
|
|
157
|
+
- [npm CLI source](https://github.com/npm/cli)
|