@natjswenson/shipflow 0.3.2 → 0.3.3
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 +53 -15
- package/SKILL.md +25 -1
- package/package.json +1 -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/CHANGELOG.md
CHANGED
|
@@ -2,21 +2,59 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@natjswenson/shipflow` are documented here.
|
|
4
4
|
|
|
5
|
-
## 0.3.
|
|
6
|
-
|
|
7
|
-
- **
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
5
|
+
## 0.3.3 (2026-08-01) — least-privilege permissions in every rendered workflow
|
|
6
|
+
|
|
7
|
+
- **Every rendered workflow granted its permissions at the workflow level, so
|
|
8
|
+
every job in the file got them whether it needed them or not.** zizmor flags
|
|
9
|
+
this as `excessive-permissions` (High), and it was failing this repo's own
|
|
10
|
+
`security / workflows` gate on `dev-to-main-automerge.yml`. All six templates
|
|
11
|
+
now deny by default (`permissions: {}`) and grant per job.
|
|
12
|
+
|
|
13
|
+
A workflow-level grant is not just untidy — it silently extends to jobs added
|
|
14
|
+
later that were never reviewed for it. Scoping is the fix; waiving the rule
|
|
15
|
+
would have kept the finding *and* the exposure.
|
|
16
|
+
|
|
17
|
+
- **`contents: write` is dropped from the auto-merge workflows entirely, not
|
|
18
|
+
moved.** `gh pr merge --auto` only *enables* native auto-merge, which is a
|
|
19
|
+
pull-requests operation; GitHub performs the merge itself afterwards under its
|
|
20
|
+
own automation rather than this token. Those workflows now grant
|
|
21
|
+
`pull-requests: write` to the two jobs that call `gh`, and nothing else.
|
|
22
|
+
|
|
23
|
+
Where `release.releaseCredential` names a real PAT (the supported setup), the
|
|
24
|
+
workflow token's permissions never applied to those steps in the first place.
|
|
25
|
+
These grants are what matters for a repo that left the credential defaulted to
|
|
26
|
+
`GITHUB_TOKEN`.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **The gitflow merge-back workflows could not open their own fallback PR.**
|
|
31
|
+
`release-merge-back` and `hotfix-merge-back` granted only `contents: write`,
|
|
32
|
+
but their "on any failure, open a PR for manual resolution" step calls
|
|
33
|
+
`gh pr create` — which needs `pull-requests: write`. Under a defaulted
|
|
34
|
+
`GITHUB_TOKEN` that step would have failed exactly when it was needed most:
|
|
35
|
+
after a merge conflict, with nothing else left to surface it. Found while
|
|
36
|
+
scoping the permissions above, not by a report.
|
|
37
|
+
|
|
38
|
+
## 0.3.2 (2026-07-28) — attempted npm-page fix; did NOT work
|
|
39
|
+
|
|
40
|
+
- **Attempted and failed: making npmjs.com render the README.** 0.3.1 put
|
|
41
|
+
README.md, LICENSE and CHANGELOG.md into the tarball (verified by downloading
|
|
42
|
+
the published artifact), which fixed what `npm install` delivers. This release
|
|
43
|
+
additionally staged those files into the package directory before
|
|
44
|
+
`npm publish` was invoked, on the theory that npm populates the registry
|
|
45
|
+
manifest's `readme` field from disk at that point. **It did not work.** After
|
|
46
|
+
publishing, `npm view <pkg> readme` still returns the literal string
|
|
47
|
+
`ERROR: No README data found!` for both packages.
|
|
48
|
+
That field appears to be sticky: it was set once by an early release that had
|
|
49
|
+
no README, and republishing with one present does not overwrite it. Since
|
|
50
|
+
npmjs.com only falls back to reading the tarball when the field is *absent*
|
|
51
|
+
(compare `zod`, which renders fine with none at all), the stale error string
|
|
52
|
+
keeps winning.
|
|
53
|
+
The ineffective staging step was reverted in the release workflow; the
|
|
54
|
+
`prepack` hook stays, because the tarball fix is real and verified. **The npm
|
|
55
|
+
web page for both packages is still blank** — an open issue, likely needing
|
|
56
|
+
npm support to clear the cached field.
|
|
57
|
+
Code unchanged in both skills.
|
|
20
58
|
|
|
21
59
|
## 0.3.1 (2026-07-28) — publish the README, LICENSE and CHANGELOG to npm
|
|
22
60
|
|
package/SKILL.md
CHANGED
|
@@ -62,7 +62,31 @@ user; the CLI is the only thing that *does*.
|
|
|
62
62
|
|
|
63
63
|
4. **Confirm branch names and required checks with the user.** Show `workflows.jobNames` from the detect output as candidate `requiredChecks` (this list is already filtered to jobs from workflows that actually trigger on `pull_request` — a job that only runs on `schedule`/`workflow_dispatch` can never satisfy a required check, so it's never offered as a candidate) and let the user confirm/edit the list. **An empty `requiredChecks` list is a fail-open state, not a valid steady state** — `shipflow apply` will hard-refuse to enable auto-merge with zero required checks (see Error handling below). Don't let the user skip this without understanding that consequence.
|
|
64
64
|
|
|
65
|
-
**If the candidate list is empty,
|
|
65
|
+
**If the candidate list is empty, a CI workflow has to exist before auto-merge
|
|
66
|
+
can be enabled. Hand that job to the `forge` skill** — authoring and *verifying*
|
|
67
|
+
workflow YAML is its whole subject, and it does things shipflow never will:
|
|
68
|
+
it resolves every action ref against the real API (no linter checks that an
|
|
69
|
+
action exists), validates each `with:` key against the action's own
|
|
70
|
+
`action.yml`, reports how many majors behind each pin is, and runs actionlint
|
|
71
|
+
and zizmor before showing you anything. Two skills answering "scaffold me a CI
|
|
72
|
+
workflow" differently is worse than either answer.
|
|
73
|
+
|
|
74
|
+
> Use the forge skill to create a `pull_request`-triggered build+test workflow
|
|
75
|
+
> for this repo, then come back here with the job name.
|
|
76
|
+
|
|
77
|
+
**If forge is not installed**, draft it here instead: investigate the repo
|
|
78
|
+
directly (`package.json`, `Cargo.toml`, `project.yml`/`.xcodeproj`, `go.mod`,
|
|
79
|
+
`pyproject.toml`, or whatever's actually there) and write a minimal,
|
|
80
|
+
conservative `pull_request`-triggered build+test workflow.
|
|
81
|
+
**Never silently overwrite an existing workflow file.** Present it and wait for
|
|
82
|
+
explicit confirmation before writing it — the same confirm-before-write pattern
|
|
83
|
+
as everything else in this skill.
|
|
84
|
+
|
|
85
|
+
Either way, say plainly that a fresh workflow is a starting point, not a
|
|
86
|
+
guarantee it's green on the first run — **a required check that never passes
|
|
87
|
+
blocks every future merge**, so the user should watch it run successfully
|
|
88
|
+
before relying on it as one. Once it exists, re-run step 1's `detect` (repo
|
|
89
|
+
state changed) and continue this step with the new job name as a real candidate.
|
|
66
90
|
|
|
67
91
|
5. **Resolve `protectionOwner`:**
|
|
68
92
|
- `"external"` → tell the user which settings-as-code artifact was found (`settingsAsCodeArtifact` in the detect output) and that shipflow will defer to it, managing only cleanup/automerge/release, not installing a competing ruleset.
|
package/package.json
CHANGED
|
@@ -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 }}"
|