Skip to content

Build Desktop Artifacts #1

Build Desktop Artifacts

Build Desktop Artifacts #1

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