@treeseed/sdk 0.12.26 → 0.12.27
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 +1 -1
- package/dist/guarantees/index.js +15 -1
- package/dist/hosting/builtins.js +4 -4
- package/dist/hosting/contracts.d.ts +3 -3
- package/dist/hosting/graph.d.ts +4 -4
- package/dist/hosting/graph.js +17 -17
- package/dist/operations/agent-tools.d.ts +1 -1
- package/dist/operations/agent-tools.js +1 -1
- package/dist/operations/providers/default.js +2 -2
- package/dist/operations/services/config-runtime.d.ts +9 -9
- package/dist/operations/services/config-runtime.js +10 -10
- package/dist/operations/services/deploy.d.ts +8 -8
- package/dist/operations/services/deploy.js +151 -151
- package/dist/operations/services/github-automation.d.ts +6 -6
- package/dist/operations/services/github-automation.js +8 -8
- package/dist/operations/services/hub-launch.d.ts +1 -1
- package/dist/operations/services/hub-launch.js +1 -1
- package/dist/operations/services/package-adapters.d.ts +2 -2
- package/dist/operations/services/package-adapters.js +1 -1
- package/dist/operations/services/project-host-operations.d.ts +1 -1
- package/dist/operations/services/project-host-operations.js +1 -1
- package/dist/operations/services/project-platform.d.ts +8 -8
- package/dist/operations/services/project-platform.js +31 -31
- package/dist/operations/services/project-web-monitor.d.ts +1 -1
- package/dist/operations/services/project-web-monitor.js +4 -4
- package/dist/operations/services/railway-deploy.d.ts +4 -4
- package/dist/operations/services/railway-deploy.js +7 -7
- package/dist/operations/services/repository-save-orchestrator.js +3 -3
- package/dist/operations/services/template-secret-sync.d.ts +1 -1
- package/dist/operations/services/template-secret-sync.js +3 -3
- package/dist/reconcile/builtin-adapters.js +86 -40
- package/dist/reconcile/contracts.d.ts +1 -1
- package/dist/reconcile/engine.d.ts +4 -4
- package/dist/reconcile/engine.js +12 -12
- package/dist/reconcile/providers/release-private.d.ts +1 -1
- package/dist/scripts/tenant-deploy.js +9 -9
- package/dist/scripts/tenant-destroy.js +7 -7
- package/dist/scripts/tenant-workflow-action.js +4 -4
- package/dist/scripts/test-scaffold.js +2 -2
- package/dist/scripts/workspace-command-e2e.js +4 -4
- package/dist/sdk-types.d.ts +2 -2
- package/dist/treedx/generated/openapi-types.d.ts +11 -11
- package/dist/treedx/types.d.ts +15 -15
- package/dist/workflow/operations.d.ts +6 -4
- package/dist/workflow/operations.js +46 -23
- package/dist/workflow.d.ts +0 -9
- package/package.json +1 -1
|
@@ -501,16 +501,16 @@ function defaultStateFromConfig(deployConfig, target) {
|
|
|
501
501
|
FORM_GUARD_KV: {
|
|
502
502
|
name: environmentScopedIdentityName(identity, "form-guard", target),
|
|
503
503
|
binding: "FORM_GUARD_KV",
|
|
504
|
-
id: `
|
|
505
|
-
previewId: `
|
|
504
|
+
id: `plan-${suffix}-form-guard`,
|
|
505
|
+
previewId: `plan-${suffix}-form-guard-preview`
|
|
506
506
|
}
|
|
507
507
|
},
|
|
508
508
|
d1Databases: {
|
|
509
509
|
SITE_DATA_DB: {
|
|
510
510
|
databaseName: environmentScopedIdentityName(identity, "site-data", target),
|
|
511
511
|
binding: "SITE_DATA_DB",
|
|
512
|
-
databaseId: `
|
|
513
|
-
previewDatabaseId: `
|
|
512
|
+
databaseId: `plan-${suffix}-site-data`,
|
|
513
|
+
previewDatabaseId: `plan-${suffix}-site-data-preview`
|
|
514
514
|
}
|
|
515
515
|
},
|
|
516
516
|
queues: {},
|
|
@@ -1164,7 +1164,7 @@ function isPlaceholderResourceId(value) {
|
|
|
1164
1164
|
if (!value || typeof value !== "string") {
|
|
1165
1165
|
return true;
|
|
1166
1166
|
}
|
|
1167
|
-
return value.startsWith("local-") || value.startsWith("
|
|
1167
|
+
return value.startsWith("local-") || value.startsWith("plan-") || value.endsWith("-id") || value.endsWith("-preview-id");
|
|
1168
1168
|
}
|
|
1169
1169
|
function buildProvisioningSummary(deployConfig, state, target) {
|
|
1170
1170
|
const webCachePolicy = resolveTreeseedWebCachePolicy(deployConfig);
|
|
@@ -1533,7 +1533,7 @@ function buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, kin
|
|
|
1533
1533
|
);
|
|
1534
1534
|
return rules;
|
|
1535
1535
|
}
|
|
1536
|
-
function reconcileCloudflareCacheRulesForTarget(role, deployConfig, state, cacheTarget, env, {
|
|
1536
|
+
function reconcileCloudflareCacheRulesForTarget(role, deployConfig, state, cacheTarget, env, { planOnly = false } = {}) {
|
|
1537
1537
|
const roleKey = role === "web" ? "Web" : "Content";
|
|
1538
1538
|
if (!cacheTarget?.host) {
|
|
1539
1539
|
return { managed: false, skipped: true, reason: "missing_host" };
|
|
@@ -1545,8 +1545,8 @@ function reconcileCloudflareCacheRulesForTarget(role, deployConfig, state, cache
|
|
|
1545
1545
|
const desiredRules = buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, role);
|
|
1546
1546
|
state.webCache[role === "web" ? "webHost" : "contentHost"] = cacheTarget.host;
|
|
1547
1547
|
state.webCache[role === "web" ? "webZoneId" : "contentZoneId"] = zoneId;
|
|
1548
|
-
if (
|
|
1549
|
-
return { managed: true,
|
|
1548
|
+
if (planOnly) {
|
|
1549
|
+
return { managed: true, planOnly: true, zoneId, host: cacheTarget.host, rules: desiredRules };
|
|
1550
1550
|
}
|
|
1551
1551
|
const rulesets = listCloudflareZoneRulesets(zoneId, env);
|
|
1552
1552
|
const existing = rulesets.find((ruleset) => ruleset?.phase === "http_request_cache_settings") ?? null;
|
|
@@ -1574,7 +1574,7 @@ function reconcileCloudflareCacheRulesForTarget(role, deployConfig, state, cache
|
|
|
1574
1574
|
state.webCache[role === "web" ? "webRulesetId" : "contentRulesetId"] = rulesetId;
|
|
1575
1575
|
return { managed: true, zoneId, host: cacheTarget.host, rulesetId };
|
|
1576
1576
|
}
|
|
1577
|
-
function reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, target, {
|
|
1577
|
+
function reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, target, { planOnly = false, env: providedEnv } = {}) {
|
|
1578
1578
|
if (!shouldManageCloudflareWebCacheRules(deployConfig, target)) {
|
|
1579
1579
|
const webTarget2 = resolvePublicWebCacheTarget(deployConfig);
|
|
1580
1580
|
const contentTarget2 = resolvePublicContentCacheTarget(deployConfig);
|
|
@@ -1599,10 +1599,10 @@ function reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, targe
|
|
|
1599
1599
|
try {
|
|
1600
1600
|
const results = [];
|
|
1601
1601
|
if (webTarget?.host) {
|
|
1602
|
-
results.push(reconcileCloudflareCacheRulesForTarget("web", deployConfig, state, webTarget, env, {
|
|
1602
|
+
results.push(reconcileCloudflareCacheRulesForTarget("web", deployConfig, state, webTarget, env, { planOnly }));
|
|
1603
1603
|
}
|
|
1604
1604
|
if (contentTarget?.host) {
|
|
1605
|
-
results.push(reconcileCloudflareCacheRulesForTarget("content", deployConfig, state, contentTarget, env, {
|
|
1605
|
+
results.push(reconcileCloudflareCacheRulesForTarget("content", deployConfig, state, contentTarget, env, { planOnly }));
|
|
1606
1606
|
}
|
|
1607
1607
|
state.webCache.rulesManaged = true;
|
|
1608
1608
|
state.webCache.lastSyncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1694,12 +1694,12 @@ function purgeSourcePageCaches(tenantRoot, options = {}) {
|
|
|
1694
1694
|
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
1695
1695
|
const urls = resolveSourcePagePurgeUrls(deployConfig);
|
|
1696
1696
|
const env = resolveCloudflareCachePurgeEnv(options);
|
|
1697
|
-
if ((options.
|
|
1697
|
+
if ((options.planOnly ?? false) || urls.length === 0 || !env) {
|
|
1698
1698
|
recordCachePurgeResult(state.webCache.deployPurge, urls.map((url) => ({ count: url ? 1 : 0 })));
|
|
1699
1699
|
writeDeployState(tenantRoot, state, { target });
|
|
1700
1700
|
return {
|
|
1701
1701
|
skipped: true,
|
|
1702
|
-
reason: options.
|
|
1702
|
+
reason: options.planOnly ? "plan" : urls.length === 0 ? "no_urls" : "missing_cloudflare_token",
|
|
1703
1703
|
urls,
|
|
1704
1704
|
results: []
|
|
1705
1705
|
};
|
|
@@ -1722,12 +1722,12 @@ function purgePublishedContentCaches(tenantRoot, urls, options = {}) {
|
|
|
1722
1722
|
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1723
1723
|
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
1724
1724
|
const env = resolveCloudflareCachePurgeEnv(options);
|
|
1725
|
-
if ((options.
|
|
1725
|
+
if ((options.planOnly ?? false) || !urls?.length || !env) {
|
|
1726
1726
|
recordCachePurgeResult(state.webCache.contentPurge, (urls ?? []).map((url) => ({ count: url ? 1 : 0 })));
|
|
1727
1727
|
writeDeployState(tenantRoot, state, { target });
|
|
1728
1728
|
return {
|
|
1729
1729
|
skipped: true,
|
|
1730
|
-
reason: options.
|
|
1730
|
+
reason: options.planOnly ? "plan" : !urls?.length ? "no_urls" : "missing_cloudflare_token",
|
|
1731
1731
|
urls: urls ?? [],
|
|
1732
1732
|
results: []
|
|
1733
1733
|
};
|
|
@@ -1958,60 +1958,60 @@ function resolveExistingD1ByName(d1Databases, expectedName, current) {
|
|
|
1958
1958
|
function looksLikeMissingResource(output) {
|
|
1959
1959
|
return /not found|does not exist|could(?: not|n't) find|couldnt find|already deleted|deleted widget|access a deleted/i.test(output);
|
|
1960
1960
|
}
|
|
1961
|
-
function deleteKvNamespace(tenantRoot, namespaceId, { env,
|
|
1961
|
+
function deleteKvNamespace(tenantRoot, namespaceId, { env, planOnly, preview = false }) {
|
|
1962
1962
|
if (!namespaceId || isPlaceholderResourceId(namespaceId)) {
|
|
1963
1963
|
return { status: "missing", id: namespaceId };
|
|
1964
1964
|
}
|
|
1965
|
-
if (
|
|
1965
|
+
if (planOnly) {
|
|
1966
1966
|
return { status: "planned", id: namespaceId, preview };
|
|
1967
1967
|
}
|
|
1968
1968
|
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1969
1969
|
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/storage/kv/namespaces/${encodeURIComponent(namespaceId)}` : null;
|
|
1970
|
-
const deleted = deleteCloudflareApiResource(path, { env,
|
|
1970
|
+
const deleted = deleteCloudflareApiResource(path, { env, planOnly: false, name: namespaceId, type: "kv-namespace" });
|
|
1971
1971
|
return { status: deleted.status, id: namespaceId, preview };
|
|
1972
1972
|
}
|
|
1973
|
-
function deleteTurnstileWidget(sitekey, { env,
|
|
1973
|
+
function deleteTurnstileWidget(sitekey, { env, planOnly, name = null }) {
|
|
1974
1974
|
if (!sitekey || isPlaceholderResourceId(sitekey)) {
|
|
1975
1975
|
return { status: "missing", sitekey, name };
|
|
1976
1976
|
}
|
|
1977
|
-
if (
|
|
1977
|
+
if (planOnly) {
|
|
1978
1978
|
return { status: "planned", sitekey, name };
|
|
1979
1979
|
}
|
|
1980
1980
|
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1981
1981
|
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/challenges/widgets/${encodeURIComponent(sitekey)}` : null;
|
|
1982
1982
|
let deleted;
|
|
1983
1983
|
try {
|
|
1984
|
-
deleted = deleteCloudflareApiResource(path, { env,
|
|
1984
|
+
deleted = deleteCloudflareApiResource(path, { env, planOnly: false, name: name ?? sitekey, type: "turnstile-widget" });
|
|
1985
1985
|
} catch (error) {
|
|
1986
1986
|
const detail = error instanceof Error ? error.message : String(error);
|
|
1987
1987
|
throw new Error(`Cloudflare Turnstile widget deletion failed. Ensure the API token has Turnstile Sites Write permission: ${detail}`);
|
|
1988
1988
|
}
|
|
1989
1989
|
return { status: deleted.status, sitekey, name };
|
|
1990
1990
|
}
|
|
1991
|
-
function deleteD1Database(tenantRoot, databaseName, { env,
|
|
1991
|
+
function deleteD1Database(tenantRoot, databaseName, { env, planOnly }) {
|
|
1992
1992
|
if (!databaseName) {
|
|
1993
1993
|
return { status: "missing", name: databaseName };
|
|
1994
1994
|
}
|
|
1995
|
-
if (
|
|
1995
|
+
if (planOnly) {
|
|
1996
1996
|
return { status: "planned", name: databaseName };
|
|
1997
1997
|
}
|
|
1998
1998
|
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1999
1999
|
const database = accountId ? listD1Databases(tenantRoot, env).find((entry) => entry?.name === databaseName) : null;
|
|
2000
2000
|
const databaseId = database?.uuid ?? database?.id ?? null;
|
|
2001
2001
|
const path = accountId && databaseId ? `/accounts/${encodeURIComponent(accountId)}/d1/database/${encodeURIComponent(databaseId)}` : null;
|
|
2002
|
-
const deleted = deleteCloudflareApiResource(path, { env,
|
|
2002
|
+
const deleted = deleteCloudflareApiResource(path, { env, planOnly: false, name: databaseName, type: "d1-database" });
|
|
2003
2003
|
return { status: deleted.status, name: databaseName, id: databaseId };
|
|
2004
2004
|
}
|
|
2005
|
-
function deleteWorker(tenantRoot, workerName, { env,
|
|
2005
|
+
function deleteWorker(tenantRoot, workerName, { env, planOnly, force = false }) {
|
|
2006
2006
|
if (!workerName) {
|
|
2007
2007
|
return { status: "missing", name: workerName };
|
|
2008
2008
|
}
|
|
2009
|
-
if (
|
|
2009
|
+
if (planOnly) {
|
|
2010
2010
|
return { status: "planned", name: workerName };
|
|
2011
2011
|
}
|
|
2012
2012
|
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2013
2013
|
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/workers/services/${encodeURIComponent(workerName)}` : null;
|
|
2014
|
-
const deleted = deleteCloudflareApiResource(path, { env,
|
|
2014
|
+
const deleted = deleteCloudflareApiResource(path, { env, planOnly: false, name: workerName, type: "worker" });
|
|
2015
2015
|
return { status: deleted.status, name: workerName };
|
|
2016
2016
|
}
|
|
2017
2017
|
function resourceOperation(provider, type, name, status, extra = {}) {
|
|
@@ -2023,11 +2023,11 @@ function resourceOperation(provider, type, name, status, extra = {}) {
|
|
|
2023
2023
|
...extra
|
|
2024
2024
|
};
|
|
2025
2025
|
}
|
|
2026
|
-
function deleteCloudflareApiResource(path, { env,
|
|
2026
|
+
function deleteCloudflareApiResource(path, { env, planOnly, name, type }) {
|
|
2027
2027
|
if (!path) {
|
|
2028
2028
|
return resourceOperation("cloudflare", type, name, "missing");
|
|
2029
2029
|
}
|
|
2030
|
-
if (
|
|
2030
|
+
if (planOnly) {
|
|
2031
2031
|
return resourceOperation("cloudflare", type, name, "planned", { path });
|
|
2032
2032
|
}
|
|
2033
2033
|
const result = cloudflareApiRequest(path, { method: "DELETE", env, allowFailure: true });
|
|
@@ -2039,13 +2039,13 @@ function deleteCloudflareApiResource(path, { env, dryRun, name, type }) {
|
|
|
2039
2039
|
function formatCloudflareErrors(payload) {
|
|
2040
2040
|
return Array.isArray(payload?.errors) ? payload.errors.map((entry) => entry?.message ?? JSON.stringify(entry)).filter(Boolean).join("; ") : "";
|
|
2041
2041
|
}
|
|
2042
|
-
function deleteQueueByName(tenantRoot, queue, { env,
|
|
2042
|
+
function deleteQueueByName(tenantRoot, queue, { env, planOnly }) {
|
|
2043
2043
|
const name = queueName(queue) ?? queue?.name ?? null;
|
|
2044
2044
|
let id = queueId(queue);
|
|
2045
2045
|
if (!name) {
|
|
2046
2046
|
return resourceOperation("cloudflare", "queue", name, "missing");
|
|
2047
2047
|
}
|
|
2048
|
-
if (
|
|
2048
|
+
if (planOnly) {
|
|
2049
2049
|
return resourceOperation("cloudflare", "queue", name, "planned", { id });
|
|
2050
2050
|
}
|
|
2051
2051
|
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
@@ -2055,7 +2055,7 @@ function deleteQueueByName(tenantRoot, queue, { env, dryRun }) {
|
|
|
2055
2055
|
}
|
|
2056
2056
|
const path = id ? `/accounts/${encodeURIComponent(accountId)}/queues/${encodeURIComponent(id)}` : null;
|
|
2057
2057
|
if (path) {
|
|
2058
|
-
const deleted = deleteCloudflareApiResource(path, { env,
|
|
2058
|
+
const deleted = deleteCloudflareApiResource(path, { env, planOnly: false, name, type: "queue" });
|
|
2059
2059
|
if (deleted.status === "deleted" || deleted.status === "missing") {
|
|
2060
2060
|
return { ...deleted, id };
|
|
2061
2061
|
}
|
|
@@ -2065,20 +2065,20 @@ function deleteQueueByName(tenantRoot, queue, { env, dryRun }) {
|
|
|
2065
2065
|
}
|
|
2066
2066
|
throw new Error(`Failed to delete queue ${name}: CLOUDFLARE_ACCOUNT_ID is not configured.`);
|
|
2067
2067
|
}
|
|
2068
|
-
function deleteR2Bucket(tenantRoot, bucketName, { env,
|
|
2068
|
+
function deleteR2Bucket(tenantRoot, bucketName, { env, planOnly, deleteData }) {
|
|
2069
2069
|
if (!bucketName) {
|
|
2070
2070
|
return resourceOperation("cloudflare", "r2-bucket", bucketName, "missing");
|
|
2071
2071
|
}
|
|
2072
2072
|
if (!deleteData) {
|
|
2073
2073
|
return resourceOperation("cloudflare", "r2-bucket", bucketName, "skipped", { reason: "data_preserved" });
|
|
2074
2074
|
}
|
|
2075
|
-
if (
|
|
2075
|
+
if (planOnly) {
|
|
2076
2076
|
return resourceOperation("cloudflare", "r2-bucket", bucketName, "planned");
|
|
2077
2077
|
}
|
|
2078
2078
|
const drained = drainR2Bucket(bucketName, { env });
|
|
2079
2079
|
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2080
2080
|
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/r2/buckets/${encodeURIComponent(bucketName)}` : null;
|
|
2081
|
-
const deleted = deleteCloudflareApiResource(path, { env,
|
|
2081
|
+
const deleted = deleteCloudflareApiResource(path, { env, planOnly: false, name: bucketName, type: "r2-bucket" });
|
|
2082
2082
|
return resourceOperation("cloudflare", "r2-bucket", bucketName, deleted.status, drained);
|
|
2083
2083
|
}
|
|
2084
2084
|
function r2ObjectKey(entry) {
|
|
@@ -2257,11 +2257,11 @@ process.stdout.write(JSON.stringify({ deleted, missing, deferred, failed }));
|
|
|
2257
2257
|
objectsDeferred: Number(parsed.deferred) || 0
|
|
2258
2258
|
};
|
|
2259
2259
|
}
|
|
2260
|
-
function deleteD1DatabaseForDestroy(tenantRoot, databaseName, { env,
|
|
2260
|
+
function deleteD1DatabaseForDestroy(tenantRoot, databaseName, { env, planOnly, deleteData }) {
|
|
2261
2261
|
if (!deleteData) {
|
|
2262
2262
|
return resourceOperation("cloudflare", "d1-database", databaseName, "skipped", { reason: "data_preserved" });
|
|
2263
2263
|
}
|
|
2264
|
-
const result = deleteD1Database(tenantRoot, databaseName, { env,
|
|
2264
|
+
const result = deleteD1Database(tenantRoot, databaseName, { env, planOnly });
|
|
2265
2265
|
return resourceOperation("cloudflare", "d1-database", databaseName, result.status, result);
|
|
2266
2266
|
}
|
|
2267
2267
|
function pagesDomainName(domain) {
|
|
@@ -2311,12 +2311,12 @@ function listPagesCustomDomainsWithWrangler(tenantRoot, projectName, { env }) {
|
|
|
2311
2311
|
return [];
|
|
2312
2312
|
}
|
|
2313
2313
|
}
|
|
2314
|
-
function deletePagesCustomDomains(tenantRoot, projectName, knownNames, { env,
|
|
2314
|
+
function deletePagesCustomDomains(tenantRoot, projectName, knownNames, { env, planOnly, knownOnly = false }) {
|
|
2315
2315
|
if (!projectName) {
|
|
2316
2316
|
return [resourceOperation("cloudflare", "pages-custom-domain", projectName, "missing")];
|
|
2317
2317
|
}
|
|
2318
2318
|
const desiredNames = [...new Set((knownNames ?? []).filter(Boolean))];
|
|
2319
|
-
if (
|
|
2319
|
+
if (planOnly) {
|
|
2320
2320
|
return desiredNames.length > 0 ? desiredNames.map((name) => resourceOperation("cloudflare", "pages-custom-domain", name, "planned", { projectName, knownOnly })) : [resourceOperation("cloudflare", "pages-custom-domain", projectName, knownOnly ? "skipped" : "planned", { reason: knownOnly ? "no_target_scoped_domain" : "project_delete_prerequisite" })];
|
|
2321
2321
|
}
|
|
2322
2322
|
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
@@ -2331,7 +2331,7 @@ function deletePagesCustomDomains(tenantRoot, projectName, knownNames, { env, dr
|
|
|
2331
2331
|
}
|
|
2332
2332
|
return domainNames.map((name) => deleteCloudflareApiResource(
|
|
2333
2333
|
`/accounts/${encodeURIComponent(accountId)}/pages/projects/${encodeURIComponent(projectName)}/domains/${encodeURIComponent(name)}`,
|
|
2334
|
-
{ env,
|
|
2334
|
+
{ env, planOnly: false, name, type: "pages-custom-domain" }
|
|
2335
2335
|
));
|
|
2336
2336
|
}
|
|
2337
2337
|
function normalizePagesDeploymentId(deployment) {
|
|
@@ -2391,11 +2391,11 @@ function listPagesDeployments(tenantRoot, projectName, { env, environment = "all
|
|
|
2391
2391
|
}
|
|
2392
2392
|
return listPagesDeploymentsWithApi(projectName, { env, environment });
|
|
2393
2393
|
}
|
|
2394
|
-
function deletePagesDeployments(tenantRoot, projectName, { env,
|
|
2394
|
+
function deletePagesDeployments(tenantRoot, projectName, { env, planOnly, environment = "all" }) {
|
|
2395
2395
|
if (!projectName) {
|
|
2396
2396
|
return resourceOperation("cloudflare", "pages-deployments", projectName, "missing");
|
|
2397
2397
|
}
|
|
2398
|
-
if (
|
|
2398
|
+
if (planOnly) {
|
|
2399
2399
|
return resourceOperation("cloudflare", "pages-deployments", projectName, "planned", { reason: "project_delete_prerequisite", environment });
|
|
2400
2400
|
}
|
|
2401
2401
|
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
@@ -2451,14 +2451,14 @@ function deletePagesDeployments(tenantRoot, projectName, { env, dryRun, environm
|
|
|
2451
2451
|
total
|
|
2452
2452
|
});
|
|
2453
2453
|
}
|
|
2454
|
-
function deletePagesProject(projectName, { env,
|
|
2454
|
+
function deletePagesProject(projectName, { env, planOnly }) {
|
|
2455
2455
|
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2456
2456
|
if (!projectName || !accountId) {
|
|
2457
2457
|
return resourceOperation("cloudflare", "pages-project", projectName, "missing");
|
|
2458
2458
|
}
|
|
2459
2459
|
return deleteCloudflareApiResource(
|
|
2460
2460
|
`/accounts/${encodeURIComponent(accountId)}/pages/projects/${encodeURIComponent(projectName)}`,
|
|
2461
|
-
{ env,
|
|
2461
|
+
{ env, planOnly, name: projectName, type: "pages-project" }
|
|
2462
2462
|
);
|
|
2463
2463
|
}
|
|
2464
2464
|
function listDnsRecordsForName(zoneId, name, env) {
|
|
@@ -2468,11 +2468,11 @@ function listDnsRecordsForName(zoneId, name, env) {
|
|
|
2468
2468
|
const result = cloudflareApiRequest(`/zones/${encodeURIComponent(zoneId)}/dns_records?name=${encodeURIComponent(name)}&per_page=100`, { env, allowFailure: true });
|
|
2469
2469
|
return Array.isArray(result?.result) ? result.result : [];
|
|
2470
2470
|
}
|
|
2471
|
-
function deleteDnsRecordsForName(deployConfig, name, { env,
|
|
2471
|
+
function deleteDnsRecordsForName(deployConfig, name, { env, planOnly }) {
|
|
2472
2472
|
if (!name) {
|
|
2473
2473
|
return [resourceOperation("cloudflare", "dns-record", name, "missing")];
|
|
2474
2474
|
}
|
|
2475
|
-
if (
|
|
2475
|
+
if (planOnly) {
|
|
2476
2476
|
return [resourceOperation("cloudflare", "dns-record", name, "planned")];
|
|
2477
2477
|
}
|
|
2478
2478
|
const zoneId = resolveCloudflareZoneIdForHost(deployConfig, name, env);
|
|
@@ -2487,11 +2487,11 @@ function deleteDnsRecordsForName(deployConfig, name, { env, dryRun }) {
|
|
|
2487
2487
|
const recordName = record?.name ?? name;
|
|
2488
2488
|
return deleteCloudflareApiResource(
|
|
2489
2489
|
`/zones/${encodeURIComponent(zoneId)}/dns_records/${encodeURIComponent(record.id)}`,
|
|
2490
|
-
{ env,
|
|
2490
|
+
{ env, planOnly: false, name: recordName, type: "dns-record" }
|
|
2491
2491
|
);
|
|
2492
2492
|
});
|
|
2493
2493
|
}
|
|
2494
|
-
function deleteTreeseedCacheRules(deployConfig, state, { env,
|
|
2494
|
+
function deleteTreeseedCacheRules(deployConfig, state, { env, planOnly }) {
|
|
2495
2495
|
const targets = [
|
|
2496
2496
|
{ role: "web", zoneId: state.webCache?.webZoneId, host: state.webCache?.webHost },
|
|
2497
2497
|
{ role: "content", zoneId: state.webCache?.contentZoneId, host: state.webCache?.contentHost }
|
|
@@ -2504,7 +2504,7 @@ function deleteTreeseedCacheRules(deployConfig, state, { env, dryRun }) {
|
|
|
2504
2504
|
if (!zoneId) {
|
|
2505
2505
|
return resourceOperation("cloudflare", "cache-rules", target.host, "blocked", { reason: "zone_unresolved" });
|
|
2506
2506
|
}
|
|
2507
|
-
if (
|
|
2507
|
+
if (planOnly) {
|
|
2508
2508
|
return resourceOperation("cloudflare", "cache-rules", target.host, "planned", { zoneId });
|
|
2509
2509
|
}
|
|
2510
2510
|
const rulesets = listCloudflareZoneRulesets(zoneId, env);
|
|
@@ -2569,7 +2569,7 @@ function configuredRailwayDestroyTargets(tenantRoot, deployConfig, scope) {
|
|
|
2569
2569
|
}
|
|
2570
2570
|
return services;
|
|
2571
2571
|
}
|
|
2572
|
-
async function destroyRailwayResources(tenantRoot, deployConfig, target, {
|
|
2572
|
+
async function destroyRailwayResources(tenantRoot, deployConfig, target, { planOnly = false, deleteData = false, env = process.env } = {}) {
|
|
2573
2573
|
const scope = target.kind === "persistent" ? target.scope : target.branchName;
|
|
2574
2574
|
const services = configuredRailwayDestroyTargets(tenantRoot, deployConfig, scope);
|
|
2575
2575
|
if (services.length === 0) {
|
|
@@ -2599,7 +2599,7 @@ async function destroyRailwayResources(tenantRoot, deployConfig, target, { dryRu
|
|
|
2599
2599
|
for (const service of serviceTargets) {
|
|
2600
2600
|
const railwayService = project.services.find((entry) => entry.name === service.serviceName || entry.id === service.serviceName) ?? null;
|
|
2601
2601
|
const shouldDeleteData = service.dataStore ? deleteData : true;
|
|
2602
|
-
operations.push(resourceOperation("railway", service.dataStore ? "postgres-service" : "service", service.serviceName, railwayService ?
|
|
2602
|
+
operations.push(resourceOperation("railway", service.dataStore ? "postgres-service" : "service", service.serviceName, railwayService ? planOnly ? "planned" : "planned" : "missing", {
|
|
2603
2603
|
projectId: project.id,
|
|
2604
2604
|
serviceId: railwayService?.id ?? null,
|
|
2605
2605
|
...service.dataStore && !shouldDeleteData ? { status: "skipped", reason: "data_preserved" } : {}
|
|
@@ -2615,7 +2615,7 @@ async function destroyRailwayResources(tenantRoot, deployConfig, target, { dryRu
|
|
|
2615
2615
|
env
|
|
2616
2616
|
});
|
|
2617
2617
|
for (const variableName of Object.keys(variables).sort()) {
|
|
2618
|
-
operations.push(resourceOperation("railway", "variable", `${service.serviceName}:${variableName}`,
|
|
2618
|
+
operations.push(resourceOperation("railway", "variable", `${service.serviceName}:${variableName}`, planOnly ? "planned" : "deleted", {
|
|
2619
2619
|
projectId: project.id,
|
|
2620
2620
|
serviceId: railwayService.id,
|
|
2621
2621
|
environmentId: environment.id,
|
|
@@ -2625,7 +2625,7 @@ async function destroyRailwayResources(tenantRoot, deployConfig, target, { dryRu
|
|
|
2625
2625
|
}
|
|
2626
2626
|
const instance = await getRailwayServiceInstance({ serviceId: railwayService.id, environmentId: environment.id, env });
|
|
2627
2627
|
if (instance.cronSchedule) {
|
|
2628
|
-
operations.push(resourceOperation("railway", "schedule", `${service.serviceName}:${instance.cronSchedule}`,
|
|
2628
|
+
operations.push(resourceOperation("railway", "schedule", `${service.serviceName}:${instance.cronSchedule}`, planOnly ? "planned" : "deleted", {
|
|
2629
2629
|
projectId: project.id,
|
|
2630
2630
|
serviceId: railwayService.id,
|
|
2631
2631
|
environmentId: environment.id,
|
|
@@ -2635,7 +2635,7 @@ async function destroyRailwayResources(tenantRoot, deployConfig, target, { dryRu
|
|
|
2635
2635
|
if (!service.dataStore && service.domain) {
|
|
2636
2636
|
const domains = await listRailwayCustomDomains({ projectId: project.id, environmentId: environment.id, serviceId: railwayService.id, env });
|
|
2637
2637
|
for (const domain of domains.filter((entry) => entry.domain === service.domain)) {
|
|
2638
|
-
if (
|
|
2638
|
+
if (planOnly) {
|
|
2639
2639
|
operations.push(resourceOperation("railway", "custom-domain", domain.domain, "planned", { id: domain.id }));
|
|
2640
2640
|
} else {
|
|
2641
2641
|
const result = await deleteRailwayCustomDomain({ domainId: domain.id, env });
|
|
@@ -2651,7 +2651,7 @@ async function destroyRailwayResources(tenantRoot, deployConfig, target, { dryRu
|
|
|
2651
2651
|
if (!matchingInstance) {
|
|
2652
2652
|
continue;
|
|
2653
2653
|
}
|
|
2654
|
-
if (
|
|
2654
|
+
if (planOnly) {
|
|
2655
2655
|
operations.push(resourceOperation("railway", "volume", volume.name, "planned", { id: volume.id, projectId: project.id }));
|
|
2656
2656
|
} else {
|
|
2657
2657
|
const result = await deleteRailwayVolume({ volumeId: volume.id, env });
|
|
@@ -2661,7 +2661,7 @@ async function destroyRailwayResources(tenantRoot, deployConfig, target, { dryRu
|
|
|
2661
2661
|
}
|
|
2662
2662
|
const shouldDeleteProject = shouldDeleteRailwayProjectAfterEnvironmentDestroy(project, scope, deleteData, environment?.id ?? null);
|
|
2663
2663
|
if (scope === "prod" && deleteData || shouldDeleteProject) {
|
|
2664
|
-
if (
|
|
2664
|
+
if (planOnly) {
|
|
2665
2665
|
operations.push(resourceOperation("railway", "project", project.name, "planned", {
|
|
2666
2666
|
id: project.id,
|
|
2667
2667
|
reason: scope === "prod" ? "prod_delete_data_cleanup" : "no_managed_persistent_environments"
|
|
@@ -2670,7 +2670,7 @@ async function destroyRailwayResources(tenantRoot, deployConfig, target, { dryRu
|
|
|
2670
2670
|
throw new Error("Railway project deletion is reconciler-owned. Use trsd reconcile destroy or live acceptance cleanup for project-scoped deletion.");
|
|
2671
2671
|
}
|
|
2672
2672
|
} else if (environment) {
|
|
2673
|
-
if (
|
|
2673
|
+
if (planOnly) {
|
|
2674
2674
|
operations.push(resourceOperation("railway", "environment", environment.name, "planned", { id: environment.id, projectId: project.id }));
|
|
2675
2675
|
} else {
|
|
2676
2676
|
const result = await deleteRailwayEnvironment({ environmentId: environment.id, env });
|
|
@@ -2689,12 +2689,12 @@ function shouldDeleteRailwayProjectAfterEnvironmentDestroy(project, scope, delet
|
|
|
2689
2689
|
const remainingManagedEnvironments = (project?.environments ?? []).filter((environment) => environment?.id !== deletedEnvironmentId).filter((environment) => environment?.name !== targetEnvironmentName).filter((environment) => managedPersistentNames.has(environment?.name));
|
|
2690
2690
|
return remainingManagedEnvironments.length === 0;
|
|
2691
2691
|
}
|
|
2692
|
-
function killPidFromFile(filePath, {
|
|
2692
|
+
function killPidFromFile(filePath, { planOnly }) {
|
|
2693
2693
|
const pid = Number.parseInt(readFileSync(filePath, "utf8").trim(), 10);
|
|
2694
2694
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
2695
2695
|
return resourceOperation("local", "dev-process", filePath, "missing");
|
|
2696
2696
|
}
|
|
2697
|
-
if (
|
|
2697
|
+
if (planOnly) {
|
|
2698
2698
|
return resourceOperation("local", "dev-process", String(pid), "planned", { pidFile: filePath });
|
|
2699
2699
|
}
|
|
2700
2700
|
try {
|
|
@@ -2747,7 +2747,7 @@ function removeDockerResource(kind, id, name) {
|
|
|
2747
2747
|
reason: result.stderr?.trim() || result.stdout?.trim() || "docker_remove_failed"
|
|
2748
2748
|
});
|
|
2749
2749
|
}
|
|
2750
|
-
function dockerLocalRuntimeResourceOperations({
|
|
2750
|
+
function dockerLocalRuntimeResourceOperations({ planOnly = false } = {}) {
|
|
2751
2751
|
if (!dockerAvailable()) {
|
|
2752
2752
|
return [resourceOperation("local", "docker-cleanup", "docker", "skipped", { reason: "docker_unavailable" })];
|
|
2753
2753
|
}
|
|
@@ -2769,7 +2769,7 @@ function dockerLocalRuntimeResourceOperations({ dryRun = false } = {}) {
|
|
|
2769
2769
|
return id && name ? { id, name } : null;
|
|
2770
2770
|
}
|
|
2771
2771
|
).filter((entry) => !["bridge", "host", "none"].includes(entry.name));
|
|
2772
|
-
if (
|
|
2772
|
+
if (planOnly) {
|
|
2773
2773
|
return [
|
|
2774
2774
|
...containers.map((entry) => resourceOperation("local", "docker-container", entry.name, "planned", { id: entry.id })),
|
|
2775
2775
|
...volumes.map((entry) => resourceOperation("local", "docker-volume", entry.name, "planned", { id: entry.id })),
|
|
@@ -2792,13 +2792,13 @@ function dockerLocalRuntimeResourceOperations({ dryRun = false } = {}) {
|
|
|
2792
2792
|
}
|
|
2793
2793
|
return operations;
|
|
2794
2794
|
}
|
|
2795
|
-
function destroyLocalRuntimeResources(tenantRoot, {
|
|
2795
|
+
function destroyLocalRuntimeResources(tenantRoot, { planOnly = false, deleteData = false } = {}) {
|
|
2796
2796
|
const operations = [];
|
|
2797
2797
|
const pidDir = resolve(tenantRoot, ".treeseed/dev-pids");
|
|
2798
2798
|
if (existsSync(pidDir)) {
|
|
2799
2799
|
for (const entry of readdirSync(pidDir)) {
|
|
2800
2800
|
if (entry.endsWith(".pid")) {
|
|
2801
|
-
operations.push(killPidFromFile(resolve(pidDir, entry), {
|
|
2801
|
+
operations.push(killPidFromFile(resolve(pidDir, entry), { planOnly }));
|
|
2802
2802
|
}
|
|
2803
2803
|
}
|
|
2804
2804
|
} else {
|
|
@@ -2816,14 +2816,14 @@ function destroyLocalRuntimeResources(tenantRoot, { dryRun = false, deleteData =
|
|
|
2816
2816
|
operations.push(resourceOperation("local", "data-path", relativePath, "missing"));
|
|
2817
2817
|
continue;
|
|
2818
2818
|
}
|
|
2819
|
-
if (
|
|
2819
|
+
if (planOnly) {
|
|
2820
2820
|
operations.push(resourceOperation("local", "data-path", relativePath, "planned"));
|
|
2821
2821
|
continue;
|
|
2822
2822
|
}
|
|
2823
2823
|
rmSync(absolutePath, { recursive: true, force: true });
|
|
2824
2824
|
operations.push(resourceOperation("local", "data-path", relativePath, "deleted"));
|
|
2825
2825
|
}
|
|
2826
|
-
operations.push(...dockerLocalRuntimeResourceOperations({
|
|
2826
|
+
operations.push(...dockerLocalRuntimeResourceOperations({ planOnly }));
|
|
2827
2827
|
} else {
|
|
2828
2828
|
operations.push(resourceOperation("local", "data-path", ".treeseed/generated/environments/local", "skipped", { reason: "data_preserved" }));
|
|
2829
2829
|
}
|
|
@@ -2883,12 +2883,12 @@ function cloudflareNameCandidates(entry) {
|
|
|
2883
2883
|
function cloudflareEntryMatchesTreeSeed(entry, tokens) {
|
|
2884
2884
|
return cloudflareNameCandidates(entry).some((candidate) => matchesTreeSeedSweep(candidate, tokens));
|
|
2885
2885
|
}
|
|
2886
|
-
function deleteDnsRecord(zoneId, record, { env,
|
|
2886
|
+
function deleteDnsRecord(zoneId, record, { env, planOnly }) {
|
|
2887
2887
|
const name = record?.name ?? record?.content ?? record?.id ?? null;
|
|
2888
2888
|
if (!zoneId || !record?.id) {
|
|
2889
2889
|
return resourceOperation("cloudflare", "dns-record", name, "missing", { zoneId });
|
|
2890
2890
|
}
|
|
2891
|
-
if (
|
|
2891
|
+
if (planOnly) {
|
|
2892
2892
|
return resourceOperation("cloudflare", "dns-record", name, "planned", {
|
|
2893
2893
|
zoneId,
|
|
2894
2894
|
id: record.id,
|
|
@@ -2898,44 +2898,44 @@ function deleteDnsRecord(zoneId, record, { env, dryRun }) {
|
|
|
2898
2898
|
}
|
|
2899
2899
|
return deleteCloudflareApiResource(
|
|
2900
2900
|
`/zones/${encodeURIComponent(zoneId)}/dns_records/${encodeURIComponent(record.id)}`,
|
|
2901
|
-
{ env,
|
|
2901
|
+
{ env, planOnly: false, name, type: "dns-record" }
|
|
2902
2902
|
);
|
|
2903
2903
|
}
|
|
2904
|
-
function sweepTreeSeedCloudflareResources(tenantRoot, deployConfig, state, { env,
|
|
2904
|
+
function sweepTreeSeedCloudflareResources(tenantRoot, deployConfig, state, { env, planOnly, deleteData }) {
|
|
2905
2905
|
const tokens = treeSeedSweepTokens(deployConfig, state);
|
|
2906
2906
|
const operations = [];
|
|
2907
2907
|
const pagesProjects = listPagesProjects(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens));
|
|
2908
2908
|
for (const project of pagesProjects) {
|
|
2909
2909
|
const projectName = project?.name ?? project?.id ?? null;
|
|
2910
|
-
operations.push(...deletePagesCustomDomains(tenantRoot, projectName, [], { env,
|
|
2911
|
-
operations.push(deletePagesDeployments(tenantRoot, projectName, { env,
|
|
2912
|
-
operations.push(deletePagesProject(projectName, { env,
|
|
2910
|
+
operations.push(...deletePagesCustomDomains(tenantRoot, projectName, [], { env, planOnly, knownOnly: false }));
|
|
2911
|
+
operations.push(deletePagesDeployments(tenantRoot, projectName, { env, planOnly, environment: "all" }));
|
|
2912
|
+
operations.push(deletePagesProject(projectName, { env, planOnly }));
|
|
2913
2913
|
}
|
|
2914
2914
|
for (const worker of listWorkers(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2915
2915
|
const name = worker?.id ?? worker?.name ?? worker?.script ?? null;
|
|
2916
|
-
const deleted = deleteWorker(tenantRoot, name, { env,
|
|
2916
|
+
const deleted = deleteWorker(tenantRoot, name, { env, planOnly, force: true });
|
|
2917
2917
|
operations.push(resourceOperation("cloudflare", "worker", name, deleted.status, { ...deleted, sweep: true }));
|
|
2918
2918
|
}
|
|
2919
2919
|
for (const namespace of listKvNamespaces(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2920
|
-
const deleted = deleteKvNamespace(tenantRoot, namespace.id, { env,
|
|
2920
|
+
const deleted = deleteKvNamespace(tenantRoot, namespace.id, { env, planOnly });
|
|
2921
2921
|
operations.push(resourceOperation("cloudflare", "kv-namespace", namespace.title ?? namespace.id, deleted.status, { ...deleted, sweep: true }));
|
|
2922
2922
|
}
|
|
2923
2923
|
for (const queue of listQueues(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2924
|
-
operations.push({ ...deleteQueueByName(tenantRoot, queue, { env,
|
|
2924
|
+
operations.push({ ...deleteQueueByName(tenantRoot, queue, { env, planOnly }), sweep: true });
|
|
2925
2925
|
}
|
|
2926
2926
|
for (const database of listD1Databases(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2927
2927
|
const name = database?.name ?? database?.uuid ?? database?.id ?? null;
|
|
2928
|
-
const deleted = deleteData ? deleteD1Database(tenantRoot, name, { env,
|
|
2928
|
+
const deleted = deleteData ? deleteD1Database(tenantRoot, name, { env, planOnly }) : null;
|
|
2929
2929
|
operations.push(resourceOperation("cloudflare", "d1-database", name, deleteData ? deleted?.status : "skipped", {
|
|
2930
2930
|
...deleteData ? deleted : { reason: "data_preserved" },
|
|
2931
2931
|
sweep: true
|
|
2932
2932
|
}));
|
|
2933
2933
|
}
|
|
2934
2934
|
for (const bucket of listR2Buckets(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2935
|
-
operations.push({ ...deleteR2Bucket(tenantRoot, bucket.name, { env,
|
|
2935
|
+
operations.push({ ...deleteR2Bucket(tenantRoot, bucket.name, { env, planOnly, deleteData }), sweep: true });
|
|
2936
2936
|
}
|
|
2937
2937
|
for (const widget of listTurnstileWidgets(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2938
|
-
const deleted = deleteTurnstileWidget(widget.sitekey, { env,
|
|
2938
|
+
const deleted = deleteTurnstileWidget(widget.sitekey, { env, planOnly, name: widget.name });
|
|
2939
2939
|
operations.push(resourceOperation("cloudflare", "turnstile-widget", widget.name ?? widget.sitekey, deleted.status, { ...deleted, sweep: true }));
|
|
2940
2940
|
}
|
|
2941
2941
|
for (const zone of listDnsZones(env)) {
|
|
@@ -2947,7 +2947,7 @@ function sweepTreeSeedCloudflareResources(tenantRoot, deployConfig, state, { env
|
|
|
2947
2947
|
if (!cloudflareEntryMatchesTreeSeed(record, tokens)) {
|
|
2948
2948
|
continue;
|
|
2949
2949
|
}
|
|
2950
|
-
operations.push({ ...deleteDnsRecord(zoneId, record, { env,
|
|
2950
|
+
operations.push({ ...deleteDnsRecord(zoneId, record, { env, planOnly }), zoneName: zone?.name ?? null, sweep: true });
|
|
2951
2951
|
}
|
|
2952
2952
|
}
|
|
2953
2953
|
return operations.length > 0 ? operations : [resourceOperation("cloudflare", "treeseed-sweep", "cloudflare", "missing", { reason: "no_matching_resources" })];
|
|
@@ -3036,7 +3036,7 @@ function localDockerDestroyVerification() {
|
|
|
3036
3036
|
totalRemaining
|
|
3037
3037
|
};
|
|
3038
3038
|
}
|
|
3039
|
-
async function sweepTreeSeedRailwayResources(deployConfig, state, { env,
|
|
3039
|
+
async function sweepTreeSeedRailwayResources(deployConfig, state, { env, planOnly }) {
|
|
3040
3040
|
if (!resolveRailwayApiToken(env)) {
|
|
3041
3041
|
return [resourceOperation("railway", "treeseed-sweep", "railway", "blocked", { reason: "missing_railway_api_token" })];
|
|
3042
3042
|
}
|
|
@@ -3048,7 +3048,7 @@ async function sweepTreeSeedRailwayResources(deployConfig, state, { env, dryRun
|
|
|
3048
3048
|
if (project.deletedAt || !matchesTreeSeedSweep(project.name, tokens)) {
|
|
3049
3049
|
continue;
|
|
3050
3050
|
}
|
|
3051
|
-
if (
|
|
3051
|
+
if (planOnly) {
|
|
3052
3052
|
operations.push(resourceOperation("railway", "project", project.name, "planned", {
|
|
3053
3053
|
id: project.id,
|
|
3054
3054
|
workspaceId: workspace.id,
|
|
@@ -3068,17 +3068,17 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
3068
3068
|
const env = {
|
|
3069
3069
|
CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig)
|
|
3070
3070
|
};
|
|
3071
|
-
const
|
|
3071
|
+
const planOnly = options.planOnly ?? false;
|
|
3072
3072
|
const deleteData = options.deleteData === true;
|
|
3073
3073
|
const force = options.force ?? false;
|
|
3074
3074
|
const sweepTreeseed = options.sweepTreeseed === true;
|
|
3075
3075
|
const destroysSharedWebSurface = target.kind === "persistent" && target.scope === "prod" && deleteData;
|
|
3076
|
-
const kvNamespaces =
|
|
3077
|
-
const d1Databases =
|
|
3078
|
-
const queues =
|
|
3079
|
-
const buckets =
|
|
3080
|
-
const pagesProjects =
|
|
3081
|
-
const turnstileWidgets =
|
|
3076
|
+
const kvNamespaces = planOnly ? [] : listKvNamespaces(tenantRoot, env);
|
|
3077
|
+
const d1Databases = planOnly ? [] : listD1Databases(tenantRoot, env);
|
|
3078
|
+
const queues = planOnly ? [] : listQueues(tenantRoot, env);
|
|
3079
|
+
const buckets = planOnly ? [] : listR2Buckets(tenantRoot, env);
|
|
3080
|
+
const pagesProjects = planOnly ? [] : listPagesProjects(tenantRoot, env);
|
|
3081
|
+
const turnstileWidgets = planOnly ? [] : listTurnstileWidgets(tenantRoot, env);
|
|
3082
3082
|
state.kvNamespaces.FORM_GUARD_KV.id = resolveExistingKvIdByName(
|
|
3083
3083
|
kvNamespaces,
|
|
3084
3084
|
state.kvNamespaces.FORM_GUARD_KV.name,
|
|
@@ -3099,16 +3099,16 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
3099
3099
|
state.turnstileWidgets.formGuard = resolveExistingTurnstileWidget(turnstileWidgets, state.turnstileWidgets?.formGuard);
|
|
3100
3100
|
const pagesProject = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
|
|
3101
3101
|
const bucket = buckets.find((entry) => entry?.name === state.content?.bucketName);
|
|
3102
|
-
const workerResult = deleteWorker(tenantRoot, state.workerName, { env,
|
|
3102
|
+
const workerResult = deleteWorker(tenantRoot, state.workerName, { env, planOnly, force });
|
|
3103
3103
|
const turnstileWidget = deleteTurnstileWidget(state.turnstileWidgets?.formGuard?.sitekey, {
|
|
3104
3104
|
env,
|
|
3105
|
-
|
|
3105
|
+
planOnly,
|
|
3106
3106
|
name: state.turnstileWidgets?.formGuard?.name
|
|
3107
3107
|
});
|
|
3108
|
-
const formGuard = deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.id, { env,
|
|
3109
|
-
const formGuardPreview = state.kvNamespaces.FORM_GUARD_KV.previewId && state.kvNamespaces.FORM_GUARD_KV.previewId !== state.kvNamespaces.FORM_GUARD_KV.id ? deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.previewId, { env,
|
|
3110
|
-
const session = state.kvNamespaces.SESSION?.id ? deleteKvNamespace(tenantRoot, state.kvNamespaces.SESSION.id, { env,
|
|
3111
|
-
const sessionPreview = state.kvNamespaces.SESSION?.previewId && state.kvNamespaces.SESSION.previewId !== state.kvNamespaces.SESSION.id ? deleteKvNamespace(tenantRoot, state.kvNamespaces.SESSION.previewId, { env,
|
|
3108
|
+
const formGuard = deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.id, { env, planOnly });
|
|
3109
|
+
const formGuardPreview = state.kvNamespaces.FORM_GUARD_KV.previewId && state.kvNamespaces.FORM_GUARD_KV.previewId !== state.kvNamespaces.FORM_GUARD_KV.id ? deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.previewId, { env, planOnly, preview: true }) : null;
|
|
3110
|
+
const session = state.kvNamespaces.SESSION?.id ? deleteKvNamespace(tenantRoot, state.kvNamespaces.SESSION.id, { env, planOnly }) : null;
|
|
3111
|
+
const sessionPreview = state.kvNamespaces.SESSION?.previewId && state.kvNamespaces.SESSION.previewId !== state.kvNamespaces.SESSION.id ? deleteKvNamespace(tenantRoot, state.kvNamespaces.SESSION.previewId, { env, planOnly, preview: true }) : null;
|
|
3112
3112
|
const knownKvIds = new Set([
|
|
3113
3113
|
state.kvNamespaces.FORM_GUARD_KV.id,
|
|
3114
3114
|
state.kvNamespaces.FORM_GUARD_KV.previewId,
|
|
@@ -3116,16 +3116,16 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
3116
3116
|
state.kvNamespaces.SESSION?.previewId
|
|
3117
3117
|
].filter(Boolean));
|
|
3118
3118
|
const legacyKvPrefix = state.identity?.deploymentKey ?? state.pages?.projectName ?? "";
|
|
3119
|
-
const legacyKvNamespaces =
|
|
3119
|
+
const legacyKvNamespaces = planOnly ? [] : kvNamespaces.filter((namespace) => {
|
|
3120
3120
|
const title = typeof namespace?.title === "string" ? namespace.title : "";
|
|
3121
3121
|
const id = typeof namespace?.id === "string" ? namespace.id : "";
|
|
3122
3122
|
return title && id && !knownKvIds.has(id) && legacyKvPrefix && title.includes(legacyKvPrefix) && title.includes(target.scope);
|
|
3123
3123
|
}).map((namespace) => {
|
|
3124
|
-
const result = deleteKvNamespace(tenantRoot, namespace.id, { env,
|
|
3124
|
+
const result = deleteKvNamespace(tenantRoot, namespace.id, { env, planOnly: false });
|
|
3125
3125
|
return resourceOperation("cloudflare", "kv-namespace", namespace.title, result.status, { ...result, legacy: true });
|
|
3126
3126
|
});
|
|
3127
|
-
const database = deleteD1DatabaseForDestroy(tenantRoot, state.d1Databases.SITE_DATA_DB.databaseName, { env,
|
|
3128
|
-
const r2Bucket = bucket ||
|
|
3127
|
+
const database = deleteD1DatabaseForDestroy(tenantRoot, state.d1Databases.SITE_DATA_DB.databaseName, { env, planOnly, deleteData });
|
|
3128
|
+
const r2Bucket = bucket || planOnly ? deleteR2Bucket(tenantRoot, state.content?.bucketName, { env, planOnly, deleteData }) : resourceOperation("cloudflare", "r2-bucket", state.content?.bucketName, "missing");
|
|
3129
3129
|
const pageDnsNames = [
|
|
3130
3130
|
state.pages?.customDomain,
|
|
3131
3131
|
deployConfig.surfaces?.web?.environments?.[target.scope]?.domain,
|
|
@@ -3135,22 +3135,22 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
3135
3135
|
deployConfig.services?.api?.environments?.[target.scope]?.domain,
|
|
3136
3136
|
deployConfig.surfaces?.api?.environments?.[target.scope]?.domain
|
|
3137
3137
|
].filter(Boolean);
|
|
3138
|
-
const dnsRecords = [.../* @__PURE__ */ new Set([...pageDnsNames, ...apiDnsNames])].flatMap((name) => deleteDnsRecordsForName(deployConfig, name, { env,
|
|
3139
|
-
const cacheRules = deleteTreeseedCacheRules(deployConfig, state, { env,
|
|
3140
|
-
const pageCustomDomains = pagesProject ||
|
|
3141
|
-
const pageDeployments = pagesProject ||
|
|
3138
|
+
const dnsRecords = [.../* @__PURE__ */ new Set([...pageDnsNames, ...apiDnsNames])].flatMap((name) => deleteDnsRecordsForName(deployConfig, name, { env, planOnly }));
|
|
3139
|
+
const cacheRules = deleteTreeseedCacheRules(deployConfig, state, { env, planOnly });
|
|
3140
|
+
const pageCustomDomains = pagesProject || planOnly ? deletePagesCustomDomains(tenantRoot, state.pages?.projectName, pageDnsNames, { env, planOnly, knownOnly: !destroysSharedWebSurface }) : [resourceOperation("cloudflare", "pages-custom-domain", state.pages?.projectName, "missing")];
|
|
3141
|
+
const pageDeployments = pagesProject || planOnly ? deletePagesDeployments(tenantRoot, state.pages?.projectName, {
|
|
3142
3142
|
env,
|
|
3143
|
-
|
|
3143
|
+
planOnly,
|
|
3144
3144
|
environment: destroysSharedWebSurface ? "all" : "preview"
|
|
3145
3145
|
}) : resourceOperation("cloudflare", "pages-deployments", state.pages?.projectName, "missing");
|
|
3146
|
-
const pages = destroysSharedWebSurface && (pagesProject ||
|
|
3146
|
+
const pages = destroysSharedWebSurface && (pagesProject || planOnly) ? deletePagesProject(state.pages?.projectName, { env, planOnly }) : resourceOperation("cloudflare", "pages-project", state.pages?.projectName, "skipped", {
|
|
3147
3147
|
reason: target.scope === "prod" ? "delete_data_required" : "shared_web_surface"
|
|
3148
3148
|
});
|
|
3149
|
-
const local = target.kind === "persistent" && target.scope === "local" ? destroyLocalRuntimeResources(tenantRoot, {
|
|
3150
|
-
const railway = await destroyRailwayResources(tenantRoot, deployConfig, target, {
|
|
3149
|
+
const local = target.kind === "persistent" && target.scope === "local" ? destroyLocalRuntimeResources(tenantRoot, { planOnly, deleteData }) : { operations: [] };
|
|
3150
|
+
const railway = await destroyRailwayResources(tenantRoot, deployConfig, target, { planOnly, deleteData, env: process.env });
|
|
3151
3151
|
const sweep = sweepTreeseed ? {
|
|
3152
|
-
cloudflare: sweepTreeSeedCloudflareResources(tenantRoot, deployConfig, state, { env,
|
|
3153
|
-
railway: await sweepTreeSeedRailwayResources(deployConfig, state, { env: process.env,
|
|
3152
|
+
cloudflare: sweepTreeSeedCloudflareResources(tenantRoot, deployConfig, state, { env, planOnly, deleteData }),
|
|
3153
|
+
railway: await sweepTreeSeedRailwayResources(deployConfig, state, { env: process.env, planOnly })
|
|
3154
3154
|
} : { cloudflare: [], railway: [] };
|
|
3155
3155
|
const operations = {
|
|
3156
3156
|
cloudflare: [
|
|
@@ -3176,7 +3176,7 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
3176
3176
|
],
|
|
3177
3177
|
local: local.operations
|
|
3178
3178
|
};
|
|
3179
|
-
const verification =
|
|
3179
|
+
const verification = planOnly ? null : {
|
|
3180
3180
|
cloudflare: cloudflareDestroyVerification(tenantRoot, deployConfig, state, env),
|
|
3181
3181
|
...target.kind === "persistent" && target.scope === "local" ? { localDocker: localDockerDestroyVerification() } : {}
|
|
3182
3182
|
};
|
|
@@ -3197,15 +3197,15 @@ function destroyCloudflareResources(tenantRoot, options = {}) {
|
|
|
3197
3197
|
const env = {
|
|
3198
3198
|
CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig)
|
|
3199
3199
|
};
|
|
3200
|
-
const
|
|
3200
|
+
const planOnly = options.planOnly ?? false;
|
|
3201
3201
|
const deleteData = options.deleteData === true;
|
|
3202
3202
|
const force = options.force ?? false;
|
|
3203
|
-
const kvNamespaces =
|
|
3204
|
-
const d1Databases =
|
|
3203
|
+
const kvNamespaces = planOnly ? [] : listKvNamespaces(tenantRoot, env);
|
|
3204
|
+
const d1Databases = planOnly ? [] : listD1Databases(tenantRoot, env);
|
|
3205
3205
|
const queues = listQueues(tenantRoot, env);
|
|
3206
|
-
const buckets =
|
|
3207
|
-
const pagesProjects =
|
|
3208
|
-
const turnstileWidgets =
|
|
3206
|
+
const buckets = planOnly ? [] : listR2Buckets(tenantRoot, env);
|
|
3207
|
+
const pagesProjects = planOnly ? [] : listPagesProjects(tenantRoot, env);
|
|
3208
|
+
const turnstileWidgets = planOnly ? [] : listTurnstileWidgets(tenantRoot, env);
|
|
3209
3209
|
state.kvNamespaces.FORM_GUARD_KV.id = resolveExistingKvIdByName(
|
|
3210
3210
|
kvNamespaces,
|
|
3211
3211
|
state.kvNamespaces.FORM_GUARD_KV.name,
|
|
@@ -3219,16 +3219,16 @@ function destroyCloudflareResources(tenantRoot, options = {}) {
|
|
|
3219
3219
|
state.turnstileWidgets.formGuard = resolveExistingTurnstileWidget(turnstileWidgets, state.turnstileWidgets?.formGuard);
|
|
3220
3220
|
const bucket = buckets.find((entry) => entry?.name === state.content?.bucketName);
|
|
3221
3221
|
const pagesProject = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
|
|
3222
|
-
const worker = deleteWorker(tenantRoot, state.workerName, { env,
|
|
3222
|
+
const worker = deleteWorker(tenantRoot, state.workerName, { env, planOnly, force });
|
|
3223
3223
|
const turnstileWidget = deleteTurnstileWidget(state.turnstileWidgets?.formGuard?.sitekey, {
|
|
3224
3224
|
env,
|
|
3225
|
-
|
|
3225
|
+
planOnly,
|
|
3226
3226
|
name: state.turnstileWidgets?.formGuard?.name
|
|
3227
3227
|
});
|
|
3228
|
-
const formGuard = deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.id, { env,
|
|
3229
|
-
const database = deleteD1DatabaseForDestroy(tenantRoot, state.d1Databases.SITE_DATA_DB.databaseName, { env,
|
|
3230
|
-
const r2Bucket = bucket ||
|
|
3231
|
-
const pages = pagesProject ||
|
|
3228
|
+
const formGuard = deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.id, { env, planOnly });
|
|
3229
|
+
const database = deleteD1DatabaseForDestroy(tenantRoot, state.d1Databases.SITE_DATA_DB.databaseName, { env, planOnly, deleteData });
|
|
3230
|
+
const r2Bucket = bucket || planOnly ? deleteR2Bucket(tenantRoot, state.content?.bucketName, { env, planOnly, deleteData }) : resourceOperation("cloudflare", "r2-bucket", state.content?.bucketName, "missing");
|
|
3231
|
+
const pages = pagesProject || planOnly ? deletePagesProject(state.pages?.projectName, { env, planOnly }) : resourceOperation("cloudflare", "pages-project", state.pages?.projectName, "missing");
|
|
3232
3232
|
const operations = {
|
|
3233
3233
|
worker,
|
|
3234
3234
|
turnstileWidget,
|
|
@@ -3269,12 +3269,12 @@ function provisionCloudflareResources(tenantRoot, options = {}) {
|
|
|
3269
3269
|
const env = {
|
|
3270
3270
|
CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig)
|
|
3271
3271
|
};
|
|
3272
|
-
const
|
|
3273
|
-
const kvNamespaces =
|
|
3274
|
-
const d1Databases =
|
|
3275
|
-
const queues =
|
|
3276
|
-
const buckets =
|
|
3277
|
-
const pagesProjects =
|
|
3272
|
+
const planOnly = options.planOnly ?? false;
|
|
3273
|
+
const kvNamespaces = planOnly ? [] : listKvNamespaces(tenantRoot, env);
|
|
3274
|
+
const d1Databases = planOnly ? [] : listD1Databases(tenantRoot, env);
|
|
3275
|
+
const queues = planOnly ? [] : listQueues(tenantRoot, env);
|
|
3276
|
+
const buckets = planOnly ? [] : listR2Buckets(tenantRoot, env);
|
|
3277
|
+
const pagesProjects = planOnly ? [] : listPagesProjects(tenantRoot, env);
|
|
3278
3278
|
const ensureKv = (binding) => {
|
|
3279
3279
|
const current = state.kvNamespaces[binding];
|
|
3280
3280
|
if (current?.id && !isPlaceholderResourceId(current.id)) {
|
|
@@ -3287,9 +3287,9 @@ function provisionCloudflareResources(tenantRoot, options = {}) {
|
|
|
3287
3287
|
state.kvNamespaces[binding].previewId = existing.id;
|
|
3288
3288
|
return;
|
|
3289
3289
|
}
|
|
3290
|
-
if (
|
|
3291
|
-
state.kvNamespaces[binding].id = `
|
|
3292
|
-
state.kvNamespaces[binding].previewId = `
|
|
3290
|
+
if (planOnly) {
|
|
3291
|
+
state.kvNamespaces[binding].id = `plan-${current.name}`;
|
|
3292
|
+
state.kvNamespaces[binding].previewId = `plan-${current.name}-preview`;
|
|
3293
3293
|
return;
|
|
3294
3294
|
}
|
|
3295
3295
|
runWrangler(["kv", "namespace", "create", current.name], { cwd: tenantRoot, capture: true, env });
|
|
@@ -3312,9 +3312,9 @@ function provisionCloudflareResources(tenantRoot, options = {}) {
|
|
|
3312
3312
|
current.previewDatabaseId = existing.previewDatabaseUuid ?? existing.uuid;
|
|
3313
3313
|
return;
|
|
3314
3314
|
}
|
|
3315
|
-
if (
|
|
3316
|
-
current.databaseId = `
|
|
3317
|
-
current.previewDatabaseId = `
|
|
3315
|
+
if (planOnly) {
|
|
3316
|
+
current.databaseId = `plan-${current.databaseName}`;
|
|
3317
|
+
current.previewDatabaseId = `plan-${current.databaseName}-preview`;
|
|
3318
3318
|
return;
|
|
3319
3319
|
}
|
|
3320
3320
|
runWrangler(["d1", "create", current.databaseName], {
|
|
@@ -3340,7 +3340,7 @@ function provisionCloudflareResources(tenantRoot, options = {}) {
|
|
|
3340
3340
|
if (exists) {
|
|
3341
3341
|
return;
|
|
3342
3342
|
}
|
|
3343
|
-
if (
|
|
3343
|
+
if (planOnly) {
|
|
3344
3344
|
return;
|
|
3345
3345
|
}
|
|
3346
3346
|
try {
|
|
@@ -3370,7 +3370,7 @@ function provisionCloudflareResources(tenantRoot, options = {}) {
|
|
|
3370
3370
|
ensurePagesProjectCompatibility(env.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "", current.projectName, env, exists, { state, target });
|
|
3371
3371
|
return;
|
|
3372
3372
|
}
|
|
3373
|
-
if (
|
|
3373
|
+
if (planOnly) {
|
|
3374
3374
|
current.url = `https://${current.projectName}.pages.dev`;
|
|
3375
3375
|
return;
|
|
3376
3376
|
}
|
|
@@ -3393,7 +3393,7 @@ function provisionCloudflareResources(tenantRoot, options = {}) {
|
|
|
3393
3393
|
ensureD1();
|
|
3394
3394
|
ensureR2Bucket();
|
|
3395
3395
|
ensurePagesProject();
|
|
3396
|
-
reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, target, {
|
|
3396
|
+
reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, target, { planOnly });
|
|
3397
3397
|
state.readiness.configured = true;
|
|
3398
3398
|
state.readiness.provisioned = hasProvisionedCloudflareResources(state);
|
|
3399
3399
|
state.readiness.deployable = state.readiness.provisioned === true;
|
|
@@ -3424,13 +3424,13 @@ function syncCloudflareSecrets(tenantRoot, options = {}) {
|
|
|
3424
3424
|
...extraSecrets
|
|
3425
3425
|
};
|
|
3426
3426
|
const synced = [];
|
|
3427
|
-
const
|
|
3427
|
+
const planOnly = options.planOnly ?? false;
|
|
3428
3428
|
for (const [key, value] of Object.entries(secrets)) {
|
|
3429
3429
|
if (!value) {
|
|
3430
3430
|
continue;
|
|
3431
3431
|
}
|
|
3432
3432
|
synced.push(key);
|
|
3433
|
-
if (
|
|
3433
|
+
if (planOnly) {
|
|
3434
3434
|
continue;
|
|
3435
3435
|
}
|
|
3436
3436
|
const command = state.pages?.projectName && target.kind === "persistent" ? [resolveWranglerBin(), "pages", "secret", "put", key, "--project-name", state.pages.projectName] : [resolveWranglerBin(), "secret", "put", key, "--config", resolveGeneratedWranglerPath(tenantRoot, { target })];
|
|
@@ -3461,14 +3461,14 @@ function verifyProvisionedCloudflareResources(tenantRoot, options = {}) {
|
|
|
3461
3461
|
const env = {
|
|
3462
3462
|
CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig)
|
|
3463
3463
|
};
|
|
3464
|
-
const
|
|
3465
|
-
const kvNamespaces =
|
|
3466
|
-
const d1Databases =
|
|
3467
|
-
const queues =
|
|
3468
|
-
const buckets =
|
|
3469
|
-
const pagesProjects =
|
|
3464
|
+
const planOnly = options.planOnly ?? false;
|
|
3465
|
+
const kvNamespaces = planOnly ? [] : listKvNamespaces(tenantRoot, env);
|
|
3466
|
+
const d1Databases = planOnly ? [] : listD1Databases(tenantRoot, env);
|
|
3467
|
+
const queues = planOnly ? [] : listQueues(tenantRoot, env);
|
|
3468
|
+
const buckets = planOnly ? [] : listR2Buckets(tenantRoot, env);
|
|
3469
|
+
const pagesProjects = planOnly ? [] : listPagesProjects(tenantRoot, env);
|
|
3470
3470
|
const livePages = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
|
|
3471
|
-
const pagesProject =
|
|
3471
|
+
const pagesProject = planOnly || !env.CLOUDFLARE_ACCOUNT_ID || !state.pages?.projectName ? livePages : cloudflareApiRequest(
|
|
3472
3472
|
`/accounts/${encodeURIComponent(env.CLOUDFLARE_ACCOUNT_ID)}/pages/projects/${encodeURIComponent(state.pages.projectName)}`,
|
|
3473
3473
|
{ env, allowFailure: true }
|
|
3474
3474
|
)?.result ?? livePages;
|
|
@@ -3486,7 +3486,7 @@ function verifyProvisionedCloudflareResources(tenantRoot, options = {}) {
|
|
|
3486
3486
|
pagesR2Binding: !state.content?.r2Binding || !pagesBindings.r2_buckets?.[state.content.r2Binding] || pageBindingConfigured("r2_buckets", state.content.r2Binding, pagesBindings.r2_buckets[state.content.r2Binding]),
|
|
3487
3487
|
webCache: !shouldManageCloudflareWebCacheRules(deployConfig, target) || state.webCache?.rulesManaged === true
|
|
3488
3488
|
};
|
|
3489
|
-
const ok =
|
|
3489
|
+
const ok = planOnly ? true : Object.values(checks).every(Boolean);
|
|
3490
3490
|
state.readiness.configured = true;
|
|
3491
3491
|
state.readiness.provisioned = ok;
|
|
3492
3492
|
state.readiness.deployable = ok;
|
|
@@ -3503,9 +3503,9 @@ function verifyProvisionedCloudflareResources(tenantRoot, options = {}) {
|
|
|
3503
3503
|
state.pages.url = target.kind === "persistent" && target.scope === "staging" ? `https://${state.pages.stagingBranch ?? "staging"}.${livePages.subdomain}` : `https://${livePages.subdomain}`;
|
|
3504
3504
|
}
|
|
3505
3505
|
}
|
|
3506
|
-
if (!
|
|
3506
|
+
if (!planOnly) {
|
|
3507
3507
|
try {
|
|
3508
|
-
reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, target, {
|
|
3508
|
+
reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, target, { planOnly: false });
|
|
3509
3509
|
} catch (error) {
|
|
3510
3510
|
state.webCache.rulesManaged = false;
|
|
3511
3511
|
state.webCache.lastError = error instanceof Error ? error.message : String(error);
|
|
@@ -3523,8 +3523,8 @@ function verifyProvisionedCloudflareResources(tenantRoot, options = {}) {
|
|
|
3523
3523
|
function runRemoteD1Migrations(tenantRoot, options = {}) {
|
|
3524
3524
|
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
3525
3525
|
const { wranglerPath, deployConfig, state } = ensureGeneratedWranglerConfig(tenantRoot, { target });
|
|
3526
|
-
if (options.
|
|
3527
|
-
return { databaseName: state.d1Databases.SITE_DATA_DB.databaseName,
|
|
3526
|
+
if (options.planOnly) {
|
|
3527
|
+
return { databaseName: state.d1Databases.SITE_DATA_DB.databaseName, planOnly: true };
|
|
3528
3528
|
}
|
|
3529
3529
|
const args = ["d1", "migrations", "apply", state.d1Databases.SITE_DATA_DB.databaseName, "--remote", "--config", wranglerPath];
|
|
3530
3530
|
const env = { CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig) };
|
|
@@ -3538,7 +3538,7 @@ function runRemoteD1Migrations(tenantRoot, options = {}) {
|
|
|
3538
3538
|
allowFailure: true
|
|
3539
3539
|
});
|
|
3540
3540
|
if (result.status === 0) {
|
|
3541
|
-
return { databaseName: state.d1Databases.SITE_DATA_DB.databaseName,
|
|
3541
|
+
return { databaseName: state.d1Databases.SITE_DATA_DB.databaseName, planOnly: false };
|
|
3542
3542
|
}
|
|
3543
3543
|
lastOutput = [result.stderr?.trim(), result.stdout?.trim()].filter(Boolean).join("\n");
|
|
3544
3544
|
if (!isTransient(lastOutput) || attempt === 3) {
|