Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .aspect/bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Generated by `aspect setup bazelrc` as a starting point: edit as your repository needs; rerun the command to restore it.
# Configures vanilla `bazel` calls to use Aspect services and recommended optimizations.
# Flags are grouped by what they presume; the groups that apply here are enabled at the
# top, the rest reached by chaining or by name. `bazel build --announce_rc` shows which group set what.


# Tuning that presumes no endpoint.
common:aspect-common --heap_dump_on_oom
common:aspect-common --experimental_repository_cache_hardlinks

# The remote cache (ASPECT_WORKFLOWS_REMOTE_CACHE where set) and the tuning a cache justifies.
common:aspect-cache --remote_upload_local_results
common:aspect-cache --remote_accept_cached
common:aspect-cache --remote_timeout=3600
common:aspect-cache --remote_retries=360
common:aspect-cache --grpc_keepalive_timeout=30s

# The BES backend (ASPECT_WORKFLOWS_BES_BACKEND where set), its enrichment, and the build-diagnostics upload the Aspect backend reads.
common:aspect-bes --grpc_keepalive_timeout=30s
common:aspect-bes --generate_json_trace_profile
common:aspect-bes --noslim_profile
common:aspect-bes --experimental_profile_include_target_label
common:aspect-bes --experimental_profile_include_primary_output
common:aspect-bes --experimental_profile_include_target_configuration
common:aspect-bes --legacy_important_outputs
common:aspect-bes --remote_build_event_upload=all

# The compact execution log, uploaded with the build diagnostics for per-action data in the Web UI. Slow on a thin link: remove this group and the --config lines naming it to skip it.
common:aspect-exec-log --execution_log_compact_file=%workspace%/.aspect/generated/exec.log.zstd

# Aspect Cloud: remote cache + BES; auth via ASPECT_API_TOKEN or `aspect auth login`.
common:aspect-cloud --config=aspect-common
common:aspect-cloud --config=aspect-cache
common:aspect-cloud --config=aspect-bes
common:aspect-cloud --config=aspect-exec-log
common:aspect-cloud --remote_cache=grpcs://cache.aspect.build
common:aspect-cloud --bes_backend=grpcs://bes.aspect.build
common:aspect-cloud --bes_results_url=https://app.aspect.build/i/
common:aspect-cloud --credential_helper=cache.aspect.build=aspect
common:aspect-cloud --credential_helper=bes.aspect.build=aspect
19 changes: 2 additions & 17 deletions .aspect/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,6 @@ BAZEL_REMOTE_FLAGS=""
[ -n "${ASPECT_WORKFLOWS_BES_RESULTS_URL:-}" ] && BAZEL_REMOTE_FLAGS="${BAZEL_REMOTE_FLAGS} --bes_results_url=${ASPECT_WORKFLOWS_BES_RESULTS_URL}"
[ -n "${ASPECT_WORKFLOWS_REMOTE_CACHE:-}" ] && BAZEL_REMOTE_FLAGS="${BAZEL_REMOTE_FLAGS} --remote_cache=${ASPECT_WORKFLOWS_REMOTE_CACHE}"
[ -n "${ASPECT_WORKFLOWS_REMOTE_BYTESTREAM_URI_PREFIX:-}" ] && BAZEL_REMOTE_FLAGS="${BAZEL_REMOTE_FLAGS} --remote_bytestream_uri_prefix=${ASPECT_WORKFLOWS_REMOTE_BYTESTREAM_URI_PREFIX}"
# x-identity authenticates the runner to the backend. Bazel scopes headers per
# channel: --remote_header covers the remote cache and executor gRPC channels
# but NOT the BES channel, which needs its own --bes_header. Set both so the
# cache/executor and the pre-build's BES stream all authenticate. Gate on the
# identity alone (mirrors get_bazelrc_flags in
# crates/aspect-cli/src/builtins/aspect/lib/environment.axl).
if [ -n "${ASPECT_WORKFLOWS_RUNNER_IDENTITY:-}" ]; then
BAZEL_REMOTE_FLAGS="${BAZEL_REMOTE_FLAGS} --remote_header=x-identity=${ASPECT_WORKFLOWS_RUNNER_IDENTITY}"
BAZEL_REMOTE_FLAGS="${BAZEL_REMOTE_FLAGS} --bes_header=x-identity=${ASPECT_WORKFLOWS_RUNNER_IDENTITY}"
fi

# --build_metadata flags for the pre-build invocation. Only set when we're
# forwarding events to a BES backend (the Aspect Web UI or similar) —
# otherwise the metadata has nowhere to surface.
Expand Down Expand Up @@ -275,7 +264,7 @@ if [ -n "${ASPECT_WORKFLOWS_RUNNER:-}" ]; then
fi

# Derive workspace subdir from the checkout path.
# Mirrors the aspect_root_dir derivation in get_bazelrc_flags in environment.axl.
# Mirrors the aspect_root_dir derivation in runner_startup_flags in aspect_flags.axl.
WORKSPACE_DIR="${BUILDKITE_BUILD_CHECKOUT_PATH:-${GITHUB_WORKSPACE:-${CIRCLE_WORKING_DIRECTORY:-${CI_PROJECT_DIR:-$(pwd)}}}}"
SUBDIR=$(basename "${WORKSPACE_DIR}" | sed 's|[^a-zA-Z0-9._-]|_|g')

Expand Down Expand Up @@ -309,11 +298,7 @@ export DISABLE_PLUGINS_FLAG
export LOCK_VERSION_FLAG

echo "Startup opts: ${BAZEL_STARTUP_OPTS}"
# Redact x-identity header values before echoing — the runner identity is an
# auth credential, and the CLI's own log redaction treats remote_header/
# bes_header values as secrets (crates/axl-runtime/src/engine/bazel/stream/
# redaction.rs). The exported BAZEL_BUILD_OPTS keeps the real value.
echo "Build opts: $(printf '%s' "${BAZEL_BUILD_OPTS}" | sed 's/x-identity=[^ ]*/x-identity=<REDACTED>/g')"
echo "Build opts: ${BAZEL_BUILD_OPTS}"

USER_BAZELRC="${HOME}/.bazelrc"
if [ -f "${USER_BAZELRC}" ]; then
Expand Down
29 changes: 23 additions & 6 deletions .aspect/config.axl
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ load("@aspect//private/feature/buildkite_annotations_test.axl", "bk_annotation_s
load("@aspect//private/feature/github_status_comments_test.axl", "pr_comment_snapshot_tests")
load("@aspect//private/helpers_test.axl", "helpers_facade_tests")
load("@aspect//private/lib/aspect_endpoint_auth_test.axl", "aspect_endpoint_auth_tests")
load("@aspect//private/lib/aspect_flags_test.axl", "aspect_flags_tests")
load("@aspect//private/lib/bazel_flags_test.axl", "bazel_flags_tests")
load("@aspect//private/lib/bazel_results_test.axl", "bazel_results_unit_tests", "template_snapshot_tests")
load("@aspect//private/lib/bazelrc_test.axl", "bazelrc_tests")
load("@aspect//private/lib/bazelrc_file_test.axl", "bazelrc_file_tests")
load("@aspect//private/lib/cache_selection_test.axl", "cache_selection_tests")
load("@aspect//private/lib/ci_test.axl", "ci_tests")
load("@aspect//private/lib/circleci_test.axl", "circleci_tests")
load("@aspect//private/lib/delivery_results_test.axl", "delivery_results_unit_tests", "delivery_template_snapshot_tests")
load("@aspect//private/lib/deliveryd_test.axl", "deliveryd_tests")
load("@aspect//private/lib/deployment_flags_test.axl", "deployment_flags_tests")
load("@aspect//private/lib/deployment_rc_test.axl", "deployment_rc_tests")
load("@aspect//private/lib/format_results_test.axl", "format_template_snapshot_tests")
load("@aspect//private/lib/format_spawn_test.axl", "format_spawn_tests")
load("@aspect//private/lib/gazelle_results_test.axl", "gazelle_template_snapshot_tests")
Expand All @@ -37,6 +39,7 @@ load("@aspect//private/lib/lifecycle_test.axl", "lifecycle_tests")
load("@aspect//private/lib/lint_comments_test.axl", "lint_comments_tests")
load("@aspect//private/lib/lint_results_test.axl", "detect_lint_tool_tests", "lint_annotation_plan_tests", "lint_template_snapshot_tests", "linter_rows_tests")
load("@aspect//private/lib/rate_limit_test.axl", "rate_limit_tests")
load("@aspect//private/lib/rc_groups_test.axl", "rc_groups_tests")
load("@aspect//private/lib/remote_executor_test.axl", "remote_executor_tests")
load("@aspect//private/lib/repro_commands_test.axl", "repro_commands_tests")
load("@aspect//private/lib/runnable_test.axl", "runnable_tests")
Expand Down Expand Up @@ -334,11 +337,25 @@ def config(ctx: ConfigContext):
ctx.tasks.add(runnable_tests)
ctx.tasks.add(cache_selection_tests)

# `aspect ci bazelrc` pure helpers — version-constraint gating
# (assumed-latest when the Bazel version is unknown), mixed plain/tuple
# flag resolution, and rc rendering (startup vs common sections).
# Run with: aspect dev test-bazelrc
ctx.tasks.add(bazelrc_tests)
# The Bazel flags Aspect sets: the catalog's endpoint keying, the runner and
# env compositions, and the opt-out with its protected set.
# Run with: aspect dev test-aspect-flags
ctx.tasks.add(aspect_flags_tests)

# The rc `aspect setup bazelrc` writes as a document — rendering (startup vs
# common sections, `--config` groups) and the `try-import` placement.
# Run with: aspect dev test-bazelrc-file
ctx.tasks.add(bazelrc_file_tests)

# The named `--config` groups the rc is built from: what each holds, which
# are enabled where, and the host group's naming.
# Run with: aspect dev test-rc-groups
ctx.tasks.add(rc_groups_tests)

# The `--config=aspect-*` sections built from deployment rows: naming,
# credential helpers, per-endpoint flags, aliases, and collisions.
# Run with: aspect dev test-deployment-rc
ctx.tasks.add(deployment_rc_tests)

# tools/bazel wrapper helpers — workspace-root detection, raw-URL
# building, wrapper-version parsing, install classification, shell
Expand Down
2 changes: 2 additions & 0 deletions .aspect/generated/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
5 changes: 4 additions & 1 deletion .bazelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Aspect's rc for vanilla `bazel`, maintained by `aspect setup bazelrc`.
try-import %workspace%/.aspect/bazelrc

import %workspace%/bazel/defaults.bazelrc

common --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
Expand Down Expand Up @@ -46,4 +49,4 @@ common --test_output=errors
# repo, so it exercises `resolve_query_flags` (lib/bazel_flags.axl) dropping
# build-only options from the query expansion. Mirrors the customer rc that
# surfaced the bug.
build --show_result=20
build --show_result=20
3 changes: 2 additions & 1 deletion crates/aspect-cli/src/builtins/aspect/MODULE.aspect
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ use_task("wrapper.axl", "install")
use_task("wrapper.axl", "uninstall")
use_task("init.axl", "init")
use_task("mcp.axl", "mcp")
use_task("bazelrc.axl", "bazelrc")
use_task("bazelrc.axl", "bazelrc", "ci_bazelrc")
use_task("warming.axl", "warming")
use_task("runner_metadata.axl", "runner_metadata")
use_task("runner_health_check.axl", "runner_health_check")

use_feature("feature/artifacts.axl", "ArtifactUpload")
use_feature("feature/bazel_flags.axl", "BazelFlags")
use_feature("feature/circleci_test_results.axl", "CircleCITestResults")
use_feature("feature/deployment.axl", "Deployment")
use_feature("feature/github_lint_comments.axl", "GithubLintComments")
Expand Down
14 changes: 9 additions & 5 deletions crates/aspect-cli/src/builtins/aspect/bazel/build_events.axl
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,17 @@ def advertised_results_url(sources: list[(str, str)], rc, bes_sinks: list, comma
return results_url
return ""

def bes_results_url_flag(sources: list[(str, str)], rc, bes_sinks: list, command: str = "build") -> list:
"""`["--bes_results_url=<url>"]` when *Bazel* uploads the BES itself, else `[]`.
def bes_results_url_flag(sources: list[(str, str)], rc, command: str = "build") -> list:
"""`["--bes_results_url=<url>"]` when *Bazel* uploads the BES itself to a backend
with an advertised viewer, else `[]`.

Only Bazel consumes this flag, and only its BES module — the one `--bes_backend`
activates — reads it, to print "Streaming build results to: …" and to key the link
on its own `invocation_id`. Passing it on the CLI-streamed path would inject a flag
nothing acts on, so that path announces the URL itself; see
on its own `invocation_id`. So the viewer is matched against Bazel's backend
alone, never the CLI's sinks: when Bazel uploads to a third party while the CLI
streams to Aspect, Aspect's viewer would name an invocation the backend indexes
under the sink's id, not Bazel's, and the third party would be handed a URL it
does not serve. The CLI-streamed path announces its own URL instead; see
`announce_bes_results_url`.

A `--bes_results_url` the user already set — command line, `.bazelrc`, or an
Expand All @@ -248,7 +252,7 @@ def bes_results_url_flag(sources: list[(str, str)], rc, bes_sinks: list, command
if not bazel_bes_backend(rc, command):
return []

results_url = advertised_results_url(sources, rc, bes_sinks, command)
results_url = advertised_results_url(sources, rc, [], command)
return ["--bes_results_url=" + results_url] if results_url else []

def bes_upload_line(uri: str, sent: int, acked: int, failed: bool) -> str:
Expand Down
20 changes: 15 additions & 5 deletions crates/aspect-cli/src/builtins/aspect/bazel/build_events_test.axl
Original file line number Diff line number Diff line change
Expand Up @@ -282,12 +282,12 @@ def _test_bes_flag_only_when_bazel_uploads(_):
URL itself instead)."""
_eq(
"bazel-side upload → wired",
bes_results_url_flag([_GCP], _fake_rc({"--bes_backend": "grpcs://bes.gcp"}), []),
bes_results_url_flag([_GCP], _fake_rc({"--bes_backend": "grpcs://bes.gcp"})),
["--bes_results_url=https://app.gcp/i/"],
)
_eq(
"cli internal client → no flag for Bazel to read",
bes_results_url_flag([_GCP], _fake_rc({}), _sinks("grpcs://bes.gcp")),
bes_results_url_flag([_GCP], _fake_rc({})),
[],
)

Expand All @@ -296,18 +296,28 @@ def _test_bes_flag_needs_an_advertised_backend(_):
it."""
_eq(
"third-party bazel backend → not wired",
bes_results_url_flag([_GCP], _fake_rc({"--bes_backend": "grpcs://bes.elsewhere"}), []),
bes_results_url_flag([_GCP], _fake_rc({"--bes_backend": "grpcs://bes.elsewhere"})),
[],
)
_eq("no forwarding at all → not wired", bes_results_url_flag([_GCP], _fake_rc({})), [])

# Dual write: Bazel uploads to a third party while the CLI streams to the
# deployment. Only Bazel's own backend decides the flag — the deployment's
# viewer would name an invocation indexed under the sink's id, not Bazel's.
# (The CLI's sinks are not an input here; `advertised_results_url` takes them
# for the CLI's own announce line.)
_eq(
"dual write → Bazel's third party gets no Aspect viewer",
bes_results_url_flag([_GCP], _fake_rc({"--bes_backend": "grpcs://bes.elsewhere"})),
[],
)
_eq("no forwarding at all → not wired", bes_results_url_flag([_GCP], _fake_rc({}), []), [])

def _test_bes_flag_yields_to_user_flag(_):
_eq(
"a user-set --bes_results_url is never overridden",
bes_results_url_flag(
[_GCP],
_fake_rc({"--bes_results_url": "https://mine.example", "--bes_backend": "grpcs://bes.gcp"}),
_sinks("grpcs://bes.gcp"),
),
[],
)
Expand Down
13 changes: 13 additions & 0 deletions crates/aspect-cli/src/builtins/aspect/bazel/build_metadata.axl
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,19 @@ def version_gte(a, b):
# legacy two-field form (NAME=friendly_kind, ID=UUID).
_TASK_METADATA_NEW_SCHEMA_MIN_VERSION = "5.18.0"

def register_bes_metadata(ctx, bazel_trait) -> None:
"""Give the invocation the `--build_metadata` a BES backend reads: the task
identity (`get_task_metadata_flags`, a `task_flags` hook since it needs the
TaskContext) and the commit and CI context (`get_build_metadata_flags`).
Idempotent, so every feature that wires a BES backend — Workflows for the
runner's, Deployment for `--remote` — can call it and the flags appear once.
Without a backend the metadata has nowhere to surface, so nothing else
registers it."""
if get_task_metadata_flags in bazel_trait.task_flags:
return
bazel_trait.task_flags.append(get_task_metadata_flags)
bazel_trait.extra_flags.extend(get_build_metadata_flags(ctx.std))

def get_task_metadata_flags(ctx):
"""Generate --build_metadata flags for the current task identity.

Expand Down
26 changes: 17 additions & 9 deletions crates/aspect-cli/src/builtins/aspect/bazel/flags.axl
Original file line number Diff line number Diff line change
Expand Up @@ -206,23 +206,30 @@ def _disclaim_passthrough(ctx) -> None:
for bucket in _PASSTHROUGH_ARGS:
_claim_bucket(ctx, bucket)

def _filter_injected(ctx, flags: list) -> list:
"""`flags` through every `trait.injected_flag_filters` hook: the user's
opt-outs, applied only to what features inject."""
for flag_filter in ctx.traits[BazelTrait].injected_flag_filters:
flags = flag_filter(flags)
return flags

def _resolve_startup_flags(ctx) -> list:
"""`ctx.args.bazel_startup_flags + pre-command passthrough +
trait.extra_startup_flags`, then the `trait.startup_flags(flags)` transform
if set. Requires a `bazel_startup_flags` CLI arg."""
trait.extra_startup_flags` (filtered), then the `trait.startup_flags(flags)`
transform if set. Requires a `bazel_startup_flags` CLI arg."""
bazel_trait = ctx.traits[BazelTrait]
_require_arg(ctx, "bazel_startup_flags")
flags = list(ctx.args.bazel_startup_flags)
flags.extend(_claim_bucket(ctx, _PASSTHROUGH_STARTUP_FLAGS_ARG))
flags.extend(bazel_trait.extra_startup_flags)
flags.extend(_filter_injected(ctx, list(bazel_trait.extra_startup_flags)))
if bazel_trait.startup_flags:
flags = bazel_trait.startup_flags(flags)
return flags

def _resolve_flags(ctx) -> list:
"""`ctx.args.bazel_flags + post-command passthrough + trait.extra_flags +
trait.task_flags(ctx) hooks`, then the `trait.flags(flags)` transform if set.
Requires a `bazel_flags` CLI arg.
trait.task_flags(ctx) hooks` (the injected part filtered), then the
`trait.flags(flags)` transform if set. Requires a `bazel_flags` CLI arg.

Passthrough flags follow the explicit `--bazel-flag` ones: both are
user-typed, and on a collision Bazel takes the last, so the direct spelling
Expand All @@ -231,9 +238,10 @@ def _resolve_flags(ctx) -> list:
_require_arg(ctx, "bazel_flags")
flags = list(ctx.args.bazel_flags)
flags.extend(_claim_bucket(ctx, _PASSTHROUGH_FLAGS_ARG))
flags.extend(bazel_trait.extra_flags)
injected = list(bazel_trait.extra_flags)
for hook in bazel_trait.task_flags:
flags.extend(hook(ctx))
injected.extend(hook(ctx))
flags.extend(_filter_injected(ctx, injected))
if bazel_trait.flags:
flags = bazel_trait.flags(flags)
return flags
Expand All @@ -254,7 +262,7 @@ def _setup_command(ctx, command: str, base_flags: list = []):
active run command every Bazel call uses.
"""
startup_flags = _resolve_startup_flags(ctx)
flags = list(ctx.traits[BazelTrait].base_flags) + list(base_flags) + _resolve_flags(ctx)
flags = _filter_injected(ctx, list(ctx.traits[BazelTrait].base_flags)) + list(base_flags) + _resolve_flags(ctx)
rc = ctx.bazel.parse_rc(
startup_flags = startup_flags,
flags = flags,
Expand All @@ -280,7 +288,7 @@ def _sibling_rc(ctx, startup_transform, command: str = "build", base_flags: list
the disclosure — pass the returned rc as a per-call `rc=` on `ctx.bazel.build`.
"""
startup_flags = startup_transform(_resolve_startup_flags(ctx))
flags = list(ctx.traits[BazelTrait].base_flags) + list(base_flags) + _resolve_flags(ctx)
flags = _filter_injected(ctx, list(ctx.traits[BazelTrait].base_flags)) + list(base_flags) + _resolve_flags(ctx)
return ctx.bazel.parse_rc(
startup_flags = startup_flags,
flags = flags,
Expand Down
2 changes: 1 addition & 1 deletion crates/aspect-cli/src/builtins/aspect/bazel/invocation.axl
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def _resolve(ctx, command: str) -> _Invocation:
# id), so the flag rides only that path — the CLI-streamed path announces
# the URL itself. A value the user already set is never overridden.
results_url = advertised_results_url(trait.bes_results_sources, rc, sinks, command)
rc_flags.extend(bes_results_url_flag(trait.bes_results_sources, rc, sinks, command))
rc_flags.extend(bes_results_url_flag(trait.bes_results_sources, rc, command))

requested = ctx.args.bazel_retry_attempts if hasattr(ctx.args, "bazel_retry_attempts") else DEFAULT_BAZEL_RETRY_ATTEMPTS

Expand Down
1 change: 1 addition & 0 deletions crates/aspect-cli/src/builtins/aspect/bazel/trait.axl
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ BazelTrait = trait(
flags = attr(typing.Callable[[list[str]], list[str]] | None, default = None, description = "Transform function called with the full flag list; return value replaces the list"),
startup_flags = attr(typing.Callable[[list[str]], list[str]] | None, default = None, description = "Transform function called with the full startup flag list; return value replaces the list"),
base_flags = attr(list[str], default = [], description = "Default Bazel flags prepended before the user's --bazel-flag overrides (user overrides win, unlike extra_flags which append after)"),
injected_flag_filters = attr(list[typing.Callable[[list], list]], default = [], description = "The user's flag policy for a task's Bazel call: filters applied to every flag features inject (base_flags, extra_flags, extra_startup_flags, task_flags results) and never to the user's own flags. The BazelFlags feature registers --bazel-flags:omit here; Workflows and Deployment inject, the spawn path filters"),
bes_backends = attr(list[str], default = [], description = "Extra CLI-streamed BES backend URIs to stream build events to"),
bes_results_sources = attr(list[(str, str)], default = [], description = "(BES backend, results viewer URL) pairs the invocation's build-result link is drawn from; matched against everywhere the build reports BES, first hit wins"),

Expand Down
Loading
Loading