feat(ci): send builds to Aspect Cloud - #1541
joshcbarnes wants to merge 1 commit into
Conversation
CI runs on stock GitHub Actions runners, not Aspect Workflows runners. The Aspect CLI wires BES from the environment (ASPECT_WORKFLOWS_BES_BACKEND and friends, set by a Workflows runner); none of those variables exist here and nothing passed --remote, so the CLI tasks were uploading nowhere. .aspect/bazelrc follows the shape `aspect setup bazelrc` generates (aspect-build/aspect-cli#1442): each flag sits in the --config group named for the endpoint it presumes, so a deployment with a cache but no BES gets the cache tuning and none of the BES tuning. --config=aspect-cloud chains aspect-common, aspect-cache and aspect-bes and adds the endpoints plus the `aspect` credential helper, scoped per host. Nothing is enabled, so a fresh clone builds exactly as before. .bazelrc gains the try-import at the top, so the repo's own lines come after and win. aspect-exec-log is deliberately not chained from aspect-cloud. Its path is %workspace%-relative and Bazel will not create the parent directory, so naming it from the e2e/* and examples/* workspaces — which have no .aspect/generated — would fail the build. Only the root workspace names it, via config.axl. One mechanism, every job. This repo has ~20 sibling workspaces under e2e/ and examples/, and the e2e test.sh scripts run vanilla `bazel`. Neither .aspect/config.axl nor the root .bazelrc reaches them: both are per-workspace. The user bazelrc is indifferent to that axis — every `bazel` process on the runner reads it regardless of directory, and the Aspect CLI re-injects it into the Bazel it spawns. So a single composite action writes it and every job family uses it. On a pull request it resolves build metadata against github.event.pull_request.head.sha, since actions/checkout leaves HEAD on the synthetic merge commit and every build would otherwise be titled "Merge <sha> into <sha>". The token moves to secrets.ASPECT_OSS_API_TOKEN, matching aspect-build/rules_lint#1002 and aspect-build/aspect-cli#1461: the org-level ASPECT_API_TOKEN authenticates as a tenant the OSS team cannot see, so uploads succeed while the UI stays empty. No fallback to the old secret — it would silently re-route builds back to the invisible tenant. ASPECT_HAS_API_TOKEN carries whether the secret exists, never its value: a fork PR gets no secrets, and the credential helper exits non-zero for a host it cannot authenticate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
py_binary startup benchmark
sys.path quality
Bazel analysis benchmark
py_image_layer benchmark
|
Connects this repo's builds to Aspect Cloud's remote cache and BES, so invocations appear in
app.aspect.build.The gap this closes
CI runs on stock GitHub Actions runners, not Aspect Workflows runners. That matters more than it looks: the Aspect CLI wires BES from the environment (
ASPECT_WORKFLOWS_BES_BACKENDand friends, set by a Workflows runner). None of those variables exist here, and nothing passed--remoteor--bes_backend, so the CLI tasks were uploading nowhere.Easy to miss, because the Aspect GitHub App's check runs travel a different path — the API, not BES. Status checks looked healthy on every PR while zero invocations reached the UI.
.aspect/bazelrc(new)Follows the shape
aspect setup bazelrcgenerates (aspect-build/aspect-cli#1442). Flags are grouped by the endpoint that justifies them, so a deployment with a cache but no BES gets the cache tuning and none of the BES tuning:aspect-common--heap_dump_on_oom,--experimental_repository_cache_hardlinksaspect-cache--remote_upload_local_results,--remote_accept_cached,--remote_timeout,--remote_retries,--grpc_keepalive_timeoutaspect-bes--legacy_important_outputs,--remote_build_event_upload=allaspect-exec-log.aspect/generated--execution_log_compact_file--config=aspect-cloudchains the first three and adds the endpoints plus theaspectcredential helper, scoped per host. Every line iscommon:<config>-scoped, so the file is inert until named — a fresh clone builds exactly as before.aspect-exec-logis deliberately not inaspect-cloud. Its path is%workspace%-relative and Bazel will not create the parent directory, so folding it back in breaks the ~20 sibling workspaces undere2e/andexamples/. Only the root workspace names it, via.aspect/config.axl..bazelrcgainstry-import %workspace%/.aspect/bazelrcat the top, so the repo's own lines come after and win.One mechanism, every job
.aspect/config.axlcan only ever cover the root workspace: it is read by the Aspect CLI alone and is per-workspace, so it reaches neither thebazel run @uvsteps in other trees nor theaspect testruns thatcdintoe2e/*andexamples/*. The repo's.bazelrchas the same workspace limit, and thee2etest.shscripts run vanillabazelbesides. The one location indifferent to both axes is the user bazelrc,$HOME/.bazelrc, which everybazelprocess on the runner reads regardless of directory — and which the Aspect CLI also re-injects into the Bazel it spawns.So a single composite action,
.github/actions/aspect-cloud/, writes that file and every job uses it.config.axlkeeps only--config=aspect-exec-log, which is genuinely workspace-bound.The action emits the
--build_metadata=keysget_build_metadata_flagsderives, plusBUILD_USER,BUILD_HOSTandCOMMIT_TAG, and runssetup-aspectsoaspectis onPATHfor the credential helper.On a pull request it resolves metadata against
github.event.pull_request.head.sharather thanHEAD.actions/checkoutleaves HEAD on GitHub's synthetic merge commit, so without this every PR build in the UI is titledMerge <sha> into <sha>and attributed to the merge's author. The head commit is not in the shallow clone, so it is fetched at depth 1 when absent, falling back toHEADif that fails.The token secret moves
secrets.ASPECT_API_TOKEN→secrets.ASPECT_OSS_API_TOKEN, matching aspect-build/rules_lint#1002 and aspect-build/aspect-cli#1461. The org-levelASPECT_API_TOKENauthenticates as a tenant the OSS team cannot see, so uploads succeed while the UI stays empty. No fallback to the old secret — a fallback would silently re-route builds back to the invisible tenant whenever the new secret is missing.ASPECT_HAS_API_TOKENcarries whether the secret exists, never its value. Three things it guards, each a real failure:CIconfig.axlsetup-aspectkeeps it out ofGITHUB_ENVso only the short-lived JWT is visible downstream, and anASPECT_API_TOKENin the environment outranks that stored credential on every invocationBefore merging
ASPECT_OSS_API_TOKENhas to exist under that name in repository or organization settings. If it does not,secrets.ASPECT_OSS_API_TOKENresolves to empty,ASPECT_HAS_API_TOKENbecomes"false", and every authenticated job quietly takes the fork-PR path instead of failing loudly.Test plan
Verified locally:
bash -nbazel info --config=aspect-cloud --announce_rcexpands throughaspect-common→aspect-cache→aspect-beswith every flag acceptedbazel build --config=aspect-exec-logwrites.aspect/generated/exec.log.zstd, andgit check-ignoreconfirms the committed.gitignorecovers itaspect buildifier --helpstill loads the editedconfig.axl;buildifier --mode=checkreports no formatting changeThe rest is observable only on CI: a run where invocations land in
app.aspect.buildunder the OSS tenant, with the PR's real head SHA rather than the merge commit, is the confirmation.🤖 Generated with Claude Code