@geekmidas/cli 10.0.0-alpha.95 → 10.0.0-alpha.96
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/{GoDaddyProvider-BwW51hPm.mjs → GoDaddyProvider-BsUKjGcg.mjs} +2 -2
- package/dist/{GoDaddyProvider-BwW51hPm.mjs.map → GoDaddyProvider-BsUKjGcg.mjs.map} +1 -1
- package/dist/{GoDaddyProvider-C_DgIkMv.cjs → GoDaddyProvider-DyHXHDHe.cjs} +2 -2
- package/dist/{GoDaddyProvider-C_DgIkMv.cjs.map → GoDaddyProvider-DyHXHDHe.cjs.map} +1 -1
- package/dist/{LegacyStateStore-D5DarNA2.mjs → LegacyStateStore-B6SOqRFi.mjs} +3 -3
- package/dist/{LegacyStateStore-D5DarNA2.mjs.map → LegacyStateStore-B6SOqRFi.mjs.map} +1 -1
- package/dist/{LegacyStateStore-BawjE_5n.cjs → LegacyStateStore-BnMcONE0.cjs} +3 -3
- package/dist/{LegacyStateStore-BawjE_5n.cjs.map → LegacyStateStore-BnMcONE0.cjs.map} +1 -1
- package/dist/{LocalStateStore-YjIqlgAz.mjs → LocalStateStore-6CrsZzqI.mjs} +2 -2
- package/dist/{LocalStateStore-YjIqlgAz.mjs.map → LocalStateStore-6CrsZzqI.mjs.map} +1 -1
- package/dist/{LocalStateStore-D8fu5WiF.cjs → LocalStateStore-pd4bbdJJ.cjs} +2 -2
- package/dist/{LocalStateStore-D8fu5WiF.cjs.map → LocalStateStore-pd4bbdJJ.cjs.map} +1 -1
- package/dist/{S3StateStore-Cm-v2IPI.cjs → S3StateStore-CoGk6ZKU.cjs} +2 -2
- package/dist/{S3StateStore-Cm-v2IPI.cjs.map → S3StateStore-CoGk6ZKU.cjs.map} +1 -1
- package/dist/{S3StateStore-CjTAVsWh.mjs → S3StateStore-DP_7Q4yb.mjs} +2 -2
- package/dist/{S3StateStore-CjTAVsWh.mjs.map → S3StateStore-DP_7Q4yb.mjs.map} +1 -1
- package/dist/{SSMStateStore-BhtM6jOh.mjs → SSMStateStore-Bo6_KDYY.mjs} +2 -2
- package/dist/{SSMStateStore-BhtM6jOh.mjs.map → SSMStateStore-Bo6_KDYY.mjs.map} +1 -1
- package/dist/{SSMStateStore-BS4BiZCN.cjs → SSMStateStore-D9s4_Do3.cjs} +2 -2
- package/dist/{SSMStateStore-BS4BiZCN.cjs.map → SSMStateStore-D9s4_Do3.cjs.map} +1 -1
- package/dist/{add-ylx8dejw.mjs → add-BQS2QZHp.mjs} +4 -4
- package/dist/{add-ylx8dejw.mjs.map → add-BQS2QZHp.mjs.map} +1 -1
- package/dist/{add-D-3IKT7W.cjs → add-CjURJ-vI.cjs} +4 -4
- package/dist/{add-D-3IKT7W.cjs.map → add-CjURJ-vI.cjs.map} +1 -1
- package/dist/{bundler-ywR3C1B_.mjs → bundler-BMM3UsGm.mjs} +5 -5
- package/dist/{bundler-ywR3C1B_.mjs.map → bundler-BMM3UsGm.mjs.map} +1 -1
- package/dist/{bundler-CI3elfZS.cjs → bundler-DmLJQ7AY.cjs} +5 -5
- package/dist/{bundler-CI3elfZS.cjs.map → bundler-DmLJQ7AY.cjs.map} +1 -1
- package/dist/{cli-Dewt2T21.cjs → cli-C2c-jOVK.cjs} +2 -2
- package/dist/{cli-Dewt2T21.cjs.map → cli-C2c-jOVK.cjs.map} +1 -1
- package/dist/{cli-BCSuCfQk.mjs → cli-Ce6NgaQF.mjs} +2 -2
- package/dist/{cli-BCSuCfQk.mjs.map → cli-Ce6NgaQF.mjs.map} +1 -1
- package/dist/{compose-C4js1JDv.mjs → compose-BJDPFBAN.mjs} +7 -7
- package/dist/{compose-C4js1JDv.mjs.map → compose-BJDPFBAN.mjs.map} +1 -1
- package/dist/{compose-Dd0XphvF.cjs → compose-ETFKqkTu.cjs} +7 -7
- package/dist/{compose-Dd0XphvF.cjs.map → compose-ETFKqkTu.cjs.map} +1 -1
- package/dist/config.cjs +1 -1
- package/dist/config.mjs +1 -1
- package/dist/deploy/public.cjs +7 -7
- package/dist/deploy/public.mjs +7 -7
- package/dist/{deploy-Bw1FjX_x.cjs → deploy-BdN0ym2i.cjs} +200 -46
- package/dist/deploy-BdN0ym2i.cjs.map +1 -0
- package/dist/{deploy-B0drCD6s.mjs → deploy-DktQl7ti.mjs} +200 -46
- package/dist/deploy-DktQl7ti.mjs.map +1 -0
- package/dist/{dns-X3oqEV4C.mjs → dns-BjHECDO7.mjs} +5 -5
- package/dist/{dns-X3oqEV4C.mjs.map → dns-BjHECDO7.mjs.map} +1 -1
- package/dist/{dns-Bmk7P9c2.cjs → dns-C-X6PLkV.cjs} +3 -3
- package/dist/{dns-Bmk7P9c2.cjs.map → dns-C-X6PLkV.cjs.map} +1 -1
- package/dist/{dns-idXfyCay.mjs → dns-C2EeR6n2.mjs} +3 -3
- package/dist/{dns-idXfyCay.mjs.map → dns-C2EeR6n2.mjs.map} +1 -1
- package/dist/{dns-CyMIq6to.cjs → dns-ClI7-d5g.cjs} +5 -5
- package/dist/{dns-CyMIq6to.cjs.map → dns-ClI7-d5g.cjs.map} +1 -1
- package/dist/{dokploy-DaT0mgLU.cjs → dokploy-CD_Bc4EV.cjs} +2 -2
- package/dist/{dokploy-DaT0mgLU.cjs.map → dokploy-CD_Bc4EV.cjs.map} +1 -1
- package/dist/{dokploy-CZYxXeMa.mjs → dokploy-CxtkS5hl.mjs} +2 -2
- package/dist/{dokploy-CZYxXeMa.mjs.map → dokploy-CxtkS5hl.mjs.map} +1 -1
- package/dist/{encryption-D0ucY4O7.mjs → encryption-BgQd4f2S.mjs} +3 -3
- package/dist/{encryption-D0ucY4O7.mjs.map → encryption-BgQd4f2S.mjs.map} +1 -1
- package/dist/{encryption-Dp0IvLVX.cjs → encryption-CqVfz-m7.cjs} +3 -3
- package/dist/{encryption-Dp0IvLVX.cjs.map → encryption-CqVfz-m7.cjs.map} +1 -1
- package/dist/{engine-WY6j_kvA.cjs → engine-OVVKtPmW.cjs} +4 -4
- package/dist/{engine-WY6j_kvA.cjs.map → engine-OVVKtPmW.cjs.map} +1 -1
- package/dist/{engine-BcuhnyQO.mjs → engine-YlXzlTUl.mjs} +4 -4
- package/dist/{engine-BcuhnyQO.mjs.map → engine-YlXzlTUl.mjs.map} +1 -1
- package/dist/{github-DgmW83OP.cjs → github-CsMloeSL.cjs} +5 -5
- package/dist/{github-DgmW83OP.cjs.map → github-CsMloeSL.cjs.map} +1 -1
- package/dist/{github-DUGA57du.mjs → github-DuuvqI3Z.mjs} +5 -5
- package/dist/{github-DUGA57du.mjs.map → github-DuuvqI3Z.mjs.map} +1 -1
- package/dist/index.cjs +48 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +48 -48
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-afgRL3Hg.cjs → migrate-BAmJeVq1.cjs} +3 -3
- package/dist/{migrate-afgRL3Hg.cjs.map → migrate-BAmJeVq1.cjs.map} +1 -1
- package/dist/{migrate-BBcG1dhS.cjs → migrate-BdkoNdIu.cjs} +3 -3
- package/dist/{migrate-BBcG1dhS.cjs.map → migrate-BdkoNdIu.cjs.map} +1 -1
- package/dist/{migrate-CZ-v5WV0.mjs → migrate-CB8B6W9y.mjs} +3 -3
- package/dist/{migrate-CZ-v5WV0.mjs.map → migrate-CB8B6W9y.mjs.map} +1 -1
- package/dist/{migrate-D6PlNv2o.mjs → migrate-CsPFHgTs.mjs} +3 -3
- package/dist/{migrate-D6PlNv2o.mjs.map → migrate-CsPFHgTs.mjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/{resolve-CFb4Navi.cjs → resolve-BDatNOoG.cjs} +3 -3
- package/dist/{resolve-CFb4Navi.cjs.map → resolve-BDatNOoG.cjs.map} +1 -1
- package/dist/{resolve-DakDhr6Z.mjs → resolve-CUne0qOm.mjs} +3 -3
- package/dist/{resolve-DakDhr6Z.mjs.map → resolve-CUne0qOm.mjs.map} +1 -1
- package/dist/{rollback-B1TIpjdE.mjs → rollback-CCdEEEcF.mjs} +5 -5
- package/dist/{rollback-B1TIpjdE.mjs.map → rollback-CCdEEEcF.mjs.map} +1 -1
- package/dist/{rollback-B-1dPP0m.cjs → rollback-CuG3DkTY.cjs} +5 -5
- package/dist/{rollback-B-1dPP0m.cjs.map → rollback-CuG3DkTY.cjs.map} +1 -1
- package/dist/{secrets-CPM0Qffe.cjs → secrets-C5qROlbp.cjs} +3 -3
- package/dist/{secrets-CPM0Qffe.cjs.map → secrets-C5qROlbp.cjs.map} +1 -1
- package/dist/{secrets-oI9ClAPR.mjs → secrets-ZikTIxEm.mjs} +3 -3
- package/dist/{secrets-oI9ClAPR.mjs.map → secrets-ZikTIxEm.mjs.map} +1 -1
- package/dist/{seeds-CnXkgKUv.cjs → seeds-BjXW-Krf.cjs} +4 -4
- package/dist/{seeds-CnXkgKUv.cjs.map → seeds-BjXW-Krf.cjs.map} +1 -1
- package/dist/{seeds-CoKkrSny.mjs → seeds-CbJSne1A.mjs} +4 -4
- package/dist/{seeds-CoKkrSny.mjs.map → seeds-CbJSne1A.mjs.map} +1 -1
- package/dist/{sniffer-CFRzIrby.mjs → sniffer-DQGBYY8i.mjs} +7 -7
- package/dist/{sniffer-CFRzIrby.mjs.map → sniffer-DQGBYY8i.mjs.map} +1 -1
- package/dist/{sniffer-vbiQ2Isn.cjs → sniffer-NPD1dyab.cjs} +7 -7
- package/dist/{sniffer-vbiQ2Isn.cjs.map → sniffer-NPD1dyab.cjs.map} +1 -1
- package/dist/{stack-B7O975TR.mjs → stack-BFq3K-_z.mjs} +3 -3
- package/dist/{stack-B7O975TR.mjs.map → stack-BFq3K-_z.mjs.map} +1 -1
- package/dist/{stack-1g57OfOw.cjs → stack-DOVvEd1T.cjs} +3 -3
- package/dist/{stack-1g57OfOw.cjs.map → stack-DOVvEd1T.cjs.map} +1 -1
- package/dist/{stages-Bt4OggMm.mjs → stages-BEn2ArJZ.mjs} +4 -4
- package/dist/{stages-Bt4OggMm.mjs.map → stages-BEn2ArJZ.mjs.map} +1 -1
- package/dist/{stages-BujUB4we.cjs → stages-Cw6urZyJ.cjs} +4 -4
- package/dist/{stages-BujUB4we.cjs.map → stages-Cw6urZyJ.cjs.map} +1 -1
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.mjs +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-DWK1PTXP.mjs → workspace-CqFUjObk.mjs} +54 -2
- package/dist/workspace-CqFUjObk.mjs.map +1 -0
- package/dist/{workspace-cSyi5nb1.cjs → workspace-DDkm4y6D.cjs} +71 -1
- package/dist/workspace-DDkm4y6D.cjs.map +1 -0
- package/package.json +15 -15
- package/dist/deploy-B0drCD6s.mjs.map +0 -1
- package/dist/deploy-Bw1FjX_x.cjs.map +0 -1
- package/dist/workspace-DWK1PTXP.mjs.map +0 -1
- package/dist/workspace-cSyi5nb1.cjs.map +0 -1
|
@@ -4,15 +4,15 @@ import { r as storedCredentials } from "./credentials-B5N5yKk6.mjs";
|
|
|
4
4
|
import { f as withOutput, r as discover } from "./discover-qjFapetL.mjs";
|
|
5
5
|
import { c as withSandbox } from "./sandbox-CbVB7uOD.mjs";
|
|
6
6
|
import { n as derivedApps, t as appKey } from "./derive-B4vDpzgP.mjs";
|
|
7
|
-
import { $ as
|
|
7
|
+
import { $ as IamUserNotOwned, G as checkS3Config, J as BucketNameUnavailable, K as AccessKeyLimit, L as resolveStageTelemetry, N as assertDeployedStage, Q as BucketRegionUnknown, U as stageProvider, W as S3_PROVISIONING, X as BucketProbeFailed, Y as BucketOwnedByAnotherProject, Z as BucketRegionMismatch, et as ProvisionedBucketUnreachable, nt as RotationInProgress, q as BucketKeySetElsewhere, rt as S3RegionRequired, tt as RecordedBucketUnreachable, u as getAppBuildOrder } from "./workspace-CqFUjObk.mjs";
|
|
8
8
|
import { o as builtinTarget } from "./define-6poNqm5O.mjs";
|
|
9
9
|
import { findWorkspaceRoot, loadWorkspaceConfig } from "./config.mjs";
|
|
10
|
-
import { E as reportDevServices, F as DeployJournal, H as createComposeState, In as applicationName, It as appEnvKeys, Ln as deployIdentity, N as stageProviderNotes, O as stageServiceDeclarations, On as secretsStoreFor, P as PROVIDER_KINDS, T as externalServices, U as createEmptyState, Wn as initStageSecrets, b as DevServicesNeedServerTarget, ct as constructGlobs, fn as usesTelemetry, j as assertStageProvidersEnabled, nr as isMainFrontendApp, rr as resolveHost, tr as NoDomainForStage, w as devServicesUsed, x as ExternalServicesNotConfigured } from "./seeds-
|
|
11
|
-
import { a as parseTargetOptions, o as resolveTarget } from "./resolve-
|
|
10
|
+
import { E as reportDevServices, F as DeployJournal, H as createComposeState, In as applicationName, It as appEnvKeys, Ln as deployIdentity, N as stageProviderNotes, O as stageServiceDeclarations, On as secretsStoreFor, P as PROVIDER_KINDS, T as externalServices, U as createEmptyState, Wn as initStageSecrets, b as DevServicesNeedServerTarget, ct as constructGlobs, fn as usesTelemetry, j as assertStageProvidersEnabled, nr as isMainFrontendApp, rr as resolveHost, tr as NoDomainForStage, w as devServicesUsed, x as ExternalServicesNotConfigured } from "./seeds-CbJSne1A.mjs";
|
|
11
|
+
import { a as parseTargetOptions, o as resolveTarget } from "./resolve-CUne0qOm.mjs";
|
|
12
12
|
import { t as LocalSandbox } from "./local-BwC2v2Y8.mjs";
|
|
13
|
-
import { S as eventError, _ as assertStateOutlivesRun, y as createStateStore } from "./sniffer-
|
|
14
|
-
import { c as stageDnsDomains, n as dnsCredentials } from "./dns-
|
|
15
|
-
import { t as deploysWithCompose } from "./dns-
|
|
13
|
+
import { S as eventError, _ as assertStateOutlivesRun, y as createStateStore } from "./sniffer-DQGBYY8i.mjs";
|
|
14
|
+
import { c as stageDnsDomains, n as dnsCredentials } from "./dns-C2EeR6n2.mjs";
|
|
15
|
+
import { t as deploysWithCompose } from "./dns-BjHECDO7.mjs";
|
|
16
16
|
import { n as assertStageCredentials } from "./credentialSchemas-BJFvfkaz.mjs";
|
|
17
17
|
import { resolve } from "node:path";
|
|
18
18
|
import { dependentsOf, kebabCase, provideKey, providedKeyFor } from "@geekmidas/manifest";
|
|
@@ -447,6 +447,74 @@ function errorName(error) {
|
|
|
447
447
|
const e = error;
|
|
448
448
|
return e?.Code ?? e?.name ?? "";
|
|
449
449
|
}
|
|
450
|
+
/** The answer an SDK call failed with. */
|
|
451
|
+
function s3Answer(error) {
|
|
452
|
+
const e = error;
|
|
453
|
+
const headers = Object.fromEntries(Object.entries(e?.$response?.headers ?? {}).map(([k, v]) => [k.toLowerCase(), v]));
|
|
454
|
+
const status = e?.$metadata?.httpStatusCode ?? e?.$response?.statusCode;
|
|
455
|
+
const region = headers["x-amz-bucket-region"];
|
|
456
|
+
const requestId = e?.$metadata?.requestId ?? headers["x-amz-request-id"];
|
|
457
|
+
const id2 = e?.$metadata?.extendedRequestId ?? headers["x-amz-id-2"];
|
|
458
|
+
return {
|
|
459
|
+
...status !== void 0 ? { status } : {},
|
|
460
|
+
name: errorName(error) || "Error",
|
|
461
|
+
message: e?.message ?? String(error),
|
|
462
|
+
...region ? { region } : {},
|
|
463
|
+
...requestId ? { requestId } : {},
|
|
464
|
+
...id2 ? { id2 } : {}
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* `HTTP 403 Unknown: UnknownError [x-amz-bucket-region: eu-west-1,
|
|
469
|
+
* x-amz-request-id: …, x-amz-id-2: …]` — everything S3 said, for a message.
|
|
470
|
+
*/
|
|
471
|
+
function describeAnswer(answer) {
|
|
472
|
+
const said = answer.message && answer.message !== answer.name ? `${answer.name}: ${answer.message}` : answer.name;
|
|
473
|
+
const headers = [
|
|
474
|
+
answer.region && `x-amz-bucket-region: ${answer.region}`,
|
|
475
|
+
answer.requestId && `x-amz-request-id: ${answer.requestId}`,
|
|
476
|
+
answer.id2 && `x-amz-id-2: ${answer.id2}`
|
|
477
|
+
].filter(Boolean);
|
|
478
|
+
return `${answer.status === void 0 ? "no response" : `HTTP ${answer.status}`} ${said}` + (headers.length > 0 ? ` [${headers.join(", ")}]` : "");
|
|
479
|
+
}
|
|
480
|
+
/** `HeadBucket`: the bucket's region when it answers, else what S3 said. */
|
|
481
|
+
async function headBucket(S3, client, bucket) {
|
|
482
|
+
try {
|
|
483
|
+
const out = await client.send(new S3.HeadBucketCommand({ Bucket: bucket }));
|
|
484
|
+
return {
|
|
485
|
+
ok: true,
|
|
486
|
+
...out.BucketRegion ? { region: out.BucketRegion } : {}
|
|
487
|
+
};
|
|
488
|
+
} catch (error) {
|
|
489
|
+
return {
|
|
490
|
+
ok: false,
|
|
491
|
+
...s3Answer(error)
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/** `GetBucketLocation`'s answer as a region: none is us-east-1, `EU` eu-west-1. */
|
|
496
|
+
async function bucketLocation(S3, client, bucket) {
|
|
497
|
+
try {
|
|
498
|
+
const location = (await client.send(new S3.GetBucketLocationCommand({ Bucket: bucket }))).LocationConstraint;
|
|
499
|
+
return { region: !location ? "us-east-1" : location === "EU" ? "eu-west-1" : location };
|
|
500
|
+
} catch (error) {
|
|
501
|
+
return { failed: s3Answer(error) };
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Where `bucket` is: `HeadBucket`'s `x-amz-bucket-region` — on a 200, a 301
|
|
506
|
+
* or a 400 alike — else `GetBucketLocation`.
|
|
507
|
+
*
|
|
508
|
+
* @throws {BucketRegionUnknown}
|
|
509
|
+
*/
|
|
510
|
+
async function bucketRegion(clients, bucket) {
|
|
511
|
+
const { S3, s3 } = clients;
|
|
512
|
+
const head = await headBucket(S3, s3, bucket);
|
|
513
|
+
if (head.region) return head.region;
|
|
514
|
+
const location = await bucketLocation(S3, s3, bucket);
|
|
515
|
+
if ("region" in location) return location.region;
|
|
516
|
+
throw new BucketRegionUnknown(bucket, head.ok ? "HTTP 200 without x-amz-bucket-region" : describeAnswer(head), describeAnswer(location.failed));
|
|
517
|
+
}
|
|
450
518
|
/** A read whose "there is none" answer is one of `missing`. */
|
|
451
519
|
async function readOr(read, missing) {
|
|
452
520
|
try {
|
|
@@ -500,7 +568,7 @@ function identityTags(ctx, id) {
|
|
|
500
568
|
}
|
|
501
569
|
];
|
|
502
570
|
}
|
|
503
|
-
async function ensureBucket(ctx,
|
|
571
|
+
async function ensureBucket(ctx, stageClients, construct) {
|
|
504
572
|
const { id } = construct;
|
|
505
573
|
const nameInput = {
|
|
506
574
|
scope: ctx.identity.scope,
|
|
@@ -509,7 +577,13 @@ async function ensureBucket(ctx, clients, construct) {
|
|
|
509
577
|
};
|
|
510
578
|
const versioning = ctx.config.versioning ?? construct.versioned;
|
|
511
579
|
const open = [...new Set(construct.servers.flatMap((s) => s.open))].sort();
|
|
512
|
-
const
|
|
580
|
+
const located = await ensureBucketName(ctx, stageClients, id, bucketName(nameInput));
|
|
581
|
+
const { bucket, exists } = located;
|
|
582
|
+
let clients = stageClients;
|
|
583
|
+
if (located.region !== stageClients.region) {
|
|
584
|
+
clients = await makeClients(ctx.credential, located.region);
|
|
585
|
+
ctx.log(` '${bucket}' is in ${located.region}, not ${stageClients.region}: '${id}' is reached and addressed there`);
|
|
586
|
+
}
|
|
513
587
|
await ensureBucketSettings(ctx, clients, {
|
|
514
588
|
id,
|
|
515
589
|
bucket,
|
|
@@ -517,10 +591,11 @@ async function ensureBucket(ctx, clients, construct) {
|
|
|
517
591
|
versioning,
|
|
518
592
|
open
|
|
519
593
|
});
|
|
594
|
+
const user = await ensureUser(ctx, clients, id, iamUserName(nameInput), userPolicy(bucket, { versioning }));
|
|
520
595
|
await ensureAccessKey(ctx, clients, {
|
|
521
596
|
id,
|
|
522
597
|
bucket,
|
|
523
|
-
...
|
|
598
|
+
...user
|
|
524
599
|
});
|
|
525
600
|
const publicUrl = bucketPublicUrl(bucket, clients.region, clients.endpoint);
|
|
526
601
|
for (const server of construct.servers) {
|
|
@@ -557,26 +632,32 @@ async function ensureBucketName(ctx, clients, id, good) {
|
|
|
557
632
|
const { S3, s3, region } = clients;
|
|
558
633
|
const key = `s3-bucket:${id}`;
|
|
559
634
|
const record = ctx.state.record(key);
|
|
560
|
-
const entry = (name) => ({
|
|
635
|
+
const entry = (name, where) => ({
|
|
561
636
|
key,
|
|
562
637
|
type: "s3-bucket",
|
|
563
638
|
data: {
|
|
564
639
|
name,
|
|
565
|
-
region
|
|
640
|
+
region: where
|
|
566
641
|
}
|
|
567
642
|
});
|
|
568
|
-
/**
|
|
643
|
+
/**
|
|
644
|
+
* Whether this account holds `name`: yes, no, or someone else does. Read
|
|
645
|
+
* by status — a `HEAD` has no body, so its error is named `Unknown` — and,
|
|
646
|
+
* when S3 says the bucket is in another region, asked again there.
|
|
647
|
+
*/
|
|
569
648
|
const probe = async (name) => {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const name = errorName(error);
|
|
575
|
-
if (name === "NotFound" || name === "NoSuchBucket") return "none";
|
|
576
|
-
if (name === "Forbidden" || name === "AccessDenied") return "taken";
|
|
577
|
-
throw error;
|
|
649
|
+
let head = await headBucket(S3, s3, name);
|
|
650
|
+
if (!head.ok && head.region && head.region !== region && (head.status === 301 || head.status === 400)) {
|
|
651
|
+
const there = await makeClients(ctx.credential, head.region);
|
|
652
|
+
head = await headBucket(there.S3, there.s3, name);
|
|
578
653
|
}
|
|
654
|
+
if (head.ok) return "ours";
|
|
655
|
+
if (head.status === 404) return "none";
|
|
656
|
+
if (head.status === 403) return "taken";
|
|
657
|
+
throw new BucketProbeFailed(name, describeAnswer(head));
|
|
579
658
|
};
|
|
659
|
+
/** Where the bucket is now — read, not assumed: it may be adopted. */
|
|
660
|
+
const located = async (name, made) => ctx.dryRun && made === "created" ? region : bucketRegion(clients, name);
|
|
580
661
|
const create = async (name) => {
|
|
581
662
|
if (ctx.dryRun) {
|
|
582
663
|
const found = await probe(name);
|
|
@@ -590,7 +671,7 @@ async function ensureBucketName(ctx, clients, id, good) {
|
|
|
590
671
|
}
|
|
591
672
|
return found;
|
|
592
673
|
}
|
|
593
|
-
await ctx.state.pending(entry(name));
|
|
674
|
+
await ctx.state.pending(entry(name, region));
|
|
594
675
|
try {
|
|
595
676
|
await s3.send(new S3.CreateBucketCommand({
|
|
596
677
|
Bucket: name,
|
|
@@ -609,23 +690,22 @@ async function ensureBucketName(ctx, clients, id, good) {
|
|
|
609
690
|
}, async () => {});
|
|
610
691
|
return "created";
|
|
611
692
|
};
|
|
612
|
-
const adopt = async (name) => {
|
|
613
|
-
const
|
|
693
|
+
const adopt = async (name, where) => {
|
|
694
|
+
const there = where === region ? clients : await makeClients(ctx.credential, where);
|
|
695
|
+
const owner = (await readOr(() => there.s3.send(new there.S3.GetBucketTaggingCommand({ Bucket: name })), ["NoSuchTagSet", "NoSuchTagSetError"]))?.TagSet?.find((t) => t.Key === TAG_PROJECT)?.Value;
|
|
614
696
|
if (owner && owner !== ctx.identity.key) throw new BucketOwnedByAnotherProject(name, owner, ctx.identity.key);
|
|
615
697
|
};
|
|
616
698
|
if (record?.status === "ready" && record.id) {
|
|
617
699
|
const found = await probe(record.id);
|
|
618
|
-
if (found === "ours") return {
|
|
619
|
-
bucket: record.id,
|
|
620
|
-
exists: true
|
|
621
|
-
};
|
|
622
700
|
if (found === "taken") throw new RecordedBucketUnreachable(id, record.id);
|
|
623
|
-
const made = await create(record.id);
|
|
701
|
+
const made = found === "ours" ? "ours" : await create(record.id);
|
|
624
702
|
if (made === "taken") throw new RecordedBucketUnreachable(id, record.id);
|
|
625
|
-
|
|
703
|
+
const where = await located(record.id, made);
|
|
704
|
+
if (!ctx.dryRun) await ctx.state.ready(entry(record.id, where), record.id);
|
|
626
705
|
return {
|
|
627
706
|
bucket: record.id,
|
|
628
|
-
exists: made === "ours"
|
|
707
|
+
exists: made === "ours",
|
|
708
|
+
region: where
|
|
629
709
|
};
|
|
630
710
|
}
|
|
631
711
|
const pending = record?.data?.name;
|
|
@@ -639,12 +719,14 @@ async function ensureBucketName(ctx, clients, id, good) {
|
|
|
639
719
|
tried.push(name);
|
|
640
720
|
const made = await create(name);
|
|
641
721
|
if (made === "taken") continue;
|
|
642
|
-
|
|
643
|
-
if (
|
|
722
|
+
const where = await located(name, made);
|
|
723
|
+
if (made === "ours") await adopt(name, where);
|
|
724
|
+
if (!ctx.dryRun) await ctx.state.ready(entry(name, where), name);
|
|
644
725
|
if (name !== good) ctx.log(` '${good}' is taken by another account; '${id}' is '${name}', recorded in the stage's state`);
|
|
645
726
|
return {
|
|
646
727
|
bucket: name,
|
|
647
|
-
exists: made === "ours"
|
|
728
|
+
exists: made === "ours",
|
|
729
|
+
region: where
|
|
648
730
|
};
|
|
649
731
|
}
|
|
650
732
|
throw new BucketNameUnavailable(id, tried.filter((n) => n !== pending));
|
|
@@ -808,8 +890,8 @@ async function ensureAccessKey(ctx, clients, input) {
|
|
|
808
890
|
const record = ctx.state.record(entryKey);
|
|
809
891
|
const given = ctx.secrets[urlKey];
|
|
810
892
|
let inUrl;
|
|
893
|
+
let parsed;
|
|
811
894
|
if (given !== void 0) {
|
|
812
|
-
let parsed;
|
|
813
895
|
try {
|
|
814
896
|
parsed = s3Url.parse(given);
|
|
815
897
|
} catch {
|
|
@@ -853,7 +935,20 @@ async function ensureAccessKey(ctx, clients, input) {
|
|
|
853
935
|
await ctx.state.ready(entry(), inUrl);
|
|
854
936
|
current = inUrl;
|
|
855
937
|
}
|
|
856
|
-
if (current !== void 0 && listed.includes(current) && inUrl === current && !ctx.rotateKeys)
|
|
938
|
+
if (current !== void 0 && listed.includes(current) && inUrl === current && !ctx.rotateKeys) {
|
|
939
|
+
if (parsed && parsed.region !== clients.region) {
|
|
940
|
+
const address = parsed;
|
|
941
|
+
await ctx.change({
|
|
942
|
+
construct: id,
|
|
943
|
+
resource: urlKey,
|
|
944
|
+
change: `name ${clients.region}, the bucket's region (it said ${address.region ?? "none"})`
|
|
945
|
+
}, () => ctx.writeSecrets({ [urlKey]: s3Url.build({
|
|
946
|
+
...address,
|
|
947
|
+
region: clients.region
|
|
948
|
+
}) }));
|
|
949
|
+
}
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
857
952
|
if (ctx.rotateKeys && previous) throw new RotationInProgress(id, previous, ctx.stage);
|
|
858
953
|
const outgoing = current && listed.includes(current) ? current : void 0;
|
|
859
954
|
if (listed.length >= 2) throw new AccessKeyLimit(user, listed);
|
|
@@ -875,28 +970,58 @@ async function ensureAccessKey(ctx, clients, input) {
|
|
|
875
970
|
accessKeyId: key.AccessKeyId,
|
|
876
971
|
secretAccessKey: key.SecretAccessKey
|
|
877
972
|
}) });
|
|
973
|
+
const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
878
974
|
await ctx.state.ready(entry(outgoing ? {
|
|
879
975
|
previous: outgoing,
|
|
880
|
-
rotatedAt:
|
|
881
|
-
|
|
976
|
+
rotatedAt: issuedAt,
|
|
977
|
+
issuedAt
|
|
978
|
+
} : { issuedAt }), key.AccessKeyId);
|
|
882
979
|
});
|
|
883
980
|
if (outgoing) ctx.log(` ${urlKey} holds the new key. This deploy moves '${ctx.stage}' onto it; the next deploy after it deletes the old key ${outgoing}`);
|
|
884
981
|
}
|
|
982
|
+
/** How long a key issued this recently counts as new to IAM. */
|
|
983
|
+
const NEW_KEY_WINDOW_MS = 6e5;
|
|
984
|
+
/** The waits between asks while a new key becomes usable: about a minute. */
|
|
985
|
+
let newKeyWaits = [
|
|
986
|
+
2e3,
|
|
987
|
+
4e3,
|
|
988
|
+
8e3,
|
|
989
|
+
15e3,
|
|
990
|
+
15e3,
|
|
991
|
+
15e3
|
|
992
|
+
];
|
|
993
|
+
/** When the key in the URL was issued, if the stage's state says it was just now. */
|
|
994
|
+
function issuedJustNow(ctx, id, accessKeyId) {
|
|
995
|
+
const record = ctx.resources?.[`iam-access-key:${id}`];
|
|
996
|
+
const issuedAt = record?.data?.issuedAt;
|
|
997
|
+
if (record?.id !== accessKeyId || typeof issuedAt !== "string") return;
|
|
998
|
+
const age = Date.now() - Date.parse(issuedAt);
|
|
999
|
+
return age >= 0 && age < NEW_KEY_WINDOW_MS ? issuedAt : void 0;
|
|
1000
|
+
}
|
|
885
1001
|
/**
|
|
886
1002
|
* Each provisioned bucket answers `HeadBucket` to the key in the stage's
|
|
887
1003
|
* secrets — the one the apps will use. A bucket whose URL carries no key is
|
|
888
1004
|
* not one this provider wrote, and the deploy's own check covers a URL that
|
|
889
1005
|
* is missing.
|
|
1006
|
+
*
|
|
1007
|
+
* A failure is read by its status, never its name — a `HEAD` has no body,
|
|
1008
|
+
* so SDK v3 calls a 301, 400 or 403 `Unknown` — and the message carries
|
|
1009
|
+
* everything S3 said: status, error, region, request ids.
|
|
1010
|
+
*
|
|
1011
|
+
* @throws {BucketRegionMismatch} the bucket is in another region than its URL says
|
|
1012
|
+
* @throws {ProvisionedBucketUnreachable}
|
|
890
1013
|
*/
|
|
891
1014
|
async function verifyS3(ctx) {
|
|
892
1015
|
const S3 = await import("@aws-sdk/client-s3");
|
|
893
1016
|
for (const { id } of bucketConstructs(ctx.manifest)) {
|
|
894
|
-
const
|
|
1017
|
+
const urlKey = provideKey(id, "url");
|
|
1018
|
+
const url = ctx.secrets[urlKey];
|
|
895
1019
|
if (url === void 0) continue;
|
|
896
1020
|
const address = s3Url.parse(url);
|
|
897
1021
|
if (!address.accessKeyId || !address.secretAccessKey) continue;
|
|
1022
|
+
const asked = address.region ?? ctx.config.region ?? "us-east-1";
|
|
898
1023
|
const client = new S3.S3Client({
|
|
899
|
-
region:
|
|
1024
|
+
region: asked,
|
|
900
1025
|
credentials: {
|
|
901
1026
|
accessKeyId: address.accessKeyId,
|
|
902
1027
|
secretAccessKey: address.secretAccessKey
|
|
@@ -905,10 +1030,35 @@ async function verifyS3(ctx) {
|
|
|
905
1030
|
...address.forcePathStyle ? { forcePathStyle: true } : {}
|
|
906
1031
|
});
|
|
907
1032
|
try {
|
|
908
|
-
await
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
1033
|
+
let head = await headBucket(S3, client, address.bucket);
|
|
1034
|
+
const issuedAt = issuedJustNow(ctx, id, address.accessKeyId);
|
|
1035
|
+
let waited = 0;
|
|
1036
|
+
if (!head.ok && head.status === 403 && issuedAt) {
|
|
1037
|
+
ctx.log?.(` ${id}: waiting for the new key ${address.accessKeyId} to become active…`);
|
|
1038
|
+
for (const wait of newKeyWaits) {
|
|
1039
|
+
await new Promise((resolve) => setTimeout(resolve, wait));
|
|
1040
|
+
waited += wait;
|
|
1041
|
+
head = await headBucket(S3, client, address.bucket);
|
|
1042
|
+
if (head.ok || head.status !== 403) break;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
if (head.ok) continue;
|
|
1046
|
+
const fail = (reason) => new ProvisionedBucketUnreachable(id, address.bucket, ctx.stage, reason);
|
|
1047
|
+
const said = describeAnswer(head);
|
|
1048
|
+
if (head.status === 301 || head.status === 400 && head.region && head.region !== asked) {
|
|
1049
|
+
let where = head.region;
|
|
1050
|
+
let located = "";
|
|
1051
|
+
if (!where) {
|
|
1052
|
+
const location = await bucketLocation(S3, client, address.bucket);
|
|
1053
|
+
if ("region" in location) where = location.region;
|
|
1054
|
+
else located = `; GetBucketLocation answered ${describeAnswer(location.failed)}`;
|
|
1055
|
+
}
|
|
1056
|
+
if (where && where !== asked) throw new BucketRegionMismatch(id, address.bucket, ctx.stage, urlKey, where, address.region, asked, said);
|
|
1057
|
+
throw fail(`S3 redirected the request (${said}${located})`);
|
|
1058
|
+
}
|
|
1059
|
+
if (head.status === 404) throw fail(`it does not exist (${said})`);
|
|
1060
|
+
if (head.status === 403) throw fail(`the key is refused — AccessDenied, or an invalid or not-yet-active key (${said})` + (issuedAt ? `. The key was issued at ${issuedAt} by this deploy, and IAM keys take seconds to become usable: it was still refused after waiting ${Math.round(waited / 1e3)}s` : ""));
|
|
1061
|
+
throw fail(said);
|
|
912
1062
|
} finally {
|
|
913
1063
|
client.destroy();
|
|
914
1064
|
}
|
|
@@ -961,7 +1111,9 @@ async function verifyStageProviders(input) {
|
|
|
961
1111
|
manifest: input.manifest,
|
|
962
1112
|
stage: input.stage,
|
|
963
1113
|
config: choice.config,
|
|
964
|
-
secrets: input.secrets
|
|
1114
|
+
secrets: input.secrets,
|
|
1115
|
+
...input.resources ? { resources: input.resources } : {},
|
|
1116
|
+
...input.log ? { log: input.log } : {}
|
|
965
1117
|
});
|
|
966
1118
|
verified.push(`${kind}: ${provider.name}`);
|
|
967
1119
|
}
|
|
@@ -1255,7 +1407,9 @@ async function assertStageReady(ctx) {
|
|
|
1255
1407
|
workspace,
|
|
1256
1408
|
manifest,
|
|
1257
1409
|
stage,
|
|
1258
|
-
secrets: supplied
|
|
1410
|
+
secrets: supplied,
|
|
1411
|
+
resources: (await ctx.state.read(stage))?.resources ?? {},
|
|
1412
|
+
log: (line) => ctx.logger.info(line)
|
|
1259
1413
|
});
|
|
1260
1414
|
for (const line of verified) ctx.logger.info(`✓ ${line} verified`);
|
|
1261
1415
|
}
|
|
@@ -1851,4 +2005,4 @@ async function load(input, sandbox) {
|
|
|
1851
2005
|
//#endregion
|
|
1852
2006
|
export { UnknownDeployApps as a, iamUserName as c, TargetBuildsNothing as i, NoDeployableApps as n, IAM_USER_PATH as o, RollbackFailed as r, bucketName as s, deploy as t };
|
|
1853
2007
|
|
|
1854
|
-
//# sourceMappingURL=deploy-
|
|
2008
|
+
//# sourceMappingURL=deploy-DktQl7ti.mjs.map
|