feat(FN-5603): add desktop release jobs for macOS and Linux with artifact a
Added GitHub Actions workflows for macOS and Linux desktop release artifacts (`release.yml` and `test-release.yml`), wired in new `dist` scripts across platforms, and extended test coverage for electron-builder config and release workflow logic, with updated documentation for the new artifacts. Fusion-Task-Id: FN-5603 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> Fusion-Task-Id: FN-5603
This commit is contained in:
118
.github/workflows/release.yml
vendored
118
.github/workflows/release.yml
vendored
@@ -159,10 +159,124 @@ jobs:
|
||||
packages/desktop/dist-electron/Fusion-*-win-*.exe.sha256
|
||||
packages/desktop/dist-electron/Fusion-*-win-*.exe.blockmap
|
||||
|
||||
# ── Build macOS desktop artifacts ────────────────────────────────────
|
||||
# macOS Developer ID signing/notarization is intentionally deferred;
|
||||
# this job emits unsigned .dmg/.zip release artifacts.
|
||||
build-desktop-macos:
|
||||
name: Build Desktop macOS DMG/ZIP
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node and install dependencies
|
||||
uses: ./.github/actions/setup-node-pnpm
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Build desktop package
|
||||
run: pnpm --filter @fusion/desktop build
|
||||
|
||||
- name: Package macOS desktop artifacts
|
||||
run: pnpm --filter @fusion/desktop dist:mac -- --publish never
|
||||
env:
|
||||
CSC_IDENTITY_AUTO_DISCOVERY: "false"
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Verify desktop macOS artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
dmgs=(packages/desktop/dist-electron/Fusion-*-mac-*.dmg)
|
||||
zips=(packages/desktop/dist-electron/Fusion-*-mac-*.zip)
|
||||
if [ ${#dmgs[@]} -eq 0 ]; then
|
||||
echo "No Fusion macOS DMG artifacts produced" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ${#zips[@]} -eq 0 ]; then
|
||||
echo "No Fusion macOS ZIP artifacts produced" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Generate desktop macOS checksums
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
for file in packages/desktop/dist-electron/Fusion-*-mac-*.dmg packages/desktop/dist-electron/Fusion-*-mac-*.zip; do
|
||||
shasum -a 256 "$file" > "$file.sha256"
|
||||
done
|
||||
|
||||
- name: Upload desktop macOS artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fusion-desktop-macos
|
||||
path: |
|
||||
packages/desktop/dist-electron/Fusion-*-mac-*.dmg
|
||||
packages/desktop/dist-electron/Fusion-*-mac-*.dmg.sha256
|
||||
packages/desktop/dist-electron/Fusion-*-mac-*.zip
|
||||
packages/desktop/dist-electron/Fusion-*-mac-*.zip.sha256
|
||||
packages/desktop/dist-electron/Fusion-*-mac-*.blockmap
|
||||
|
||||
# ── Build Linux desktop artifacts ────────────────────────────────────
|
||||
build-desktop-linux:
|
||||
name: Build Desktop Linux Artifacts
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node and install dependencies
|
||||
uses: ./.github/actions/setup-node-pnpm
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Build desktop package
|
||||
run: pnpm --filter @fusion/desktop build
|
||||
|
||||
- name: Package Linux desktop artifacts
|
||||
run: pnpm --filter @fusion/desktop dist:linux -- --publish never
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Verify desktop Linux AppImage artifact
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
appimages=(packages/desktop/dist-electron/Fusion-*-linux-*.AppImage)
|
||||
if [ ${#appimages[@]} -eq 0 ]; then
|
||||
echo "No Fusion Linux AppImage artifacts produced" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Generate desktop Linux checksums
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
for file in packages/desktop/dist-electron/Fusion-*-linux-*.AppImage packages/desktop/dist-electron/Fusion-*-linux-*.deb packages/desktop/dist-electron/Fusion-*-linux-*.tar.gz; do
|
||||
sha256sum "$file" > "$file.sha256"
|
||||
done
|
||||
|
||||
- name: Upload desktop Linux artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fusion-desktop-linux
|
||||
if-no-files-found: ignore
|
||||
path: |
|
||||
packages/desktop/dist-electron/Fusion-*-linux-*.AppImage
|
||||
packages/desktop/dist-electron/Fusion-*-linux-*.AppImage.sha256
|
||||
packages/desktop/dist-electron/Fusion-*-linux-*.deb
|
||||
packages/desktop/dist-electron/Fusion-*-linux-*.deb.sha256
|
||||
packages/desktop/dist-electron/Fusion-*-linux-*.tar.gz
|
||||
packages/desktop/dist-electron/Fusion-*-linux-*.tar.gz.sha256
|
||||
|
||||
# ── Create GitHub Release ─────────────────────────────────────────────
|
||||
github-release:
|
||||
name: Create GitHub Release
|
||||
needs: [build-binaries, build-desktop-windows]
|
||||
needs: [build-binaries, build-desktop-windows, build-desktop-macos, build-desktop-linux]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -176,7 +290,7 @@ jobs:
|
||||
- name: Collect release files
|
||||
run: |
|
||||
mkdir release-files
|
||||
find artifacts -type f \( -name "fn-*" -o -name "*.sha256" -o -name "*.exe" -o -name "*.exe.sha256" -o -name "*.blockmap" \) -exec cp {} release-files/ \;
|
||||
find artifacts -type f \( -name "fn-*" -o -name "*.sha256" -o -name "*.exe" -o -name "*.exe.sha256" -o -name "*.blockmap" -o -name "*.dmg" -o -name "*.dmg.sha256" -o -name "*.zip" -o -name "*.zip.sha256" -o -name "*.AppImage" -o -name "*.AppImage.sha256" -o -name "*.deb" -o -name "*.deb.sha256" -o -name "*.tar.gz" -o -name "*.tar.gz.sha256" \) -exec cp {} release-files/ \;
|
||||
ls -la release-files/
|
||||
|
||||
- name: Create GitHub Release
|
||||
|
||||
118
.github/workflows/test-release.yml
vendored
118
.github/workflows/test-release.yml
vendored
@@ -162,10 +162,124 @@ jobs:
|
||||
packages/desktop/dist-electron/Fusion-*-win-*.exe.sha256
|
||||
packages/desktop/dist-electron/Fusion-*-win-*.exe.blockmap
|
||||
|
||||
# ── Build macOS desktop artifacts ────────────────────────────────────
|
||||
# macOS Developer ID signing/notarization is intentionally deferred;
|
||||
# this job emits unsigned .dmg/.zip release artifacts.
|
||||
build-desktop-macos:
|
||||
name: Build Desktop macOS DMG/ZIP
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node and install dependencies
|
||||
uses: ./.github/actions/setup-node-pnpm
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Build desktop package
|
||||
run: pnpm --filter @fusion/desktop build
|
||||
|
||||
- name: Package macOS desktop artifacts
|
||||
run: pnpm --filter @fusion/desktop dist:mac -- --publish never
|
||||
env:
|
||||
CSC_IDENTITY_AUTO_DISCOVERY: "false"
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Verify desktop macOS artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
dmgs=(packages/desktop/dist-electron/Fusion-*-mac-*.dmg)
|
||||
zips=(packages/desktop/dist-electron/Fusion-*-mac-*.zip)
|
||||
if [ ${#dmgs[@]} -eq 0 ]; then
|
||||
echo "No Fusion macOS DMG artifacts produced" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ${#zips[@]} -eq 0 ]; then
|
||||
echo "No Fusion macOS ZIP artifacts produced" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Generate desktop macOS checksums
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
for file in packages/desktop/dist-electron/Fusion-*-mac-*.dmg packages/desktop/dist-electron/Fusion-*-mac-*.zip; do
|
||||
shasum -a 256 "$file" > "$file.sha256"
|
||||
done
|
||||
|
||||
- name: Upload desktop macOS artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fusion-desktop-macos
|
||||
path: |
|
||||
packages/desktop/dist-electron/Fusion-*-mac-*.dmg
|
||||
packages/desktop/dist-electron/Fusion-*-mac-*.dmg.sha256
|
||||
packages/desktop/dist-electron/Fusion-*-mac-*.zip
|
||||
packages/desktop/dist-electron/Fusion-*-mac-*.zip.sha256
|
||||
packages/desktop/dist-electron/Fusion-*-mac-*.blockmap
|
||||
|
||||
# ── Build Linux desktop artifacts ────────────────────────────────────
|
||||
build-desktop-linux:
|
||||
name: Build Desktop Linux Artifacts
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node and install dependencies
|
||||
uses: ./.github/actions/setup-node-pnpm
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Build desktop package
|
||||
run: pnpm --filter @fusion/desktop build
|
||||
|
||||
- name: Package Linux desktop artifacts
|
||||
run: pnpm --filter @fusion/desktop dist:linux -- --publish never
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Verify desktop Linux AppImage artifact
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
appimages=(packages/desktop/dist-electron/Fusion-*-linux-*.AppImage)
|
||||
if [ ${#appimages[@]} -eq 0 ]; then
|
||||
echo "No Fusion Linux AppImage artifacts produced" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Generate desktop Linux checksums
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
for file in packages/desktop/dist-electron/Fusion-*-linux-*.AppImage packages/desktop/dist-electron/Fusion-*-linux-*.deb packages/desktop/dist-electron/Fusion-*-linux-*.tar.gz; do
|
||||
sha256sum "$file" > "$file.sha256"
|
||||
done
|
||||
|
||||
- name: Upload desktop Linux artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fusion-desktop-linux
|
||||
if-no-files-found: ignore
|
||||
path: |
|
||||
packages/desktop/dist-electron/Fusion-*-linux-*.AppImage
|
||||
packages/desktop/dist-electron/Fusion-*-linux-*.AppImage.sha256
|
||||
packages/desktop/dist-electron/Fusion-*-linux-*.deb
|
||||
packages/desktop/dist-electron/Fusion-*-linux-*.deb.sha256
|
||||
packages/desktop/dist-electron/Fusion-*-linux-*.tar.gz
|
||||
packages/desktop/dist-electron/Fusion-*-linux-*.tar.gz.sha256
|
||||
|
||||
# ── Collect all artifacts ─────────────────────────────────────────────
|
||||
collect:
|
||||
name: Collect Artifacts
|
||||
needs: [build-binaries, build-desktop-windows]
|
||||
needs: [build-binaries, build-desktop-windows, build-desktop-macos, build-desktop-linux]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -177,7 +291,7 @@ jobs:
|
||||
- name: Combine artifacts
|
||||
run: |
|
||||
mkdir combined
|
||||
find artifacts -type f \( -name "fn-*" -o -name "*.sha256" -o -name "*.exe" -o -name "*.exe.sha256" -o -name "*.blockmap" \) -exec cp {} combined/ \;
|
||||
find artifacts -type f \( -name "fn-*" -o -name "*.sha256" -o -name "*.exe" -o -name "*.exe.sha256" -o -name "*.blockmap" -o -name "*.dmg" -o -name "*.dmg.sha256" -o -name "*.zip" -o -name "*.zip.sha256" -o -name "*.AppImage" -o -name "*.AppImage.sha256" -o -name "*.deb" -o -name "*.deb.sha256" -o -name "*.tar.gz" -o -name "*.tar.gz.sha256" \) -exec cp {} combined/ \;
|
||||
ls -la combined/
|
||||
|
||||
- name: Upload combined archive
|
||||
|
||||
@@ -341,6 +341,8 @@ Tray icons are generated from `packages/dashboard/app/public/logo.svg`.
|
||||
- `pnpm --filter @fusion/desktop pack` — generate unpacked artifacts via electron-builder (`--dir`)
|
||||
- `pnpm --filter @fusion/desktop dist` — generate installable desktop artifacts via electron-builder
|
||||
- `pnpm --filter @fusion/desktop dist:win` — generate Windows installable artifacts (`--win`)
|
||||
- `pnpm --filter @fusion/desktop dist:mac` — generate macOS installable artifacts (`--mac`)
|
||||
- `pnpm --filter @fusion/desktop dist:linux` — generate Linux installable artifacts (`--linux`)
|
||||
- `pnpm dist:desktop:win` — workspace alias to build desktop assets then run Windows packaging
|
||||
|
||||
## Packaging
|
||||
@@ -350,8 +352,13 @@ Desktop packaging is configured in `electron-builder.yml`.
|
||||
- Output directory: `packages/desktop/dist-electron`
|
||||
- Targets: macOS (`dmg`, `zip`), Windows (`nsis`, `portable`), Linux (`AppImage`, `deb`, `tar.gz`)
|
||||
- Windows artifacts: `Fusion-<version>-win-x64.exe` and `Fusion-<version>-win-arm64.exe` (both NSIS + portable variants) in `packages/desktop/dist-electron/`
|
||||
- Binary GitHub Release workflow (`.github/workflows/release.yml`) now attaches Windows desktop artifacts: x64 + arm64 outputs (NSIS + portable), matching `.exe.sha256` sidecars, and `.blockmap` files.
|
||||
- Binary GitHub Release workflow (`.github/workflows/release.yml`) now attaches desktop artifacts for all supported platforms:
|
||||
- Windows: x64 + arm64 outputs (NSIS + portable), matching `.exe.sha256` sidecars, and `.blockmap` files.
|
||||
- macOS: `Fusion-<version>-mac-arm64.dmg`, `Fusion-<version>-mac-x64.dmg`, matching `.zip` variants, `.sha256` sidecars, and `.blockmap` files.
|
||||
- Linux: `Fusion-<version>-linux-x64.AppImage` with `.sha256`, plus best-effort `.deb` and `.tar.gz` outputs (and sidecars) when available on the runner image.
|
||||
- Tag-less release rehearsal workflow (`.github/workflows/test-release.yml`) mirrors that artifact collection path without publishing a real GitHub Release.
|
||||
- macOS Developer ID signing/notarization and Linux signing are not wired in these release jobs yet; release-attached macOS/Linux artifacts are currently unsigned and follow-up tasks will add signing flows.
|
||||
- Linux `.deb` and `.tar.gz` outputs are best-effort and may be absent on some runner images without failing the release.
|
||||
- Isolated manual Windows build path: `.github/workflows/desktop-windows.yml` (`workflow_dispatch` on `windows-latest`) runs `electron-builder --win --x64 --arm64 --publish never`.
|
||||
- ARM64 artifacts are cross-built on the `windows-latest` x64 runner; execution/validation still requires a Windows ARM64 device or emulator.
|
||||
- Deep link protocol: `fusion://`
|
||||
|
||||
@@ -27,7 +27,9 @@
|
||||
"generate:icons": "tsx scripts/generate-icons.ts",
|
||||
"pack": "electron-builder --dir",
|
||||
"dist": "electron-builder",
|
||||
"dist:win": "electron-builder --win"
|
||||
"dist:win": "electron-builder --win",
|
||||
"dist:mac": "electron-builder --mac",
|
||||
"dist:linux": "electron-builder --linux"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fusion/core": "workspace:*",
|
||||
|
||||
@@ -11,7 +11,7 @@ async function readDesktopFile(relativePath: string): Promise<string> {
|
||||
return readFile(path.join(desktopRoot, relativePath), "utf-8");
|
||||
}
|
||||
|
||||
describe("electron-builder windows config", () => {
|
||||
describe("electron-builder desktop config", () => {
|
||||
it("keeps required Windows packaging targets and metadata", async () => {
|
||||
const builderConfig = await readDesktopFile("electron-builder.yml");
|
||||
|
||||
@@ -53,11 +53,34 @@ describe("electron-builder windows config", () => {
|
||||
expect(builderConfig).not.toContain("certificateSubjectName:");
|
||||
});
|
||||
|
||||
it("exposes a dedicated dist:win script", async () => {
|
||||
it("exposes dedicated platform-specific dist scripts", async () => {
|
||||
const packageJsonRaw = await readDesktopFile("package.json");
|
||||
const packageJson = JSON.parse(packageJsonRaw) as { scripts?: Record<string, string> };
|
||||
|
||||
expect(packageJson.scripts?.["dist:win"]).toBe("electron-builder --win");
|
||||
expect(packageJson.scripts?.["dist:mac"]).toBe("electron-builder --mac");
|
||||
expect(packageJson.scripts?.["dist:linux"]).toBe("electron-builder --linux");
|
||||
});
|
||||
|
||||
it("keeps required mac and linux targets", async () => {
|
||||
const builderConfig = await readDesktopFile("electron-builder.yml");
|
||||
|
||||
expect(builderConfig).toMatch(/mac:\s*[\s\S]*?target:\s*[\s\S]*?-\s*target:\s*dmg/m);
|
||||
expect(builderConfig).toMatch(/mac:\s*[\s\S]*?target:\s*[\s\S]*?-\s*target:\s*zip/m);
|
||||
|
||||
const linuxSectionMatch = builderConfig.match(/linux:\s*[\s\S]*$/m);
|
||||
expect(linuxSectionMatch?.[0]).toBeDefined();
|
||||
|
||||
const linuxSection = linuxSectionMatch![0];
|
||||
expect(linuxSection).toMatch(/target:\s*[\s\S]*?-\s*target:\s*AppImage/m);
|
||||
expect(linuxSection).toMatch(/target:\s*[\s\S]*?-\s*target:\s*deb/m);
|
||||
expect(linuxSection).toMatch(/target:\s*[\s\S]*?-\s*target:\s*tar\.gz/m);
|
||||
|
||||
const linuxTargetEntries = Array.from(
|
||||
linuxSection.matchAll(/-\s*target:\s*([^\n\r]+)/g),
|
||||
(match) => match[1]?.trim(),
|
||||
);
|
||||
expect(linuxTargetEntries[0]).toBe("AppImage");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ async function readRepoFile(relativePath: string): Promise<string> {
|
||||
}
|
||||
|
||||
describe("desktop release workflow wiring", () => {
|
||||
it("adds windows desktop build job to release and test-release workflows", async () => {
|
||||
it("adds desktop build jobs for windows, macOS, and linux", async () => {
|
||||
const release = await readRepoFile(".github/workflows/release.yml");
|
||||
const testRelease = await readRepoFile(".github/workflows/test-release.yml");
|
||||
|
||||
@@ -21,16 +21,41 @@ describe("desktop release workflow wiring", () => {
|
||||
expect(workflow).toContain("runs-on: windows-latest");
|
||||
expect(workflow).toMatch(/pnpm --filter @fusion\/desktop dist:win|electron-builder --win/);
|
||||
expect(workflow).toContain("name: fusion-desktop-windows");
|
||||
|
||||
expect(workflow).toContain("build-desktop-macos:");
|
||||
expect(workflow).toContain("runs-on: macos-latest");
|
||||
expect(workflow).toMatch(/pnpm --filter @fusion\/desktop dist:mac|electron-builder --mac/);
|
||||
expect(workflow).toContain("name: fusion-desktop-macos");
|
||||
|
||||
expect(workflow).toContain("build-desktop-linux:");
|
||||
expect(workflow).toContain("runs-on: ubuntu-latest");
|
||||
expect(workflow).toMatch(/pnpm --filter @fusion\/desktop dist:linux|electron-builder --linux/);
|
||||
expect(workflow).toContain("name: fusion-desktop-linux");
|
||||
}
|
||||
});
|
||||
|
||||
it("wires release aggregation to include desktop exe assets", async () => {
|
||||
it("wires release aggregation to include desktop assets across platforms", async () => {
|
||||
const release = await readRepoFile(".github/workflows/release.yml");
|
||||
|
||||
expect(release).toContain("needs: [build-binaries, build-desktop-windows]");
|
||||
expect(release).toContain(
|
||||
"needs: [build-binaries, build-desktop-windows, build-desktop-macos, build-desktop-linux]",
|
||||
);
|
||||
expect(release).toContain('find artifacts -type f \\(');
|
||||
expect(release).toContain('-name "*.exe"');
|
||||
expect(release).toContain('-name "*.exe.sha256"');
|
||||
expect(release).toContain('-name "*.blockmap"');
|
||||
expect(release).toContain('-name "*.dmg"');
|
||||
expect(release).toContain('-name "*.zip"');
|
||||
expect(release).toContain('-name "*.AppImage"');
|
||||
expect(release).toContain('-name "*.deb"');
|
||||
expect(release).toContain('-name "*.tar.gz"');
|
||||
});
|
||||
|
||||
it("wires test-release collect job to wait for all desktop build jobs", async () => {
|
||||
const testRelease = await readRepoFile(".github/workflows/test-release.yml");
|
||||
|
||||
expect(testRelease).toContain(
|
||||
"needs: [build-binaries, build-desktop-windows, build-desktop-macos, build-desktop-linux]",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user