diff --git a/src/spec-node/collectionCommonUtils/packageCommandImpl.ts b/src/spec-node/collectionCommonUtils/packageCommandImpl.ts index 2be6ace02..5d14238b8 100644 --- a/src/spec-node/collectionCommonUtils/packageCommandImpl.ts +++ b/src/spec-node/collectionCommonUtils/packageCommandImpl.ts @@ -183,17 +183,27 @@ export async function packageCollection(args: PackageCommandInput, collectionTyp // Validate minimal folder structure const devcontainerJsonName = `devcontainer-${collectionType}.json`; + // Be tolerant of other folders that may not contain Feature source code if (!(await isLocalFile(path.join(folder, devcontainerJsonName)))) { - output.write(`(!) WARNING: ${collectionType} '${c}' is missing a ${devcontainerJsonName}. Skipping... `, LogLevel.Warning); + output.write(`(!) WARNING: ${collectionType} '${c}' is missing a ${devcontainerJsonName}. Skipping...`, LogLevel.Warning); continue; } const tmpSrcDir = path.join(os.tmpdir(), `/templates-src-output-${Date.now()}`); await cpDirectoryLocal(folder, tmpSrcDir); + const jsonPath = path.join(tmpSrcDir, devcontainerJsonName); + const metadata = jsonc.parse(await readLocalFile(jsonPath, 'utf-8')); - const archiveName = getArchiveName(c, collectionType); + if (!metadata.id || !metadata.version || !metadata.name) { + output.write(`ERROR: ${collectionType} '${c}' is missing one of the following required properties in its ${devcontainerJsonName}: 'id', 'version', 'name'.`, LogLevel.Error); + return; + } - const jsonPath = path.join(tmpSrcDir, devcontainerJsonName); + // Validate that the 'id' field in the metadata property matches the folder name + if (c !== metadata.id) { + output.write(`ERROR: ${collectionType} id '${metadata.id}' does not match its containing folder name '${c}'.`, LogLevel.Error); + return; + } if (collectionType === 'feature') { const installShPath = path.join(tmpSrcDir, 'install.sh'); @@ -201,7 +211,6 @@ export async function packageCollection(args: PackageCommandInput, collectionTyp output.write(`Feature '${c}' is missing an install.sh`, LogLevel.Error); return; } - await addsAdditionalFeatureProps(jsonPath, output); } else if (collectionType === 'template') { if (!(await addsAdditionalTemplateProps(tmpSrcDir, jsonPath, output))) { @@ -209,13 +218,9 @@ export async function packageCollection(args: PackageCommandInput, collectionTyp } } + const archiveName = getArchiveName(c, collectionType); await tarDirectory(tmpSrcDir, archiveName, outputDir); - const metadata = jsonc.parse(await readLocalFile(jsonPath, 'utf-8')); - if (!metadata.id || !metadata.version || !metadata.name) { - output.write(`${collectionType} '${c}' is missing one of the following required properties in its ${devcontainerJsonName}: 'id', 'version', 'name'.`, LogLevel.Error); - return; - } metadatas.push(metadata); await rmLocal(tmpSrcDir, { recursive: true, force: true }); } diff --git a/src/spec-node/featuresCLI/packageCommandImpl.ts b/src/spec-node/featuresCLI/packageCommandImpl.ts index 4f9abdc6a..605bcf1ae 100644 --- a/src/spec-node/featuresCLI/packageCommandImpl.ts +++ b/src/spec-node/featuresCLI/packageCommandImpl.ts @@ -23,15 +23,15 @@ export async function doFeaturesPackageCommand(args: PackageCommandInput): Promi let metadataOutput: Feature[] | undefined = []; if (isSingleFeature) { // Package individual features - output.write('Packaging single feature...', LogLevel.Info); + output.write('Packaging single Feature...', LogLevel.Info); metadataOutput = await packageSingleFeature(args); } else { - output.write('Packaging feature collection...', LogLevel.Info); + output.write('Packaging Feature collection...', LogLevel.Info); metadataOutput = await packageFeatureCollection(args); } if (!metadataOutput) { - output.write('Failed to package features', LogLevel.Error); + output.write('Failed to package Features', LogLevel.Error); return undefined; }