@ontrails/trails 1.0.0-beta.2 → 1.0.0-beta.22
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 +647 -0
- package/README.md +26 -0
- package/package.json +28 -7
- package/src/app.ts +86 -2
- package/src/clack.ts +22 -0
- package/src/cli.ts +330 -11
- package/src/completions.ts +240 -0
- package/src/lifecycle-source-io.ts +33 -0
- package/src/load-app-mirror.ts +202 -0
- package/src/local-state-io.ts +153 -0
- package/src/mcp-app.ts +30 -0
- package/src/mcp-options.ts +77 -0
- package/src/mcp.ts +8 -0
- package/src/project-writes.ts +377 -0
- package/src/release/bindings.ts +39 -0
- package/src/release/check.ts +818 -0
- package/src/release/config.ts +63 -0
- package/src/release/contract-facts.ts +425 -0
- package/src/release/index.ts +85 -0
- package/src/release/native-bun-publish.ts +651 -0
- package/src/release/native-bun-registry.ts +350 -0
- package/src/release/packed-artifacts-smoke.ts +236 -0
- package/src/release/smoke.ts +46 -0
- package/src/release/wayfinder-dogfood-smoke.ts +226 -0
- package/src/retired-topo-command.ts +36 -0
- package/src/run-adapter-check.ts +76 -0
- package/src/run-collision.ts +126 -0
- package/src/run-completions-install.ts +179 -0
- package/src/run-example.ts +149 -0
- package/src/run-examples.ts +148 -0
- package/src/run-quiet.ts +75 -0
- package/src/run-release-check.ts +74 -0
- package/src/run-trace.ts +273 -0
- package/src/run-warden.ts +39 -0
- package/src/run-watch.ts +432 -0
- package/src/scaffold-version-sync.ts +183 -0
- package/src/scaffold-versions.generated.ts +12 -0
- package/src/trails/adapter-check.ts +244 -0
- package/src/trails/add-surface.ts +94 -40
- package/src/trails/add-trail.ts +79 -41
- package/src/trails/add-verify.ts +95 -25
- package/src/trails/compile.ts +67 -0
- package/src/trails/completions-complete.ts +165 -0
- package/src/trails/completions.ts +47 -0
- package/src/trails/create-adapter.ts +1084 -0
- package/src/trails/create-scaffold.ts +399 -104
- package/src/trails/create-versions.ts +62 -0
- package/src/trails/create.ts +185 -71
- package/src/trails/deprecate.ts +59 -0
- package/src/trails/dev-clean.ts +82 -0
- package/src/trails/dev-reset.ts +50 -0
- package/src/trails/dev-stats.ts +72 -0
- package/src/trails/dev-support.ts +340 -0
- package/src/trails/doctor.ts +56 -0
- package/src/trails/draft-promote.ts +949 -0
- package/src/trails/guide.ts +74 -68
- package/src/trails/load-app.ts +1143 -15
- package/src/trails/project.ts +17 -3
- package/src/trails/release-check.ts +104 -0
- package/src/trails/release-smoke.ts +48 -0
- package/src/trails/revise.ts +53 -0
- package/src/trails/root-dir.ts +21 -0
- package/src/trails/run-example.ts +491 -0
- package/src/trails/run-examples.ts +145 -0
- package/src/trails/run.ts +410 -0
- package/src/trails/scaffold-json.ts +58 -0
- package/src/trails/survey.ts +881 -226
- package/src/trails/topo-activation.ts +385 -0
- package/src/trails/topo-constants.ts +2 -0
- package/src/trails/topo-history.ts +47 -0
- package/src/trails/topo-output-schemas.ts +248 -0
- package/src/trails/topo-pin.ts +52 -0
- package/src/trails/topo-read-support.ts +313 -0
- package/src/trails/topo-reports.ts +807 -0
- package/src/trails/topo-store-support.ts +174 -0
- package/src/trails/topo-support.ts +220 -0
- package/src/trails/topo-unpin.ts +61 -0
- package/src/trails/topo.ts +106 -0
- package/src/trails/validate.ts +38 -0
- package/src/trails/version-lifecycle-support.ts +945 -0
- package/src/trails/warden-guide.ts +129 -0
- package/src/trails/warden.ts +165 -58
- package/src/versions.ts +31 -0
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -1
- package/__tests__/examples.test.ts +0 -6
- package/dist/bin/trails.d.ts +0 -3
- package/dist/bin/trails.d.ts.map +0 -1
- package/dist/bin/trails.js +0 -4
- package/dist/bin/trails.js.map +0 -1
- package/dist/src/app.d.ts +0 -2
- package/dist/src/app.d.ts.map +0 -1
- package/dist/src/app.js +0 -11
- package/dist/src/app.js.map +0 -1
- package/dist/src/clack.d.ts +0 -9
- package/dist/src/clack.d.ts.map +0 -1
- package/dist/src/clack.js +0 -62
- package/dist/src/clack.js.map +0 -1
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js +0 -13
- package/dist/src/cli.js.map +0 -1
- package/dist/src/trails/add-surface.d.ts +0 -13
- package/dist/src/trails/add-surface.d.ts.map +0 -1
- package/dist/src/trails/add-surface.js +0 -88
- package/dist/src/trails/add-surface.js.map +0 -1
- package/dist/src/trails/add-trail.d.ts +0 -11
- package/dist/src/trails/add-trail.d.ts.map +0 -1
- package/dist/src/trails/add-trail.js +0 -85
- package/dist/src/trails/add-trail.js.map +0 -1
- package/dist/src/trails/add-verify.d.ts +0 -10
- package/dist/src/trails/add-verify.d.ts.map +0 -1
- package/dist/src/trails/add-verify.js +0 -67
- package/dist/src/trails/add-verify.js.map +0 -1
- package/dist/src/trails/create-scaffold.d.ts +0 -15
- package/dist/src/trails/create-scaffold.d.ts.map +0 -1
- package/dist/src/trails/create-scaffold.js +0 -288
- package/dist/src/trails/create-scaffold.js.map +0 -1
- package/dist/src/trails/create.d.ts +0 -22
- package/dist/src/trails/create.d.ts.map +0 -1
- package/dist/src/trails/create.js +0 -121
- package/dist/src/trails/create.js.map +0 -1
- package/dist/src/trails/guide.d.ts +0 -11
- package/dist/src/trails/guide.d.ts.map +0 -1
- package/dist/src/trails/guide.js +0 -80
- package/dist/src/trails/guide.js.map +0 -1
- package/dist/src/trails/load-app.d.ts +0 -4
- package/dist/src/trails/load-app.d.ts.map +0 -1
- package/dist/src/trails/load-app.js +0 -24
- package/dist/src/trails/load-app.js.map +0 -1
- package/dist/src/trails/project.d.ts +0 -8
- package/dist/src/trails/project.d.ts.map +0 -1
- package/dist/src/trails/project.js +0 -43
- package/dist/src/trails/project.js.map +0 -1
- package/dist/src/trails/survey.d.ts +0 -33
- package/dist/src/trails/survey.d.ts.map +0 -1
- package/dist/src/trails/survey.js +0 -225
- package/dist/src/trails/survey.js.map +0 -1
- package/dist/src/trails/warden.d.ts +0 -19
- package/dist/src/trails/warden.d.ts.map +0 -1
- package/dist/src/trails/warden.js +0 -88
- package/dist/src/trails/warden.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/__tests__/create.test.ts +0 -349
- package/src/__tests__/guide.test.ts +0 -91
- package/src/__tests__/load-app.test.ts +0 -15
- package/src/__tests__/survey.test.ts +0 -161
- package/src/__tests__/warden.test.ts +0 -74
- package/tsconfig.json +0 -9
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const releaseFactTypeValues = [
|
|
4
|
+
'package-content',
|
|
5
|
+
'public-trail-contract',
|
|
6
|
+
] as const;
|
|
7
|
+
|
|
8
|
+
export const releaseIntentSourceValues = [
|
|
9
|
+
'changeset',
|
|
10
|
+
'no-release-override',
|
|
11
|
+
] as const;
|
|
12
|
+
|
|
13
|
+
export const releaseRuleSeverityValues = ['error', 'warning'] as const;
|
|
14
|
+
|
|
15
|
+
export const releaseFactTypeSchema = z.enum(releaseFactTypeValues);
|
|
16
|
+
export const releaseIntentSourceSchema = z.enum(releaseIntentSourceValues);
|
|
17
|
+
export const releaseRuleSeveritySchema = z.enum(releaseRuleSeverityValues);
|
|
18
|
+
|
|
19
|
+
export const releaseRuleSchema = z.object({
|
|
20
|
+
description: z.string().optional(),
|
|
21
|
+
enabled: z.boolean().default(true),
|
|
22
|
+
facts: z.array(releaseFactTypeSchema).min(1),
|
|
23
|
+
id: z.string().min(1),
|
|
24
|
+
intent: z.array(releaseIntentSourceSchema).min(1).default(['changeset']),
|
|
25
|
+
severity: releaseRuleSeveritySchema.default('error'),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export type ReleaseFactType = z.output<typeof releaseFactTypeSchema>;
|
|
29
|
+
export type ReleaseIntentSource = z.output<typeof releaseIntentSourceSchema>;
|
|
30
|
+
export type ReleaseRule = z.output<typeof releaseRuleSchema>;
|
|
31
|
+
export type ReleaseRuleInput = z.input<typeof releaseRuleSchema>;
|
|
32
|
+
|
|
33
|
+
export const defaultReleaseRules = [
|
|
34
|
+
{
|
|
35
|
+
description:
|
|
36
|
+
'Publishable package content changes require positive release intent.',
|
|
37
|
+
enabled: true,
|
|
38
|
+
facts: ['package-content'],
|
|
39
|
+
id: 'package-content-requires-intent',
|
|
40
|
+
intent: ['changeset', 'no-release-override'],
|
|
41
|
+
severity: 'error',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
description:
|
|
45
|
+
'Public trail contract changes require positive release intent.',
|
|
46
|
+
enabled: true,
|
|
47
|
+
facts: ['public-trail-contract'],
|
|
48
|
+
id: 'public-trail-contract-requires-intent',
|
|
49
|
+
intent: ['changeset', 'no-release-override'],
|
|
50
|
+
severity: 'error',
|
|
51
|
+
},
|
|
52
|
+
] as const satisfies readonly ReleaseRuleInput[];
|
|
53
|
+
|
|
54
|
+
export const releaseConfigSchema = z
|
|
55
|
+
.object({
|
|
56
|
+
rules: z.array(releaseRuleSchema).default(() => [...defaultReleaseRules]),
|
|
57
|
+
})
|
|
58
|
+
.default({ rules: [...defaultReleaseRules] });
|
|
59
|
+
|
|
60
|
+
export type ReleaseConfig = z.output<typeof releaseConfigSchema>;
|
|
61
|
+
export type ReleaseConfigInput = z.input<typeof releaseConfigSchema>;
|
|
62
|
+
|
|
63
|
+
export const defaultReleaseConfig = releaseConfigSchema.parse({});
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import ts from 'typescript';
|
|
6
|
+
|
|
7
|
+
import type { WorkspaceInfo } from './check.js';
|
|
8
|
+
|
|
9
|
+
export type ContractReleaseFactAspect =
|
|
10
|
+
| 'input'
|
|
11
|
+
| 'output'
|
|
12
|
+
| 'surfaces'
|
|
13
|
+
| 'trail'
|
|
14
|
+
| 'visibility';
|
|
15
|
+
|
|
16
|
+
export interface ContractReleaseFact {
|
|
17
|
+
readonly aspect: ContractReleaseFactAspect;
|
|
18
|
+
readonly baseHash: string | null;
|
|
19
|
+
readonly changedFiles: readonly string[];
|
|
20
|
+
readonly currentHash: string | null;
|
|
21
|
+
readonly packageName?: string;
|
|
22
|
+
readonly path: string;
|
|
23
|
+
readonly trailId: string;
|
|
24
|
+
readonly workspacePath?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ContractReleaseFactInput {
|
|
28
|
+
readonly baseRef?: string;
|
|
29
|
+
readonly changedFiles: readonly string[];
|
|
30
|
+
readonly repoRoot: string;
|
|
31
|
+
readonly workspaces: readonly WorkspaceInfo[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ContractSourceSnapshot {
|
|
35
|
+
readonly baseSource: string | null;
|
|
36
|
+
readonly changedFiles?: readonly string[];
|
|
37
|
+
readonly currentSource: string | null;
|
|
38
|
+
readonly packageName?: string;
|
|
39
|
+
readonly path: string;
|
|
40
|
+
readonly workspacePath?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface TrailDefinition {
|
|
44
|
+
readonly aspects: Readonly<Record<ContractReleaseFactAspect, string | null>>;
|
|
45
|
+
readonly trailId: string;
|
|
46
|
+
readonly visibility: 'internal' | 'public';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const NON_SHIPPING_SOURCE_PATTERNS = [
|
|
50
|
+
/(?:^|\/)__tests__(?:\/|$)/u,
|
|
51
|
+
/(?:^|\/)__snapshots__(?:\/|$)/u,
|
|
52
|
+
/(?:^|\/)dist(?:\/|$)/u,
|
|
53
|
+
/(?:^|\/)\.turbo(?:\/|$)/u,
|
|
54
|
+
/(?:^|\/)node_modules(?:\/|$)/u,
|
|
55
|
+
/\.(?:test|spec|snap)\.[cm]?[jt]sx?$/u,
|
|
56
|
+
/\.test-d\.ts$/u,
|
|
57
|
+
/\.tsbuildinfo$/u,
|
|
58
|
+
] as const;
|
|
59
|
+
|
|
60
|
+
const SOURCE_PATH_PATTERN = /\.[cm]?[jt]sx?$/u;
|
|
61
|
+
|
|
62
|
+
const normalizePath = (path: string): string =>
|
|
63
|
+
path.replaceAll('\\', '/').replace(/^\.\//u, '');
|
|
64
|
+
|
|
65
|
+
const isPublishableOnTrailsWorkspace = (workspace: WorkspaceInfo): boolean =>
|
|
66
|
+
!workspace.isPrivate && workspace.name.startsWith('@ontrails/');
|
|
67
|
+
|
|
68
|
+
const isUnderWorkspace = (filePath: string, workspacePath: string): boolean =>
|
|
69
|
+
filePath === workspacePath || filePath.startsWith(`${workspacePath}/`);
|
|
70
|
+
|
|
71
|
+
const getWorkspaceRelativePath = (
|
|
72
|
+
filePath: string,
|
|
73
|
+
workspacePath: string
|
|
74
|
+
): string => filePath.slice(workspacePath.length + 1);
|
|
75
|
+
|
|
76
|
+
const isNonShippingSourcePath = (workspaceRelativePath: string): boolean =>
|
|
77
|
+
NON_SHIPPING_SOURCE_PATTERNS.some((pattern) =>
|
|
78
|
+
pattern.test(workspaceRelativePath)
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const hashSource = (source: string | null): string | null =>
|
|
82
|
+
source === null
|
|
83
|
+
? null
|
|
84
|
+
: createHash('sha256').update(source).digest('hex').slice(0, 16);
|
|
85
|
+
|
|
86
|
+
const readCurrentSource = (
|
|
87
|
+
repoRoot: string,
|
|
88
|
+
relativePath: string
|
|
89
|
+
): string | null => {
|
|
90
|
+
const absolutePath = join(repoRoot, relativePath);
|
|
91
|
+
return existsSync(absolutePath) ? readFileSync(absolutePath, 'utf8') : null;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const readBaseSource = (
|
|
95
|
+
repoRoot: string,
|
|
96
|
+
baseRef: string | undefined,
|
|
97
|
+
relativePath: string
|
|
98
|
+
): string | null => {
|
|
99
|
+
if (!baseRef) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const result = Bun.spawnSync({
|
|
104
|
+
cmd: ['git', 'show', `${baseRef}:${relativePath}`],
|
|
105
|
+
cwd: repoRoot,
|
|
106
|
+
stderr: 'pipe',
|
|
107
|
+
stdout: 'pipe',
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return result.exitCode === 0 ? result.stdout.toString() : null;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const workspaceForFile = (
|
|
114
|
+
filePath: string,
|
|
115
|
+
workspaces: readonly WorkspaceInfo[]
|
|
116
|
+
): WorkspaceInfo | undefined =>
|
|
117
|
+
workspaces
|
|
118
|
+
.filter(isPublishableOnTrailsWorkspace)
|
|
119
|
+
.find((workspace) => isUnderWorkspace(filePath, workspace.relativePath));
|
|
120
|
+
|
|
121
|
+
export const createContractSourceSnapshots = (
|
|
122
|
+
input: ContractReleaseFactInput
|
|
123
|
+
): readonly ContractSourceSnapshot[] => {
|
|
124
|
+
const snapshots: ContractSourceSnapshot[] = [];
|
|
125
|
+
|
|
126
|
+
for (const changedFile of input.changedFiles.map(normalizePath)) {
|
|
127
|
+
if (!SOURCE_PATH_PATTERN.test(changedFile)) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const workspace = workspaceForFile(changedFile, input.workspaces);
|
|
132
|
+
if (!workspace) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const workspaceRelativePath = getWorkspaceRelativePath(
|
|
137
|
+
changedFile,
|
|
138
|
+
workspace.relativePath
|
|
139
|
+
);
|
|
140
|
+
if (isNonShippingSourcePath(workspaceRelativePath)) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const baseSource = readBaseSource(
|
|
145
|
+
input.repoRoot,
|
|
146
|
+
input.baseRef,
|
|
147
|
+
changedFile
|
|
148
|
+
);
|
|
149
|
+
const currentSource = readCurrentSource(input.repoRoot, changedFile);
|
|
150
|
+
if (baseSource === null && currentSource === null) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
snapshots.push({
|
|
155
|
+
baseSource,
|
|
156
|
+
changedFiles: [changedFile],
|
|
157
|
+
currentSource,
|
|
158
|
+
packageName: workspace.name,
|
|
159
|
+
path: changedFile,
|
|
160
|
+
workspacePath: workspace.relativePath,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return snapshots;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const isPropertyNamed = (
|
|
168
|
+
property: ts.ObjectLiteralElementLike,
|
|
169
|
+
name: string
|
|
170
|
+
): property is ts.PropertyAssignment => {
|
|
171
|
+
if (!ts.isPropertyAssignment(property)) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const propertyName = property.name;
|
|
176
|
+
return (
|
|
177
|
+
(ts.isIdentifier(propertyName) && propertyName.text === name) ||
|
|
178
|
+
(ts.isStringLiteral(propertyName) && propertyName.text === name)
|
|
179
|
+
);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const findProperty = (
|
|
183
|
+
object: ts.ObjectLiteralExpression,
|
|
184
|
+
name: string
|
|
185
|
+
): ts.PropertyAssignment | undefined =>
|
|
186
|
+
object.properties.find((property) => isPropertyNamed(property, name));
|
|
187
|
+
|
|
188
|
+
const isTrailCall = (node: ts.CallExpression): boolean =>
|
|
189
|
+
ts.isIdentifier(node.expression) && node.expression.text === 'trail';
|
|
190
|
+
|
|
191
|
+
const literalText = (node: ts.Expression | undefined): string | undefined =>
|
|
192
|
+
node && ts.isStringLiteralLike(node) ? node.text : undefined;
|
|
193
|
+
|
|
194
|
+
const normalizeText = (value: string): string =>
|
|
195
|
+
value.replaceAll(/\s+/gu, ' ').trim();
|
|
196
|
+
|
|
197
|
+
const normalizeExpressionText = (
|
|
198
|
+
expression: ts.Expression | undefined,
|
|
199
|
+
sourceFile: ts.SourceFile,
|
|
200
|
+
constInitializers: ReadonlyMap<string, ts.Expression>
|
|
201
|
+
): string | null => {
|
|
202
|
+
if (!expression) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (ts.isIdentifier(expression)) {
|
|
207
|
+
const initializer = constInitializers.get(expression.text);
|
|
208
|
+
if (initializer) {
|
|
209
|
+
return `${expression.text}=${normalizeText(
|
|
210
|
+
initializer.getText(sourceFile)
|
|
211
|
+
)}`;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return normalizeText(expression.getText(sourceFile));
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const visibilityFor = (
|
|
219
|
+
object: ts.ObjectLiteralExpression
|
|
220
|
+
): TrailDefinition['visibility'] => {
|
|
221
|
+
const visibility = findProperty(object, 'visibility');
|
|
222
|
+
const value = literalText(visibility?.initializer);
|
|
223
|
+
if (value === 'internal') {
|
|
224
|
+
return 'internal';
|
|
225
|
+
}
|
|
226
|
+
return 'public';
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const collectConstInitializers = (
|
|
230
|
+
sourceFile: ts.SourceFile
|
|
231
|
+
): ReadonlyMap<string, ts.Expression> => {
|
|
232
|
+
const initializers = new Map<string, ts.Expression>();
|
|
233
|
+
|
|
234
|
+
const visit = (node: ts.Node): void => {
|
|
235
|
+
if (
|
|
236
|
+
ts.isVariableDeclaration(node) &&
|
|
237
|
+
ts.isIdentifier(node.name) &&
|
|
238
|
+
node.initializer
|
|
239
|
+
) {
|
|
240
|
+
initializers.set(node.name.text, node.initializer);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
ts.forEachChild(node, visit);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
visit(sourceFile);
|
|
247
|
+
return initializers;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const collectTrailDefinitions = (
|
|
251
|
+
source: string
|
|
252
|
+
): readonly TrailDefinition[] => {
|
|
253
|
+
const sourceFile = ts.createSourceFile(
|
|
254
|
+
'release-contract-facts.ts',
|
|
255
|
+
source,
|
|
256
|
+
ts.ScriptTarget.Latest,
|
|
257
|
+
true,
|
|
258
|
+
ts.ScriptKind.TS
|
|
259
|
+
);
|
|
260
|
+
const constInitializers = collectConstInitializers(sourceFile);
|
|
261
|
+
const definitions: TrailDefinition[] = [];
|
|
262
|
+
|
|
263
|
+
const visit = (node: ts.Node): void => {
|
|
264
|
+
if (ts.isCallExpression(node) && isTrailCall(node)) {
|
|
265
|
+
const [idArgument, spec] = node.arguments;
|
|
266
|
+
const trailId = literalText(idArgument);
|
|
267
|
+
|
|
268
|
+
if (trailId && spec && ts.isObjectLiteralExpression(spec)) {
|
|
269
|
+
const visibility = visibilityFor(spec);
|
|
270
|
+
definitions.push({
|
|
271
|
+
aspects: {
|
|
272
|
+
input: normalizeExpressionText(
|
|
273
|
+
findProperty(spec, 'input')?.initializer,
|
|
274
|
+
sourceFile,
|
|
275
|
+
constInitializers
|
|
276
|
+
),
|
|
277
|
+
output: normalizeExpressionText(
|
|
278
|
+
findProperty(spec, 'output')?.initializer,
|
|
279
|
+
sourceFile,
|
|
280
|
+
constInitializers
|
|
281
|
+
),
|
|
282
|
+
surfaces: normalizeExpressionText(
|
|
283
|
+
findProperty(spec, 'surfaces')?.initializer,
|
|
284
|
+
sourceFile,
|
|
285
|
+
constInitializers
|
|
286
|
+
),
|
|
287
|
+
trail: normalizeText(trailId),
|
|
288
|
+
visibility,
|
|
289
|
+
},
|
|
290
|
+
trailId,
|
|
291
|
+
visibility,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
ts.forEachChild(node, visit);
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
visit(sourceFile);
|
|
300
|
+
return definitions;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const definitionsById = (
|
|
304
|
+
definitions: readonly TrailDefinition[]
|
|
305
|
+
): ReadonlyMap<string, TrailDefinition> =>
|
|
306
|
+
new Map(definitions.map((definition) => [definition.trailId, definition]));
|
|
307
|
+
|
|
308
|
+
const isPublicContract = (definition: TrailDefinition | undefined): boolean =>
|
|
309
|
+
definition !== undefined && definition.visibility !== 'internal';
|
|
310
|
+
|
|
311
|
+
const fact = (
|
|
312
|
+
snapshot: ContractSourceSnapshot,
|
|
313
|
+
trailId: string,
|
|
314
|
+
aspect: ContractReleaseFactAspect,
|
|
315
|
+
baseValue: string | null,
|
|
316
|
+
currentValue: string | null
|
|
317
|
+
): ContractReleaseFact => ({
|
|
318
|
+
aspect,
|
|
319
|
+
baseHash: hashSource(baseValue),
|
|
320
|
+
changedFiles: snapshot.changedFiles ?? [snapshot.path],
|
|
321
|
+
currentHash: hashSource(currentValue),
|
|
322
|
+
...(snapshot.packageName ? { packageName: snapshot.packageName } : {}),
|
|
323
|
+
path: snapshot.path,
|
|
324
|
+
trailId,
|
|
325
|
+
...(snapshot.workspacePath ? { workspacePath: snapshot.workspacePath } : {}),
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const compareDefinitions = (
|
|
329
|
+
snapshot: ContractSourceSnapshot,
|
|
330
|
+
baseDefinition: TrailDefinition | undefined,
|
|
331
|
+
currentDefinition: TrailDefinition | undefined
|
|
332
|
+
): readonly ContractReleaseFact[] => {
|
|
333
|
+
if (
|
|
334
|
+
!isPublicContract(baseDefinition) &&
|
|
335
|
+
!isPublicContract(currentDefinition)
|
|
336
|
+
) {
|
|
337
|
+
return [];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const trailId =
|
|
341
|
+
currentDefinition?.trailId ?? baseDefinition?.trailId ?? '(unknown)';
|
|
342
|
+
|
|
343
|
+
if (!baseDefinition || !currentDefinition) {
|
|
344
|
+
return [
|
|
345
|
+
fact(
|
|
346
|
+
snapshot,
|
|
347
|
+
trailId,
|
|
348
|
+
'trail',
|
|
349
|
+
baseDefinition?.aspects.trail ?? null,
|
|
350
|
+
currentDefinition?.aspects.trail ?? null
|
|
351
|
+
),
|
|
352
|
+
];
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const facts: ContractReleaseFact[] = [];
|
|
356
|
+
|
|
357
|
+
for (const aspect of ['input', 'output', 'surfaces'] as const) {
|
|
358
|
+
const baseValue = baseDefinition.aspects[aspect];
|
|
359
|
+
const currentValue = currentDefinition.aspects[aspect];
|
|
360
|
+
if (baseValue !== currentValue) {
|
|
361
|
+
facts.push(fact(snapshot, trailId, aspect, baseValue, currentValue));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (baseDefinition.visibility !== currentDefinition.visibility) {
|
|
366
|
+
facts.push(
|
|
367
|
+
fact(
|
|
368
|
+
snapshot,
|
|
369
|
+
trailId,
|
|
370
|
+
'visibility',
|
|
371
|
+
baseDefinition.visibility,
|
|
372
|
+
currentDefinition.visibility
|
|
373
|
+
)
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return facts;
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
export const findPublicTrailContractChangeFactsFromSnapshots = (
|
|
381
|
+
snapshots: readonly ContractSourceSnapshot[]
|
|
382
|
+
): readonly ContractReleaseFact[] => {
|
|
383
|
+
const facts: ContractReleaseFact[] = [];
|
|
384
|
+
|
|
385
|
+
for (const snapshot of snapshots) {
|
|
386
|
+
const baseDefinitions = definitionsById(
|
|
387
|
+
snapshot.baseSource === null
|
|
388
|
+
? []
|
|
389
|
+
: collectTrailDefinitions(snapshot.baseSource)
|
|
390
|
+
);
|
|
391
|
+
const currentDefinitions = definitionsById(
|
|
392
|
+
snapshot.currentSource === null
|
|
393
|
+
? []
|
|
394
|
+
: collectTrailDefinitions(snapshot.currentSource)
|
|
395
|
+
);
|
|
396
|
+
const trailIds = new Set([
|
|
397
|
+
...baseDefinitions.keys(),
|
|
398
|
+
...currentDefinitions.keys(),
|
|
399
|
+
]);
|
|
400
|
+
|
|
401
|
+
for (const trailId of [...trailIds].toSorted()) {
|
|
402
|
+
facts.push(
|
|
403
|
+
...compareDefinitions(
|
|
404
|
+
snapshot,
|
|
405
|
+
baseDefinitions.get(trailId),
|
|
406
|
+
currentDefinitions.get(trailId)
|
|
407
|
+
)
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return facts.toSorted(
|
|
413
|
+
(left, right) =>
|
|
414
|
+
left.path.localeCompare(right.path) ||
|
|
415
|
+
left.trailId.localeCompare(right.trailId) ||
|
|
416
|
+
left.aspect.localeCompare(right.aspect)
|
|
417
|
+
);
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
export const findPublicTrailContractChangeFacts = (
|
|
421
|
+
input: ContractReleaseFactInput
|
|
422
|
+
): readonly ContractReleaseFact[] =>
|
|
423
|
+
findPublicTrailContractChangeFactsFromSnapshots(
|
|
424
|
+
createContractSourceSnapshots(input)
|
|
425
|
+
);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export {
|
|
2
|
+
nativeBunReleaseBinding,
|
|
3
|
+
releaseBindingCapabilityValues,
|
|
4
|
+
releaseBindingKindValues,
|
|
5
|
+
releaseBindingPlacementValues,
|
|
6
|
+
type ReleaseBindingCapability,
|
|
7
|
+
type ReleaseBindingDescriptor,
|
|
8
|
+
type ReleaseBindingKind,
|
|
9
|
+
type ReleaseBindingPlacement,
|
|
10
|
+
} from './bindings.js';
|
|
11
|
+
export {
|
|
12
|
+
checkReleaseRules,
|
|
13
|
+
discoverWorkspaces,
|
|
14
|
+
formatReleaseCheckReport,
|
|
15
|
+
loadReleaseConfig,
|
|
16
|
+
runReleaseCheck,
|
|
17
|
+
runReleaseCheckCli,
|
|
18
|
+
type ReleaseCheckReport,
|
|
19
|
+
type ReleaseCheckInput,
|
|
20
|
+
type ReleaseCheckResult,
|
|
21
|
+
type ReleaseConfigLoadResult,
|
|
22
|
+
type RunReleaseCheckOptions,
|
|
23
|
+
type WorkspaceInfo,
|
|
24
|
+
} from './check.js';
|
|
25
|
+
export {
|
|
26
|
+
createContractSourceSnapshots,
|
|
27
|
+
findPublicTrailContractChangeFacts,
|
|
28
|
+
findPublicTrailContractChangeFactsFromSnapshots,
|
|
29
|
+
type ContractReleaseFact,
|
|
30
|
+
type ContractReleaseFactAspect,
|
|
31
|
+
type ContractReleaseFactInput,
|
|
32
|
+
type ContractSourceSnapshot,
|
|
33
|
+
} from './contract-facts.js';
|
|
34
|
+
export {
|
|
35
|
+
defaultReleaseConfig,
|
|
36
|
+
defaultReleaseRules,
|
|
37
|
+
releaseConfigSchema,
|
|
38
|
+
releaseFactTypeSchema,
|
|
39
|
+
releaseFactTypeValues,
|
|
40
|
+
releaseIntentSourceSchema,
|
|
41
|
+
releaseIntentSourceValues,
|
|
42
|
+
releaseRuleSchema,
|
|
43
|
+
releaseRuleSeveritySchema,
|
|
44
|
+
releaseRuleSeverityValues,
|
|
45
|
+
type ReleaseConfig,
|
|
46
|
+
type ReleaseConfigInput,
|
|
47
|
+
type ReleaseFactType,
|
|
48
|
+
type ReleaseIntentSource,
|
|
49
|
+
type ReleaseRule,
|
|
50
|
+
type ReleaseRuleInput,
|
|
51
|
+
} from './config.js';
|
|
52
|
+
export {
|
|
53
|
+
findPackedFirstPartyDependencyMismatches,
|
|
54
|
+
runNativeBunPublishCli,
|
|
55
|
+
type NativeBunPublishOptions,
|
|
56
|
+
type NativeBunPublishPackageJson,
|
|
57
|
+
type NativeBunPublishWorkspace,
|
|
58
|
+
} from './native-bun-publish.js';
|
|
59
|
+
export {
|
|
60
|
+
checkRegistryPosture,
|
|
61
|
+
discoverRegistryWorkspaces,
|
|
62
|
+
formatDistTagSummary,
|
|
63
|
+
registryPostureErrors,
|
|
64
|
+
runRegistryPreflight,
|
|
65
|
+
runRegistryPreflightCli,
|
|
66
|
+
type RegistryPreflightOptions,
|
|
67
|
+
type RegistryResult,
|
|
68
|
+
type RegistryView,
|
|
69
|
+
type RegistryWorkspace,
|
|
70
|
+
} from './native-bun-registry.js';
|
|
71
|
+
export {
|
|
72
|
+
releaseSmokeCheckValues,
|
|
73
|
+
runReleaseSmoke,
|
|
74
|
+
type ReleaseSmokeCheck,
|
|
75
|
+
type ReleaseSmokeCheckResult,
|
|
76
|
+
type ReleaseSmokeResult,
|
|
77
|
+
} from './smoke.js';
|
|
78
|
+
export {
|
|
79
|
+
runPackedArtifactsSmoke,
|
|
80
|
+
type PackedArtifactsSmokeResult,
|
|
81
|
+
} from './packed-artifacts-smoke.js';
|
|
82
|
+
export {
|
|
83
|
+
runWayfinderDogfoodSmoke,
|
|
84
|
+
type WayfinderDogfoodSmokeResult,
|
|
85
|
+
} from './wayfinder-dogfood-smoke.js';
|