@haystackeditor/cli 0.19.0 → 0.21.0
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/README.md +38 -35
- package/dist/commands/fleet-policy.js +1 -22
- package/dist/commands/init.js +376 -129
- package/dist/commands/install-session-hooks.js +27 -10
- package/dist/commands/login.js +133 -34
- package/dist/commands/onboarding-contract.js +6 -0
- package/dist/commands/setup.js +1 -2
- package/dist/commands/verify-onboarding.js +365 -0
- package/dist/commands/verify-precompute.js +37 -14
- package/dist/commands/verify.js +108 -25
- package/dist/index.js +99 -26
- package/dist/schema.js +5 -1
- package/dist/utils/config.js +0 -16
- package/dist/utils/haystack-api.js +12 -1
- package/dist/utils/secrets.js +0 -51
- package/dist/utils/telemetry.js +32 -0
- package/package.json +2 -2
- package/schemas/init.v1.json +35 -0
- package/schemas/login.v1.json +17 -0
- package/schemas/verify-answer.v1.json +86 -0
- package/schemas/verify-onboarding.v1.json +13 -0
- package/schemas/verify.v1.json +270 -2
- package/dist/utils/detect.js +0 -507
|
@@ -45,6 +45,13 @@ export const SUBSCRIPTION_REQUIRED_MESSAGE = 'This account has no active Haystac
|
|
|
45
45
|
* repository and team). Its message names the repository; print it as is.
|
|
46
46
|
*/
|
|
47
47
|
export const REPO_NOT_ENTITLED_CODE = 'repo_not_entitled';
|
|
48
|
+
/**
|
|
49
|
+
* The server returns this `code` (HTTP 403, beside a readable `error`) when the
|
|
50
|
+
* repository's owner has not installed the Haystack GitHub App, which the
|
|
51
|
+
* service needs to read the repository.
|
|
52
|
+
*/
|
|
53
|
+
export const INSTALLATION_REQUIRED_CODE = 'installation-required';
|
|
54
|
+
export const HAYSTACK_APP_INSTALL_URL = 'https://github.com/apps/haystack-code-reviewer-pr-hook/installations/new';
|
|
48
55
|
/**
|
|
49
56
|
* Extract the server's own message (and its `error` code) if the body is
|
|
50
57
|
* JSON; truncate raw bodies.
|
|
@@ -66,7 +73,8 @@ async function serverMessage(response) {
|
|
|
66
73
|
const parsed = JSON.parse(text);
|
|
67
74
|
return {
|
|
68
75
|
detail: parsed.message ?? parsed.error ?? null,
|
|
69
|
-
|
|
76
|
+
// Routes that answer { error: <readable text>, code } carry the stable code in `code`.
|
|
77
|
+
code: typeof parsed.code === 'string' ? parsed.code : typeof parsed.error === 'string' ? parsed.error : null,
|
|
70
78
|
};
|
|
71
79
|
}
|
|
72
80
|
catch {
|
|
@@ -90,6 +98,9 @@ export async function classifyHttpError(response, context) {
|
|
|
90
98
|
: 'This account is not entitled to use Haystack on this repository. See https://haystack.sh/pricing or email akshay@haystack.sh to get access.';
|
|
91
99
|
return new HaystackApiError(403, message, code);
|
|
92
100
|
}
|
|
101
|
+
if (response.status === 403 && code === INSTALLATION_REQUIRED_CODE) {
|
|
102
|
+
return new HaystackApiError(403, `${detail ?? 'Install the Haystack GitHub App on the repository\'s owner'}: ${HAYSTACK_APP_INSTALL_URL}`, code);
|
|
103
|
+
}
|
|
93
104
|
if (response.status === 401) {
|
|
94
105
|
return new HaystackApiError(401, `${context}: authentication failed${suffix}. Run \`haystack login\` to re-authenticate.`);
|
|
95
106
|
}
|
package/dist/utils/secrets.js
CHANGED
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
* - detect-secrets
|
|
10
10
|
* - truffleHog
|
|
11
11
|
*/
|
|
12
|
-
import * as fs from 'fs/promises';
|
|
13
|
-
import * as path from 'path';
|
|
14
12
|
/**
|
|
15
13
|
* Common secret patterns
|
|
16
14
|
*/
|
|
@@ -188,52 +186,3 @@ export function scanForSecrets(content, filename) {
|
|
|
188
186
|
}
|
|
189
187
|
return findings;
|
|
190
188
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Scan a file for secrets
|
|
193
|
-
*/
|
|
194
|
-
export async function scanFile(filePath) {
|
|
195
|
-
try {
|
|
196
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
197
|
-
return scanForSecrets(content, path.basename(filePath));
|
|
198
|
-
}
|
|
199
|
-
catch {
|
|
200
|
-
return [];
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Scan .haystack.json specifically for secrets
|
|
205
|
-
*/
|
|
206
|
-
export async function scanHaystackConfig(configPath) {
|
|
207
|
-
return scanFile(configPath);
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Validate that a config doesn't contain hardcoded secrets
|
|
211
|
-
* Returns true if safe, false if secrets detected
|
|
212
|
-
*/
|
|
213
|
-
export async function validateConfigSecurity(configPath) {
|
|
214
|
-
const findings = await scanHaystackConfig(configPath);
|
|
215
|
-
const highSeverity = findings.filter((f) => f.severity === 'high');
|
|
216
|
-
return {
|
|
217
|
-
safe: highSeverity.length === 0,
|
|
218
|
-
findings,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Get a security report string for display
|
|
223
|
-
*/
|
|
224
|
-
export function formatSecurityReport(findings) {
|
|
225
|
-
if (findings.length === 0) {
|
|
226
|
-
return '✓ No secrets detected';
|
|
227
|
-
}
|
|
228
|
-
const lines = [
|
|
229
|
-
`⚠️ ${findings.length} potential secret(s) detected:\n`,
|
|
230
|
-
];
|
|
231
|
-
for (const finding of findings) {
|
|
232
|
-
const severity = finding.severity === 'high' ? '🔴' : finding.severity === 'medium' ? '🟡' : '🟢';
|
|
233
|
-
lines.push(` ${severity} ${finding.file}:${finding.line}:${finding.column}`);
|
|
234
|
-
lines.push(` ${finding.description}`);
|
|
235
|
-
lines.push(` Matched: ${finding.match}\n`);
|
|
236
|
-
}
|
|
237
|
-
lines.push('Use $VARIABLE syntax to reference secrets instead of hardcoding values.\n');
|
|
238
|
-
return lines.join('\n');
|
|
239
|
-
}
|
package/dist/utils/telemetry.js
CHANGED
|
@@ -197,6 +197,38 @@ export function trackPrecomputeEvent(outcome, wallMs, timeoutMs) {
|
|
|
197
197
|
// Telemetry must never crash or delay the CLI.
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
|
+
/** Track one onboarding state `haystack verify` saw: started, blocked (with the block's kind) or ready. */
|
|
201
|
+
export function trackOnboardingEvent(outcome, kind) {
|
|
202
|
+
try {
|
|
203
|
+
if (telemetryDisabled() || typeof globalThis.fetch !== 'function')
|
|
204
|
+
return;
|
|
205
|
+
const event = {
|
|
206
|
+
api_key: POSTHOG_API_KEY,
|
|
207
|
+
distinct_id: getDistinctId(),
|
|
208
|
+
event: 'cli_verify_onboarding',
|
|
209
|
+
properties: {
|
|
210
|
+
cli_version: getCLIVersion(),
|
|
211
|
+
os: process.platform,
|
|
212
|
+
arch: process.arch,
|
|
213
|
+
timestamp: new Date().toISOString(),
|
|
214
|
+
...minimizeMetadata({ outcome, ...(kind === undefined ? {} : { kind }) }),
|
|
215
|
+
$ip: null,
|
|
216
|
+
},
|
|
217
|
+
timestamp: new Date().toISOString(),
|
|
218
|
+
};
|
|
219
|
+
globalThis.fetch(`${POSTHOG_HOST}/capture/`, {
|
|
220
|
+
method: 'POST',
|
|
221
|
+
headers: { 'Content-Type': 'application/json' },
|
|
222
|
+
body: JSON.stringify(event),
|
|
223
|
+
}).catch(() => {
|
|
224
|
+
// Documented fire-and-forget product telemetry: delivery cannot affect
|
|
225
|
+
// the CLI command being measured.
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
// Telemetry must never crash the CLI
|
|
230
|
+
}
|
|
231
|
+
}
|
|
200
232
|
function getCLIVersion() {
|
|
201
233
|
try {
|
|
202
234
|
const packageJson = JSON.parse(readFileSync(PACKAGE_JSON_PATH, 'utf8'));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haystackeditor/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.21.0",
|
|
4
|
+
"description": "haystack verify: run your app with and without a change, and see what the change broke",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"haystack": "dist/index.js"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://docs.haystackeditor.com/cli/schemas/init.v1.json",
|
|
4
|
+
"title": "Haystack init: this repository's setup for `haystack verify`",
|
|
5
|
+
"description": "Machine-readable output of `haystack init --json`. `changes` are the project-local changes init planned (made when status is done); `onboarding` is the base's OnboardingStatusV1 (onboarding_contracts.ts, APP-ONBOARDING-V1) as POST /api/agent/cloud-verifier/onboarding returns it, null unless status is done. Exit codes: 0 done (onboarding running or ready), 1 failed (the error envelope), 2 planned (nothing written; run with --yes), 3 done but onboarding blocked, 4 login-required, 5 app-required (install the Haystack GitHub App), 6 done but onboarding stopped before finishing (run init again).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["schema_version", "status", "repository", "changes", "notices", "onboarding"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": { "const": "1.0.0" },
|
|
11
|
+
"status": { "enum": ["done", "planned", "login-required", "app-required"] },
|
|
12
|
+
"repository": { "type": "string" },
|
|
13
|
+
"changes": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"additionalProperties": false,
|
|
18
|
+
"required": ["path", "action", "reason", "diff"],
|
|
19
|
+
"properties": {
|
|
20
|
+
"path": { "type": "string" },
|
|
21
|
+
"action": { "enum": ["create", "update"] },
|
|
22
|
+
"reason": { "type": "string" },
|
|
23
|
+
"diff": { "type": "array", "items": { "type": "string" } }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"notices": { "type": "array", "items": { "type": "string" } },
|
|
28
|
+
"onboarding": {
|
|
29
|
+
"oneOf": [
|
|
30
|
+
{ "type": "null" },
|
|
31
|
+
{ "$ref": "verify.v1.json#/$defs/onboardingStatus" }
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://docs.haystackeditor.com/cli/schemas/login.v1.json",
|
|
4
|
+
"title": "Haystack login: a GitHub device-flow sign-in",
|
|
5
|
+
"description": "Machine-readable output of `haystack login --json`. With --no-wait, status is pending and carries the URL to open and the code to enter (never the device code); the next `haystack login` finishes it. Otherwise status is logged-in with the account signed in.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["schema_version", "status", "verificationUri", "userCode", "expiresAt", "login"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": { "const": "1.0.0" },
|
|
11
|
+
"status": { "enum": ["pending", "logged-in"] },
|
|
12
|
+
"verificationUri": { "type": ["string", "null"] },
|
|
13
|
+
"userCode": { "type": ["string", "null"] },
|
|
14
|
+
"expiresAt": { "type": ["string", "null"], "format": "date-time" },
|
|
15
|
+
"login": { "type": ["string", "null"] }
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://docs.haystackeditor.com/cli/schemas/verify-answer.v1.json",
|
|
4
|
+
"title": "Haystack verify answer: the repository's onboarding facts after an answer",
|
|
5
|
+
"description": "Machine-readable output of `haystack verify answer <question-id> <choice> --json`: OnboardingFactsView (onboarding_contracts.ts, APP-ONBOARDING-V1) as POST /api/agent/cloud-verifier/onboarding/answers returns it.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": [
|
|
9
|
+
"schema_version",
|
|
10
|
+
"facts"
|
|
11
|
+
],
|
|
12
|
+
"properties": {
|
|
13
|
+
"schema_version": {
|
|
14
|
+
"const": "1.0.0"
|
|
15
|
+
},
|
|
16
|
+
"facts": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"required": [
|
|
19
|
+
"repository",
|
|
20
|
+
"facts",
|
|
21
|
+
"factsDigest",
|
|
22
|
+
"openQuestions"
|
|
23
|
+
],
|
|
24
|
+
"properties": {
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "string"
|
|
27
|
+
},
|
|
28
|
+
"factsDigest": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
31
|
+
},
|
|
32
|
+
"facts": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"required": [
|
|
35
|
+
"dbProfile",
|
|
36
|
+
"answers"
|
|
37
|
+
],
|
|
38
|
+
"properties": {
|
|
39
|
+
"dbProfile": {
|
|
40
|
+
"oneOf": [
|
|
41
|
+
{
|
|
42
|
+
"type": "null"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"type": "string",
|
|
46
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
"answers": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"additionalProperties": {
|
|
53
|
+
"type": "string"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"openQuestions": {
|
|
59
|
+
"type": "array",
|
|
60
|
+
"items": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"required": [
|
|
63
|
+
"id",
|
|
64
|
+
"message",
|
|
65
|
+
"choices"
|
|
66
|
+
],
|
|
67
|
+
"properties": {
|
|
68
|
+
"id": {
|
|
69
|
+
"type": "string"
|
|
70
|
+
},
|
|
71
|
+
"message": {
|
|
72
|
+
"type": "string"
|
|
73
|
+
},
|
|
74
|
+
"choices": {
|
|
75
|
+
"type": "array",
|
|
76
|
+
"items": {
|
|
77
|
+
"type": "string"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://docs.haystackeditor.com/cli/schemas/verify-onboarding.v1.json",
|
|
4
|
+
"title": "Haystack verify onboarding: the onboarding of this checkout's base",
|
|
5
|
+
"description": "Machine-readable output of `haystack verify onboarding --json`: OnboardingStatusV1 (onboarding_contracts.ts, APP-ONBOARDING-V1) of the base `haystack verify` would use, as GET /api/agent/cloud-verifier/onboarding?repository=&baseCommit= returns it.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["schema_version", "onboarding"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": { "const": "1.0.0" },
|
|
11
|
+
"onboarding": { "$ref": "verify.v1.json#/$defs/onboardingStatus" }
|
|
12
|
+
}
|
|
13
|
+
}
|
package/schemas/verify.v1.json
CHANGED
|
@@ -2,12 +2,33 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://docs.haystackeditor.com/cli/schemas/verify.v1.json",
|
|
4
4
|
"title": "Haystack verify: the crawl of the current change (CRAWL-V1)",
|
|
5
|
-
"description": "Machine-readable output of `haystack verify --json`: the caller's crawl of the exact captured checkout (its code and its change title), as GET /api/agent/cloud-verifier/crawls?repository=&workCommit= or, for a crawl the command just submitted, GET /api/agent/cloud-verifier/crawls/:runId?repository= returns it, or null when no crawl of the capture could be started. The crawl shapes mirror infra/lambda/haystack-design-verifier-shared/crawl_contracts.ts (with amendments 1, 8 and
|
|
5
|
+
"description": "Machine-readable output of `haystack verify --json`: the caller's crawl of the exact captured checkout (its code and its change title), as GET /api/agent/cloud-verifier/crawls?repository=&workCommit= or, for a crawl the command just submitted, GET /api/agent/cloud-verifier/crawls/:runId?repository= returns it, or null when no crawl of the capture could be started. The crawl shapes mirror infra/lambda/haystack-design-verifier-shared/crawl_contracts.ts (with amendments 1, 8, 9, 10 and 11); that file wins where they differ. Crawl amendment 11 (schema 1.0.1): when the capture's base is not onboarded yet, `onboarding` is the base's OnboardingStatusV1 (onboarding_contracts.ts, APP-ONBOARDING-V1) and `crawl` is null until the app is ready; it is null when onboarding was not involved.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": ["schema_version", "crawl"],
|
|
9
9
|
"properties": {
|
|
10
|
-
"
|
|
10
|
+
"onboarding": {
|
|
11
|
+
"oneOf": [
|
|
12
|
+
{
|
|
13
|
+
"type": "null"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"$ref": "#/$defs/onboardingStatus"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"schema_version": { "const": "1.0.2" },
|
|
21
|
+
"change": {
|
|
22
|
+
"description": "Schema 1.0.2: what this verify checked: the capture's base commit (where HEAD meets origin's default branch), its work commit (committed and uncommitted changes), and the files that differ between them.",
|
|
23
|
+
"type": "object",
|
|
24
|
+
"additionalProperties": false,
|
|
25
|
+
"required": ["baseCommit", "workCommit", "files"],
|
|
26
|
+
"properties": {
|
|
27
|
+
"baseCommit": { "$ref": "#/$defs/commit" },
|
|
28
|
+
"workCommit": { "$ref": "#/$defs/commit" },
|
|
29
|
+
"files": { "type": "array", "items": { "type": "string" } }
|
|
30
|
+
}
|
|
31
|
+
},
|
|
11
32
|
"crawl": {
|
|
12
33
|
"oneOf": [
|
|
13
34
|
{ "type": "null" },
|
|
@@ -16,6 +37,253 @@
|
|
|
16
37
|
}
|
|
17
38
|
},
|
|
18
39
|
"$defs": {
|
|
40
|
+
"onboardingBlock": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"required": [
|
|
43
|
+
"kind",
|
|
44
|
+
"stage",
|
|
45
|
+
"reason",
|
|
46
|
+
"evidence",
|
|
47
|
+
"ask"
|
|
48
|
+
],
|
|
49
|
+
"properties": {
|
|
50
|
+
"kind": {
|
|
51
|
+
"enum": [
|
|
52
|
+
"not-enabled",
|
|
53
|
+
"no-web-app",
|
|
54
|
+
"unsupported",
|
|
55
|
+
"question",
|
|
56
|
+
"outside-credential",
|
|
57
|
+
"outside-dependency",
|
|
58
|
+
"evidence-too-large",
|
|
59
|
+
"budget",
|
|
60
|
+
"proof-failed"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"stage": {
|
|
64
|
+
"enum": [
|
|
65
|
+
"admission",
|
|
66
|
+
"inventory",
|
|
67
|
+
"plan",
|
|
68
|
+
"runtime",
|
|
69
|
+
"boot",
|
|
70
|
+
"data",
|
|
71
|
+
"accounts",
|
|
72
|
+
"workflow",
|
|
73
|
+
"publish",
|
|
74
|
+
"cleanup"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"reason": {
|
|
78
|
+
"type": "string"
|
|
79
|
+
},
|
|
80
|
+
"evidence": {
|
|
81
|
+
"$ref": "#/$defs/strings"
|
|
82
|
+
},
|
|
83
|
+
"names": {
|
|
84
|
+
"$ref": "#/$defs/strings"
|
|
85
|
+
},
|
|
86
|
+
"ask": {
|
|
87
|
+
"oneOf": [
|
|
88
|
+
{
|
|
89
|
+
"type": "null"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"type": "object",
|
|
93
|
+
"required": [
|
|
94
|
+
"message"
|
|
95
|
+
],
|
|
96
|
+
"properties": {
|
|
97
|
+
"message": {
|
|
98
|
+
"type": "string"
|
|
99
|
+
},
|
|
100
|
+
"command": {
|
|
101
|
+
"type": "string"
|
|
102
|
+
},
|
|
103
|
+
"question": {
|
|
104
|
+
"type": "object",
|
|
105
|
+
"required": [
|
|
106
|
+
"id",
|
|
107
|
+
"choices"
|
|
108
|
+
],
|
|
109
|
+
"properties": {
|
|
110
|
+
"id": {
|
|
111
|
+
"type": "string"
|
|
112
|
+
},
|
|
113
|
+
"choices": {
|
|
114
|
+
"$ref": "#/$defs/strings"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"onboardingStatus": {
|
|
125
|
+
"type": "object",
|
|
126
|
+
"required": [
|
|
127
|
+
"version",
|
|
128
|
+
"repository",
|
|
129
|
+
"baseCommit",
|
|
130
|
+
"state",
|
|
131
|
+
"onboardRunId",
|
|
132
|
+
"recordDigest",
|
|
133
|
+
"checkpoint",
|
|
134
|
+
"block",
|
|
135
|
+
"dataAbsences",
|
|
136
|
+
"versionChoices"
|
|
137
|
+
],
|
|
138
|
+
"properties": {
|
|
139
|
+
"version": {
|
|
140
|
+
"const": "app-onboarding-v1"
|
|
141
|
+
},
|
|
142
|
+
"repository": {
|
|
143
|
+
"type": "string"
|
|
144
|
+
},
|
|
145
|
+
"baseCommit": {
|
|
146
|
+
"$ref": "#/$defs/commit"
|
|
147
|
+
},
|
|
148
|
+
"state": {
|
|
149
|
+
"enum": [
|
|
150
|
+
"ready",
|
|
151
|
+
"onboarding",
|
|
152
|
+
"blocked",
|
|
153
|
+
"incomplete",
|
|
154
|
+
"cancelled",
|
|
155
|
+
"not-started"
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
"onboardRunId": {
|
|
159
|
+
"oneOf": [
|
|
160
|
+
{
|
|
161
|
+
"type": "null"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"type": "string",
|
|
165
|
+
"pattern": "^ob_[0-9a-f]{48}$"
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
"recordDigest": {
|
|
170
|
+
"oneOf": [
|
|
171
|
+
{
|
|
172
|
+
"type": "null"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"type": "string",
|
|
176
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
"checkpoint": {
|
|
181
|
+
"oneOf": [
|
|
182
|
+
{
|
|
183
|
+
"type": "null"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"type": "object",
|
|
187
|
+
"required": [
|
|
188
|
+
"status",
|
|
189
|
+
"stage",
|
|
190
|
+
"execution",
|
|
191
|
+
"owned"
|
|
192
|
+
],
|
|
193
|
+
"properties": {
|
|
194
|
+
"status": {
|
|
195
|
+
"enum": [
|
|
196
|
+
"queued",
|
|
197
|
+
"running",
|
|
198
|
+
"cleanup-pending",
|
|
199
|
+
"published",
|
|
200
|
+
"reused",
|
|
201
|
+
"blocked",
|
|
202
|
+
"incomplete",
|
|
203
|
+
"cancelled"
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
"stage": {
|
|
207
|
+
"enum": [
|
|
208
|
+
"inventory",
|
|
209
|
+
"plan",
|
|
210
|
+
"runtime",
|
|
211
|
+
"boot",
|
|
212
|
+
"data",
|
|
213
|
+
"accounts",
|
|
214
|
+
"workflow",
|
|
215
|
+
"publish",
|
|
216
|
+
"cleanup"
|
|
217
|
+
]
|
|
218
|
+
},
|
|
219
|
+
"owned": {
|
|
220
|
+
"$ref": "#/$defs/strings"
|
|
221
|
+
},
|
|
222
|
+
"execution": {
|
|
223
|
+
"type": "object",
|
|
224
|
+
"required": [
|
|
225
|
+
"stages"
|
|
226
|
+
],
|
|
227
|
+
"properties": {
|
|
228
|
+
"stages": {
|
|
229
|
+
"type": "array",
|
|
230
|
+
"items": {
|
|
231
|
+
"type": "object",
|
|
232
|
+
"required": [
|
|
233
|
+
"stage",
|
|
234
|
+
"status"
|
|
235
|
+
],
|
|
236
|
+
"properties": {
|
|
237
|
+
"stage": {
|
|
238
|
+
"enum": [
|
|
239
|
+
"inventory",
|
|
240
|
+
"plan",
|
|
241
|
+
"runtime",
|
|
242
|
+
"boot",
|
|
243
|
+
"data",
|
|
244
|
+
"accounts",
|
|
245
|
+
"workflow",
|
|
246
|
+
"publish",
|
|
247
|
+
"cleanup"
|
|
248
|
+
]
|
|
249
|
+
},
|
|
250
|
+
"status": {
|
|
251
|
+
"enum": [
|
|
252
|
+
"pending",
|
|
253
|
+
"running",
|
|
254
|
+
"ok",
|
|
255
|
+
"reused",
|
|
256
|
+
"failed",
|
|
257
|
+
"blocked"
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
"block": {
|
|
270
|
+
"oneOf": [
|
|
271
|
+
{
|
|
272
|
+
"type": "null"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"$ref": "#/$defs/onboardingBlock"
|
|
276
|
+
}
|
|
277
|
+
]
|
|
278
|
+
},
|
|
279
|
+
"dataAbsences": {
|
|
280
|
+
"$ref": "#/$defs/strings"
|
|
281
|
+
},
|
|
282
|
+
"versionChoices": {
|
|
283
|
+
"$ref": "#/$defs/strings"
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
},
|
|
19
287
|
"runId": { "type": "string", "pattern": "^cv_[0-9a-f]{48}$" },
|
|
20
288
|
"commit": { "type": "string", "pattern": "^[0-9a-f]{40}$" },
|
|
21
289
|
"count": { "type": "integer", "minimum": 0 },
|