@smoothbricks/cli 0.10.5 โ†’ 0.10.6

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 (41) hide show
  1. package/dist/cli.js +2 -2
  2. package/dist/github-ci/index.d.ts.map +1 -1
  3. package/dist/github-ci/index.js +5 -3
  4. package/dist/lib/json.d.ts +2 -0
  5. package/dist/lib/json.d.ts.map +1 -1
  6. package/dist/lib/json.js +2 -2
  7. package/dist/monorepo/ci-workflow.d.ts +2 -0
  8. package/dist/monorepo/ci-workflow.d.ts.map +1 -1
  9. package/dist/monorepo/ci-workflow.js +10 -4
  10. package/dist/monorepo/github-runs-on.d.ts +14 -0
  11. package/dist/monorepo/github-runs-on.d.ts.map +1 -0
  12. package/dist/monorepo/github-runs-on.js +43 -0
  13. package/dist/monorepo/index.d.ts +2 -2
  14. package/dist/monorepo/index.d.ts.map +1 -1
  15. package/dist/monorepo/index.js +7 -7
  16. package/dist/monorepo/managed-files.d.ts +30 -3
  17. package/dist/monorepo/managed-files.d.ts.map +1 -1
  18. package/dist/monorepo/managed-files.js +78 -63
  19. package/dist/monorepo/packs/index.js +1 -1
  20. package/dist/monorepo/publish-workflow.d.ts +3 -0
  21. package/dist/monorepo/publish-workflow.d.ts.map +1 -1
  22. package/dist/monorepo/publish-workflow.js +17 -14
  23. package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +24 -7
  24. package/managed/templates/github/actions/cache-nix-devenv/action.yml +13 -11
  25. package/managed/templates/github/actions/save-nix-devenv/action.yml +34 -3
  26. package/managed/templates/github/actions/setup-devenv/action.yml +44 -15
  27. package/managed/templates/github/workflows/managed-files.yml +64 -12
  28. package/package.json +1 -1
  29. package/src/cli.ts +2 -2
  30. package/src/github-ci/index.test.ts +3 -3
  31. package/src/github-ci/index.ts +6 -3
  32. package/src/lib/json.ts +2 -0
  33. package/src/monorepo/__tests__/ci-workflow.test.ts +37 -1
  34. package/src/monorepo/__tests__/publish-workflow.test.ts +28 -1
  35. package/src/monorepo/ci-workflow.ts +14 -4
  36. package/src/monorepo/github-runs-on.ts +45 -0
  37. package/src/monorepo/index.ts +7 -7
  38. package/src/monorepo/managed-files.test.ts +49 -0
  39. package/src/monorepo/managed-files.ts +100 -64
  40. package/src/monorepo/packs/index.ts +1 -1
  41. package/src/monorepo/publish-workflow.ts +23 -15
@@ -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,6 +2,7 @@
2
2
  import synchronizedPrettier from '@prettier/sync';
3
3
  import { LINUX_PLATFORM_TARGET_GLOBS, MACOS_PLATFORM_TARGET_GLOBS, } from '@smoothbricks/nx-plugin/workspace-config-policy';
4
4
  import { isSmoothBricksCodebasePackageName } from '../lib/cli-package.js';
5
+ import { renderRunsOnLine } from './github-runs-on.js';
5
6
  const PUBLISH_WORKFLOW_FORMAT_OPTIONS = Object.freeze({
6
7
  parser: 'yaml',
7
8
  printWidth: 120,
@@ -338,7 +339,8 @@ function yamlLinesForStep(step, options) {
338
339
  case PublishWorkflowStepKind.UploadTraceDbs:
339
340
  return [
340
341
  ` - name: ${step.name}`,
341
- ` if: ${githubExpression('failure()')}`,
342
+ ' # Default is success(); failure() uploads traces only when the job already failed.',
343
+ ' if: failure()',
342
344
  ' uses: actions/upload-artifact@v7.0.1',
343
345
  ' with:',
344
346
  ` name: trace-results-${githubExpression('github.run_id')}`,
@@ -362,7 +364,8 @@ function yamlLinesForStep(step, options) {
362
364
  case PublishWorkflowStepKind.SaveNixDevenv:
363
365
  return [
364
366
  ` - name: ${step.name}`,
365
- ` if: ${githubExpression('always()')}`,
367
+ ' # success() is default; always() still saves GH Nix cache after a red job.',
368
+ ' if: always()',
366
369
  ' uses: ./.github/actions/save-nix-devenv',
367
370
  ' with:',
368
371
  ` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`,
@@ -392,7 +395,7 @@ function deployEnvLines(options) {
392
395
  }
393
396
  function conditionalRunStep(step, run) {
394
397
  const condition = "steps.version.outputs.mode != 'none'";
395
- return [` - name: ${step.name}`, ` if: ${githubExpression(condition)}`, ` run: ${run}`];
398
+ return [` - name: ${step.name}`, ` if: ${condition}`, ` run: ${run}`];
396
399
  }
397
400
  function renderSingleJobPublishWorkflowSteps(steps, options) {
398
401
  const lines = [];
@@ -402,7 +405,7 @@ function renderSingleJobPublishWorkflowSteps(steps, options) {
402
405
  lines.push(...yamlLinesForStep(step, options));
403
406
  lines.push('');
404
407
  if (step.kind === PublishWorkflowStepKind.Build) {
405
- lines.push(' - name: ๐Ÿง Build supplemental Linux targets', ` if: ${githubExpression("steps.version.outputs.mode != 'none'")}`, ` run: smoo github-ci nx-run-many --targets "${LINUX_PLATFORM_TARGET_GLOBS.join(',')}" --projects "${githubExpression('steps.version.outputs.projects')}"`, '');
408
+ lines.push(' - name: ๐Ÿง Build supplemental Linux targets', " if: steps.version.outputs.mode != 'none'", ` run: smoo github-ci nx-run-many --targets "${LINUX_PLATFORM_TARGET_GLOBS.join(',')}" --projects "${githubExpression('steps.version.outputs.projects')}"`, '');
406
409
  }
407
410
  }
408
411
  return `${lines.join('\n').trimEnd()}\n`;
@@ -447,7 +450,7 @@ defaults:
447
450
 
448
451
  jobs:
449
452
  linux-release-candidate:
450
- runs-on: ubuntu-latest
453
+ ${renderRunsOnLine(options.runsOn)}
451
454
  permissions:
452
455
  contents: write
453
456
  id-token: none
@@ -508,7 +511,7 @@ function renderLinuxReleaseCandidateSteps(steps, options) {
508
511
  lines.push(` - name: ${step.name}`, ' uses: actions/checkout@v6.0.2', ' with:', ` ref: ${githubExpression('github.sha')}`, ' filter: blob:none', ' fetch-depth: 0');
509
512
  }
510
513
  else if (step.kind === PublishWorkflowStepKind.Build) {
511
- lines.push(` - name: ${step.name}`, ` if: ${githubExpression("steps.version.outputs.mode != 'none'")}`, ' run:', ` smoo github-ci nx-run-many --targets build --projects "${githubExpression('steps.version.outputs.projects')}" --collect-outputs "${githubExpression('runner.temp')}/release-build-outputs"`);
514
+ lines.push(` - name: ${step.name}`, " if: steps.version.outputs.mode != 'none'", ' run:', ` smoo github-ci nx-run-many --targets build --projects "${githubExpression('steps.version.outputs.projects')}" --collect-outputs "${githubExpression('runner.temp')}/release-build-outputs"`);
512
515
  }
513
516
  else {
514
517
  lines.push(...yamlLinesForStep(step, options));
@@ -519,16 +522,16 @@ function renderLinuxReleaseCandidateSteps(steps, options) {
519
522
  stepNumber += 1;
520
523
  }
521
524
  if (step.kind === PublishWorkflowStepKind.Build && hasLinuxPlatformTargets(options)) {
522
- lines.push('', ` # Step ${stepNumber}`, ' - name: ๐Ÿง Build supplemental Linux targets', ` if: ${githubExpression("steps.version.outputs.mode != 'none'")}`, ' run:', ` smoo github-ci nx-run-many --targets "${LINUX_PLATFORM_TARGET_GLOBS.join(',')}" --projects "${githubExpression('steps.version.outputs.projects')}" --collect-outputs "${githubExpression('runner.temp')}/linux-platform-outputs"`);
525
+ lines.push('', ` # Step ${stepNumber}`, ' - name: ๐Ÿง Build supplemental Linux targets', " if: steps.version.outputs.mode != 'none'", ' run:', ` smoo github-ci nx-run-many --targets "${LINUX_PLATFORM_TARGET_GLOBS.join(',')}" --projects "${githubExpression('steps.version.outputs.projects')}" --collect-outputs "${githubExpression('runner.temp')}/linux-platform-outputs"`);
523
526
  stepNumber += 1;
524
527
  }
525
528
  lines.push('');
526
529
  }
527
- lines.push(' # --- Candidate transfer --------------------------------------------------', '', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ“ฆ Bundle validated release state', ' run:', ` mkdir -p "${githubExpression('runner.temp')}/publish-release-state" && git bundle create`, ` "${githubExpression('runner.temp')}/publish-release-state/release-state.bundle" HEAD --tags && git rev-parse HEAD >`, ` "${githubExpression('runner.temp')}/publish-release-state/release-head"`, '', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ“ค Upload validated release state', ' uses: actions/upload-artifact@v7.0.1', ' with:', ` name: publish-release-state-${githubExpression('github.run_id')}`, ` path: ${githubExpression('runner.temp')}/publish-release-state`, ' if-no-files-found: error', ' retention-days: 1', '', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ“ค Upload validated build outputs', ` if: ${githubExpression("steps.version.outputs.mode != 'none'")}`, ' uses: actions/upload-artifact@v7.0.1', ' with:', ` name: publish-release-outputs-${githubExpression('github.run_id')}`, ` path: ${githubExpression('runner.temp')}/release-build-outputs`, ' if-no-files-found: error', ' retention-days: 1', ' include-hidden-files: true');
530
+ lines.push(' # --- Candidate transfer --------------------------------------------------', '', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ“ฆ Bundle validated release state', ' run:', ` mkdir -p "${githubExpression('runner.temp')}/publish-release-state" && git bundle create`, ` "${githubExpression('runner.temp')}/publish-release-state/release-state.bundle" HEAD --tags && git rev-parse HEAD >`, ` "${githubExpression('runner.temp')}/publish-release-state/release-head"`, '', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ“ค Upload validated release state', ' uses: actions/upload-artifact@v7.0.1', ' with:', ` name: publish-release-state-${githubExpression('github.run_id')}`, ` path: ${githubExpression('runner.temp')}/publish-release-state`, ' if-no-files-found: error', ' retention-days: 1', '', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ“ค Upload validated build outputs', " if: steps.version.outputs.mode != 'none'", ' uses: actions/upload-artifact@v7.0.1', ' with:', ` name: publish-release-outputs-${githubExpression('github.run_id')}`, ` path: ${githubExpression('runner.temp')}/release-build-outputs`, ' if-no-files-found: error', ' retention-days: 1', ' include-hidden-files: true');
528
531
  if (hasLinuxPlatformTargets(options)) {
529
- lines.push('', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ“ค Upload supplemental Linux outputs', ` if: ${githubExpression("steps.version.outputs.mode != 'none'")}`, ' uses: actions/upload-artifact@v7.0.1', ' with:', ` name: publish-linux-outputs-${githubExpression('github.run_id')}`, ` path: ${githubExpression('runner.temp')}/linux-platform-outputs`, ' if-no-files-found: error', ' retention-days: 1', ' include-hidden-files: true');
532
+ lines.push('', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ“ค Upload supplemental Linux outputs', " if: steps.version.outputs.mode != 'none'", ' uses: actions/upload-artifact@v7.0.1', ' with:', ` name: publish-linux-outputs-${githubExpression('github.run_id')}`, ` path: ${githubExpression('runner.temp')}/linux-platform-outputs`, ' if-no-files-found: error', ' retention-days: 1', ' include-hidden-files: true');
530
533
  }
531
- lines.push('', ' # --- Cleanup ------------------------------------------------------------', '', ` # Step ${stepNumber}`, ' - name: ๐Ÿงน Cleanup and cache Nix/devenv', ` if: ${githubExpression('always()')}`, ' uses: ./.github/actions/save-nix-devenv', ' with:', ` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`, ` devenv-cache-hit: ${githubExpression('steps.setup.outputs.devenv-cache-hit')}`);
534
+ lines.push('', ' # --- Cleanup ------------------------------------------------------------', '', ` # Step ${stepNumber}`, ' - name: ๐Ÿงน Cleanup and cache Nix/devenv', ' # success() is default; always() still saves GH Nix cache after a red job.', ' if: always()', ' uses: ./.github/actions/save-nix-devenv', ' with:', ` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`, ` devenv-cache-hit: ${githubExpression('steps.setup.outputs.devenv-cache-hit')}`);
532
535
  return lines.join('\n').trimEnd();
533
536
  }
534
537
  function renderMacosPlatformSteps(options) {
@@ -554,7 +557,7 @@ function renderMacosPlatformSteps(options) {
554
557
  if (isSmoothBricksCodebasePackageName(options.repoName)) {
555
558
  lines.push('', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ—๏ธ Build self-hosted smoo', ' # SmoothBricks self-hosts smoo from source for release commands.', ' run: nx build cli');
556
559
  }
557
- lines.push('', ` # Step ${stepNumber++}`, ' - name: ๐ŸŽ Build selected macOS and iOS release outputs', ' run:', ` smoo release build-platform-outputs --bump "${githubExpression('inputs.bump')}" --ref "${githubExpression('github.sha')}" --targets "${MACOS_PLATFORM_TARGET_GLOBS.join(',')}" --output`, ` "${githubExpression('runner.temp')}/macos-platform-outputs"`, '', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ“ค Upload macOS platform outputs', ' uses: actions/upload-artifact@v7.0.1', ' with:', ` name: publish-macos-outputs-${githubExpression('github.run_id')}`, ` path: ${githubExpression('runner.temp')}/macos-platform-outputs`, ' if-no-files-found: error', ' retention-days: 1', ' include-hidden-files: true', '', ' # --- Cleanup ------------------------------------------------------------', '', ` # Step ${stepNumber}`, ' - name: ๐Ÿงน Cleanup and cache Nix/devenv', ` if: ${githubExpression('always()')}`, ' uses: ./.github/actions/save-nix-devenv', ' with:', ` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`, ` devenv-cache-hit: ${githubExpression('steps.setup.outputs.devenv-cache-hit')}`);
560
+ lines.push('', ` # Step ${stepNumber++}`, ' - name: ๐ŸŽ Build selected macOS and iOS release outputs', ' run:', ` smoo release build-platform-outputs --bump "${githubExpression('inputs.bump')}" --ref "${githubExpression('github.sha')}" --targets "${MACOS_PLATFORM_TARGET_GLOBS.join(',')}" --output`, ` "${githubExpression('runner.temp')}/macos-platform-outputs"`, '', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ“ค Upload macOS platform outputs', ' uses: actions/upload-artifact@v7.0.1', ' with:', ` name: publish-macos-outputs-${githubExpression('github.run_id')}`, ` path: ${githubExpression('runner.temp')}/macos-platform-outputs`, ' if-no-files-found: error', ' retention-days: 1', ' include-hidden-files: true', '', ' # --- Cleanup ------------------------------------------------------------', '', ` # Step ${stepNumber}`, ' - name: ๐Ÿงน Cleanup and cache Nix/devenv', ' # success() is default; always() still saves GH Nix cache after a red job.', ' if: always()', ' uses: ./.github/actions/save-nix-devenv', ' with:', ` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`, ` devenv-cache-hit: ${githubExpression('steps.setup.outputs.devenv-cache-hit')}`);
558
561
  return lines.join('\n').trimEnd();
559
562
  }
560
563
  function renderFinalLinuxPublishSteps(options) {
@@ -597,15 +600,15 @@ function renderFinalLinuxPublishSteps(options) {
597
600
  lines.push('', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ—๏ธ Build self-hosted smoo', ' # SmoothBricks self-hosts smoo from source for release commands.', ' run: nx build cli');
598
601
  }
599
602
  lines.push('', ' # --- Repair -------------------------------------------------------------', '', ` # Step ${stepNumber++}`, ' - name: ๐Ÿงฏ Repair pending releases', ' run:', ` smoo release repair-pending --ref "${githubExpression('github.sha')}" --platform-outputs "${githubExpression('runner.temp')}/publish-artifacts/publish-macos-outputs-${githubExpression('github.run_id')}/repairs" --dry-run`, ` "${githubExpression('inputs.dry_run')}"`, '', ` # Step ${stepNumber++}`, ' - name: โ™ป๏ธ Restore validated release state', ' run:', ` git fetch "${githubExpression('runner.temp')}/publish-artifacts/publish-release-state-${githubExpression('github.run_id')}/release-state.bundle" HEAD --tags && git reset`, ` --hard "$(cat "${githubExpression('runner.temp')}/publish-artifacts/publish-release-state-${githubExpression('github.run_id')}/release-head")"`);
600
- lines.push('', ' # --- Validation ---------------------------------------------------------', '', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ“ฆ Apply verified Linux outputs', ` if: ${githubExpression(`${mode} != 'none'`)}`, ' run:', ` smoo github-ci apply-outputs --source-sha "${githubExpression('needs.linux-release-candidate.outputs.release-sha')}"`, ` "${githubExpression('runner.temp')}/publish-artifacts/publish-release-outputs-${githubExpression('github.run_id')}"`);
603
+ lines.push('', ' # --- Validation ---------------------------------------------------------', '', ` # Step ${stepNumber++}`, ' - name: ๐Ÿ“ฆ Apply verified Linux outputs', ` if: ${mode} != 'none'`, ' run:', ` smoo github-ci apply-outputs --source-sha "${githubExpression('needs.linux-release-candidate.outputs.release-sha')}"`, ` "${githubExpression('runner.temp')}/publish-artifacts/publish-release-outputs-${githubExpression('github.run_id')}"`);
601
604
  if (hasLinuxPlatformTargets(options)) {
602
605
  lines.push(` "${githubExpression('runner.temp')}/publish-artifacts/publish-linux-outputs-${githubExpression('github.run_id')}"`);
603
606
  }
604
- lines.push('', ` # Step ${stepNumber++}`, ' - name: ๐ŸŽ Apply verified macOS outputs', ` if: ${githubExpression(`${mode} != 'none'`)}`, ' run:', ` smoo github-ci apply-outputs --source-sha "${githubExpression('github.sha')}"`, ` "${githubExpression('runner.temp')}/publish-artifacts/publish-macos-outputs-${githubExpression('github.run_id')}/current"`, '', ` # Step ${stepNumber++}`, ` - name: โœ… Validate restored release (${githubExpression(mode)})`, ` if: ${githubExpression(`${mode} != 'none'`)}`, ' run: smoo monorepo validate', '', ' # --- Release ------------------------------------------------------------', '', ` # Step ${stepNumber++}`, ` - name: ๐Ÿ“ฆ Publish release (${githubExpression(mode)})`, ' # smoo packs with Bun, then publishes tarballs with npm. Existing', ' # packages must already exist on npm and use trusted publishing/OIDC.', ' # Missing package names are bootstrapped locally before trust setup.', ` run: smoo release publish --bump "${githubExpression('inputs.bump')}" --dry-run "${githubExpression('inputs.dry_run')}"`);
607
+ lines.push('', ` # Step ${stepNumber++}`, ' - name: ๐ŸŽ Apply verified macOS outputs', ` if: ${mode} != 'none'`, ' run:', ` smoo github-ci apply-outputs --source-sha "${githubExpression('github.sha')}"`, ` "${githubExpression('runner.temp')}/publish-artifacts/publish-macos-outputs-${githubExpression('github.run_id')}/current"`, '', ` # Step ${stepNumber++}`, ` - name: โœ… Validate restored release (${githubExpression(mode)})`, ` if: ${mode} != 'none'`, ' run: smoo monorepo validate', '', ' # --- Release ------------------------------------------------------------', '', ` # Step ${stepNumber++}`, ` - name: ๐Ÿ“ฆ Publish release (${githubExpression(mode)})`, ' # smoo packs with Bun, then publishes tarballs with npm. Existing', ' # packages must already exist on npm and use trusted publishing/OIDC.', ' # Missing package names are bootstrapped locally before trust setup.', ` run: smoo release publish --bump "${githubExpression('inputs.bump')}" --dry-run "${githubExpression('inputs.dry_run')}"`);
605
608
  if (options.deploy === true) {
606
609
  lines.push('', ` # Step ${stepNumber++}`, ' - name: ๐Ÿš€ Deploy production', ' if:', " ${{ needs.linux-release-candidate.outputs.mode != 'none' && inputs.deploy_environment == 'production' &&", " inputs.dry_run != 'true' }}", ...deployEnvLines(options), ' run: smoo github-ci nx-deploy --configuration production --mode run-many --verify --name "Deploy Production"');
607
610
  }
608
- lines.push('', ' # --- Cleanup ------------------------------------------------------------', '', ` # Step ${stepNumber}`, ' - name: ๐Ÿงน Cleanup and cache Nix/devenv', ` if: ${githubExpression('always()')}`, ' uses: ./.github/actions/save-nix-devenv', ' with:', ` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`, ` devenv-cache-hit: ${githubExpression('steps.setup.outputs.devenv-cache-hit')}`);
611
+ lines.push('', ' # --- Cleanup ------------------------------------------------------------', '', ` # Step ${stepNumber}`, ' - name: ๐Ÿงน Cleanup and cache Nix/devenv', ' # success() is default; always() still saves GH Nix cache after a red job.', ' if: always()', ' uses: ./.github/actions/save-nix-devenv', ' with:', ` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`, ` devenv-cache-hit: ${githubExpression('steps.setup.outputs.devenv-cache-hit')}`);
609
612
  return lines.join('\n').trimEnd();
610
613
  }
611
614
  function hasMacosPlatformTargets(options) {
@@ -1,8 +1,13 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
4
  NIX_STORE_NAR="${NIX_STORE_NAR:-/tmp/nix-store.nar}"
5
5
  nix_store_cmd="/nix/var/nix/profiles/default/bin/nix-store"
6
+ DEVENV_FLAKE="${DEVENV_FLAKE:-github:cachix/devenv}"
7
+ # Host CI cache (GARM bind-mount). Prefer env from runner profile.
8
+ # Path comes from the runner (GARM profile). Ephemeral runners leave unset;
9
+ # devenv enterShell defaults to $PWD/.cache/ttsc.
10
+ TTSC_CACHE_DIR="${TTSC_CACHE_DIR:-}"
6
11
  # Resolve from this script's location, not the caller's cwd. GitHub Actions
7
12
  # runs this from tooling/direnv today, but direct cwd-changing helpers are easy
8
13
  # to misuse and break on repeated calls.
@@ -36,12 +41,21 @@ restore_nix_store() {
36
41
  }
37
42
 
38
43
  install_devenv() {
39
- sudo mkdir -p /nix/var/nix/gcroots/ci
40
- if ! command -v devenv >/dev/null 2>&1; then
41
- nix profile add --accept-flake-config nixpkgs#devenv
44
+ # Shared host /nix/store is the package cache. Image may already provide
45
+ # devenv from github:cachix/devenv; otherwise profile-add the same flake
46
+ # (links store paths; re-fetch only when missing).
47
+ if command -v devenv >/dev/null 2>&1; then
48
+ echo "using existing devenv: $(command -v devenv) ($(devenv version 2>/dev/null || true))"
49
+ else
50
+ echo "nix profile add ${DEVENV_FLAKE}"
51
+ nix profile add --accept-flake-config "$DEVENV_FLAKE"
52
+ fi
53
+ if [ -d "$HOME/.nix-profile/bin" ]; then
54
+ echo "$HOME/.nix-profile/bin" >> "${GITHUB_PATH:-/dev/null}"
55
+ fi
56
+ if [ -n "$TTSC_CACHE_DIR" ]; then
57
+ mkdir -p "$TTSC_CACHE_DIR"
42
58
  fi
43
- echo "$HOME/.nix-profile/bin" >> "$GITHUB_PATH"
44
- sudo ln -sf "$HOME/.nix-profile" /nix/var/nix/gcroots/ci/profile
45
59
  }
46
60
 
47
61
  build_devenv_shell() {
@@ -50,7 +64,10 @@ build_devenv_shell() {
50
64
  # independent workflow step shells that run after this composite action.
51
65
  if [ -n "${GITHUB_ENV:-}" ]; then
52
66
  echo "TTSC_TSGO_BINARY=$repo_root/node_modules/@typescript/native/bin/tsc" >> "$GITHUB_ENV"
53
- echo "TTSC_CACHE_DIR=$repo_root/.cache/ttsc" >> "$GITHUB_ENV"
67
+ # Only re-export when the runner (or a prior step) already set the path.
68
+ if [ -n "$TTSC_CACHE_DIR" ]; then
69
+ echo "TTSC_CACHE_DIR=$TTSC_CACHE_DIR" >> "$GITHUB_ENV"
70
+ fi
54
71
  fi
55
72
  # Add repo-local tools only after the shell exists; cleanup steps use an
56
73
  # explicit PATH because failures before this point must still refresh caches.
@@ -1,5 +1,7 @@
1
1
  name: Cache Nix profiles, devenv and direnv
2
- description: Restore cached Nix/devenv state for SmoothBricks GitHub Actions jobs.
2
+ description: >
3
+ Two independent segments: large nix (NAR/profiles, ephemeral runners only) and small devenv (.devenv/.direnv โ€” eval
4
+ SQLite, shell scripts, profile; all runners).
3
5
 
4
6
  inputs:
5
7
  segment:
@@ -19,12 +21,11 @@ outputs:
19
21
  runs:
20
22
  using: composite
21
23
  steps:
22
- # The large cache is the Nix profile plus exported store NAR. Its key tracks
23
- # the expensive shell closure inputs only; tiny direnv/setup script changes
24
- # should not invalidate a 1GB+ store cache.
25
- - name: Restore Nix profiles, devenv and direnv
24
+ # Large segment: Nix profile + exported store NAR. Ephemeral runners only.
25
+ # Key = shell closure inputs; direnv/setup script edits must not bust a multi-GB NAR.
26
+ - name: Restore Nix profiles and store NAR
26
27
  id: restore-nix
27
- if: ${{ inputs.segment == 'nix' && inputs.mode == 'restore' }}
28
+ if: inputs.segment == 'nix' && inputs.mode == 'restore'
28
29
  uses: actions/cache/restore@v5.0.5
29
30
  with:
30
31
  path: |
@@ -39,7 +40,7 @@ runs:
39
40
  ${{ runner.os }}-${{ runner.arch }}-nix-v3-
40
41
 
41
42
  - name: Save Nix profiles and store NAR
42
- if: ${{ inputs.segment == 'nix' && inputs.mode == 'save' }}
43
+ if: inputs.segment == 'nix' && inputs.mode == 'save'
43
44
  uses: actions/cache/save@v5.0.5
44
45
  with:
45
46
  path: |
@@ -51,11 +52,12 @@ runs:
51
52
  ${{ runner.os }}-${{ runner.arch }}-nix-v3-${{ hashFiles('tooling/direnv/devenv.yaml',
52
53
  'tooling/direnv/devenv.nix', 'tooling/direnv/devenv.lock') }}
53
54
 
54
- # .devenv/.direnv are small, but they contain absolute /nix/store pointers.
55
- # Workflows restore this segment only after an exact Nix cache hit.
55
+ # Small segment: devenv eval cache (nix-eval-cache.db), shell-*.sh, profile, .direnv.
56
+ # Own key. Setup restores on every runner; not gated on nix NAR hit. Devenv drops a
57
+ # shell cache row when drv/out are missing (GC) and re-evals/realizes.
56
58
  - name: Restore devenv and direnv state
57
59
  id: restore-devenv
58
- if: ${{ inputs.segment == 'devenv' && inputs.mode == 'restore' }}
60
+ if: inputs.segment == 'devenv' && inputs.mode == 'restore'
59
61
  uses: actions/cache/restore@v5.0.5
60
62
  with:
61
63
  path: |
@@ -69,7 +71,7 @@ runs:
69
71
  ${{ runner.os }}-${{ runner.arch }}-devenv-v3-
70
72
 
71
73
  - name: Save devenv and direnv state
72
- if: ${{ inputs.segment == 'devenv' && inputs.mode == 'save' }}
74
+ if: inputs.segment == 'devenv' && inputs.mode == 'save'
73
75
  uses: actions/cache/save@v5.0.5
74
76
  with:
75
77
  path: |
@@ -1,5 +1,7 @@
1
1
  name: Save Nix/devenv caches
2
- description: Cleanup the Nix store and explicitly save Nix/devenv cache segments.
2
+ description: >
3
+ Save cache segments after the job. Nix store NAR stays ephemeral-only (host uses shared /nix/store). Devenv/direnv
4
+ eval-cache segment saves on every runner when setup missed and build-shell produced nix-eval-cache.db.
3
5
 
4
6
  inputs:
5
7
  nix-cache-hit:
@@ -14,8 +16,19 @@ inputs:
14
16
  runs:
15
17
  using: composite
16
18
  steps:
19
+ - name: ๐Ÿ”Ž Runner kind
20
+ id: runner-kind
21
+ shell: bash
22
+ run: |
23
+ if [ "${NIX_REMOTE:-}" = daemon ] && [ -d /var/cache/ci ]; then
24
+ echo "host=true" >> "$GITHUB_OUTPUT"
25
+ else
26
+ echo "host=false" >> "$GITHUB_OUTPUT"
27
+ fi
28
+
17
29
  - name: ๐Ÿงน Prepare Nix/devenv cache save
18
30
  id: cleanup-cache
31
+ if: steps.runner-kind.outputs.host != 'true'
19
32
  shell: bash
20
33
  working-directory: tooling/direnv
21
34
  env:
@@ -33,14 +46,32 @@ runs:
33
46
  smoo github-ci cleanup-cache
34
47
 
35
48
  - name: ๐ŸงŠ Save Nix profiles and store NAR
36
- if: ${{ steps.cleanup-cache.outputs.cache-ready == 'true' && inputs.nix-cache-hit != 'true' }}
49
+ if:
50
+ ${{ steps.runner-kind.outputs.host != 'true' && steps.cleanup-cache.outputs.cache-ready == 'true' &&
51
+ inputs.nix-cache-hit != 'true' }}
37
52
  uses: ./.github/actions/cache-nix-devenv
38
53
  with:
39
54
  segment: nix
40
55
  mode: save
41
56
 
57
+ # Devenv segment is independent of nix NAR readiness and host vs ephemeral.
58
+ # Skip when setup never finished (no eval DB) so if: always() cannot poison
59
+ # the next restore with an empty/partial key.
60
+ - name: ๐Ÿ”Ž Devenv save ready
61
+ id: devenv-save
62
+ if: inputs.devenv-cache-hit != 'true'
63
+ shell: bash
64
+ run: |
65
+ db="${GITHUB_WORKSPACE}/tooling/direnv/.devenv/nix-eval-cache.db"
66
+ if [[ -f "$db" ]]; then
67
+ echo "ready=true" >> "$GITHUB_OUTPUT"
68
+ else
69
+ echo "ready=false" >> "$GITHUB_OUTPUT"
70
+ echo "skip devenv save: no nix-eval-cache.db"
71
+ fi
72
+
42
73
  - name: ๐ŸงŠ Save devenv and direnv
43
- if: ${{ steps.cleanup-cache.outputs.cache-ready == 'true' && inputs.devenv-cache-hit != 'true' }}
74
+ if: ${{ inputs.devenv-cache-hit != 'true' && steps.devenv-save.outputs.ready == 'true' }}
44
75
  uses: ./.github/actions/cache-nix-devenv
45
76
  with:
46
77
  segment: devenv
@@ -1,44 +1,71 @@
1
1
  name: Setup Nix/devenv
2
- description: Restore caches and build the SmoothBricks devenv shell after checkout.
2
+ description: >
3
+ Build the SmoothBricks devenv shell after checkout. Restores the small devenv/direnv actions/cache segment on every
4
+ runner (eval SQLite + shell scripts). On GARM/NixOS (NIX_REMOTE=daemon + /var/cache/ci) skips Nix install and the
5
+ large store NAR (host store). On ubuntu/macOS installs Nix and restores the NAR/profile cache.
3
6
 
4
7
  outputs:
5
8
  nix-cache-hit:
6
9
  description: Whether the Nix profile/store cache restored from the exact primary key.
7
- value: ${{ steps.nix-cache.outputs.cache-hit }}
10
+ value: ${{ steps.nix-cache.outputs.cache-hit || 'false' }}
8
11
  devenv-cache-hit:
9
12
  description: Whether the devenv/direnv cache restored from the exact primary key.
10
- value: ${{ steps.devenv-cache.outputs.cache-hit }}
13
+ value: ${{ steps.devenv-cache.outputs.cache-hit || 'false' }}
14
+ host-runner:
15
+ description: Whether this job is on a host-nix runner (GARM) with shared store/caches.
16
+ value: ${{ steps.runner-kind.outputs.host }}
11
17
 
12
18
  runs:
13
19
  using: composite
14
20
  steps:
15
21
  # This action is intentionally post-checkout only. Repo-local actions do not
16
22
  # exist before actions/checkout has populated the working tree.
23
+
24
+ # Runner image/profile owns paths (NIX_REMOTE, /var/cache/ci/*). Detect here
25
+ # so every workflow can `uses: setup-devenv` without forking step graphs.
26
+ - name: ๐Ÿ”Ž Runner kind
27
+ id: runner-kind
28
+ shell: bash
29
+ run: |
30
+ if [ "${NIX_REMOTE:-}" = daemon ] && [ -d /var/cache/ci ]; then
31
+ echo "host=true" >> "$GITHUB_OUTPUT"
32
+ echo "host-nix runner (shared store + /var/cache/ci)"
33
+ else
34
+ echo "host=false" >> "$GITHUB_OUTPUT"
35
+ echo "ephemeral runner (install Nix + GH caches)"
36
+ fi
37
+
38
+ # Devenv segment (own key): tooling/direnv/.devenv + .direnv โ€” includes
39
+ # nix-eval-cache.db. Independent of the nix NAR; restore on host and ephemeral
40
+ # whether or not the store cache hit. Missing store paths are fine: devenv
41
+ # verifies shell drv/out exist on eval-cache hit and invalidates + re-evals
42
+ # / realizes when GC removed them. Packages still come from host store, NAR,
43
+ # or Cachix โ€” this blob only skips evaluation.
44
+ - name: ๐ŸงŠ Cache devenv and direnv
45
+ id: devenv-cache
46
+ uses: ./.github/actions/cache-nix-devenv
47
+ with:
48
+ segment: devenv
49
+
17
50
  - name: โ„๏ธ Install Nix
51
+ if: steps.runner-kind.outputs.host != 'true'
18
52
  uses: DeterminateSystems/determinate-nix-action@v3
19
53
 
20
54
  - name: ๐ŸงŠ Cache Nix profiles and store NAR
21
55
  id: nix-cache
56
+ if: steps.runner-kind.outputs.host != 'true'
22
57
  uses: ./.github/actions/cache-nix-devenv
23
58
  with:
24
59
  segment: nix
25
60
 
26
61
  - name: โ™ป๏ธ Restore Nix store from NAR
62
+ if: steps.runner-kind.outputs.host != 'true'
27
63
  shell: bash
28
64
  working-directory: tooling/direnv
29
65
  run: ./github-actions-bootstrap.sh restore-store
30
66
 
31
- # Restore direnv state only when the Nix cache key is exact. Partial Nix
32
- # restores can contain a NAR for a different shell closure, and .direnv
33
- # points directly at derivations inside that closure.
34
- - name: ๐ŸงŠ Cache devenv and direnv
35
- id: devenv-cache
36
- if: ${{ steps.nix-cache.outputs.cache-hit == 'true' }}
37
- uses: ./.github/actions/cache-nix-devenv
38
- with:
39
- segment: devenv
40
-
41
67
  - name: โšก Enable Cachix
68
+ if: steps.runner-kind.outputs.host != 'true'
42
69
  uses: cachix/cachix-action@v17
43
70
  with:
44
71
  name: devenv
@@ -48,12 +75,14 @@ runs:
48
75
  working-directory: tooling/direnv
49
76
  run: ./github-actions-bootstrap.sh install-devenv
50
77
 
78
+ # node_modules + ttsc plugin binaries: GH Actions cache on ephemeral runners.
79
+ # Host runners keep those under the /var/cache/ci bind (bun / ttsc env).
51
80
  - name: ๐Ÿ“ฆ Cache node_modules
81
+ if: steps.runner-kind.outputs.host != 'true'
52
82
  uses: ./.github/actions/cache-node-modules
53
83
 
54
- # Restores compiled ttsc native plugins into .cache/ttsc/plugins. TTSC_CACHE_DIR
55
- # is set by devenv enterShell (+ bootstrap GITHUB_ENV for later steps).
56
84
  - name: ๐ŸงŠ Cache ttsc plugins
85
+ if: steps.runner-kind.outputs.host != 'true'
57
86
  uses: ./.github/actions/cache-ttsc-plugins
58
87
 
59
88
  - name: ๐Ÿš Build devenv shell
@@ -9,10 +9,17 @@
9
9
  # change is upstream code that deserves a human look before it lands. Re-runs
10
10
  # refresh the same branch in place.
11
11
  #
12
- # NOTE: managed files include .github/workflows/*, and the default GITHUB_TOKEN
13
- # is server-side forbidden to push workflow-file changes. Configure a GitHub App
14
- # or PAT token with contents:write + workflows:write as MANAGED_FILES_TOKEN;
15
- # without it, runs that touch workflow files fail at push with a clear error.
12
+ # REQUIRED SECRET: MANAGED_FILES_TOKEN
13
+ # Managed files include .github/workflows/*. The default GITHUB_TOKEN is
14
+ # server-side forbidden to create/update workflow files, even with
15
+ # contents:write. Configure a GitHub App installation token or classic/fine-
16
+ # grained PAT with:
17
+ # - contents: write
18
+ # - pull-requests: write
19
+ # - workflows: write
20
+ # as repository secret MANAGED_FILES_TOKEN. Without it, any update that
21
+ # touches workflow YAML fails at push (this is the usual "managed files
22
+ # workflow isn't doing anything" failure mode).
16
23
  name: Managed files
17
24
 
18
25
  on:
@@ -41,6 +48,7 @@ jobs:
41
48
  filter: blob:none
42
49
  fetch-depth: 0
43
50
  persist-credentials: true
51
+ # Prefer MANAGED_FILES_TOKEN so push can update workflow files.
44
52
  token: ${{ secrets.MANAGED_FILES_TOKEN || github.token }}
45
53
 
46
54
  - name: ๐Ÿงฑ Setup Nix/devenv
@@ -53,27 +61,71 @@ jobs:
53
61
  - name: ๐Ÿ“ค Push review branch and open PR
54
62
  env:
55
63
  GH_TOKEN: ${{ secrets.MANAGED_FILES_TOKEN || github.token }}
64
+ # secrets.* is empty when unset โ€” used to fail before a useless push.
65
+ MANAGED_FILES_TOKEN_CONFIGURED: ${{ secrets.MANAGED_FILES_TOKEN != '' }}
56
66
  run: |
67
+ set -euo pipefail
57
68
  cd "$(git rev-parse --show-toplevel)"
69
+
58
70
  if [ -z "$(git status --porcelain)" ]; then
59
71
  echo "Managed files are in sync; nothing to do."
60
72
  exit 0
61
73
  fi
74
+
75
+ echo "Pending managed-file changes:"
76
+ git status --porcelain
77
+
78
+ workflow_changes="$(git status --porcelain -- .github/workflows || true)"
79
+ if [ -n "$workflow_changes" ] && [ "${MANAGED_FILES_TOKEN_CONFIGURED}" != "true" ]; then
80
+ {
81
+ echo "::error title=MANAGED_FILES_TOKEN required::Managed update touches .github/workflows/* but repository secret MANAGED_FILES_TOKEN is not set."
82
+ echo ""
83
+ echo "GitHub refuses pushes that create/update workflow files when the credentials are the default GITHUB_TOKEN / a GitHub App without the workflows permission:"
84
+ echo " refusing to allow a GitHub App to create or update workflow ... without \`workflows\` permission"
85
+ echo ""
86
+ echo "Fix:"
87
+ echo " 1. Create a GitHub App (or PAT) with contents:write, pull-requests:write, workflows:write"
88
+ echo " 2. Install it on this repo"
89
+ echo " 3. Store an installation token or PAT as repository secret MANAGED_FILES_TOKEN"
90
+ echo " 4. Re-run this workflow"
91
+ echo ""
92
+ echo "Workflow files that would be updated:"
93
+ echo "$workflow_changes"
94
+ } >&2
95
+ exit 1
96
+ fi
97
+
62
98
  git config user.name "github-actions[bot]"
63
99
  git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
64
100
  git switch -C chore/managed-files
65
101
  git add -A
66
102
  git commit -m "chore: regenerate smoo-managed files"
67
- git push --force origin chore/managed-files
68
- gh pr create \
69
- --head chore/managed-files \
70
- --base "${GITHUB_REF_NAME}" \
71
- --title "chore: regenerate smoo-managed files" \
72
- --body "Automated refresh of smoo-managed files after drift (usually a @smoothbricks/cli pin bump). One persistent PR; re-runs force-push this branch in place. Review the template changes - do NOT enable auto-merge." \
73
- 2>/dev/null || echo "PR already open; branch refreshed in place."
103
+
104
+ if ! git push --force origin chore/managed-files 2> /tmp/managed-files-push.err; then
105
+ cat /tmp/managed-files-push.err >&2
106
+ if grep -q 'workflows' /tmp/managed-files-push.err; then
107
+ {
108
+ echo ""
109
+ echo "::error title=workflows permission missing::Push rejected because the token cannot update GitHub Actions workflow files."
110
+ echo "Set repository secret MANAGED_FILES_TOKEN to a GitHub App/PAT with contents:write + pull-requests:write + workflows:write, then re-run."
111
+ } >&2
112
+ fi
113
+ exit 1
114
+ fi
115
+
116
+ if ! gh pr view chore/managed-files --json number --jq .number >/dev/null 2>&1; then
117
+ gh pr create \
118
+ --head chore/managed-files \
119
+ --base "${GITHUB_REF_NAME}" \
120
+ --title "chore: regenerate smoo-managed files" \
121
+ --body "Automated refresh of smoo-managed files after drift (usually a \`@smoothbricks/cli\` pin bump). One persistent PR; re-runs force-push this branch in place. Review the template changes โ€” do NOT enable auto-merge."
122
+ else
123
+ echo "PR already open for chore/managed-files; branch refreshed in place."
124
+ fi
74
125
 
75
126
  - name: ๐Ÿงน Cleanup and cache Nix/devenv
76
- if: ${{ always() }}
127
+ # success() is default; always() still saves GH Nix cache after a red job.
128
+ if: always()
77
129
  uses: ./.github/actions/save-nix-devenv
78
130
  with:
79
131
  nix-cache-hit: ${{ steps.setup.outputs.nix-cache-hit }}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smoothbricks/cli",
3
- "version": "0.10.5",
3
+ "version": "0.10.6",
4
4
  "type": "module",
5
5
  "description": "SmoothBricks monorepo automation CLI",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -66,11 +66,11 @@ function buildProgram(): Command {
66
66
  .option('--warn', 'report drift as warnings (GitHub annotations) instead of failing')
67
67
  .action(async (options: { warn?: boolean }) => {
68
68
  const { checkManagedFiles } = await import('./monorepo/index.js');
69
- checkManagedFiles(await findRepoRoot(), { warn: options.warn });
69
+ await checkManagedFiles(await findRepoRoot(), { warn: options.warn });
70
70
  });
71
71
  monorepo.command('diff').action(async () => {
72
72
  const { diffManagedFiles } = await import('./monorepo/index.js');
73
- diffManagedFiles(await findRepoRoot());
73
+ await diffManagedFiles(await findRepoRoot());
74
74
  });
75
75
  monorepo
76
76
  .command('validate-commit-msg <commitMsgFile>')
@@ -38,7 +38,7 @@ describe('GitHub CI Nx target expansion', () => {
38
38
  ['test', ['api', 'desktop']],
39
39
  ]);
40
40
  expect(expanded.runs.map((run) => nxRunManyArgs(run))).toEqual([
41
- ['run-many', '-t', 'test', '--projects=api,desktop', '--parallel=5'],
41
+ ['run-many', '-t', 'test', '--projects=api,desktop', '--parallel=100%'],
42
42
  ]);
43
43
  });
44
44
 
@@ -65,7 +65,7 @@ describe('GitHub CI Nx target expansion', () => {
65
65
 
66
66
  expect(expanded.unmatchedGlobs).toEqual([]);
67
67
  expect(expanded.runs.map((run) => nxRunManyArgs(run))).toEqual([
68
- ['run-many', '-t', 'missing', '--projects=api', '--parallel=5'],
68
+ ['run-many', '-t', 'missing', '--projects=api', '--parallel=100%'],
69
69
  ]);
70
70
  });
71
71
 
@@ -114,7 +114,7 @@ describe('GitHub CI Nx target expansion', () => {
114
114
  '-t',
115
115
  'test',
116
116
  '--exclude=tag:ci:skip:test',
117
- '--parallel=5',
117
+ '--parallel=100%',
118
118
  ]);
119
119
  expect(nxRunManyArgs({ target: 'test', projects: projects.slice(0, 1) })).not.toContain(
120
120
  '--exclude=tag:ci:skip:test',
@@ -27,6 +27,9 @@ const parseNxProjectDeployConfigurations = typia.json.createIsParse<NxProjectDep
27
27
 
28
28
  type NxSmartMode = 'auto' | 'affected' | 'run-many';
29
29
 
30
+ /** Nx workers = host cores (CI runners sized for full machine use). */
31
+ const NX_PARALLEL = '100%';
32
+
30
33
  export async function cleanupGithubCiCache(root: string): Promise<void> {
31
34
  const githubOutput = process.env.GITHUB_OUTPUT;
32
35
  const markCacheReady = async (ready: boolean): Promise<void> => {
@@ -139,7 +142,7 @@ export function nxSmartArgs(target: string, mode: 'affected' | 'run-many', confi
139
142
  if (configuration) {
140
143
  args.push(`--configuration=${configuration}`);
141
144
  }
142
- args.push(`--exclude=tag:ci:skip:${target}`, '--parallel=5');
145
+ args.push(`--exclude=tag:ci:skip:${target}`, `--parallel=${NX_PARALLEL}`);
143
146
  return args;
144
147
  }
145
148
 
@@ -262,7 +265,7 @@ export function nxRunManyArgs(run: NxTargetRun, configuration?: string): string[
262
265
  if (configuration) {
263
266
  nxArgs.push(`--configuration=${configuration}`);
264
267
  }
265
- nxArgs.push('--parallel=5');
268
+ nxArgs.push(`--parallel=${NX_PARALLEL}`);
266
269
  return nxArgs;
267
270
  }
268
271
 
@@ -343,7 +346,7 @@ export async function githubCiNxDeploy(
343
346
  const projectList = projects.join(',');
344
347
  const targets = options.verify === true ? ['build', 'lint', 'test', 'deploy'] : ['deploy'];
345
348
  for (const target of targets) {
346
- const nxArgs = ['run-many', '-t', target, `--projects=${projectList}`, '--parallel=5'];
349
+ const nxArgs = ['run-many', '-t', target, `--projects=${projectList}`, `--parallel=${NX_PARALLEL}`];
347
350
  if (target === 'deploy') {
348
351
  nxArgs.push(`--configuration=${options.configuration}`);
349
352
  }