Build Desktop Artifacts #1
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: Build Desktop Artifacts | |
| on: | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| concurrency: | |
| group: build-desktop-artifacts-ci-latest | |
| cancel-in-progress: false | |
| jobs: | |
| prepare: | |
| name: Prepare Build Version | |
| runs-on: ubuntu-22.04 | |
| outputs: | |
| build_version: ${{ steps.meta.outputs.build_version }} | |
| steps: | |
| - name: Determine CI build version | |
| id: meta | |
| shell: bash | |
| run: echo "build_version=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" | |
| prepare-ci-release: | |
| name: Prepare ci-latest Pre-Release | |
| runs-on: ubuntu-22.04 | |
| needs: prepare | |
| steps: | |
| - name: Recreate ci-latest pre-release | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const tag = "ci-latest"; | |
| const releaseName = "CI Latest"; | |
| const ref = `tags/${tag}`; | |
| const releases = await github.paginate(github.rest.repos.listReleases, { | |
| owner, | |
| repo, | |
| per_page: 100, | |
| }); | |
| const release = releases.find((candidate) => candidate.tag_name === tag); | |
| if (release) { | |
| core.info(`Deleting existing release ${release.id} for tag ${tag}.`); | |
| await github.rest.repos.deleteRelease({ | |
| owner, | |
| repo, | |
| release_id: release.id, | |
| }); | |
| } | |
| try { | |
| core.info(`Deleting existing tag ref ${ref}.`); | |
| await github.rest.git.deleteRef({ owner, repo, ref }); | |
| } catch (error) { | |
| if (error.status !== 404) { | |
| throw error; | |
| } | |
| core.info(`Tag ref ${ref} does not exist yet.`); | |
| } | |
| core.info(`Creating tag ${tag} at ${context.sha}.`); | |
| await github.rest.git.createRef({ | |
| owner, | |
| repo, | |
| ref: `refs/${ref}`, | |
| sha: context.sha, | |
| }); | |
| core.info(`Creating pre-release ${tag} for ${context.sha}.`); | |
| await github.rest.repos.createRelease({ | |
| owner, | |
| repo, | |
| tag_name: tag, | |
| target_commitish: context.sha, | |
| name: releaseName, | |
| draft: false, | |
| prerelease: true, | |
| make_latest: "false", | |
| }); | |
| build-macos: | |
| name: macOS | |
| runs-on: macos-14 | |
| needs: [prepare, prepare-ci-release] | |
| env: | |
| SOLOKEYS_GUI_VERSION: ${{ needs.prepare.outputs.build_version }} | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Install libusb | |
| run: brew install libusb | |
| - name: Configure macOS signing certificate | |
| shell: bash | |
| env: | |
| APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| APPLE_DEVELOPER_ID_APPLICATION: ${{ secrets.APPLE_DEVELOPER_ID_APPLICATION }} | |
| run: | | |
| if [[ -z "${APPLE_CERTIFICATE_P12}" || -z "${APPLE_CERTIFICATE_PASSWORD}" || -z "${APPLE_DEVELOPER_ID_APPLICATION}" ]]; then | |
| echo "macOS signing secrets not configured; using ad-hoc signing." | |
| echo "MACOS_CODESIGN_IDENTITY=-" >> "${GITHUB_ENV}" | |
| exit 0 | |
| fi | |
| KEYCHAIN_PATH="${RUNNER_TEMP}/solokeys-signing.keychain-db" | |
| KEYCHAIN_PASSWORD="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')" | |
| CERT_PATH="${RUNNER_TEMP}/developer-id.p12" | |
| export CERT_PATH | |
| python3 - <<'PY' | |
| import base64 | |
| import os | |
| from pathlib import Path | |
| Path(os.environ["CERT_PATH"]).write_bytes( | |
| base64.b64decode(os.environ["APPLE_CERTIFICATE_P12"]) | |
| ) | |
| PY | |
| security create-keychain -p "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}" | |
| security set-keychain-settings -lut 21600 "${KEYCHAIN_PATH}" | |
| security unlock-keychain -p "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}" | |
| security import "${CERT_PATH}" \ | |
| -k "${KEYCHAIN_PATH}" \ | |
| -P "${APPLE_CERTIFICATE_PASSWORD}" \ | |
| -T /usr/bin/codesign \ | |
| -T /usr/bin/productsign | |
| security list-keychains -d user -s "${KEYCHAIN_PATH}" | |
| security default-keychain -s "${KEYCHAIN_PATH}" | |
| security set-key-partition-list \ | |
| -S apple-tool:,apple:,codesign: \ | |
| -s \ | |
| -k "${KEYCHAIN_PASSWORD}" \ | |
| "${KEYCHAIN_PATH}" | |
| echo "MACOS_CODESIGN_IDENTITY=${APPLE_DEVELOPER_ID_APPLICATION}" >> "${GITHUB_ENV}" | |
| - name: Configure macOS notarization key | |
| shell: bash | |
| env: | |
| APPLE_NOTARY_KEY_BASE64: ${{ secrets.APPLE_NOTARY_KEY_BASE64 }} | |
| APPLE_NOTARY_KEY_ID: ${{ secrets.APPLE_NOTARY_KEY_ID }} | |
| APPLE_NOTARY_ISSUER_ID: ${{ secrets.APPLE_NOTARY_ISSUER_ID }} | |
| run: | | |
| if [[ "${MACOS_CODESIGN_IDENTITY:-}" == "-" || -z "${APPLE_NOTARY_KEY_BASE64}" || -z "${APPLE_NOTARY_KEY_ID}" || -z "${APPLE_NOTARY_ISSUER_ID}" ]]; then | |
| echo "macOS notarization secrets not configured; skipping notarization." | |
| echo "MACOS_NOTARIZE=0" >> "${GITHUB_ENV}" | |
| exit 0 | |
| fi | |
| NOTARY_KEY_PATH="${RUNNER_TEMP}/AuthKey_${APPLE_NOTARY_KEY_ID}.p8" | |
| export NOTARY_KEY_PATH | |
| python3 - <<'PY' | |
| import base64 | |
| import os | |
| from pathlib import Path | |
| Path(os.environ["NOTARY_KEY_PATH"]).write_bytes( | |
| base64.b64decode(os.environ["APPLE_NOTARY_KEY_BASE64"]) | |
| ) | |
| PY | |
| chmod 600 "${NOTARY_KEY_PATH}" | |
| echo "APPLE_NOTARY_KEY_PATH=${NOTARY_KEY_PATH}" >> "${GITHUB_ENV}" | |
| echo "APPLE_NOTARY_KEY_ID=${APPLE_NOTARY_KEY_ID}" >> "${GITHUB_ENV}" | |
| echo "APPLE_NOTARY_ISSUER_ID=${APPLE_NOTARY_ISSUER_ID}" >> "${GITHUB_ENV}" | |
| echo "MACOS_NOTARIZE=1" >> "${GITHUB_ENV}" | |
| - name: Build app bundle and DMG | |
| run: | | |
| chmod +x build_macos.sh | |
| ./build_macos.sh | |
| - name: Upload macOS to pre-release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ci-latest | |
| name: CI Latest | |
| draft: false | |
| prerelease: true | |
| make_latest: false | |
| overwrite_files: true | |
| files: dist/SoloKeys-GUI-*.dmg | |
| build-windows: | |
| name: Windows | |
| runs-on: windows-2022 | |
| needs: [prepare, prepare-ci-release] | |
| env: | |
| SOLOKEYS_GUI_VERSION: ${{ needs.prepare.outputs.build_version }} | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Download libusb | |
| shell: pwsh | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| $zipPath = "$env:RUNNER_TEMP\libusb.zip" | |
| $extractDir = "$env:RUNNER_TEMP\libusb" | |
| Invoke-WebRequest ` | |
| -Uri "https://github.com/libusb/libusb/releases/download/v1.0.27/libusb-1.0.27.7z" ` | |
| -OutFile $zipPath | |
| 7z x $zipPath "-o$extractDir" | Out-Null | |
| $dllPath = Get-ChildItem -Path $extractDir -Recurse -Filter "libusb-1.0.dll" | | |
| Where-Object { $_.FullName -match "MS64\\dll\\libusb-1\.0\.dll$" } | | |
| Select-Object -First 1 -ExpandProperty FullName | |
| if (-not $dllPath) { | |
| throw "Could not locate libusb-1.0.dll in extracted archive." | |
| } | |
| "LIBUSB_PATH=$dllPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| - name: Install Inno Setup | |
| shell: pwsh | |
| run: choco install innosetup --no-progress -y | |
| - name: Configure Windows signing certificate | |
| shell: pwsh | |
| env: | |
| WINDOWS_CERTIFICATE_PFX: ${{ secrets.WINDOWS_CERTIFICATE_PFX }} | |
| WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| if ([string]::IsNullOrWhiteSpace($env:WINDOWS_CERTIFICATE_PFX) -or [string]::IsNullOrWhiteSpace($env:WINDOWS_CERTIFICATE_PASSWORD)) { | |
| Write-Host "Windows signing secrets not configured; building unsigned installer." | |
| return | |
| } | |
| $certPath = Join-Path $env:RUNNER_TEMP "windows-codesign.pfx" | |
| [IO.File]::WriteAllBytes($certPath, [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE_PFX)) | |
| "WINDOWS_CODESIGN_CERT_PATH=$certPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| "WINDOWS_CODESIGN_CERT_PASSWORD=$env:WINDOWS_CERTIFICATE_PASSWORD" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| - name: Build Windows package | |
| shell: cmd | |
| run: build_windows.bat | |
| - name: Upload Windows to pre-release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ci-latest | |
| name: CI Latest | |
| draft: false | |
| prerelease: true | |
| make_latest: false | |
| overwrite_files: true | |
| files: dist/installer/SoloKeys-GUI-Setup-*.exe | |
| build-linux: | |
| name: Linux | |
| runs-on: ubuntu-22.04 | |
| needs: [prepare, prepare-ci-release] | |
| env: | |
| SOLOKEYS_GUI_VERSION: ${{ needs.prepare.outputs.build_version }} | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y curl dpkg-dev file libfuse2 libpcsclite-dev rpm swig | |
| - name: Build Linux artifacts | |
| run: | | |
| chmod +x build_linux_deb.sh | |
| chmod +x build_linux_rpm.sh | |
| chmod +x build_linux_appimage.sh | |
| ./build_linux_deb.sh | |
| ./build_linux_rpm.sh | |
| ./build_linux_appimage.sh | |
| - name: Upload Linux to pre-release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ci-latest | |
| name: CI Latest | |
| draft: false | |
| prerelease: true | |
| make_latest: false | |
| overwrite_files: true | |
| files: | | |
| dist/solokeys-gui_*.deb | |
| dist/solokeys-gui-*.rpm | |
| dist/SoloKeys-GUI-*.AppImage |