test: migrate e2e js_image_oci to pure bzlmod (#2836)

### Changes are visible to end-users: no

### Test plan

- Covered by existing test cases
- New test cases added
diff --git a/.aspect/workflows/config.yaml b/.aspect/workflows/config.yaml
index ffb5c70..a42faee 100644
--- a/.aspect/workflows/config.yaml
+++ b/.aspect/workflows/config.yaml
@@ -30,11 +30,8 @@
                   without: true
             - buildifier:
                   without: true
-            # Broken on bazel8?
-            - bazel-8:
-                  without: true
-            # Still depends on some WORKSPACE setup
-            - bazel-9:
+            # The `llvm` module's hermetic toolchain requires Bazel 8+.
+            - bazel-7:
                   without: true
     e2e/nextjs:
         icon: nextjs
diff --git a/e2e/js_image_oci/.bazeliskrc b/e2e/js_image_oci/.bazeliskrc
new file mode 100644
index 0000000..dbe2e50
--- /dev/null
+++ b/e2e/js_image_oci/.bazeliskrc
@@ -0,0 +1 @@
+USE_BAZEL_VERSION=8.x
diff --git a/e2e/js_image_oci/.bazelrc b/e2e/js_image_oci/.bazelrc
index 63de545..60d7d89 100644
--- a/e2e/js_image_oci/.bazelrc
+++ b/e2e/js_image_oci/.bazelrc
@@ -3,16 +3,6 @@
 
 ### YOUR PROJECT SPECIFIC OPTIONS GO HERE ###
 
-# TODO: fully migrate to bzlmod
-common --enable_workspace
-
-# There are some empty globs in the LLVM toolchain
-common --noincompatible_disallow_empty_glob
-
-# Don't try and auto detect the cc toolchain, as we use our llvm.
-build --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
-build --incompatible_enable_cc_toolchain_resolution
-
 # Load any settings & overrides specific to the current user from `.aspect/bazelrc/user.bazelrc`.
 # This file should appear in `.gitignore` so that settings are not shared with team members. This
 # should be last statement in this config so the user configuration is able to overwrite flags from
diff --git a/e2e/js_image_oci/MODULE.bazel b/e2e/js_image_oci/MODULE.bazel
index 83a8672..5b9cbdb 100644
--- a/e2e/js_image_oci/MODULE.bazel
+++ b/e2e/js_image_oci/MODULE.bazel
@@ -12,9 +12,18 @@
 
 bazel_dep(name = "bazel_lib", version = "3.0.0", dev_dependency = True)
 bazel_dep(name = "container_structure_test", version = "1.21.1", dev_dependency = True)
+bazel_dep(
+    name = "googleapis",
+    version = "0.0.0-20260422-20ac242a",
+    dev_dependency = True,
+    repo_name = "com_google_googleapis",
+)
+bazel_dep(name = "llvm", version = "0.8.0", dev_dependency = True)
 bazel_dep(name = "platforms", version = "0.0.10", dev_dependency = True)
 bazel_dep(name = "rules_nodejs", version = "6.7.3", dev_dependency = True)
-bazel_dep(name = "rules_oci", version = "2.0.0-alpha2", dev_dependency = True)
+bazel_dep(name = "rules_oci", version = "2.3.0", dev_dependency = True)
+
+register_toolchains("@llvm//toolchain:all")
 
 npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm", dev_dependency = True)
 npm.npm_translate_lock(
@@ -41,7 +50,18 @@
         "linux/s390x",
     ],
 )
-use_repo(oci, "debian")
+use_repo(
+    oci,
+    "debian",
+    "debian_linux_386",
+    "debian_linux_amd64",
+    "debian_linux_arm64_v8",
+    "debian_linux_arm_v5",
+    "debian_linux_arm_v7",
+    "debian_linux_mips64le",
+    "debian_linux_ppc64le",
+    "debian_linux_s390x",
+)
 
 node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node", dev_dependency = True)
 use_repo(node, "nodejs_toolchains")
diff --git a/e2e/js_image_oci/WORKSPACE.bzlmod b/e2e/js_image_oci/WORKSPACE.bzlmod
deleted file mode 100644
index 86fc14c..0000000
--- a/e2e/js_image_oci/WORKSPACE.bzlmod
+++ /dev/null
@@ -1,114 +0,0 @@
-# Subset of WORKSPACE that isn't covered by MODULE.bazel
-
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
-
-# GRPC
-http_archive(
-    name = "rules_proto",
-    sha256 = "dc3fb206a2cb3441b485eb1e423165b231235a1ea9b031b4433cf7bc1fa460dd",
-    strip_prefix = "rules_proto-5.3.0-21.7",
-    urls = [
-        "https://github.com/bazelbuild/rules_proto/archive/refs/tags/5.3.0-21.7.tar.gz",
-    ],
-)
-
-load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
-
-rules_proto_dependencies()
-
-rules_proto_toolchains()
-
-http_archive(
-    name = "com_google_googleapis",
-    sha256 = "65d053dbcde1b8722be785e639d69d66f245337a1bb6da716681bce2ae8d2a45",
-    strip_prefix = "googleapis-6adf7b1787284ca71b90439d886a88a239d115c4",
-    urls = ["https://github.com/googleapis/googleapis/archive/6adf7b1787284ca71b90439d886a88a239d115c4.tar.gz"],
-)
-
-load("@com_google_googleapis//:repository_rules.bzl", "switched_rules_by_language")
-
-switched_rules_by_language(name = "com_google_googleapis_imports")
-
-# LLVM
-http_archive(
-    name = "com_grail_bazel_toolchain",
-    sha256 = "a9fc7cf01d0ea0a935bd9e3674dd3103766db77dfc6aafcb447a7ddd6ca24a78",
-    strip_prefix = "toolchains_llvm-c65ef7a45907016a754e5bf5bfabac76eb702fd3",
-    urls = ["https://github.com/bazel-contrib/toolchains_llvm/archive/c65ef7a45907016a754e5bf5bfabac76eb702fd3.tar.gz"],
-)
-
-_SYSROOT_LINUX_BUILD_FILE = """
-filegroup(
-    name = "sysroot",
-    srcs = glob(["*/**"]),
-    visibility = ["//visibility:public"],
-)
-"""
-
-_SYSROOT_DARWIN_BUILD_FILE = """
-filegroup(
-    name = "sysroot",
-    srcs = glob(
-        include = ["**"],
-        exclude = ["**/*:*"],
-    ),
-    visibility = ["//visibility:public"],
-)
-"""
-
-http_archive(
-    name = "org_chromium_sysroot_linux_arm64",
-    build_file_content = _SYSROOT_LINUX_BUILD_FILE,
-    sha256 = "cf2fefded0449f06d3cf634bfa94ffed60dbe47f2a14d2900b00eb9bcfb104b8",
-    urls = ["https://commondatastorage.googleapis.com/chrome-linux-sysroot/toolchain/80fc74e431f37f590d0c85f16a9d8709088929e8/debian_bullseye_arm64_sysroot.tar.xz"],
-)
-
-http_archive(
-    name = "org_chromium_sysroot_linux_x86_64",
-    build_file_content = _SYSROOT_LINUX_BUILD_FILE,
-    sha256 = "04b94ba1098b71f8543cb0ba6c36a6ea2890d4d417b04a08b907d96b38a48574",
-    urls = ["https://commondatastorage.googleapis.com/chrome-linux-sysroot/toolchain/f5f68713249b52b35db9e08f67184cac392369ab/debian_bullseye_amd64_sysroot.tar.xz"],
-)
-
-http_archive(
-    name = "sysroot_darwin_universal",
-    build_file_content = _SYSROOT_DARWIN_BUILD_FILE,
-    # The ruby header has an infinite symlink that we need to remove.
-    patch_cmds = ["rm System/Library/Frameworks/Ruby.framework/Versions/Current/Headers/ruby/ruby"],
-    sha256 = "71ae00a90be7a8c382179014969cec30d50e6e627570af283fbe52132958daaf",
-    strip_prefix = "MacOSX11.3.sdk",
-    urls = ["https://s3.us-east-2.amazonaws.com/static.aspect.build/sysroots/MacOSX11.3.sdk.tar.xz"],
-)
-
-load("@com_grail_bazel_toolchain//toolchain:deps.bzl", "bazel_toolchain_dependencies")
-
-bazel_toolchain_dependencies()
-
-load("@com_grail_bazel_toolchain//toolchain:rules.bzl", "llvm_toolchain")
-
-llvm_toolchain(
-    name = "llvm_toolchain",
-    llvm_version = "14.0.0",
-    sha256 = {
-        "darwin-aarch64": "1b8975db6b638b308c1ee437291f44cf8f67a2fb926eb2e6464efd180e843368",
-        "linux-x86_64": "564fcbd79c991e93fdf75f262fa7ac6553ec1dd04622f5d7db2a764c5dc7fac6",
-    },
-    strip_prefix = {
-        "darwin-aarch64": "clang+llvm-14.0.0-arm64-apple-darwin",
-        "linux-x86_64": "clang+llvm-14.0.0-x86_64-linux-gnu",
-    },
-    sysroot = {
-        "linux-aarch64": "@org_chromium_sysroot_linux_arm64//:sysroot",
-        "linux-x86_64": "@org_chromium_sysroot_linux_x86_64//:sysroot",
-        "darwin-aarch64": "@sysroot_darwin_universal//:sysroot",
-        "darwin-x86_64": "@sysroot_darwin_universal//:sysroot",
-    },
-    urls = {
-        "darwin-aarch64": ["https://github.com/aspect-forks/llvm-project/releases/download/aspect-release-14.0.0/clang+llvm-14.0.0-arm64-apple-darwin.tar.xz"],
-        "linux-x86_64": ["https://github.com/aspect-forks/llvm-project/releases/download/aspect-release-14.0.0/clang+llvm-14.0.0-x86_64-linux-gnu.tar.xz"],
-    },
-)
-
-load("@llvm_toolchain//:toolchains.bzl", "llvm_register_toolchains")
-
-llvm_register_toolchains()
diff --git a/e2e/js_image_oci/repo/.bazelversion b/e2e/js_image_oci/repo/.bazelversion
index 2006aa2..b332604 120000
--- a/e2e/js_image_oci/repo/.bazelversion
+++ b/e2e/js_image_oci/repo/.bazelversion
@@ -1 +1 @@
-../../../.bazelversion
\ No newline at end of file
+../.bazelversion
\ No newline at end of file
diff --git a/e2e/js_image_oci/src/BUILD.bazel b/e2e/js_image_oci/src/BUILD.bazel
index e605079..4f44ed8 100644
--- a/e2e/js_image_oci/src/BUILD.bazel
+++ b/e2e/js_image_oci/src/BUILD.bazel
@@ -1,6 +1,6 @@
 load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_image_layer")
 load("@container_structure_test//:defs.bzl", "container_structure_test")
-load("@rules_oci//oci:defs.bzl", "oci_image", "oci_tarball")
+load("@rules_oci//oci:defs.bzl", "oci_image", "oci_load")
 
 platform(
     name = "linux_amd64",
@@ -86,10 +86,10 @@
 # FIXME: due to bzlmod Node.js toolchain issuehttps://github.com/aspect-build/rules_js/issues/1530,
 # on MacOS, this target must be built with `--extra_toolchains` to select right the linux Node.js
 # toolchain for the image layer:
-# bazel run //src:image_tarball --extra_toolchains=@nodejs_toolchains//:linux_arm64_toolchain_target
+# bazel run //src:image_load --extra_toolchains=@nodejs_toolchains//:linux_arm64_toolchain_target
 # docker run -t e2e_js_image_oci:latest
-oci_tarball(
-    name = "image_tarball",
+oci_load(
+    name = "image_load",
     image = ":image",
     repo_tags = ["e2e_js_image_oci:latest"],
 )