@smoothbricks/cli 0.10.2 → 0.10.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/README.md +25 -13
- package/dist/cli.js +24 -0
- package/dist/github-ci/index.d.ts +17 -2
- package/dist/github-ci/index.d.ts.map +1 -1
- package/dist/github-ci/index.js +141 -11
- package/dist/github-ci/outputs.d.ts +22 -0
- package/dist/github-ci/outputs.d.ts.map +1 -0
- package/dist/github-ci/outputs.js +649 -0
- package/dist/monorepo/ci-workflow.js +1 -1
- package/dist/monorepo/managed-files.d.ts +1 -0
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +38 -0
- package/dist/monorepo/package-policy.d.ts.map +1 -1
- package/dist/monorepo/package-policy.js +26 -0
- package/dist/monorepo/packs/index.d.ts +3 -0
- package/dist/monorepo/packs/index.d.ts.map +1 -1
- package/dist/monorepo/packs/index.js +5 -2
- package/dist/monorepo/publish-workflow.d.ts +1 -0
- package/dist/monorepo/publish-workflow.d.ts.map +1 -1
- package/dist/monorepo/publish-workflow.js +239 -2
- package/dist/nx/index.d.ts +6 -0
- package/dist/nx/index.d.ts.map +1 -1
- package/dist/nx/index.js +55 -5
- package/dist/release/github-release.d.ts +3 -0
- package/dist/release/github-release.d.ts.map +1 -1
- package/dist/release/github-release.js +11 -0
- package/dist/release/index.d.ts +19 -1
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +140 -31
- package/dist/release/orchestration.d.ts +8 -1
- package/dist/release/orchestration.d.ts.map +1 -1
- package/dist/release/orchestration.js +38 -1
- package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +5 -0
- package/managed/raw/tooling/direnv/setup-environment.ts +40 -3
- package/managed/templates/github/actions/cache-nix-devenv/action.yml +12 -10
- package/managed/templates/github/actions/cache-node-modules/action.yml +3 -2
- package/managed/templates/github/actions/cache-nx/action.yml +2 -2
- package/managed/templates/github/actions/save-nix-devenv/action.yml +11 -3
- package/package.json +7 -4
- package/src/cli.ts +30 -4
- package/src/github-ci/index.test.ts +486 -0
- package/src/github-ci/index.ts +172 -13
- package/src/github-ci/outputs.ts +506 -0
- package/src/monorepo/__tests__/ci-workflow.test.ts +11 -0
- package/src/monorepo/__tests__/publish-workflow.test.ts +222 -1
- package/src/monorepo/ci-workflow.ts +1 -1
- package/src/monorepo/managed-files.test.ts +62 -0
- package/src/monorepo/managed-files.ts +41 -0
- package/src/monorepo/package-policy.test.ts +50 -1
- package/src/monorepo/package-policy.ts +28 -0
- package/src/monorepo/packs/index.test.ts +18 -1
- package/src/monorepo/packs/index.ts +7 -3
- package/src/monorepo/publish-workflow.ts +479 -2
- package/src/nx/index.test.ts +39 -0
- package/src/nx/index.ts +65 -5
- package/src/release/__tests__/fixture-repo.test.ts +18 -16
- package/src/release/__tests__/github-release.test.ts +11 -0
- package/src/release/__tests__/orchestration.test.ts +57 -2
- package/src/release/__tests__/trust-publisher.test.ts +91 -2
- package/src/release/github-release.ts +12 -0
- package/src/release/index.ts +218 -40
- package/src/release/orchestration.ts +55 -3
|
@@ -175,7 +175,9 @@ describe('release planning with fixture git repositories', () => {
|
|
|
175
175
|
const summaries = await repairPendingTargets(shell, pending, restoreRef, false);
|
|
176
176
|
|
|
177
177
|
expect(shell.checkouts).toEqual([githubOnlySha, npmAndGithubSha, restoreRef]);
|
|
178
|
-
expect(shell.devenvLoads).toBe(
|
|
178
|
+
expect(shell.devenvLoads).toBe(3);
|
|
179
|
+
expect(shell.devenvRefs).toEqual([githubOnlySha, npmAndGithubSha, headSha]);
|
|
180
|
+
await expect(readFile(join(runner, '.generated-tool-ref'), 'utf8')).resolves.toBe(`${headSha}\n`);
|
|
179
181
|
expect(shell.builds).toEqual([['@scope/b']]);
|
|
180
182
|
expect(shell.publishes).toEqual([{ name: '@scope/b', version: '2.0.0-beta.1', distTag: 'next', dryRun: false }]);
|
|
181
183
|
expect(shell.githubCreates).toEqual([
|
|
@@ -189,40 +191,36 @@ describe('release planning with fixture git repositories', () => {
|
|
|
189
191
|
});
|
|
190
192
|
});
|
|
191
193
|
|
|
192
|
-
it('pushes current release refs to a local bare remote
|
|
194
|
+
it('pushes current release refs to a local bare remote', async () => {
|
|
193
195
|
await withFixtureRepo(async (author) => {
|
|
194
196
|
await writeWorkspace(author);
|
|
195
197
|
await writeBuildablePackage(author, '@scope/pushed', 'packages/pushed', '1.0.0');
|
|
196
198
|
await git(author, ['add', '.']);
|
|
197
199
|
await git(author, ['commit', '-m', 'release pushed package']);
|
|
200
|
+
const releaseSha = await gitOutput(author, ['rev-parse', 'HEAD']);
|
|
198
201
|
await git(author, ['init', '--bare', 'remote.git']);
|
|
199
202
|
await git(author, ['remote', 'add', 'origin', join(author, 'remote.git')]);
|
|
200
203
|
await git(author, ['push', 'origin', 'main']);
|
|
201
|
-
await git(author, ['clone', '--branch', 'main', join(author, 'remote.git'), 'runner']);
|
|
202
|
-
const runner = join(author, 'runner');
|
|
203
|
-
await git(runner, ['config', 'user.name', 'Test User']);
|
|
204
|
-
await git(runner, ['config', 'user.email', 'test@example.com']);
|
|
205
204
|
const pkg: ReleasePackageInfo = {
|
|
206
205
|
name: '@scope/pushed',
|
|
207
206
|
projectName: 'pushed',
|
|
208
207
|
path: 'packages/pushed',
|
|
209
208
|
version: '1.0.0',
|
|
210
209
|
};
|
|
211
|
-
const shell = new LocalGitRepairShell(
|
|
210
|
+
const shell = new LocalGitRepairShell(author);
|
|
212
211
|
|
|
213
212
|
const summary = await completeReleaseAtHead(shell, [pkg], false, false);
|
|
214
213
|
|
|
215
214
|
expect(summary.pushed).toBe(true);
|
|
216
215
|
expect(shell.pushes).toEqual([['pushed@1.0.0']]);
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
);
|
|
216
|
+
const remoteTags = await gitOutput(author, [
|
|
217
|
+
'ls-remote',
|
|
218
|
+
'--tags',
|
|
219
|
+
'origin',
|
|
220
|
+
'refs/tags/pushed@1.0.0^{}',
|
|
221
|
+
'refs/tags/@scope/pushed@1.0.0^{}',
|
|
222
|
+
]);
|
|
223
|
+
expect(remoteTags).toBe(`${releaseSha}\trefs/tags/pushed@1.0.0^{}`);
|
|
226
224
|
});
|
|
227
225
|
});
|
|
228
226
|
|
|
@@ -295,6 +293,7 @@ class LocalGitRepairShell implements ReleaseRepairShell<ReleasePackageInfo> {
|
|
|
295
293
|
readonly publishes: Array<{ name: string; version: string; distTag: string; dryRun: boolean }> = [];
|
|
296
294
|
readonly githubCreates: Array<{ name: string; version: string; dryRun: boolean }> = [];
|
|
297
295
|
devenvLoads = 0;
|
|
296
|
+
readonly devenvRefs: string[] = [];
|
|
298
297
|
|
|
299
298
|
constructor(private readonly root: string) {}
|
|
300
299
|
|
|
@@ -360,6 +359,9 @@ class LocalGitRepairShell implements ReleaseRepairShell<ReleasePackageInfo> {
|
|
|
360
359
|
|
|
361
360
|
async withDevenvEnv<T>(runWithEnv: () => Promise<T>): Promise<T> {
|
|
362
361
|
this.devenvLoads += 1;
|
|
362
|
+
const currentRef = await this.gitHead();
|
|
363
|
+
this.devenvRefs.push(currentRef);
|
|
364
|
+
await writeFile(join(this.root, '.generated-tool-ref'), `${currentRef}\n`);
|
|
363
365
|
return runWithEnv();
|
|
364
366
|
}
|
|
365
367
|
|
|
@@ -4,6 +4,7 @@ import type { ReleasePackageInfo } from '../core.js';
|
|
|
4
4
|
import {
|
|
5
5
|
createOrUpdateGithubRelease,
|
|
6
6
|
type GithubReleaseWriteShell,
|
|
7
|
+
githubReleaseLookupExists,
|
|
7
8
|
nxProjectChangelogArgs,
|
|
8
9
|
projectChangelogContents,
|
|
9
10
|
} from '../github-release.js';
|
|
@@ -26,6 +27,7 @@ describe('GitHub release helpers', () => {
|
|
|
26
27
|
gitPush: false,
|
|
27
28
|
stageChanges: false,
|
|
28
29
|
createRelease: false,
|
|
30
|
+
forceChangelogGeneration: true,
|
|
29
31
|
deleteVersionPlans: false,
|
|
30
32
|
dryRun: false,
|
|
31
33
|
from: 'pkg@1.2.2',
|
|
@@ -41,6 +43,7 @@ describe('GitHub release helpers', () => {
|
|
|
41
43
|
gitPush: false,
|
|
42
44
|
stageChanges: false,
|
|
43
45
|
createRelease: false,
|
|
46
|
+
forceChangelogGeneration: true,
|
|
44
47
|
deleteVersionPlans: false,
|
|
45
48
|
dryRun: true,
|
|
46
49
|
firstRelease: true,
|
|
@@ -66,6 +69,14 @@ describe('GitHub release helpers', () => {
|
|
|
66
69
|
);
|
|
67
70
|
});
|
|
68
71
|
|
|
72
|
+
it('distinguishes missing releases from transient GitHub lookup failures', () => {
|
|
73
|
+
expect(githubReleaseLookupExists('pkg@1.2.3', 0, '{"tagName":"pkg@1.2.3"}', '')).toBe(true);
|
|
74
|
+
expect(githubReleaseLookupExists('pkg@1.2.3', 1, '', 'HTTP 404: release not found')).toBe(false);
|
|
75
|
+
expect(() => githubReleaseLookupExists('pkg@1.2.3', 1, '', 'HTTP 503: Service Unavailable')).toThrow(
|
|
76
|
+
'Unable to inspect GitHub Release pkg@1.2.3.\nHTTP 503: Service Unavailable',
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
69
80
|
it('creates a missing stable GitHub Release from the generated notes file', async () => {
|
|
70
81
|
const shell = new RecordingGithubReleaseShell(false);
|
|
71
82
|
|
|
@@ -2,7 +2,9 @@ import { describe, expect, it } from 'bun:test';
|
|
|
2
2
|
import { type ReleasePackageInfo, type ReleaseTarget, releaseTag } from '../core.js';
|
|
3
3
|
import {
|
|
4
4
|
bumpStableReleaseToNext,
|
|
5
|
+
collectRepairPlatformOutputs,
|
|
5
6
|
completeReleaseAtHead,
|
|
7
|
+
type ReleaseRepairOutputsShell,
|
|
6
8
|
type ReleaseRepairShell,
|
|
7
9
|
type ReleaseVersionShell,
|
|
8
10
|
repairPendingTargets,
|
|
@@ -23,14 +25,16 @@ const prerelease: ReleasePackageInfo = {
|
|
|
23
25
|
};
|
|
24
26
|
|
|
25
27
|
describe('release orchestration', () => {
|
|
26
|
-
it('repairs an older target
|
|
28
|
+
it('repairs an older target and refreshes devenv after restoring the caller ref', async () => {
|
|
27
29
|
const target = releaseTarget('older-release', [stable, prerelease], [stable], [prerelease]);
|
|
28
30
|
const shell = new RecordingRepairShell();
|
|
29
31
|
|
|
30
32
|
const summaries = await repairPendingTargets(shell, [target], 'restore-ref', false);
|
|
31
33
|
|
|
32
34
|
expect(shell.checkouts).toEqual(['older-release', 'restore-ref']);
|
|
33
|
-
expect(shell.devenvLoads).toBe(
|
|
35
|
+
expect(shell.devenvLoads).toBe(2);
|
|
36
|
+
expect(shell.devenvRefs).toEqual(['older-release', 'restore-ref']);
|
|
37
|
+
expect(shell.prepares).toEqual(['older-release']);
|
|
34
38
|
expect(shell.builds).toEqual([['@scope/stable']]);
|
|
35
39
|
expect(shell.publishes).toEqual([{ name: '@scope/stable', distTag: 'latest', dryRun: false }]);
|
|
36
40
|
expect(shell.githubCreates).toEqual([{ name: '@scope/prerelease', dryRun: false }]);
|
|
@@ -78,6 +82,29 @@ describe('release orchestration', () => {
|
|
|
78
82
|
]);
|
|
79
83
|
});
|
|
80
84
|
|
|
85
|
+
it('collects historical platform outputs only for npm-missing repair targets', async () => {
|
|
86
|
+
const githubOnly = releaseTarget('github-only', [prerelease], [], [prerelease]);
|
|
87
|
+
const npmMissing = releaseTarget('npm-missing', [stable], [stable], []);
|
|
88
|
+
const shell = new RecordingRepairOutputsShell();
|
|
89
|
+
|
|
90
|
+
await collectRepairPlatformOutputs(shell, [githubOnly, npmMissing], 'restore-ref');
|
|
91
|
+
|
|
92
|
+
expect(shell.checkouts).toEqual(['npm-missing', 'restore-ref']);
|
|
93
|
+
expect(shell.devenvRefs).toEqual(['npm-missing', 'restore-ref']);
|
|
94
|
+
expect(shell.collected).toEqual(['npm-missing']);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('does nothing when no pending release needs npm platform outputs', async () => {
|
|
98
|
+
const shell = new RecordingRepairOutputsShell();
|
|
99
|
+
const githubOnly = releaseTarget('github-only', [prerelease], [], [prerelease]);
|
|
100
|
+
|
|
101
|
+
await collectRepairPlatformOutputs(shell, [githubOnly], 'restore-ref');
|
|
102
|
+
|
|
103
|
+
expect(shell.checkouts).toEqual([]);
|
|
104
|
+
expect(shell.devenvRefs).toEqual([]);
|
|
105
|
+
expect(shell.collected).toEqual([]);
|
|
106
|
+
});
|
|
107
|
+
|
|
81
108
|
it('publishes a partial HEAD release by building npm-missing packages and creating missing GitHub Releases', async () => {
|
|
82
109
|
const shell = new RecordingRepairShell({ npmMissing: ['@scope/stable'], githubMissing: ['@scope/prerelease'] });
|
|
83
110
|
|
|
@@ -216,15 +243,21 @@ class RecordingRepairShell implements ReleaseRepairShell<ReleasePackageInfo> {
|
|
|
216
243
|
readonly npmQueries: string[][] = [];
|
|
217
244
|
readonly githubQueries: string[][] = [];
|
|
218
245
|
devenvLoads = 0;
|
|
246
|
+
readonly devenvRefs: string[] = [];
|
|
219
247
|
currentRef = 'head';
|
|
220
248
|
private readonly npmMissing: Set<string>;
|
|
221
249
|
private readonly githubMissing: Set<string>;
|
|
250
|
+
readonly prepares: string[] = [];
|
|
222
251
|
|
|
223
252
|
constructor(options: { npmMissing?: string[]; githubMissing?: string[] } = {}) {
|
|
224
253
|
this.npmMissing = new Set(options.npmMissing ?? []);
|
|
225
254
|
this.githubMissing = new Set(options.githubMissing ?? []);
|
|
226
255
|
}
|
|
227
256
|
|
|
257
|
+
async prepareRepairTarget(target: ReleaseTarget<ReleasePackageInfo>): Promise<void> {
|
|
258
|
+
this.prepares.push(target.sha);
|
|
259
|
+
}
|
|
260
|
+
|
|
228
261
|
async gitHead(): Promise<string> {
|
|
229
262
|
return this.currentRef;
|
|
230
263
|
}
|
|
@@ -264,10 +297,32 @@ class RecordingRepairShell implements ReleaseRepairShell<ReleasePackageInfo> {
|
|
|
264
297
|
|
|
265
298
|
async withDevenvEnv<T>(runWithEnv: () => Promise<T>): Promise<T> {
|
|
266
299
|
this.devenvLoads += 1;
|
|
300
|
+
this.devenvRefs.push(this.currentRef);
|
|
267
301
|
return runWithEnv();
|
|
268
302
|
}
|
|
269
303
|
}
|
|
270
304
|
|
|
305
|
+
class RecordingRepairOutputsShell implements ReleaseRepairOutputsShell<ReleasePackageInfo> {
|
|
306
|
+
readonly checkouts: string[] = [];
|
|
307
|
+
readonly collected: string[] = [];
|
|
308
|
+
readonly devenvRefs: string[] = [];
|
|
309
|
+
private currentRef = 'head';
|
|
310
|
+
|
|
311
|
+
async checkout(ref: string): Promise<void> {
|
|
312
|
+
this.currentRef = ref;
|
|
313
|
+
this.checkouts.push(ref);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async withDevenvEnv<T>(runWithEnv: () => Promise<T>): Promise<T> {
|
|
317
|
+
this.devenvRefs.push(this.currentRef);
|
|
318
|
+
return runWithEnv();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async collectRepairTargetOutputs(target: ReleaseTarget<ReleasePackageInfo>): Promise<void> {
|
|
322
|
+
this.collected.push(target.sha);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
271
326
|
class RecordingVersionShell implements ReleaseVersionShell<ReleasePackageInfo> {
|
|
272
327
|
readonly ensureCalls: string[][] = [];
|
|
273
328
|
readonly nxRuns: Array<{ packages: string[]; bump: string; dryRun: boolean }> = [];
|
|
@@ -3,8 +3,11 @@ import type { BootstrapNpmPackagesOptions } from '../bootstrap-npm-packages.js';
|
|
|
3
3
|
import type { ReleasePackageInfo } from '../core.js';
|
|
4
4
|
import {
|
|
5
5
|
configureTrustedPublishers,
|
|
6
|
+
npmTrustGithubArgs,
|
|
7
|
+
npmTrustListAccessDenied,
|
|
6
8
|
parseTrustedPublishers,
|
|
7
9
|
type TrustedPublisher,
|
|
10
|
+
type TrustedPublisherLookup,
|
|
8
11
|
type TrustPublisherShell,
|
|
9
12
|
} from '../index.js';
|
|
10
13
|
|
|
@@ -101,6 +104,84 @@ describe('trusted publisher setup', () => {
|
|
|
101
104
|
expect(shell.events).toEqual([`exists:${missing.name}`, `list:${missing.name}`, `trust:${missing.name}:false`]);
|
|
102
105
|
});
|
|
103
106
|
|
|
107
|
+
it('reports package owners and opens npm login after trust access is denied', async () => {
|
|
108
|
+
const shell = new RecordingTrustPublisherShell({
|
|
109
|
+
packages: [stable],
|
|
110
|
+
existing: [stable.name],
|
|
111
|
+
trustedPublisherResponses: {
|
|
112
|
+
[stable.name]: [
|
|
113
|
+
{
|
|
114
|
+
status: 'access-denied',
|
|
115
|
+
identity: 'current-user',
|
|
116
|
+
owners: 'package-owner <owner@example.test>',
|
|
117
|
+
},
|
|
118
|
+
[],
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await configureTrustedPublishers(shell, {});
|
|
124
|
+
|
|
125
|
+
expect(shell.events).toEqual([
|
|
126
|
+
`exists:${stable.name}`,
|
|
127
|
+
`list:${stable.name}`,
|
|
128
|
+
'login',
|
|
129
|
+
`list:${stable.name}`,
|
|
130
|
+
`trust:${stable.name}:false`,
|
|
131
|
+
]);
|
|
132
|
+
expect(shell.errors.join('\n')).toContain('npm account "current-user"');
|
|
133
|
+
expect(shell.errors.join('\n')).toContain('package-owner <owner@example.test>');
|
|
134
|
+
expect(shell.logs).toContain(
|
|
135
|
+
`${stable.name}: opening npm browser login. Sign in as a listed package owner, then return here.`,
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('stops after one owner login when the selected account still lacks access', async () => {
|
|
140
|
+
const shell = new RecordingTrustPublisherShell({
|
|
141
|
+
packages: [stable],
|
|
142
|
+
existing: [stable.name],
|
|
143
|
+
trustedPublisherResponses: {
|
|
144
|
+
[stable.name]: [
|
|
145
|
+
{
|
|
146
|
+
status: 'access-denied',
|
|
147
|
+
identity: 'current-user',
|
|
148
|
+
owners: 'package-owner <owner@example.test>',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
status: 'access-denied',
|
|
152
|
+
identity: 'wrong-user',
|
|
153
|
+
owners: 'package-owner <owner@example.test>',
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await expect(configureTrustedPublishers(shell, {})).rejects.toThrow(
|
|
160
|
+
'npm browser login completed, but the selected account still cannot manage this package',
|
|
161
|
+
);
|
|
162
|
+
expect(shell.events).toEqual([`exists:${stable.name}`, `list:${stable.name}`, 'login', `list:${stable.name}`]);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('grants the trusted workflow permission to run npm publish', () => {
|
|
166
|
+
expect(npmTrustGithubArgs(stable.name, 'scope/repo', 'publish.yml', false)).toEqual([
|
|
167
|
+
'trust',
|
|
168
|
+
'github',
|
|
169
|
+
stable.name,
|
|
170
|
+
'--file',
|
|
171
|
+
'publish.yml',
|
|
172
|
+
'--repo',
|
|
173
|
+
'scope/repo',
|
|
174
|
+
'--allow-publish',
|
|
175
|
+
'--yes',
|
|
176
|
+
]);
|
|
177
|
+
expect(npmTrustGithubArgs(stable.name, 'scope/repo', 'publish.yml', true).at(-1)).toBe('--dry-run');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('recognizes npm trust access-denied output', () => {
|
|
181
|
+
expect(npmTrustListAccessDenied('{"error":{"code":"E403"}}', '')).toBe(true);
|
|
182
|
+
expect(npmTrustListAccessDenied('', 'npm error code EOTP')).toBe(false);
|
|
183
|
+
});
|
|
184
|
+
|
|
104
185
|
it('parses empty npm trust list output as no trusted publishers', () => {
|
|
105
186
|
expect(parseTrustedPublishers('', stable.name)).toEqual([]);
|
|
106
187
|
expect(parseTrustedPublishers('null', stable.name)).toEqual([]);
|
|
@@ -116,15 +197,18 @@ class RecordingTrustPublisherShell implements TrustPublisherShell<ReleasePackage
|
|
|
116
197
|
private readonly packages: ReleasePackageInfo[];
|
|
117
198
|
private readonly existing: Set<string>;
|
|
118
199
|
private readonly trustedPublisherByPackage: Record<string, TrustedPublisher[]>;
|
|
200
|
+
private readonly trustedPublisherResponses: Record<string, TrustedPublisherLookup[]>;
|
|
119
201
|
|
|
120
202
|
constructor(options: {
|
|
121
203
|
packages: ReleasePackageInfo[];
|
|
122
204
|
existing: string[];
|
|
123
205
|
trustedPublishers?: Record<string, TrustedPublisher[]>;
|
|
206
|
+
trustedPublisherResponses?: Record<string, TrustedPublisherLookup[]>;
|
|
124
207
|
}) {
|
|
125
208
|
this.packages = options.packages;
|
|
126
209
|
this.existing = new Set(options.existing);
|
|
127
210
|
this.trustedPublisherByPackage = options.trustedPublishers ?? {};
|
|
211
|
+
this.trustedPublisherResponses = options.trustedPublisherResponses ?? {};
|
|
128
212
|
}
|
|
129
213
|
|
|
130
214
|
listReleasePackages(): ReleasePackageInfo[] {
|
|
@@ -145,14 +229,19 @@ class RecordingTrustPublisherShell implements TrustPublisherShell<ReleasePackage
|
|
|
145
229
|
return missingPackages;
|
|
146
230
|
}
|
|
147
231
|
|
|
232
|
+
async login(): Promise<void> {
|
|
233
|
+
this.events.push('login');
|
|
234
|
+
}
|
|
235
|
+
|
|
148
236
|
async trustPublisher(pkg: ReleasePackageInfo, dryRun: boolean): Promise<'configured' | 'already-configured'> {
|
|
149
237
|
this.events.push(`trust:${pkg.name}:${dryRun}`);
|
|
150
238
|
return 'configured';
|
|
151
239
|
}
|
|
152
240
|
|
|
153
|
-
async trustedPublishers(pkg: ReleasePackageInfo): Promise<
|
|
241
|
+
async trustedPublishers(pkg: ReleasePackageInfo): Promise<TrustedPublisherLookup> {
|
|
154
242
|
this.events.push(`list:${pkg.name}`);
|
|
155
|
-
|
|
243
|
+
const response = this.trustedPublisherResponses[pkg.name]?.shift();
|
|
244
|
+
return response ?? this.trustedPublisherByPackage[pkg.name] ?? [];
|
|
156
245
|
}
|
|
157
246
|
|
|
158
247
|
log(message: string): void {
|
|
@@ -71,6 +71,7 @@ export function nxProjectChangelogArgs(pkg: ReleasePackageInfo, previousTag: str
|
|
|
71
71
|
gitPush: false,
|
|
72
72
|
stageChanges: false,
|
|
73
73
|
createRelease: false,
|
|
74
|
+
forceChangelogGeneration: true,
|
|
74
75
|
deleteVersionPlans: false,
|
|
75
76
|
dryRun,
|
|
76
77
|
} satisfies ChangelogOptions;
|
|
@@ -85,6 +86,17 @@ export function projectChangelogContents(result: ProjectChangelogLookupResult, p
|
|
|
85
86
|
return changelog.contents;
|
|
86
87
|
}
|
|
87
88
|
|
|
89
|
+
export function githubReleaseLookupExists(tag: string, exitCode: number, stdout: string, stderr: string): boolean {
|
|
90
|
+
if (exitCode === 0) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
const details = [stderr.trim(), stdout.trim()].filter(Boolean).join('\n');
|
|
94
|
+
if (/\bHTTP 404\b|release not found/i.test(details)) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
throw new Error(`Unable to inspect GitHub Release ${tag}.${details ? `\n${details}` : ''}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
88
100
|
export function githubReleaseCommandArgs(
|
|
89
101
|
tag: string,
|
|
90
102
|
notesFile: string,
|