From 6e6b491f9465bcddcb670aec75797ea62f396871 Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Wed, 13 May 2026 16:33:00 +0200 Subject: [PATCH 1/4] ci: Add sbom and attestation --- .ci/docker.sh | 5 +- .github/workflows/build.yml | 107 +++++++++++++++++++++++++++- .github/workflows/libretro.yml | 123 +++++++++++++++++++++++++++++++-- 3 files changed, 226 insertions(+), 9 deletions(-) diff --git a/.ci/docker.sh b/.ci/docker.sh index d4df835c4..1747f8832 100755 --- a/.ci/docker.sh +++ b/.ci/docker.sh @@ -14,4 +14,7 @@ echo "Tag name is: $TAG_NAME" docker build -f docker/azahar-room/Dockerfile -t azahar-room:$TAG_NAME . mkdir -p build -docker save azahar-room:$TAG_NAME > build/azahar-room-$TAG_NAME.dockerimage +FILENAME="azahar-room-$TAG_NAME.dockerimage" +docker save azahar-room:$TAG_NAME > build/$FILENAME + +echo "DOCKER_IMAGE_PATH=artifacts/$FILENAME" >> $GITHUB_ENV \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 70a8417f0..559566b40 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,11 @@ on: pull_request: branches: [ master ] +permissions: + id-token: write + contents: read + attestations: write + jobs: source: if: ${{ !github.head_ref }} @@ -17,11 +22,26 @@ jobs: submodules: recursive - name: Pack run: ./.ci/source.sh + - name: Generate SBOM + if: ${{ github.ref_type == 'tag' }} + uses: anchore/sbom-action@v0 + with: + path: ./ + format: spdx-json + output-file: artifacts/source.spdx.json + upload-artifact: false - name: Upload uses: actions/upload-artifact@v4 with: name: source path: artifacts/ + - name: Attest artifacts + if: ${{ github.ref_type == 'tag' }} + uses: actions/attest@v4 + with: + subject-path: | + artifacts/*.tar.xz + sbom-path: artifacts/source.spdx.json linux-x86_64: runs-on: ubuntu-latest @@ -39,13 +59,14 @@ jobs: OS: linux TARGET: ${{ matrix.target }} SHOULD_RUN: ${{ (matrix.target != 'appimage-wayland' || github.ref_type == 'tag') }} + CACHE_ENABLED: ${{ github.ref_type != 'tag' }} steps: - uses: actions/checkout@v4 if: ${{ env.SHOULD_RUN == 'true' }} with: submodules: recursive - name: Set up cache - if: ${{ env.SHOULD_RUN == 'true' }} + if: ${{ env.SHOULD_RUN == 'true' && env.CACHE_ENABLED == 'true' }} uses: actions/cache@v4 with: path: ${{ env.CCACHE_DIR }} @@ -64,12 +85,27 @@ jobs: if: ${{ matrix.target == 'appimage-wayland' && env.SHOULD_RUN == 'true' }} run: | mv artifacts/azahar.AppImage artifacts/azahar-wayland.AppImage + - name: Generate SBOM + if: ${{ contains(matrix.target, 'appimage') && github.ref_type == 'tag' && env.SHOULD_RUN == 'true' }} + uses: anchore/sbom-action@v0 + with: + path: build/ + format: spdx-json + output-file: artifacts/linux-x86_64-${{ matrix.target }}.spdx.json + upload-artifact: false - name: Upload if: ${{ contains(matrix.target, 'appimage') && env.SHOULD_RUN == 'true' }} uses: actions/upload-artifact@v4 with: name: ${{ github.job }}-${{ matrix.target }} path: artifacts/ + - name: Attest artifacts + if: ${{ contains(matrix.target, 'appimage') && github.ref_type == 'tag' && env.SHOULD_RUN == 'true' }} + uses: actions/attest@v4 + with: + subject-path: | + artifacts/*.AppImage + sbom-path: artifacts/linux-x86_64-${{ matrix.target }}.spdx.json linux-arm64: runs-on: ubuntu-24.04-arm @@ -106,12 +142,14 @@ jobs: CCACHE_DIR: ${{ github.workspace }}/.ccache CCACHE_COMPILERCHECK: content CCACHE_SLOPPINESS: time_macros + CACHE_ENABLED: ${{ github.ref_type != 'tag' }} OS: macos steps: - uses: actions/checkout@v4 with: submodules: recursive - name: Set up cache + if: ${{ env.CACHE_ENABLED == 'true' }} uses: actions/cache@v4 with: path: ${{ env.CCACHE_DIR }} @@ -136,11 +174,26 @@ jobs: env: PACK_INDIVIDUALLY: 1 run: ./.ci/pack.sh + - name: Generate SBOM + if: ${{ github.ref_type == 'tag' }} + uses: anchore/sbom-action@v0 + with: + path: build/ + format: spdx-json + output-file: artifacts/macos.spdx.json + upload-artifact: false - name: Upload uses: actions/upload-artifact@v4 with: name: ${{ env.OS }} path: artifacts/ + - name: Attest artifacts + if: ${{ github.ref_type == 'tag' }} + uses: actions/attest@v4 + with: + subject-path: | + artifacts/*.zip + sbom-path: artifacts/macos.spdx.json windows: strategy: @@ -165,6 +218,7 @@ jobs: CCACHE_DIR: ${{ github.workspace }}/.ccache CCACHE_COMPILERCHECK: content CCACHE_SLOPPINESS: time_macros + CACHE_ENABLED: ${{ github.ref_type != 'tag' }} OS: windows TARGET: ${{ matrix.target }} steps: @@ -172,6 +226,7 @@ jobs: with: submodules: recursive - name: Set up cache + if: ${{ env.CACHE_ENABLED == 'true' }} uses: actions/cache@v4 with: path: ${{ env.CCACHE_DIR }} @@ -236,11 +291,27 @@ jobs: mv ./*.exe ../../artifacts/ - name: Pack run: ./.ci/pack.sh + - name: Generate SBOM + if: ${{ github.ref_type == 'tag' }} + uses: anchore/sbom-action@v0 + with: + path: build/ + format: spdx-json + output-file: artifacts/windows-${{ matrix.target }}.spdx.json + upload-artifact: false - name: Upload uses: actions/upload-artifact@v4 with: name: ${{ env.OS }}-${{ env.TARGET }} path: artifacts/ + - name: Attest artifacts + if: ${{ github.ref_type == 'tag' }} + uses: actions/attest@v4 + with: + subject-path: | + artifacts/*.zip + artifacts/*.exe + sbom-path: artifacts/windows-${{ matrix.target }}.spdx.json android: runs-on: ubuntu-latest @@ -252,6 +323,7 @@ jobs: CCACHE_DIR: ${{ github.workspace }}/.ccache CCACHE_COMPILERCHECK: content CCACHE_SLOPPINESS: time_macros + CACHE_ENABLED: ${{ github.ref_type != 'tag' }} OS: android TARGET: ${{ matrix.target }} SHOULD_RUN: ${{ (matrix.target == 'vanilla' || github.ref_type == 'tag') }} @@ -261,7 +333,7 @@ jobs: with: submodules: recursive - name: Set up cache - if: ${{ env.SHOULD_RUN == 'true' }} + if: ${{ env.SHOULD_RUN == 'true' && env.CACHE_ENABLED == 'true' }} uses: actions/cache@v4 with: path: | @@ -300,12 +372,28 @@ jobs: working-directory: src/android/app env: UNPACKED: 1 + - name: Generate SBOM + if: ${{ github.ref_type == 'tag' }} + uses: anchore/sbom-action@v0 + with: + path: src/android + format: spdx-json + output-file: src/android/app/artifacts/android-${{ matrix.target }}.spdx.json + upload-artifact: false - name: Upload if: ${{ env.SHOULD_RUN == 'true' }} uses: actions/upload-artifact@v4 with: name: ${{ env.OS }}-${{ env.TARGET }} path: src/android/app/artifacts/ + - name: Attest artifacts + if: ${{ github.ref_type == 'tag' }} + uses: actions/attest@v4 + with: + subject-path: | + src/android/app/artifacts/*.apk + src/android/app/artifacts/*.aab + sbom-path: src/android/app/artifacts/android-${{ matrix.target }}.spdx.json docker: runs-on: ubuntu-latest @@ -325,8 +413,23 @@ jobs: run: | mkdir -p artifacts mv build/*.dockerimage artifacts/ + - name: Generate SBOM + if: ${{ github.ref_type == 'tag' }} + uses: anchore/sbom-action@v0 + with: + image: ${{ env.DOCKER_IMAGE_PATH }} + format: spdx-json + output-file: artifacts/docker-room.spdx.json + upload-artifact: false - name: Upload uses: actions/upload-artifact@v4 with: name: docker path: artifacts/ + - name: Attest artifacts + if: ${{ github.ref_type == 'tag' }} + uses: actions/attest@v4 + with: + subject-path: | + artifacts/*.dockerimage + sbom-path: artifacts/docker-room.spdx.json diff --git a/.github/workflows/libretro.yml b/.github/workflows/libretro.yml index 7739a0e8a..10c137327 100644 --- a/.github/workflows/libretro.yml +++ b/.github/workflows/libretro.yml @@ -11,6 +11,11 @@ on: env: CORE_ARGS: -DENABLE_LIBRETRO=ON +permissions: + id-token: write + contents: read + attestations: write + jobs: android: runs-on: ubuntu-22.04 @@ -48,11 +53,29 @@ jobs: llvm-strip -s $BUILD_DIR/$EXTRA_PATH/azahar_libretro.* - name: Pack run: ./.ci/libretro-pack.sh + - name: Generate SBOM + if: ${{ github.ref_type == 'tag' }} + uses: anchore/sbom-action@v0 + with: + path: build/ + format: spdx-json + output-file: libretro-android.spdx.json + upload-artifact: false - name: Upload uses: actions/upload-artifact@v4 with: name: ${{ env.OS }}-${{ env.TARGET }} - path: ./*.zip + path: | + ./*.zip + ./*.spdx.json + - name: Attest artifacts + if: ${{ github.ref_type == 'tag' }} + uses: actions/attest@v4 + with: + subject-path: | + ./*.zip + sbom-path: libretro-android.spdx.json + linux: runs-on: ubuntu-22.04 env: @@ -76,11 +99,29 @@ jobs: llvm-strip -s $BUILD_DIR/$EXTRA_PATH/azahar_libretro.* - name: Pack run: ./.ci/libretro-pack.sh + - name: Generate SBOM + if: ${{ github.ref_type == 'tag' }} + uses: anchore/sbom-action@v0 + with: + path: build/ + format: spdx-json + output-file: libretro-linux.spdx.json + upload-artifact: false - name: Upload uses: actions/upload-artifact@v4 with: name: ${{ env.OS }}-${{ env.TARGET }} - path: ./*.zip + path: | + ./*.zip + ./*.spdx.json + - name: Attest artifacts + if: ${{ github.ref_type == 'tag' }} + uses: actions/attest@v4 + with: + subject-path: | + ./*.zip + sbom-path: libretro-linux.spdx.json + windows: runs-on: ubuntu-latest env: @@ -108,11 +149,28 @@ jobs: x86_64-w64-mingw32.static-strip -s $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*" - name: Pack run: ./.ci/libretro-pack.sh + - name: Generate SBOM + if: ${{ github.ref_type == 'tag' }} + uses: anchore/sbom-action@v0 + with: + path: build/ + format: spdx-json + output-file: libretro-windows.spdx.json + upload-artifact: false - name: Upload uses: actions/upload-artifact@v4 with: name: ${{ env.OS }}-${{ env.TARGET }} - path: ./*.zip + path: | + ./*.zip + ./*.spdx.json + - name: Attest artifacts + if: ${{ github.ref_type == 'tag' }} + uses: actions/attest@v4 + with: + subject-path: | + ./*.zip + sbom-path: libretro-windows.spdx.json macos: runs-on: macos-26 strategy: @@ -137,11 +195,29 @@ jobs: strip -x $BUILD_DIR/$EXTRA_PATH/azahar_libretro.* - name: Pack run: ./.ci/libretro-pack.sh + - name: Generate SBOM + if: ${{ github.ref_type == 'tag' }} + uses: anchore/sbom-action@v0 + with: + path: build/ + format: spdx-json + output-file: libretro-macos-${{ matrix.target }}.spdx.json + upload-artifact: false - name: Upload uses: actions/upload-artifact@v4 with: name: ${{ env.OS }}-${{ env.TARGET }} - path: ./*.zip + path: | + ./*.zip + ./*.spdx.json + - name: Attest artifacts + if: ${{ github.ref_type == 'tag' }} + uses: actions/attest@v4 + with: + subject-path: | + ./*.zip + sbom-path: libretro-macos-${{ matrix.target }}.spdx.json + ios: runs-on: macos-26 env: @@ -161,11 +237,29 @@ jobs: strip -x $BUILD_DIR/$EXTRA_PATH/azahar_libretro.* - name: Pack run: ./.ci/libretro-pack.sh + - name: Generate SBOM + if: ${{ github.ref_type == 'tag' }} + uses: anchore/sbom-action@v0 + with: + path: build/ + format: spdx-json + output-file: libretro-ios.spdx.json + upload-artifact: false - name: Upload uses: actions/upload-artifact@v4 with: name: ${{ env.OS }}-${{ env.TARGET }} - path: ./*.zip + path: | + ./*.zip + ./*.spdx.json + - name: Attest artifacts + if: ${{ github.ref_type == 'tag' }} + uses: actions/attest@v4 + with: + subject-path: | + ./*.zip + sbom-path: libretro-ios.spdx.json + tvos: runs-on: macos-26 env: @@ -185,8 +279,25 @@ jobs: strip -x $BUILD_DIR/$EXTRA_PATH/azahar_libretro.* - name: Pack run: ./.ci/libretro-pack.sh + - name: Generate SBOM + if: ${{ github.ref_type == 'tag' }} + uses: anchore/sbom-action@v0 + with: + path: build/ + format: spdx-json + output-file: libretro-tvos.spdx.json + upload-artifact: false - name: Upload uses: actions/upload-artifact@v4 with: name: ${{ env.OS }}-${{ env.TARGET }} - path: ./*.zip + path: | + ./*.zip + ./*.spdx.json + - name: Attest artifacts + if: ${{ github.ref_type == 'tag' }} + uses: actions/attest@v4 + with: + subject-path: | + ./*.zip + sbom-path: libretro-tvos.spdx.json From 3980e94f729b0cc8bfd8da0912abc698491b6845 Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Thu, 14 May 2026 00:16:24 +0200 Subject: [PATCH 2/4] tools: Add verify-release.sh --- tools/verify-release.sh | 122 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 tools/verify-release.sh diff --git a/tools/verify-release.sh b/tools/verify-release.sh new file mode 100644 index 000000000..3ccc419e6 --- /dev/null +++ b/tools/verify-release.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash + +# Copyright Citra Emulator Project / Azahar Emulator Project +# Licensed under GPLv2 or any later version +# Refer to the license.txt file included. + +set -euo pipefail + +# Usage: +# ./verify-release.sh +# +# Example: +# ./verify-release.sh azahar-emu/azahar 2126.0 +# +# Behavior: +# - Downloads all release assets +# - Verifies asset is published in the release +# - Verifies SPDX attestations for every asset +# +# Notes: +# - Draft release support requires authentication with permission +# to view the draft release. +# - gh release verify-asset currently does NOT support draft releases. + +if [[ $# -ne 2 ]]; then + echo "Usage: $0 " + exit 1 +fi + +command -v gh >/dev/null 2>&1 || { + echo "ERROR: GitHub CLI (gh) is not installed or not in PATH" + exit 1 +} + +REPO="$1" +TAG="$2" + +echo "==> Fetching release metadata" + +IS_DRAFT=$( + gh release view "$TAG" \ + --repo "$REPO" \ + --json isDraft \ + --jq '.isDraft' +) + +WORKDIR="release-${TAG}" + +rm -rf "$WORKDIR" +mkdir -p "$WORKDIR" +cd "$WORKDIR" + +echo +echo "==> Downloading release assets" + +gh release download "$TAG" \ + --repo "$REPO" + +echo +echo "==> Fetching asset list" + +mapfile -t ASSETS < <( + gh release view "$TAG" \ + --repo "$REPO" \ + --json assets \ + --jq '.assets[].name' +) + +echo +echo "==> Release type: $( + [[ "$IS_DRAFT" == "true" ]] && echo "draft" || echo "published" +)" + +echo +echo "==> Verifying assets" + +for asset in "${ASSETS[@]}"; do + # Skip attestation files themselves + if [[ "$asset" == *.intoto.jsonl ]]; then + continue + fi + + if [[ ! -f "$asset" ]]; then + echo "ERROR: Missing downloaded asset: $asset" + exit 1 + fi + + echo + echo "========================================" + echo "Asset: $asset" + echo "========================================" + + echo "1/2 release verify-asset" + + if [[ "$IS_DRAFT" != "true" ]]; then + gh release verify-asset "$TAG" "$asset" \ + --repo "$REPO" + echo + else + echo "SKIPPED (draft releases unsupported)" + echo + fi + + echo "2/2 attestation verify (SPDX)" + + if [[ "$asset" == *.sha256sum ]]; then + echo "SKIPPED (sha256sum does not need verification)" + else + gh attestation verify "$asset" \ + --repo "$REPO" \ + --predicate-type https://spdx.dev/Document + fi + + echo "OK: $asset" +done + +rm -rf "$WORKDIR" + +echo +echo "========================================" +echo "All assets verified successfully" +echo "========================================" \ No newline at end of file From 33c99e5bcdb6717f24d33ec408986954cc132c0b Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Thu, 14 May 2026 12:07:57 +0100 Subject: [PATCH 3/4] verify-release.sh: Set executable permission --- tools/verify-release.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tools/verify-release.sh diff --git a/tools/verify-release.sh b/tools/verify-release.sh old mode 100644 new mode 100755 From d2790753de20a81bd0259197e870a2cf04a37abb Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Thu, 14 May 2026 12:11:52 +0100 Subject: [PATCH 4/4] verify-release.sh: Put downloads into a gitignored directory --- .gitignore | 3 +++ tools/verify-release.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3a44642e1..b234d6bc5 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,6 @@ VULKAN_SDK/ # Version info files GIT-COMMIT GIT-TAG + +# verify-release.sh downloads +verify/ \ No newline at end of file diff --git a/tools/verify-release.sh b/tools/verify-release.sh index 3ccc419e6..ac5fc949c 100755 --- a/tools/verify-release.sh +++ b/tools/verify-release.sh @@ -44,7 +44,7 @@ IS_DRAFT=$( --jq '.isDraft' ) -WORKDIR="release-${TAG}" +WORKDIR="verify/release-${TAG}" rm -rf "$WORKDIR" mkdir -p "$WORKDIR"