Skip to content

macos-package

macos-package #82

Workflow file for this run

name: macos-package
on:
release:
types:
- published
- prereleased
workflow_dispatch:
inputs:
release_tag:
description: 'Release tag to upload assets to (e.g. v0.2.0). Leave empty to skip release upload.'
required: false
type: string
distribution:
description: 'Build distribution. Leave empty to infer from release_tag.'
required: false
type: choice
options:
- stable
- beta
app_base_url:
description: 'Desktop cloud base URL override.'
required: false
type: string
auth_base_url:
description: 'Desktop auth base URL override.'
required: false
type: string
permissions:
contents: write
concurrency:
group: macos-package-${{ github.ref }}
cancel-in-progress: false
jobs:
resolve_context:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
outputs:
tag: ${{ steps.tag.outputs.tag }}
distribution: ${{ steps.distribution.outputs.distribution }}
environment_name: ${{ steps.distribution.outputs.environment_name }}
steps:
- name: Resolve release tag
id: tag
shell: bash
run: |
TAG=""
if [ "${{ github.event_name }}" = "release" ]; then
TAG="${{ github.event.release.tag_name }}"
elif [ -n "${{ inputs.release_tag }}" ]; then
TAG="${{ inputs.release_tag }}"
fi
echo "Resolved release tag: ${TAG:-<empty>}"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Resolve distribution
id: distribution
shell: bash
env:
INPUT_DISTRIBUTION: ${{ inputs.distribution }}
RESOLVED_TAG: ${{ steps.tag.outputs.tag }}
run: |
DISTRIBUTION="stable"
if [ "${{ github.event_name }}" = "release" ]; then
if [ "${{ github.event.release.prerelease }}" = "true" ]; then
DISTRIBUTION="beta"
fi
elif [ -n "$INPUT_DISTRIBUTION" ]; then
DISTRIBUTION="$INPUT_DISTRIBUTION"
elif [ -n "$RESOLVED_TAG" ]; then
IS_PRERELEASE="$(gh release view "$RESOLVED_TAG" --repo "$GITHUB_REPOSITORY" --json isPrerelease -q '.isPrerelease')"
echo "Resolved prerelease flag: ${IS_PRERELEASE}"
if [ "$IS_PRERELEASE" = "true" ]; then
DISTRIBUTION="beta"
fi
fi
echo "Resolved distribution: $DISTRIBUTION"
echo "distribution=$DISTRIBUTION" >> "$GITHUB_OUTPUT"
echo "environment_name=$DISTRIBUTION" >> "$GITHUB_OUTPUT"
package:
needs: resolve_context
runs-on: macos-latest
environment: ${{ needs.resolve_context.outputs.environment_name }}
strategy:
matrix:
arch: [arm64, x64]
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ github.event_name == 'release' && needs.resolve_context.outputs.tag || github.ref }}
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: lts/*
- name: Enable Corepack
run: corepack enable
- name: Get yarn cache directory
id: yarn-cache
run: echo "dir=$(yarn config get cacheFolder)" >> "$GITHUB_OUTPUT"
- name: Cache yarn dependencies
uses: actions/cache@v5
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: yarn-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
restore-keys: yarn-${{ runner.os }}-
- name: Cache Next.js build
uses: actions/cache@v5
with:
path: ${{ github.workspace }}/apps/web/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('yarn.lock') }}-${{ hashFiles('apps/web/**/*.js', 'apps/web/**/*.jsx', 'apps/web/**/*.ts', 'apps/web/**/*.tsx') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('yarn.lock') }}-
${{ runner.os }}-nextjs-
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: yarn install --immutable
- name: Apply release version to desktop build
shell: bash
env:
RELEASE_TAG: ${{ needs.resolve_context.outputs.tag }}
run: |
RELEASE_VERSION="$(
RELEASE_TAG="$RELEASE_TAG" node -e "const fs=require('fs'); const tag=(process.env.RELEASE_TAG || '').trim(); const fallback=JSON.parse(fs.readFileSync('apps/electron/package.json','utf8')).version; const version=tag ? tag.replace(/^v/, '') : fallback; if (!/^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) { console.error('Unsupported release version: ' + version); process.exit(1); } process.stdout.write(version);"
)"
VERSION="$RELEASE_VERSION" node -e "const fs=require('fs'); const targets=['package.json','apps/web/package.json','apps/electron/package.json']; const version=process.env.VERSION; for (const target of targets) { const data=JSON.parse(fs.readFileSync(target,'utf8')); data.version=version; const indent=target === 'package.json' ? 2 : 4; fs.writeFileSync(target, JSON.stringify(data, null, indent) + '\\n'); }"
echo "Applied desktop build version: $RELEASE_VERSION"
- name: Resolve build config
id: build_config
shell: bash
env:
DISTRIBUTION: ${{ needs.resolve_context.outputs.distribution }}
INPUT_APP_BASE_URL: ${{ inputs.app_base_url }}
INPUT_AUTH_BASE_URL: ${{ inputs.auth_base_url }}
DEFAULT_NEXT_PUBLIC_DORY_CLOUD_API_URL: ${{ secrets.NEXT_PUBLIC_DORY_CLOUD_API_URL }}
DEFAULT_BETTER_AUTH_URL: ${{ secrets.BETTER_AUTH_URL }}
run: |
if [ "$DISTRIBUTION" = "beta" ]; then
APP_BASE_URL="${INPUT_APP_BASE_URL:-https://beta-app.getdory.dev}"
APP_BASE_URL="${APP_BASE_URL%/}"
AUTH_BASE_URL="${INPUT_AUTH_BASE_URL:-${DEFAULT_BETTER_AUTH_URL:-$APP_BASE_URL}}"
AUTH_BASE_URL="${AUTH_BASE_URL%/}"
NEXT_PUBLIC_DORY_CLOUD_API_URL="${DEFAULT_NEXT_PUBLIC_DORY_CLOUD_API_URL:-${APP_BASE_URL}/api}"
NEXT_PUBLIC_DORY_CLOUD_API_URL="${NEXT_PUBLIC_DORY_CLOUD_API_URL%/}"
DORY_UPDATE_CHANNEL="beta"
DORY_ELECTRON_APP_ID="com.dory.app.beta"
DORY_PROTOCOL_SCHEME="dory-beta"
else
APP_BASE_URL="${INPUT_APP_BASE_URL:-}"
AUTH_BASE_URL="${INPUT_AUTH_BASE_URL:-$DEFAULT_BETTER_AUTH_URL}"
NEXT_PUBLIC_DORY_CLOUD_API_URL="$DEFAULT_NEXT_PUBLIC_DORY_CLOUD_API_URL"
DORY_UPDATE_CHANNEL="latest"
DORY_ELECTRON_APP_ID=""
DORY_PROTOCOL_SCHEME=""
fi
echo "distribution=$DISTRIBUTION" >> "$GITHUB_OUTPUT"
echo "app_base_url=$APP_BASE_URL" >> "$GITHUB_OUTPUT"
echo "auth_base_url=$AUTH_BASE_URL" >> "$GITHUB_OUTPUT"
echo "next_public_dory_cloud_api_url=$NEXT_PUBLIC_DORY_CLOUD_API_URL" >> "$GITHUB_OUTPUT"
echo "update_channel=$DORY_UPDATE_CHANNEL" >> "$GITHUB_OUTPUT"
echo "electron_app_id=$DORY_ELECTRON_APP_ID" >> "$GITHUB_OUTPUT"
echo "protocol_scheme=$DORY_PROTOCOL_SCHEME" >> "$GITHUB_OUTPUT"
- name: Prepare Apple notarization env
run: |
cat > apps/electron/.env.apple <<EOF
APPLE_TEAM_ID=${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD=${{ secrets.APPLE_ID_PASSWORD }}
APPLE_ID=${{ secrets.APPLE_ID }}
EOF
- name: Prepare web env
shell: bash
env:
AUTH_BASE_URL: ${{ steps.build_config.outputs.auth_base_url }}
NEXT_PUBLIC_DORY_CLOUD_API_URL: ${{ steps.build_config.outputs.next_public_dory_cloud_api_url }}
run: |
cat > apps/web/.env <<EOF
BETTER_AUTH_URL=$AUTH_BASE_URL
DORY_RUNTIME=desktop
DB_TYPE=pglite
DORY_CLOUD_API_URL=$NEXT_PUBLIC_DORY_CLOUD_API_URL
NEXT_PUBLIC_DORY_RUNTIME=desktop
NEXT_PUBLIC_DORY_CLOUD_API_URL=$NEXT_PUBLIC_DORY_CLOUD_API_URL
EOF
- name: Build Electron app (mac ${{ matrix.arch }})
env:
CSC_LINK: ${{ secrets.APPLE_CERT_BASE64 }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERT_PASSWORD }}
CSC_IDENTITY_AUTO_DISCOVERY: true
DORY_UPDATE_CHANNEL: ${{ steps.build_config.outputs.update_channel }}
DORY_DISTRIBUTION: ${{ needs.resolve_context.outputs.distribution }}
DORY_APP_BASE_URL: ${{ steps.build_config.outputs.app_base_url }}
DORY_ELECTRON_APP_ID: ${{ steps.build_config.outputs.electron_app_id }}
DORY_PROTOCOL_SCHEME: ${{ steps.build_config.outputs.protocol_scheme }}
DORY_BUILD_ARCH: ${{ matrix.arch }}
run: |
yarn run electron:standalone
yarn workspace dory run compile
yarn workspace dory exec electron-builder --config electron-builder.mjs --mac --${{ matrix.arch }} --publish never
- name: Upload workflow artifact
uses: actions/upload-artifact@v7
with:
name: ${{ needs.resolve_context.outputs.distribution == 'beta' && format('electron-macos-beta-{0}-dist', matrix.arch) || format('electron-macos-{0}-dist', matrix.arch) }}
path: apps/electron/dist/*
if-no-files-found: error
publish_release_assets:
needs: [resolve_context, package]
if: needs.resolve_context.outputs.tag != ''
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ needs.resolve_context.outputs.tag != '' && needs.resolve_context.outputs.tag || github.ref }}
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: lts/*
- name: Enable Corepack
run: corepack enable
- name: Install dependencies
run: yarn install --immutable
- name: Download arm64 artifact
uses: actions/download-artifact@v5
with:
name: ${{ needs.resolve_context.outputs.distribution == 'beta' && 'electron-macos-beta-arm64-dist' || 'electron-macos-arm64-dist' }}
path: .tmp/macos-release-assets/arm64
- name: Download x64 artifact
uses: actions/download-artifact@v5
with:
name: ${{ needs.resolve_context.outputs.distribution == 'beta' && 'electron-macos-beta-x64-dist' || 'electron-macos-x64-dist' }}
path: .tmp/macos-release-assets/x64
- name: Prepare macOS release assets
run: node ./scripts/prepare-macos-update-assets.mjs .tmp/macos-release-assets/arm64 .tmp/macos-release-assets/x64 .tmp/macos-release-assets/out
- name: Upload macOS release assets
shell: bash
run: |
TAG="${{ needs.resolve_context.outputs.tag }}"
find .tmp/macos-release-assets/out -maxdepth 1 -type f \
\( -name "*.dmg" -o -name "*.zip" -o -name "*.blockmap" -o -name "*.yml" \) \
-print0 | xargs -0 -I {} gh release upload "$TAG" "{}" --clobber
update-homebrew:
needs: [resolve_context, package, publish_release_assets]
if: needs.resolve_context.outputs.tag != '' && needs.resolve_context.outputs.distribution == 'stable'
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
steps:
- name: Dispatch Homebrew tap update
run: |
gh workflow run "update-homebrew.yml" \
--repo "${{ github.repository }}" \
--ref "${{ github.ref_name }}" \
-f tag="${{ needs.resolve_context.outputs.tag }}"