Skip to content

Release Installers

Release Installers #27

name: Release Installers
# Build native installers (mac dmg / win exe) and showcase tarballs, then
# attach them as assets to a GitHub Release (created on demand if the tag
# does not exist yet).
#
# This workflow is deliberately **manual-only** and independent of the npm
# publish pipeline in `release.yml`, so a broken installer build can be
# retried without re-running Changesets, and vice-versa.
on:
workflow_dispatch:
inputs:
tag:
description: >
GitHub Release tag to attach assets to. If the tag/Release does
not exist yet, it will be created automatically. When left
empty, the tag defaults to `lynxtron-go-v<version>` on `main`
(tracking Changesets patch bumps), or
`lynxtron-go-v<version>-<sha6>` on any other branch.
required: false
type: string
permissions: {}
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false
jobs:
installers:
name: Installers (${{ matrix.os }})
permissions:
contents: write
strategy:
fail-fast: false
# Serialize so the two OS jobs don't race to create the same release.
max-parallel: 1
matrix:
os: [macos-latest, windows-2022]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install Dependencies
run: |
npm i -g corepack@latest --force
corepack enable
pnpm install
- name: Resolve release tag
id: tag
shell: bash
run: |
if [ -n "${{ inputs.tag }}" ]; then
tag="${{ inputs.tag }}"
else
version=$(node -p "require('./lynxtron-go/package.json').version")
# main: v<version> (stable, tracks Changesets patch bumps)
# other branches: v<version>-<sha6> (pre-release; disambiguates
# per-commit builds so they don't clobber the stable Release)
if [ "$GITHUB_REF" = "refs/heads/main" ]; then
tag="lynxtron-go-v${version}"
else
sha6=$(printf '%s' "$GITHUB_SHA" | cut -c1-6)
tag="lynxtron-go-v${version}-${sha6}"
fi
fi
echo "tag=$tag" >> "$GITHUB_OUTPUT"
- name: Import macOS signing certificate
if: runner.os == 'macOS'
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
certificate_path="$RUNNER_TEMP/lynxtron-go-signing.p12"
keychain_path="$RUNNER_TEMP/lynxtron-go-signing.keychain-db"
printf '%s' "$MACOS_CERTIFICATE" | base64 --decode > "$certificate_path"
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 "$certificate_path" \
-P "$MACOS_CERTIFICATE_PWD" \
-A \
-t cert \
-f pkcs12 \
-k "$keychain_path"
security set-key-partition-list \
-S apple-tool:,apple:,codesign: \
-s \
-k "$KEYCHAIN_PASSWORD" \
"$keychain_path"
security list-keychains -d user -s "$keychain_path"
security default-keychain -d user -s "$keychain_path"
security find-identity -v -p codesigning "$keychain_path"
# macOS: dmg installer + publishable showcase tarballs (native .node
# addons are host-platform specific, so tarballs are built here).
- name: Build macOS installer
if: runner.os == 'macOS'
env:
APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY: ${{ runner.temp }}/lynxtron-notarization.p8
run: |
test -n "$APPLE_API_KEY_BASE64"
test -n "$APPLE_API_KEY_ID"
test -n "$APPLE_API_ISSUER"
printf '%s' "$APPLE_API_KEY_BASE64" | base64 --decode > "$APPLE_API_KEY"
chmod 600 "$APPLE_API_KEY"
pnpm --dir lynxtron-go run pack
- name: Verify macOS distribution
if: runner.os == 'macOS'
run: |
app_path=$(find lynxtron-go/dist -type d -name 'Lynxtron Go.app' -print -quit)
test -n "$app_path"
codesign --verify --deep --strict --verbose=2 "$app_path"
codesign --display --verbose=4 "$app_path"
xcrun stapler validate "$app_path"
spctl --assess --type execute --verbose=4 "$app_path"
- name: Pack showcases
if: runner.os == 'macOS'
run: node scripts/pack-showcases.mjs
# Windows: nsis installer.
- name: Build Windows installer
if: runner.os == 'Windows'
run: pnpm --dir lynxtron-go run pack:win
- name: Upload Release assets
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag.outputs.tag }}
fail_on_unmatched_files: false
files: |
lynxtron-go/dist/*.dmg
lynxtron-go/dist/*.exe
dist/showcase-artifacts/*.tgz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Clean up macOS signing materials
if: runner.os == 'macOS' && always()
run: |
security delete-keychain "$RUNNER_TEMP/lynxtron-go-signing.keychain-db" || true
rm -f "$RUNNER_TEMP/lynxtron-go-signing.p12"
rm -f "$RUNNER_TEMP/lynxtron-notarization.p8"