@package-broker/cli 2.0.0-rc.1 → 2.0.0-rc.2
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/.tsbuildinfo +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +29 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/cloudflare/api.d.ts +5 -2
- package/dist/commands/cloudflare/api.d.ts.map +1 -1
- package/dist/commands/cloudflare/api.js +35 -13
- package/dist/commands/cloudflare/api.js.map +1 -1
- package/dist/commands/cloudflare/deploy.d.ts +32 -2
- package/dist/commands/cloudflare/deploy.d.ts.map +1 -1
- package/dist/commands/cloudflare/deploy.js +398 -76
- package/dist/commands/cloudflare/deploy.js.map +1 -1
- package/dist/commands/cloudflare/inventory.d.ts +16 -2
- package/dist/commands/cloudflare/inventory.d.ts.map +1 -1
- package/dist/commands/cloudflare/inventory.js +61 -5
- package/dist/commands/cloudflare/inventory.js.map +1 -1
- package/dist/commands/cloudflare/providers-file.d.ts +17 -0
- package/dist/commands/cloudflare/providers-file.d.ts.map +1 -0
- package/dist/commands/cloudflare/providers-file.js +155 -0
- package/dist/commands/cloudflare/providers-file.js.map +1 -0
- package/dist/commands/cloudflare/routes.d.ts +3 -0
- package/dist/commands/cloudflare/routes.d.ts.map +1 -0
- package/dist/commands/cloudflare/routes.js +26 -0
- package/dist/commands/cloudflare/routes.js.map +1 -0
- package/dist/commands/cloudflare/setup.d.ts +2 -0
- package/dist/commands/cloudflare/setup.d.ts.map +1 -1
- package/dist/commands/cloudflare/setup.js +8 -8
- package/dist/commands/cloudflare/setup.js.map +1 -1
- package/dist/commands/cloudflare/smoke.d.ts.map +1 -1
- package/dist/commands/cloudflare/smoke.js +10 -24
- package/dist/commands/cloudflare/smoke.js.map +1 -1
- package/dist/commands/cloudflare/teardown.d.ts +6 -3
- package/dist/commands/cloudflare/teardown.d.ts.map +1 -1
- package/dist/commands/cloudflare/teardown.js +122 -44
- package/dist/commands/cloudflare/teardown.js.map +1 -1
- package/dist/commands/cloudflare/zone.d.ts +3 -0
- package/dist/commands/cloudflare/zone.d.ts.map +1 -0
- package/dist/commands/cloudflare/zone.js +73 -0
- package/dist/commands/cloudflare/zone.js.map +1 -0
- package/dist/commands/cloudflare.js +1 -1
- package/dist/commands/cloudflare.js.map +1 -1
- package/dist/manifest.d.ts +2 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +12 -0
- package/dist/manifest.js.map +1 -1
- package/dist/prebuilt-expected.json +3 -3
- package/package.json +7 -6
|
@@ -4,13 +4,24 @@ import { dirname, extname, isAbsolute, relative, resolve, sep } from 'node:path'
|
|
|
4
4
|
import { createOtlpExporter } from '@package-broker/core/platform/otlp';
|
|
5
5
|
import { parseUiManifest } from '@package-broker/core/platform/ui-manifest';
|
|
6
6
|
import { TRACE_FIELDS, validatePlatformPorts, } from '@package-broker/core/platform/port-config';
|
|
7
|
-
import { PLUGIN_BINDABLE_PORTS } from '@package-broker/core/plugin';
|
|
7
|
+
import { PLUGIN_BINDABLE_PORTS, providerSecretName, } from '@package-broker/core/plugin';
|
|
8
|
+
import { parseRuntimeProviders, planProviders, } from '@package-broker/plugin-sso-oidc/definitions';
|
|
9
|
+
import { snapshotIdentityDescriptor } from '@package-broker/core/kernel/registry';
|
|
10
|
+
import { orgContext } from '@package-broker/core/db/port';
|
|
11
|
+
import { isValidTrustAnchor } from '@package-broker/core/identity/port';
|
|
12
|
+
import { createOrganizationBootstrap } from '@package-broker/db-sqlite/repositories/organization';
|
|
13
|
+
import { readProviderBinding } from '@package-broker/db-sqlite/repositories/provider-binding';
|
|
14
|
+
import { DbError } from '@package-broker/core/db/errors';
|
|
15
|
+
import { createD1HttpExecutor, isLostResponse } from '../../cloudflare/d1-http-executor.js';
|
|
8
16
|
import { assertPrebuiltConfiguration, materializePrebuilt, prebuiltEnvelope, removePrebuilt, } from '../../prebuilt.js';
|
|
9
17
|
import { builtRouteProblems, loadBuiltMigrations } from '../built-manifest.js';
|
|
18
|
+
import { validateProvidersDocument } from './providers-file.js';
|
|
10
19
|
import { normalizeQueueConsumer } from './queue-consumer.js';
|
|
11
20
|
import { assertMigrationsApplied } from './migrations-check.js';
|
|
12
21
|
import { d1BindingId, workerSettingsDifferences } from './worker-settings.js';
|
|
13
22
|
import { CloudflareApi, CloudflareError, cloudflareCredentials, commandEnvelope, failCommand, object, wasNotSent, } from './api.js';
|
|
23
|
+
import { resolveZone } from './zone.js';
|
|
24
|
+
import { routeConflict } from './routes.js';
|
|
14
25
|
import { InventoryStore, InventoryTransaction, digest, installIdentity, recordDeploymentHistory, DeploymentHistoryCapacityError, safeBuildInfo, } from './inventory.js';
|
|
15
26
|
export function tracesMetadata(traces) {
|
|
16
27
|
if (traces === undefined)
|
|
@@ -21,11 +32,24 @@ export function tracesMetadata(traces) {
|
|
|
21
32
|
}
|
|
22
33
|
const COMPATIBILITY = { compatibility_date: '2026-10-01', compatibility_flags: ['nodejs_compat'] };
|
|
23
34
|
const ASSET_CONFIG = { run_worker_first: true, html_handling: 'none', not_found_handling: 'none' };
|
|
35
|
+
const PREVIEW_BINDING_FIELDS = {
|
|
36
|
+
d1: 'id',
|
|
37
|
+
r2_bucket: 'bucket_name',
|
|
38
|
+
queue: 'queue_name',
|
|
39
|
+
analytics_engine: 'dataset',
|
|
40
|
+
plain_text: 'text',
|
|
41
|
+
secret_text: null,
|
|
42
|
+
assets: null,
|
|
43
|
+
};
|
|
24
44
|
const uuid = (v) => typeof v === 'string' &&
|
|
25
45
|
/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(v);
|
|
26
46
|
const safeId = (v) => typeof v === 'string' && /^[a-zA-Z0-9_-]{1,128}$/.test(v);
|
|
27
|
-
const
|
|
28
|
-
|
|
47
|
+
const inputMessages = new WeakMap();
|
|
48
|
+
const invalid = (message) => {
|
|
49
|
+
const error = new CloudflareError('invalid_input', 2, false, 'deploy');
|
|
50
|
+
if (message)
|
|
51
|
+
inputMessages.set(error, message);
|
|
52
|
+
throw error;
|
|
29
53
|
};
|
|
30
54
|
const conflict = (field, ...fields) => {
|
|
31
55
|
throw new CloudflareError('configuration_conflict', 8, false, 'deploy', null, null, null, null, [
|
|
@@ -36,12 +60,161 @@ const conflict = (field, ...fields) => {
|
|
|
36
60
|
const unknown = () => {
|
|
37
61
|
throw new CloudflareError('outcome_unknown', 6, false, 'deploy');
|
|
38
62
|
};
|
|
63
|
+
export function assertProvidersAccepted(option, identity) {
|
|
64
|
+
if (option?.kind !== 'set' || identity?.runtimeProviders)
|
|
65
|
+
return;
|
|
66
|
+
const message = identity
|
|
67
|
+
? 'Cannot set providers: artifact runtimeProviders is false'
|
|
68
|
+
: 'Cannot set providers on a legacy artifact without identity metadata';
|
|
69
|
+
try {
|
|
70
|
+
invalid(message);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw Object.assign(error, { message });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function desiredProviders(option, identity, live) {
|
|
77
|
+
if (option?.kind === 'set') {
|
|
78
|
+
assertProvidersAccepted(option, identity);
|
|
79
|
+
return option.value;
|
|
80
|
+
}
|
|
81
|
+
if (option?.kind === 'remove')
|
|
82
|
+
return null;
|
|
83
|
+
const found = (live?.bindings ?? [])
|
|
84
|
+
.map(object)
|
|
85
|
+
.filter((b) => b.name === 'PB_IDP_PROVIDERS');
|
|
86
|
+
if (!found.length)
|
|
87
|
+
return null;
|
|
88
|
+
try {
|
|
89
|
+
const binding = found[0];
|
|
90
|
+
if (found.length !== 1 || binding.type !== 'plain_text' || typeof binding.text !== 'string')
|
|
91
|
+
throw new Error();
|
|
92
|
+
validateProvidersDocument(binding.text, 'deploy', true);
|
|
93
|
+
return binding.text;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return invalid('live PB_IDP_PROVIDERS is not valid; pass --providers <file> or --providers none');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function planned({ id, kind, anchor }) {
|
|
100
|
+
const value = anchor.slice(anchor.indexOf(':') + 1);
|
|
101
|
+
const slash = value.indexOf('/');
|
|
102
|
+
return kind === 'oidc'
|
|
103
|
+
? { id, kind, issuer: value }
|
|
104
|
+
: { id, kind, teamDomain: value.slice(0, slash), audience: value.slice(slash + 1) };
|
|
105
|
+
}
|
|
106
|
+
function identityDigest(descriptor, providers) {
|
|
107
|
+
return descriptor === null && providers === null
|
|
108
|
+
? null
|
|
109
|
+
: digest(canonical({ descriptor, providers }));
|
|
110
|
+
}
|
|
111
|
+
function providerPlan(descriptor, providersText) {
|
|
112
|
+
let runtime = parseRuntimeProviders(undefined);
|
|
113
|
+
if (descriptor.runtimeProviders)
|
|
114
|
+
try {
|
|
115
|
+
runtime = parseRuntimeProviders(providersText ?? undefined);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
runtime = { ...runtime, documentProblem: 'unreadable' };
|
|
119
|
+
}
|
|
120
|
+
const plan = planProviders(descriptor.compiled.map(planned), runtime);
|
|
121
|
+
return { runtime, plan };
|
|
122
|
+
}
|
|
123
|
+
function effectiveAnchors(descriptor, providersText) {
|
|
124
|
+
if (descriptor === null)
|
|
125
|
+
return [];
|
|
126
|
+
return providerPlan(descriptor, providersText).plan.effective.map((entry) => ({
|
|
127
|
+
id: entry.id,
|
|
128
|
+
anchor: entry.kind === 'oidc'
|
|
129
|
+
? `oidc:${entry.issuer}`
|
|
130
|
+
: `access:${entry.teamDomain}/${entry.audience}`,
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
const MISSING_SCHEMA = /\bno such table: (?:main\.)?organization\b/;
|
|
134
|
+
async function anchorPreflight(connection, databaseId, anchors, missingSchemaSkips) {
|
|
135
|
+
let changed;
|
|
136
|
+
try {
|
|
137
|
+
if (typeof databaseId !== 'string' || databaseId === '')
|
|
138
|
+
throw new TypeError('no database');
|
|
139
|
+
const executor = createD1HttpExecutor({ ...connection, databaseId });
|
|
140
|
+
let organization;
|
|
141
|
+
try {
|
|
142
|
+
organization = await createOrganizationBootstrap(executor).findSingleton();
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
if (missingSchemaSkips && missingSchema(error))
|
|
146
|
+
return 'skipped';
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
if (organization === null)
|
|
150
|
+
return 'checked';
|
|
151
|
+
const ctx = orgContext(organization.id);
|
|
152
|
+
for (const { id, anchor } of anchors) {
|
|
153
|
+
const binding = await readProviderBinding(executor, ctx, id);
|
|
154
|
+
if (binding === null)
|
|
155
|
+
continue;
|
|
156
|
+
if (typeof binding.anchor !== 'string' || !isValidTrustAnchor(binding.anchor))
|
|
157
|
+
throw new TypeError('malformed provider binding');
|
|
158
|
+
if (binding.anchor !== anchor) {
|
|
159
|
+
changed = id;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
throw new CloudflareError('provider_failure', 1, false, 'deploy');
|
|
166
|
+
}
|
|
167
|
+
if (changed === undefined)
|
|
168
|
+
return 'checked';
|
|
169
|
+
const message = `Provider ${changed} is bound to a different trust anchor; run pb admin rebind-provider before deploying this definition`;
|
|
170
|
+
const error = new CloudflareError('provider_anchor_changed', 2, false, 'deploy');
|
|
171
|
+
inputMessages.set(error, message);
|
|
172
|
+
throw Object.assign(error, { message });
|
|
173
|
+
}
|
|
174
|
+
function missingSchema(error) {
|
|
175
|
+
return (error instanceof DbError &&
|
|
176
|
+
error.kind === 'unknown' &&
|
|
177
|
+
!isLostResponse(error) &&
|
|
178
|
+
MISSING_SCHEMA.test(error.message));
|
|
179
|
+
}
|
|
180
|
+
export function dryRunIdentity(descriptor, providersText, identityHash, skippedChecks = []) {
|
|
181
|
+
if (descriptor === null)
|
|
182
|
+
return null;
|
|
183
|
+
const { runtime, plan } = providerPlan(descriptor, providersText);
|
|
184
|
+
return {
|
|
185
|
+
providers: plan.effective.map((entry) => ({
|
|
186
|
+
id: entry.id,
|
|
187
|
+
type: entry.kind,
|
|
188
|
+
source: runtime.accepted.includes(entry) ? 'runtime' : 'compiled',
|
|
189
|
+
})),
|
|
190
|
+
overrides: [...plan.overridden],
|
|
191
|
+
displaced: [
|
|
192
|
+
...plan.displaced.map(({ compiled: id, by }) => ({ id, reason: 'displaced', by })),
|
|
193
|
+
...plan.suppressed.map((id) => ({ id, reason: 'suppressed', by: null })),
|
|
194
|
+
...plan.overflow.map((id) => ({ id, reason: 'overflow', by: null })),
|
|
195
|
+
],
|
|
196
|
+
runtime_providers_applied: descriptor.runtimeProviders,
|
|
197
|
+
runtime_document_problem: runtime.documentProblem,
|
|
198
|
+
providers_sha256: providersText === null ? null : digest(providersText),
|
|
199
|
+
identity_sha256: identityHash,
|
|
200
|
+
skipped_checks: skippedChecks,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function frozenProviders(attempt) {
|
|
204
|
+
const value = attempt.providers_text ?? null;
|
|
205
|
+
if ((value === null ? null : digest(value)) !== (attempt.providers_sha256 ?? null))
|
|
206
|
+
return conflict('providers');
|
|
207
|
+
const parsed = parseRuntimeProviders(value ?? undefined);
|
|
208
|
+
if (parsed.documentProblem !== null || parsed.rejected.length)
|
|
209
|
+
return invalid('Frozen PB_IDP_PROVIDERS is not valid');
|
|
210
|
+
return value;
|
|
211
|
+
}
|
|
39
212
|
function canonical(value) {
|
|
40
213
|
if (Array.isArray(value))
|
|
41
214
|
return `[${value.map(canonical).join(',')}]`;
|
|
42
215
|
if (value && typeof value === 'object')
|
|
43
216
|
return `{${Object.entries(value)
|
|
44
|
-
.sort(([a], [b]) => a
|
|
217
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
45
218
|
.map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`)
|
|
46
219
|
.join(',')}}`;
|
|
47
220
|
return JSON.stringify(value);
|
|
@@ -126,6 +299,10 @@ export async function validateDeploymentArtifact(out) {
|
|
|
126
299
|
return invalid();
|
|
127
300
|
const info = safeBuildInfo(JSON.parse((await read('build-info.json')).toString('utf8')));
|
|
128
301
|
const manifest = object(JSON.parse((await read('generated/manifest.json')).toString('utf8')));
|
|
302
|
+
const hasIdentity = Object.hasOwn(manifest, 'identity');
|
|
303
|
+
const identity = hasIdentity ? snapshotIdentityDescriptor(manifest.identity) : null;
|
|
304
|
+
if (hasIdentity && identity === null)
|
|
305
|
+
return invalid();
|
|
129
306
|
if (manifest.schemaVersion !== 1 ||
|
|
130
307
|
manifest.platform !== 'cloudflare' ||
|
|
131
308
|
!Array.isArray(manifest.plugins) ||
|
|
@@ -194,7 +371,16 @@ export async function validateDeploymentArtifact(out) {
|
|
|
194
371
|
assets: ASSET_CONFIG,
|
|
195
372
|
ports,
|
|
196
373
|
}));
|
|
197
|
-
return {
|
|
374
|
+
return {
|
|
375
|
+
module,
|
|
376
|
+
info,
|
|
377
|
+
ports,
|
|
378
|
+
assets,
|
|
379
|
+
assetsHash: exactAssetsHash,
|
|
380
|
+
artifactHash,
|
|
381
|
+
migrations,
|
|
382
|
+
identity,
|
|
383
|
+
};
|
|
198
384
|
}
|
|
199
385
|
catch {
|
|
200
386
|
return invalid();
|
|
@@ -203,6 +389,12 @@ export async function validateDeploymentArtifact(out) {
|
|
|
203
389
|
export const unverifiedDeployContract = {
|
|
204
390
|
versionsCreateScript: false,
|
|
205
391
|
secretInheritance: false,
|
|
392
|
+
preview: async () => {
|
|
393
|
+
throw new CloudflareError('capability_unknown', 1, false, 'deploy');
|
|
394
|
+
},
|
|
395
|
+
domainZone: async () => {
|
|
396
|
+
throw new CloudflareError('domain_zone_not_supported', 1, false, 'deploy');
|
|
397
|
+
},
|
|
206
398
|
};
|
|
207
399
|
/** Cloudflare's direct-upload example hashes base64 bytes plus the extension without its dot. */
|
|
208
400
|
export function cloudflareAssetHash(bytes, name) {
|
|
@@ -214,7 +406,56 @@ export function cloudflareAssetHash(bytes, name) {
|
|
|
214
406
|
export const cloudflareDeployContract = {
|
|
215
407
|
versionsCreateScript: false,
|
|
216
408
|
secretInheritance: true,
|
|
409
|
+
domainZone: resolveZone,
|
|
217
410
|
assetHash: cloudflareAssetHash,
|
|
411
|
+
preview: async (api, script, version) => {
|
|
412
|
+
try {
|
|
413
|
+
if (!uuid(version) || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(script) || script.length > 54)
|
|
414
|
+
return invalid();
|
|
415
|
+
const result = object((await api.request(`/workers/scripts/${script}/versions/${version}`)).result);
|
|
416
|
+
if (!uuid(result.id))
|
|
417
|
+
throw new CloudflareError('provider_failure', 1, false, 'deploy');
|
|
418
|
+
if (result.id !== version)
|
|
419
|
+
return conflict('preview');
|
|
420
|
+
const resources = object(result.resources);
|
|
421
|
+
if (resources.bindings === undefined)
|
|
422
|
+
return conflict('preview');
|
|
423
|
+
if (!Array.isArray(resources.bindings))
|
|
424
|
+
throw new CloudflareError('provider_failure', 1, false, 'deploy');
|
|
425
|
+
if (!resources.bindings.length)
|
|
426
|
+
return conflict('preview');
|
|
427
|
+
const names = new Set();
|
|
428
|
+
for (const raw of resources.bindings) {
|
|
429
|
+
const binding = object(raw);
|
|
430
|
+
if (typeof binding.type !== 'string' || typeof binding.name !== 'string' || !binding.name)
|
|
431
|
+
throw new CloudflareError('provider_failure', 1, false, 'deploy');
|
|
432
|
+
if (!Object.hasOwn(PREVIEW_BINDING_FIELDS, binding.type) || names.has(binding.name))
|
|
433
|
+
return conflict('preview');
|
|
434
|
+
names.add(binding.name);
|
|
435
|
+
const field = PREVIEW_BINDING_FIELDS[binding.type];
|
|
436
|
+
if (field && typeof binding[field] !== 'string')
|
|
437
|
+
throw new CloudflareError('provider_failure', 1, false, 'deploy');
|
|
438
|
+
if (field && field !== 'text' && !binding[field])
|
|
439
|
+
return conflict('preview');
|
|
440
|
+
if (binding.type === 'secret_text' &&
|
|
441
|
+
(Object.hasOwn(binding, 'text') || Object.hasOwn(binding, 'value')))
|
|
442
|
+
return conflict('preview');
|
|
443
|
+
}
|
|
444
|
+
const workerOrigin = await workersDevOrigin(api, script);
|
|
445
|
+
return {
|
|
446
|
+
version_id: result.id,
|
|
447
|
+
url: `https://${version.slice(0, 8).toLowerCase()}-${new URL(workerOrigin).hostname}`,
|
|
448
|
+
bindings_verified: true,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
if (!(error instanceof CloudflareError) || error.stage === 'deploy')
|
|
453
|
+
throw error;
|
|
454
|
+
const staged = new CloudflareError(error.code, error.exitCode, error.retryable, 'deploy', error.status, error.check, error.providerCode, error.source, error.fields);
|
|
455
|
+
staged.message = error.message;
|
|
456
|
+
throw staged;
|
|
457
|
+
}
|
|
458
|
+
},
|
|
218
459
|
createScript: async (api, script, form) => {
|
|
219
460
|
const result = object((await api.request(`/workers/scripts/${script}`, 'PUT', form)).result);
|
|
220
461
|
if (result.id !== script)
|
|
@@ -259,7 +500,7 @@ function distribution(a) {
|
|
|
259
500
|
async function endpoints(api, base) {
|
|
260
501
|
const r = object((await api.request(`${base}/subdomain`)).result);
|
|
261
502
|
if (typeof r.enabled !== 'boolean' || typeof r.previews_enabled !== 'boolean')
|
|
262
|
-
throw new CloudflareError('
|
|
503
|
+
throw new CloudflareError('provider_failure');
|
|
263
504
|
return { enabled: r.enabled, previews_enabled: r.previews_enabled };
|
|
264
505
|
}
|
|
265
506
|
function routeInput(raw, script) {
|
|
@@ -281,7 +522,7 @@ function routeInput(raw, script) {
|
|
|
281
522
|
function secretNames(o) {
|
|
282
523
|
const names = [...(o.secret ?? []), ...(o.secrets === undefined ? [] : o.secrets.split('\n'))];
|
|
283
524
|
if (new Set(names).size !== names.length ||
|
|
284
|
-
names.some((n) => !/^[A-Z][A-Z0-9_]{0,127}$/.test(n) ||
|
|
525
|
+
names.some((n) => !/^[A-Z][A-Z0-9_]{0,127}$/.test(n) || ['PB_TOKEN_PEPPER', 'PB_IDP_PROVIDERS'].includes(n)))
|
|
285
526
|
return invalid();
|
|
286
527
|
return names.sort();
|
|
287
528
|
}
|
|
@@ -506,23 +747,16 @@ async function readSettings(api, base, j, routes, domain, workerExists = true, m
|
|
|
506
747
|
for (const zone of new Set([...routes.map((r) => r.zone_id), ...managedZones])) {
|
|
507
748
|
const listed = await zoneRoutes(api, zone);
|
|
508
749
|
for (const r of listed) {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
}))
|
|
517
|
-
return conflict('routes');
|
|
518
|
-
}
|
|
519
|
-
if (r.script === j.identity.script_name ||
|
|
520
|
-
routes.some((x) => x.zone_id === zone && x.pattern === r.pattern)) {
|
|
521
|
-
if (r.script !== j.identity.script_name ||
|
|
522
|
-
!safeId(r.id) ||
|
|
750
|
+
const reason = routeConflict(zone, r, routes, j.identity.script_name);
|
|
751
|
+
if (reason)
|
|
752
|
+
return conflict('routes', `routes:${reason}`, ...(typeof r.id === 'string' && /^[a-f0-9]{32}$/.test(r.id)
|
|
753
|
+
? [`routes:route_id:${r.id}`]
|
|
754
|
+
: []));
|
|
755
|
+
if (r.script === j.identity.script_name) {
|
|
756
|
+
if (!safeId(r.id) ||
|
|
523
757
|
typeof r.pattern !== 'string' ||
|
|
524
758
|
!/^[a-zA-Z0-9*./:-]{1,256}$/.test(r.pattern))
|
|
525
|
-
return conflict('routes');
|
|
759
|
+
return conflict('routes', 'routes:own_invalid');
|
|
526
760
|
foundRoutes.push({ id: r.id, zone_id: zone, pattern: r.pattern, script: r.script });
|
|
527
761
|
}
|
|
528
762
|
}
|
|
@@ -536,20 +770,8 @@ async function readSettings(api, base, j, routes, domain, workerExists = true, m
|
|
|
536
770
|
domain: foundDomain,
|
|
537
771
|
};
|
|
538
772
|
}
|
|
539
|
-
async function zoneRoutes(api, zone) {
|
|
540
|
-
|
|
541
|
-
const all = [];
|
|
542
|
-
for (let page = 1; page <= 10000; page++) {
|
|
543
|
-
const r = await api.requestZone(zone, `/workers/routes?page=${page}&per_page=100`);
|
|
544
|
-
if (!Array.isArray(r.result))
|
|
545
|
-
return conflict('routes');
|
|
546
|
-
all.push(...r.result.map(object));
|
|
547
|
-
if (r.result_info?.total_pages === page || r.result_info?.total_count === all.length)
|
|
548
|
-
return all;
|
|
549
|
-
if (typeof r.result_info?.total_pages !== 'number' || r.result_info.total_pages < page)
|
|
550
|
-
return conflict('routes');
|
|
551
|
-
}
|
|
552
|
-
return conflict('routes');
|
|
773
|
+
export async function zoneRoutes(api, zone) {
|
|
774
|
+
return api.snapshotZone(zone, '/workers/routes');
|
|
553
775
|
}
|
|
554
776
|
function bindings(j, ports, url) {
|
|
555
777
|
const b = [
|
|
@@ -697,7 +919,7 @@ export async function reconcileCandidatePreview(api, store, j) {
|
|
|
697
919
|
/** Smoke owns this finally block. Persisted intent also covers process interruption. */
|
|
698
920
|
export async function withCandidatePreview(api, store, j, contract, probe) {
|
|
699
921
|
const a = j.deployment?.attempt;
|
|
700
|
-
if (!a?.version_id || a.requested_rollout === 100
|
|
922
|
+
if (!a?.version_id || a.requested_rollout === 100)
|
|
701
923
|
return invalid();
|
|
702
924
|
await reconcileCandidatePreview(api, store, j);
|
|
703
925
|
const base = `/workers/scripts/${j.identity.script_name}`;
|
|
@@ -1057,12 +1279,15 @@ export async function deploy(o, operations = {}) {
|
|
|
1057
1279
|
if (requestedRoutes.some((r) => r.pattern.replace(/^https:\/\//, '').split('/')[0] !== new URL(url).hostname))
|
|
1058
1280
|
return invalid();
|
|
1059
1281
|
const artifact = await validateDeploymentArtifact(out);
|
|
1282
|
+
if (o.providers?.kind === 'set')
|
|
1283
|
+
desiredProviders(o.providers, artifact.identity, null);
|
|
1060
1284
|
const endpointEnv = artifact.ports?.observability.endpointEnv;
|
|
1061
1285
|
const headersEnv = artifact.ports?.observability.headersEnv;
|
|
1062
1286
|
const generatedNames = [
|
|
1063
1287
|
'DB',
|
|
1064
1288
|
'DEPLOYMENT_ID',
|
|
1065
1289
|
'PB_PUBLIC_URL',
|
|
1290
|
+
'PB_IDP_PROVIDERS',
|
|
1066
1291
|
'ASSETS',
|
|
1067
1292
|
...(artifact.ports
|
|
1068
1293
|
? [
|
|
@@ -1164,6 +1389,12 @@ export async function deploy(o, operations = {}) {
|
|
|
1164
1389
|
if (journal.deployment && journal.deployment.attempt.preview !== 'disabled') {
|
|
1165
1390
|
if (o.dryRun) {
|
|
1166
1391
|
output.plan.push({ stage: 'deploy', action: 'reconcile_preview_cleanup' });
|
|
1392
|
+
const known = o.providers?.kind === 'set'
|
|
1393
|
+
? o.providers.value
|
|
1394
|
+
: o.providers?.kind === 'remove'
|
|
1395
|
+
? null
|
|
1396
|
+
: undefined;
|
|
1397
|
+
output.identity = dryRunIdentity(artifact.identity, known ?? null, known === undefined ? null : identityDigest(artifact.identity, known), known === undefined ? ['live_providers'] : []);
|
|
1167
1398
|
output.status = 'planned';
|
|
1168
1399
|
return succeed();
|
|
1169
1400
|
}
|
|
@@ -1205,22 +1436,22 @@ export async function deploy(o, operations = {}) {
|
|
|
1205
1436
|
return conflict('deployment');
|
|
1206
1437
|
if (!current.versions.length && rollout !== 100)
|
|
1207
1438
|
return invalid();
|
|
1439
|
+
let prior;
|
|
1208
1440
|
if (current.versions.length === 1) {
|
|
1209
|
-
|
|
1441
|
+
prior = object((await api.request(`${base}/versions/${current.versions[0].version_id}`)).result);
|
|
1210
1442
|
if (prior.id !== current.versions[0].version_id)
|
|
1211
1443
|
return conflict('version_id');
|
|
1212
1444
|
const annotations = object(prior.annotations);
|
|
1213
1445
|
if (annotations['pb-maintenance-shell'] === 'true')
|
|
1214
1446
|
throw new CloudflareError('capability_unknown', 1, false, 'deploy');
|
|
1215
1447
|
}
|
|
1448
|
+
const workerOrigin = !url || rollout < 100 ? await workersDevOrigin(api, identity.script_name) : null;
|
|
1216
1449
|
if (!url)
|
|
1217
|
-
url =
|
|
1450
|
+
url = workerOrigin;
|
|
1218
1451
|
if (!exists && !contract.versionsCreateScript && !contract.createScript)
|
|
1219
1452
|
throw new CloudflareError('capability_unknown');
|
|
1220
1453
|
if (!contract.assetHash)
|
|
1221
1454
|
throw new CloudflareError('capability_unknown');
|
|
1222
|
-
if (rollout < 100 && !contract.preview)
|
|
1223
|
-
throw new CloudflareError('capability_unknown');
|
|
1224
1455
|
const custom = !!(o.domain || requestedRoutes.length || o.publicUrl);
|
|
1225
1456
|
const policy = { enabled: !custom, previews_enabled: false };
|
|
1226
1457
|
const journalled = journal.deployment;
|
|
@@ -1243,7 +1474,68 @@ export async function deploy(o, operations = {}) {
|
|
|
1243
1474
|
return invalid();
|
|
1244
1475
|
if (installedNames.length && !contract.secretInheritance)
|
|
1245
1476
|
throw new CloudflareError('capability_unknown');
|
|
1477
|
+
const priorAttempt = journal.deployment?.attempt;
|
|
1478
|
+
const resuming = priorAttempt &&
|
|
1479
|
+
[
|
|
1480
|
+
'preparing_upload',
|
|
1481
|
+
'upload_pending',
|
|
1482
|
+
'uploaded',
|
|
1483
|
+
'configuring',
|
|
1484
|
+
'activation_pending',
|
|
1485
|
+
].includes(priorAttempt.phase);
|
|
1486
|
+
if (resuming) {
|
|
1487
|
+
if (priorAttempt.artifact_sha256 !== artifact.artifactHash)
|
|
1488
|
+
return conflict('artifact_sha256');
|
|
1489
|
+
if (priorAttempt.requested_rollout !== rollout)
|
|
1490
|
+
return conflict('requested_rollout');
|
|
1491
|
+
}
|
|
1492
|
+
else if (current.versions.length > 1 && recordedVerified?.requested_rollout !== rollout)
|
|
1493
|
+
return conflict('deployment');
|
|
1494
|
+
const removeSecret = () => invalid('Remove the secret first: wrangler secret delete PB_IDP_PROVIDERS');
|
|
1495
|
+
if (installedNames.includes('PB_IDP_PROVIDERS'))
|
|
1496
|
+
return removeSecret();
|
|
1497
|
+
const sources = [];
|
|
1498
|
+
for (const version of current.versions) {
|
|
1499
|
+
const result = prior ?? object((await api.request(`${base}/versions/${version.version_id}`)).result);
|
|
1500
|
+
if (result.id !== version.version_id)
|
|
1501
|
+
return conflict('version_id');
|
|
1502
|
+
const resources = object(result.resources ?? {});
|
|
1503
|
+
if (!Array.isArray(resources.bindings))
|
|
1504
|
+
return conflict('providers');
|
|
1505
|
+
if (resources.bindings
|
|
1506
|
+
.map(object)
|
|
1507
|
+
.some((b) => b.name === 'PB_IDP_PROVIDERS' && b.type === 'secret_text'))
|
|
1508
|
+
return removeSecret();
|
|
1509
|
+
sources.push(resources);
|
|
1510
|
+
}
|
|
1511
|
+
const frozen = resuming ? frozenProviders(priorAttempt) : null;
|
|
1512
|
+
let providersText = frozen;
|
|
1513
|
+
if (o.providers?.kind === 'set' || o.providers?.kind === 'remove') {
|
|
1514
|
+
providersText = desiredProviders(o.providers, artifact.identity, null);
|
|
1515
|
+
if (resuming) {
|
|
1516
|
+
if (providersText !== frozen &&
|
|
1517
|
+
!(o.providers.kind === 'set' &&
|
|
1518
|
+
frozen !== null &&
|
|
1519
|
+
validateProvidersDocument(frozen).value === providersText))
|
|
1520
|
+
return conflict('settings_sha256');
|
|
1521
|
+
providersText = frozen;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
else if (!resuming) {
|
|
1525
|
+
if (sources.some((r) => r.bindings.map(object).some((b) => b.name === 'PB_IDP_PROVIDERS')))
|
|
1526
|
+
checkManaged();
|
|
1527
|
+
const values = sources.map((r) => desiredProviders(o.providers, artifact.identity, r));
|
|
1528
|
+
if (new Set(values).size > 1)
|
|
1529
|
+
return conflict('providers');
|
|
1530
|
+
providersText = values[0] ?? null;
|
|
1531
|
+
}
|
|
1532
|
+
const identityHash = identityDigest(artifact.identity, providersText);
|
|
1533
|
+
const anchors = effectiveAnchors(artifact.identity, providersText);
|
|
1534
|
+
if (o.dryRun)
|
|
1535
|
+
output.identity = dryRunIdentity(artifact.identity, providersText, identityHash);
|
|
1246
1536
|
const plainBindings = bindings(journal, artifact.ports, url);
|
|
1537
|
+
if (providersText !== null)
|
|
1538
|
+
plainBindings.push({ type: 'plain_text', name: 'PB_IDP_PROVIDERS', text: providersText });
|
|
1247
1539
|
if (endpointEnv) {
|
|
1248
1540
|
if (selectedSecrets.includes(endpointEnv) || installedNames.includes(endpointEnv))
|
|
1249
1541
|
return invalid();
|
|
@@ -1252,6 +1544,21 @@ export async function deploy(o, operations = {}) {
|
|
|
1252
1544
|
const collisionNames = new Set(plainBindings.map((b) => b.name));
|
|
1253
1545
|
if ([...selectedSecrets, ...installedNames].some((n) => collisionNames.has(n)))
|
|
1254
1546
|
return invalid();
|
|
1547
|
+
const pending = journal.deployment?.attempt;
|
|
1548
|
+
const pendingNames = pending &&
|
|
1549
|
+
['upload_pending', 'uploaded', 'configuring', 'activation_pending'].includes(pending.phase)
|
|
1550
|
+
? pending.secret_names
|
|
1551
|
+
: [];
|
|
1552
|
+
const missingSecrets = [
|
|
1553
|
+
...new Set(parseRuntimeProviders(providersText ?? undefined)
|
|
1554
|
+
.accepted.filter((p) => p.kind === 'oidc')
|
|
1555
|
+
.map((p) => providerSecretName(p.id, 'CLIENT_SECRET'))),
|
|
1556
|
+
].filter((name) => !installedNames.includes(name) &&
|
|
1557
|
+
!pendingNames.includes(name) &&
|
|
1558
|
+
!(selectedSecrets.includes(name) && o.env[name]));
|
|
1559
|
+
if (missingSecrets.length)
|
|
1560
|
+
return invalid(`Missing provider secrets: ${missingSecrets.join(', ')}; deliver with --secret <ENV_NAME> or --secrets <ENV_NAMES>`);
|
|
1561
|
+
output.checks.push({ name: 'provider_secrets', status: 'verified' });
|
|
1255
1562
|
const updates = [];
|
|
1256
1563
|
for (const name of selectedSecrets) {
|
|
1257
1564
|
if ((name === 'PB_MASTER_KEY' || name === 'PB_MASTER_KEY_PREVIOUS') &&
|
|
@@ -1265,25 +1572,26 @@ export async function deploy(o, operations = {}) {
|
|
|
1265
1572
|
else if (!installedNames.includes(name))
|
|
1266
1573
|
return invalid();
|
|
1267
1574
|
}
|
|
1268
|
-
const pending = journal.deployment?.attempt;
|
|
1269
|
-
const pendingNames = pending &&
|
|
1270
|
-
['upload_pending', 'uploaded', 'configuring', 'activation_pending'].includes(pending.phase)
|
|
1271
|
-
? pending.secret_names
|
|
1272
|
-
: [];
|
|
1273
1575
|
const allNames = [
|
|
1274
1576
|
...new Set([...installedNames, ...pendingNames, ...updates.map((b) => String(b.name))]),
|
|
1275
1577
|
].sort();
|
|
1578
|
+
const variableCount = new Set([
|
|
1579
|
+
...plainBindings.filter((b) => b.type === 'plain_text').map((b) => String(b.name)),
|
|
1580
|
+
...allNames,
|
|
1581
|
+
]).size;
|
|
1582
|
+
const variableLimit = o.workersPaid ? 128 : 64;
|
|
1583
|
+
if (variableCount > variableLimit)
|
|
1584
|
+
return invalid(`Worker environment variable count ${variableCount} exceeds limit ${variableLimit}`);
|
|
1585
|
+
output.checks.push({ name: 'variable_budget', status: 'verified' });
|
|
1276
1586
|
if (!allNames.includes('PB_MASTER_KEY'))
|
|
1277
1587
|
return invalid();
|
|
1278
1588
|
if (headersEnv && !allNames.includes(headersEnv))
|
|
1279
1589
|
return invalid();
|
|
1280
1590
|
let domain = live.domain;
|
|
1281
1591
|
if (o.domain && !domain) {
|
|
1282
|
-
if (!contract.domainZone)
|
|
1283
|
-
throw new CloudflareError('capability_unknown');
|
|
1284
1592
|
const zone = await contract.domainZone(api, o.domain);
|
|
1285
1593
|
if (!/^[a-f0-9]{32}$/.test(zone))
|
|
1286
|
-
throw new CloudflareError('
|
|
1594
|
+
throw new CloudflareError('provider_failure', 1, false, 'deploy');
|
|
1287
1595
|
domain = {
|
|
1288
1596
|
id: null,
|
|
1289
1597
|
zone_id: zone,
|
|
@@ -1319,7 +1627,6 @@ export async function deploy(o, operations = {}) {
|
|
|
1319
1627
|
ledgerHash,
|
|
1320
1628
|
}));
|
|
1321
1629
|
const priorJournal = journal.deployment;
|
|
1322
|
-
const priorAttempt = priorJournal?.attempt;
|
|
1323
1630
|
const verified = priorAttempt?.phase === 'verified' && priorAttempt.smoke === 'passed'
|
|
1324
1631
|
? priorAttempt
|
|
1325
1632
|
: (priorJournal?.last_verified ?? null);
|
|
@@ -1332,6 +1639,9 @@ export async function deploy(o, operations = {}) {
|
|
|
1332
1639
|
livePlain !== undefined &&
|
|
1333
1640
|
plainBindings.every((b) => livePlain.some((x) => equal(x, b))) &&
|
|
1334
1641
|
livePlain.length === plainBindings.length;
|
|
1642
|
+
if (anchors.length &&
|
|
1643
|
+
(await anchorPreflight({ ...auth, ...(o.fetch ? { fetch: o.fetch } : {}) }, journal.resources.d1?.id, anchors, o.dryRun === true)) === 'skipped')
|
|
1644
|
+
output.identity?.skipped_checks.push('anchor_preflight');
|
|
1335
1645
|
if (verified &&
|
|
1336
1646
|
plainMatch &&
|
|
1337
1647
|
verified.artifact_sha256 === artifact.artifactHash &&
|
|
@@ -1347,37 +1657,36 @@ export async function deploy(o, operations = {}) {
|
|
|
1347
1657
|
output.status = o.dryRun ? 'planned' : 'ok';
|
|
1348
1658
|
return succeed();
|
|
1349
1659
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1660
|
+
if (rollout < 100 &&
|
|
1661
|
+
!resuming &&
|
|
1662
|
+
(identityHash !== (verified?.identity_sha256 ?? null) ||
|
|
1663
|
+
(identityHash !== null &&
|
|
1664
|
+
priorAttempt &&
|
|
1665
|
+
priorAttempt !== verified &&
|
|
1666
|
+
(!verified || !equal(current.versions, distribution(verified))))))
|
|
1667
|
+
return invalid('partial rollout is not allowed when SSO provider configuration changes; deploy with --rollout 100 first');
|
|
1358
1668
|
if (resuming &&
|
|
1359
1669
|
(priorAttempt.phase !== 'preparing_upload' || priorAttempt.script_creation !== undefined) &&
|
|
1360
1670
|
updates.some((b) => b.name !== 'PB_MASTER_KEY' && b.name !== 'PB_MASTER_KEY_PREVIOUS'))
|
|
1361
1671
|
return conflict('bindings:secret_text');
|
|
1362
1672
|
if (resuming) {
|
|
1363
|
-
if (priorAttempt.artifact_sha256 !== artifact.artifactHash)
|
|
1364
|
-
return conflict('artifact_sha256');
|
|
1365
1673
|
if (priorAttempt.settings_sha256 !== settingsHash)
|
|
1366
1674
|
return conflict('settings_sha256');
|
|
1367
|
-
if (priorAttempt.requested_rollout !== rollout)
|
|
1368
|
-
return conflict('requested_rollout');
|
|
1369
1675
|
}
|
|
1370
|
-
if (!resuming)
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1676
|
+
if (!resuming && current.versions.length > 1)
|
|
1677
|
+
return conflict('deployment');
|
|
1678
|
+
checkManaged();
|
|
1679
|
+
function checkManaged() {
|
|
1680
|
+
if (resuming)
|
|
1681
|
+
return;
|
|
1682
|
+
let managed = priorAttempt?.intended_settings ?? recordedVerified?.intended_settings;
|
|
1374
1683
|
if (priorAttempt?.phase === 'failed' || priorAttempt?.phase === 'rolled_back')
|
|
1375
1684
|
managed =
|
|
1376
1685
|
restoredSnapshot(priorAttempt, priorAttempt.phase === 'failed' && equal(current, beforeCandidate(priorAttempt))
|
|
1377
1686
|
? live
|
|
1378
1687
|
: undefined) ?? conflict('managed_settings');
|
|
1379
|
-
if (live.routes.some((r) => !
|
|
1380
|
-
return conflict('routes');
|
|
1688
|
+
if (live.routes.some((r) => !requestedRoutes.some((x) => x.zone_id === r.zone_id && x.pattern === r.pattern)))
|
|
1689
|
+
return conflict('routes', 'routes:unmanaged_own');
|
|
1381
1690
|
// Never take over pre-existing attachments without a previous managed receipt.
|
|
1382
1691
|
if ((live.consumer || live.schedules.length || live.routes.length || live.domain) &&
|
|
1383
1692
|
(!managed || !matchesManaged(live, managed)))
|
|
@@ -1407,14 +1716,19 @@ export async function deploy(o, operations = {}) {
|
|
|
1407
1716
|
return succeed();
|
|
1408
1717
|
}
|
|
1409
1718
|
let a;
|
|
1410
|
-
if (resuming)
|
|
1719
|
+
if (resuming) {
|
|
1411
1720
|
a = priorAttempt;
|
|
1721
|
+
a.identity_sha256 = identityHash;
|
|
1722
|
+
}
|
|
1412
1723
|
else {
|
|
1413
1724
|
a = {
|
|
1414
1725
|
operation_id: randomUUID(),
|
|
1415
1726
|
artifact_sha256: artifact.artifactHash,
|
|
1416
1727
|
assets_sha256: artifact.assetsHash,
|
|
1417
1728
|
settings_sha256: settingsHash,
|
|
1729
|
+
providers_sha256: providersText === null ? null : digest(providersText),
|
|
1730
|
+
identity_sha256: identityHash,
|
|
1731
|
+
...(providersText === null ? {} : { providers_text: providersText }),
|
|
1418
1732
|
secret_names: allNames,
|
|
1419
1733
|
build_info: artifact.info,
|
|
1420
1734
|
version_id: null,
|
|
@@ -1557,14 +1871,20 @@ export async function deploy(o, operations = {}) {
|
|
|
1557
1871
|
if (!a.version_id)
|
|
1558
1872
|
return unknown();
|
|
1559
1873
|
await version(api, base, a.version_id, a, allNames, plainBindings);
|
|
1874
|
+
if (providersText !== null) {
|
|
1875
|
+
recordDeploymentHistory(journal.deployment, {
|
|
1876
|
+
plainBinding: { name: 'PB_IDP_PROVIDERS', text: providersText },
|
|
1877
|
+
});
|
|
1878
|
+
await save(store, journal, now);
|
|
1879
|
+
}
|
|
1560
1880
|
if (rollout < 100) {
|
|
1561
1881
|
const preview = await contract.preview(api, identity.script_name, a.version_id);
|
|
1562
1882
|
if (preview.version_id !== a.version_id || !preview.bindings_verified)
|
|
1563
1883
|
return conflict('preview');
|
|
1564
1884
|
const probeUrl = origin(preview.url);
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1885
|
+
const host = new URL(probeUrl).hostname;
|
|
1886
|
+
const prefix = a.version_id.slice(0, 8).toLowerCase();
|
|
1887
|
+
if (host !== `${prefix}-${new URL(workerOrigin).hostname}`)
|
|
1568
1888
|
return conflict('preview');
|
|
1569
1889
|
a.candidate_probe_url = probeUrl;
|
|
1570
1890
|
await save(store, journal, now);
|
|
@@ -1587,13 +1907,13 @@ export async function deploy(o, operations = {}) {
|
|
|
1587
1907
|
if (!equal(part(expected), part(a.prior_settings)) &&
|
|
1588
1908
|
!equal(part(expected), part(a.intended_settings)) &&
|
|
1589
1909
|
!(kind === 'endpoints' && equal(expected.endpoints, restrictivePolicy(a))))
|
|
1590
|
-
return conflict(kind === 'consumer' ? 'queue_consumer' : kind);
|
|
1910
|
+
return conflict(kind === 'consumer' ? 'queue_consumer' : kind, ...(kind === 'routes' ? ['routes:drift'] : []));
|
|
1591
1911
|
}
|
|
1592
1912
|
for (const kind of ['consumer', 'schedules', 'routes', 'domain']) {
|
|
1593
1913
|
const before = await readSettings(api, base, journal, requestedRoutes, o.domain, true, managedZones);
|
|
1594
1914
|
const part = (v) => withoutIds(v)[kind];
|
|
1595
1915
|
if (!equal(part(before), part(expected)) && !equal(part(before), part(a.intended_settings)))
|
|
1596
|
-
return conflict(kind === 'consumer' ? 'queue_consumer' : kind);
|
|
1916
|
+
return conflict(kind === 'consumer' ? 'queue_consumer' : kind, ...(kind === 'routes' ? ['routes:drift'] : []));
|
|
1597
1917
|
if (!equal(part(before), part(a.intended_settings))) {
|
|
1598
1918
|
const change = {
|
|
1599
1919
|
kind,
|
|
@@ -1615,7 +1935,7 @@ export async function deploy(o, operations = {}) {
|
|
|
1615
1935
|
}
|
|
1616
1936
|
const observed = await readSettings(api, base, journal, requestedRoutes, o.domain, true, managedZones);
|
|
1617
1937
|
if (!equal(part(observed), part(a.intended_settings)))
|
|
1618
|
-
return conflict(kind === 'consumer' ? 'queue_consumer' : kind);
|
|
1938
|
+
return conflict(kind === 'consumer' ? 'queue_consumer' : kind, ...(kind === 'routes' ? ['routes:drift'] : []));
|
|
1619
1939
|
expected = { ...expected, [kind]: observed[kind] };
|
|
1620
1940
|
a.intended_settings = { ...a.intended_settings, [kind]: observed[kind] };
|
|
1621
1941
|
if (!equal(withoutIds(observed), withoutIds(expected)))
|
|
@@ -1722,7 +2042,9 @@ export async function deploy(o, operations = {}) {
|
|
|
1722
2042
|
: failure instanceof CloudflareError && failure.stage === 'prebuilt'
|
|
1723
2043
|
? failure
|
|
1724
2044
|
: failure instanceof CloudflareError
|
|
1725
|
-
? new CloudflareError(failure.code, failure.exitCode, failure.retryable, 'deploy', failure.status, failure.check, failure.providerCode, failure.source, failure.fields)
|
|
2045
|
+
? Object.assign(new CloudflareError(failure.code, failure.exitCode, failure.retryable, 'deploy', failure.status, failure.check, failure.providerCode, failure.source, failure.fields), {
|
|
2046
|
+
message: inputMessages.get(failure) ?? new CloudflareError(failure.code).message,
|
|
2047
|
+
})
|
|
1726
2048
|
: new CloudflareError('provider_failure', 1, false, 'deploy')));
|
|
1727
2049
|
return result;
|
|
1728
2050
|
}
|