@kindgi/cli 0.1.8 → 0.1.9-rc.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 +59 -2
- package/dist/api-address.d.ts +104 -12
- package/dist/api-address.d.ts.map +1 -1
- package/dist/api-address.js +169 -30
- package/dist/api-address.js.map +1 -1
- package/dist/cli.js +12 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/approvals.d.ts.map +1 -1
- package/dist/commands/approvals.js +15 -3
- package/dist/commands/approvals.js.map +1 -1
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +27 -0
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/build.d.ts +18 -0
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +118 -13
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/conversations.d.ts.map +1 -1
- package/dist/commands/conversations.js +13 -3
- package/dist/commands/conversations.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +235 -53
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/deployments.d.ts +3 -0
- package/dist/commands/deployments.d.ts.map +1 -0
- package/dist/commands/deployments.js +196 -0
- package/dist/commands/deployments.js.map +1 -0
- package/dist/commands/doctor.d.ts +1 -5
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +42 -22
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/egress.d.ts +9 -0
- package/dist/commands/egress.d.ts.map +1 -0
- package/dist/commands/egress.js +40 -0
- package/dist/commands/egress.js.map +1 -0
- package/dist/commands/env-scoped.d.ts +5 -0
- package/dist/commands/env-scoped.d.ts.map +1 -1
- package/dist/commands/env-scoped.js +64 -19
- package/dist/commands/env-scoped.js.map +1 -1
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +91 -30
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/environments.d.ts +6 -0
- package/dist/commands/environments.d.ts.map +1 -0
- package/dist/commands/environments.js +166 -0
- package/dist/commands/environments.js.map +1 -0
- package/dist/commands/exports.d.ts.map +1 -1
- package/dist/commands/exports.js +118 -3
- package/dist/commands/exports.js.map +1 -1
- package/dist/commands/feedback.d.ts +1 -2
- package/dist/commands/feedback.d.ts.map +1 -1
- package/dist/commands/feedback.js +55 -24
- package/dist/commands/feedback.js.map +1 -1
- package/dist/commands/helpers.d.ts +6 -0
- package/dist/commands/helpers.d.ts.map +1 -1
- package/dist/commands/helpers.js +8 -0
- package/dist/commands/helpers.js.map +1 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +12 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/license.d.ts +3 -0
- package/dist/commands/license.d.ts.map +1 -1
- package/dist/commands/license.js +1 -1
- package/dist/commands/license.js.map +1 -1
- package/dist/commands/people.d.ts.map +1 -1
- package/dist/commands/people.js +105 -10
- package/dist/commands/people.js.map +1 -1
- package/dist/commands/projects.d.ts.map +1 -1
- package/dist/commands/projects.js +133 -11
- package/dist/commands/projects.js.map +1 -1
- package/dist/commands/promote.d.ts +38 -0
- package/dist/commands/promote.d.ts.map +1 -0
- package/dist/commands/promote.js +371 -0
- package/dist/commands/promote.js.map +1 -0
- package/dist/commands/proposals.d.ts.map +1 -1
- package/dist/commands/proposals.js +2 -1
- package/dist/commands/proposals.js.map +1 -1
- package/dist/commands/providers.d.ts.map +1 -1
- package/dist/commands/providers.js +29 -5
- package/dist/commands/providers.js.map +1 -1
- package/dist/commands/schedules.d.ts.map +1 -1
- package/dist/commands/schedules.js +30 -2
- package/dist/commands/schedules.js.map +1 -1
- package/dist/commands/secrets.d.ts.map +1 -1
- package/dist/commands/secrets.js +65 -40
- package/dist/commands/secrets.js.map +1 -1
- package/dist/commands/sign-in.d.ts +3 -0
- package/dist/commands/sign-in.d.ts.map +1 -0
- package/dist/commands/sign-in.js +61 -0
- package/dist/commands/sign-in.js.map +1 -0
- package/dist/commands/sites.d.ts +3 -0
- package/dist/commands/sites.d.ts.map +1 -0
- package/dist/commands/sites.js +244 -0
- package/dist/commands/sites.js.map +1 -0
- package/dist/commands/tools.d.ts.map +1 -1
- package/dist/commands/tools.js +2 -2
- package/dist/commands/tools.js.map +1 -1
- package/dist/commands/types.d.ts +11 -0
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/webhooks.d.ts.map +1 -1
- package/dist/commands/webhooks.js +38 -2
- package/dist/commands/webhooks.js.map +1 -1
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +20 -7
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +38 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +12 -1
- package/dist/context.js.map +1 -1
- package/dist/deploy/defaults.d.ts.map +1 -1
- package/dist/deploy/defaults.js +7 -1
- package/dist/deploy/defaults.js.map +1 -1
- package/dist/deploy/pack-service-text.d.ts +29 -0
- package/dist/deploy/pack-service-text.d.ts.map +1 -0
- package/dist/deploy/pack-service-text.js +72 -0
- package/dist/deploy/pack-service-text.js.map +1 -0
- package/dist/deploy/post.d.ts +17 -4
- package/dist/deploy/post.d.ts.map +1 -1
- package/dist/deploy/post.js +177 -60
- package/dist/deploy/post.js.map +1 -1
- package/dist/deploy/promotion-path.d.ts +44 -0
- package/dist/deploy/promotion-path.d.ts.map +1 -0
- package/dist/deploy/promotion-path.js +86 -0
- package/dist/deploy/promotion-path.js.map +1 -0
- package/dist/deploy/response.d.ts +3 -0
- package/dist/deploy/response.d.ts.map +1 -1
- package/dist/deploy/response.js +66 -12
- package/dist/deploy/response.js.map +1 -1
- package/dist/deploy/runners.d.ts +99 -5
- package/dist/deploy/runners.d.ts.map +1 -1
- package/dist/dev/java-builder.d.ts.map +1 -1
- package/dist/dev/java-builder.js +19 -3
- package/dist/dev/java-builder.js.map +1 -1
- package/dist/dev/python-builder.d.ts.map +1 -1
- package/dist/dev/python-builder.js +18 -3
- package/dist/dev/python-builder.js.map +1 -1
- package/dist/dev/runtime-container.d.ts +28 -3
- package/dist/dev/runtime-container.d.ts.map +1 -1
- package/dist/dev/runtime-container.js +103 -36
- package/dist/dev/runtime-container.js.map +1 -1
- package/dist/dev/runtime-image.d.ts +1 -1
- package/dist/dev/runtime-image.d.ts.map +1 -1
- package/dist/dev/runtime-image.js +1 -1
- package/dist/dev/runtime-image.js.map +1 -1
- package/dist/dev/scala-builder.d.ts.map +1 -1
- package/dist/dev/scala-builder.js +55 -21
- package/dist/dev/scala-builder.js.map +1 -1
- package/dist/dev/shared-watch.d.ts +30 -0
- package/dist/dev/shared-watch.d.ts.map +1 -1
- package/dist/dev/shared-watch.js +46 -1
- package/dist/dev/shared-watch.js.map +1 -1
- package/dist/env/pack-env-hint.d.ts +52 -0
- package/dist/env/pack-env-hint.d.ts.map +1 -0
- package/dist/env/pack-env-hint.js +57 -0
- package/dist/env/pack-env-hint.js.map +1 -0
- package/dist/env/pack-env-plan.d.ts +2 -0
- package/dist/env/pack-env-plan.d.ts.map +1 -1
- package/dist/env/pack-env-plan.js +2 -1
- package/dist/env/pack-env-plan.js.map +1 -1
- package/dist/env-projects.d.ts +23 -0
- package/dist/env-projects.d.ts.map +1 -0
- package/dist/env-projects.js +28 -0
- package/dist/env-projects.js.map +1 -0
- package/dist/exports/create.d.ts +71 -0
- package/dist/exports/create.d.ts.map +1 -0
- package/dist/exports/create.js +576 -0
- package/dist/exports/create.js.map +1 -0
- package/dist/exports/plan.d.ts +69 -0
- package/dist/exports/plan.d.ts.map +1 -0
- package/dist/exports/plan.js +496 -0
- package/dist/exports/plan.js.map +1 -0
- package/dist/exports/reader.d.ts +48 -0
- package/dist/exports/reader.d.ts.map +1 -0
- package/dist/exports/reader.js +181 -0
- package/dist/exports/reader.js.map +1 -0
- package/dist/feedback/entries.d.ts +53 -0
- package/dist/feedback/entries.d.ts.map +1 -0
- package/dist/feedback/entries.js +173 -0
- package/dist/feedback/entries.js.map +1 -0
- package/dist/feedback/intake.d.ts +37 -0
- package/dist/feedback/intake.d.ts.map +1 -0
- package/dist/feedback/intake.js +101 -0
- package/dist/feedback/intake.js.map +1 -0
- package/dist/feedback/protocol.d.ts +111 -0
- package/dist/feedback/protocol.d.ts.map +1 -0
- package/dist/feedback/protocol.js +143 -0
- package/dist/feedback/protocol.js.map +1 -0
- package/dist/feedback/redact.d.ts +34 -0
- package/dist/feedback/redact.d.ts.map +1 -0
- package/dist/feedback/redact.js +258 -0
- package/dist/feedback/redact.js.map +1 -0
- package/dist/feedback/report.d.ts +51 -0
- package/dist/feedback/report.d.ts.map +1 -0
- package/dist/feedback/report.js +122 -0
- package/dist/feedback/report.js.map +1 -0
- package/dist/feedback/run.d.ts +21 -0
- package/dist/feedback/run.d.ts.map +1 -0
- package/dist/feedback/run.js +578 -0
- package/dist/feedback/run.js.map +1 -0
- package/dist/feedback/store.d.ts +24 -0
- package/dist/feedback/store.d.ts.map +1 -0
- package/dist/feedback/store.js +117 -0
- package/dist/feedback/store.js.map +1 -0
- package/dist/main.d.ts +6 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +86 -5
- package/dist/main.js.map +1 -1
- package/dist/node-version.d.ts +13 -0
- package/dist/node-version.d.ts.map +1 -0
- package/dist/node-version.js +37 -0
- package/dist/node-version.js.map +1 -0
- package/dist/parse.d.ts +8 -2
- package/dist/parse.d.ts.map +1 -1
- package/dist/parse.js +7 -2
- package/dist/parse.js.map +1 -1
- package/dist/project-ref.d.ts +44 -0
- package/dist/project-ref.d.ts.map +1 -0
- package/dist/project-ref.js +69 -0
- package/dist/project-ref.js.map +1 -0
- package/dist/sdk-skills/kindgi-authoring-agents/SKILL.md +15 -2
- package/dist/sdk-skills/kindgi-authoring-flows/SKILL.md +1 -1
- package/dist/sdk-skills/kindgi-authoring-guardrails/SKILL.md +44 -7
- package/dist/sdk-skills/kindgi-authoring-mcp-servers/SKILL.md +11 -2
- package/dist/sdk-skills/kindgi-authoring-providers/SKILL.md +60 -13
- package/dist/sdk-skills/kindgi-authoring-tools/SKILL.md +19 -2
- package/dist/sdk-skills/kindgi-framework-feedback/SKILL.md +82 -3
- package/dist/sdk-skills/kindgi-getting-started/SKILL.md +1 -1
- package/dist/sdk-skills/kindgi-java-authoring-agents/SKILL.md +17 -3
- package/dist/sdk-skills/kindgi-java-authoring-flows/SKILL.md +1 -1
- package/dist/sdk-skills/kindgi-java-authoring-guardrails/SKILL.md +43 -6
- package/dist/sdk-skills/kindgi-java-authoring-tools/SKILL.md +19 -2
- package/dist/sdk-skills/kindgi-java-getting-started/SKILL.md +1 -1
- package/dist/sdk-skills/kindgi-python-authoring-agents/SKILL.md +17 -3
- package/dist/sdk-skills/kindgi-python-authoring-flows/SKILL.md +1 -1
- package/dist/sdk-skills/kindgi-python-authoring-guardrails/SKILL.md +49 -13
- package/dist/sdk-skills/kindgi-python-authoring-tools/SKILL.md +27 -3
- package/dist/sdk-skills/kindgi-python-getting-started/SKILL.md +1 -1
- package/dist/sdk-skills/kindgi-scala-authoring-agents/SKILL.md +17 -3
- package/dist/sdk-skills/kindgi-scala-authoring-flows/SKILL.md +1 -1
- package/dist/sdk-skills/kindgi-scala-authoring-guardrails/SKILL.md +43 -6
- package/dist/sdk-skills/kindgi-scala-authoring-tools/SKILL.md +19 -2
- package/dist/sdk-skills/kindgi-scala-getting-started/SKILL.md +1 -1
- package/dist/sdk-skills/kindgi-using/SKILL.md +1 -1
- package/dist/slug.d.ts +18 -0
- package/dist/slug.d.ts.map +1 -0
- package/dist/slug.js +29 -0
- package/dist/slug.js.map +1 -0
- package/dist/templates/drupal/AGENTS.md +2 -0
- package/dist/templates/java/AGENTS.md +2 -0
- package/dist/templates/minimal/AGENTS.md +2 -0
- package/dist/templates/python/AGENTS.md +2 -0
- package/dist/templates/sample/AGENTS.md +2 -0
- package/dist/templates/scala/AGENTS.md +2 -0
- package/dist/templates/woocommerce/AGENTS.md +11 -4
- package/dist/templates/woocommerce/README.md.tmpl +47 -4
- package/dist/templates/woocommerce/agents/store-agent/index.test.ts.tmpl +13 -0
- package/dist/templates/woocommerce/agents/store-agent/index.ts.tmpl +11 -1
- package/dist/templates/woocommerce/tools/publish-product/index.ts.tmpl +7 -1
- package/dist/templates/woocommerce/tools/refund/index.ts.tmpl +7 -2
- package/dist/templates/woocommerce/tools/refund-large/index.ts.tmpl +8 -2
- package/dist/templates/woocommerce/tools/set-price/index.ts.tmpl +6 -0
- package/dist/value-target.d.ts +139 -0
- package/dist/value-target.d.ts.map +1 -0
- package/dist/value-target.js +231 -0
- package/dist/value-target.js.map +1 -0
- package/package.json +13 -13
package/dist/commands/deploy.js
CHANGED
|
@@ -28,21 +28,30 @@ import { stat } from 'node:fs/promises';
|
|
|
28
28
|
import { isAbsolute, join, resolve } from 'node:path';
|
|
29
29
|
import { LOCAL_ENV_NAME, packValues, readPackEnv } from '@kindgi/secrets-dotenv';
|
|
30
30
|
import { deployApiAddress, hostedApiAddress } from '../api-address.js';
|
|
31
|
-
import { HOSTED_BUILD } from '../build/hosted.js';
|
|
32
31
|
import { isEnvelopeSigned } from '../deploy/envelope-loader.js';
|
|
32
|
+
import { packServiceProblemText, packServiceRetiredText } from '../deploy/pack-service-text.js';
|
|
33
33
|
import { buildPostBody, deriveIdempotencyKey, deriveSyncSecretsIdempotencyKey, } from '../deploy/post.js';
|
|
34
|
+
import { promotionPath } from '../deploy/promotion-path.js';
|
|
34
35
|
import { renderResponse } from '../deploy/response.js';
|
|
36
|
+
import { envProjectRefs } from '../env-projects.js';
|
|
37
|
+
import { packEnvProblemLines, packEnvSecretSetCommand, packEnvSetCommand, } from '../env/pack-env-hint.js';
|
|
35
38
|
import { describePackEnvPlan, plainSecretReason, planPackEnv, } from '../env/pack-env-plan.js';
|
|
36
39
|
import { renderJson } from '../output.js';
|
|
37
40
|
import { loadPackConfig } from '../pack-config.js';
|
|
41
|
+
import { PROJECT_ID_RE } from '../project-ref.js';
|
|
38
42
|
import { isWait, sendWaitingOutRateLimits } from '../rate-limit-wait.js';
|
|
43
|
+
import { SLUG } from '../slug.js';
|
|
44
|
+
import { environmentsOf, resolveValueTarget } from '../value-target.js';
|
|
39
45
|
import { runBuild } from './build.js';
|
|
40
46
|
/** Default relative path to look for a pre-built envelope. */
|
|
41
47
|
export const DEFAULT_ENVELOPE_RELATIVE_PATH = '.kindgi/build/deploy-envelope.json';
|
|
42
48
|
export const deployCommand = {
|
|
43
49
|
kind: 'leaf',
|
|
44
50
|
name: 'deploy',
|
|
45
|
-
description: 'Deploy a signed pack image to Kindgi. Reads deploy-envelope.json (or runs `kindgi build` inline) + POSTs to /v1/deployments.'
|
|
51
|
+
description: 'Deploy a signed pack image to Kindgi. Reads deploy-envelope.json (or runs `kindgi build` inline) + POSTs to /v1/deployments. ' +
|
|
52
|
+
"`--hosted` (or the env block's `build: 'hosted'`) deploys to Kindgi hosted: an inline build runs on the hosted build service (as `kindgi build --hosted`), and after the deploy it waits, up to 5 minutes, for the deployment's pack service to be ready. " +
|
|
53
|
+
"A deploy into the Default project with a pack tool whose id is an MCP endpoint's tool's is refused (`tool-kind-mismatch`), and nothing is deployed.",
|
|
54
|
+
hostedRun: { defaultEnv: 'staging' },
|
|
46
55
|
usage: 'kindgi deploy [--env <name>] [--from-envelope <path>] [--endpoint <url>] [--token <bearer>] ' +
|
|
47
56
|
'[--tenant <id>] [--project <id|slug>] [--hosted] [--idempotency-key <str>] [--dry-run] [--sync-secrets] [--allow-missing-env] ' +
|
|
48
57
|
'[--target <t>] [--build-endpoint <url>] [--out <dir>] [--artifact-version <v>] [--published-at <iso>] ' +
|
|
@@ -69,10 +78,6 @@ export const deployCommand = {
|
|
|
69
78
|
type: 'string',
|
|
70
79
|
description: "The project to deploy into, one per client: its id, or its slug. A slug two orgs share is refused: pass the id. Default: a tenant-wide deploy, into the tenant's Default project.",
|
|
71
80
|
},
|
|
72
|
-
hosted: {
|
|
73
|
-
type: 'boolean',
|
|
74
|
-
description: "Deploy to Kindgi hosted: an inline build runs on the hosted build service (as `kindgi build --hosted`), and after the deploy it waits, up to 5 minutes, for the deployment's pack service to be ready. Same as the env block's `build: 'hosted'`.",
|
|
75
|
-
},
|
|
76
81
|
'idempotency-key': {
|
|
77
82
|
type: 'string',
|
|
78
83
|
description: 'The `Idempotency-Key` header. Default: a hash of the request body, so the same image deployed again returns the same deployment.',
|
|
@@ -85,7 +90,7 @@ export const deployCommand = {
|
|
|
85
90
|
// verticals rotate secrets on a separate cadence.
|
|
86
91
|
'sync-secrets': {
|
|
87
92
|
type: 'boolean',
|
|
88
|
-
description: "After the deploy lands, also send the values in `.env.<env>` to the deployment's secrets. Off by default.",
|
|
93
|
+
description: "After the deploy lands, also send the values in `.env.<env>` to the deployment's secrets. Off by default. A hosted deploy needs --env with it.",
|
|
89
94
|
},
|
|
90
95
|
// Deploy although a name the pack's env.required declares has no
|
|
91
96
|
// value for --env (the pack service won't be ready until it has).
|
|
@@ -104,7 +109,7 @@ export const deployCommand = {
|
|
|
104
109
|
},
|
|
105
110
|
out: {
|
|
106
111
|
type: 'string',
|
|
107
|
-
description: 'Where the envelope is looked for, and an inline build writes
|
|
112
|
+
description: 'Where the envelope is looked for, and an inline build writes (default: `.kindgi/build`). An inline build applies the same --out rules as `kindgi build`: not the pack root or a directory containing it, and under it for a TypeScript pack.',
|
|
108
113
|
},
|
|
109
114
|
'artifact-version': {
|
|
110
115
|
type: 'string',
|
|
@@ -173,13 +178,49 @@ export async function runDeploy(ctx) {
|
|
|
173
178
|
const envRefusal = checkPackEnv(args, line);
|
|
174
179
|
if (envRefusal !== undefined)
|
|
175
180
|
return envRefusal;
|
|
181
|
+
// On a runtime that keeps its values under one env, a pack environment
|
|
182
|
+
// with a `project` is that project, and its secret sync goes there, under
|
|
183
|
+
// the env the runtime serves. Any other env's sync is refused here,
|
|
184
|
+
// before a build or an upload, as the runtime would refuse the sync after
|
|
185
|
+
// the deployment.
|
|
186
|
+
let syncEnvName = args.envName;
|
|
187
|
+
let environmentProject;
|
|
188
|
+
const named = args.environments[args.envName] !== undefined;
|
|
189
|
+
if ((args.syncSecrets || named) && runners.servedEnv !== undefined) {
|
|
190
|
+
const target = resolveValueTarget({
|
|
191
|
+
flag: args.envName,
|
|
192
|
+
served: await runners.servedEnv({
|
|
193
|
+
endpoint: args.endpoint,
|
|
194
|
+
token: args.token,
|
|
195
|
+
fetchImpl: ctx.fetch,
|
|
196
|
+
...(ctx.stopSignal !== undefined && { signal: ctx.stopSignal }),
|
|
197
|
+
}),
|
|
198
|
+
write: args.syncSecrets,
|
|
199
|
+
what: `the secrets in .env.${args.envName}`,
|
|
200
|
+
mayOmit: false,
|
|
201
|
+
environments: args.environments,
|
|
202
|
+
});
|
|
203
|
+
if (target.kind === 'refused')
|
|
204
|
+
return failure(target.stderr);
|
|
205
|
+
if (target.kind === 'ok' && target.environment !== undefined) {
|
|
206
|
+
environmentProject = target.environment.projectRef;
|
|
207
|
+
syncEnvName = target.envName;
|
|
208
|
+
line(` Project: ${environmentProject} (environments.${args.envName}.project)`);
|
|
209
|
+
if (args.syncSecrets)
|
|
210
|
+
line(` Secrets: under ${syncEnvName}, the env the runtime serves`);
|
|
211
|
+
line('');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
176
214
|
// ---- 1. The project to deploy into (--project) ---------------------
|
|
177
215
|
//
|
|
178
216
|
// Resolved first: a project that isn't there fails before a build, and
|
|
179
217
|
// a hosted build carries the project's id as its label.
|
|
180
|
-
const project = await
|
|
218
|
+
const project = await resolveDeployProject(args, environmentProject, runners, ctx);
|
|
181
219
|
if (project.kind === 'error')
|
|
182
220
|
return failure(project.message);
|
|
221
|
+
const hostedEnvRefusal = refuseHostedConfigEnv(args, project.id);
|
|
222
|
+
if (hostedEnvRefusal !== undefined)
|
|
223
|
+
return hostedEnvRefusal;
|
|
183
224
|
// ---- 2. Ensure envelope on disk -------------------------------------
|
|
184
225
|
const envelopeReady = await ensureEnvelopeExists(ctx, args, runners, line, project.id);
|
|
185
226
|
if (envelopeReady.kind === 'error')
|
|
@@ -286,6 +327,7 @@ export async function runDeploy(ctx) {
|
|
|
286
327
|
...(ctx.stopSignal !== undefined && { signal: ctx.stopSignal }),
|
|
287
328
|
}), deployRefusal);
|
|
288
329
|
// ---- 10. Render response ---------------------------------------------
|
|
330
|
+
const promotionPath = await promotionPathOf(args, runners, ctx, result);
|
|
289
331
|
const rendered = renderResponse(result, {
|
|
290
332
|
envName: args.envName,
|
|
291
333
|
endpoint: args.endpoint,
|
|
@@ -296,6 +338,7 @@ export async function runDeploy(ctx) {
|
|
|
296
338
|
signerKeyId: envelope.signerKeyId ?? '(unsigned)',
|
|
297
339
|
envelopePath: args.envelopePath,
|
|
298
340
|
idempotencyKey,
|
|
341
|
+
promotionPath,
|
|
299
342
|
});
|
|
300
343
|
for (const l of rendered.banner)
|
|
301
344
|
line(l);
|
|
@@ -318,7 +361,7 @@ export async function runDeploy(ctx) {
|
|
|
318
361
|
const syncSummary = { performed: false };
|
|
319
362
|
const deployedRecord = result.kind === 'created' || result.kind === 'replayed' ? result.record : null;
|
|
320
363
|
if (args.syncSecrets && deployedRecord !== null) {
|
|
321
|
-
const syncOutcome = await runSecretsSync(ctx, args, runners, deployedRecord.deploymentId, line);
|
|
364
|
+
const syncOutcome = await runSecretsSync(ctx, args, syncEnvName, runners, deployedRecord.deploymentId, line);
|
|
322
365
|
Object.assign(syncSummary, syncOutcome);
|
|
323
366
|
}
|
|
324
367
|
// ---- 12. Hosted: wait for the deployment's pack service --------------
|
|
@@ -327,7 +370,7 @@ export async function runDeploy(ctx) {
|
|
|
327
370
|
// deployment is in place whatever the wait finds.
|
|
328
371
|
let packService;
|
|
329
372
|
if (args.hosted && deployedRecord !== null && deployedRecord.endpoint !== undefined) {
|
|
330
|
-
const waited = await waitForPackService(ctx, args, runners, deployedRecord, line);
|
|
373
|
+
const waited = await waitForPackService(ctx, args, runners, deployedRecord, line, project.id);
|
|
331
374
|
if (waited.kind === 'error') {
|
|
332
375
|
return { kind: 'error', stderr: `${banner.join('\n')}\n${waited.message}`, exitCode: 1 };
|
|
333
376
|
}
|
|
@@ -370,7 +413,9 @@ function deployRefusal(result) {
|
|
|
370
413
|
* opted in via `--sync-secrets`. Warns loudly when secret-shaped keys
|
|
371
414
|
* are present (they should live in `kindgi secrets set` instead).
|
|
372
415
|
*/
|
|
373
|
-
async function runSecretsSync(ctx, args,
|
|
416
|
+
async function runSecretsSync(ctx, args,
|
|
417
|
+
/** The env the values go under: `--env`, or the one a runtime that keeps its values under one serves. */
|
|
418
|
+
envName, runners, deploymentId, line) {
|
|
374
419
|
const file = await detectSecretsFile(args, '');
|
|
375
420
|
if (file === null || file.entries.length === 0) {
|
|
376
421
|
line(' --sync-secrets set, but no `.env.<envName>` entries to sync. Skipping.');
|
|
@@ -389,7 +434,7 @@ async function runSecretsSync(ctx, args, runners, deploymentId, line) {
|
|
|
389
434
|
line('');
|
|
390
435
|
}
|
|
391
436
|
const syncBody = {
|
|
392
|
-
envName
|
|
437
|
+
envName,
|
|
393
438
|
secrets: file.entries.map((e) => ({ name: e.name, value: e.value })),
|
|
394
439
|
};
|
|
395
440
|
const idempotencyKey = deriveSyncSecretsIdempotencyKey(deploymentId, syncBody);
|
|
@@ -437,6 +482,10 @@ async function runSecretsSync(ctx, args, runners, deploymentId, line) {
|
|
|
437
482
|
* value (unless `--allow-missing-env`) or a secret is given in the clear.
|
|
438
483
|
*/
|
|
439
484
|
function checkPackEnv(args, line) {
|
|
485
|
+
// A hosted pack service gets the values set in the workspace, not these:
|
|
486
|
+
// `refuseHostedConfigEnv`, once the project is known.
|
|
487
|
+
if (args.hosted)
|
|
488
|
+
return undefined;
|
|
440
489
|
const plan = args.packEnv;
|
|
441
490
|
if (plan.entries.length === 0 && plan.problems.length === 0 && plan.warnings.length === 0) {
|
|
442
491
|
return undefined;
|
|
@@ -456,6 +505,27 @@ function checkPackEnv(args, line) {
|
|
|
456
505
|
}
|
|
457
506
|
return undefined;
|
|
458
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* A hosted deploy: Kindgi Cloud gives the pack service the env values set
|
|
510
|
+
* in the workspace (`kindgi env set`, a secret with `kindgi secrets set`),
|
|
511
|
+
* at the deployment's project, then its org, then the tenant.
|
|
512
|
+
* `environments.<env>.env` in kindgi.config isn't read there, so a config
|
|
513
|
+
* that gives any is refused before anything is built or sent, naming each
|
|
514
|
+
* name and how to set it.
|
|
515
|
+
*/
|
|
516
|
+
function refuseHostedConfigEnv(args, projectId) {
|
|
517
|
+
const plan = args.packEnv;
|
|
518
|
+
if (!args.hosted || (plan.given.length === 0 && plan.problems.length === 0))
|
|
519
|
+
return undefined;
|
|
520
|
+
const block = `environments.${args.envName}.env`;
|
|
521
|
+
const names = plan.given.length === 0 ? '' : ` (${plan.given.join(', ')})`;
|
|
522
|
+
const target = { envName: args.envName, ...(projectId !== undefined && { projectId }) };
|
|
523
|
+
const command = packEnvSetCommand({ ...target, hasProjectMapping: false });
|
|
524
|
+
const secrets = plan.entries.some((e) => e.source?.kind === 'secret')
|
|
525
|
+
? ` (a secret with \`${packEnvSecretSetCommand(target)}\`)`
|
|
526
|
+
: '';
|
|
527
|
+
return failure(`kindgi deploy: on Kindgi Cloud the pack service gets the env values set in your workspace, not ${block} in kindgi.config${names}. Set each with \`${command}\`${secrets}, then remove ${block} from kindgi.config and deploy again.\n`);
|
|
528
|
+
}
|
|
459
529
|
// ---------------------------------------------------------------------
|
|
460
530
|
// Envelope existence — either read from disk or inline-build
|
|
461
531
|
// ---------------------------------------------------------------------
|
|
@@ -607,25 +677,15 @@ const PACK_SERVICE_WAIT_MS = 5 * 60 * 1000;
|
|
|
607
677
|
const PACK_SERVICE_POLL_MS = 2_000;
|
|
608
678
|
/** Reads in a row that may fail before it stops waiting. */
|
|
609
679
|
const PACK_SERVICE_READ_TRIES = 3;
|
|
610
|
-
/** Why a deployment has no pack service yet, by its `problem.code`. */
|
|
611
|
-
const PACK_SERVICE_PROBLEMS = {
|
|
612
|
-
'tenant-not-provisioned': 'your workspace has nowhere to run pack services yet',
|
|
613
|
-
'capacity-unavailable': 'there is no room for another pack service just now',
|
|
614
|
-
'service-start-failed': "the pack's image was deployed but didn't start",
|
|
615
|
-
'image-not-allowed': "the image is outside your workspace's own repository",
|
|
616
|
-
'endpoint-url-invalid': "the platform couldn't connect the pack service",
|
|
617
|
-
'endpoint-host-not-allowed': "the platform couldn't connect the pack service",
|
|
618
|
-
'endpoint-name-mismatch': "the platform couldn't connect the pack service",
|
|
619
|
-
'endpoint-unreachable': "the pack service didn't answer",
|
|
620
|
-
'endpoint-info-mismatch': 'the pack service answered as another pack or version',
|
|
621
|
-
};
|
|
622
680
|
/**
|
|
623
681
|
* Waits for the deployment's own pack service to be `ready`, reading the
|
|
624
|
-
* deployment again every 2 s and saying each new state once.
|
|
625
|
-
*
|
|
626
|
-
*
|
|
682
|
+
* deployment again every 2 s and saying each new state once. A `cleared`
|
|
683
|
+
* one that's `waiting` (a suspension that took it down was lifted) is
|
|
684
|
+
* waited for like a pending one. Fails when it's `cleared` otherwise (saying
|
|
685
|
+
* why, when the server says), after 5 minutes, after a few failed reads in
|
|
686
|
+
* a row, or on Ctrl+C. The deployment is in place whatever it finds.
|
|
627
687
|
*/
|
|
628
|
-
async function waitForPackService(ctx, args, runners, record, line) {
|
|
688
|
+
async function waitForPackService(ctx, args, runners, record, line, projectId) {
|
|
629
689
|
const read = runners.getDeployment;
|
|
630
690
|
if (read === undefined) {
|
|
631
691
|
return {
|
|
@@ -639,7 +699,17 @@ async function waitForPackService(ctx, args, runners, record, line) {
|
|
|
639
699
|
const started = now();
|
|
640
700
|
let endpoint = record.endpoint;
|
|
641
701
|
let said = '';
|
|
702
|
+
let saidEnv = '';
|
|
642
703
|
let failedReads = 0;
|
|
704
|
+
// What stands in the way of its env, said once, and again when it changes.
|
|
705
|
+
const sayPackEnv = async () => {
|
|
706
|
+
const lines = await packEnvLines(ctx, args, runners, id, projectId);
|
|
707
|
+
const text = lines.join('\n');
|
|
708
|
+
if (text !== '' && text !== saidEnv)
|
|
709
|
+
for (const l of lines)
|
|
710
|
+
line(` ${l}`);
|
|
711
|
+
saidEnv = text;
|
|
712
|
+
};
|
|
643
713
|
line(' Waiting for the pack service');
|
|
644
714
|
for (;;) {
|
|
645
715
|
const settled = settledPackService(endpoint, id);
|
|
@@ -648,9 +718,14 @@ async function waitForPackService(ctx, args, runners, record, line) {
|
|
|
648
718
|
line(' ✓ ready\n');
|
|
649
719
|
return settled;
|
|
650
720
|
}
|
|
651
|
-
const state =
|
|
652
|
-
if (state !== said)
|
|
721
|
+
const state = waitingState(endpoint);
|
|
722
|
+
if (state !== said) {
|
|
653
723
|
line(` … ${state}`);
|
|
724
|
+
// At once, before the platform records it as the problem; and again then.
|
|
725
|
+
if (said === '' || endpoint.problem?.code.startsWith('pack-env-') === true) {
|
|
726
|
+
await sayPackEnv();
|
|
727
|
+
}
|
|
728
|
+
}
|
|
654
729
|
said = state;
|
|
655
730
|
if (now() - started >= PACK_SERVICE_WAIT_MS) {
|
|
656
731
|
return stoppedWaiting(`The pack service isn't ready after 5 minutes: ${state}`, id);
|
|
@@ -671,6 +746,29 @@ async function waitForPackService(ctx, args, runners, record, line) {
|
|
|
671
746
|
}
|
|
672
747
|
}
|
|
673
748
|
}
|
|
749
|
+
/**
|
|
750
|
+
* Where the deployment's pack declares names that stand in the way, said
|
|
751
|
+
* as what to do: nothing when none does, when the server doesn't give pack
|
|
752
|
+
* services their env (or is older), or when it can't be read.
|
|
753
|
+
*/
|
|
754
|
+
async function packEnvLines(ctx, args, runners, deploymentId, projectId) {
|
|
755
|
+
if (runners.getPackEnv === undefined)
|
|
756
|
+
return [];
|
|
757
|
+
const got = await runners.getPackEnv({
|
|
758
|
+
endpoint: args.endpoint,
|
|
759
|
+
token: args.token,
|
|
760
|
+
deploymentId,
|
|
761
|
+
fetchImpl: ctx.fetch,
|
|
762
|
+
...(ctx.stopSignal !== undefined && { signal: ctx.stopSignal }),
|
|
763
|
+
});
|
|
764
|
+
if (got.kind !== 'ok')
|
|
765
|
+
return [];
|
|
766
|
+
return packEnvProblemLines(got.packEnv.data, {
|
|
767
|
+
envName: got.packEnv.envName,
|
|
768
|
+
...(projectId !== undefined && { projectId }),
|
|
769
|
+
hasProjectMapping: false,
|
|
770
|
+
});
|
|
771
|
+
}
|
|
674
772
|
/** The deployment's pack service now, read again. */
|
|
675
773
|
async function readPackService(read, ctx, args, deploymentId) {
|
|
676
774
|
const got = await read({
|
|
@@ -689,24 +787,39 @@ function stoppedWaiting(why, deploymentId) {
|
|
|
689
787
|
message: `${why}. deploymentId ${deploymentId}. Run the same \`kindgi deploy --hosted\` again to keep waiting: it sends the same deployment, and the server answers with this one.\n`,
|
|
690
788
|
};
|
|
691
789
|
}
|
|
692
|
-
/**
|
|
790
|
+
/**
|
|
791
|
+
* A pack service that's done with: `ready`, or `cleared` and not `waiting`
|
|
792
|
+
* (a failure, with the server's reason when it gives one: an older server
|
|
793
|
+
* gives none); else undefined.
|
|
794
|
+
*/
|
|
693
795
|
function settledPackService(endpoint, id) {
|
|
694
796
|
if (endpoint.status === 'ready')
|
|
695
797
|
return { kind: 'ok', endpoint };
|
|
696
|
-
if (endpoint.status
|
|
798
|
+
if (endpoint.status !== 'cleared' || endpoint.waiting === true)
|
|
799
|
+
return undefined;
|
|
800
|
+
if (endpoint.retired === undefined) {
|
|
697
801
|
return {
|
|
698
802
|
kind: 'error',
|
|
699
|
-
message: `The deployment's pack service was taken down
|
|
803
|
+
message: `The deployment's pack service was taken down (cleared). deploymentId ${id}.\n`,
|
|
700
804
|
};
|
|
701
805
|
}
|
|
702
|
-
|
|
806
|
+
const why = packServiceRetiredText(endpoint.retired);
|
|
807
|
+
return {
|
|
808
|
+
kind: 'error',
|
|
809
|
+
message: `The deployment's pack service was taken down (cleared, ${endpoint.retired}): ${why}. deploymentId ${id}.\n`,
|
|
810
|
+
};
|
|
703
811
|
}
|
|
704
|
-
/**
|
|
705
|
-
|
|
812
|
+
/**
|
|
813
|
+
* A pack service being waited for, as the wait says it: `pending`, or
|
|
814
|
+
* `cleared, waiting for its pack service again`, and why when there's a
|
|
815
|
+
* problem.
|
|
816
|
+
*/
|
|
817
|
+
function waitingState(endpoint) {
|
|
818
|
+
const state = endpoint.status === 'cleared' ? 'cleared, waiting for its pack service again' : 'pending';
|
|
706
819
|
if (endpoint.problem === undefined)
|
|
707
|
-
return
|
|
708
|
-
const why =
|
|
709
|
-
return
|
|
820
|
+
return state;
|
|
821
|
+
const why = packServiceProblemText(endpoint.problem.code);
|
|
822
|
+
return `${state} (${endpoint.problem.code}): ${why}`;
|
|
710
823
|
}
|
|
711
824
|
function sleepUnlessStopped(ms, signal) {
|
|
712
825
|
return new Promise((resolve) => {
|
|
@@ -719,18 +832,33 @@ function sleepUnlessStopped(ms, signal) {
|
|
|
719
832
|
signal?.addEventListener('abort', done, { once: true });
|
|
720
833
|
});
|
|
721
834
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
835
|
+
/**
|
|
836
|
+
* The project to deploy into: `--project`, else the pack environment's
|
|
837
|
+
* `project` when the runtime keeps its values under one env (`fromEnvironment`).
|
|
838
|
+
* A `--project` that isn't that environment's project is refused.
|
|
839
|
+
*/
|
|
840
|
+
async function resolveDeployProject(args, fromEnvironment, runners, ctx) {
|
|
841
|
+
const flag = await resolveProject(args.projectRef, '--project', args, runners, ctx);
|
|
842
|
+
if (flag.kind === 'error' || fromEnvironment === undefined)
|
|
843
|
+
return flag;
|
|
844
|
+
const where = `environments.${args.envName}.project`;
|
|
845
|
+
const environment = await resolveProject(fromEnvironment, where, args, runners, ctx);
|
|
846
|
+
if (environment.kind === 'error' || args.projectRef === undefined)
|
|
847
|
+
return environment;
|
|
848
|
+
if (flag.id === environment.id)
|
|
849
|
+
return environment;
|
|
850
|
+
return {
|
|
851
|
+
kind: 'error',
|
|
852
|
+
message: `--project ${args.projectRef} isn't the ${args.envName} environment's project, ${fromEnvironment} (${where}). Leave --project out, or deploy with the --env whose project it is.\n`,
|
|
853
|
+
};
|
|
854
|
+
}
|
|
726
855
|
/**
|
|
727
856
|
* `--project`: an id goes as it is (the server answers 404 for one of no
|
|
728
857
|
* project of the tenant); a slug is looked up, and refused when it names
|
|
729
858
|
* no project, or several (a slug is unique within an org, so two orgs may
|
|
730
859
|
* share one). Absent: tenant-wide.
|
|
731
860
|
*/
|
|
732
|
-
async function resolveProject(args, runners, ctx) {
|
|
733
|
-
const ref = args.projectRef;
|
|
861
|
+
async function resolveProject(ref, where, args, runners, ctx) {
|
|
734
862
|
if (ref === undefined)
|
|
735
863
|
return { kind: 'ok' };
|
|
736
864
|
if (PROJECT_ID_RE.test(ref))
|
|
@@ -738,7 +866,7 @@ async function resolveProject(args, runners, ctx) {
|
|
|
738
866
|
if (runners.findProjects === undefined) {
|
|
739
867
|
return {
|
|
740
868
|
kind: 'error',
|
|
741
|
-
message:
|
|
869
|
+
message: `${where} ${ref}: this build can't look a slug up. Pass the project's id.\n`,
|
|
742
870
|
};
|
|
743
871
|
}
|
|
744
872
|
const found = await runners.findProjects({
|
|
@@ -751,21 +879,21 @@ async function resolveProject(args, runners, ctx) {
|
|
|
751
879
|
if (found.kind === 'error') {
|
|
752
880
|
return {
|
|
753
881
|
kind: 'error',
|
|
754
|
-
message:
|
|
882
|
+
message: `${where} ${ref}: couldn't list the tenant's projects: ${found.message}\n`,
|
|
755
883
|
};
|
|
756
884
|
}
|
|
757
885
|
const [only, ...more] = found.projects;
|
|
758
886
|
if (only === undefined) {
|
|
759
887
|
return {
|
|
760
888
|
kind: 'error',
|
|
761
|
-
message:
|
|
889
|
+
message: `${where} ${ref}: no project of this tenant has that slug. \`kindgi projects list\` shows them; \`kindgi projects create --name=<name> --slug=${SLUG.test(ref) ? ref : '<slug>'}\` makes one.\n`,
|
|
762
890
|
};
|
|
763
891
|
}
|
|
764
892
|
if (more.length > 0) {
|
|
765
893
|
const ids = found.projects.map((p) => `${p.id} (${p.name}${p.orgId === undefined ? '' : `, org ${p.orgId}`})`);
|
|
766
894
|
return {
|
|
767
895
|
kind: 'error',
|
|
768
|
-
message:
|
|
896
|
+
message: `${where} ${ref}: ${found.projects.length} projects of this tenant have that slug, in different orgs: ${ids.join(', ')}. Pass the id.\n`,
|
|
769
897
|
};
|
|
770
898
|
}
|
|
771
899
|
return { kind: 'ok', id: only.id };
|
|
@@ -782,7 +910,8 @@ async function resolveDeployArgs(ctx) {
|
|
|
782
910
|
const cfg = config.config;
|
|
783
911
|
// --env — default staging (matches build's default).
|
|
784
912
|
const envFlag = ctx.options.env;
|
|
785
|
-
const
|
|
913
|
+
const envGiven = typeof envFlag === 'string' && envFlag !== '';
|
|
914
|
+
const envName = envGiven ? envFlag : 'staging';
|
|
786
915
|
const envBlock = cfg.environments?.[envName] !== undefined
|
|
787
916
|
? cfg.environments[envName]
|
|
788
917
|
: undefined;
|
|
@@ -790,14 +919,25 @@ async function resolveDeployArgs(ctx) {
|
|
|
790
919
|
const outFlag = ctx.options.out;
|
|
791
920
|
const outDirRaw = typeof outFlag === 'string' && outFlag !== '' ? outFlag : '.kindgi/build';
|
|
792
921
|
const outDir = isAbsolute(outDirRaw) ? outDirRaw : resolve(packDir, outDirRaw);
|
|
793
|
-
|
|
794
|
-
|
|
922
|
+
// --hosted, or the env block's `build: 'hosted'` (main.ts, `hostedRun`).
|
|
923
|
+
const hosted = ctx.hosted;
|
|
924
|
+
if (ctx.globals.hosted && ctx.options['build-endpoint'] !== undefined) {
|
|
795
925
|
return {
|
|
796
926
|
kind: 'error',
|
|
797
927
|
stderr: '--hosted builds on the hosted build service, and --build-endpoint names another build server: leave one out.\n',
|
|
798
928
|
exitCode: 1,
|
|
799
929
|
};
|
|
800
930
|
}
|
|
931
|
+
// A hosted runtime reads a deployment's secrets in the env it serves.
|
|
932
|
+
// Without --env the sync would send `.env.staging` under `staging`,
|
|
933
|
+
// which it may never read: the env is named, not defaulted.
|
|
934
|
+
if (hosted && ctx.options['sync-secrets'] === true && !envGiven) {
|
|
935
|
+
return {
|
|
936
|
+
kind: 'error',
|
|
937
|
+
stderr: "--sync-secrets on a hosted deploy needs --env. A hosted runtime reads a deployment's secrets in the env it serves; without --env, the values in .env.staging would go up under staging. Pass --env=<the env it serves>.\n",
|
|
938
|
+
exitCode: 1,
|
|
939
|
+
};
|
|
940
|
+
}
|
|
801
941
|
// The API (api-address.ts): --endpoint > the env block's `endpoint` >
|
|
802
942
|
// the CLI's own address (`--url`, KINDGI_API_URL or the stored one).
|
|
803
943
|
// A stored key goes only to the address stored with it; a hosted
|
|
@@ -833,6 +973,9 @@ async function resolveDeployArgs(ctx) {
|
|
|
833
973
|
exitCode: 1,
|
|
834
974
|
};
|
|
835
975
|
}
|
|
976
|
+
// No --endpoint or env `endpoint`: the key goes to the CLI's own address.
|
|
977
|
+
if (named.every((n) => n.url === undefined || n.url === ''))
|
|
978
|
+
ctx.noteOwnAddressKey();
|
|
836
979
|
// --tenant — used only as a consistency check against the envelope.
|
|
837
980
|
const tenantFlag = ctx.options.tenant;
|
|
838
981
|
const explicitTenantId = typeof tenantFlag === 'string' && tenantFlag !== '' ? tenantFlag : undefined;
|
|
@@ -877,11 +1020,50 @@ async function resolveDeployArgs(ctx) {
|
|
|
877
1020
|
dryRun,
|
|
878
1021
|
syncSecrets,
|
|
879
1022
|
envBlock,
|
|
1023
|
+
environments: environmentsOf(cfg),
|
|
880
1024
|
packEnv: packEnv.plan,
|
|
881
1025
|
allowMissingEnv: ctx.options['allow-missing-env'] === true,
|
|
882
1026
|
},
|
|
883
1027
|
};
|
|
884
1028
|
}
|
|
1029
|
+
/**
|
|
1030
|
+
* A deploy refused into a later environment of its order
|
|
1031
|
+
* (`409 environment-takes-promotions`): each environment from the order's
|
|
1032
|
+
* first to that one, named from the pack config, for the hint. `undefined`
|
|
1033
|
+
* for any other answer, and when the refusal doesn't name the first (the
|
|
1034
|
+
* caller can't read it) or the reads fail: the hint then says the rule
|
|
1035
|
+
* without names.
|
|
1036
|
+
*/
|
|
1037
|
+
async function promotionPathOf(args, runners, ctx, result) {
|
|
1038
|
+
if (result.kind !== 'wire-error' || result.error.code !== 'environment-takes-promotions') {
|
|
1039
|
+
return undefined;
|
|
1040
|
+
}
|
|
1041
|
+
const { position, firstProjectId } = result.error;
|
|
1042
|
+
if (position === undefined || firstProjectId === undefined)
|
|
1043
|
+
return undefined;
|
|
1044
|
+
if (runners.listProjects === undefined || runners.listEnvironmentOrders === undefined) {
|
|
1045
|
+
return undefined;
|
|
1046
|
+
}
|
|
1047
|
+
const opts = {
|
|
1048
|
+
endpoint: args.endpoint,
|
|
1049
|
+
token: args.token,
|
|
1050
|
+
fetchImpl: ctx.fetch,
|
|
1051
|
+
...(ctx.stopSignal !== undefined && { signal: ctx.stopSignal }),
|
|
1052
|
+
};
|
|
1053
|
+
const [projects, orders] = await Promise.all([
|
|
1054
|
+
runners.listProjects(opts),
|
|
1055
|
+
runners.listEnvironmentOrders(opts),
|
|
1056
|
+
]);
|
|
1057
|
+
if (projects.kind === 'error' || orders.kind === 'error')
|
|
1058
|
+
return undefined;
|
|
1059
|
+
return promotionPath({
|
|
1060
|
+
firstProjectId,
|
|
1061
|
+
position,
|
|
1062
|
+
orders: orders.orders,
|
|
1063
|
+
projects: projects.projects,
|
|
1064
|
+
environments: envProjectRefs(args.environments),
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
885
1067
|
async function loadDeployConfig(ctx, packDir) {
|
|
886
1068
|
const outcome = await loadPackConfig(ctx, packDir);
|
|
887
1069
|
if (outcome.kind === 'ok')
|