@smoothbricks/cli 0.11.12 → 0.11.14

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 (39) hide show
  1. package/README.md +104 -20
  2. package/dist/cli.js +6 -1
  3. package/dist/lib/json.d.ts +51 -0
  4. package/dist/lib/json.d.ts.map +1 -1
  5. package/dist/lib/json.js +111 -67
  6. package/dist/monorepo/ci-workflow.d.ts +27 -2
  7. package/dist/monorepo/ci-workflow.d.ts.map +1 -1
  8. package/dist/monorepo/ci-workflow.js +151 -11
  9. package/dist/monorepo/index.d.ts +2 -0
  10. package/dist/monorepo/index.d.ts.map +1 -1
  11. package/dist/monorepo/index.js +1 -1
  12. package/dist/monorepo/managed-files.d.ts +3 -1
  13. package/dist/monorepo/managed-files.d.ts.map +1 -1
  14. package/dist/monorepo/managed-files.js +3 -0
  15. package/dist/monorepo/packed-package.d.ts +3 -3
  16. package/dist/monorepo/packed-package.d.ts.map +1 -1
  17. package/dist/monorepo/packed-package.js +11 -6
  18. package/dist/monorepo/packs/index.d.ts +8 -0
  19. package/dist/monorepo/packs/index.d.ts.map +1 -1
  20. package/dist/monorepo/packs/index.js +6 -5
  21. package/dist/monorepo/publish-workflow.d.ts +8 -1
  22. package/dist/monorepo/publish-workflow.d.ts.map +1 -1
  23. package/dist/monorepo/publish-workflow.js +20 -6
  24. package/managed/raw/tooling/direnv/devenv.smoo.nix +28 -4
  25. package/managed/raw/tooling/direnv/secret-references.ts +138 -53
  26. package/managed/templates/github/actions/save-nix-devenv/action.yml +2 -2
  27. package/managed/templates/github/actions/setup-devenv/action.yml +54 -5
  28. package/package.json +2 -2
  29. package/src/cli.ts +10 -1
  30. package/src/lib/json.ts +52 -0
  31. package/src/monorepo/__tests__/ci-workflow.test.ts +183 -0
  32. package/src/monorepo/__tests__/publish-workflow.test.ts +32 -0
  33. package/src/monorepo/ci-workflow.ts +180 -11
  34. package/src/monorepo/index.ts +6 -1
  35. package/src/monorepo/managed-files.ts +6 -0
  36. package/src/monorepo/packed-package.ts +21 -6
  37. package/src/monorepo/packs/index.ts +14 -5
  38. package/src/monorepo/publish-workflow.ts +32 -5
  39. package/src/monorepo/secret-references.test.ts +28 -2
@@ -36,6 +36,14 @@ export interface MonorepoContext {
36
36
  root: string;
37
37
  syncRuntime: boolean;
38
38
  verbose?: boolean;
39
+ /**
40
+ * Nx project names the validation is about, when a caller knows: the
41
+ * release's candidates. The build phase and the packed-package packs scope
42
+ * to them; manifest-level packs still see the whole repository, because a
43
+ * package's hygiene does not depend on which packages ship today. Absent,
44
+ * everything is validated and everything is built.
45
+ */
46
+ projects?: readonly string[];
39
47
  }
40
48
 
41
49
  export interface ValidatePackOptions {
@@ -174,19 +182,19 @@ const packs: MonorepoPack[] = [
174
182
  {
175
183
  name: 'packed-package-publint',
176
184
  validatePostBuild(ctx) {
177
- return validatePackedPublishablePackagePublint(ctx.root);
185
+ return validatePackedPublishablePackagePublint(ctx.root, ctx.projects);
178
186
  },
179
187
  },
180
188
  {
181
189
  name: 'packed-package-manifest',
182
190
  validatePostBuild(ctx) {
183
- return validatePackedPublishablePackageManifest(ctx.root);
191
+ return validatePackedPublishablePackageManifest(ctx.root, ctx.projects);
184
192
  },
185
193
  },
186
194
  {
187
195
  name: 'packed-package-types',
188
196
  validatePostBuild(ctx) {
189
- return validatePackedPublishablePackageTypes(ctx.root);
197
+ return validatePackedPublishablePackageTypes(ctx.root, ctx.projects);
190
198
  },
191
199
  },
192
200
  {
@@ -369,9 +377,10 @@ async function runBuild(ctx: MonorepoContext, options: ValidatePackOptions = {})
369
377
  if (options.verbose) {
370
378
  printCheckHeading('build', true);
371
379
  }
380
+ const args = ['run-many', '-t', 'build', ...(ctx.projects?.length ? ['-p', ctx.projects.join(',')] : [])];
372
381
  const result = options.verbose
373
- ? { exitCode: await runStatus('nx', ['run-many', '-t', 'build'], ctx.root, false), stdout: '', stderr: '' }
374
- : await runResult('nx', ['run-many', '-t', 'build'], ctx.root);
382
+ ? { exitCode: await runStatus('nx', args, ctx.root, false), stdout: '', stderr: '' }
383
+ : await runResult('nx', args, ctx.root);
375
384
  const status = result.exitCode;
376
385
  if (status !== 0) {
377
386
  if (!options.verbose) {
@@ -11,6 +11,7 @@ import { isSmoothBricksCodebasePackageName } from '../lib/cli-package.js';
11
11
  import type {
12
12
  PackageCargoCredentialsConfig,
13
13
  PackagePrivateNpmConfig,
14
+ PackageRemoteCacheConfig,
14
15
  PackageSmooGithub,
15
16
  PackageSourceCheckoutConfig,
16
17
  } from '../lib/json.js';
@@ -21,6 +22,7 @@ import {
21
22
  cargoCredentialStepLines,
22
23
  type DeployStepSecretConfig,
23
24
  deployStepSecretEnvLines,
25
+ remoteCacheJobEnvLines,
24
26
  sourceCheckoutsStepLines,
25
27
  } from './ci-workflow.js';
26
28
  import { GITHUB_HOSTED_LINUX_RUNNER, renderRunsOnLine, type WorkflowRunsOn } from './github-runs-on.js';
@@ -125,6 +127,13 @@ export interface PublishWorkflowDefinitionOptions extends DeployStepSecretConfig
125
127
  * become job env because every later cargo fetch resolves them.
126
128
  */
127
129
  cargoCredentials?: PackageCargoCredentialsConfig;
130
+ /**
131
+ * Declared self-hosted Nx remote cache. Every job here runs Nx targets —
132
+ * the release-candidate build, the platform build, the publish gate — so
133
+ * each carries the server and access token and reuses what CI already
134
+ * built for the same hashes.
135
+ */
136
+ remoteCache?: PackageRemoteCacheConfig;
128
137
  }
129
138
 
130
139
  export interface PublishWorkflowInputs {
@@ -466,7 +475,7 @@ jobs:
466
475
  publish:
467
476
  ${options.release === false ? renderRunsOnLine(options.runsOn) : publishJobRunsOnLine(options)}
468
477
  env:
469
- GH_TOKEN: ${githubExpression('github.token')}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
478
+ GH_TOKEN: ${githubExpression('github.token')}${remoteCacheJobEnv(options)}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
470
479
  steps:
471
480
  `;
472
481
  }
@@ -605,7 +614,14 @@ function yamlLinesForStep(step: PublishWorkflowStep, options: PublishWorkflowDef
605
614
  'failure()',
606
615
  );
607
616
  case PublishWorkflowStepKind.ValidateMonorepoConfig:
608
- return conditionalRunStep(step, 'smoo monorepo validate');
617
+ // Scoped to the release's candidates: the build phase and the packed-package
618
+ // checks cover what ships. Unscoped, validate compiled every project - on a
619
+ // release touching no Rust that was 3m30 of the cowshed CLI on the critical
620
+ // path, for a binary the release did not contain.
621
+ return conditionalRunStep(
622
+ step,
623
+ `smoo monorepo validate --projects "${githubExpression('steps.version.outputs.projects')}"`,
624
+ );
609
625
  case PublishWorkflowStepKind.TagRelease:
610
626
  return tagReleaseStepLines(step.name);
611
627
  case PublishWorkflowStepKind.PublishRelease:
@@ -717,6 +733,17 @@ function cargoCredentialsJobEnv(options: PublishWorkflowDefinitionOptions): stri
717
733
  return rendered === '' ? '' : `\n${rendered}`;
718
734
  }
719
735
 
736
+ /**
737
+ * The remote cache is job env for the same reason: every Nx target in the job
738
+ * reads it, and a release build that missed the cache would rebuild what CI
739
+ * already built for the identical hashes. Only `${{ secrets.NAME }}` is
740
+ * rendered for the token. Malformed declarations throw here, not in CI.
741
+ */
742
+ function remoteCacheJobEnv(options: PublishWorkflowDefinitionOptions): string {
743
+ const rendered = remoteCacheJobEnvLines(options.remoteCache).trimEnd();
744
+ return rendered === '' ? '' : `\n${rendered}`;
745
+ }
746
+
720
747
  function renderSingleJobPublishWorkflowSteps(
721
748
  steps: PublishWorkflowStep[],
722
749
  options: PublishWorkflowDefinitionOptions,
@@ -796,7 +823,7 @@ ${renderRunsOnLine(options.runsOn)}
796
823
  mode: ${githubExpression('steps.version.outputs.mode')}
797
824
  release-sha: ${githubExpression('steps.release-state.outputs.sha')}
798
825
  env:
799
- GH_TOKEN: ${githubExpression('github.token')}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
826
+ GH_TOKEN: ${githubExpression('github.token')}${remoteCacheJobEnv(options)}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
800
827
  steps:
801
828
  ${renderLinuxReleaseCandidateSteps(steps, options)}
802
829
 
@@ -806,7 +833,7 @@ ${renderMacosJobHeaderLines(options)}
806
833
  contents: read
807
834
  id-token: none
808
835
  env:
809
- GH_TOKEN: ${githubExpression('github.token')}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
836
+ GH_TOKEN: ${githubExpression('github.token')}${remoteCacheJobEnv(options)}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
810
837
  ${Object.entries(options.platformProducer?.env ?? {})
811
838
  .map(([name, value]) => ` ${name}: ${JSON.stringify(value)}`)
812
839
  .join('\n')}
@@ -821,7 +848,7 @@ ${publishJobRunsOnLine(options)}
821
848
  id-token: write
822
849
  env:
823
850
  TTSC_TSGO_BINARY: ${githubExpression('github.workspace')}/node_modules/@typescript/native/bin/tsc
824
- GH_TOKEN: ${githubExpression('github.token')}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
851
+ GH_TOKEN: ${githubExpression('github.token')}${remoteCacheJobEnv(options)}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
825
852
  steps:
826
853
  ${renderFinalLinuxPublishSteps(options)}
827
854
  `;
@@ -136,14 +136,18 @@ async function resolveInBootstrap(options: {
136
136
 
137
137
  /** Builds a fixture repository whose package.json and .npmrc are the resolver's real inputs. */
138
138
  async function withFixture(
139
- options: { secrets: Record<string, SecretSpec>; npmrc?: string },
139
+ options: { secrets: Record<string, SecretSpec>; npmrc?: string; remoteCache?: unknown },
140
140
  run: (root: string) => Promise<void>,
141
141
  ): Promise<void> {
142
142
  const root = await mkdtemp(join(tmpdir(), 'smoo-secret-references-'));
143
143
  try {
144
144
  await writeFile(
145
145
  join(root, 'package.json'),
146
- JSON.stringify({ name: 'fixture', version: '0.0.0', smoo: { secrets: options.secrets } }),
146
+ JSON.stringify({
147
+ name: 'fixture',
148
+ version: '0.0.0',
149
+ smoo: { secrets: options.secrets, remoteCache: options.remoteCache },
150
+ }),
147
151
  );
148
152
  if (options.npmrc !== undefined) {
149
153
  await writeFile(join(root, '.npmrc'), options.npmrc);
@@ -249,6 +253,28 @@ describe('resolveSecretEnvironment', () => {
249
253
  });
250
254
  });
251
255
 
256
+ it('an unreachable cache-token provider does not block the install, while other secrets still refuse', async () => {
257
+ await withFixture(
258
+ {
259
+ secrets: {
260
+ NX_REMOTE_CACHE_TOKEN: { command: ['definitely-not-a-real-smoo-binary-xyz'] },
261
+ SMOO_TOKEN: { command: emit('tok-from-provider') },
262
+ },
263
+ remoteCache: { server: 'https://nx-cache.example.net', tokenSecret: 'NX_REMOTE_CACHE_TOKEN' },
264
+ },
265
+ async (root) => {
266
+ // A cache is an optimization: its provider being down installs
267
+ // dependencies anyway. Every other declared secret keeps its refusal.
268
+ expect(await resolveInBootstrap({ root, env: {} })).toEqual({
269
+ resolved: { SMOO_TOKEN: 'tok-from-provider' },
270
+ });
271
+ const withoutCacheDeclaration = await resolveInBootstrap({ root, env: { CI: 'true' } });
272
+ expect(withoutCacheDeclaration.error).toContain('SMOO_TOKEN');
273
+ expect(withoutCacheDeclaration.error).not.toContain('NX_REMOTE_CACHE_TOKEN');
274
+ },
275
+ );
276
+ });
277
+
252
278
  it('a present variable is not reported while another refuses in CI', async () => {
253
279
  await withFixture(
254
280
  {