@natjswenson/shipflow 0.3.2 → 0.4.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/CHANGELOG.md +131 -15
- package/README.md +107 -26
- package/SKILL.md +99 -1
- package/bin/shipflow.js +124 -0
- package/lib/apply.mjs +8 -1
- package/lib/release.mjs +813 -0
- package/package.json +1 -1
- package/skill-invariants.json +21 -1
- package/templates/dev-main-promotion/dev-to-main-automerge.yml.tmpl +14 -3
- package/templates/gitflow/hotfix-automerge.yml.tmpl +11 -3
- package/templates/gitflow/hotfix-merge-back.yml.tmpl +8 -2
- package/templates/gitflow/release-automerge.yml.tmpl +11 -3
- package/templates/gitflow/release-merge-back.yml.tmpl +8 -2
- package/templates/github-flow/main-automerge.yml.tmpl +11 -3
package/package.json
CHANGED
package/skill-invariants.json
CHANGED
|
@@ -81,13 +81,33 @@
|
|
|
81
81
|
"pattern": "always with the explicit\\s+`@latest` tag, never bare",
|
|
82
82
|
"rationale": "Self-discovered 2026-07-15 during PAT-wiring dogfood on claude-skills itself: a bare `npx -y @natjswenson/shipflow <command>` silently resolved a stale global install (0.2.0) instead of fetching the current version from the registry, with no warning — meaning every fix through 0.2.5 (including the Critical template-injection fix) was silently skipped. Every CLI invocation in this file must pin @latest."
|
|
83
83
|
},
|
|
84
|
+
{
|
|
85
|
+
"id": "collateral-must-be-named-before-cutting",
|
|
86
|
+
"pattern": "[Nn]ever run `release-cut` without naming that list to the user first",
|
|
87
|
+
"rationale": "A dev -> main promotion is atomic and carries ALL of dev, so cutting a release for one named component also releases every other component sitting bumped-but-untagged on dev. That is the worst failure this flow has — the user asked for one thing and got several, with tags and npm publishes that cannot be un-cut. The collateral list must be spoken aloud before the irreversible step, not merely present in JSON the agent may skim past."
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "tag-read-back-is-the-only-proof",
|
|
91
|
+
"pattern": "A dispatched workflow, a merged PR and a green check are \\*\\*not\\*\\* a release",
|
|
92
|
+
"rationale": "Every intermediate signal in the release path can succeed while the tag is never cut — a dispatch exits 0 for a run that later fails, a promotion can merge while the release job errors, and _release.yml deliberately no-ops when a tag already exists. The only evidence a release happened is the tag fetched back from origin, which is why release-cut polls for it and why the agent must not report success from anything else."
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "zero-major-bump-cap-is-the-users-call",
|
|
96
|
+
"pattern": "going to 1\\.0\\.0 is a\\s+release decision, never a commit message",
|
|
97
|
+
"rationale": "Conventional-commit tooling maps a breaking change to a major bump, which for an 0.x component means silently declaring 1.0.0 — an API-stability promise no commit message is entitled to make on the maintainer's behalf. suggestBump caps it at minor and reports capped: true so the promotion is offered, never taken."
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": "prepare-isolated-from-unrelated-work",
|
|
101
|
+
"pattern": "throwaway git worktree",
|
|
102
|
+
"rationale": "A real repo's working tree routinely holds unrelated uncommitted work from parallel sessions — this monorepo's did, including an entire untracked skill, while this feature was written. Branching and staging under that tree is how someone else's in-flight work gets swept into a release commit. The isolated worktree makes that structurally impossible rather than relying on careful pathspecs."
|
|
103
|
+
},
|
|
84
104
|
{
|
|
85
105
|
"id": "ambiguous-pattern-no-silent-pick",
|
|
86
106
|
"pattern": "present all 3 (templates|patterns).{0,60}ask the user to choose",
|
|
87
107
|
"rationale": "Ambiguous/greenfield autodetection must never silently pick a workflow pattern — mirrors the existing protectionOwner disambiguation precedent (ambiguous-protection-owner-prompt)."
|
|
88
108
|
}
|
|
89
109
|
],
|
|
90
|
-
"cli_commands_referenced": ["detect", "plan", "apply", "releases", "release-dispatch", "rename-default-branch"],
|
|
110
|
+
"cli_commands_referenced": ["detect", "plan", "apply", "releases", "release-dispatch", "release-status", "release-prepare", "release-cut", "rename-default-branch"],
|
|
91
111
|
"_baseline_comment": "Baseline eval sets: deterministic, offline, $0 checks pinned against artifacts from real local runs. These gate `ci / shipflow` alongside the unit tests. Every entry names the test that enforces it so tools/lint_baseline.py can verify the declaration is not aspirational.",
|
|
92
112
|
"baseline": [
|
|
93
113
|
{
|
|
@@ -24,9 +24,18 @@ on:
|
|
|
24
24
|
types: [opened, reopened, synchronize, closed]
|
|
25
25
|
branches: [{{MAIN_BRANCH}}]
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
# Deny by default at the workflow level, grant per job. A workflow-level grant
|
|
28
|
+
# applies to every job in the file, including ones added later that never
|
|
29
|
+
# needed it — which is why zizmor's excessive-permissions rule flags it, and
|
|
30
|
+
# why the fix is to scope rather than to waive.
|
|
31
|
+
#
|
|
32
|
+
# `contents: write` is dropped entirely, not moved. `gh pr merge --auto` only
|
|
33
|
+
# *enables* native auto-merge, a pull-requests operation; GitHub performs the
|
|
34
|
+
# merge itself afterwards, under its own automation rather than this token. The
|
|
35
|
+
# grants below are what matters when releaseCredential is left as GITHUB_TOKEN
|
|
36
|
+
# — where a real PAT is configured (the supported setup, see above), the
|
|
37
|
+
# workflow token's permissions do not apply to these steps at all.
|
|
38
|
+
permissions: {}
|
|
30
39
|
|
|
31
40
|
jobs:
|
|
32
41
|
# Enables native GitHub auto-merge on open/reopen/synchronize — this job
|
|
@@ -39,6 +48,8 @@ jobs:
|
|
|
39
48
|
github.event.action != 'closed' &&
|
|
40
49
|
github.event.pull_request.head.ref == '{{DEV_BRANCH}}'
|
|
41
50
|
runs-on: ubuntu-latest
|
|
51
|
+
permissions:
|
|
52
|
+
pull-requests: write
|
|
42
53
|
steps:
|
|
43
54
|
- name: Enable auto-merge
|
|
44
55
|
run: gh pr merge --auto {{MERGE_FLAG}} "${{ github.event.pull_request.number }}" --repo "${{ github.repository }}"
|
|
@@ -24,9 +24,15 @@ on:
|
|
|
24
24
|
types: [opened, reopened, synchronize, closed]
|
|
25
25
|
branches: [{{MAIN_BRANCH}}]
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
# Deny by default at the workflow level, grant per job. A workflow-level grant
|
|
28
|
+
# applies to every job in the file, including ones added later that never
|
|
29
|
+
# needed it — which is why zizmor's excessive-permissions rule flags it, and
|
|
30
|
+
# why the fix is to scope rather than to waive.
|
|
31
|
+
#
|
|
32
|
+
# `contents: write` is dropped entirely, not moved. `gh pr merge --auto` only
|
|
33
|
+
# *enables* native auto-merge, a pull-requests operation; GitHub performs the
|
|
34
|
+
# merge itself afterwards, under its own automation rather than this token.
|
|
35
|
+
permissions: {}
|
|
30
36
|
|
|
31
37
|
jobs:
|
|
32
38
|
# Enables native GitHub auto-merge on open/reopen/synchronize — this job
|
|
@@ -39,6 +45,8 @@ jobs:
|
|
|
39
45
|
github.event.action != 'closed' &&
|
|
40
46
|
startsWith(github.event.pull_request.head.ref, '{{HOTFIX_BRANCH_PREFIX}}')
|
|
41
47
|
runs-on: ubuntu-latest
|
|
48
|
+
permissions:
|
|
49
|
+
pull-requests: write
|
|
42
50
|
steps:
|
|
43
51
|
- name: Enable auto-merge
|
|
44
52
|
run: gh pr merge --auto {{MERGE_FLAG}} "${{ github.event.pull_request.number }}" --repo "${{ github.repository }}"
|
|
@@ -15,8 +15,11 @@ on:
|
|
|
15
15
|
types: [closed]
|
|
16
16
|
branches: [{{MAIN_BRANCH}}]
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
# Deny by default at the workflow level, grant per job — a workflow-level grant
|
|
19
|
+
# applies to every job in the file, including ones added later that never needed
|
|
20
|
+
# it. `contents: write` is genuinely required here (this job pushes a merge
|
|
21
|
+
# commit), so it is scoped to the job rather than dropped.
|
|
22
|
+
permissions: {}
|
|
20
23
|
|
|
21
24
|
jobs:
|
|
22
25
|
merge-back-to-dev:
|
|
@@ -24,6 +27,9 @@ jobs:
|
|
|
24
27
|
github.event.pull_request.merged == true &&
|
|
25
28
|
startsWith(github.event.pull_request.head.ref, '{{HOTFIX_BRANCH_PREFIX}}')
|
|
26
29
|
runs-on: ubuntu-latest
|
|
30
|
+
permissions:
|
|
31
|
+
contents: write
|
|
32
|
+
pull-requests: write
|
|
27
33
|
steps:
|
|
28
34
|
- uses: actions/checkout@v4
|
|
29
35
|
with:
|
|
@@ -24,9 +24,15 @@ on:
|
|
|
24
24
|
types: [opened, reopened, synchronize, closed]
|
|
25
25
|
branches: [{{MAIN_BRANCH}}]
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
# Deny by default at the workflow level, grant per job. A workflow-level grant
|
|
28
|
+
# applies to every job in the file, including ones added later that never
|
|
29
|
+
# needed it — which is why zizmor's excessive-permissions rule flags it, and
|
|
30
|
+
# why the fix is to scope rather than to waive.
|
|
31
|
+
#
|
|
32
|
+
# `contents: write` is dropped entirely, not moved. `gh pr merge --auto` only
|
|
33
|
+
# *enables* native auto-merge, a pull-requests operation; GitHub performs the
|
|
34
|
+
# merge itself afterwards, under its own automation rather than this token.
|
|
35
|
+
permissions: {}
|
|
30
36
|
|
|
31
37
|
jobs:
|
|
32
38
|
# Enables native GitHub auto-merge on open/reopen/synchronize — this job
|
|
@@ -39,6 +45,8 @@ jobs:
|
|
|
39
45
|
github.event.action != 'closed' &&
|
|
40
46
|
startsWith(github.event.pull_request.head.ref, '{{RELEASE_BRANCH_PREFIX}}')
|
|
41
47
|
runs-on: ubuntu-latest
|
|
48
|
+
permissions:
|
|
49
|
+
pull-requests: write
|
|
42
50
|
steps:
|
|
43
51
|
- name: Enable auto-merge
|
|
44
52
|
run: gh pr merge --auto {{MERGE_FLAG}} "${{ github.event.pull_request.number }}" --repo "${{ github.repository }}"
|
|
@@ -15,8 +15,11 @@ on:
|
|
|
15
15
|
types: [closed]
|
|
16
16
|
branches: [{{MAIN_BRANCH}}]
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
# Deny by default at the workflow level, grant per job — a workflow-level grant
|
|
19
|
+
# applies to every job in the file, including ones added later that never needed
|
|
20
|
+
# it. `contents: write` is genuinely required here (this job pushes a merge
|
|
21
|
+
# commit), so it is scoped to the job rather than dropped.
|
|
22
|
+
permissions: {}
|
|
20
23
|
|
|
21
24
|
jobs:
|
|
22
25
|
merge-back-to-dev:
|
|
@@ -24,6 +27,9 @@ jobs:
|
|
|
24
27
|
github.event.pull_request.merged == true &&
|
|
25
28
|
startsWith(github.event.pull_request.head.ref, '{{RELEASE_BRANCH_PREFIX}}')
|
|
26
29
|
runs-on: ubuntu-latest
|
|
30
|
+
permissions:
|
|
31
|
+
contents: write
|
|
32
|
+
pull-requests: write
|
|
27
33
|
steps:
|
|
28
34
|
- uses: actions/checkout@v4
|
|
29
35
|
with:
|
|
@@ -28,9 +28,15 @@ on:
|
|
|
28
28
|
types: [opened, reopened, synchronize, closed]
|
|
29
29
|
branches: [{{MAIN_BRANCH}}]
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
# Deny by default at the workflow level, grant per job. A workflow-level grant
|
|
32
|
+
# applies to every job in the file, including ones added later that never
|
|
33
|
+
# needed it — which is why zizmor's excessive-permissions rule flags it, and
|
|
34
|
+
# why the fix is to scope rather than to waive.
|
|
35
|
+
#
|
|
36
|
+
# `contents: write` is dropped entirely, not moved. `gh pr merge --auto` only
|
|
37
|
+
# *enables* native auto-merge, a pull-requests operation; GitHub performs the
|
|
38
|
+
# merge itself afterwards, under its own automation rather than this token.
|
|
39
|
+
permissions: {}
|
|
34
40
|
|
|
35
41
|
jobs:
|
|
36
42
|
# Enables native GitHub auto-merge on open/reopen/synchronize — this job
|
|
@@ -41,6 +47,8 @@ jobs:
|
|
|
41
47
|
auto-merge:
|
|
42
48
|
if: github.event.action != 'closed'
|
|
43
49
|
runs-on: ubuntu-latest
|
|
50
|
+
permissions:
|
|
51
|
+
pull-requests: write
|
|
44
52
|
steps:
|
|
45
53
|
- name: Enable auto-merge
|
|
46
54
|
run: gh pr merge --auto {{MERGE_FLAG}} "${{ github.event.pull_request.number }}" --repo "${{ github.repository }}"
|