genesis-compiler 1.2.12 → 1.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,156 +0,0 @@
1
- import { access } from 'node:fs/promises';
2
- import path from 'node:path';
3
-
4
- import { asDiagnostic } from './errors.js';
5
- import { gitContext } from './git.js';
6
- import { runProcess } from './process.js';
7
- import { readStack } from './stack.js';
8
- import { withStackEnvironmentDefaults } from './stack-environment-defaults.js';
9
- import { sha256, stableJson, uniqueSorted } from './utils.js';
10
- import { GENESIS_CONTRACTS } from './contracts.js';
11
-
12
- const DEFAULT_FINITE_COMMAND_TIMEOUT_MS = 30 * 60 * 1_000;
13
-
14
- async function emit(onEvent, event) {
15
- try { await onEvent?.(event); } catch { /* Progress observers do not control preparation. */ }
16
- }
17
-
18
- export async function inspectWorkspaceSetupForStack({ projectRoot: root, stack }) {
19
- const diagnostics = [...stack.workspaceSetup.diagnostics];
20
- const applicableSteps = [];
21
- if (diagnostics.length === 0) {
22
- const waitingFor = [];
23
- for (const step of stack.workspaceSetup.steps) {
24
- if (step.runWhen) {
25
- try {
26
- await access(path.join(root, step.runWhen));
27
- applicableSteps.push(step);
28
- } catch (error) {
29
- if (!['ENOENT', 'ENOTDIR'].includes(error?.code)) throw error;
30
- }
31
- continue;
32
- }
33
- applicableSteps.push(step);
34
- if (!step.readyWhen) continue;
35
- try {
36
- await access(path.join(root, step.readyWhen));
37
- } catch (error) {
38
- if (!['ENOENT', 'ENOTDIR'].includes(error?.code)) throw error;
39
- waitingFor.push(step.readyWhen);
40
- }
41
- }
42
- if (waitingFor.length > 0) {
43
- const paths = uniqueSorted(waitingFor);
44
- diagnostics.push({
45
- code: 'STACK_WORKSPACE_SETUP_WAITING',
46
- message: `Workspace setup is waiting for project ${paths.length === 1 ? 'path' : 'paths'}: ${paths.join(', ')}.`,
47
- details: { paths },
48
- });
49
- }
50
- }
51
- const waiting = diagnostics.some(({ code }) => code === 'STACK_WORKSPACE_SETUP_WAITING');
52
- const blocked = diagnostics.some(({ code }) => code !== 'STACK_WORKSPACE_SETUP_WAITING');
53
- let status = 'unconfigured';
54
- if (blocked) status = 'blocked';
55
- else if (!waiting && applicableSteps.length > 0) status = 'ready';
56
- const recipeHash = status === 'ready'
57
- ? sha256(stableJson({ version: 1, steps: applicableSteps }))
58
- : '';
59
- return {
60
- contract: GENESIS_CONTRACTS.workspaceSetup,
61
- status,
62
- stackHash: stack.identityHash,
63
- recipeHash,
64
- components: stack.components.map(({ id }) => id),
65
- source: stack.workspaceSetup.source,
66
- runtimeRequirements: uniqueSorted(
67
- applicableSteps.flatMap((step) => step.runtimeRequirements),
68
- ),
69
- steps: applicableSteps,
70
- diagnostics,
71
- };
72
- }
73
-
74
- /** Read the Stack's workspace preparation recipe without executing it. */
75
- export async function inspectProjectWorkspaceSetup({
76
- projectRoot,
77
- stackPackages = [],
78
- } = {}) {
79
- const root = (await gitContext(projectRoot)).repositoryRoot;
80
- const stack = await readStack(root, { stackPackages });
81
- return inspectWorkspaceSetupForStack({ projectRoot: root, stack });
82
- }
83
-
84
- /** Execute the selected Stack's exact workspace recipe with the caller's environment. */
85
- export async function prepareProjectWorkspace({
86
- environment = process.env,
87
- onEvent,
88
- processRunner = runProcess,
89
- projectRoot,
90
- signal,
91
- stackPackages = [],
92
- timeoutMs = DEFAULT_FINITE_COMMAND_TIMEOUT_MS,
93
- } = {}) {
94
- const root = (await gitContext(projectRoot)).repositoryRoot;
95
- const stack = await readStack(root, { stackPackages });
96
- const setup = await inspectWorkspaceSetupForStack({ projectRoot: root, stack });
97
- if (setup.status !== 'ready') {
98
- const summary = setup.diagnostics.map(({ message }) => message).join(' ')
99
- || 'The selected Stack declares no applicable workspace preparation commands.';
100
- return {
101
- ...setup,
102
- summary,
103
- commands: [],
104
- };
105
- }
106
-
107
- const resolvedEnvironment = withStackEnvironmentDefaults(
108
- environment,
109
- stack.environmentDefaults,
110
- );
111
- const commands = [];
112
- try {
113
- for (const step of setup.steps) {
114
- await emit(onEvent, {
115
- type: 'genesis.workspace-preparation',
116
- code: 'WORKSPACE_PREPARATION_STARTED',
117
- message: `Preparing: ${step.label}.`,
118
- details: { label: step.label, argv: step.argv, workdir: step.workdir },
119
- });
120
- await processRunner(step.argv[0], step.argv.slice(1), {
121
- cwd: path.resolve(root, step.workdir),
122
- env: resolvedEnvironment,
123
- maxBytes: 32 * 1024 * 1024,
124
- code: 'WORKSPACE_PREPARATION_FAILED',
125
- signal,
126
- timeoutMs,
127
- });
128
- commands.push({
129
- label: step.label,
130
- argv: step.argv,
131
- workdir: step.workdir,
132
- });
133
- await emit(onEvent, {
134
- type: 'genesis.workspace-preparation',
135
- code: 'WORKSPACE_PREPARATION_COMPLETED',
136
- message: `Prepared: ${step.label}.`,
137
- details: { label: step.label, argv: step.argv, workdir: step.workdir },
138
- });
139
- }
140
- return {
141
- ...setup,
142
- status: 'passed',
143
- summary: `Prepared the workspace with ${commands.length} command${commands.length === 1 ? '' : 's'}.`,
144
- commands,
145
- diagnostics: [],
146
- };
147
- } catch (error) {
148
- return {
149
- ...setup,
150
- status: 'failed',
151
- summary: error.message,
152
- commands,
153
- diagnostics: [asDiagnostic(error)],
154
- };
155
- }
156
- }