Skip to content
Merged
Show file tree
Hide file tree
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
31 changes: 22 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
name: Release desktop

# Builds the Electron desktop app for macOS, Windows, and Linux and
# publishes the artifacts to a GitHub draft release. Trigger by pushing
# a `v*` tag (e.g. `git tag v0.1.50 && git push origin v0.1.50`) or
# manually from the Actions tab.
# Builds the Electron desktop app for macOS (arm64 + Intel x64), Windows, and
# Linux and publishes the artifacts to a GitHub draft release. Trigger by pushing
# a `v*` tag (e.g. `git tag v0.1.50 && git push origin v0.1.50`) or manually from
# the Actions tab.
#
# Binaries are **not code-signed** (see docs/architecture/desktop.md). First-run on
# macOS/Windows will show an "unidentified developer" / SmartScreen
# warning until signing certs are wired up — tracked on ROADMAP.md.
# macOS builds are **Developer ID code-signed** here (CSC_LINK / CSC_KEY_PASSWORD
# secrets, gated to the macOS runners). Notarization is done AFTER CI by
# scripts/notarize-release.mjs (Pattern C) so the build never blocks on Apple's
# notary queue. Windows ships unsigned (SmartScreen prompt) until an EV cert is
# wired up — tracked on ROADMAP.md.

on:
push:
Expand Down Expand Up @@ -38,12 +40,18 @@ jobs:
include:
- os: macos-latest
name: macOS (arm64)
dist_script: dist:desktop:mac
dist_script: dist:desktop:mac:arm64
# Native Intel runner — building x64 here means better-sqlite3 + the
# Prisma engine are compiled as x64 natively (no broken cross-build).
# macos-15-intel is GitHub's Intel image (available until ~Fall 2027).
- os: macos-15-intel
name: macOS (x64)
dist_script: dist:desktop:mac:x64
- os: windows-latest
name: Windows (x64)
dist_script: dist:desktop:win
- os: ubuntu-latest
name: Linux (x64 + arm64)
name: Linux (x64)
dist_script: dist:desktop:linux

steps:
Expand All @@ -69,6 +77,11 @@ jobs:
# We only set it on tag pushes so manual workflow_dispatch runs with
# dry_run=true leave no trace.
GH_TOKEN: ${{ (github.event_name == 'push' && !inputs.dry_run) && secrets.GITHUB_TOKEN || '' }}
# macOS Developer ID signing. Gated to the macOS runners so the Windows
# job never tries to load a mac .p12 from CSC_LINK. Notarization is NOT
# done here (mac.notarize: false) — see scripts/notarize-release.mjs.
CSC_LINK: ${{ startsWith(matrix.os, 'macos') && secrets.CSC_LINK || '' }}
CSC_KEY_PASSWORD: ${{ startsWith(matrix.os, 'macos') && secrets.CSC_KEY_PASSWORD || '' }}

- name: Upload build artifacts
uses: actions/upload-artifact@v4
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,15 @@ packages/ui/src/**/*.js
packages/ui/src/**/*.js.map
packages/ui/src/**/*.d.ts
packages/ui/src/**/*.d.ts.map
# assets.d.ts is a hand-written ambient shim (Vite asset + import.meta.env
# types), not a tsc-emitted artifact — whitelist it so CI typecheck has it.
!packages/ui/src/assets.d.ts
packages/web/src/**/*.js
packages/web/src/**/*.js.map
packages/web/src/**/*.d.ts
packages/web/src/**/*.d.ts.map
# vite-env.d.ts is hand-written (references ui/src/assets.d.ts) — whitelist it.
!packages/web/src/vite-env.d.ts
# findings.md #33 — these collide with Vite's `.js`-over-`.ts`
# resolver and shipped on a stale `tsc --emit`. ambient.d.ts is the
# only legitimate `.d.ts` in apps/desktop/src; whitelist it back.
Expand Down
53 changes: 45 additions & 8 deletions apps/desktop/electron-builder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,46 @@ extraResources:

mac:
category: public.app-category.developer-tools
icon: resources/icons/512x512.png
x64ArchFiles: 'Contents/Resources/app.asar.unpacked/node_modules/**/*.node'
icon: resources/icons/icon.icns
# Developer ID code-signing. The cert is supplied in CI via CSC_LINK /
# CSC_KEY_PASSWORD (gated to the macOS runners in release.yml) and locally via
# the matching identity in the login keychain.
identity: Julia Kafarska (8Y2UTZ2NBZ)
hardenedRuntime: true
gatekeeperAssess: false
# Entitlements live in resources/ (the buildResources dir, git-tracked) — NOT
# in build/, which is gitignored: the file would be absent in a fresh CI
# checkout and the custom entitlements (network.server,
# disable-library-validation, …) silently dropped during signing.
entitlements: resources/entitlements.mac.plist
entitlementsInherit: resources/entitlements.mac.plist
# Notarization is done locally (Pattern C) by scripts/notarize-release.mjs
# after CI, so CI never blocks on Apple's notary queue. Keep this false.
notarize: false
# No arch pinned on purpose: each native runner picks its own slice via the
# CLI flag (dist:mac:arm64 → --arm64, dist:mac:x64 → --x64). Pinning arch here
# overrides the CLI flag and cross-builds the broken off-arch slice (better-
# sqlite3 / Prisma engine come out wrong-arch on a non-native runner).
target:
- target: dmg
arch: [arm64]
- target: zip
arch: [arm64]
hardenedRuntime: false
- dmg
- zip

# Branded install window (background + drag-to-Applications layout). ${arch} in
# the name keeps the arm64 and x64 DMGs (built on separate runners) distinct.
dmg:
background: resources/dmg-background.png
iconSize: 80
window:
width: 500
height: 320
contents:
- x: 145
y: 75
- x: 350
y: 75
type: link
path: /Applications
artifactName: ${productName}-${version}-${arch}.${ext}

win:
icon: resources/icons/icon.ico
Expand All @@ -53,14 +85,19 @@ win:
nsis:
oneClick: false
allowToChangeInstallationDirectory: true
installerIcon: resources/icons/icon.ico
uninstallerIcon: resources/icons/icon.ico
artifactName: ICE-Setup-${version}.${ext}

linux:
icon: resources/icons
category: Development
# x64 only: ubuntu-latest is x64-only (no QEMU), so an arm64 target would fail
# the native rebuild and drop the whole Linux job. Add a native arm64 runner if
# arm64 Linux is ever required.
target:
- target: AppImage
arch: [x64, arm64]
arch: [x64]
- target: deb
arch: [x64]

Expand Down
Binary file added apps/desktop/resources/dmg-background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions apps/desktop/resources/dmg-background.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions apps/desktop/resources/entitlements.mac.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>
Binary file modified apps/desktop/resources/icon-rounded.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/desktop/resources/icons/1024x1024.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/desktop/resources/icons/128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/desktop/resources/icons/16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/desktop/resources/icons/24x24.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/desktop/resources/icons/256x256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/desktop/resources/icons/32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/desktop/resources/icons/48x48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/desktop/resources/icons/512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/desktop/resources/icons/64x64.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/desktop/resources/icons/icon.icns
Binary file not shown.
Binary file modified apps/desktop/resources/icons/icon.ico
Binary file not shown.
28 changes: 12 additions & 16 deletions docs/architecture/desktop.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ The Electron desktop app is a fully self-contained ICE: no separate server, no D
## Status

- Works for daily dev.
- Build pipeline is wired (`pnpm dist:desktop`, `dist:desktop:mac`, `:win`, `:linux`).
- **v0.1 binaries are not yet code-signed or notarized.** First-run on macOS shows the standard "unidentified developer" prompt; Windows shows SmartScreen. See "First-run instructions" below.
- Auto-update is wired through `electron-updater` against GitHub Releases - will activate once signed binaries are published.
- Build pipeline is wired (`pnpm dist:desktop`, `dist:desktop:mac:arm64`/`:x64`, `:win`, `:linux`).
- macOS ships **Apple Silicon (arm64) and Intel (x64)** builds, each built on a native runner; Linux ships **x64** AppImage + `.deb`; Windows ships an x64 NSIS installer.
- **macOS binaries are Developer ID code-signed and notarized** — signed in CI (`CSC_LINK`/`CSC_KEY_PASSWORD`) and notarized after CI via `scripts/notarize-release.mjs` ("Pattern C"). First-run is clean, no "unidentified developer" prompt. **Windows is still unsigned** (SmartScreen prompt) until an EV cert is wired up.
- Auto-update is wired through `electron-updater` against GitHub Releases. Because the two macOS arches are built on separate runners, publishing a single merged `latest-mac.yml` (so both arches get an update channel) is a pending follow-up.

## First-run instructions for v0.1 (unsigned)
## First-run instructions

### macOS

1. Download the `.dmg` from the GitHub release.
2. Drag ICE to Applications.
3. The first time you double-click, macOS will refuse and say it can't verify the developer. **Right-click → Open** → confirm the dialog. After the first run macOS remembers the choice.
4. Alternative: System Settings → Privacy & Security → scroll to the "ICE was blocked" message → **Open Anyway**.
Signed + notarized: download the `.dmg` (arm64 for Apple Silicon, x64 for Intel), drag ICE to Applications, and double-click — it opens with no Gatekeeper dialog.

> If a release was ever published before notarization completed, macOS may show "cannot verify the developer." Fallback: **Right-click → Open** → confirm, or System Settings → Privacy & Security → **Open Anyway**.

### Windows

Expand All @@ -28,17 +28,13 @@ The Electron desktop app is a fully self-contained ICE: no separate server, no D

`.AppImage` and `.deb` builds are unsigned but Linux distros generally don't gate on that. If the AppImage refuses to launch, install `libfuse2` (`sudo apt install libfuse2` on Debian/Ubuntu).

## v0.2 code-signing plan

Targets for v0.2:
## Code-signing status

- **macOS**: Apple Developer ID Application certificate + notarytool submission. Removes the "unidentified developer" prompt and activates Gatekeeper trust on first launch.
- **Windows**: EV (Extended Validation) certificate from a recognized CA, signing both `.exe` and `.msi`. EV is what gets SmartScreen to trust the binary without the "more info" prompt.
- **macOS — done.** Apple Developer ID Application certificate + `notarytool` submission. Signing runs in CI; notarization runs locally after CI (`scripts/notarize-release.mjs`, "Pattern C") so the build never blocks on Apple's notary queue. Removes the "unidentified developer" prompt. See [releasing.md](#) / `scripts/notarize-release.mjs` for the keychain-profile setup (`ice-notary`).
- **Windows — pending.** EV (Extended Validation) certificate from a recognized CA to sign the `.exe`. EV is what gets SmartScreen to trust the binary without the "more info" prompt. Wire via `WIN_CSC_LINK`/`WIN_CSC_KEY_PASSWORD`.
- **Linux**: keep `.AppImage` + `.deb` as the primary distribution; explore `flathub` and Snap once usage justifies it.

Once those certs are in place, `electron-updater` activates and the in-app updater takes over - no more "go to GitHub Releases" step for end users.

Code-signing cost is part of the project's operational budget; the ROADMAP entry tracks the procurement timeline.
Windows code-signing cost is part of the project's operational budget; the ROADMAP entry tracks the procurement timeline.

## Package layout

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ice",
"license": "Apache-2.0",
"version": "0.1.900",
"version": "0.1.902",
"description": "ICE - Integrated Cloud Environment (Web + Backend)",
"private": true,
"type": "module",
Expand All @@ -23,6 +23,8 @@
"build:desktop": "pnpm --filter @ice/desktop build",
"dist:desktop": "pnpm --filter @ice/desktop dist",
"dist:desktop:mac": "pnpm --filter @ice/desktop dist:mac",
"dist:desktop:mac:arm64": "pnpm --filter @ice/desktop dist:mac:arm64",
"dist:desktop:mac:x64": "pnpm --filter @ice/desktop dist:mac:x64",
"dist:desktop:win": "pnpm --filter @ice/desktop dist:win",
"dist:desktop:linux": "pnpm --filter @ice/desktop dist:linux",
"release": "node scripts/release.mjs",
Expand Down
51 changes: 51 additions & 0 deletions packages/ui/src/assets.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Type shims for asset imports resolved by Vite.
// SVG and image imports become string URLs when bundled.

interface ImportMetaEnv {
readonly VITE_API_URL?: string;
readonly [key: string]: string | undefined;
}

interface ImportMeta {
readonly env: ImportMetaEnv;
}

declare module '*.svg' {
const src: string;
export default src;
}

declare module '*.svg?url' {
const src: string;
export default src;
}

declare module '*.svg?raw' {
const src: string;
export default src;
}

declare module '*.png' {
const src: string;
export default src;
}

declare module '*.jpg' {
const src: string;
export default src;
}

declare module '*.jpeg' {
const src: string;
export default src;
}

declare module '*.webp' {
const src: string;
export default src;
}

declare module '*.gif' {
const src: string;
export default src;
}
2 changes: 2 additions & 0 deletions packages/web/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="../../ui/src/assets.d.ts" />
Loading
Loading