@williamthorsen/release-kit 10.2.1 → 10.3.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/.readyup/kits/default.js +78 -55
- package/.readyup/kits/npm-auto-publish.js +169 -57
- package/.readyup/manifest.json +6 -6
- package/CHANGELOG.md +54 -0
- package/README.md +18 -6
- package/bin/release-kit.js +1 -1
- package/dist/esm/bin/release-kit.js +4 -4
- package/dist/esm/buildChangelogEntries.js +17 -15
- package/dist/esm/buildDependencyGraph.js +6 -2
- package/dist/esm/buildReleaseSummary.d.ts +1 -1
- package/dist/esm/changelogJsonFile.d.ts +1 -3
- package/dist/esm/changelogJsonFile.js +4 -18
- package/dist/esm/changelogJsonUtils.js +7 -7
- package/dist/esm/changelogOverrides.js +13 -12
- package/dist/esm/checkWorkTypesDrift.js +1 -1
- package/dist/esm/commitCommand.js +3 -13
- package/dist/esm/createTags.js +3 -14
- package/dist/esm/deriveWorkspaceConfig.js +3 -3
- package/dist/esm/detectPackageManager.js +2 -2
- package/dist/esm/detectUndeclaredTagPrefixes.js +1 -1
- package/dist/esm/discoverWorkspaces.js +1 -1
- package/dist/esm/getCommitsSinceTarget.js +2 -2
- package/dist/esm/help/prepareHelp.d.ts +1 -1
- package/dist/esm/help/prepareHelp.js +2 -2
- package/dist/esm/init/checks.js +1 -1
- package/dist/esm/init/detectRepoType.js +1 -1
- package/dist/esm/injectReleaseNotesIntoReadme.d.ts +2 -0
- package/dist/esm/injectReleaseNotesIntoReadme.js +13 -10
- package/dist/esm/loadConfig.js +8 -6
- package/dist/esm/planReleaseNotesPreviews.d.ts +13 -0
- package/dist/esm/planReleaseNotesPreviews.js +43 -0
- package/dist/esm/planVersionBump.d.ts +9 -0
- package/dist/esm/planVersionBump.js +54 -0
- package/dist/esm/prepareCommand.d.ts +0 -4
- package/dist/esm/prepareCommand.js +51 -45
- package/dist/esm/propagateBumps.js +1 -1
- package/dist/esm/releaseFiles.d.ts +4 -0
- package/dist/esm/releaseFiles.js +34 -0
- package/dist/esm/releasePlan.d.ts +10 -0
- package/dist/esm/releasePlan.js +53 -0
- package/dist/esm/releasePrepare.d.ts +3 -3
- package/dist/esm/releasePrepare.js +53 -54
- package/dist/esm/releasePrepareMono.d.ts +3 -2
- package/dist/esm/releasePrepareMono.js +50 -53
- package/dist/esm/releasePrepareProject.d.ts +3 -0
- package/dist/esm/releasePrepareProject.js +33 -37
- package/dist/esm/renderChangelogMarkdown.d.ts +0 -7
- package/dist/esm/renderChangelogMarkdown.js +0 -13
- package/dist/esm/reportPrepare.d.ts +5 -1
- package/dist/esm/reportPrepare.js +37 -17
- package/dist/esm/sync-labels/generateCommand.js +4 -2
- package/dist/esm/sync-labels/presets.js +5 -5
- package/dist/esm/sync-labels/resolveLabels.js +5 -3
- package/dist/esm/syncWorkTypes.js +1 -1
- package/dist/esm/types.d.ts +4 -9
- package/dist/esm/validateConfig.js +11 -11
- package/dist/esm/workTypesData.d.ts +1 -1
- package/dist/esm/workTypesUtils.js +1 -1
- package/package.json +3 -3
- package/dist/esm/bumpAllVersions.d.ts +0 -3
- package/dist/esm/bumpAllVersions.js +0 -61
- package/dist/esm/writeReleaseNotesPreviews.d.ts +0 -18
- package/dist/esm/writeReleaseNotesPreviews.js +0 -63
package/.readyup/kits/default.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @noformat — @generated. Do not edit. Compiled by rdy. */
|
|
2
2
|
/* eslint-disable */
|
|
3
|
-
export const __readyupVersion = "0.
|
|
3
|
+
export const __readyupVersion = "0.24.0";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
// .readyup/kits/default.ts
|
|
@@ -43,7 +43,7 @@ function detectRepoType() {
|
|
|
43
43
|
if (existsSync("package.json")) {
|
|
44
44
|
const raw = readFileSync("package.json", "utf8");
|
|
45
45
|
const pkg = parseJsonRecord(raw);
|
|
46
|
-
if (pkg !== void 0 && Array.isArray(pkg
|
|
46
|
+
if (pkg !== void 0 && Array.isArray(pkg["workspaces"])) {
|
|
47
47
|
return "monorepo";
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -52,15 +52,21 @@ function detectRepoType() {
|
|
|
52
52
|
|
|
53
53
|
// .readyup/kits/default.ts
|
|
54
54
|
function getMinVersion() {
|
|
55
|
-
const picked = { "version": "10.
|
|
56
|
-
if (typeof picked
|
|
55
|
+
const picked = { "version": "10.3.0" };
|
|
56
|
+
if (typeof picked["version"] !== "string") {
|
|
57
57
|
throw new TypeError("release-kit/package.json: 'version' must be a string");
|
|
58
58
|
}
|
|
59
|
-
return picked
|
|
59
|
+
return picked["version"];
|
|
60
60
|
}
|
|
61
61
|
function hasPublishablePackages() {
|
|
62
62
|
return discoverWorkspaces({ filter: (w) => w.isPackage }).length > 0;
|
|
63
63
|
}
|
|
64
|
+
var CONFIG_EXPORT_PATTERNS = [
|
|
65
|
+
/export\s+default\b/,
|
|
66
|
+
/export\s+(?:const|let|var)\s+config\b/,
|
|
67
|
+
/export\s*\{[^}]*\b(?:config|default)\b[^}]*\}/,
|
|
68
|
+
/export\s*\*/
|
|
69
|
+
];
|
|
64
70
|
var CLIFF_TEMPLATE_HASH = "93b72e0b1393cd6b1fe8e2a0e303cd326fd323435951b0493396b305af32d2ec";
|
|
65
71
|
var COMMON_PRESET_HASH = "86f9e1db9000793a91168e8c6b5695311a422ee208121324549c068fe67fa184";
|
|
66
72
|
var SYNC_LABELS_WORKFLOW_HASH = "d6e2403fb551d2d415f679125989c92760444eec887644565b2e05c9bf8f4c1e";
|
|
@@ -111,6 +117,7 @@ var default_default = defineRdyKit({
|
|
|
111
117
|
{
|
|
112
118
|
name: "release.yaml does not reference deprecated tag ref",
|
|
113
119
|
severity: "error",
|
|
120
|
+
quiet: true,
|
|
114
121
|
check: () => fileDoesNotContain(".github/workflows/release.yaml", /@(release|publish)-workflow-v[0-9]/),
|
|
115
122
|
fix: "Update release.yaml to use @workflow/release-v1 (run `release-kit init --force` to regenerate, or replace the ref manually)"
|
|
116
123
|
}
|
|
@@ -135,50 +142,79 @@ var default_default = defineRdyKit({
|
|
|
135
142
|
{
|
|
136
143
|
name: "publish.yaml does not reference deprecated tag ref",
|
|
137
144
|
severity: "error",
|
|
145
|
+
quiet: true,
|
|
138
146
|
check: () => fileDoesNotContain(".github/workflows/publish.yaml", /@(release|publish)-workflow-v[0-9]/),
|
|
139
147
|
fix: "Update publish.yaml to use @workflow/publish-v1 (run `release-kit init --force` to regenerate, or replace the ref manually)"
|
|
140
148
|
}
|
|
141
149
|
]
|
|
142
150
|
},
|
|
143
151
|
{
|
|
144
|
-
name: "
|
|
145
|
-
severity: "warn",
|
|
146
|
-
skip: () => !fileExists(".config/release-kit.config.ts") ? "no release-kit config file" : false,
|
|
147
|
-
check: () => releaseNotesConfigIsConsistent(),
|
|
148
|
-
fix: "Either enable changelogJson.enabled or disable releaseNotes.shouldInjectIntoReadme"
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
name: "config does not use removed releaseNotes.shouldCreateGithubRelease",
|
|
152
|
+
name: ".config/release-kit.config.ts exports a config",
|
|
152
153
|
severity: "error",
|
|
153
154
|
skip: () => !fileExists(".config/release-kit.config.ts") ? "no release-kit config file" : false,
|
|
154
|
-
check: () =>
|
|
155
|
-
fix: "
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
name: ".config/release-kit.config.ts uses defineConfig",
|
|
159
|
-
severity: "recommend",
|
|
160
|
-
skip: () => !fileExists(".config/release-kit.config.ts") ? "no release-kit config file" : false,
|
|
161
|
-
check: () => fileContains(".config/release-kit.config.ts", /defineConfig/),
|
|
162
|
-
fix: "Wrap your config export with defineConfig() from @williamthorsen/release-kit/config for type safety"
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
name: "releaseNotes.shouldInjectIntoReadme is true",
|
|
166
|
-
severity: "warn",
|
|
167
|
-
skip: () => !fileExists(".config/release-kit.config.ts") ? "no release-kit config file" : false,
|
|
168
|
-
check: () => releaseNotesInjectsIntoReadme(),
|
|
169
|
-
fix: "Set releaseNotes.shouldInjectIntoReadme to true in .config/release-kit.config.ts",
|
|
155
|
+
check: () => configFileExportsConfig(),
|
|
156
|
+
fix: "Export the config from .config/release-kit.config.ts as a default export or as a named `config` export; release-kit resolves no other export",
|
|
170
157
|
checks: [
|
|
171
158
|
{
|
|
172
|
-
name: "
|
|
159
|
+
name: "releaseNotes config is consistent with changelogJson",
|
|
160
|
+
severity: "warn",
|
|
161
|
+
check: () => releaseNotesConfigIsConsistent(),
|
|
162
|
+
fix: "Either enable changelogJson.enabled or disable releaseNotes.shouldInjectIntoReadme"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: ".config/release-kit.config.ts uses defineConfig",
|
|
166
|
+
severity: "recommend",
|
|
167
|
+
check: () => fileContains(".config/release-kit.config.ts", /defineConfig/),
|
|
168
|
+
fix: "Wrap your config export with defineConfig() from @williamthorsen/release-kit/config for type safety"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: "releaseNotes.shouldInjectIntoReadme is true",
|
|
172
|
+
severity: "warn",
|
|
173
|
+
check: () => releaseNotesInjectsIntoReadme(),
|
|
174
|
+
fix: "Set releaseNotes.shouldInjectIntoReadme to true in .config/release-kit.config.ts",
|
|
175
|
+
checks: [
|
|
176
|
+
{
|
|
177
|
+
name: "README contains release-notes section markers",
|
|
178
|
+
severity: "warn",
|
|
179
|
+
check: readmesHaveReleaseNotesMarkers,
|
|
180
|
+
fix: "Add `<!-- section:release-notes -->` and `<!-- /section:release-notes -->` markers to each affected README"
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: "repoLabels block declared in .config/release-kit.config.ts",
|
|
186
|
+
severity: "recommend",
|
|
187
|
+
check: () => fileContains(".config/release-kit.config.ts", /repoLabels/),
|
|
188
|
+
fix: "Run `release-kit sync-labels init` to seed a repoLabels block, then customize labels"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: ".github/labels.yaml exists",
|
|
173
192
|
severity: "warn",
|
|
174
|
-
|
|
175
|
-
|
|
193
|
+
skip: () => !fileContains(".config/release-kit.config.ts", /repoLabels/) ? "no repoLabels config" : false,
|
|
194
|
+
check: () => fileExists(".github/labels.yaml"),
|
|
195
|
+
fix: "Run `release-kit sync-labels generate` to produce the labels file",
|
|
196
|
+
checks: [
|
|
197
|
+
{
|
|
198
|
+
name: "labels.yaml has current common preset",
|
|
199
|
+
severity: "warn",
|
|
200
|
+
check: () => labelsHaveCurrentPresetHash("common", COMMON_PRESET_HASH),
|
|
201
|
+
fix: "Run `release-kit sync-labels generate` to incorporate updated common labels"
|
|
202
|
+
}
|
|
203
|
+
]
|
|
176
204
|
}
|
|
177
205
|
]
|
|
178
206
|
},
|
|
207
|
+
{
|
|
208
|
+
name: "config does not use removed releaseNotes.shouldCreateGithubRelease",
|
|
209
|
+
severity: "error",
|
|
210
|
+
quiet: true,
|
|
211
|
+
check: () => fileDoesNotContain(".config/release-kit.config.ts", /shouldCreateGithubRelease/),
|
|
212
|
+
fix: "Remove 'shouldCreateGithubRelease' from .config/release-kit.config.ts. Adoption of GitHub Releases is now signaled by installing the create-github-release workflow (see release-kit README for setup)."
|
|
213
|
+
},
|
|
179
214
|
{
|
|
180
215
|
name: "git-cliff not in devDependencies",
|
|
181
216
|
severity: "recommend",
|
|
217
|
+
quiet: true,
|
|
182
218
|
check: () => !hasDevDependency("git-cliff"),
|
|
183
219
|
fix: "pnpm remove git-cliff \u2014 release-kit handles changelog generation directly"
|
|
184
220
|
},
|
|
@@ -206,41 +242,26 @@ var default_default = defineRdyKit({
|
|
|
206
242
|
{
|
|
207
243
|
name: "sync-labels.yaml does not reference deprecated tag ref",
|
|
208
244
|
severity: "error",
|
|
245
|
+
quiet: true,
|
|
209
246
|
check: () => fileDoesNotContain(".github/workflows/sync-labels.yaml", /@sync-labels-workflow-v[0-9]/),
|
|
210
247
|
fix: "Update sync-labels.yaml to use @workflow/sync-labels-v1 (run `release-kit sync-labels init --force` to regenerate, or replace the ref manually)"
|
|
211
248
|
},
|
|
212
249
|
{
|
|
213
250
|
name: "retired .config/sync-labels.config.ts is absent",
|
|
214
251
|
severity: "error",
|
|
252
|
+
quiet: true,
|
|
215
253
|
check: () => !fileExists(".config/sync-labels.config.ts"),
|
|
216
254
|
fix: "Move the labels into the repoLabels block of .config/release-kit.config.ts, then delete .config/sync-labels.config.ts"
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
name: "repoLabels block declared in .config/release-kit.config.ts",
|
|
220
|
-
severity: "recommend",
|
|
221
|
-
skip: () => !fileExists(".config/release-kit.config.ts") ? "no release-kit config file" : false,
|
|
222
|
-
check: () => fileContains(".config/release-kit.config.ts", /repoLabels/),
|
|
223
|
-
fix: "Run `release-kit sync-labels init` to seed a repoLabels block, then customize labels"
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
name: ".github/labels.yaml exists",
|
|
227
|
-
severity: "warn",
|
|
228
|
-
skip: () => !fileContains(".config/release-kit.config.ts", /repoLabels/) ? "no repoLabels config" : false,
|
|
229
|
-
check: () => fileExists(".github/labels.yaml"),
|
|
230
|
-
fix: "Run `release-kit sync-labels generate` to produce the labels file",
|
|
231
|
-
checks: [
|
|
232
|
-
{
|
|
233
|
-
name: "labels.yaml has current common preset",
|
|
234
|
-
severity: "warn",
|
|
235
|
-
check: () => labelsHaveCurrentPresetHash("common", COMMON_PRESET_HASH),
|
|
236
|
-
fix: "Run `release-kit sync-labels generate` to incorporate updated common labels"
|
|
237
|
-
}
|
|
238
|
-
]
|
|
239
255
|
}
|
|
240
256
|
]
|
|
241
257
|
}
|
|
242
258
|
]
|
|
243
259
|
});
|
|
260
|
+
function configFileExportsConfig() {
|
|
261
|
+
const content = readFile(".config/release-kit.config.ts");
|
|
262
|
+
if (content === void 0) return false;
|
|
263
|
+
return CONFIG_EXPORT_PATTERNS.some((pattern) => pattern.test(content));
|
|
264
|
+
}
|
|
244
265
|
function labelsHaveCurrentPresetHash(presetName, expectedHash) {
|
|
245
266
|
const content = readFile(".github/labels.yaml");
|
|
246
267
|
if (content === void 0) return false;
|
|
@@ -253,7 +274,8 @@ function readmeHasReleaseNotesMarkers(content) {
|
|
|
253
274
|
}
|
|
254
275
|
function readmesHaveReleaseNotesMarkers() {
|
|
255
276
|
const failing = [];
|
|
256
|
-
|
|
277
|
+
const packageWorkspaces = discoverWorkspaces({ filter: (w) => w.isPackage });
|
|
278
|
+
for (const { dir } of packageWorkspaces) {
|
|
257
279
|
const readmePath = dir === "." ? "README.md" : `${dir}/README.md`;
|
|
258
280
|
const content = readFile(readmePath);
|
|
259
281
|
if (content === void 0 || !readmeHasReleaseNotesMarkers(content)) {
|
|
@@ -287,6 +309,7 @@ export {
|
|
|
287
309
|
RELEASE_WORKFLOW_HASH_MONOREPO,
|
|
288
310
|
RELEASE_WORKFLOW_HASH_SINGLE,
|
|
289
311
|
SYNC_LABELS_WORKFLOW_HASH,
|
|
312
|
+
configFileExportsConfig,
|
|
290
313
|
default_default as default,
|
|
291
314
|
readmeHasReleaseNotesMarkers,
|
|
292
315
|
readmesHaveReleaseNotesMarkers
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/** @noformat — @generated. Do not edit. Compiled by rdy. */
|
|
2
2
|
/* eslint-disable */
|
|
3
|
-
export const __readyupVersion = "0.
|
|
3
|
+
export const __readyupVersion = "0.24.0";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
// .readyup/kits/npm-auto-publish.ts
|
|
7
7
|
import { execSync } from "node:child_process";
|
|
8
8
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
9
9
|
import path from "node:path";
|
|
10
|
-
import { defineRdyChecklist, defineRdyKit
|
|
10
|
+
import { defineRdyChecklist, defineRdyKit } from "readyup";
|
|
11
11
|
import {
|
|
12
12
|
discoverWorkspaces,
|
|
13
13
|
fileContains,
|
|
@@ -17,33 +17,43 @@ import {
|
|
|
17
17
|
readFile,
|
|
18
18
|
readJsonFile
|
|
19
19
|
} from "readyup/check-utils";
|
|
20
|
+
var AUTH_ERROR_CODES = /* @__PURE__ */ new Set(["E401", "ENEEDAUTH"]);
|
|
20
21
|
var PUBLISH_WORKFLOW_FILE = "publish.yaml";
|
|
21
|
-
var
|
|
22
|
+
var UNREACHABLE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
23
|
+
"EAI_AGAIN",
|
|
24
|
+
"ECONNREFUSED",
|
|
25
|
+
"ECONNRESET",
|
|
26
|
+
"ENETUNREACH",
|
|
27
|
+
"ENOTFOUND",
|
|
28
|
+
"ERR_SOCKET_TIMEOUT",
|
|
29
|
+
"ETIMEDOUT"
|
|
30
|
+
]);
|
|
31
|
+
var repoChecklist = defineRdyChecklist({
|
|
22
32
|
name: "repo",
|
|
23
|
-
|
|
33
|
+
checks: [
|
|
24
34
|
{
|
|
25
35
|
name: "publish.yaml exists",
|
|
36
|
+
skip: () => skipIfNothingPublishable(),
|
|
26
37
|
check: () => fileExists(".github/workflows/publish.yaml"),
|
|
27
|
-
fix: 'Run "release-kit init" to scaffold the publish workflow, or create .github/workflows/publish.yaml manually'
|
|
38
|
+
fix: 'Run "release-kit init" to scaffold the publish workflow, or create .github/workflows/publish.yaml manually',
|
|
39
|
+
checks: [
|
|
40
|
+
{
|
|
41
|
+
name: "id-token: write permission declared",
|
|
42
|
+
check: () => fileContains(".github/workflows/publish.yaml", /id-token:\s*write/),
|
|
43
|
+
fix: 'Add "permissions: { id-token: write, contents: read }" to .github/workflows/publish.yaml \u2014 required for OIDC-based npm authentication'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "No legacy token references in workflow files",
|
|
47
|
+
quiet: true,
|
|
48
|
+
check: () => !hasTokenReferences(),
|
|
49
|
+
fix: "Remove NPM_TOKEN/NODE_AUTH_TOKEN references from workflow files; OIDC auth replaces token-based auth"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "Provenance setting matches repo visibility",
|
|
53
|
+
check: checkProvenanceMatchesVisibility
|
|
54
|
+
}
|
|
55
|
+
]
|
|
28
56
|
}
|
|
29
|
-
],
|
|
30
|
-
groups: [
|
|
31
|
-
[
|
|
32
|
-
{
|
|
33
|
-
name: "id-token: write permission declared",
|
|
34
|
-
check: () => fileContains(".github/workflows/publish.yaml", /id-token:\s*write/),
|
|
35
|
-
fix: 'Add "permissions: { id-token: write, contents: read }" to .github/workflows/publish.yaml \u2014 required for OIDC-based npm authentication'
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
name: "No legacy token references in workflow files",
|
|
39
|
-
check: () => !hasTokenReferences(),
|
|
40
|
-
fix: "Remove NPM_TOKEN/NODE_AUTH_TOKEN references from workflow files; OIDC auth replaces token-based auth"
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
name: "Provenance setting matches repo visibility",
|
|
44
|
-
check: checkProvenanceMatchesVisibility
|
|
45
|
-
}
|
|
46
|
-
]
|
|
47
57
|
]
|
|
48
58
|
});
|
|
49
59
|
var packagesChecklist = defineRdyChecklist({
|
|
@@ -53,7 +63,7 @@ var packagesChecklist = defineRdyChecklist({
|
|
|
53
63
|
name: 'packageManager field starts with "pnpm"',
|
|
54
64
|
check: () => {
|
|
55
65
|
const rootPkg = readJsonFile("package.json");
|
|
56
|
-
const pm = typeof rootPkg?.packageManager === "string" ? rootPkg
|
|
66
|
+
const pm = typeof rootPkg?.["packageManager"] === "string" ? rootPkg["packageManager"] : "";
|
|
57
67
|
return pm.startsWith("pnpm");
|
|
58
68
|
},
|
|
59
69
|
fix: 'Set "packageManager": "pnpm@..." in root package.json'
|
|
@@ -64,9 +74,22 @@ var packagesChecklist = defineRdyChecklist({
|
|
|
64
74
|
fix: "Ensure pnpm-workspace.yaml lists package globs, or that a root package.json exists"
|
|
65
75
|
}
|
|
66
76
|
],
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
checks: [
|
|
78
|
+
{
|
|
79
|
+
name: "npm session is usable",
|
|
80
|
+
skip: () => skipIfNothingPublishable(),
|
|
81
|
+
check: () => {
|
|
82
|
+
const auth = getCachedNpmAuthStatus();
|
|
83
|
+
return auth.status === "authenticated" ? { ok: true } : { ok: false, detail: auth.detail };
|
|
84
|
+
},
|
|
85
|
+
// readyup reads `fix` before it consults `skip`, and again when it validates the kit, so a getter here would
|
|
86
|
+
// reach the registry on every run regardless of the skip. Per-outcome wording goes in the check's detail.
|
|
87
|
+
fix: 'Restore a usable npm session: log in with "npm login", or restore access to the registry, which the trusted-publisher check queries directly',
|
|
88
|
+
get checks() {
|
|
89
|
+
return discoverWorkspaces().map((workspace) => buildWorkspaceCheck(workspace));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
]
|
|
70
93
|
});
|
|
71
94
|
var npm_auto_publish_default = defineRdyKit({
|
|
72
95
|
fixLocation: "inline",
|
|
@@ -78,7 +101,7 @@ function buildWorkspaceCheck(workspace) {
|
|
|
78
101
|
const children = [
|
|
79
102
|
{
|
|
80
103
|
name: "repository field exists",
|
|
81
|
-
check: () => workspace.packageJson
|
|
104
|
+
check: () => workspace.packageJson["repository"] !== void 0 && workspace.packageJson["repository"] !== null,
|
|
82
105
|
fix: `Add a "repository" field to ${pkgJsonPath} pointing to the GitHub repo`
|
|
83
106
|
}
|
|
84
107
|
];
|
|
@@ -100,7 +123,7 @@ function buildWorkspaceCheck(workspace) {
|
|
|
100
123
|
checks: [
|
|
101
124
|
{
|
|
102
125
|
name: "trusted publisher configured",
|
|
103
|
-
check: () =>
|
|
126
|
+
check: () => checkTrustedPublisher(displayName),
|
|
104
127
|
get fix() {
|
|
105
128
|
return `Run: npm trust github ${displayName} --repo ${getCachedOwnerRepo()} --file ${PUBLISH_WORKFLOW_FILE}`;
|
|
106
129
|
}
|
|
@@ -110,7 +133,7 @@ function buildWorkspaceCheck(workspace) {
|
|
|
110
133
|
{
|
|
111
134
|
name: "files field exists",
|
|
112
135
|
severity: "warn",
|
|
113
|
-
check: () => workspace.packageJson
|
|
136
|
+
check: () => workspace.packageJson["files"] !== void 0,
|
|
114
137
|
fix: `Add a "files" field to ${pkgJsonPath} to control which files are included in the published tarball`
|
|
115
138
|
}
|
|
116
139
|
);
|
|
@@ -148,13 +171,79 @@ function checkProvenanceMatchesVisibility() {
|
|
|
148
171
|
}
|
|
149
172
|
return { ok: true };
|
|
150
173
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
174
|
+
function checkTrustedPublisher(packageName) {
|
|
175
|
+
const result = classifyTrustQuery(
|
|
176
|
+
runNpmJson(`npm trust list ${packageName} --json`),
|
|
177
|
+
getCachedOwnerRepo(),
|
|
178
|
+
PUBLISH_WORKFLOW_FILE
|
|
179
|
+
);
|
|
180
|
+
switch (result.status) {
|
|
181
|
+
case "configured":
|
|
182
|
+
return { ok: true };
|
|
183
|
+
case "not-configured":
|
|
184
|
+
return { ok: false, detail: "No trusted publisher is configured for this package" };
|
|
185
|
+
default:
|
|
186
|
+
return { ok: false, detail: result.detail };
|
|
155
187
|
}
|
|
156
|
-
return cachedOwnerRepo;
|
|
157
188
|
}
|
|
189
|
+
function classifyNpmAuth(result) {
|
|
190
|
+
if (result.exitOk) {
|
|
191
|
+
return { status: "authenticated" };
|
|
192
|
+
}
|
|
193
|
+
const error = readNpmError(result.stdout);
|
|
194
|
+
if (error === void 0) {
|
|
195
|
+
return { status: "unreachable", detail: "The npm registry query failed without a readable error payload" };
|
|
196
|
+
}
|
|
197
|
+
if (AUTH_ERROR_CODES.has(error.code)) {
|
|
198
|
+
return { status: "unauthenticated", detail: `The npm registry rejected the session (${error.code})` };
|
|
199
|
+
}
|
|
200
|
+
if (UNREACHABLE_ERROR_CODES.has(error.code)) {
|
|
201
|
+
return { status: "unreachable", detail: `Cannot reach the npm registry (${error.code})` };
|
|
202
|
+
}
|
|
203
|
+
return { status: "unreachable", detail: `The npm registry query failed (${error.code}): ${error.summary}` };
|
|
204
|
+
}
|
|
205
|
+
function classifyTrustQuery(result, expectedRepo, expectedFile) {
|
|
206
|
+
if (!result.exitOk) {
|
|
207
|
+
const error = readNpmError(result.stdout);
|
|
208
|
+
if (error === void 0) {
|
|
209
|
+
return { status: "error", detail: "The npm trust query failed without a readable error payload" };
|
|
210
|
+
}
|
|
211
|
+
if (error.code === "E404") {
|
|
212
|
+
return { status: "not-configured" };
|
|
213
|
+
}
|
|
214
|
+
return { status: "error", detail: `The npm trust query failed (${error.code}): ${error.summary}` };
|
|
215
|
+
}
|
|
216
|
+
const relationships = readTrustRelationships(result.stdout);
|
|
217
|
+
if (relationships === void 0) {
|
|
218
|
+
return { status: "error", detail: "The npm trust query returned a payload this check cannot read" };
|
|
219
|
+
}
|
|
220
|
+
if (relationships.length === 0) {
|
|
221
|
+
return { status: "not-configured" };
|
|
222
|
+
}
|
|
223
|
+
const isMatch = relationships.some(
|
|
224
|
+
(relationship) => relationship.type === "github" && relationship.repository === expectedRepo && relationship.file === expectedFile
|
|
225
|
+
);
|
|
226
|
+
return isMatch ? { status: "configured" } : {
|
|
227
|
+
status: "mismatched",
|
|
228
|
+
detail: `Expected github ${expectedRepo} (${expectedFile}); found ${describeTrustRelationships(relationships)}`
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function describeTrustRelationships(relationships) {
|
|
232
|
+
return relationships.map((relationship) => {
|
|
233
|
+
const type = typeof relationship.type === "string" ? relationship.type : "(unknown)";
|
|
234
|
+
const repository = typeof relationship.repository === "string" ? relationship.repository : "(unknown)";
|
|
235
|
+
const file = typeof relationship.file === "string" ? relationship.file : "(unknown)";
|
|
236
|
+
return `${type} ${repository} (${file})`;
|
|
237
|
+
}).join(", ");
|
|
238
|
+
}
|
|
239
|
+
var getCachedNpmAuthStatus = /* @__PURE__ */ (() => {
|
|
240
|
+
let cached;
|
|
241
|
+
return () => cached ??= classifyNpmAuth(runNpmJson("npm whoami --json"));
|
|
242
|
+
})();
|
|
243
|
+
var getCachedOwnerRepo = /* @__PURE__ */ (() => {
|
|
244
|
+
let cached;
|
|
245
|
+
return () => cached ??= getOwnerRepo();
|
|
246
|
+
})();
|
|
158
247
|
function getOwnerRepo() {
|
|
159
248
|
const url = execSync("git remote get-url origin", {
|
|
160
249
|
encoding: "utf8"
|
|
@@ -183,27 +272,6 @@ function hasTokenReferences() {
|
|
|
183
272
|
}
|
|
184
273
|
return false;
|
|
185
274
|
}
|
|
186
|
-
function hasTrustedPublisher(packageName, expectedRepo, expectedFile) {
|
|
187
|
-
let output;
|
|
188
|
-
try {
|
|
189
|
-
output = execSync(`npm trust list ${packageName} --json`, {
|
|
190
|
-
encoding: "utf8",
|
|
191
|
-
stdio: "pipe"
|
|
192
|
-
});
|
|
193
|
-
} catch {
|
|
194
|
-
return false;
|
|
195
|
-
}
|
|
196
|
-
let parsed;
|
|
197
|
-
try {
|
|
198
|
-
parsed = JSON.parse(output);
|
|
199
|
-
} catch {
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
if (!isRecord(parsed)) {
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
return parsed.type === "github" && parsed.repository === expectedRepo && parsed.file === expectedFile;
|
|
206
|
-
}
|
|
207
275
|
function isPublishedToNpm(packageName) {
|
|
208
276
|
try {
|
|
209
277
|
execSync(`npm view ${packageName} version`, {
|
|
@@ -225,11 +293,55 @@ function isRepoPrivate() {
|
|
|
225
293
|
function parseProvenanceSetting(workflowContent) {
|
|
226
294
|
return /^[^#]*provenance:\s*['"]?true['"]?/im.test(workflowContent);
|
|
227
295
|
}
|
|
296
|
+
function readNpmError(stdout) {
|
|
297
|
+
let parsed;
|
|
298
|
+
try {
|
|
299
|
+
parsed = JSON.parse(stdout);
|
|
300
|
+
} catch {
|
|
301
|
+
return void 0;
|
|
302
|
+
}
|
|
303
|
+
if (!isRecord(parsed) || !isRecord(parsed["error"]) || typeof parsed["error"]["code"] !== "string") {
|
|
304
|
+
return void 0;
|
|
305
|
+
}
|
|
306
|
+
const { code, summary } = parsed["error"];
|
|
307
|
+
return { code, summary: typeof summary === "string" ? summary : "" };
|
|
308
|
+
}
|
|
309
|
+
function readTrustRelationships(stdout) {
|
|
310
|
+
let parsed;
|
|
311
|
+
try {
|
|
312
|
+
parsed = JSON.parse(stdout);
|
|
313
|
+
} catch {
|
|
314
|
+
return void 0;
|
|
315
|
+
}
|
|
316
|
+
if (Array.isArray(parsed)) {
|
|
317
|
+
return parsed.filter((entry) => isRecord(entry) && "type" in entry);
|
|
318
|
+
}
|
|
319
|
+
if (!isRecord(parsed)) {
|
|
320
|
+
return void 0;
|
|
321
|
+
}
|
|
322
|
+
return "type" in parsed ? [parsed] : [];
|
|
323
|
+
}
|
|
324
|
+
function runNpmJson(command) {
|
|
325
|
+
try {
|
|
326
|
+
return { exitOk: true, stdout: execSync(command, { encoding: "utf8", stdio: "pipe" }) };
|
|
327
|
+
} catch (error) {
|
|
328
|
+
const stdout = isRecord(error) && typeof error["stdout"] === "string" ? error["stdout"] : "";
|
|
329
|
+
return { exitOk: false, stdout };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function skipIfNothingPublishable() {
|
|
333
|
+
const publishable = discoverWorkspaces({ filter: (workspace) => workspace.isPackage });
|
|
334
|
+
return publishable.length > 0 ? false : "no publishable packages";
|
|
335
|
+
}
|
|
228
336
|
function skipIfNotPublishable(workspace) {
|
|
229
337
|
return workspace.isPackage ? false : "package.json#private is true";
|
|
230
338
|
}
|
|
231
339
|
export {
|
|
232
340
|
buildWorkspaceCheck,
|
|
341
|
+
classifyNpmAuth,
|
|
342
|
+
classifyTrustQuery,
|
|
233
343
|
npm_auto_publish_default as default,
|
|
234
|
-
|
|
344
|
+
packagesChecklist,
|
|
345
|
+
skipIfNotPublishable,
|
|
346
|
+
skipIfNothingPublishable
|
|
235
347
|
};
|
package/.readyup/manifest.json
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
],
|
|
8
8
|
"name": "default",
|
|
9
9
|
"path": "kits/default.js",
|
|
10
|
-
"readyupVersion": "0.
|
|
10
|
+
"readyupVersion": "0.24.0",
|
|
11
11
|
"source": "kits/default.ts",
|
|
12
|
-
"sourceHash": "
|
|
13
|
-
"targetHash": "
|
|
12
|
+
"sourceHash": "fe239d01",
|
|
13
|
+
"targetHash": "9bd8fb5d"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"checklists": [
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
],
|
|
20
20
|
"name": "npm-auto-publish",
|
|
21
21
|
"path": "kits/npm-auto-publish.js",
|
|
22
|
-
"readyupVersion": "0.
|
|
22
|
+
"readyupVersion": "0.24.0",
|
|
23
23
|
"source": "kits/npm-auto-publish.ts",
|
|
24
|
-
"sourceHash": "
|
|
25
|
-
"targetHash": "
|
|
24
|
+
"sourceHash": "ac484613",
|
|
25
|
+
"targetHash": "f790e7d5"
|
|
26
26
|
}
|
|
27
27
|
]
|
|
28
28
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,60 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 10.3.0 — 2026-08-08
|
|
6
|
+
|
|
7
|
+
### 🎉 Features
|
|
8
|
+
|
|
9
|
+
- Stop reporting absence checks that pass (#610)
|
|
10
|
+
|
|
11
|
+
A ReadyUp check that checks for a problem (such as an unwanted file or a stale config reference) now stays silent unless it finds one. The `nmr`, `release-kit`, and `v11y-check` ReadyUp kits previously printed a line for every such check on every run.
|
|
12
|
+
|
|
13
|
+
- Stop reporting non-issues in the readyup kits (#613)
|
|
14
|
+
|
|
15
|
+
Modifies the `release-kit` and `nmr` ReadyUp kits to avoid verbose reporting when an initial check determines that further checks aren't applicable. In particular, a repo with no `release-kit` config file gets a single line in the `release-kit` report, and a repo that publishes nothing now bypasses the `npm-auto-publish` kit instead of failing it.
|
|
16
|
+
|
|
17
|
+
### 🐛 Bug fixes
|
|
18
|
+
|
|
19
|
+
- Publish build output atomically so a failed build cannot destroy it (#631)
|
|
20
|
+
|
|
21
|
+
Fixes an issue where a failed or still-running `nmr build` could leave a package with no build output at all, breaking the tooling that a subsequent build depends on. Separately, the error reported when a package's build output is missing now names the correct recovery command.
|
|
22
|
+
|
|
23
|
+
### ♻️ Refactoring
|
|
24
|
+
|
|
25
|
+
- Retire the deferred-lint-rule list and enforce its rules (#628)
|
|
26
|
+
|
|
27
|
+
Errors that `release-kit` rethrows now carry the original error as their cause, previously available only as message text. Help text and documentation for `--with-release-notes` now use the same placeholder notation for its output paths.
|
|
28
|
+
|
|
29
|
+
All violations of lint rules in the `release-kit` project are now cleared, and the list of deferred lint rules is retired, restoring associated rules to a max severity of "error" during strict linting.
|
|
30
|
+
|
|
31
|
+
### ⚙️ Tooling
|
|
32
|
+
|
|
33
|
+
- Migrate to the shared tsconfig baseline (#626)
|
|
34
|
+
|
|
35
|
+
Adopts `@williamthorsen/tsconfig` as the standard TypeScript configuration for this repo, replacing the previous hand-maintained copy. Reading a property through an index signature now requires bracket notation or a type that declares the property. The `nmr` build now fails on a base TypeScript config it cannot resolve, rather than building without it, and it now resolves a base config named by package name alone.
|
|
36
|
+
|
|
37
|
+
## 10.2.2 — 2026-08-05
|
|
38
|
+
|
|
39
|
+
### 🐛 Bug fixes
|
|
40
|
+
|
|
41
|
+
- Report the missing npm login once, not per package (#598)
|
|
42
|
+
|
|
43
|
+
Fixes an issue where running release-kit's publish-readiness checks without an npm login reported a trusted-publisher failure for every published package. The missing login is now reported once, with `npm login` as the fix. An unreachable registry now has its own check. The trusted publisher check produces more informative failure messages.
|
|
44
|
+
|
|
45
|
+
- Make prepare all-or-nothing and report the tags file by path (#601)
|
|
46
|
+
|
|
47
|
+
Fixes an issue where a failed `prepare` could leave the release flow in a broken intermediate state. A release now lands completely or not at all, and a formatting failure no longer discards one that already landed. When a write fails partway, the error names every file that reached disk, every file that did not, and the commands that undo them.
|
|
48
|
+
|
|
49
|
+
`commit` and `tag` now report the full path of the tags file they looked for, and distinguish a missing file from one that could not be read, so that running from the wrong directory or worktree is no longer reported as a release that never happened.
|
|
50
|
+
|
|
51
|
+
Runs with `--with-release-notes` now list the preview files they write, and `--dry-run` names the ones a real run would write.
|
|
52
|
+
|
|
53
|
+
### 🧪 Tests
|
|
54
|
+
|
|
55
|
+
- Clear the deferred vitest test rules and enforce them (#602)
|
|
56
|
+
|
|
57
|
+
Fixes a subset of previously deferred lint violations and restores the severity of Vitest lint rules to "error" for purposes strict linting.
|
|
58
|
+
|
|
5
59
|
## 10.2.1 — 2026-08-04
|
|
6
60
|
|
|
7
61
|
### 🐛 Bug fixes
|