Skip to content

Release v0.8.0 with NBER-CLI Desktop #7

Release v0.8.0 with NBER-CLI Desktop

Release v0.8.0 with NBER-CLI Desktop #7

Workflow file for this run

name: Desktop
on:
pull_request:
push:
branches:
- main
- master
tags:
- "v*"
workflow_dispatch:
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: "3.11"
- uses: astral-sh/setup-uv@v8
- uses: actions/setup-node@v6
with:
node-version: "20"
cache: npm
cache-dependency-path: desktop/package-lock.json
- name: Install Python dependencies
run: uv sync --dev --extra server
- name: Run Python lint
run: uv run ruff check .
- name: Run Python tests
run: uv run pytest
- name: Install desktop dependencies
working-directory: desktop
run: npm ci
- name: Run frontend lint
working-directory: desktop
run: npm run lint
- name: Run frontend tests
working-directory: desktop
run: npm run test
- name: Build frontend
working-directory: desktop
run: npm run build
build:
needs: check
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- os: macos-15
target: aarch64-apple-darwin
bundles: app,dmg
artifact-name: nber-cli-desktop-macos-arm64
release-platform: macos
- os: macos-15-intel
target: x86_64-apple-darwin
bundles: app,dmg
artifact-name: nber-cli-desktop-macos-x64
release-platform: macos
- os: windows-2022
target: x86_64-pc-windows-msvc
bundles: nsis
artifact-name: nber-cli-desktop-windows-x64
release-platform: windows
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: "3.11"
- uses: astral-sh/setup-uv@v8
- uses: actions/setup-node@v6
with:
node-version: "20"
cache: npm
cache-dependency-path: desktop/package-lock.json
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install Python dependencies
run: uv sync --dev --extra server --group desktop-build
- name: Install desktop dependencies
working-directory: desktop
run: npm ci
# Signing is opt-in while the project ships without paid certificates.
# Once certificates are funded, configure the secrets and set
# DESKTOP_REQUIRE_SIGNING=true to restore mandatory signing checks.
- name: Validate macOS release signing secrets
if: startsWith(github.ref, 'refs/tags/v') && runner.os == 'macOS' && vars.DESKTOP_REQUIRE_SIGNING == 'true'
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
shell: bash
run: uv run python scripts/validate-desktop-signing.py --platform macos --require-signed
- name: Validate Windows release signing secrets
if: startsWith(github.ref, 'refs/tags/v') && runner.os == 'Windows' && vars.DESKTOP_REQUIRE_SIGNING == 'true'
env:
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
shell: pwsh
run: uv run python scripts/validate-desktop-signing.py --platform windows --require-signed
- name: Build sidecar
run: uv run python scripts/build-sidecar.py --clean --target-triple ${{ matrix.target }}
- name: Import Apple Developer ID certificate
if: runner.os == 'macOS'
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
shell: bash
run: |
if [ -z "$APPLE_CERTIFICATE" ]; then
echo "APPLE_CERTIFICATE is not configured; building without macOS signing."
exit 0
fi
if [ -z "$KEYCHAIN_PASSWORD" ]; then
echo "KEYCHAIN_PASSWORD is required for macOS signing."
exit 1
fi
echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -t 3600 -u build.keychain
security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application" | head -n 1 || true)
if [ -z "$CERT_INFO" ]; then
echo "Developer ID Application certificate not found in keychain."
exit 1
fi
CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}')
echo "APPLE_SIGNING_IDENTITY=$CERT_ID" >> "$GITHUB_ENV"
- name: Import Windows code signing certificate
if: runner.os == 'Windows'
env:
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
shell: pwsh
run: |
if (-not $env:WINDOWS_CERTIFICATE) {
Write-Host "WINDOWS_CERTIFICATE is not configured; building without Windows signing."
exit 0
}
$certificatePath = "$env:RUNNER_TEMP\windows-certificate.pfx"
[IO.File]::WriteAllBytes($certificatePath, [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE))
$securePassword = ConvertTo-SecureString $env:WINDOWS_CERTIFICATE_PASSWORD -AsPlainText -Force
$certificate = Import-PfxCertificate -FilePath $certificatePath -CertStoreLocation Cert:\CurrentUser\My -Password $securePassword
if (-not $certificate.Thumbprint) {
throw "Imported Windows certificate did not expose a thumbprint."
}
"WINDOWS_CERTIFICATE_THUMBPRINT=$($certificate.Thumbprint)" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Prepare Tauri signing configuration
env:
APPLE_SIGNING_IDENTITY: ${{ env.APPLE_SIGNING_IDENTITY }}
APPLE_PROVIDER_SHORT_NAME: ${{ secrets.APPLE_PROVIDER_SHORT_NAME }}
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ env.WINDOWS_CERTIFICATE_THUMBPRINT }}
WINDOWS_DIGEST_ALGORITHM: ${{ secrets.WINDOWS_DIGEST_ALGORITHM }}
WINDOWS_TIMESTAMP_URL: ${{ secrets.WINDOWS_TIMESTAMP_URL }}
run: uv run python scripts/prepare-tauri-signing.py
- name: Build Tauri app
working-directory: desktop
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: npm run tauri build -- --target ${{ matrix.target }} --bundles ${{ matrix.bundles }}
- name: Normalize release artifact names
run: uv run python scripts/normalize-desktop-artifacts.py --platform ${{ matrix.release-platform }} --target-triple ${{ matrix.target }}
- name: Check release artifacts
run: uv run python scripts/check-desktop-release.py --platform ${{ matrix.release-platform }} --max-mb 80
- name: Smoke test desktop app
run: uv run python scripts/smoke-desktop-app.py --install-from-package
- name: Check signed and notarized macOS release artifacts
if: startsWith(github.ref, 'refs/tags/v') && matrix.release-platform == 'macos' && vars.DESKTOP_REQUIRE_SIGNING == 'true'
run: uv run python scripts/check-desktop-release.py --platform macos --max-mb 80 --require-signed --require-notarized
- name: Check signed Windows release artifacts
if: startsWith(github.ref, 'refs/tags/v') && matrix.release-platform == 'windows' && vars.DESKTOP_REQUIRE_SIGNING == 'true'
run: uv run python scripts/check-desktop-release.py --platform ${{ matrix.release-platform }} --max-mb 80 --require-signed
- name: Upload desktop artifacts
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.artifact-name }}
path: |
desktop/src-tauri/target/**/release/bundle/**/*.dmg
desktop/src-tauri/target/**/release/bundle/**/*.app
desktop/src-tauri/target/**/release/bundle/**/*.exe
desktop/src-tauri/target/**/release/bundle/**/*.msi
- name: Upload GitHub Release assets
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v3
with:
draft: true
files: |
desktop/src-tauri/target/**/release/bundle/**/*.dmg
desktop/src-tauri/target/**/release/bundle/**/*.exe
desktop/src-tauri/target/**/release/bundle/**/*.msi