@percher/core 0.4.1 → 0.4.3
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/commands/alerts.d.ts +2 -1
- package/dist/commands/alerts.d.ts.map +1 -1
- package/dist/commands/alerts.js +2 -0
- package/dist/commands/alerts.js.map +1 -1
- package/dist/commands/cache.d.ts +23 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +24 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +1 -1
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +12 -1
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/doctor.d.ts +126 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +445 -313
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/export.d.ts +5 -14
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +1 -1
- package/dist/commands/preview-branch.d.ts +31 -0
- package/dist/commands/preview-branch.d.ts.map +1 -0
- package/dist/commands/preview-branch.js +55 -0
- package/dist/commands/preview-branch.js.map +1 -0
- package/dist/commands/publish-api-error.d.ts +15 -0
- package/dist/commands/publish-api-error.d.ts.map +1 -1
- package/dist/commands/publish-api-error.js +214 -14
- package/dist/commands/publish-api-error.js.map +1 -1
- package/dist/commands/publish-failure.d.ts +3 -1
- package/dist/commands/publish-failure.d.ts.map +1 -1
- package/dist/commands/publish-failure.js +22 -9
- package/dist/commands/publish-failure.js.map +1 -1
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +62 -328
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +4 -4
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/redeploy.js +3 -3
- package/dist/commands/redeploy.js.map +1 -1
- package/dist/commands/rename.d.ts +7 -0
- package/dist/commands/rename.d.ts.map +1 -1
- package/dist/commands/rename.js +32 -1
- package/dist/commands/rename.js.map +1 -1
- package/dist/commands/wait-deploy.d.ts +12 -2
- package/dist/commands/wait-deploy.d.ts.map +1 -1
- package/dist/commands/wait-deploy.js +115 -46
- package/dist/commands/wait-deploy.js.map +1 -1
- package/dist/env-scan-source.d.ts +8 -1
- package/dist/env-scan-source.d.ts.map +1 -1
- package/dist/env-scan-source.js +99 -23
- package/dist/env-scan-source.js.map +1 -1
- package/dist/error-classifier.d.ts +1 -1
- package/dist/error-classifier.d.ts.map +1 -1
- package/dist/error-classifier.js +77 -1
- package/dist/error-classifier.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/plans.d.ts +31 -9
- package/dist/plans.d.ts.map +1 -1
- package/dist/plans.js +46 -17
- package/dist/plans.js.map +1 -1
- package/dist/poll-deployment.d.ts +3 -3
- package/dist/poll-deployment.d.ts.map +1 -1
- package/dist/poll-deployment.js +5 -4
- package/dist/poll-deployment.js.map +1 -1
- package/dist/publish-retry.d.ts.map +1 -1
- package/dist/publish-retry.js +2 -3
- package/dist/publish-retry.js.map +1 -1
- package/dist/recovery.d.ts +13 -0
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +14 -0
- package/dist/recovery.js.map +1 -1
- package/package.json +4 -4
- package/dist/commands/continue.d.ts +0 -48
- package/dist/commands/continue.d.ts.map +0 -1
- package/dist/commands/continue.js +0 -121
- package/dist/commands/continue.js.map +0 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { abortableSleep, isTransientNetworkError, } from "@percher/client";
|
|
1
2
|
import { TIMEOUTS } from "@percher/shared/timeouts";
|
|
2
3
|
import { z } from "zod";
|
|
4
|
+
import { PercherCoreError } from "../errors";
|
|
3
5
|
import { RECOVERY_NONE, recoveryAsk, recoveryNone, recoveryWait, } from "../recovery";
|
|
4
6
|
import { classifyDeploymentFailure } from "./publish-failure";
|
|
5
7
|
export const waitForDeployInputSchema = z.object({
|
|
@@ -27,69 +29,130 @@ export const waitForDeployInputSchema = z.object({
|
|
|
27
29
|
* `wait_deploy` recovery so the agent loops without growing the call
|
|
28
30
|
* stack. Never starts a new deploy.
|
|
29
31
|
*/
|
|
30
|
-
export async function waitForDeploy(ctx, input) {
|
|
32
|
+
export async function waitForDeploy(ctx, input, opts) {
|
|
31
33
|
const timeoutSeconds = input.timeoutSeconds ?? TIMEOUTS.mcpWaitDeployDefault / 1000;
|
|
32
34
|
const pollIntervalSeconds = input.pollIntervalSeconds ?? 2;
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
]);
|
|
35
|
+
// The budget is wall-clock from HERE — it covers the initial fetches
|
|
36
|
+
// too, and the controller aborts any in-flight HTTP at the deadline so
|
|
37
|
+
// a stalled request can't exceed the timeout arbitrarily. The caller's
|
|
38
|
+
// signal feeds the same controller, so external cancellation behaves
|
|
39
|
+
// exactly like a deadline hit.
|
|
39
40
|
const startedAt = Date.now();
|
|
40
41
|
const deadlineMs = startedAt + timeoutSeconds * 1000;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
const controller = new AbortController();
|
|
43
|
+
const abortTimer = setTimeout(() => controller.abort(), timeoutSeconds * 1000);
|
|
44
|
+
const onExternalAbort = () => controller.abort();
|
|
45
|
+
opts?.signal?.addEventListener("abort", onExternalAbort, { once: true });
|
|
46
|
+
if (opts?.signal?.aborted)
|
|
47
|
+
controller.abort();
|
|
48
|
+
try {
|
|
49
|
+
// Fetch app + initial deployment in parallel; the app row is needed for
|
|
50
|
+
// the URL on success and for failure-classification context.
|
|
51
|
+
let app;
|
|
52
|
+
let initial;
|
|
47
53
|
try {
|
|
48
|
-
|
|
54
|
+
[app, initial] = await Promise.all([
|
|
55
|
+
ctx.client.apps.get(input.app, { signal: controller.signal }),
|
|
56
|
+
ctx.client.apps.getDeployment(input.app, input.deployId, { signal: controller.signal }),
|
|
57
|
+
]);
|
|
49
58
|
}
|
|
50
59
|
catch (err) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
if (controller.signal.aborted) {
|
|
61
|
+
// Budget exhausted before we had any status to report — there is
|
|
62
|
+
// no deployment row to build a still_running result from.
|
|
63
|
+
throw new PercherCoreError(`wait-for-deploy timed out after ${timeoutSeconds}s before the first status fetch completed`, {
|
|
64
|
+
code: "wait.timeout",
|
|
65
|
+
hint: "The API is slow or unreachable. Retry with a larger timeout, or run percher doctor.",
|
|
66
|
+
});
|
|
58
67
|
}
|
|
59
68
|
throw err;
|
|
60
69
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
let deployment = initial;
|
|
71
|
+
while (!isTerminal(deployment.status)) {
|
|
72
|
+
if (controller.signal.aborted || Date.now() >= deadlineMs) {
|
|
73
|
+
return buildStillRunningResult(app, deployment, startedAt);
|
|
74
|
+
}
|
|
75
|
+
// Clamp the sleep to the remaining window so a sub-interval
|
|
76
|
+
// timeoutSeconds is honored — a 1s budget must not sleep the full
|
|
77
|
+
// 2s poll interval before the deadline check can fire. The helper
|
|
78
|
+
// wakes immediately on abort AND checks an already-aborted signal
|
|
79
|
+
// before scheduling, so no timer outlives a cancellation.
|
|
80
|
+
const sleepMs = Math.min(pollIntervalSeconds * 1000, deadlineMs - Date.now());
|
|
81
|
+
await abortableSleep(sleepMs, controller.signal);
|
|
82
|
+
// Re-check after sleeping — never start a poll with zero budget,
|
|
83
|
+
// and bail straight out on cancellation.
|
|
84
|
+
if (controller.signal.aborted || Date.now() >= deadlineMs) {
|
|
85
|
+
return buildStillRunningResult(app, deployment, startedAt);
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
deployment = await ctx.client.apps.getDeployment(input.app, input.deployId, {
|
|
89
|
+
signal: controller.signal,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
// Deadline abort: report the last known state as still_running —
|
|
94
|
+
// the deploy may well be progressing; the budget just ran out.
|
|
95
|
+
if (controller.signal.aborted) {
|
|
96
|
+
return buildStillRunningResult(app, deployment, startedAt, "status fetch exceeded the wait budget");
|
|
97
|
+
}
|
|
98
|
+
// Transient fetch errors during a short wait don't justify
|
|
99
|
+
// throwing — return still_running so the agent retries. The
|
|
100
|
+
// deploy may already be live server-side; the agent's next call
|
|
101
|
+
// will pick that up.
|
|
102
|
+
if (isTransientNetworkError(err)) {
|
|
103
|
+
const msg = err.message ?? String(err);
|
|
104
|
+
return buildStillRunningResult(app, deployment, startedAt, msg);
|
|
105
|
+
}
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (deployment.status === "failed") {
|
|
110
|
+
// Enrichment carries the CALLER'S signal (not the deadline
|
|
111
|
+
// controller): an abandoned call must release its build-log
|
|
112
|
+
// fetches + retry sleeps, but a deadline hit mid-enrichment
|
|
113
|
+
// should still finish classifying — the deploy IS terminal.
|
|
114
|
+
const { error, recovery, summary } = await classifyDeploymentFailure({
|
|
115
|
+
ctx,
|
|
116
|
+
app,
|
|
117
|
+
deployment,
|
|
118
|
+
signal: opts?.signal,
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
status: "failed",
|
|
122
|
+
app: { id: app.id, name: app.name, url: app.url },
|
|
123
|
+
deployment,
|
|
124
|
+
elapsedSeconds: Math.round((Date.now() - startedAt) / 1000),
|
|
125
|
+
totalElapsedSeconds: secondsSinceCreated(deployment),
|
|
126
|
+
error,
|
|
127
|
+
recovery,
|
|
128
|
+
summary,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (deployment.status === "replaced") {
|
|
132
|
+
return await buildReplacedResult({ ctx, app, deployment, startedAt, signal: opts?.signal });
|
|
133
|
+
}
|
|
134
|
+
const url = deployment.previewUrl ?? deployment.url ?? app.url;
|
|
68
135
|
return {
|
|
69
|
-
status: "
|
|
136
|
+
status: "live",
|
|
70
137
|
app: { id: app.id, name: app.name, url: app.url },
|
|
71
138
|
deployment,
|
|
139
|
+
url,
|
|
72
140
|
elapsedSeconds: Math.round((Date.now() - startedAt) / 1000),
|
|
73
141
|
totalElapsedSeconds: secondsSinceCreated(deployment),
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
summary,
|
|
142
|
+
recovery: RECOVERY_NONE,
|
|
143
|
+
summary: `${app.name} deploy ${deployment.id} is live → ${url}`,
|
|
77
144
|
};
|
|
78
145
|
}
|
|
79
|
-
|
|
80
|
-
|
|
146
|
+
finally {
|
|
147
|
+
// Disarm the deadline timer AND abort the controller: if one of the
|
|
148
|
+
// parallel initial fetches failed, its still-pending sibling would
|
|
149
|
+
// otherwise keep its socket (and the process) alive until the server
|
|
150
|
+
// gave up. Aborting after a clean return is a no-op — nothing
|
|
151
|
+
// carrying this signal remains in flight.
|
|
152
|
+
clearTimeout(abortTimer);
|
|
153
|
+
controller.abort();
|
|
154
|
+
opts?.signal?.removeEventListener("abort", onExternalAbort);
|
|
81
155
|
}
|
|
82
|
-
const url = deployment.previewUrl ?? deployment.url ?? app.url;
|
|
83
|
-
return {
|
|
84
|
-
status: "live",
|
|
85
|
-
app: { id: app.id, name: app.name, url: app.url },
|
|
86
|
-
deployment,
|
|
87
|
-
url,
|
|
88
|
-
elapsedSeconds: Math.round((Date.now() - startedAt) / 1000),
|
|
89
|
-
totalElapsedSeconds: secondsSinceCreated(deployment),
|
|
90
|
-
recovery: RECOVERY_NONE,
|
|
91
|
-
summary: `${app.name} deploy ${deployment.id} is live → ${url}`,
|
|
92
|
-
};
|
|
93
156
|
}
|
|
94
157
|
function isTerminal(status) {
|
|
95
158
|
return status === "live" || status === "failed" || status === "replaced";
|
|
@@ -124,6 +187,7 @@ export async function resolveReplaced(opts) {
|
|
|
124
187
|
const deploys = await ctx.client.apps.listDeploys(app.name, {
|
|
125
188
|
limit: 5,
|
|
126
189
|
type: deployType,
|
|
190
|
+
signal: opts.signal,
|
|
127
191
|
});
|
|
128
192
|
// Skip the row we already know is replaced; pick the most recent
|
|
129
193
|
// deploy that's not this one. The list is ordered desc by createdAt.
|
|
@@ -182,7 +246,12 @@ export async function resolveReplaced(opts) {
|
|
|
182
246
|
}
|
|
183
247
|
async function buildReplacedResult(opts) {
|
|
184
248
|
const { ctx, app, deployment, startedAt } = opts;
|
|
185
|
-
const resolved = await resolveReplaced({
|
|
249
|
+
const resolved = await resolveReplaced({
|
|
250
|
+
ctx,
|
|
251
|
+
app,
|
|
252
|
+
replacedDeployment: deployment,
|
|
253
|
+
signal: opts.signal,
|
|
254
|
+
});
|
|
186
255
|
return {
|
|
187
256
|
status: "replaced",
|
|
188
257
|
app: { id: app.id, name: app.name, url: app.url },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wait-deploy.js","sourceRoot":"","sources":["../../src/commands/wait-deploy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wait-deploy.js","sourceRoot":"","sources":["../../src/commands/wait-deploy.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
* Phase 6c uses these references to enforce the `[env]` contract in
|
|
6
6
|
* `percher.toml`: any static key not declared in `required`, `optional`, or
|
|
7
7
|
* `ignore` blocks the deploy with a `fix_config` problem.
|
|
8
|
+
*
|
|
9
|
+
* `static_with_default` is a relaxation of `static`: same syntactic shape
|
|
10
|
+
* but the source already supplies a fallback value, so the env-scan
|
|
11
|
+
* dashboard classifies these as "optional" instead of "required". The
|
|
12
|
+
* env-gate still treats `static_with_default` as `static` for the
|
|
13
|
+
* declared-keys check — if you reference a key (even with a fallback)
|
|
14
|
+
* you should still declare it so future readers know it's a knob.
|
|
8
15
|
*/
|
|
9
16
|
export interface EnvReference {
|
|
10
17
|
/** The env key referenced. Empty string when `shape` is `"dynamic"`. */
|
|
@@ -16,7 +23,7 @@ export interface EnvReference {
|
|
|
16
23
|
/** Verbatim source line (trimmed) for diagnostics. */
|
|
17
24
|
context: string;
|
|
18
25
|
/** Static (literal key) vs dynamic (e.g. `process.env[varName]`). */
|
|
19
|
-
shape: "static" | "dynamic";
|
|
26
|
+
shape: "static" | "static_with_default" | "dynamic";
|
|
20
27
|
}
|
|
21
28
|
export interface ScanOptions {
|
|
22
29
|
/** Optional list of relative paths to skip on top of the built-in defaults. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-scan-source.d.ts","sourceRoot":"","sources":["../src/env-scan-source.ts"],"names":[],"mappings":"AAGA
|
|
1
|
+
{"version":3,"file":"env-scan-source.d.ts","sourceRoot":"","sources":["../src/env-scan-source.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,KAAK,EAAE,QAAQ,GAAG,qBAAqB,GAAG,SAAS,CAAC;CACrD;AAED,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAqXD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CA+CzB"}
|
package/dist/env-scan-source.js
CHANGED
|
@@ -35,34 +35,33 @@ const SOURCE_EXTS = new Set([
|
|
|
35
35
|
".py",
|
|
36
36
|
]);
|
|
37
37
|
const DEFAULT_MAX_FILE_BYTES = 1024 * 1024;
|
|
38
|
-
/**
|
|
39
|
-
* Static patterns. Each pattern's first capture group (or first non-empty
|
|
40
|
-
* group, for the multi-quote alternation patterns) is the literal env key.
|
|
41
|
-
*
|
|
42
|
-
* Env-key shape inside the patterns: `[A-Za-z_][A-Za-z0-9_]*` — standard
|
|
43
|
-
* POSIX-ish identifier. Permissive on case to keep false negatives low; the
|
|
44
|
-
* Phase 6c gate enforces `UPPER_SNAKE_CASE` against the `[env]` contract.
|
|
45
|
-
*
|
|
46
|
-
* Word-boundary discipline:
|
|
47
|
-
* - `(?<![A-Za-z0-9_$])` (negative lookbehind) prevents `myprocess.env.KEY`
|
|
48
|
-
* or `xprocess.env.KEY` from matching.
|
|
49
|
-
* - The class includes `$` because JS identifiers can contain `$`.
|
|
50
|
-
* - For dot access, `(?![A-Za-z0-9_])` after the key prevents trailing-name
|
|
51
|
-
* confusion (defence in depth — the captured group is greedy on
|
|
52
|
-
* identifier chars so this is mostly belt-and-braces).
|
|
53
|
-
*/
|
|
54
38
|
const STATIC_PATTERNS = [
|
|
55
39
|
// process.env.KEY | Bun.env.KEY | import.meta.env.KEY
|
|
56
|
-
|
|
40
|
+
{
|
|
41
|
+
re: /(?<![A-Za-z0-9_$])(?:process\.env|Bun\.env|import\.meta\.env)\.([A-Za-z_][A-Za-z0-9_]*)(?![A-Za-z0-9_])/g,
|
|
42
|
+
kind: "js-after",
|
|
43
|
+
},
|
|
57
44
|
// process.env["KEY"] | process.env['KEY'] | Bun.env["KEY"] | Bun.env['KEY']
|
|
58
|
-
|
|
45
|
+
{
|
|
46
|
+
re: /(?<![A-Za-z0-9_$])(?:process\.env|Bun\.env)\[\s*(?:"([A-Za-z_][A-Za-z0-9_]*)"|'([A-Za-z_][A-Za-z0-9_]*)')\s*\]/g,
|
|
47
|
+
kind: "js-after",
|
|
48
|
+
},
|
|
59
49
|
// Deno.env.get("KEY") | Deno.env.get('KEY')
|
|
60
|
-
|
|
50
|
+
{
|
|
51
|
+
re: /(?<![A-Za-z0-9_$])Deno\.env\.get\(\s*(?:"([A-Za-z_][A-Za-z0-9_]*)"|'([A-Za-z_][A-Za-z0-9_]*)')\s*\)/g,
|
|
52
|
+
kind: "js-after",
|
|
53
|
+
},
|
|
61
54
|
// Python: os.environ.get("KEY") | os.environ.get('KEY')
|
|
62
55
|
// os.getenv("KEY") | os.getenv('KEY')
|
|
63
|
-
|
|
56
|
+
{
|
|
57
|
+
re: /(?<![A-Za-z0-9_])os\.(?:environ\.get|getenv)\(\s*(?:"([A-Za-z_][A-Za-z0-9_]*)"|'([A-Za-z_][A-Za-z0-9_]*)')\s*[,)]/g,
|
|
58
|
+
kind: "py-getenv",
|
|
59
|
+
},
|
|
64
60
|
// Python: os.environ["KEY"] | os.environ['KEY']
|
|
65
|
-
|
|
61
|
+
{
|
|
62
|
+
re: /(?<![A-Za-z0-9_])os\.environ\[\s*(?:"([A-Za-z_][A-Za-z0-9_]*)"|'([A-Za-z_][A-Za-z0-9_]*)')\s*\]/g,
|
|
63
|
+
kind: "py-bracket",
|
|
64
|
+
},
|
|
66
65
|
];
|
|
67
66
|
/**
|
|
68
67
|
* Dynamic patterns — bracketed access where the argument is NOT a string
|
|
@@ -82,6 +81,71 @@ const DYNAMIC_PATTERNS = [
|
|
|
82
81
|
// Python: os.environ.get(<expr>) | os.getenv(<expr>)
|
|
83
82
|
{ re: /(?<![A-Za-z0-9_])os\.(?:environ\.get|getenv)\(\s*([^,)]+?)\s*[,)]/g, argGroup: 1 },
|
|
84
83
|
];
|
|
84
|
+
/**
|
|
85
|
+
* Detects whether a JS-style static env reference is followed by an
|
|
86
|
+
* inline default (`||`, `??`, or a ternary `?` with `process.env.X` as
|
|
87
|
+
* the condition). Called with the slice of `code` starting immediately
|
|
88
|
+
* after the matched `process.env.KEY` / `Bun.env.KEY` / `Deno.env.get()`
|
|
89
|
+
* span.
|
|
90
|
+
*
|
|
91
|
+
* Heuristic — false negatives preferred over false positives:
|
|
92
|
+
*
|
|
93
|
+
* process.env.KEY || "default" → true (logical-or default)
|
|
94
|
+
* process.env.KEY ?? "default" → true (nullish-coalescing)
|
|
95
|
+
* process.env.KEY ? a : b → true (ternary; whatever the
|
|
96
|
+
* branches do, the code
|
|
97
|
+
* already handles the
|
|
98
|
+
* "unset" case)
|
|
99
|
+
* process.env.KEY.split(",") → false (chained method call;
|
|
100
|
+
* throws if KEY is unset)
|
|
101
|
+
* process.env.KEY; → false (bare reference)
|
|
102
|
+
*
|
|
103
|
+
* Optional-chaining alone (`process.env.KEY?.split(",")`) is NOT a default:
|
|
104
|
+
* `?.` short-circuits to `undefined`, and downstream code that consumes that
|
|
105
|
+
* `undefined` can still crash or misbehave. We only count it as a default
|
|
106
|
+
* when an actual fallback (`||` / `??`) appears later in the look-ahead
|
|
107
|
+
* window — e.g. `process.env.KEY?.split(",") || []`.
|
|
108
|
+
*/
|
|
109
|
+
function hasJsDefaultAfter(after) {
|
|
110
|
+
// Skip leading whitespace within the look-ahead window.
|
|
111
|
+
let i = 0;
|
|
112
|
+
while (i < after.length && (after.charCodeAt(i) === 0x20 || after.charCodeAt(i) === 0x09)) {
|
|
113
|
+
i += 1;
|
|
114
|
+
}
|
|
115
|
+
if (i >= after.length)
|
|
116
|
+
return false;
|
|
117
|
+
const a = after[i];
|
|
118
|
+
const b = after[i + 1];
|
|
119
|
+
// `||` or `??` — real fallback.
|
|
120
|
+
if ((a === "|" && b === "|") || (a === "?" && b === "?"))
|
|
121
|
+
return true;
|
|
122
|
+
// `?.` is optional chaining, not a default. Scan the rest of the look-ahead
|
|
123
|
+
// window for an actual `||` / `??` further down the expression. We do not
|
|
124
|
+
// track paren/bracket balance precisely; in the rare case the fallback
|
|
125
|
+
// belongs to a sibling expression we'd over-count, but the bias is toward
|
|
126
|
+
// "has fallback" only when one is textually present.
|
|
127
|
+
if (a === "?" && b === ".") {
|
|
128
|
+
const rest = after.slice(i + 2);
|
|
129
|
+
return /\|\||\?\?/.test(rest);
|
|
130
|
+
}
|
|
131
|
+
// Bare `?` is a ternary — both branches define values, so it acts as a
|
|
132
|
+
// default in the same sense as `||` / `??`.
|
|
133
|
+
if (a === "?")
|
|
134
|
+
return true;
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Detects whether a Python `os.getenv("KEY", ...)` /
|
|
139
|
+
* `os.environ.get("KEY", ...)` call passed a second argument.
|
|
140
|
+
*
|
|
141
|
+
* Called with the matched span from the regex; the patterns terminate on
|
|
142
|
+
* `[,)]`, so `m[0]` ending with `,` means the source had a second
|
|
143
|
+
* argument (the default). `os.environ["KEY"]` has no default form and
|
|
144
|
+
* never reaches this helper.
|
|
145
|
+
*/
|
|
146
|
+
function pythonGetenvHasDefault(matchSpan) {
|
|
147
|
+
return matchSpan.endsWith(",");
|
|
148
|
+
}
|
|
85
149
|
/**
|
|
86
150
|
* True iff the bracket/argument expression is a plain quoted string literal
|
|
87
151
|
* with no interpolation. Backtick template literals containing `${...}` are
|
|
@@ -214,7 +278,7 @@ function extractFromSource(source, relPath, isPython) {
|
|
|
214
278
|
// same span (a static `process.env["KEY"]` would also match the dynamic
|
|
215
279
|
// bracket pattern, so we record the static keys and let dynamic skip
|
|
216
280
|
// arguments that are string literals).
|
|
217
|
-
for (const re of STATIC_PATTERNS) {
|
|
281
|
+
for (const { re, kind } of STATIC_PATTERNS) {
|
|
218
282
|
re.lastIndex = 0;
|
|
219
283
|
let m;
|
|
220
284
|
// biome-ignore lint/suspicious/noAssignInExpressions: standard regex exec loop
|
|
@@ -233,12 +297,24 @@ function extractFromSource(source, relPath, isPython) {
|
|
|
233
297
|
if (seen.has(dedup))
|
|
234
298
|
continue;
|
|
235
299
|
seen.add(dedup);
|
|
300
|
+
let hasDefault = false;
|
|
301
|
+
if (kind === "js-after") {
|
|
302
|
+
// Look at the slice immediately after the match for an inline
|
|
303
|
+
// fallback. 200 chars is plenty for `|| "default"` / `?? x` and
|
|
304
|
+
// bounds the worst case on a long minified line.
|
|
305
|
+
const after = code.slice(m.index + m[0].length, m.index + m[0].length + 200);
|
|
306
|
+
hasDefault = hasJsDefaultAfter(after);
|
|
307
|
+
}
|
|
308
|
+
else if (kind === "py-getenv") {
|
|
309
|
+
hasDefault = pythonGetenvHasDefault(m[0]);
|
|
310
|
+
}
|
|
311
|
+
// py-bracket has no default form; hasDefault stays false.
|
|
236
312
|
refs.push({
|
|
237
313
|
key,
|
|
238
314
|
file: relPath,
|
|
239
315
|
line: lineNo,
|
|
240
316
|
context,
|
|
241
|
-
shape: "static",
|
|
317
|
+
shape: hasDefault ? "static_with_default" : "static",
|
|
242
318
|
});
|
|
243
319
|
}
|
|
244
320
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-scan-source.js","sourceRoot":"","sources":["../src/env-scan-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"env-scan-source.js","sourceRoot":"","sources":["../src/env-scan-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* both. Extracting a shared catalog to `@percher/shared` is on the
|
|
16
16
|
* follow-up list.
|
|
17
17
|
*/
|
|
18
|
-
export type ErrorClass = "build_failed" | "health_check_timeout" | "port_mismatch" | "missing_env" | "oom_killed" | "permission_denied" | "runtime_crash" | "config_invalid" | "missing_dependency" | "infra_unavailable";
|
|
18
|
+
export type ErrorClass = "build_failed" | "health_check_timeout" | "port_mismatch" | "missing_env" | "oom_killed" | "permission_denied" | "runtime_crash" | "config_invalid" | "missing_dependency" | "infra_unavailable" | "platform_at_capacity";
|
|
19
19
|
export type DeployPhase = "build" | "start" | "health_check" | "config" | "infra";
|
|
20
20
|
export interface ClassifiedError {
|
|
21
21
|
title: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-classifier.d.ts","sourceRoot":"","sources":["../src/error-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,UAAU,GAClB,cAAc,GACd,sBAAsB,GACtB,eAAe,GACf,aAAa,GACb,YAAY,GACZ,mBAAmB,GACnB,eAAe,GACf,gBAAgB,GAChB,oBAAoB,GACpB,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"error-classifier.d.ts","sourceRoot":"","sources":["../src/error-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,UAAU,GAClB,cAAc,GACd,sBAAsB,GACtB,eAAe,GACf,aAAa,GACb,YAAY,GACZ,mBAAmB,GACnB,eAAe,GACf,gBAAgB,GAChB,oBAAoB,GACpB,mBAAmB,GAOnB,sBAAsB,CAAC;AAE3B,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,cAAc,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElF,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,UAAU,EAAE,UAAU,CAAC;IACvB,4CAA4C;IAC5C,KAAK,EAAE,WAAW,CAAC;IACnB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,+DAA+D;IAC/D,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AA6cD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CA2B7F"}
|
package/dist/error-classifier.js
CHANGED
|
@@ -88,6 +88,30 @@ const patterns = [
|
|
|
88
88
|
missingEnvVars: [],
|
|
89
89
|
},
|
|
90
90
|
},
|
|
91
|
+
// Track A — A3: a 502/Bad Gateway during deploy is the proxy in front of
|
|
92
|
+
// the app (Caddy / the api→worker hop) failing transiently, not the
|
|
93
|
+
// user's code. Without this it classified to `undefined` → "unknown
|
|
94
|
+
// counts as attributable" → a transient gateway blip fed the
|
|
95
|
+
// failure-loop. Maps to `infra_unavailable` (already non-attributable)
|
|
96
|
+
// so it never punishes the dev. Kept BELOW the upstream-unreachable
|
|
97
|
+
// entry above (more specific worker/Nix strings win first) and ABOVE
|
|
98
|
+
// every app-attributable pattern so a 502 is never re-read as an app
|
|
99
|
+
// error. `\b502\b` is intentionally broad per the owner-locked spec —
|
|
100
|
+
// the worst case is a genuinely-looping app whose log happens to
|
|
101
|
+
// contain "502" escaping suspension, which is the safe direction to err.
|
|
102
|
+
{
|
|
103
|
+
pattern: /Bad Gateway|HTTP 502|\b502\b/i,
|
|
104
|
+
result: {
|
|
105
|
+
title: "Gateway error (transient)",
|
|
106
|
+
explanation: "A gateway in front of your app returned 502 Bad Gateway while the platform was routing the deploy. This is a transient platform-side hiccup, not your app code.",
|
|
107
|
+
suggestion: "Wait 30 seconds and run percher publish again. A single retry almost always lands cleanly. If 502s persist across several spaced-out retries, check status.percher.app — that's a platform incident, not your app.",
|
|
108
|
+
errorClass: "infra_unavailable",
|
|
109
|
+
phase: "infra",
|
|
110
|
+
cause: "bad_gateway",
|
|
111
|
+
relevantFiles: [],
|
|
112
|
+
missingEnvVars: [],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
91
115
|
{
|
|
92
116
|
pattern: /start command could not be found|no start command/i,
|
|
93
117
|
result: {
|
|
@@ -229,8 +253,60 @@ const patterns = [
|
|
|
229
253
|
missingEnvVars: [],
|
|
230
254
|
},
|
|
231
255
|
},
|
|
256
|
+
// Track A — A1: platform-at-capacity / box-admission rejection. MUST sit
|
|
257
|
+
// BEFORE the PocketBase pattern below: when the box fills during a
|
|
258
|
+
// PocketBase-sidecar start, the failure string mentions "pocketbase" and
|
|
259
|
+
// the loose PB matcher would swallow it into the generic "PocketBase
|
|
260
|
+
// error" config advice — telling the dev to fix their toml for a problem
|
|
261
|
+
// that is purely the platform being full. First-wins ordering makes the
|
|
262
|
+
// capacity verdict consistent regardless of which stage the box filled
|
|
263
|
+
// at. The tokens match the admission-control rejection reason
|
|
264
|
+
// ("…would exceed committed_budget…") and the build-log capacity banner
|
|
265
|
+
// ("The platform is at capacity"). Deliberately NOT matched: the
|
|
266
|
+
// per-account pool message ("Pool limit exceeded … your plan has only …")
|
|
267
|
+
// which is app-attributable (the user must upgrade or trim) and carries
|
|
268
|
+
// none of these platform tokens.
|
|
269
|
+
//
|
|
270
|
+
// Tokens are deliberately ANCHORED to platform/admission wording (codex
|
|
271
|
+
// P2): a bare `exceed.*budget` / `at capacity` would also swallow ordinary
|
|
272
|
+
// app errors like "token budget exceeded" or "connection pool at capacity"
|
|
273
|
+
// and let them escape the failure-loop guard. `committed_budget` is the
|
|
274
|
+
// literal token in the admission reason; "platform is at capacity" is the
|
|
275
|
+
// build-log banner (run-stage); "box admission" is the banner's own
|
|
276
|
+
// header ("--- Box admission control ---"). All three are Percher-only
|
|
277
|
+
// strings — `box admission` is scoped (codex P2 r2) so a user app's own
|
|
278
|
+
// "admission control" error (a k8s admission webhook, a policy-engine
|
|
279
|
+
// denial) is NOT mislabeled as the platform being full. The `\b` before
|
|
280
|
+
// `box` stops `sandbox admission denied` (box is a suffix of sandbox)
|
|
281
|
+
// from matching, while still allowing "Box admission" / "per-box
|
|
282
|
+
// admission" (codex P2 r3).
|
|
283
|
+
{
|
|
284
|
+
pattern: /committed_budget|\bbox admission|platform (?:is )?at capacity/i,
|
|
285
|
+
result: {
|
|
286
|
+
title: "Platform at capacity",
|
|
287
|
+
explanation: "The platform couldn't fit this deploy right now — the box is full, so the box-admission gate refused to start the container. This is a platform-side limit, not a problem with your app code or configuration.",
|
|
288
|
+
suggestion: "Wait a few minutes and run percher publish again — capacity frees up as other deploys finish. This failure does not count against your app, and a retry usually lands once there's room. If it persists, contact the operator.",
|
|
289
|
+
errorClass: "platform_at_capacity",
|
|
290
|
+
phase: "infra",
|
|
291
|
+
cause: "box_admission_rejected",
|
|
292
|
+
relevantFiles: [],
|
|
293
|
+
missingEnvVars: [],
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
// The PocketBase matcher is deliberately TIGHT (sync-locked with the API
|
|
297
|
+
// classifier): it must fire on a real PB failure but NOT on a bare
|
|
298
|
+
// `POCKETBASE_URL` / `POCKETBASE_ADMIN_*` env mention that can appear in a
|
|
299
|
+
// PB app's build log or container output. The old `/POCKETBASE/i`
|
|
300
|
+
// alternative matched that env-var NAME, so any unrelated failure for a PB
|
|
301
|
+
// app risked being rewritten into misleading "fix your percher.toml" advice
|
|
302
|
+
// (the class of bug behind the 2026-06-02 kvittakvitto report). Now
|
|
303
|
+
// "pocketbase" must sit within ~40 same-line chars of an actual failure
|
|
304
|
+
// word — matching the real platform strings ("PocketBase authentication
|
|
305
|
+
// failed", "PocketBase health check timed out after Nms", "PocketBase
|
|
306
|
+
// superuser creation could not be verified") and app-side "failed to
|
|
307
|
+
// connect to PocketBase".
|
|
232
308
|
{
|
|
233
|
-
pattern: /
|
|
309
|
+
pattern: /pocketbase[^\n]{0,40}(?:error|fail|crash|panic|exit|unhealthy|could ?not|cannot|connection refused|timed out|not (?:start|ready|reachable))|(?:failed to (?:start|connect|reach)|error (?:starting|connecting to)|unable to (?:start|reach|connect to))[^\n]{0,40}pocketbase/i,
|
|
234
310
|
result: {
|
|
235
311
|
title: "PocketBase error",
|
|
236
312
|
explanation: "PocketBase could not start or connect properly.",
|