Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
284 changes: 284 additions & 0 deletions .github/workflows/desktop-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
name: Desktop Release

on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
inputs:
platform:
description: "Desktop platform to build"
required: true
default: all
type: choice
options:
- all
- macos
- linux
- windows
publish:
description: "Upload artifacts to the GitHub Release"
required: true
default: true
type: boolean

permissions:
contents: write
packages: read

concurrency:
group: desktop-release-${{ github.ref }}
cancel-in-progress: true

jobs:
release-meta:
name: Resolve release metadata
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.meta.outputs.tag }}
version: ${{ steps.meta.outputs.version }}
platform: ${{ steps.meta.outputs.platform }}
should_publish: ${{ steps.meta.outputs.should_publish }}
checkout_ref: ${{ steps.meta.outputs.checkout_ref }}
steps:
- name: Checkout selected ref
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ github.ref_name }}

- name: Resolve tag and version
id: meta
shell: bash
run: |
set -euo pipefail

if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
platform="all"
should_publish="true"
else
platform="${{ github.event.inputs.platform }}"
if [[ "${{ github.event.inputs.publish }}" == "true" ]]; then
should_publish="true"
else
should_publish="false"
fi
fi

checkout_ref="${GITHUB_REF_NAME}"

if [[ "$should_publish" == "true" ]]; then
if [[ ! "$checkout_ref" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::publish=true requires ref to look like vX.Y.Z or vX.Y.Z-suffix; got '$checkout_ref'."
exit 1
fi
tag="$checkout_ref"
version="${checkout_ref#v}"
else
tag=""
version="$(node -p "require('./package.json').version")"
fi

echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "platform=$platform" >> "$GITHUB_OUTPUT"
echo "should_publish=$should_publish" >> "$GITHUB_OUTPUT"
echo "checkout_ref=$checkout_ref" >> "$GITHUB_OUTPUT"

create-release:
name: Create GitHub release
needs: release-meta
if: needs.release-meta.outputs.should_publish == 'true'
runs-on: ubuntu-latest
steps:
- name: Ensure GitHub release exists
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
tag="${{ needs.release-meta.outputs.tag }}"
if gh release view "$tag" --repo "${{ github.repository }}" >/dev/null 2>&1; then
echo "Release $tag already exists."
else
gh release create "$tag" --repo "${{ github.repository }}" --title "$tag" --generate-notes
fi

publish-macos:
name: Publish macOS (${{ matrix.arch }})
needs: [release-meta, create-release]
if: |
always() &&
(needs.create-release.result == 'success' || needs.create-release.result == 'skipped') &&
(needs.release-meta.outputs.platform == 'all' || needs.release-meta.outputs.platform == 'macos')
runs-on: macos-15
strategy:
fail-fast: false
matrix:
arch: [x64, arm64]
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ needs.release-meta.outputs.checkout_ref }}

- uses: actions/setup-go@v6
with:
go-version-file: daemon/go.mod
cache-dependency-path: daemon/go.sum

- uses: actions/setup-node@v6
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm ci

- name: Set desktop package version
run: node electron/scripts/set-desktop-version.cjs "${{ needs.release-meta.outputs.version }}"

- name: Materialize notarization API key
shell: bash
env:
APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
run: |
set -euo pipefail
key_path="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8"
printf '%s' "$APPLE_API_KEY_BASE64" | base64 -D > "$key_path"
chmod 600 "$key_path"
echo "APPLE_API_KEY=$key_path" >> "$GITHUB_ENV"

- name: Build desktop release
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: npm run build:desktop -- --mac --${{ matrix.arch }} --publish never

- name: Upload desktop artifacts to release
if: needs.release-meta.outputs.should_publish == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
files=()
while IFS= read -r -d '' f; do
files+=("$f")
done < <(find release -maxdepth 1 -type f -print0 | sort -z)
if (( ${#files[@]} == 0 )); then
echo "::error::No release artifacts found in release"
exit 1
fi
gh release upload "${{ needs.release-meta.outputs.tag }}" "${files[@]}" --clobber --repo "${{ github.repository }}"

publish-linux:
name: Publish Linux
needs: [release-meta, create-release]
if: |
always() &&
(needs.create-release.result == 'success' || needs.create-release.result == 'skipped') &&
(needs.release-meta.outputs.platform == 'all' || needs.release-meta.outputs.platform == 'linux')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ needs.release-meta.outputs.checkout_ref }}

- uses: actions/setup-go@v6
with:
go-version-file: daemon/go.mod
cache-dependency-path: daemon/go.sum

- uses: actions/setup-node@v6
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm ci

- name: Set desktop package version
run: node electron/scripts/set-desktop-version.cjs "${{ needs.release-meta.outputs.version }}"

- name: Build desktop release
env:
CSC_IDENTITY_AUTO_DISCOVERY: "false"
run: npm run build:desktop -- --linux --x64 --publish never

- name: Upload desktop artifacts to release
if: needs.release-meta.outputs.should_publish == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
files=()
while IFS= read -r -d '' f; do
files+=("$f")
done < <(find release -maxdepth 1 -type f -print0 | sort -z)
if (( ${#files[@]} == 0 )); then
echo "::error::No release artifacts found in release"
exit 1
fi
gh release upload "${{ needs.release-meta.outputs.tag }}" "${files[@]}" --clobber --repo "${{ github.repository }}"

publish-windows:
name: Publish Windows (${{ matrix.arch }})
needs: [release-meta, create-release]
if: |
always() &&
(needs.create-release.result == 'success' || needs.create-release.result == 'skipped') &&
(needs.release-meta.outputs.platform == 'all' || needs.release-meta.outputs.platform == 'windows')
runs-on: windows-2025-vs2026
strategy:
fail-fast: false
matrix:
arch: [x64, arm64]
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ needs.release-meta.outputs.checkout_ref }}

- uses: actions/setup-go@v6
with:
go-version-file: daemon/go.mod
cache-dependency-path: daemon/go.sum

- uses: actions/setup-node@v6
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm ci

- name: Set desktop package version
run: node electron/scripts/set-desktop-version.cjs "${{ needs.release-meta.outputs.version }}"

- name: Build desktop release
env:
CSC_IDENTITY_AUTO_DISCOVERY: "false"
run: npm run build:desktop -- --win --${{ matrix.arch }} --publish never

- name: Upload desktop artifacts to release
if: needs.release-meta.outputs.should_publish == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
files=()
while IFS= read -r -d '' f; do
files+=("$f")
done < <(find release -maxdepth 1 -type f -print0 | sort -z)
if (( ${#files[@]} == 0 )); then
echo "::error::No release artifacts found in release"
exit 1
fi
gh release upload "${{ needs.release-meta.outputs.tag }}" "${files[@]}" --clobber --repo "${{ github.repository }}"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,6 @@ docs/

# Local scratch
/tmp/

# Local signing / notarization credentials
/electron/cred/
Binary file added electron/build/background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/build/background@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
81 changes: 81 additions & 0 deletions electron/electron-builder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
appId: com.crew44.desktop
productName: Crew44
copyright: Copyright © 2026 Crew44
asar: true
directories:
output: release
buildResources: electron/build
files:
- package.json
- electron/**/*
- dist/**/*
- "!**/.DS_Store"
- "!**/*.map"
extraResources:
- from: bin/
to: bin/
filter:
- crew44-daemon*
publish:
provider: github
owner: getcrew44
repo: crew44
releaseType: release
mac:
category: public.app-category.developer-tools
icon: electron/assets/crew44.icns
target:
- dmg
- zip
artifactName: Crew44-${version}-mac-${arch}.${ext}
hardenedRuntime: true
notarize: true
gatekeeperAssess: false
strictVerify: false
entitlements: electron/build/entitlements.mac.plist
entitlementsInherit: electron/build/entitlements.mac.plist
electronLanguages:
- en
- en_GB
- zh_CN
- zh_TW
dmg:
artifactName: Crew44-${version}-mac-${arch}.${ext}
background: background.png
icon: electron/assets/crew44.icns
title: "${productName} ${version}"
iconSize: 96
iconTextSize: 14
window:
x: 160
y: 120
width: 793
height: 496
contents:
- x: 220
y: 296
type: file
- x: 573
y: 296
type: link
path: /Applications
win:
icon: electron/assets/crew44.ico
artifactName: Crew44-Setup-${version}-${arch}.${ext}
target:
- target: nsis
arch:
- x64
- arm64
nsis:
oneClick: false
perMachine: false
allowToChangeInstallationDirectory: true
shortcutName: Crew44
linux:
category: Development
artifactName: Crew44-${version}-linux-${arch}.${ext}
target:
- AppImage
- deb
npmRebuild: false
Loading