Skip to content

[anneal][v2] Add charon execution engine, generate command CLI, and integration tests #772

[anneal][v2] Add charon execution engine, generate command CLI, and integration tests

[anneal][v2] Add charon execution engine, generate command CLI, and integration tests #772

Workflow file for this run

# Copyright 2026 The Fuchsia Authors
#
# Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
# <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
# license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
name: Anneal Tests
on:
push:
branches:
- main
pull_request:
merge_group:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: -Dwarnings
RUSTDOCFLAGS: -Dwarnings
CARGO_ZEROCOPY_AUTO_INSTALL_TOOLCHAIN: 1
jobs:
anneal_tests:
name: Anneal Tests
runs-on: ubuntu-latest
needs: v2_nix_cache
permissions:
actions: read # Required to download the toolchain artifact.
contents: write # Required to push benchmark data to the storage branch
env:
ANNEAL_TOOLCHAIN_DIR: ${{ github.workspace }}/anneal/target/anneal-toolchain
__ZEROCOPY_LOCAL_DEV: 1
RUSTFLAGS: ""
RUSTDOCFLAGS: ""
RUST_TEST_THREADS: "1"
steps:
- name: Record job start time
id: job_start_time
run: echo "unix=$(date +%s)" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install stable Rust
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # zizmor: ignore[superfluous-actions]
with:
toolchain: stable
- name: Download Anneal toolchain archive
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: anneal-exocrate.tar.zst
path: anneal/v2/target
# Ensure `llms-full.txt` file is up-to-date.
- name: Check doc generation
run: cargo run -p doc_gen -- --check
working-directory: anneal
- name: Install Anneal toolchain archive
run: cargo run setup --local-archive v2/target/anneal-exocrate.tar.zst
working-directory: anneal
# Run unit tests separately, as they're much less likely to have bugs
# during local development, and this makes the GitHub Actions output
# easier to skim (in particular, it's clear at a glance whether a failure
# is due to unit or integration tests).
- name: Run unit tests
run: cargo test --verbose --bin cargo-anneal
working-directory: anneal
# We duplicate running unit tests since they're very cheap compared to
# integration tests, and this way it's easier to be sure that we run all
# tests instead of specifically trying to carve out unit tests and risk
# missing test categories.
- name: Run all tests
run: |
start=$(date +%s)
cargo test --verbose
end=$(date +%s)
duration=$((end - start))
echo "Test Time: $duration seconds"
echo "[{\"name\": \"Test Time\", \"unit\": \"seconds\", \"value\": $duration}]" > test_time.json
working-directory: anneal
- name: Combine benchmarks
env:
JOB_START_TIME_UNIX: ${{ steps.job_start_time.outputs.unix }}
run: |
total_duration=$(( $(date +%s) - $JOB_START_TIME_UNIX ))
echo "Total CI Duration (All Steps): $total_duration seconds"
echo "[{\"name\": \"Total CI Duration (All Steps)\", \"unit\": \"seconds\", \"value\": $total_duration}]" > total_time.json
jq -n \
--slurpfile test anneal/test_time.json \
--slurpfile total total_time.json \
'[
$test[0][0],
$total[0][0]
]' > output.json
- name: Store CI duration benchmarks
uses: benchmark-action/github-action-benchmark@a60cea5bc7b49e15c1f58f411161f99e0df48372 # v1.22.0
with:
name: CI Durations
tool: 'customSmallerIsBetter'
output-file-path: output.json
gh-pages-branch: benchmark-data
auto-push: true
save-data-file: ${{ github.ref == 'refs/heads/main' }}
benchmark-data-dir-path: dashboard
fail-on-alert: false
github-token: ${{ secrets.GITHUB_TOKEN }}
verify_examples:
name: Verify example (${{ matrix.example }})
runs-on: ubuntu-latest
needs: v2_nix_cache
permissions:
actions: read # Required to download the toolchain artifact.
contents: read
env:
ANNEAL_TOOLCHAIN_DIR: ${{ github.workspace }}/anneal/target/anneal-toolchain
__ZEROCOPY_LOCAL_DEV: 1
RUSTFLAGS: ""
RUSTDOCFLAGS: ""
strategy:
fail-fast: false
matrix:
example:
- abs
- anatomy
- checked_add
- const_generics
- design_doc
- linked_list
- namespaces
- never_type
- ptr_concat
- size_of_align_of
- swap
- unchecked_get
- update_max
steps:
- name: Free up disk space
run: |
sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/share/boost
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install stable Rust
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # zizmor: ignore[superfluous-actions]
with:
toolchain: stable
- name: Download Anneal toolchain archive
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: anneal-exocrate.tar.zst
path: anneal/v2/target
- name: Install Anneal toolchain archive
run: cargo run setup --local-archive v2/target/anneal-exocrate.tar.zst
working-directory: anneal
- name: Verify example
env:
EXAMPLE: ${{ matrix.example }}
run: |
KNOWN_FAILING=("design_doc" "never_type" "ptr_concat")
example="$EXAMPLE"
expect_failure=0
for kf in "${KNOWN_FAILING[@]}"; do
if [ "$kf" = "$example" ]; then
expect_failure=1
break
fi
done
echo "Verifying $example (expect failure: $expect_failure)"
if cargo run verify --unsound-allow-is-valid --example "$example"; then
if [ "$expect_failure" -eq 1 ]; then
echo "::error::Example $example succeeded but was expected to fail."
exit 1
else
echo "Example $example succeeded."
fi
else
if [ "$expect_failure" -eq 1 ]; then
echo "Example $example failed as expected."
else
echo "::error::Example $example failed."
exit 1
fi
fi
working-directory: anneal
# Build the Nix-produced toolchain archive once and fan out the exact archive
# as a workflow artifact. This avoids forcing every downstream matrix runner
# to realize the same Nix closure through Magic Nix Cache, which can run into
# GitHub Actions cache throttling under parallel fan-out.
v2_nix_cache:
name: Build Anneal Toolchain Archive
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
# Restore a local Nix binary cache populated only by trusted `main`
# pushes. Pull requests and merge-queue runs may read the default-branch
# cache, but they never save their own entries; that keeps sibling PRs
# from publishing toolchain archives for each other. This cache is only
# for cross-run reuse in the builder job. The workflow artifact below
# remains the cross-job fan-out mechanism for the current run.
#
# Keep the key inputs in sync with the local files that influence
# `.#omnibus-archive-ci`. It intentionally excludes most Anneal source
# files so ordinary PRs can reuse the archive built from `main`.
- name: Restore Anneal Nix binary cache
id: restore_anneal_nix_cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: anneal/v2/target/nix-cache
key: anneal-v2-nix-cache-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('anneal/v2/flake.nix', 'anneal/v2/flake.lock', 'anneal/v2/rewrite-lake-vendor.py') }}
- name: Install Nix
uses: DeterminateSystems/determinate-nix-action@441b9e401ac050c38a07d8313748c5c2d17e8aff # v3.6.1
# On Ubuntu 24.04 (currently `ubuntu-latest`), AppArmor restricts unprivileged user namespaces by default.
# The Nix build sandbox runs `steam-run` (which uses `bubblewrap`/`bwrap`) during the `mathlib-cache-download`
# phase to create an FHS environment. `bwrap` requires creating a user namespace to set up uid mappings,
# which fails with "Permission denied" unless this restriction is temporarily disabled on the host.
#
# We temporarily disable it right before the `nix build` step and re-enable it immediately after
# to maintain the principle of least privilege.
#
# FIXME(#3412): Deduplicate this with what's repeated below?
- name: Enable unprivileged user namespaces (Ubuntu 24.04)
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- name: Build Anneal toolchain archive
env:
ANNEAL_NIX_CACHE_HIT: ${{ steps.restore_anneal_nix_cache.outputs.cache-hit }}
ANNEAL_TRUSTED_MAIN_PUSH: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: |
set -euo pipefail
mkdir -p target
nix_args=()
if [ -f target/nix-cache/nix-cache-info ]; then
nix_args+=(--extra-substituters "file://$PWD/target/nix-cache/?trusted=1")
fi
archive_attr=omnibus-archive
if [ "$ANNEAL_TRUSTED_MAIN_PUSH" = "true" ]; then
# Trusted main-branch pushes pay the compression cost once so
# future PRs can download the smaller archive from the cache.
archive_attr=omnibus-archive-ci
elif [ "$ANNEAL_NIX_CACHE_HIT" = "true" ]; then
# When the level-19 archive is already in the restored Nix cache,
# substituting it is cheaper than rebuilding the level-1 archive.
archive_attr=omnibus-archive-ci
fi
nix build "${nix_args[@]}" ".#$archive_attr" --out-link target/anneal-exocrate.tar.zst
archive="$(readlink -f target/anneal-exocrate.tar.zst)"
rm target/anneal-exocrate.tar.zst
cp "$archive" target/anneal-exocrate.tar.zst
working-directory: anneal/v2
# Re-enable the AppArmor namespace restriction to restore the runner host's default security posture.
# `if: always()` ensures this cleanup step runs even if the Nix build fails.
- name: Restore AppArmor restriction
if: always()
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=1
# Populate the default-branch cache only from trusted `main` pushes.
# GitHub's dependency-cache scoping makes entries saved from `main`
# visible to PRs targeting `main`, while entries from PR merge refs are
# isolated to that PR. Saving only here keeps the cache useful across PRs
# without letting a PR publish an archive for unrelated runs.
- name: Populate Anneal Nix binary cache
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.restore_anneal_nix_cache.outputs.cache-hit != 'true'
run: |
set -euo pipefail
mkdir -p target/nix-cache
nix copy .#omnibus-archive-ci \
--to "file://$PWD/target/nix-cache/?compression=zstd&compression-level=19&trusted=1"
working-directory: anneal/v2
- name: Save Anneal Nix binary cache
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.restore_anneal_nix_cache.outputs.cache-hit != 'true'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: anneal/v2/target/nix-cache
key: ${{ steps.restore_anneal_nix_cache.outputs.cache-primary-key }}
- name: Upload Anneal toolchain archive
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
path: anneal/v2/target/anneal-exocrate.tar.zst
if-no-files-found: error
retention-days: 1
archive: false
v2:
name: Run V2 tests
runs-on: ubuntu-latest
# Depending on `v2_nix_cache` avoids duplicate Nix builds; this job only
# downloads the exact archive that the builder job produced.
needs: v2_nix_cache
permissions:
actions: read # Required to download the toolchain artifact.
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Download Anneal toolchain archive
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: anneal-exocrate.tar.zst
path: anneal/v2/target
# FIXME: Pin this nightly to the same Rust date encoded in
# anneal/v2/flake.nix, or derive it from the archive metadata, so v2 CI is
# reproducible instead of following whatever nightly happens to be latest.
- name: Install latest nightly Rust
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # zizmor: ignore[superfluous-actions]
with:
toolchain: nightly
- name: Run V2 tests
run: cargo test --workspace --all-features # include, e.g., tests that assume exocrate prebuilt
working-directory: anneal/v2
# Used to signal to branch protections that all other jobs have succeeded.
all-jobs-succeed:
# WARNING: This name is load-bearing! It's how GitHub's settings UI
# configures which jobs to block on. DO NOT change this name without
# updating the settings UI to match.
name: All checks succeeded (anneal.yml)
# On failure, we run and unconditionally exit with a failing status code.
# On success, this job is skipped. Jobs skipped using `if:` are considered
# to have succeeded:
#
# https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks
if: failure()
runs-on: ubuntu-latest
needs: [anneal_tests, verify_examples, v2_nix_cache, v2]
steps:
- name: Mark the job as failed
run: exit 1