@smoothbricks/cli 0.10.2 → 0.10.4
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 +31 -23
- package/dist/bun/preload.d.ts +13 -0
- package/dist/bun/preload.d.ts.map +1 -0
- package/dist/bun/preload.js +12 -0
- package/dist/cli.js +36 -4
- package/dist/generate/index.d.ts.map +1 -1
- package/dist/generate/index.js +0 -6
- 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 +192 -26
- 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 +648 -0
- package/dist/lib/cli-package.js +2 -2
- package/dist/lib/json.d.ts +118 -11
- package/dist/lib/json.d.ts.map +1 -1
- package/dist/lib/json.js +317 -29
- package/dist/lib/workspace.d.ts +7 -14
- package/dist/lib/workspace.d.ts.map +1 -1
- package/dist/lib/workspace.js +76 -82
- package/dist/monorepo/ci-workflow.d.ts +2 -0
- package/dist/monorepo/ci-workflow.d.ts.map +1 -1
- package/dist/monorepo/ci-workflow.js +28 -2
- package/dist/monorepo/git-config.js +2 -2
- package/dist/monorepo/index.d.ts +3 -1
- package/dist/monorepo/index.d.ts.map +1 -1
- package/dist/monorepo/index.js +6 -2
- package/dist/monorepo/lockfile.d.ts +13 -1
- package/dist/monorepo/lockfile.d.ts.map +1 -1
- package/dist/monorepo/lockfile.js +33 -21
- package/dist/monorepo/managed-files.d.ts +12 -0
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +86 -28
- package/dist/monorepo/package-policy.d.ts.map +1 -1
- package/dist/monorepo/package-policy.js +78 -45
- package/dist/monorepo/packed-manifest.d.ts +5 -2
- package/dist/monorepo/packed-manifest.d.ts.map +1 -1
- package/dist/monorepo/packed-manifest.js +16 -47
- package/dist/monorepo/packed-package.d.ts.map +1 -1
- package/dist/monorepo/packed-package.js +28 -16
- 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 +18 -6
- 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 +242 -3
- package/dist/monorepo/runtime.d.ts +15 -0
- package/dist/monorepo/runtime.d.ts.map +1 -1
- package/dist/monorepo/runtime.js +66 -12
- package/dist/monorepo/tool-validation.d.ts.map +1 -1
- package/dist/monorepo/tool-validation.js +32 -23
- package/dist/monorepo/wrangler.d.ts.map +1 -1
- package/dist/monorepo/wrangler.js +13 -13
- package/dist/nx/index.d.ts +12 -4
- package/dist/nx/index.d.ts.map +1 -1
- package/dist/nx/index.js +86 -19
- package/dist/pr/index.d.ts.map +1 -1
- package/dist/pr/index.js +28 -32
- package/dist/release/candidates.d.ts +3 -2
- package/dist/release/candidates.d.ts.map +1 -1
- package/dist/release/candidates.js +4 -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 +20 -1
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +221 -89
- 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/dist/wrangler/prepare-env.d.ts +0 -1
- package/dist/wrangler/prepare-env.d.ts.map +1 -1
- package/dist/wrangler/prepare-env.js +100 -51
- package/dist/wrangler/scaffold.d.ts.map +1 -1
- package/dist/wrangler/scaffold.js +12 -12
- package/managed/raw/git-format-staged.yml +19 -1
- package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +6 -0
- package/managed/raw/tooling/direnv/merge-newer-pins.sh +3 -3
- package/managed/raw/tooling/direnv/setup-environment.ts +187 -19
- package/managed/raw/tooling/git-hooks/pre-commit.sh +0 -6
- package/managed/templates/github/actions/cache-nix-devenv/action.yml +12 -10
- package/managed/templates/github/actions/cache-node-modules/action.yml +6 -2
- package/managed/templates/github/actions/cache-nx/action.yml +2 -2
- package/managed/templates/github/actions/cache-ttsc-plugins/action.yml +19 -0
- package/managed/templates/github/actions/save-nix-devenv/action.yml +11 -3
- package/managed/templates/github/actions/setup-devenv/action.yml +5 -0
- package/managed/templates/github/workflows/managed-files.yml +80 -0
- package/package.json +18 -4
- package/src/bun/preload.ts +12 -0
- package/src/cli.ts +49 -11
- package/src/generate/index.ts +0 -6
- package/src/github-ci/index.test.ts +486 -0
- package/src/github-ci/index.ts +220 -30
- package/src/github-ci/outputs.ts +505 -0
- package/src/lib/cli-package.ts +2 -2
- package/src/lib/json.ts +291 -32
- package/src/lib/workspace.ts +88 -100
- package/src/monorepo/__tests__/ci-workflow.test.ts +14 -3
- package/src/monorepo/__tests__/publish-workflow.test.ts +222 -1
- package/src/monorepo/ci-workflow.ts +28 -2
- package/src/monorepo/git-config.ts +2 -2
- package/src/monorepo/index.ts +6 -2
- package/src/monorepo/lockfile.test.ts +35 -36
- package/src/monorepo/lockfile.ts +48 -24
- package/src/monorepo/managed-files.test.ts +63 -0
- package/src/monorepo/managed-files.ts +94 -34
- package/src/monorepo/package-policy.test.ts +70 -51
- package/src/monorepo/package-policy.ts +104 -59
- package/src/monorepo/packed-manifest.ts +18 -52
- package/src/monorepo/packed-package.ts +43 -22
- package/src/monorepo/packs/index.test.ts +18 -1
- package/src/monorepo/packs/index.ts +20 -7
- package/src/monorepo/publish-workflow.ts +482 -3
- package/src/monorepo/runtime.test.ts +42 -3
- package/src/monorepo/runtime.ts +90 -12
- package/src/monorepo/tool-validation.ts +35 -25
- package/src/monorepo/wrangler.test.ts +11 -15
- package/src/monorepo/wrangler.ts +15 -15
- package/src/nx/index.test.ts +43 -0
- package/src/nx/index.ts +110 -23
- package/src/pr/index.ts +42 -36
- package/src/release/__tests__/candidates.test.ts +5 -4
- 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 +98 -2
- package/src/release/candidates.ts +10 -17
- package/src/release/github-release.ts +12 -0
- package/src/release/index.ts +321 -112
- package/src/release/orchestration.ts +55 -3
- package/src/wrangler/prepare-env.ts +75 -54
- package/src/wrangler/scaffold.test.ts +9 -12
- package/src/wrangler/scaffold.ts +13 -13
|
@@ -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,91 @@ 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 E401')).toBe(true);
|
|
183
|
+
expect(
|
|
184
|
+
npmTrustListAccessDenied(
|
|
185
|
+
'',
|
|
186
|
+
'npm error 401 Unauthorized - GET https://registry.npmjs.org/-/package/@scope%2fpkg/trust - {"success":false,"error":"You must be logged in to publish packages."}',
|
|
187
|
+
),
|
|
188
|
+
).toBe(true);
|
|
189
|
+
expect(npmTrustListAccessDenied('', 'npm error code EOTP')).toBe(false);
|
|
190
|
+
});
|
|
191
|
+
|
|
104
192
|
it('parses empty npm trust list output as no trusted publishers', () => {
|
|
105
193
|
expect(parseTrustedPublishers('', stable.name)).toEqual([]);
|
|
106
194
|
expect(parseTrustedPublishers('null', stable.name)).toEqual([]);
|
|
@@ -116,15 +204,18 @@ class RecordingTrustPublisherShell implements TrustPublisherShell<ReleasePackage
|
|
|
116
204
|
private readonly packages: ReleasePackageInfo[];
|
|
117
205
|
private readonly existing: Set<string>;
|
|
118
206
|
private readonly trustedPublisherByPackage: Record<string, TrustedPublisher[]>;
|
|
207
|
+
private readonly trustedPublisherResponses: Record<string, TrustedPublisherLookup[]>;
|
|
119
208
|
|
|
120
209
|
constructor(options: {
|
|
121
210
|
packages: ReleasePackageInfo[];
|
|
122
211
|
existing: string[];
|
|
123
212
|
trustedPublishers?: Record<string, TrustedPublisher[]>;
|
|
213
|
+
trustedPublisherResponses?: Record<string, TrustedPublisherLookup[]>;
|
|
124
214
|
}) {
|
|
125
215
|
this.packages = options.packages;
|
|
126
216
|
this.existing = new Set(options.existing);
|
|
127
217
|
this.trustedPublisherByPackage = options.trustedPublishers ?? {};
|
|
218
|
+
this.trustedPublisherResponses = options.trustedPublisherResponses ?? {};
|
|
128
219
|
}
|
|
129
220
|
|
|
130
221
|
listReleasePackages(): ReleasePackageInfo[] {
|
|
@@ -145,14 +236,19 @@ class RecordingTrustPublisherShell implements TrustPublisherShell<ReleasePackage
|
|
|
145
236
|
return missingPackages;
|
|
146
237
|
}
|
|
147
238
|
|
|
239
|
+
async login(): Promise<void> {
|
|
240
|
+
this.events.push('login');
|
|
241
|
+
}
|
|
242
|
+
|
|
148
243
|
async trustPublisher(pkg: ReleasePackageInfo, dryRun: boolean): Promise<'configured' | 'already-configured'> {
|
|
149
244
|
this.events.push(`trust:${pkg.name}:${dryRun}`);
|
|
150
245
|
return 'configured';
|
|
151
246
|
}
|
|
152
247
|
|
|
153
|
-
async trustedPublishers(pkg: ReleasePackageInfo): Promise<
|
|
248
|
+
async trustedPublishers(pkg: ReleasePackageInfo): Promise<TrustedPublisherLookup> {
|
|
154
249
|
this.events.push(`list:${pkg.name}`);
|
|
155
|
-
|
|
250
|
+
const response = this.trustedPublisherResponses[pkg.name]?.shift();
|
|
251
|
+
return response ?? this.trustedPublisherByPackage[pkg.name] ?? [];
|
|
156
252
|
}
|
|
157
253
|
|
|
158
254
|
log(message: string): void {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { PackageJson } from '../lib/json.js';
|
|
2
2
|
import type { ReleasePackageInfo } from './core.js';
|
|
3
3
|
import { releaseTag } from './core.js';
|
|
4
4
|
|
|
@@ -6,8 +6,8 @@ export interface AutoReleaseCandidateShell {
|
|
|
6
6
|
gitRefExists(ref: string): Promise<boolean>;
|
|
7
7
|
latestStableReleaseRef(projectName: string): Promise<string | null>;
|
|
8
8
|
packageChangedFilesSince(ref: string, packagePath: string): Promise<string[]>;
|
|
9
|
-
packageJsonAtRef(ref: string, packagePath: string): Promise<
|
|
10
|
-
currentPackageJson(packagePath: string): Promise<
|
|
9
|
+
packageJsonAtRef(ref: string, packagePath: string): Promise<PackageJson | null>;
|
|
10
|
+
currentPackageJson(packagePath: string): Promise<PackageJson | null>;
|
|
11
11
|
packageBuildInputPatterns(projectName: string, packagePath: string): Promise<string[]>;
|
|
12
12
|
packageHasHistory(packagePath: string): Promise<boolean>;
|
|
13
13
|
}
|
|
@@ -104,23 +104,16 @@ async function packageHasReleasableChangesSince<Package extends ReleasePackageIn
|
|
|
104
104
|
return false;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
function releasableManifestChanged(
|
|
108
|
-
previousManifest: Record<string, unknown>,
|
|
109
|
-
currentManifest: Record<string, unknown>,
|
|
110
|
-
): boolean {
|
|
107
|
+
function releasableManifestChanged(previousManifest: PackageJson, currentManifest: PackageJson): boolean {
|
|
111
108
|
for (const key of releasableManifestKeys) {
|
|
112
|
-
if (!stableJsonEqual(previousManifest
|
|
109
|
+
if (!stableJsonEqual(Reflect.get(previousManifest, key), Reflect.get(currentManifest, key))) {
|
|
113
110
|
return true;
|
|
114
111
|
}
|
|
115
112
|
}
|
|
116
113
|
return false;
|
|
117
114
|
}
|
|
118
115
|
|
|
119
|
-
function isReleasablePackagePath(
|
|
120
|
-
path: string,
|
|
121
|
-
manifest: Record<string, unknown> | null,
|
|
122
|
-
buildInputPatterns: string[],
|
|
123
|
-
): boolean {
|
|
116
|
+
function isReleasablePackagePath(path: string, manifest: PackageJson | null, buildInputPatterns: string[]): boolean {
|
|
124
117
|
return (
|
|
125
118
|
isBuildInputPath(path, buildInputPatterns) || isPackageMetadataPath(path) || isManifestFilesPath(path, manifest)
|
|
126
119
|
);
|
|
@@ -195,13 +188,13 @@ function isPackageMetadataPath(path: string): boolean {
|
|
|
195
188
|
return /^(README|LICENSE|CHANGELOG)(\.|$)/.test(path);
|
|
196
189
|
}
|
|
197
190
|
|
|
198
|
-
function isManifestFilesPath(path: string, manifest:
|
|
191
|
+
function isManifestFilesPath(path: string, manifest: PackageJson | null): boolean {
|
|
199
192
|
const files = manifest?.files;
|
|
200
193
|
if (!Array.isArray(files)) {
|
|
201
194
|
return false;
|
|
202
195
|
}
|
|
203
196
|
for (const entry of files) {
|
|
204
|
-
if (
|
|
197
|
+
if (entry.length === 0 || entry.startsWith('!')) {
|
|
205
198
|
continue;
|
|
206
199
|
}
|
|
207
200
|
if (matchesManifestFilesEntry(path, entry)) {
|
|
@@ -227,10 +220,10 @@ function stableJson(value: unknown): unknown {
|
|
|
227
220
|
if (Array.isArray(value)) {
|
|
228
221
|
return value.map(stableJson);
|
|
229
222
|
}
|
|
230
|
-
if (
|
|
223
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
231
224
|
const normalized: Record<string, unknown> = {};
|
|
232
225
|
for (const key of Object.keys(value).sort()) {
|
|
233
|
-
normalized[key] = stableJson(value
|
|
226
|
+
normalized[key] = stableJson(Reflect.get(value, key));
|
|
234
227
|
}
|
|
235
228
|
return normalized;
|
|
236
229
|
}
|
|
@@ -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,
|