@natjswenson/shipflow 0.3.1 → 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 CHANGED
@@ -2,6 +2,60 @@
2
2
 
3
3
  All notable changes to `@natjswenson/shipflow` are documented here.
4
4
 
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.
58
+
5
59
  ## 0.3.1 (2026-07-28) — publish the README, LICENSE and CHANGELOG to npm
6
60
 
7
61
  - **Fixed: the npm package shipped with no README, LICENSE or CHANGELOG.**
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, offer to scaffold a starter CI workflow yourself** — this is a judgment call for the agent, not something shipflow's CLI does (the CLI stays free of per-language/build-tool logic). Investigate the repo directly (`package.json`, `Cargo.toml`, `project.yml`/`.xcodeproj`, `go.mod`, `pyproject.toml`, or whatever's actually there) and draft a minimal, conservative `pull_request`-triggered build+test workflow. **Never silently overwrite an existing workflow file.** Present the drafted YAML to the user and wait for explicit confirmation before writing it — the same confirm-before-write pattern as everything else in this skill. Say plainly that this is a best-effort starting point inferred from repo structure, not a guarantee it's green on the first run — a required check that never passes blocks every future merge, so the user should watch it actually run successfully before relying on it as a required check. Once it exists, re-run step 1's `detect` (repo state changed) and continue this step with the new job name as a real candidate.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@natjswenson/shipflow",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Scaffold a configurable dev/main branching, auto-merge, branch-cleanup, and release-tagging workflow into any repo",
5
5
  "license": "MIT",
6
6
  "author": "Nate Swenson",
@@ -24,9 +24,18 @@ on:
24
24
  types: [opened, reopened, synchronize, closed]
25
25
  branches: [{{MAIN_BRANCH}}]
26
26
 
27
- permissions:
28
- contents: write
29
- pull-requests: write
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
- permissions:
28
- contents: write
29
- pull-requests: write
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
- permissions:
19
- contents: write
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
- permissions:
28
- contents: write
29
- pull-requests: write
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
- permissions:
19
- contents: write
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
- permissions:
32
- contents: write
33
- pull-requests: write
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 }}"