Skip to content

Commit 768b95c

Browse files
committed
full release workflow
1 parent 7a6adb7 commit 768b95c

File tree

1 file changed

+171
-0
lines changed

1 file changed

+171
-0
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
name: Build / Notarize macOS & Windows → Release
2+
3+
on:
4+
# Manual trigger
5+
workflow_dispatch:
6+
7+
env: # shared across jobs
8+
APP_NAME: Localforge
9+
NODE_VERSION: '20'
10+
11+
jobs:
12+
# ────────────────────────────────────────────────────────────────────────────────
13+
# 1. Build matrix – macOS + Windows run in parallel
14+
# ────────────────────────────────────────────────────────────────────────────────
15+
build:
16+
strategy:
17+
matrix:
18+
os: [macos-14, windows-latest] # add linux-latest if you need it
19+
runs-on: ${{ matrix.os }}
20+
21+
steps:
22+
# ---------- common ----------
23+
- name: Checkout code
24+
- uses: actions/checkout@v4
25+
26+
- name: Setup Node.js
27+
uses: actions/setup-node@v4
28+
with:
29+
node-version: '20' # Or your required version
30+
cache: 'npm'
31+
32+
- name: Install dependencies
33+
run: npm ci
34+
35+
# ---------- macOS-only : import cert & env ----------
36+
- name: Import macOS code-sign cert
37+
if: runner.os == 'macOS'
38+
id: import_cert # Give this step an ID to reference its output
39+
uses: apple-actions/import-codesign-certs@v2
40+
with:
41+
p12-file-base64: ${{ secrets.MACOS_CERTIFICATE_P12 }}
42+
p12-password: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
43+
44+
# ---------- build / package ----------
45+
- name: Package Electron app
46+
env:
47+
# mac signing ID available only on mac runner
48+
MACOS_SIGNING_IDENTITY_FROM_ACTION: ${{ steps.import_cert.outputs.signing-identity }}
49+
run: |
50+
if [[ "$RUNNER_OS" == "macOS" ]]; then
51+
npm run package:mac
52+
else
53+
npm run package:win
54+
fi
55+
56+
# ---------- macOS-only : notarize, staple, dmg ----------
57+
- name: Notarise & staple (macOS)
58+
if: runner.os == 'macOS'
59+
env:
60+
APPLE_ID: ${{ secrets.APPLE_ID_EMAIL }}
61+
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
62+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
63+
run: |
64+
APP_PATH="dist/Localforge-darwin-arm64/Localforge.app"
65+
ZIP_PATH="dist/Localforge-mac.zip"
66+
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "$ZIP_PATH"
67+
68+
echo "Submitting to notarisation…"
69+
xcrun notarytool submit "$ZIP_PATH" \
70+
--apple-id "$APPLE_ID" \
71+
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
72+
--team-id "$APPLE_TEAM_ID" \
73+
--output-format json --wait
74+
75+
xcrun stapler staple --verbose "$APP_PATH"
76+
77+
npx electron-installer-dmg "$APP_PATH" "Localforge" \
78+
--out="dist/installers" \
79+
--icon="build/icons/mac/icon.icns" \
80+
--overwrite
81+
82+
# ---------- Windows portable build via electron-packager --------
83+
- name: Package Windows (portable)
84+
if: runner.os == 'Windows'
85+
run: npm run package:win
86+
87+
# Optional signing ------------------------------------------------- (120$, maybe later)
88+
# - name: Sign main executable
89+
# if: runner.os == 'Windows' && env.CSC_LINK != ''
90+
# env:
91+
# CSC_LINK: ${{ secrets.WIN_CODESIGN_PFX }}
92+
# CSC_KEY_PASSWORD: ${{ secrets.WIN_CODESIGN_PFX_PASSWORD }}
93+
# run: |
94+
# echo "%CSC_LINK%" | base64 -d > code.pfx
95+
# # Sign the main EXE
96+
# "/Program Files (x86)/Windows Kits/10/bin/10.0.22621.0/x64/signtool.exe" sign ^
97+
# /f code.pfx ^
98+
# /p "%CSC_KEY_PASSWORD%" ^
99+
# /tr http://timestamp.digicert.com ^
100+
# /td sha256 ^
101+
# /fd sha256 ^
102+
# "dist/Localforge-win32-x64/Localforge.exe"
103+
104+
# Zip the folder so users download one file -----------------------
105+
- name: Zip portable build
106+
if: runner.os == 'Windows'
107+
run: |
108+
7z a -r dist/Localforge-win32-x64.zip dist/Localforge-win32-x64\*
109+
110+
# ───────────── macOS artifact (.dmg) ─────────────
111+
- name: Upload macOS artifact
112+
if: runner.os == 'macOS'
113+
uses: actions/upload-artifact@v4
114+
with:
115+
name: localforge-osx-installer # <- release job will grab *-installer
116+
path: dist/installers/*.dmg
117+
if-no-files-found: error
118+
119+
# ───────────── Windows artifact (portable .zip or installer) ─────────────
120+
- name: Upload Windows artifact
121+
if: runner.os == 'Windows'
122+
uses: actions/upload-artifact@v4
123+
with:
124+
name: localforge-windows-installer # <- release job will grab *-installer
125+
# adjust path later if you move to electron-builder (.exe / .msi)
126+
path: dist/Localforge-win32-x64.zip
127+
if-no-files-found: error
128+
129+
# ────────────────────────────────────────────────────────────────────────────────
130+
# 2. Release job – waits for both installers, attaches to one GitHub Release
131+
# ────────────────────────────────────────────────────────────────────────────────
132+
release:
133+
needs: build
134+
runs-on: ubuntu-latest
135+
136+
steps:
137+
- uses: actions/checkout@v4
138+
# we need the tags in order to figure out the latest one
139+
with: { fetch-depth: 0 }
140+
141+
# ---------- decide which tag to use ----------
142+
- name: Compute tag name
143+
id: tag
144+
run: |
145+
# If this run was triggered by `git push --tag`, use that tag
146+
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
147+
TAG_NAME="${GITHUB_REF#refs/tags/}"
148+
else
149+
# Manual run → grab *latest* annotated or lightweight tag
150+
git fetch --tags --force
151+
TAG_NAME="$(git describe --tags --abbrev=0)"
152+
fi
153+
echo "Using tag: $TAG_NAME"
154+
echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT"
155+
156+
- uses: actions/download-artifact@v4
157+
with:
158+
pattern: '*-installer' # grabs macOS-installer & Windows-installer
159+
path: .
160+
161+
# publish / update the release
162+
- name: Publish GitHub Release
163+
uses: softprops/action-gh-release@v1
164+
with:
165+
tag_name: ${{ steps.tag.outputs.tag_name }}
166+
files: |
167+
*.dmg
168+
*.exe
169+
*.msi
170+
draft: false
171+
prerelease: false

0 commit comments

Comments
 (0)