@package-broker/cli 2.0.0-rc.3 → 2.0.0-rc.4
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/build-artifacts.d.ts.map +1 -1
- package/dist/build-artifacts.js +1 -0
- package/dist/build-artifacts.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +7 -2
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +55 -7
- package/dist/cli.js.map +1 -1
- package/dist/commands/admin.d.ts +6 -2
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +59 -4
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/cloudflare/api.d.ts +1 -0
- package/dist/commands/cloudflare/api.d.ts.map +1 -1
- package/dist/commands/cloudflare/api.js +15 -3
- package/dist/commands/cloudflare/api.js.map +1 -1
- package/dist/commands/cloudflare/deploy.d.ts +10 -0
- package/dist/commands/cloudflare/deploy.d.ts.map +1 -1
- package/dist/commands/cloudflare/deploy.js +215 -71
- package/dist/commands/cloudflare/deploy.js.map +1 -1
- package/dist/commands/cloudflare/input-messages.d.ts +3 -0
- package/dist/commands/cloudflare/input-messages.d.ts.map +1 -0
- package/dist/commands/cloudflare/input-messages.js +2 -0
- package/dist/commands/cloudflare/input-messages.js.map +1 -0
- package/dist/commands/cloudflare/inventory.d.ts +3 -1
- package/dist/commands/cloudflare/inventory.d.ts.map +1 -1
- package/dist/commands/cloudflare/inventory.js +17 -2
- package/dist/commands/cloudflare/inventory.js.map +1 -1
- package/dist/commands/cloudflare/local-auth.d.ts +34 -0
- package/dist/commands/cloudflare/local-auth.d.ts.map +1 -0
- package/dist/commands/cloudflare/local-auth.js +156 -0
- package/dist/commands/cloudflare/local-auth.js.map +1 -0
- package/dist/commands/cloudflare/setup.d.ts +3 -0
- package/dist/commands/cloudflare/setup.d.ts.map +1 -1
- package/dist/commands/cloudflare/setup.js +188 -43
- package/dist/commands/cloudflare/setup.js.map +1 -1
- package/dist/commands/cloudflare/teardown.d.ts.map +1 -1
- package/dist/commands/cloudflare/teardown.js +7 -3
- package/dist/commands/cloudflare/teardown.js.map +1 -1
- package/dist/commands/openapi-specs.d.ts.map +1 -1
- package/dist/commands/openapi-specs.js +20 -0
- package/dist/commands/openapi-specs.js.map +1 -1
- package/dist/manifest.d.ts +8 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +52 -1
- package/dist/manifest.js.map +1 -1
- package/dist/prebuilt-expected.json +3 -3
- package/package.json +7 -7
|
@@ -15,11 +15,14 @@ import { DbError } from '@package-broker/core/db/errors';
|
|
|
15
15
|
import { createD1HttpExecutor, isLostResponse } from '../../cloudflare/d1-http-executor.js';
|
|
16
16
|
import { assertPrebuiltConfiguration, materializePrebuilt, prebuiltEnvelope, removePrebuilt, } from '../../prebuilt.js';
|
|
17
17
|
import { builtRouteProblems, loadBuiltMigrations } from '../built-manifest.js';
|
|
18
|
+
import { snapshotLocalAuthCapability } from '../../manifest.js';
|
|
18
19
|
import { validateProvidersDocument } from './providers-file.js';
|
|
19
20
|
import { normalizeQueueConsumer } from './queue-consumer.js';
|
|
20
21
|
import { assertMigrationsApplied } from './migrations-check.js';
|
|
21
22
|
import { d1BindingId, workerSettingsDifferences } from './worker-settings.js';
|
|
22
23
|
import { CloudflareApi, CloudflareError, cloudflareCredentials, commandEnvelope, failCommand, object, wasNotSent, } from './api.js';
|
|
24
|
+
import { inputMessages } from './input-messages.js';
|
|
25
|
+
import { resolveLocalAuth, verifiedDeployment, frozenLocalAuthAttempt, readLocalAuthSources, liveLocalAuthModes, } from './local-auth.js';
|
|
23
26
|
import { resolveZone } from './zone.js';
|
|
24
27
|
import { routeConflict } from './routes.js';
|
|
25
28
|
import { InventoryStore, InventoryTransaction, digest, installIdentity, recordDeploymentHistory, DeploymentHistoryCapacityError, safeBuildInfo, } from './inventory.js';
|
|
@@ -44,7 +47,6 @@ const PREVIEW_BINDING_FIELDS = {
|
|
|
44
47
|
const uuid = (v) => typeof v === 'string' &&
|
|
45
48
|
/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(v);
|
|
46
49
|
const safeId = (v) => typeof v === 'string' && /^[a-zA-Z0-9_-]{1,128}$/.test(v);
|
|
47
|
-
const inputMessages = new WeakMap();
|
|
48
50
|
const invalid = (message) => {
|
|
49
51
|
const error = new CloudflareError('invalid_input', 2, false, 'deploy');
|
|
50
52
|
if (message)
|
|
@@ -60,6 +62,12 @@ const conflict = (field, ...fields) => {
|
|
|
60
62
|
const unknown = () => {
|
|
61
63
|
throw new CloudflareError('outcome_unknown', 6, false, 'deploy');
|
|
62
64
|
};
|
|
65
|
+
export function assertLocalAuthAccepted(mode, capability) {
|
|
66
|
+
if (mode === 'enabled')
|
|
67
|
+
return;
|
|
68
|
+
if (mode !== 'disabled' || snapshotLocalAuthCapability(capability) === null)
|
|
69
|
+
invalid('Cannot disable local sign-in on an artifact without local-auth support');
|
|
70
|
+
}
|
|
63
71
|
export function assertProvidersAccepted(option, identity) {
|
|
64
72
|
if (option?.kind !== 'set' || identity?.runtimeProviders)
|
|
65
73
|
return;
|
|
@@ -96,12 +104,18 @@ function desiredProviders(option, identity, live) {
|
|
|
96
104
|
return invalid('live PB_IDP_PROVIDERS is not valid; pass --providers <file> or --providers none');
|
|
97
105
|
}
|
|
98
106
|
}
|
|
99
|
-
function planned({ id, kind, anchor }) {
|
|
107
|
+
function planned({ id, kind, anchor, policy, }) {
|
|
100
108
|
const value = anchor.slice(anchor.indexOf(':') + 1);
|
|
101
109
|
const slash = value.indexOf('/');
|
|
102
110
|
return kind === 'oidc'
|
|
103
|
-
? { id, kind, issuer: value }
|
|
104
|
-
: {
|
|
111
|
+
? { id, kind, issuer: value, ...(policy === undefined ? {} : { policy }) }
|
|
112
|
+
: {
|
|
113
|
+
id,
|
|
114
|
+
kind,
|
|
115
|
+
teamDomain: value.slice(0, slash),
|
|
116
|
+
audience: value.slice(slash + 1),
|
|
117
|
+
...(policy === undefined ? {} : { policy }),
|
|
118
|
+
};
|
|
105
119
|
}
|
|
106
120
|
function identityDigest(descriptor, providers) {
|
|
107
121
|
return descriptor === null && providers === null
|
|
@@ -120,6 +134,60 @@ function providerPlan(descriptor, providersText) {
|
|
|
120
134
|
const plan = planProviders(descriptor.compiled.map(planned), runtime);
|
|
121
135
|
return { runtime, plan };
|
|
122
136
|
}
|
|
137
|
+
export async function resolveProviderText(descriptor, option, sources, frozen, checkManaged) {
|
|
138
|
+
if (frozen) {
|
|
139
|
+
const text = frozenProviders(frozen);
|
|
140
|
+
if (option?.kind === 'set' || option?.kind === 'remove') {
|
|
141
|
+
const requested = desiredProviders(option, descriptor, null);
|
|
142
|
+
if (requested !== text &&
|
|
143
|
+
!(option.kind === 'set' &&
|
|
144
|
+
text !== null &&
|
|
145
|
+
validateProvidersDocument(text).value === requested))
|
|
146
|
+
return conflict('settings_sha256');
|
|
147
|
+
}
|
|
148
|
+
return text;
|
|
149
|
+
}
|
|
150
|
+
if (option?.kind === 'set' || option?.kind === 'remove')
|
|
151
|
+
return desiredProviders(option, descriptor, null);
|
|
152
|
+
if (sources.some((source) => source.bindings.map(object).some((b) => b.name === 'PB_IDP_PROVIDERS')))
|
|
153
|
+
await checkManaged();
|
|
154
|
+
const values = sources.map((source) => desiredProviders(option, descriptor, source));
|
|
155
|
+
if (new Set(values).size > 1)
|
|
156
|
+
return conflict('providers');
|
|
157
|
+
return values[0] ?? null;
|
|
158
|
+
}
|
|
159
|
+
export function setupProviderCandidate(descriptor, text) {
|
|
160
|
+
return descriptor === null ? [] : providerPlan(descriptor, text).plan.effective;
|
|
161
|
+
}
|
|
162
|
+
export async function checkSetupManagedProviders(api, journal, routes, domain) {
|
|
163
|
+
const requested = routeInput(routes, journal.identity.script_name);
|
|
164
|
+
const deployment = journal.deployment;
|
|
165
|
+
const zones = [
|
|
166
|
+
...new Set([
|
|
167
|
+
deployment?.attempt.prior_settings,
|
|
168
|
+
deployment?.attempt.intended_settings,
|
|
169
|
+
deployment?.last_verified?.intended_settings,
|
|
170
|
+
].flatMap((settings) => settings?.routes.map((r) => r.zone_id) ?? [])),
|
|
171
|
+
];
|
|
172
|
+
const base = `/workers/scripts/${journal.identity.script_name}`;
|
|
173
|
+
const live = await readSettings(api, base, journal, requested, domain, true, zones);
|
|
174
|
+
const current = await active(api, base);
|
|
175
|
+
checkManagedSettings(journal.deployment?.attempt, verifiedDeployment(deployment), live, current, requested);
|
|
176
|
+
}
|
|
177
|
+
function checkManagedSettings(priorAttempt, recordedVerified, live, current, requestedRoutes) {
|
|
178
|
+
let managed = priorAttempt?.intended_settings ?? recordedVerified?.intended_settings;
|
|
179
|
+
if (priorAttempt?.phase === 'failed' || priorAttempt?.phase === 'rolled_back')
|
|
180
|
+
managed =
|
|
181
|
+
restoredSnapshot(priorAttempt, priorAttempt.phase === 'failed' && equal(current, beforeCandidate(priorAttempt))
|
|
182
|
+
? live
|
|
183
|
+
: undefined) ?? conflict('managed_settings');
|
|
184
|
+
if (live.routes.some((r) => !requestedRoutes.some((x) => x.zone_id === r.zone_id && x.pattern === r.pattern)))
|
|
185
|
+
return conflict('routes', 'routes:unmanaged_own');
|
|
186
|
+
// Never take over pre-existing attachments without a previous managed receipt.
|
|
187
|
+
if ((live.consumer || live.schedules.length || live.routes.length || live.domain) &&
|
|
188
|
+
(!managed || !matchesManaged(live, managed)))
|
|
189
|
+
return conflict('managed_settings');
|
|
190
|
+
}
|
|
123
191
|
function effectiveAnchors(descriptor, providersText) {
|
|
124
192
|
if (descriptor === null)
|
|
125
193
|
return [];
|
|
@@ -303,6 +371,10 @@ export async function validateDeploymentArtifact(out) {
|
|
|
303
371
|
const identity = hasIdentity ? snapshotIdentityDescriptor(manifest.identity) : null;
|
|
304
372
|
if (hasIdentity && identity === null)
|
|
305
373
|
return invalid();
|
|
374
|
+
const hasLocalAuth = Object.hasOwn(manifest, 'localAuth');
|
|
375
|
+
const localAuth = hasLocalAuth ? snapshotLocalAuthCapability(manifest.localAuth) : null;
|
|
376
|
+
if (hasLocalAuth && localAuth === null)
|
|
377
|
+
return invalid();
|
|
306
378
|
if (manifest.schemaVersion !== 1 ||
|
|
307
379
|
manifest.platform !== 'cloudflare' ||
|
|
308
380
|
!Array.isArray(manifest.plugins) ||
|
|
@@ -380,6 +452,7 @@ export async function validateDeploymentArtifact(out) {
|
|
|
380
452
|
artifactHash,
|
|
381
453
|
migrations,
|
|
382
454
|
identity,
|
|
455
|
+
localAuth,
|
|
383
456
|
};
|
|
384
457
|
}
|
|
385
458
|
catch {
|
|
@@ -521,8 +594,11 @@ function routeInput(raw, script) {
|
|
|
521
594
|
}
|
|
522
595
|
function secretNames(o) {
|
|
523
596
|
const names = [...(o.secret ?? []), ...(o.secrets === undefined ? [] : o.secrets.split('\n'))];
|
|
597
|
+
if (names.includes('PB_AUTH_LOCAL'))
|
|
598
|
+
return invalid('Remove the secret first: wrangler secret delete PB_AUTH_LOCAL');
|
|
524
599
|
if (new Set(names).size !== names.length ||
|
|
525
|
-
names.some((n) => !/^[A-Z][A-Z0-9_]{0,127}$/.test(n) ||
|
|
600
|
+
names.some((n) => !/^[A-Z][A-Z0-9_]{0,127}$/.test(n) ||
|
|
601
|
+
['PB_TOKEN_PEPPER', 'PB_IDP_PROVIDERS', 'PB_AUTH_LOCAL'].includes(n)))
|
|
526
602
|
return invalid();
|
|
527
603
|
return names.sort();
|
|
528
604
|
}
|
|
@@ -1242,6 +1318,9 @@ export async function deploy(o, operations = {}) {
|
|
|
1242
1318
|
let uploadSent = false;
|
|
1243
1319
|
let creatingWorker = false;
|
|
1244
1320
|
try {
|
|
1321
|
+
const cpuMs = o.cpuMs ?? 120000;
|
|
1322
|
+
if (!Number.isSafeInteger(cpuMs) || cpuMs < 1)
|
|
1323
|
+
return invalid();
|
|
1245
1324
|
if (o.prebuilt)
|
|
1246
1325
|
await assertPrebuiltConfiguration(resolve(o.project ?? dirname(resolve(o.configPath ?? 'broker.config.ts'))), o.configPath);
|
|
1247
1326
|
const auth = cloudflareCredentials(o.env);
|
|
@@ -1288,6 +1367,7 @@ export async function deploy(o, operations = {}) {
|
|
|
1288
1367
|
'DEPLOYMENT_ID',
|
|
1289
1368
|
'PB_PUBLIC_URL',
|
|
1290
1369
|
'PB_IDP_PROVIDERS',
|
|
1370
|
+
'PB_AUTH_LOCAL',
|
|
1291
1371
|
'ASSETS',
|
|
1292
1372
|
...(artifact.ports
|
|
1293
1373
|
? [
|
|
@@ -1379,6 +1459,24 @@ export async function deploy(o, operations = {}) {
|
|
|
1379
1459
|
return conflict('worker:identity');
|
|
1380
1460
|
const exists = matches.length === 1;
|
|
1381
1461
|
creatingWorker = !exists;
|
|
1462
|
+
if (!exists) {
|
|
1463
|
+
try {
|
|
1464
|
+
await api.request(`${base}/settings`);
|
|
1465
|
+
if (journal.deployment?.attempt.script_creation !== undefined ||
|
|
1466
|
+
journal.resources.worker?.name === identity.script_name) {
|
|
1467
|
+
const error = new CloudflareError('outcome_unknown', 6, false, 'deploy');
|
|
1468
|
+
inputMessages.set(error, 'The journal owns this Worker creation, but the Worker list is stale; retry deploy after the Worker becomes visible.');
|
|
1469
|
+
throw error;
|
|
1470
|
+
}
|
|
1471
|
+
return conflict('worker:identity');
|
|
1472
|
+
}
|
|
1473
|
+
catch (error) {
|
|
1474
|
+
if (!(error instanceof CloudflareError) ||
|
|
1475
|
+
error.status !== 404 ||
|
|
1476
|
+
error.providerCode !== 10007)
|
|
1477
|
+
throw error;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1382
1480
|
if (exists) {
|
|
1383
1481
|
const tags = matches[0].tags;
|
|
1384
1482
|
if (!Array.isArray(tags) ||
|
|
@@ -1402,6 +1500,23 @@ export async function deploy(o, operations = {}) {
|
|
|
1402
1500
|
return conflict('preview_cleanup'); // Re-read the complete configuration on a fresh run after interrupted cleanup.
|
|
1403
1501
|
}
|
|
1404
1502
|
const workerSettings = exists ? await ownedWorkerSettings(api, base, journal) : null;
|
|
1503
|
+
const interrupted = journal.deployment?.attempt;
|
|
1504
|
+
if (exists &&
|
|
1505
|
+
!o.dryRun &&
|
|
1506
|
+
interrupted &&
|
|
1507
|
+
[
|
|
1508
|
+
'preparing_upload',
|
|
1509
|
+
'upload_pending',
|
|
1510
|
+
'uploaded',
|
|
1511
|
+
'configuring',
|
|
1512
|
+
'activation_pending',
|
|
1513
|
+
].includes(interrupted.phase)) {
|
|
1514
|
+
const liveEndpoints = await endpoints(api, base);
|
|
1515
|
+
await enforceEndpoints(api, store, journal, {
|
|
1516
|
+
enabled: liveEndpoints.enabled && restrictivePolicy(interrupted).enabled,
|
|
1517
|
+
previews_enabled: false,
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1405
1520
|
if (workerSettings) {
|
|
1406
1521
|
for (const raw of workerSettings.bindings.map(object)) {
|
|
1407
1522
|
const b = normalizedBinding(raw);
|
|
@@ -1426,10 +1541,7 @@ export async function deploy(o, operations = {}) {
|
|
|
1426
1541
|
}
|
|
1427
1542
|
await liveResources(api, journal, artifact.ports);
|
|
1428
1543
|
let current = exists ? await active(api, base) : { id: null, versions: [] };
|
|
1429
|
-
const recordedVerified = journal.deployment
|
|
1430
|
-
journal.deployment.attempt.smoke === 'passed'
|
|
1431
|
-
? journal.deployment.attempt
|
|
1432
|
-
: journal.deployment?.last_verified;
|
|
1544
|
+
const recordedVerified = verifiedDeployment(journal.deployment);
|
|
1433
1545
|
if (current.versions.length > 1 &&
|
|
1434
1546
|
journal.deployment?.attempt.phase !== 'activation_pending' &&
|
|
1435
1547
|
!(recordedVerified && equal(current.versions, distribution(recordedVerified))))
|
|
@@ -1475,14 +1587,7 @@ export async function deploy(o, operations = {}) {
|
|
|
1475
1587
|
if (installedNames.length && !contract.secretInheritance)
|
|
1476
1588
|
throw new CloudflareError('capability_unknown');
|
|
1477
1589
|
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);
|
|
1590
|
+
const resuming = priorAttempt && frozenLocalAuthAttempt(priorAttempt, 'deploy') !== undefined;
|
|
1486
1591
|
if (resuming) {
|
|
1487
1592
|
if (priorAttempt.artifact_sha256 !== artifact.artifactHash)
|
|
1488
1593
|
return conflict('artifact_sha256');
|
|
@@ -1494,46 +1599,54 @@ export async function deploy(o, operations = {}) {
|
|
|
1494
1599
|
const removeSecret = () => invalid('Remove the secret first: wrangler secret delete PB_IDP_PROVIDERS');
|
|
1495
1600
|
if (installedNames.includes('PB_IDP_PROVIDERS'))
|
|
1496
1601
|
return removeSecret();
|
|
1497
|
-
const sources =
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
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
|
-
}
|
|
1602
|
+
const sources = exists
|
|
1603
|
+
? await readLocalAuthSources({
|
|
1604
|
+
api,
|
|
1605
|
+
base,
|
|
1606
|
+
versions: current.versions,
|
|
1607
|
+
settings: workerSettings,
|
|
1608
|
+
prior,
|
|
1609
|
+
stage: 'deploy',
|
|
1610
|
+
})
|
|
1611
|
+
: [];
|
|
1612
|
+
const localAuth = resolveLocalAuth({
|
|
1613
|
+
flag: o.localAuth,
|
|
1614
|
+
verified: recordedVerified,
|
|
1615
|
+
env: o.env.PB_AUTH_LOCAL,
|
|
1616
|
+
sources: exists ? sources : null,
|
|
1617
|
+
secretNames: [...selectedSecrets, ...installedNames],
|
|
1618
|
+
...(resuming ? { frozen: priorAttempt } : {}),
|
|
1619
|
+
});
|
|
1620
|
+
const liveModes = liveLocalAuthModes(sources, 'deploy');
|
|
1621
|
+
if (liveModes.some((mode) => mode !== localAuth) ||
|
|
1622
|
+
(recordedVerified && localAuth !== (recordedVerified.local_auth ?? 'enabled')))
|
|
1623
|
+
output.checks.push({
|
|
1624
|
+
name: 'local_auth_change',
|
|
1625
|
+
status: 'notice',
|
|
1626
|
+
source: `Local sign-in configuration changes to ${localAuth}.`,
|
|
1627
|
+
});
|
|
1628
|
+
assertLocalAuthAccepted(localAuth, artifact.localAuth);
|
|
1629
|
+
output.checks.push({ name: 'local_auth', status: localAuth });
|
|
1630
|
+
const providersText = await resolveProviderText(artifact.identity, o.providers, sources, resuming ? priorAttempt : undefined, checkManaged);
|
|
1532
1631
|
const identityHash = identityDigest(artifact.identity, providersText);
|
|
1632
|
+
if (recordedVerified &&
|
|
1633
|
+
!resuming &&
|
|
1634
|
+
identityHash !== (recordedVerified.identity_sha256 ?? null))
|
|
1635
|
+
output.checks.push({
|
|
1636
|
+
name: 'sso_artifact_upgrade',
|
|
1637
|
+
status: 'notice',
|
|
1638
|
+
source: 'The first deploy of a rebuilt artifact after upgrading counts as an SSO configuration change; one partial rollout will be refused. Deploy with --rollout 100 first.',
|
|
1639
|
+
});
|
|
1533
1640
|
const anchors = effectiveAnchors(artifact.identity, providersText);
|
|
1534
1641
|
if (o.dryRun)
|
|
1535
1642
|
output.identity = dryRunIdentity(artifact.identity, providersText, identityHash);
|
|
1643
|
+
const effectiveProviders = artifact.identity === null
|
|
1644
|
+
? []
|
|
1645
|
+
: providerPlan(artifact.identity, providersText).plan.effective;
|
|
1646
|
+
if (localAuth === 'disabled' && effectiveProviders.length === 0)
|
|
1647
|
+
return invalid('Cannot disable local sign-in without a usable SSO provider');
|
|
1536
1648
|
const plainBindings = bindings(journal, artifact.ports, url);
|
|
1649
|
+
plainBindings.push({ type: 'plain_text', name: 'PB_AUTH_LOCAL', text: localAuth });
|
|
1537
1650
|
if (providersText !== null)
|
|
1538
1651
|
plainBindings.push({ type: 'plain_text', name: 'PB_IDP_PROVIDERS', text: providersText });
|
|
1539
1652
|
if (endpointEnv) {
|
|
@@ -1550,8 +1663,11 @@ export async function deploy(o, operations = {}) {
|
|
|
1550
1663
|
? pending.secret_names
|
|
1551
1664
|
: [];
|
|
1552
1665
|
const missingSecrets = [
|
|
1553
|
-
...new Set(
|
|
1554
|
-
|
|
1666
|
+
...new Set([
|
|
1667
|
+
...parseRuntimeProviders(providersText ?? undefined).accepted,
|
|
1668
|
+
...(localAuth === 'disabled' ? effectiveProviders : []),
|
|
1669
|
+
]
|
|
1670
|
+
.filter((p) => p.kind === 'oidc')
|
|
1555
1671
|
.map((p) => providerSecretName(p.id, 'CLIENT_SECRET'))),
|
|
1556
1672
|
].filter((name) => !installedNames.includes(name) &&
|
|
1557
1673
|
!pendingNames.includes(name) &&
|
|
@@ -1627,9 +1743,7 @@ export async function deploy(o, operations = {}) {
|
|
|
1627
1743
|
ledgerHash,
|
|
1628
1744
|
}));
|
|
1629
1745
|
const priorJournal = journal.deployment;
|
|
1630
|
-
const verified =
|
|
1631
|
-
? priorAttempt
|
|
1632
|
-
: (priorJournal?.last_verified ?? null);
|
|
1746
|
+
const verified = verifiedDeployment(priorJournal);
|
|
1633
1747
|
const livePlain = workerSettings &&
|
|
1634
1748
|
workerSettings.bindings
|
|
1635
1749
|
.map(object)
|
|
@@ -1642,6 +1756,12 @@ export async function deploy(o, operations = {}) {
|
|
|
1642
1756
|
if (anchors.length &&
|
|
1643
1757
|
(await anchorPreflight({ ...auth, ...(o.fetch ? { fetch: o.fetch } : {}) }, journal.resources.d1?.id, anchors, o.dryRun === true)) === 'skipped')
|
|
1644
1758
|
output.identity?.skipped_checks.push('anchor_preflight');
|
|
1759
|
+
if (rollout < 100 &&
|
|
1760
|
+
!resuming &&
|
|
1761
|
+
(localAuth !== (verified?.local_auth ?? 'enabled') ||
|
|
1762
|
+
sources.some((source) => resolveLocalAuth({ sources: [source] }) !==
|
|
1763
|
+
localAuth)))
|
|
1764
|
+
return invalid('partial rollout is not allowed when local sign-in configuration changes; deploy with --rollout 100 first');
|
|
1645
1765
|
if (verified &&
|
|
1646
1766
|
plainMatch &&
|
|
1647
1767
|
verified.artifact_sha256 === artifact.artifactHash &&
|
|
@@ -1651,6 +1771,7 @@ export async function deploy(o, operations = {}) {
|
|
|
1651
1771
|
equal(current.versions, distribution(verified)) &&
|
|
1652
1772
|
equal(withoutIds(live), withoutIds(intended)) &&
|
|
1653
1773
|
!updates.some((b) => b.name !== 'PB_MASTER_KEY' && b.name !== 'PB_MASTER_KEY_PREVIOUS')) {
|
|
1774
|
+
await version(api, base, verified.version_id, verified, allNames, plainBindings);
|
|
1654
1775
|
outputAttempt(output, verified);
|
|
1655
1776
|
output.plan.push({ stage: 'deploy', action: 'unchanged' });
|
|
1656
1777
|
output.checks.push({ name: 'endpoint_policy', status: 'verified', ...policy });
|
|
@@ -1673,30 +1794,37 @@ export async function deploy(o, operations = {}) {
|
|
|
1673
1794
|
if (priorAttempt.settings_sha256 !== settingsHash)
|
|
1674
1795
|
return conflict('settings_sha256');
|
|
1675
1796
|
}
|
|
1797
|
+
if (resuming &&
|
|
1798
|
+
(priorAttempt.version_id ||
|
|
1799
|
+
priorAttempt.phase === 'upload_pending' ||
|
|
1800
|
+
priorAttempt.script_creation)) {
|
|
1801
|
+
let id = priorAttempt.version_id;
|
|
1802
|
+
const creation = priorAttempt.script_creation !== undefined;
|
|
1803
|
+
if (!id) {
|
|
1804
|
+
const versions = await api.list(`${base}/versions`, 'items');
|
|
1805
|
+
const matches = versions.filter((v) => versionAnnotationsMatch(object(v.annotations), priorAttempt, creation));
|
|
1806
|
+
if (matches.length !== 1 || !uuid(matches[0].id))
|
|
1807
|
+
return unknown();
|
|
1808
|
+
id = matches[0].id;
|
|
1809
|
+
}
|
|
1810
|
+
await version(api, base, id, priorAttempt, allNames, plainBindings, creation);
|
|
1811
|
+
}
|
|
1676
1812
|
if (!resuming && current.versions.length > 1)
|
|
1677
1813
|
return conflict('deployment');
|
|
1678
1814
|
checkManaged();
|
|
1679
1815
|
function checkManaged() {
|
|
1680
1816
|
if (resuming)
|
|
1681
1817
|
return;
|
|
1682
|
-
|
|
1683
|
-
if (priorAttempt?.phase === 'failed' || priorAttempt?.phase === 'rolled_back')
|
|
1684
|
-
managed =
|
|
1685
|
-
restoredSnapshot(priorAttempt, priorAttempt.phase === 'failed' && equal(current, beforeCandidate(priorAttempt))
|
|
1686
|
-
? live
|
|
1687
|
-
: undefined) ?? conflict('managed_settings');
|
|
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');
|
|
1690
|
-
// Never take over pre-existing attachments without a previous managed receipt.
|
|
1691
|
-
if ((live.consumer || live.schedules.length || live.routes.length || live.domain) &&
|
|
1692
|
-
(!managed || !matchesManaged(live, managed)))
|
|
1693
|
-
return conflict('managed_settings');
|
|
1818
|
+
checkManagedSettings(priorAttempt, recordedVerified, live, current, requestedRoutes);
|
|
1694
1819
|
}
|
|
1695
1820
|
if (priorJournal) {
|
|
1696
1821
|
const pendingHistory = structuredClone(priorJournal);
|
|
1697
1822
|
recordDeploymentHistory(pendingHistory, {
|
|
1698
1823
|
plainBinding: { name: 'PB_PUBLIC_URL', text: url },
|
|
1699
1824
|
});
|
|
1825
|
+
recordDeploymentHistory(pendingHistory, {
|
|
1826
|
+
plainBinding: { name: 'PB_AUTH_LOCAL', text: localAuth },
|
|
1827
|
+
});
|
|
1700
1828
|
recordDeploymentHistory(pendingHistory, {
|
|
1701
1829
|
reserveVersion: !resuming || !priorAttempt.version_id,
|
|
1702
1830
|
...(endpointEnv ? { plainBinding: { name: endpointEnv, text: telemetryEndpoint } } : {}),
|
|
@@ -1719,6 +1847,7 @@ export async function deploy(o, operations = {}) {
|
|
|
1719
1847
|
if (resuming) {
|
|
1720
1848
|
a = priorAttempt;
|
|
1721
1849
|
a.identity_sha256 = identityHash;
|
|
1850
|
+
a.local_auth = localAuth;
|
|
1722
1851
|
}
|
|
1723
1852
|
else {
|
|
1724
1853
|
a = {
|
|
@@ -1728,6 +1857,7 @@ export async function deploy(o, operations = {}) {
|
|
|
1728
1857
|
settings_sha256: settingsHash,
|
|
1729
1858
|
providers_sha256: providersText === null ? null : digest(providersText),
|
|
1730
1859
|
identity_sha256: identityHash,
|
|
1860
|
+
local_auth: localAuth,
|
|
1731
1861
|
...(providersText === null ? {} : { providers_text: providersText }),
|
|
1732
1862
|
secret_names: allNames,
|
|
1733
1863
|
build_info: artifact.info,
|
|
@@ -1757,6 +1887,9 @@ export async function deploy(o, operations = {}) {
|
|
|
1757
1887
|
last_verified: verified,
|
|
1758
1888
|
...(priorJournal?.history ? { history: structuredClone(priorJournal.history) } : {}),
|
|
1759
1889
|
};
|
|
1890
|
+
recordDeploymentHistory(journal.deployment, {
|
|
1891
|
+
plainBinding: { name: 'PB_AUTH_LOCAL', text: localAuth },
|
|
1892
|
+
});
|
|
1760
1893
|
recordDeploymentHistory(journal.deployment, {
|
|
1761
1894
|
...(priorAttempt?.version_id ? { versionId: priorAttempt.version_id } : {}),
|
|
1762
1895
|
...(endpointEnv ? { plainBinding: { name: endpointEnv, text: telemetryEndpoint } } : {}),
|
|
@@ -1764,10 +1897,16 @@ export async function deploy(o, operations = {}) {
|
|
|
1764
1897
|
await save(store, journal, now);
|
|
1765
1898
|
}
|
|
1766
1899
|
attemptStarted = true;
|
|
1767
|
-
if (
|
|
1900
|
+
if (journal.deployment) {
|
|
1768
1901
|
recordDeploymentHistory(journal.deployment, {
|
|
1769
|
-
plainBinding: { name:
|
|
1902
|
+
plainBinding: { name: 'PB_AUTH_LOCAL', text: localAuth },
|
|
1770
1903
|
});
|
|
1904
|
+
if (endpointEnv)
|
|
1905
|
+
recordDeploymentHistory(journal.deployment, {
|
|
1906
|
+
plainBinding: { name: endpointEnv, text: telemetryEndpoint },
|
|
1907
|
+
});
|
|
1908
|
+
await save(store, journal, now);
|
|
1909
|
+
}
|
|
1771
1910
|
outputAttempt(output, a);
|
|
1772
1911
|
if (a.script_creation?.state === 'pending' ||
|
|
1773
1912
|
(a.script_creation?.state === 'complete' && a.phase === 'preparing_upload')) {
|
|
@@ -1800,6 +1939,7 @@ export async function deploy(o, operations = {}) {
|
|
|
1800
1939
|
form.set('metadata', JSON.stringify({
|
|
1801
1940
|
...COMPATIBILITY,
|
|
1802
1941
|
main_module: 'worker.js',
|
|
1942
|
+
limits: { cpu_ms: cpuMs },
|
|
1803
1943
|
bindings: [...plainBindings, ...updates],
|
|
1804
1944
|
...(installedNames.length ? { keep_bindings: ['secret_text'] } : {}),
|
|
1805
1945
|
assets: { jwt: token, config: ASSET_CONFIG },
|
|
@@ -1871,6 +2011,10 @@ export async function deploy(o, operations = {}) {
|
|
|
1871
2011
|
if (!a.version_id)
|
|
1872
2012
|
return unknown();
|
|
1873
2013
|
await version(api, base, a.version_id, a, allNames, plainBindings);
|
|
2014
|
+
recordDeploymentHistory(journal.deployment, {
|
|
2015
|
+
plainBinding: { name: 'PB_AUTH_LOCAL', text: localAuth },
|
|
2016
|
+
});
|
|
2017
|
+
await save(store, journal, now);
|
|
1874
2018
|
if (providersText !== null) {
|
|
1875
2019
|
recordDeploymentHistory(journal.deployment, {
|
|
1876
2020
|
plainBinding: { name: 'PB_IDP_PROVIDERS', text: providersText },
|