@smoothbricks/cli 0.11.11 → 0.11.13

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 (37) hide show
  1. package/README.md +104 -20
  2. package/dist/lib/json.d.ts +51 -0
  3. package/dist/lib/json.d.ts.map +1 -1
  4. package/dist/lib/json.js +111 -67
  5. package/dist/monorepo/cargo-policy.d.ts +25 -1
  6. package/dist/monorepo/cargo-policy.d.ts.map +1 -1
  7. package/dist/monorepo/cargo-policy.js +396 -4
  8. package/dist/monorepo/ci-workflow.d.ts +27 -2
  9. package/dist/monorepo/ci-workflow.d.ts.map +1 -1
  10. package/dist/monorepo/ci-workflow.js +151 -11
  11. package/dist/monorepo/index.d.ts.map +1 -1
  12. package/dist/monorepo/index.js +5 -1
  13. package/dist/monorepo/managed-files.d.ts +3 -1
  14. package/dist/monorepo/managed-files.d.ts.map +1 -1
  15. package/dist/monorepo/managed-files.js +3 -0
  16. package/dist/monorepo/packs/index.d.ts.map +1 -1
  17. package/dist/monorepo/packs/index.js +13 -0
  18. package/dist/monorepo/publish-workflow.d.ts +8 -1
  19. package/dist/monorepo/publish-workflow.d.ts.map +1 -1
  20. package/dist/monorepo/publish-workflow.js +15 -5
  21. package/managed/raw/tooling/direnv/devenv.smoo.nix +28 -2
  22. package/managed/raw/tooling/direnv/secret-references.ts +248 -51
  23. package/managed/templates/github/actions/save-nix-devenv/action.yml +2 -2
  24. package/managed/templates/github/actions/setup-devenv/action.yml +43 -5
  25. package/package.json +2 -2
  26. package/src/lib/json.ts +52 -0
  27. package/src/monorepo/__tests__/ci-workflow.test.ts +183 -0
  28. package/src/monorepo/__tests__/publish-workflow.test.ts +29 -0
  29. package/src/monorepo/cargo-policy.test.ts +206 -3
  30. package/src/monorepo/cargo-policy.ts +341 -2
  31. package/src/monorepo/ci-workflow.ts +180 -11
  32. package/src/monorepo/index.ts +5 -1
  33. package/src/monorepo/managed-files.ts +6 -0
  34. package/src/monorepo/packs/index.test.ts +32 -0
  35. package/src/monorepo/packs/index.ts +13 -0
  36. package/src/monorepo/publish-workflow.ts +24 -4
  37. package/src/monorepo/secret-references.test.ts +175 -2
@@ -80,8 +80,9 @@ jobs:
80
80
  name: Validate
81
81
  ${renderRunsOnLine(options.runsOn)}
82
82
  timeout-minutes: 45
83
- ${options.privateNpm?.readTokenEnv || options.cargoCredentials !== undefined
84
- ? ` # Fork PRs receive no secrets; private dependency installs cannot run there.
83
+ ${options.privateNpm?.readTokenEnv || options.cargoCredentials !== undefined || options.remoteCache !== undefined
84
+ ? ` # Fork PRs receive no secrets, so neither a private dependency install nor an
85
+ # authenticated remote cache read can run there.
85
86
  if: \${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
86
87
  `
87
88
  : ''}${environmentLine(options.deploy ? options.environments?.staging : undefined)}${options.e2eDeployment
@@ -90,7 +91,7 @@ ${options.privateNpm?.readTokenEnv || options.cargoCredentials !== undefined
90
91
  `
91
92
  : ''} env:
92
93
  GH_TOKEN: ${githubExpression('github.token')}
93
- ${cargoCredentialJobEnvLines(options.cargoCredentials)}${privateNpmReadTokenJobEnv(options)} steps:
94
+ ${remoteCacheJobEnvLines(options.remoteCache)}${cargoCredentialJobEnvLines(options.cargoCredentials)}${privateNpmReadTokenJobEnv(options)} steps:
94
95
  `;
95
96
  }
96
97
  function githubExpression(expression) {
@@ -343,6 +344,68 @@ export function normalizeSourceCheckout(config) {
343
344
  }
344
345
  return config;
345
346
  }
347
+ /**
348
+ * Job env lines carrying the declared Nx remote cache. The pair rides the job
349
+ * env rather than a step because every Nx invocation in the job — build, lint,
350
+ * tests, deploy — reads it, and Nx enables the cache on a nonempty server
351
+ * alone: an empty or absent token would make it authenticate with nothing and
352
+ * fail every task on 401, which is why the job carries the same fork-PR gate
353
+ * as a private dependency install (see `renderCiWorkflowHeader`).
354
+ *
355
+ * A declared `internalServer` is the address managed runners use, exactly as a
356
+ * git origin's `internalMirror` is: both say this repository's runners sit
357
+ * inside that network, and shells outside it keep the public origin.
358
+ * Mechanism errors surface at managed-file render time, never inside CI.
359
+ */
360
+ export function remoteCacheJobEnvLines(config) {
361
+ if (config === undefined) {
362
+ return '';
363
+ }
364
+ const normalized = normalizeRemoteCache(config);
365
+ return [
366
+ ` NX_SELF_HOSTED_REMOTE_CACHE_SERVER: ${normalized.internalServer ?? normalized.server}\n`,
367
+ ` NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN: ${githubExpression(`secrets.${normalized.tokenSecret}`)}\n`,
368
+ ].join('');
369
+ }
370
+ /** Mechanism errors surface at managed-file render time, never inside CI. */
371
+ export function normalizeRemoteCache(config) {
372
+ assertCacheOrigin('server', config.server);
373
+ if (config.internalServer !== undefined) {
374
+ assertCacheOrigin('internalServer', config.internalServer);
375
+ if (config.internalServer === config.server) {
376
+ throw new Error(`smoo.remoteCache internalServer repeats server ${config.server}; drop internalServer or point it at the address internal runners reach`);
377
+ }
378
+ }
379
+ if (!/^[A-Z_][A-Z0-9_]*$/.test(config.tokenSecret)) {
380
+ throw new Error(`smoo.remoteCache needs an upper-case secret name for tokenSecret, got ${JSON.stringify(config.tokenSecret)}`);
381
+ }
382
+ return config;
383
+ }
384
+ /**
385
+ * A cache origin is exactly `scheme://host[:port]`. Nx appends
386
+ * `/v1/cache/<hash>` to it, so a trailing slash silently requests a doubled
387
+ * slash the server routes nowhere, and a path or credential would be dropped
388
+ * or leaked rather than honored.
389
+ */
390
+ function assertCacheOrigin(field, value) {
391
+ let url = null;
392
+ try {
393
+ url = new URL(value);
394
+ }
395
+ catch {
396
+ url = null;
397
+ }
398
+ if (url === null ||
399
+ (url.protocol !== 'https:' && url.protocol !== 'http:') ||
400
+ url.pathname !== '/' ||
401
+ value.endsWith('/') ||
402
+ url.username !== '' ||
403
+ url.password !== '' ||
404
+ url.search !== '' ||
405
+ url.hash !== '') {
406
+ throw new Error(`smoo.remoteCache ${field} needs a credential-free http(s) origin with no path and no trailing slash, got ${JSON.stringify(value)}`);
407
+ }
408
+ }
346
409
  /**
347
410
  * Job env lines carrying the declared Cargo credential secrets. Both registry
348
411
  * tokens and git-origin tokens ride the job env so the credential helper is
@@ -371,7 +434,10 @@ export function cargoCredentialJobEnvLines(config) {
371
434
  * `internalMirror` are rewritten to the mirror with `url.<mirror>.insteadOf`
372
435
  * and the helper answers the same credential for the mirror's host, since git
373
436
  * passes helpers the rewritten URL. The helper reads the environment at call
374
- * time and answers only for declared origins.
437
+ * time and answers only for declared origins. Declared `sshOrigins` rewrite
438
+ * onto the same mirror and stay credential-free: the rewrite happens before
439
+ * transport, so git only ever asks for the mirror, and an SSH pin the runner
440
+ * has no key for fails loudly instead of collecting a token.
375
441
  * Mechanism errors surface at managed-file render time, never inside CI.
376
442
  */
377
443
  export function cargoCredentialStepLines(step, config) {
@@ -451,22 +517,49 @@ export function cargoCredentialStepLines(step, config) {
451
517
  lines.push(' fi');
452
518
  lines.push(' # Internal mirrors rewrite the declared origin prefix so runners');
453
519
  lines.push(' # that cannot reach the public URL fetch over guest networking.');
520
+ if (origins.some((origin) => origin.sshOrigins !== undefined)) {
521
+ lines.push(' # The same forge over SSH, as Cargo and uv pin it: git matches');
522
+ lines.push(' # insteadOf values as literal URL prefixes, so every declared');
523
+ lines.push(' # spelling rewrites onto the mirror on its own line.');
524
+ }
454
525
  for (const origin of origins) {
455
526
  if (origin.internalMirror === undefined) {
456
527
  continue;
457
528
  }
458
- const originUrl = new URL(origin.origin);
459
529
  const mirrorUrl = new URL(origin.internalMirror);
460
- const originBase = `${originUrl.protocol}//${originUrl.host}/`.replaceAll("'", "'\\''");
461
530
  const mirrorBase = `${mirrorUrl.protocol}//${mirrorUrl.host}/`.replaceAll("'", "'\\''");
462
- lines.push(' echo "GIT_CONFIG_KEY_${idx}=url.' + mirrorBase + '.insteadOf"');
463
- lines.push(' echo "GIT_CONFIG_VALUE_${idx}=' + originBase + '"');
464
- lines.push(' idx=$((idx + 1))');
531
+ for (const prefix of mirrorRewritePrefixes(origin)) {
532
+ lines.push(' echo "GIT_CONFIG_KEY_${idx}=url.' + mirrorBase + '.insteadOf"');
533
+ lines.push(' echo "GIT_CONFIG_VALUE_${idx}=' + prefix.replaceAll("'", "'\\''") + '"');
534
+ lines.push(' idx=$((idx + 1))');
535
+ }
465
536
  }
466
537
  lines.push(' echo "GIT_CONFIG_COUNT=${idx}"');
467
538
  lines.push(' } >> "$GITHUB_ENV"');
468
539
  return lines;
469
540
  }
541
+ /**
542
+ * Every URL prefix a mirrored origin rewrites from: the declared https origin
543
+ * first, then each declared SSH spelling of the same forge. One `insteadOf`
544
+ * value per prefix, because git matches them as literal prefixes and derives
545
+ * no spelling from another. Rendering runs after `normalizeCargoCredentials`,
546
+ * which parsed and refused every malformed entry.
547
+ */
548
+ function mirrorRewritePrefixes(origin) {
549
+ const originUrl = new URL(origin.origin);
550
+ return [
551
+ `${originUrl.protocol}//${originUrl.host}/`,
552
+ ...(origin.sshOrigins ?? []).map((spelling) => sshRewritePrefix(new URL(spelling))),
553
+ ];
554
+ }
555
+ /**
556
+ * The SSH spelling as a rewritable prefix: userinfo is part of the spelling
557
+ * git matches, and the trailing slash keeps `ssh://host:2223` from also
558
+ * rewriting `ssh://host:22230/`.
559
+ */
560
+ function sshRewritePrefix(spelling) {
561
+ return `ssh://${spelling.username === '' ? '' : `${spelling.username}@`}${spelling.host}/`;
562
+ }
470
563
  function distinctCargoTokenEnvs(config) {
471
564
  const seen = new Set();
472
565
  for (const env of config.registryTokenEnvs ?? []) {
@@ -485,12 +578,22 @@ export function normalizeCargoCredentials(config) {
485
578
  }
486
579
  }
487
580
  const hosts = new Set();
581
+ const sshPrefixes = new Set();
488
582
  for (const origin of config.gitOrigins ?? []) {
489
583
  const host = new URL(normalizeCargoGitOrigin(origin).origin).host;
490
584
  if (hosts.has(host)) {
491
585
  throw new Error(`smoo.github.cargoCredentials repeats git origin host ${host}; declare one token per origin`);
492
586
  }
493
587
  hosts.add(host);
588
+ for (const spelling of origin.sshOrigins ?? []) {
589
+ // One spelling, one rewrite, whole config: a repeat renders two
590
+ // identical insteadOf keys and git silently keeps the last one read.
591
+ const prefix = sshRewritePrefix(assertSshOrigin(spelling));
592
+ if (sshPrefixes.has(prefix)) {
593
+ throw new Error(`smoo.github.cargoCredentials repeats the sshOrigins spelling ${prefix}; declare each spelling once, on the origin whose mirror rewrites it`);
594
+ }
595
+ sshPrefixes.add(prefix);
596
+ }
494
597
  }
495
598
  if ((config.registryTokenEnvs?.length ?? 0) === 0 && (config.gitOrigins?.length ?? 0) === 0) {
496
599
  throw new Error('smoo.github.cargoCredentials needs at least one gitOrigins or registryTokenEnvs entry, got an empty configuration');
@@ -538,8 +641,45 @@ function normalizeCargoGitOrigin(origin) {
538
641
  throw new Error(`smoo.github.cargoCredentials gitOrigins entry rewrites ${origin.origin} to itself; drop internalMirror or point it at the internal mirror`);
539
642
  }
540
643
  }
644
+ if (origin.sshOrigins !== undefined) {
645
+ if (origin.internalMirror === undefined) {
646
+ throw new Error(`smoo.github.cargoCredentials gitOrigins entry declares sshOrigins for ${origin.origin} without an internalMirror to rewrite them onto; an SSH spelling is a rewrite source and nothing else`);
647
+ }
648
+ if (origin.sshOrigins.length === 0) {
649
+ throw new Error(`smoo.github.cargoCredentials gitOrigins entry for ${origin.origin} declares an empty sshOrigins list; name every SSH spelling a lockfile can carry, or omit the field`);
650
+ }
651
+ for (const spelling of origin.sshOrigins) {
652
+ assertSshOrigin(spelling);
653
+ }
654
+ }
541
655
  return origin;
542
656
  }
657
+ /**
658
+ * One declared SSH spelling, parsed. Credential-free and path-free: a
659
+ * password here would be a secret committed in package.json, and a path
660
+ * would rewrite a single repository instead of the forge. scp syntax
661
+ * (`git@host:org/repo.git`) is no URL, so git cannot rewrite it from a Cargo
662
+ * or uv pin at all; the `ssh://` spelling is the one to declare.
663
+ */
664
+ function assertSshOrigin(spelling) {
665
+ let ssh = null;
666
+ try {
667
+ ssh = new URL(spelling);
668
+ }
669
+ catch {
670
+ ssh = null;
671
+ }
672
+ if (ssh === null ||
673
+ ssh.protocol !== 'ssh:' ||
674
+ ssh.host === '' ||
675
+ (ssh.pathname !== '' && ssh.pathname !== '/') ||
676
+ ssh.password !== '' ||
677
+ ssh.search !== '' ||
678
+ ssh.hash !== '') {
679
+ throw new Error(`smoo.github.cargoCredentials gitOrigins entry needs credential-free ssh:// sshOrigins without a path, got ${JSON.stringify(spelling)}`);
680
+ }
681
+ return ssh;
682
+ }
543
683
  /**
544
684
  * The same-repo gate as prettier folds it: the raw single line exceeds the
545
685
  * print width, so the generator emits the folded form itself to keep the
@@ -660,7 +800,7 @@ ${renderRunsOnLine(options.runsOn)}
660
800
  ${environmentLine(options.environments?.staging)} if: \${{ needs.main.result == 'success' && needs.main.outputs.deployment-stage != '' }}
661
801
  env:
662
802
  GH_TOKEN: \${{ github.token }}
663
- ${cargoCredentialJobEnvLines(options.cargoCredentials)}${privateNpmReadTokenJobEnv(options)} steps:
803
+ ${remoteCacheJobEnvLines(options.remoteCache)}${cargoCredentialJobEnvLines(options.cargoCredentials)}${privateNpmReadTokenJobEnv(options)} steps:
664
804
  ${renderCiWorkflowSteps(followUpSetupSteps(options, numbers), options)}
665
805
  # Step ${numbers.middle}
666
806
  - name: E2E Tests (Deployed Stage)
@@ -691,7 +831,7 @@ ${renderRunsOnLine(options.runsOn)}
691
831
  if: \${{ !cancelled() && github.event_name == 'push' && github.ref == ${stagingRefLiteral(options)} && needs.main.result == 'success'${e2eGate} }}
692
832
  ${environmentLine(options.environments?.production)} env:
693
833
  GH_TOKEN: \${{ github.token }}
694
- ${cargoCredentialJobEnvLines(options.cargoCredentials)}${privateNpmReadTokenJobEnv(options)} steps:
834
+ ${remoteCacheJobEnvLines(options.remoteCache)}${cargoCredentialJobEnvLines(options.cargoCredentials)}${privateNpmReadTokenJobEnv(options)} steps:
695
835
  ${renderCiWorkflowSteps(followUpSetupSteps(options, numbers), options)}
696
836
  # Step ${numbers.middle}
697
837
  - name: 🚀 Deploy Production
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/monorepo/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AA2BxD,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,4BAA4B;IAC3C,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAQpF;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBjG;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAcpE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBrG;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAElE;AAED,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,GAAE,4BAAiC,EAC1C,IAAI,SAAgB,GACnB,IAAI,CAoBN;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B,GAAG,MAAM,CAW3G;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAI5D;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,uBAAuB,EAChC,KAAK,GAAE,qBAAoD,GAC1D,OAAO,CAAC,IAAI,CAAC,CA8Bf;AAED,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/monorepo/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AA2BxD,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,4BAA4B;IAC3C,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAQpF;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBjG;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBpE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBrG;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAElE;AAED,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,GAAE,4BAAiC,EAC1C,IAAI,SAAgB,GACnB,IAAI,CAoBN;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B,GAAG,MAAM,CAW3G;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAI5D;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,uBAAuB,EAChC,KAAK,GAAE,qBAAoD,GAC1D,OAAO,CAAC,IAAI,CAAC,CA8Bf;AAED,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,CAAC"}
@@ -2,7 +2,7 @@ import { appendFileSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { printCommandOutput, run, runResult } from '../lib/run.js';
3
3
  import { escapeRegex, getWorkspacePackages, getWorkspacePatterns, listReleasePackages } from '../lib/workspace.js';
4
4
  import { readProjectTargets } from '../nx/index.js';
5
- import { validateCargoCachePolicy } from './cargo-policy.js';
5
+ import { applyCargoFeatureUnification, validateCargoCachePolicy } from './cargo-policy.js';
6
6
  import { formatCommitMessage, stagedDeletedPublicPackages, validateBreakingDisclosure, validateCommitMessage, } from './commit-msg.js';
7
7
  import { applyWorkspaceGitConfig } from './git-config.js';
8
8
  import { syncBunLockfileVersions } from './lockfile.js';
@@ -49,6 +49,10 @@ export async function updateManagedFiles(root) {
49
49
  // Tool dependency policy (typescript API 6, @typescript/native for ttsc, nx, …)
50
50
  // lives next to managed templates — update must install them, not only rewrite files.
51
51
  await applyToolConfigDefaults(root);
52
+ // Rust's half of the same job: a multi-crate Cargo workspace must resolve
53
+ // features once for every member, or each per-crate cargo invocation
54
+ // recompiles the shared graph for its own selection.
55
+ applyCargoFeatureUnification(root);
52
56
  syncBunLockfileVersions(root, { mode: 'install' });
53
57
  console.log('installing workspace dependencies (bun install)');
54
58
  await run('bun', ['install', '--no-summary'], root);
@@ -1,4 +1,4 @@
1
- import { type NonEmptyArray, type PackageCargoCredentialsConfig, type PackagePrivateNpmConfig, type PackageSmooGithub, type PackageSmooGithubEnvironments, type PackageSourceCheckoutConfig } from '../lib/json.js';
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
3
  /**
4
4
  * Repos may append their own content to a managed file below this marker —
@@ -77,6 +77,8 @@ export interface ManagedFileContext {
77
77
  sourceCheckouts?: PackageSourceCheckoutConfig[];
78
78
  /** Declared Cargo private-dependency credentials from the root smoo config; absent means none. */
79
79
  cargoCredentials?: PackageCargoCredentialsConfig;
80
+ /** Declared self-hosted Nx remote cache from the root smoo config; absent means local caching only. */
81
+ remoteCache?: PackageRemoteCacheConfig;
80
82
  }
81
83
  interface DeployTargetInfo {
82
84
  exists: boolean;
@@ -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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"}
@@ -294,6 +294,7 @@ function getManagedContent(file, context) {
294
294
  privateNpm: context.privateNpm,
295
295
  sourceCheckouts: context.sourceCheckouts,
296
296
  cargoCredentials: context.cargoCredentials,
297
+ remoteCache: context.remoteCache,
297
298
  environments: context.ciEnvironments,
298
299
  deploySecrets: context.ciDeploySecrets,
299
300
  e2eSecrets: context.ciE2eSecrets,
@@ -317,6 +318,7 @@ function getManagedContent(file, context) {
317
318
  privateNpm: context.privateNpm,
318
319
  sourceCheckouts: context.sourceCheckouts,
319
320
  cargoCredentials: context.cargoCredentials,
321
+ remoteCache: context.remoteCache,
320
322
  deploySecrets: context.ciDeploySecrets,
321
323
  });
322
324
  }
@@ -378,6 +380,7 @@ async function getManagedFileContext(root) {
378
380
  platformTargetGlobs,
379
381
  sourceCheckouts,
380
382
  cargoCredentials,
383
+ remoteCache: manifest?.smoo?.remoteCache,
381
384
  macosPlatformArchitectures: macosPlatformArchitecturesForTest(targetNames),
382
385
  privateNpm,
383
386
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/monorepo/packs/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,mBAAmB,CAAC;AAM5E,OAAO,EAKL,KAAK,sBAAsB,EAS5B,MAAM,sBAAsB,CAAC;AAU9B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClD,WAAW,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACzD,YAAY,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1D,gBAAgB,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAClE,iBAAiB,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CACpE;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CAC9F;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAkID,0DAA0D;AAC1D,eAAO,MAAM,YAAY,gBAAQ,CAAC;AAElC,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAItE;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,eAAe,EACpB,OAAO,GAAE,mBAAwB,EACjC,KAAK,GAAE,oBAAyB,GAC/B,OAAO,CAAC,kBAAkB,CAAC,CA0B7B;AAuMD,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAexG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/monorepo/packs/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,mBAAmB,CAAC;AAO5E,OAAO,EAKL,KAAK,sBAAsB,EAS5B,MAAM,sBAAsB,CAAC;AAU9B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClD,WAAW,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACzD,YAAY,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1D,gBAAgB,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAClE,iBAAiB,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CACpE;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CAC9F;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AA8ID,0DAA0D;AAC1D,eAAO,MAAM,YAAY,gBAAQ,CAAC;AAElC,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAItE;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,eAAe,EACpB,OAAO,GAAE,mBAAwB,EACjC,KAAK,GAAE,oBAAyB,GAC/B,OAAO,CAAC,kBAAkB,CAAC,CA0B7B;AAuMD,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAexG"}
@@ -2,6 +2,7 @@ import { chmodSync, existsSync, statSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { printCommandOutput, runResult, runStatus } from '../../lib/run.js';
4
4
  import { readProjectTargets } from '../../nx/index.js';
5
+ import { applyCargoFeatureUnification, validateCargoCachePolicy } from '../cargo-policy.js';
5
6
  import { validateGoToolchainAgreement } from '../go-toolchain.js';
6
7
  import { syncBunLockfileVersions, validateBunLockfileVersions } from '../lockfile.js';
7
8
  import { validateDevenvModuleImport, warnOnManagedFileDrift } from '../managed-files.js';
@@ -64,6 +65,18 @@ const packs = [
64
65
  return validateDevenvModuleImport(ctx.root);
65
66
  },
66
67
  },
68
+ {
69
+ // Cargo's build-cache and feature-unification conventions. `--fix` writes
70
+ // the workspace feature unification a multi-crate workspace needs; the rest
71
+ // of the policy is a verdict a human has to act on.
72
+ name: 'cargo',
73
+ fixPreBuild(ctx) {
74
+ applyCargoFeatureUnification(ctx.root);
75
+ },
76
+ validatePreBuild(ctx) {
77
+ return validateCargoCachePolicy(ctx.root);
78
+ },
79
+ },
67
80
  {
68
81
  name: 'publishing',
69
82
  init(ctx) {
@@ -1,4 +1,4 @@
1
- import type { PackageCargoCredentialsConfig, PackagePrivateNpmConfig, PackageSmooGithub, PackageSourceCheckoutConfig } from '../lib/json.js';
1
+ import type { PackageCargoCredentialsConfig, PackagePrivateNpmConfig, PackageRemoteCacheConfig, PackageSmooGithub, PackageSourceCheckoutConfig } from '../lib/json.js';
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';
@@ -72,6 +72,13 @@ export interface PublishWorkflowDefinitionOptions extends DeployStepSecretConfig
72
72
  * become job env because every later cargo fetch resolves them.
73
73
  */
74
74
  cargoCredentials?: PackageCargoCredentialsConfig;
75
+ /**
76
+ * Declared self-hosted Nx remote cache. Every job here runs Nx targets —
77
+ * the release-candidate build, the platform build, the publish gate — so
78
+ * each carries the server and access token and reuses what CI already
79
+ * built for the same hashes.
80
+ */
81
+ remoteCache?: PackageRemoteCacheConfig;
75
82
  }
76
83
  export interface PublishWorkflowInputs {
77
84
  bump: PublishWorkflowBump;
@@ -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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"}
@@ -2,7 +2,7 @@
2
2
  import { LINUX_PLATFORM_TARGET_GLOBS, MACOS_PLATFORM_TARGET_GLOBS, } from '@smoothbricks/nx-plugin/workspace-config-policy';
3
3
  import { makeModuleSynchronized } from 'make-synchronized';
4
4
  import { isSmoothBricksCodebasePackageName } from '../lib/cli-package.js';
5
- import { artifactStepLines, CiWorkflowStepKind, cargoCredentialJobEnvLines, cargoCredentialStepLines, deployStepSecretEnvLines, sourceCheckoutsStepLines, } from './ci-workflow.js';
5
+ import { artifactStepLines, CiWorkflowStepKind, cargoCredentialJobEnvLines, cargoCredentialStepLines, deployStepSecretEnvLines, remoteCacheJobEnvLines, sourceCheckoutsStepLines, } from './ci-workflow.js';
6
6
  import { GITHUB_HOSTED_LINUX_RUNNER, renderRunsOnLine } from './github-runs-on.js';
7
7
  const PUBLISH_WORKFLOW_FORMAT_OPTIONS = Object.freeze({
8
8
  parser: 'yaml',
@@ -321,7 +321,7 @@ jobs:
321
321
  publish:
322
322
  ${options.release === false ? renderRunsOnLine(options.runsOn) : publishJobRunsOnLine(options)}
323
323
  env:
324
- GH_TOKEN: ${githubExpression('github.token')}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
324
+ GH_TOKEN: ${githubExpression('github.token')}${remoteCacheJobEnv(options)}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
325
325
  steps:
326
326
  `;
327
327
  }
@@ -538,6 +538,16 @@ function cargoCredentialsJobEnv(options) {
538
538
  const rendered = cargoCredentialJobEnvLines(options.cargoCredentials).trimEnd();
539
539
  return rendered === '' ? '' : `\n${rendered}`;
540
540
  }
541
+ /**
542
+ * The remote cache is job env for the same reason: every Nx target in the job
543
+ * reads it, and a release build that missed the cache would rebuild what CI
544
+ * already built for the identical hashes. Only `${{ secrets.NAME }}` is
545
+ * rendered for the token. Malformed declarations throw here, not in CI.
546
+ */
547
+ function remoteCacheJobEnv(options) {
548
+ const rendered = remoteCacheJobEnvLines(options.remoteCache).trimEnd();
549
+ return rendered === '' ? '' : `\n${rendered}`;
550
+ }
541
551
  function renderSingleJobPublishWorkflowSteps(steps, options) {
542
552
  const lines = [];
543
553
  for (const step of steps) {
@@ -605,7 +615,7 @@ ${renderRunsOnLine(options.runsOn)}
605
615
  mode: ${githubExpression('steps.version.outputs.mode')}
606
616
  release-sha: ${githubExpression('steps.release-state.outputs.sha')}
607
617
  env:
608
- GH_TOKEN: ${githubExpression('github.token')}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
618
+ GH_TOKEN: ${githubExpression('github.token')}${remoteCacheJobEnv(options)}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
609
619
  steps:
610
620
  ${renderLinuxReleaseCandidateSteps(steps, options)}
611
621
 
@@ -615,7 +625,7 @@ ${renderMacosJobHeaderLines(options)}
615
625
  contents: read
616
626
  id-token: none
617
627
  env:
618
- GH_TOKEN: ${githubExpression('github.token')}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
628
+ GH_TOKEN: ${githubExpression('github.token')}${remoteCacheJobEnv(options)}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
619
629
  ${Object.entries(options.platformProducer?.env ?? {})
620
630
  .map(([name, value]) => ` ${name}: ${JSON.stringify(value)}`)
621
631
  .join('\n')}
@@ -630,7 +640,7 @@ ${publishJobRunsOnLine(options)}
630
640
  id-token: write
631
641
  env:
632
642
  TTSC_TSGO_BINARY: ${githubExpression('github.workspace')}/node_modules/@typescript/native/bin/tsc
633
- GH_TOKEN: ${githubExpression('github.token')}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
643
+ GH_TOKEN: ${githubExpression('github.token')}${remoteCacheJobEnv(options)}${cargoCredentialsJobEnv(options)}${privateNpmInstallJobEnv(options)}
634
644
  steps:
635
645
  ${renderFinalLinuxPublishSteps(options)}
636
646
  `;
@@ -221,6 +221,12 @@
221
221
  # variants per crate and 26 GB of junk before this existed), and a sweep
222
222
  # prunes them without touching the warm current-fingerprint surface.
223
223
  pkgs.cargo-sweep
224
+ # The stable-toolchain arm of smoo's workspace feature-unification policy:
225
+ # `cargo hakari` generates and wires the workspace-hack crate that unifies
226
+ # features when `[resolver] feature-unification` — nightly-only, and what
227
+ # the channel above selects — is unavailable. `smoo monorepo validate` runs
228
+ # `cargo hakari verify` for a workspace that took that route.
229
+ pkgs.cargo-hakari
224
230
  ];
225
231
 
226
232
  enterShell = lib.mkMerge [
@@ -268,7 +274,17 @@
268
274
  # 5. The shared setup-environment.ts bootstraps repository dependencies; a
269
275
  # failure aborts shell entry instead of yielding a half-working shell.
270
276
  # Repo-owned enterShell bodies merge after this prologue.
271
- # 6. GOROOT is unset rather than set. With devenv's Go pinned to the patch
277
+ # 6. The declared Nx remote cache (`smoo.remoteCache`), if the repository
278
+ # has one. secret-references.ts prints the two variables Nx reads — the
279
+ # server and the access token — and prints nothing at all unless the
280
+ # declared token has a value, because Nx accepts only 200 or 404 from a
281
+ # cache server: an unauthenticated one fails every task on 401 instead
282
+ # of missing quietly. It is eval-ed into THIS shell because that is
283
+ # where Nx runs; the export covers those two variables only, so the rule
284
+ # that keeps `smoo.secrets` inside the setup child still holds. An
285
+ # inherited server wins, so a CI job env is never overwritten, and a
286
+ # missing token costs a stderr line rather than shell entry.
287
+ # 7. GOROOT is unset rather than set. With devenv's Go pinned to the patch
272
288
  # release ttsc vendors, a GOROOT crossing cannot misfire on version at all,
273
289
  # so this is belt-and-braces rather than the fix — it keeps the isolation
274
290
  # boundary intact even while the two are momentarily out of step, e.g. after
@@ -280,7 +296,7 @@
280
296
  # resolves its own GOROOT from its own binary, which makes "whose Go is
281
297
  # whose" a property of which binary is invoked — the only thing that can
282
298
  # actually be reasoned about.
283
- # 7. On Darwin, drop nix CC/CXX so xcodebuild finds Xcode's clang (it supports
299
+ # 8. On Darwin, drop nix CC/CXX so xcodebuild finds Xcode's clang (it supports
284
300
  # -index-store-path); bun/node native addons find compilers through
285
301
  # node-gyp. CC/CXX are what xcodebuild reads, which is why they go rather
286
302
  # than being pointed somewhere else.
@@ -310,6 +326,15 @@
310
326
  (lib.mkBefore ''
311
327
  cd "$DEVENV_ROOT/../.."
312
328
  export PATH="$("$PWD/tooling/direnv/repo-path")"
329
+ # devenv enters the shell with TMPDIR unset (not empty) on every platform.
330
+ # Tools then fall back to /tmp, which on Darwin is not the per-user
331
+ # temporary directory the OS hands out, and a test that asserts the
332
+ # parent carries TMPDIR (so its env_clear check means something) fails in
333
+ # the shell and passes outside it. Ask the OS; an explicit value wins.
334
+ if [ -z "''${TMPDIR:-}" ]; then
335
+ TMPDIR="$(getconf DARWIN_USER_TEMP_DIR 2>/dev/null || true)"
336
+ export TMPDIR="''${TMPDIR:-/tmp}"
337
+ fi
313
338
  export TTSC_TSGO_BINARY="$PWD/node_modules/@typescript/native/bin/tsc"
314
339
  if [ -d "$HOME/.cowshed/caches" ]; then
315
340
  export TTSC_CACHE_DIR="''${TTSC_CACHE_DIR:-$HOME/.cowshed/caches/ttsc}"
@@ -324,6 +349,7 @@
324
349
  export GOFLAGS="''${GOFLAGS:--trimpath}"
325
350
  unset GOROOT
326
351
  bun "$DEVENV_ROOT/setup-environment.ts" || exit $?
352
+ eval "$(bun "$DEVENV_ROOT/secret-references.ts" "$PWD")"
327
353
  export NX_SOCKET_DIR="''${NX_SOCKET_DIR:-$DEVENV_RUNTIME/nx}"
328
354
  mkdir -p "$NX_SOCKET_DIR"
329
355
  ${lib.optionalString pkgs.stdenv.isDarwin ''