|
| 1 | +import { Argv } from 'yargs'; |
| 2 | +import { LogLevel, mapLogLevel } from '../../spec-utils/log'; |
| 3 | +import { getPackageConfig } from '../../spec-utils/product'; |
| 4 | +import { createLog } from '../devContainers'; |
| 5 | +import { fetchOCIManifestIfExists, getRef } from '../../spec-configuration/containerCollectionsOCI'; |
| 6 | + |
| 7 | +import { UnpackArgv } from '../devContainersSpecCLI'; |
| 8 | + |
| 9 | +export function templateMetadataOptions(y: Argv) { |
| 10 | + return y |
| 11 | + .options({ |
| 12 | + 'log-level': { choices: ['info' as 'info', 'debug' as 'debug', 'trace' as 'trace'], default: 'info' as 'info', description: 'Log level.' }, |
| 13 | + }) |
| 14 | + .positional('templateId', { type: 'string', demandOption: true, description: 'Template Identifier' }); |
| 15 | +} |
| 16 | + |
| 17 | +export type TemplateMetadataArgs = UnpackArgv<ReturnType<typeof templateMetadataOptions>>; |
| 18 | + |
| 19 | +export function templateMetadataHandler(args: TemplateMetadataArgs) { |
| 20 | + (async () => await templateMetadata(args))().catch(console.error); |
| 21 | +} |
| 22 | + |
| 23 | +async function templateMetadata({ |
| 24 | + 'log-level': inputLogLevel, |
| 25 | + 'templateId': templateId, |
| 26 | +}: TemplateMetadataArgs) { |
| 27 | + const disposables: (() => Promise<unknown> | undefined)[] = []; |
| 28 | + const dispose = async () => { |
| 29 | + await Promise.all(disposables.map(d => d())); |
| 30 | + }; |
| 31 | + |
| 32 | + const pkg = getPackageConfig(); |
| 33 | + |
| 34 | + const output = createLog({ |
| 35 | + logLevel: mapLogLevel(inputLogLevel), |
| 36 | + logFormat: 'text', |
| 37 | + log: (str) => process.stderr.write(str), |
| 38 | + terminalDimensions: undefined, |
| 39 | + }, pkg, new Date(), disposables); |
| 40 | + |
| 41 | + const params = { output, env: process.env }; |
| 42 | + output.write(`Fetching metadata for ${templateId}`, LogLevel.Trace); |
| 43 | + |
| 44 | + const templateRef = getRef(output, templateId); |
| 45 | + if (!templateRef) { |
| 46 | + console.log(JSON.stringify({})); |
| 47 | + process.exit(1); |
| 48 | + } |
| 49 | + |
| 50 | + const manifestContainer = await fetchOCIManifestIfExists(params, templateRef, undefined); |
| 51 | + if (!manifestContainer) { |
| 52 | + console.log(JSON.stringify({})); |
| 53 | + process.exit(1); |
| 54 | + } |
| 55 | + |
| 56 | + const { manifestObj, canonicalId } = manifestContainer; |
| 57 | + output.write(`Template '${templateId}' resolved to '${canonicalId}'`, LogLevel.Trace); |
| 58 | + |
| 59 | + // Templates must have been published with a CLI post commit |
| 60 | + // https://github.com/devcontainers/cli/commit/6c6aebfa7b74aea9d67760fd1e74b09573d31536 |
| 61 | + // in order to contain attached metadata. |
| 62 | + const metadata = manifestObj.annotations?.['dev.containers.metadata']; |
| 63 | + if (!metadata) { |
| 64 | + output.write(`Template resolved to '${canonicalId}' but does not contain metadata on its manifest.`, LogLevel.Warning); |
| 65 | + output.write(`Ask the Template owner to republish this Template to populate the manifest.`, LogLevel.Warning); |
| 66 | + console.log(JSON.stringify({})); |
| 67 | + process.exit(1); |
| 68 | + } |
| 69 | + |
| 70 | + const unescaped = JSON.parse(metadata); |
| 71 | + console.log(JSON.stringify(unescaped)); |
| 72 | + await dispose(); |
| 73 | + process.exit(); |
| 74 | +} |
0 commit comments