@fabrika/provider-zerops 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/src/cli-args.ts +2 -1
- package/src/descriptor.ts +45 -1
- package/src/installation/admin-options.ts +4 -32
- package/src/installation/deploy-options.ts +6 -34
- package/src/installation/index.ts +41 -17
- package/src/installation/init-options.ts +148 -0
- package/src/installation/init.ts +94 -221
- package/src/installation/install-collaborators.ts +2 -3
- package/src/installation/install-options.ts +6 -43
- package/src/installation/install.ts +7 -31
- package/src/installation/sidecar.ts +4 -1
- package/src/installation/upgrade-options.ts +3 -12
- package/src/managed-env.ts +11 -3
- package/src/manifest.ts +6 -3
- package/src/provider.ts +1 -9
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// The pin is checked BEFORE anything leaves this machine: `assertPinnedTag` refuses a branch or a SHA
|
|
9
9
|
// (ADR-0025) without a network call, so `--to=main` fails on the spot rather than after a clone.
|
|
10
10
|
|
|
11
|
+
import { describeArgument, readFlag } from '@fabrika/installation-init'
|
|
11
12
|
import { assertPinnedTag, defaultCheckoutDir } from './sidecar'
|
|
12
13
|
|
|
13
14
|
/** Where the sidecar repository this command edits is found. */
|
|
@@ -43,16 +44,6 @@ export const UPGRADE_DRY_RUN_FLAG = '--dry-run'
|
|
|
43
44
|
*/
|
|
44
45
|
const REPO_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._-]*$/
|
|
45
46
|
|
|
46
|
-
const readFlag = (argv: readonly string[], name: string): string | undefined => {
|
|
47
|
-
const prefix = `${name}=`
|
|
48
|
-
const matches = argv.filter((arg) => arg.startsWith(prefix))
|
|
49
|
-
if (matches.length > 1) {
|
|
50
|
-
throw new Error(`${name} was given more than once`)
|
|
51
|
-
}
|
|
52
|
-
const value = matches[0]?.slice(prefix.length)
|
|
53
|
-
return value === undefined || value.trim() === '' ? undefined : value.trim()
|
|
54
|
-
}
|
|
55
|
-
|
|
56
47
|
const assertKnownArguments = (argv: readonly string[]): void => {
|
|
57
48
|
for (const arg of argv) {
|
|
58
49
|
if (arg === UPGRADE_DRY_RUN_FLAG || !arg.startsWith('-')) {
|
|
@@ -61,7 +52,7 @@ const assertKnownArguments = (argv: readonly string[]): void => {
|
|
|
61
52
|
const name = arg.split('=')[0] ?? arg
|
|
62
53
|
if (!FLAGS.some((flag) => flag === name) || !arg.includes('=')) {
|
|
63
54
|
throw new Error(
|
|
64
|
-
`unexpected argument
|
|
55
|
+
`unexpected argument ${describeArgument(arg)}. Run \`fabrika platform upgrade --provider=zerops --help\` for the surface`,
|
|
65
56
|
)
|
|
66
57
|
}
|
|
67
58
|
}
|
|
@@ -71,7 +62,7 @@ const assertKnownArguments = (argv: readonly string[]): void => {
|
|
|
71
62
|
const readInstallation = (argv: readonly string[]): string | undefined => {
|
|
72
63
|
const positional = argv.filter((arg) => !arg.startsWith('-'))
|
|
73
64
|
if (positional.length > 1) {
|
|
74
|
-
throw new Error(
|
|
65
|
+
throw new Error('unexpected second positional argument: name ONE installation')
|
|
75
66
|
}
|
|
76
67
|
const installation = positional[0]?.trim()
|
|
77
68
|
return installation === undefined || installation === '' ? undefined : installation
|
package/src/managed-env.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { JsonValue } from '@fabrika/provider-contract'
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
import { renderYaml } from './compile'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
assertOwnRunWhenExtending,
|
|
6
|
+
executionZeropsDocument,
|
|
7
|
+
parseZeropsDescriptor,
|
|
8
|
+
type PreparedZeropsDescriptor,
|
|
9
|
+
selectZeropsDescriptorSetup,
|
|
10
|
+
} from './descriptor'
|
|
5
11
|
|
|
6
12
|
export type ManagedKey =
|
|
7
13
|
| 'FABRIKA_RELEASE'
|
|
@@ -93,7 +99,9 @@ export function overlayRelease(contents: string, setup: string | undefined, rele
|
|
|
93
99
|
const parsed = parseZeropsDescriptor(contents)
|
|
94
100
|
assertAuthoredManagedDocument(parsed.document)
|
|
95
101
|
const selected = selectZeropsDescriptorSetup(parsed, setup)
|
|
96
|
-
|
|
102
|
+
assertOwnRunWhenExtending(parsed, selected)
|
|
103
|
+
const execution = executionZeropsDocument(parsed, selected)
|
|
104
|
+
const entries = execution['zerops']
|
|
97
105
|
if (!Array.isArray(entries)) throw new Error('zerops: descriptor requires setup entries')
|
|
98
106
|
const overlaid = release === null ? entries : entries.map((entry) => {
|
|
99
107
|
if (!isMapping(entry) || entry['setup'] !== selected) return entry
|
|
@@ -101,5 +109,5 @@ export function overlayRelease(contents: string, setup: string | undefined, rele
|
|
|
101
109
|
const env = isMapping(run['envVariables']) ? run['envVariables'] : {}
|
|
102
110
|
return { ...entry, run: { ...run, envVariables: { ...env, FABRIKA_RELEASE: release } } }
|
|
103
111
|
})
|
|
104
|
-
return { contents: renderYaml({ ...
|
|
112
|
+
return { contents: renderYaml({ ...execution, zerops: overlaid }), setup: selected }
|
|
105
113
|
}
|
package/src/manifest.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { JsonValue, ProviderCodec } from '@fabrika/provider-contract'
|
|
|
4
4
|
import { normalizeGateRule, proxyGateRuleFieldsSchema } from '@fabrika/proxy-core/manifest'
|
|
5
5
|
import { z } from 'zod'
|
|
6
6
|
import { compileImport, ENV_ISOLATION, OVERRIDE, renderYaml } from './compile'
|
|
7
|
-
import { parseZeropsDescriptor, selectZeropsDescriptorSetup } from './descriptor'
|
|
7
|
+
import { assertOwnRunWhenExtending, executionZeropsDocument, parseZeropsDescriptor, selectZeropsDescriptorSetup } from './descriptor'
|
|
8
8
|
import { assertAuthoredManagedDocument, assertAuthoredManagedInputs } from './managed-env'
|
|
9
9
|
import { allowedReferenceHosts, foreignReferenceHost } from './service-references'
|
|
10
10
|
import type { ZeropsAppConfig, ZeropsNamespaceResourceRequirement, ZeropsProxySpec, ZeropsSharedPostgresBinding } from './types'
|
|
@@ -288,14 +288,17 @@ export function parseFabrikaManifest(value: unknown, expected: ManifestExpectati
|
|
|
288
288
|
if (!serviceHostnames.includes(deployService)) {
|
|
289
289
|
throw new Error('invalid fabrika manifest Zerops deploy service')
|
|
290
290
|
}
|
|
291
|
+
// Only the selected setup and what it extends reach this app's service, so a sibling setup for another
|
|
292
|
+
// placement may reference services this app does not own.
|
|
293
|
+
const selectedSetup = selectZeropsDescriptorSetup(parsedDescriptor, zeropsSetup)
|
|
294
|
+
assertOwnRunWhenExtending(parsedDescriptor, selectedSetup)
|
|
291
295
|
const foreignInDescriptor = foreignReferenceInValue(
|
|
292
|
-
parsedDescriptor
|
|
296
|
+
executionZeropsDocument(parsedDescriptor, selectedSetup),
|
|
293
297
|
allowedReferenceHosts(serviceHostnames),
|
|
294
298
|
)
|
|
295
299
|
if (foreignInDescriptor !== null) {
|
|
296
300
|
throw new Error(`invalid fabrika manifest: zerops.yaml references service \`${foreignInDescriptor}\`, which this app does not own`)
|
|
297
301
|
}
|
|
298
|
-
selectZeropsDescriptorSetup(parsedDescriptor, zeropsSetup)
|
|
299
302
|
if (proxy !== undefined) {
|
|
300
303
|
const separator = proxy.upstream.indexOf(':')
|
|
301
304
|
const upstreamHostname = separator === -1 ? proxy.upstream : proxy.upstream.slice(0, separator)
|
package/src/provider.ts
CHANGED
|
@@ -77,8 +77,6 @@ const POLL_INTERVAL_MS = 3000
|
|
|
77
77
|
/** A run stops waiting for its version this long after it started. Zerops' own pipeline limit is 1 hour. */
|
|
78
78
|
export const ZEROPS_VERSION_DEADLINE_MS = 75 * 60 * 1000
|
|
79
79
|
|
|
80
|
-
const RELEASE_KEY = 'FABRIKA_RELEASE'
|
|
81
|
-
|
|
82
80
|
/** Abandon the step if the run was cancelled. Checked before every real mutation and each poll iteration. */
|
|
83
81
|
const assertRunning = (signal: AbortSignal): void => {
|
|
84
82
|
if (signal.aborted) {
|
|
@@ -419,7 +417,7 @@ const writeServiceEnv = async (env: StepEnv): Promise<void> => {
|
|
|
419
417
|
const written = SERVICE_MANAGED_KEYS.filter((key) => managedValues[key] !== null)
|
|
420
418
|
const removed = SERVICE_MANAGED_KEYS.filter((key) => managedValues[key] === null)
|
|
421
419
|
log(` [dry-run] would write service variables on ${serviceId}: ${written.join(', ') || '(none)'}`)
|
|
422
|
-
log(` [dry-run] would remove service variables on ${serviceId} if present: ${
|
|
420
|
+
log(` [dry-run] would remove service variables on ${serviceId} if present: ${removed.join(', ') || '(none)'}`)
|
|
423
421
|
return
|
|
424
422
|
}
|
|
425
423
|
assertRunning(signal)
|
|
@@ -439,12 +437,6 @@ const writeServiceEnv = async (env: StepEnv): Promise<void> => {
|
|
|
439
437
|
log(` ${key}: unchanged`)
|
|
440
438
|
}
|
|
441
439
|
}
|
|
442
|
-
const strayRelease = records.find((record) => record.key === RELEASE_KEY)
|
|
443
|
-
if (strayRelease !== undefined) {
|
|
444
|
-
assertRunning(signal)
|
|
445
|
-
await zerops.api.deleteServiceEnv({ envId: strayRelease.id, signal })
|
|
446
|
-
log(` ${RELEASE_KEY}: removed`)
|
|
447
|
-
}
|
|
448
440
|
}
|
|
449
441
|
|
|
450
442
|
/** Run one step's effect. Resolves on success, throws on failure (caught + recorded by `runSteps`). */
|