Skip to content
Merged
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
174 changes: 174 additions & 0 deletions .github/workflows/pr-build-installer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
name: PR Build Installer

# On-demand, signed macOS test builds for a pull request. Add the
# `build-installer` label to package apps/code and get a downloadable .dmg/.zip
# attached to the run, with a sticky comment on the PR linking to it. Testers
# download the artifact and run the app locally.
#
# This deliberately does NOT publish a GitHub release or notarize the build. It
# signs (so the app launches after a right-click → Open) but skips notarization,
# which lives in the real release pipeline (code-release.yml, on v* tags).
#
# Gating: only same-repo PRs run — fork PRs never receive the signing/AWS
# secrets, so building them would red spuriously.

on:
pull_request:
types: [opened, synchronize, reopened, labeled]

concurrency:
group: pr-build-installer-${{ github.head_ref || github.ref }}
cancel-in-progress: true

jobs:
build-macos:
# Run on a same-repo PR that carries the `build-installer` label (either
# just added, or already present on a push).
if: >-
github.event.pull_request.head.repo.full_name == github.repository &&
(
(github.event.action == 'labeled' && github.event.label.name == 'build-installer') ||
(github.event.action != 'labeled' && contains(github.event.pull_request.labels.*.name, 'build-installer'))
)
runs-on: macos-15
timeout-minutes: 45
permissions:
id-token: write
contents: read
env:
NODE_OPTIONS: "--max-old-space-size=8192"
NODE_ENV: production
npm_config_arch: arm64
npm_config_platform: darwin
VITE_POSTHOG_API_KEY: ${{ secrets.VITE_POSTHOG_API_KEY }}
VITE_POSTHOG_API_HOST: ${{ secrets.VITE_POSTHOG_API_HOST }}
POSTHOG_SOURCEMAP_API_KEY: ${{ secrets.POSTHOG_SOURCEMAP_API_KEY }}
POSTHOG_ENV_ID: ${{ secrets.POSTHOG_ENV_ID }}
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}
CSC_LINK: ${{ secrets.APPLE_CODESIGN_CERT_BASE64 }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CODESIGN_CERT_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
# Sign but do not notarize: a signed-but-unnotarized app launches for a
# tester after a right-click → Open. Notarization runs only in the tag
# release (code-release.yml).
SKIP_NOTARIZE: "1"
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false

- name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9

- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1
with:
role-to-assume: ${{ secrets.AWS_TWIG_APP_ASSETS_ROLE_ARN }}
aws-region: ${{ secrets.AWS_TWIG_APP_ASSETS_REGION }}
mask-aws-account-id: true
unset-current-credentials: true

- name: Download BerkeleyMono fonts from S3
run: aws s3 cp s3://${{ secrets.AWS_TWIG_APP_ASSETS_BUCKET }}/fonts/BerkeleyMono/ apps/code/assets/fonts/BerkeleyMono/ --recursive

- name: Build workspace packages
run: |
pnpm --filter @posthog/electron-trpc run build
pnpm --filter @posthog/platform run build
pnpm --filter @posthog/shared run build
pnpm --filter @posthog/git run build
pnpm --filter @posthog/enricher run build
pnpm --filter @posthog/agent run build

# build/Assets.car is gitignored; regenerate it so the packaged app ships
# the liquid-glass icon (the script falls back to .icns if actool fails).
- name: Compile macOS liquid-glass icon
working-directory: apps/code
run: bash scripts/compile-glass-icon.sh

- name: Build app
working-directory: apps/code
run: |
pnpm exec electron-vite build
pnpm exec electron-builder build --mac --arm64 --publish never --config electron-builder.ts

- name: Upload installer artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: code-macos-arm64
path: |
apps/code/out/*.dmg
apps/code/out/*-mac.zip
retention-days: 7
Comment thread
gantoine marked this conversation as resolved.
if-no-files-found: error

comment:
# Update the sticky comment once the build finishes, on success OR failure,
# so a failed re-run replaces a prior "ready" comment instead of leaving it
# pointing at stale artifacts. Skipped only when build-macos itself was
# skipped (no label / fork), where there is nothing to report.
needs: build-macos
if: ${{ !cancelled() && (needs.build-macos.result == 'success' || needs.build-macos.result == 'failure') }}
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
issues: write
pull-requests: write
Comment thread
gantoine marked this conversation as resolved.
steps:
- name: Post build status on the PR
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
BUILD_RESULT: ${{ needs.build-macos.result }}
run: |
set -euo pipefail
MARKER="<!-- pr-installer-build -->"
RUN_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
if [ "${BUILD_RESULT}" = "success" ]; then
{
echo "$MARKER"
echo "### 🖥️ macOS test build (Apple Silicon)"
echo ""
echo "A signed macOS build for this PR is ready to download and run."
echo ""
echo "**[⬇️ Open this run and download the \`code-macos-arm64\` artifact](${RUN_URL}#artifacts)** (Apple Silicon / M-series)."
echo ""
echo "Unzip it, then open the \`.dmg\` (or the \`.app\` inside the \`-mac.zip\`)."
echo ""
echo "> ℹ️ The build is **signed but not notarized**, so macOS may warn on first launch."
echo "> Right-click the app → **Open**, or run \`xattr -dr com.apple.quarantine \"PostHog Code.app\"\`."
echo ""
echo "<sub>Built from ${GITHUB_SHA} · rebuilds on each push while the \`build-installer\` label is present. Downloading requires being signed in to GitHub with repo access.</sub>"
} > comment-body.md
else
{
echo "$MARKER"
echo "### 🖥️ macOS test build failed"
echo ""
echo "The macOS build for this PR **failed**, so there is no downloadable artifact for the latest commit."
echo ""
echo "**[🔎 View the failed run](${RUN_URL})** for logs."
echo ""
echo "<sub>Failed on ${GITHUB_SHA} · rebuilds on each push while the \`build-installer\` label is present.</sub>"
} > comment-body.md
fi

COMMENT_ID=$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate \
--jq ".[] | select(.body | contains(\"${MARKER}\")) | .id" | head -n1 || true)

if [ -n "${COMMENT_ID}" ]; then
gh api --method PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${COMMENT_ID}" -F body=@comment-body.md
else
gh api --method POST "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" -F body=@comment-body.md
fi