@miller-tech/uap 1.46.7 → 1.48.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/dist/.tsbuildinfo +1 -1
- package/dist/bin/cli.js +9 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/cli/deliver.d.ts +14 -0
- package/dist/cli/deliver.d.ts.map +1 -1
- package/dist/cli/deliver.js +204 -11
- package/dist/cli/deliver.js.map +1 -1
- package/dist/cli/setup.d.ts +1 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +19 -0
- package/dist/cli/setup.js.map +1 -1
- package/dist/delivery/agentic-executor.d.ts +8 -0
- package/dist/delivery/agentic-executor.d.ts.map +1 -1
- package/dist/delivery/agentic-executor.js +12 -2
- package/dist/delivery/agentic-executor.js.map +1 -1
- package/dist/delivery/applier.d.ts +8 -0
- package/dist/delivery/applier.d.ts.map +1 -1
- package/dist/delivery/applier.js +35 -1
- package/dist/delivery/applier.js.map +1 -1
- package/dist/delivery/auto-optimizer.d.ts +11 -0
- package/dist/delivery/auto-optimizer.d.ts.map +1 -1
- package/dist/delivery/auto-optimizer.js +11 -2
- package/dist/delivery/auto-optimizer.js.map +1 -1
- package/dist/delivery/ci-watcher.d.ts +72 -0
- package/dist/delivery/ci-watcher.d.ts.map +1 -0
- package/dist/delivery/ci-watcher.js +221 -0
- package/dist/delivery/ci-watcher.js.map +1 -0
- package/dist/delivery/deploy-dev-gate.d.ts +44 -0
- package/dist/delivery/deploy-dev-gate.d.ts.map +1 -0
- package/dist/delivery/deploy-dev-gate.js +175 -0
- package/dist/delivery/deploy-dev-gate.js.map +1 -0
- package/dist/delivery/verifier-ladder.d.ts +77 -0
- package/dist/delivery/verifier-ladder.d.ts.map +1 -1
- package/dist/delivery/verifier-ladder.js +225 -3
- package/dist/delivery/verifier-ladder.js.map +1 -1
- package/dist/utils/self-update.d.ts +53 -0
- package/dist/utils/self-update.d.ts.map +1 -0
- package/dist/utils/self-update.js +135 -0
- package/dist/utils/self-update.js.map +1 -0
- package/package.json +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI watcher: the commit/push boundary between local convergence and CI/CD.
|
|
3
|
+
*
|
|
4
|
+
* Once the convergence loop reaches local-green, this module commits the
|
|
5
|
+
* applied files on the current worktree branch, pushes (never force), resolves
|
|
6
|
+
* the CI run triggered for that exact commit (matched by head SHA, not "latest
|
|
7
|
+
* run"), watches it to a conclusion, and — on failure — converts the failed
|
|
8
|
+
* logs into sanitized convergence feedback so the loop can re-converge against
|
|
9
|
+
* what CI/staging/prod actually reported.
|
|
10
|
+
*
|
|
11
|
+
* Honors the repo conventions:
|
|
12
|
+
* - never pushes `master`/`main` directly (merge-to-master stays in the PR
|
|
13
|
+
* flow); a protected branch yields `status: 'skipped'`.
|
|
14
|
+
* - never force-pushes.
|
|
15
|
+
* - sanitizes secrets out of CI logs before they enter a model prompt.
|
|
16
|
+
*/
|
|
17
|
+
export interface ExecResult {
|
|
18
|
+
code: number | null;
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
}
|
|
22
|
+
export type ExecFn = (command: string, args: string[], opts?: {
|
|
23
|
+
cwd?: string;
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
}) => Promise<ExecResult>;
|
|
26
|
+
export type DeployEnvironment = 'dev' | 'staging' | 'prod';
|
|
27
|
+
export interface CiWatchOptions {
|
|
28
|
+
projectRoot: string;
|
|
29
|
+
/** Branch to push. Resolved from HEAD when omitted. */
|
|
30
|
+
branch?: string;
|
|
31
|
+
/** Remote name (default 'origin'). */
|
|
32
|
+
remote?: string;
|
|
33
|
+
commitMessage: string;
|
|
34
|
+
/** Files to stage; empty ⇒ `git add -A`. */
|
|
35
|
+
files: string[];
|
|
36
|
+
/** Poll interval while watching (default 15s). */
|
|
37
|
+
pollIntervalMs?: number;
|
|
38
|
+
/** Overall watch budget (default 20 min). */
|
|
39
|
+
timeoutMs?: number;
|
|
40
|
+
/** Require these environments' deploy jobs to be green (job names `deploy-<env>`). */
|
|
41
|
+
watchEnvironments?: DeployEnvironment[];
|
|
42
|
+
/** Max retries resolving the run after push (default 8). */
|
|
43
|
+
resolveAttempts?: number;
|
|
44
|
+
/** Max chars of failed-log feedback (default 4000). */
|
|
45
|
+
outputTailChars?: number;
|
|
46
|
+
/** Injectable command runner (tests); defaults to a real execFile runner. */
|
|
47
|
+
exec?: ExecFn;
|
|
48
|
+
/** Injectable sleep (tests); defaults to real setTimeout. */
|
|
49
|
+
sleep?: (ms: number) => Promise<void>;
|
|
50
|
+
/** Progress/heartbeat callback (keeps the coordinator from marking stale). */
|
|
51
|
+
onProgress?: (message: string) => void;
|
|
52
|
+
}
|
|
53
|
+
export type CiWatchStatus = 'green' | 'failed' | 'timeout' | 'no-run' | 'skipped';
|
|
54
|
+
export interface CiWatchResult {
|
|
55
|
+
pushed: boolean;
|
|
56
|
+
runId: string | null;
|
|
57
|
+
status: CiWatchStatus;
|
|
58
|
+
/** Sanitized failure feedback for the next convergence pass. */
|
|
59
|
+
feedback?: string;
|
|
60
|
+
runUrl?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Strip secrets out of CI logs before they are injected into a model prompt.
|
|
64
|
+
* Masks common token shapes and `KEY=secret` assignments for sensitive keys.
|
|
65
|
+
*/
|
|
66
|
+
export declare function sanitizeCiLog(text: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* Commit applied files on the worktree branch, push (non-force), then watch the
|
|
69
|
+
* triggered CI run and report a status the convergence loop can act on.
|
|
70
|
+
*/
|
|
71
|
+
export declare function commitPushAndWatch(options: CiWatchOptions): Promise<CiWatchResult>;
|
|
72
|
+
//# sourceMappingURL=ci-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-watcher.d.ts","sourceRoot":"","sources":["../../src/delivery/ci-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,MAAM,GAAG,CACnB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,KACxC,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACxC,4DAA4D;IAC5D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,8EAA8E;IAC9E,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElF,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,aAAa,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA4BD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAyBlD;AAqBD;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CA0ExF"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI watcher: the commit/push boundary between local convergence and CI/CD.
|
|
3
|
+
*
|
|
4
|
+
* Once the convergence loop reaches local-green, this module commits the
|
|
5
|
+
* applied files on the current worktree branch, pushes (never force), resolves
|
|
6
|
+
* the CI run triggered for that exact commit (matched by head SHA, not "latest
|
|
7
|
+
* run"), watches it to a conclusion, and — on failure — converts the failed
|
|
8
|
+
* logs into sanitized convergence feedback so the loop can re-converge against
|
|
9
|
+
* what CI/staging/prod actually reported.
|
|
10
|
+
*
|
|
11
|
+
* Honors the repo conventions:
|
|
12
|
+
* - never pushes `master`/`main` directly (merge-to-master stays in the PR
|
|
13
|
+
* flow); a protected branch yields `status: 'skipped'`.
|
|
14
|
+
* - never force-pushes.
|
|
15
|
+
* - sanitizes secrets out of CI logs before they enter a model prompt.
|
|
16
|
+
*/
|
|
17
|
+
import { execFile as execFileCb } from 'child_process';
|
|
18
|
+
const PROTECTED_BRANCHES = new Set(['master', 'main']);
|
|
19
|
+
const DEFAULT_POLL_MS = 15_000;
|
|
20
|
+
const DEFAULT_TIMEOUT_MS = 20 * 60_000;
|
|
21
|
+
const DEFAULT_RESOLVE_ATTEMPTS = 8;
|
|
22
|
+
const DEFAULT_TAIL_CHARS = 4_000;
|
|
23
|
+
const defaultExec = (command, args, opts = {}) => new Promise((resolve) => {
|
|
24
|
+
execFileCb(command, args, { cwd: opts.cwd, timeout: opts.timeoutMs, maxBuffer: 10 * 1024 * 1024, encoding: 'utf-8' }, (error, stdout, stderr) => {
|
|
25
|
+
const code = error && typeof error.code === 'number'
|
|
26
|
+
? (error.code)
|
|
27
|
+
: error
|
|
28
|
+
? 1
|
|
29
|
+
: 0;
|
|
30
|
+
resolve({ code, stdout: stdout ?? '', stderr: stderr ?? '' });
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
34
|
+
/**
|
|
35
|
+
* Strip secrets out of CI logs before they are injected into a model prompt.
|
|
36
|
+
* Masks common token shapes and `KEY=secret` assignments for sensitive keys.
|
|
37
|
+
*/
|
|
38
|
+
export function sanitizeCiLog(text) {
|
|
39
|
+
return (text
|
|
40
|
+
// GitHub tokens (PAT, OAuth, server, fine-grained) and generic gh* tokens
|
|
41
|
+
.replace(/\b(gh[pousr]|github_pat)_[A-Za-z0-9_]{20,}\b/g, '***')
|
|
42
|
+
// Provider tokens: Slack, Stripe, Google API, npm, OpenAI/Anthropic-style
|
|
43
|
+
.replace(/\bxox[baprs]-[A-Za-z0-9-]{10,}/g, '***')
|
|
44
|
+
.replace(/\bsk_(live|test)_[A-Za-z0-9]{16,}\b/g, '***')
|
|
45
|
+
.replace(/\bAIza[0-9A-Za-z_-]{35}\b/g, '***')
|
|
46
|
+
.replace(/\bnpm_[A-Za-z0-9]{36}\b/g, '***')
|
|
47
|
+
.replace(/\bsk-[A-Za-z0-9_-]{20,}\b/g, '***')
|
|
48
|
+
// JWTs (header.payload.signature) — contain '.'/'-', so the base64 rule misses them
|
|
49
|
+
.replace(/\beyJ[A-Za-z0-9._-]{20,}\b/g, '***')
|
|
50
|
+
// Bearer / token headers (allow base64 chars +/=)
|
|
51
|
+
.replace(/\b(Bearer|token)\s+[A-Za-z0-9._/+=-]{12,}/gi, '$1 ***')
|
|
52
|
+
// AWS access key ids
|
|
53
|
+
.replace(/\bAKIA[0-9A-Z]{16}\b/g, '***')
|
|
54
|
+
// KEY=value / KEY: value assignments for sensitive-looking keys
|
|
55
|
+
.replace(/\b([A-Z0-9_]*(?:API_?KEY|TOKEN|SECRET|PASSWORD|PASSWD|PWD|CREDENTIAL|PRIVATE_KEY|AUTH|ACCESS_KEY)[A-Z0-9_]*)\s*[=:]\s*\S+/gi, '$1=***')
|
|
56
|
+
// long base64-ish blobs (>=40 chars) that look like encoded secrets
|
|
57
|
+
.replace(/\b[A-Za-z0-9+/]{40,}={0,2}\b/g, '***'));
|
|
58
|
+
}
|
|
59
|
+
function truncateTail(text, maxChars) {
|
|
60
|
+
const trimmed = text.trim();
|
|
61
|
+
if (trimmed.length <= maxChars)
|
|
62
|
+
return trimmed;
|
|
63
|
+
return `…(truncated)…\n${trimmed.slice(-maxChars)}`;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Commit applied files on the worktree branch, push (non-force), then watch the
|
|
67
|
+
* triggered CI run and report a status the convergence loop can act on.
|
|
68
|
+
*/
|
|
69
|
+
export async function commitPushAndWatch(options) {
|
|
70
|
+
const exec = options.exec ?? defaultExec;
|
|
71
|
+
const sleep = options.sleep ?? defaultSleep;
|
|
72
|
+
const remote = options.remote ?? 'origin';
|
|
73
|
+
const cwd = options.projectRoot;
|
|
74
|
+
const progress = options.onProgress ?? (() => undefined);
|
|
75
|
+
const tailChars = options.outputTailChars ?? DEFAULT_TAIL_CHARS;
|
|
76
|
+
// 1. Resolve and guard the branch.
|
|
77
|
+
let branch = options.branch;
|
|
78
|
+
if (!branch) {
|
|
79
|
+
const head = await exec('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd });
|
|
80
|
+
branch = head.stdout.trim();
|
|
81
|
+
}
|
|
82
|
+
if (!branch || PROTECTED_BRANCHES.has(branch)) {
|
|
83
|
+
return {
|
|
84
|
+
pushed: false,
|
|
85
|
+
runId: null,
|
|
86
|
+
status: 'skipped',
|
|
87
|
+
feedback: `CI watch skipped: refusing to push protected branch '${branch || '(unknown)'}'. Merge to master via the PR flow.`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// 2. Commit (tolerate "nothing to commit").
|
|
91
|
+
if (options.files.length > 0) {
|
|
92
|
+
await exec('git', ['add', '--', ...options.files], { cwd });
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
await exec('git', ['add', '-A'], { cwd });
|
|
96
|
+
}
|
|
97
|
+
const commit = await exec('git', ['commit', '-m', options.commitMessage], { cwd });
|
|
98
|
+
if (commit.code !== 0 && !/nothing to commit/i.test(`${commit.stdout}\n${commit.stderr}`)) {
|
|
99
|
+
return {
|
|
100
|
+
pushed: false,
|
|
101
|
+
runId: null,
|
|
102
|
+
status: 'skipped',
|
|
103
|
+
feedback: `CI watch skipped: commit failed — ${sanitizeCiLog(truncateTail(commit.stderr, 500))}`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// 3. Resolve the head SHA we are about to push.
|
|
107
|
+
const sha = (await exec('git', ['rev-parse', 'HEAD'], { cwd })).stdout.trim();
|
|
108
|
+
// 4. Push (non-force, never force-with-lease).
|
|
109
|
+
progress(`Pushing ${branch} to ${remote}…`);
|
|
110
|
+
const push = await exec('git', ['push', remote, branch], { cwd });
|
|
111
|
+
if (push.code !== 0) {
|
|
112
|
+
return {
|
|
113
|
+
pushed: false,
|
|
114
|
+
runId: null,
|
|
115
|
+
status: 'skipped',
|
|
116
|
+
feedback: `CI watch skipped: push failed — ${sanitizeCiLog(truncateTail(push.stderr, 500))}`,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// 5. Resolve the run for this exact commit (match by head SHA).
|
|
120
|
+
const run = await resolveRun(exec, cwd, branch, sha, options.resolveAttempts ?? DEFAULT_RESOLVE_ATTEMPTS, sleep, options.pollIntervalMs ?? DEFAULT_POLL_MS, progress);
|
|
121
|
+
if (!run) {
|
|
122
|
+
return {
|
|
123
|
+
pushed: true,
|
|
124
|
+
runId: null,
|
|
125
|
+
status: 'no-run',
|
|
126
|
+
feedback: `Pushed ${sha.slice(0, 8)} to ${branch}, but no CI run was found for it. CI may be disabled for this branch.`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// 6. Watch to conclusion.
|
|
130
|
+
return watchRun(exec, cwd, run, {
|
|
131
|
+
pollIntervalMs: options.pollIntervalMs ?? DEFAULT_POLL_MS,
|
|
132
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
133
|
+
watchEnvironments: options.watchEnvironments,
|
|
134
|
+
tailChars,
|
|
135
|
+
sleep,
|
|
136
|
+
progress,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async function resolveRun(exec, cwd, branch, sha, attempts, sleep, pollMs, progress) {
|
|
140
|
+
for (let i = 0; i < attempts; i++) {
|
|
141
|
+
const res = await exec('gh', ['run', 'list', '--branch', branch, '--json', 'databaseId,headSha,status,url', '--limit', '20'], { cwd });
|
|
142
|
+
if (res.code === 0) {
|
|
143
|
+
try {
|
|
144
|
+
const runs = JSON.parse(res.stdout);
|
|
145
|
+
const match = runs.find((r) => r.headSha === sha);
|
|
146
|
+
if (match)
|
|
147
|
+
return match;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
/* malformed json — retry */
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
progress(`Waiting for CI run on ${sha.slice(0, 8)} (attempt ${i + 1}/${attempts})…`);
|
|
154
|
+
if (i < attempts - 1)
|
|
155
|
+
await sleep(pollMs);
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
async function watchRun(exec, cwd, run, params) {
|
|
160
|
+
const runId = String(run.databaseId);
|
|
161
|
+
const deadline = params.timeoutMs;
|
|
162
|
+
let elapsed = 0;
|
|
163
|
+
for (;;) {
|
|
164
|
+
const res = await exec('gh', ['run', 'view', runId, '--json', 'status,conclusion,jobs,url'], { cwd });
|
|
165
|
+
let view = null;
|
|
166
|
+
if (res.code === 0) {
|
|
167
|
+
try {
|
|
168
|
+
view = JSON.parse(res.stdout);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
view = null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (view && view.status === 'completed') {
|
|
175
|
+
const runUrl = view.url ?? run.url;
|
|
176
|
+
const envProblem = requiredEnvFailure(view.jobs ?? [], params.watchEnvironments);
|
|
177
|
+
if (view.conclusion === 'success' && !envProblem) {
|
|
178
|
+
return { pushed: true, runId, status: 'green', runUrl };
|
|
179
|
+
}
|
|
180
|
+
const feedback = await buildFailureFeedback(exec, cwd, runId, envProblem, params.tailChars);
|
|
181
|
+
return { pushed: true, runId, status: 'failed', feedback, runUrl };
|
|
182
|
+
}
|
|
183
|
+
if (elapsed >= deadline) {
|
|
184
|
+
return {
|
|
185
|
+
pushed: true,
|
|
186
|
+
runId,
|
|
187
|
+
status: 'timeout',
|
|
188
|
+
runUrl: view?.url ?? run.url,
|
|
189
|
+
feedback: `CI run ${runId} did not conclude within ${Math.round(deadline / 60000)} min. See ${view?.url ?? run.url ?? 'the run'} for status.`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
params.progress(`Watching CI run ${runId} (${view?.status ?? 'pending'})…`);
|
|
193
|
+
await params.sleep(params.pollIntervalMs);
|
|
194
|
+
elapsed += params.pollIntervalMs;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/** Returns a description when a required environment's deploy job is not green. */
|
|
198
|
+
function requiredEnvFailure(jobs, envs) {
|
|
199
|
+
if (!envs || envs.length === 0)
|
|
200
|
+
return null;
|
|
201
|
+
for (const env of envs) {
|
|
202
|
+
const jobName = `deploy-${env}`;
|
|
203
|
+
const job = jobs.find((j) => j.name === jobName || j.name.startsWith(`${jobName} `));
|
|
204
|
+
if (!job)
|
|
205
|
+
return `Required ${env} deploy job ('${jobName}') was not found in the CI run.`;
|
|
206
|
+
if (job.conclusion !== 'success') {
|
|
207
|
+
return `Required ${env} deploy job ('${jobName}') concluded '${job.conclusion ?? job.status}'.`;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
async function buildFailureFeedback(exec, cwd, runId, envProblem, tailChars) {
|
|
213
|
+
const logs = await exec('gh', ['run', 'view', runId, '--log-failed'], { cwd });
|
|
214
|
+
const raw = logs.code === 0 ? logs.stdout : `${logs.stdout}\n${logs.stderr}`;
|
|
215
|
+
const sanitized = sanitizeCiLog(truncateTail(raw, tailChars));
|
|
216
|
+
const header = envProblem
|
|
217
|
+
? `ci-feedback: ${envProblem}`
|
|
218
|
+
: 'ci-feedback: the CI run failed after the change was pushed. Fix the failure below so dev/staging/prod verification passes.';
|
|
219
|
+
return `${header}\n\nFailed CI logs:\n\`\`\`\n${sanitized || '(no log output captured)'}\n\`\`\``;
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=ci-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-watcher.js","sourceRoot":"","sources":["../../src/delivery/ci-watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local dev deploy + smoke gate (the `deploy-dev` tier).
|
|
3
|
+
*
|
|
4
|
+
* Runs a deploy-dev rung as a full lifecycle: optionally bring a docker-compose
|
|
5
|
+
* stack up (`docker compose up -d --wait`), run the smoke/health check, then
|
|
6
|
+
* ALWAYS tear the stack down (even on smoke failure or timeout). This gives the
|
|
7
|
+
* convergence loop real "does it actually run" feedback locally, before the
|
|
8
|
+
* change is ever committed.
|
|
9
|
+
*
|
|
10
|
+
* Safety:
|
|
11
|
+
* - When the rung needs docker and docker is unavailable, the tier is reported
|
|
12
|
+
* as SKIPPED (not failed) so a missing runtime never poisons convergence
|
|
13
|
+
* feedback or blocks delivery.
|
|
14
|
+
* - `UAP_DELIVER_NO_DEPLOY=1` force-skips the tier.
|
|
15
|
+
* - The deploy command inherits {@link sanitizedEnv} (secrets stripped,
|
|
16
|
+
* `CI=true`), exactly like the other gate rungs.
|
|
17
|
+
*/
|
|
18
|
+
import { type GateRung, type LadderOptions, type LadderResult, type RungResult } from './verifier-ladder.js';
|
|
19
|
+
export interface DeployDevOptions {
|
|
20
|
+
/** Max chars of command output kept on failure (default 2000). */
|
|
21
|
+
outputTailChars?: number;
|
|
22
|
+
/** Compose bring-up timeout (default 120s). */
|
|
23
|
+
bringUpTimeoutMs?: number;
|
|
24
|
+
/** Teardown timeout (default 30s). */
|
|
25
|
+
teardownTimeoutMs?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Override docker availability detection — primarily for tests. When
|
|
28
|
+
* undefined the runner probes `docker --version`.
|
|
29
|
+
*/
|
|
30
|
+
dockerAvailable?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Run one deploy-dev rung through its bring-up → smoke → teardown lifecycle.
|
|
34
|
+
* Teardown is best-effort and runs in `finally`; a teardown failure never
|
|
35
|
+
* flips a passing smoke result.
|
|
36
|
+
*/
|
|
37
|
+
export declare function runDeployDevRung(rung: GateRung, projectRoot: string, options?: DeployDevOptions): RungResult;
|
|
38
|
+
/**
|
|
39
|
+
* LadderRunFn-compatible runner for the deploy-dev tier: runs each rung through
|
|
40
|
+
* {@link runDeployDevRung} and aggregates into a {@link LadderResult}. Wire this
|
|
41
|
+
* as `runTieredLadder`'s `deployDevRunner`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function runDeployDevLadder(rungs: GateRung[], projectRoot: string, options?: LadderOptions): LadderResult;
|
|
44
|
+
//# sourceMappingURL=deploy-dev-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy-dev-gate.d.ts","sourceRoot":"","sources":["../../src/delivery/deploy-dev-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAKL,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,UAAU,EAChB,MAAM,sBAAsB,CAAC;AAK9B,MAAM,WAAW,gBAAgB;IAC/B,kEAAkE;IAClE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+CAA+C;IAC/C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sCAAsC;IACtC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA6BD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,gBAAqB,GAC7B,UAAU,CAoGZ;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,QAAQ,EAAE,EACjB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,aAAkB,GAC1B,YAAY,CAqBd"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local dev deploy + smoke gate (the `deploy-dev` tier).
|
|
3
|
+
*
|
|
4
|
+
* Runs a deploy-dev rung as a full lifecycle: optionally bring a docker-compose
|
|
5
|
+
* stack up (`docker compose up -d --wait`), run the smoke/health check, then
|
|
6
|
+
* ALWAYS tear the stack down (even on smoke failure or timeout). This gives the
|
|
7
|
+
* convergence loop real "does it actually run" feedback locally, before the
|
|
8
|
+
* change is ever committed.
|
|
9
|
+
*
|
|
10
|
+
* Safety:
|
|
11
|
+
* - When the rung needs docker and docker is unavailable, the tier is reported
|
|
12
|
+
* as SKIPPED (not failed) so a missing runtime never poisons convergence
|
|
13
|
+
* feedback or blocks delivery.
|
|
14
|
+
* - `UAP_DELIVER_NO_DEPLOY=1` force-skips the tier.
|
|
15
|
+
* - The deploy command inherits {@link sanitizedEnv} (secrets stripped,
|
|
16
|
+
* `CI=true`), exactly like the other gate rungs.
|
|
17
|
+
*/
|
|
18
|
+
import { spawnSync } from 'child_process';
|
|
19
|
+
import { DEFAULT_TAIL_CHARS, formatFeedback, sanitizedEnv, truncateTail, } from './verifier-ladder.js';
|
|
20
|
+
const DEFAULT_BRINGUP_TIMEOUT_MS = 120_000;
|
|
21
|
+
const DEFAULT_TEARDOWN_TIMEOUT_MS = 30_000;
|
|
22
|
+
/** True when the rung's bring-up or teardown shells out to docker. */
|
|
23
|
+
function needsDocker(rung) {
|
|
24
|
+
return rung.command === 'docker' || rung.teardown?.command === 'docker';
|
|
25
|
+
}
|
|
26
|
+
function dockerIsAvailable(override) {
|
|
27
|
+
if (typeof override === 'boolean')
|
|
28
|
+
return override;
|
|
29
|
+
try {
|
|
30
|
+
const res = spawnSync('docker', ['--version'], { timeout: 5_000, encoding: 'utf-8' });
|
|
31
|
+
return res.status === 0;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function skipped(rung, note) {
|
|
38
|
+
return {
|
|
39
|
+
id: rung.id,
|
|
40
|
+
name: rung.name,
|
|
41
|
+
passed: false,
|
|
42
|
+
skipped: true,
|
|
43
|
+
exitCode: null,
|
|
44
|
+
durationMs: 0,
|
|
45
|
+
outputTail: note,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Run one deploy-dev rung through its bring-up → smoke → teardown lifecycle.
|
|
50
|
+
* Teardown is best-effort and runs in `finally`; a teardown failure never
|
|
51
|
+
* flips a passing smoke result.
|
|
52
|
+
*/
|
|
53
|
+
export function runDeployDevRung(rung, projectRoot, options = {}) {
|
|
54
|
+
const tailChars = options.outputTailChars ?? DEFAULT_TAIL_CHARS;
|
|
55
|
+
if (process.env.UAP_DELIVER_NO_DEPLOY === '1') {
|
|
56
|
+
return skipped(rung, 'deploy-dev skipped (UAP_DELIVER_NO_DEPLOY=1)');
|
|
57
|
+
}
|
|
58
|
+
const usesDocker = needsDocker(rung);
|
|
59
|
+
if (usesDocker && !dockerIsAvailable(options.dockerAvailable)) {
|
|
60
|
+
// Missing runtime is not a code failure — degrade to skipped.
|
|
61
|
+
return skipped(rung, 'deploy-dev skipped (docker unavailable)');
|
|
62
|
+
}
|
|
63
|
+
const env = sanitizedEnv();
|
|
64
|
+
const start = Date.now();
|
|
65
|
+
const useCompose = usesDocker && !!rung.teardown;
|
|
66
|
+
try {
|
|
67
|
+
// 1. Bring up the compose stack (blocks on container healthchecks).
|
|
68
|
+
if (useCompose) {
|
|
69
|
+
const up = spawnSync('docker', ['compose', 'up', '-d', '--wait'], {
|
|
70
|
+
cwd: projectRoot,
|
|
71
|
+
encoding: 'utf-8',
|
|
72
|
+
timeout: options.bringUpTimeoutMs ?? DEFAULT_BRINGUP_TIMEOUT_MS,
|
|
73
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
74
|
+
env,
|
|
75
|
+
});
|
|
76
|
+
if (up.status !== 0) {
|
|
77
|
+
const errCode = up.error?.code;
|
|
78
|
+
const timedOut = errCode === 'ETIMEDOUT' || (up.error && up.signal === 'SIGTERM');
|
|
79
|
+
const diag = timedOut
|
|
80
|
+
? `Compose bring-up timed out after ${options.bringUpTimeoutMs ?? DEFAULT_BRINGUP_TIMEOUT_MS}ms.`
|
|
81
|
+
: 'Compose bring-up failed.';
|
|
82
|
+
return {
|
|
83
|
+
id: rung.id,
|
|
84
|
+
name: rung.name,
|
|
85
|
+
passed: false,
|
|
86
|
+
skipped: false,
|
|
87
|
+
exitCode: up.status,
|
|
88
|
+
failureReason: timedOut ? 'timeout' : 'exit',
|
|
89
|
+
durationMs: Date.now() - start,
|
|
90
|
+
outputTail: truncateTail(`${diag}\n${up.stdout ?? ''}\n${up.stderr ?? ''}`, tailChars),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// 2. Smoke / health check.
|
|
95
|
+
const smoke = spawnSync(rung.command, rung.args, {
|
|
96
|
+
cwd: projectRoot,
|
|
97
|
+
encoding: 'utf-8',
|
|
98
|
+
timeout: rung.timeoutMs,
|
|
99
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
100
|
+
env,
|
|
101
|
+
});
|
|
102
|
+
const passed = smoke.status === 0;
|
|
103
|
+
let failureReason;
|
|
104
|
+
let diagnostic = '';
|
|
105
|
+
if (!passed) {
|
|
106
|
+
const errCode = smoke.error?.code;
|
|
107
|
+
if (errCode === 'ETIMEDOUT' || (smoke.error && smoke.signal === 'SIGTERM')) {
|
|
108
|
+
failureReason = 'timeout';
|
|
109
|
+
diagnostic = `Smoke check timed out after ${rung.timeoutMs}ms.`;
|
|
110
|
+
}
|
|
111
|
+
else if (smoke.error) {
|
|
112
|
+
failureReason = 'spawn-error';
|
|
113
|
+
diagnostic = `Smoke check could not run: ${smoke.error.message}`;
|
|
114
|
+
}
|
|
115
|
+
else if (smoke.signal) {
|
|
116
|
+
failureReason = 'signal';
|
|
117
|
+
diagnostic = `Smoke check killed by signal ${smoke.signal}.`;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
failureReason = 'exit';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
id: rung.id,
|
|
125
|
+
name: rung.name,
|
|
126
|
+
passed,
|
|
127
|
+
skipped: false,
|
|
128
|
+
exitCode: smoke.status,
|
|
129
|
+
failureReason,
|
|
130
|
+
durationMs: Date.now() - start,
|
|
131
|
+
outputTail: passed
|
|
132
|
+
? ''
|
|
133
|
+
: truncateTail(`${diagnostic}\n${smoke.stdout ?? ''}\n${smoke.stderr ?? ''}`, tailChars),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
// 3. Teardown — always, best-effort.
|
|
138
|
+
if (rung.teardown) {
|
|
139
|
+
try {
|
|
140
|
+
spawnSync(rung.teardown.command, rung.teardown.args, {
|
|
141
|
+
cwd: projectRoot,
|
|
142
|
+
encoding: 'utf-8',
|
|
143
|
+
timeout: options.teardownTimeoutMs ?? rung.teardown.timeoutMs ?? DEFAULT_TEARDOWN_TIMEOUT_MS,
|
|
144
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
145
|
+
env,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
/* teardown is best-effort; never flips the rung result */
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* LadderRunFn-compatible runner for the deploy-dev tier: runs each rung through
|
|
156
|
+
* {@link runDeployDevRung} and aggregates into a {@link LadderResult}. Wire this
|
|
157
|
+
* as `runTieredLadder`'s `deployDevRunner`.
|
|
158
|
+
*/
|
|
159
|
+
export function runDeployDevLadder(rungs, projectRoot, options = {}) {
|
|
160
|
+
const tailChars = options.outputTailChars ?? DEFAULT_TAIL_CHARS;
|
|
161
|
+
const results = rungs.map((rung) => runDeployDevRung(rung, projectRoot, { outputTailChars: tailChars }));
|
|
162
|
+
// A skipped deploy-dev rung (docker missing / opted out) must not block
|
|
163
|
+
// delivery — treat it as a non-blocking pass for the gate aggregate.
|
|
164
|
+
const requiredRungs = rungs.filter((r) => r.required);
|
|
165
|
+
const requiredOk = requiredRungs.every((rung) => results.some((r) => r.id === rung.id && (r.passed || r.skipped)));
|
|
166
|
+
const passedCount = results.filter((r) => r.passed).length;
|
|
167
|
+
const score = results.length > 0 ? passedCount / results.length : 1;
|
|
168
|
+
return {
|
|
169
|
+
passed: requiredOk,
|
|
170
|
+
score,
|
|
171
|
+
results,
|
|
172
|
+
feedback: formatFeedback(results, rungs),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=deploy-dev-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy-dev-gate.js","sourceRoot":"","sources":["../../src/delivery/deploy-dev-gate.ts"],"names":[],"mappings":"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"}
|