@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
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { Release } from '../../../deployment/release/legacy/index.js';
|
|
2
|
-
import type { AstraleProcessResult } from '../astrale/process.js';
|
|
3
|
-
export interface DevelopmentViewDependencies {
|
|
4
|
-
run(input: {
|
|
5
|
-
readonly executable: string;
|
|
6
|
-
readonly args: readonly string[];
|
|
7
|
-
readonly projectDir: string;
|
|
8
|
-
readonly signal: AbortSignal;
|
|
9
|
-
readonly timeoutMs: number;
|
|
10
|
-
}): Promise<AstraleProcessResult>;
|
|
11
|
-
}
|
|
12
|
-
/** Keep one browser tab alive across successful remote deployments. */
|
|
13
|
-
export declare class DevelopmentView {
|
|
14
|
-
#private;
|
|
15
|
-
constructor(input: {
|
|
16
|
-
readonly executable: string;
|
|
17
|
-
readonly projectDir: string;
|
|
18
|
-
readonly instance: string;
|
|
19
|
-
readonly identity?: string;
|
|
20
|
-
readonly credential?: string;
|
|
21
|
-
readonly dependencies?: DevelopmentViewDependencies;
|
|
22
|
-
});
|
|
23
|
-
/** Require in-place refresh before publishing anything that depends on this CLI feature. */
|
|
24
|
-
prepare(signal: AbortSignal): Promise<void>;
|
|
25
|
-
/** Called after each committed deployment; the development loop owns candidate deduplication. */
|
|
26
|
-
refresh(release: Release, signal: AbortSignal): Promise<string | undefined>;
|
|
27
|
-
close(signal: AbortSignal): Promise<void>;
|
|
28
|
-
}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { astraleAuthorityArguments } from '../astrale/authority.js';
|
|
2
|
-
import { runAstrale } from '../astrale/process.js';
|
|
3
|
-
/** Keep one browser tab alive across successful remote deployments. */
|
|
4
|
-
export class DevelopmentView {
|
|
5
|
-
#executable;
|
|
6
|
-
#projectDir;
|
|
7
|
-
#instance;
|
|
8
|
-
#authority;
|
|
9
|
-
#dependencies;
|
|
10
|
-
#current;
|
|
11
|
-
#prepared = false;
|
|
12
|
-
constructor(input) {
|
|
13
|
-
this.#executable = input.executable;
|
|
14
|
-
this.#projectDir = input.projectDir;
|
|
15
|
-
this.#instance = input.instance;
|
|
16
|
-
this.#authority = astraleAuthorityArguments({
|
|
17
|
-
...(input.identity === undefined ? {} : { identity: input.identity }),
|
|
18
|
-
...(input.credential === undefined ? {} : { credential: input.credential }),
|
|
19
|
-
});
|
|
20
|
-
this.#dependencies = input.dependencies ?? { run: runAstrale };
|
|
21
|
-
}
|
|
22
|
-
/** Require in-place refresh before publishing anything that depends on this CLI feature. */
|
|
23
|
-
async prepare(signal) {
|
|
24
|
-
if (this.#prepared)
|
|
25
|
-
return;
|
|
26
|
-
const result = await this.#dependencies.run({
|
|
27
|
-
executable: this.#executable,
|
|
28
|
-
projectDir: this.#projectDir,
|
|
29
|
-
signal,
|
|
30
|
-
timeoutMs: 10_000,
|
|
31
|
-
args: ['view', '--help'],
|
|
32
|
-
});
|
|
33
|
-
if (result.code !== 0 || !result.stdout.includes('--refresh <id>')) {
|
|
34
|
-
throw new Error('This Astrale CLI cannot refresh development Views. Run astrale update before pnpm dev.');
|
|
35
|
-
}
|
|
36
|
-
this.#prepared = true;
|
|
37
|
-
}
|
|
38
|
-
/** Called after each committed deployment; the development loop owns candidate deduplication. */
|
|
39
|
-
async refresh(release, signal) {
|
|
40
|
-
const frontend = release.build.frontend;
|
|
41
|
-
if (frontend === undefined) {
|
|
42
|
-
await this.close(AbortSignal.timeout(30_000));
|
|
43
|
-
return undefined;
|
|
44
|
-
}
|
|
45
|
-
const path = `/:${frontend.entrypoint.origin}:view.${frontend.entrypoint.name}`;
|
|
46
|
-
const previous = this.#current;
|
|
47
|
-
if (previous?.path === path) {
|
|
48
|
-
const refreshed = await this.#dependencies.run({
|
|
49
|
-
executable: this.#executable,
|
|
50
|
-
projectDir: this.#projectDir,
|
|
51
|
-
signal,
|
|
52
|
-
timeoutMs: 60_000,
|
|
53
|
-
args: ['--ci', '--no-prompt', 'view', '--refresh', previous.id, '--json'],
|
|
54
|
-
});
|
|
55
|
-
if (refreshed.code !== 0)
|
|
56
|
-
throw new Error('Astrale CLI could not refresh the development View.');
|
|
57
|
-
return previous.pageUrl;
|
|
58
|
-
}
|
|
59
|
-
const next = await this.#open(path, signal);
|
|
60
|
-
if (previous !== undefined) {
|
|
61
|
-
try {
|
|
62
|
-
await this.#close(previous.id, AbortSignal.timeout(30_000));
|
|
63
|
-
}
|
|
64
|
-
catch (cause) {
|
|
65
|
-
try {
|
|
66
|
-
await this.#close(next.id, AbortSignal.timeout(30_000));
|
|
67
|
-
}
|
|
68
|
-
catch (rollback) {
|
|
69
|
-
throw new AggregateError([cause, rollback], 'Astrale View replacement and rollback cleanup failed.');
|
|
70
|
-
}
|
|
71
|
-
throw cause;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
this.#current = next;
|
|
75
|
-
return next.pageUrl;
|
|
76
|
-
}
|
|
77
|
-
async close(signal) {
|
|
78
|
-
const current = this.#current;
|
|
79
|
-
if (current === undefined)
|
|
80
|
-
return;
|
|
81
|
-
await this.#close(current.id, signal);
|
|
82
|
-
if (this.#current === current) {
|
|
83
|
-
this.#current = undefined;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
async #open(path, signal) {
|
|
87
|
-
const result = await this.#dependencies.run({
|
|
88
|
-
executable: this.#executable,
|
|
89
|
-
projectDir: this.#projectDir,
|
|
90
|
-
signal,
|
|
91
|
-
timeoutMs: 60_000,
|
|
92
|
-
args: [
|
|
93
|
-
'--ci',
|
|
94
|
-
'--no-prompt',
|
|
95
|
-
'view',
|
|
96
|
-
path,
|
|
97
|
-
'--browser',
|
|
98
|
-
'--instance',
|
|
99
|
-
this.#instance,
|
|
100
|
-
'--json',
|
|
101
|
-
...this.#authority,
|
|
102
|
-
],
|
|
103
|
-
});
|
|
104
|
-
if (result.code !== 0)
|
|
105
|
-
throw new Error('Astrale CLI could not open the development View.');
|
|
106
|
-
return { ...decodeOpenedView(result.stdout), path };
|
|
107
|
-
}
|
|
108
|
-
async #close(id, signal) {
|
|
109
|
-
const result = await this.#dependencies.run({
|
|
110
|
-
executable: this.#executable,
|
|
111
|
-
projectDir: this.#projectDir,
|
|
112
|
-
signal,
|
|
113
|
-
timeoutMs: 30_000,
|
|
114
|
-
args: ['--ci', '--no-prompt', 'view', '--close', id, '--json'],
|
|
115
|
-
});
|
|
116
|
-
if (result.code !== 0)
|
|
117
|
-
throw new Error('Astrale CLI could not close the development View.');
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
function decodeOpenedView(input) {
|
|
121
|
-
let value;
|
|
122
|
-
try {
|
|
123
|
-
value = JSON.parse(input);
|
|
124
|
-
}
|
|
125
|
-
catch (cause) {
|
|
126
|
-
throw new TypeError('Astrale View output is invalid.', { cause });
|
|
127
|
-
}
|
|
128
|
-
const session = record(value) ? value.session : undefined;
|
|
129
|
-
const id = record(session) ? session.id : undefined;
|
|
130
|
-
const pageUrl = record(session) ? session.pageUrl : undefined;
|
|
131
|
-
if (typeof id !== 'string' || id.length === 0 || !webUrl(pageUrl)) {
|
|
132
|
-
throw new TypeError('Astrale View output is invalid.');
|
|
133
|
-
}
|
|
134
|
-
return Object.freeze({ id, pageUrl });
|
|
135
|
-
}
|
|
136
|
-
function webUrl(input) {
|
|
137
|
-
if (typeof input !== 'string' || input.length === 0 || input.length > 2_048)
|
|
138
|
-
return false;
|
|
139
|
-
try {
|
|
140
|
-
const value = new URL(input);
|
|
141
|
-
return ((value.protocol === 'http:' || value.protocol === 'https:') &&
|
|
142
|
-
value.username === '' &&
|
|
143
|
-
value.password === '');
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
function record(input) {
|
|
150
|
-
return input !== null && typeof input === 'object' && !Array.isArray(input);
|
|
151
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { InstallationTarget, ReadyDeployResult } from '../../deployment/adapter/index.js';
|
|
2
|
-
import type { InstallationEvidence } from './installation/index.js';
|
|
3
|
-
import { writeDeploymentEvidence } from './evidence.js';
|
|
4
|
-
import { reconcileInstallation } from './installation/index.js';
|
|
5
|
-
export interface EnvironmentReleaseDependencies {
|
|
6
|
-
readonly writeEvidence: typeof writeDeploymentEvidence;
|
|
7
|
-
readonly reconcileInstallation: typeof reconcileInstallation;
|
|
8
|
-
}
|
|
9
|
-
/** Retain verified provider evidence and reconcile one optional durable Kernel installation. */
|
|
10
|
-
export declare function reconcileEnvironmentRelease(input: {
|
|
11
|
-
readonly projectDir: string;
|
|
12
|
-
readonly environment: string;
|
|
13
|
-
readonly adapter: {
|
|
14
|
-
readonly name: string;
|
|
15
|
-
readonly version: string;
|
|
16
|
-
};
|
|
17
|
-
readonly deployment: ReadyDeployResult;
|
|
18
|
-
readonly installation?: InstallationTarget;
|
|
19
|
-
readonly deployOnly: boolean;
|
|
20
|
-
readonly identity?: string;
|
|
21
|
-
readonly credential?: string;
|
|
22
|
-
readonly installationExecutable?: string;
|
|
23
|
-
readonly signal: AbortSignal;
|
|
24
|
-
}, dependencies?: EnvironmentReleaseDependencies): Promise<InstallationEvidence | undefined>;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { writeDeploymentEvidence } from './evidence.js';
|
|
2
|
-
import { reconcileInstallation } from './installation/index.js';
|
|
3
|
-
/** Retain verified provider evidence and reconcile one optional durable Kernel installation. */
|
|
4
|
-
export async function reconcileEnvironmentRelease(input, dependencies = {
|
|
5
|
-
writeEvidence: writeDeploymentEvidence,
|
|
6
|
-
reconcileInstallation,
|
|
7
|
-
}) {
|
|
8
|
-
try {
|
|
9
|
-
await dependencies.writeEvidence({
|
|
10
|
-
projectDir: input.projectDir,
|
|
11
|
-
environment: input.environment,
|
|
12
|
-
adapter: input.adapter,
|
|
13
|
-
deployment: input.deployment,
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
catch (cause) {
|
|
17
|
-
throw new TypeError(`Domain ${input.deployment.release.publication.origin} remains deployed, but its local evidence could not be saved. Kernel installation was not attempted.`, { cause });
|
|
18
|
-
}
|
|
19
|
-
if (input.installation === undefined || input.deployOnly)
|
|
20
|
-
return undefined;
|
|
21
|
-
if (input.installationExecutable === undefined) {
|
|
22
|
-
throw new TypeError('A compatible Astrale CLI must be pinned before deploying an installed Environment.');
|
|
23
|
-
}
|
|
24
|
-
const installation = await dependencies.reconcileInstallation({
|
|
25
|
-
projectDir: input.projectDir,
|
|
26
|
-
target: input.installation,
|
|
27
|
-
executable: input.installationExecutable,
|
|
28
|
-
...(input.identity === undefined ? {} : { identity: input.identity }),
|
|
29
|
-
...(input.credential === undefined ? {} : { credential: input.credential }),
|
|
30
|
-
release: input.deployment.release,
|
|
31
|
-
signal: input.signal,
|
|
32
|
-
});
|
|
33
|
-
try {
|
|
34
|
-
await dependencies.writeEvidence({
|
|
35
|
-
projectDir: input.projectDir,
|
|
36
|
-
environment: input.environment,
|
|
37
|
-
adapter: input.adapter,
|
|
38
|
-
deployment: input.deployment,
|
|
39
|
-
installation,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
catch (cause) {
|
|
43
|
-
throw new TypeError(`Domain ${installation.origin} remains installed on ${installation.instance}, but its local evidence could not be saved. The installation was not rolled back.`, { cause });
|
|
44
|
-
}
|
|
45
|
-
return installation;
|
|
46
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { ReadyDeployResult } from '../../deployment/adapter/index.js';
|
|
2
|
-
import type { InstallationEvidence } from './installation/index.js';
|
|
3
|
-
export declare const DEPLOYMENT_EVIDENCE_VERSION: 1;
|
|
4
|
-
/** Persist one non-secret, non-authoritative continuity observation atomically. */
|
|
5
|
-
export declare function writeDeploymentEvidence(input: {
|
|
6
|
-
readonly projectDir: string;
|
|
7
|
-
readonly environment: string;
|
|
8
|
-
readonly adapter: {
|
|
9
|
-
readonly name: string;
|
|
10
|
-
readonly version: string;
|
|
11
|
-
};
|
|
12
|
-
readonly deployment: ReadyDeployResult;
|
|
13
|
-
readonly installation?: InstallationEvidence;
|
|
14
|
-
}): Promise<string>;
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { mkdir, rename, unlink, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
export const DEPLOYMENT_EVIDENCE_VERSION = 1;
|
|
5
|
-
/** Persist one non-secret, non-authoritative continuity observation atomically. */
|
|
6
|
-
export async function writeDeploymentEvidence(input) {
|
|
7
|
-
const directory = join(input.projectDir, '.astrale', 'environments', input.environment);
|
|
8
|
-
const path = join(directory, 'deployment.json');
|
|
9
|
-
await mkdir(directory, { recursive: true });
|
|
10
|
-
const provider = input.deployment.progress.provider.evidence;
|
|
11
|
-
const value = Object.freeze({
|
|
12
|
-
version: DEPLOYMENT_EVIDENCE_VERSION,
|
|
13
|
-
environment: input.environment,
|
|
14
|
-
adapter: Object.freeze({ name: input.adapter.name, version: input.adapter.version }),
|
|
15
|
-
deployment: Object.freeze({
|
|
16
|
-
provider: provider.provider,
|
|
17
|
-
id: provider.deployment,
|
|
18
|
-
artifact: provider.artifact,
|
|
19
|
-
issuer: input.deployment.release.addressing.issuer,
|
|
20
|
-
publicUrl: input.deployment.release.addressing.publicUrl,
|
|
21
|
-
origin: input.deployment.release.publication.origin,
|
|
22
|
-
revision: input.deployment.release.publication.schema.revision,
|
|
23
|
-
etag: input.deployment.release.publication.etag,
|
|
24
|
-
readiness: 'ready',
|
|
25
|
-
...(provider.preflight === undefined ? {} : { preflight: provider.preflight }),
|
|
26
|
-
}),
|
|
27
|
-
...(input.installation === undefined
|
|
28
|
-
? {}
|
|
29
|
-
: {
|
|
30
|
-
installation: Object.freeze({
|
|
31
|
-
instance: input.installation.instance,
|
|
32
|
-
origin: input.installation.origin,
|
|
33
|
-
issuer: input.installation.issuer,
|
|
34
|
-
revision: input.installation.revision,
|
|
35
|
-
etag: input.installation.etag,
|
|
36
|
-
generation: input.installation.generation,
|
|
37
|
-
status: input.installation.status,
|
|
38
|
-
}),
|
|
39
|
-
}),
|
|
40
|
-
});
|
|
41
|
-
const staging = join(directory, `.deployment-${randomUUID()}.json`);
|
|
42
|
-
await writeFile(staging, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600, flag: 'wx' });
|
|
43
|
-
try {
|
|
44
|
-
await rename(staging, path);
|
|
45
|
-
}
|
|
46
|
-
finally {
|
|
47
|
-
await unlink(staging).catch((cause) => {
|
|
48
|
-
if (cause.code !== 'ENOENT')
|
|
49
|
-
throw cause;
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
return path;
|
|
53
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { reconcileInstallation } from './reconcile.js';
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export interface InstallationObservation {
|
|
2
|
-
readonly origin: string;
|
|
3
|
-
readonly revision: string;
|
|
4
|
-
readonly issuer: string;
|
|
5
|
-
readonly generation: string;
|
|
6
|
-
readonly etag: string;
|
|
7
|
-
}
|
|
8
|
-
/** Admit exact installed Release evidence returned by Kernel introspection. */
|
|
9
|
-
export declare function decodeInstallationObservation(input: string): InstallationObservation;
|
|
10
|
-
export declare function canonicalIssuer(input: string): string;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/** Admit exact installed Release evidence returned by Kernel introspection. */
|
|
2
|
-
export function decodeInstallationObservation(input) {
|
|
3
|
-
let decoded;
|
|
4
|
-
try {
|
|
5
|
-
decoded = JSON.parse(input);
|
|
6
|
-
}
|
|
7
|
-
catch (cause) {
|
|
8
|
-
throw new TypeError('Astrale introspection did not return one JSON value.', { cause });
|
|
9
|
-
}
|
|
10
|
-
const root = record(decoded);
|
|
11
|
-
const publication = record(root.publication);
|
|
12
|
-
const identity = record(publication.identity);
|
|
13
|
-
if (typeof root.origin !== 'string' ||
|
|
14
|
-
typeof root.revision !== 'string' ||
|
|
15
|
-
typeof root.generation !== 'string' ||
|
|
16
|
-
!/^sha256:[0-9a-f]{64}$/u.test(root.generation) ||
|
|
17
|
-
publication.origin !== root.origin ||
|
|
18
|
-
publication.revision !== root.revision ||
|
|
19
|
-
typeof identity.issuer !== 'string' ||
|
|
20
|
-
typeof publication.etag !== 'string' ||
|
|
21
|
-
!/^sha256:[0-9a-f]{64}$/u.test(publication.etag)) {
|
|
22
|
-
throw new TypeError('Astrale introspection returned invalid installed Domain evidence.');
|
|
23
|
-
}
|
|
24
|
-
return Object.freeze({
|
|
25
|
-
origin: root.origin,
|
|
26
|
-
revision: root.revision,
|
|
27
|
-
issuer: canonicalIssuer(identity.issuer),
|
|
28
|
-
generation: root.generation,
|
|
29
|
-
etag: publication.etag,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
export function canonicalIssuer(input) {
|
|
33
|
-
const url = new URL(input);
|
|
34
|
-
if (url.href !== url.origin && url.href !== `${url.origin}/`) {
|
|
35
|
-
throw new TypeError('Release issuer must be one canonical origin.');
|
|
36
|
-
}
|
|
37
|
-
return url.origin;
|
|
38
|
-
}
|
|
39
|
-
function record(input) {
|
|
40
|
-
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
|
|
41
|
-
throw new TypeError('Astrale introspection returned invalid installed Domain evidence.');
|
|
42
|
-
}
|
|
43
|
-
return input;
|
|
44
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { InstallationTarget } from '../../../deployment/adapter/index.js';
|
|
2
|
-
import type { Release } from '../../../deployment/release/legacy/index.js';
|
|
3
|
-
import type { AstraleProcessResult } from '../astrale/process.js';
|
|
4
|
-
export interface InstallationEvidence {
|
|
5
|
-
readonly status: 'unchanged' | 'installed' | 'upgraded';
|
|
6
|
-
readonly instance: string;
|
|
7
|
-
readonly origin: string;
|
|
8
|
-
readonly issuer: string;
|
|
9
|
-
readonly revision: string;
|
|
10
|
-
readonly generation: string;
|
|
11
|
-
readonly etag: string;
|
|
12
|
-
}
|
|
13
|
-
export type AstraleCommandResult = AstraleProcessResult;
|
|
14
|
-
export interface InstallationDependencies {
|
|
15
|
-
run(input: {
|
|
16
|
-
readonly executable: string;
|
|
17
|
-
readonly args: readonly string[];
|
|
18
|
-
readonly projectDir: string;
|
|
19
|
-
readonly signal: AbortSignal;
|
|
20
|
-
readonly timeoutMs: number;
|
|
21
|
-
}): Promise<AstraleCommandResult>;
|
|
22
|
-
}
|
|
23
|
-
/** Reconcile one observed Release without ever replacing a different issuer. */
|
|
24
|
-
export declare function reconcileInstallation(input: {
|
|
25
|
-
readonly projectDir: string;
|
|
26
|
-
readonly target: InstallationTarget;
|
|
27
|
-
readonly executable: string;
|
|
28
|
-
readonly identity?: string;
|
|
29
|
-
readonly credential?: string;
|
|
30
|
-
readonly release: Release;
|
|
31
|
-
readonly signal: AbortSignal;
|
|
32
|
-
}, dependencies?: InstallationDependencies): Promise<InstallationEvidence>;
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { astraleAuthorityArguments } from '../astrale/authority.js';
|
|
2
|
-
import { AstraleCliError, isDomainNotInstalledFailure, astraleFailure } from '../astrale/failure.js';
|
|
3
|
-
import { runAstrale } from '../astrale/process.js';
|
|
4
|
-
import { canonicalIssuer, decodeInstallationObservation } from './observation.js';
|
|
5
|
-
/** Reconcile one observed Release without ever replacing a different issuer. */
|
|
6
|
-
export async function reconcileInstallation(input, dependencies = {
|
|
7
|
-
run: runAstrale,
|
|
8
|
-
}) {
|
|
9
|
-
const origin = input.release.publication.origin;
|
|
10
|
-
const revision = input.release.publication.schema.revision;
|
|
11
|
-
const issuer = canonicalIssuer(input.release.addressing.issuer);
|
|
12
|
-
const etag = input.release.publication.etag;
|
|
13
|
-
const identity = input.identity ?? input.target.identity;
|
|
14
|
-
const authority = astraleAuthorityArguments({
|
|
15
|
-
...(identity === undefined ? {} : { identity }),
|
|
16
|
-
...(input.credential === undefined ? {} : { credential: input.credential }),
|
|
17
|
-
});
|
|
18
|
-
const executable = input.executable;
|
|
19
|
-
const before = await observe(input, origin, authority, executable, dependencies);
|
|
20
|
-
if (before !== undefined) {
|
|
21
|
-
assertIdentity(before, { origin, issuer });
|
|
22
|
-
if (before.revision === revision && before.etag === etag) {
|
|
23
|
-
return evidence('unchanged', input.target.instance, before);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
let installationFailure;
|
|
27
|
-
try {
|
|
28
|
-
const installed = await dependencies.run({
|
|
29
|
-
executable,
|
|
30
|
-
projectDir: input.projectDir,
|
|
31
|
-
signal: input.signal,
|
|
32
|
-
timeoutMs: 250_000,
|
|
33
|
-
args: [
|
|
34
|
-
'--ci',
|
|
35
|
-
'--no-prompt',
|
|
36
|
-
'domain',
|
|
37
|
-
'install',
|
|
38
|
-
input.release.addressing.publicUrl,
|
|
39
|
-
'--direct',
|
|
40
|
-
'--allow-identity-override',
|
|
41
|
-
'-i',
|
|
42
|
-
input.target.instance,
|
|
43
|
-
'--json',
|
|
44
|
-
'--timeout',
|
|
45
|
-
'240000',
|
|
46
|
-
...authority,
|
|
47
|
-
],
|
|
48
|
-
});
|
|
49
|
-
if (installed.code !== 0)
|
|
50
|
-
installationFailure = astraleFailure('domain install', installed);
|
|
51
|
-
}
|
|
52
|
-
catch (cause) {
|
|
53
|
-
installationFailure = cause;
|
|
54
|
-
}
|
|
55
|
-
let after;
|
|
56
|
-
try {
|
|
57
|
-
after = await observe(input, origin, authority, executable, dependencies);
|
|
58
|
-
}
|
|
59
|
-
catch (cause) {
|
|
60
|
-
throw installationError(installationFailure === undefined ? [cause] : [installationFailure, cause], 'Domain installation outcome is unknown because reconciliation failed.');
|
|
61
|
-
}
|
|
62
|
-
if (installationFailure !== undefined && after === undefined) {
|
|
63
|
-
throw installationError([installationFailure], 'Domain installation failed and no installed Release was observed.');
|
|
64
|
-
}
|
|
65
|
-
if (after === undefined)
|
|
66
|
-
throw new TypeError('Installed Domain was absent after installation.');
|
|
67
|
-
try {
|
|
68
|
-
assertIdentity(after, { origin, issuer });
|
|
69
|
-
if (after.revision !== revision || after.etag !== etag) {
|
|
70
|
-
throw new TypeError('Installed Domain evidence differs from the deployed Release.');
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
catch (cause) {
|
|
74
|
-
if (installationFailure === undefined)
|
|
75
|
-
throw cause;
|
|
76
|
-
throw installationError([installationFailure, cause], 'Domain installation failed with a different observed Release.');
|
|
77
|
-
}
|
|
78
|
-
return evidence(before === undefined ? 'installed' : 'upgraded', input.target.instance, after);
|
|
79
|
-
}
|
|
80
|
-
function installationError(causes, message) {
|
|
81
|
-
const diagnostics = causes
|
|
82
|
-
.filter((cause) => cause instanceof AstraleCliError)
|
|
83
|
-
.map((cause) => cause.message);
|
|
84
|
-
return new AggregateError(causes, [message, ...diagnostics].join(' '));
|
|
85
|
-
}
|
|
86
|
-
async function observe(input, origin, authority, executable, dependencies) {
|
|
87
|
-
const result = await dependencies.run({
|
|
88
|
-
executable,
|
|
89
|
-
projectDir: input.projectDir,
|
|
90
|
-
signal: input.signal,
|
|
91
|
-
timeoutMs: 70_000,
|
|
92
|
-
args: [
|
|
93
|
-
'--ci',
|
|
94
|
-
'--no-prompt',
|
|
95
|
-
'introspect',
|
|
96
|
-
origin,
|
|
97
|
-
'-i',
|
|
98
|
-
input.target.instance,
|
|
99
|
-
'--json',
|
|
100
|
-
'--timeout',
|
|
101
|
-
'60000',
|
|
102
|
-
...authority,
|
|
103
|
-
],
|
|
104
|
-
});
|
|
105
|
-
if (result.code !== 0) {
|
|
106
|
-
if (isDomainNotInstalledFailure(result.stderr))
|
|
107
|
-
return undefined;
|
|
108
|
-
throw astraleFailure(`introspect ${origin}`, result);
|
|
109
|
-
}
|
|
110
|
-
return decodeInstallationObservation(result.stdout);
|
|
111
|
-
}
|
|
112
|
-
function assertIdentity(observed, expected) {
|
|
113
|
-
if (observed.origin !== expected.origin) {
|
|
114
|
-
throw new TypeError('Installed Domain evidence has a different origin.');
|
|
115
|
-
}
|
|
116
|
-
if (observed.issuer !== expected.issuer) {
|
|
117
|
-
throw new TypeError(`Installed Domain issuer ${observed.issuer} differs from deployment issuer ${expected.issuer}; refusing replacement.`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
function evidence(status, instance, observed) {
|
|
121
|
-
return Object.freeze({ status, instance, ...observed });
|
|
122
|
-
}
|
|
File without changes
|