run402 3.7.14 → 3.8.1
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 +4 -2
- package/cli.mjs +6 -0
- package/core-dist/config.js +9 -0
- package/core-dist/keystore.js +103 -68
- package/core-dist/profile-state.js +137 -0
- package/lib/config.mjs +31 -20
- package/lib/credentials.mjs +273 -0
- package/lib/deploy-v2.mjs +11 -0
- package/lib/doctor.mjs +18 -2
- package/lib/domains.mjs +40 -20
- package/lib/next-actions.mjs +7 -0
- package/lib/projects.mjs +65 -27
- package/lib/sdk-errors.mjs +24 -3
- package/lib/sdk.mjs +3 -2
- package/lib/up.mjs +19 -0
- package/lib/up.test.mjs +29 -0
- package/lib/update-check.mjs +722 -0
- package/lib/update-check.test.mjs +438 -0
- package/package.json +1 -1
- package/sdk/core-dist/config.js +9 -0
- package/sdk/core-dist/keystore.js +103 -68
- package/sdk/core-dist/profile-state.js +137 -0
- package/sdk/dist/credentials.d.ts +24 -7
- package/sdk/dist/credentials.d.ts.map +1 -1
- package/sdk/dist/credentials.js +4 -4
- package/sdk/dist/errors.d.ts +27 -1
- package/sdk/dist/errors.d.ts.map +1 -1
- package/sdk/dist/errors.js +61 -3
- package/sdk/dist/errors.js.map +1 -1
- package/sdk/dist/index.d.ts +16 -2
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +9 -3
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/kernel.d.ts +9 -0
- package/sdk/dist/kernel.d.ts.map +1 -1
- package/sdk/dist/kernel.js +40 -1
- package/sdk/dist/kernel.js.map +1 -1
- package/sdk/dist/namespaces/ai.d.ts.map +1 -1
- package/sdk/dist/namespaces/ai.js +5 -10
- package/sdk/dist/namespaces/ai.js.map +1 -1
- package/sdk/dist/namespaces/apps.d.ts.map +1 -1
- package/sdk/dist/namespaces/apps.js +5 -13
- package/sdk/dist/namespaces/apps.js.map +1 -1
- package/sdk/dist/namespaces/assets.d.ts.map +1 -1
- package/sdk/dist/namespaces/assets.js +9 -22
- package/sdk/dist/namespaces/assets.js.map +1 -1
- package/sdk/dist/namespaces/auth.d.ts.map +1 -1
- package/sdk/dist/namespaces/auth.js +16 -43
- package/sdk/dist/namespaces/auth.js.map +1 -1
- package/sdk/dist/namespaces/contracts.d.ts.map +1 -1
- package/sdk/dist/namespaces/contracts.js +12 -31
- package/sdk/dist/namespaces/contracts.js.map +1 -1
- package/sdk/dist/namespaces/credentials.d.ts +48 -0
- package/sdk/dist/namespaces/credentials.d.ts.map +1 -0
- package/sdk/dist/namespaces/credentials.js +115 -0
- package/sdk/dist/namespaces/credentials.js.map +1 -0
- package/sdk/dist/namespaces/domains.d.ts +8 -2
- package/sdk/dist/namespaces/domains.d.ts.map +1 -1
- package/sdk/dist/namespaces/domains.js +41 -23
- package/sdk/dist/namespaces/domains.js.map +1 -1
- package/sdk/dist/namespaces/email.d.ts.map +1 -1
- package/sdk/dist/namespaces/email.js +8 -19
- package/sdk/dist/namespaces/email.js.map +1 -1
- package/sdk/dist/namespaces/functions.d.ts.map +1 -1
- package/sdk/dist/namespaces/functions.js +14 -37
- package/sdk/dist/namespaces/functions.js.map +1 -1
- package/sdk/dist/namespaces/jobs.d.ts.map +1 -1
- package/sdk/dist/namespaces/jobs.js +8 -19
- package/sdk/dist/namespaces/jobs.js.map +1 -1
- package/sdk/dist/namespaces/projects.d.ts +4 -4
- package/sdk/dist/namespaces/projects.d.ts.map +1 -1
- package/sdk/dist/namespaces/projects.js +18 -42
- package/sdk/dist/namespaces/projects.js.map +1 -1
- package/sdk/dist/namespaces/secrets.d.ts.map +1 -1
- package/sdk/dist/namespaces/secrets.js +5 -10
- package/sdk/dist/namespaces/secrets.js.map +1 -1
- package/sdk/dist/namespaces/sender-domain.d.ts.map +1 -1
- package/sdk/dist/namespaces/sender-domain.js +6 -16
- package/sdk/dist/namespaces/sender-domain.js.map +1 -1
- package/sdk/dist/namespaces/subdomains.d.ts.map +1 -1
- package/sdk/dist/namespaces/subdomains.js +5 -10
- package/sdk/dist/namespaces/subdomains.js.map +1 -1
- package/sdk/dist/node/actions-node.d.ts.map +1 -1
- package/sdk/dist/node/actions-node.js +46 -15
- package/sdk/dist/node/actions-node.js.map +1 -1
- package/sdk/dist/node/credentials.d.ts +18 -4
- package/sdk/dist/node/credentials.d.ts.map +1 -1
- package/sdk/dist/node/credentials.js +37 -9
- package/sdk/dist/node/credentials.js.map +1 -1
- package/sdk/dist/node/index.d.ts +11 -2
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +26 -1
- package/sdk/dist/node/index.js.map +1 -1
- package/sdk/dist/node/target-profile.d.ts.map +1 -1
- package/sdk/dist/node/target-profile.js +6 -4
- package/sdk/dist/node/target-profile.js.map +1 -1
- package/sdk/dist/project-auth-classification.d.ts +125 -0
- package/sdk/dist/project-auth-classification.d.ts.map +1 -0
- package/sdk/dist/project-auth-classification.js +135 -0
- package/sdk/dist/project-auth-classification.js.map +1 -0
- package/sdk/dist/project-credentials.d.ts +4 -0
- package/sdk/dist/project-credentials.d.ts.map +1 -0
- package/sdk/dist/project-credentials.js +9 -0
- package/sdk/dist/project-credentials.js.map +1 -0
package/lib/projects.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
|
-
import {
|
|
2
|
+
import { allowanceAuthHeaders, resolveProjectId, getActiveProjectId, isCoreApiTarget } from "./config.mjs";
|
|
3
3
|
import { loadLiveOperatorSession } from "../core-dist/operator-session.js";
|
|
4
4
|
import { loadLiveControlPlaneSession } from "../core-dist/control-plane-session.js";
|
|
5
5
|
import { withAutoApprove } from "./operator.mjs";
|
|
@@ -19,8 +19,7 @@ Subcommands:
|
|
|
19
19
|
list [--org <id>] [--all] List your projects from the server (name, site_url, custom domains, org_id, active marker)
|
|
20
20
|
rename <id> --name <label> Rename a project (fix an auto-generated name)
|
|
21
21
|
get [id] Authoritative server read: status, org, tier, active deploy, mailbox, usage vs limits (live; no keys)
|
|
22
|
-
|
|
23
|
-
keys [id] Print anon_key and service_key as JSON (local keystore only)
|
|
22
|
+
current Show the active project pointer and validation status
|
|
24
23
|
sql [id] "<query>" [--file <path>] [--params '<json>'] Run a SQL query (supports parameterized queries)
|
|
25
24
|
rest [id] <table> [params] Query a table via the REST API (PostgREST)
|
|
26
25
|
usage [id] Show compute/storage usage for a project
|
|
@@ -45,7 +44,7 @@ Examples:
|
|
|
45
44
|
run402 projects list --all
|
|
46
45
|
run402 projects rename prj_abc123 --name "My Site"
|
|
47
46
|
run402 projects get prj_abc123
|
|
48
|
-
run402 projects
|
|
47
|
+
run402 projects current
|
|
49
48
|
run402 projects sql prj_abc123 "SELECT * FROM users LIMIT 5"
|
|
50
49
|
run402 projects sql prj_abc123 "SELECT * FROM users WHERE id = $1" --params '[42]'
|
|
51
50
|
run402 projects sql prj_abc123 --file setup.sql
|
|
@@ -56,7 +55,6 @@ Examples:
|
|
|
56
55
|
run402 projects validate-expose prj_abc123 --file manifest.json
|
|
57
56
|
run402 projects apply-expose prj_abc123 --file manifest.json
|
|
58
57
|
run402 projects get-expose prj_abc123
|
|
59
|
-
run402 projects keys prj_abc123
|
|
60
58
|
run402 projects delete prj_abc123 --confirm
|
|
61
59
|
|
|
62
60
|
Global options (any command):
|
|
@@ -69,7 +67,9 @@ Notes:
|
|
|
69
67
|
- Most commands that take <id> default to the active project when omitted
|
|
70
68
|
(set it with 'run402 projects use <id>'). Project IDs start with 'prj_';
|
|
71
69
|
any first positional that doesn't is treated as the next argument instead.
|
|
72
|
-
- 'list'
|
|
70
|
+
- 'list', 'get', and 'use' are SERVER-authoritative project identity flows.
|
|
71
|
+
Local project keys live under 'run402 credentials project-keys ...'.
|
|
72
|
+
- 'list' is a SERVER read, not the local key cache: it shows every project the
|
|
73
73
|
active wallet can reach (membership-scoped), with name, site_url, custom
|
|
74
74
|
domains, and org_id. '--org <id>' filters to one org; '--all' reads the
|
|
75
75
|
cross-wallet inventory for every wallet controlling your operator email
|
|
@@ -129,7 +129,7 @@ Examples:
|
|
|
129
129
|
run402 projects list --all
|
|
130
130
|
run402 projects list --wallet work
|
|
131
131
|
`,
|
|
132
|
-
|
|
132
|
+
rename: `run402 projects rename — Rename a project
|
|
133
133
|
|
|
134
134
|
Usage:
|
|
135
135
|
run402 projects rename <id> --name <label>
|
|
@@ -147,6 +147,16 @@ Notes:
|
|
|
147
147
|
|
|
148
148
|
Examples:
|
|
149
149
|
run402 projects rename prj_abc123 --name "My Site"
|
|
150
|
+
`,
|
|
151
|
+
current: `run402 projects current — Inspect active project selection
|
|
152
|
+
|
|
153
|
+
Usage:
|
|
154
|
+
run402 projects current
|
|
155
|
+
|
|
156
|
+
Notes:
|
|
157
|
+
- The active project pointer is local non-secret profile state, not proof that
|
|
158
|
+
local project keys exist.
|
|
159
|
+
- The command attempts an authoritative server read and reports validation.
|
|
150
160
|
`,
|
|
151
161
|
provision: `run402 projects provision — Provision a new Postgres project
|
|
152
162
|
|
|
@@ -541,28 +551,55 @@ async function get(projectId) {
|
|
|
541
551
|
}
|
|
542
552
|
}
|
|
543
553
|
|
|
544
|
-
async function
|
|
554
|
+
async function current() {
|
|
555
|
+
const projectId = getActiveProjectId();
|
|
556
|
+
const out = {
|
|
557
|
+
active_project_id: projectId ?? null,
|
|
558
|
+
source: "profile_state",
|
|
559
|
+
validation: { checked: false, ok: false },
|
|
560
|
+
};
|
|
561
|
+
if (!projectId) {
|
|
562
|
+
console.log(JSON.stringify(out, null, 2));
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
545
565
|
try {
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
rest_url: `${API}/rest/v1`,
|
|
550
|
-
anon_key: data.anon_key,
|
|
551
|
-
service_key: data.service_key,
|
|
552
|
-
site_url: data.site_url || null,
|
|
553
|
-
}, null, 2));
|
|
566
|
+
const detail = await getSdk().projects.get(projectId);
|
|
567
|
+
out.validation = { checked: true, ok: true };
|
|
568
|
+
out.project = detail;
|
|
554
569
|
} catch (err) {
|
|
555
|
-
|
|
556
|
-
|
|
570
|
+
out.validation = {
|
|
571
|
+
checked: true,
|
|
572
|
+
ok: false,
|
|
573
|
+
code: err?.code ?? null,
|
|
574
|
+
message: err?.message ?? String(err),
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
console.log(JSON.stringify(out, null, 2));
|
|
557
578
|
}
|
|
558
579
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
580
|
+
function commandMoved(name) {
|
|
581
|
+
fail({
|
|
582
|
+
code: "COMMAND_MOVED",
|
|
583
|
+
message: `run402 projects ${name} moved to the explicit local credential-cache surface.`,
|
|
584
|
+
hint: "Use `run402 credentials project-keys status/export --project <id>`.",
|
|
585
|
+
details: {
|
|
586
|
+
old_command: `projects ${name}`,
|
|
587
|
+
new_command: name === "keys" ? "credentials project-keys export --reveal" : "credentials project-keys status",
|
|
588
|
+
source: "local_cache",
|
|
589
|
+
},
|
|
590
|
+
next_actions: [
|
|
591
|
+
{
|
|
592
|
+
type: "run_command",
|
|
593
|
+
command: "run402 credentials project-keys status --project <id>",
|
|
594
|
+
why: "Inspect cached key presence without revealing secrets.",
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
type: "run_command",
|
|
598
|
+
command: "run402 credentials project-keys export --project <id> --reveal",
|
|
599
|
+
why: "Reveal cached project keys only when you explicitly need secret material.",
|
|
600
|
+
},
|
|
601
|
+
],
|
|
602
|
+
});
|
|
566
603
|
}
|
|
567
604
|
|
|
568
605
|
async function sqlCmd(projectId, args = []) {
|
|
@@ -794,10 +831,11 @@ export async function run(sub, args) {
|
|
|
794
831
|
case "provision": await provision(args); break;
|
|
795
832
|
case "use": await use(args[0]); break;
|
|
796
833
|
case "list": await list(args); break;
|
|
834
|
+
case "current": await current(); break;
|
|
797
835
|
case "rename": { const { projectId, rest } = resolvePositionalProject(args, { rejectBareFirst: true, valueFlags: FLAGS_BY_SUB.rename.values }); await rename(projectId, rest); break; }
|
|
798
836
|
case "get": { const { projectId } = resolvePositionalProject(args, { rejectBareFirst: true }); await get(projectId); break; }
|
|
799
|
-
case "info":
|
|
800
|
-
case "keys":
|
|
837
|
+
case "info": commandMoved("info"); break;
|
|
838
|
+
case "keys": commandMoved("keys"); break;
|
|
801
839
|
case "sql": { const { projectId, rest } = resolvePositionalProject(args, { maxBarePositionals: 1, valueFlags: FLAGS_BY_SUB.sql.values, rejectBareFirstWhenFlagPresent: ["--file"] }); await sqlCmd(projectId, rest); break; }
|
|
802
840
|
case "rest": { const { projectId, rest: restArgs } = resolvePositionalProject(args); await rest(projectId, restArgs[0], restArgs[1]); break; }
|
|
803
841
|
case "usage": { const { projectId } = resolvePositionalProject(args, { rejectBareFirst: true }); await usage(projectId); break; }
|
package/lib/sdk-errors.mjs
CHANGED
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
* Maps SDK `Run402Error` subclasses into the CLI's canonical error envelope:
|
|
5
5
|
* `{status: "error", code, message, retryable, safe_to_retry, ...}` on stderr,
|
|
6
6
|
* `process.exit(1)`. Preserves specific behaviors:
|
|
7
|
-
* - `
|
|
8
|
-
*
|
|
9
|
-
* local-registry miss from a gateway 404.
|
|
7
|
+
* - `ProjectCredentialNotFound` → preserves
|
|
8
|
+
* `code: "PROJECT_CREDENTIAL_NOT_FOUND"` and local-cache provenance.
|
|
10
9
|
* - HTML / non-JSON error bodies → `body_preview` field (first 500 chars),
|
|
11
10
|
* matching GH-84 behavior.
|
|
12
11
|
* - Network errors → `{status: "error", message: "..."}`.
|
|
@@ -66,6 +65,18 @@ export function parseFlagJson(name, value) {
|
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
export function reportSdkError(err) {
|
|
68
|
+
if (err?.name === "ProjectCredentialNotFound" || err?.code === "PROJECT_CREDENTIAL_NOT_FOUND") {
|
|
69
|
+
fail({
|
|
70
|
+
code: "PROJECT_CREDENTIAL_NOT_FOUND",
|
|
71
|
+
message: err?.message || "Local project credentials are not cached.",
|
|
72
|
+
hint: "This is a local credential-cache miss, not proof that the project does not exist. Use a principal-auth command, or import keys with `run402 credentials project-keys import --project <id> --service-key-stdin` for credential-required operations.",
|
|
73
|
+
details: err?.details,
|
|
74
|
+
next_actions: err?.nextActions,
|
|
75
|
+
retryable: err?.retryable ?? false,
|
|
76
|
+
safe_to_retry: err?.safeToRetry ?? true,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
69
80
|
if (err?.name === "ProjectNotFound") {
|
|
70
81
|
const id = err.projectId || "";
|
|
71
82
|
const hint = id && !String(id).startsWith("prj_")
|
|
@@ -117,6 +128,16 @@ export function reportSdkError(err) {
|
|
|
117
128
|
"Returned as 403 even when the project does not exist, so verify the project id too.";
|
|
118
129
|
}
|
|
119
130
|
|
|
131
|
+
if (typeof payload.code === "string" && payload.code.startsWith("PROJECT_CREDENTIAL_") && payload.hint === undefined) {
|
|
132
|
+
if (payload.code === "PROJECT_CREDENTIAL_PROJECT_MISMATCH") {
|
|
133
|
+
payload.hint =
|
|
134
|
+
"The supplied service key belongs to a different project than the explicit project id. Select the matching project key, or omit service-key mode and use the default principal-auth path when the command supports it.";
|
|
135
|
+
} else if (payload.code === "PROJECT_CREDENTIAL_INVALID" || payload.code === "PROJECT_CREDENTIAL_EXPIRED") {
|
|
136
|
+
payload.hint =
|
|
137
|
+
"The project credential was present but rejected by the server. Re-import or rotate the project key, or use a principal-auth command when available.";
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
120
141
|
// Keep `status: "error"` as the outer envelope even if the response body
|
|
121
142
|
// happened to contain its own `status` field (e.g. `{"status":"degraded"}`
|
|
122
143
|
// from /health 503 responses). Downstream scripts match on this sentinel.
|
package/lib/sdk.mjs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { run402 } from "#sdk/node";
|
|
11
|
-
import { allowanceFile,
|
|
11
|
+
import { allowanceFile, profileStateFile, projectCredentialsFile } from "./config.mjs";
|
|
12
12
|
|
|
13
13
|
export function getSdk(opts = {}) {
|
|
14
14
|
// surface: "cli" opts the default credential resolution into `auto` — wallet
|
|
@@ -16,7 +16,8 @@ export function getSdk(opts = {}) {
|
|
|
16
16
|
return run402({
|
|
17
17
|
surface: "cli",
|
|
18
18
|
allowancePath: allowanceFile(),
|
|
19
|
-
keystorePath:
|
|
19
|
+
keystorePath: projectCredentialsFile(),
|
|
20
|
+
profileStatePath: profileStateFile(),
|
|
20
21
|
...opts,
|
|
21
22
|
});
|
|
22
23
|
}
|
package/lib/up.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { stdin as input, stderr as output } from "node:process";
|
|
|
3
3
|
import { getSdk } from "./sdk.mjs";
|
|
4
4
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
5
5
|
import { assertKnownFlags, flagValue, normalizeArgv, positionalArgs } from "./argparse.mjs";
|
|
6
|
+
import { createUpdateCheckScheduler, emitUpdateNotice } from "./update-check.mjs";
|
|
6
7
|
|
|
7
8
|
const HELP = `run402 up — Provision/link/deploy the current app
|
|
8
9
|
|
|
@@ -44,6 +45,11 @@ Options:
|
|
|
44
45
|
--json-stream Emit NDJSON progress events on stdout and a final result event.
|
|
45
46
|
--quiet Suppress action progress events on stderr.
|
|
46
47
|
|
|
48
|
+
Update notices:
|
|
49
|
+
Stale CLI notices are advisory and never change the result payload or exit
|
|
50
|
+
code. Non-streaming notices are JSON on stderr; --json-stream emits
|
|
51
|
+
cli.update_available as an NDJSON event.
|
|
52
|
+
|
|
47
53
|
Project resolution:
|
|
48
54
|
explicit --project > .run402/project.json > manifest project_id > approved
|
|
49
55
|
project creation from --name > approved active-project fallback.
|
|
@@ -184,6 +190,10 @@ export async function run(args = []) {
|
|
|
184
190
|
});
|
|
185
191
|
}
|
|
186
192
|
const allowWarningCodes = collectRepeatedValues(parsed, "--allow-warning");
|
|
193
|
+
const updateScheduler = createUpdateCheckScheduler({
|
|
194
|
+
command: ["run402", "up", ...parsed],
|
|
195
|
+
});
|
|
196
|
+
emitUpdateNotice(updateScheduler.cachedNotice, { jsonStream, quiet });
|
|
187
197
|
|
|
188
198
|
try {
|
|
189
199
|
const sdk = getSdk();
|
|
@@ -224,11 +234,20 @@ export async function run(args = []) {
|
|
|
224
234
|
} else {
|
|
225
235
|
console.log(JSON.stringify(result, null, 2));
|
|
226
236
|
}
|
|
237
|
+
if (shouldExitNonZeroForUpResult(result)) {
|
|
238
|
+
process.exitCode = 1;
|
|
239
|
+
}
|
|
227
240
|
} catch (err) {
|
|
228
241
|
reportSdkError(err);
|
|
229
242
|
}
|
|
230
243
|
}
|
|
231
244
|
|
|
245
|
+
export function shouldExitNonZeroForUpResult(result) {
|
|
246
|
+
return result?.action === "up" &&
|
|
247
|
+
result?.mode === "apply" &&
|
|
248
|
+
result?.result?.app_result?.status === "deployed_unverified";
|
|
249
|
+
}
|
|
250
|
+
|
|
232
251
|
function parseExecutionMode(args) {
|
|
233
252
|
const modes = [];
|
|
234
253
|
if (args.includes("--check")) modes.push("--check");
|
package/lib/up.test.mjs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { shouldExitNonZeroForUpResult } from "./up.mjs";
|
|
4
|
+
|
|
5
|
+
test("run402 up exits nonzero for deployed-but-unverified app results", () => {
|
|
6
|
+
assert.equal(shouldExitNonZeroForUpResult({
|
|
7
|
+
action: "up",
|
|
8
|
+
mode: "apply",
|
|
9
|
+
result: {
|
|
10
|
+
app_result: { status: "deployed_unverified" },
|
|
11
|
+
},
|
|
12
|
+
}), true);
|
|
13
|
+
|
|
14
|
+
assert.equal(shouldExitNonZeroForUpResult({
|
|
15
|
+
action: "up",
|
|
16
|
+
mode: "apply",
|
|
17
|
+
result: {
|
|
18
|
+
app_result: { status: "succeeded" },
|
|
19
|
+
},
|
|
20
|
+
}), false);
|
|
21
|
+
|
|
22
|
+
assert.equal(shouldExitNonZeroForUpResult({
|
|
23
|
+
action: "up",
|
|
24
|
+
mode: "check",
|
|
25
|
+
result: {
|
|
26
|
+
app_result: { status: "deployed_unverified" },
|
|
27
|
+
},
|
|
28
|
+
}), false);
|
|
29
|
+
});
|