Cockatrice/.github/workflows/desktop-build.yml
Lukas Brübach 2c249438d5 [CI] Publish a gcovr line-coverage report from the test run
Sanitizers tell us what a run did wrong; they say nothing about what it never
reached. This adds the other half: an instrumented build whose report says which
of our own lines no test executes.

Add an ENABLE_COVERAGE option that compiles with --coverage -O0 -g, mirroring the
ENABLE_SANITIZERS opt-in including the per-source carve-out that keeps vendored
trees out of the denominator. -O0 is deliberate: at higher optimisation levels GCC
merges and relocates lines, so the report would describe code that no longer exists
in that form. Only GCC is supported, since Clang's intermediate format needs
llvm-cov; asking for coverage under Clang is a hard error rather than a build that
silently produces unreadable .gcno files.

Keep the filters in a committed gcovr.cfg rather than in the invocation. The tests
themselves are excluded because they are compiled with the same instrumentation
and are only ever executed by the run doing the measuring, so leaving them in would
report a healthier project than we have. The process entry points and the client's
window chrome go too: each needs a display, an interactive terminal or a live
database before it does anything a headless test could observe, so they would
otherwise sit at 0% forever and bury real movement.

Give coverage its own matrix leg rather than a second flag on the sanitizer leg.
The two answer different questions, and combining them slows both down without
telling us more. The new variant key gives the leg its own Docker image and ccache
entry so the two legs cannot race on the same tag.

The leg publishes an HTML report with per-file pages, LCOV, Cobertura XML and a
JSON summary. gcovr is a report-time dependency rather than a build dependency, so
a missing one warns and skips instead of failing an otherwise green build.
2026-10-10 19:51:50 +02:00

631 lines
24 KiB
YAML

name: Build Desktop
permissions:
actions: write # needed to delete entries in GHA cache (update ccache)
attestations: write # needed to persist the attestation.
contents: write # needed for e.g. vcpkg dependency graph updates
id-token: write # needed for signing certificate in attestation
on:
push:
branches:
- master
paths:
- '*/**' # matches all files not in root
- '!**.md'
- '!.github/**'
- '!.tx/**'
- '!doc/**'
- '.github/workflows/desktop-build.yml'
- 'CMakeLists.txt'
- 'vcpkg.json'
- 'vcpkg' # needed to match submodule bumps (gitlink)
tags:
- '*'
pull_request:
paths:
- '*/**' # matches all files not in root
- '!**.md'
- '!.github/**'
- '!.tx/**'
- '!doc/**'
- '.github/workflows/desktop-build.yml'
- 'CMakeLists.txt'
- 'vcpkg.json'
- 'vcpkg' # needed to match submodule bumps (gitlink)
# Cancel earlier, unfinished runs of this workflow on the same branch (unless on tag --> release)
concurrency:
group: "${{ github.workflow }} @ ${{ github.ref_name }}"
cancel-in-progress: ${{ github.ref_type != 'tag' }}
jobs:
configure:
name: Configure
runs-on: ubuntu-slim # https://github.com/actions/runner-images/blob/main/images/ubuntu-slim/ubuntu-slim-Readme.md
outputs:
tag: ${{ steps.configure.outputs.tag }}
sha: ${{ steps.configure.outputs.sha }}
steps:
- name: "Configure"
env:
RESOLVED_SHA: ${{ case(github.event_name == 'pull_request', github.event.pull_request.head.sha, github.sha) }}
id: configure
shell: bash
run: |
if [[ "$GITHUB_REF_TYPE" == 'tag' ]]; then # release
echo "tag=$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT"
fi
echo "sha=$RESOLVED_SHA" >> "$GITHUB_OUTPUT"
- name: "Checkout"
if: steps.configure.outputs.tag != null
uses: actions/checkout@v7
with:
fetch-depth: 0 # fetch all history for all branches and tags
- name: "Prepare release parameters"
id: prepare
if: steps.configure.outputs.tag != null
shell: bash
env:
TAG: ${{ steps.configure.outputs.tag }}
run: .ci/prep_release.sh
- name: "Create release"
if: steps.configure.outputs.tag != null
id: create_release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
tag_name: ${{ steps.configure.outputs.tag }}
target: ${{ steps.configure.outputs.sha }}
release_name: ${{ steps.prepare.outputs.title }}
body_path: ${{ steps.prepare.outputs.body_path }}
prerelease: ${{ steps.prepare.outputs.is_beta }}
run: |
args=()
[[ $prerelease == yes ]] && args+=(--prerelease)
gh release create "$tag_name" --verify-tag --draft "${args[@]}" \
--target "$target" \
--title "$release_name" \
--notes-file "$body_path"
build-linux:
strategy:
fail-fast: false
matrix:
# The files in ".ci/$distro$version" correspond to the values given here
include:
- distro: Arch
allow-failure: yes
package: skip # We are packaged in Arch already
- distro: Servatrice_Debian
version: 12
package: DEB
server_only: yes
test: skip
- distro: Debian
version: 12
package: DEB
test: skip # Running tests on all distros is superfluous
- distro: Debian
version: 13
package: DEB
- distro: Fedora
version: 43
package: RPM
test: skip # Running tests on all distros is superfluous
- distro: Fedora
version: 44
package: RPM
- distro: Ubuntu
version: 24.04
package: DEB
test: skip # Running tests on all distros is superfluous
- distro: Ubuntu
version: 26.04
package: DEB
# The one leg that runs the suite under AddressSanitizer and
# UndefinedBehaviorSanitizer. Debian 13 and Fedora 44 keep covering the
# uninstrumented suite, so nothing is lost by instrumenting this one.
sanitize: yes
- distro: Ubuntu
version: 26.04
variant: cover # separate image and ccache, same distro
# The one leg that runs instrumented for line coverage and publishes the report as
# a workflow artifact. It is a separate leg rather than a second flag on the
# sanitizer leg because the two instruments answer different questions: the
# sanitizer leg reports what the run did wrong, the coverage leg reports what the
# run never touched at all. Combining them would also slow both down. Packaging is
# skipped because the leg above already publishes the DEB for this distro.
coverage: yes
package: skip
name: ${{ matrix.distro }} ${{ matrix.version }}${{ matrix.variant && format(' {0}', matrix.variant) || '' }}
needs: configure
runs-on: ubuntu-latest # https://github.com/actions/runner-images
continue-on-error: ${{ matrix.allow-failure == 'yes' }}
timeout-minutes: 70
env:
CACHE: ${{ github.workspace }}/.cache/${{ matrix.distro }}${{ matrix.version }}${{ matrix.variant }} # directory for caching docker image and ccache
CCACHE_EVICTION_AGE: 7d
CCACHE_SIZE: 600M # space of all repo is 10Gi: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
CMAKE_GENERATOR: 'Ninja'
NAME: ${{ matrix.distro }}${{ matrix.version }}
IMAGE_SUFFIX: ${{ matrix.variant }}
steps:
- name: "Checkout"
uses: actions/checkout@v7
- name: "Restore compiler cache (ccache)"
id: restore_ccache
uses: actions/cache/restore@v6
with:
key: ccache-${{ matrix.distro }}${{ matrix.version }}${{ matrix.variant }}
path: ${{ env.CACHE }}
- name: "Build ${{ matrix.distro }} ${{ matrix.version }} Docker image"
shell: bash
run: source .ci/docker.sh --build
- name: "Build debug and test"
if: matrix.test != 'skip'
shell: bash
env:
sanitize: '${{ matrix.sanitize }}'
coverage: '${{ matrix.coverage }}'
run: |
source .ci/docker.sh
args=()
[[ $GITHUB_REF == "refs/heads/master" ]] && args+=(--evict-ccache "$CCACHE_EVICTION_AGE")
args+=(--ccache "$CCACHE_SIZE")
args+=(--cmake-generator "$CMAKE_GENERATOR")
extra=()
[[ $sanitize == yes ]] && extra+=(--sanitize)
[[ $coverage == yes ]] && extra+=(--coverage)
RUN --server --debug --test "${extra[@]}" "${args[@]}"
# RUN bind-mounts the workspace into the container, so the report the build wrote into
# build/ is already on the host and no second container is needed to collect it.
- name: "Collect coverage report"
if: matrix.coverage == 'yes'
shell: bash
run: |
mkdir -p coverage
# --html-details writes one page per source file plus a stylesheet and script, and the
# index links to all of them by relative name. Copying the index alone would publish a
# report where every link 404s, so the whole family has to travel together.
cp build/coverage-details* coverage/
cp build/coverage.lcov build/coverage.xml build/coverage-summary.json coverage/
ls -la coverage/
- name: "Upload coverage artifact"
if: matrix.coverage == 'yes'
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
if-no-files-found: warn
- name: "Build release package"
id: build
if: matrix.package != 'skip'
shell: bash
env:
SUFFIX: '-${{ matrix.distro }}${{ matrix.version }}'
package: '${{ matrix.package }}'
server_only: '${{ matrix.server_only }}'
run: |
source .ci/docker.sh
args=()
[[ $server_only == yes ]] && args+=(--no-client)
[[ $GITHUB_REF == "refs/heads/master" ]] && args+=(--evict-ccache "$CCACHE_EVICTION_AGE")
args+=(--ccache "$CCACHE_SIZE")
args+=(--cmake-generator "$CMAKE_GENERATOR")
args+=(--suffix "$SUFFIX")
RUN --server --release --package "$package" "${args[@]}"
# Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342
- name: "Delete remote compiler cache (ccache)"
if: github.ref == 'refs/heads/master' && steps.restore_ccache.outputs.cache-hit
continue-on-error: true
env:
CACHE_PRIMARY_KEY: ${{ steps.restore_ccache.outputs.cache-primary-key }}
GH_TOKEN: ${{ github.token }}
run: |
if gh cache delete --repo "$GITHUB_REPOSITORY" "$CACHE_PRIMARY_KEY"; then
echo "Cache deleted successfully"
fi
- name: "Save updated compiler cache (ccache)"
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@v6
with:
key: ${{ steps.restore_ccache.outputs.cache-primary-key }}
path: ${{ env.CACHE }}
- name: "Upload artifact"
id: upload_artifact
if: matrix.package != 'skip'
uses: actions/upload-artifact@v7
with:
archive: false
if-no-files-found: error
path: ${{ steps.build.outputs.path }}
- name: "Upload to release"
id: upload_release
if: matrix.package != 'skip' && needs.configure.outputs.tag != null
shell: bash
env:
asset_name: ${{ steps.build.outputs.fullname }}
asset_path: ${{ steps.build.outputs.path }}
GH_TOKEN: ${{ github.token }}
tag_name: ${{ needs.configure.outputs.tag }}
run: gh release upload "$tag_name" "$asset_path#$asset_name"
- name: "Attest binary provenance"
id: attestation
if: steps.upload_release.outcome == 'success'
uses: actions/attest@v4
with:
show-summary: false
subject-path: ${{ steps.build.outputs.path }}
- name: "Verify binary attestation"
if: steps.attestation.outcome == 'success'
shell: bash
env:
BUILD_PATH: ${{ steps.build.outputs.path }}
GH_TOKEN: ${{ github.token }}
run: gh attestation verify "$BUILD_PATH" --repo Cockatrice/Cockatrice
build-vcpkg:
strategy:
fail-fast: false
matrix:
include:
- os: macOS
target: 13 # EOL 2025-09-15
runner: macos-15-intel # https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md
# macos-26-intel is very slow and fails in CPack during DMG config if not increasing Finder timeout
ccache_eviction_age: 7d
cmake_generator: Ninja
make_package: 1
override_target: 13
package_suffix: "-macOS13_Intel"
qt_version: 6.11.*
qt_modules: qtimageformats qtmultimedia qtshadertools qtwebsockets
soc: Intel
type: Release
use_ccache: 1
xcode: "26.3"
- os: macOS
target: 14 # EOL 2026-??
runner: macos-26 # https://github.com/actions/runner-images/blob/main/images/macos/macos-26-arm64-Readme.md
ccache_eviction_age: 7d
cmake_generator: Ninja
make_package: 1
override_target: 14
package_suffix: "-macOS14"
qt_version: 6.11.*
qt_modules: qtimageformats qtmultimedia qtshadertools qtwebsockets
soc: Apple
type: Release
use_ccache: 1
xcode: "26.6"
- os: macOS
target: 15
runner: macos-26 # https://github.com/actions/runner-images/blob/main/images/macos/macos-26-arm64-Readme.md
ccache_eviction_age: 7d
cmake_generator: Ninja
make_package: 1
override_target: 15
package_suffix: "-macOS15"
qt_version: 6.11.*
qt_modules: qtimageformats qtmultimedia qtshadertools qtwebsockets
soc: Apple
type: Release
use_ccache: 1
xcode: "26.6"
- os: macOS
target: 26
runner: macos-26 # https://github.com/actions/runner-images/blob/main/images/macos/macos-26-arm64-Readme.md
ccache_eviction_age: 7d
cmake_generator: Ninja
qt_version: 6.11.*
qt_modules: qtimageformats qtmultimedia qtshadertools qtwebsockets
soc: Apple
type: Debug
use_ccache: 1
xcode: "26.6"
- os: Windows
target: 10
runner: windows-2025 # https://github.com/actions/runner-images/blob/main/images/windows/Windows2025-VS2026-Readme.md
cmake_generator: "Visual Studio 18 2026"
cmake_generator_platform: x64
make_package: 1
package_suffix: "-Win10"
qt_version: 6.11.*
qt_modules: qtimageformats qtmultimedia qtshadertools qtwebsockets
type: Release
name: ${{ matrix.os }} ${{ matrix.target }}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
needs: configure
runs-on: ${{ matrix.runner }}
timeout-minutes: 100
env:
CCACHE_DIR: ${{ github.workspace }}/.cache/
CCACHE_SIZE: 600M # space of all repo is 10Gi: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
steps:
- name: "Checkout"
uses: actions/checkout@v7
with:
submodules: recursive
- name: "[Windows] Add msbuild to PATH"
if: matrix.os == 'Windows'
uses: microsoft/setup-msbuild@v3
with:
msbuild-architecture: x64
- name: "[macOS] Setup ccache"
if: matrix.os == 'macOS' && matrix.use_ccache == 1
run: brew install ccache
- name: "[macOS] Restore compiler cache (ccache)"
if: matrix.os == 'macOS' && matrix.use_ccache == 1
id: restore_ccache
uses: actions/cache/restore@v6
with:
key: ccache-${{ matrix.runner }}_${{ matrix.override_target }}-Xcode${{ matrix.xcode }}
path: ${{ env.CCACHE_DIR }}
- name: "[macOS] Install aqtinstall"
if: matrix.os == 'macOS'
run: pipx install aqtinstall
# Resolve given wildcard versions (e.g. Qt 6.6.*) to latest version via aqtinstall to avoid stale caches on new releases
- name: "[macOS] Resolve latest Qt from ${{ matrix.qt_version }} input"
if: matrix.os == 'macOS'
env:
QT_VERSION: ${{ matrix.qt_version }}
id: resolve_qt_version
shell: bash
run: .ci/resolve_latest_aqt_qt_version.sh "$QT_VERSION"
- name: "[macOS] Restore thin Qt ${{ steps.resolve_qt_version.outputs.version }}"
if: matrix.os == 'macOS'
id: restore_qt
uses: actions/cache/restore@v6
with:
key: Qt-${{ steps.resolve_qt_version.outputs.version }}-macOS-${{ matrix.soc }}-${{ matrix.qt_modules }}-thin
path: ${{ github.workspace }}/Qt
# Using jurplel/install-qt-action to install Qt without using brew
# Qt build using vcpkg either just fails or takes too long to build
- name: "[macOS] Install fat Qt ${{ matrix.qt_version }}"
if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true'
uses: jurplel/install-qt-action@v4
with:
cache: false
# cache-key-prefix: Qt
dir: ${{ github.workspace }} # thinning script depends on this location
modules: ${{ matrix.qt_modules }}
version: ${{ matrix.qt_version }}
- name: "[macOS] Create thin Qt libraries"
if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true'
run: .ci/thin_macos_qtlib.sh
- name: "[macOS] Cache thin Qt libraries"
if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true' && github.ref == 'refs/heads/master'
uses: actions/cache/save@v6
with:
key: ${{ steps.restore_qt.outputs.cache-primary-key }}
path: ${{ github.workspace }}/Qt
- name: "[Windows] Install Qt ${{ matrix.qt_version }}"
if: matrix.os == 'Windows'
uses: jurplel/install-qt-action@v4
with:
# Qt 6.11.0 only works with aqtinstall directly from git until aqtinstall 3.4 is released
aqtsource: git+https://github.com/miurahr/aqtinstall.git
cache: ${{ github.ref == 'refs/heads/master' }}
cache-key-prefix: Qt
modules: ${{ matrix.qt_modules }}
version: ${{ matrix.qt_version }}
- name: "[Windows] Install NSIS"
if: matrix.os == 'Windows'
shell: bash
run: choco install nsis
- name: "Setup vcpkg cache"
id: vcpkg-cache
uses: TAServers/vcpkg-cache@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
# Uses environment variables, see compile.sh for more details
- name: "Build Cockatrice"
id: build
shell: bash
env:
BUILDTYPE: '${{ matrix.type }}'
CCACHE_EVICTION_AGE: ${{ matrix.ccache_eviction_age }}
CMAKE_GENERATOR: ${{ matrix.cmake_generator }}
CMAKE_GENERATOR_PLATFORM: ${{ matrix.cmake_generator_platform }}
DEVELOPER_DIR: '/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer'
GITHUB_TOKEN: ${{ github.token }} # needed for vcpkg dependency graph updates, see VCPKG_FEATURE_FLAGS
MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
MAKE_PACKAGE: '${{ matrix.make_package }}'
PACKAGE_SUFFIX: '${{ matrix.package_suffix }}'
TARGET_MACOS_VERSION: ${{ matrix.override_target }}
USE_CCACHE: ${{ matrix.use_ccache }}
VCPKG_BINARY_SOURCES: "clear;files,${{ steps.vcpkg-cache.outputs.path }},${{ case(github.ref == 'refs/heads/master', 'readwrite', 'read') }}"
VCPKG_DISABLE_METRICS: 1
VCPKG_FEATURE_FLAGS: dependencygraph
run: .ci/compile.sh --server --test --vcpkg
# CPack reports a failed makensis run as "Problem running NSIS command"
# plus a path to NSISOutput.log, and that log lives only on the runner, so
# the actual compile error is otherwise nowhere in the workflow output.
# Dump it, or a broken installer script is undiagnosable from CI alone.
# shell: bash matters here - the default pwsh resolves `find` to Windows'
# text-search find.exe, which errors instead of searching.
- name: "Dump NSIS compile log"
if: failure() && matrix.os == 'Windows'
shell: bash
run: find . -name NSISOutput.log -exec cat {} +
# Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342
- name: "[macOS] Delete remote compiler cache (ccache)"
if: matrix.os == 'macOS' && matrix.use_ccache == 1 && github.ref == 'refs/heads/master' && steps.restore_ccache.outputs.cache-hit
continue-on-error: true
env:
CACHE_PRIMARY_KEY: ${{ steps.restore_ccache.outputs.cache-primary-key }}
GH_TOKEN: ${{ github.token }}
run: |
if gh cache delete --repo "$GITHUB_REPOSITORY" "$CACHE_PRIMARY_KEY"; then
echo "Cache deleted successfully"
fi
- name: "[macOS] Cache updated compiler cache (ccache)"
if: matrix.os == 'macOS' && matrix.use_ccache == 1 && github.ref == 'refs/heads/master'
uses: actions/cache/save@v6
with:
key: ${{ steps.restore_ccache.outputs.cache-primary-key }}
path: ${{ env.CCACHE_DIR }}
- name: "[macOS] Sign app bundle"
if: matrix.os == 'macOS' && matrix.make_package && needs.configure.outputs.tag != null
id: sign_macos
env:
BUILD_PATH: ${{ steps.build.outputs.path }}
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
run: |
if [[ -n "$MACOS_CERTIFICATE_NAME" ]]
then
security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
/usr/bin/codesign --sign="$MACOS_CERTIFICATE_NAME" --entitlements=".ci/macos.entitlements" --options=runtime --force --deep --timestamp --verbose "$BUILD_PATH"
fi
- name: "[macOS] Notarize app bundle"
if: matrix.os == 'macOS' && steps.sign_macos.outcome == 'success'
env:
BUILD_PATH: ${{ steps.build.outputs.path }}
MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}
MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }}
MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}
run: |
if [[ -n "$MACOS_NOTARIZATION_APPLE_ID" ]]
then
# Store the notarization credentials so that we can prevent a UI password dialog from blocking the CI
echo "Create keychain profile"
xcrun notarytool store-credentials "notarytool-profile" --apple-id "$MACOS_NOTARIZATION_APPLE_ID" --team-id "$MACOS_NOTARIZATION_TEAM_ID" --password "$MACOS_NOTARIZATION_PWD"
# We can't notarize an app bundle directly, but we need to compress it as an archive.
# Therefore, we create a zip file containing our app bundle, so that we can send it to the
# notarization service
echo "Creating temp notarization archive"
ditto -c -k --keepParent "$BUILD_PATH" "notarization.zip"
# Here we send the notarization request to the Apple's Notarization service, waiting for the result.
# This typically takes a few seconds inside a CI environment, but it might take more depending on the App
# characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if
# you're curious
echo "Notarize app"
xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait
# Finally, we need to "attach the staple" to our executable, which will allow our app to be
# validated by macOS even when an internet connection is not available.
echo "Attach staple"
xcrun stapler staple "$BUILD_PATH"
fi
- name: "Upload artifact"
if: matrix.make_package
id: upload_artifact
uses: actions/upload-artifact@v7
with:
archive: false
if-no-files-found: error
path: ${{ steps.build.outputs.path }}
- name: "[Windows] Upload PDBs (Program Databases)"
if: matrix.os == 'Windows' && github.ref_type != 'tag'
uses: actions/upload-artifact@v7
with:
if-no-files-found: error
name: ${{ steps.build.outputs.name }}-PDBs
path: |
build/cockatrice/Release/*.pdb
build/oracle/Release/*.pdb
build/servatrice/Release/*.pdb
- name: "Upload to release"
if: needs.configure.outputs.tag != null && matrix.make_package == '1'
id: upload_release
shell: bash
env:
asset_name: ${{ steps.build.outputs.fullname }}
asset_path: ${{ steps.build.outputs.path }}
GH_TOKEN: ${{ github.token }}
tag_name: ${{ needs.configure.outputs.tag }}
run: gh release upload "$tag_name" "$asset_path#$asset_name"
- name: "Attest binary provenance"
if: steps.upload_release.outcome == 'success'
id: attestation
uses: actions/attest@v4
with:
show-summary: false
subject-path: ${{ steps.build.outputs.path }}
- name: "Verify binary attestation"
if: steps.attestation.outcome == 'success'
shell: bash
env:
BUILD_PATH: ${{ steps.build.outputs.path }}
GH_TOKEN: ${{ github.token }}
run: gh attestation verify "$BUILD_PATH" --repo Cockatrice/Cockatrice