feat(cloud-tasks): add multi-arch OCI image target#431
Conversation
cloud-tasks had no container image target under Bazel: nvct-service declared only the Spring Boot jar, so it could not be released by any mechanism. Add the missing piece. - rules/oci: new java_oci_image macro, mirroring go_oci_image. Packages a runnable jar and emits the same target set, including the _index multi-arch label that release tooling pushes. - MODULE.bazel: pull nvcr.io/nvidia/distroless/java (25-jdk-v4.0.8) pinned by digest, matching the distroless_go / distroless_cc convention and the base the pre-Bazel nvct-service Dockerfile used. - nvct-service: java_oci_image target producing nvct-service-oss. Two base-image details the macro encodes so callers cannot get them wrong: the distroless entrypoint is the shelless_ulimit shim and oci_image REPLACES rather than appends the base entrypoint, so the default entrypoint keeps the shim (dropping it silently leaves the container on a 1024 fd soft limit, the same issue grpc-proxy hit); and java is invoked via /usr/bin/java rather than JAVA_HOME, which the base sets per-architecture and would break arm64. Verified: bazel query resolves //src/control-plane-services/cloud-tasks/nvct-service:nvct-service-oss-image_index Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
📝 WalkthroughWalkthroughAdds reusable Java OCI image support with a pinned multi-architecture Distroless Java base, default Java entrypoint construction, environment and working-directory forwarding, an ChangesJava OCI image support
Estimated code review effort: 3 (Moderate) | ~25 minutes Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant nvct_service
participant java_oci_image
participant pkg_tar
participant create_oci_image
participant distroless_java
participant image_contract_test
nvct_service->>java_oci_image: Provide executable JAR and runtime settings
java_oci_image->>pkg_tar: Package JAR at /usr/share/app.jar
java_oci_image->>create_oci_image: Provide layer, entrypoint, env, and workdir
distroless_java->>create_oci_image: Provide multi-arch base image
image_contract_test->>create_oci_image: Inspect generated image tar
create_oci_image-->>image_contract_test: Return layers and runtime metadata
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@rules/oci/private/java.bzl`:
- Around line 95-99: Align the custom base-image contract with the emitted
entrypoint: update the documentation for the “base” attribute to require Java at
/usr/bin/java, or change the generated command to use a configurable Java path.
Keep the contract and the actual startup command consistent.
In `@src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel`:
- Around line 54-58: Add a repository-native test target alongside the
nvct-service-oss-image definition to verify the generated multi-architecture
image index, including expected platform manifests and the configured
entrypoint. Use existing image-index test utilities or conventions, and ensure
the target is included in the standard test runner; otherwise document in the PR
why automated coverage is not applicable.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: f71fa8c6-dfbd-493c-bf7f-00a3667feecb
📒 Files selected for processing (4)
MODULE.bazelrules/oci/defs.bzlrules/oci/private/java.bzlsrc/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel
Generated by bazel; additive only (the distroless_java oci_pull entries plus the usagesDigest). No existing pin changes. Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
sanjay-saxena
left a comment
There was a problem hiding this comment.
@balajinvda -- I asked Codex to review the PR and here are its findings:
Findings
Blocking: Docker runtime parity is incomplete.
-
java.bzl preserves the shelless_ulimit executable but not ULIMIT_FLAG=1. Without that variable, the shim does not raise the file-descriptor limit. It also drops Cloud Tasks’ JDK_JAVA_OPTIONS and /home/app working directory from the existing Dockerfile.
-
The shared macro should support env and workdir; Apps should supply:
- ULIMIT_FLAG=1
- Existing JDK_JAVA_OPTIONS
- workdir = "/home/app"
Important: jar_path does not control where the JAR is installed.
pkg_tar preserves the source JAR basename, while jar_path only changes the entrypoint. A future caller using a JAR not named app.jar, or setting /usr/share/app.jar, could produce an image whose entrypoint references a nonexistent file. The packaging rule should explicitly rename/place the JAR at jar_path, or remove that configurable attribute and enforce /app.jar.
Important: CI is not testing the image.
The image targets inherit the manual tag, so the green Bazel lanes do not prove the image can be built or started. The PR only records a local build. I agree with CodeRabbit that there should be a repository-native test or explicit CI step validating:
- Both architectures exist.
- Entrypoint, environment and working directory are correct.
- /app.jar exists.
- Ideally, the host-architecture image starts successfully.
Scope clarification: this PR does not publish images yet.
The PR description acknowledges this. It creates the _index artifact needed by release tooling, but:
- Cloud Tasks is not selectable in image-push-manual.yml.
- No public release descriptor/version source is added.
- The internal release configuration is still separate.
I would describe this PR as “Java OCI build support,” with publishing completed and proven by its follow-up.
Minor: custom base contract is misleading.
The base documentation says it needs a JRE on PATH, but the generated entrypoint specifically requires /usr/bin/java and /usr/bin/shelless_ulimit. Either document that exact contract or make those paths configurable.
Recommendation
I would approve the overall design after findings 1–3 are addressed. The root-level distroless Java pin is appropriate shared infrastructure, not a Cloud Tasks dependency leak. The reusable rule is also the right abstraction; it just needs to preserve the real Docker runtime contract and be covered by CI before other Java services copy it.
Addresses both review comments. Contract test: the image's runtime contract was only verified by hand, and every part of it fails at container startup rather than at build time. The test asserts the jar is at /app.jar (the path the entrypoint names), the base image's shelless_ulimit shim survives (oci_image replaces rather than appends the base entrypoint), and java is invoked via the arch-stable /usr/bin/java rather than a per-arch JAVA_HOME path. Unlike the Go services' image_entrypoint_mode_test it does not assert an exec bit: a jar is read by the JVM, never executed, so mode 0644 is correct. sh_test is loaded explicitly from @rules_shell. The Go subtrees rely on it being a built-in global, which works only because they are still on Bazel 8; cloud-tasks builds in the root module on Bazel 9, where that global is gone. java_bin: the base attribute documented "a JRE on PATH" while the generated entrypoint hardcoded /usr/bin/java, so a compliant custom base with Java elsewhere would have failed at startup. The path is now a configurable attribute defaulting to /usr/bin/java, and the base contract states the requirement precisely. Verified: test passes against the real image, and fails when given a wrong expected entrypoint (negative control), so it is not vacuous. Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh`:
- Line 34: Update the tar-listing checks around the candidate and extracted
image validations in image_contract_test.sh to avoid grep’s quiet early exit
under pipefail. Replace the grep -q-style matching with a full-stream-consuming
approach while preserving the existing regular expression and valid-image
acceptance behavior.
- Around line 30-44: The image contract test must validate the final merged
filesystem rather than merely finding app.jar in any individual layer. Replace
the per-layer jar_found check with ordered layer application into an assembled
root filesystem, honoring whiteout/deletion semantics, then assert that the
resulting root contains /app.jar.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: c68995a3-b7c3-4b44-8ce7-f4a6f08c11c5
⛔ Files ignored due to path filters (1)
MODULE.bazel.lockis excluded by!**/*.lock,!**/MODULE.bazel.lock
📒 Files selected for processing (3)
rules/oci/private/java.bzlsrc/control-plane-services/cloud-tasks/nvct-service/BUILD.bazelsrc/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh
🚧 Files skipped from review as they are similar to previous changes (2)
- rules/oci/private/java.bzl
- src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel
Addresses the blocking and important findings from review. The image did not reproduce the runtime the pre-Bazel nvct-service/Dockerfile establishes. Runtime parity. The Dockerfile sets ULIMIT_FLAG=1, JDK_JAVA_OPTIONS, WORKDIR /home/app, and installs the jar at /usr/share/app.jar. None of that was carried over. ULIMIT_FLAG is load-bearing: the base's shelless_ulimit shim reads it, so preserving the shim in the entrypoint while dropping the variable meant the shim ran and raised nothing. JDK_JAVA_OPTIONS carries the container heap sizing (MaxRAMPercentage=40.0), so losing it silently changes JVM memory behavior in production. create_oci_image and java_oci_image now take env and workdir, both optional and defaulting to leaving the base untouched, so go_oci_image callers are unaffected. jar_path now controls where the jar is installed. pkg_tar preserves the source basename, so jar_path only renamed the entrypoint: a jar not already called app.jar, or a jar_path pointing elsewhere, produced an image whose entrypoint referenced a nonexistent file. pkg_files renames and relocates it, so the layer and the entrypoint are derived from one value and cannot disagree. This is what made /usr/share/app.jar expressible at all. Test coverage extended to the full contract: jar location, entrypoint, ULIMIT_FLAG, MaxRAMPercentage, and WorkingDir. Verified on the built index, both amd64 and arm64: Entrypoint /usr/bin/shelless_ulimit /usr/bin/java -jar /usr/share/app.jar WorkingDir /home/app ULIMIT_FLAG=1 and JDK_JAVA_OPTIONS present Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
|
@sanjay-saxena thanks, and thanks to Codex. Findings 1-3 were all real; 1 and 2 were genuine bugs, not just doc gaps. Fixed in 343d5d5. 1. Docker runtime parity (blocking). You were right, and it was worse than the finding states: the Dockerfile also installs the jar at
2. jar_path did not control installation (important). Correct, and this is what made 3. CI is not testing the image (important). Partly addressed, and I want to be precise about what is and is not covered. Not covered, and I did not claim otherwise: multi-arch manifest assertions and actually starting the container. The test inspects the host-arch tarball. I verified both architectures by hand on the built index: Automating the arch and start-up checks needs a Docker-capable lane, which cloud-tasks already has ( 4. Scope. Agreed, and I will retitle to "Java OCI build support". It creates the 5. Custom base contract (minor). Fixed earlier in a0aab8e: The test also has a negative control: it fails when given a wrong expected entrypoint, so it is not vacuous. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel`:
- Around line 63-72: Restore the `nvct_library_test(name = "tests")` target in
the BUILD file alongside the existing `sh_test`; retain the image-contract test
and ensure the standard test invocation runs the service’s Java test suite as
well.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: b4303be7-46ba-4ccf-beb0-f1eaf2dfb468
📒 Files selected for processing (4)
rules/oci/private/common.bzlrules/oci/private/java.bzlsrc/control-plane-services/cloud-tasks/nvct-service/BUILD.bazelsrc/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh
🚧 Files skipped from review as they are similar to previous changes (1)
- rules/oci/private/java.bzl
Three review findings. Restore nvct_library_test(name = "tests"). An earlier edit to this BUILD file replaced a span of text rather than inserting, and silently removed the Java test suite along with it. The image contract test does not substitute for it; both targets belong here. Re-added the image targets additively so this cannot recur. Avoid grep -q inside a pipeline. Under pipefail, grep -q closes the pipe on the first match, the producer takes SIGPIPE, and the pipeline reports failure, so a valid image could be rejected. The pipeline cases now consume the full stream. The remaining grep -q calls read a file directly rather than a pipe, so they carry no SIGPIPE risk and are unchanged. Check whiteouts. Finding the jar in some layer is necessary but not sufficient: a later layer can delete it with an OCI whiteout marker, leaving the merged filesystem without it. The test now also asserts no layer whites out the jar, its parent directories, or the tree via an opaque marker. Verified: contract test passes, and //...:tests resolves again. Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh (1)
86-90: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winAssert exact environment entries and the complete JVM option value.
Unanchored matching allows unrelated fields such as
NOT_ULIMIT_FLAG=1to satisfy the check. It also validates only theMaxRAMPercentagesubstring, so the other requiredJDK_JAVA_OPTIONSvalues configured insrc/control-plane-services/cloud-tasks/nvct-service/BUILD.bazelcould be dropped without failing this test. Compare exactEnventries after selecting the config blob.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh` around lines 86 - 90, Update the metadata validation loop in image_contract_test.sh to select the config blob, extract its Env entries, and compare complete entries exactly rather than using unanchored substring matching. Validate the full JDK_JAVA_OPTIONS value, including all required JVM options from the service configuration, while retaining the existing checks for ULIMIT_FLAG=1 and the WorkingDir field.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh`:
- Around line 53-55: Update the tar-entry regex in the image contract test so
the opaque-whiteout pattern matches only the root, /usr, and /usr/share ancestor
directories of the jar. Preserve the existing matching behavior for the other
whiteout entries and prevent opaque whiteouts in unrelated directories from
failing the test.
- Around line 67-70: Update the image contract test loops around the Entrypoint
and Env assertions to resolve the image manifest or index to its config digest,
then inspect only that OCI config blob. Stop scanning every regular file in the
extracted archive, including layer tars or bundled payloads, while preserving
the existing expected Entrypoint and Env validations against the resolved
config.
---
Outside diff comments:
In `@src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh`:
- Around line 86-90: Update the metadata validation loop in
image_contract_test.sh to select the config blob, extract its Env entries, and
compare complete entries exactly rather than using unanchored substring
matching. Validate the full JDK_JAVA_OPTIONS value, including all required JVM
options from the service configuration, while retaining the existing checks for
ULIMIT_FLAG=1 and the WorkingDir field.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: bb0a5ee2-97e2-4852-a8aa-dbef6900049f
📒 Files selected for processing (2)
src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazelsrc/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh
🚧 Files skipped from review as they are similar to previous changes (1)
- src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel
| if tar -tf "${candidate}" \ | ||
| | grep -E '^(\./)?usr/share/\.wh\.app\.jar$|^(\./)?usr/\.wh\.share$|^(\./)?\.wh\.usr$|\.wh\.\.wh\.opq$' \ | ||
| >/dev/null; then |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
set -euo pipefail
printf '--- file outline ---\n'
ast-grep outline src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh || true
printf '\n--- target excerpt ---\n'
cat -n src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh | sed -n '1,140p'
printf '\n--- search whiteout-related patterns ---\n'
rg -n "\.wh\.\.wh\.\.opq|\.wh\.app\.jar|\.wh\.share|\.wh\.usr|whiteout" src/control-plane-services/cloud-tasks/nvct-service -SRepository: NVIDIA/nvcf
Length of output: 240
🏁 Script executed:
set -euo pipefail
echo '--- file outline ---'
ast-grep outline src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh || true
echo
echo '--- target excerpt ---'
cat -n src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh | sed -n '1,140p'
echo
echo '--- search whiteout-related patterns ---'
rg -n "\.wh\.\.wh\.\.opq|\.wh\.app\.jar|\.wh\.share|\.wh\.usr|whiteout" src/control-plane-services/cloud-tasks/nvct-service -SRepository: NVIDIA/nvcf
Length of output: 5381
Scope the opaque-whiteout match to the jar’s ancestor directories. The final \.wh\.\.wh\.\.opq alternative matches opaque whiteouts anywhere in any layer, so an unrelated directory can fail this contract test. Restrict it to /, /usr, and /usr/share only.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh`
around lines 53 - 55, Update the tar-entry regex in the image contract test so
the opaque-whiteout pattern matches only the root, /usr, and /usr/share ancestor
directories of the jar. Preserve the existing matching behavior for the other
whiteout entries and prevent opaque whiteouts in unrelated directories from
failing the test.
| while IFS= read -r meta; do | ||
| grep -q '"Entrypoint"' "${meta}" 2>/dev/null || continue | ||
| if tr -d ' \n' < "${meta}" \ | ||
| | grep '"Entrypoint":\["/usr/bin/shelless_ulimit","/usr/bin/java","-jar","/usr/share/app.jar"\]' >/dev/null; then |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## repo files"
git ls-files | sed -n '1,200p'
echo
echo "## AGENTS files"
fd -a 'AGENTS.md' .
echo
echo "## target file around relevant lines"
nl -ba src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh | sed -n '1,220p'Repository: NVIDIA/nvcf
Length of output: 13088
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## target file around relevant lines"
nl -ba src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh | sed -n '1,220p'
echo
echo "## search for manifest/config handling in related files"
rg -n "manifest|config digest|Entrypoint|Env|image_contract_test" src/control-plane-services/cloud-tasks/nvct-service -SRepository: NVIDIA/nvcf
Length of output: 226
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## AGENTS files"
fd -a 'AGENTS.md' .
echo
echo "## target file"
nl -ba src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh | sed -n '1,220p'
echo
echo "## related references"
rg -n "manifest|config digest|Entrypoint|Env|image_contract_test" src/control-plane-services/cloud-tasks/nvct-service -SRepository: NVIDIA/nvcf
Length of output: 2126
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file=src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh
echo "## exists?"
test -f "$file" && echo yes || echo no
echo
echo "## relevant excerpt"
sed -n '55,110p' "$file" | nl -ba
echo
echo "## show any manifest/index parsing in this script"
grep -nE 'manifest|index|config|digest|Entrypoint|Env' "$file"Repository: NVIDIA/nvcf
Length of output: 226
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## subtree guidance"
sed -n '1,220p' src/control-plane-services/cloud-tasks/AGENTS.md
echo
echo "## image_contract_test.sh excerpt"
sed -n '1,220p' src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh
echo
echo "## references in the script"
grep -nE 'manifest|index|config|digest|Entrypoint|Env|tar|oci|archive|blob' src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.shRepository: NVIDIA/nvcf
Length of output: 7559
Inspect the OCI config blob directly
These loops read every regular file under the extracted archive, so a layer tar or bundled payload containing the expected Entrypoint/Env text can make the test pass even if the actual image config is wrong. Resolve manifest.json/index to the config digest and assert against that blob only.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh`
around lines 67 - 70, Update the image contract test loops around the Entrypoint
and Env assertions to resolve the image manifest or index to its config digest,
then inspect only that OCI config blob. Stop scanning every regular file in the
extracted archive, including layer tars or bundled payloads, while preserving
the existing expected Entrypoint and Env validations against the resolved
config.
Why
cloud-tasks had no container image target under Bazel.
nvct-service/BUILD.bazeldeclared only the Spring Boot jar (spring_boot_app(name = "app")), with nooci_image/oci_image_indexanywhere in the subtree, so the service could not be released by any mechanism: the release backend'soci_pushhad nothing to point at.This adds the missing piece so cloud-tasks containers can be cut.
What changed
rules/oci: newjava_oci_imagemacro (there was onlygo_oci_image). Packages a runnable jar and emits the same target set as the Go macro, including the_indexmulti-arch label that release tooling pushes.MODULE.bazel: pullnvcr.io/nvidia/distroless/java(25-jdk-v4.0.8) pinned by digest, matching thedistroless_go/distroless_ccconvention and the base the pre-Bazelnvct-service/Dockerfileused.nvct-service/BUILD.bazel:java_oci_imagetarget producingnvct-service-oss.Two base-image details are encoded in the macro so callers cannot get them wrong:
ENTRYPOINT ["/usr/bin/shelless_ulimit"], a shim that raises the soft file-descriptor limit, andoci_imageREPLACES rather than appends the base entrypoint. Omitting the shim silently leaves the container on the default 1024 fd soft limit. This is the same issue grpc-proxy hit for its Go image.javais invoked via/usr/bin/java, not viaJAVA_HOME. The base setsJAVA_HOMEper-architecture (/usr/lib/jvm/temurin-25-jdk-amd64vs-arm64), so aJAVA_HOME-derived path would break the arm64 half of the image index./usr/bin/javais present on both arches (verified in the image layers).Customer Release Notes
Not customer visible yet. This adds the build target; no image is published until the release wiring lands.
Testing
bazel build //src/control-plane-services/cloud-tasks/nvct-service:nvct-service-oss-image_indexsucceeds (1734 actions). Verified on the built artifact:application/vnd.oci.image.index.v1+jsonwith bothlinux/amd64andlinux/arm64/v8.['/usr/bin/shelless_ulimit', '/usr/bin/java', '-jar', '/app.jar'].Notes
This is the build target only. Cutting an actual cloud-tasks release still needs, separately: a version source and a
release:block intools/ci/subproject-validations.yaml, and aconfig/services/cloud-tasks.yamlin nvcf-internal. nvcf-internal MR 96 adds thesource.module_rootsupport that lets a root-module service such as cloud-tasks stage a release without first splitting the Java tree into standalone modules.Push the
_indexlabel, never:nvct-service-oss-image, which is the single host-arch image.References
None
Related Merge Requests/Pull Requests
nvcf-internal MR 96 (root-module support in the Bazel staging build).
Dependencies
None. The distroless java base is an existing NVIDIA public image, pinned by digest.
Summary by CodeRabbit
New Features
Tests