@void/deploy-cloudflare 0.27.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +4 -0
- package/dist/index.mjs +84 -86
- package/package.json +5 -5
package/dist/index.d.mts
CHANGED
|
@@ -479,6 +479,10 @@ export declare function createCloudflareMetadataRunner(options: {
|
|
|
479
479
|
env?: Readonly<Record<string, string | undefined>>;
|
|
480
480
|
}): CloudflareCommandRunner;
|
|
481
481
|
//#endregion
|
|
482
|
+
//#region src/diagnostics.d.ts
|
|
483
|
+
/** Keep the uploader's error, excluding stdout binding tables and redacting common credential formats. */
|
|
484
|
+
export declare function cloudflareCommandDiagnostic(result: CloudflareCommandResult): string | undefined;
|
|
485
|
+
//#endregion
|
|
482
486
|
//#region src/secret-draft.d.ts
|
|
483
487
|
export declare const VOID_SECRET_DRAFT_SOURCE = "export default { fetch() { return new Response(\"\"); } };\n";
|
|
484
488
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { isDeepStrictEqual, stripVTControlCharacters } from "node:util";
|
|
2
|
-
import { isRecord } from "@void/deploy-core";
|
|
2
|
+
import { canonicalJson, isRecord } from "@void/deploy-core";
|
|
3
3
|
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { SyntaxKind, applyEdits, createScanner, modify, parse, parseTree } from "jsonc-parser";
|
|
5
5
|
import nodePath from "node:path";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
|
+
import { setTimeout } from "node:timers/promises";
|
|
8
9
|
//#region src/config.ts
|
|
9
10
|
/** Parse a root Wrangler JSON/JSONC config without accepting a partial parse. */
|
|
10
11
|
function parseRootWranglerConfig(configPath) {
|
|
@@ -21,6 +22,14 @@ function parseRootWranglerConfig(configPath) {
|
|
|
21
22
|
if (errors.length > 0 || typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
|
|
22
23
|
return parsed;
|
|
23
24
|
}
|
|
25
|
+
/** Set one value in JSONC text, keeping comments and formatting elsewhere. */
|
|
26
|
+
function setJsoncValue(text, path, value) {
|
|
27
|
+
return applyEdits(text, modify(text, path, value, { formattingOptions: {
|
|
28
|
+
insertSpaces: true,
|
|
29
|
+
tabSize: 2,
|
|
30
|
+
eol: "\n"
|
|
31
|
+
} }));
|
|
32
|
+
}
|
|
24
33
|
function readWranglerTopLevelString(config, field) {
|
|
25
34
|
return readConfigEntryString(config, field);
|
|
26
35
|
}
|
|
@@ -574,11 +583,7 @@ function writeCloudflareFrameworkDeployConfig(options) {
|
|
|
574
583
|
const initial = existsSync(options.configPath) ? readFileSync(options.configPath, "utf-8") : "{}\n";
|
|
575
584
|
let content = initial;
|
|
576
585
|
const upsert = (path, value) => {
|
|
577
|
-
content =
|
|
578
|
-
insertSpaces: true,
|
|
579
|
-
tabSize: 2,
|
|
580
|
-
eol: "\n"
|
|
581
|
-
} }));
|
|
586
|
+
content = setJsoncValue(content, path, value);
|
|
582
587
|
};
|
|
583
588
|
upsert(["name"], options.name);
|
|
584
589
|
upsert(["account_id"], options.accountId);
|
|
@@ -692,12 +697,7 @@ function writeCloudflareStaticDeployConfig(options) {
|
|
|
692
697
|
const originalComments = extractCommentTokens(source);
|
|
693
698
|
let content = source;
|
|
694
699
|
const upsert = (path, value) => {
|
|
695
|
-
|
|
696
|
-
insertSpaces: true,
|
|
697
|
-
tabSize: 2,
|
|
698
|
-
eol: "\n"
|
|
699
|
-
} });
|
|
700
|
-
content = applyEdits(content, edits);
|
|
700
|
+
content = setJsoncValue(content, path, value);
|
|
701
701
|
};
|
|
702
702
|
upsert(["name"], options.name);
|
|
703
703
|
upsert(["account_id"], options.accountId);
|
|
@@ -781,6 +781,14 @@ function cloudflareCommandDiagnostic(result) {
|
|
|
781
781
|
if (message === stderr && !/(?:^|\n)[ \t]*(?:[✘×X][ \t]+)?\[ERROR\]/i.test(stderr)) {
|
|
782
782
|
const box = /(?:^|\n)[ \t]*┌[ \t]*(?:APIError|Error)\b[^\n]*\n(?:[ \t]*│[^\n]*\n)*[ \t]*└[^\n]*/i.exec(stdout);
|
|
783
783
|
if (box) message = box[0].trim();
|
|
784
|
+
else {
|
|
785
|
+
const lines = stdout.split("\n");
|
|
786
|
+
const abort = lines.find((line) => line.startsWith("■ Aborting the upload operation because of conflicts."));
|
|
787
|
+
if (abort) {
|
|
788
|
+
const reason = "You are about to upload a Worker that was last updated via the script API.";
|
|
789
|
+
message = [lines.includes(`▲ ${reason}`) ? reason : "", abort.slice(3)].filter(Boolean).join("\n");
|
|
790
|
+
}
|
|
791
|
+
}
|
|
784
792
|
}
|
|
785
793
|
message = stripVTControlCharacters(message).split(/\n\s*(?:🪵|Logs (?:were )?written|If you think this is a bug)/)[0].trim();
|
|
786
794
|
if (!message) return;
|
|
@@ -910,11 +918,10 @@ function pickWranglerTriggerContext(source) {
|
|
|
910
918
|
* schema-2 snapshot. The bounded loop fails closed if traffic never stabilizes.
|
|
911
919
|
*/
|
|
912
920
|
async function stabilizeCloudflareVersionTriggers(options) {
|
|
913
|
-
const configPath = options.configPath;
|
|
914
921
|
let cleanupBaseline = mergeTriggerCleanupBaselines(options.previousTriggerConfig, options.targetTriggerConfig);
|
|
915
922
|
const attempts = Math.max(1, options.attempts ?? 4);
|
|
916
923
|
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
917
|
-
const owner = await readCurrentProductionVersion(options
|
|
924
|
+
const owner = await readCurrentProductionVersion(options);
|
|
918
925
|
if (owner.kind === "failed") return {
|
|
919
926
|
ok: false,
|
|
920
927
|
reason: "status-failed",
|
|
@@ -932,7 +939,7 @@ async function stabilizeCloudflareVersionTriggers(options) {
|
|
|
932
939
|
const activeVersionId = owner.versionId;
|
|
933
940
|
let activeTriggerConfig = activeVersionId === options.targetVersionId ? options.targetTriggerConfig : options.knownTriggerConfigs?.[activeVersionId];
|
|
934
941
|
if (!activeTriggerConfig && activeVersionId !== options.targetVersionId) {
|
|
935
|
-
const metadata = await readCloudflareVersionMetadata(options,
|
|
942
|
+
const metadata = await readCloudflareVersionMetadata(options, activeVersionId);
|
|
936
943
|
activeTriggerConfig = metadata?.schema === 2 ? metadata.triggerConfig : void 0;
|
|
937
944
|
}
|
|
938
945
|
if (!activeTriggerConfig) return {
|
|
@@ -957,7 +964,7 @@ async function stabilizeCloudflareVersionTriggers(options) {
|
|
|
957
964
|
activeVersionId
|
|
958
965
|
};
|
|
959
966
|
cleanupBaseline = mergeTriggerCleanupBaselines(cleanupBaseline, activeTriggerConfig);
|
|
960
|
-
const after = await readCurrentProductionVersion(options
|
|
967
|
+
const after = await readCurrentProductionVersion(options);
|
|
961
968
|
if (after.kind === "failed") return {
|
|
962
969
|
ok: false,
|
|
963
970
|
reason: "status-failed",
|
|
@@ -1097,12 +1104,7 @@ function nonVersionedSettingsHash(config) {
|
|
|
1097
1104
|
"tail_consumers",
|
|
1098
1105
|
"streaming_tail_consumers"
|
|
1099
1106
|
].filter((key) => config[key] !== void 0).map((key) => [key, config[key]]));
|
|
1100
|
-
return createHash("sha256").update(
|
|
1101
|
-
}
|
|
1102
|
-
function stableJson(value) {
|
|
1103
|
-
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
1104
|
-
if (isRecord(value)) return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
|
|
1105
|
-
return JSON.stringify(value);
|
|
1107
|
+
return createHash("sha256").update(canonicalJson(settings)).digest("hex");
|
|
1106
1108
|
}
|
|
1107
1109
|
function parseTriggerConfig(value) {
|
|
1108
1110
|
if (!isRecord(value)) return null;
|
|
@@ -1190,13 +1192,12 @@ async function activateCloudflareVersion(options) {
|
|
|
1190
1192
|
}
|
|
1191
1193
|
}
|
|
1192
1194
|
async function activateCloudflareVersionWithSecretsFile(options) {
|
|
1193
|
-
const configPath = options.configPath;
|
|
1194
1195
|
if (!options.verifyUploader()) return {
|
|
1195
1196
|
ok: false,
|
|
1196
1197
|
reason: "uploader-changed"
|
|
1197
1198
|
};
|
|
1198
1199
|
options.onProgress?.("Checking the current Worker deployment...");
|
|
1199
|
-
const previous = await readCurrentProductionVersion(options
|
|
1200
|
+
const previous = await readCurrentProductionVersion(options);
|
|
1200
1201
|
if (previous.kind === "failed") return {
|
|
1201
1202
|
ok: false,
|
|
1202
1203
|
reason: "status-failed",
|
|
@@ -1230,7 +1231,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1230
1231
|
let activeSecretsPinned = false;
|
|
1231
1232
|
let pinnedActiveSecrets;
|
|
1232
1233
|
if (options.expectedPreviousVersionId !== void 0 || options.verifyInheritedSecrets === true) {
|
|
1233
|
-
const recent = await readRecentCloudflareVersionIds(options
|
|
1234
|
+
const recent = await readRecentCloudflareVersionIds(options);
|
|
1234
1235
|
if (recent === null) return {
|
|
1235
1236
|
ok: false,
|
|
1236
1237
|
reason: "inheritance-source-unavailable"
|
|
@@ -1249,13 +1250,13 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1249
1250
|
};
|
|
1250
1251
|
}
|
|
1251
1252
|
if (options.verifyInheritedSecrets === true && previous.versionId !== latestVersionId && !trustedCandidateChain) {
|
|
1252
|
-
const replacesAllRemoteSecrets = await candidateReplacesAllRemoteSecrets(options,
|
|
1253
|
+
const replacesAllRemoteSecrets = await candidateReplacesAllRemoteSecrets(options, previous.versionId, latestVersionId);
|
|
1253
1254
|
if (replacesAllRemoteSecrets === null) return {
|
|
1254
1255
|
ok: false,
|
|
1255
1256
|
reason: "inheritance-source-unavailable"
|
|
1256
1257
|
};
|
|
1257
1258
|
if (!replacesAllRemoteSecrets) {
|
|
1258
|
-
const activeSecrets = previous.versionId ? await readCloudflareVersionSecretBindings(options,
|
|
1259
|
+
const activeSecrets = previous.versionId ? await readCloudflareVersionSecretBindings(options, previous.versionId) : null;
|
|
1259
1260
|
if (!activeSecrets || !options.pinActiveSecrets || !await options.pinActiveSecrets(previous.versionId, activeSecrets)) return {
|
|
1260
1261
|
ok: false,
|
|
1261
1262
|
reason: "inheritance-source-changed",
|
|
@@ -1270,10 +1271,10 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1270
1271
|
}
|
|
1271
1272
|
const desiredMetadata = parseCloudflareVersionMessage(options.message);
|
|
1272
1273
|
let desiredTriggerConfig = desiredTriggerConfigFor(desiredMetadata, options.configPath);
|
|
1273
|
-
let previousVersion = previousVersionId && desiredMetadata?.schema === 2 ? await readCloudflareVersionInspection(options,
|
|
1274
|
+
let previousVersion = previousVersionId && desiredMetadata?.schema === 2 ? await readCloudflareVersionInspection(options, previousVersionId, inheritanceBaseline?.map((version) => version.inspection)) : null;
|
|
1274
1275
|
async function previousIsSecretOnlyDraft() {
|
|
1275
1276
|
if (!previousVersionId) return false;
|
|
1276
|
-
previousVersion ??= await readCloudflareVersionInspection(options,
|
|
1277
|
+
previousVersion ??= await readCloudflareVersionInspection(options, previousVersionId, inheritanceBaseline?.map((version) => version.inspection));
|
|
1277
1278
|
return previousVersion?.isSecretOnlyDraft === true;
|
|
1278
1279
|
}
|
|
1279
1280
|
const previousMetadata = desiredMetadata?.schema === 2 ? previousVersion?.metadata : null;
|
|
@@ -1314,7 +1315,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1314
1315
|
if (bootstrapVoidDraft || !activeSecretsPinned && options.pinActiveSecrets) {
|
|
1315
1316
|
let activeSecrets;
|
|
1316
1317
|
try {
|
|
1317
|
-
activeSecrets = await readCloudflareVersionSecretBindings(options,
|
|
1318
|
+
activeSecrets = await readCloudflareVersionSecretBindings(options, previous.versionId);
|
|
1318
1319
|
} catch {
|
|
1319
1320
|
return {
|
|
1320
1321
|
ok: false,
|
|
@@ -1348,12 +1349,12 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1348
1349
|
return bootstrapFirstDeployment({
|
|
1349
1350
|
...options,
|
|
1350
1351
|
message
|
|
1351
|
-
},
|
|
1352
|
+
}, previous.versionId, options.atomic === true, previousTriggerConfig);
|
|
1352
1353
|
}
|
|
1353
1354
|
if (previous.versionId !== null && !activeSecretsPinned && options.pinActiveSecrets && options.candidateSecretsFilePath === void 0) {
|
|
1354
1355
|
let activeSecrets;
|
|
1355
1356
|
try {
|
|
1356
|
-
activeSecrets = await readCloudflareVersionSecretBindings(options,
|
|
1357
|
+
activeSecrets = await readCloudflareVersionSecretBindings(options, previous.versionId);
|
|
1357
1358
|
} catch {
|
|
1358
1359
|
return {
|
|
1359
1360
|
ok: false,
|
|
@@ -1390,7 +1391,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1390
1391
|
ok: false,
|
|
1391
1392
|
reason: "uploader-changed"
|
|
1392
1393
|
};
|
|
1393
|
-
const owner = await readCurrentProductionVersion(options
|
|
1394
|
+
const owner = await readCurrentProductionVersion(options);
|
|
1394
1395
|
if (owner.kind === "failed") return {
|
|
1395
1396
|
ok: false,
|
|
1396
1397
|
reason: "status-failed",
|
|
@@ -1409,7 +1410,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1409
1410
|
options.onProgress?.("Uploading the Worker and assets...");
|
|
1410
1411
|
const upload = await options.runWrangler({
|
|
1411
1412
|
type: "versions.upload",
|
|
1412
|
-
configPath,
|
|
1413
|
+
configPath: options.configPath,
|
|
1413
1414
|
workerName: options.workerName,
|
|
1414
1415
|
message,
|
|
1415
1416
|
secretsFilePath: options.candidateSecretsFilePath,
|
|
@@ -1422,7 +1423,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1422
1423
|
if (!options.preserveTriggers && isBootstrapError && (previous.versionId === null || options.allowFirstDeploymentBootstrap === true || await previousIsSecretOnlyDraft())) return bootstrapFirstDeployment({
|
|
1423
1424
|
...options,
|
|
1424
1425
|
message
|
|
1425
|
-
},
|
|
1426
|
+
}, previous.versionId, false, previousTriggerConfig);
|
|
1426
1427
|
return {
|
|
1427
1428
|
ok: false,
|
|
1428
1429
|
reason: "upload-failed",
|
|
@@ -1440,7 +1441,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1440
1441
|
};
|
|
1441
1442
|
options.onProgress?.("Verifying the uploaded Worker...");
|
|
1442
1443
|
if (inheritanceBaseline) {
|
|
1443
|
-
const recent = await readRecentCloudflareVersionIds(options
|
|
1444
|
+
const recent = await readRecentCloudflareVersionIds(options);
|
|
1444
1445
|
const previousLatest = inheritanceBaseline.at(-1);
|
|
1445
1446
|
const expected = [...inheritanceBaseline.map((version) => version.id), versionId].slice(-10);
|
|
1446
1447
|
if (recent === null) return {
|
|
@@ -1482,7 +1483,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1482
1483
|
if (!options.preserveTriggers && (previous.versionId === null || options.allowFirstDeploymentBootstrap === true || await previousIsSecretOnlyDraft())) return bootstrapFirstDeployment({
|
|
1483
1484
|
...options,
|
|
1484
1485
|
message
|
|
1485
|
-
},
|
|
1486
|
+
}, previous.versionId, false, previousTriggerConfig);
|
|
1486
1487
|
readinessUrl = desiredTriggerConfig.workers_dev ? workersDevReadinessUrl(options.workerName, options.workersDevSubdomain) : customRouteReadinessUrl(desiredTriggerConfig.routes);
|
|
1487
1488
|
readinessUsesCustomRoute = !desiredTriggerConfig.workers_dev && readinessUrl !== void 0;
|
|
1488
1489
|
if (!readinessUrl) return {
|
|
@@ -1493,17 +1494,17 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1493
1494
|
};
|
|
1494
1495
|
const fallbackSubdomain = extractWorkersDevSubdomain(readinessUrl);
|
|
1495
1496
|
if (fallbackSubdomain && fallbackSubdomain === options.readinessHeadersWorkersDevSubdomain) authenticatedWorkersDevSubdomain = fallbackSubdomain;
|
|
1496
|
-
const stagingFailure = await stageCandidateForReadiness(options,
|
|
1497
|
+
const stagingFailure = await stageCandidateForReadiness(options, previous.versionId, versionId, message);
|
|
1497
1498
|
if (stagingFailure) return stagingFailure;
|
|
1498
1499
|
stagedVersionOverride = true;
|
|
1499
1500
|
}
|
|
1500
|
-
const probe = (url) => probeReadiness(`${url.replace(/\/$/, "")}/__void/ready`, options.workerName, readinessUsesCustomRoute ? void 0 : options.readinessHeaders, authenticatedWorkersDevSubdomain, stagedVersionOverride ? versionId : void 0, options.fetch ?? globalThis.fetch, options.sleep ??
|
|
1501
|
+
const probe = (url) => probeReadiness(`${url.replace(/\/$/, "")}/__void/ready`, options.workerName, readinessUsesCustomRoute ? void 0 : options.readinessHeaders, authenticatedWorkersDevSubdomain, stagedVersionOverride ? versionId : void 0, options.fetch ?? globalThis.fetch, options.sleep ?? setTimeout, options.readinessAttempts ?? 6, Number.POSITIVE_INFINITY, options.requireVersionMetadata ? versionId : void 0);
|
|
1501
1502
|
options.onProgress?.("Waiting for the Worker to become ready...");
|
|
1502
1503
|
let ready = await probe(readinessUrl);
|
|
1503
1504
|
if (ready === "access-denied" && !stagedVersionOverride && previous.versionId !== null && !options.requirePreviewForReadiness) {
|
|
1504
1505
|
const stableReadinessUrl = desiredTriggerConfig.workers_dev ? workersDevReadinessUrl(options.workerName, extractWorkersDevSubdomain(readinessUrl) ?? options.workersDevSubdomain) : customRouteReadinessUrl(desiredTriggerConfig.routes);
|
|
1505
1506
|
if (stableReadinessUrl && stableReadinessUrl !== readinessUrl) {
|
|
1506
|
-
const stagingFailure = await stageCandidateForReadiness(options,
|
|
1507
|
+
const stagingFailure = await stageCandidateForReadiness(options, previous.versionId, versionId, message);
|
|
1507
1508
|
if (stagingFailure) return stagingFailure;
|
|
1508
1509
|
readinessUrl = stableReadinessUrl;
|
|
1509
1510
|
authenticatedWorkersDevSubdomain = extractWorkersDevSubdomain(stableReadinessUrl);
|
|
@@ -1517,16 +1518,16 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1517
1518
|
reason: ready === "access-denied" ? "readiness-access-denied" : "readiness-failed",
|
|
1518
1519
|
versionId,
|
|
1519
1520
|
previousVersionId: previous.versionId,
|
|
1520
|
-
rollback: stagedVersionOverride ? await restorePreviousVersion(options,
|
|
1521
|
+
rollback: stagedVersionOverride ? await restorePreviousVersion(options, previous.versionId, "readiness failed", [stagedVersionPercentages(previous.versionId, versionId)]) : "not-needed"
|
|
1521
1522
|
};
|
|
1522
1523
|
if (!options.verifyUploader()) return {
|
|
1523
1524
|
ok: false,
|
|
1524
1525
|
reason: "uploader-changed",
|
|
1525
1526
|
versionId,
|
|
1526
1527
|
previousVersionId: previous.versionId,
|
|
1527
|
-
rollback: stagedVersionOverride ? await restorePreviousVersion(options,
|
|
1528
|
+
rollback: stagedVersionOverride ? await restorePreviousVersion(options, previous.versionId, "uploader verification failed", [stagedVersionPercentages(previous.versionId, versionId)]) : "not-needed"
|
|
1528
1529
|
};
|
|
1529
|
-
const ownerBeforeActivation = await readCurrentProductionVersion(options
|
|
1530
|
+
const ownerBeforeActivation = await readCurrentProductionVersion(options);
|
|
1530
1531
|
if (ownerBeforeActivation.kind === "failed") return {
|
|
1531
1532
|
ok: false,
|
|
1532
1533
|
reason: "status-failed",
|
|
@@ -1554,14 +1555,14 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1554
1555
|
triggerRepair: "succeeded"
|
|
1555
1556
|
};
|
|
1556
1557
|
options.onProgress?.("Activating the Worker...");
|
|
1557
|
-
const activate = await deployVersionPercentages(options,
|
|
1558
|
+
const activate = await deployVersionPercentages(options, [`${versionId}@100`], message);
|
|
1558
1559
|
if (!isCleanCommandExit(activate)) return {
|
|
1559
1560
|
ok: false,
|
|
1560
1561
|
reason: "activation-failed",
|
|
1561
1562
|
status: activate.status,
|
|
1562
1563
|
versionId,
|
|
1563
1564
|
previousVersionId: previous.versionId,
|
|
1564
|
-
rollback: stagedVersionOverride ? await restorePreviousVersion(options,
|
|
1565
|
+
rollback: stagedVersionOverride ? await restorePreviousVersion(options, previous.versionId, "version activation failed", [stagedVersionPercentages(previous.versionId, versionId), [{
|
|
1565
1566
|
versionId,
|
|
1566
1567
|
percentage: 100
|
|
1567
1568
|
}]]) : "not-needed"
|
|
@@ -1574,7 +1575,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1574
1575
|
rollback: previous.versionId ? "unsafe" : "unavailable"
|
|
1575
1576
|
};
|
|
1576
1577
|
if (options.preserveTriggers) {
|
|
1577
|
-
const owner = await readCurrentProductionVersion(options
|
|
1578
|
+
const owner = await readCurrentProductionVersion(options);
|
|
1578
1579
|
if (owner.kind !== "ok" || owner.versionId !== versionId) return {
|
|
1579
1580
|
ok: false,
|
|
1580
1581
|
reason: "deployment-changed",
|
|
@@ -1625,7 +1626,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1625
1626
|
previousVersionId: previous.versionId,
|
|
1626
1627
|
rollback: "unsafe"
|
|
1627
1628
|
};
|
|
1628
|
-
const ownerBeforeCompensation = await readCurrentProductionVersion(options
|
|
1629
|
+
const ownerBeforeCompensation = await readCurrentProductionVersion(options);
|
|
1629
1630
|
if (ownerBeforeCompensation.kind !== "ok" || ownerBeforeCompensation.versionId === null) return {
|
|
1630
1631
|
ok: false,
|
|
1631
1632
|
reason: "triggers-failed",
|
|
@@ -1657,7 +1658,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1657
1658
|
triggerRepair: triggerRepairOutcome(repair)
|
|
1658
1659
|
};
|
|
1659
1660
|
}
|
|
1660
|
-
const rollback = await restorePreviousVersionAndTriggers(options,
|
|
1661
|
+
const rollback = await restorePreviousVersionAndTriggers(options, previous.versionId, "trigger activation failed", desiredTriggerConfig, versionId, versionedPreviousTriggerConfigWithTraffic);
|
|
1661
1662
|
return {
|
|
1662
1663
|
ok: false,
|
|
1663
1664
|
reason: "triggers-failed",
|
|
@@ -1690,8 +1691,8 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1690
1691
|
...workersDevSubdomainResult(readinessUrl)
|
|
1691
1692
|
};
|
|
1692
1693
|
}
|
|
1693
|
-
async function restorePreviousVersionAndTriggers(options,
|
|
1694
|
-
const traffic = await restorePreviousVersion(options,
|
|
1694
|
+
async function restorePreviousVersionAndTriggers(options, previousVersionId, cause, failedTriggerConfig, failedVersionId, capturedTriggerConfig) {
|
|
1695
|
+
const traffic = await restorePreviousVersion(options, previousVersionId, cause, [[{
|
|
1695
1696
|
versionId: failedVersionId,
|
|
1696
1697
|
percentage: 100
|
|
1697
1698
|
}]]);
|
|
@@ -1699,7 +1700,7 @@ async function restorePreviousVersionAndTriggers(options, configPath, previousVe
|
|
|
1699
1700
|
if (!options.verifyUploader()) return "unsafe";
|
|
1700
1701
|
let targetTriggerConfig = capturedTriggerConfig;
|
|
1701
1702
|
if (!targetTriggerConfig) {
|
|
1702
|
-
const metadata = await readCloudflareVersionMetadata(options,
|
|
1703
|
+
const metadata = await readCloudflareVersionMetadata(options, previousVersionId);
|
|
1703
1704
|
if (metadata?.schema !== 2) return "unavailable";
|
|
1704
1705
|
targetTriggerConfig = metadata.triggerConfig;
|
|
1705
1706
|
}
|
|
@@ -1718,16 +1719,16 @@ async function restorePreviousVersionAndTriggers(options, configPath, previousVe
|
|
|
1718
1719
|
});
|
|
1719
1720
|
return triggers.ok && triggers.targetOwnsTraffic ? "succeeded" : "failed";
|
|
1720
1721
|
}
|
|
1721
|
-
async function readCloudflareVersionMetadata(options,
|
|
1722
|
-
return (await readCloudflareVersionInspection(options,
|
|
1722
|
+
async function readCloudflareVersionMetadata(options, versionId) {
|
|
1723
|
+
return (await readCloudflareVersionInspection(options, versionId))?.metadata ?? null;
|
|
1723
1724
|
}
|
|
1724
|
-
async function readCloudflareVersionInspection(options,
|
|
1725
|
+
async function readCloudflareVersionInspection(options, versionId, knownVersions) {
|
|
1725
1726
|
try {
|
|
1726
1727
|
let versions = knownVersions;
|
|
1727
1728
|
if (versions === void 0) {
|
|
1728
1729
|
const result = await options.runWrangler({
|
|
1729
1730
|
type: "versions.list",
|
|
1730
|
-
configPath,
|
|
1731
|
+
configPath: options.configPath,
|
|
1731
1732
|
workerName: options.workerName
|
|
1732
1733
|
}, { capture: true });
|
|
1733
1734
|
if (!isCleanCommandExit(result)) return null;
|
|
@@ -1743,7 +1744,7 @@ async function readCloudflareVersionInspection(options, configPath, versionId, k
|
|
|
1743
1744
|
const inspection = await options.runWrangler({
|
|
1744
1745
|
type: "versions.view",
|
|
1745
1746
|
versionId,
|
|
1746
|
-
configPath,
|
|
1747
|
+
configPath: options.configPath,
|
|
1747
1748
|
workerName: options.workerName,
|
|
1748
1749
|
includeSource: true
|
|
1749
1750
|
}, { capture: true });
|
|
@@ -1765,10 +1766,10 @@ async function readCloudflareVersionInspection(options, configPath, versionId, k
|
|
|
1765
1766
|
}
|
|
1766
1767
|
return null;
|
|
1767
1768
|
}
|
|
1768
|
-
async function readRecentCloudflareVersionIds(options
|
|
1769
|
+
async function readRecentCloudflareVersionIds(options) {
|
|
1769
1770
|
const result = await options.runWrangler({
|
|
1770
1771
|
type: "versions.list",
|
|
1771
|
-
configPath,
|
|
1772
|
+
configPath: options.configPath,
|
|
1772
1773
|
workerName: options.workerName
|
|
1773
1774
|
}, { capture: true });
|
|
1774
1775
|
if (!isCleanCommandExit(result)) {
|
|
@@ -1786,21 +1787,21 @@ async function readRecentCloudflareVersionIds(options, configPath) {
|
|
|
1786
1787
|
function arraysEqual(left, right) {
|
|
1787
1788
|
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1788
1789
|
}
|
|
1789
|
-
async function candidateReplacesAllRemoteSecrets(options,
|
|
1790
|
+
async function candidateReplacesAllRemoteSecrets(options, activeVersionId, latestVersionId) {
|
|
1790
1791
|
const versionIds = [...new Set([activeVersionId, latestVersionId].filter(Boolean))];
|
|
1791
1792
|
const secretBindings = [];
|
|
1792
1793
|
for (const versionId of versionIds) {
|
|
1793
|
-
const bindings = await readCloudflareVersionSecretBindings(options,
|
|
1794
|
+
const bindings = await readCloudflareVersionSecretBindings(options, versionId);
|
|
1794
1795
|
if (bindings === null) return null;
|
|
1795
1796
|
secretBindings.push(bindings);
|
|
1796
1797
|
}
|
|
1797
1798
|
return secretBindings.every((bindings) => bindings.every((binding) => binding.type === "secret_text" && Object.hasOwn(options.candidateSecrets ?? {}, binding.name)));
|
|
1798
1799
|
}
|
|
1799
|
-
async function readCloudflareVersionSecretBindings(options,
|
|
1800
|
+
async function readCloudflareVersionSecretBindings(options, versionId) {
|
|
1800
1801
|
const result = await options.runWrangler({
|
|
1801
1802
|
type: "versions.view",
|
|
1802
1803
|
versionId,
|
|
1803
|
-
configPath,
|
|
1804
|
+
configPath: options.configPath,
|
|
1804
1805
|
workerName: options.workerName
|
|
1805
1806
|
}, { capture: true });
|
|
1806
1807
|
if (!isCleanCommandExit(result)) return null;
|
|
@@ -1825,13 +1826,13 @@ async function readCloudflareVersionSecretBindings(options, configPath, versionI
|
|
|
1825
1826
|
* brand-new Worker can therefore need one ordinary deploy before later releases use preview-first
|
|
1826
1827
|
* version activation. This fallback requires prior proof that no application had been deployed.
|
|
1827
1828
|
*/
|
|
1828
|
-
async function bootstrapFirstDeployment(options,
|
|
1829
|
+
async function bootstrapFirstDeployment(options, previousVersionId, atomic = false, previousTriggerConfig) {
|
|
1829
1830
|
if (!options.verifyUploader()) return {
|
|
1830
1831
|
ok: false,
|
|
1831
1832
|
reason: "uploader-changed"
|
|
1832
1833
|
};
|
|
1833
1834
|
{
|
|
1834
|
-
const current = await readCurrentProductionVersion(options
|
|
1835
|
+
const current = await readCurrentProductionVersion(options);
|
|
1835
1836
|
if (current.kind === "failed") return {
|
|
1836
1837
|
ok: false,
|
|
1837
1838
|
reason: "status-failed",
|
|
@@ -1850,7 +1851,7 @@ async function bootstrapFirstDeployment(options, configPath, previousVersionId,
|
|
|
1850
1851
|
options.onProgress?.(atomic ? "Publishing the Worker atomically..." : "Publishing the first Worker deployment...");
|
|
1851
1852
|
const bootstrap = await options.runWrangler({
|
|
1852
1853
|
type: "worker.deploy",
|
|
1853
|
-
configPath,
|
|
1854
|
+
configPath: options.configPath,
|
|
1854
1855
|
workerName: options.workerName,
|
|
1855
1856
|
message: options.message,
|
|
1856
1857
|
secretsFilePath: options.candidateSecretsFilePath,
|
|
@@ -1899,7 +1900,7 @@ async function bootstrapFirstDeployment(options, configPath, previousVersionId,
|
|
|
1899
1900
|
publishEmptyCrons: options.publishEmptyCrons
|
|
1900
1901
|
};
|
|
1901
1902
|
const verifyPublishedOwner = async () => {
|
|
1902
|
-
const owner = await readCurrentProductionVersion(options
|
|
1903
|
+
const owner = await readCurrentProductionVersion(options);
|
|
1903
1904
|
if (owner.kind === "failed") return {
|
|
1904
1905
|
ok: false,
|
|
1905
1906
|
reason: "status-failed",
|
|
@@ -1976,7 +1977,7 @@ async function bootstrapFirstDeployment(options, configPath, previousVersionId,
|
|
|
1976
1977
|
const customDomainBootstrap = !atomic && usesCustomDomain;
|
|
1977
1978
|
const readinessTimeoutMs = customDomainBootstrap ? 3e5 : 6e4;
|
|
1978
1979
|
options.onProgress?.("Waiting for the Worker to become ready...");
|
|
1979
|
-
const ready = await probeReadiness(`${previewUrl.replace(/\/$/, "")}/__void/ready`, options.workerName, extractWorkersDevSubdomain(previewUrl) ? options.readinessHeaders : void 0, extractWorkersDevSubdomain(previewUrl), void 0, options.fetch ?? globalThis.fetch, options.sleep ??
|
|
1980
|
+
const ready = await probeReadiness(`${previewUrl.replace(/\/$/, "")}/__void/ready`, options.workerName, extractWorkersDevSubdomain(previewUrl) ? options.readinessHeaders : void 0, extractWorkersDevSubdomain(previewUrl), void 0, options.fetch ?? globalThis.fetch, options.sleep ?? setTimeout, options.readinessAttempts ?? (customDomainBootstrap ? 150 : 30), readinessTimeoutMs, options.requireVersionMetadata ? versionId : void 0);
|
|
1980
1981
|
if (ready !== "ready") return {
|
|
1981
1982
|
ok: false,
|
|
1982
1983
|
reason: ready === "access-denied" ? atomic ? "atomic-readiness-access-denied" : "bootstrap-readiness-access-denied" : atomic ? "atomic-readiness-failed" : "bootstrap-readiness-failed",
|
|
@@ -1992,7 +1993,7 @@ async function bootstrapFirstDeployment(options, configPath, previousVersionId,
|
|
|
1992
1993
|
previousVersionId,
|
|
1993
1994
|
rollback: "unsafe"
|
|
1994
1995
|
};
|
|
1995
|
-
const ownerAfterReadiness = await readCurrentProductionVersion(options
|
|
1996
|
+
const ownerAfterReadiness = await readCurrentProductionVersion(options);
|
|
1996
1997
|
if (ownerAfterReadiness.kind === "failed") return {
|
|
1997
1998
|
ok: false,
|
|
1998
1999
|
reason: "status-failed",
|
|
@@ -2048,10 +2049,10 @@ function commandDiagnosticFields(result) {
|
|
|
2048
2049
|
function isContainersUnavailable(result) {
|
|
2049
2050
|
return result.failure === "containers-unavailable" || (result.errors?.some(({ message }) => /Cloudflare Containers[^\n]*(?:Workers Paid|not (?:enabled|available)|subscription)/i.test(message) || /\/containers\/me(?:\b|\?)/i.test(message) && /\b403\b/.test(message)) ?? false);
|
|
2050
2051
|
}
|
|
2051
|
-
async function readCurrentProductionVersion(options
|
|
2052
|
+
async function readCurrentProductionVersion(options) {
|
|
2052
2053
|
const result = await options.runWrangler({
|
|
2053
2054
|
type: "deployments.status",
|
|
2054
|
-
configPath,
|
|
2055
|
+
configPath: options.configPath,
|
|
2055
2056
|
workerName: options.workerName
|
|
2056
2057
|
}, { capture: true });
|
|
2057
2058
|
if (!isCleanCommandExit(result)) {
|
|
@@ -2101,7 +2102,7 @@ function stagedVersionPercentages(previousVersionId, versionId) {
|
|
|
2101
2102
|
}] : [];
|
|
2102
2103
|
}
|
|
2103
2104
|
/** Stage the candidate at 0% beside the unchanged previous version so readiness can select it. */
|
|
2104
|
-
async function stageCandidateForReadiness(options,
|
|
2105
|
+
async function stageCandidateForReadiness(options, previousVersionId, versionId, message) {
|
|
2105
2106
|
if (!options.verifyUploader()) return {
|
|
2106
2107
|
ok: false,
|
|
2107
2108
|
reason: "uploader-changed",
|
|
@@ -2109,9 +2110,9 @@ async function stageCandidateForReadiness(options, configPath, previousVersionId
|
|
|
2109
2110
|
previousVersionId,
|
|
2110
2111
|
rollback: "not-needed"
|
|
2111
2112
|
};
|
|
2112
|
-
const ownerFailure = await verifyReadinessStagingOwner(options,
|
|
2113
|
+
const ownerFailure = await verifyReadinessStagingOwner(options, previousVersionId, versionId);
|
|
2113
2114
|
if (ownerFailure) return ownerFailure;
|
|
2114
|
-
const staged = await deployVersionPercentages(options,
|
|
2115
|
+
const staged = await deployVersionPercentages(options, [`${previousVersionId}@100`, `${versionId}@0`], message);
|
|
2115
2116
|
if (!isCleanCommandExit(staged)) {
|
|
2116
2117
|
const cloudflareErrorCode = staged.errorCode;
|
|
2117
2118
|
return {
|
|
@@ -2127,8 +2128,8 @@ async function stageCandidateForReadiness(options, configPath, previousVersionId
|
|
|
2127
2128
|
}
|
|
2128
2129
|
return null;
|
|
2129
2130
|
}
|
|
2130
|
-
async function verifyReadinessStagingOwner(options,
|
|
2131
|
-
const owner = await readCurrentProductionVersion(options
|
|
2131
|
+
async function verifyReadinessStagingOwner(options, previousVersionId, versionId) {
|
|
2132
|
+
const owner = await readCurrentProductionVersion(options);
|
|
2132
2133
|
if (owner.kind === "failed") return {
|
|
2133
2134
|
ok: false,
|
|
2134
2135
|
reason: "status-failed",
|
|
@@ -2184,8 +2185,8 @@ async function probeReadiness(url, workerName, readinessHeaders, authenticatedWo
|
|
|
2184
2185
|
if (response && !response.bodyUsed) await response.body?.cancel().catch(() => {});
|
|
2185
2186
|
}
|
|
2186
2187
|
if (attempt + 1 < attempts) {
|
|
2187
|
-
const
|
|
2188
|
-
if (
|
|
2188
|
+
const waitMs = Math.min(250 * 2 ** attempt, 2e3, deadline - Date.now());
|
|
2189
|
+
if (waitMs > 0) await sleep(waitMs);
|
|
2189
2190
|
}
|
|
2190
2191
|
}
|
|
2191
2192
|
return "failed";
|
|
@@ -2214,7 +2215,7 @@ async function isCloudflareAccessDenial(response) {
|
|
|
2214
2215
|
return false;
|
|
2215
2216
|
}
|
|
2216
2217
|
}
|
|
2217
|
-
async function deployVersionPercentages(options,
|
|
2218
|
+
async function deployVersionPercentages(options, percentages, message) {
|
|
2218
2219
|
const metadata = parseCloudflareVersionMessage(message);
|
|
2219
2220
|
if ((options.preserveTriggers || metadata?.schema === 2 && metadata.triggerConfig.preserve) && options.configPath) return deployCloudflareVersionTraffic({
|
|
2220
2221
|
...options,
|
|
@@ -2225,7 +2226,7 @@ async function deployVersionPercentages(options, configPath, percentages, messag
|
|
|
2225
2226
|
return options.runWrangler({
|
|
2226
2227
|
type: "versions.deploy",
|
|
2227
2228
|
versions: versionPercentages(percentages),
|
|
2228
|
-
configPath,
|
|
2229
|
+
configPath: options.configPath,
|
|
2229
2230
|
workerName: options.workerName,
|
|
2230
2231
|
message
|
|
2231
2232
|
}, { capture: false });
|
|
@@ -2251,12 +2252,12 @@ async function deployCloudflareVersionTraffic(options) {
|
|
|
2251
2252
|
});
|
|
2252
2253
|
}
|
|
2253
2254
|
}
|
|
2254
|
-
async function restorePreviousVersion(options,
|
|
2255
|
+
async function restorePreviousVersion(options, previousVersionId, cause, expectedOwnedTraffic) {
|
|
2255
2256
|
if (!previousVersionId) return "unavailable";
|
|
2256
2257
|
if (!options.verifyUploader()) return "unsafe";
|
|
2257
|
-
const owner = await readCurrentProductionVersion(options
|
|
2258
|
+
const owner = await readCurrentProductionVersion(options);
|
|
2258
2259
|
if (owner.kind !== "ok" || !expectedOwnedTraffic.some((expected) => productionVersionPercentagesEqual(owner.versions, expected))) return "unsafe";
|
|
2259
|
-
return isCleanCommandExit(await deployVersionPercentages(options,
|
|
2260
|
+
return isCleanCommandExit(await deployVersionPercentages(options, [`${previousVersionId}@100`], `Void automatic rollback after ${cause}.`)) ? "succeeded" : "failed";
|
|
2260
2261
|
}
|
|
2261
2262
|
function workersDevReadinessUrl(workerName, value) {
|
|
2262
2263
|
const subdomain = normalizeWorkersDevSubdomain(value);
|
|
@@ -2290,9 +2291,6 @@ function workersDevSubdomainResult(url) {
|
|
|
2290
2291
|
const workersDevSubdomain = extractWorkersDevSubdomain(url);
|
|
2291
2292
|
return workersDevSubdomain ? { workersDevSubdomain } : {};
|
|
2292
2293
|
}
|
|
2293
|
-
function defaultSleep(milliseconds) {
|
|
2294
|
-
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
2295
|
-
}
|
|
2296
2294
|
//#endregion
|
|
2297
2295
|
//#region src/metadata.ts
|
|
2298
2296
|
/**
|
|
@@ -2385,4 +2383,4 @@ function createCloudflareMetadataRunner(options) {
|
|
|
2385
2383
|
};
|
|
2386
2384
|
}
|
|
2387
2385
|
//#endregion
|
|
2388
|
-
export { CloudflareWorkerInspectionError, VOID_SECRET_DRAFT_SOURCE, activateCloudflareVersion, assertCloudflareBindingsPreserved, assertCloudflareDurableObjectLifecycleSupported, assertExistingCloudflareConfig, createCloudflareMetadataRunner, deployCloudflareVersionTraffic, deployCloudflareVersionTriggers, encodeCloudflareVersionMessage, executeCloudflareDeploy, extractWorkersDevSubdomain, normalizeWorkersDevSubdomain, parseCloudflareVersionMessage, parseRootWranglerConfig, readBindingFieldPresence, readCloudflareTriggerConfig, readCloudflareVersionMigrationTag, readCloudflareWorkerMigrationTag, readConfigEntryString, readExistingCloudflareWorker, readStoredResourceName, readWranglerTopLevelString, renderCloudflareStaticEntry, resolveD1MigrateTarget, stabilizeCloudflareVersionTriggers, stageCloudflareStaticAssets, writeCloudflareFrameworkDeployConfig, writeCloudflareStaticDeployConfig };
|
|
2386
|
+
export { CloudflareWorkerInspectionError, VOID_SECRET_DRAFT_SOURCE, activateCloudflareVersion, assertCloudflareBindingsPreserved, assertCloudflareDurableObjectLifecycleSupported, assertExistingCloudflareConfig, cloudflareCommandDiagnostic, createCloudflareMetadataRunner, deployCloudflareVersionTraffic, deployCloudflareVersionTriggers, encodeCloudflareVersionMessage, executeCloudflareDeploy, extractWorkersDevSubdomain, normalizeWorkersDevSubdomain, parseCloudflareVersionMessage, parseRootWranglerConfig, readBindingFieldPresence, readCloudflareTriggerConfig, readCloudflareVersionMigrationTag, readCloudflareWorkerMigrationTag, readConfigEntryString, readExistingCloudflareWorker, readStoredResourceName, readWranglerTopLevelString, renderCloudflareStaticEntry, resolveD1MigrateTarget, stabilizeCloudflareVersionTriggers, stageCloudflareStaticAssets, writeCloudflareFrameworkDeployConfig, writeCloudflareStaticDeployConfig };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@void/deploy-cloudflare",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Cloudflare deployment primitives for Void runtimes and deployment adapters.",
|
|
5
5
|
"homepage": "https://void.cloud",
|
|
6
6
|
"bugs": {
|
|
@@ -26,15 +26,15 @@
|
|
|
26
26
|
"access": "public"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@void/deploy-core": "0.
|
|
29
|
+
"@void/deploy-core": "1.0.3",
|
|
30
30
|
"jsonc-parser": "^3.3.1"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@cloudflare/workers-types": "^5.
|
|
34
|
-
"@types/node": "^26.6.
|
|
33
|
+
"@cloudflare/workers-types": "^5.20261014.1",
|
|
34
|
+
"@types/node": "^26.6.5",
|
|
35
35
|
"pathslash": "^0.1.0",
|
|
36
36
|
"typescript": "7.0.2",
|
|
37
|
-
"vite-plus": "1.
|
|
37
|
+
"vite-plus": "1.2.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "vp pack",
|