@astrale-os/sdk 0.6.0-beta.13 → 0.6.0-beta.14
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 +4 -0
- package/dist/deployment/adapter/adapter.d.ts +1 -6
- package/dist/deployment/adapter/legacy/adapter.d.ts +0 -10
- package/dist/deployment/adapter/legacy/deploy/context.d.ts +0 -2
- package/dist/deployment/adapter/legacy/deploy/result.d.ts +2 -3
- package/dist/deployment/adapter/legacy/index.d.ts +10 -8
- package/dist/deployment/adapter/legacy/prepare/context.d.ts +0 -2
- package/dist/deployment/adapter/release/context.d.ts +16 -2
- package/dist/deployment/address/index.d.ts +1 -1
- package/dist/deployment/address/index.js +1 -0
- package/dist/deployment/address/summary.d.ts +30 -0
- package/dist/deployment/address/summary.js +117 -1
- package/dist/deployment/build/index.d.ts +1 -1
- package/dist/deployment/build/index.js +1 -1
- package/dist/deployment/build/manifest.d.ts +5 -0
- package/dist/deployment/build/manifest.js +10 -0
- package/dist/deployment/index.d.ts +1 -1
- package/dist/deployment/release/legacy/index.d.ts +15 -10
- package/dist/deployment/release/legacy/index.js +15 -10
- package/dist/deployment/verify/readiness/deployment.js +3 -13
- package/dist/deployment/verify/readiness/fetch.d.ts +13 -0
- package/dist/deployment/verify/readiness/fetch.js +26 -0
- package/dist/deployment/verify/readiness/identity.d.ts +3 -6
- package/dist/deployment/verify/readiness/identity.js +3 -3
- package/dist/deployment/verify/readiness/index.d.ts +12 -8
- package/dist/deployment/verify/readiness/index.js +15 -4
- package/dist/deployment/verify/readiness/legacy/index.d.ts +2 -0
- package/dist/deployment/verify/readiness/legacy/index.js +2 -0
- package/dist/deployment/verify/readiness/legacy/publication.d.ts +12 -0
- package/dist/deployment/verify/readiness/legacy/publication.js +23 -0
- package/dist/deployment/verify/readiness/release.d.ts +8 -0
- package/dist/deployment/verify/readiness/release.js +22 -0
- package/dist/execution/identity/authentication.d.ts +7 -0
- package/dist/execution/identity/authentication.js +7 -1
- package/dist/execution/identity/delivery.d.ts +11 -4
- package/dist/execution/identity/delivery.js +47 -20
- package/dist/execution/identity/identity.d.ts +7 -3
- package/dist/execution/identity/identity.js +2 -2
- package/dist/execution/invocation/admission/authentication.d.ts +1 -2
- package/dist/execution/invocation/admission/authentication.js +1 -2
- package/dist/execution/invocation/admission/authority.d.ts +10 -4
- package/dist/execution/invocation/admission/authority.js +5 -1
- package/dist/execution/invocation/invocation.js +51 -14
- package/dist/execution/runtime/index.d.ts +1 -0
- package/dist/execution/runtime/index.js +1 -0
- package/dist/execution/runtime/load.d.ts +3 -3
- package/dist/execution/runtime/load.js +3 -2
- package/dist/execution/runtime/release.d.ts +22 -0
- package/dist/execution/runtime/release.js +14 -0
- package/dist/execution/serve.d.ts +2 -2
- package/dist/execution/service/create.js +9 -7
- package/dist/execution/service/release.d.ts +2 -2
- package/dist/execution/service/release.js +3 -2
- package/dist/execution/service/service.d.ts +9 -5
- package/dist/execution/service/serving/delivery.d.ts +6 -0
- package/dist/execution/service/{publication → serving}/delivery.js +5 -1
- package/dist/execution/service/{publication → serving}/index.d.ts +1 -1
- package/dist/execution/service/serving/installation.d.ts +4 -0
- package/dist/execution/service/serving/installation.js +9 -0
- package/dist/execution/service/serving/legacy/index.d.ts +15 -0
- package/dist/execution/service/serving/legacy/index.js +19 -0
- package/dist/execution/service/serving/source.d.ts +18 -0
- package/dist/execution/service/serving/source.js +19 -0
- package/dist/project/define.js +57 -28
- package/dist/project/install-command.d.ts +10 -0
- package/dist/project/install-command.js +13 -0
- package/dist/project/project.d.ts +4 -2
- package/dist/tooling/cli/arguments.d.ts +0 -2
- package/dist/tooling/cli/arguments.js +22 -15
- package/dist/tooling/cli/commit.d.ts +23 -0
- package/dist/tooling/cli/commit.js +216 -0
- package/dist/tooling/cli/deploy-mode.d.ts +19 -12
- package/dist/tooling/cli/deploy-mode.js +14 -32
- package/dist/tooling/cli/deploy-result.d.ts +10 -7
- package/dist/tooling/cli/deploy-result.js +4 -3
- package/dist/tooling/cli/development/candidate.d.ts +2 -3
- package/dist/tooling/cli/development/candidate.js +2 -9
- package/dist/tooling/cli/development/deployment.d.ts +2 -8
- package/dist/tooling/cli/development/deployment.js +2 -21
- package/dist/tooling/cli/development/develop.d.ts +4 -3
- package/dist/tooling/cli/development/develop.js +8 -112
- package/dist/tooling/cli/development/report.d.ts +1 -3
- package/dist/tooling/cli/development/report.js +1 -4
- package/dist/tooling/cli/help.js +28 -26
- package/dist/tooling/cli/immutable-deployment.d.ts +27 -0
- package/dist/tooling/cli/immutable-deployment.js +95 -0
- package/dist/tooling/cli/index.d.ts +2 -4
- package/dist/tooling/cli/index.js +2 -3
- package/dist/tooling/cli/legacy/deploy-result.d.ts +5 -3
- package/dist/tooling/cli/legacy/deploy-result.js +5 -3
- package/dist/tooling/cli/legacy/stable-target-adapter.d.ts +5 -3
- package/dist/tooling/cli/legacy/stable-target-deploy.d.ts +42 -0
- package/dist/tooling/cli/legacy/stable-target-deploy.js +104 -0
- package/dist/tooling/cli/orchestrate.js +46 -97
- package/dist/tooling/cli/secrets.d.ts +9 -0
- package/dist/tooling/cli/secrets.js +19 -0
- package/package.json +1 -1
- package/dist/deployment/verify/readiness/publication.d.ts +0 -11
- package/dist/deployment/verify/readiness/publication.js +0 -41
- package/dist/execution/service/publication/delivery.d.ts +0 -2
- package/dist/execution/service/publication/installation.d.ts +0 -3
- package/dist/execution/service/publication/installation.js +0 -8
- package/dist/execution/service/publication/source.d.ts +0 -3
- package/dist/execution/service/publication/source.js +0 -11
- package/dist/tooling/cli/astrale/authority.d.ts +0 -5
- package/dist/tooling/cli/astrale/authority.js +0 -11
- package/dist/tooling/cli/astrale/executable.d.ts +0 -2
- package/dist/tooling/cli/astrale/executable.js +0 -47
- package/dist/tooling/cli/astrale/failure.d.ts +0 -6
- package/dist/tooling/cli/astrale/failure.js +0 -48
- package/dist/tooling/cli/astrale/instance.d.ts +0 -11
- package/dist/tooling/cli/astrale/instance.js +0 -52
- package/dist/tooling/cli/astrale/process.d.ts +0 -13
- package/dist/tooling/cli/astrale/process.js +0 -94
- package/dist/tooling/cli/astrale/version.d.ts +0 -18
- package/dist/tooling/cli/astrale/version.js +0 -55
- package/dist/tooling/cli/development/release-evidence.d.ts +0 -4
- package/dist/tooling/cli/development/release-evidence.js +0 -10
- package/dist/tooling/cli/development/session-file.d.ts +0 -53
- package/dist/tooling/cli/development/session-file.js +0 -337
- package/dist/tooling/cli/development/target-lock.d.ts +0 -16
- package/dist/tooling/cli/development/target-lock.js +0 -68
- package/dist/tooling/cli/development/view.d.ts +0 -28
- package/dist/tooling/cli/development/view.js +0 -151
- package/dist/tooling/cli/environment-release.d.ts +0 -24
- package/dist/tooling/cli/environment-release.js +0 -46
- package/dist/tooling/cli/evidence.d.ts +0 -14
- package/dist/tooling/cli/evidence.js +0 -53
- package/dist/tooling/cli/installation/index.d.ts +0 -2
- package/dist/tooling/cli/installation/index.js +0 -1
- package/dist/tooling/cli/installation/observation.d.ts +0 -10
- package/dist/tooling/cli/installation/observation.js +0 -44
- package/dist/tooling/cli/installation/reconcile.d.ts +0 -32
- package/dist/tooling/cli/installation/reconcile.js +0 -122
- /package/dist/execution/service/{publication → serving}/index.js +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { verifyDeployment, verifyPreflight } from '../../../deployment/index.js';
|
|
2
|
+
import { bundleBuild } from '../build.js';
|
|
3
|
+
import { error, warn } from '../log.js';
|
|
4
|
+
import { environmentSecrets } from '../secrets.js';
|
|
5
|
+
import { stableTargetDeployResult } from './deploy-result.js';
|
|
6
|
+
import { stableTargetAdapter } from './stable-target-adapter.js';
|
|
7
|
+
import { stableTargetBuildWarning } from './uploaded-build.js';
|
|
8
|
+
/**
|
|
9
|
+
* What a direct-mode deploy prints on stderr before any effect (AM-59): its Environment replaces
|
|
10
|
+
* one stable target in place and serves a v3 Publication only, which the deployment model
|
|
11
|
+
* replaces with one immutable deployment per release.
|
|
12
|
+
*
|
|
13
|
+
* @deprecated Stable-target deploy path only; see `deployStableTarget`.
|
|
14
|
+
*/
|
|
15
|
+
export function directModeDeprecation(environment, reasons) {
|
|
16
|
+
const selected = reasons.map((reason) => {
|
|
17
|
+
if (reason.reason === 'direct-parameter')
|
|
18
|
+
return reason.parameter;
|
|
19
|
+
return reason.reason === 'no-namespace' ? 'no namespace' : 'no deployRelease';
|
|
20
|
+
});
|
|
21
|
+
return (`Environment ${environment} deploys in direct mode (${selected.join(', ')}), which is ` +
|
|
22
|
+
'deprecated and will be removed: it replaces one stable target in place and serves a v3 ' +
|
|
23
|
+
'Publication only. Its replacement deploys one immutable deployment per release, through an ' +
|
|
24
|
+
'adapter that implements deployRelease.');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Deploy one Environment on the legacy direct-mode path (CT33): bundle exactly as
|
|
28
|
+
* `astrale-domain build` does, let `prepare` place that Bundle at the Environment's stable target
|
|
29
|
+
* and `deploy` replace the code it runs, then verify the v3 Publication it serves. It warns first,
|
|
30
|
+
* then behaves exactly as before. The SDK never installs the deployed Domain here and makes no
|
|
31
|
+
* Kernel call of its own; adapter-astrale's instance mode still calls the Services of its instance
|
|
32
|
+
* through that instance's Kernel, and installs the Services Domain there when it is missing.
|
|
33
|
+
*
|
|
34
|
+
* Returns the deploy result, or nothing once a failure is reported on stderr.
|
|
35
|
+
*
|
|
36
|
+
* @deprecated Stable-target deploy path only. Replacement: `deployImmutable`, the immutable
|
|
37
|
+
* deployment of one sealed release through `deployRelease`. Consumers: the direct-mode
|
|
38
|
+
* configurations of domains ai-gateway, closing, grc, integrations and issues (until SV15), domains
|
|
39
|
+
* organization (until it leaves direct mode, AM-147), the Services `beta` and `prod` (until SV12),
|
|
40
|
+
* `development` (until SV9) and frozen `admin-beta` (until D19) environments, shell (until SH2)
|
|
41
|
+
* and its frozen `legacy-v3` (until D22), ui:domain (until UI1), and adapter-astrale's instance
|
|
42
|
+
* mode until adapter-astrale implements deployRelease (S8). Deleted with the legacy deploy path
|
|
43
|
+
* (D10), once no configuration on any repository's main selects it (CT33, AM-147).
|
|
44
|
+
*/
|
|
45
|
+
export async function deployStableTarget(input) {
|
|
46
|
+
const { build, signal, report } = input;
|
|
47
|
+
warn(directModeDeprecation(input.environment, input.reasons));
|
|
48
|
+
const adapter = stableTargetAdapter(input.definition.deployment.adapter);
|
|
49
|
+
const parameters = input.definition.deployment.parameters;
|
|
50
|
+
// `deploy` rebuilds exactly as `build` does; preparation then only places that Bundle.
|
|
51
|
+
const bundle = await bundleBuild({
|
|
52
|
+
bundler: adapter.bundler,
|
|
53
|
+
projectDir: input.projectDir,
|
|
54
|
+
build,
|
|
55
|
+
runtime: input.runtime,
|
|
56
|
+
signal,
|
|
57
|
+
});
|
|
58
|
+
const source = await adapter.prepare(parameters, {
|
|
59
|
+
projectDir: input.projectDir,
|
|
60
|
+
environment: input.environment,
|
|
61
|
+
mode: 'release',
|
|
62
|
+
signal,
|
|
63
|
+
build,
|
|
64
|
+
runtime: input.runtime,
|
|
65
|
+
bundle,
|
|
66
|
+
});
|
|
67
|
+
const artifact = verifyPreflight({ build, runtime: input.runtime, source });
|
|
68
|
+
const secrets = environmentSecrets(adapter, parameters, input.projectDir, input.runtime.runtime);
|
|
69
|
+
const deployed = await adapter.deploy(parameters, {
|
|
70
|
+
projectDir: input.projectDir,
|
|
71
|
+
environment: input.environment,
|
|
72
|
+
...(input.identity === undefined ? {} : { identity: input.identity }),
|
|
73
|
+
secrets,
|
|
74
|
+
artifact,
|
|
75
|
+
signal,
|
|
76
|
+
});
|
|
77
|
+
const result = verifyDeployment(artifact, deployed);
|
|
78
|
+
if (result.status === 'failed') {
|
|
79
|
+
error(`Deployment failed during ${result.phase}: ${result.error.message}`);
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
const uploaded = result.progress.provider.evidence.build;
|
|
83
|
+
if (uploaded !== undefined) {
|
|
84
|
+
// prepare/deploy places a stable target: a differing upload only warns.
|
|
85
|
+
const warning = stableTargetBuildWarning(bundle, uploaded);
|
|
86
|
+
if (warning !== undefined)
|
|
87
|
+
warn(warning);
|
|
88
|
+
}
|
|
89
|
+
report(`Domain: ${result.release.publication.origin}`);
|
|
90
|
+
report(`Environment: ${input.environment}`);
|
|
91
|
+
report(`Adapter: ${adapter.name}@${adapter.version}`);
|
|
92
|
+
report(`Deployment: ${result.progress.provider.evidence.deployment}`);
|
|
93
|
+
if (result.progress.provider.evidence.preflight !== undefined) {
|
|
94
|
+
report(`Preflight: ${result.progress.provider.evidence.preflight}; public readiness verified`);
|
|
95
|
+
}
|
|
96
|
+
report(`Public: ${result.release.addressing.publicUrl}`);
|
|
97
|
+
report(`Publication: ${result.release.publication.schema.revision} · ${result.release.publication.etag}`);
|
|
98
|
+
report('Ready: deployment verified');
|
|
99
|
+
return stableTargetDeployResult({
|
|
100
|
+
environment: input.environment,
|
|
101
|
+
adapter,
|
|
102
|
+
deployment: result,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { dirname,
|
|
3
|
-
import { admitRuntime, compile, resolveRuntime
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { admitRuntime, compile, resolveRuntime } from '../../deployment/index.js';
|
|
4
4
|
import { packageDomain } from '../packaging/index.js';
|
|
5
5
|
import { parseArgs } from './arguments.js';
|
|
6
|
-
import { resolveCompatibleAstrale } from './astrale/version.js';
|
|
7
6
|
import { buildResult, bundleBuild, bundlerCopyWarnings, projectBundlers } from './build.js';
|
|
8
7
|
import { BunRequiredError } from './bun.js';
|
|
8
|
+
import { selectDeployMode } from './deploy-mode.js';
|
|
9
9
|
import { deprecationWarnings } from './deprecations.js';
|
|
10
10
|
import { develop } from './development/develop.js';
|
|
11
|
-
import { loadDeclaredSecrets } from './dotenv.js';
|
|
12
|
-
import { reconcileEnvironmentRelease } from './environment-release.js';
|
|
13
11
|
import { ERROR_USAGE, helpRequest, helpText } from './help.js';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { stableTargetBuildWarning } from './legacy/uploaded-build.js';
|
|
12
|
+
import { deployImmutable } from './immutable-deployment.js';
|
|
13
|
+
import { deployStableTarget } from './legacy/stable-target-deploy.js';
|
|
17
14
|
import { error, info, progress, warn } from './log.js';
|
|
18
15
|
import { importModule, loadProject, runtimeEntrypoint, selectEnvironment } from './project.js';
|
|
19
16
|
import { executeTests } from './test.js';
|
|
@@ -60,10 +57,18 @@ export async function run(argv) {
|
|
|
60
57
|
error(`No astrale.config.ts found in ${projectDir}.`);
|
|
61
58
|
return 1;
|
|
62
59
|
}
|
|
60
|
+
const project = await loadProject(configPath);
|
|
63
61
|
if (parsed.command === 'dev') {
|
|
62
|
+
// `dev` deploys only legacy direct-mode Environments; any other is refused before any effect.
|
|
63
|
+
const mode = selectDeployMode({
|
|
64
|
+
command: 'dev',
|
|
65
|
+
environment: parsed.env,
|
|
66
|
+
definition: selectEnvironment(project, parsed.env),
|
|
67
|
+
});
|
|
68
|
+
if (refused(mode))
|
|
69
|
+
return 1;
|
|
64
70
|
return executeDevelopmentCommand(parsed, { configPath, projectDir });
|
|
65
71
|
}
|
|
66
|
-
const project = await loadProject(configPath);
|
|
67
72
|
if (parsed.command === 'test') {
|
|
68
73
|
selectEnvironment(project, parsed.env);
|
|
69
74
|
return executeTests({
|
|
@@ -111,97 +116,56 @@ export async function run(argv) {
|
|
|
111
116
|
}
|
|
112
117
|
}
|
|
113
118
|
const environment = selectEnvironment(project, parsed.env);
|
|
119
|
+
// The configuration selects the deploy mode, before any effect (CT33).
|
|
120
|
+
const mode = selectDeployMode({
|
|
121
|
+
command: 'deploy',
|
|
122
|
+
environment: parsed.env,
|
|
123
|
+
definition: environment,
|
|
124
|
+
});
|
|
125
|
+
if (refused(mode))
|
|
126
|
+
return 1;
|
|
114
127
|
// With `--json`, stdout carries only the DeployResultV1; progress goes to stderr.
|
|
115
128
|
const report = progress(parsed.json === true);
|
|
116
|
-
const adapter = stableTargetAdapter(environment.deployment.adapter);
|
|
117
|
-
const parameters = environment.deployment.parameters;
|
|
118
129
|
const lifecycle = deploymentLifecycle();
|
|
119
130
|
try {
|
|
120
|
-
|
|
121
|
-
const bundle = await bundleBuild({
|
|
122
|
-
bundler: adapter.bundler,
|
|
123
|
-
projectDir,
|
|
124
|
-
build,
|
|
125
|
-
runtime: resolved,
|
|
126
|
-
signal: lifecycle.signal,
|
|
127
|
-
});
|
|
128
|
-
const source = await adapter.prepare(parameters, {
|
|
131
|
+
const deployment = {
|
|
129
132
|
projectDir,
|
|
130
133
|
environment: parsed.env,
|
|
131
|
-
...(environment.installation === undefined ? {} : { installation: environment.installation }),
|
|
132
|
-
mode: 'release',
|
|
133
|
-
signal: lifecycle.signal,
|
|
134
134
|
build,
|
|
135
135
|
runtime: resolved,
|
|
136
|
-
bundle,
|
|
137
|
-
});
|
|
138
|
-
const artifact = verifyPreflight({ build, runtime: resolved, source });
|
|
139
|
-
const secrets = secretsFor(adapter, parameters, projectDir, resolved.runtime);
|
|
140
|
-
const installationExecutable = environment.installation === undefined || parsed.deployOnly
|
|
141
|
-
? undefined
|
|
142
|
-
: await resolveCompatibleAstrale({ projectDir, signal: lifecycle.signal });
|
|
143
|
-
const deployed = await adapter.deploy(parameters, {
|
|
144
|
-
projectDir,
|
|
145
|
-
environment: parsed.env,
|
|
146
|
-
...(environment.installation === undefined ? {} : { installation: environment.installation }),
|
|
147
136
|
...(parsed.identity === undefined ? {} : { identity: parsed.identity }),
|
|
148
|
-
secrets,
|
|
149
|
-
artifact,
|
|
150
137
|
signal: lifecycle.signal,
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
report(`Domain: ${result.release.publication.origin}`);
|
|
165
|
-
report(`Environment: ${parsed.env}`);
|
|
166
|
-
report(`Adapter: ${adapter.name}@${adapter.version}`);
|
|
167
|
-
report(`Deployment: ${result.progress.provider.evidence.deployment}`);
|
|
168
|
-
if (result.progress.provider.evidence.preflight !== undefined) {
|
|
169
|
-
report(`Preflight: ${result.progress.provider.evidence.preflight}; public readiness verified`);
|
|
170
|
-
}
|
|
171
|
-
report(`Public: ${result.release.addressing.publicUrl}`);
|
|
172
|
-
report(`Publication: ${result.release.publication.schema.revision} · ${result.release.publication.etag}`);
|
|
173
|
-
const installed = await reconcileEnvironmentRelease({
|
|
174
|
-
projectDir,
|
|
175
|
-
environment: parsed.env,
|
|
176
|
-
adapter,
|
|
177
|
-
deployment: result,
|
|
178
|
-
...(environment.installation === undefined ? {} : { installation: environment.installation }),
|
|
179
|
-
deployOnly: parsed.deployOnly ?? false,
|
|
180
|
-
...(installationExecutable === undefined ? {} : { installationExecutable }),
|
|
181
|
-
...(parsed.identity === undefined ? {} : { identity: parsed.identity }),
|
|
182
|
-
signal: lifecycle.signal,
|
|
183
|
-
});
|
|
184
|
-
if (installed === undefined) {
|
|
185
|
-
report('Installation: skipped');
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
report(`Installation: ${installed.instance} · ${installed.status}`);
|
|
189
|
-
}
|
|
190
|
-
report('Ready: deployment verified');
|
|
191
|
-
if (parsed.json === true) {
|
|
192
|
-
const deployed = stableTargetDeployResult({
|
|
193
|
-
environment: parsed.env,
|
|
194
|
-
adapter,
|
|
195
|
-
deployment: result,
|
|
138
|
+
report,
|
|
139
|
+
};
|
|
140
|
+
const deployed = mode.kind === 'canonical'
|
|
141
|
+
? await deployImmutable({
|
|
142
|
+
...deployment,
|
|
143
|
+
adapter: environment.deployment.adapter,
|
|
144
|
+
parameters: environment.deployment.parameters,
|
|
145
|
+
})
|
|
146
|
+
: await deployStableTarget({
|
|
147
|
+
...deployment,
|
|
148
|
+
definition: environment,
|
|
149
|
+
reasons: mode.reasons,
|
|
196
150
|
});
|
|
151
|
+
if (deployed === undefined)
|
|
152
|
+
return 1;
|
|
153
|
+
if (parsed.json === true)
|
|
197
154
|
process.stdout.write(`${JSON.stringify(deployed, null, 2)}\n`);
|
|
198
|
-
}
|
|
199
155
|
return 0;
|
|
200
156
|
}
|
|
201
157
|
finally {
|
|
202
158
|
lifecycle.dispose();
|
|
203
159
|
}
|
|
204
160
|
}
|
|
161
|
+
/** Report every refusal of a refused deploy mode, each with what to write instead. */
|
|
162
|
+
function refused(mode) {
|
|
163
|
+
if (mode.kind !== 'refused')
|
|
164
|
+
return false;
|
|
165
|
+
for (const refusal of mode.refusals)
|
|
166
|
+
error(refusal.guidance);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
205
169
|
export function executeDevelopmentCommand(parsed, input, execute = develop) {
|
|
206
170
|
if (parsed.command !== 'dev')
|
|
207
171
|
throw new TypeError('Development execution requires the dev command.');
|
|
@@ -209,10 +173,8 @@ export function executeDevelopmentCommand(parsed, input, execute = develop) {
|
|
|
209
173
|
configPath: input.configPath,
|
|
210
174
|
projectDir: input.projectDir,
|
|
211
175
|
environment: parsed.env,
|
|
212
|
-
deployOnly: parsed.deployOnly ?? false,
|
|
213
176
|
...(parsed.identity === undefined ? {} : { identity: parsed.identity }),
|
|
214
177
|
...(parsed.credentialFile === undefined ? {} : { credentialFile: parsed.credentialFile }),
|
|
215
|
-
...(parsed.sessionFile === undefined ? {} : { sessionFile: parsed.sessionFile }),
|
|
216
178
|
...(parsed.secretsFile === undefined ? {} : { secretsFile: parsed.secretsFile }),
|
|
217
179
|
});
|
|
218
180
|
}
|
|
@@ -234,19 +196,6 @@ async function lint(parsed) {
|
|
|
234
196
|
throw cause;
|
|
235
197
|
}
|
|
236
198
|
}
|
|
237
|
-
function secretsFor(adapter, parameters, projectDir, runtime) {
|
|
238
|
-
const file = adapter.secretsFile?.(parameters);
|
|
239
|
-
if (file === undefined)
|
|
240
|
-
return Object.freeze({});
|
|
241
|
-
const path = isAbsolute(file) ? file : join(projectDir, file);
|
|
242
|
-
// An Integration-free Runtime has no Providers and therefore no author-owned
|
|
243
|
-
// environment secrets. Adapter-owned secrets, such as the signing identity,
|
|
244
|
-
// are resolved separately by the adapter and must not make this file required.
|
|
245
|
-
if (Object.keys(runtime.integrations).length === 0 && !existsSync(path)) {
|
|
246
|
-
return Object.freeze({});
|
|
247
|
-
}
|
|
248
|
-
return Object.freeze(loadDeclaredSecrets(path, file));
|
|
249
|
-
}
|
|
250
199
|
function findConfig(projectDir) {
|
|
251
200
|
return CONFIG_NAMES.map((name) => join(projectDir, name)).find(existsSync);
|
|
252
201
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Runtime } from '../../application/runtime/index.js';
|
|
2
|
+
import type { Adapter, LegacyAdapter } from '../../deployment/adapter/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* The secrets one Environment's deployment binds: the values of the secrets file its adapter
|
|
5
|
+
* declares. An Integration-free Runtime has no Providers and therefore no author-owned secrets, so
|
|
6
|
+
* its file may be absent; adapter-owned secrets, such as a signing identity, are the adapter's to
|
|
7
|
+
* resolve and never make this file required.
|
|
8
|
+
*/
|
|
9
|
+
export declare function environmentSecrets(adapter: Adapter<object> | LegacyAdapter<object>, parameters: object, projectDir: string, runtime: Runtime): Readonly<Record<string, string>>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, join } from 'node:path';
|
|
3
|
+
import { loadDeclaredSecrets } from './dotenv.js';
|
|
4
|
+
/**
|
|
5
|
+
* The secrets one Environment's deployment binds: the values of the secrets file its adapter
|
|
6
|
+
* declares. An Integration-free Runtime has no Providers and therefore no author-owned secrets, so
|
|
7
|
+
* its file may be absent; adapter-owned secrets, such as a signing identity, are the adapter's to
|
|
8
|
+
* resolve and never make this file required.
|
|
9
|
+
*/
|
|
10
|
+
export function environmentSecrets(adapter, parameters, projectDir, runtime) {
|
|
11
|
+
const file = adapter.secretsFile?.(parameters);
|
|
12
|
+
if (file === undefined)
|
|
13
|
+
return Object.freeze({});
|
|
14
|
+
const path = isAbsolute(file) ? file : join(projectDir, file);
|
|
15
|
+
if (Object.keys(runtime.integrations).length === 0 && !existsSync(path)) {
|
|
16
|
+
return Object.freeze({});
|
|
17
|
+
}
|
|
18
|
+
return Object.freeze(loadDeclaredSecrets(path, file));
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import * as publication from '@astrale-os/kernel-protocol/release/legacy';
|
|
2
|
-
import type { Release } from '../../release/legacy/index.js';
|
|
3
|
-
export type ReadinessFetch = (input: string, init: {
|
|
4
|
-
readonly redirect: 'error';
|
|
5
|
-
readonly signal?: AbortSignal;
|
|
6
|
-
}) => Promise<Response>;
|
|
7
|
-
/** Fetch and verify the exact live Publication and Bundle for one Release. */
|
|
8
|
-
export declare function verifyPublication(release: Release, options?: {
|
|
9
|
-
readonly fetch?: ReadinessFetch;
|
|
10
|
-
readonly signal?: AbortSignal;
|
|
11
|
-
}): Promise<publication.Publication>;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import * as publication from '@astrale-os/kernel-protocol/release/legacy';
|
|
2
|
-
const MAXIMUM_PUBLICATION_BYTES = 1024 * 1024;
|
|
3
|
-
const MAXIMUM_BUNDLE_BYTES = 16 * 1024 * 1024;
|
|
4
|
-
/** Fetch and verify the exact live Publication and Bundle for one Release. */
|
|
5
|
-
export async function verifyPublication(release, options = {}) {
|
|
6
|
-
const fetcher = options.fetch ?? globalThis.fetch;
|
|
7
|
-
const publicationUrl = publication.url(release.addressing.publicUrl);
|
|
8
|
-
const response = await fetchRequired(fetcher, publicationUrl, options.signal);
|
|
9
|
-
const deployed = publication.accept(JSON.parse(await text(response, publicationUrl, MAXIMUM_PUBLICATION_BYTES)));
|
|
10
|
-
if (!publication.matchesReference(deployed, publication.reference(release.publication))) {
|
|
11
|
-
throw new Error('Live Publication differs from the expected Release.');
|
|
12
|
-
}
|
|
13
|
-
const bundleResponse = await fetchRequired(fetcher, deployed.schema.bundle.href, options.signal);
|
|
14
|
-
const bytes = new Uint8Array(await bounded(bundleResponse, deployed.schema.bundle.href, MAXIMUM_BUNDLE_BYTES));
|
|
15
|
-
publication.decodeBundle(deployed, bytes);
|
|
16
|
-
return deployed;
|
|
17
|
-
}
|
|
18
|
-
async function fetchRequired(fetcher, url, signal) {
|
|
19
|
-
const response = await fetcher(url, {
|
|
20
|
-
redirect: 'error',
|
|
21
|
-
...(signal === undefined ? {} : { signal }),
|
|
22
|
-
});
|
|
23
|
-
if (!response.ok)
|
|
24
|
-
throw new Error(`GET ${url} returned ${response.status}.`);
|
|
25
|
-
return response;
|
|
26
|
-
}
|
|
27
|
-
async function bounded(response, url, maximum) {
|
|
28
|
-
const bytes = await response.arrayBuffer();
|
|
29
|
-
if (bytes.byteLength > maximum)
|
|
30
|
-
throw new Error(`GET ${url} exceeded ${maximum} bytes.`);
|
|
31
|
-
return bytes;
|
|
32
|
-
}
|
|
33
|
-
async function text(response, url, maximum) {
|
|
34
|
-
const bytes = await bounded(response, url, maximum);
|
|
35
|
-
try {
|
|
36
|
-
return new TextDecoder('utf-8', { fatal: true, ignoreBOM: false }).decode(bytes);
|
|
37
|
-
}
|
|
38
|
-
catch (cause) {
|
|
39
|
-
throw new Error(`GET ${url} returned invalid UTF-8.`, { cause });
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export function installation(release, provider) {
|
|
2
|
-
const advertised = release.publication.delivery !== undefined;
|
|
3
|
-
if (advertised !== (provider !== undefined)) {
|
|
4
|
-
throw new TypeError(advertised
|
|
5
|
-
? 'Release advertises Publication delivery without a Provider.'
|
|
6
|
-
: 'Publication delivery Provider has no Release endpoint.');
|
|
7
|
-
}
|
|
8
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { preparePublicationSnapshot } from '@astrale-os/kernel-server';
|
|
2
|
-
export function source(runtime) {
|
|
3
|
-
const release = runtime.release;
|
|
4
|
-
const snapshot = preparePublicationSnapshot({
|
|
5
|
-
publication: release.publication,
|
|
6
|
-
bundle: release.build.schema.bundle.bytes(),
|
|
7
|
-
table: release.routes,
|
|
8
|
-
domain: runtime.domain,
|
|
9
|
-
});
|
|
10
|
-
return Object.freeze({ current: () => snapshot });
|
|
11
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/** Build one mutually exclusive authority argument set for an Astrale CLI operation. */
|
|
2
|
-
export function astraleAuthorityArguments(input) {
|
|
3
|
-
if (input.identity !== undefined && input.credential !== undefined) {
|
|
4
|
-
throw new TypeError('Astrale authority cannot combine an identity and a credential.');
|
|
5
|
-
}
|
|
6
|
-
return input.credential === undefined
|
|
7
|
-
? input.identity === undefined
|
|
8
|
-
? Object.freeze([])
|
|
9
|
-
: Object.freeze(['--as', input.identity])
|
|
10
|
-
: Object.freeze(['--creds', input.credential]);
|
|
11
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { constants } from 'node:fs';
|
|
2
|
-
import { access, readFile, realpath } from 'node:fs/promises';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
|
-
import { delimiter, isAbsolute, join } from 'node:path';
|
|
5
|
-
/** Resolve one exact executable for a complete sequence of Kernel CLI operations. */
|
|
6
|
-
export async function resolveAstraleExecutable(input = 'astrale') {
|
|
7
|
-
const candidates = isAbsolute(input) ? [input] : await executableCandidates(input);
|
|
8
|
-
for (const candidate of candidates) {
|
|
9
|
-
try {
|
|
10
|
-
await access(candidate, constants.X_OK);
|
|
11
|
-
return await realpath(candidate);
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
// Continue through the bounded recorded-install and PATH candidates.
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
throw new TypeError('Astrale CLI is unavailable. Install it with the official Astrale installer.');
|
|
18
|
-
}
|
|
19
|
-
async function executableCandidates(input) {
|
|
20
|
-
const path = (process.env.PATH ?? '')
|
|
21
|
-
.split(delimiter)
|
|
22
|
-
.filter(Boolean)
|
|
23
|
-
.map((directory) => join(directory, input));
|
|
24
|
-
if (input !== 'astrale')
|
|
25
|
-
return path;
|
|
26
|
-
const installed = await recordedInstallation();
|
|
27
|
-
return installed === undefined ? path : [installed, ...path];
|
|
28
|
-
}
|
|
29
|
-
async function recordedInstallation() {
|
|
30
|
-
const home = process.env.ASTRALE_HOME ?? join(homedir(), '.astrale');
|
|
31
|
-
try {
|
|
32
|
-
const decoded = JSON.parse(await readFile(join(home, 'install.json'), 'utf8'));
|
|
33
|
-
if (typeof decoded !== 'object' ||
|
|
34
|
-
decoded === null ||
|
|
35
|
-
!('method' in decoded) ||
|
|
36
|
-
decoded.method !== 'script' ||
|
|
37
|
-
!('bin' in decoded) ||
|
|
38
|
-
typeof decoded.bin !== 'string' ||
|
|
39
|
-
!isAbsolute(decoded.bin)) {
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
return decoded.bin;
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
return undefined;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { AstraleProcessResult } from './process.js';
|
|
2
|
-
/** Only sanitized CLI diagnostics may be promoted through installation failures. */
|
|
3
|
-
export declare class AstraleCliError extends Error {
|
|
4
|
-
}
|
|
5
|
-
export declare function astraleFailure(command: string, result: AstraleProcessResult): Error;
|
|
6
|
-
export declare function isDomainNotInstalledFailure(stderr: string): boolean;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/** Only sanitized CLI diagnostics may be promoted through installation failures. */
|
|
2
|
-
export class AstraleCliError extends Error {
|
|
3
|
-
}
|
|
4
|
-
export function astraleFailure(command, result) {
|
|
5
|
-
let diagnostic;
|
|
6
|
-
try {
|
|
7
|
-
const failure = record(JSON.parse(result.stderr));
|
|
8
|
-
const error = admittedCode(failure.error);
|
|
9
|
-
const reason = recordOrUndefined(failure.reason);
|
|
10
|
-
const reasonCode = admittedCode(reason?.code);
|
|
11
|
-
diagnostic =
|
|
12
|
-
error === undefined ? undefined : reasonCode === undefined ? error : `${error}/${reasonCode}`;
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
// Never forward raw stderr because a credential-bearing command owns it.
|
|
16
|
-
}
|
|
17
|
-
return new AstraleCliError(`Astrale CLI ${command} failed${diagnostic === undefined ? '' : ` (${diagnostic})`}.`);
|
|
18
|
-
}
|
|
19
|
-
function admittedCode(input) {
|
|
20
|
-
return typeof input === 'string' && /^[A-Z][A-Z0-9_]{0,63}$/u.test(input) ? input : undefined;
|
|
21
|
-
}
|
|
22
|
-
function recordOrUndefined(input) {
|
|
23
|
-
return input !== null && typeof input === 'object' && !Array.isArray(input)
|
|
24
|
-
? input
|
|
25
|
-
: undefined;
|
|
26
|
-
}
|
|
27
|
-
export function isDomainNotInstalledFailure(stderr) {
|
|
28
|
-
try {
|
|
29
|
-
const failure = record(JSON.parse(stderr));
|
|
30
|
-
if (failure.error === 'DOMAIN_NOT_INSTALLED')
|
|
31
|
-
return true;
|
|
32
|
-
const reason = failure.reason;
|
|
33
|
-
return (failure.error === 'RESPONSE_ERROR' &&
|
|
34
|
-
reason !== null &&
|
|
35
|
-
typeof reason === 'object' &&
|
|
36
|
-
!Array.isArray(reason) &&
|
|
37
|
-
Reflect.get(reason, 'code') === 'SCHEMA_NOT_FOUND');
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function record(input) {
|
|
44
|
-
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
|
|
45
|
-
throw new TypeError('Expected an object response.');
|
|
46
|
-
}
|
|
47
|
-
return input;
|
|
48
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { runAstrale } from './process.js';
|
|
2
|
-
/** Resolve and probe a bookmark through the pinned CLI, preserving its TLS trust. */
|
|
3
|
-
export declare function resolveAstraleInstanceIssuer(input: {
|
|
4
|
-
readonly executable: string;
|
|
5
|
-
readonly projectDir: string;
|
|
6
|
-
readonly instance: string;
|
|
7
|
-
readonly expectedIssuer?: string;
|
|
8
|
-
readonly signal: AbortSignal;
|
|
9
|
-
}, dependencies?: {
|
|
10
|
-
readonly run: typeof runAstrale;
|
|
11
|
-
}): Promise<string>;
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { astraleFailure } from './failure.js';
|
|
2
|
-
import { runAstrale } from './process.js';
|
|
3
|
-
/** Resolve and probe a bookmark through the pinned CLI, preserving its TLS trust. */
|
|
4
|
-
export async function resolveAstraleInstanceIssuer(input, dependencies = { run: runAstrale }) {
|
|
5
|
-
const result = await dependencies.run({
|
|
6
|
-
executable: input.executable,
|
|
7
|
-
projectDir: input.projectDir,
|
|
8
|
-
signal: input.signal,
|
|
9
|
-
timeoutMs: 30_000,
|
|
10
|
-
args: ['--ci', '--no-prompt', 'instance', 'status', input.instance, '--bookmarked', '--json'],
|
|
11
|
-
});
|
|
12
|
-
if (result.code !== 0)
|
|
13
|
-
throw astraleFailure('instance status', result);
|
|
14
|
-
let value;
|
|
15
|
-
try {
|
|
16
|
-
value = JSON.parse(result.stdout);
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
throw new TypeError('Astrale instance status did not return one JSON value.');
|
|
20
|
-
}
|
|
21
|
-
if (value === null ||
|
|
22
|
-
typeof value !== 'object' ||
|
|
23
|
-
Array.isArray(value) ||
|
|
24
|
-
!('kind' in value) ||
|
|
25
|
-
value.kind !== 'bookmark' ||
|
|
26
|
-
!('state' in value) ||
|
|
27
|
-
value.state !== 'ready' ||
|
|
28
|
-
!('issuer' in value) ||
|
|
29
|
-
typeof value.issuer !== 'string') {
|
|
30
|
-
throw new TypeError('Astrale instance status returned invalid Kernel identity evidence.');
|
|
31
|
-
}
|
|
32
|
-
let issuer;
|
|
33
|
-
try {
|
|
34
|
-
issuer = new URL(value.issuer);
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
throw new TypeError('Astrale instance status returned an invalid Kernel issuer.');
|
|
38
|
-
}
|
|
39
|
-
if (!['https:', 'http:'].includes(issuer.protocol) ||
|
|
40
|
-
issuer.username ||
|
|
41
|
-
issuer.password ||
|
|
42
|
-
issuer.search ||
|
|
43
|
-
issuer.hash) {
|
|
44
|
-
throw new TypeError('Astrale instance status returned an invalid Kernel issuer.');
|
|
45
|
-
}
|
|
46
|
-
// Child Kernels share an origin, but their path-qualified identities remain distinct.
|
|
47
|
-
const canonical = issuer.href.replace(/\/$/u, '');
|
|
48
|
-
if (input.expectedIssuer !== undefined && canonical !== input.expectedIssuer) {
|
|
49
|
-
throw new Error('Installation bookmark changed Kernel; restart pnpm dev to apply it.');
|
|
50
|
-
}
|
|
51
|
-
return canonical;
|
|
52
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export interface AstraleProcessResult {
|
|
2
|
-
readonly code: number;
|
|
3
|
-
readonly stdout: string;
|
|
4
|
-
readonly stderr: string;
|
|
5
|
-
}
|
|
6
|
-
/** Run one exact Kernel CLI executable with bounded output, time, and termination. */
|
|
7
|
-
export declare function runAstrale(input: {
|
|
8
|
-
readonly executable: string;
|
|
9
|
-
readonly args: readonly string[];
|
|
10
|
-
readonly projectDir: string;
|
|
11
|
-
readonly signal: AbortSignal;
|
|
12
|
-
readonly timeoutMs: number;
|
|
13
|
-
}): Promise<AstraleProcessResult>;
|