Release Installers #27
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: 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" |