Skip to content

Commit b5a0b52

Browse files
committed
WIP: snapshot golden tests scaffolding
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent 4cb8124 commit b5a0b52

19 files changed

Lines changed: 2068 additions & 11 deletions

File tree

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
2+
3+
# Regenerate snapshot goldens stored at
4+
# ghcr.io/hyperlight-dev/hyperlight-snapshot-goldens.
5+
#
6+
# Run order:
7+
#
8+
# 1. Bump `GOLDENS_VERSION` in
9+
# `src/hyperlight_host/tests/snapshot_goldens/platform.rs` and any
10+
# matching constants in
11+
# `src/hyperlight_host/src/sandbox/snapshot/tripwires.rs`.
12+
# 2. Push the bump on a branch.
13+
# 3. Dispatch this workflow against the branch, passing the same
14+
# version string as the `version` input.
15+
#
16+
# The workflow walks every (hv, cpu, config) cell, dumps the
17+
# canonical init+call snapshots locally, then `oras copy`s each as
18+
# its own GHCR tag named
19+
# `{version}-{hv}-{cpu}-{profile}-{kind}`.
20+
21+
name: Regenerate Snapshot Goldens
22+
23+
on:
24+
workflow_dispatch:
25+
inputs:
26+
version:
27+
description: Goldens version string. Must match GOLDENS_VERSION in source (e.g. "v1.0").
28+
required: true
29+
type: string
30+
31+
env:
32+
CARGO_TERM_COLOR: always
33+
RUST_BACKTRACE: full
34+
GHCR_IMAGE: ghcr.io/hyperlight-dev/hyperlight-snapshot-goldens
35+
36+
permissions:
37+
contents: read
38+
packages: write
39+
40+
defaults:
41+
run:
42+
shell: bash
43+
44+
jobs:
45+
build-guests:
46+
strategy:
47+
matrix:
48+
config: [debug, release]
49+
uses: ./.github/workflows/dep_build_guests.yml
50+
with:
51+
config: ${{ matrix.config }}
52+
secrets: inherit
53+
54+
dump-and-push:
55+
needs: build-guests
56+
strategy:
57+
fail-fast: false
58+
matrix:
59+
hypervisor: [kvm, mshv3, hyperv-ws2025]
60+
cpu: [amd, intel]
61+
config: [debug, release]
62+
runs-on: ${{ fromJson(
63+
format('["self-hosted", "{0}", "X64", "1ES.Pool=hld-{1}-{2}", "JobId=regen-goldens-{3}-{4}-{5}-{6}"]',
64+
matrix.hypervisor == 'hyperv-ws2025' && 'Windows' || 'Linux',
65+
matrix.hypervisor == 'hyperv-ws2025' && 'win2025' || matrix.hypervisor == 'mshv3' && 'azlinux3-mshv' || matrix.hypervisor,
66+
matrix.cpu,
67+
matrix.config,
68+
github.run_id,
69+
github.run_number,
70+
github.run_attempt)) }}
71+
steps:
72+
- uses: actions/checkout@v6
73+
74+
- uses: hyperlight-dev/ci-setup-workflow@v1.9.0
75+
with:
76+
rust-toolchain: "1.89"
77+
env:
78+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
79+
80+
- name: Fix cargo home permissions
81+
if: runner.os == 'Linux'
82+
run: sudo chown -R $(id -u):$(id -g) /opt/cargo || true
83+
84+
- name: Download Rust guests
85+
uses: actions/download-artifact@v7
86+
with:
87+
name: rust-guests-${{ matrix.config }}
88+
path: src/tests/rust_guests/bin/${{ matrix.config }}/
89+
90+
- name: Install oras
91+
run: |
92+
set -euo pipefail
93+
if command -v oras >/dev/null 2>&1; then
94+
echo "oras already installed: $(oras version)"
95+
exit 0
96+
fi
97+
ORAS_VERSION=1.2.2
98+
if [ "${{ runner.os }}" = "Windows" ]; then
99+
curl -sSLO "https://github.com/oras-project/oras/releases/download/v${ORAS_VERSION}/oras_${ORAS_VERSION}_windows_amd64.zip"
100+
unzip -q "oras_${ORAS_VERSION}_windows_amd64.zip" -d oras_install
101+
mv oras_install/oras.exe /usr/bin/oras.exe
102+
else
103+
curl -sSLO "https://github.com/oras-project/oras/releases/download/v${ORAS_VERSION}/oras_${ORAS_VERSION}_linux_amd64.tar.gz"
104+
mkdir -p oras_install
105+
tar -xzf "oras_${ORAS_VERSION}_linux_amd64.tar.gz" -C oras_install
106+
sudo install -m 0755 oras_install/oras /usr/local/bin/oras
107+
fi
108+
oras version
109+
110+
- name: Verify GOLDENS_VERSION matches input
111+
run: |
112+
set -euo pipefail
113+
IN_SRC=$(grep -oE 'GOLDENS_VERSION: &str = "[^"]+"' src/hyperlight_host/tests/snapshot_goldens/platform.rs | head -n1 | sed -E 's/.*"([^"]+)".*/\1/')
114+
echo "GOLDENS_VERSION in source: ${IN_SRC}"
115+
echo "version input: ${{ inputs.version }}"
116+
if [ "${IN_SRC}" != "${{ inputs.version }}" ]; then
117+
echo "::error::version input does not match GOLDENS_VERSION in source"
118+
exit 1
119+
fi
120+
121+
- name: Generate snapshots
122+
run: |
123+
set -euo pipefail
124+
OUT="${{ github.workspace }}/snapshot-goldens-out"
125+
mkdir -p "$OUT"
126+
PROFILE_FLAG=$([ "${{ matrix.config }}" = "release" ] && echo "--release" || echo "")
127+
FEATURES=$([ "${{ matrix.hypervisor }}" = "mshv3" ] && echo "mshv3" || echo "kvm")
128+
if [ "${{ runner.os }}" = "Windows" ]; then FEATURES=""; fi
129+
cargo test $PROFILE_FLAG -p hyperlight-host \
130+
${FEATURES:+--no-default-features --features "$FEATURES,build-metadata"} \
131+
--test snapshot_goldens -- generate "$OUT"
132+
ls -la "$OUT"
133+
134+
- name: Log in to GHCR
135+
run: |
136+
echo "${{ secrets.GITHUB_TOKEN }}" | oras login ghcr.io -u "${{ github.actor }}" --password-stdin
137+
138+
- name: Push goldens to GHCR
139+
run: |
140+
set -euo pipefail
141+
OUT="${{ github.workspace }}/snapshot-goldens-out"
142+
for layout in "$OUT"/*/; do
143+
tag=$(basename "$layout")
144+
tag=${tag%/}
145+
echo "::group::push ${tag}"
146+
oras copy --from-oci-layout "${layout%/}:${tag}" "${GHCR_IMAGE}:${tag}"
147+
echo "::endgroup::"
148+
done

.github/workflows/ValidatePullRequest.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ jobs:
8989
# See: https://github.com/actions/runner/issues/2205
9090
if: ${{ !cancelled() && !failure() }}
9191
strategy:
92-
fail-fast: true
92+
fail-fast: false
9393
matrix:
9494
hypervisor: ['hyperv-ws2025', mshv3, kvm]
9595
cpu: [amd, intel]

Cargo.lock

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Justfile

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,3 +568,52 @@ install-vcpkg:
568568
569569
install-flatbuffers-with-vcpkg: install-vcpkg
570570
cd ../vcpkg && ./vcpkg install flatbuffers || cd -
571+
572+
###################################
573+
### SNAPSHOT GOLDEN HELPERS ###
574+
###################################
575+
# Custom-harness test binary that verifies / regenerates snapshot
576+
# goldens stored on an OCI registry. The test binary itself never
577+
# touches the network: it reads only from
578+
# target/snapshot-goldens-cache/{version}/{tag}/. Populating that
579+
# cache is the job of `snapshot-goldens-pull`, which shells out to
580+
# `oras` (install from https://oras.land).
581+
582+
# Default OCI registry image (without tag) that hosts the goldens.
583+
default-snapshot-goldens-image := "ghcr.io/hyperlight-dev/hyperlight-snapshot-goldens"
584+
585+
# Verify the local snapshots against the goldens for the current
586+
# GOLDENS_VERSION. Run `snapshot-goldens-pull` first to populate
587+
# the local cache; missing cache entries cause hard test failures
588+
# (the harness does not skip).
589+
snapshot-goldens target=default-target:
590+
cargo test {{ if target == "release" { "--release" } else { "" } }} \
591+
-p hyperlight-host --test snapshot_goldens
592+
593+
# Pull goldens for the local platform's two tags (init + call)
594+
# from `image` into the on-disk cache used by `snapshot-goldens`.
595+
# Auto-detects hypervisor and CPU vendor on Linux; pass
596+
# `profile=release` to fetch the release-profile tags.
597+
snapshot-goldens-pull image=default-snapshot-goldens-image profile="debug":
598+
#!/usr/bin/env bash
599+
set -euo pipefail
600+
version=$(awk -F'"' '/GOLDENS_VERSION: &str =/{print $2; exit}' src/hyperlight_host/tests/snapshot_goldens/platform.rs)
601+
hv=${HYPERLIGHT_GOLDENS_HV:-$([[ -e /dev/mshv ]] && echo mshv || ([[ -e /dev/kvm ]] && echo kvm))}
602+
cpu=$(awk -F: '/vendor_id/{gsub(/ /,"",$2); print $2; exit}' /proc/cpuinfo \
603+
| sed 's/GenuineIntel/intel/;s/AuthenticAMD/amd/')
604+
[[ -n "${hv:-}" && -n "${cpu:-}" ]] || { echo "snapshot-goldens-pull: could not detect hv/cpu (set HYPERLIGHT_GOLDENS_HV)" >&2; exit 1; }
605+
for kind in init call; do
606+
tag="${version}-${hv}-${cpu}-{{ profile }}-${kind}"
607+
dir="target/snapshot-goldens-cache/${version}/${tag}"
608+
mkdir -p "${dir}"
609+
oras copy --to-oci-layout "{{ image }}:${tag}" "${dir}:${tag}"
610+
done
611+
612+
# Generate the canonical local snapshots into the cache that
613+
# `snapshot-goldens` reads from. Locally, `snapshot-goldens-generate`
614+
# followed by `snapshot-goldens` is a pure local round-trip with
615+
# no registry involved. The regen workflow calls the harness
616+
# directly with an explicit out-dir for staging.
617+
snapshot-goldens-generate target=default-target:
618+
cargo test {{ if target == "release" { "--release" } else { "" } }} \
619+
-p hyperlight-host --test snapshot_goldens -- generate

0 commit comments

Comments
 (0)