@smoothbricks/cli 0.11.16 โ†’ 0.11.18

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.
Files changed (79) hide show
  1. package/README.md +65 -4
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +47 -0
  4. package/dist/github-ci/index.d.ts +1 -1
  5. package/dist/github-ci/index.d.ts.map +1 -1
  6. package/dist/github-ci/index.js +7 -10
  7. package/dist/lib/secret-names.d.ts +35 -0
  8. package/dist/lib/secret-names.d.ts.map +1 -0
  9. package/dist/lib/secret-names.js +61 -0
  10. package/dist/monorepo/ci-workflow.d.ts +56 -0
  11. package/dist/monorepo/ci-workflow.d.ts.map +1 -1
  12. package/dist/monorepo/ci-workflow.js +214 -8
  13. package/dist/monorepo/managed-files.d.ts +23 -0
  14. package/dist/monorepo/managed-files.d.ts.map +1 -1
  15. package/dist/monorepo/managed-files.js +39 -1
  16. package/dist/monorepo/publish-workflow.d.ts +10 -1
  17. package/dist/monorepo/publish-workflow.d.ts.map +1 -1
  18. package/dist/monorepo/publish-workflow.js +58 -20
  19. package/dist/release/github-release.d.ts +10 -0
  20. package/dist/release/github-release.d.ts.map +1 -1
  21. package/dist/release/github-release.js +11 -5
  22. package/dist/release/index.d.ts.map +1 -1
  23. package/dist/release/index.js +4 -5
  24. package/dist/secrets/commands.d.ts +84 -0
  25. package/dist/secrets/commands.d.ts.map +1 -0
  26. package/dist/secrets/commands.js +375 -0
  27. package/dist/secrets/index.d.ts +103 -0
  28. package/dist/secrets/index.d.ts.map +1 -0
  29. package/dist/secrets/index.js +255 -0
  30. package/dist/secrets/repository.d.ts +60 -0
  31. package/dist/secrets/repository.d.ts.map +1 -0
  32. package/dist/secrets/repository.js +145 -0
  33. package/dist/wrangler/cloudflare.d.ts +7 -0
  34. package/dist/wrangler/cloudflare.d.ts.map +1 -1
  35. package/dist/wrangler/cloudflare.js +10 -0
  36. package/dist/wrangler/deploy-stage.d.ts +13 -2
  37. package/dist/wrangler/deploy-stage.d.ts.map +1 -1
  38. package/dist/wrangler/deploy-stage.js +89 -78
  39. package/dist/wrangler/deployed-version.d.ts +44 -0
  40. package/dist/wrangler/deployed-version.d.ts.map +1 -0
  41. package/dist/wrangler/deployed-version.js +87 -0
  42. package/dist/wrangler/live-version.d.ts +146 -0
  43. package/dist/wrangler/live-version.d.ts.map +1 -0
  44. package/dist/wrangler/live-version.js +326 -0
  45. package/dist/wrangler/stage-secrets.d.ts +57 -0
  46. package/dist/wrangler/stage-secrets.d.ts.map +1 -0
  47. package/dist/wrangler/stage-secrets.js +178 -0
  48. package/managed/raw/tooling/direnv/devenv.smoo.nix +9 -1
  49. package/managed/raw/tooling/git-hooks/pre-push.sh +30 -29
  50. package/package.json +2 -2
  51. package/src/cli.ts +69 -1
  52. package/src/github-ci/index.test.ts +92 -8
  53. package/src/github-ci/index.ts +7 -10
  54. package/src/lib/secret-names.ts +70 -0
  55. package/src/monorepo/__tests__/ci-workflow.test.ts +188 -2
  56. package/src/monorepo/__tests__/publish-workflow.test.ts +33 -15
  57. package/src/monorepo/ci-workflow.ts +294 -8
  58. package/src/monorepo/managed-files.test.ts +28 -1
  59. package/src/monorepo/managed-files.ts +56 -2
  60. package/src/monorepo/package-policy.test.ts +1 -1
  61. package/src/monorepo/publish-workflow.ts +65 -19
  62. package/src/monorepo/secret-references.test.ts +1 -1
  63. package/src/release/__tests__/github-release.test.ts +8 -4
  64. package/src/release/__tests__/private-npm-status.test.ts +2 -2
  65. package/src/release/github-release.ts +13 -5
  66. package/src/release/index.ts +4 -4
  67. package/src/secrets/commands.test.ts +28 -0
  68. package/src/secrets/commands.ts +412 -0
  69. package/src/secrets/index.test.ts +209 -0
  70. package/src/secrets/index.ts +287 -0
  71. package/src/secrets/repository.test.ts +98 -0
  72. package/src/secrets/repository.ts +164 -0
  73. package/src/wrangler/cloudflare.ts +18 -0
  74. package/src/wrangler/deploy-stage.test.ts +448 -23
  75. package/src/wrangler/deploy-stage.ts +142 -83
  76. package/src/wrangler/deployed-version.test.ts +150 -0
  77. package/src/wrangler/deployed-version.ts +130 -0
  78. package/src/wrangler/live-version.ts +363 -0
  79. package/src/wrangler/stage-secrets.ts +146 -0
@@ -1,4 +1,5 @@
1
1
  /* biome-ignore-all lint/suspicious/noTemplateCurlyInString: GitHub Actions expressions are emitted literally. */
2
+ import { cargoCrossTestArchiveFile, cargoCrossTestArchiveTargetName, cargoCrossTestTargetName, } from '@smoothbricks/nx-plugin/cross-check-policy';
2
3
  import { PRODUCTION_PUSH_DEPLOY_TAG } from '../lib/deploy-tags.js';
3
4
  import { renderRunsOnLine } from './github-runs-on.js';
4
5
  export var CiWorkflowStepKind;
@@ -13,6 +14,11 @@ export var CiWorkflowStepKind;
13
14
  CiWorkflowStepKind["BrowserTests"] = "browser-tests";
14
15
  CiWorkflowStepKind["Lint"] = "lint";
15
16
  CiWorkflowStepKind["UnitTests"] = "unit-tests";
17
+ CiWorkflowStepKind["CrossToolchainPreflight"] = "cross-toolchain-preflight";
18
+ CiWorkflowStepKind["CrossTestArchives"] = "cross-test-archives";
19
+ CiWorkflowStepKind["UploadCrossTestArchives"] = "upload-cross-test-archives";
20
+ CiWorkflowStepKind["DownloadCrossTestArchives"] = "download-cross-test-archives";
21
+ CiWorkflowStepKind["CrossTests"] = "cross-tests";
16
22
  CiWorkflowStepKind["ManagedFilesCheck"] = "managed-files-check";
17
23
  CiWorkflowStepKind["ManagedFilesDispatch"] = "managed-files-dispatch";
18
24
  CiWorkflowStepKind["Deploy"] = "deploy";
@@ -33,6 +39,15 @@ export function defineCiWorkflow(options) {
33
39
  steps.push({ kind: CiWorkflowStepKind.Deploy, name: '๐Ÿš€ Deploy Stage' });
34
40
  }
35
41
  steps.push({ kind: CiWorkflowStepKind.Lint, name: '๐Ÿ” Lint' }, { kind: CiWorkflowStepKind.UnitTests, name: '๐Ÿงช Unit Tests' });
42
+ if (options.crossTestArchives?.length) {
43
+ if (options.platformProducer !== undefined) {
44
+ steps.push({
45
+ kind: CiWorkflowStepKind.CrossToolchainPreflight,
46
+ name: 'Check cross-platform toolchain prerequisites',
47
+ });
48
+ }
49
+ steps.push({ kind: CiWorkflowStepKind.CrossTestArchives, name: '๐ŸŽฏ Build cross-target test archives' }, { kind: CiWorkflowStepKind.UploadCrossTestArchives, name: '๐Ÿ“ค Upload cross-target test archives' });
50
+ }
36
51
  if (options.browserTests) {
37
52
  steps.push({ kind: CiWorkflowStepKind.BrowserTests, name: '๐ŸŒ Browser Tests' });
38
53
  }
@@ -44,6 +59,7 @@ export function renderCiWorkflowYaml(options) {
44
59
  return [
45
60
  renderCiWorkflowHeader(options),
46
61
  renderCiWorkflowSteps(steps, options),
62
+ renderCrossTestExecutionJob(options),
47
63
  renderE2eDeploymentJob(options),
48
64
  renderProductionDeployJob(options),
49
65
  ].join('');
@@ -64,12 +80,19 @@ permissions:
64
80
  ${options.deploy ? ' deployments: write\n' : ''} statuses: write
65
81
 
66
82
  concurrency:
67
- # One in-flight run per ref. Pushes to the staging push branch queue behind a
68
- # running workflow instead of canceling it, so a newer push never cancels a
69
- # production deployment mid-flight. Pull requests and other branches keep
70
- # canceling superseded runs.
83
+ ${options.deploy
84
+ ? ` # One in-flight run per ref. Pushes to the staging push branch queue behind a
85
+ # running workflow instead of canceling it, so a newer push never cancels the
86
+ # deploy job mid-flight. Pull requests and other branches keep canceling
87
+ # superseded runs.
71
88
  group: \${{ github.workflow }}-\${{ github.ref }}
72
- cancel-in-progress: \${{ github.ref != ${stagingRefLiteral(options)} }}
89
+ cancel-in-progress: \${{ github.ref != ${stagingRefLiteral(options)} }}`
90
+ : ` # This workflow validates and never deploys, so there is no in-flight
91
+ # deployment for a newer push to protect: every ref cancels its superseded
92
+ # runs. Queuing them instead serializes the staging branch, and a burst of
93
+ # pushes then reports the newest commit one full run per queued push late.
94
+ group: \${{ github.workflow }}-\${{ github.ref }}
95
+ cancel-in-progress: true`}
73
96
 
74
97
  defaults:
75
98
  run:
@@ -209,6 +232,61 @@ function yamlLinesForStep(step, options) {
209
232
  return nxSmartStep(step, 'lint', 'Lint');
210
233
  case CiWorkflowStepKind.UnitTests:
211
234
  return nxSmartStep(step, 'test', 'Unit Tests');
235
+ case CiWorkflowStepKind.CrossToolchainPreflight:
236
+ // The repository's own preflight, the same one the publish workflow's
237
+ // cross artifact job runs โ€” an SDK to provision, a linker to check.
238
+ // `working-directory: .` because this workflow's steps default to the
239
+ // direnv shell directory while a preflight is repository-relative.
240
+ return [
241
+ ` - name: ${step.name}`,
242
+ ' working-directory: .',
243
+ ...platformProducerStepEnvLines(options),
244
+ ' run: |',
245
+ ' set -euo pipefail',
246
+ ...crossToolchainPreflight(options).map((line) => ` ${line}`),
247
+ ];
248
+ case CiWorkflowStepKind.CrossTestArchives:
249
+ // Cross-COMPILES the workspace's test binaries for each declared foreign
250
+ // triple and packs them; runs nothing. `nx-run-many` rather than
251
+ // `nx-smart`, because an affected-graph selection would skip the archive
252
+ // on a commit that changed nothing Rust โ€” and the sibling job that
253
+ // executes it has no way to build one.
254
+ return [
255
+ ` - name: ${step.name}`,
256
+ ...platformProducerStepEnvLines(options),
257
+ ` run: smoo github-ci nx-run-many --targets "${crossArchiveTargetNames(options).join(',')}"`,
258
+ ];
259
+ case CiWorkflowStepKind.UploadCrossTestArchives:
260
+ // retention-days: 1 โ€” the only consumer is the sibling job in this same
261
+ // run, and these archives are the whole workspace's test binaries.
262
+ return artifactStepLines(options.actionsProvider, step.name, 'upload', [
263
+ `name: ${CROSS_TEST_ARCHIVE_ARTIFACT}`,
264
+ 'path: |',
265
+ ...crossTestArchives(options).map((archive) => ` ${archive.path}`),
266
+ // error, not ignore: a missing archive means the cross build silently
267
+ // produced nothing, and the execute job would then have nothing to run
268
+ // and no reason to say so.
269
+ 'if-no-files-found: error',
270
+ 'retention-days: 1',
271
+ ]);
272
+ case CiWorkflowStepKind.DownloadCrossTestArchives:
273
+ // Restores each archive at the path its own Nx target declares as output,
274
+ // which is where the runner target reads it from. upload-artifact roots
275
+ // the artifact at the least common ancestor of what it uploaded, so the
276
+ // download path is that shared directory.
277
+ return artifactStepLines(options.actionsProvider, step.name, 'download', [
278
+ `name: ${CROSS_TEST_ARCHIVE_ARTIFACT}`,
279
+ `path: ${crossTestArchiveDirectory(options)}`,
280
+ ]);
281
+ case CiWorkflowStepKind.CrossTests:
282
+ // EXECUTES, and that is all it can do: every one of these targets is
283
+ // `nextest run --archive-file`, which extracts prebuilt binaries into its
284
+ // own temporary directory. nextest never invokes cargo on this path
285
+ // (measured), so nothing here can compile even if a toolchain is present.
286
+ return [
287
+ ` - name: ${step.name}`,
288
+ ` run: smoo github-ci nx-run-many --targets "${darwinCrossTestTargetNames(options).join(',')}"`,
289
+ ];
212
290
  case CiWorkflowStepKind.ManagedFilesCheck:
213
291
  // The authoritative drift check is nearly free here (devenv is already
214
292
  // up). --warn never fails the job and publishes the step output
@@ -758,12 +836,13 @@ function renderYamlList(values, spaces) {
758
836
  const indent = ' '.repeat(spaces);
759
837
  return values.map((value) => `${indent}- ${value}`).join('\n');
760
838
  }
761
- function followUpStepNumbers(options) {
839
+ function followUpStepNumbers(options, middleSteps = 1) {
762
840
  let number = 3;
763
841
  const cargo = options.cargoCredentials !== undefined ? number++ : undefined;
764
842
  const sources = options.sourceCheckouts?.length ? number++ : undefined;
765
843
  const setup = number++;
766
- const middle = number++;
844
+ const middle = number;
845
+ number += middleSteps;
767
846
  const cleanup = number++;
768
847
  return { cargo, sources, setup, middle, cleanup };
769
848
  }
@@ -787,6 +866,131 @@ function followUpSetupSteps(options, numbers) {
787
866
  function followUpCleanupStep(numbers) {
788
867
  return [{ kind: CiWorkflowStepKind.SaveNixDevenv, name: '๐Ÿงน Cleanup and cache Nix/devenv', number: numbers.cleanup }];
789
868
  }
869
+ /**
870
+ * How long the darwin execution job may take, download included. Generous
871
+ * because the archive is the whole workspace's test binaries, and bounded
872
+ * because the runner behind `macosRunsOn` may be one person's laptop: an
873
+ * unreachable runner queues, and this number is when the run says so.
874
+ */
875
+ const CROSS_TEST_JOB_TIMEOUT_MINUTES = 30;
876
+ /** Rust names every Apple-desktop triple this way; iOS and simulator do not. */
877
+ const DARWIN_TRIPLE_SUFFIX = '-apple-darwin';
878
+ /** One artifact carrying every triple's archive, scoped to the run that built it. */
879
+ const CROSS_TEST_ARCHIVE_ARTIFACT = `cross-test-archives-${githubExpression('github.run_id')}`;
880
+ /**
881
+ * Executes the cross-built test binaries on a machine where they are native.
882
+ *
883
+ * This job COMPILES NOTHING, and cannot: its only Rust input is the archive the
884
+ * Validate job uploaded, and `nextest run --archive-file` extracts prebuilt
885
+ * binaries rather than invoking cargo (measured: zero cargo invocations). It
886
+ * needs the checkout for the workspace `--workspace-remap .` points at and for
887
+ * Nx to resolve the target, and setup-devenv for `nx` and `cargo-nextest`
888
+ * themselves โ€” the repository's ordinary shell, not a cross toolchain, no SDK,
889
+ * no `rustup target add`.
890
+ *
891
+ * `needs: main` and a success gate rather than `!cancelled()`: an archive that
892
+ * was never uploaded cannot be executed, and a job that starts anyway would
893
+ * fail on a missing artifact and report it as a test failure.
894
+ *
895
+ * Only `*-apple-darwin` archives run here, because macOS runner labels are the
896
+ * only native runner this declaration names. A repository declaring some other
897
+ * foreign triple still gets its archive BUILT โ€” which proves that triple
898
+ * compiles โ€” and no execution job, which is the honest rendering of having
899
+ * nowhere to run it.
900
+ */
901
+ function renderCrossTestExecutionJob(options) {
902
+ const archives = darwinCrossTestArchives(options);
903
+ if (archives.length === 0)
904
+ return '';
905
+ const numbers = followUpStepNumbers(options, 2);
906
+ const middleSteps = [
907
+ {
908
+ kind: CiWorkflowStepKind.DownloadCrossTestArchives,
909
+ name: '๐Ÿ“ฅ Download cross-target test archives',
910
+ number: numbers.middle,
911
+ },
912
+ { kind: CiWorkflowStepKind.CrossTests, name: '๐Ÿงช Cross-Target Unit Tests', number: numbers.middle + 1 },
913
+ ];
914
+ return `
915
+ macos-cross-tests:
916
+ name: Unit Tests (${archives.map((archive) => archive.triple).join(', ')})
917
+ needs: main
918
+ ${options.macosRunsOn === undefined ? ' runs-on: macos-latest' : renderRunsOnLine(options.macosRunsOn)}
919
+ timeout-minutes: ${CROSS_TEST_JOB_TIMEOUT_MINUTES}
920
+ if: \${{ needs.main.result == 'success' }}
921
+ env:
922
+ GH_TOKEN: \${{ github.token }}
923
+ ${remoteCacheJobEnvLines(options.remoteCache)}${cargoCredentialJobEnvLines(options.cargoCredentials)}${privateNpmReadTokenJobEnv(options)} steps:
924
+ ${renderCiWorkflowSteps(followUpSetupSteps(options, numbers), options)}
925
+ ${renderCiWorkflowSteps(middleSteps, options)}
926
+ ${renderCiWorkflowSteps(followUpCleanupStep(numbers), options)}`;
927
+ }
928
+ /**
929
+ * The declared archives, checked against the naming the Nx graph would have
930
+ * produced. A path that is not `cargoCrossTestArchiveFile(triple)` under some
931
+ * project root cannot have come from that target, so it is refused here rather
932
+ * than uploaded as an artifact nothing on the other side can find.
933
+ */
934
+ function crossTestArchives(options) {
935
+ return (options.crossTestArchives ?? []).map((archive) => {
936
+ const suffix = cargoCrossTestArchiveFile(archive.triple);
937
+ const path = archive.path.split('\\').join('/');
938
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(archive.triple)) {
939
+ throw new Error(`smoo cross test archive triple must be a target triple, got ${JSON.stringify(archive.triple)}`);
940
+ }
941
+ if (path !== suffix && !path.endsWith(`/${suffix}`)) {
942
+ throw new Error(`smoo cross test archive for ${archive.triple} must be a repository-relative ${suffix}, got ${archive.path}`);
943
+ }
944
+ if (path.startsWith('/') || path.split('/').includes('..')) {
945
+ throw new Error(`smoo cross test archive path must stay inside the repository, got ${archive.path}`);
946
+ }
947
+ return { triple: archive.triple, path };
948
+ });
949
+ }
950
+ function crossArchiveTargetNames(options) {
951
+ return crossTestArchives(options).map((archive) => cargoCrossTestArchiveTargetName(archive.triple));
952
+ }
953
+ /**
954
+ * The declared preflight, as lines. A declaration with nothing in it is a
955
+ * mechanism error, and it surfaces here at render time rather than as a step
956
+ * whose `run:` block is empty.
957
+ */
958
+ function crossToolchainPreflight(options) {
959
+ const preflight = options.platformProducer?.preflight ?? '';
960
+ if (preflight.trim().length === 0) {
961
+ throw new Error('smoo.github.platformProducer requires a nonempty toolchain preflight command.');
962
+ }
963
+ return preflight.split('\n');
964
+ }
965
+ /** The producer environment, step-scoped, quoted exactly as the publish workflow quotes it. */
966
+ function platformProducerStepEnvLines(options) {
967
+ const entries = Object.entries(options.platformProducer?.env ?? {});
968
+ if (entries.length === 0)
969
+ return [];
970
+ return [' env:', ...entries.map(([name, value]) => ` ${name}: ${JSON.stringify(value)}`)];
971
+ }
972
+ function darwinCrossTestArchives(options) {
973
+ return crossTestArchives(options).filter((archive) => archive.triple.endsWith(DARWIN_TRIPLE_SUFFIX));
974
+ }
975
+ function darwinCrossTestTargetNames(options) {
976
+ return darwinCrossTestArchives(options).map((archive) => cargoCrossTestTargetName(archive.triple));
977
+ }
978
+ /**
979
+ * Where the download lands. upload-artifact roots an artifact at the least
980
+ * common ancestor of the files it uploaded, so every archive in one artifact
981
+ * has to share a parent directory for the restored layout to be the one the
982
+ * runner targets read โ€” true for one cargo workspace, false the moment a
983
+ * second one also declares cross targets, which is refused here instead of
984
+ * silently restoring the archives one directory too high.
985
+ */
986
+ function crossTestArchiveDirectory(options) {
987
+ const directories = new Set(darwinCrossTestArchives(options).map((archive) => archive.path.slice(0, archive.path.lastIndexOf('/') + 1) || './'));
988
+ if (directories.size > 1) {
989
+ throw new Error(`smoo cross test archives must share one directory to travel as one artifact, got ${[...directories].join(', ')}`);
990
+ }
991
+ const [directory] = [...directories];
992
+ return (directory ?? './').replace(/\/$/, '');
993
+ }
790
994
  function renderE2eDeploymentJob(options) {
791
995
  if (!options.e2eDeployment)
792
996
  return '';
@@ -828,6 +1032,7 @@ function renderProductionDeployJob(options) {
828
1032
  needs: ${needs}
829
1033
  ${renderRunsOnLine(options.runsOn)}
830
1034
  timeout-minutes: 30
1035
+ # prettier-ignore
831
1036
  if: \${{ !cancelled() && github.event_name == 'push' && github.ref == ${stagingRefLiteral(options)} && needs.main.result == 'success'${e2eGate} }}
832
1037
  ${environmentLine(options.environments?.production)} env:
833
1038
  GH_TOKEN: \${{ github.token }}
@@ -835,7 +1040,8 @@ ${remoteCacheJobEnvLines(options.remoteCache)}${cargoCredentialJobEnvLines(optio
835
1040
  ${renderCiWorkflowSteps(followUpSetupSteps(options, numbers), options)}
836
1041
  # Step ${numbers.middle}
837
1042
  - name: ๐Ÿš€ Deploy Production
838
- ${renderOptionalLines(deployStepSecretEnvLines(options))} run: smoo github-ci nx-deploy --stage production --mode run-many --select-tag ${PRODUCTION_PUSH_DEPLOY_TAG} --name "Deploy Production" --step ${numbers.middle}
1043
+ ${renderOptionalLines(deployStepSecretEnvLines(options))} # prettier-ignore
1044
+ run: smoo github-ci nx-deploy --stage production --mode run-many --select-tag ${PRODUCTION_PUSH_DEPLOY_TAG} --name "Deploy Production" --step ${numbers.middle}
839
1045
 
840
1046
  ${renderCiWorkflowSteps(followUpCleanupStep(numbers), options)}`;
841
1047
  }
@@ -1,5 +1,6 @@
1
1
  import { type NonEmptyArray, type PackageCargoCredentialsConfig, type PackagePrivateNpmConfig, type PackageRemoteCacheConfig, type PackageSmooGithub, type PackageSmooGithubEnvironments, type PackageSourceCheckoutConfig } from '../lib/json.js';
2
2
  import { type NxProjects } from '../nx/index.js';
3
+ import { type CiCrossTestArchive } from './ci-workflow.js';
3
4
  /**
4
5
  * Repos may append their own content to a managed file below this marker โ€”
5
6
  * e.g. extra merge drivers in .gitattributes. Everything from the marker
@@ -81,6 +82,12 @@ export interface ManagedFileContext {
81
82
  cargoCredentials?: PackageCargoCredentialsConfig;
82
83
  /** Declared self-hosted Nx remote cache from the root smoo config; absent means local caching only. */
83
84
  remoteCache?: PackageRemoteCacheConfig;
85
+ /**
86
+ * Cross-built test archives the graph can produce, one per foreign target
87
+ * triple the cargo workspace declares. Empty for a repository that declares
88
+ * none, which renders a CI workflow with no cross steps and no macOS job.
89
+ */
90
+ crossTestArchives: CiCrossTestArchive[];
84
91
  }
85
92
  interface DeployTargetInfo {
86
93
  exists: boolean;
@@ -94,6 +101,22 @@ export declare function applyManagedFiles(root: string, mode: 'update' | 'check'
94
101
  /** Renders a generated workflow through its real managed-file descriptor, so the context wiring is covered. */
95
102
  export declare function renderManagedWorkflowForTest(source: 'ci-workflow' | 'publish-workflow', context: ManagedFileContext): string;
96
103
  export declare function hasExactTargetForTest(targetNames: Iterable<string>, target: string): boolean;
104
+ /**
105
+ * Test seam: the cross-built test archives this graph can produce, read back
106
+ * out of the inferred `cargo-cross-test-archive-<triple>` targets.
107
+ *
108
+ * Derived, not configured. The triple is declared exactly once โ€” in the cargo
109
+ * workspace's own `[workspace.metadata.smoothbricks.test] cross-targets`, where
110
+ * a Rust target triple belongs โ€” and both halves of the workflow are rendered
111
+ * from the targets that declaration produced. A second copy in `smoo.github`
112
+ * could name a triple the graph cannot build, and CI would discover that only
113
+ * on the runner.
114
+ *
115
+ * The path is the target's own output location, so the artifact the Linux job
116
+ * uploads and the file the macOS job's runner opens are the same string
117
+ * computed once.
118
+ */
119
+ export declare function crossTestArchivesForTest(projects: NxProjects): CiCrossTestArchive[];
97
120
  /**
98
121
  * The platform families the release workflow produces: the graph's families
99
122
  * minus the ones the repository excludes. Excluding a family the graph does not
@@ -1 +1 @@
1
- {"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/monorepo/managed-files.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/monorepo/managed-files.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,12 @@
1
1
  import { appendFileSync, existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, join, resolve } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
+ import { cargoCrossTestArchiveFile, cargoCrossTestTripleFromArchiveTarget, } from '@smoothbricks/nx-plugin/cross-check-policy';
4
5
  import { MACOS_PLATFORM_TARGET_GLOBS, PLATFORM_TARGET_GLOBS } from '@smoothbricks/nx-plugin/workspace-config-policy';
5
6
  import { isStageDerivedDeploy, PRODUCTION_PUSH_DEPLOY_TAG } from '../lib/deploy-tags.js';
6
7
  import { ciPushBranches, readValidatedPackageJson, } from '../lib/json.js';
7
8
  import { listReleasePackages, packageInfo } from '../lib/workspace.js';
8
- import { loadNxProjects, targetNamesFromProjects } from '../nx/index.js';
9
+ import { loadNxProjects, projectRootFromNxProjectJson, targetNamesFromNxProjectJson, targetNamesFromProjects, } from '../nx/index.js';
9
10
  import { resolvePrivateNpmWorkflowConfig } from '../release/private-npm.js';
10
11
  import { privateNpmReadTokenJobEnv, renderCiWorkflowYaml } from './ci-workflow.js';
11
12
  import { renderRunsOnLine } from './github-runs-on.js';
@@ -305,6 +306,9 @@ function getManagedContent(file, context) {
305
306
  deploySecrets: context.ciDeploySecrets,
306
307
  e2eSecrets: context.ciE2eSecrets,
307
308
  productionOnPush: context.hasProductionPushDeployTargets,
309
+ crossTestArchives: context.crossTestArchives,
310
+ macosRunsOn: context.macosRunsOn,
311
+ platformProducer: context.platformProducer,
308
312
  });
309
313
  }
310
314
  if (file.source === 'publish-workflow') {
@@ -397,11 +401,45 @@ async function getManagedFileContext(root) {
397
401
  ? macosPlatformArchitecturesForTest(targetNames)
398
402
  : [],
399
403
  privateNpm,
404
+ crossTestArchives: crossTestArchivesForTest(nxProjects),
400
405
  };
401
406
  }
402
407
  export function hasExactTargetForTest(targetNames, target) {
403
408
  return [...targetNames].includes(target);
404
409
  }
410
+ /**
411
+ * Test seam: the cross-built test archives this graph can produce, read back
412
+ * out of the inferred `cargo-cross-test-archive-<triple>` targets.
413
+ *
414
+ * Derived, not configured. The triple is declared exactly once โ€” in the cargo
415
+ * workspace's own `[workspace.metadata.smoothbricks.test] cross-targets`, where
416
+ * a Rust target triple belongs โ€” and both halves of the workflow are rendered
417
+ * from the targets that declaration produced. A second copy in `smoo.github`
418
+ * could name a triple the graph cannot build, and CI would discover that only
419
+ * on the runner.
420
+ *
421
+ * The path is the target's own output location, so the artifact the Linux job
422
+ * uploads and the file the macOS job's runner opens are the same string
423
+ * computed once.
424
+ */
425
+ export function crossTestArchivesForTest(projects) {
426
+ const archives = [];
427
+ for (const project of Object.values(projects)) {
428
+ const projectRoot = projectRootFromNxProjectJson(project);
429
+ for (const targetName of targetNamesFromNxProjectJson(project)) {
430
+ const triple = cargoCrossTestTripleFromArchiveTarget(targetName);
431
+ if (triple === null) {
432
+ continue;
433
+ }
434
+ const file = cargoCrossTestArchiveFile(triple);
435
+ archives.push({
436
+ triple,
437
+ path: projectRoot === undefined || projectRoot === '.' ? file : `${projectRoot}/${file}`,
438
+ });
439
+ }
440
+ }
441
+ return archives.sort((left, right) => left.triple.localeCompare(right.triple));
442
+ }
405
443
  /**
406
444
  * The platform families the release workflow produces: the graph's families
407
445
  * minus the ones the repository excludes. Excluding a family the graph does not
@@ -2,7 +2,7 @@ import type { PackageCargoCredentialsConfig, PackagePrivateNpmConfig, PackageRem
2
2
  import { type DeployStepSecretConfig } from './ci-workflow.js';
3
3
  import { type WorkflowRunsOn } from './github-runs-on.js';
4
4
  export type PublishWorkflowBump = 'auto' | 'patch' | 'minor' | 'major' | 'prerelease';
5
- export type PublishWorkflowCondition = 'version-mode-not-none' | 'deploy-production' | 'deploy-production-standalone' | 'failure' | 'always';
5
+ export type PublishWorkflowCondition = 'plan-mode-not-none' | 'version-mode-not-none' | 'deploy-production' | 'deploy-production-standalone' | 'failure' | 'always';
6
6
  export type PublishWorkflowNxTarget = 'build' | 'lint' | 'test';
7
7
  export type PublishWorkflowDeployStage = 'none' | 'production';
8
8
  export declare enum PublishWorkflowStepKind {
@@ -13,6 +13,7 @@ export declare enum PublishWorkflowStepKind {
13
13
  ConfigureReleaseAuthor = "configure-release-author",
14
14
  BuildNxVersionActions = "build-nx-version-actions",
15
15
  RepairPendingReleases = "repair-pending-releases",
16
+ PlanRelease = "plan-release",
16
17
  VersionRelease = "version-release",
17
18
  CheckManagedMonorepoFiles = "check-managed-monorepo-files",
18
19
  Build = "build",
@@ -105,6 +106,14 @@ export interface PublishWorkflowCallbacks {
105
106
  bump: PublishWorkflowBump;
106
107
  dryRun: boolean;
107
108
  }): Promise<PublishWorkflowVersionOutputs>;
109
+ /**
110
+ * The selection the bump WILL make, computed without writing anything, so the
111
+ * gates that run before the version commit know what to run over. Defaults to
112
+ * a dry-run `versionRelease` when a caller supplies none.
113
+ */
114
+ planRelease?(input: {
115
+ bump: PublishWorkflowBump;
116
+ }): Promise<PublishWorkflowVersionOutputs>;
108
117
  checkManagedMonorepoFiles(): Promise<void>;
109
118
  nxRunMany(input: {
110
119
  target: PublishWorkflowNxTarget;
@@ -1 +1 @@
1
- {"version":3,"file":"publish-workflow.d.ts","sourceRoot":"","sources":["../../src/monorepo/publish-workflow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"publish-workflow.d.ts","sourceRoot":"","sources":["../../src/monorepo/publish-workflow.ts"],"names":[],"mappings":"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"}
@@ -31,6 +31,7 @@ export var PublishWorkflowStepKind;
31
31
  PublishWorkflowStepKind["ConfigureReleaseAuthor"] = "configure-release-author";
32
32
  PublishWorkflowStepKind["BuildNxVersionActions"] = "build-nx-version-actions";
33
33
  PublishWorkflowStepKind["RepairPendingReleases"] = "repair-pending-releases";
34
+ PublishWorkflowStepKind["PlanRelease"] = "plan-release";
34
35
  PublishWorkflowStepKind["VersionRelease"] = "version-release";
35
36
  PublishWorkflowStepKind["CheckManagedMonorepoFiles"] = "check-managed-monorepo-files";
36
37
  PublishWorkflowStepKind["Build"] = "build";
@@ -45,6 +46,9 @@ export var PublishWorkflowStepKind;
45
46
  })(PublishWorkflowStepKind || (PublishWorkflowStepKind = {}));
46
47
  export function definePublishWorkflow(options = {}) {
47
48
  const versionMode = githubExpression('steps.version.outputs.mode');
49
+ // Steps that run before the bump must label themselves from the plan: the
50
+ // version step's outputs do not exist yet, so the interpolation renders empty.
51
+ const planMode = githubExpression('steps.plan.outputs.mode');
48
52
  if (options.release === false) {
49
53
  return { steps: defineDeployOnlyWorkflowSteps(options) };
50
54
  }
@@ -86,30 +90,37 @@ export function definePublishWorkflow(options = {}) {
86
90
  kind: PublishWorkflowStepKind.RepairPendingReleases,
87
91
  name: '๐Ÿงฏ Repair pending releases',
88
92
  },
89
- { kind: PublishWorkflowStepKind.VersionRelease, name: '๐Ÿ”ข Version release', id: 'version' },
93
+ // Plan first, gate second, bump third. Lint and test hash the source, so
94
+ // running them BEFORE the version commit means their task hashes match
95
+ // the ones ci already computed for the same source โ€” the publish reuses
96
+ // that cache instead of re-running 189 tasks. Build stays after the bump
97
+ // on purpose: an artifact built from pre-bump sources would ship the
98
+ // previous version string.
99
+ { kind: PublishWorkflowStepKind.PlanRelease, name: '๐Ÿงญ Plan release', id: 'plan' },
90
100
  {
91
101
  kind: PublishWorkflowStepKind.CheckManagedMonorepoFiles,
92
- name: `โœ… Check managed monorepo files (${versionMode})`,
93
- condition: 'version-mode-not-none',
94
- },
95
- {
96
- kind: PublishWorkflowStepKind.Build,
97
- name: `๐Ÿ”จ Build (${versionMode})`,
98
- condition: 'version-mode-not-none',
99
- nxTarget: 'build',
102
+ name: `โœ… Check managed monorepo files (${planMode})`,
103
+ condition: 'plan-mode-not-none',
100
104
  },
101
105
  {
102
106
  kind: PublishWorkflowStepKind.Lint,
103
- name: `๐Ÿ” Lint (${versionMode})`,
104
- condition: 'version-mode-not-none',
107
+ name: `๐Ÿ” Lint (${planMode})`,
108
+ condition: 'plan-mode-not-none',
105
109
  nxTarget: 'lint',
106
110
  },
107
111
  {
108
112
  kind: PublishWorkflowStepKind.UnitTests,
109
- name: `๐Ÿงช Unit Tests (${versionMode})`,
110
- condition: 'version-mode-not-none',
113
+ name: `๐Ÿงช Unit Tests (${planMode})`,
114
+ condition: 'plan-mode-not-none',
111
115
  nxTarget: 'test',
112
116
  },
117
+ { kind: PublishWorkflowStepKind.VersionRelease, name: '๐Ÿ”ข Version release', id: 'version' },
118
+ {
119
+ kind: PublishWorkflowStepKind.Build,
120
+ name: `๐Ÿ”จ Build (${versionMode})`,
121
+ condition: 'version-mode-not-none',
122
+ nxTarget: 'build',
123
+ },
113
124
  { kind: PublishWorkflowStepKind.UploadTraceDbs, name: '๐Ÿ“Ž Upload trace DBs', condition: 'failure' },
114
125
  {
115
126
  kind: PublishWorkflowStepKind.ValidateMonorepoConfig,
@@ -158,10 +169,11 @@ function numberWorkflowSteps(steps) {
158
169
  export async function runPublishWorkflow(workflow, context) {
159
170
  let setupOutputs = { nixCacheHit: '', devenvCacheHit: '' };
160
171
  let version = { mode: 'none', projects: [] };
172
+ let plan = { mode: 'none', projects: [] };
161
173
  let failed = false;
162
174
  let failure;
163
175
  for (const step of workflow.steps) {
164
- if (!shouldRunStep(step, version, failed, context.inputs)) {
176
+ if (!shouldRunStep(step, version, failed, context.inputs, plan)) {
165
177
  continue;
166
178
  }
167
179
  try {
@@ -187,6 +199,11 @@ export async function runPublishWorkflow(workflow, context) {
187
199
  dryRun: context.inputs.dryRun,
188
200
  });
189
201
  break;
202
+ case PublishWorkflowStepKind.PlanRelease:
203
+ plan = context.callbacks.planRelease
204
+ ? await context.callbacks.planRelease({ bump: context.inputs.bump })
205
+ : await context.callbacks.versionRelease({ bump: context.inputs.bump, dryRun: true });
206
+ break;
190
207
  case PublishWorkflowStepKind.VersionRelease:
191
208
  version = await context.callbacks.versionRelease(context.inputs);
192
209
  break;
@@ -199,7 +216,12 @@ export async function runPublishWorkflow(workflow, context) {
199
216
  if (!step.nxTarget) {
200
217
  throw new Error(`Workflow step ${step.kind} is missing an Nx target.`);
201
218
  }
202
- await context.callbacks.nxRunMany({ target: step.nxTarget, projects: version.projects });
219
+ // Lint and test run before the bump exists, so they select from the
220
+ // plan; build runs after it and selects from the written version.
221
+ await context.callbacks.nxRunMany({
222
+ target: step.nxTarget,
223
+ projects: step.kind === PublishWorkflowStepKind.Build ? version.projects : plan.projects,
224
+ });
203
225
  break;
204
226
  case PublishWorkflowStepKind.UploadTraceDbs:
205
227
  await context.callbacks.uploadTraceDbs();
@@ -234,7 +256,10 @@ export async function runPublishWorkflow(workflow, context) {
234
256
  }
235
257
  return { version, failed };
236
258
  }
237
- function shouldRunStep(step, version, failed, inputs) {
259
+ function shouldRunStep(step, version, failed, inputs, plan = version) {
260
+ if (step.condition === 'plan-mode-not-none') {
261
+ return plan.mode !== 'none';
262
+ }
238
263
  if (step.condition === 'version-mode-not-none') {
239
264
  return version.mode !== 'none';
240
265
  }
@@ -421,6 +446,16 @@ function yamlLinesForStep(step, options) {
421
446
  ...privateNpmPublisherStepEnv(options),
422
447
  ` run: smoo release repair-pending --dry-run "${githubExpression('inputs.dry_run')}"`,
423
448
  ];
449
+ case PublishWorkflowStepKind.PlanRelease:
450
+ // The same selection the bump will make, computed without writing: the
451
+ // gates need `mode` and `projects` before any version commit exists.
452
+ return [
453
+ ` - name: ${step.name}`,
454
+ ' id: plan',
455
+ ' run:',
456
+ ` smoo release version --bump "${githubExpression('inputs.bump')}" --projects "${githubExpression('inputs.projects')}" --dry-run "true" --github-output`,
457
+ ' "$GITHUB_OUTPUT"',
458
+ ];
424
459
  case PublishWorkflowStepKind.VersionRelease:
425
460
  return [
426
461
  ` - name: ${step.name}`,
@@ -436,9 +471,9 @@ function yamlLinesForStep(step, options) {
436
471
  case PublishWorkflowStepKind.Build:
437
472
  return conditionalRunStep(step, `smoo github-ci nx-run-many --targets build --projects "${githubExpression('steps.version.outputs.projects')}"`);
438
473
  case PublishWorkflowStepKind.Lint:
439
- return conditionalRunStep(step, `smoo github-ci nx-run-many --targets lint --projects "${githubExpression('steps.version.outputs.projects')}"`);
474
+ return conditionalRunStep(step, `smoo github-ci nx-run-many --targets lint --projects "${githubExpression('steps.plan.outputs.projects')}"`);
440
475
  case PublishWorkflowStepKind.UnitTests:
441
- return conditionalRunStep(step, `smoo github-ci nx-run-many --targets test --projects "${githubExpression('steps.version.outputs.projects')}"`);
476
+ return conditionalRunStep(step, `smoo github-ci nx-run-many --targets test --projects "${githubExpression('steps.plan.outputs.projects')}"`);
442
477
  case PublishWorkflowStepKind.UploadTraceDbs:
443
478
  return artifactStepLines(options.actionsProvider, step.name, 'upload', [
444
479
  `name: trace-results-${githubExpression('github.run_id')}`,
@@ -513,8 +548,11 @@ function deployProductionStep(step, options) {
513
548
  ];
514
549
  }
515
550
  function conditionalRunStep(step, run) {
516
- const condition = "steps.version.outputs.mode != 'none'";
517
- return [` - name: ${step.name}`, ` if: ${condition}`, ` run: ${run}`];
551
+ // A step gated on the plan must read the PLAN's mode: it runs before the
552
+ // version step exists, and `steps.version.outputs.mode` is empty there, which
553
+ // silently skips the gate rather than running it.
554
+ const source = step.condition === 'plan-mode-not-none' ? 'plan' : 'version';
555
+ return [` - name: ${step.name}`, ` if: steps.${source}.outputs.mode != 'none'`, ` run: ${run}`];
518
556
  }
519
557
  function siblingSourceCheckoutStepLines(options) {
520
558
  const checkouts = options.sourceCheckouts;