Files

237 lines
11 KiB
YAML

name: Build and release
on:
push:
branches: ['**']
tags: ['v*']
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: build-${{ github.ref }}
cancel-in-progress: false
jobs:
metadata:
runs-on: ubuntu-latest
outputs:
app_version: ${{ steps.release.outputs.app_version }}
release_tag: ${{ steps.release.outputs.release_tag }}
publish: ${{ steps.release.outputs.publish }}
prerelease: ${{ steps.release.outputs.prerelease }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.12'
- name: Validate release contracts
run: bash scripts/tests/run-release-tests.sh
- name: Resolve release metadata
id: release
run: python3 scripts/release/metadata.py --github-output "$GITHUB_OUTPUT"
macos:
needs: metadata
runs-on: macos-15
env:
MACOSX_DEPLOYMENT_TARGET: '11.0'
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Verify signing identity before compiling
if: github.event_name != 'pull_request' && github.repository == 'ZASENJC/ttree'
env:
MACOS_COMMUNITY_CERTIFICATE: ${{ secrets.MACOS_COMMUNITY_CERTIFICATE }}
MACOS_COMMUNITY_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_COMMUNITY_CERTIFICATE_PASSWORD }}
run: scripts/macos/preflight.sh
- uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
with:
targets: aarch64-apple-darwin,x86_64-apple-darwin
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.12'
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22'
- name: Prepare Metal compiler
run: xcrun -f metal >/dev/null 2>&1 || xcodebuild -downloadComponent MetalToolchain
- uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
id: cargo-cache
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: macos-universal-${{ hashFiles('Cargo.lock', 'Cargo.toml', 'apps/**/Cargo.toml') }}
restore-keys: macos-universal-
- name: Check and test shared libraries and native apps
run: |
cargo check --workspace --locked
cargo test --workspace --locked
- name: Build universal app without signing material
id: build
run: scripts/macos/package.sh universal-apple-darwin "$GITHUB_RUN_NUMBER" --build-only
- name: Sign and verify distributable macOS assets
if: github.event_name != 'pull_request' && github.repository == 'ZASENJC/ttree'
env:
MACOS_COMMUNITY_CERTIFICATE: ${{ secrets.MACOS_COMMUNITY_CERTIFICATE }}
MACOS_COMMUNITY_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_COMMUNITY_CERTIFICATE_PASSWORD }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
RELEASE_TAG: ${{ needs.metadata.outputs.release_tag }}
APP_VERSION: ${{ needs.metadata.outputs.app_version }}
run: |
brew list minisign >/dev/null 2>&1 || brew install minisign
scripts/macos/package.sh universal-apple-darwin "$GITHUB_RUN_NUMBER" --package-only
scripts/release/generate-manifest.sh dist/macos "$RELEASE_TAG" "$GITHUB_REPOSITORY"
scripts/release/verify-macos-assets.sh dist/macos "$RELEASE_TAG" "$GITHUB_REPOSITORY" "$APP_VERSION"
python3 scripts/release/checksums.py dist/macos --write macos.sha256
- name: Upload signed macOS assets
if: github.event_name != 'pull_request' && github.repository == 'ZASENJC/ttree'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: macos-${{ github.run_id }}-${{ github.run_attempt }}
path: dist/macos/
if-no-files-found: error
retention-days: 7
compression-level: 0
- name: Preserve compiled Mac dependencies after packaging failures
if: always() && steps.build.outcome == 'success' && steps.cargo-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ steps.cargo-cache.outputs.cache-primary-key }}
windows:
needs: metadata
runs-on: windows-2022
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
with:
targets: x86_64-pc-windows-msvc
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.12'
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22'
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: windows-x64-${{ hashFiles('Cargo.lock', 'Cargo.toml', 'apps/**/Cargo.toml') }}
restore-keys: windows-x64-
- name: Check and test shared libraries and native apps
run: |
cargo check --workspace --locked
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
cargo test --workspace --locked
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: Build opt-in updater verification executable without signing material
if: github.event_name != 'pull_request' && github.repository == 'ZASENJC/ttree'
run: |
cargo build --locked --release -p ttree-windows --features updater-smoke --target x86_64-pc-windows-msvc
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
New-Item -ItemType Directory -Force target/updater-probe | Out-Null
Copy-Item target/x86_64-pc-windows-msvc/release/ttree-windows.exe target/updater-probe/TTREE.exe
- name: Build and validate portable ZIP
run: ./scripts/windows/package.ps1
- name: Launch packaged Windows app
run: ./scripts/windows/smoke.ps1 -Exe target/package/windows/TTREE/TTREE.exe -Output target/windows-smoke
- name: Sign local upgrade fixture after compilation
if: github.event_name != 'pull_request' && github.repository == 'ZASENJC/ttree'
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
bash scripts/release/validate-secrets.sh updater
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
New-Item -ItemType Directory -Force target/updater-source | Out-Null
Copy-Item dist/windows/*.zip target/updater-source/
python scripts/release/checksums.py target/updater-source --write SHA256SUMS
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$env:TAURI_PRIVATE_KEY = $env:TAURI_SIGNING_PRIVATE_KEY
$env:TAURI_PRIVATE_KEY_PASSWORD = $env:TAURI_SIGNING_PRIVATE_KEY_PASSWORD
Copy-Item target/updater-source/SHA256SUMS target/updater-checksums.txt
npx --yes --package @tauri-apps/cli@2.8.4 tauri signer sign target/updater-checksums.txt | Out-Null
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Copy-Item target/updater-checksums.txt.sig target/updater-source/SHA256SUMS.sig
Remove-Item target/updater-checksums.txt, target/updater-checksums.txt.sig
- name: Verify real Windows upgrade and restart
if: github.event_name != 'pull_request' && github.repository == 'ZASENJC/ttree'
run: ./scripts/windows/upgrade-smoke.ps1 -ProbeExe target/updater-probe/TTREE.exe -ReleaseDirectory target/updater-source -ExpectedExe target/package/windows/TTREE/TTREE.exe -Version '${{ needs.metadata.outputs.app_version }}' -Output target/windows-smoke
- name: Record Windows checksums
run: python scripts/release/checksums.py dist/windows --write windows.sha256
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: windows-${{ github.run_id }}-${{ github.run_attempt }}
path: dist/windows/
if-no-files-found: error
retention-days: 7
compression-level: 0
- name: Upload Windows smoke evidence
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: windows-smoke-${{ github.run_id }}-${{ github.run_attempt }}
path: target/windows-smoke/
if-no-files-found: warn
retention-days: 7
publish:
needs: [metadata, macos, windows]
if: needs.metadata.outputs.publish == 'true' && github.repository == 'ZASENJC/ttree'
runs-on: ubuntu-latest
permissions:
contents: write
env:
APP_VERSION: ${{ needs.metadata.outputs.app_version }}
RELEASE_TAG: ${{ needs.metadata.outputs.release_tag }}
PRERELEASE: ${{ needs.metadata.outputs.prerelease }}
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.12'
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22'
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: macos-${{ github.run_id }}-${{ github.run_attempt }}
path: dist/release
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: windows-${{ github.run_id }}-${{ github.run_attempt }}
path: dist/release
- name: Verify transferred assets and updater signature
env:
VERIFY_MACOS_CONTAINERS: '0'
run: |
sudo apt-get update -qq
sudo apt-get install -y minisign
python3 scripts/release/checksums.py dist/release --check macos.sha256 --check windows.sha256
scripts/release/verify-macos-assets.sh dist/release "$RELEASE_TAG" "$GITHUB_REPOSITORY" "$APP_VERSION"
python3 scripts/release/verify-assets.py dist/release "$APP_VERSION"
rm dist/release/macos.sha256 dist/release/windows.sha256
python3 scripts/release/checksums.py dist/release --write SHA256SUMS
- name: Sign cross-platform update checksums
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: scripts/release/sign-checksums.sh dist/release
- name: Generate release notes and publish verified draft
run: scripts/release/publish.sh