docs: Enhance documentation across multiple modules for clarity and structure
- Updated CONFIGURATION.md to improve navigation and added mermaid diagrams for better visualization of processes. - Revised IPC_ARCHITECTURE.md to clarify the security model and added diagrams to illustrate the architecture. - Improved README.md files in core-api, core-events, core-i18n, and core-storage for consistency and clarity, including better descriptions and structural enhancements.
This commit is contained in:
@@ -1,56 +1,56 @@
|
||||
# Auto-Update System
|
||||
[← Back to Root](../../../README.md)
|
||||
|
||||
The Unified Update Lifecycle.
|
||||
# Desktop Auto-Update System
|
||||
|
||||
> This document defines the strategic implementation of our cross-platform auto-update system. Powered by **electron-updater**, this architecture ensures seamless, background delivery of application patches, maintaining version parity across our global user base.
|
||||
`apps/desktop` utilizes a unified update lifecycle powered by **electron-updater**. This architecture ensures seamless, background delivery of application patches, maintaining version parity across our global user base.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Reactive Update Flow](#reactive-update-flow)
|
||||
- [Current Provider: GitHub Releases](#current-provider-github-releases)
|
||||
- [Release Workflow: The Deterministic Pipeline](#release-workflow-the-deterministic-pipeline)
|
||||
- [CI/CD Environment Variables](#cicd-environment-variables)
|
||||
- [Deployment Strategies](#deployment-strategies)
|
||||
- [Code Signing: The Trust Boundary](#code-signing-the-trust-boundary)
|
||||
- [Testing Updates in Development](#testing-updates-in-development)
|
||||
- [Diagnostic Runbook](#diagnostic-runbook)
|
||||
|
||||
---
|
||||
|
||||
## Reactive Update Flow
|
||||
## 🏗 Reactive Update Flow
|
||||
|
||||
The following diagram illustrates the **Reactive Update Flow**, bridging the Node.js Main Process with the React UI layer through a secure IPC event stream.
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ Main Process (src/main/index.ts) │
|
||||
│ │
|
||||
│ autoUpdater.checkForUpdatesAndNotify() │
|
||||
│ │ │
|
||||
│ ├─→ 'checking-for-update' │
|
||||
│ ├─→ 'update-available' → { version, releaseDate } │
|
||||
│ ├─→ 'download-progress' → { percent, bytesPerSecond } │
|
||||
│ ├─→ 'update-downloaded' → { version, releaseNotes } │
|
||||
│ └─→ 'error' → { message } │
|
||||
│ │
|
||||
│ sendToRenderer('updater:*', payload) │
|
||||
├──────────────────────── IPC Bridge ──────────────────────────────┤
|
||||
│ Preload (src/preload/index.ts) │
|
||||
│ │
|
||||
│ contextBridge.exposeInMainWorld('electronAPI', { │
|
||||
│ onUpdateAvailable, onDownloadProgress, │
|
||||
│ onUpdateDownloaded, onUpdateError, │
|
||||
│ checkForUpdates, installUpdate │
|
||||
│ }) │
|
||||
├──────────────────────── Renderer ────────────────────────────────┤
|
||||
│ React App (apps/web) │
|
||||
│ │
|
||||
│ useElectronUpdater() hook │
|
||||
│ → Reactive state: status, progress, updateInfo, errorMessage │
|
||||
│ → Actions: checkForUpdates(), installUpdate() │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```mermaid
|
||||
graph TD
|
||||
%% ─── Styling Definitions (Dark-Mode Friendly Enterprise Palette) ───
|
||||
classDef appEntity fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e3a8a
|
||||
classDef coreEntity fill:#e2e8f0,stroke:#64748b,stroke-width:2px,color:#0f172a
|
||||
classDef ipcBridge fill:#10b981,stroke:#047857,stroke-width:2px,color:#ffffff
|
||||
|
||||
%% ─── Subgraphs ───
|
||||
subgraph MainProcess ["Main Process (src/main/index.ts)"]
|
||||
AUTO[autoUpdater.checkForUpdatesAndNotify]
|
||||
EVENTS{{Update Events: progress, downloaded, error}}
|
||||
SENDER[sendToRenderer]
|
||||
end
|
||||
|
||||
subgraph Preload ["IPC Bridge (src/preload/index.ts)"]
|
||||
EXPOSE{contextBridge.exposeInMainWorld}
|
||||
end
|
||||
|
||||
subgraph Renderer ["Renderer (React App - apps/web)"]
|
||||
HOOK([useElectronUpdater Hook])
|
||||
ACTIONS[UI Actions: Install, Check]
|
||||
end
|
||||
|
||||
%% ─── Flow & Relationships ───
|
||||
AUTO ---> EVENTS
|
||||
EVENTS ---> SENDER
|
||||
SENDER ===>|'updater:*' Event Stream| EXPOSE
|
||||
EXPOSE ===>|electronAPI window object| HOOK
|
||||
HOOK -.->|Reactive State Status and Progress| ACTIONS
|
||||
ACTIONS -.->|ipcRenderer.invoke| EXPOSE
|
||||
EXPOSE -.->|Trigger Update or Install| AUTO
|
||||
|
||||
%% ─── Apply Styles ───
|
||||
class AUTO,EVENTS,SENDER coreEntity;
|
||||
class EXPOSE ipcBridge;
|
||||
class HOOK,ACTIONS appEntity;
|
||||
|
||||
%% ─── Subgraph Backgrounds (Transparent for Native GitHub Support) ───
|
||||
style MainProcess fill:transparent,stroke:#94a3b8,stroke-width:2px,stroke-dasharray: 5 5
|
||||
style Preload fill:transparent,stroke:#10b981,stroke-width:2px,stroke-dasharray: 5 5
|
||||
style Renderer fill:transparent,stroke:#3b82f6,stroke-width:2px,stroke-dasharray: 5 5
|
||||
```
|
||||
|
||||
### Lifecycle Sequence
|
||||
@@ -63,7 +63,7 @@ The following diagram illustrates the **Reactive Update Flow**, bridging the Nod
|
||||
|
||||
---
|
||||
|
||||
## Current Provider: GitHub Releases
|
||||
## 🌐 Current Provider: GitHub Releases
|
||||
|
||||
The update provider is declared in `electron-builder.yml`:
|
||||
|
||||
@@ -77,6 +77,7 @@ publish:
|
||||
### Operational Mechanics
|
||||
|
||||
1. When `electron-builder --publish always` executes, it:
|
||||
|
||||
- Compiles the application for the target platform.
|
||||
- Uploads the installer(s) to a **GitHub Release** tagged with the version from `package.json`.
|
||||
- Generates and uploads the platform-specific manifest: `latest.yml` (Windows), `latest-mac.yml` (macOS), or `latest-linux.yml` (Linux).
|
||||
@@ -88,7 +89,7 @@ publish:
|
||||
|
||||
---
|
||||
|
||||
## Release Workflow: The Deterministic Pipeline
|
||||
## 🚀 Release Workflow: The Deterministic Pipeline
|
||||
|
||||
To maintain release integrity, follow this deterministic pipeline to synchronize web assets and native binaries.
|
||||
|
||||
@@ -109,8 +110,7 @@ pnpm run prebuild
|
||||
GH_TOKEN=your_token electron-builder --publish always --config electron-builder.yml
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> **Treat `GH_TOKEN` as a critical secret.** It grants write access to your repository's release assets. Never commit it to version control, never log it in CI output, and always inject it via encrypted secrets or a vault.
|
||||
> [!CAUTION] > **Treat `GH_TOKEN` as a critical secret.** It grants write access to your repository's release assets. Never commit it to version control, never log it in CI output, and always inject it via encrypted secrets or a vault.
|
||||
|
||||
### Automated Release (GitHub Actions)
|
||||
|
||||
@@ -155,18 +155,18 @@ jobs:
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Environment Variables
|
||||
## 🔐 CI/CD Environment Variables
|
||||
|
||||
| Variable | Required | Platform | Description |
|
||||
|---|---|---|---|
|
||||
| `GH_TOKEN` | Yes | All | GitHub personal access token with `repo` scope. Authorizes release artifact uploads. |
|
||||
| `CSC_LINK` | macOS/Windows | macOS, Windows | Base64-encoded `.p12` code signing certificate. Generate with: `base64 -i cert.p12 \| pbcopy` |
|
||||
| `CSC_KEY_PASSWORD` | macOS/Windows | macOS, Windows | Passphrase for the `.p12` certificate. |
|
||||
| `APPLE_ID` | macOS only | macOS | Apple ID email for notarization submission. |
|
||||
| `APPLE_APP_SPECIFIC_PASSWORD` | macOS only | macOS | App-specific password generated at [appleid.apple.com](https://appleid.apple.com). |
|
||||
| `APPLE_TEAM_ID` | macOS only | macOS | Your Apple Developer Team ID. |
|
||||
| `WIN_CSC_LINK` | Windows only | Windows | Separate Windows code signing certificate (if different from `CSC_LINK`). |
|
||||
| `WIN_CSC_KEY_PASSWORD` | Windows only | Windows | Passphrase for the Windows certificate. |
|
||||
| Variable | Required | Platform | Description |
|
||||
| ----------------------------- | ------------- | -------------- | --------------------------------------------------------------------------------------------- |
|
||||
| `GH_TOKEN` | Yes | All | GitHub personal access token with `repo` scope. Authorizes release artifact uploads. |
|
||||
| `CSC_LINK` | macOS/Windows | macOS, Windows | Base64-encoded `.p12` code signing certificate. Generate with: `base64 -i cert.p12 \| pbcopy` |
|
||||
| `CSC_KEY_PASSWORD` | macOS/Windows | macOS, Windows | Passphrase for the `.p12` certificate. |
|
||||
| `APPLE_ID` | macOS only | macOS | Apple ID email for notarization submission. |
|
||||
| `APPLE_APP_SPECIFIC_PASSWORD` | macOS only | macOS | App-specific password generated at [appleid.apple.com](https://appleid.apple.com). |
|
||||
| `APPLE_TEAM_ID` | macOS only | macOS | Your Apple Developer Team ID. |
|
||||
| `WIN_CSC_LINK` | Windows only | Windows | Separate Windows code signing certificate (if different from `CSC_LINK`). |
|
||||
| `WIN_CSC_KEY_PASSWORD` | Windows only | Windows | Passphrase for the Windows certificate. |
|
||||
|
||||
### Configuring Secrets
|
||||
|
||||
@@ -176,7 +176,7 @@ jobs:
|
||||
|
||||
---
|
||||
|
||||
## Deployment Strategies
|
||||
## ☁️ Deployment Strategies
|
||||
|
||||
### AWS S3 (Private Infrastructure)
|
||||
|
||||
@@ -195,13 +195,14 @@ publish:
|
||||
|
||||
**Additional environment variables:**
|
||||
|
||||
| Variable | Description |
|
||||
|---|---|
|
||||
| `AWS_ACCESS_KEY_ID` | IAM access key with S3 `PutObject` and `GetObject` permissions |
|
||||
| `AWS_SECRET_ACCESS_KEY` | IAM secret key |
|
||||
| Variable | Description |
|
||||
| ----------------------- | -------------------------------------------------------------- |
|
||||
| `AWS_ACCESS_KEY_ID` | IAM access key with S3 `PutObject` and `GetObject` permissions |
|
||||
| `AWS_SECRET_ACCESS_KEY` | IAM secret key |
|
||||
|
||||
**Bucket structure:**
|
||||
```
|
||||
|
||||
```text
|
||||
your-bucket/desktop-releases/
|
||||
├── latest.yml (Windows manifest)
|
||||
├── latest-mac.yml (macOS manifest)
|
||||
@@ -222,13 +223,12 @@ For self-hosted infrastructure (Nginx, Caddy, etc.):
|
||||
```yaml
|
||||
publish:
|
||||
provider: generic
|
||||
url: https://updates.your-domain.com/desktop
|
||||
url: [https://updates.your-domain.com/desktop](https://updates.your-domain.com/desktop)
|
||||
```
|
||||
|
||||
Your server must host the same directory structure as the S3 layout above.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **MIME Type Configuration**: Ensure your file server correctly serves `.yml` files with `text/yaml` and installer binaries with `application/octet-stream`. Incorrect MIME types will cause download corruption or silent update failures.
|
||||
> [!IMPORTANT] > **MIME Type Configuration**: Ensure your file server correctly serves `.yml` files with `text/yaml` and installer binaries with `application/octet-stream`. Incorrect MIME types will cause download corruption or silent update failures.
|
||||
|
||||
**Nginx reference:**
|
||||
|
||||
@@ -253,10 +253,9 @@ server {
|
||||
|
||||
---
|
||||
|
||||
## Code Signing: The Trust Boundary
|
||||
## 🛡️ Code Signing: The Trust Boundary
|
||||
|
||||
> [!WARNING]
|
||||
> **Code signing is not merely a requirement — it is the Trust Boundary established by the operating system.** macOS Gatekeeper will explicitly terminate unsigned applications or refuse background updates to maintain system integrity. Windows SmartScreen will display alarming warnings to users. Without valid signatures, `electron-updater` will **reject update payloads entirely**.
|
||||
> [!WARNING] > **Code signing is not merely a requirement — it is the Trust Boundary established by the operating system.** macOS Gatekeeper will explicitly terminate unsigned applications or refuse background updates to maintain system integrity. Windows SmartScreen will display alarming warnings to users. Without valid signatures, `electron-updater` will **reject update payloads entirely**.
|
||||
|
||||
### macOS
|
||||
|
||||
@@ -273,7 +272,7 @@ server {
|
||||
```xml
|
||||
<?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">
|
||||
"[http://www.apple.com/DTDs/PropertyList-1.0.dtd](http://www.apple.com/DTDs/PropertyList-1.0.dtd)">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
@@ -300,7 +299,7 @@ server {
|
||||
|
||||
---
|
||||
|
||||
## Testing Updates in Development
|
||||
## 🧪 Testing Updates in Development
|
||||
|
||||
> [!NOTE]
|
||||
> The auto-updater is **intentionally disabled** in development mode to prevent runtime crashes. Setting `forceDevUpdateConfig` requires a `dev-app-update.yml` file, which introduces unnecessary complexity during local development.
|
||||
@@ -317,6 +316,7 @@ if (IS_DEV) {
|
||||
```
|
||||
|
||||
This means:
|
||||
|
||||
- No update check is performed on startup.
|
||||
- No `electron-updater` events are emitted.
|
||||
- The `useElectronUpdater()` hook will remain in `idle` status.
|
||||
@@ -347,52 +347,34 @@ python3 -m http.server 8080 --directory .
|
||||
|
||||
---
|
||||
|
||||
## Diagnostic Runbook
|
||||
## ⚠️ Diagnostic Runbook
|
||||
|
||||
### Issue: "Update check failed" on startup
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Symptom** | Console logs `[AutoUpdater] Startup check failed (possibly offline)` |
|
||||
| **Root Cause** | The machine is offline, or the update server (GitHub/S3/generic) is unreachable. |
|
||||
| **Resolution** | No action required. The error is caught in a `try/catch` block, logged to the console, and the application continues to function normally. The next check will occur on the next app launch. |
|
||||
|
||||
---
|
||||
| Symptom | Root Cause | Resolution |
|
||||
| -------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Console logs `[AutoUpdater] Startup check failed (possibly offline)` | The machine is offline, or the update server (GitHub/S3/generic) is unreachable. | No action required. The error is caught in a `try/catch` block, logged to the console, and the application continues to function normally. The next check will occur on the next app launch. |
|
||||
|
||||
### Issue: `app-update.yml` not found in production build
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Symptom** | `electron-updater` throws "Cannot find app-update.yml" immediately after launch. |
|
||||
| **Root Cause** | The `publish` block in `electron-builder.yml` is missing or misconfigured. `electron-builder` generates `app-update.yml` only when a valid provider is declared. |
|
||||
| **Resolution** | Verify the `publish` block exists in `electron-builder.yml`. Run `electron-builder --publish never` and inspect `release/*/resources/app-update.yml` to confirm generation. |
|
||||
|
||||
---
|
||||
| Symptom | Root Cause | Resolution |
|
||||
| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `electron-updater` throws "Cannot find app-update.yml" immediately after launch. | The `publish` block in `electron-builder.yml` is missing or misconfigured. `electron-builder` generates `app-update.yml` only when a valid provider is declared. | Verify the `publish` block exists in `electron-builder.yml`. Run `electron-builder --publish never` and inspect `release/*/resources/app-update.yml` to confirm generation. |
|
||||
|
||||
### Issue: "Cannot update: code signature is invalid" (macOS)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Symptom** | The updater downloads a new version but refuses to apply it, logging a signature validation error. |
|
||||
| **Root Cause** | The application was not signed, or the signing certificate has expired / been revoked. |
|
||||
| **Resolution** | Ensure `CSC_LINK` and `CSC_KEY_PASSWORD` are correctly set in CI. Verify the packaged app with: `codesign --verify --deep --strict release/mac*/Desktop.app`. Re-sign and re-publish if the certificate was rotated. |
|
||||
|
||||
---
|
||||
| Symptom | Root Cause | Resolution |
|
||||
| -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| The updater downloads a new version but refuses to apply it, logging a signature validation error. | The application was not signed, or the signing certificate has expired / been revoked. | Ensure `CSC_LINK` and `CSC_KEY_PASSWORD` are correctly set in CI. Verify the packaged app with: `codesign --verify --deep --strict release/mac*/Desktop.app`. Re-sign and re-publish if the certificate was rotated. |
|
||||
|
||||
### Issue: Updates work on Windows/Linux but not macOS
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Symptom** | Windows and Linux users receive updates, but macOS users see no update prompt. |
|
||||
| **Root Cause** | macOS requires **both** a valid code signature AND Apple notarization. Without notarization, Gatekeeper silently quarantines the update payload. |
|
||||
| **Resolution** | Provide all Apple credential environment variables (`APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID`) and ensure `hardenedRuntime: true` is set in `electron-builder.yml`. Re-package and re-publish. |
|
||||
|
||||
---
|
||||
| Symptom | Root Cause | Resolution |
|
||||
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Windows and Linux users receive updates, but macOS users see no update prompt. | macOS requires **both** a valid code signature AND Apple notarization. Without notarization, Gatekeeper silently quarantines the update payload. | Provide all Apple credential environment variables (`APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID`) and ensure `hardenedRuntime: true` is set in `electron-builder.yml`. Re-package and re-publish. |
|
||||
|
||||
### Issue: S3/Generic provider returns corrupted downloads
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Symptom** | Users report that the update downloads but fails to install, or the downloaded file is 0 bytes. |
|
||||
| **Root Cause** | The file server is serving update manifests or binaries with incorrect MIME types, or a CDN is caching stale `latest*.yml` files. |
|
||||
| **Resolution** | Verify MIME types: `.yml` → `text/yaml`, `.exe`/`.dmg`/`.AppImage`/`.zip` → `application/octet-stream`. Add `Cache-Control: no-cache` headers to `latest*.yml` responses. Invalidate CDN cache after publishing a new release. |
|
||||
| Symptom | Root Cause | Resolution |
|
||||
| ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Users report that the update downloads but fails to install, or the downloaded file is 0 bytes. | The file server is serving update manifests or binaries with incorrect MIME types, or a CDN is caching stale `latest*.yml` files. | Verify MIME types: `.yml` → `text/yaml`, `.exe`/`.dmg`/`.AppImage`/`.zip` → `application/octet-stream`. Add `Cache-Control: no-cache` headers to `latest*.yml` responses. Invalidate CDN cache after publishing a new release. |
|
||||
|
||||
Reference in New Issue
Block a user