@rtorcato/repo-tooling 3.15.0 → 3.15.1

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.
@@ -259,6 +259,13 @@ export async function checkDependabot(dir) {
259
259
  if (!automerge.includes("dependency-group == 'dev-minor'")) {
260
260
  deltas.push('auto-merge workflow merges production bumps (missing dev-minor group gate)');
261
261
  }
262
+ // #458: the group gate alone lets peerDependencies through, because
263
+ // Dependabot files a package that is both dev and peer under
264
+ // "development". The canonical workflow re-checks the names against the
265
+ // manifests; a workflow without that step is the #423 hole reopened.
266
+ if (!automerge.includes("steps.gate.outputs.safe == 'true'")) {
267
+ deltas.push('auto-merge workflow trusts the dev-minor group name (missing peerDependencies check)');
268
+ }
262
269
  }
263
270
  else {
264
271
  deltas.push('missing dependabot-automerge workflow');
@@ -37,8 +37,11 @@ export function dependabotConfig(ecosystem) {
37
37
  update-types:
38
38
  - minor
39
39
  - patch
40
- # The only tier that auto-merges on green — dev tooling never reaches a
41
- # consumer of the published package.
40
+ # The tier that can auto-merge on green. "development" is Dependabot's
41
+ # classification, not a safety property: a package listed in both
42
+ # devDependencies and peerDependencies lands here, and peer deps are part
43
+ # of a published package's contract (#458). The auto-merge workflow
44
+ # re-checks every name against the manifests rather than trusting this.
42
45
  dev-minor:
43
46
  dependency-type: development
44
47
  update-types:
@@ -130,6 +133,13 @@ export function dependabotIgnoreRules(content) {
130
133
  *
131
134
  * The group name is the one \`dependabotConfig()\` writes — the two files are a
132
135
  * paired unit and have to move together.
136
+ *
137
+ * **The group name alone is not enough** (#458). Dependabot classifies a package
138
+ * listed in both \`devDependencies\` and \`peerDependencies\` as *development*, so
139
+ * it lands in \`dev-minor\` — and \`peerDependencies\` are part of what a published
140
+ * package hands its consumers. This repo had 32 such packages, and 13 of them
141
+ * rode a single "dev-only" group PR. So the workflow resolves every bumped name
142
+ * against the tracked manifests and stands down if any of them ships.
133
143
  */
134
144
  export const DEPENDABOT_AUTOMERGE_WORKFLOW = `name: Dependabot auto-merge
135
145
 
@@ -150,11 +160,50 @@ jobs:
150
160
  with:
151
161
  github-token: \${{ secrets.GITHUB_TOKEN }}
152
162
 
163
+ - uses: actions/checkout@v7
164
+
165
+ # The group name is Dependabot's opinion, not a safety property: a package
166
+ # in both devDependencies and peerDependencies is classified "development"
167
+ # and lands in dev-minor, while peerDependencies ship to consumers of a
168
+ # published package (#458). So resolve every bumped name against the
169
+ # tracked manifests and stand down if any of them reaches a consumer.
170
+ #
171
+ # npm-specific by design. A repo with no package.json (Swift, Python) finds
172
+ # nothing here and falls back to the group + semver gate below.
173
+ - name: Check whether any bumped package ships to consumers
174
+ id: gate
175
+ env:
176
+ NAMES: \${{ steps.metadata.outputs.dependency-names }}
177
+ run: |
178
+ set -euo pipefail
179
+
180
+ # A private workspace publishes nothing, so its deps reach nobody.
181
+ ships=$(git ls-files -- 'package.json' '*/package.json' | while read -r f; do
182
+ if [ "$(jq -r '.private // false' "$f")" = 'true' ]; then continue; fi
183
+ jq -r '((.dependencies // {}) + (.optionalDependencies // {}) + (.peerDependencies // {})) | keys[]' "$f"
184
+ done | sort -u)
185
+
186
+ safe=true
187
+ # No names reported means we cannot verify anything — fail closed.
188
+ if [ -z "\${NAMES//[, ]/}" ]; then
189
+ echo "::notice::no dependency names reported — leaving this PR for a human"
190
+ safe=false
191
+ fi
192
+ for name in \${NAMES//,/ }; do
193
+ if printf '%s\\n' "$ships" | grep -qxF -- "$name"; then
194
+ echo "::notice::$name ships to consumers — leaving this PR for a human"
195
+ safe=false
196
+ break
197
+ fi
198
+ done
199
+ echo "safe=$safe" >> "$GITHUB_OUTPUT"
200
+
153
201
  # Belt and braces: the dev-minor group is already declared minor+patch in
154
202
  # dependabot.yml, but this workflow is the security gate and shouldn't
155
203
  # trust a config file a consumer repo can edit independently.
156
204
  - name: Auto-merge dev-dependency patch and minor updates
157
205
  if: |
206
+ steps.gate.outputs.safe == 'true' &&
158
207
  steps.metadata.outputs.dependency-group == 'dev-minor' &&
159
208
  (steps.metadata.outputs.update-type == 'version-update:semver-patch' ||
160
209
  steps.metadata.outputs.update-type == 'version-update:semver-minor')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rtorcato/repo-tooling",
3
- "version": "3.15.0",
3
+ "version": "3.15.1",
4
4
  "description": "One CLI to scaffold, audit and fix your repo's whole toolchain — linting, tests, commits, releases & CI.",
5
5
  "type": "module",
6
6
  "keywords": [