@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/README.md
CHANGED
|
@@ -228,21 +228,20 @@ candidates to downstream dependents because that would duplicate Nx's dependency
|
|
|
228
228
|
|
|
229
229
|
`smoo` keeps Nx target names predictable and separates tool work from aggregate workflows.
|
|
230
230
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
Bun test typechecking, non-TypeScript build-tool steps, and aggregate targets.
|
|
231
|
+
`@smoothbricks/nx-plugin` owns transformer-aware TypeScript targets because this workspace compiles through `ttsc`.
|
|
232
|
+
Configuring `@nx/js/typescript` would run `tsc`/`tsgo` directly and bypass the Typia and LMAO transformers. A package
|
|
233
|
+
`tsconfig.lib.json` therefore produces the concrete `tsc-js` target from the SmoothBricks plugin. The same plugin also
|
|
234
|
+
infers Bun test typechecking, non-TypeScript build-tool steps, and aggregate targets.
|
|
236
235
|
|
|
237
236
|
Concrete targets use `{tool}-{output}` names and describe the tool that runs and the artifact or purpose it produces:
|
|
238
237
|
|
|
239
|
-
- `tsc-js` comes from
|
|
238
|
+
- `tsc-js` comes from `@smoothbricks/nx-plugin` and runs `ttsc` for package JavaScript/declaration output.
|
|
240
239
|
- Packages that run `bun test` must have `tsconfig.test.json`. Bun executes tests without typechecking, so smoo creates
|
|
241
240
|
a no-emit `typecheck-tests` target from that config and wires it into validation. Other test runners may own their own
|
|
242
241
|
typecheck path.
|
|
243
242
|
- Test tsconfigs are validation configs, not TypeScript build-mode projects. They must use `noEmit`, must not set
|
|
244
243
|
`composite: true`, and package root `tsconfig.json` must not reference `./tsconfig.test.json`. The inferred
|
|
245
|
-
`typecheck-tests` target runs `
|
|
244
|
+
`typecheck-tests` target runs `ttsc --noEmit -p tsconfig.test.json` after `build` instead.
|
|
246
245
|
- Non-TypeScript build steps come from explicit tool configuration. For example, a package `build.zig` must expose named
|
|
247
246
|
`b.step("name", ...)` entries; each non-reserved step becomes a `zig-name` target such as `zig-wasm`.
|
|
248
247
|
- `build` is an aggregate. It exists only when there is at least one concrete build target such as `tsc-js`,
|
|
@@ -250,6 +249,13 @@ Concrete targets use `{tool}-{output}` names and describe the tool that runs and
|
|
|
250
249
|
`*-html`, `*-css`, `*-ios`, `*-android`, `*-native`, `*-napi`, `*-bun`, and `*-wasm` instead of duplicating commands.
|
|
251
250
|
- `lint` is an aggregate validation target. It is not a formatting target.
|
|
252
251
|
|
|
252
|
+
The root `@typescript/native` dependency follows TypeScript's documented side-by-side pattern: it aliases TypeScript 7
|
|
253
|
+
and supplies the native compiler used by `ttsc`. Because `ttsc` resolves only the unscoped package by default, the
|
|
254
|
+
managed devenv shell sets `TTSC_TSGO_BINARY` to `node_modules/@typescript/native/bin/tsc`; the GitHub setup action
|
|
255
|
+
persists that absolute path through `GITHUB_ENV`. Nx and other JavaScript tooling still require the full TypeScript
|
|
256
|
+
compiler API, so workspace `typescript` dependencies stay on TypeScript 6. Both dependencies and the environment binding
|
|
257
|
+
are required: TypeScript 7 under the unscoped name breaks Nx API calls such as `readConfigFile`.
|
|
258
|
+
|
|
253
259
|
Explicit Nx target names must not contain `:`. Nx already uses colon syntax at the CLI boundary:
|
|
254
260
|
`project:target:configuration`. Allowing target names like `build:wasm` makes command parsing and package-script aliases
|
|
255
261
|
look like configurations, and it prevents a clean split between concrete tool-output targets and aggregate targets.
|
|
@@ -286,6 +292,9 @@ smoo monorepo diff
|
|
|
286
292
|
|
|
287
293
|
`check` fails when a managed file is missing or stale. `diff` reports drift without writing files.
|
|
288
294
|
|
|
295
|
+
The generated publish workflow is canonical Prettier YAML. Running the repository formatter over
|
|
296
|
+
`.github/workflows/publish.yml` is byte-stable and does not create managed-file drift.
|
|
297
|
+
|
|
289
298
|
## Formatting And Git Hooks
|
|
290
299
|
|
|
291
300
|
The root `lint:fix` script runs [`git-format-staged`][git-format-staged] with
|
|
@@ -467,12 +476,15 @@ Publishing:
|
|
|
467
476
|
for targeted bootstraps.
|
|
468
477
|
- `smoo release trust-publisher` configures [npm trusted publishing][npm-trusted-publishing] for every owned release
|
|
469
478
|
package. It uses the root `package.json` `repository.url` as the GitHub `owner/repo`, uses `publish.yml` as the
|
|
470
|
-
trusted workflow,
|
|
471
|
-
toolchain may lag the npm CLI feature. It does
|
|
472
|
-
`npm trust github` own authentication so npm can offer the 5-minute
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
479
|
+
trusted workflow, grants that workflow npm's `--allow-publish` permission, and runs `npm trust` through
|
|
480
|
+
`nix shell nixpkgs#nodejs_latest` because the Lambda-pinned Node 24/npm toolchain may lag the npm CLI feature. It does
|
|
481
|
+
not pre-login: `npm trust list` and `npm trust github` own normal authentication so npm can offer the 5-minute
|
|
482
|
+
trust/publish challenge bypass. If `npm trust list` denies access, smoo reports the active npm identity and package
|
|
483
|
+
owners, opens `npm login --auth-type=web` once so the operator can switch accounts, then retries the lookup. A second
|
|
484
|
+
denial fails instead of looping. Pass `--package <name...>` to target specific owned packages. Pass `--bootstrap` to
|
|
485
|
+
create missing npm package names first, then configure trusted publishing in the same command. With `--bootstrap`,
|
|
486
|
+
`--skip-login` only skips the placeholder publish login. Existing matching trusted publishers are skipped via
|
|
487
|
+
`npm trust list <package> --json`.
|
|
476
488
|
|
|
477
489
|
GitHub Releases:
|
|
478
490
|
|
package/dist/cli.js
CHANGED
|
@@ -125,10 +125,25 @@ function buildProgram() {
|
|
|
125
125
|
.command('repair-pending')
|
|
126
126
|
.description('Repair incomplete older release commits before releasing the current HEAD')
|
|
127
127
|
.option('--dry-run [dryRun]', 'run without pushing, publishing, or writing GitHub Releases')
|
|
128
|
+
.option('--ref <ref>', 'fixed release graph ref to inspect')
|
|
129
|
+
.option('--platform-outputs <path>', 'cross-platform repair outputs grouped by release SHA')
|
|
128
130
|
.action(async (options) => {
|
|
131
|
+
// The source self-hosting shim has no Typia transform; release commands import transformed output validators.
|
|
129
132
|
const { releaseRepairPending } = await import('./release/index.js');
|
|
130
133
|
await releaseRepairPending(await findRepoRoot(), { ...options, dryRun: booleanOption(options.dryRun) });
|
|
131
134
|
});
|
|
135
|
+
release
|
|
136
|
+
.command('build-platform-outputs')
|
|
137
|
+
.description('Build selected current and pending-release platform outputs')
|
|
138
|
+
.requiredOption('--bump <bump>', 'auto, patch, minor, major, or prerelease')
|
|
139
|
+
.requiredOption('--targets <targets>', 'comma-separated Nx platform target names or globs')
|
|
140
|
+
.requiredOption('--output <path>', 'output directory for current and repair artifacts')
|
|
141
|
+
.option('--ref <ref>', 'fixed release graph ref to inspect')
|
|
142
|
+
.action(async (options) => {
|
|
143
|
+
// The source self-hosting shim has no Typia transform; release commands import transformed output validators.
|
|
144
|
+
const { releaseCollectPlatformOutputs } = await import('./release/index.js');
|
|
145
|
+
await releaseCollectPlatformOutputs(await findRepoRoot(), options);
|
|
146
|
+
});
|
|
132
147
|
release
|
|
133
148
|
.command('version')
|
|
134
149
|
.option('--bump <bump>', 'auto, patch, minor, major, or prerelease', 'auto')
|
|
@@ -268,10 +283,19 @@ function buildProgram() {
|
|
|
268
283
|
.requiredOption('--targets <targets>')
|
|
269
284
|
.option('--projects <projects>')
|
|
270
285
|
.option('--configuration <configuration>')
|
|
286
|
+
.option('--collect-outputs <directory>')
|
|
271
287
|
.action(async (options) => {
|
|
272
288
|
const { githubCiNxRunMany } = await import('./github-ci/index.js');
|
|
273
289
|
await githubCiNxRunMany(await findRepoRoot(), options);
|
|
274
290
|
});
|
|
291
|
+
githubCi
|
|
292
|
+
.command('apply-outputs <directories...>')
|
|
293
|
+
.requiredOption('--source-sha <sha>', 'expected source commit SHA')
|
|
294
|
+
.action(async (directories, options) => {
|
|
295
|
+
// GitHub CI commands stay lazy so source self-hosting can initialize Typia only at manifest boundaries.
|
|
296
|
+
const { githubCiApplyOutputs } = await import('./github-ci/index.js');
|
|
297
|
+
await githubCiApplyOutputs(await findRepoRoot(), directories, options.sourceSha);
|
|
298
|
+
});
|
|
275
299
|
githubCi
|
|
276
300
|
.command('nx-deploy')
|
|
277
301
|
.requiredOption('--configuration <configuration>')
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { type ProjectTargets } from '../nx/index.js';
|
|
2
|
+
import type { NxTargetRun } from './outputs.js';
|
|
1
3
|
type NxSmartMode = 'auto' | 'affected' | 'run-many';
|
|
2
4
|
export declare function cleanupGithubCiCache(root: string): Promise<void>;
|
|
5
|
+
export declare function nxSmartArgs(target: string, mode: 'affected' | 'run-many', configuration?: string): string[];
|
|
3
6
|
export declare function githubCiNxSmart(root: string, options: {
|
|
4
7
|
target: string;
|
|
5
8
|
name?: string;
|
|
@@ -7,11 +10,23 @@ export declare function githubCiNxSmart(root: string, options: {
|
|
|
7
10
|
mode?: NxSmartMode;
|
|
8
11
|
configuration?: string;
|
|
9
12
|
}): Promise<void>;
|
|
10
|
-
export
|
|
13
|
+
export interface NxRunManyOptions {
|
|
11
14
|
targets: string;
|
|
12
15
|
projects?: string;
|
|
13
16
|
configuration?: string;
|
|
14
|
-
|
|
17
|
+
collectOutputs?: string;
|
|
18
|
+
allowEmptyProjects?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface ExpandedNxTargetRuns {
|
|
21
|
+
runs: NxTargetRun[];
|
|
22
|
+
unmatchedGlobs: string[];
|
|
23
|
+
}
|
|
24
|
+
export declare function expandNxTargetRuns(projects: ProjectTargets[], options: NxRunManyOptions): ExpandedNxTargetRuns;
|
|
25
|
+
export declare function expandNxTargetDependencyRuns(runs: NxTargetRun[]): NxTargetRun[];
|
|
26
|
+
export declare function nxRunManyArgs(run: NxTargetRun, configuration?: string): string[];
|
|
27
|
+
export declare function readGitHeadSha(root: string): Promise<string>;
|
|
28
|
+
export declare function githubCiNxRunMany(root: string, options: NxRunManyOptions): Promise<void>;
|
|
29
|
+
export declare function githubCiApplyOutputs(root: string, directories: string[], expectedSourceSha: string): Promise<void>;
|
|
15
30
|
export declare function githubCiNxDeploy(root: string, options: {
|
|
16
31
|
configuration: string;
|
|
17
32
|
mode?: NxSmartMode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/github-ci/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/github-ci/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,gBAAgB,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,KAAK,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;AAEpD,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBtE;AAmFD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAO3G;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,WAAW,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACpG,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,oBAAoB,CAiC9G;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAwC/E;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAehF;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGlE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAa9F;AAED,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EAAE,EACrB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC,CAGf;AAgCD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,WAAW,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GACrG,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
|
package/dist/github-ci/index.js
CHANGED
|
@@ -3,6 +3,7 @@ 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 { readProjectTargets } from '../nx/index.js';
|
|
6
7
|
export async function cleanupGithubCiCache(root) {
|
|
7
8
|
const githubOutput = process.env.GITHUB_OUTPUT;
|
|
8
9
|
const markCacheReady = async (ready) => {
|
|
@@ -100,31 +101,160 @@ async function addReferencesFrom(roots, path, cwd) {
|
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
}
|
|
104
|
+
export function nxSmartArgs(target, mode, configuration) {
|
|
105
|
+
const args = [mode, '-t', target];
|
|
106
|
+
if (configuration) {
|
|
107
|
+
args.push(`--configuration=${configuration}`);
|
|
108
|
+
}
|
|
109
|
+
args.push(`--exclude=tag:ci:skip:${target}`, '--parallel=5');
|
|
110
|
+
return args;
|
|
111
|
+
}
|
|
103
112
|
export async function githubCiNxSmart(root, options) {
|
|
104
113
|
const name = options.name ?? options.target;
|
|
105
114
|
const step = options.step ?? '';
|
|
106
115
|
await createGithubStatus(name, step);
|
|
107
116
|
const mode = resolveNxSmartMode(options.mode ?? 'auto');
|
|
108
|
-
const nxArgs =
|
|
109
|
-
if (options.configuration) {
|
|
110
|
-
nxArgs.push(`--configuration=${options.configuration}`);
|
|
111
|
-
}
|
|
112
|
-
nxArgs.push('--parallel=5');
|
|
117
|
+
const nxArgs = nxSmartArgs(options.target, mode, options.configuration);
|
|
113
118
|
const status = await runStatus('nx', nxArgs, root);
|
|
114
119
|
await updateGithubStatus(name, status === 0 ? 'success' : 'failure', step);
|
|
115
120
|
if (status !== 0) {
|
|
116
121
|
throw new Error(`nx ${nxArgs.join(' ')} failed with exit code ${status}`);
|
|
117
122
|
}
|
|
118
123
|
}
|
|
124
|
+
export function expandNxTargetRuns(projects, options) {
|
|
125
|
+
const selectedProjects = selectProjects(projects, options.projects);
|
|
126
|
+
if (options.projects !== undefined && selectedProjects.length === 0 && options.allowEmptyProjects !== true) {
|
|
127
|
+
throw new Error(`No Nx projects matched --projects ${options.projects}.`);
|
|
128
|
+
}
|
|
129
|
+
const selectedTargetNames = [...new Set(selectedProjects.flatMap((project) => project.targets))].sort((a, b) => a.localeCompare(b));
|
|
130
|
+
const runs = [];
|
|
131
|
+
const unmatchedGlobs = [];
|
|
132
|
+
const addedTargets = new Set();
|
|
133
|
+
for (const targetPattern of commaSeparatedValues(options.targets)) {
|
|
134
|
+
const isGlob = isGlobPattern(targetPattern);
|
|
135
|
+
const targets = isGlob
|
|
136
|
+
? selectedTargetNames.filter((target) => new Bun.Glob(targetPattern).match(target))
|
|
137
|
+
: [targetPattern];
|
|
138
|
+
if (isGlob && targets.length === 0) {
|
|
139
|
+
unmatchedGlobs.push(targetPattern);
|
|
140
|
+
}
|
|
141
|
+
for (const target of targets) {
|
|
142
|
+
if (addedTargets.has(target)) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const owners = selectedProjects.filter((project) => project.targets.includes(target));
|
|
146
|
+
const runProjects = owners.length > 0 ? owners : selectedProjects;
|
|
147
|
+
if (runProjects.length === 0) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
addedTargets.add(target);
|
|
151
|
+
runs.push({ target, projects: runProjects });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return { runs, unmatchedGlobs };
|
|
155
|
+
}
|
|
156
|
+
export function expandNxTargetDependencyRuns(runs) {
|
|
157
|
+
const expanded = [];
|
|
158
|
+
const added = new Set();
|
|
159
|
+
const visiting = new Set();
|
|
160
|
+
const visit = (project, target) => {
|
|
161
|
+
const key = `${project.project}:${target}`;
|
|
162
|
+
if (added.has(key)) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (visiting.has(key)) {
|
|
166
|
+
throw new Error(`Nx target dependency cycle detected at ${key}.`);
|
|
167
|
+
}
|
|
168
|
+
visiting.add(key);
|
|
169
|
+
for (const dependency of project.targetDependencies?.get(target) ?? []) {
|
|
170
|
+
if (dependency.startsWith('^')) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const dependencyTargets = isGlobPattern(dependency)
|
|
174
|
+
? project.targets.filter((candidate) => new Bun.Glob(dependency).match(candidate))
|
|
175
|
+
: project.targets.includes(dependency)
|
|
176
|
+
? [dependency]
|
|
177
|
+
: [];
|
|
178
|
+
for (const dependencyTarget of dependencyTargets.sort((left, right) => left.localeCompare(right))) {
|
|
179
|
+
visit(project, dependencyTarget);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
visiting.delete(key);
|
|
183
|
+
added.add(key);
|
|
184
|
+
if ((project.targetOutputs?.get(target)?.length ?? 0) > 0) {
|
|
185
|
+
expanded.push({ target, projects: [project] });
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
for (const run of runs) {
|
|
189
|
+
for (const project of run.projects) {
|
|
190
|
+
visit(project, run.target);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return expanded;
|
|
194
|
+
}
|
|
195
|
+
export function nxRunManyArgs(run, configuration) {
|
|
196
|
+
if (run.projects.length === 0) {
|
|
197
|
+
throw new Error(`Nx target ${run.target} has no selected projects.`);
|
|
198
|
+
}
|
|
199
|
+
const nxArgs = [
|
|
200
|
+
'run-many',
|
|
201
|
+
'-t',
|
|
202
|
+
run.target,
|
|
203
|
+
`--projects=${run.projects.map((project) => project.project).join(',')}`,
|
|
204
|
+
];
|
|
205
|
+
if (configuration) {
|
|
206
|
+
nxArgs.push(`--configuration=${configuration}`);
|
|
207
|
+
}
|
|
208
|
+
nxArgs.push('--parallel=5');
|
|
209
|
+
return nxArgs;
|
|
210
|
+
}
|
|
211
|
+
export async function readGitHeadSha(root) {
|
|
212
|
+
// invariant throw: GitHub CI commands require a valid repository root.
|
|
213
|
+
return decode((await $ `git rev-parse HEAD`.cwd(root).quiet()).stdout).trim();
|
|
214
|
+
}
|
|
119
215
|
export async function githubCiNxRunMany(root, options) {
|
|
120
|
-
const
|
|
121
|
-
if (
|
|
122
|
-
|
|
216
|
+
const expanded = expandNxTargetRuns(await readProjectTargets(root), options);
|
|
217
|
+
if (expanded.unmatchedGlobs.length > 0) {
|
|
218
|
+
console.log(`No Nx targets matched target glob(s): ${expanded.unmatchedGlobs.join(', ')}; skipping.`);
|
|
219
|
+
}
|
|
220
|
+
for (const targetRun of expanded.runs) {
|
|
221
|
+
await run('nx', nxRunManyArgs(targetRun, options.configuration), root);
|
|
123
222
|
}
|
|
124
|
-
if (options.
|
|
125
|
-
|
|
223
|
+
if (options.collectOutputs) {
|
|
224
|
+
const { collectNxOutputs } = await loadOutputBoundary();
|
|
225
|
+
const sourceSha = await readGitHeadSha(root);
|
|
226
|
+
await collectNxOutputs(root, options.collectOutputs, expandNxTargetDependencyRuns(expanded.runs), sourceSha);
|
|
126
227
|
}
|
|
127
|
-
|
|
228
|
+
}
|
|
229
|
+
export async function githubCiApplyOutputs(root, directories, expectedSourceSha) {
|
|
230
|
+
const { applyCollectedOutputs } = await loadOutputBoundary();
|
|
231
|
+
await applyCollectedOutputs(root, directories, expectedSourceSha);
|
|
232
|
+
}
|
|
233
|
+
async function loadOutputBoundary() {
|
|
234
|
+
if (import.meta.url.endsWith('/src/github-ci/index.ts')) {
|
|
235
|
+
// The source self-hosting shim has no Typia transform; register it before loading manifest validators.
|
|
236
|
+
await import('@smoothbricks/validation/bun/preload');
|
|
237
|
+
}
|
|
238
|
+
// This boundary must stay lazy because the transformed dist and source self-hosting paths initialize Typia differently.
|
|
239
|
+
return import('./outputs.js');
|
|
240
|
+
}
|
|
241
|
+
function isGlobPattern(value) {
|
|
242
|
+
return /[*?{[]/.test(value);
|
|
243
|
+
}
|
|
244
|
+
function selectProjects(projects, selectors) {
|
|
245
|
+
if (selectors === undefined) {
|
|
246
|
+
return projects.slice().sort((left, right) => left.project.localeCompare(right.project));
|
|
247
|
+
}
|
|
248
|
+
const patterns = commaSeparatedValues(selectors);
|
|
249
|
+
return projects
|
|
250
|
+
.filter((project) => patterns.some((pattern) => new Bun.Glob(pattern).match(project.project)))
|
|
251
|
+
.sort((left, right) => left.project.localeCompare(right.project));
|
|
252
|
+
}
|
|
253
|
+
function commaSeparatedValues(value) {
|
|
254
|
+
return value
|
|
255
|
+
.split(',')
|
|
256
|
+
.map((entry) => entry.trim())
|
|
257
|
+
.filter(Boolean);
|
|
128
258
|
}
|
|
129
259
|
export async function githubCiNxDeploy(root, options) {
|
|
130
260
|
const name = options.name ?? `Deploy ${options.configuration}`;
|
|
@@ -185,9 +315,10 @@ function nxProjectList(output) {
|
|
|
185
315
|
return Array.isArray(parsed) ? parsed.filter((project) => typeof project === 'string') : [];
|
|
186
316
|
}
|
|
187
317
|
function recordValue(value) {
|
|
188
|
-
return value
|
|
189
|
-
|
|
190
|
-
|
|
318
|
+
return isRecord(value) ? value : undefined;
|
|
319
|
+
}
|
|
320
|
+
function isRecord(value) {
|
|
321
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
191
322
|
}
|
|
192
323
|
async function createGithubStatus(name, step) {
|
|
193
324
|
await postGithubStatus(name, 'pending', `Running ${name}...`, step);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ProjectTargets } from '../nx/index.js';
|
|
2
|
+
export interface CollectedOutputFile {
|
|
3
|
+
project: string;
|
|
4
|
+
target: string;
|
|
5
|
+
output: string;
|
|
6
|
+
path: string;
|
|
7
|
+
size: number;
|
|
8
|
+
sha256: string;
|
|
9
|
+
}
|
|
10
|
+
export interface CollectedOutputsManifest {
|
|
11
|
+
version: 1;
|
|
12
|
+
sourceSha: string;
|
|
13
|
+
files: CollectedOutputFile[];
|
|
14
|
+
}
|
|
15
|
+
export interface NxTargetRun {
|
|
16
|
+
target: string;
|
|
17
|
+
projects: ProjectTargets[];
|
|
18
|
+
}
|
|
19
|
+
export declare function collectNxOutputs(root: string, destination: string, runs: NxTargetRun[], sourceSha: string): Promise<CollectedOutputsManifest>;
|
|
20
|
+
export declare function applyCollectedOutputs(root: string, directories: string[], expectedSourceSha: string, projects?: ProjectTargets[]): Promise<void>;
|
|
21
|
+
export declare function resolveDeclaredOutput(declaredOutput: string, project: ProjectTargets): string;
|
|
22
|
+
//# sourceMappingURL=outputs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outputs.d.ts","sourceRoot":"","sources":["../../src/github-ci/outputs.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,gBAAgB,CAAC;AAEzE,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,mBAAmB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B;AAWD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,WAAW,EAAE,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,wBAAwB,CAAC,CAgEnC;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EAAE,EACrB,iBAAiB,EAAE,MAAM,EACzB,QAAQ,CAAC,EAAE,cAAc,EAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CAqGf;AAED,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAa7F"}
|