From 7331d1aa2b616cc1d9536c6a8dc6a01d5df91e42 Mon Sep 17 00:00:00 2001 From: Jeffrey Wall <265409180+jeffwall-curlewlabs@users.noreply.github.com> Date: Mon, 12 Oct 2026 19:22:15 -0700 Subject: [PATCH 1/2] Watch host_key.py so a module upgrade reaches existing output bases The rule runs host_key.py through rctx.path, which Bazel does not record. An upgrade that changed only the script, with defs.bzl and the rule's attributes unchanged, left an existing output base on the old script's key and watches until some other watched path changed. 0.0.3 was such an upgrade: its watches through links reached a fresh output base but not one that had run 0.0.2. The real-Bazel check now runs a scratch copy of the module, replaces its host_key.py in a running server, and expects the action to run again; without the watch that step reuses the old result. --- README.md | 13 +++++++------ defs.bzl | 8 +++++++- tests/check_platform.py | 25 +++++++++++++++++++++++-- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 02e550f..6aaf816 100644 --- a/README.md +++ b/README.md @@ -80,12 +80,13 @@ The selected directory remains in the key because host tools can record its path ## When the key changes -The rule asks Bazel to watch everything it read, except search path entries -and listings on the macOS system volume: those change only with the OS build, -which Bazel watches instead. When any of it changes, Bazel recomputes the key at -its next command, including in a server that is already running, and every -action whose key changed runs again or comes from a cache entry made under the -same key. +The rule asks Bazel to watch everything it read, except search path entries and +listings on the macOS system volume: those change only with the OS build, which +Bazel watches instead. It also watches its own `host_key.py`, so upgrading this +module brings an existing output base onto the new release's key and watches. +When any of it changes, Bazel recomputes the key at its next command, including +in a server that is already running, and every action whose key changed runs +again or comes from a cache entry made under the same key. Bazel reads each watched file in full when a new server first uses the repository. Leaving the system volume out spares that check a read of every diff --git a/defs.bzl b/defs.bzl index 572a626..5c24f15 100644 --- a/defs.bzl +++ b/defs.bzl @@ -18,7 +18,13 @@ def _host_cache_key_impl(rctx): interpreter = rctx.path(python) if python.startswith("/") else rctx.which(python) if interpreter == None or not interpreter.exists: fail("host_cache_key: no Python interpreter at %r; set the `python` attribute" % python) - command = [interpreter, rctx.path(Label("//:host_key.py"))] + script = Label("//:host_key.py") + + # Bazel records nothing for a file reached through rctx.path. Unwatched, an + # upgrade that changes only host_key.py would leave an existing output base + # on the old script's key and watches until some other watched path changed. + rctx.watch(script) + command = [interpreter, rctx.path(script)] for directory in rctx.attr.search_path: command.extend(["--search-path", directory]) for prefix in rctx.attr.homebrew_prefixes: diff --git a/tests/check_platform.py b/tests/check_platform.py index 7edcd1d..a22cc96 100755 --- a/tests/check_platform.py +++ b/tests/check_platform.py @@ -33,6 +33,14 @@ ) """ +# Stands in for a release that changed only host_key.py: a report under the +# rule's interface naming another key. +UPGRADED_SCRIPT = """\ +import json + +print(json.dumps({"key": "upgraded", "fields": {}, "watch_files": [], "watch_directories": []})) +""" + def consumer_module( module_root: Path, tools: Path, prefix: Path | None, developer_dir: str = "" @@ -154,6 +162,12 @@ def expect(step: str, stderr: str, wanted: str) -> None: def check_watched_changes(bazel: str, module_root: Path, scratch: Path) -> None: + # A copy, so a step can upgrade the module's own script. Convenience links + # into an output base would be followed and copied whole. + module = scratch / "module" + shutil.copytree( + module_root, module, ignore=shutil.ignore_patterns(".git", "bazel-*") + ) # The search path names the tools through a link to their directory. tools = scratch / "tools" (scratch / "tools-one").mkdir() @@ -166,9 +180,9 @@ def check_watched_changes(bazel: str, module_root: Path, scratch: Path) -> None: write_sbom(keg, BOTTLE) workspace = scratch / "consumer" workspace.mkdir() - shutil.copy(module_root / ".bazelversion", workspace / ".bazelversion") + shutil.copy(module / ".bazelversion", workspace / ".bazelversion") (workspace / "BUILD.bazel").write_text(CONSUMER_BUILD) - (workspace / "MODULE.bazel").write_text(consumer_module(module_root, tools, prefix)) + (workspace / "MODULE.bazel").write_text(consumer_module(module, tools, prefix)) consumer = Consumer(bazel, workspace, scratch / "disk-cache") first, second = scratch / "first-output-base", scratch / "second-output-base" @@ -209,6 +223,13 @@ def check_watched_changes(bazel: str, module_root: Path, scratch: Path) -> None: consumer.build(second), "executed", ) + + # Upgrading this module can change host_key.py and nothing else; the + # key follows only because the rule watches its own script. + (module / "host_key.py").write_text(UPGRADED_SCRIPT) + expect( + "host_key.py upgraded, running server", consumer.build(second), "executed" + ) finally: consumer.shutdown() From 33c1956699738c07c1bf68439780b1526213c1be Mon Sep 17 00:00:00 2001 From: Jeffrey Wall <265409180+jeffwall-curlewlabs@users.noreply.github.com> Date: Mon, 12 Oct 2026 19:22:15 -0700 Subject: [PATCH 2/2] Release 0.0.4 Carries the rule's watch on its own host_key.py, so upgrading to this release re-runs the rule in every output base, and a later upgrade that changes only the script reaches existing output bases too. --- MODULE.bazel | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 066fa82..a5a68ea 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,6 @@ module( name = "rules_host_cache_key", - version = "0.0.3", + version = "0.0.4", ) bazel_dep(name = "platforms", version = "1.1.0") diff --git a/README.md b/README.md index 6aaf816..c2f573f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ key, so: Add to `MODULE.bazel`: ```starlark -bazel_dep(name = "rules_host_cache_key", version = "0.0.3") +bazel_dep(name = "rules_host_cache_key", version = "0.0.4") host_cache_key = use_repo_rule("@rules_host_cache_key//:defs.bzl", "host_cache_key") host_cache_key(name = "host_cache_key")