macos-package #82
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: 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 }}" |