@smoothbricks/cli 0.10.1 → 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 +145 -14
- 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/lib/conflict-markers.d.ts.map +1 -1
- package/dist/lib/workspace.d.ts +1 -1
- package/dist/lib/workspace.d.ts.map +1 -1
- package/dist/monorepo/ci-workflow.d.ts.map +1 -1
- package/dist/monorepo/ci-workflow.js +2 -1
- package/dist/monorepo/managed-files.d.ts +2 -0
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +50 -5
- 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 +240 -2
- package/dist/monorepo/tool-validation.d.ts.map +1 -1
- package/dist/monorepo/tool-validation.js +20 -7
- 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 +5 -2
- 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/dist/wrangler/prepare-env.d.ts.map +1 -1
- package/dist/wrangler/prepare-env.js +1 -1
- package/dist/wrangler/scaffold.d.ts.map +1 -1
- package/dist/wrangler/scaffold.js +1 -1
- package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +5 -0
- package/managed/raw/tooling/direnv/setup-environment.ts +32 -1
- 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 -14
- package/src/cli.ts +30 -4
- package/src/github-ci/index.test.ts +486 -0
- package/src/github-ci/index.ts +177 -16
- package/src/github-ci/outputs.ts +506 -0
- package/src/monorepo/__tests__/ci-workflow.test.ts +13 -0
- package/src/monorepo/__tests__/publish-workflow.test.ts +234 -5
- package/src/monorepo/ci-workflow.ts +3 -1
- package/src/monorepo/managed-files.test.ts +75 -0
- package/src/monorepo/managed-files.ts +61 -5
- package/src/monorepo/package-policy.test.ts +57 -38
- 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 +481 -2
- package/src/monorepo/tool-validation.test.ts +59 -24
- package/src/monorepo/tool-validation.ts +21 -7
- package/src/monorepo/wrangler.test.ts +9 -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
- package/src/wrangler/prepare-env.ts +14 -4
- package/src/wrangler/scaffold.ts +8 -3
package/src/github-ci/index.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { appendFile, mkdtemp, realpath, rename, rm } from 'node:fs/promises';
|
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import { $ } from 'bun';
|
|
5
5
|
import { decode, run, runStatus } from '../lib/run.js';
|
|
6
|
+
import { type ProjectTargets, readProjectTargets } from '../nx/index.js';
|
|
7
|
+
import type { NxTargetRun } from './outputs.js';
|
|
6
8
|
|
|
7
9
|
type NxSmartMode = 'auto' | 'affected' | 'run-many';
|
|
8
10
|
|
|
@@ -113,6 +115,15 @@ async function addReferencesFrom(roots: Set<string>, path: string, cwd: string):
|
|
|
113
115
|
}
|
|
114
116
|
}
|
|
115
117
|
|
|
118
|
+
export function nxSmartArgs(target: string, mode: 'affected' | 'run-many', configuration?: string): string[] {
|
|
119
|
+
const args = [mode, '-t', target];
|
|
120
|
+
if (configuration) {
|
|
121
|
+
args.push(`--configuration=${configuration}`);
|
|
122
|
+
}
|
|
123
|
+
args.push(`--exclude=tag:ci:skip:${target}`, '--parallel=5');
|
|
124
|
+
return args;
|
|
125
|
+
}
|
|
126
|
+
|
|
116
127
|
export async function githubCiNxSmart(
|
|
117
128
|
root: string,
|
|
118
129
|
options: { target: string; name?: string; step?: string; mode?: NxSmartMode; configuration?: string },
|
|
@@ -121,11 +132,7 @@ export async function githubCiNxSmart(
|
|
|
121
132
|
const step = options.step ?? '';
|
|
122
133
|
await createGithubStatus(name, step);
|
|
123
134
|
const mode = resolveNxSmartMode(options.mode ?? 'auto');
|
|
124
|
-
const nxArgs =
|
|
125
|
-
if (options.configuration) {
|
|
126
|
-
nxArgs.push(`--configuration=${options.configuration}`);
|
|
127
|
-
}
|
|
128
|
-
nxArgs.push('--parallel=5');
|
|
135
|
+
const nxArgs = nxSmartArgs(options.target, mode, options.configuration);
|
|
129
136
|
const status = await runStatus('nx', nxArgs, root);
|
|
130
137
|
await updateGithubStatus(name, status === 0 ? 'success' : 'failure', step);
|
|
131
138
|
if (status !== 0) {
|
|
@@ -133,18 +140,170 @@ export async function githubCiNxSmart(
|
|
|
133
140
|
}
|
|
134
141
|
}
|
|
135
142
|
|
|
136
|
-
export
|
|
143
|
+
export interface NxRunManyOptions {
|
|
144
|
+
targets: string;
|
|
145
|
+
projects?: string;
|
|
146
|
+
configuration?: string;
|
|
147
|
+
collectOutputs?: string;
|
|
148
|
+
allowEmptyProjects?: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface ExpandedNxTargetRuns {
|
|
152
|
+
runs: NxTargetRun[];
|
|
153
|
+
unmatchedGlobs: string[];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function expandNxTargetRuns(projects: ProjectTargets[], options: NxRunManyOptions): ExpandedNxTargetRuns {
|
|
157
|
+
const selectedProjects = selectProjects(projects, options.projects);
|
|
158
|
+
if (options.projects !== undefined && selectedProjects.length === 0 && options.allowEmptyProjects !== true) {
|
|
159
|
+
throw new Error(`No Nx projects matched --projects ${options.projects}.`);
|
|
160
|
+
}
|
|
161
|
+
const selectedTargetNames = [...new Set(selectedProjects.flatMap((project) => project.targets))].sort((a, b) =>
|
|
162
|
+
a.localeCompare(b),
|
|
163
|
+
);
|
|
164
|
+
const runs: NxTargetRun[] = [];
|
|
165
|
+
const unmatchedGlobs: string[] = [];
|
|
166
|
+
const addedTargets = new Set<string>();
|
|
167
|
+
for (const targetPattern of commaSeparatedValues(options.targets)) {
|
|
168
|
+
const isGlob = isGlobPattern(targetPattern);
|
|
169
|
+
const targets = isGlob
|
|
170
|
+
? selectedTargetNames.filter((target) => new Bun.Glob(targetPattern).match(target))
|
|
171
|
+
: [targetPattern];
|
|
172
|
+
if (isGlob && targets.length === 0) {
|
|
173
|
+
unmatchedGlobs.push(targetPattern);
|
|
174
|
+
}
|
|
175
|
+
for (const target of targets) {
|
|
176
|
+
if (addedTargets.has(target)) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const owners = selectedProjects.filter((project) => project.targets.includes(target));
|
|
180
|
+
const runProjects = owners.length > 0 ? owners : selectedProjects;
|
|
181
|
+
if (runProjects.length === 0) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
addedTargets.add(target);
|
|
185
|
+
runs.push({ target, projects: runProjects });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return { runs, unmatchedGlobs };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function expandNxTargetDependencyRuns(runs: NxTargetRun[]): NxTargetRun[] {
|
|
192
|
+
const expanded: NxTargetRun[] = [];
|
|
193
|
+
const added = new Set<string>();
|
|
194
|
+
const visiting = new Set<string>();
|
|
195
|
+
|
|
196
|
+
const visit = (project: ProjectTargets, target: string): void => {
|
|
197
|
+
const key = `${project.project}:${target}`;
|
|
198
|
+
if (added.has(key)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (visiting.has(key)) {
|
|
202
|
+
throw new Error(`Nx target dependency cycle detected at ${key}.`);
|
|
203
|
+
}
|
|
204
|
+
visiting.add(key);
|
|
205
|
+
for (const dependency of project.targetDependencies?.get(target) ?? []) {
|
|
206
|
+
if (dependency.startsWith('^')) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const dependencyTargets = isGlobPattern(dependency)
|
|
210
|
+
? project.targets.filter((candidate) => new Bun.Glob(dependency).match(candidate))
|
|
211
|
+
: project.targets.includes(dependency)
|
|
212
|
+
? [dependency]
|
|
213
|
+
: [];
|
|
214
|
+
for (const dependencyTarget of dependencyTargets.sort((left, right) => left.localeCompare(right))) {
|
|
215
|
+
visit(project, dependencyTarget);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
visiting.delete(key);
|
|
219
|
+
added.add(key);
|
|
220
|
+
if ((project.targetOutputs?.get(target)?.length ?? 0) > 0) {
|
|
221
|
+
expanded.push({ target, projects: [project] });
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
for (const run of runs) {
|
|
226
|
+
for (const project of run.projects) {
|
|
227
|
+
visit(project, run.target);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return expanded;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function nxRunManyArgs(run: NxTargetRun, configuration?: string): string[] {
|
|
234
|
+
if (run.projects.length === 0) {
|
|
235
|
+
throw new Error(`Nx target ${run.target} has no selected projects.`);
|
|
236
|
+
}
|
|
237
|
+
const nxArgs = [
|
|
238
|
+
'run-many',
|
|
239
|
+
'-t',
|
|
240
|
+
run.target,
|
|
241
|
+
`--projects=${run.projects.map((project) => project.project).join(',')}`,
|
|
242
|
+
];
|
|
243
|
+
if (configuration) {
|
|
244
|
+
nxArgs.push(`--configuration=${configuration}`);
|
|
245
|
+
}
|
|
246
|
+
nxArgs.push('--parallel=5');
|
|
247
|
+
return nxArgs;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export async function readGitHeadSha(root: string): Promise<string> {
|
|
251
|
+
// invariant throw: GitHub CI commands require a valid repository root.
|
|
252
|
+
return decode((await $`git rev-parse HEAD`.cwd(root).quiet()).stdout).trim();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export async function githubCiNxRunMany(root: string, options: NxRunManyOptions): Promise<void> {
|
|
256
|
+
const expanded = expandNxTargetRuns(await readProjectTargets(root), options);
|
|
257
|
+
if (expanded.unmatchedGlobs.length > 0) {
|
|
258
|
+
console.log(`No Nx targets matched target glob(s): ${expanded.unmatchedGlobs.join(', ')}; skipping.`);
|
|
259
|
+
}
|
|
260
|
+
for (const targetRun of expanded.runs) {
|
|
261
|
+
await run('nx', nxRunManyArgs(targetRun, options.configuration), root);
|
|
262
|
+
}
|
|
263
|
+
if (options.collectOutputs) {
|
|
264
|
+
const { collectNxOutputs } = await loadOutputBoundary();
|
|
265
|
+
const sourceSha = await readGitHeadSha(root);
|
|
266
|
+
await collectNxOutputs(root, options.collectOutputs, expandNxTargetDependencyRuns(expanded.runs), sourceSha);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export async function githubCiApplyOutputs(
|
|
137
271
|
root: string,
|
|
138
|
-
|
|
272
|
+
directories: string[],
|
|
273
|
+
expectedSourceSha: string,
|
|
139
274
|
): Promise<void> {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
275
|
+
const { applyCollectedOutputs } = await loadOutputBoundary();
|
|
276
|
+
await applyCollectedOutputs(root, directories, expectedSourceSha);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async function loadOutputBoundary() {
|
|
280
|
+
if (import.meta.url.endsWith('/src/github-ci/index.ts')) {
|
|
281
|
+
// The source self-hosting shim has no Typia transform; register it before loading manifest validators.
|
|
282
|
+
await import('@smoothbricks/validation/bun/preload');
|
|
143
283
|
}
|
|
144
|
-
|
|
145
|
-
|
|
284
|
+
// This boundary must stay lazy because the transformed dist and source self-hosting paths initialize Typia differently.
|
|
285
|
+
return import('./outputs.js');
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function isGlobPattern(value: string): boolean {
|
|
289
|
+
return /[*?{[]/.test(value);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function selectProjects(projects: ProjectTargets[], selectors: string | undefined): ProjectTargets[] {
|
|
293
|
+
if (selectors === undefined) {
|
|
294
|
+
return projects.slice().sort((left, right) => left.project.localeCompare(right.project));
|
|
146
295
|
}
|
|
147
|
-
|
|
296
|
+
const patterns = commaSeparatedValues(selectors);
|
|
297
|
+
return projects
|
|
298
|
+
.filter((project) => patterns.some((pattern) => new Bun.Glob(pattern).match(project.project)))
|
|
299
|
+
.sort((left, right) => left.project.localeCompare(right.project));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function commaSeparatedValues(value: string): string[] {
|
|
303
|
+
return value
|
|
304
|
+
.split(',')
|
|
305
|
+
.map((entry) => entry.trim())
|
|
306
|
+
.filter(Boolean);
|
|
148
307
|
}
|
|
149
308
|
|
|
150
309
|
export async function githubCiNxDeploy(
|
|
@@ -220,9 +379,11 @@ function nxProjectList(output: string): string[] {
|
|
|
220
379
|
}
|
|
221
380
|
|
|
222
381
|
function recordValue(value: unknown): Record<string, unknown> | undefined {
|
|
223
|
-
return value
|
|
224
|
-
|
|
225
|
-
|
|
382
|
+
return isRecord(value) ? value : undefined;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
386
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
226
387
|
}
|
|
227
388
|
|
|
228
389
|
async function createGithubStatus(name: string, step: string): Promise<void> {
|