@smoothbricks/cli 0.1.1 → 0.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/README.md +161 -32
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +110 -10
- package/dist/devenv/index.d.ts +10 -0
- package/dist/devenv/index.d.ts.map +1 -0
- package/dist/devenv/index.js +27 -0
- package/dist/generate/index.d.ts +20 -0
- package/dist/generate/index.d.ts.map +1 -0
- package/dist/generate/index.js +47 -0
- package/dist/lib/cli-package.d.ts +4 -0
- package/dist/lib/cli-package.d.ts.map +1 -0
- package/dist/lib/cli-package.js +15 -0
- package/dist/lib/run.d.ts +6 -0
- package/dist/lib/run.d.ts.map +1 -1
- package/dist/lib/run.js +32 -0
- package/dist/lib/workspace.d.ts +25 -1
- package/dist/lib/workspace.d.ts.map +1 -1
- package/dist/lib/workspace.js +79 -17
- package/dist/monorepo/commit-msg.d.ts +8 -1
- package/dist/monorepo/commit-msg.d.ts.map +1 -1
- package/dist/monorepo/commit-msg.js +85 -7
- package/dist/monorepo/index.d.ts +19 -2
- package/dist/monorepo/index.d.ts.map +1 -1
- package/dist/monorepo/index.js +104 -16
- package/dist/monorepo/lockfile.d.ts.map +1 -1
- package/dist/monorepo/lockfile.js +9 -0
- package/dist/monorepo/managed-files.js +1 -1
- package/dist/monorepo/nx-sync.d.ts +2 -1
- package/dist/monorepo/nx-sync.d.ts.map +1 -1
- package/dist/monorepo/nx-sync.js +8 -2
- package/dist/monorepo/package-hygiene.d.ts +6 -2
- package/dist/monorepo/package-hygiene.d.ts.map +1 -1
- package/dist/monorepo/package-hygiene.js +12 -4
- package/dist/monorepo/package-policy.d.ts +13 -4
- package/dist/monorepo/package-policy.d.ts.map +1 -1
- package/dist/monorepo/package-policy.js +208 -113
- package/dist/monorepo/packed-package.d.ts +3 -0
- package/dist/monorepo/packed-package.d.ts.map +1 -1
- package/dist/monorepo/packed-package.js +68 -36
- package/dist/monorepo/packs/index.d.ts +20 -1
- package/dist/monorepo/packs/index.d.ts.map +1 -1
- package/dist/monorepo/packs/index.js +238 -19
- package/dist/monorepo/publish-workflow.d.ts +0 -4
- package/dist/monorepo/publish-workflow.d.ts.map +1 -1
- package/dist/monorepo/publish-workflow.js +7 -15
- package/dist/monorepo/tool-validation.d.ts +22 -0
- package/dist/monorepo/tool-validation.d.ts.map +1 -0
- package/dist/monorepo/tool-validation.js +399 -0
- package/dist/nx/index.d.ts +25 -0
- package/dist/nx/index.d.ts.map +1 -0
- package/dist/nx/index.js +112 -0
- package/dist/release/bootstrap-npm-packages.d.ts +19 -0
- package/dist/release/bootstrap-npm-packages.d.ts.map +1 -0
- package/dist/release/bootstrap-npm-packages.js +58 -0
- package/dist/release/candidates.d.ts +11 -0
- package/dist/release/candidates.d.ts.map +1 -0
- package/dist/release/candidates.js +181 -0
- package/dist/release/core.d.ts +4 -1
- package/dist/release/core.d.ts.map +1 -1
- package/dist/release/core.js +76 -9
- package/dist/release/github-release.d.ts +1 -0
- package/dist/release/github-release.d.ts.map +1 -1
- package/dist/release/github-release.js +3 -3
- package/dist/release/index.d.ts +32 -1
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +488 -64
- package/dist/release/npm-auth.d.ts +0 -1
- package/dist/release/npm-auth.d.ts.map +1 -1
- package/dist/release/npm-auth.js +4 -16
- package/dist/release/orchestration.d.ts +11 -2
- package/dist/release/orchestration.d.ts.map +1 -1
- package/dist/release/orchestration.js +41 -3
- package/dist/release/publish-plan.js +1 -1
- package/managed/raw/git-format-staged.yml +54 -0
- package/managed/raw/tooling/git-hooks/commit-msg.sh +1 -1
- package/managed/templates/github/actions/setup-devenv/action.yml +1 -7
- package/managed/templates/github/workflows/ci.yml +4 -2
- package/package.json +15 -8
- package/src/cli.ts +153 -22
- package/src/devenv/index.test.ts +49 -0
- package/src/devenv/index.ts +38 -0
- package/src/generate/index.ts +92 -0
- package/src/lib/cli-package.ts +18 -0
- package/src/lib/run.ts +44 -0
- package/src/lib/workspace.ts +112 -17
- package/src/monorepo/__tests__/publish-workflow.test.ts +16 -9
- package/src/monorepo/commit-msg.test.ts +45 -0
- package/src/monorepo/commit-msg.ts +107 -7
- package/src/monorepo/index.ts +142 -16
- package/src/monorepo/lockfile.ts +9 -0
- package/src/monorepo/managed-files.ts +1 -1
- package/src/monorepo/nx-sync.ts +9 -2
- package/src/monorepo/package-hygiene.test.ts +34 -0
- package/src/monorepo/package-hygiene.ts +26 -4
- package/src/monorepo/package-policy.test.ts +1130 -0
- package/src/monorepo/package-policy.ts +237 -114
- package/src/monorepo/packed-package.ts +89 -37
- package/src/monorepo/packs/index.test.ts +189 -0
- package/src/monorepo/packs/index.ts +311 -20
- package/src/monorepo/publish-workflow.ts +10 -19
- package/src/monorepo/setup-test-tracing.test.ts +120 -0
- package/src/monorepo/tool-validation.test.ts +261 -0
- package/src/monorepo/tool-validation.ts +462 -0
- package/src/nx/index.test.ts +73 -0
- package/src/nx/index.ts +139 -0
- package/src/release/__tests__/bootstrap-npm-packages.test.ts +127 -0
- package/src/release/__tests__/candidates.test.ts +262 -0
- package/src/release/__tests__/core-properties.test.ts +6 -6
- package/src/release/__tests__/core-scenarios.test.ts +27 -20
- package/src/release/__tests__/core.test.ts +52 -5
- package/src/release/__tests__/fixture-repo.test.ts +90 -31
- package/src/release/__tests__/github-release.test.ts +22 -24
- package/src/release/__tests__/helpers/fixture-repo.ts +1 -0
- package/src/release/__tests__/npm-auth.test.ts +12 -21
- package/src/release/__tests__/orchestration.test.ts +142 -12
- package/src/release/__tests__/publish-plan.test.ts +9 -3
- package/src/release/__tests__/retag-unpublished.test.ts +10 -10
- package/src/release/__tests__/trust-publisher.test.ts +165 -0
- package/src/release/bootstrap-npm-packages.ts +88 -0
- package/src/release/candidates.ts +225 -0
- package/src/release/core.ts +96 -10
- package/src/release/github-release.ts +3 -3
- package/src/release/index.ts +658 -74
- package/src/release/npm-auth.ts +4 -33
- package/src/release/orchestration.ts +60 -5
- package/src/release/publish-plan.ts +1 -1
- package/dist/nx-version-actions.cjs +0 -25
- package/dist/nx-version-actions.d.cts +0 -6
- package/dist/nx-version-actions.d.cts.map +0 -1
- package/managed/raw/.git-format-staged.yml +0 -47
- package/src/nx-version-actions.cts +0 -36
|
@@ -25,14 +25,14 @@ describe('release planning with fixture git repositories', () => {
|
|
|
25
25
|
await git(root, ['add', '.']);
|
|
26
26
|
await git(root, ['commit', '-m', 'initial release']);
|
|
27
27
|
const first = await gitOutput(root, ['rev-parse', 'HEAD']);
|
|
28
|
-
await tag(root, '
|
|
29
|
-
await tag(root, '
|
|
28
|
+
await tag(root, 'a@1.0.0', '2025-01-01T00:00:00Z');
|
|
29
|
+
await tag(root, 'b@1.0.0', '2025-01-01T00:00:01Z');
|
|
30
30
|
|
|
31
31
|
await writePackage(root, '@scope/a', 'packages/a', '1.1.0');
|
|
32
32
|
await git(root, ['add', '.']);
|
|
33
33
|
await git(root, ['commit', '-m', 'second release']);
|
|
34
34
|
const second = await gitOutput(root, ['rev-parse', 'HEAD']);
|
|
35
|
-
await tag(root, '
|
|
35
|
+
await tag(root, 'a@1.1.0', '2025-01-02T00:00:00Z');
|
|
36
36
|
|
|
37
37
|
await writeFile(join(root, 'readme.md'), 'not a release\n');
|
|
38
38
|
await git(root, ['add', '.']);
|
|
@@ -41,11 +41,11 @@ describe('release planning with fixture git repositories', () => {
|
|
|
41
41
|
await tag(root, 'not-owned@9.9.9', '2025-01-03T00:00:00Z');
|
|
42
42
|
|
|
43
43
|
const npmPublished = new Set(['@scope/a@1.0.0', '@scope/b@1.0.0']);
|
|
44
|
-
const githubReleases = new Set(['
|
|
44
|
+
const githubReleases = new Set(['a@1.0.0']);
|
|
45
45
|
const records = await collectOwnedReleaseTagRecords(
|
|
46
46
|
[
|
|
47
|
-
{ name: '@scope/a', path: 'packages/a' },
|
|
48
|
-
{ name: '@scope/b', path: 'packages/b' },
|
|
47
|
+
{ name: '@scope/a', projectName: 'a', path: 'packages/a' },
|
|
48
|
+
{ name: '@scope/b', projectName: 'b', path: 'packages/b' },
|
|
49
49
|
],
|
|
50
50
|
head,
|
|
51
51
|
{
|
|
@@ -59,7 +59,7 @@ describe('release planning with fixture git repositories', () => {
|
|
|
59
59
|
},
|
|
60
60
|
);
|
|
61
61
|
|
|
62
|
-
expect(records.map((record) => record.tag)).toEqual(['
|
|
62
|
+
expect(records.map((record) => record.tag)).toEqual(['a@1.1.0', 'b@1.0.0', 'a@1.0.0']);
|
|
63
63
|
const pending = pendingReleaseTargets(records, head);
|
|
64
64
|
expect(pending.map((target) => target.sha)).toEqual([first, second]);
|
|
65
65
|
expect(pending[0]?.packages.map((pkg) => pkg.name)).toEqual(['@scope/b']);
|
|
@@ -74,7 +74,7 @@ describe('release planning with fixture git repositories', () => {
|
|
|
74
74
|
await writePackage(root, '@scope/a', 'packages/a', '1.0.0');
|
|
75
75
|
await git(root, ['add', '.']);
|
|
76
76
|
await git(root, ['commit', '-m', 'release a']);
|
|
77
|
-
await tag(root, '
|
|
77
|
+
await tag(root, 'a@1.0.0', '2025-01-01T00:00:00Z');
|
|
78
78
|
await git(root, ['init', '--bare', 'remote.git']);
|
|
79
79
|
await git(root, ['remote', 'add', 'origin', join(root, 'remote.git')]);
|
|
80
80
|
await git(root, ['push', 'origin', 'main', '--tags']);
|
|
@@ -83,14 +83,18 @@ describe('release planning with fixture git repositories', () => {
|
|
|
83
83
|
await git(checkout, ['fetch', '--tags', 'origin', 'main']);
|
|
84
84
|
const head = await gitOutput(checkout, ['rev-parse', 'HEAD']);
|
|
85
85
|
|
|
86
|
-
const records = await collectOwnedReleaseTagRecords(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
const records = await collectOwnedReleaseTagRecords(
|
|
87
|
+
[{ name: '@scope/a', projectName: 'a', path: 'packages/a' }],
|
|
88
|
+
head,
|
|
89
|
+
{
|
|
90
|
+
listReleaseTagsByCreatorDate: () => gitReleaseTagsByCreatorDate(checkout),
|
|
91
|
+
isAncestor: (ancestor, descendant) => gitIsAncestor(checkout, ancestor, descendant),
|
|
92
|
+
packageVersionAtRef: (packagePath, ref) => packageVersionAtRef(checkout, packagePath, ref),
|
|
93
|
+
durableTagState: async () => ({ npmPublished: false, githubReleaseExists: false }),
|
|
94
|
+
},
|
|
95
|
+
);
|
|
92
96
|
|
|
93
|
-
expect(records.map((record) => record.tag)).toEqual(['
|
|
97
|
+
expect(records.map((record) => record.tag)).toEqual(['a@1.0.0']);
|
|
94
98
|
expect(pendingReleaseTargets(records, 'not-head').map((target) => target.sha)).toEqual([head]);
|
|
95
99
|
});
|
|
96
100
|
});
|
|
@@ -101,7 +105,7 @@ describe('release planning with fixture git repositories', () => {
|
|
|
101
105
|
await writeBuildablePackage(root, '@scope/a', 'packages/a');
|
|
102
106
|
await writeBuildablePackage(root, '@scope/b', 'packages/b');
|
|
103
107
|
|
|
104
|
-
await $`nx run-many -t build --projects=${'
|
|
108
|
+
await $`nx run-many -t build --projects=${'a,b'}`.cwd(root).quiet();
|
|
105
109
|
|
|
106
110
|
await expect(readFile(join(root, 'packages/a/dist/index.js'), 'utf8')).resolves.toBe('{}\n');
|
|
107
111
|
await expect(readFile(join(root, 'packages/b/dist/index.js'), 'utf8')).resolves.toBe('{}\n');
|
|
@@ -115,26 +119,26 @@ describe('release planning with fixture git repositories', () => {
|
|
|
115
119
|
await writeBuildablePackage(author, '@scope/b', 'packages/b', '1.0.0');
|
|
116
120
|
await git(author, ['add', '.']);
|
|
117
121
|
await git(author, ['commit', '-m', 'initial release']);
|
|
118
|
-
await tag(author, '
|
|
119
|
-
await tag(author, '
|
|
122
|
+
await tag(author, 'a@1.0.0', '2025-01-01T00:00:00Z');
|
|
123
|
+
await tag(author, 'b@1.0.0', '2025-01-01T00:00:01Z');
|
|
120
124
|
|
|
121
125
|
await writeBuildablePackage(author, '@scope/a', 'packages/a', '1.1.0');
|
|
122
126
|
await git(author, ['add', 'packages/a/package.json']);
|
|
123
127
|
await git(author, ['commit', '-m', 'release a 1.1.0']);
|
|
124
128
|
const githubOnlySha = await gitOutput(author, ['rev-parse', 'HEAD']);
|
|
125
|
-
await tag(author, '
|
|
129
|
+
await tag(author, 'a@1.1.0', '2025-01-02T00:00:00Z');
|
|
126
130
|
|
|
127
131
|
await writeBuildablePackage(author, '@scope/b', 'packages/b', '2.0.0-beta.1');
|
|
128
132
|
await git(author, ['add', 'packages/b/package.json']);
|
|
129
133
|
await git(author, ['commit', '-m', 'release b prerelease']);
|
|
130
134
|
const npmAndGithubSha = await gitOutput(author, ['rev-parse', 'HEAD']);
|
|
131
|
-
await tag(author, '
|
|
135
|
+
await tag(author, 'b@2.0.0-beta.1', '2025-01-03T00:00:00Z');
|
|
132
136
|
|
|
133
137
|
await writeBuildablePackage(author, '@scope/a', 'packages/a', '1.2.0');
|
|
134
138
|
await git(author, ['add', 'packages/a/package.json']);
|
|
135
139
|
await git(author, ['commit', '-m', 'head release a 1.2.0']);
|
|
136
140
|
const headSha = await gitOutput(author, ['rev-parse', 'HEAD']);
|
|
137
|
-
await tag(author, '
|
|
141
|
+
await tag(author, 'a@1.2.0', '2025-01-04T00:00:00Z');
|
|
138
142
|
|
|
139
143
|
await git(author, ['init', '--bare', 'remote.git']);
|
|
140
144
|
await git(author, ['remote', 'add', 'origin', join(author, 'remote.git')]);
|
|
@@ -147,7 +151,7 @@ describe('release planning with fixture git repositories', () => {
|
|
|
147
151
|
const restoreRef = 'origin/main';
|
|
148
152
|
const packages = releaseFixturePackages();
|
|
149
153
|
const npmPublished = new Set(['@scope/a@1.0.0', '@scope/b@1.0.0', '@scope/a@1.1.0']);
|
|
150
|
-
const githubReleases = new Set(['
|
|
154
|
+
const githubReleases = new Set(['a@1.0.0', 'b@1.0.0']);
|
|
151
155
|
|
|
152
156
|
const records = await collectOwnedReleaseTagRecords(packages, restoreRef, {
|
|
153
157
|
listReleaseTagsByCreatorDate: () => gitReleaseTagsByCreatorDate(runner),
|
|
@@ -177,7 +181,7 @@ describe('release planning with fixture git repositories', () => {
|
|
|
177
181
|
{ name: '@scope/a', version: '1.1.0', dryRun: false },
|
|
178
182
|
{ name: '@scope/b', version: '2.0.0-beta.1', dryRun: false },
|
|
179
183
|
]);
|
|
180
|
-
expect(shell.pushes).toEqual([['
|
|
184
|
+
expect(shell.pushes).toEqual([['a@1.1.0'], ['b@2.0.0-beta.1']]);
|
|
181
185
|
expect(summaries.map((summary) => summary.sha)).toEqual([githubOnlySha, npmAndGithubSha]);
|
|
182
186
|
await expect(readFile(join(runner, 'packages/b/dist/index.js'), 'utf8')).resolves.toBe('{}\n');
|
|
183
187
|
await expect(readFile(join(runner, 'packages/a/dist/index.js'), 'utf8')).rejects.toThrow();
|
|
@@ -197,27 +201,81 @@ describe('release planning with fixture git repositories', () => {
|
|
|
197
201
|
const runner = join(author, 'runner');
|
|
198
202
|
await git(runner, ['config', 'user.name', 'Test User']);
|
|
199
203
|
await git(runner, ['config', 'user.email', 'test@example.com']);
|
|
200
|
-
const pkg: ReleasePackageInfo = {
|
|
204
|
+
const pkg: ReleasePackageInfo = {
|
|
205
|
+
name: '@scope/pushed',
|
|
206
|
+
projectName: 'pushed',
|
|
207
|
+
path: 'packages/pushed',
|
|
208
|
+
version: '1.0.0',
|
|
209
|
+
};
|
|
201
210
|
const shell = new LocalGitRepairShell(runner);
|
|
202
211
|
|
|
203
212
|
const summary = await completeReleaseAtHead(shell, [pkg], false, false);
|
|
204
213
|
|
|
205
214
|
expect(summary.pushed).toBe(true);
|
|
206
|
-
expect(shell.pushes).toEqual([['
|
|
215
|
+
expect(shell.pushes).toEqual([['pushed@1.0.0']]);
|
|
207
216
|
await $`git clone --branch main ${join(author, 'remote.git')} auditor`.cwd(author).quiet();
|
|
208
217
|
const auditor = join(author, 'auditor');
|
|
209
218
|
await git(auditor, ['fetch', '--tags', 'origin', 'main']);
|
|
210
|
-
await expect(gitOutput(auditor, ['rev-parse', 'refs/tags
|
|
219
|
+
await expect(gitOutput(auditor, ['rev-parse', 'refs/tags/pushed@1.0.0^{}'])).resolves.toBe(
|
|
211
220
|
await gitOutput(runner, ['rev-parse', 'HEAD']),
|
|
212
221
|
);
|
|
222
|
+
await expect(gitSucceeds(auditor, ['rev-parse', '--verify', 'refs/tags/@scope/pushed@1.0.0'])).resolves.toBe(
|
|
223
|
+
false,
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('repairs a scoped package from its project-name release tag without creating a package-name tag', async () => {
|
|
229
|
+
await withFixtureRepo(async (author) => {
|
|
230
|
+
await writeWorkspace(author);
|
|
231
|
+
await writeBuildablePackage(author, '@scope/cli', 'packages/cli', '0.2.0');
|
|
232
|
+
await git(author, ['add', '.']);
|
|
233
|
+
await git(author, ['commit', '-m', 'release cli 0.2.0']);
|
|
234
|
+
const releaseSha = await gitOutput(author, ['rev-parse', 'HEAD']);
|
|
235
|
+
await tag(author, 'cli@0.2.0', '2025-01-01T00:00:00Z');
|
|
236
|
+
|
|
237
|
+
await git(author, ['init', '--bare', 'remote.git']);
|
|
238
|
+
await git(author, ['remote', 'add', 'origin', join(author, 'remote.git')]);
|
|
239
|
+
await git(author, ['push', 'origin', 'main', '--tags']);
|
|
240
|
+
await $`git clone --branch main ${join(author, 'remote.git')} runner`.cwd(author).quiet();
|
|
241
|
+
const runner = join(author, 'runner');
|
|
242
|
+
await git(runner, ['config', 'user.name', 'Test User']);
|
|
243
|
+
await git(runner, ['config', 'user.email', 'test@example.com']);
|
|
244
|
+
await git(runner, ['fetch', '--tags', 'origin', 'main']);
|
|
245
|
+
|
|
246
|
+
const pkg: ReleasePackageInfo = {
|
|
247
|
+
name: '@scope/cli',
|
|
248
|
+
projectName: 'cli',
|
|
249
|
+
path: 'packages/cli',
|
|
250
|
+
version: '0.0.0',
|
|
251
|
+
};
|
|
252
|
+
const records = await collectOwnedReleaseTagRecords([pkg], 'origin/main', {
|
|
253
|
+
listReleaseTagsByCreatorDate: () => gitReleaseTagsByCreatorDate(runner),
|
|
254
|
+
isAncestor: (ancestor, descendant) => gitIsAncestor(runner, ancestor, descendant),
|
|
255
|
+
packageVersionAtRef: (packagePath, ref) => packageVersionAtRef(runner, packagePath, ref),
|
|
256
|
+
durableTagState: async () => ({ npmPublished: false, githubReleaseExists: false }),
|
|
257
|
+
});
|
|
258
|
+
const pending = pendingReleaseTargets(records, 'not-head');
|
|
259
|
+
|
|
260
|
+
const shell = new LocalGitRepairShell(runner);
|
|
261
|
+
const summaries = await repairPendingTargets(shell, pending, 'origin/main', false);
|
|
262
|
+
|
|
263
|
+
expect(records.map((record) => record.tag)).toEqual(['cli@0.2.0']);
|
|
264
|
+
expect(pending.map((target) => target.sha)).toEqual([releaseSha]);
|
|
265
|
+
expect(shell.pushes).toEqual([['cli@0.2.0']]);
|
|
266
|
+
expect(shell.publishes).toEqual([{ name: '@scope/cli', version: '0.2.0', distTag: 'latest', dryRun: false }]);
|
|
267
|
+
expect(shell.githubCreates).toEqual([{ name: '@scope/cli', version: '0.2.0', dryRun: false }]);
|
|
268
|
+
expect(summaries[0]?.packages.map((releasePackage) => releasePackage.name)).toEqual(['@scope/cli']);
|
|
269
|
+
await expect(gitSucceeds(runner, ['rev-parse', '--verify', 'refs/tags/cli@0.2.0'])).resolves.toBe(true);
|
|
270
|
+
await expect(gitSucceeds(runner, ['rev-parse', '--verify', 'refs/tags/@scope/cli@0.2.0'])).resolves.toBe(false);
|
|
213
271
|
});
|
|
214
272
|
});
|
|
215
273
|
});
|
|
216
274
|
|
|
217
275
|
function releaseFixturePackages(): ReleasePackageInfo[] {
|
|
218
276
|
return [
|
|
219
|
-
{ name: '@scope/a', path: 'packages/a', version: '0.0.0' },
|
|
220
|
-
{ name: '@scope/b', path: 'packages/b', version: '0.0.0' },
|
|
277
|
+
{ name: '@scope/a', projectName: 'a', path: 'packages/a', version: '0.0.0' },
|
|
278
|
+
{ name: '@scope/b', projectName: 'b', path: 'packages/b', version: '0.0.0' },
|
|
221
279
|
];
|
|
222
280
|
}
|
|
223
281
|
|
|
@@ -236,7 +294,7 @@ class LocalGitRepairShell implements ReleaseRepairShell<ReleasePackageInfo> {
|
|
|
236
294
|
}
|
|
237
295
|
|
|
238
296
|
async pushReleaseRefs(packages: ReleasePackageInfo[]): Promise<boolean> {
|
|
239
|
-
this.pushes.push(packages.map((pkg) =>
|
|
297
|
+
this.pushes.push(packages.map((pkg) => releaseTag(pkg)));
|
|
240
298
|
await git(this.root, ['fetch', '--tags', 'origin', 'main']);
|
|
241
299
|
for (const pkg of packages) {
|
|
242
300
|
await this.ensureLocalReleaseTag(pkg);
|
|
@@ -265,7 +323,7 @@ class LocalGitRepairShell implements ReleaseRepairShell<ReleasePackageInfo> {
|
|
|
265
323
|
|
|
266
324
|
async buildReleaseCandidate(packages: ReleasePackageInfo[]): Promise<void> {
|
|
267
325
|
this.builds.push(packages.map((pkg) => pkg.name));
|
|
268
|
-
await $`nx run-many -t build --projects=${packages.map((pkg) => pkg.
|
|
326
|
+
await $`nx run-many -t build --projects=${packages.map((pkg) => pkg.projectName).join(',')}`.cwd(this.root).quiet();
|
|
269
327
|
}
|
|
270
328
|
|
|
271
329
|
async publishPackage(pkg: ReleasePackageInfo, distTag: string, dryRun: boolean): Promise<void> {
|
|
@@ -276,8 +334,9 @@ class LocalGitRepairShell implements ReleaseRepairShell<ReleasePackageInfo> {
|
|
|
276
334
|
return [];
|
|
277
335
|
}
|
|
278
336
|
|
|
279
|
-
async createGithubRelease(pkg: ReleasePackageInfo, dryRun: boolean): Promise<
|
|
337
|
+
async createGithubRelease(pkg: ReleasePackageInfo, dryRun: boolean): Promise<string | null> {
|
|
280
338
|
this.githubCreates.push({ name: pkg.name, version: pkg.version, dryRun });
|
|
339
|
+
return dryRun ? null : `https://github.test/${releaseTag(pkg)}`;
|
|
281
340
|
}
|
|
282
341
|
|
|
283
342
|
async checkout(ref: string): Promise<void> {
|
|
@@ -8,14 +8,19 @@ import {
|
|
|
8
8
|
projectChangelogContents,
|
|
9
9
|
} from '../github-release.js';
|
|
10
10
|
|
|
11
|
-
const stable: ReleasePackageInfo = { name: '@scope/pkg', path: 'packages/pkg', version: '1.2.3' };
|
|
12
|
-
const prerelease: ReleasePackageInfo = {
|
|
11
|
+
const stable: ReleasePackageInfo = { name: '@scope/pkg', projectName: 'pkg', path: 'packages/pkg', version: '1.2.3' };
|
|
12
|
+
const prerelease: ReleasePackageInfo = {
|
|
13
|
+
name: '@scope/pkg',
|
|
14
|
+
projectName: 'pkg',
|
|
15
|
+
path: 'packages/pkg',
|
|
16
|
+
version: '2.0.0-beta.1',
|
|
17
|
+
};
|
|
13
18
|
|
|
14
19
|
describe('GitHub release helpers', () => {
|
|
15
20
|
it('configures the Nx changelog API for render-only project changelogs', () => {
|
|
16
|
-
expect(nxProjectChangelogArgs(stable, '
|
|
21
|
+
expect(nxProjectChangelogArgs(stable, 'pkg@1.2.2', false)).toEqual({
|
|
17
22
|
version: '1.2.3',
|
|
18
|
-
projects: ['
|
|
23
|
+
projects: ['pkg'],
|
|
19
24
|
gitCommit: false,
|
|
20
25
|
gitTag: false,
|
|
21
26
|
gitPush: false,
|
|
@@ -23,14 +28,14 @@ describe('GitHub release helpers', () => {
|
|
|
23
28
|
createRelease: false,
|
|
24
29
|
deleteVersionPlans: false,
|
|
25
30
|
dryRun: false,
|
|
26
|
-
from: '
|
|
31
|
+
from: 'pkg@1.2.2',
|
|
27
32
|
});
|
|
28
33
|
});
|
|
29
34
|
|
|
30
35
|
it('marks the Nx changelog request as a first release when there is no previous tag', () => {
|
|
31
36
|
expect(nxProjectChangelogArgs(stable, null, true)).toEqual({
|
|
32
37
|
version: '1.2.3',
|
|
33
|
-
projects: ['
|
|
38
|
+
projects: ['pkg'],
|
|
34
39
|
gitCommit: false,
|
|
35
40
|
gitTag: false,
|
|
36
41
|
gitPush: false,
|
|
@@ -47,17 +52,17 @@ describe('GitHub release helpers', () => {
|
|
|
47
52
|
projectChangelogContents(
|
|
48
53
|
{
|
|
49
54
|
projectChangelogs: {
|
|
50
|
-
|
|
55
|
+
pkg: { contents: 'generated release notes' },
|
|
51
56
|
},
|
|
52
57
|
},
|
|
53
|
-
'
|
|
58
|
+
'pkg',
|
|
54
59
|
),
|
|
55
60
|
).toBe('generated release notes');
|
|
56
61
|
});
|
|
57
62
|
|
|
58
63
|
it('fails when Nx omits the requested project changelog', () => {
|
|
59
|
-
expect(() => projectChangelogContents({ projectChangelogs: {} }, '
|
|
60
|
-
'Nx did not generate a project changelog for
|
|
64
|
+
expect(() => projectChangelogContents({ projectChangelogs: {} }, 'pkg')).toThrow(
|
|
65
|
+
'Nx did not generate a project changelog for pkg.',
|
|
61
66
|
);
|
|
62
67
|
});
|
|
63
68
|
|
|
@@ -66,18 +71,11 @@ describe('GitHub release helpers', () => {
|
|
|
66
71
|
|
|
67
72
|
await createOrUpdateGithubRelease(stable, 'stable notes', shell);
|
|
68
73
|
|
|
69
|
-
expect(shell.existsQueries).toEqual(['
|
|
74
|
+
expect(shell.existsQueries).toEqual(['pkg@1.2.3']);
|
|
70
75
|
expect(shell.notes).toEqual(['stable notes']);
|
|
71
|
-
expect(shell.logs).toEqual(['@scope/pkg@1.2.3: creating GitHub Release for
|
|
76
|
+
expect(shell.logs).toEqual(['@scope/pkg@1.2.3: creating GitHub Release for pkg@1.2.3.']);
|
|
72
77
|
const command = onlyCommand(shell.commands);
|
|
73
|
-
expect(command.slice(0, 6)).toEqual([
|
|
74
|
-
'release',
|
|
75
|
-
'create',
|
|
76
|
-
'@scope/pkg@1.2.3',
|
|
77
|
-
'--title',
|
|
78
|
-
'@scope/pkg@1.2.3',
|
|
79
|
-
'--notes-file',
|
|
80
|
-
]);
|
|
78
|
+
expect(command.slice(0, 6)).toEqual(['release', 'create', 'pkg@1.2.3', '--title', 'pkg@1.2.3', '--notes-file']);
|
|
81
79
|
expect(command).toContain('--verify-tag');
|
|
82
80
|
expect(command).toContain('--latest=true');
|
|
83
81
|
expect(command).not.toContain('--prerelease');
|
|
@@ -88,16 +86,16 @@ describe('GitHub release helpers', () => {
|
|
|
88
86
|
|
|
89
87
|
await createOrUpdateGithubRelease(prerelease, 'prerelease notes', shell);
|
|
90
88
|
|
|
91
|
-
expect(shell.existsQueries).toEqual(['
|
|
89
|
+
expect(shell.existsQueries).toEqual(['pkg@2.0.0-beta.1']);
|
|
92
90
|
expect(shell.notes).toEqual(['prerelease notes']);
|
|
93
|
-
expect(shell.logs).toEqual(['@scope/pkg@2.0.0-beta.1: updating GitHub Release for
|
|
91
|
+
expect(shell.logs).toEqual(['@scope/pkg@2.0.0-beta.1: updating GitHub Release for pkg@2.0.0-beta.1.']);
|
|
94
92
|
const command = onlyCommand(shell.commands);
|
|
95
93
|
expect(command.slice(0, 6)).toEqual([
|
|
96
94
|
'release',
|
|
97
95
|
'edit',
|
|
98
|
-
'
|
|
96
|
+
'pkg@2.0.0-beta.1',
|
|
99
97
|
'--title',
|
|
100
|
-
'
|
|
98
|
+
'pkg@2.0.0-beta.1',
|
|
101
99
|
'--notes-file',
|
|
102
100
|
]);
|
|
103
101
|
expect(command).toContain('--verify-tag');
|
|
@@ -12,12 +12,10 @@ const pkg: Pick<ReleasePackageInfo, 'name' | 'version'> = { name: '@scope/pkg',
|
|
|
12
12
|
describe('npm publish auth diagnostics', () => {
|
|
13
13
|
it('explains trusted publishing setup when an existing package publish is unauthenticated', () => {
|
|
14
14
|
const message = npmPublishAuthFailureMessage(pkg, {
|
|
15
|
-
useBootstrapToken: false,
|
|
16
15
|
tokenPresent: true,
|
|
17
16
|
repository: 'smoothbricks/codebase',
|
|
18
17
|
});
|
|
19
18
|
const markdown = npmPublishAuthFailureMarkdown(pkg, {
|
|
20
|
-
useBootstrapToken: false,
|
|
21
19
|
tokenPresent: true,
|
|
22
20
|
repository: 'smoothbricks/codebase',
|
|
23
21
|
});
|
|
@@ -32,16 +30,12 @@ describe('npm publish auth diagnostics', () => {
|
|
|
32
30
|
expect(markdown).toContain('repository `smoothbricks/codebase` and workflow `publish.yml`');
|
|
33
31
|
});
|
|
34
32
|
|
|
35
|
-
it('
|
|
36
|
-
const message = npmPublishAuthFailureMessage(pkg, {
|
|
37
|
-
const markdown = npmPublishAuthFailureMarkdown(pkg, {
|
|
33
|
+
it('points first package publishes to local placeholder bootstrap', () => {
|
|
34
|
+
const message = npmPublishAuthFailureMessage(pkg, { tokenPresent: false });
|
|
35
|
+
const markdown = npmPublishAuthFailureMarkdown(pkg, { tokenPresent: false });
|
|
38
36
|
|
|
39
|
-
expect(message).toContain('
|
|
40
|
-
expect(
|
|
41
|
-
expect(message).toContain('After the first successful publish, run:');
|
|
42
|
-
expect(message).toContain('smoo release trust-publisher');
|
|
43
|
-
expect(markdown).toContain('Add a temporary `NPM_TOKEN` repository secret');
|
|
44
|
-
expect(markdown).toContain('After the first successful publish, run `smoo release trust-publisher`');
|
|
37
|
+
expect(message).toContain('smoo release trust-publisher --bootstrap');
|
|
38
|
+
expect(markdown).toContain('smoo release trust-publisher --bootstrap');
|
|
45
39
|
});
|
|
46
40
|
|
|
47
41
|
it('reports trusted-publishing guidance and writes it to the publish summary after existing package auth failure', async () => {
|
|
@@ -49,7 +43,6 @@ describe('npm publish auth diagnostics', () => {
|
|
|
49
43
|
|
|
50
44
|
await expect(
|
|
51
45
|
publishWithAuthDiagnostics(pkg, shell, {
|
|
52
|
-
useBootstrapToken: false,
|
|
53
46
|
tokenPresent: true,
|
|
54
47
|
repository: 'smoothbricks/codebase',
|
|
55
48
|
}),
|
|
@@ -66,26 +59,24 @@ describe('npm publish auth diagnostics', () => {
|
|
|
66
59
|
expect(shell.logs).toEqual([]);
|
|
67
60
|
});
|
|
68
61
|
|
|
69
|
-
it('reports bootstrap
|
|
62
|
+
it('reports bootstrap command guidance and writes it to the publish summary after first-publish auth failure', async () => {
|
|
70
63
|
const shell = new RecordingPublishShell({ publishFails: true });
|
|
71
64
|
|
|
72
|
-
await expect(
|
|
73
|
-
|
|
74
|
-
)
|
|
65
|
+
await expect(publishWithAuthDiagnostics(pkg, shell, { tokenPresent: false })).rejects.toThrow(
|
|
66
|
+
'@scope/pkg@1.2.3: npm publish authentication failed',
|
|
67
|
+
);
|
|
75
68
|
|
|
76
69
|
expect(shell.errors).toHaveLength(1);
|
|
77
|
-
expect(shell.errors[0]).toContain('
|
|
78
|
-
expect(shell.errors[0]).toContain('NODE_AUTH_TOKEN/NPM_TOKEN is not set');
|
|
79
|
-
expect(shell.errors[0]).toContain('smoo release trust-publisher');
|
|
70
|
+
expect(shell.errors[0]).toContain('smoo release trust-publisher --bootstrap');
|
|
80
71
|
expect(shell.summaries).toHaveLength(1);
|
|
81
|
-
expect(shell.summaries[0]).toContain('
|
|
72
|
+
expect(shell.summaries[0]).toContain('smoo release trust-publisher --bootstrap');
|
|
82
73
|
expect(shell.logs).toEqual([]);
|
|
83
74
|
});
|
|
84
75
|
|
|
85
76
|
it('continues without auth warning when the package version appears on npm after publish failure', async () => {
|
|
86
77
|
const shell = new RecordingPublishShell({ publishFails: true, versionVisibleAfterFailure: true });
|
|
87
78
|
|
|
88
|
-
await publishWithAuthDiagnostics(pkg, shell, {
|
|
79
|
+
await publishWithAuthDiagnostics(pkg, shell, { tokenPresent: true });
|
|
89
80
|
|
|
90
81
|
expect(shell.errors).toEqual([]);
|
|
91
82
|
expect(shell.summaries).toEqual([]);
|