diff --git a/src/spec-configuration/containerCollectionsOCIPush.ts b/src/spec-configuration/containerCollectionsOCIPush.ts index 4175babc7..24f811663 100644 --- a/src/spec-configuration/containerCollectionsOCIPush.ts +++ b/src/spec-configuration/containerCollectionsOCIPush.ts @@ -11,7 +11,7 @@ import { requestEnsureAuthenticated } from './httpOCIRegistry'; // Devcontainer Spec (features) : https://containers.dev/implementors/features-distribution/#oci-registry // Devcontainer Spec (templates): https://github.com/devcontainers/spec/blob/main/proposals/devcontainer-templates-distribution.md#oci-registry // OCI Spec : https://github.com/opencontainers/distribution-spec/blob/main/spec.md#push -export async function pushOCIFeatureOrTemplate(params: CommonParams, ociRef: OCIRef, pathToTgz: string, tags: string[], collectionType: string, featureAnnotations = {}): Promise { +export async function pushOCIFeatureOrTemplate(params: CommonParams, ociRef: OCIRef, pathToTgz: string, tags: string[], collectionType: string, annotations: { [key: string]: string } = {}): Promise { const { output } = params; output.write(`-- Starting push of ${collectionType} '${ociRef.id}' to '${ociRef.resource}' with tags '${tags.join(', ')}'`); @@ -25,7 +25,7 @@ export async function pushOCIFeatureOrTemplate(params: CommonParams, ociRef: OCI const dataBytes = fs.readFileSync(pathToTgz); // Generate Manifest for given feature/template artifact. - const manifest = await generateCompleteManifestForIndividualFeatureOrTemplate(output, dataBytes, pathToTgz, ociRef, collectionType, featureAnnotations); + const manifest = await generateCompleteManifestForIndividualFeatureOrTemplate(output, dataBytes, pathToTgz, ociRef, collectionType, annotations); if (!manifest) { output.write(`Failed to generate manifest for ${ociRef.id}`, LogLevel.Error); return; @@ -268,14 +268,13 @@ async function putBlob(params: CommonParams, blobPutLocationUriPath: string, oci // Generate a layer that follows the `application/vnd.devcontainers.layer.v1+tar` mediaType as defined in // Devcontainer Spec (features) : https://containers.dev/implementors/features-distribution/#oci-registry // Devcontainer Spec (templates): https://github.com/devcontainers/spec/blob/main/proposals/devcontainer-templates-distribution.md#oci-registry -async function generateCompleteManifestForIndividualFeatureOrTemplate(output: Log, dataBytes: Buffer, pathToTgz: string, ociRef: OCIRef, collectionType: string, featureAnnotations = {}): Promise { +async function generateCompleteManifestForIndividualFeatureOrTemplate(output: Log, dataBytes: Buffer, pathToTgz: string, ociRef: OCIRef, collectionType: string, annotations: { [key: string]: string } = {}): Promise { const tgzLayer = await calculateDataLayer(output, dataBytes, path.basename(pathToTgz), DEVCONTAINER_TAR_LAYER_MEDIATYPE); if (!tgzLayer) { output.write(`Failed to calculate tgz layer.`, LogLevel.Error); return undefined; } - let annotations: { [key: string]: string } = featureAnnotations; // Specific registries look for certain optional metadata // in the manifest, in this case for UI presentation. if (ociRef.registry === 'ghcr.io') { diff --git a/src/spec-node/collectionCommonUtils/publishCommandImpl.ts b/src/spec-node/collectionCommonUtils/publishCommandImpl.ts index c7321fb93..ebdb433e8 100644 --- a/src/spec-node/collectionCommonUtils/publishCommandImpl.ts +++ b/src/spec-node/collectionCommonUtils/publishCommandImpl.ts @@ -39,7 +39,7 @@ export function getSemanticTags(version: string, tags: string[], output: Log) { return semanticVersions; } -export async function doPublishCommand(params: CommonParams, version: string, ociRef: OCIRef, outputDir: string, collectionType: string, archiveName: string, featureAnnotations = {}) { +export async function doPublishCommand(params: CommonParams, version: string, ociRef: OCIRef, outputDir: string, collectionType: string, archiveName: string, annotations: { [key: string]: string } = {}) { const { output } = params; output.write(`Fetching published versions...`, LogLevel.Info); @@ -54,7 +54,7 @@ export async function doPublishCommand(params: CommonParams, version: string, oc if (!!semanticTags) { output.write(`Publishing tags: ${semanticTags.toString()}...`, LogLevel.Info); const pathToTgz = path.join(outputDir, archiveName); - const digest = await pushOCIFeatureOrTemplate(params, ociRef, pathToTgz, semanticTags, collectionType, featureAnnotations); + const digest = await pushOCIFeatureOrTemplate(params, ociRef, pathToTgz, semanticTags, collectionType, annotations); if (!digest) { output.write(`(!) ERR: Failed to publish ${collectionType}: '${ociRef.resource}'`, LogLevel.Error); return; diff --git a/src/spec-node/templatesCLI/publish.ts b/src/spec-node/templatesCLI/publish.ts index 858eebe4b..33b4c206a 100644 --- a/src/spec-node/templatesCLI/publish.ts +++ b/src/spec-node/templatesCLI/publish.ts @@ -88,7 +88,14 @@ async function templatesPublish({ } const archiveName = getArchiveName(t.id, collectionType); - const publishResult = await doPublishCommand(params, t.version, templateRef, outputDir, collectionType, archiveName); + + // Properties here are available on the manifest without needing to download the full Template archive. + const templateAnnotations = { + 'dev.containers.metadata': JSON.stringify(t), + }; + output.write(`Template Annotations: ${JSON.stringify(templateAnnotations)}`, LogLevel.Debug); + + const publishResult = await doPublishCommand(params, t.version, templateRef, outputDir, collectionType, archiveName, templateAnnotations); if (!publishResult) { output.write(`(!) ERR: Failed to publish '${resource}'`, LogLevel.Error); process.exit(1);