Files
OrcaSlicer/.github/workflows/build_orca.yml
T
SoftFever 948e6637fb Sign Windows release builds with the SignPath release certificate
Release-branch builds sign every unsigned executable, DLL and Python module in the x64 and arm64 portable bundles and installers, after approval in SignPath. The binaries and installer now carry OrcaSlicer version information.
2026-10-19 00:32:44 +08:00

896 lines
47 KiB
YAML

on:
workflow_call:
inputs:
cache-key:
required: false
type: string
cache-path:
required: false
type: string
os:
required: true
type: string
arch:
required: false
type: string
compiler:
required: false
type: string
default: msvc
macos-combine-only:
required: false
type: boolean
default: false
jobs:
build_orca:
name: Build OrcaSlicer
runs-on: ${{ inputs.os }}
env:
date:
ver:
ver_pure:
ubuntu-ver: '2404'
ubuntu-ver-str: '_Ubuntu2404'
ORCA_UPDATER_SIG_KEY: ${{ secrets.ORCA_UPDATER_SIG_KEY }}
NSIS_VERSION: '3.13'
NSIS_SHA256: BA63DFFC4410EE89193E1CB5A41989991BD77C61068DA17E3156D136B7B0B3D8
steps:
- name: Checkout
uses: actions/checkout@v7
with:
lfs: 'false'
- name: load cached deps
if: ${{ !(runner.os == 'macOS' && inputs.macos-combine-only) }}
uses: actions/cache@v6
with:
path: ${{ github.workspace }}/${{ inputs.cache-path }}
key: ${{ inputs.cache-key }}
fail-on-cache-miss: true
- uses: lukka/get-cmake@latest
with:
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
useLocalCache: true # <--= Use the local cache (default is 'false').
useCloudCache: true
# Compiler cache. Pushes save it, so main keeps it warm; pull requests
# restore it and discard what they compiled. Objects are keyed on the
# preprocessed source, the compiler and the flags, so a leg only ever
# hits its own entries. A failed install costs the caching, not the build.
- name: Name the compiler cache leg
if: ${{ !inputs.macos-combine-only }}
shell: bash
run: |
leg="${{ runner.os }}-${{ inputs.arch || 'amd64' }}${{ runner.os == 'Windows' && format('-{0}', inputs.compiler) || '' }}"
# clang-cl refuses a precompiled header from another cl.exe build and ccache
# does not hash that build, so each one gets its own cache. The build number
# is read from cl.exe itself; the toolset directory keeps its name across patches.
if [ "${{ runner.os }}" = Windows ]; then
vswhere='/c/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe'
toolset=$(tr -d '\r\n' < "$("$vswhere" -latest -products '*' -find 'VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt' | tr -d '\r')")
cl=$("$vswhere" -latest -products '*' -find 'VC\Tools\MSVC\'"$toolset"'\**\cl.exe' | tr -d '\r' | head -1)
leg="$leg-vc$("$cl" 2>&1 | grep -o -E 'Version [0-9.]+' | cut -d' ' -f2)"
fi
echo "CCACHE_LEG=$leg" >> "$GITHUB_ENV"
echo "CCACHE_ENTRY=ccache-$leg-${{ github.run_id }}-${{ github.run_attempt }}" >> "$GITHUB_ENV"
# The action only installs and configures ccache. Restore and save go
# through actions/cache with one path string, since the cache service
# only matches entries saved under the identical path and the action
# spells it differently on Windows.
- name: Compiler cache
id: ccache
if: ${{ !inputs.macos-combine-only }}
continue-on-error: true
uses: hendrikmuhs/ccache-action@v1.2.24
with:
key: ${{ env.CCACHE_LEG }}
max-size: 3G
restore: false
save: false
# ccache -s runs as its own step; no summary table per job.
job-summary: ''
- name: Restore compiler cache
id: ccache_restore
if: ${{ steps.ccache.outcome == 'success' }}
uses: actions/cache/restore@v6
with:
path: ${{ github.workspace }}/.ccache
key: ${{ env.CCACHE_ENTRY }}
restore-keys: ccache-${{ env.CCACHE_LEG }}-
- name: Enable compiler cache
if: ${{ steps.ccache.outcome == 'success' }}
shell: bash
run: |
echo "CMAKE_C_COMPILER_LAUNCHER=ccache" >> "$GITHUB_ENV"
echo "CMAKE_CXX_COMPILER_LAUNCHER=ccache" >> "$GITHUB_ENV"
# Headers a fresh checkout has just written, the few files that
# use __DATE__ or __TIME__, and the precompiled header, whose
# macros ccache cannot see.
echo "CCACHE_SLOPPINESS=pch_defines,time_macros,include_file_mtime,include_file_ctime" >> "$GITHUB_ENV"
# Hash the includes the compiler reports instead of preprocessing
# every miss before compiling it.
echo "CCACHE_DEPEND=1" >> "$GITHUB_ENV"
# The restored directory carries the previous run's counters.
ccache -z
- name: Get the version and date on Ubuntu and macOS
if: runner.os != 'Windows'
run: |
ver_pure=$(grep 'set(SoftFever_VERSION' version.inc | cut -d '"' -f2)
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
ver="PR-${{ github.event.number }}"
git_commit_hash="${{ github.event.pull_request.head.sha }}"
else
ver=V$ver_pure
git_commit_hash=""
fi
echo "ver=$ver" >> $GITHUB_ENV
echo "ver_pure=$ver_pure" >> $GITHUB_ENV
echo "date=$(date +'%Y%m%d')" >> $GITHUB_ENV
echo "git_commit_hash=$git_commit_hash" >> $GITHUB_ENV
# Per-arch Linux AppImage naming: amd64 keeps the historical unsuffixed
# name (arch_suffix empty). Unused on macOS/Windows.
if [ '${{ inputs.arch }}' = 'aarch64' ]; then
echo "arch_suffix=_aarch64" >> $GITHUB_ENV
fi
shell: bash
- name: Get the version and date on Windows
if: runner.os == 'Windows'
run: |
$date = Get-Date -Format 'yyyyMMdd'
$ref = "${{ github.ref }}"
$eventName = "${{ github.event_name }}"
$prNumber = "${{ github.event.number }}"
if ($eventName -eq 'pull_request') {
$ver = "PR" + $prNumber
$git_commit_hash = "${{ github.event.pull_request.head.sha }}"
} else {
$versionContent = Get-Content version.inc -Raw
if ($versionContent -match 'set\(SoftFever_VERSION "(.*?)"\)') {
$ver = $matches[1]
}
$ver = "V$ver"
$git_commit_hash = ""
}
echo "ver=$ver" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
echo "date=$date" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
echo "git_commit_hash=$git_commit_hash" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
echo "date: ${{ env.date }} version: ${{ env.ver }}"
shell: pwsh
# Mac
- name: Install tools mac
if: runner.os == 'macOS' && !inputs.macos-combine-only
run: |
if [ -z "${{ vars.SELF_HOSTED }}" ]; then
brew install libtool
brew list
fi
mkdir -p ${{ github.workspace }}/deps/build/${{ inputs.arch }}
- name: Free disk space
if: runner.os == 'macOS' && !inputs.macos-combine-only && !vars.SELF_HOSTED
run: |
df -hI /
# Deleting the unused Xcodes takes minutes, so only do it when the
# build might not fit. A cold build uses under 4 GiB after this step.
avail_gib=$(df -g / | awk 'NR == 2 { print $4 }')
if [ "$avail_gib" -ge 15 ]; then
echo "$avail_gib GiB free, enough for the build"
exit 0
fi
# Keep only the selected Xcode
xcode=$(basename "$(cd "$(xcode-select -p)/../.." && pwd -P)")
sudo find /Applications -maxdepth 1 -type d -name "Xcode_*.app" ! -name "$xcode" -exec rm -rf {} +
sudo rm -rf ~/Library/Developer/CoreSimulator/Caches/*
df -hI /
- name: Build slicer mac
if: runner.os == 'macOS' && !inputs.macos-combine-only
working-directory: ${{ github.workspace }}
# arm64 only: build the tests here; the unit_tests_macos job runs them.
env:
ORCA_TESTS_BUILD_ONLY: ${{ inputs.arch == 'arm64' && '1' || '' }}
run: |
./build_release_macos.sh -s -n -x ${{ !vars.SELF_HOSTED && '-j 3' || '' }} -a ${{ inputs.arch }} -t 10.15 ${{ inputs.arch == 'arm64' && '-T' || '' }}
- name: Pack unit tests mac
if: runner.os == 'macOS' && !inputs.macos-combine-only && inputs.arch == 'arm64'
working-directory: ${{ github.workspace }}
run: tar -cvf build_tests.tar build/arm64/tests
- name: Upload Test Artifact mac
if: runner.os == 'macOS' && !inputs.macos-combine-only && inputs.arch == 'arm64'
uses: actions/upload-artifact@v7
with:
name: ${{ github.sha }}-tests-macos-arm64
overwrite: true
path: build_tests.tar
retention-days: 5
if-no-files-found: error
- name: Build system preset cache (macOS)
if: runner.os == 'macOS' && !inputs.macos-combine-only
working-directory: ${{ github.workspace }}
shell: bash
# The bundle was already packed from resources/, so the caches have to be
# installed into it here; the source tree keeps its JSONs for later jobs.
run: ./scripts/build_preset_cache.sh -b build/${{ inputs.arch }} build/${{ inputs.arch }}/OrcaSlicer/OrcaSlicer.app/Contents/Resources/profiles
- name: Pack macOS app bundle ${{ inputs.arch }}
if: runner.os == 'macOS' && !inputs.macos-combine-only
working-directory: ${{ github.workspace }}
run: |
tar -czvf OrcaSlicer_Mac_bundle_${{ inputs.arch }}_${{ github.sha }}.tar.gz -C build/${{ inputs.arch }} OrcaSlicer
- name: Upload macOS app bundle ${{ inputs.arch }}
if: runner.os == 'macOS' && !inputs.macos-combine-only
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_Mac_bundle_${{ inputs.arch }}_${{ github.sha }}
path: ${{ github.workspace }}/OrcaSlicer_Mac_bundle_${{ inputs.arch }}_${{ github.sha }}.tar.gz
- name: Download macOS app bundles
if: runner.os == 'macOS' && inputs.macos-combine-only
uses: actions/download-artifact@v8
with:
pattern: OrcaSlicer_Mac_bundle_*_${{ github.sha }}
path: ${{ github.workspace }}/mac_bundles
- name: Extract macOS app bundles
if: runner.os == 'macOS' && inputs.macos-combine-only
working-directory: ${{ github.workspace }}
run: |
mkdir -p build/arm64 build/x86_64
arm_bundle=$(find "${{ github.workspace }}/mac_bundles/OrcaSlicer_Mac_bundle_arm64_${{ github.sha }}" -name '*.tar.gz' -print -quit)
x86_bundle=$(find "${{ github.workspace }}/mac_bundles/OrcaSlicer_Mac_bundle_x86_64_${{ github.sha }}" -name '*.tar.gz' -print -quit)
tar -xzvf "$arm_bundle" -C "${{ github.workspace }}/build/arm64"
tar -xzvf "$x86_bundle" -C "${{ github.workspace }}/build/x86_64"
- name: Build universal mac app bundle
if: runner.os == 'macOS' && inputs.macos-combine-only
working-directory: ${{ github.workspace }}
run: |
./build_release_macos.sh -u -x ${{ !vars.SELF_HOSTED && '-j 3' || '' }} -a universal -t 10.15
# Thanks to RaySajuuk, it's working now
- name: Sign app and notary
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'macOS' && inputs.macos-combine-only
timeout-minutes: 30
working-directory: ${{ github.workspace }}
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
CERTIFICATE_ID: ${{ secrets.MACOS_CERTIFICATE_ID }}
run: |
# Load the `retry` helper (retries flaky commands such as `hdiutil create`).
source ${{ github.workspace }}/scripts/retry.sh
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode --output $CERTIFICATE_PATH
security create-keychain -p $KEYCHAIN_PASSWORD $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p $KEYCHAIN_PASSWORD $KEYCHAIN_PATH
# Set the temporary keychain as the default to prevent codesign from accessing the locked login keychain
security default-keychain -s "$KEYCHAIN_PATH"
security import $CERTIFICATE_PATH -P $P12_PASSWORD -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $P12_PASSWORD $KEYCHAIN_PATH
# codesign --deep is deprecated and cannot sign the bundled Python
# runtime (see relocate_python_runtime in build_release_macos.sh).
# Sign every nested Mach-O in the bundle explicitly -- notarization
# requires the hardened-runtime signature on each -- then seal the
# bundle itself last, and verify so coverage gaps fail at CI time
# rather than at notarization.
ENTITLEMENTS=${{ github.workspace }}/scripts/disable_validation.entitlements
sign() {
codesign --force --options runtime --timestamp --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE_ID" "$@"
}
sign_app() {
local app="$1"
local machos=()
# file is batched via xargs (per-file it costs minutes); universal
# binaries also print per-architecture lines, dropped by grep -v.
while IFS= read -r f; do machos+=("$f"); done < <(
find "$app" -type f -print0 |
xargs -0 file --no-pad --mime-type -- |
grep -v ' (for architecture ' |
grep ': application/x-mach-binary$' |
sed 's|: application/x-mach-binary$||'
)
if [ "${#machos[@]}" -eq 0 ]; then
echo "ERROR: no Mach-O files found in $app -- detection is broken" >&2
exit 1
fi
echo "signing ${#machos[@]} Mach-O files in $app"
sign "${machos[@]}"
sign --verbose "$app"
codesign --verify --deep --strict --verbose=2 "$app"
}
sign_app "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app"
# Sign OrcaSlicer_profile_validator.app if it exists
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
sign_app "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app"
fi
# Create main OrcaSlicer DMG without the profile validator helper
mkdir -p ${{ github.workspace }}/build/universal/OrcaSlicer_dmg
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/*
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/Applications
retry hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
codesign --deep --force --verbose --options runtime --timestamp --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE_ID" OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
# Create separate OrcaSlicer_profile_validator DMG if the app exists
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
mkdir -p ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/*
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/Applications
retry hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
codesign --deep --force --verbose --options runtime --timestamp --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE_ID" OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
fi
# Notarize main DMG
xcrun notarytool submit "OrcaSlicer_Mac_universal_${{ env.ver }}.dmg" --apple-id "${{ secrets.APPLE_DEV_ACCOUNT }}" --team-id "${{ secrets.TEAM_ID }}" --password "${{ secrets.APP_PWD }}" --wait
retry xcrun stapler staple OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
# Notarize profile validator DMG if it exists
if [ -f "OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg" ]; then
xcrun notarytool submit "OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg" --apple-id "${{ secrets.APPLE_DEV_ACCOUNT }}" --team-id "${{ secrets.TEAM_ID }}" --password "${{ secrets.APP_PWD }}" --wait
retry xcrun stapler staple OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
fi
- name: Create DMG without notary
if: github.ref != 'refs/heads/main' && runner.os == 'macOS' && inputs.macos-combine-only
working-directory: ${{ github.workspace }}
run: |
# Load the `retry` helper (retries flaky commands such as `hdiutil create`).
source ${{ github.workspace }}/scripts/retry.sh
mkdir -p ${{ github.workspace }}/build/universal/OrcaSlicer_dmg
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/*
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/Applications
retry hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
# Create separate OrcaSlicer_profile_validator DMG if the app exists
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
mkdir -p ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/*
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/Applications
retry hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
fi
# Delete the per-arch bundles only after signing/DMG creation succeeded, so a
# failed run keeps them available for a re-run instead of forcing a full rebuild.
- name: Delete intermediate per-arch artifacts
if: success() && runner.os == 'macOS' && inputs.macos-combine-only
uses: geekyeggo/delete-artifact@v6
with:
name: |
OrcaSlicer_Mac_bundle_arm64_${{ github.sha }}
OrcaSlicer_Mac_bundle_x86_64_${{ github.sha }}
- name: Upload artifacts mac
if: runner.os == 'macOS' && inputs.macos-combine-only
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_Mac_universal_${{ env.ver }}
path: ${{ github.workspace }}/OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
- name: Upload OrcaSlicer_profile_validator DMG mac
if: runner.os == 'macOS' && inputs.macos-combine-only && !vars.SELF_HOSTED
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_profile_validator_Mac_universal_DMG_${{ env.ver }}
path: ${{ github.workspace }}/OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
if-no-files-found: ignore
- name: Deploy Mac release
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && runner.os == 'macOS' && inputs.macos-combine-only && !vars.SELF_HOSTED
uses: ./.github/actions/deploy-nightly
with:
asset_path: ${{ github.workspace }}/OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
asset_name: OrcaSlicer_Mac_universal_nightly.dmg
# The DMG is only built when the validator app was.
- name: Deploy Mac OrcaSlicer_profile_validator DMG release
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && runner.os == 'macOS' && inputs.macos-combine-only && !vars.SELF_HOSTED && hashFiles(format('OrcaSlicer_profile_validator_Mac_universal_{0}.dmg', env.ver)) != ''
uses: ./.github/actions/deploy-nightly
with:
asset_path: ${{ github.workspace }}/OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
asset_name: OrcaSlicer_profile_validator_Mac_universal_nightly.dmg
# Windows
- name: Set Windows build variables
if: runner.os == 'Windows'
shell: pwsh
run: |
if ("${{ inputs.arch }}" -eq "arm64") {
"BUILD_DIR=build-arm64" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
"ARCH_SUFFIX=_arm64" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
} else {
"BUILD_DIR=build" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
"ARCH_SUFFIX=_x64" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
}
# Release branches ship binaries and installers signed through SignPath with the
# release certificate. Every signing request waits for a manual approval in
# SignPath, so main and pull request builds stay unsigned.
"SIGN_WINDOWS=${{ github.repository == 'OrcaSlicer/OrcaSlicer' && startsWith(github.ref, 'refs/heads/release/') && !vars.SELF_HOSTED }}" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
- name: setup MSVC
if: runner.os == 'Windows'
uses: microsoft/setup-msbuild@v3
# NSIS builds the installer. A pinned portable copy is kept in the Actions
# cache, so most runs never download it, and a cache miss fetches the
# release archive from SourceForge instead of going through the Chocolatey
# community feed, which hosted runners often cannot reach. Prepending it to
# PATH also makes cpack use it over the NSIS preinstalled on some images.
- name: Restore NSIS
id: nsis-cache
if: runner.os == 'Windows' && !vars.SELF_HOSTED
uses: actions/cache/restore@v6
with:
path: ${{ runner.temp }}/nsis
key: nsis-${{ env.NSIS_VERSION }}
- name: Install NSIS
if: runner.os == 'Windows' && !vars.SELF_HOSTED
shell: pwsh
run: |
dir "C:/Program Files (x86)/Windows Kits/10/Include"
$nsisDir = Join-Path $env:RUNNER_TEMP 'nsis'
$makensis = Join-Path $nsisDir 'makensis.exe'
if (-not (Test-Path $makensis)) {
$zip = Join-Path $env:RUNNER_TEMP "nsis-$env:NSIS_VERSION.zip"
curl.exe -fsSL --retry 5 --retry-all-errors --retry-delay 15 -o $zip "https://downloads.sourceforge.net/project/nsis/NSIS%203/$env:NSIS_VERSION/nsis-$env:NSIS_VERSION.zip"
if ($LASTEXITCODE -ne 0) { throw "NSIS $env:NSIS_VERSION download failed." }
if ((Get-FileHash $zip -Algorithm SHA256).Hash -ne $env:NSIS_SHA256) {
throw "NSIS $env:NSIS_VERSION archive does not match its pinned SHA256."
}
Expand-Archive $zip $env:RUNNER_TEMP
Move-Item (Join-Path $env:RUNNER_TEMP "nsis-$env:NSIS_VERSION") $nsisDir
}
& $makensis /VERSION
$nsisDir | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
- name: Save NSIS
if: runner.os == 'Windows' && !vars.SELF_HOSTED && steps.nsis-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: ${{ runner.temp }}/nsis
key: nsis-${{ env.NSIS_VERSION }}
- name: Build slicer Win
if: runner.os == 'Windows'
working-directory: ${{ github.workspace }}
# --tests builds the unit tests too; the unit_tests_windows_* jobs run them.
run: |
# cache-path is the install directory inside the deps build directory.
$deps = (Split-Path "${{ github.workspace }}/${{ inputs.cache-path }}").Replace('\', '/')
# -l compiles with Visual Studio's clang-cl and -x builds with Ninja; --msvc --msbuild is cl under the Visual Studio generator.
$flags = if ("${{ inputs.compiler }}" -eq "clang") { "-l", "-x" } else { "--msvc", "--msbuild" }
.\build_win.bat -s --tests -i --arch ${{ inputs.arch }} --build-dir $env:BUILD_DIR --deps-dir $deps @flags
shell: pwsh
- name: Build system preset cache (Windows)
if: runner.os == 'Windows'
shell: cmd
# Shipped into both the already-installed tree (portable zip, MSIX) and
# the checkout cpack re-installs from when it builds the NSIS installer.
run: scripts\build_preset_cache.bat --prune-source "%BUILD_DIR%" "resources\profiles" "%BUILD_DIR%\OrcaSlicer\resources\profiles"
- name: Pack unit tests Win
if: runner.os == 'Windows'
working-directory: ${{ github.workspace }}
shell: pwsh
run: tar -cvf build_tests.tar ${{ env.BUILD_DIR }}/tests
- name: Upload Test Artifact Win
if: runner.os == 'Windows'
uses: actions/upload-artifact@v7
with:
name: ${{ github.sha }}-tests-windows-${{ inputs.arch }}
overwrite: true
path: build_tests.tar
retention-days: 5
if-no-files-found: error
# Signs OrcaSlicer's own binaries and every bundled executable, DLL and Python
# extension module that ships without a signature; files their vendor signed, such
# as the Microsoft runtime, keep that signature. See signpath/README.md.
- name: Stage Windows binaries for SignPath
id: stage-windows-binaries
if: env.SIGN_WINDOWS == 'true'
shell: pwsh
run: |
$version = "${{ env.ver }}" -replace '^V', ''
./scripts/stage-for-signpath.ps1 -Bundle "${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer" -Destination "${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/unsigned-binaries" -ProductVersion $version
"version=$version" | Out-File -Append -FilePath $env:GITHUB_OUTPUT -Encoding utf8
- name: Upload unsigned Windows binaries for SignPath
id: upload-windows-binaries
if: env.SIGN_WINDOWS == 'true'
uses: actions/upload-artifact@v7
with:
name: signpath_windows_binaries${{ env.ARCH_SUFFIX }}_unsigned
path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/unsigned-binaries
retention-days: 1
if-no-files-found: error
# Waits until the request is approved in SignPath.
- name: Sign Windows binaries with SignPath
if: env.SIGN_WINDOWS == 'true'
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
project-slug: OrcaSlicer
signing-policy-slug: release-signing
artifact-configuration-slug: windows-binaries-v1
github-artifact-id: ${{ steps.upload-windows-binaries.outputs.artifact-id }}
parameters: |
version: "${{ steps.stage-windows-binaries.outputs.version }}"
wait-for-completion: true
wait-for-completion-timeout-in-seconds: 10800
output-artifact-directory: ${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/signed-binaries
- name: Verify Windows binary signatures
if: env.SIGN_WINDOWS == 'true'
shell: pwsh
run: ./scripts/verify-authenticode.ps1 -ArtifactDirectory '${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/signed-binaries'
- name: Replace Windows binaries with signed output
if: env.SIGN_WINDOWS == 'true'
shell: pwsh
run: ./scripts/restore-from-signpath.ps1 -SignedDirectory "${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/signed-binaries" -Bundle "${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer"
# NSIS is x86-only; it runs (and the installer it emits runs) under ARM64's
# x86 emulation, packaging the native arm64 payload from build-arm64.
# The installer packs the installed OrcaSlicer/ tree, which the portable zip and the
# MSIX are made from too, instead of re-running the install rules: those take
# CPython and uv from the dependency tree and the download cache, which keep the
# unsigned copies.
- name: Create installer Win
if: runner.os == 'Windows' && !vars.SELF_HOSTED
working-directory: ${{ github.workspace }}/${{ env.BUILD_DIR }}
run: |
$bundle = "${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer".Replace('\', '/')
cpack -G NSIS -D CPACK_INSTALL_CMAKE_PROJECTS= -D "CPACK_INSTALLED_DIRECTORIES=$bundle;."
# cpack stages what the installer packs under _CPack_Packages/<system>/NSIS/<package>;
# every executable and DLL there must carry a valid signature, ours or its vendor's.
- name: Verify every binary the installer packs is signed
if: env.SIGN_WINDOWS == 'true'
shell: pwsh
run: |
$payload = Get-Item "${{ github.workspace }}/${{ env.BUILD_DIR }}/_CPack_Packages/*/NSIS/OrcaSlicer_Windows_Installer_${{ env.ver }}${{ env.ARCH_SUFFIX }}"
./scripts/verify-authenticode.ps1 -ArtifactDirectory $payload.FullName
- name: Stage Windows installer for SignPath
if: env.SIGN_WINDOWS == 'true'
shell: pwsh
run: ./scripts/stage-for-signpath.ps1 -Path "${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer_Windows_Installer_${{ env.ver }}${{ env.ARCH_SUFFIX }}.exe" -Destination "${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/unsigned-installer" -ProductVersion "${{ steps.stage-windows-binaries.outputs.version }}"
- name: Upload unsigned Windows installer for SignPath
id: upload-windows-installer
if: env.SIGN_WINDOWS == 'true'
uses: actions/upload-artifact@v7
with:
name: signpath_windows_installer${{ env.ARCH_SUFFIX }}_unsigned
path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/unsigned-installer
retention-days: 1
if-no-files-found: error
- name: Sign Windows installer with SignPath
if: env.SIGN_WINDOWS == 'true'
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
project-slug: OrcaSlicer
signing-policy-slug: release-signing
artifact-configuration-slug: windows-installer-v1
github-artifact-id: ${{ steps.upload-windows-installer.outputs.artifact-id }}
parameters: |
version: "${{ steps.stage-windows-binaries.outputs.version }}"
wait-for-completion: true
wait-for-completion-timeout-in-seconds: 3600
output-artifact-directory: ${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/signed-installer
- name: Verify Windows installer signature
if: env.SIGN_WINDOWS == 'true'
shell: pwsh
run: ./scripts/verify-authenticode.ps1 -ArtifactDirectory '${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/signed-installer' -Files 'OrcaSlicer_Windows_Installer_${{ env.ver }}${{ env.ARCH_SUFFIX }}.exe'
- name: Replace Windows installer with signed output
if: env.SIGN_WINDOWS == 'true'
shell: pwsh
run: Copy-Item -LiteralPath "${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/signed-installer/OrcaSlicer_Windows_Installer_${{ env.ver }}${{ env.ARCH_SUFFIX }}.exe" -Destination "${{ github.workspace }}/${{ env.BUILD_DIR }}" -Force
- name: Pack app
if: runner.os == 'Windows'
working-directory: ${{ github.workspace }}/${{ env.BUILD_DIR }}
shell: pwsh
run: |
$zipPath = "OrcaSlicer_Windows_${{ env.ver }}${{ env.ARCH_SUFFIX }}_portable.zip"
Remove-Item -LiteralPath $zipPath -Force -ErrorAction SilentlyContinue
& "C:/Program Files/7-Zip/7z.exe" a -tzip $zipPath "${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer"
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
- name: Pack PDB
if: runner.os == 'Windows' && inputs.arch != 'arm64' && !vars.SELF_HOSTED
working-directory: ${{ github.workspace }}/${{ env.BUILD_DIR }}/src/Release
shell: cmd
run: '"C:/Program Files/7-Zip/7z.exe" a -m0=lzma2 -mx9 Debug_PDB_${{ env.ver }}_for_developers_only.7z *.pdb'
- name: Upload artifacts Win zip
if: runner.os == 'Windows'
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_Windows_${{ env.ver }}${{ env.ARCH_SUFFIX }}_portable
path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer
if-no-files-found: error
- name: Upload artifacts Win installer
if: runner.os == 'Windows' && !vars.SELF_HOSTED
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_Windows_${{ env.ver }}${{ env.ARCH_SUFFIX }}
path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer*.exe
- name: Upload artifacts Win PDB
if: runner.os == 'Windows' && inputs.arch != 'arm64' && !vars.SELF_HOSTED
uses: actions/upload-artifact@v7
with:
name: PDB
path: ${{ github.workspace }}/build/src/Release/Debug_PDB_${{ env.ver }}_for_developers_only.7z
- name: Upload OrcaSlicer_profile_validator Win
if: runner.os == 'Windows' && inputs.arch != 'arm64' && !vars.SELF_HOSTED
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_profile_validator_Windows_${{ env.ver }}
path: ${{ github.workspace }}/build/src/Release/OrcaSlicer_profile_validator.exe
- name: Deploy Windows release portable
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && runner.os == 'Windows' && !vars.SELF_HOSTED
uses: ./.github/actions/deploy-nightly
with:
asset_path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer_Windows_${{ env.ver }}${{ env.ARCH_SUFFIX }}_portable.zip
asset_name: OrcaSlicer_Windows${{ env.ARCH_SUFFIX }}_nightly_portable.zip
content_type: application/x-zip-compressed
- name: Deploy Windows release installer
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && runner.os == 'Windows' && !vars.SELF_HOSTED
uses: ./.github/actions/deploy-nightly
with:
asset_path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer_Windows_Installer_${{ env.ver }}${{ env.ARCH_SUFFIX }}.exe
asset_name: OrcaSlicer_Windows_Installer${{ env.ARCH_SUFFIX }}_nightly.exe
content_type: application/x-msdownload
- name: Deploy Windows OrcaSlicer_profile_validator release
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && runner.os == 'Windows' && inputs.arch != 'arm64' && !vars.SELF_HOSTED
uses: ./.github/actions/deploy-nightly
with:
asset_path: ${{ github.workspace }}/build/src/Release/OrcaSlicer_profile_validator.exe
asset_name: OrcaSlicer_profile_validator_Windows_nightly.exe
content_type: application/x-msdownload
- name: Build MSIX Store package Win
if: runner.os == 'Windows' && !vars.SELF_HOSTED
working-directory: ${{ github.workspace }}
shell: pwsh
run: |
./scripts/msix/build_msix.ps1 `
-InstallDir "${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer" `
-OutputPath "${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer_Windows_MSIX_${{ env.ver }}${{ env.ARCH_SUFFIX }}.msix" `
-Architecture "${{ inputs.arch }}" `
-IdentityName "${{ vars.ORCA_MSIX_IDENTITY_NAME || 'OrcaSlicer.OrcaSlicer' }}" `
-Publisher "${{ vars.ORCA_MSIX_PUBLISHER || 'CN=38F7EA55-C73B-4072-B3B2-C8E0EA15BB82' }}" `
-PublisherDisplayName "${{ vars.ORCA_MSIX_PUBLISHER_DISPLAY_NAME || 'OrcaSlicer' }}"
- name: Upload artifacts Win MSIX
if: runner.os == 'Windows' && !vars.SELF_HOSTED
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_Windows_MSIX_${{ env.ver }}${{ env.ARCH_SUFFIX }}
path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer_Windows_MSIX_${{ env.ver }}${{ env.ARCH_SUFFIX }}.msix
# Ubuntu
- name: Apt-Install Dependencies
if: runner.os == 'Linux' && !vars.SELF_HOSTED
uses: ./.github/actions/apt-install-deps
# Tests must built at the same time as the slicer;
# if you untangle them feel free to separate them here too
- name: Build slicer and tests
if: runner.os == 'Linux'
shell: bash
run: |
# Build + tar the unit tests (-t) on both Linux legs so each arch
# (x86_64 + aarch64) gets tested by its own unit_tests_linux_* job.
./build_linux.sh -istrlL
./scripts/check_appimage_libs.sh ./build/package ./build/package/bin/orca-slicer
appimage=./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage
mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage "$appimage"
chmod +x "$appimage"
tar -cvf build_tests.tar build/tests
# Use tar because upload-artifacts won't always preserve directory structure
# and doesn't preserve file permissions
- name: Upload Test Artifact
if: runner.os == 'Linux'
uses: actions/upload-artifact@v7
with:
name: ${{ github.sha }}-tests-linux-${{ inputs.arch == 'aarch64' && 'aarch64' || 'x86_64' }}
overwrite: true
path: build_tests.tar
retention-days: 5
if-no-files-found: error
- name: Build system preset cache (Linux)
if: runner.os == 'Linux'
shell: bash
run: |
# Both were packed from resources/ before the caches existed, so the
# AppImage is unpacked first and the caches shipped into it and into
# the package tree; the source tree keeps its JSONs for later steps.
appimage=$(find build -maxdepth 1 -name "OrcaSlicer_Linux_AppImage*.AppImage" | head -1)
chmod +x "$appimage"
"$appimage" --appimage-extract
./scripts/build_preset_cache.sh -b build build/package/resources/profiles squashfs-root/resources/profiles
appimagetool=$(find build -name "appimagetool.AppImage" | head -1)
ARCH=$(uname -m) "$appimagetool" --appimage-extract-and-run squashfs-root "$appimage"
rm -rf squashfs-root
# Ship the freshly-built validator so slice_check_linux (build_all.yml)
# can slice-sweep the shipped profiles with this PR's engine. Taken from
# the aarch64 leg so the sweep also exercises the arm build; x86_64 on
# SELF_HOSTED, which skips arm64.
- name: Upload profile validator (for slice check)
if: ${{ runner.os == 'Linux' && (vars.SELF_HOSTED && inputs.arch != 'aarch64' || !vars.SELF_HOSTED && inputs.arch == 'aarch64') }}
uses: actions/upload-artifact@v7
with:
name: ${{ github.sha }}-profile-validator-linux-${{ inputs.arch == 'aarch64' && 'aarch64' || 'x86_64' }}
overwrite: true
path: ./build/src/Release/OrcaSlicer_profile_validator
retention-days: 5
if-no-files-found: error
- name: Run external slicer regression tests
if: runner.os == 'Linux' && inputs.arch != 'aarch64'
timeout-minutes: 20
shell: bash
run: |
test_repo_dir="${{ runner.temp }}/orca-test-repo"
rm -rf "$test_repo_dir"
git clone --depth 1 https://github.com/OrcaSlicer/orca-test-repo.git "$test_repo_dir"
python3 "$test_repo_dir/run_test.py" "${{ github.workspace }}/build/package/bin/orca-slicer"
- name: Build orca_custom_preset_tests
if: github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64'
working-directory: ${{ github.workspace }}/build/src/Release
shell: bash
run: |
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -g 1
cd ${{ github.workspace }}/resources/profiles
zip -r orca_custom_preset_tests.zip user/
- name: Upload artifacts Ubuntu
if: ${{ ! env.ACT && runner.os == 'Linux' }}
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_Linux_ubuntu_${{ env.ubuntu-ver }}${{ env.arch_suffix }}_${{ env.ver }}
path: "./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage"
- name: Upload OrcaSlicer_profile_validator Ubuntu
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
path: './build/src/Release/OrcaSlicer_profile_validator'
# generate_system_cache is what scripts/build_preset_cache.sh bakes the
# <vendor>.opc caches with; it was already built by the "Build system
# preset cache (Linux)" step above. The .opc format is 64-bit
# little-endian native, i.e. identical across every platform Orca ships,
# so only the Linux binary is published.
- name: Upload generate_system_cache Ubuntu
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
uses: actions/upload-artifact@v7
with:
name: generate_system_cache_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
path: './build/src/dev-utils/Release/generate_system_cache'
- name: Deploy Ubuntu release
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
uses: ./.github/actions/deploy-nightly
with:
asset_path: ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage
asset_name: OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_nightly.AppImage
- name: Deploy Ubuntu release
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
run: |
git -c user.name="${GITHUB_ACTOR}" -c user.email="${GITHUB_ACTOR}@users.noreply.github.com" tag -f -a nightly-builds "${GITHUB_SHA}" -m nightly-builds
git push -f origin refs/tags/nightly-builds
- name: Deploy Ubuntu OrcaSlicer_profile_validator release
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
uses: ./.github/actions/deploy-nightly
with:
asset_path: ./build/src/Release/OrcaSlicer_profile_validator
asset_name: OrcaSlicer_profile_validator_Linux${{ env.ubuntu-ver-str }}_nightly
- name: Deploy Ubuntu generate_system_cache release
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
uses: ./.github/actions/deploy-nightly
with:
asset_path: ./build/src/dev-utils/Release/generate_system_cache
asset_name: generate_system_cache_Linux${{ env.ubuntu-ver-str }}_nightly
- name: Deploy orca_custom_preset_tests
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
uses: ./.github/actions/deploy-nightly
with:
asset_path: ${{ github.workspace }}/resources/profiles/orca_custom_preset_tests.zip
asset_name: orca_custom_preset_tests.zip
# The build has just touched everything it can use, so an object
# untouched for a week is dead, usually orphaned by a flag change.
- name: Compiler cache statistics
if: ${{ always() && steps.ccache.outcome == 'success' }}
shell: bash
run: |
ccache --evict-older-than 7d
ccache -s -v || ccache -s
# Entries are immutable, so the new one is saved first and the older
# ones for this leg on this ref are dropped afterwards: a failed save
# leaves the previous entry in place. A cancelled or failed build saves
# too, since what it compiled is still valid; a restore that did not
# finish does not, since the directory may be a truncated copy.
- name: Save compiler cache
id: ccache_save
if: ${{ always() && steps.ccache_restore.outcome == 'success' && github.event_name != 'pull_request' }}
uses: actions/cache/save@v6
with:
path: ${{ github.workspace }}/.ccache
key: ${{ env.CCACHE_ENTRY }}
- name: Drop older compiler cache entries
if: ${{ always() && steps.ccache_save.outcome == 'success' }}
# A read-only token (fork PRs) cannot delete; that only costs storage.
# Older means a lower run id, so two runs finishing close together keep
# the newer entry whichever of them cleans up last.
continue-on-error: true
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
# The save step reports success even when its tar failed, so keep
# the older entries unless the new one is listed.
if ! gh cache list --ref "$GITHUB_REF" --key "$CCACHE_ENTRY" --json key \
| jq -e --arg entry "$CCACHE_ENTRY" 'any(.[]; .key == $entry)' > /dev/null; then
echo "$CCACHE_ENTRY was not saved; keeping the older entries."
exit 0
fi
gh cache list --ref "$GITHUB_REF" --key "ccache-$CCACHE_LEG-" --limit 100 --json id,key \
| jq -r --arg prefix "ccache-$CCACHE_LEG-" --argjson run "$GITHUB_RUN_ID" \
'.[] | select((.key | ltrimstr($prefix) | split("-")[0] | tonumber?) < $run) | .id' \
| tr -d '\r' \
| while read -r id; do gh cache delete "$id"; done