runcloud 0.2.177 → 0.2.178
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.
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const PATH = '/run-cloud/admin/fleet-releases';
|
|
2
|
+
export const RELEASE_CHANNELS = ['ios/dev', 'ios/prod', 'android/dev', 'android/prod'];
|
|
3
|
+
export async function listReleaseChannels(client) {
|
|
4
|
+
const body = await client.get(PATH, { signal: AbortSignal.timeout(30000) });
|
|
5
|
+
if (!body?.configured)
|
|
6
|
+
throw new Error('Fleet release storage is not configured on this API');
|
|
7
|
+
return { unavailable: body.unavailable === true, channels: body.channels ?? [] };
|
|
8
|
+
}
|
|
9
|
+
export async function promoteReleaseChannel(client, input) {
|
|
10
|
+
if (!RELEASE_CHANNELS.includes(input.channel))
|
|
11
|
+
throw new Error(`Channel must be one of ${RELEASE_CHANNELS.join(', ')}`);
|
|
12
|
+
const { channels, unavailable } = await listReleaseChannels(client);
|
|
13
|
+
if (unavailable)
|
|
14
|
+
throw new Error('Fleet release storage is unavailable; nothing was promoted');
|
|
15
|
+
const current = channels.find(channel => channel.channel === input.channel);
|
|
16
|
+
return await client.post(PATH, { channel: input.channel, version: input.version, reason: input.reason,
|
|
17
|
+
digest: input.digest ?? null, artifact: input.artifact ?? null, expectedRevision: current?.revision ?? 0 }, { signal: AbortSignal.timeout(30000) });
|
|
18
|
+
}
|
|
19
|
+
export function releaseText(channels) {
|
|
20
|
+
return RELEASE_CHANNELS.map(name => {
|
|
21
|
+
const channel = channels.find(item => item.channel === name);
|
|
22
|
+
return channel
|
|
23
|
+
? `${name.padEnd(13)} ${channel.version} r${channel.revision} by ${channel.promotedBy} at ${channel.promotedAt}${channel.digest ? ` ${channel.digest}` : ''}`
|
|
24
|
+
: `${name.padEnd(13)} unset (hosts use their inventory pin)`;
|
|
25
|
+
}).join('\n');
|
|
26
|
+
}
|
|
@@ -4,6 +4,7 @@ import { readFileSync, statSync } from 'node:fs';
|
|
|
4
4
|
import { ApiClient, ApiError } from '../api.js';
|
|
5
5
|
import { requireCredentials } from '../config.js';
|
|
6
6
|
import { surveyFleet, surveyText } from './run-cloud-fleet-survey.js';
|
|
7
|
+
import { listReleaseChannels, promoteReleaseChannel, releaseText } from './run-cloud-fleet-releases.js';
|
|
7
8
|
const ROOT = '/run-cloud/admin/simulators';
|
|
8
9
|
const RANGES = ['1h', '6h', '24h', '7d', '30d', '90d', '1y', '2y'];
|
|
9
10
|
export const ACCESSIBILITY_CODES = ['accessibility_device_unavailable', 'accessibility_capture_failed'];
|
|
@@ -300,7 +301,7 @@ function printReport(report, json) {
|
|
|
300
301
|
console.log(`${failure.timestamp ?? 'time missing'} ${failure.code ?? failure.type ?? 'failure'} host=${failure.hostId ?? 'unknown'} device=${failure.deviceId ?? 'unknown'} providerSession=${failure.providerSessionId ?? 'unknown'}; ${failure.correlation.causation}`);
|
|
301
302
|
}
|
|
302
303
|
export function registerRunCloudFleet(program) {
|
|
303
|
-
const fleet = program.command('fleet').description('
|
|
304
|
+
const fleet = program.command('fleet').description('Admin fleet telemetry, diagnosis, events, failure evidence and serve-sim release channels');
|
|
304
305
|
fleet.command('survey [hostId]').description('Survey all fleet signals in seconds; authenticated admin reads, no local services')
|
|
305
306
|
.option('--json', 'Structured metrics, diagnosis, observations and coverage')
|
|
306
307
|
.option('--range <range>', 'Alert/input-lag window: 1h, 6h, 24h, 7d', '24h')
|
|
@@ -330,6 +331,19 @@ export function registerRunCloudFleet(program) {
|
|
|
330
331
|
requireFleetReadAccess(result.coverage.reason);
|
|
331
332
|
console.log(JSON.stringify({ coverage: result.coverage, items: result.items.map(eventEvidence) }, null, 2));
|
|
332
333
|
}));
|
|
334
|
+
fleet.command('releases').description('Show the serve-sim release channel each pool runs (admin read)')
|
|
335
|
+
.option('--json', 'Structured channel records')
|
|
336
|
+
.action(opts => action(async () => {
|
|
337
|
+
const { channels, unavailable } = await listReleaseChannels(credentialsClient());
|
|
338
|
+
console.log(opts.json ? JSON.stringify({ unavailable, channels }, null, 2) : `${unavailable ? 'Warning: release storage was partly unavailable\n' : ''}${releaseText(channels)}`);
|
|
339
|
+
}));
|
|
340
|
+
fleet.command('promote <channel> <version>').description('Point one pool (ios|android/dev|prod) at a serve-sim build; audited admin write, no host is touched')
|
|
341
|
+
.requiredOption('--reason <text>', 'Why this build is promoted; kept in the audit history')
|
|
342
|
+
.option('--digest <sha256>', 'Artifact content digest, sha256:<64 hex>').option('--artifact <ref>', 'Where the artifact comes from, e.g. gh-run:<id>')
|
|
343
|
+
.action((channel, version, opts) => action(async () => {
|
|
344
|
+
const release = await promoteReleaseChannel(credentialsClient(), { channel, version, reason: opts.reason, digest: opts.digest, artifact: opts.artifact });
|
|
345
|
+
console.log(`${release.channel} -> ${release.version} (revision ${release.revision}; was ${release.previous?.version ?? 'unset'})`);
|
|
346
|
+
}));
|
|
333
347
|
fleet.command('logs <hostId>').description('Read bounded retained host logs; no SSH or host changes')
|
|
334
348
|
.option('--source <source>', 'service, warm_pool, or event_log', 'service').option('--limit <rows>', 'Maximum lines (1..500)', '100')
|
|
335
349
|
.action((hostId, opts) => action(async () => {
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = '0.2.
|
|
1
|
+
export const CLI_VERSION = '0.2.178';
|
package/package.json
CHANGED
|
Binary file
|
package/runcloud-0.2.177.tgz
DELETED
|
Binary file
|