Release v0.8.0 with NBER-CLI Desktop #7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |