Skip to content

feat: support env from a bzl file in bundle_fetch (env_bzl) - #432

Closed
acecilia wants to merge 1 commit into
bazel-contrib:mainfrom
acecilia:feat/bundle-fetch-env-bzl
Closed

acecilia wants to merge 1 commit into
bazel-contrib:mainfrom
acecilia:feat/bundle-fetch-env-bzl

Conversation

@acecilia

@acecilia acecilia commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Problem

rb_bundle_install compiles native gem extensions (json, prism, ffi, ...) with toolchains resolved from the environment (e.g. on macOS mkmf resolves clang and the SDK through xcrun). Consumers that need to control that selection face two dead ends:

  • MODULE.bazel values must be static strings, and load() is not allowed in MODULE.bazel, so machine-resolved env (e.g. DEVELOPER_DIR pointing at the Xcode a project pins, or values exposed by a generated repository) cannot reach the install action.
  • Delegating resolution to the host's ambient environment is non-hermetic and invisible to the action cache: a toolchain change does not invalidate cached compiled extensions.

Solution

Optional env_bzl label attr on the bundle_fetch tag. The generated BUILD of the bundle repository loads it as a dict named bundle_env and merges it with the static env:

# MODULE.bazel
ruby.bundle_fetch(
    name = "ruby_bundle",
    env_bzl = "//Bazel/dependencies:ruby_bundle_env.bzl",
    ...
)

Real-world example of the referenced //Bazel/dependencies:ruby_bundle_env.bzl, from a large consumer repo. The @developer_dir repository is generated by a repository rule that resolves the Xcode pinned by the project (same mechanism the repo uses to select the Xcode for all its Apple builds), so the bundle install compiles native extensions with the project's Xcode:

load("@developer_dir//:developer_dir.bzl", "DEVELOPER_DIR")

# Environment for the @ruby_bundle install action, injected via the rules_ruby
# `env_bzl` extension. Native extensions (json, prism, etc.) compile with the
# host clang, which resolves its toolchain through DEVELOPER_DIR. Resolving it
# from the project's pinned Xcode makes the compilation use the project's
# Xcode, and a change of Xcode invalidates the bundle install action cache.
bundle_env = {"DEVELOPER_DIR": DEVELOPER_DIR}

Because the load happens at BUILD-evaluation time:

  • values can come from generated repositories (any repo, including ones resolved per machine),
  • the resolved values participate in the action key of the generated rb_bundle_install action, so a change of e.g. toolchain selection re-runs the install (and per-machine values keep remote caches from sharing entries across differently-provisioned hosts).

This intentionally does not couple rules_ruby to any platform toolchain (e.g. apple_support): each consumer injects its own policy.

Testing

Validated end-to-end in the consumer repo above: DEVELOPER_DIR (resolved from a repository that picks the pinned Xcode) is present in the generated bundle_install_*.sh (verified via bazel aquery), and changing the value invalidates and re-runs the bundle install action.

The `env` tag attribute only accepts static strings, and `load()` is not
allowed in MODULE.bazel, so machine-resolved values (e.g. a toolchain
selected via a generated repository) cannot reach the generated
rb_bundle_install action. Add an optional `env_bzl` label whose exported
`bundle_env` dict is loaded into the generated BUILD and merged with
`env`, resolved at BUILD-evaluation time so values participate in the
action cache key.

Upstream: bazel-contrib#432
@acecilia
acecilia force-pushed the feat/bundle-fetch-env-bzl branch from 6def310 to 3f9e477 Compare October 27, 2026 20:30
@acecilia
acecilia marked this pull request as draft October 27, 2026 20:43
@acecilia acecilia closed this Oct 27, 2026
@acecilia
acecilia deleted the feat/bundle-fetch-env-bzl branch October 27, 2026 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant