diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 7d83fd2f..6efa75e2 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -27,9 +27,42 @@ permissions: actions: read # Required for workflow access jobs: + # Resolve component versions once: pinned in versions.env, else upstream latest + resolve-tags: + name: resolve component tags + runs-on: ubuntu-latest + outputs: + snphost: ${{ steps.resolve.outputs.snphost }} + snpguest: ${{ steps.resolve.outputs.snpguest }} + beacon: ${{ steps.resolve.outputs.beacon }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Resolve tags (pin from versions.env, fallback to latest) + id: resolve + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + . versions.env + resolve() { # $1=repo $2=pinned tag + if [ -n "$2" ]; then + echo "$2" + else + gh release view --repo "$1" --json tagName --jq '.tagName' + fi + } + { + echo "snphost=$(resolve virtee/snphost "${SNPHOST_TAG:-}")" + echo "snpguest=$(resolve virtee/snpguest "${SNPGUEST_TAG:-}")" + echo "beacon=$(resolve AMDEPYC/beacon "${BEACON_TAG:-}")" + } >> "$GITHUB_OUTPUT" + # Development release for tag or main pushes create-release: name: create new release for tagged or latest assets + needs: resolve-tags runs-on: ubuntu-latest if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.replace_release == 'true') steps: @@ -63,6 +96,7 @@ jobs: build: name: ${{ matrix.distro }}-${{ matrix.release }} + needs: resolve-tags runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -99,17 +133,15 @@ jobs: sudo apt update sudo apt install -y mkosi - - name: Fetch latest release tags - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Apply resolved release tags run: | set -euo pipefail - - # Fetch latest tags using gh CLI (automatically authenticated) - SNPGUEST_TAG=$(gh release view --repo virtee/snpguest --json tagName --jq '.tagName') - BEACON_TAG=$(gh release view --repo AMDEPYC/beacon --json tagName --jq '.tagName') - SNPHOST_TAG=$(gh release view --repo virtee/snphost --json tagName --jq '.tagName') - + + # Tags resolved once by the resolve-tags job (pinned in versions.env, else upstream latest) + SNPGUEST_TAG="${{ needs.resolve-tags.outputs.snpguest }}" + BEACON_TAG="${{ needs.resolve-tags.outputs.beacon }}" + SNPHOST_TAG="${{ needs.resolve-tags.outputs.snphost }}" + # Inject environment variables into mkosi.conf files sed -i "/^\[Build\]/a\Environment=SNPGUEST_TAG=$SNPGUEST_TAG" modules/build/common/snpguest/mkosi.conf sed -i "/^\[Build\]/a\Environment=BEACON_TAG=$BEACON_TAG" modules/build/host/beacon/mkosi.conf diff --git a/README.md b/README.md index 527003e3..73127380 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ Each certification run automatically creates a GitHub Issue containing the resul Host and Guest images are constructed in GitHub Workflows via [`mkosi`](https://github.com/systemd/mkosi). Host images are designed to be booted on a SEV-enabled EPYC server, and are configured with a series of tests in the form of custom systemd services that will run on an embedded guest image. The resulting host and guest images are available in GitHub releases. +The upstream release versions of the components baked into the images — [`snphost`](https://github.com/virtee/snphost), [`snpguest`](https://github.com/virtee/snpguest), and [`beacon`](https://github.com/AMDEPYC/beacon) — are pinned in [`versions.env`](./versions.env) at the repository root. Bumps are made deliberately via a pull request so that any breakage in a new upstream release is caught at review time; the `build-and-release` workflow resolves the pinned tags (falling back to the upstream latest for any empty entry) and reports the resolved `repository → version` pairs in the `resolve-tags` job summary. Leave a value empty in `versions.env` to track the upstream latest release at build time. + [cert-3.0]: ./docs/certifications.md#amd-epyc-7003-milan [cert-3.1]: ./docs/certifications.md#amd-epyc-9004-genoa [cert-4.0]: ./docs/certifications.md#amd-epyc-7004-bergamo diff --git a/docs/how-to-add-new-os-images.md b/docs/how-to-add-new-os-images.md index b06fbd9e..0cec7ee7 100644 --- a/docs/how-to-add-new-os-images.md +++ b/docs/how-to-add-new-os-images.md @@ -53,6 +53,8 @@ You can create and publish new host or guest images in `sev-certify` with `mkosi Then, verify for successful execution with the built new host and guest OS images under `devel` tag. + The `build-and-release` workflow resolves the `snphost`, `snpguest` and `beacon` release tags in its `resolve-tags` job before building. Versions are pinned in [`versions.env`](https://github.com/AMDEPYC/sev-certify/blob/main/versions.env) at the repository root (an empty entry falls back to the upstream latest release). The resolved repository/version pairs are shown as a table in the `resolve-tags` job summary. + If the `build-and-release` workflow fails, you need to troubleshoot for the error in the workflow log, implement changes, and retest until successful execution. - **Test the new OS Host/Guest Images:** @@ -183,6 +185,8 @@ make GENERATE_MANIFEST=yes guest-- The `GENERATE_MANIFEST` variable defaults to empty in the Makefile. When set to a truthy value (`y`, `yes`, or `1`), mkosi is invoked with the environment variable to generate manifest files. +The `make tags` target resolves the `snphost`, `snpguest` and `beacon` versions from [`versions.env`](https://github.com/AMDEPYC/sev-certify/blob/main/versions.env); pinned entries are used as-is and empty entries fall back to fetching the upstream latest release. + **For local builds using mkosi directly**, pass the environment variable: ```bash diff --git a/images/Makefile b/images/Makefile index a00878d7..65bf1e5b 100644 --- a/images/Makefile +++ b/images/Makefile @@ -5,22 +5,28 @@ CLEAN_IMAGES := $(addprefix clean-,$(IMAGES)) GH_AUTH := $(if $(GITHUB_TOKEN),-H "Authorization: Bearer $(GITHUB_TOKEN)") GH_LATEST = curl -sf $(GH_AUTH) https://api.github.com/repos/$(1)/releases/latest | jq -r '.tag_name' +# Pinned component versions (override on the command line with make VAR=x) +-include ../versions.env + .PHONY: all clean list status tags $(IMAGES) $(CLEAN_IMAGES) +# Pinned versions from ../versions.env are used as-is; empty entries fall back +# to fetching the latest upstream release. tags: @set -e; \ trap 'rm -f .tags.tmp' EXIT; \ fetch() { \ - tag=$$($(call GH_LATEST,$$1)); \ + tag=$$3; \ + if [ -z "$$tag" ]; then tag=$$($(call GH_LATEST,$$1)); fi; \ if [ -z "$$tag" ] || [ "$$tag" = "null" ]; then \ - echo "Failed to fetch tag for $$1. Try: export GITHUB_TOKEN=\$$(gh auth token)" >&2; \ + echo "Failed to resolve tag for $$1. Try: export GITHUB_TOKEN=\$$(gh auth token)" >&2; \ exit 1; \ fi; \ echo "$$2=$$tag"; \ }; \ - { fetch virtee/snpguest SNPGUEST_TAG; \ - fetch AMDEPYC/beacon BEACON_TAG; \ - fetch virtee/snphost SNPHOST_TAG; } > .tags.tmp; \ + { fetch virtee/snpguest SNPGUEST_TAG "$(SNPGUEST_TAG)"; \ + fetch AMDEPYC/beacon BEACON_TAG "$(BEACON_TAG)"; \ + fetch virtee/snphost SNPHOST_TAG "$(SNPHOST_TAG)"; } > .tags.tmp; \ mv .tags.tmp .tags @cat .tags diff --git a/versions.env b/versions.env new file mode 100644 index 00000000..c113f54f --- /dev/null +++ b/versions.env @@ -0,0 +1,6 @@ +# Upstream release versions used to build the host/guest images. +# Bump deliberately via a PR so any breakage in a new release is caught at review time. +# Leave a value empty to fall back to the upstream latest release at build time. +SNPHOST_TAG=v0.9.0 +SNPGUEST_TAG=v0.10.0 +BEACON_TAG=v1.1.0