@ontrails/trails 1.0.0-beta.24 → 1.0.0-beta.29
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/CHANGELOG.md +189 -0
- package/README.md +6 -5
- package/package.json +16 -14
- package/src/app.ts +42 -3
- package/src/cli.ts +17 -3
- package/src/local-state-io.ts +20 -0
- package/src/mcp-app.ts +10 -0
- package/src/mcp-options.ts +2 -3
- package/src/project-writes.ts +11 -11
- package/src/release/check.ts +41 -25
- package/src/release/index.ts +40 -0
- package/src/release/native-bun-registry.ts +282 -19
- package/src/release/pack-coherence.ts +457 -0
- package/src/release/policy.ts +1684 -0
- package/src/release/semver.ts +104 -0
- package/src/release/wayfinder-dogfood-smoke.ts +600 -66
- package/src/run-completions-install.ts +2 -2
- package/src/run-schema.ts +41 -0
- package/src/run-wayfind-outline.ts +166 -0
- package/src/trails/adapter-check.ts +1 -1
- package/src/trails/add-surface.ts +1 -1
- package/src/trails/add-verify.ts +3 -3
- package/src/trails/compile.ts +17 -25
- package/src/trails/create-scaffold.ts +14 -14
- package/src/trails/create.ts +5 -5
- package/src/trails/dev-support.ts +44 -24
- package/src/trails/doctor.ts +23 -2
- package/src/trails/draft-promote.ts +7 -7
- package/src/trails/guide.ts +15 -19
- package/src/trails/load-app.ts +58 -9
- package/src/trails/operator-context.ts +66 -0
- package/src/trails/regrade.ts +72 -0
- package/src/trails/release-check.ts +1 -0
- package/src/trails/run-example.ts +21 -37
- package/src/trails/run-examples.ts +16 -32
- package/src/trails/run.ts +31 -44
- package/src/trails/survey.ts +76 -62
- package/src/trails/topo-output-schemas.ts +11 -0
- package/src/trails/topo-pin.ts +6 -20
- package/src/trails/topo-read-support.ts +91 -41
- package/src/trails/topo-reports.ts +4 -2
- package/src/trails/topo-store-support.ts +172 -39
- package/src/trails/topo-support.ts +1 -2
- package/src/trails/topo.ts +5 -19
- package/src/trails/validate.ts +9 -20
- package/src/trails/version-lifecycle-support.ts +10 -20
- package/src/trails/warden.ts +18 -0
- package/src/trails/wayfind.ts +1001 -0
|
@@ -7,18 +7,20 @@
|
|
|
7
7
|
|
|
8
8
|
import { Database } from 'bun:sqlite';
|
|
9
9
|
|
|
10
|
-
import type { Topo } from '@ontrails/core';
|
|
10
|
+
import type { CliCommandAliasInput, Topo } from '@ontrails/core';
|
|
11
11
|
import {
|
|
12
12
|
ConflictError,
|
|
13
13
|
deriveTrailsDir,
|
|
14
14
|
InternalError,
|
|
15
15
|
openWriteTrailsDb,
|
|
16
16
|
Result,
|
|
17
|
+
TimeoutError,
|
|
17
18
|
} from '@ontrails/core';
|
|
18
19
|
import type {
|
|
19
20
|
LockManifest,
|
|
20
21
|
TopoGraph,
|
|
21
22
|
TopoSnapshot,
|
|
23
|
+
TrailsLock,
|
|
22
24
|
} from '@ontrails/topographer';
|
|
23
25
|
import type { StoredTopoExport } from '@ontrails/topographer/backend-support';
|
|
24
26
|
import {
|
|
@@ -27,14 +29,15 @@ import {
|
|
|
27
29
|
deriveTopoGraphDiff,
|
|
28
30
|
deriveTopoGraphHash,
|
|
29
31
|
readTopoGraph,
|
|
30
|
-
|
|
31
|
-
writeTopoGraph,
|
|
32
|
+
writeTrailsLock,
|
|
32
33
|
} from '@ontrails/topographer';
|
|
33
34
|
import {
|
|
34
35
|
createStoredTopoSnapshot,
|
|
35
36
|
getStoredTopoExport,
|
|
36
37
|
} from '@ontrails/topographer/backend-support';
|
|
37
38
|
|
|
39
|
+
import { removeRootRelativeFileIfPresent } from '../local-state-io.js';
|
|
40
|
+
|
|
38
41
|
import type { TopoExportReport } from './topo-support.js';
|
|
39
42
|
import {
|
|
40
43
|
deriveRootDir,
|
|
@@ -42,15 +45,21 @@ import {
|
|
|
42
45
|
readGitState,
|
|
43
46
|
} from './topo-support.js';
|
|
44
47
|
|
|
48
|
+
type CliAliasesOption = Readonly<
|
|
49
|
+
Record<string, readonly CliCommandAliasInput[]>
|
|
50
|
+
>;
|
|
51
|
+
|
|
45
52
|
const persistAndReadStoredExport = (
|
|
46
53
|
app: Topo,
|
|
47
54
|
db: ReturnType<typeof openWriteTrailsDb>,
|
|
48
|
-
rootDir: string
|
|
55
|
+
rootDir: string,
|
|
56
|
+
options?: { readonly cliAliases?: CliAliasesOption | undefined } | undefined
|
|
49
57
|
): Result<
|
|
50
58
|
{ snapshot: TopoSnapshot; storedExport: StoredTopoExport },
|
|
51
59
|
Error
|
|
52
60
|
> => {
|
|
53
61
|
const snapshotResult = createStoredTopoSnapshot(db, app, {
|
|
62
|
+
cliAliases: options?.cliAliases,
|
|
54
63
|
...readGitState(rootDir),
|
|
55
64
|
...deriveTopoCounts(app),
|
|
56
65
|
});
|
|
@@ -75,15 +84,58 @@ const persistAndReadStoredExport = (
|
|
|
75
84
|
});
|
|
76
85
|
};
|
|
77
86
|
|
|
87
|
+
const asError = (error: unknown): Error =>
|
|
88
|
+
error instanceof Error ? error : new Error(String(error));
|
|
89
|
+
|
|
90
|
+
const readErrorCode = (error: unknown): string | undefined => {
|
|
91
|
+
if (typeof error !== 'object' || error === null || !('code' in error)) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const { code } = error as { readonly code?: unknown };
|
|
95
|
+
return typeof code === 'string' ? code : undefined;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const isSqliteLockError = (error: unknown): boolean => {
|
|
99
|
+
const code = readErrorCode(error);
|
|
100
|
+
if (code === 'SQLITE_BUSY' || code === 'SQLITE_LOCKED') {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
return asError(error).message.match(/database is (locked|busy)/i) !== null;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const mapTopoExportError = (error: unknown): Error => {
|
|
107
|
+
if (!isSqliteLockError(error)) {
|
|
108
|
+
return error instanceof Error
|
|
109
|
+
? error
|
|
110
|
+
: new InternalError('Unable to write topo artifacts');
|
|
111
|
+
}
|
|
112
|
+
return new TimeoutError(
|
|
113
|
+
'Timed out waiting for the Trails topo store lock while compiling artifacts. Another topo write may be running; retry after it finishes.',
|
|
114
|
+
{
|
|
115
|
+
cause: asError(error),
|
|
116
|
+
context: {
|
|
117
|
+
operation: 'compile',
|
|
118
|
+
reason: 'sqlite-lock-contention',
|
|
119
|
+
resource: 'trails.db',
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
78
125
|
export const deriveCurrentTopoExport = (
|
|
79
126
|
app: Topo,
|
|
80
|
-
options?: {
|
|
127
|
+
options?: {
|
|
128
|
+
readonly cliAliases?: CliAliasesOption | undefined;
|
|
129
|
+
readonly rootDir?: string;
|
|
130
|
+
}
|
|
81
131
|
): Result<StoredTopoExport, Error> => {
|
|
82
132
|
const rootDir = deriveRootDir(options?.rootDir);
|
|
83
133
|
const db = new Database(':memory:');
|
|
84
134
|
|
|
85
135
|
try {
|
|
86
|
-
const projected = persistAndReadStoredExport(app, db, rootDir
|
|
136
|
+
const projected = persistAndReadStoredExport(app, db, rootDir, {
|
|
137
|
+
cliAliases: options?.cliAliases,
|
|
138
|
+
});
|
|
87
139
|
return projected.isErr()
|
|
88
140
|
? projected
|
|
89
141
|
: Result.ok(projected.value.storedExport);
|
|
@@ -92,12 +144,22 @@ export const deriveCurrentTopoExport = (
|
|
|
92
144
|
}
|
|
93
145
|
};
|
|
94
146
|
|
|
95
|
-
const
|
|
147
|
+
const readPreviousCommittedTopo = async (
|
|
148
|
+
rootDir: string
|
|
149
|
+
): Promise<TopoGraph | null> => {
|
|
150
|
+
const rootTopo = await readTopoGraph({ dir: rootDir });
|
|
151
|
+
return rootTopo ?? readTopoGraph({ dir: deriveTrailsDir({ rootDir }) });
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const prepareStoredExportArtifacts = async (
|
|
96
155
|
storedExport: StoredTopoExport,
|
|
97
|
-
|
|
156
|
+
rootDir: string,
|
|
98
157
|
options?: { readonly force?: boolean | undefined }
|
|
99
|
-
): Promise<
|
|
100
|
-
|
|
158
|
+
): Promise<{
|
|
159
|
+
readonly hash: string;
|
|
160
|
+
readonly topoGraph: TopoGraph;
|
|
161
|
+
}> => {
|
|
162
|
+
const previousTopo = await readPreviousCommittedTopo(rootDir);
|
|
101
163
|
const nextTopo = JSON.parse(storedExport.topoGraphJson) as TopoGraph;
|
|
102
164
|
const diff =
|
|
103
165
|
previousTopo === null
|
|
@@ -118,57 +180,128 @@ const writeStoredExportArtifacts = async (
|
|
|
118
180
|
? topoGraphBase
|
|
119
181
|
: annotateTopoGraphForces(topoGraphBase, diff.breaking);
|
|
120
182
|
const hash = deriveTopoGraphHash(topoGraph);
|
|
121
|
-
const lockManifest = {
|
|
122
|
-
...(JSON.parse(storedExport.lockManifestJson) as LockManifest),
|
|
123
|
-
artifacts: [
|
|
124
|
-
{
|
|
125
|
-
path: 'topo.lock',
|
|
126
|
-
role: 'topo',
|
|
127
|
-
sha256: hash,
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
} satisfies LockManifest;
|
|
131
|
-
|
|
132
|
-
const topoPath = await writeTopoGraph(topoGraph, { dir: trailsDir });
|
|
133
|
-
const lockPath = await writeLockManifest(lockManifest, { dir: trailsDir });
|
|
134
183
|
|
|
135
184
|
return {
|
|
136
185
|
hash,
|
|
186
|
+
topoGraph,
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const LEGACY_COMMITTED_ARTIFACTS = [
|
|
191
|
+
'.trails/topo.lock',
|
|
192
|
+
'.trails/trails.lock',
|
|
193
|
+
] as const;
|
|
194
|
+
|
|
195
|
+
const removeLegacyCommittedArtifacts = (
|
|
196
|
+
rootDir: string
|
|
197
|
+
): Result<void, Error> => {
|
|
198
|
+
for (const relativePath of LEGACY_COMMITTED_ARTIFACTS) {
|
|
199
|
+
const removed = removeRootRelativeFileIfPresent(rootDir, relativePath);
|
|
200
|
+
if (removed.isErr()) {
|
|
201
|
+
return removed;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return Result.ok();
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const writeStoredExportArtifacts = async (
|
|
209
|
+
storedExport: StoredTopoExport,
|
|
210
|
+
rootDir: string,
|
|
211
|
+
options?: { readonly force?: boolean | undefined }
|
|
212
|
+
): Promise<Pick<TopoExportReport, 'hash' | 'lockPath'>> => {
|
|
213
|
+
const prepared = await prepareStoredExportArtifacts(
|
|
214
|
+
storedExport,
|
|
215
|
+
rootDir,
|
|
216
|
+
options
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const lockManifest = JSON.parse(
|
|
220
|
+
storedExport.lockManifestJson
|
|
221
|
+
) as LockManifest;
|
|
222
|
+
const lockPath = await writeTrailsLock(
|
|
223
|
+
{
|
|
224
|
+
scope: lockManifest.scope,
|
|
225
|
+
summary: {
|
|
226
|
+
contours: prepared.topoGraph.entries.filter(
|
|
227
|
+
(entry) => entry.kind === 'contour'
|
|
228
|
+
).length,
|
|
229
|
+
resources: prepared.topoGraph.entries.filter(
|
|
230
|
+
(entry) => entry.kind === 'resource'
|
|
231
|
+
).length,
|
|
232
|
+
signals: prepared.topoGraph.entries.filter(
|
|
233
|
+
(entry) => entry.kind === 'signal'
|
|
234
|
+
).length,
|
|
235
|
+
trails: prepared.topoGraph.entries.filter(
|
|
236
|
+
(entry) => entry.kind === 'trail'
|
|
237
|
+
).length,
|
|
238
|
+
},
|
|
239
|
+
topoGraph: prepared.topoGraph,
|
|
240
|
+
topoGraphHash: prepared.hash,
|
|
241
|
+
version: 4,
|
|
242
|
+
} as TrailsLock,
|
|
243
|
+
{ dir: rootDir }
|
|
244
|
+
);
|
|
245
|
+
const removedLegacyArtifacts = removeLegacyCommittedArtifacts(rootDir);
|
|
246
|
+
if (removedLegacyArtifacts.isErr()) {
|
|
247
|
+
throw removedLegacyArtifacts.error;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
hash: prepared.hash,
|
|
137
252
|
lockPath,
|
|
138
|
-
topoPath,
|
|
139
253
|
};
|
|
140
254
|
};
|
|
141
255
|
|
|
142
256
|
export const exportCurrentTopo = async (
|
|
143
257
|
app: Topo,
|
|
144
|
-
options?: {
|
|
258
|
+
options?: {
|
|
259
|
+
readonly cliAliases?: CliAliasesOption | undefined;
|
|
260
|
+
readonly force?: boolean | undefined;
|
|
261
|
+
readonly rootDir?: string;
|
|
262
|
+
}
|
|
145
263
|
): Promise<Result<TopoExportReport, Error>> => {
|
|
146
264
|
const rootDir = deriveRootDir(options?.rootDir);
|
|
147
|
-
|
|
265
|
+
let db: ReturnType<typeof openWriteTrailsDb> | undefined;
|
|
148
266
|
|
|
149
267
|
try {
|
|
150
|
-
const
|
|
268
|
+
const candidate = deriveCurrentTopoExport(app, {
|
|
269
|
+
cliAliases: options?.cliAliases,
|
|
270
|
+
rootDir,
|
|
271
|
+
});
|
|
272
|
+
if (candidate.isErr()) {
|
|
273
|
+
return candidate;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
await prepareStoredExportArtifacts(candidate.value, rootDir, {
|
|
278
|
+
force: options?.force,
|
|
279
|
+
});
|
|
280
|
+
} catch (error: unknown) {
|
|
281
|
+
return Result.err(mapTopoExportError(error));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
db = openWriteTrailsDb({ rootDir });
|
|
285
|
+
const persisted = persistAndReadStoredExport(app, db, rootDir, {
|
|
286
|
+
cliAliases: options?.cliAliases,
|
|
287
|
+
});
|
|
151
288
|
if (persisted.isErr()) {
|
|
152
289
|
return persisted;
|
|
153
290
|
}
|
|
154
291
|
|
|
155
292
|
const { snapshot, storedExport } = persisted.value;
|
|
156
|
-
let artifacts: Pick<TopoExportReport, 'hash' | 'lockPath'
|
|
293
|
+
let artifacts: Pick<TopoExportReport, 'hash' | 'lockPath'>;
|
|
157
294
|
try {
|
|
158
|
-
artifacts = await writeStoredExportArtifacts(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
{ force: options?.force }
|
|
162
|
-
);
|
|
295
|
+
artifacts = await writeStoredExportArtifacts(storedExport, rootDir, {
|
|
296
|
+
force: options?.force,
|
|
297
|
+
});
|
|
163
298
|
} catch (error: unknown) {
|
|
164
|
-
return Result.err(
|
|
165
|
-
error instanceof Error
|
|
166
|
-
? error
|
|
167
|
-
: new InternalError('Unable to write topo artifacts')
|
|
168
|
-
);
|
|
299
|
+
return Result.err(mapTopoExportError(error));
|
|
169
300
|
}
|
|
170
301
|
return Result.ok({ ...artifacts, snapshot });
|
|
302
|
+
} catch (error: unknown) {
|
|
303
|
+
return Result.err(mapTopoExportError(error));
|
|
171
304
|
} finally {
|
|
172
|
-
db
|
|
305
|
+
db?.close();
|
|
173
306
|
}
|
|
174
307
|
};
|
|
@@ -33,7 +33,7 @@ export const topoSnapshotOutput = z.object({
|
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
export const DEFAULT_TOPO_HISTORY_LIMIT = 10;
|
|
36
|
-
export const LOCK_PATH = '
|
|
36
|
+
export const LOCK_PATH = 'trails.lock';
|
|
37
37
|
const EXAMPLE_APP_MODULE = fileURLToPath(new URL('../app.ts', import.meta.url));
|
|
38
38
|
|
|
39
39
|
const uniqueExampleRootName = (name: string): string =>
|
|
@@ -59,7 +59,6 @@ export interface TopoExportReport {
|
|
|
59
59
|
readonly hash: string;
|
|
60
60
|
readonly lockPath: string;
|
|
61
61
|
readonly snapshot: TopoSnapshot;
|
|
62
|
-
readonly topoPath: string;
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
export interface TopoValidateReport {
|
package/src/trails/topo.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Result, trail } from '@ontrails/core';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { resolveTrailRootDir } from './root-dir.js';
|
|
4
|
+
import { withFreshOperatorApp } from './operator-context.js';
|
|
6
5
|
import { activationOverviewOutput } from './topo-output-schemas.js';
|
|
7
6
|
import { buildTopoSummary } from './topo-read-support.js';
|
|
8
7
|
import { createIsolatedExampleInput } from './topo-support.js';
|
|
@@ -73,23 +72,10 @@ const summaryOutput = z.object({
|
|
|
73
72
|
});
|
|
74
73
|
|
|
75
74
|
export const topoTrail = trail('topo', {
|
|
76
|
-
blaze: async (input, ctx) =>
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
const rootDir = rootDirResult.value;
|
|
82
|
-
const leaseResult = await tryLoadFreshAppLease(input.module, rootDir);
|
|
83
|
-
if (leaseResult.isErr()) {
|
|
84
|
-
return leaseResult;
|
|
85
|
-
}
|
|
86
|
-
const lease = leaseResult.value;
|
|
87
|
-
try {
|
|
88
|
-
return Result.ok(buildTopoSummary(lease.app, { rootDir }));
|
|
89
|
-
} finally {
|
|
90
|
-
lease.release();
|
|
91
|
-
}
|
|
92
|
-
},
|
|
75
|
+
blaze: async (input, ctx) =>
|
|
76
|
+
withFreshOperatorApp(input, ctx, ({ lease, rootDir }) =>
|
|
77
|
+
Result.ok(buildTopoSummary(lease.app, { rootDir }))
|
|
78
|
+
),
|
|
93
79
|
description: 'Show the current topo summary and entry list',
|
|
94
80
|
examples: [
|
|
95
81
|
{
|
package/src/trails/validate.ts
CHANGED
|
@@ -1,29 +1,18 @@
|
|
|
1
1
|
import { trail } from '@ontrails/core';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { resolveTrailRootDir } from './root-dir.js';
|
|
4
|
+
import { withFreshOperatorApp } from './operator-context.js';
|
|
6
5
|
import { validateCurrentTopo } from './topo-read-support.js';
|
|
7
6
|
|
|
8
7
|
export const validateTrail = trail('validate', {
|
|
9
|
-
blaze: async (input, ctx) =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return leaseResult;
|
|
18
|
-
}
|
|
19
|
-
const lease = leaseResult.value;
|
|
20
|
-
try {
|
|
21
|
-
return await validateCurrentTopo(lease.app, { rootDir });
|
|
22
|
-
} finally {
|
|
23
|
-
lease.release();
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
description: 'Validate that committed topo artifacts match the current topo',
|
|
8
|
+
blaze: async (input, ctx) =>
|
|
9
|
+
withFreshOperatorApp(input, ctx, ({ lease, rootDir }) =>
|
|
10
|
+
validateCurrentTopo(lease.app, {
|
|
11
|
+
cliAliases: lease.cliAliases,
|
|
12
|
+
rootDir,
|
|
13
|
+
})
|
|
14
|
+
),
|
|
15
|
+
description: 'Validate that root trails.lock matches the current topo',
|
|
27
16
|
input: z.object({
|
|
28
17
|
module: z.string().optional().describe('Path to the app module'),
|
|
29
18
|
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
@@ -7,12 +7,11 @@ import { deriveTopoGraph } from '@ontrails/topographer';
|
|
|
7
7
|
import type { TopoGraph, TopoGraphForceEntry } from '@ontrails/topographer';
|
|
8
8
|
import { findTrailDefinitions, parse } from '@ontrails/warden/ast';
|
|
9
9
|
|
|
10
|
-
import { tryLoadFreshAppLease } from './load-app.js';
|
|
11
10
|
import {
|
|
12
11
|
readLifecycleSourceFile,
|
|
13
12
|
writeLifecycleSourceFile,
|
|
14
13
|
} from '../lifecycle-source-io.js';
|
|
15
|
-
import {
|
|
14
|
+
import { withFreshAppLease, withOperatorRootDir } from './operator-context.js';
|
|
16
15
|
|
|
17
16
|
export type LifecycleEntryKind = 'revision' | 'fork';
|
|
18
17
|
export type LifecycleStatusKind = 'deprecated' | 'archived';
|
|
@@ -569,7 +568,7 @@ export const reviseTrailSource = (
|
|
|
569
568
|
);
|
|
570
569
|
const written = writeLifecycleSourceFile(match.value.filePath, nextSource);
|
|
571
570
|
if (written.isErr()) {
|
|
572
|
-
return
|
|
571
|
+
return written;
|
|
573
572
|
}
|
|
574
573
|
|
|
575
574
|
return Result.ok({
|
|
@@ -732,7 +731,7 @@ export const setVersionStatusSource = (
|
|
|
732
731
|
}
|
|
733
732
|
const written = writeLifecycleSourceFile(match.value.filePath, nextSource);
|
|
734
733
|
if (written.isErr()) {
|
|
735
|
-
return
|
|
734
|
+
return written;
|
|
736
735
|
}
|
|
737
736
|
|
|
738
737
|
return Result.ok({
|
|
@@ -776,7 +775,7 @@ export const forkVersionEntrySource = (
|
|
|
776
775
|
}
|
|
777
776
|
const written = writeLifecycleSourceFile(match.value.filePath, nextSource);
|
|
778
777
|
if (written.isErr()) {
|
|
779
|
-
return
|
|
778
|
+
return written;
|
|
780
779
|
}
|
|
781
780
|
|
|
782
781
|
return Result.ok({
|
|
@@ -798,21 +797,12 @@ export const withLifecycleApp = async <T>(
|
|
|
798
797
|
app: Topo,
|
|
799
798
|
rootDir: string
|
|
800
799
|
) => Result<T, Error> | Promise<Result<T, Error>>
|
|
801
|
-
): Promise<Result<T, Error>> =>
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
if (lease.isErr()) {
|
|
808
|
-
return Result.err(lease.error);
|
|
809
|
-
}
|
|
810
|
-
try {
|
|
811
|
-
return await consume(lease.value.app, root.value);
|
|
812
|
-
} finally {
|
|
813
|
-
lease.value.release();
|
|
814
|
-
}
|
|
815
|
-
};
|
|
800
|
+
): Promise<Result<T, Error>> =>
|
|
801
|
+
withOperatorRootDir(input, { cwd }, (rootDir) =>
|
|
802
|
+
withFreshAppLease(input.module, rootDir, (lease) =>
|
|
803
|
+
consume(lease.app, rootDir)
|
|
804
|
+
)
|
|
805
|
+
);
|
|
816
806
|
|
|
817
807
|
export const findLifecycleTrail = (
|
|
818
808
|
app: Topo,
|
package/src/trails/warden.ts
CHANGED
|
@@ -16,6 +16,11 @@ import {
|
|
|
16
16
|
} from '@ontrails/warden';
|
|
17
17
|
import { z } from 'zod';
|
|
18
18
|
|
|
19
|
+
import {
|
|
20
|
+
createIsolatedExampleRoot,
|
|
21
|
+
writeIsolatedExampleTextFile,
|
|
22
|
+
} from '../local-state-io.js';
|
|
23
|
+
|
|
19
24
|
import { resolveTrailRootDir } from './root-dir.js';
|
|
20
25
|
|
|
21
26
|
// ---------------------------------------------------------------------------
|
|
@@ -68,6 +73,14 @@ const wardenInputSchema = z.object({
|
|
|
68
73
|
|
|
69
74
|
type WardenTrailInput = z.infer<typeof wardenInputSchema>;
|
|
70
75
|
|
|
76
|
+
const createIsolatedWardenExampleRoot = (name: string): string => {
|
|
77
|
+
const rootDir = createIsolatedExampleRoot(
|
|
78
|
+
`warden-${name}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`
|
|
79
|
+
);
|
|
80
|
+
writeIsolatedExampleTextFile(rootDir, 'src/clean.ts', 'export {};\n');
|
|
81
|
+
return rootDir;
|
|
82
|
+
};
|
|
83
|
+
|
|
71
84
|
const pushFlag = (args: string[], condition: boolean, flag: string): void => {
|
|
72
85
|
if (condition) {
|
|
73
86
|
args.push(flag);
|
|
@@ -170,13 +183,18 @@ export const wardenTrail = trail('warden', {
|
|
|
170
183
|
examples: [
|
|
171
184
|
{
|
|
172
185
|
input: {
|
|
186
|
+
depth: 'source',
|
|
173
187
|
lock: 'skip',
|
|
188
|
+
rootDir: createIsolatedWardenExampleRoot('default'),
|
|
174
189
|
},
|
|
175
190
|
name: 'Default warden run',
|
|
176
191
|
},
|
|
177
192
|
{
|
|
178
193
|
input: {
|
|
194
|
+
depth: 'source',
|
|
179
195
|
format: 'github',
|
|
196
|
+
lock: 'skip',
|
|
197
|
+
rootDir: createIsolatedWardenExampleRoot('github'),
|
|
180
198
|
},
|
|
181
199
|
name: 'GitHub Actions annotations',
|
|
182
200
|
},
|