From cc84e73433e4ac4932d88e7ea3439d80e5b81497 Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Tue, 5 May 2026 10:53:43 -0700 Subject: [PATCH 1/3] feat(builtins): add `aspect ci warming` task MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleans `${ASPECT__STORAGE_PATH}/{bazel,output,caches/repository}` — matching rosetta's WarmingTaskRef cleanup — then runs `bazel build --nobuild` against the given targets so the repository, output, and bazel caches under the ephemeral mount are populated and ready for an Aspect Workflows warming archive to capture. Default targets `//...`; positional args override. Honors BazelTrait flags / startup_flags contributions so warming sees the same `--config=...` etc. as real builds. Pairs with #1062 (env-var routing of the launcher / AXL caches into the same ephemeral mount) and a forthcoming silo PR that adds these subtrees to the warming archive. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/builtins/aspect/MODULE.aspect | 1 + .../src/builtins/aspect/warming.axl | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 crates/aspect-cli/src/builtins/aspect/warming.axl diff --git a/crates/aspect-cli/src/builtins/aspect/MODULE.aspect b/crates/aspect-cli/src/builtins/aspect/MODULE.aspect index 753180db6..c57e9d389 100644 --- a/crates/aspect-cli/src/builtins/aspect/MODULE.aspect +++ b/crates/aspect-cli/src/builtins/aspect/MODULE.aspect @@ -9,6 +9,7 @@ use_task("delivery.axl", "delivery") use_task("lint.axl", "lint") use_task("format.axl", "format") use_task("gazelle.axl", "gazelle") +use_task("warming.axl", "warming") use_feature("feature/artifacts.axl", "ArtifactUpload") use_feature("feature/github_lint_comments.axl", "GithubLintComments") diff --git a/crates/aspect-cli/src/builtins/aspect/warming.axl b/crates/aspect-cli/src/builtins/aspect/warming.axl new file mode 100644 index 000000000..ebca7e48f --- /dev/null +++ b/crates/aspect-cli/src/builtins/aspect/warming.axl @@ -0,0 +1,74 @@ +"""A 'warming' task: pre-populate Bazel caches on a CI runner. + +Cleans the prior Bazel state under ${ASPECT__STORAGE_PATH}, then runs +`bazel build --nobuild` against the given targets so the repository, +output, and bazel caches under that mount (and the aspect-launcher / +aspect-cli caches under ${ASPECT__STORAGE_PATH}/caches/{aspect-launcher, +aspect-cli}) are populated and ready for the warming archive. +""" +load("./traits.axl", "BazelTrait") + + +def _impl(ctx: TaskContext) -> int: + mount = ctx.std.env.var("ASPECT__STORAGE_PATH") + if mount: + # Match rosetta's pre-warming cleanup. Best-effort: bazel may have + # left output dirs read-only, so chmod first; spawn errors are + # tolerated since the dirs may not exist yet on a fresh runner. + for sub in ["bazel", "output", "caches/repository"]: + d = mount + "/" + sub + ctx.std.process.command("find").args([d, "-type", "d", "-exec", "chmod", "u+w", "{}", "+"]).spawn().wait() + ctx.std.process.command("rm").args(["-rf", d]).spawn().wait() + + bazel_trait = ctx.traits[BazelTrait] + + flags = ["--nobuild"] + flags.extend(ctx.args.bazel_flags) + flags.extend(bazel_trait.extra_flags) + for hook in bazel_trait.task_flags: + flags.extend(hook(ctx)) + if bazel_trait.flags: + flags = bazel_trait.flags(flags) + + startup_flags = list(ctx.args.bazel_startup_flags) + startup_flags.extend(bazel_trait.extra_startup_flags) + if ctx.args.output_base: + startup_flags.insert(0, "--output_base=" + ctx.args.output_base) + if bazel_trait.startup_flags: + startup_flags = bazel_trait.startup_flags(startup_flags) + ctx.bazel.startup_flags.extend(startup_flags) + + invocation = ctx.bazel.build( + flags = flags, + *ctx.args.targets, + ) + return invocation.wait().code + + +warming = task( + group = ["ci"], + summary = "Pre-populate Bazel caches on a CI runner.", + description = "Cleans prior Bazel state under ${ASPECT__STORAGE_PATH}, then runs `bazel build --nobuild` against the given targets so subsequent jobs on the same runner pool start with hot caches. Intended to run on Aspect Workflows warming runners; the populated caches are captured by the warming archive.", + implementation = _impl, + traits = [BazelTrait], + args = { + "targets": args.positional( + minimum = 1, + maximum = 512, + default = ["..."], + description = "Bazel target patterns to warm. Defaults to '...' which expands to all rule targets in the package at and beneath the current directory.", + ), + "bazel_flags": args.string_list( + long = "bazel-flag", + description = "Additional Bazel flags forwarded to the build (e.g. --bazel-flag=--config=ci). Repeat the flag to pass multiple.", + ), + "bazel_startup_flags": args.string_list( + long = "bazel-startup-flag", + description = "Additional Bazel startup flags. Repeat the flag to pass multiple.", + ), + "output_base": args.string( + default = "", + description = "Bazel output base path. Use to target a specific Bazel server instance.", + ), + }, +) From 32e83f663e837bd9234c378acfc9402768cbc543 Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Tue, 5 May 2026 11:40:09 -0700 Subject: [PATCH 2/3] feat(warming): also invoke warming_archive on Aspect Workflows runners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the loop on the customer-facing warming workflow. After the bazel cache populate step succeeds, when ASPECT_WORKFLOWS_RUNNER is set the task now invokes /etc/aspect/workflows/bin/warming_archive to upload the populated caches to the warming bucket — so the customer's GitHub Actions workflow drops to: steps: - uses: actions/checkout@v4 - name: Warming Task run: aspect ci warming Off a Workflows runner the archive step is skipped with a warning to stderr; the populate step still runs so the task is useful for local testing. --- .../src/builtins/aspect/warming.axl | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/crates/aspect-cli/src/builtins/aspect/warming.axl b/crates/aspect-cli/src/builtins/aspect/warming.axl index ebca7e48f..3b361df2e 100644 --- a/crates/aspect-cli/src/builtins/aspect/warming.axl +++ b/crates/aspect-cli/src/builtins/aspect/warming.axl @@ -4,12 +4,20 @@ Cleans the prior Bazel state under ${ASPECT__STORAGE_PATH}, then runs `bazel build --nobuild` against the given targets so the repository, output, and bazel caches under that mount (and the aspect-launcher / aspect-cli caches under ${ASPECT__STORAGE_PATH}/caches/{aspect-launcher, -aspect-cli}) are populated and ready for the warming archive. +aspect-cli}) are populated and ready for the warming archive. When +running on an Aspect Workflows runner, this task also invokes +/etc/aspect/workflows/bin/warming_archive to upload the populated +caches to the warming bucket — so the customer's CI workflow only +needs to call `aspect ci warming`. """ load("./traits.axl", "BazelTrait") +WARMING_ARCHIVE_BIN = "/etc/aspect/workflows/bin/warming_archive" + + def _impl(ctx: TaskContext) -> int: + on_workflows_runner = bool(ctx.std.env.var("ASPECT_WORKFLOWS_RUNNER")) mount = ctx.std.env.var("ASPECT__STORAGE_PATH") if mount: # Match rosetta's pre-warming cleanup. Best-effort: bazel may have @@ -42,7 +50,24 @@ def _impl(ctx: TaskContext) -> int: flags = flags, *ctx.args.targets, ) - return invocation.wait().code + code = invocation.wait().code + if code != 0: + return code + + if on_workflows_runner: + # Upload the populated caches to the warming bucket. Inherits the + # script's stdout/stderr so the user sees its progress in real time. + archive = ctx.std.process.command(WARMING_ARCHIVE_BIN).spawn().wait() + return archive.code + + ctx.std.io.stderr.write( + "warning: ASPECT_WORKFLOWS_RUNNER is not set; skipping the warming " + + "archive upload step. The bazel cache populate step ran successfully. " + + "On an Aspect Workflows runner this task also invokes " + + WARMING_ARCHIVE_BIN + " to upload the populated caches to the warming " + + "bucket.\n" + ) + return 0 warming = task( From 6c8fdfd424f18f6667de1f4abbc976beb31acb79 Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Tue, 5 May 2026 12:44:27 -0700 Subject: [PATCH 3/3] Output more values --- .buildkite/pipeline.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.buildkite/pipeline.yaml b/.buildkite/pipeline.yaml index 4b57470ab..f79ca0b04 100644 --- a/.buildkite/pipeline.yaml +++ b/.buildkite/pipeline.yaml @@ -47,6 +47,10 @@ steps: ; do val=$$(printenv "$$var") && echo " $$var=$$val" || true done + echo "ASPECT_LAUNCHER_CACHE / ASPECT_CLI_CACHE:" + for var in ASPECT_LAUNCHER_CACHE ASPECT_CLI_CACHE; do + val=$$(printenv "$$var") && echo " $$var=$$val" || true + done - key: pre-build label: ":aspect: Pre-build CLI"