@astrale-os/sdk 0.6.0-beta.16 → 0.6.0-beta.17
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/deployment/address/summary.d.ts +3 -2
- package/dist/tooling/cli/arguments.d.ts +2 -0
- package/dist/tooling/cli/arguments.js +9 -0
- package/dist/tooling/cli/help.js +26 -14
- package/dist/tooling/cli/index.d.ts +1 -1
- package/dist/tooling/cli/installed-on.d.ts +83 -0
- package/dist/tooling/cli/installed-on.js +156 -0
- package/dist/tooling/cli/list.d.ts +56 -22
- package/dist/tooling/cli/list.js +111 -32
- package/dist/tooling/cli/orchestrate.js +23 -5
- package/dist/tooling/cli/registry/client.d.ts +11 -11
- package/dist/tooling/cli/registry/client.js +41 -18
- package/dist/tooling/cli/registry/error.d.ts +11 -0
- package/dist/tooling/cli/registry/error.js +17 -0
- package/dist/tooling/cli/registry/executable.d.ts +9 -0
- package/dist/tooling/cli/registry/executable.js +14 -3
- package/dist/tooling/cli/registry/index.d.ts +4 -1
- package/dist/tooling/cli/registry/index.js +3 -1
- package/dist/tooling/cli/registry/installations.d.ts +67 -0
- package/dist/tooling/cli/registry/installations.js +108 -0
- package/dist/tooling/cli/registry/process.d.ts +16 -1
- package/dist/tooling/cli/registry/process.js +39 -4
- package/package.json +1 -1
|
@@ -30,8 +30,9 @@ export declare const PLATFORM_OWNER = "platform";
|
|
|
30
30
|
* `summary:<label>` in the routing KV of a platform dispatch namespace (CT20, AM-131): the
|
|
31
31
|
* `DeploymentSummaryV1` of one deployment without its record, which stays at `record:<label>`.
|
|
32
32
|
*
|
|
33
|
-
* Its writers are the deployer (`cloudflare({ namespace })`), which writes it
|
|
34
|
-
* before `record:<label>`,
|
|
33
|
+
* Its writers are the deployer (`cloudflare({ namespace })`), which writes it at activation,
|
|
34
|
+
* before `record:<label>`, names in it the new key of a script it recreates under the label after
|
|
35
|
+
* an operator deleted it, and marks it published (`retain`), and the platform retirement tooling
|
|
35
36
|
* (SV6), which records a withdrawal. So it names the deployment by its label (`id` is the label),
|
|
36
37
|
* is owned by `platform`, is `active` until withdrawn, and carries no `lastCallAt` or `expiresAt`:
|
|
37
38
|
* nothing on the platform namespace keeps those clocks, and listings read them from the calls
|
|
@@ -19,6 +19,8 @@ export interface ParsedArgs {
|
|
|
19
19
|
/** `yank --undo`: restore the version instead of yanking it. */
|
|
20
20
|
readonly undo?: boolean;
|
|
21
21
|
readonly allowDirty?: boolean;
|
|
22
|
+
/** `list`: instances to ask what they run, in argument order. */
|
|
23
|
+
readonly instances?: readonly string[];
|
|
22
24
|
}
|
|
23
25
|
/** Parse the frozen `astrale-domain` command grammar without performing effects. */
|
|
24
26
|
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
@@ -30,6 +30,7 @@ export function parseArgs(argv) {
|
|
|
30
30
|
let suite;
|
|
31
31
|
let format;
|
|
32
32
|
let environment;
|
|
33
|
+
const instances = [];
|
|
33
34
|
const cleaned = [];
|
|
34
35
|
for (let index = 0; index < rest.length; index += 1) {
|
|
35
36
|
const argument = rest[index];
|
|
@@ -63,6 +64,13 @@ export function parseArgs(argv) {
|
|
|
63
64
|
else if (argument.startsWith('--environment=')) {
|
|
64
65
|
environment = requiredValue('--environment', argument.slice('--environment='.length));
|
|
65
66
|
}
|
|
67
|
+
else if (command === 'list' && argument === '--instance') {
|
|
68
|
+
// Only `list` asks instances; elsewhere `--instance` stays unknown, as deploy removed it.
|
|
69
|
+
instances.push(requiredValue('--instance', rest[++index]));
|
|
70
|
+
}
|
|
71
|
+
else if (command === 'list' && argument.startsWith('--instance=')) {
|
|
72
|
+
instances.push(requiredValue('--instance', argument.slice('--instance='.length)));
|
|
73
|
+
}
|
|
66
74
|
else if (argument === '--suite') {
|
|
67
75
|
suite = requiredValue('--suite', rest[++index]);
|
|
68
76
|
}
|
|
@@ -163,6 +171,7 @@ export function parseArgs(argv) {
|
|
|
163
171
|
env: environment ?? '',
|
|
164
172
|
...(json ? { json: true } : {}),
|
|
165
173
|
...(identity === undefined ? {} : { identity }),
|
|
174
|
+
...(instances.length === 0 ? {} : { instances: [...new Set(instances)] }),
|
|
166
175
|
};
|
|
167
176
|
case 'build':
|
|
168
177
|
case 'package':
|
package/dist/tooling/cli/help.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { installCommand } from '../../project/install-command.js';
|
|
2
2
|
import { COMMANDS } from './arguments.js';
|
|
3
|
+
import { MINIMUM_INSTALLATIONS_CLI_VERSION } from './registry/executable.js';
|
|
3
4
|
/** Resolve help without parsing a project or performing any command effects. */
|
|
4
5
|
export function helpRequest(argv) {
|
|
5
6
|
if (!argv.some((argument) => argument === '--help' || argument === '-h'))
|
|
@@ -157,39 +158,50 @@ Behavior:
|
|
|
157
158
|
astrale domain install takes, its digests, adapter and state. Exit status: 0 deployed,
|
|
158
159
|
1 failed or refused, 2 usage error; a failed run prints nothing on stdout.
|
|
159
160
|
`,
|
|
160
|
-
list: `List the deployments of the Project's Environments
|
|
161
|
+
list: `List the deployments of the Project's Environments, their versions and where they run.
|
|
161
162
|
|
|
162
163
|
Usage:
|
|
163
|
-
astrale-domain list [--environment <name>] [--json]
|
|
164
|
+
astrale-domain list [--environment <name>] [--instance <name>]... [--json]
|
|
164
165
|
|
|
165
166
|
Options:
|
|
166
167
|
--environment <name> List one Environment instead of every Environment.
|
|
168
|
+
--instance <name> Ask this instance what it runs, for INSTALLED ON (repeatable).
|
|
167
169
|
--json Print one ListResultV1 on stdout; notices go to stderr.
|
|
168
|
-
--as <identity> Select the Astrale identity
|
|
170
|
+
--as <identity> Select the Astrale identity for the hosts, the registry and the
|
|
171
|
+
instances.
|
|
169
172
|
-h, --help Show help for list.
|
|
170
173
|
|
|
171
174
|
Behavior:
|
|
172
175
|
Asks the adapter of each Environment that deploys immutable deployments for the
|
|
173
|
-
deployments its host keeps for the caller, and prints each one's release, its
|
|
174
|
-
Environment and state,
|
|
175
|
-
${installCommand()} takes.
|
|
176
|
-
|
|
177
|
-
staging.
|
|
176
|
+
deployments its host keeps for the caller, and prints each one's release, its version
|
|
177
|
+
or name, Environment and state, the instances it is installed on, its last call and
|
|
178
|
+
its expiry, and the URL ${installCommand()} takes.
|
|
179
|
+
A deployment is named by the registry's version of its release (1.5.0) or of its build
|
|
180
|
+
(1.5.0 · staging), else from its record's commit, such as 1.4.2 + 7 commits · a1b2c3d ·
|
|
181
|
+
staging. Names are computed, never stored, and printed whole.
|
|
182
|
+
INSTALLED ON names each --instance whose Kernel pins the deployment. ? marks what is
|
|
183
|
+
unknown: no instance was asked, an instance could not tell (such as one whose Kernel
|
|
184
|
+
does not list installed releases), or its listing shows only what you can read there;
|
|
185
|
+
stderr says which and why. An unknown instance is never shown as running nothing; —
|
|
186
|
+
means every instance asked listed all it runs and none runs it.
|
|
187
|
+
The registry and the instances are read through the Astrale CLI (${MINIMUM_INSTALLATIONS_CLI_VERSION} or
|
|
188
|
+
newer) with the caller's identity, only once a deployment is listed.
|
|
189
|
+
A registry that cannot be read fails the listing; an unknown instance never does.
|
|
178
190
|
A preview expires 30 days after its activation or its last call, whichever is
|
|
179
191
|
later; a published deployment never does. Last calls are read from the host as it
|
|
180
192
|
counts them, so a host whose last calls cannot be read is not listed. A platform
|
|
181
193
|
dispatch namespace reads them from its calls dataset, and without Account Analytics
|
|
182
194
|
Read lists its deployments without last calls or expiries.
|
|
183
|
-
Listing changes nothing, at any host or instance.
|
|
195
|
+
Listing changes nothing, at any host, registry or instance.
|
|
184
196
|
An Environment in legacy direct mode, refused by deploy, or whose adapter lists no
|
|
185
197
|
deployments is reported on stderr and, with --json, under unlisted. A deployment
|
|
186
198
|
whose record this SDK does not admit is still listed, from what its host knows.
|
|
187
|
-
With --json, each deployment carries its call target: the path of
|
|
188
|
-
Admin's Services, for astrale call "<path>.method.setSecret" --admin, or the
|
|
199
|
+
With --json, each deployment carries its installedOn and its call target: the path of
|
|
200
|
+
its node on Admin's Services, for astrale call "<path>.method.setSecret" --admin, or the
|
|
189
201
|
namespace and script of a platform dispatch namespace, which its operator tooling
|
|
190
|
-
takes.
|
|
191
|
-
Exit status: 0 listed, 1 a host could not be
|
|
192
|
-
stdout), 2 usage error.
|
|
202
|
+
takes; installations says what of INSTALLED ON is unknown.
|
|
203
|
+
Exit status: 0 listed, 1 a host or the registry could not be read (with --json,
|
|
204
|
+
nothing on stdout), 2 usage error.
|
|
193
205
|
`,
|
|
194
206
|
diff: `Show the changes since the last published version and the version they require.
|
|
195
207
|
|
|
@@ -15,7 +15,7 @@ export { run } from './run.js';
|
|
|
15
15
|
export type { BuildResultV1 } from './build.js';
|
|
16
16
|
export type { DeployResultV1, DeploymentSecretsState } from './deploy-result.js';
|
|
17
17
|
export type { DiffChangeV1, DiffReportV1 } from './diff/index.js';
|
|
18
|
-
export type { ListedDeploymentV1, ListResultV1, UnlistedEnvironmentV1 } from './list.js';
|
|
18
|
+
export type { InstallationSourcesV1, InstallationUnknownReason, InstalledOnV1, InstanceSourceV1, ListedDeploymentV1, ListResultV1, UnlistedEnvironmentV1, } from './list.js';
|
|
19
19
|
export type { PublishReportV1 } from './publish/index.js';
|
|
20
20
|
export type { YankReportV1 } from './yank.js';
|
|
21
21
|
export { selectDeployMode } from './deploy-mode.js';
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { DomainRegistry, InstallationUnknownReason, InstanceInstallation } from './registry/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* One instance known to pin a deployment (INSTALLÉ SUR, DX [.83795]): an instance named with
|
|
4
|
+
* `--instance`, as its Kernel lists it (CT24). Admin does not say which instances pin a release
|
|
5
|
+
* (A15 is deferred, AM-241), so an instance is known only when it is asked.
|
|
6
|
+
*/
|
|
7
|
+
export interface InstalledOnV1 {
|
|
8
|
+
readonly source: 'instance';
|
|
9
|
+
/** The instance as `--instance` named it. */
|
|
10
|
+
readonly instance: string;
|
|
11
|
+
/** The instance Kernel URL that answered. */
|
|
12
|
+
readonly kernel: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Where INSTALLÉ SUR comes from and what of it is unknown. An instance whose installations are
|
|
16
|
+
* unknown is partial, never "not installed" (AM-198); an instance is known only when `--instance`
|
|
17
|
+
* names it ([.59528], AM-241).
|
|
18
|
+
*/
|
|
19
|
+
export interface InstallationSourcesV1 {
|
|
20
|
+
/**
|
|
21
|
+
* True when an instance may run a deployment that no source names: an `--instance` that could
|
|
22
|
+
* not say or whose listing is itself partial (a Kernel lists only what the caller reads, AM-83,
|
|
23
|
+
* AM-84), or no `--instance` at all. False only when every `--instance` listed all it runs.
|
|
24
|
+
*/
|
|
25
|
+
readonly partial: boolean;
|
|
26
|
+
/** One entry per distinct `--instance`, in argument order. */
|
|
27
|
+
readonly instances: readonly InstanceSourceV1[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* One `--instance`: what its Kernel answered, why that is unknown, or that it was not asked. A
|
|
31
|
+
* `read` listing names the deployments it pins; with `partial` (always, from C7) one it does not
|
|
32
|
+
* name may still run there.
|
|
33
|
+
*/
|
|
34
|
+
export type InstanceSourceV1 = {
|
|
35
|
+
readonly instance: string;
|
|
36
|
+
readonly state: 'read';
|
|
37
|
+
readonly kernel: string;
|
|
38
|
+
readonly partial: boolean;
|
|
39
|
+
} | {
|
|
40
|
+
readonly instance: string;
|
|
41
|
+
readonly state: 'unknown';
|
|
42
|
+
readonly reason: InstallationUnknownReason;
|
|
43
|
+
readonly code: string;
|
|
44
|
+
} | {
|
|
45
|
+
readonly instance: string;
|
|
46
|
+
readonly state: 'skipped';
|
|
47
|
+
};
|
|
48
|
+
/** What the sources answered, kept to join each listed deployment to the instances that pin it. */
|
|
49
|
+
export interface InstallationsRead {
|
|
50
|
+
readonly sources: InstallationSourcesV1;
|
|
51
|
+
readonly instances: readonly {
|
|
52
|
+
readonly instance: string;
|
|
53
|
+
readonly kernel: string;
|
|
54
|
+
readonly installations: readonly InstanceInstallation[];
|
|
55
|
+
}[];
|
|
56
|
+
}
|
|
57
|
+
/** Nothing was listed: no source is asked. */
|
|
58
|
+
export declare function skippedInstallations(instances: readonly string[]): InstallationsRead;
|
|
59
|
+
/**
|
|
60
|
+
* Ask every `--instance` whether it pins a deployment of the origin. An instance that cannot tell
|
|
61
|
+
* is recorded as unknown, with why, and the listing stays partial for it (AM-198); a refusal's
|
|
62
|
+
* message is never relayed, only its code. Only a stop of `signal`, the one every registry read
|
|
63
|
+
* follows, fails it: an interrupted listing is never printed as a partial one.
|
|
64
|
+
*/
|
|
65
|
+
export declare function readInstallations(registry: Pick<DomainRegistry, 'instance'>, origin: string, instances: readonly string[], signal: AbortSignal): Promise<InstallationsRead>;
|
|
66
|
+
/**
|
|
67
|
+
* The instances known to pin the deployment served at `url`: an installation whose Kernel lists
|
|
68
|
+
* its pin under that deployment URL runs that deployment, since one immutable deployment has one
|
|
69
|
+
* URL. In `--instance` argument order.
|
|
70
|
+
*/
|
|
71
|
+
export declare function installedOn(url: string, read: InstallationsRead): readonly InstalledOnV1[];
|
|
72
|
+
/**
|
|
73
|
+
* The INSTALLED ON cell: the instances known to pin the deployment, then `?` when an instance may
|
|
74
|
+
* run it unnamed (the notices say which); `—` only when every `--instance` listed all it runs and
|
|
75
|
+
* none pins it.
|
|
76
|
+
*/
|
|
77
|
+
export declare function installedOnCell(installed: readonly InstalledOnV1[], sources: InstallationSourcesV1): string;
|
|
78
|
+
/**
|
|
79
|
+
* One line for stderr per source that could not tell, or one when no instance was asked: an
|
|
80
|
+
* unknown instance is partial, never "not installed" (AM-198). Only codes the CLI reported are
|
|
81
|
+
* named, never its messages.
|
|
82
|
+
*/
|
|
83
|
+
export declare function installationNotices(sources: InstallationSourcesV1): readonly string[];
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { isAdminFailure } from './registry/index.js';
|
|
2
|
+
/** `--instance` listings run at once; each is one CLI process reading one instance Kernel. */
|
|
3
|
+
const INSTANCE_READS = 4;
|
|
4
|
+
/** Nothing was listed: no source is asked. */
|
|
5
|
+
export function skippedInstallations(instances) {
|
|
6
|
+
return Object.freeze({
|
|
7
|
+
sources: Object.freeze({
|
|
8
|
+
partial: false,
|
|
9
|
+
instances: Object.freeze(distinct(instances).map((instance) => Object.freeze({ instance, state: 'skipped' }))),
|
|
10
|
+
}),
|
|
11
|
+
instances: Object.freeze([]),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Ask every `--instance` whether it pins a deployment of the origin. An instance that cannot tell
|
|
16
|
+
* is recorded as unknown, with why, and the listing stays partial for it (AM-198); a refusal's
|
|
17
|
+
* message is never relayed, only its code. Only a stop of `signal`, the one every registry read
|
|
18
|
+
* follows, fails it: an interrupted listing is never printed as a partial one.
|
|
19
|
+
*/
|
|
20
|
+
export async function readInstallations(registry, origin, instances, signal) {
|
|
21
|
+
const listed = await mapBounded(distinct(instances), INSTANCE_READS, async (instance) => {
|
|
22
|
+
try {
|
|
23
|
+
return { instance, listing: await registry.instance(instance) };
|
|
24
|
+
}
|
|
25
|
+
catch (cause) {
|
|
26
|
+
if (signal.aborted)
|
|
27
|
+
throw cause;
|
|
28
|
+
// The CLI could not run for this instance: unknown, never a failure of the listing.
|
|
29
|
+
return {
|
|
30
|
+
instance,
|
|
31
|
+
listing: {
|
|
32
|
+
kind: 'unknown',
|
|
33
|
+
reason: 'unavailable',
|
|
34
|
+
code: 'REGISTRY_CLI_FAILED',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
const instanceSources = [];
|
|
40
|
+
const read = [];
|
|
41
|
+
for (const { instance, listing } of listed) {
|
|
42
|
+
if (listing.kind === 'listed') {
|
|
43
|
+
instanceSources.push(Object.freeze({
|
|
44
|
+
instance,
|
|
45
|
+
state: 'read',
|
|
46
|
+
kernel: listing.kernel,
|
|
47
|
+
partial: listing.partial,
|
|
48
|
+
}));
|
|
49
|
+
read.push(Object.freeze({
|
|
50
|
+
instance,
|
|
51
|
+
kernel: listing.kernel,
|
|
52
|
+
installations: listing.installations.filter((entry) => entry.origin === origin),
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
instanceSources.push(Object.freeze({ instance, state: 'unknown', reason: listing.reason, code: listing.code }));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const partial = instanceSources.length === 0 ||
|
|
60
|
+
instanceSources.some((entry) => entry.state === 'unknown' || (entry.state === 'read' && entry.partial));
|
|
61
|
+
return Object.freeze({
|
|
62
|
+
sources: Object.freeze({ partial, instances: Object.freeze(instanceSources) }),
|
|
63
|
+
instances: Object.freeze(read),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The instances known to pin the deployment served at `url`: an installation whose Kernel lists
|
|
68
|
+
* its pin under that deployment URL runs that deployment, since one immutable deployment has one
|
|
69
|
+
* URL. In `--instance` argument order.
|
|
70
|
+
*/
|
|
71
|
+
export function installedOn(url, read) {
|
|
72
|
+
const deployment = urlOrigin(url);
|
|
73
|
+
if (deployment === undefined)
|
|
74
|
+
return Object.freeze([]);
|
|
75
|
+
const asked = read.instances
|
|
76
|
+
.filter((entry) => entry.installations.some((installation) => urlOrigin(installation.url) === deployment))
|
|
77
|
+
.map((entry) => Object.freeze({
|
|
78
|
+
source: 'instance',
|
|
79
|
+
instance: entry.instance,
|
|
80
|
+
kernel: entry.kernel,
|
|
81
|
+
}));
|
|
82
|
+
return Object.freeze(asked);
|
|
83
|
+
}
|
|
84
|
+
/** Names shown before the cell counts the rest; the JSON keeps them all. */
|
|
85
|
+
const CELL_NAMES = 3;
|
|
86
|
+
/**
|
|
87
|
+
* The INSTALLED ON cell: the instances known to pin the deployment, then `?` when an instance may
|
|
88
|
+
* run it unnamed (the notices say which); `—` only when every `--instance` listed all it runs and
|
|
89
|
+
* none pins it.
|
|
90
|
+
*/
|
|
91
|
+
export function installedOnCell(installed, sources) {
|
|
92
|
+
const names = distinct(installed.map((entry) => entry.instance));
|
|
93
|
+
const shown = names.length > CELL_NAMES
|
|
94
|
+
? `${names.slice(0, CELL_NAMES).join(', ')} +${names.length - CELL_NAMES}`
|
|
95
|
+
: names.join(', ');
|
|
96
|
+
if (!sources.partial)
|
|
97
|
+
return names.length === 0 ? '—' : shown;
|
|
98
|
+
return names.length === 0 ? '?' : `${shown}, ?`;
|
|
99
|
+
}
|
|
100
|
+
const INSTANCE_REASONS = {
|
|
101
|
+
timeout: 'it did not answer in time',
|
|
102
|
+
refused: 'its Kernel refused the read',
|
|
103
|
+
unavailable: 'it could not be reached',
|
|
104
|
+
unsupported: 'its Kernel does not list installed releases',
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* One line for stderr per source that could not tell, or one when no instance was asked: an
|
|
108
|
+
* unknown instance is partial, never "not installed" (AM-198). Only codes the CLI reported are
|
|
109
|
+
* named, never its messages.
|
|
110
|
+
*/
|
|
111
|
+
export function installationNotices(sources) {
|
|
112
|
+
const notices = [];
|
|
113
|
+
if (sources.instances.length === 0) {
|
|
114
|
+
notices.push('INSTALLED ON is unknown: no instance was asked, and Admin does not say which instances ' +
|
|
115
|
+
'pin a deployment. Ask an instance with --instance <name>.');
|
|
116
|
+
}
|
|
117
|
+
for (const entry of sources.instances) {
|
|
118
|
+
if (entry.state === 'read') {
|
|
119
|
+
if (entry.partial) {
|
|
120
|
+
notices.push(`INSTALLED ON is partial: instance ${entry.instance} lists only what its Kernel shows ` +
|
|
121
|
+
'this caller, so a deployment it does not list may still run there.');
|
|
122
|
+
}
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (entry.state !== 'unknown')
|
|
126
|
+
continue;
|
|
127
|
+
notices.push(`INSTALLED ON is partial: what instance ${entry.instance} runs is unknown (${entry.code}): ` +
|
|
128
|
+
(isAdminFailure(entry.code)
|
|
129
|
+
? "the Astrale CLI could not read Admin's registry, which names what the instance runs."
|
|
130
|
+
: `${INSTANCE_REASONS[entry.reason]}.`));
|
|
131
|
+
}
|
|
132
|
+
return Object.freeze(notices);
|
|
133
|
+
}
|
|
134
|
+
function urlOrigin(input) {
|
|
135
|
+
try {
|
|
136
|
+
return new URL(input).origin;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function distinct(values) {
|
|
143
|
+
return [...new Set(values)];
|
|
144
|
+
}
|
|
145
|
+
async function mapBounded(inputs, limit, map) {
|
|
146
|
+
const outputs = Array.from({ length: inputs.length });
|
|
147
|
+
let next = 0;
|
|
148
|
+
const worker = async () => {
|
|
149
|
+
while (next < inputs.length) {
|
|
150
|
+
const index = next++;
|
|
151
|
+
outputs[index] = await map(inputs[index]);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
await Promise.all(Array.from({ length: Math.min(limit, inputs.length) }, worker));
|
|
155
|
+
return outputs;
|
|
156
|
+
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { DeploymentRecordV1, DeploymentSummaryV1 } from '../../deployment/address/index.js';
|
|
2
|
-
import type { DeploymentCallTarget
|
|
2
|
+
import type { DeploymentCallTarget } from '../../deployment/index.js';
|
|
3
|
+
import type { InstallationSourcesV1, InstalledOnV1 } from './installed-on.js';
|
|
3
4
|
import type { LoadedEnvironment } from './project.js';
|
|
5
|
+
import type { DomainRegistry } from './registry/index.js';
|
|
6
|
+
export type { InstallationSourcesV1, InstalledOnV1, InstanceSourceV1 } from './installed-on.js';
|
|
7
|
+
export type { InstallationUnknownReason } from './registry/index.js';
|
|
4
8
|
/**
|
|
5
9
|
* `astrale-domain list --json` on stdout: the deployments of the Project's Environments that their
|
|
6
|
-
* hosts keep for the caller, each with its computed name
|
|
7
|
-
* nothing, at any host or instance.
|
|
10
|
+
* hosts keep for the caller, each with its computed name, the instances known to pin it and where
|
|
11
|
+
* it is called. It changes nothing, at any host, registry or instance.
|
|
8
12
|
*/
|
|
9
13
|
export interface ListResultV1 {
|
|
10
14
|
readonly format: 'astrale.deployment-list';
|
|
@@ -17,6 +21,8 @@ export interface ListResultV1 {
|
|
|
17
21
|
* complete exactly when this is empty.
|
|
18
22
|
*/
|
|
19
23
|
readonly unlisted: readonly UnlistedEnvironmentV1[];
|
|
24
|
+
/** Where every `installedOn` comes from, and what of it is unknown (AM-198). */
|
|
25
|
+
readonly installations: InstallationSourcesV1;
|
|
20
26
|
}
|
|
21
27
|
/**
|
|
22
28
|
* One deployment: what its host knows (`DeploymentSummaryV1`), the Environment it was listed for
|
|
@@ -31,10 +37,19 @@ export type ListedDeploymentV1 = Omit<DeploymentSummaryV1, 'record'> & {
|
|
|
31
37
|
/** `<adapter name>@<adapter version>` */
|
|
32
38
|
readonly adapter: string;
|
|
33
39
|
/**
|
|
34
|
-
* The deployment's name (`deploymentName`, CT28), computed
|
|
35
|
-
* `1.
|
|
40
|
+
* The deployment's name (`deploymentName`, CT28), computed and never stored: the version of the
|
|
41
|
+
* registry that names its release (`1.5.0`), or its build elsewhere (`1.5.0 · staging`), else
|
|
42
|
+
* from its record's commit (`1.4.2 + 7 commits · a1b2c3d · staging`). Print it as it is,
|
|
43
|
+
* environment included (AM-57).
|
|
36
44
|
*/
|
|
37
45
|
readonly name: string;
|
|
46
|
+
/**
|
|
47
|
+
* The instances known to pin this deployment: those `--instance` asked, as their Kernels list
|
|
48
|
+
* them (CT24). Partial by nature: an instance is known only when asked, and one whose
|
|
49
|
+
* installations are unknown is under `installations`, never absent from here as "not installed"
|
|
50
|
+
* (AM-198).
|
|
51
|
+
*/
|
|
52
|
+
readonly installedOn: readonly InstalledOnV1[];
|
|
38
53
|
/**
|
|
39
54
|
* The record its deployer declared, as `acceptDeploymentRecord` admits it. `null` when there is
|
|
40
55
|
* no admitted record: the host returns a record this SDK refuses or one that describes another
|
|
@@ -66,7 +81,10 @@ export interface UnlistedEnvironmentV1 {
|
|
|
66
81
|
/** What a listing found, beside the result: what to tell a person, and what failed. */
|
|
67
82
|
export interface DeploymentListing {
|
|
68
83
|
readonly result: ListResultV1;
|
|
69
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* One line for each Environment not listed, each record not admitted and each source of
|
|
86
|
+
* INSTALLED ON that could not tell, for stderr.
|
|
87
|
+
*/
|
|
70
88
|
readonly notices: readonly string[];
|
|
71
89
|
/** Environments whose host could not be listed: their deployments are missing from the result. */
|
|
72
90
|
readonly failures: readonly {
|
|
@@ -74,9 +92,21 @@ export interface DeploymentListing {
|
|
|
74
92
|
readonly adapter: string;
|
|
75
93
|
readonly error: Error;
|
|
76
94
|
}[];
|
|
95
|
+
/**
|
|
96
|
+
* The Domain registry could not be read, so no deployment can be named by its version and the
|
|
97
|
+
* result is not to be printed: versions are never dropped silently (AM-224). That is a missing
|
|
98
|
+
* Astrale CLI, one older than the listing's floor, no identity it may use, an Admin it cannot
|
|
99
|
+
* reach, an index it cannot read, or an interruption while it reads; never a source of
|
|
100
|
+
* INSTALLED ON, which only makes the listing partial.
|
|
101
|
+
*/
|
|
102
|
+
readonly registryFailure?: Error;
|
|
77
103
|
}
|
|
104
|
+
/** What the listing reads through the Astrale CLI: the registry index and the instances. */
|
|
105
|
+
export type DeploymentRegistry = Pick<DomainRegistry, 'index' | 'instance'>;
|
|
78
106
|
/**
|
|
79
|
-
* List the deployments of the selected Environments, one adapter `list` call each
|
|
107
|
+
* List the deployments of the selected Environments, one adapter `list` call each, then name them
|
|
108
|
+
* and say where they are installed (tech [.59528]: the records, the registry's versions and the
|
|
109
|
+
* instances' pins).
|
|
80
110
|
*
|
|
81
111
|
* Only an Environment that deploys immutable deployments (CT33 canonical) is listed; the others are
|
|
82
112
|
* `unlisted`, with the reason. A record the host returns but `acceptDeploymentRecord` refuses never
|
|
@@ -85,6 +115,14 @@ export interface DeploymentListing {
|
|
|
85
115
|
* record itself may fail its own list (Admin's Services does, AM-152). An Environment whose host
|
|
86
116
|
* cannot be listed, or whose adapter answers anything but deployments, is a failure, and the others
|
|
87
117
|
* are still listed.
|
|
118
|
+
*
|
|
119
|
+
* Once a deployment is listed, the registry is opened once: its index of the origin names each
|
|
120
|
+
* deployment by the version of its release or build (CT28; none when the origin is unregistered or
|
|
121
|
+
* not readable), and the instances that pin each deployment come from each `--instance` (CT24);
|
|
122
|
+
* Admin does not say which instances pin a release (AM-241). A registry that cannot be opened or
|
|
123
|
+
* whose index cannot be read is `registryFailure`, and the instance reads still running are
|
|
124
|
+
* stopped at once; an instance that cannot tell never fails the listing: it is partial (AM-198,
|
|
125
|
+
* AM-224).
|
|
88
126
|
*/
|
|
89
127
|
export declare function listDeployments(input: {
|
|
90
128
|
readonly origin: string;
|
|
@@ -92,23 +130,19 @@ export declare function listDeployments(input: {
|
|
|
92
130
|
readonly environments: readonly (readonly [string, LoadedEnvironment])[];
|
|
93
131
|
readonly identity?: string;
|
|
94
132
|
readonly signal: AbortSignal;
|
|
133
|
+
/**
|
|
134
|
+
* Opens the Domain registry, once, and only when at least one deployment is listed. Every read
|
|
135
|
+
* it makes follows `signal`, which also stops when the index cannot be read.
|
|
136
|
+
*/
|
|
137
|
+
readonly registry: (signal: AbortSignal) => Promise<DeploymentRegistry>;
|
|
138
|
+
/** Instances to ask what they run (`--instance`, repeatable). */
|
|
139
|
+
readonly instances?: readonly string[];
|
|
95
140
|
}): Promise<DeploymentListing>;
|
|
96
|
-
/**
|
|
97
|
-
* One listed deployment, its record admitted when it describes exactly this deployment of this
|
|
98
|
-
* origin and Environment. Its name comes from the admitted record; otherwise it is named in the
|
|
99
|
-
* listed Environment, with the commit of the stored record only when that record names this
|
|
100
|
-
* deployment, origin and Environment, as `deploymentName` reads a commit.
|
|
101
|
-
*/
|
|
102
|
-
export declare function listedDeployment(deployment: ListedDeployment, listing: {
|
|
103
|
-
readonly origin: string;
|
|
104
|
-
readonly environment: string;
|
|
105
|
-
readonly adapter: string;
|
|
106
|
-
}): ListedDeploymentV1;
|
|
107
141
|
/**
|
|
108
142
|
* The listing as a table for a person, at `now`: each deployment's release, its name as computed
|
|
109
|
-
* (never shortened, AM-57), its Environment and state,
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* was not.
|
|
143
|
+
* (never shortened, AM-57), its Environment and state, the instances known to pin it (`?` when a
|
|
144
|
+
* source could not tell, AM-198), its last call and its expiry, and the URL `astrale domain
|
|
145
|
+
* install` takes. Without deployments, it says so only for what was listed: nothing when a host
|
|
146
|
+
* `failed` (stderr says which), and that some Environments were not listed when any was not.
|
|
113
147
|
*/
|
|
114
148
|
export declare function renderDeploymentList(result: ListResultV1, now: number, failed?: boolean): readonly string[];
|