@smoothbricks/cli 0.10.3 → 0.10.4

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 (111) hide show
  1. package/README.md +9 -13
  2. package/dist/bun/preload.d.ts +13 -0
  3. package/dist/bun/preload.d.ts.map +1 -0
  4. package/dist/bun/preload.js +12 -0
  5. package/dist/cli.js +12 -4
  6. package/dist/generate/index.d.ts.map +1 -1
  7. package/dist/generate/index.js +0 -6
  8. package/dist/github-ci/index.d.ts.map +1 -1
  9. package/dist/github-ci/index.js +52 -16
  10. package/dist/github-ci/outputs.d.ts.map +1 -1
  11. package/dist/github-ci/outputs.js +1 -2
  12. package/dist/lib/cli-package.js +2 -2
  13. package/dist/lib/json.d.ts +118 -11
  14. package/dist/lib/json.d.ts.map +1 -1
  15. package/dist/lib/json.js +317 -29
  16. package/dist/lib/workspace.d.ts +7 -14
  17. package/dist/lib/workspace.d.ts.map +1 -1
  18. package/dist/lib/workspace.js +76 -82
  19. package/dist/monorepo/ci-workflow.d.ts +2 -0
  20. package/dist/monorepo/ci-workflow.d.ts.map +1 -1
  21. package/dist/monorepo/ci-workflow.js +27 -1
  22. package/dist/monorepo/git-config.js +2 -2
  23. package/dist/monorepo/index.d.ts +3 -1
  24. package/dist/monorepo/index.d.ts.map +1 -1
  25. package/dist/monorepo/index.js +6 -2
  26. package/dist/monorepo/lockfile.d.ts +13 -1
  27. package/dist/monorepo/lockfile.d.ts.map +1 -1
  28. package/dist/monorepo/lockfile.js +33 -21
  29. package/dist/monorepo/managed-files.d.ts +11 -0
  30. package/dist/monorepo/managed-files.d.ts.map +1 -1
  31. package/dist/monorepo/managed-files.js +52 -32
  32. package/dist/monorepo/package-policy.d.ts.map +1 -1
  33. package/dist/monorepo/package-policy.js +52 -45
  34. package/dist/monorepo/packed-manifest.d.ts +5 -2
  35. package/dist/monorepo/packed-manifest.d.ts.map +1 -1
  36. package/dist/monorepo/packed-manifest.js +16 -47
  37. package/dist/monorepo/packed-package.d.ts.map +1 -1
  38. package/dist/monorepo/packed-package.js +28 -16
  39. package/dist/monorepo/packs/index.d.ts.map +1 -1
  40. package/dist/monorepo/packs/index.js +13 -4
  41. package/dist/monorepo/publish-workflow.js +3 -1
  42. package/dist/monorepo/runtime.d.ts +15 -0
  43. package/dist/monorepo/runtime.d.ts.map +1 -1
  44. package/dist/monorepo/runtime.js +66 -12
  45. package/dist/monorepo/tool-validation.d.ts.map +1 -1
  46. package/dist/monorepo/tool-validation.js +32 -23
  47. package/dist/monorepo/wrangler.d.ts.map +1 -1
  48. package/dist/monorepo/wrangler.js +13 -13
  49. package/dist/nx/index.d.ts +9 -7
  50. package/dist/nx/index.d.ts.map +1 -1
  51. package/dist/nx/index.js +48 -31
  52. package/dist/pr/index.d.ts.map +1 -1
  53. package/dist/pr/index.js +28 -32
  54. package/dist/release/candidates.d.ts +3 -2
  55. package/dist/release/candidates.d.ts.map +1 -1
  56. package/dist/release/candidates.js +4 -5
  57. package/dist/release/index.d.ts +1 -0
  58. package/dist/release/index.d.ts.map +1 -1
  59. package/dist/release/index.js +82 -59
  60. package/dist/wrangler/prepare-env.d.ts +0 -1
  61. package/dist/wrangler/prepare-env.d.ts.map +1 -1
  62. package/dist/wrangler/prepare-env.js +100 -51
  63. package/dist/wrangler/scaffold.d.ts.map +1 -1
  64. package/dist/wrangler/scaffold.js +12 -12
  65. package/managed/raw/git-format-staged.yml +19 -1
  66. package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +2 -1
  67. package/managed/raw/tooling/direnv/merge-newer-pins.sh +3 -3
  68. package/managed/raw/tooling/direnv/setup-environment.ts +150 -19
  69. package/managed/raw/tooling/git-hooks/pre-commit.sh +0 -6
  70. package/managed/templates/github/actions/cache-node-modules/action.yml +5 -2
  71. package/managed/templates/github/actions/cache-ttsc-plugins/action.yml +19 -0
  72. package/managed/templates/github/actions/setup-devenv/action.yml +5 -0
  73. package/managed/templates/github/workflows/managed-files.yml +80 -0
  74. package/package.json +15 -4
  75. package/src/bun/preload.ts +12 -0
  76. package/src/cli.ts +19 -7
  77. package/src/generate/index.ts +0 -6
  78. package/src/github-ci/index.ts +49 -18
  79. package/src/github-ci/outputs.ts +1 -2
  80. package/src/lib/cli-package.ts +2 -2
  81. package/src/lib/json.ts +291 -32
  82. package/src/lib/workspace.ts +88 -100
  83. package/src/monorepo/__tests__/ci-workflow.test.ts +3 -3
  84. package/src/monorepo/ci-workflow.ts +27 -1
  85. package/src/monorepo/git-config.ts +2 -2
  86. package/src/monorepo/index.ts +6 -2
  87. package/src/monorepo/lockfile.test.ts +35 -36
  88. package/src/monorepo/lockfile.ts +48 -24
  89. package/src/monorepo/managed-files.test.ts +1 -0
  90. package/src/monorepo/managed-files.ts +57 -38
  91. package/src/monorepo/package-policy.test.ts +20 -50
  92. package/src/monorepo/package-policy.ts +76 -59
  93. package/src/monorepo/packed-manifest.ts +18 -52
  94. package/src/monorepo/packed-package.ts +43 -22
  95. package/src/monorepo/packs/index.ts +13 -4
  96. package/src/monorepo/publish-workflow.ts +3 -1
  97. package/src/monorepo/runtime.test.ts +42 -3
  98. package/src/monorepo/runtime.ts +90 -12
  99. package/src/monorepo/tool-validation.ts +35 -25
  100. package/src/monorepo/wrangler.test.ts +11 -15
  101. package/src/monorepo/wrangler.ts +15 -15
  102. package/src/nx/index.test.ts +9 -5
  103. package/src/nx/index.ts +69 -42
  104. package/src/pr/index.ts +42 -36
  105. package/src/release/__tests__/candidates.test.ts +5 -4
  106. package/src/release/__tests__/trust-publisher.test.ts +7 -0
  107. package/src/release/candidates.ts +10 -17
  108. package/src/release/index.ts +104 -73
  109. package/src/wrangler/prepare-env.ts +75 -54
  110. package/src/wrangler/scaffold.test.ts +9 -12
  111. package/src/wrangler/scaffold.ts +13 -13
@@ -51,6 +51,11 @@ runs:
51
51
  - name: 📦 Cache node_modules
52
52
  uses: ./.github/actions/cache-node-modules
53
53
 
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
+ - name: 🧊 Cache ttsc plugins
57
+ uses: ./.github/actions/cache-ttsc-plugins
58
+
54
59
  - name: 🐚 Build devenv shell
55
60
  shell: bash
56
61
  working-directory: tooling/direnv
@@ -0,0 +1,80 @@
1
+ # Managed by @smoothbricks/cli (smoo monorepo update) - do not edit by hand.
2
+ #
3
+ # Self-healing loop for smoo-managed files: ci.yml runs the authoritative
4
+ # `smoo monorepo check` on every default-branch push (devenv is already up
5
+ # there, so the check is ~free) and dispatches THIS workflow only when real
6
+ # drift exists - typically after a @smoothbricks/cli pin bump. It runs
7
+ # `smoo monorepo update` and pushes the result to ONE persistent review branch
8
+ # (chore/managed-files) with ONE PR. The PR is NEVER auto-merged: a template
9
+ # change is upstream code that deserves a human look before it lands. Re-runs
10
+ # refresh the same branch in place.
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.
16
+ name: Managed files
17
+
18
+ on:
19
+ workflow_dispatch:
20
+
21
+ permissions:
22
+ contents: write
23
+ pull-requests: write
24
+
25
+ concurrency:
26
+ group: managed-files
27
+ cancel-in-progress: true
28
+
29
+ defaults:
30
+ run:
31
+ working-directory: tooling/direnv
32
+
33
+ jobs:
34
+ update:
35
+ runs-on: ubuntu-latest
36
+ timeout-minutes: 30
37
+ steps:
38
+ - name: 📥 Checkout
39
+ uses: actions/checkout@v6.0.2
40
+ with:
41
+ filter: blob:none
42
+ fetch-depth: 0
43
+ persist-credentials: true
44
+ token: ${{ secrets.MANAGED_FILES_TOKEN || github.token }}
45
+
46
+ - name: 🧱 Setup Nix/devenv
47
+ id: setup
48
+ uses: ./.github/actions/setup-devenv
49
+
50
+ - name: 🔄 Regenerate managed files
51
+ run: smoo monorepo update
52
+
53
+ - name: 📤 Push review branch and open PR
54
+ env:
55
+ GH_TOKEN: ${{ secrets.MANAGED_FILES_TOKEN || github.token }}
56
+ run: |
57
+ cd "$(git rev-parse --show-toplevel)"
58
+ if [ -z "$(git status --porcelain)" ]; then
59
+ echo "Managed files are in sync; nothing to do."
60
+ exit 0
61
+ fi
62
+ git config user.name "github-actions[bot]"
63
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
64
+ git switch -C chore/managed-files
65
+ git add -A
66
+ 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."
74
+
75
+ - name: 🧹 Cleanup and cache Nix/devenv
76
+ if: ${{ always() }}
77
+ uses: ./.github/actions/save-nix-devenv
78
+ with:
79
+ nix-cache-hit: ${{ steps.setup.outputs.nix-cache-hit }}
80
+ devenv-cache-hit: ${{ steps.setup.outputs.devenv-cache-hit }}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smoothbricks/cli",
3
- "version": "0.10.3",
3
+ "version": "0.10.4",
4
4
  "type": "module",
5
5
  "description": "SmoothBricks monorepo automation CLI",
6
6
  "bin": {
@@ -20,6 +20,13 @@
20
20
  "import": "./dist/index.js",
21
21
  "default": "./dist/index.js"
22
22
  },
23
+ "./bun/preload": {
24
+ "types": "./dist/bun/preload.d.ts",
25
+ "bun": "./src/bun/preload.ts",
26
+ "development": "./src/bun/preload.ts",
27
+ "import": "./dist/bun/preload.js",
28
+ "default": "./dist/bun/preload.js"
29
+ },
23
30
  "./monorepo/runtime": {
24
31
  "types": "./dist/monorepo/runtime.d.ts",
25
32
  "bun": "./src/monorepo/runtime.ts",
@@ -42,6 +49,10 @@
42
49
  "default": "./dist/wrangler/prepare-env.js"
43
50
  }
44
51
  },
52
+ "sideEffects": [
53
+ "./dist/bun/preload.js",
54
+ "./src/bun/preload.ts"
55
+ ],
45
56
  "files": [
46
57
  "README.md",
47
58
  "bin",
@@ -53,8 +64,8 @@
53
64
  "dependencies": {
54
65
  "@arethetypeswrong/core": "^0.18.2",
55
66
  "@prettier/sync": "^0.6.1",
56
- "@smoothbricks/nx-plugin": "0.3.2",
57
- "@smoothbricks/validation": "0.1.3",
67
+ "@smoothbricks/nx-plugin": "0.3.3",
68
+ "@smoothbricks/validation": "0.1.4",
58
69
  "commander": "^14.0.3",
59
70
  "prettier": "^3.6.1",
60
71
  "publint": "^0.3.18",
@@ -62,7 +73,7 @@
62
73
  "sherif": "^1.11.1",
63
74
  "toml-eslint-parser": "^1.0.3",
64
75
  "tslib": "^2.8.1",
65
- "typia": "13.1.1"
76
+ "typia": "13.1.19"
66
77
  },
67
78
  "devDependencies": {
68
79
  "fast-check": "^4.4.0"
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Bun runtime preload for consumers of `@smoothbricks/cli`.
3
+ *
4
+ * Re-exports the Typia/ttsc transform preload from `@smoothbricks/validation`.
5
+ * External repos depend on the CLI package only — they must not need a direct
6
+ * `@smoothbricks/validation` dependency just to run `smoo` against source.
7
+ *
8
+ * Usage:
9
+ * import '@smoothbricks/cli/bun/preload';
10
+ * // or bunfig.toml: preload = ["@smoothbricks/cli/bun/preload"]
11
+ */
12
+ import '@smoothbricks/validation/bun/preload';
package/src/cli.ts CHANGED
@@ -61,10 +61,13 @@ function buildProgram(): Command {
61
61
  const { updateManagedFiles } = await import('./monorepo/index.js');
62
62
  updateManagedFiles(await findRepoRoot());
63
63
  });
64
- monorepo.command('check').action(async () => {
65
- const { checkManagedFiles } = await import('./monorepo/index.js');
66
- checkManagedFiles(await findRepoRoot());
67
- });
64
+ monorepo
65
+ .command('check')
66
+ .option('--warn', 'report drift as warnings (GitHub annotations) instead of failing')
67
+ .action(async (options: { warn?: boolean }) => {
68
+ const { checkManagedFiles } = await import('./monorepo/index.js');
69
+ checkManagedFiles(await findRepoRoot(), { warn: options.warn });
70
+ });
68
71
  monorepo.command('diff').action(async () => {
69
72
  const { diffManagedFiles } = await import('./monorepo/index.js');
70
73
  diffManagedFiles(await findRepoRoot());
@@ -78,10 +81,19 @@ function buildProgram(): Command {
78
81
  });
79
82
  monorepo
80
83
  .command('sync-bun-lockfile-versions')
81
- .option('--stage', 'stage bun.lock when versions were resynced (pre-commit self-heal); quiet when clean')
82
- .action(async (options: { stage?: boolean }) => {
84
+ .option('--stage', 'stage bun.lock when versions were resynced; quiet when clean')
85
+ .option(
86
+ '--mode <mode>',
87
+ 'install: match package.json (CI); publish: map unpublished -next to last stable tag (pre-pack)',
88
+ 'publish',
89
+ )
90
+ .action(async (options: { stage?: boolean; mode?: 'install' | 'publish' }) => {
83
91
  const { syncBunLockfileVersions } = await import('./monorepo/index.js');
84
- syncBunLockfileVersions(await findRepoRoot(), options.stage ? { log: false, stage: true } : {});
92
+ const mode = options.mode === 'install' ? 'install' : 'publish';
93
+ syncBunLockfileVersions(await findRepoRoot(), {
94
+ mode,
95
+ ...(options.stage ? { log: false, stage: true } : {}),
96
+ });
85
97
  });
86
98
  monorepo
87
99
  .command('list-release-packages')
@@ -42,12 +42,6 @@ export const variants: Record<string, GenerateVariant> = {
42
42
  args: (name) => ['--name', name, '--variant', 'ts-lib'],
43
43
  options: [{ flag: '--public', description: 'configure for npm publication' }],
44
44
  },
45
- 'ts-zig': {
46
- generator: 'create-package',
47
- description: 'Create a TypeScript + Zig/WASM hybrid package',
48
- args: (name) => ['--name', name, '--variant', 'ts-zig'],
49
- options: [{ flag: '--public', description: 'configure for npm publication' }],
50
- },
51
45
  'make-public': {
52
46
  generator: 'make-public',
53
47
  description: 'Promote a private package to npm publication',
@@ -1,11 +1,30 @@
1
- import { existsSync } from 'node:fs';
1
+ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { appendFile, mkdtemp, realpath, rename, rm } from 'node:fs/promises';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { $ } from 'bun';
5
+ import typia from 'typia';
6
+ import { parseStringArrayText } from '../lib/json.js';
5
7
  import { decode, run, runStatus } from '../lib/run.js';
6
8
  import { type ProjectTargets, readProjectTargets } from '../nx/index.js';
7
9
  import type { NxTargetRun } from './outputs.js';
8
10
 
11
+ interface GithubActionsEventPayload {
12
+ repository?: {
13
+ default_branch?: string;
14
+ };
15
+ }
16
+
17
+ interface NxProjectDeployConfigurations {
18
+ targets?: {
19
+ deploy?: {
20
+ configurations?: Record<string, unknown>;
21
+ };
22
+ };
23
+ }
24
+
25
+ const parseGithubActionsEvent = typia.json.createIsParse<GithubActionsEventPayload>();
26
+ const parseNxProjectDeployConfigurations = typia.json.createIsParse<NxProjectDeployConfigurations>();
27
+
9
28
  type NxSmartMode = 'auto' | 'affected' | 'run-many';
10
29
 
11
30
  export async function cleanupGithubCiCache(root: string): Promise<void> {
@@ -279,7 +298,7 @@ export async function githubCiApplyOutputs(
279
298
  async function loadOutputBoundary() {
280
299
  if (import.meta.url.endsWith('/src/github-ci/index.ts')) {
281
300
  // The source self-hosting shim has no Typia transform; register it before loading manifest validators.
282
- await import('@smoothbricks/validation/bun/preload');
301
+ await import('@smoothbricks/cli/bun/preload');
283
302
  }
284
303
  // This boundary must stay lazy because the transformed dist and source self-hosting paths initialize Typia differently.
285
304
  return import('./outputs.js');
@@ -341,7 +360,31 @@ function resolveNxSmartMode(mode: NxSmartMode): 'affected' | 'run-many' {
341
360
  if (mode === 'affected' || mode === 'run-many') {
342
361
  return mode;
343
362
  }
344
- return process.env.GITHUB_EVENT_NAME === 'push' && process.env.GITHUB_REF_NAME === 'main' ? 'run-many' : 'affected';
363
+ const defaultBranch = eventDefaultBranch() ?? 'main';
364
+ if (process.env.GITHUB_EVENT_NAME === 'push') {
365
+ return process.env.GITHUB_REF_NAME === defaultBranch ? 'run-many' : 'affected';
366
+ }
367
+ // A PR into a NON-default branch is an integration surface (e.g. a mirror-sync
368
+ // review branch): its base moves outside the default-branch workflow that
369
+ // affected scoping is calibrated against, so under-selection can pass PR CI
370
+ // and only fail after merge. Validate those PRs in full.
371
+ if (process.env.GITHUB_EVENT_NAME === 'pull_request') {
372
+ const base = process.env.GITHUB_BASE_REF;
373
+ return base && base !== defaultBranch ? 'run-many' : 'affected';
374
+ }
375
+ return 'affected';
376
+ }
377
+
378
+ /** The repository default branch from the Actions event payload. */
379
+ function eventDefaultBranch(): string | undefined {
380
+ const eventPath = process.env.GITHUB_EVENT_PATH;
381
+ if (!eventPath) return undefined;
382
+ try {
383
+ const payload = parseGithubActionsEvent(readFileSync(eventPath, 'utf8'));
384
+ return payload?.repository?.default_branch || undefined;
385
+ } catch {
386
+ return undefined;
387
+ }
345
388
  }
346
389
 
347
390
  async function deployProjectsWithConfiguration(
@@ -366,24 +409,12 @@ async function deployProjectsWithConfiguration(
366
409
 
367
410
  async function deployTargetHasConfiguration(root: string, project: string, configuration: string): Promise<boolean> {
368
411
  const result = await $`nx show project ${project} --json`.cwd(root).quiet();
369
- const parsed: unknown = JSON.parse(decode(result.stdout));
370
- const targets = recordValue(parsed)?.targets;
371
- const deploy = recordValue(targets)?.deploy;
372
- const configurations = recordValue(deploy)?.configurations;
373
- return recordValue(configurations)?.[configuration] !== undefined;
412
+ const parsed = parseNxProjectDeployConfigurations(decode(result.stdout));
413
+ return parsed?.targets?.deploy?.configurations?.[configuration] !== undefined;
374
414
  }
375
415
 
376
416
  function nxProjectList(output: string): string[] {
377
- const parsed: unknown = JSON.parse(output);
378
- return Array.isArray(parsed) ? parsed.filter((project): project is string => typeof project === 'string') : [];
379
- }
380
-
381
- function recordValue(value: unknown): Record<string, unknown> | undefined {
382
- return isRecord(value) ? value : undefined;
383
- }
384
-
385
- function isRecord(value: unknown): value is Record<string, unknown> {
386
- return value !== null && typeof value === 'object' && !Array.isArray(value);
417
+ return parseStringArrayText(output) ?? [];
387
418
  }
388
419
 
389
420
  async function createGithubStatus(name: string, step: string): Promise<void> {
@@ -2,7 +2,6 @@ import { createHash } from 'node:crypto';
2
2
  import { createReadStream, type Dirent, type Stats } from 'node:fs';
3
3
  import { copyFile, lstat, mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
4
4
  import { isAbsolute, relative, resolve, sep } from 'node:path';
5
- import { isRecord } from '@smoothbricks/validation';
6
5
  import typia from 'typia';
7
6
  import { type ProjectTargets, readProjectTargets } from '../nx/index.js';
8
7
 
@@ -492,7 +491,7 @@ function assertGitSha(value: string, description: string): void {
492
491
  }
493
492
 
494
493
  function isMissingPathError(error: unknown): boolean {
495
- return isRecord(error) && error.code === 'ENOENT';
494
+ return error instanceof Error && 'code' in error && error.code === 'ENOENT';
496
495
  }
497
496
 
498
497
  function sha256File(path: string): Promise<string> {
@@ -1,12 +1,12 @@
1
1
  import { fileURLToPath } from 'node:url';
2
- import { readJsonObject, stringProperty } from './json.js';
2
+ import { readJsonObject } from './json.js';
3
3
 
4
4
  export const smoothBricksCodebasePackageName = '@smoothbricks/codebase';
5
5
  export const cliPackageVersion = readCliPackageVersion();
6
6
 
7
7
  function readCliPackageVersion(): string {
8
8
  const pkg = readJsonObject(fileURLToPath(new URL('../../package.json', import.meta.url)));
9
- const version = pkg ? stringProperty(pkg, 'version') : null;
9
+ const version = pkg?.version;
10
10
  if (!version) {
11
11
  throw new Error('Unable to read @smoothbricks/cli package version.');
12
12
  }