From a999d467efd8745daf817982bde8970657ed3217 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Mon, 15 Jun 2026 12:40:20 +0000 Subject: [PATCH 1/5] Dev container images info issue fix regarding history documentation and cgmanifest --- build/config.json | 2 +- build/src/utils/config.js | 19 +++++++++++++++++-- build/src/utils/image-content-extractor.js | 21 +++++++++++++++------ src/dotnet/manifest.json | 4 ++-- src/java-8/manifest.json | 4 ++-- src/universal/manifest.json | 5 ++--- 6 files changed, 39 insertions(+), 16 deletions(-) diff --git a/build/config.json b/build/config.json index 20f24d5455..19912f2266 100644 --- a/build/config.json +++ b/build/config.json @@ -183,7 +183,7 @@ "downloadUrl": "https://github.com/cli/cli" }, "yarn": { - "versionCommand": "yarn --version", + "versionCommand": "yarn --version 2>/dev/null | grep -oE '[0-9]+\\.[0-9]+\\.[0-9]+'", "downloadUrl": "https://yarnpkg.com/" }, "Maven": { diff --git a/build/src/utils/config.js b/build/src/utils/config.js index 3bea536bc1..81e8d31237 100644 --- a/build/src/utils/config.js +++ b/build/src/utils/config.js @@ -214,6 +214,7 @@ function getTagsForVersion(definitionId, version, registry, registryPath, varian } } + const seen = new Set(); return tags.reduce((list, tag) => { // One of the tags that needs to be supported is one where there is no version, but there // are other attributes. For example, python:3 in addition to python:0.35.0-3. So, a version @@ -224,7 +225,11 @@ function getTagsForVersion(definitionId, version, registry, registryPath, varian .replace(/\$\{?VARIANT\}?/, variant || 'NOVARIANT') .replace('-NOVARIANT', ''); if (baseTag.charAt(baseTag.length - 1) !== ':') { - list.push(`${registry}/${registryPath}/${baseTag}`); + const fullTag = `${registry}/${registryPath}/${baseTag}`; + if (!seen.has(fullTag)) { + seen.add(fullTag); + list.push(fullTag); + } } return list; }, []); @@ -305,13 +310,23 @@ function getTagList(definitionId, release, versionPartHandling, registry, regist // If this variant should also be used for the the latest tag, add it. The "latest" value could be // true, false, or a specific variant. "true" assumes the first variant is the latest. const definitionLatestProperty = config.definitionBuildSettings[definitionId].latest; - return tagList.concat((updateLatest + const allTags = tagList.concat((updateLatest && definitionLatestProperty && (!allVariants || variant === definitionLatestProperty || (definitionLatestProperty === true && variant === firstVariant))) ? getLatestTag(definitionId, registry, registryPath) : []); + + // Deduplicate tags while preserving order + const seen = new Set(); + return allTags.filter((tag) => { + if (seen.has(tag)) { + return false; + } + seen.add(tag); + return true; + }); } const getDefinitionObject = (id, variant) => { diff --git a/build/src/utils/image-content-extractor.js b/build/src/utils/image-content-extractor.js index 237d871e12..ee1759aaae 100644 --- a/build/src/utils/image-content-extractor.js +++ b/build/src/utils/image-content-extractor.js @@ -271,12 +271,21 @@ async function getPipPackageInfo(imageTagOrContainerName, packageList, usePipx, console.log('(*) Gathering information about pip packages...'); const versionLookup = usePipx ? await getPipxVersionLookup(imageTagOrContainerName) : await getPipVersionLookup(imageTagOrContainerName, imageId); - return packageList.map((package) => { - return { - name: package, - version: versionLookup[package] - }; - }); + return packageList.reduce((list, package) => { + const version = versionLookup[package]; + // Skip packages that aren't actually installed in the inspected environment. + // Emitting an entry without a version produces blank markdown rows and + // conflicting/duplicate cgmanifest.json (SBOM) registrations. + if (version) { + list.push({ + name: package, + version: version + }); + } else { + console.log(`(!) Warning: Could not determine version for pip package "${package}" - skipping.`); + } + return list; + }, []); } function getUserName(imageId) { diff --git a/src/dotnet/manifest.json b/src/dotnet/manifest.json index 2a6c118b34..398095e7e6 100644 --- a/src/dotnet/manifest.json +++ b/src/dotnet/manifest.json @@ -1,5 +1,5 @@ { - "version": "2.1.1", + "version": "2.1.2", "variants": [ "11.0-preview-resolute", "10.0-noble", @@ -80,7 +80,7 @@ "languages": { ".NET": { "cgIgnore": true, - "versionCommand": "dotnet --version | grep -oE '[0-9]+\\.[0-9]+\\.[0-9]+' | tr -d '\\n' && echo \\\" (\\$(dotnet --info | grep -ozP 'Host.*:\\s*Version:\\s*\\K[0-9]\\.[0-9]\\.[0-9]' | tr '\\0' '\\n'))\\\"", + "versionCommand": "dotnet --version | grep -oE '[0-9]+\\.[0-9]+\\.[0-9]+' | tr -d '\\n' && echo \\\" (\\$(dotnet --info | grep -ozP 'Host.*:\\s*Version:\\s*\\K[0-9]+\\.[0-9]+\\.[0-9]+' | tr '\\0' '\\n'))\\\"", "path": "/usr", "downloadUrl": "https://dotnet.microsoft.com/" } diff --git a/src/java-8/manifest.json b/src/java-8/manifest.json index 061ed0470d..fa090a3177 100644 --- a/src/java-8/manifest.json +++ b/src/java-8/manifest.json @@ -1,5 +1,5 @@ { - "version": "3.0.9", + "version": "3.1.0", "variants": [ "trixie", "bookworm" @@ -56,7 +56,7 @@ "languages": { "Java": { "cgIgnore": true, - "versionCommand": "/usr/local/sdkman/candidates/java/current/bin/java -version 2>&1 | grep -ozP '^openjdk\\sversion\\s\\\"\\K[^\\\"]+' | tr '\\0' '\\n' && /usr/local/openjdk-*/bin/java --version | grep -ozP 'openjdk\\s+\\K[0-9]+\\.[0-9]+\\.[0-9]+' | tr '\\0' '\\n'", + "versionCommand": "/usr/local/sdkman/candidates/java/current/bin/java -version 2>&1 | grep -ozP '^openjdk\\sversion\\s\\\"\\K[^\\\"]+' | tr '\\0' '\\n'", "path": "/usr/local/sdkman/candidates/java/current
/usr/local" } } diff --git a/src/universal/manifest.json b/src/universal/manifest.json index 70ac1eca1c..1d674eebe5 100644 --- a/src/universal/manifest.json +++ b/src/universal/manifest.json @@ -1,5 +1,5 @@ { - "version": "6.0.4", + "version": "6.0.5", "build": { "latest": true, "rootDistro": "debian", @@ -119,8 +119,7 @@ "plotly", "jupyterlab_git", "certifi", - "setuptools", - "wheel" + "setuptools" ], "other": { "git": {}, From c755049702ae4b966642ab3b2474f01ff25a3786 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Mon, 15 Jun 2026 17:14:05 +0000 Subject: [PATCH 2/5] Updating review comment. --- build/src/utils/image-content-extractor.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/build/src/utils/image-content-extractor.js b/build/src/utils/image-content-extractor.js index ee1759aaae..4f47e2fef0 100644 --- a/build/src/utils/image-content-extractor.js +++ b/build/src/utils/image-content-extractor.js @@ -271,8 +271,15 @@ async function getPipPackageInfo(imageTagOrContainerName, packageList, usePipx, console.log('(*) Gathering information about pip packages...'); const versionLookup = usePipx ? await getPipxVersionLookup(imageTagOrContainerName) : await getPipVersionLookup(imageTagOrContainerName, imageId); + // Build a fallback lookup keyed by PEP 503 normalized names so packages that + // differ only by casing/normalization (e.g. "pyOpenssl" vs "pyOpenSSL") still resolve. + const normalizedVersionLookup = Object.keys(versionLookup).reduce((prev, name) => { + prev[normalizePipPackageName(name)] = versionLookup[name]; + return prev; + }, {}); + return packageList.reduce((list, package) => { - const version = versionLookup[package]; + const version = versionLookup[package] || normalizedVersionLookup[normalizePipPackageName(package)]; // Skip packages that aren't actually installed in the inspected environment. // Emitting an entry without a version produces blank markdown rows and // conflicting/duplicate cgmanifest.json (SBOM) registrations. @@ -288,6 +295,12 @@ async function getPipPackageInfo(imageTagOrContainerName, packageList, usePipx, }, []); } +// Normalize a Python package name per PEP 503 (lowercase, runs of "-_." collapsed to "-") +// so lookups are tolerant of casing/separator differences between manifests and pip output. +function normalizePipPackageName(name) { + return name.replace(/[-_.]+/g, '-').toLowerCase(); +} + function getUserName(imageId) { return imageId === "universal" ? "codespace" : undefined; } From 4a523e45b8482e18fa5f75d98afdca97f93d1126 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Tue, 16 Jun 2026 08:49:49 +0000 Subject: [PATCH 3/5] Updating manifest versions --- src/dotnet/manifest.json | 2 +- src/universal/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotnet/manifest.json b/src/dotnet/manifest.json index 398095e7e6..de6bf64c15 100644 --- a/src/dotnet/manifest.json +++ b/src/dotnet/manifest.json @@ -1,5 +1,5 @@ { - "version": "2.1.2", + "version": "2.1.3", "variants": [ "11.0-preview-resolute", "10.0-noble", diff --git a/src/universal/manifest.json b/src/universal/manifest.json index ff42cfe270..3cf7ec4b41 100644 --- a/src/universal/manifest.json +++ b/src/universal/manifest.json @@ -1,5 +1,5 @@ { - "version": "6.0.6", + "version": "6.0.7", "build": { "latest": true, "rootDistro": "debian", From ae2495e003057779b9023a1bc8e9426684f80de6 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Mon, 22 Jun 2026 13:09:07 +0000 Subject: [PATCH 4/5] Implementing review comment. --- build/src/utils/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/src/utils/config.js b/build/src/utils/config.js index 81e8d31237..cda02c6657 100644 --- a/build/src/utils/config.js +++ b/build/src/utils/config.js @@ -320,7 +320,7 @@ function getTagList(definitionId, release, versionPartHandling, registry, regist // Deduplicate tags while preserving order const seen = new Set(); - return allTags.filter((tag) => { + return allTags.filter((tag) => { if (seen.has(tag)) { return false; } From 58bb425691efe810a30c36b11d4f2d024fd74612 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Tue, 23 Jun 2026 13:52:18 +0000 Subject: [PATCH 5/5] Correct indentation. --- build/src/utils/config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/src/utils/config.js b/build/src/utils/config.js index cda02c6657..96b5b2b8ac 100644 --- a/build/src/utils/config.js +++ b/build/src/utils/config.js @@ -310,14 +310,14 @@ function getTagList(definitionId, release, versionPartHandling, registry, regist // If this variant should also be used for the the latest tag, add it. The "latest" value could be // true, false, or a specific variant. "true" assumes the first variant is the latest. const definitionLatestProperty = config.definitionBuildSettings[definitionId].latest; - const allTags = tagList.concat((updateLatest + const allTags = tagList.concat((updateLatest && definitionLatestProperty && (!allVariants - || variant === definitionLatestProperty + || variant === definitionLatestProperty || (definitionLatestProperty === true && variant === firstVariant))) ? getLatestTag(definitionId, registry, registryPath) : []); - + // Deduplicate tags while preserving order const seen = new Set(); return allTags.filter((tag) => {