Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.aspect.build/llms.txt

Use this file to discover all available pages before exploring further.

aspect delivery builds and dispatches Bazel targets (services, binaries, containers) to their destination. Its defining feature is content-hash-based change detection: a target is only re-delivered if its build outputs have actually changed, not just because a commit landed on main. On a monorepo with hundreds of services, this prevents cascade deploys. If a commit touches //backend/api:server but not //frontend/web:app, delivery re-deploys only the API service. The frontend deploy is a no-op — recorded as “already delivered” for this content, not skipped and not re-run.

Selective delivery in depth

Standard CI deploys trigger on commit SHA: every push to main re-deploys everything. At monorepo scale this means 200 services re-deploying when 1 changed. aspect delivery uses content hashes instead: Phase 1: Hash extraction A custom hashsum Bazel aspect runs alongside the normal build. It produces a per-target action digest by re-running the build with --experimental_remote_require_cached and extracting digest hashes from a gRPC execution log. This gives a stable, hermetic hash of each target’s actual build outputs — not the source inputs, not the git SHA. Phase 2: State query deliveryd (a Unix-socket HTTP server started automatically on Aspect Workflows CI runners) stores delivery history as (label, digest) → delivery event tuples, keyed by commit SHA + task key. Before dispatching, the task queries deliveryd to check whether each (label, hash) pair has already been delivered. If yes, the target is skipped. If no, it’s a delivery candidate. Phase 3: Dispatch Surviving candidates download their runfiles, then bazel run each target in parallel (up to --max-parallelization goroutines, defaulting to hardware thread count). After each dispatch, deliveryd records the result.

Why content hashes beat git SHAs

Git SHAs capture “what committed?”, not “what changed in the outputs?”. Two commits that produce identical binaries (e.g., a docs-only change alongside a code change) get different SHAs. Content hashes capture the actual artifact identity, so identical outputs are never re-delivered.

Configuration

Delivery mode

aspect delivery --mode=selective --task-key delivery   # default: skip unchanged
aspect delivery --mode=always --task-key delivery       # always deliver all resolved targets
selective is the correct mode for production pipelines. always is useful for initial setup, debugging, or forced rollouts.

Resolving delivery targets

aspect delivery --query='kind(container_push, //services/...)' --task-key delivery
The --query flag is a Bazel query expression. Any Bazel query syntax works: kind(...), attr(...), unions, intersections.

Forcing re-delivery

aspect delivery --force-target=//services/api:push --task-key delivery
Repeatable. Forces the listed targets to deliver even if their content hash matches a prior delivery. Useful for rollbacks or emergency re-deploys.

Dry run

aspect delivery --dry-run --task-key delivery
Runs phases 1 and 2 (hash extraction, state query) without dispatching. Prints which targets would be delivered. Useful for validating your query and change-detection setup before the first real deploy.

Parallelism

aspect delivery --max-parallelization=4 --task-key delivery
Default: all hardware threads. Set lower if your deploy targets have rate limits or serial ordering requirements.

CI examples

on:
  push:
    branches: [main]

jobs:
  delivery:
    runs-on: [self-hosted, aspect-workflows, aspect-default]
    permissions:
      id-token: write
    steps:
      - uses: actions/checkout@v6
      - uses: aspect-build/setup-aspect@c22a8f64fb38f82f59ce809cd7eb9f8ae096da44 # v2026.23.2
        with:
          aspect-api-token: ${{ secrets.ASPECT_API_TOKEN }}
      - run: >
          aspect delivery
          --task-key delivery
          --query='kind(container_push, //services/...)'