From e248c4fad3cc9491b57e0f72e62904867840f399 Mon Sep 17 00:00:00 2001 From: Scott Dixon Date: Sat, 1 Aug 2026 08:30:34 -0700 Subject: [PATCH 1/2] Adding Bazelisk/Bazel to the toolshed --- README.md | 3 +- toolshed/CHANGELOG.md | 52 ++++++++++++++ toolshed/Dockerfile | 78 ++++++++++++++++++++- toolshed/bazelisk-1.29.0-linux-amd64.sha256 | 1 + toolshed/bazelisk-1.29.0-linux-arm64.sha256 | 1 + 5 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 toolshed/bazelisk-1.29.0-linux-amd64.sha256 create mode 100644 toolshed/bazelisk-1.29.0-linux-arm64.sha256 diff --git a/README.md b/README.md index d5d7ade..eadf4a1 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ See [toolshed/README.md](toolshed/README.md) for build instructions and | tag | Python | GCC (native) | GCC (arm-none-eabi) | Clang (native) | Cmake | Host Platforms | Other Utilities | |----------|--------|--------------|---------------------|----------------|-------|----------------|-----------------| +| [ts26.4.3](https://github.com/OpenCyphal/docker_toolchains/pkgs/container/toolshed) | 3.14 | 15.2.0 | 14.2.rel1 | 22.1.2 | 4.2.3 | | | | [ts26.4.2](https://github.com/OpenCyphal/docker_toolchains/pkgs/container/toolshed) | 3.14 | 15.2.0 | 14.2.rel1 | 22.1.2 | 4.2.3 | | | | [ts26.4.1](https://github.com/OpenCyphal/docker_toolchains/pkgs/container/toolshed) | 3.14 | 15.2.0 | 14.2.rel1 | 22.1.2 | 4.2.3 | | | | [ts24.4.3](https://github.com/OpenCyphal/docker_toolchains/pkgs/container/toolshed) | | | 13.3.1 | | 3.31.5 | | | @@ -48,7 +49,7 @@ You can use this in your workflow yaml like this: jobs: my-job: runs-on: ubuntu-latest - container: ghcr.io/opencyphal/toolshed:ts26.4.2 + container: ghcr.io/opencyphal/toolshed:ts26.4.3 ``` diff --git a/toolshed/CHANGELOG.md b/toolshed/CHANGELOG.md index 43af7ba..0bf8be5 100644 --- a/toolshed/CHANGELOG.md +++ b/toolshed/CHANGELOG.md @@ -3,6 +3,58 @@ Per-release notes for the `ghcr.io/opencyphal/toolshed` image. The summary table of every published tag lives in the [top-level README](../README.md#opencyphaltoolshedts). +## ts26.4.3 + +Adds: + +- Bazel, via bazelisk 1.29.0, on both `linux/amd64` and `linux/arm64` +- Bazel 9.2.0, unpacked into the image rather than fetched on first use + +`bazel` on `PATH` is bazelisk, not a Bazel release binary. Bazel's own apt +repository publishes `amd64` only while this image ships both architectures — the +same constraint that decided how Dafny is installed in ts26.4.2 — and bazelisk +publishes both. Installing the launcher rather than a fixed binary is also what +lets a project pin its own Bazel through `.bazelversion`. + +`BAZELISK_HOME` is `/opt/bazelisk`, world-readable, rather than the default under +`$HOME`. The default is invisible to any other user the container runs as, which +CI routinely does; this is the trap `dotnet tool install --tool-path` avoids for +Dafny. + +**`USE_BAZEL_VERSION` is deliberately not set.** It takes precedence over a +project's `.bazelversion` — measured, not assumed — so setting it here would +silently build every project with 9.2.0 whatever that project pinned. A project +that wants the pre-warmed toolchain writes `9.2.0` into its own `.bazelversion`, +which is where that decision belongs; a project pinning anything else downloads it +on first use. + +### Bazel builds in this image are not offline + +Worth stating plainly, because it is the first thing anyone reaching for Bazel +will assume otherwise. + +Bazel's hermeticity guarantees cover action *execution* — sandboxed actions, +declared inputs, no leakage from the host. They have never covered the fetch +phase. Under bzlmod that phase is unavoidable: a module declaring **no** +dependencies at all still resolves `bazel_tools`' transitive deps +(`apple_support`, `protobuf`, `rules_java`, `rules_python`, `platforms`, +`rules_cc`, `bazel_skylib`, and more) from `bcr.bazel.build` before analysis +begins. `--noenable_bzlmod` does not escape it on Bazel 9. + +None of the obvious mitigations move this, all measured against a cold cache: +a warmed `--repository_cache` holds archives rather than registry metadata; a +committed `MODULE.bazel.lock` records the resolution but still fetches the module +files; and `bazel vendor --vendor_dir` does not cover registry access either. What +does work is a warm per-user `$HOME/.cache/bazel`, which is no help to CI running +as an arbitrary uid. + +A project that needs offline or air-gapped builds points Bazel at its own registry +mirror (`--registry=file:///...`) or vendors into its own workspace. Those are +per-project decisions — which modules you need is a function of your dependency +graph — so this image deliberately does not pin `--registry`, ship a partial +mirror that would go stale at every Bazel bump, or otherwise get in the way. It +supplies the toolchain; the dependency policy stays with the project. + ## ts26.4.2 Adds: diff --git a/toolshed/Dockerfile b/toolshed/Dockerfile index 7119925..6685883 100644 --- a/toolshed/Dockerfile +++ b/toolshed/Dockerfile @@ -271,6 +271,82 @@ RUN printf 'method Smoke(x: int) requires x > 0 ensures x + 1 > 1 { }\n' > /tmp/ && dafny verify /tmp/smoke.dfy \ && rm /tmp/smoke.dfy -# +---------------------------------------------------------------------------+# LEAVE THE WORKDIR AS /repo +# +---------------------------------------------------------------------------+ +# | INSTALL BAZEL +# +---------------------------------------------------------------------------+ +# bazelisk rather than a Bazel release binary, and from GitHub rather than from +# apt. Bazel's own apt repository publishes amd64 only, and this image ships both +# architectures -- the same constraint that decided Dafny above. bazelisk +# publishes linux/amd64 and linux/arm64, so it is the one route that serves both. +# +# Installing bazelisk as `bazel` is also what lets a project pin its own Bazel: +# bazelisk reads .bazelversion from the workspace root and fetches that version. +FROM dafny AS bazel +WORKDIR /tmp +COPY cached-download.sh . + +ARG BAZELISK_VERSION=1.29.0 +# The version pre-warmed into the image below. A project pinning this one gets it +# without a download; any other version is fetched on first use. +ARG BAZEL_VERSION=9.2.0 + +# BAZELISK_HOME defaults to $HOME/.cache/bazelisk, which is invisible to any other +# user the container runs as -- and CI routinely runs as one. A shared path, made +# world-readable below, is what lets all of them use the pre-warmed Bazel. This is +# the same trap `dotnet tool install --tool-path` avoids for Dafny. +ENV BAZELISK_HOME=/opt/bazelisk + +ARG BAZELISK_CHECKSUM_FILE=bazelisk-${BAZELISK_VERSION}-linux-${TARGETARCH}.sha256 +COPY ${BAZELISK_CHECKSUM_FILE} . +RUN --mount=type=cache,target=/bazelisk-download \ + ./cached-download.sh \ + --cache-target "/bazelisk-download" \ + --download-url "https://github.com/bazelbuild/bazelisk/releases/download/v${BAZELISK_VERSION}/bazelisk-linux-${TARGETARCH}" \ + --download-to-file "/bazelisk-download/bazelisk-linux-${TARGETARCH}" \ + --checksums "/tmp/${BAZELISK_CHECKSUM_FILE}" \ + --algorithm 256 \ + && install -m 0755 "/bazelisk-download/bazelisk-linux-${TARGETARCH}" /usr/local/bin/bazel + +# Bazel itself is unpacked into BAZELISK_HOME here rather than on first use. The +# throwaway workspace is what makes that happen: bazelisk honours .bazelversion +# only at a workspace root, and with no workspace it resolves "latest" over the +# network instead. +# +# Deliberately NOT setting USE_BAZEL_VERSION. That variable takes precedence over +# a project's .bazelversion -- measured, not assumed -- so an image setting it +# would silently build every project with this version whatever the project +# pinned. A project that wants the pre-warmed Bazel commits `9.2.0` to its own +# .bazelversion, which is where that decision belongs. +RUN mkdir -p /tmp/bazel-warm \ + && cd /tmp/bazel-warm \ + && printf 'module(name = "warm", version = "0.0.1")\n' > MODULE.bazel \ + && touch BUILD.bazel \ + && echo "${BAZEL_VERSION}" > .bazelversion \ + && bazel --version \ + && bazel shutdown \ + && cd /tmp \ + && rm -rf /tmp/bazel-warm \ + && chmod -R a+rX "${BAZELISK_HOME}" + +# Build something rather than just running --version. Launching proves bazelisk +# resolved a binary; building additionally proves that binary can execute an +# action and write an output, which is the part that silently goes missing. +# +# This step needs network, and so will any real Bazel build: Bazel resolves even +# its built-in module dependencies through the Bazel Central Registry, exactly as +# npm, cargo, and pip in this image fetch theirs. The image supplies the +# toolchain, not a hermetic dependency cache. +RUN mkdir -p /tmp/bazel-smoke \ + && cd /tmp/bazel-smoke \ + && printf 'module(name = "smoke", version = "0.0.1")\n' > MODULE.bazel \ + && printf 'genrule(name = "g", outs = ["o.txt"], cmd = "echo built > $@")\n' > BUILD.bazel \ + && echo "${BAZEL_VERSION}" > .bazelversion \ + && bazel build //:g \ + && grep -q built bazel-bin/o.txt \ + && bazel shutdown \ + && cd /tmp \ + && rm -rf /tmp/bazel-smoke + +# +---------------------------------------------------------------------------+ # LEAVE THE WORKDIR AS /repo WORKDIR /repo diff --git a/toolshed/bazelisk-1.29.0-linux-amd64.sha256 b/toolshed/bazelisk-1.29.0-linux-amd64.sha256 new file mode 100644 index 0000000..60a7b98 --- /dev/null +++ b/toolshed/bazelisk-1.29.0-linux-amd64.sha256 @@ -0,0 +1 @@ +5a408715e932c0250d28bd84555f12edbf70117de42f9181691c736eacc4a992 bazelisk-linux-amd64 diff --git a/toolshed/bazelisk-1.29.0-linux-arm64.sha256 b/toolshed/bazelisk-1.29.0-linux-arm64.sha256 new file mode 100644 index 0000000..791be5a --- /dev/null +++ b/toolshed/bazelisk-1.29.0-linux-arm64.sha256 @@ -0,0 +1 @@ +e20e8b0f4f240091b7a55bf17b9398bd4f40ee70ae0208dff95dd4c445fb4010 bazelisk-linux-arm64 From 6cf860add138d9a984e92787bca7c737451fa20a Mon Sep 17 00:00:00 2001 From: Scott Dixon Date: Sat, 1 Aug 2026 08:37:57 -0700 Subject: [PATCH 2/2] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- toolshed/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/toolshed/Dockerfile b/toolshed/Dockerfile index 6685883..83d2272 100644 --- a/toolshed/Dockerfile +++ b/toolshed/Dockerfile @@ -326,7 +326,8 @@ RUN mkdir -p /tmp/bazel-warm \ && bazel shutdown \ && cd /tmp \ && rm -rf /tmp/bazel-warm \ - && chmod -R a+rX "${BAZELISK_HOME}" + && chmod -R a+rwX "${BAZELISK_HOME}" \ + && chmod 1777 "${BAZELISK_HOME}" # Build something rather than just running --version. Launching proves bazelisk # resolved a binary; building additionally proves that binary can execute an