@smoothbricks/cli 0.10.5 โ 0.10.7
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.
- package/dist/cli.js +3 -2
- package/dist/github-ci/index.d.ts +1 -0
- package/dist/github-ci/index.d.ts.map +1 -1
- package/dist/github-ci/index.js +28 -5
- package/dist/lib/json.d.ts +3 -1
- package/dist/lib/json.d.ts.map +1 -1
- package/dist/lib/json.js +5 -20
- package/dist/lib/workspace.d.ts.map +1 -1
- package/dist/lib/workspace.js +1 -0
- package/dist/monorepo/ci-workflow.d.ts +2 -0
- package/dist/monorepo/ci-workflow.d.ts.map +1 -1
- package/dist/monorepo/ci-workflow.js +10 -4
- package/dist/monorepo/github-runs-on.d.ts +14 -0
- package/dist/monorepo/github-runs-on.d.ts.map +1 -0
- package/dist/monorepo/github-runs-on.js +43 -0
- package/dist/monorepo/index.d.ts +2 -2
- package/dist/monorepo/index.d.ts.map +1 -1
- package/dist/monorepo/index.js +7 -7
- package/dist/monorepo/managed-files.d.ts +14 -3
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +54 -74
- package/dist/monorepo/package-policy.js +2 -2
- package/dist/monorepo/packs/index.d.ts.map +1 -1
- package/dist/monorepo/packs/index.js +3 -1
- package/dist/monorepo/publish-workflow.d.ts +3 -0
- package/dist/monorepo/publish-workflow.d.ts.map +1 -1
- package/dist/monorepo/publish-workflow.js +17 -14
- package/dist/monorepo/runtime.d.ts +2 -2
- package/dist/monorepo/runtime.d.ts.map +1 -1
- package/dist/monorepo/runtime.js +11 -16
- package/dist/monorepo/tool-validation.d.ts.map +1 -1
- package/dist/monorepo/tool-validation.js +20 -23
- package/dist/nx/index.d.ts +7 -4
- package/dist/nx/index.d.ts.map +1 -1
- package/dist/nx/index.js +117 -56
- package/managed/raw/tooling/devenv +71 -0
- package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +24 -7
- package/managed/templates/github/actions/cache-nix-devenv/action.yml +13 -11
- package/managed/templates/github/actions/save-nix-devenv/action.yml +34 -3
- package/managed/templates/github/actions/setup-devenv/action.yml +44 -15
- package/managed/templates/github/workflows/managed-files.yml +64 -12
- package/package.json +4 -4
- package/src/cli.ts +10 -3
- package/src/github-ci/index.test.ts +53 -4
- package/src/github-ci/index.ts +37 -5
- package/src/lib/json.ts +3 -1
- package/src/lib/workspace.ts +1 -0
- package/src/monorepo/__tests__/ci-workflow.test.ts +37 -1
- package/src/monorepo/__tests__/publish-workflow.test.ts +39 -4
- package/src/monorepo/ci-workflow.ts +14 -4
- package/src/monorepo/github-runs-on.ts +45 -0
- package/src/monorepo/index.ts +7 -7
- package/src/monorepo/managed-files.test.ts +58 -0
- package/src/monorepo/managed-files.ts +59 -76
- package/src/monorepo/package-policy.test.ts +31 -0
- package/src/monorepo/package-policy.ts +2 -2
- package/src/monorepo/packs/index.ts +3 -1
- package/src/monorepo/publish-workflow.ts +28 -15
- package/src/monorepo/runtime.test.ts +23 -17
- package/src/monorepo/runtime.ts +12 -17
- package/src/monorepo/tool-validation.test.ts +15 -8
- package/src/monorepo/tool-validation.ts +20 -25
- package/src/nx/index.test.ts +45 -14
- package/src/nx/index.ts +133 -64
- package/managed/raw/patches/ttsc@0.19.3.patch +0 -67
|
@@ -40,8 +40,7 @@ const rootDevDependencies = [
|
|
|
40
40
|
},
|
|
41
41
|
{ name: 'nx', fallbackVersion: '23.1.0', minimumVersion: '23.1.0' },
|
|
42
42
|
{ name: 'prettier', fallbackVersion: '^3.6.1', minimumVersion: '3.6.0', prefix: '^' },
|
|
43
|
-
|
|
44
|
-
{ name: 'ttsc', fallbackVersion: '0.19.3' },
|
|
43
|
+
{ name: 'ttsc', fallbackVersion: '0.25.0' },
|
|
45
44
|
// Nx and typescript-eslint still load the TypeScript JS API (6.x).
|
|
46
45
|
// Compilation is exclusively delegated to ttsc by the Nx plugin targets.
|
|
47
46
|
{ name: 'typescript', fallbackVersion: '^6.0.3', minimumVersion: '6.0.0', prefix: '^' },
|
|
@@ -56,8 +55,7 @@ const requiredDevenvPackages = ['bun', 'git', 'git-format-staged', 'jq', 'alejan
|
|
|
56
55
|
// with the runtime is validated against the live PATH (validateRootRuntimeVersions),
|
|
57
56
|
// never against a version template here.
|
|
58
57
|
const nodePackagePattern = /(^|\s)nodejs(_\d+|_latest)?(\s|#|$)/m;
|
|
59
|
-
const
|
|
60
|
-
const TTSC_PATCH_PATH = 'patches/ttsc@0.19.3.patch';
|
|
58
|
+
const obsoleteTtscPatchedDependencyKey = 'ttsc@0.19.3';
|
|
61
59
|
export async function applyToolConfigDefaults(root) {
|
|
62
60
|
const context = await readToolContext(root);
|
|
63
61
|
await applyRootPackageToolDefaults(root, context);
|
|
@@ -67,7 +65,7 @@ export async function applyToolConfigDefaults(root) {
|
|
|
67
65
|
export async function validateToolConfig(root) {
|
|
68
66
|
const context = await readToolContext(root);
|
|
69
67
|
return (validateRootDevDependencies(context.policy, context.rootPackage) +
|
|
70
|
-
|
|
68
|
+
validateObsoleteTtscPatchRemoved(context.rootPackage) +
|
|
71
69
|
validateToolingPackage(root, context.policy) +
|
|
72
70
|
validateToolingWorkspace(context.rootPackage) +
|
|
73
71
|
validateDevenvPackages(root));
|
|
@@ -89,7 +87,7 @@ export async function applyRootDevDependencyDefaults(root, context) {
|
|
|
89
87
|
if (delete devDependencies['@smoothbricks/cli']) {
|
|
90
88
|
changed = true;
|
|
91
89
|
}
|
|
92
|
-
changed =
|
|
90
|
+
changed = removeObsoleteTtscPatch(pkg) || changed;
|
|
93
91
|
if (changed) {
|
|
94
92
|
writeJsonObject(join(root, 'package.json'), pkg);
|
|
95
93
|
console.log('updated package.json workspace tool dependencies');
|
|
@@ -105,7 +103,6 @@ async function applyRootPackageToolDefaults(root, context) {
|
|
|
105
103
|
}
|
|
106
104
|
let dependencyChanged = false;
|
|
107
105
|
let workspaceChanged = false;
|
|
108
|
-
let patchChanged = false;
|
|
109
106
|
const devDependencies = ensureDependencyMap(pkg, 'devDependencies');
|
|
110
107
|
for (const dependency of rootDevDependencies) {
|
|
111
108
|
const current = devDependencies[dependency.name];
|
|
@@ -118,8 +115,8 @@ async function applyRootPackageToolDefaults(root, context) {
|
|
|
118
115
|
dependencyChanged = true;
|
|
119
116
|
}
|
|
120
117
|
workspaceChanged = addWorkspacePattern(pkg, 'tooling');
|
|
121
|
-
|
|
122
|
-
if (dependencyChanged || workspaceChanged
|
|
118
|
+
dependencyChanged = removeObsoleteTtscPatch(pkg) || dependencyChanged;
|
|
119
|
+
if (dependencyChanged || workspaceChanged) {
|
|
123
120
|
writeJsonObject(join(root, 'package.json'), pkg);
|
|
124
121
|
}
|
|
125
122
|
console.log(dependencyChanged
|
|
@@ -128,9 +125,6 @@ async function applyRootPackageToolDefaults(root, context) {
|
|
|
128
125
|
console.log(workspaceChanged
|
|
129
126
|
? 'updated package.json tooling workspace'
|
|
130
127
|
: 'unchanged package.json tooling workspace');
|
|
131
|
-
console.log(patchChanged
|
|
132
|
-
? 'updated package.json patchedDependencies for ttsc'
|
|
133
|
-
: 'unchanged package.json patchedDependencies for ttsc');
|
|
134
128
|
}
|
|
135
129
|
export function applyToolingPackageDefaults(root, policy) {
|
|
136
130
|
const path = join(root, 'tooling', 'package.json');
|
|
@@ -219,20 +213,23 @@ export function validateRootDevDependencies(policy, rootPackage) {
|
|
|
219
213
|
}
|
|
220
214
|
return failures;
|
|
221
215
|
}
|
|
222
|
-
function
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
function validateTtscPatchedDependency(rootPackage) {
|
|
227
|
-
if (!rootPackage) {
|
|
228
|
-
return 0;
|
|
216
|
+
function removeObsoleteTtscPatch(pkg) {
|
|
217
|
+
const patchedDependencies = pkg.patchedDependencies;
|
|
218
|
+
if (!patchedDependencies || !(obsoleteTtscPatchedDependencyKey in patchedDependencies)) {
|
|
219
|
+
return false;
|
|
229
220
|
}
|
|
230
|
-
|
|
231
|
-
if (
|
|
221
|
+
delete patchedDependencies[obsoleteTtscPatchedDependencyKey];
|
|
222
|
+
if (Object.keys(patchedDependencies).length === 0) {
|
|
223
|
+
delete pkg.patchedDependencies;
|
|
224
|
+
}
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
function validateObsoleteTtscPatchRemoved(rootPackage) {
|
|
228
|
+
const patchedDependencies = rootPackage?.patchedDependencies;
|
|
229
|
+
if (!patchedDependencies || !(obsoleteTtscPatchedDependencyKey in patchedDependencies)) {
|
|
232
230
|
return 0;
|
|
233
231
|
}
|
|
234
|
-
console.error(`package.json patchedDependencies.${
|
|
235
|
-
(typeof actual === 'string' ? `; found ${actual}` : ' (required for declaration-emit fix)'));
|
|
232
|
+
console.error(`package.json patchedDependencies.${obsoleteTtscPatchedDependencyKey} must be removed; ttsc 0.25.0 emits typia declarations without the obsolete patch`);
|
|
236
233
|
return 1;
|
|
237
234
|
}
|
|
238
235
|
export function validateToolingPackage(root, policy) {
|
package/dist/nx/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { NxProjectJson, NxTargetConfig, NxTargetOptions } from '../lib/json.js';
|
|
2
2
|
export interface ProjectTargets {
|
|
3
3
|
project: string;
|
|
4
4
|
root?: string;
|
|
@@ -6,6 +6,7 @@ export interface ProjectTargets {
|
|
|
6
6
|
buildDependsOn?: string[];
|
|
7
7
|
targetDependencies?: Map<string, string[]>;
|
|
8
8
|
targetExecutors?: Map<string, string>;
|
|
9
|
+
targetOptions?: Map<string, NxTargetOptions>;
|
|
9
10
|
targetOutputs?: Map<string, string[]>;
|
|
10
11
|
targetScripts?: Map<string, string>;
|
|
11
12
|
}
|
|
@@ -14,24 +15,26 @@ export interface CommandInvocation {
|
|
|
14
15
|
args: string[];
|
|
15
16
|
}
|
|
16
17
|
export declare function nxResetCommand(): CommandInvocation;
|
|
17
|
-
export declare function nxShowProjectCommand(project: string): CommandInvocation;
|
|
18
18
|
export declare function nxCacheDirectories(root: string): string[];
|
|
19
19
|
export declare function targetNamesFromNxProjectJson(value: NxProjectJson | null | undefined): string[];
|
|
20
20
|
export declare function projectRootFromNxProjectJson(value: NxProjectJson | null | undefined): string | undefined;
|
|
21
|
-
export declare function buildDependsOnFromNxProjectJson(value: NxProjectJson | null | undefined): string[] | undefined;
|
|
22
21
|
export declare function targetDependenciesFromNxProjectJson(value: NxProjectJson | null | undefined): Map<string, string[]>;
|
|
23
22
|
export declare function targetExecutorsFromNxProjectJson(value: NxProjectJson | null | undefined): Map<string, string>;
|
|
23
|
+
export declare function targetOptionsFromNxProjectJson(value: NxProjectJson | null | undefined): Map<string, NxTargetOptions>;
|
|
24
24
|
export declare function targetOutputsFromNxProjectJson(value: NxProjectJson | null | undefined): Map<string, string[]>;
|
|
25
25
|
export declare function targetScriptsFromNxProjectJson(value: NxProjectJson | null | undefined): Map<string, string>;
|
|
26
26
|
export declare function formatProjectTargetLines(projects: ProjectTargets[]): string;
|
|
27
27
|
export declare function projectNamesWithTarget(projects: ProjectTargets[], target: string): string[];
|
|
28
|
-
export declare function projectNamesFromNxShowProjectsOutput(output: string): string[];
|
|
29
28
|
export declare function listTargets(root: string): Promise<void>;
|
|
30
29
|
export declare function listProjects(root: string, options: {
|
|
31
30
|
withTarget?: string;
|
|
32
31
|
}): Promise<void>;
|
|
33
32
|
export declare function resetCache(root: string): Promise<void>;
|
|
34
33
|
export declare function cleanCache(root: string): Promise<void>;
|
|
34
|
+
export type NxProjects = Readonly<Record<string, NxProjectJson>>;
|
|
35
|
+
export declare function loadNxProjects(root: string): Promise<NxProjects>;
|
|
36
|
+
export declare function targetNamesFromProjects(projects: NxProjects): string[];
|
|
37
|
+
export declare function projectTargetsFromNxProjects(projects: NxProjects): ProjectTargets[];
|
|
35
38
|
export declare function readProjectTargets(root: string): Promise<ProjectTargets[]>;
|
|
36
39
|
export type { NxProjectJson, NxTargetConfig };
|
|
37
40
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/nx/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nx/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nx/index.ts"],"names":[],"mappings":"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"}
|
package/dist/nx/index.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { lstat, rm } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
// Nx package exports are CLI-oriented; this is the module the CLI uses for graph + daemon IPC.
|
|
5
|
+
import { createProjectGraphAsync } from 'nx/src/project-graph/project-graph.js';
|
|
6
|
+
import { workspaceRoot as primaryWorkspaceRoot } from 'nx/src/utils/workspace-root.js';
|
|
7
|
+
import { run, runResult } from '../lib/run.js';
|
|
7
8
|
export function nxResetCommand() {
|
|
8
9
|
return { command: 'nx', args: ['reset'] };
|
|
9
10
|
}
|
|
10
|
-
export function nxShowProjectCommand(project) {
|
|
11
|
-
return { command: 'nx', args: ['show', 'project', project, '--json'] };
|
|
12
|
-
}
|
|
13
11
|
export function nxCacheDirectories(root) {
|
|
14
12
|
return [join(root, '.nx/cache'), join(root, '.nx/workspace-data'), join(root, 'node_modules/.cache/nx')];
|
|
15
13
|
}
|
|
@@ -20,9 +18,6 @@ export function targetNamesFromNxProjectJson(value) {
|
|
|
20
18
|
export function projectRootFromNxProjectJson(value) {
|
|
21
19
|
return typeof value?.root === 'string' ? value.root : undefined;
|
|
22
20
|
}
|
|
23
|
-
export function buildDependsOnFromNxProjectJson(value) {
|
|
24
|
-
return targetDependenciesFromNxProjectJson(value).get('build');
|
|
25
|
-
}
|
|
26
21
|
export function targetDependenciesFromNxProjectJson(value) {
|
|
27
22
|
const targets = value?.targets;
|
|
28
23
|
const dependencies = new Map();
|
|
@@ -79,6 +74,19 @@ export function targetExecutorsFromNxProjectJson(value) {
|
|
|
79
74
|
}
|
|
80
75
|
return executors;
|
|
81
76
|
}
|
|
77
|
+
export function targetOptionsFromNxProjectJson(value) {
|
|
78
|
+
const targets = value?.targets;
|
|
79
|
+
const options = new Map();
|
|
80
|
+
if (!targets) {
|
|
81
|
+
return options;
|
|
82
|
+
}
|
|
83
|
+
for (const [targetName, target] of Object.entries(targets)) {
|
|
84
|
+
if (target.options) {
|
|
85
|
+
options.set(targetName, target.options);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return options;
|
|
89
|
+
}
|
|
82
90
|
export function targetOutputsFromNxProjectJson(value) {
|
|
83
91
|
return targetStringArraysFromNxProjectJson(value, 'outputs');
|
|
84
92
|
}
|
|
@@ -127,28 +135,6 @@ export function projectNamesWithTarget(projects, target) {
|
|
|
127
135
|
.map((project) => project.project)
|
|
128
136
|
.sort((a, b) => a.localeCompare(b));
|
|
129
137
|
}
|
|
130
|
-
export function projectNamesFromNxShowProjectsOutput(output) {
|
|
131
|
-
const trimmed = output.trim();
|
|
132
|
-
if (!trimmed) {
|
|
133
|
-
return [];
|
|
134
|
-
}
|
|
135
|
-
if (trimmed.startsWith('[')) {
|
|
136
|
-
try {
|
|
137
|
-
const parsed = parseStringArrayText(trimmed);
|
|
138
|
-
if (parsed) {
|
|
139
|
-
return parsed.sort((a, b) => a.localeCompare(b));
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
// Fall through to legacy newline parsing for older Nx output shapes.
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return trimmed
|
|
147
|
-
.split('\n')
|
|
148
|
-
.map((line) => line.trim())
|
|
149
|
-
.filter(Boolean)
|
|
150
|
-
.sort((a, b) => a.localeCompare(b));
|
|
151
|
-
}
|
|
152
138
|
export async function listTargets(root) {
|
|
153
139
|
const output = formatProjectTargetLines(await readProjectTargets(root));
|
|
154
140
|
if (output) {
|
|
@@ -182,30 +168,105 @@ export async function cleanCache(root) {
|
|
|
182
168
|
console.log(`Removed ${path}`);
|
|
183
169
|
}
|
|
184
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Load the resolved project graph through Nx's API. The current workspace uses the
|
|
173
|
+
* daemon in-process; foreign roots use an isolated process because Nx snapshots its
|
|
174
|
+
* workspace cache paths when the module loads.
|
|
175
|
+
*/
|
|
176
|
+
let graphLoadTail = Promise.resolve();
|
|
177
|
+
export function loadNxProjects(root) {
|
|
178
|
+
const result = graphLoadTail.then(() => loadNxProjectsNow(root));
|
|
179
|
+
graphLoadTail = result.then(() => undefined, () => undefined);
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
async function loadNxProjectsNow(root) {
|
|
183
|
+
if (root !== primaryWorkspaceRoot) {
|
|
184
|
+
return loadForeignNxProjects(root);
|
|
185
|
+
}
|
|
186
|
+
const graph = await createProjectGraphAsync({ exitOnError: false });
|
|
187
|
+
return nxProjectsFromNodes(graph.nodes);
|
|
188
|
+
}
|
|
189
|
+
const FOREIGN_GRAPH_SCRIPT = `
|
|
190
|
+
import('nx/src/project-graph/project-graph.js')
|
|
191
|
+
.then(async ({ createProjectGraphAsync }) => {
|
|
192
|
+
const graph = await createProjectGraphAsync({ exitOnError: false });
|
|
193
|
+
const projects = Object.fromEntries(
|
|
194
|
+
Object.entries(graph.nodes).map(([name, node]) => [
|
|
195
|
+
name,
|
|
196
|
+
{ ...node.data, name: node.data?.name ?? name, targets: node.data?.targets ?? {} },
|
|
197
|
+
]),
|
|
198
|
+
);
|
|
199
|
+
process.stdout.write(JSON.stringify(projects));
|
|
200
|
+
})
|
|
201
|
+
.catch((error) => {
|
|
202
|
+
console.error(error);
|
|
203
|
+
process.exitCode = 1;
|
|
204
|
+
});
|
|
205
|
+
`;
|
|
206
|
+
async function loadForeignNxProjects(root) {
|
|
207
|
+
const result = await runResult(process.execPath, ['--eval', FOREIGN_GRAPH_SCRIPT], root, {
|
|
208
|
+
NX_DAEMON: 'false',
|
|
209
|
+
NX_ISOLATE_PLUGINS: 'false',
|
|
210
|
+
NX_WORKSPACE_ROOT_PATH: root,
|
|
211
|
+
});
|
|
212
|
+
if (result.exitCode !== 0) {
|
|
213
|
+
throw new Error(`Failed to load Nx project graph for ${root}: ${result.stderr.trim()}`);
|
|
214
|
+
}
|
|
215
|
+
const parsed = JSON.parse(result.stdout);
|
|
216
|
+
if (!isNxProjects(parsed)) {
|
|
217
|
+
throw new Error(`Nx returned an invalid project graph for ${root}`);
|
|
218
|
+
}
|
|
219
|
+
return parsed;
|
|
220
|
+
}
|
|
221
|
+
function nxProjectsFromNodes(nodes) {
|
|
222
|
+
const projects = {};
|
|
223
|
+
for (const [name, node] of Object.entries(nodes)) {
|
|
224
|
+
projects[name] = {
|
|
225
|
+
...node.data,
|
|
226
|
+
name: node.data.name ?? name,
|
|
227
|
+
targets: node.data.targets ?? {},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return projects;
|
|
231
|
+
}
|
|
232
|
+
function isNxProjects(value) {
|
|
233
|
+
if (!isRecord(value))
|
|
234
|
+
return false;
|
|
235
|
+
return Object.values(value).every((project) => isRecord(project) &&
|
|
236
|
+
(project.name === undefined || typeof project.name === 'string') &&
|
|
237
|
+
(project.root === undefined || typeof project.root === 'string') &&
|
|
238
|
+
(project.targets === undefined || (isRecord(project.targets) && Object.values(project.targets).every(isRecord))));
|
|
239
|
+
}
|
|
240
|
+
function isRecord(value) {
|
|
241
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
242
|
+
}
|
|
243
|
+
export function targetNamesFromProjects(projects) {
|
|
244
|
+
const names = new Set();
|
|
245
|
+
for (const project of Object.values(projects)) {
|
|
246
|
+
for (const targetName of targetNamesFromNxProjectJson(project)) {
|
|
247
|
+
names.add(targetName);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return [...names];
|
|
251
|
+
}
|
|
252
|
+
export function projectTargetsFromNxProjects(projects) {
|
|
253
|
+
return Object.entries(projects)
|
|
254
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
255
|
+
.map(([project, metadata]) => {
|
|
256
|
+
const targetDependencies = targetDependenciesFromNxProjectJson(metadata);
|
|
257
|
+
return {
|
|
258
|
+
project,
|
|
259
|
+
root: projectRootFromNxProjectJson(metadata),
|
|
260
|
+
targets: targetNamesFromNxProjectJson(metadata),
|
|
261
|
+
buildDependsOn: targetDependencies.get('build'),
|
|
262
|
+
targetDependencies,
|
|
263
|
+
targetExecutors: targetExecutorsFromNxProjectJson(metadata),
|
|
264
|
+
targetOptions: targetOptionsFromNxProjectJson(metadata),
|
|
265
|
+
targetOutputs: targetOutputsFromNxProjectJson(metadata),
|
|
266
|
+
targetScripts: targetScriptsFromNxProjectJson(metadata),
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
}
|
|
185
270
|
export async function readProjectTargets(root) {
|
|
186
|
-
|
|
187
|
-
return Promise.all(projects.map((project) => readProjectTarget(root, project)));
|
|
188
|
-
}
|
|
189
|
-
async function readNxProjectNames(root) {
|
|
190
|
-
const result = await $ `nx show projects`.cwd(root).quiet();
|
|
191
|
-
return projectNamesFromNxShowProjectsOutput(decode(result.stdout));
|
|
192
|
-
}
|
|
193
|
-
async function readProjectTarget(root, project) {
|
|
194
|
-
const command = nxShowProjectCommand(project);
|
|
195
|
-
const result = await $ `${command.command} ${command.args}`.cwd(root).quiet();
|
|
196
|
-
const parsed = parseNxProjectJsonText(decode(result.stdout));
|
|
197
|
-
if (!parsed) {
|
|
198
|
-
throw new Error(`Unable to inspect Nx project ${project}.`);
|
|
199
|
-
}
|
|
200
|
-
const targetDependencies = targetDependenciesFromNxProjectJson(parsed);
|
|
201
|
-
return {
|
|
202
|
-
project,
|
|
203
|
-
root: projectRootFromNxProjectJson(parsed),
|
|
204
|
-
targets: targetNamesFromNxProjectJson(parsed),
|
|
205
|
-
buildDependsOn: targetDependencies.get('build'),
|
|
206
|
-
targetDependencies,
|
|
207
|
-
targetExecutors: targetExecutorsFromNxProjectJson(parsed),
|
|
208
|
-
targetOutputs: targetOutputsFromNxProjectJson(parsed),
|
|
209
|
-
targetScripts: targetScriptsFromNxProjectJson(parsed),
|
|
210
|
-
};
|
|
271
|
+
return projectTargetsFromNxProjects(await loadNxProjects(root));
|
|
211
272
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Wrapper script to run devenv from anywhere in the monorepo
|
|
3
|
+
# This allows us to keep all devenv files in tooling/direnv
|
|
4
|
+
|
|
5
|
+
# Save the current working directory
|
|
6
|
+
ORIGINAL_PWD="$(pwd)"
|
|
7
|
+
SELF_PATH="$(realpath "$0")"
|
|
8
|
+
|
|
9
|
+
# Find the root of the git repository
|
|
10
|
+
TOOLING_DIR="$(dirname "$SELF_PATH")"
|
|
11
|
+
DEVENV_DIR="$TOOLING_DIR/direnv"
|
|
12
|
+
if [ ! -d "$DEVENV_DIR" ]; then
|
|
13
|
+
echo "Error: Could not find devenv directory at $DEVENV_DIR" >&2
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Find the actual Nix-built devenv binary, skipping every wrapper script.
|
|
18
|
+
# WHY: PATH routinely holds more than one of these wrappers at once โ this
|
|
19
|
+
# repo's tooling/devenv, sibling repos' wrappers, and direnv-stacked parent
|
|
20
|
+
# environments. A plain "not equal to SELF_PATH" check happily selects a
|
|
21
|
+
# *different* wrapper and exec's it, so `devenv` bounces into the wrong repo
|
|
22
|
+
# (or self-execs forever on cheap checks like `devenv version`). The real devenv
|
|
23
|
+
# is always the Nix-managed executable under /nix/store, so match that and ignore
|
|
24
|
+
# every wrapper regardless of PATH ordering. (Matching the store path is also more
|
|
25
|
+
# robust than a "is it a binary" test: Nix sometimes ships tools as makeWrapper
|
|
26
|
+
# shell scripts, which are still the correct entry point.)
|
|
27
|
+
DEVENV_BIN=""
|
|
28
|
+
while IFS= read -r candidate; do
|
|
29
|
+
candidate_path="$(realpath "$candidate")"
|
|
30
|
+
case "$candidate_path" in
|
|
31
|
+
/nix/store/*)
|
|
32
|
+
if [ -f "$candidate_path" ] && [ -x "$candidate_path" ]; then
|
|
33
|
+
DEVENV_BIN="$candidate_path"
|
|
34
|
+
break
|
|
35
|
+
fi
|
|
36
|
+
;;
|
|
37
|
+
esac
|
|
38
|
+
done < <(which -a devenv 2>/dev/null)
|
|
39
|
+
|
|
40
|
+
if [ -z "$DEVENV_BIN" ]; then
|
|
41
|
+
echo "Error: Could not find the Nix-built devenv binary (under /nix/store) in PATH" >&2
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# If we're in a project, just run devenv
|
|
46
|
+
if [ -f devenv.nix ]; then
|
|
47
|
+
exec "$DEVENV_BIN" "$@"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Change to the tooling/direnv directory where devenv files are located
|
|
51
|
+
cd "$DEVENV_DIR" || exit 1
|
|
52
|
+
|
|
53
|
+
# Export the original directory so enterShell can use it
|
|
54
|
+
export DEVENV_SHELL_PWD="$ORIGINAL_PWD"
|
|
55
|
+
|
|
56
|
+
# Run the actual devenv command with all arguments passed through.
|
|
57
|
+
# For updates, refresh nvfetcher-managed overlay sources first so packages like
|
|
58
|
+
# jmunch-mcp and jcodemunch-mcp are current before devenv.lock is updated.
|
|
59
|
+
if [ "${1:-}" = "update" ]; then
|
|
60
|
+
if [ -f "$DEVENV_DIR/nixpkgs-overlay/nvfetcher.toml" ]; then
|
|
61
|
+
echo "Running nvfetcher for nixpkgs-overlay/..."
|
|
62
|
+
(cd "$DEVENV_DIR/nixpkgs-overlay" && echo "
|
|
63
|
+
[keys]
|
|
64
|
+
github = \"$(gh auth token)\"
|
|
65
|
+
" > .gh-auth && (nvfetcher -k .gh-auth -o _sources || true) && rm .gh-auth)
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
exec "$DEVENV_BIN" "$@"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
exec "$DEVENV_BIN" "$@"
|
|
@@ -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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
#
|
|
23
|
-
#
|
|
24
|
-
|
|
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:
|
|
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:
|
|
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
|
-
#
|
|
55
|
-
#
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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: ${{
|
|
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
|