mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-11 23:02:56 +01:00
Refactored dev-docs folder after multiple additions to organise correctly
This commit is contained in:
153
dev-docs/tasks/phase-8-distribution/PROGRESS.md
Normal file
153
dev-docs/tasks/phase-8-distribution/PROGRESS.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# Phase 8: Auto-Update & Distribution - Progress Tracker
|
||||
|
||||
**Last Updated:** 2026-01-07
|
||||
**Overall Status:** 🔴 Not Started
|
||||
|
||||
---
|
||||
|
||||
## Quick Summary
|
||||
|
||||
| Metric | Value |
|
||||
| ------------ | ------ |
|
||||
| Total Tasks | 5 |
|
||||
| Completed | 0 |
|
||||
| In Progress | 0 |
|
||||
| Not Started | 5 |
|
||||
| **Progress** | **0%** |
|
||||
|
||||
**Estimated Effort:** 38-56 hours (excluding optional Windows signing)
|
||||
|
||||
---
|
||||
|
||||
## Task Status
|
||||
|
||||
| Task | Name | Status | Effort | Doc |
|
||||
| ---- | -------------------------------- | -------------- | ------ | -------------------------------------------- |
|
||||
| 7.1 | Rebrand to Nodegex | 🔴 Not Started | 4-6h | [TASK-7.1](./TASK-7.1-rebrand-nodegex.md) |
|
||||
| 7.2 | Fix macOS Code Signing | 🔴 Not Started | 8-12h | [TASK-7.2](./TASK-7.2-macos-signing.md) |
|
||||
| 7.3 | Configure Auto-Update Publishing | 🔴 Not Started | 4-6h | [TASK-7.3](./TASK-7.3-auto-update-config.md) |
|
||||
| 7.4 | Linux Universal Distribution | 🔴 Not Started | 6-8h | [TASK-7.4](./TASK-7.4-linux-distribution.md) |
|
||||
| 7.5 | GitHub Actions CI/CD | 🔴 Not Started | 12-16h | [TASK-7.5](./TASK-7.5-github-actions.md) |
|
||||
| 7.6 | Windows Code Signing | 🔴 Not Started | 4-8h | _(optional, no doc yet)_ |
|
||||
|
||||
---
|
||||
|
||||
## Task Details
|
||||
|
||||
### 7.1 Rebrand to Nodegex
|
||||
|
||||
**Status:** 🔴 Not Started
|
||||
|
||||
Rename application from OpenNoodl to Nodegex across all user-facing surfaces:
|
||||
|
||||
- Package.json productName, appId, protocols
|
||||
- Window titles and UI strings
|
||||
- Protocol handlers (`nodegex://`)
|
||||
- userData paths with migration for existing users
|
||||
|
||||
### 7.2 Fix macOS Code Signing
|
||||
|
||||
**Status:** 🔴 Not Started
|
||||
|
||||
Configure electron-builder for automatic signing (eliminates 30+ manual file signatures):
|
||||
|
||||
- Certificate configuration via `CSC_NAME`
|
||||
- Entitlements for hardened runtime
|
||||
- Automatic notarization via afterSign hook
|
||||
- Support for both Intel and Apple Silicon
|
||||
|
||||
### 7.3 Configure Auto-Update Publishing
|
||||
|
||||
**Status:** 🔴 Not Started
|
||||
|
||||
Connect existing electron-updater infrastructure to GitHub Releases:
|
||||
|
||||
- Add publish configuration to package.json
|
||||
- Configure update server URL
|
||||
- Generate `latest-*.yml` manifests
|
||||
- Test update detection and installation
|
||||
|
||||
### 7.4 Linux Universal Distribution
|
||||
|
||||
**Status:** 🔴 Not Started
|
||||
|
||||
Add AppImage and .deb targets:
|
||||
|
||||
- AppImage for universal distribution (auto-update supported)
|
||||
- .deb for Debian/Ubuntu native experience
|
||||
- Handle native module compatibility (dugite, desktop-trampoline)
|
||||
- Test on Ubuntu 22.04/24.04 LTS
|
||||
|
||||
### 7.5 GitHub Actions CI/CD
|
||||
|
||||
**Status:** 🔴 Not Started
|
||||
|
||||
Create automated build pipeline:
|
||||
|
||||
- Matrix build for macOS (x64, arm64), Windows (x64), Linux (x64)
|
||||
- Secure certificate storage via GitHub Secrets
|
||||
- Automatic GitHub Release creation on tag push
|
||||
- Update manifest generation
|
||||
|
||||
### 7.6 Windows Code Signing (Optional)
|
||||
|
||||
**Status:** 🔴 Not Started
|
||||
|
||||
Add Windows code signing to eliminate SmartScreen warnings:
|
||||
|
||||
- Obtain code signing certificate (EV or standard)
|
||||
- Configure in electron-builder
|
||||
- Add to CI/CD pipeline
|
||||
|
||||
---
|
||||
|
||||
## Status Legend
|
||||
|
||||
- 🔴 **Not Started** - Work has not begun
|
||||
- 🟡 **In Progress** - Actively being worked on
|
||||
- 🟢 **Complete** - Finished and verified
|
||||
|
||||
---
|
||||
|
||||
## Recent Updates
|
||||
|
||||
| Date | Update |
|
||||
| ---------- | ------------------------------------------------- |
|
||||
| 2026-01-07 | Updated PROGRESS.md to reflect actual task status |
|
||||
| 2026-01-07 | Renumbered from Phase 7 to Phase 8 |
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
**Depends on:** Phase 0-3 (stable editor)
|
||||
|
||||
**Task Dependencies:**
|
||||
|
||||
```
|
||||
7.1 Rebrand ──┬──► 7.2 macOS Signing ──┐
|
||||
├──► 7.3 Auto-Update ────┼──► 7.5 GitHub Actions CI/CD
|
||||
└──► 7.4 Linux Distro ───┘
|
||||
│
|
||||
▼
|
||||
7.6 Windows Signing (optional)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. ✅ User can receive update notification without losing projects
|
||||
2. ✅ macOS build requires zero manual signing steps
|
||||
3. ✅ Linux AppImage runs on Ubuntu 22.04+ without dependencies
|
||||
4. ✅ `git tag v1.2.0 && git push --tags` triggers full release
|
||||
5. ✅ All UI shows "Nodegex" branding
|
||||
6. ✅ Existing OpenNoodl users' data migrates automatically
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
Previously Phase 7 "auto-update-and-distribution". Covers macOS code signing, Windows signing, auto-update infrastructure, Linux distribution, and GitHub Actions CI/CD.
|
||||
|
||||
See [README.md](./README.md) for comprehensive technical analysis and architecture decisions.
|
||||
203
dev-docs/tasks/phase-8-distribution/README.md
Normal file
203
dev-docs/tasks/phase-8-distribution/README.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# Phase 8: Auto-Update & Cross-Platform Deployment Infrastructure
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Phase 8 transforms Nodegex from a manually-distributed application requiring full reinstalls into a professionally deployed desktop application with seamless auto-updates across Windows, macOS (Intel & Apple Silicon), and Linux.
|
||||
|
||||
**Current Pain Points:**
|
||||
- Manual code signing of 30+ files for each macOS build
|
||||
- Users must download and reinstall for every update, losing local preferences
|
||||
- No Linux universal distribution
|
||||
- No automated CI/CD pipeline
|
||||
- Rebranding from OpenNoodl to Nodegex not complete
|
||||
|
||||
**End State:**
|
||||
- Push a git tag → GitHub Actions builds all platforms → Users see "Update Available" → One-click update
|
||||
- User data (projects, preferences) persists across updates
|
||||
- Professional code signing handled automatically
|
||||
- Linux support via AppImage (universal) and .deb (Debian/Ubuntu)
|
||||
|
||||
## Why This Matters
|
||||
|
||||
1. **User Experience**: Currently users must re-add all projects after every update. This is a deal-breaker for adoption.
|
||||
2. **Development Velocity**: Manual signing and packaging takes hours per release. Automated CI/CD enables rapid iteration.
|
||||
3. **Community Growth**: Linux users are a significant portion of the open-source developer community.
|
||||
4. **Professional Credibility**: Auto-updates are expected in modern desktop applications.
|
||||
|
||||
## Technical Analysis
|
||||
|
||||
### Existing Infrastructure (What We Have)
|
||||
|
||||
| Component | Status | Location |
|
||||
|-----------|--------|----------|
|
||||
| electron-updater | ✅ Installed | `autoupdater.js` |
|
||||
| Update UI | ✅ Complete | `BaseWindow.tsx`, `TitleBar` |
|
||||
| Notarization script | ✅ Exists | `build/macos-notarize.js` |
|
||||
| electron-builder config | ⚠️ Incomplete | `package.json` build section |
|
||||
| Publish config | ❌ Missing | Needs GitHub Releases setup |
|
||||
| CI/CD | ❌ Missing | Needs GitHub Actions |
|
||||
|
||||
### The Mac Signing Problem Diagnosed
|
||||
|
||||
The 30+ manual signatures happen because **electron-builder's automatic signing isn't configured correctly**.
|
||||
|
||||
When properly configured, electron-builder signs in this order (automatically):
|
||||
1. All binaries in `asar.unpacked` (dugite, desktop-trampoline)
|
||||
2. Helper apps (GPU, Plugin, Renderer)
|
||||
3. Frameworks (Electron, Squirrel, Mantle, ReactiveObjC)
|
||||
4. Main executable
|
||||
5. The .app bundle itself
|
||||
|
||||
**Root Cause**: Missing `CSC_LINK` or `CSC_NAME` environment variable. Without this, electron-builder skips signing entirely, then notarization fails.
|
||||
|
||||
**The Fix**:
|
||||
```bash
|
||||
# Option 1: Certificate file (for CI)
|
||||
export CSC_LINK="path/to/certificate.p12"
|
||||
export CSC_KEY_PASSWORD="certificate-password"
|
||||
|
||||
# Option 2: Keychain certificate (for local builds)
|
||||
export CSC_NAME="Developer ID Application: Osborne Solutions (Y35J975HXR)"
|
||||
```
|
||||
|
||||
### User Data Persistence
|
||||
|
||||
This is already solved by Electron's architecture:
|
||||
|
||||
| Platform | userData Location | Survives Updates? |
|
||||
|----------|------------------|-------------------|
|
||||
| Windows | `%APPDATA%/Nodegex` | ✅ Yes |
|
||||
| macOS | `~/Library/Application Support/Nodegex` | ✅ Yes |
|
||||
| Linux | `~/.config/Nodegex` | ✅ Yes |
|
||||
|
||||
The project list uses `localStorage` which is stored in `userData`. The reason Richard is losing data is because users are doing **fresh installs** (delete app, download new, install) rather than using the auto-update mechanism.
|
||||
|
||||
Once auto-update works, this problem disappears.
|
||||
|
||||
## Task Breakdown
|
||||
|
||||
### Task 7.1: Rebrand to Nodegex
|
||||
**Effort**: 4-6 hours | **Complexity**: Low
|
||||
|
||||
Update all user-facing references from OpenNoodl/Noodl to Nodegex:
|
||||
- `package.json` productName, appId, description
|
||||
- Window titles and UI strings
|
||||
- Protocol handlers (`nodegex://`)
|
||||
- userData paths (with migration for existing users)
|
||||
- Documentation and comments
|
||||
|
||||
### Task 7.2: Fix macOS Code Signing
|
||||
**Effort**: 8-12 hours | **Complexity**: High
|
||||
|
||||
Configure electron-builder to sign automatically:
|
||||
- Verify certificate is in keychain correctly
|
||||
- Add `CSC_NAME` to build environment
|
||||
- Test that all 30+ files are signed automatically
|
||||
- Verify notarization succeeds
|
||||
- Test on both Intel and Apple Silicon
|
||||
|
||||
### Task 7.3: Configure Auto-Update Publishing
|
||||
**Effort**: 4-6 hours | **Complexity**: Medium
|
||||
|
||||
Add GitHub Releases as update source:
|
||||
- Add `publish` config to package.json
|
||||
- Configure update server URL
|
||||
- Test update detection and download
|
||||
- Test quit-and-install flow
|
||||
|
||||
### Task 7.4: Linux Universal Distribution
|
||||
**Effort**: 6-8 hours | **Complexity**: Medium
|
||||
|
||||
Add AppImage and .deb targets:
|
||||
- Configure AppImage with auto-update support
|
||||
- Configure .deb for Debian/Ubuntu
|
||||
- Handle native module compatibility
|
||||
- Test on Ubuntu 22.04/24.04
|
||||
|
||||
### Task 7.5: GitHub Actions CI/CD
|
||||
**Effort**: 12-16 hours | **Complexity**: High
|
||||
|
||||
Create automated build pipeline:
|
||||
- Matrix build for all platforms/architectures
|
||||
- Secure certificate storage via GitHub Secrets
|
||||
- Automatic GitHub Release creation
|
||||
- Version tagging workflow
|
||||
|
||||
### Task 7.6: Windows Code Signing (Optional Enhancement)
|
||||
**Effort**: 4-8 hours | **Complexity**: Medium
|
||||
|
||||
Add Windows code signing to eliminate SmartScreen warnings:
|
||||
- Obtain code signing certificate (EV or standard)
|
||||
- Configure in electron-builder
|
||||
- Add to CI/CD pipeline
|
||||
|
||||
## Architecture Decisions
|
||||
|
||||
### Update Distribution: GitHub Releases
|
||||
|
||||
**Why GitHub Releases over other options:**
|
||||
|
||||
| Option | Pros | Cons |
|
||||
|--------|------|------|
|
||||
| GitHub Releases | Free, integrated with repo, electron-updater native support | Public releases only |
|
||||
| S3/CloudFront | Private releases, full control | Cost, complexity |
|
||||
| Nuts/Hazel | More control | Self-hosted, maintenance |
|
||||
| Electron Forge | Modern tooling | Migration effort |
|
||||
|
||||
**Decision**: GitHub Releases - simplest path, zero cost, electron-builder native support.
|
||||
|
||||
### Linux Format: AppImage + .deb
|
||||
|
||||
**Why AppImage:**
|
||||
- Single file, no installation required
|
||||
- Works on any Linux distribution
|
||||
- electron-updater supports AppImage auto-updates
|
||||
- No root required
|
||||
|
||||
**Why .deb:**
|
||||
- Native experience for Debian/Ubuntu users (60%+ of Linux desktop)
|
||||
- Integrates with system package manager
|
||||
- Desktop integration (menus, file associations)
|
||||
|
||||
### Signing Certificate Storage
|
||||
|
||||
**Local Development**: Keychain (macOS) / Certificate Store (Windows)
|
||||
**CI/CD**: GitHub Secrets with base64-encoded certificates
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. ✅ User can receive update notification without losing projects
|
||||
2. ✅ macOS build requires zero manual signing steps
|
||||
3. ✅ Linux AppImage runs on Ubuntu 22.04+ without dependencies
|
||||
4. ✅ `git tag v1.2.0 && git push --tags` triggers full release
|
||||
5. ✅ All UI shows "Nodegex" branding
|
||||
6. ✅ Existing OpenNoodl users' data migrates automatically
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|------|------------|--------|------------|
|
||||
| Apple certificate issues | Medium | High | Document exact certificate setup steps |
|
||||
| Native module compatibility | Medium | Medium | Test dugite/desktop-trampoline on all platforms |
|
||||
| Auto-update breaks for some users | Low | High | Include manual download fallback |
|
||||
| Linux dependency issues | Medium | Medium | Test on fresh VM installations |
|
||||
|
||||
## Timeline Estimate
|
||||
|
||||
| Task | Effort | Dependencies |
|
||||
|------|--------|--------------|
|
||||
| 7.1 Rebrand | 4-6h | None |
|
||||
| 7.2 macOS Signing | 8-12h | 7.1 |
|
||||
| 7.3 Auto-Update Config | 4-6h | 7.1 |
|
||||
| 7.4 Linux Distribution | 6-8h | 7.1 |
|
||||
| 7.5 GitHub Actions | 12-16h | 7.2, 7.3, 7.4 |
|
||||
| 7.6 Windows Signing | 4-8h | 7.5 (optional) |
|
||||
|
||||
**Total**: 38-56 hours (excluding Windows signing)
|
||||
|
||||
## References
|
||||
|
||||
- [electron-builder Code Signing](https://www.electron.build/code-signing)
|
||||
- [electron-updater Documentation](https://www.electron.build/auto-update)
|
||||
- [Apple Notarization](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution)
|
||||
- [GitHub Actions for Electron](https://www.electron.build/multi-platform-build#github-actions)
|
||||
303
dev-docs/tasks/phase-8-distribution/TASK-7.1-rebrand-nodegex.md
Normal file
303
dev-docs/tasks/phase-8-distribution/TASK-7.1-rebrand-nodegex.md
Normal file
@@ -0,0 +1,303 @@
|
||||
# Task 7.1: Rebrand to Nodegex
|
||||
|
||||
## Overview
|
||||
|
||||
Rename the application from "OpenNoodl" to "Nodegex" (Node Graph Expression) across all user-facing surfaces while maintaining backward compatibility for existing users.
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
- Application name and branding
|
||||
- Window titles
|
||||
- Protocol handlers
|
||||
- Package identifiers
|
||||
- userData path (with migration)
|
||||
- Documentation references
|
||||
|
||||
### Out of Scope (Keep as "noodl")
|
||||
- Internal package names (noodl-editor, noodl-runtime, etc.)
|
||||
- Code variables and function names
|
||||
- Git history
|
||||
- NPM package names (if published)
|
||||
|
||||
## Changes Required
|
||||
|
||||
### 1. Package Configuration
|
||||
|
||||
**`packages/noodl-editor/package.json`**
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "noodl-editor", // Keep internal
|
||||
"productName": "Nodegex", // Change from OpenNoodl
|
||||
"description": "Full stack low-code React app builder - Nodegex",
|
||||
"author": "The Low Code Foundation",
|
||||
"homepage": "https://nodegex.dev", // Update when domain ready
|
||||
"build": {
|
||||
"appId": "com.nodegex.app", // Change from com.opennoodl.app
|
||||
"protocols": {
|
||||
"name": "nodegex", // Change from opennoodl
|
||||
"schemes": ["nodegex"] // Change from opennoodl
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Main Process
|
||||
|
||||
**`packages/noodl-editor/src/main/main.js`**
|
||||
|
||||
Update any hardcoded "OpenNoodl" or "Noodl" strings in:
|
||||
- Window titles
|
||||
- Dialog messages
|
||||
- Menu items
|
||||
|
||||
```javascript
|
||||
// Example changes
|
||||
const mainWindow = new BrowserWindow({
|
||||
title: 'Nodegex', // Was: OpenNoodl
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Window Titles
|
||||
|
||||
**`packages/noodl-editor/src/editor/src/views/windows/BaseWindow/BaseWindow.tsx`**
|
||||
|
||||
```typescript
|
||||
export function BaseWindow({
|
||||
title = ProjectModel.instance.name, // Default project name as title
|
||||
// ...
|
||||
}) {
|
||||
// The TitleBar component may show app name
|
||||
}
|
||||
```
|
||||
|
||||
**`packages/noodl-core-ui/src/components/app/TitleBar/TitleBar.tsx`**
|
||||
|
||||
Check for any hardcoded "Noodl" references.
|
||||
|
||||
### 4. Platform Identification
|
||||
|
||||
**`packages/noodl-platform-electron/src/platform-electron.ts`**
|
||||
|
||||
The userData path is determined by Electron using productName. After changing productName, the path becomes:
|
||||
- Windows: `%APPDATA%/Nodegex`
|
||||
- macOS: `~/Library/Application Support/Nodegex`
|
||||
- Linux: `~/.config/Nodegex`
|
||||
|
||||
### 5. User Data Migration
|
||||
|
||||
**Critical**: Existing users have data in the old location. We need migration.
|
||||
|
||||
**Create: `packages/noodl-editor/src/main/src/migration.js`**
|
||||
|
||||
```javascript
|
||||
const { app } = require('electron');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const OLD_NAMES = ['OpenNoodl', 'Noodl']; // Possible old names
|
||||
const NEW_NAME = 'Nodegex';
|
||||
|
||||
function migrateUserData() {
|
||||
const userDataPath = app.getPath('userData');
|
||||
|
||||
// Check if we're already in the new location
|
||||
if (userDataPath.includes(NEW_NAME)) {
|
||||
// Look for old data to migrate
|
||||
for (const oldName of OLD_NAMES) {
|
||||
const oldPath = userDataPath.replace(NEW_NAME, oldName);
|
||||
|
||||
if (fs.existsSync(oldPath) && !fs.existsSync(path.join(userDataPath, '.migrated'))) {
|
||||
console.log(`Migrating user data from ${oldPath} to ${userDataPath}`);
|
||||
|
||||
// Copy contents (not move, safer)
|
||||
copyDirectory(oldPath, userDataPath);
|
||||
|
||||
// Mark as migrated
|
||||
fs.writeFileSync(path.join(userDataPath, '.migrated'), oldPath);
|
||||
|
||||
console.log('Migration complete');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function copyDirectory(src, dest) {
|
||||
if (!fs.existsSync(dest)) {
|
||||
fs.mkdirSync(dest, { recursive: true });
|
||||
}
|
||||
|
||||
const entries = fs.readdirSync(src, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.join(src, entry.name);
|
||||
const destPath = path.join(dest, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
copyDirectory(srcPath, destPath);
|
||||
} else {
|
||||
// Don't overwrite existing files in new location
|
||||
if (!fs.existsSync(destPath)) {
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { migrateUserData };
|
||||
```
|
||||
|
||||
**Update: `packages/noodl-editor/src/main/main.js`**
|
||||
|
||||
```javascript
|
||||
const { migrateUserData } = require('./src/migration');
|
||||
|
||||
app.on('ready', () => {
|
||||
// Migrate before anything else
|
||||
migrateUserData();
|
||||
|
||||
// ... rest of app initialization
|
||||
});
|
||||
```
|
||||
|
||||
### 6. Protocol Handler
|
||||
|
||||
Update deep links from `opennoodl://` to `nodegex://`
|
||||
|
||||
**`packages/noodl-editor/src/main/main.js`**
|
||||
|
||||
```javascript
|
||||
// Register protocol handler
|
||||
app.setAsDefaultProtocolClient('nodegex');
|
||||
|
||||
// Handle incoming URLs
|
||||
app.on('open-url', (event, url) => {
|
||||
if (url.startsWith('nodegex://')) {
|
||||
// Handle URL
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 7. macOS Info.plist
|
||||
|
||||
**`packages/noodl-editor/build/Info.plist`** (if exists) or via electron-builder:
|
||||
|
||||
```json
|
||||
{
|
||||
"build": {
|
||||
"mac": {
|
||||
"extendInfo": {
|
||||
"CFBundleDisplayName": "Nodegex",
|
||||
"CFBundleName": "Nodegex",
|
||||
"LSMultipleInstancesProhibited": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8. UI Strings
|
||||
|
||||
Search for and replace user-facing strings:
|
||||
|
||||
```bash
|
||||
# Find all references
|
||||
grep -r "OpenNoodl\|Noodl" packages/ --include="*.tsx" --include="*.ts" --include="*.js"
|
||||
```
|
||||
|
||||
Common locations:
|
||||
- About dialogs
|
||||
- Error messages
|
||||
- Welcome screens
|
||||
- Help text
|
||||
- Tooltips
|
||||
|
||||
### 9. Launcher
|
||||
|
||||
**`packages/noodl-core-ui/src/preview/launcher/Launcher/`**
|
||||
|
||||
- Update any branding in launcher UI
|
||||
- Logo/icon references
|
||||
- Welcome messages
|
||||
|
||||
### 10. Build Assets
|
||||
|
||||
**`packages/noodl-editor/build/`**
|
||||
|
||||
- Icon files: Keep filenames, update icon content if needed
|
||||
- Installer background images
|
||||
- DMG background
|
||||
|
||||
### 11. Code Comments (Low Priority)
|
||||
|
||||
Internal comments can remain as "Noodl" for historical context. Only update user-visible strings.
|
||||
|
||||
## File Change Summary
|
||||
|
||||
| File | Change Type |
|
||||
|------|-------------|
|
||||
| `packages/noodl-editor/package.json` | productName, appId, protocols |
|
||||
| `packages/noodl-editor/src/main/main.js` | Add migration, protocol handler |
|
||||
| `packages/noodl-editor/src/main/src/migration.js` | Create new |
|
||||
| `packages/noodl-core-ui/**/TitleBar*` | Check for hardcoded strings |
|
||||
| `packages/noodl-core-ui/**/Launcher*` | Branding updates |
|
||||
| Various `.tsx`, `.ts` files | User-facing string changes |
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Fresh Install
|
||||
- [ ] App installs as "Nodegex"
|
||||
- [ ] userData created in correct location
|
||||
- [ ] Protocol handler `nodegex://` works
|
||||
- [ ] App icon shows correctly
|
||||
- [ ] Window title shows "Nodegex"
|
||||
- [ ] About dialog shows "Nodegex"
|
||||
|
||||
### Upgrade from OpenNoodl
|
||||
- [ ] User data migrates automatically
|
||||
- [ ] Projects list preserved
|
||||
- [ ] Settings preserved
|
||||
- [ ] No duplicate data created
|
||||
|
||||
### Platform Specific
|
||||
- [ ] Windows: Start menu shows "Nodegex"
|
||||
- [ ] macOS: Menu bar shows "Nodegex"
|
||||
- [ ] macOS: Dock shows "Nodegex"
|
||||
- [ ] Linux: Desktop entry shows "Nodegex"
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If issues arise, the migration is non-destructive:
|
||||
1. Old userData folder is preserved
|
||||
2. Migration marker file indicates completion
|
||||
3. Can revert productName and migrate back
|
||||
|
||||
## Search Patterns
|
||||
|
||||
Use these to find remaining references:
|
||||
|
||||
```bash
|
||||
# Case-insensitive search for noodl
|
||||
grep -ri "noodl" packages/ --include="*.tsx" --include="*.ts" --include="*.js" \
|
||||
| grep -v "node_modules" \
|
||||
| grep -v ".bundle." \
|
||||
| grep -v "// " \
|
||||
| grep -v "* "
|
||||
|
||||
# Specific product names
|
||||
grep -r "OpenNoodl\|opennoodl\|com\.opennoodl" packages/
|
||||
```
|
||||
|
||||
## Notes on Internal Names
|
||||
|
||||
These should **NOT** change:
|
||||
- `noodl-editor` package name
|
||||
- `noodl-runtime` package name
|
||||
- `noodl-core-ui` package name
|
||||
- `@noodl/` npm scope (if any)
|
||||
- Internal imports like `from '@noodl-models/...'`
|
||||
|
||||
Changing these would require massive refactoring with no user benefit.
|
||||
390
dev-docs/tasks/phase-8-distribution/TASK-7.2-macos-signing.md
Normal file
390
dev-docs/tasks/phase-8-distribution/TASK-7.2-macos-signing.md
Normal file
@@ -0,0 +1,390 @@
|
||||
# Task 7.2: Fix macOS Automatic Code Signing
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Currently, macOS builds require manual code signing of 30+ individual files using a bash script. This process:
|
||||
- Takes 15-30 minutes per build
|
||||
- Is error-prone (easy to miss files or sign in wrong order)
|
||||
- Must be repeated for both Intel (x64) and Apple Silicon (arm64)
|
||||
- Blocks automation via CI/CD
|
||||
|
||||
**Root Cause**: electron-builder's automatic signing isn't configured, so it skips signing entirely.
|
||||
|
||||
## Current Manual Process (What We're Eliminating)
|
||||
|
||||
```bash
|
||||
# Current painful workflow:
|
||||
1. Run electron-builder (produces unsigned app)
|
||||
2. Manually run signing script with 30+ codesign commands
|
||||
3. Sign in specific order (inner files first, .app last)
|
||||
4. Hope you didn't miss anything
|
||||
5. Run notarization
|
||||
6. Wait 5-10 minutes for Apple
|
||||
7. Staple the notarization ticket
|
||||
8. Repeat for other architecture
|
||||
```
|
||||
|
||||
## Target Automated Process
|
||||
|
||||
```bash
|
||||
# Target workflow:
|
||||
export CSC_NAME="Developer ID Application: Osborne Solutions (Y35J975HXR)"
|
||||
export APPLE_ID="your@email.com"
|
||||
export APPLE_APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx"
|
||||
export APPLE_TEAM_ID="Y35J975HXR"
|
||||
|
||||
npm run build # Everything happens automatically
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Verify Certificate Setup
|
||||
|
||||
**Step 1.1: Check Keychain**
|
||||
|
||||
```bash
|
||||
# List all Developer ID certificates
|
||||
security find-identity -v -p codesigning
|
||||
|
||||
# Should show something like:
|
||||
# 1) ABCD1234... "Developer ID Application: Osborne Solutions (Y35J975HXR)"
|
||||
```
|
||||
|
||||
**Step 1.2: Verify Certificate Chain**
|
||||
|
||||
```bash
|
||||
# Check certificate details
|
||||
security find-certificate -c "Developer ID Application: Osborne Solutions" -p | \
|
||||
openssl x509 -noout -subject -issuer -dates
|
||||
```
|
||||
|
||||
**Step 1.3: Test Manual Signing**
|
||||
|
||||
```bash
|
||||
# Create a simple test binary
|
||||
echo 'int main() { return 0; }' | clang -x c - -o /tmp/test
|
||||
codesign --sign "Developer ID Application: Osborne Solutions (Y35J975HXR)" \
|
||||
--options runtime /tmp/test
|
||||
codesign --verify --verbose /tmp/test
|
||||
```
|
||||
|
||||
### Phase 2: Configure electron-builder
|
||||
|
||||
**Step 2.1: Update package.json**
|
||||
|
||||
```json
|
||||
{
|
||||
"build": {
|
||||
"appId": "com.nodegex.app",
|
||||
"productName": "Nodegex",
|
||||
"afterSign": "./build/macos-notarize.js",
|
||||
|
||||
"mac": {
|
||||
"category": "public.app-category.developer-tools",
|
||||
"hardenedRuntime": true,
|
||||
"gatekeeperAssess": false,
|
||||
"entitlements": "build/entitlements.mac.plist",
|
||||
"entitlementsInherit": "build/entitlements.mac.plist",
|
||||
"target": [
|
||||
{ "target": "dmg", "arch": ["x64", "arm64"] },
|
||||
{ "target": "zip", "arch": ["x64", "arm64"] }
|
||||
],
|
||||
"signIgnore": [],
|
||||
"extendInfo": {
|
||||
"LSMultipleInstancesProhibited": true,
|
||||
"NSMicrophoneUsageDescription": "Allow Nodegex apps to access the microphone?",
|
||||
"NSCameraUsageDescription": "Allow Nodegex apps to access the camera?"
|
||||
}
|
||||
},
|
||||
|
||||
"dmg": {
|
||||
"sign": false
|
||||
},
|
||||
|
||||
"publish": {
|
||||
"provider": "github",
|
||||
"owner": "the-low-code-foundation",
|
||||
"repo": "opennoodl",
|
||||
"releaseType": "release"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key Configuration Notes:**
|
||||
|
||||
| Setting | Purpose |
|
||||
|---------|---------|
|
||||
| `hardenedRuntime: true` | Required for notarization |
|
||||
| `gatekeeperAssess: false` | Skip Gatekeeper check during build (faster) |
|
||||
| `entitlementsInherit` | Apply entitlements to all nested executables |
|
||||
| `dmg.sign: false` | DMG signing is usually unnecessary and can cause issues |
|
||||
|
||||
**Step 2.2: Verify entitlements.mac.plist**
|
||||
|
||||
```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">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- Required for Electron -->
|
||||
<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.disable-library-validation</key>
|
||||
<true/>
|
||||
|
||||
<!-- Required for Node.js child processes (git, etc.) -->
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
|
||||
<!-- Network access -->
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
|
||||
<!-- File access for projects -->
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
|
||||
<!-- Accessibility for some UI features -->
|
||||
<key>com.apple.security.automation.apple-events</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
**Step 2.3: Update notarization script**
|
||||
|
||||
```javascript
|
||||
// build/macos-notarize.js
|
||||
const { notarize } = require('@electron/notarize');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = async function notarizing(context) {
|
||||
const { electronPlatformName, appOutDir } = context;
|
||||
|
||||
if (electronPlatformName !== 'darwin') {
|
||||
console.log('Skipping notarization: not macOS');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for required environment variables
|
||||
const appleId = process.env.APPLE_ID;
|
||||
const appleIdPassword = process.env.APPLE_APP_SPECIFIC_PASSWORD;
|
||||
const teamId = process.env.APPLE_TEAM_ID;
|
||||
|
||||
if (!appleId || !appleIdPassword || !teamId) {
|
||||
console.log('Skipping notarization: missing credentials');
|
||||
console.log('Set APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, and APPLE_TEAM_ID');
|
||||
return;
|
||||
}
|
||||
|
||||
const appName = context.packager.appInfo.productFilename;
|
||||
const appPath = path.join(appOutDir, `${appName}.app`);
|
||||
|
||||
console.log(`Notarizing ${appPath}...`);
|
||||
|
||||
try {
|
||||
await notarize({
|
||||
appPath,
|
||||
appleId,
|
||||
appleIdPassword,
|
||||
teamId,
|
||||
tool: 'notarytool' // Faster than legacy altool
|
||||
});
|
||||
console.log('Notarization complete!');
|
||||
} catch (error) {
|
||||
console.error('Notarization failed:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Phase 3: Handle Native Modules in asar.unpacked
|
||||
|
||||
The dugite and desktop-trampoline binaries are in `asar.unpacked` which requires special handling.
|
||||
|
||||
**Step 3.1: Verify asar configuration**
|
||||
|
||||
```json
|
||||
{
|
||||
"build": {
|
||||
"asarUnpack": [
|
||||
"node_modules/dugite/**/*",
|
||||
"node_modules/desktop-trampoline/**/*"
|
||||
],
|
||||
"files": [
|
||||
"**/*",
|
||||
"!node_modules/dugite/git/**/*",
|
||||
"node_modules/dugite/git/bin/*",
|
||||
"node_modules/dugite/git/libexec/git-core/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3.2: electron-builder automatically signs asar.unpacked**
|
||||
|
||||
When `CSC_NAME` or `CSC_LINK` is set, electron-builder will:
|
||||
1. Find all Mach-O binaries in `asar.unpacked`
|
||||
2. Sign each with hardened runtime and entitlements
|
||||
3. Sign them in correct dependency order
|
||||
|
||||
### Phase 4: Build Environment Setup
|
||||
|
||||
**Step 4.1: Create build script**
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/build-mac.sh
|
||||
|
||||
set -e
|
||||
|
||||
# Certificate identity (must match keychain exactly)
|
||||
export CSC_NAME="Developer ID Application: Osborne Solutions (Y35J975HXR)"
|
||||
|
||||
# Apple notarization credentials
|
||||
export APPLE_ID="${APPLE_ID:?Set APPLE_ID environment variable}"
|
||||
export APPLE_APP_SPECIFIC_PASSWORD="${APPLE_APP_SPECIFIC_PASSWORD:?Set APPLE_APP_SPECIFIC_PASSWORD}"
|
||||
export APPLE_TEAM_ID="Y35J975HXR"
|
||||
|
||||
# Build for specified architecture or both
|
||||
ARCH="${1:-universal}"
|
||||
|
||||
case "$ARCH" in
|
||||
x64)
|
||||
npx electron-builder --mac --x64
|
||||
;;
|
||||
arm64)
|
||||
npx electron-builder --mac --arm64
|
||||
;;
|
||||
universal|both)
|
||||
npx electron-builder --mac --x64 --arm64
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [x64|arm64|universal]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "Build complete! Check dist/ for output."
|
||||
```
|
||||
|
||||
**Step 4.2: Add to package.json scripts**
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build:mac": "./scripts/build-mac.sh",
|
||||
"build:mac:x64": "./scripts/build-mac.sh x64",
|
||||
"build:mac:arm64": "./scripts/build-mac.sh arm64"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 5: Verification
|
||||
|
||||
**Step 5.1: Verify all signatures**
|
||||
|
||||
```bash
|
||||
# Check the .app bundle
|
||||
codesign --verify --deep --strict --verbose=2 "dist/mac-arm64/Nodegex.app"
|
||||
|
||||
# Check specific problematic files
|
||||
codesign -dv "dist/mac-arm64/Nodegex.app/Contents/Resources/app.asar.unpacked/node_modules/dugite/git/bin/git"
|
||||
|
||||
# Verify notarization
|
||||
spctl --assess --type execute --verbose "dist/mac-arm64/Nodegex.app"
|
||||
```
|
||||
|
||||
**Step 5.2: Test Gatekeeper**
|
||||
|
||||
```bash
|
||||
# This simulates what happens when a user downloads and opens the app
|
||||
xattr -d com.apple.quarantine "dist/mac-arm64/Nodegex.app"
|
||||
xattr -w com.apple.quarantine "0081;5f8a1234;Safari;12345678-1234-1234-1234-123456789ABC" "dist/mac-arm64/Nodegex.app"
|
||||
open "dist/mac-arm64/Nodegex.app"
|
||||
```
|
||||
|
||||
**Step 5.3: Verify notarization stapling**
|
||||
|
||||
```bash
|
||||
stapler validate "dist/Nodegex-1.2.0-arm64.dmg"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "The signature is invalid" or signing fails
|
||||
|
||||
```bash
|
||||
# Reset code signing
|
||||
codesign --remove-signature "path/to/file"
|
||||
|
||||
# Check certificate validity
|
||||
security find-certificate -c "Developer ID" -p | openssl x509 -checkend 0
|
||||
```
|
||||
|
||||
### "errSecInternalComponent" error
|
||||
|
||||
The certificate private key isn't accessible:
|
||||
```bash
|
||||
# Unlock keychain
|
||||
security unlock-keychain -p "password" ~/Library/Keychains/login.keychain-db
|
||||
|
||||
# Or in CI, create a temporary keychain
|
||||
security create-keychain -p "" build.keychain
|
||||
security import certificate.p12 -k build.keychain -P "$CERT_PASSWORD" -T /usr/bin/codesign
|
||||
security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain
|
||||
```
|
||||
|
||||
### Notarization timeout or failure
|
||||
|
||||
```bash
|
||||
# Check notarization history
|
||||
xcrun notarytool history --apple-id "$APPLE_ID" --password "$APPLE_APP_SPECIFIC_PASSWORD" --team-id "$APPLE_TEAM_ID"
|
||||
|
||||
# Get details on specific submission
|
||||
xcrun notarytool log <submission-id> --apple-id "$APPLE_ID" --password "$APPLE_APP_SPECIFIC_PASSWORD" --team-id "$APPLE_TEAM_ID"
|
||||
```
|
||||
|
||||
### dugite binaries not signed
|
||||
|
||||
Verify they're correctly unpacked:
|
||||
```bash
|
||||
ls -la "dist/mac-arm64/Nodegex.app/Contents/Resources/app.asar.unpacked/node_modules/dugite/git/bin/"
|
||||
```
|
||||
|
||||
If missing, check `asarUnpack` patterns in build config.
|
||||
|
||||
## Files to Modify
|
||||
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `packages/noodl-editor/package.json` | Update build config, add mac targets |
|
||||
| `packages/noodl-editor/build/entitlements.mac.plist` | Verify all required entitlements |
|
||||
| `packages/noodl-editor/build/macos-notarize.js` | Update to use notarytool |
|
||||
| `scripts/noodl-editor/build-editor.ts` | Add CSC_NAME handling |
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. ✅ `npm run build:mac:arm64` produces signed app with zero manual steps
|
||||
2. ✅ `codesign --verify --deep --strict` passes
|
||||
3. ✅ `spctl --assess --type execute` returns "accepted"
|
||||
4. ✅ All 30+ files from manual script are signed automatically
|
||||
5. ✅ App opens on fresh macOS install without Gatekeeper warning
|
||||
|
||||
## Environment Variables Reference
|
||||
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `CSC_NAME` | Yes* | Certificate name in keychain |
|
||||
| `CSC_LINK` | Yes* | Path to .p12 certificate file (CI) |
|
||||
| `CSC_KEY_PASSWORD` | With CSC_LINK | Certificate password |
|
||||
| `APPLE_ID` | For notarization | Apple Developer account email |
|
||||
| `APPLE_APP_SPECIFIC_PASSWORD` | For notarization | App-specific password from appleid.apple.com |
|
||||
| `APPLE_TEAM_ID` | For notarization | Team ID (e.g., Y35J975HXR) |
|
||||
|
||||
*One of `CSC_NAME` or `CSC_LINK` is required for signing.
|
||||
@@ -0,0 +1,358 @@
|
||||
# Task 7.3: Configure Auto-Update Publishing
|
||||
|
||||
## Overview
|
||||
|
||||
Connect the existing auto-update infrastructure to GitHub Releases so users receive update notifications without manual downloads.
|
||||
|
||||
## What Already Exists
|
||||
|
||||
The codebase already has:
|
||||
1. **electron-updater** - Installed and configured in `autoupdater.js`
|
||||
2. **Update UI** - TitleBar shows "Update Available" state
|
||||
3. **Confirmation Dialog** - Asks user to restart
|
||||
4. **IPC Handlers** - Communication between main and renderer
|
||||
|
||||
What's missing: **The publish URL configuration**.
|
||||
|
||||
## How Auto-Update Works
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Auto-Update Flow │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. App Starts │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 2. Check GitHub Releases for latest-{platform}.yml │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 3. Compare versions (semver) │
|
||||
│ │ │
|
||||
│ ├─── Same version ──► Do nothing, check again in 60s │
|
||||
│ │ │
|
||||
│ └─── New version ──► Download in background │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 4. Download complete ──► Show "Update Available" in TitleBar │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 5. User clicks ──► Show confirmation dialog │
|
||||
│ │ │
|
||||
│ ├─── "Later" ──► Dismiss │
|
||||
│ │ │
|
||||
│ └─── "Restart" ──► quitAndInstall() │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 6. App restarts with new version │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Step 1: Add Publish Configuration
|
||||
|
||||
**`packages/noodl-editor/package.json`**
|
||||
|
||||
```json
|
||||
{
|
||||
"build": {
|
||||
"publish": {
|
||||
"provider": "github",
|
||||
"owner": "the-low-code-foundation",
|
||||
"repo": "opennoodl",
|
||||
"releaseType": "release"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Configuration Options:**
|
||||
|
||||
| Setting | Value | Description |
|
||||
|---------|-------|-------------|
|
||||
| `provider` | `"github"` | Use GitHub Releases |
|
||||
| `owner` | `"the-low-code-foundation"` | GitHub org/user |
|
||||
| `repo` | `"opennoodl"` | Repository name |
|
||||
| `releaseType` | `"release"` | Only stable releases (not drafts/prereleases) |
|
||||
|
||||
### Step 2: Update autoupdater.js
|
||||
|
||||
**`packages/noodl-editor/src/main/src/autoupdater.js`**
|
||||
|
||||
```javascript
|
||||
const { app, ipcMain } = require('electron');
|
||||
const { autoUpdater } = require('electron-updater');
|
||||
const log = require('electron-log');
|
||||
|
||||
// Configure logging
|
||||
autoUpdater.logger = log;
|
||||
autoUpdater.logger.transports.file.level = 'info';
|
||||
|
||||
// Disable auto-download so user can choose
|
||||
autoUpdater.autoDownload = false;
|
||||
|
||||
function setupAutoUpdate(window) {
|
||||
// Skip in dev mode
|
||||
if (process.env.devMode === 'true' || process.env.autoUpdate === 'no') {
|
||||
log.info('Auto-update disabled in dev mode');
|
||||
return;
|
||||
}
|
||||
|
||||
// Linux: Only AppImage supports auto-update
|
||||
if (process.platform === 'linux' && !process.env.APPIMAGE) {
|
||||
log.info('Auto-update only available for AppImage on Linux');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for updates on startup
|
||||
checkForUpdates();
|
||||
|
||||
// Check periodically (every 60 seconds)
|
||||
setInterval(checkForUpdates, 60 * 1000);
|
||||
|
||||
function checkForUpdates() {
|
||||
log.info('Checking for updates...');
|
||||
autoUpdater.checkForUpdates().catch((err) => {
|
||||
log.error('Update check failed:', err);
|
||||
});
|
||||
}
|
||||
|
||||
// Update available - ask user if they want to download
|
||||
autoUpdater.on('update-available', (info) => {
|
||||
log.info('Update available:', info.version);
|
||||
|
||||
// Start download automatically (runs in background)
|
||||
autoUpdater.downloadUpdate().catch((err) => {
|
||||
log.error('Download failed:', err);
|
||||
});
|
||||
});
|
||||
|
||||
// No update available
|
||||
autoUpdater.on('update-not-available', (info) => {
|
||||
log.info('No update available. Current version:', app.getVersion());
|
||||
});
|
||||
|
||||
// Download progress
|
||||
autoUpdater.on('download-progress', (progress) => {
|
||||
log.info(`Download progress: ${progress.percent.toFixed(1)}%`);
|
||||
|
||||
// Optionally send to renderer for progress UI
|
||||
if (window && !window.isDestroyed()) {
|
||||
window.webContents.send('updateDownloadProgress', progress);
|
||||
}
|
||||
});
|
||||
|
||||
// Download complete - notify user
|
||||
autoUpdater.on('update-downloaded', (info) => {
|
||||
log.info('Update downloaded:', info.version);
|
||||
|
||||
if (window && !window.isDestroyed()) {
|
||||
window.webContents.send('showAutoUpdatePopup', {
|
||||
version: info.version,
|
||||
releaseNotes: info.releaseNotes
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle user response
|
||||
ipcMain.on('autoUpdatePopupClosed', (event, restartNow) => {
|
||||
if (restartNow) {
|
||||
log.info('User requested restart for update');
|
||||
autoUpdater.quitAndInstall(false, true);
|
||||
} else {
|
||||
log.info('User deferred update');
|
||||
}
|
||||
});
|
||||
|
||||
// Error handling
|
||||
autoUpdater.on('error', (error) => {
|
||||
log.error('Auto-updater error:', error);
|
||||
|
||||
// Don't spam logs - wait before retrying
|
||||
setTimeout(checkForUpdates, 5 * 60 * 1000); // Retry in 5 minutes
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setupAutoUpdate
|
||||
};
|
||||
```
|
||||
|
||||
### Step 3: Enhance Update Dialog (Optional)
|
||||
|
||||
**`packages/noodl-editor/src/editor/src/views/windows/BaseWindow/BaseWindow.tsx`**
|
||||
|
||||
```typescript
|
||||
const [AutoUpdateDialog, autoUpdateConfirmation] = useConfirmationDialog({
|
||||
title: 'Update Available',
|
||||
message: `Version ${updateInfo?.version || 'new'} is ready to install.
|
||||
|
||||
Release notes:
|
||||
${updateInfo?.releaseNotes || 'Bug fixes and improvements.'}
|
||||
|
||||
Restart now to update?`,
|
||||
confirmButtonLabel: 'Restart Now',
|
||||
cancelButtonLabel: 'Later'
|
||||
});
|
||||
```
|
||||
|
||||
### Step 4: Update Manifests
|
||||
|
||||
When you build with `--publish always`, electron-builder creates:
|
||||
|
||||
**`latest.yml`** (Windows)
|
||||
```yaml
|
||||
version: 1.2.0
|
||||
files:
|
||||
- url: Nodegex-Setup-1.2.0.exe
|
||||
sha512: abc123...
|
||||
size: 85000000
|
||||
path: Nodegex-Setup-1.2.0.exe
|
||||
sha512: abc123...
|
||||
releaseDate: '2024-01-15T10:30:00.000Z'
|
||||
```
|
||||
|
||||
**`latest-mac.yml`** (macOS)
|
||||
```yaml
|
||||
version: 1.2.0
|
||||
files:
|
||||
- url: Nodegex-1.2.0-arm64.dmg
|
||||
sha512: def456...
|
||||
size: 150000000
|
||||
- url: Nodegex-1.2.0-x64.dmg
|
||||
sha512: ghi789...
|
||||
size: 155000000
|
||||
path: Nodegex-1.2.0-arm64.dmg
|
||||
sha512: def456...
|
||||
releaseDate: '2024-01-15T10:30:00.000Z'
|
||||
```
|
||||
|
||||
**`latest-linux.yml`** (Linux)
|
||||
```yaml
|
||||
version: 1.2.0
|
||||
files:
|
||||
- url: Nodegex-1.2.0-x64.AppImage
|
||||
sha512: jkl012...
|
||||
size: 120000000
|
||||
path: Nodegex-1.2.0-x64.AppImage
|
||||
sha512: jkl012...
|
||||
releaseDate: '2024-01-15T10:30:00.000Z'
|
||||
```
|
||||
|
||||
### Step 5: Test Locally
|
||||
|
||||
**Create a test release:**
|
||||
|
||||
```bash
|
||||
# Build with publish (but don't actually publish)
|
||||
cd packages/noodl-editor
|
||||
npx electron-builder --mac --publish never
|
||||
|
||||
# Check generated files
|
||||
ls dist/
|
||||
# Should include: latest-mac.yml
|
||||
```
|
||||
|
||||
**Test update detection:**
|
||||
|
||||
```bash
|
||||
# 1. Install an older version
|
||||
# 2. Create a GitHub Release with newer version
|
||||
# 3. Launch old version
|
||||
# 4. Watch logs for update detection:
|
||||
tail -f ~/Library/Logs/Nodegex/main.log
|
||||
```
|
||||
|
||||
### Step 6: Configure Release Channels (Optional)
|
||||
|
||||
For beta/alpha testing:
|
||||
|
||||
```javascript
|
||||
// In autoupdater.js
|
||||
autoUpdater.channel = 'latest'; // Default
|
||||
|
||||
// Or allow user to opt into beta:
|
||||
autoUpdater.channel = userPreferences.updateChannel || 'latest';
|
||||
// Channels: 'latest' (stable), 'beta', 'alpha'
|
||||
```
|
||||
|
||||
electron-builder creates separate manifests:
|
||||
- `latest.yml` - Stable releases
|
||||
- `beta.yml` - Beta releases
|
||||
- `alpha.yml` - Alpha releases
|
||||
|
||||
## Files to Modify
|
||||
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `packages/noodl-editor/package.json` | Add publish configuration |
|
||||
| `packages/noodl-editor/src/main/src/autoupdater.js` | Enhance with logging, progress |
|
||||
| `packages/noodl-editor/src/editor/src/views/windows/BaseWindow/BaseWindow.tsx` | Optional: Better update dialog |
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Local Testing
|
||||
- [ ] Build produces `latest-*.yml` files
|
||||
- [ ] App connects to GitHub Releases API
|
||||
- [ ] Update detection works (with test release)
|
||||
- [ ] Download progress shown (optional)
|
||||
- [ ] "Restart" installs update
|
||||
- [ ] "Later" dismisses dialog
|
||||
- [ ] App restarts with new version
|
||||
|
||||
### Platform Testing
|
||||
- [ ] macOS Intel: Download correct arch
|
||||
- [ ] macOS ARM: Download correct arch
|
||||
- [ ] Windows: NSIS installer works
|
||||
- [ ] Linux AppImage: Update replaces file
|
||||
|
||||
### Edge Cases
|
||||
- [ ] Offline: Graceful failure, retry later
|
||||
- [ ] Partial download: Resume or restart
|
||||
- [ ] Corrupted download: SHA512 check fails, retry
|
||||
- [ ] Downgrade prevention: Don't install older version
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Update not detected
|
||||
|
||||
Check logs:
|
||||
```bash
|
||||
# macOS
|
||||
cat ~/Library/Logs/Nodegex/main.log
|
||||
|
||||
# Windows
|
||||
type %USERPROFILE%\AppData\Roaming\Nodegex\logs\main.log
|
||||
|
||||
# Linux
|
||||
cat ~/.config/Nodegex/logs/main.log
|
||||
```
|
||||
|
||||
### Wrong architecture downloaded
|
||||
|
||||
Verify `latest-mac.yml` has both arch entries and correct `sha512` values.
|
||||
|
||||
### "Cannot find latest.yml"
|
||||
|
||||
Either:
|
||||
1. Build wasn't published to GitHub Releases
|
||||
2. Release is still in draft mode
|
||||
3. Network/proxy issues
|
||||
|
||||
### Update downloads but doesn't install
|
||||
|
||||
Check:
|
||||
1. SHA512 mismatch (corrupted download)
|
||||
2. Disk space
|
||||
3. Permissions (can write to app directory)
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. ✅ App checks for updates on startup
|
||||
2. ✅ Update notification appears for new releases
|
||||
3. ✅ Background download doesn't interrupt work
|
||||
4. ✅ Restart installs update seamlessly
|
||||
5. ✅ User data preserved after update
|
||||
6. ✅ Works on all three platforms
|
||||
@@ -0,0 +1,386 @@
|
||||
# Task 7.4: Linux Universal Distribution
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Linux support is currently incomplete:
|
||||
- Only `.deb` target configured
|
||||
- Someone added Arch Linux support (AUR?) but status unclear
|
||||
- No AppImage for universal distribution
|
||||
- No auto-update support for Linux
|
||||
- Native modules (dugite, desktop-trampoline) may have compatibility issues
|
||||
|
||||
## Goals
|
||||
|
||||
1. **AppImage**: Universal format that works on any Linux distribution
|
||||
2. **.deb**: Native experience for Debian/Ubuntu users (largest desktop Linux market)
|
||||
3. **Auto-update**: AppImage supports electron-updater
|
||||
4. **Tested**: Verified on Ubuntu 22.04 LTS and 24.04 LTS
|
||||
|
||||
## Linux Desktop Market Context
|
||||
|
||||
| Distribution Family | Market Share | Target Format |
|
||||
|---------------------|--------------|---------------|
|
||||
| Ubuntu/Debian | ~60% | .deb + AppImage |
|
||||
| Fedora/RHEL | ~15% | AppImage (RPM optional) |
|
||||
| Arch | ~10% | AppImage + AUR |
|
||||
| Other | ~15% | AppImage |
|
||||
|
||||
**Decision**: AppImage as primary (works everywhere), .deb as secondary (native experience for majority).
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Configure electron-builder for Linux
|
||||
|
||||
**Step 1.1: Update package.json**
|
||||
|
||||
```json
|
||||
{
|
||||
"build": {
|
||||
"linux": {
|
||||
"target": [
|
||||
{
|
||||
"target": "AppImage",
|
||||
"arch": ["x64"]
|
||||
},
|
||||
{
|
||||
"target": "deb",
|
||||
"arch": ["x64"]
|
||||
}
|
||||
],
|
||||
"category": "Development",
|
||||
"icon": "build/icons",
|
||||
"synopsis": "Visual low-code React development platform",
|
||||
"description": "Nodegex is a full-stack low-code platform for building React applications visually.",
|
||||
"desktop": {
|
||||
"Name": "Nodegex",
|
||||
"Comment": "Visual React Development",
|
||||
"Categories": "Development;IDE;",
|
||||
"Keywords": "react;low-code;visual;programming;node;"
|
||||
},
|
||||
"mimeTypes": [
|
||||
"x-scheme-handler/nodegex"
|
||||
]
|
||||
},
|
||||
|
||||
"appImage": {
|
||||
"artifactName": "${productName}-${version}-${arch}.AppImage",
|
||||
"license": "LICENSE"
|
||||
},
|
||||
|
||||
"deb": {
|
||||
"artifactName": "${productName}-${version}-${arch}.deb",
|
||||
"depends": [
|
||||
"libgtk-3-0",
|
||||
"libnotify4",
|
||||
"libnss3",
|
||||
"libxss1",
|
||||
"libxtst6",
|
||||
"xdg-utils",
|
||||
"libatspi2.0-0",
|
||||
"libuuid1",
|
||||
"libsecret-1-0"
|
||||
],
|
||||
"category": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 2: Handle Native Modules
|
||||
|
||||
The trickiest part is ensuring dugite's embedded git and desktop-trampoline work on Linux.
|
||||
|
||||
**Step 2.1: Verify dugite Linux binaries**
|
||||
|
||||
dugite downloads platform-specific git binaries. Verify they're included:
|
||||
|
||||
```bash
|
||||
# After build, check the AppImage contents
|
||||
./Nodegex-1.2.0-x64.AppImage --appimage-extract
|
||||
ls -la squashfs-root/resources/app.asar.unpacked/node_modules/dugite/git/
|
||||
```
|
||||
|
||||
**Step 2.2: Check desktop-trampoline**
|
||||
|
||||
```bash
|
||||
ls -la squashfs-root/resources/app.asar.unpacked/node_modules/desktop-trampoline/build/Release/
|
||||
file squashfs-root/resources/app.asar.unpacked/node_modules/desktop-trampoline/build/Release/desktop-trampoline
|
||||
```
|
||||
|
||||
**Step 2.3: Verify library dependencies**
|
||||
|
||||
```bash
|
||||
# Check for missing shared libraries
|
||||
ldd squashfs-root/resources/app.asar.unpacked/node_modules/dugite/git/bin/git
|
||||
ldd squashfs-root/nodegex
|
||||
```
|
||||
|
||||
### Phase 3: AppImage Auto-Update Support
|
||||
|
||||
AppImage is the only Linux format that supports electron-updater.
|
||||
|
||||
**Step 3.1: How it works**
|
||||
|
||||
1. electron-updater checks GitHub Releases for `latest-linux.yml`
|
||||
2. Downloads new `.AppImage` to temp location
|
||||
3. User confirms restart
|
||||
4. New AppImage replaces old one
|
||||
5. App restarts
|
||||
|
||||
**Step 3.2: Required publish config**
|
||||
|
||||
```json
|
||||
{
|
||||
"build": {
|
||||
"publish": {
|
||||
"provider": "github",
|
||||
"owner": "the-low-code-foundation",
|
||||
"repo": "opennoodl"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3.3: Auto-update behavior**
|
||||
|
||||
The existing `autoupdater.js` already handles Linux correctly:
|
||||
|
||||
```javascript
|
||||
if (process.platform === 'linux') {
|
||||
return; // Currently disabled
|
||||
}
|
||||
```
|
||||
|
||||
We need to **enable** it for AppImage:
|
||||
|
||||
```javascript
|
||||
function setupAutoUpdate(window) {
|
||||
if (process.env.autoUpdate === 'no') return;
|
||||
|
||||
// AppImage auto-update works, .deb does not
|
||||
if (process.platform === 'linux' && !process.env.APPIMAGE) {
|
||||
console.log('Auto-update only available for AppImage');
|
||||
return;
|
||||
}
|
||||
|
||||
// ... rest of auto-update logic
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: Icon Generation
|
||||
|
||||
Linux needs multiple icon sizes in PNG format.
|
||||
|
||||
**Step 4.1: Create icon set**
|
||||
|
||||
```bash
|
||||
# From a 1024x1024 source icon
|
||||
mkdir -p build/icons
|
||||
|
||||
for size in 16 24 32 48 64 128 256 512 1024; do
|
||||
convert icon-source.png -resize ${size}x${size} build/icons/${size}x${size}.png
|
||||
done
|
||||
```
|
||||
|
||||
**Step 4.2: Directory structure**
|
||||
|
||||
```
|
||||
build/
|
||||
icons/
|
||||
16x16.png
|
||||
24x24.png
|
||||
32x32.png
|
||||
48x48.png
|
||||
64x64.png
|
||||
128x128.png
|
||||
256x256.png
|
||||
512x512.png
|
||||
1024x1024.png
|
||||
```
|
||||
|
||||
### Phase 5: Protocol Handler Registration
|
||||
|
||||
For `nodegex://` URLs to work:
|
||||
|
||||
**Step 5.1: Desktop file configuration**
|
||||
|
||||
The `mimeTypes` config in package.json creates the association. Additionally, update the `protocols` config:
|
||||
|
||||
```json
|
||||
{
|
||||
"build": {
|
||||
"protocols": {
|
||||
"name": "nodegex",
|
||||
"schemes": ["nodegex"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 5.2: Manual registration (if needed)**
|
||||
|
||||
```bash
|
||||
# For AppImage users who need manual registration
|
||||
xdg-mime default nodegex.desktop x-scheme-handler/nodegex
|
||||
```
|
||||
|
||||
### Phase 6: Build Script
|
||||
|
||||
**Step 6.1: Create Linux build script**
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/build-linux.sh
|
||||
|
||||
set -e
|
||||
|
||||
# Build for x64 (most common)
|
||||
# ARM64 support would require additional setup for native modules
|
||||
|
||||
echo "Building Linux targets..."
|
||||
|
||||
# AppImage
|
||||
npx electron-builder --linux AppImage --x64
|
||||
|
||||
# Debian package
|
||||
npx electron-builder --linux deb --x64
|
||||
|
||||
echo "Build complete!"
|
||||
echo ""
|
||||
echo "Outputs:"
|
||||
ls -la dist/*.AppImage dist/*.deb 2>/dev/null || echo "No artifacts found"
|
||||
```
|
||||
|
||||
**Step 6.2: Add to package.json**
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build:linux": "./scripts/build-linux.sh",
|
||||
"build:linux:appimage": "electron-builder --linux AppImage --x64",
|
||||
"build:linux:deb": "electron-builder --linux deb --x64"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 7: Testing
|
||||
|
||||
**Step 7.1: Test on fresh Ubuntu VM**
|
||||
|
||||
```bash
|
||||
# Ubuntu 22.04 LTS
|
||||
sudo apt update
|
||||
sudo apt install -y libfuse2 # Required for AppImage
|
||||
|
||||
chmod +x Nodegex-1.2.0-x64.AppImage
|
||||
./Nodegex-1.2.0-x64.AppImage
|
||||
```
|
||||
|
||||
**Step 7.2: Test .deb installation**
|
||||
|
||||
```bash
|
||||
sudo dpkg -i Nodegex-1.2.0-x64.deb
|
||||
# If dependencies missing:
|
||||
sudo apt-get install -f
|
||||
|
||||
# Launch
|
||||
nodegex
|
||||
```
|
||||
|
||||
**Step 7.3: Test protocol handler**
|
||||
|
||||
```bash
|
||||
xdg-open "nodegex://test"
|
||||
```
|
||||
|
||||
**Step 7.4: Verify auto-update (AppImage only)**
|
||||
|
||||
1. Install older version
|
||||
2. Create GitHub Release with newer version
|
||||
3. Wait for update notification
|
||||
4. Click "Restart"
|
||||
5. Verify new version launches
|
||||
|
||||
## Known Issues & Workarounds
|
||||
|
||||
### AppImage FUSE dependency
|
||||
|
||||
Ubuntu 22.04+ doesn't include FUSE by default:
|
||||
|
||||
```bash
|
||||
# Users need to install:
|
||||
sudo apt install libfuse2
|
||||
```
|
||||
|
||||
Document this in release notes.
|
||||
|
||||
### Wayland compatibility
|
||||
|
||||
Some Electron features behave differently on Wayland vs X11:
|
||||
|
||||
```bash
|
||||
# Force X11 if issues occur
|
||||
GDK_BACKEND=x11 ./Nodegex.AppImage
|
||||
```
|
||||
|
||||
### Sandbox issues on some distributions
|
||||
|
||||
If sandbox errors occur:
|
||||
|
||||
```bash
|
||||
# Disable sandbox (less secure but works)
|
||||
./Nodegex.AppImage --no-sandbox
|
||||
```
|
||||
|
||||
Or fix system-wide:
|
||||
```bash
|
||||
sudo sysctl -w kernel.unprivileged_userns_clone=1
|
||||
```
|
||||
|
||||
## Files to Modify
|
||||
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `packages/noodl-editor/package.json` | Add Linux targets, icons, desktop config |
|
||||
| `packages/noodl-editor/src/main/src/autoupdater.js` | Enable for AppImage |
|
||||
| `packages/noodl-editor/build/icons/` | Add PNG icon set |
|
||||
| `scripts/build-linux.sh` | Create build script |
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. ✅ AppImage runs on fresh Ubuntu 22.04 LTS
|
||||
2. ✅ AppImage runs on fresh Ubuntu 24.04 LTS
|
||||
3. ✅ .deb installs without manual dependency resolution
|
||||
4. ✅ Auto-update works for AppImage distribution
|
||||
5. ✅ `nodegex://` protocol handler works
|
||||
6. ✅ Desktop integration (icon, menu entry) works
|
||||
|
||||
## Distribution Channels
|
||||
|
||||
### GitHub Releases
|
||||
|
||||
Both AppImage and .deb uploaded to releases.
|
||||
|
||||
### Optional: Snapcraft
|
||||
|
||||
```yaml
|
||||
# snap/snapcraft.yaml (future enhancement)
|
||||
name: nodegex
|
||||
base: core22
|
||||
version: '1.2.0'
|
||||
summary: Visual low-code React development platform
|
||||
confinement: classic
|
||||
```
|
||||
|
||||
### Optional: Flathub
|
||||
|
||||
Flatpak provides another universal format but requires more setup and maintenance.
|
||||
|
||||
## ARM64 Consideration
|
||||
|
||||
ARM64 Linux (Raspberry Pi, etc.) would require:
|
||||
- Cross-compilation setup
|
||||
- ARM64 dugite binaries
|
||||
- ARM64 desktop-trampoline build
|
||||
|
||||
This is out of scope for initial release but could be added later.
|
||||
467
dev-docs/tasks/phase-8-distribution/TASK-7.5-github-actions.md
Normal file
467
dev-docs/tasks/phase-8-distribution/TASK-7.5-github-actions.md
Normal file
@@ -0,0 +1,467 @@
|
||||
# Task 7.5: GitHub Actions CI/CD Pipeline
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Currently, building Nodegex for distribution requires:
|
||||
- Manual builds on each platform (macOS, Windows, Linux)
|
||||
- Access to a macOS machine for Apple Silicon builds
|
||||
- Manual code signing
|
||||
- Manual upload to distribution channels
|
||||
- No automated testing before release
|
||||
|
||||
## Goals
|
||||
|
||||
1. **Automated Builds**: Push tag → builds start automatically
|
||||
2. **All Platforms**: macOS (x64 + arm64), Windows (x64), Linux (x64)
|
||||
3. **Code Signing**: Automatic for all platforms
|
||||
4. **GitHub Releases**: Automatic creation with all artifacts
|
||||
5. **Update Manifests**: `latest.yml`, `latest-mac.yml`, `latest-linux.yml` generated
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ GitHub Actions Workflow │
|
||||
├──────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Tag Push (v1.2.0) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Build Matrix │ │
|
||||
│ ├─────────────────┬─────────────────┬─────────────────────────────┤ │
|
||||
│ │ macOS x64 │ macOS arm64 │ Windows x64 │ Linux x64 │ │
|
||||
│ │ (macos-13) │ (macos-14) │ (windows) │ (ubuntu) │ │
|
||||
│ │ │ │ │ │ │
|
||||
│ │ • Build │ • Build │ • Build │ • Build │ │
|
||||
│ │ • Sign │ • Sign │ • Sign* │ • No sign │ │
|
||||
│ │ • Notarize │ • Notarize │ │ │ │
|
||||
│ └─────────────────┴─────────────────┴───────────────┴─────────────┘ │
|
||||
│ │ │ │ │ │
|
||||
│ └───────────────────┴─────────────────┴───────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ GitHub Release │
|
||||
│ • DMG (x64, arm64) │
|
||||
│ • ZIP (x64, arm64) │
|
||||
│ • EXE installer │
|
||||
│ • AppImage │
|
||||
│ • DEB │
|
||||
│ • latest*.yml manifests │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Create Workflow File
|
||||
|
||||
```yaml
|
||||
# .github/workflows/release.yml
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# macOS Intel
|
||||
- os: macos-13
|
||||
platform: darwin
|
||||
arch: x64
|
||||
|
||||
# macOS Apple Silicon
|
||||
- os: macos-14
|
||||
platform: darwin
|
||||
arch: arm64
|
||||
|
||||
# Windows
|
||||
- os: windows-latest
|
||||
platform: win32
|
||||
arch: x64
|
||||
|
||||
# Linux
|
||||
- os: ubuntu-22.04
|
||||
platform: linux
|
||||
arch: x64
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build packages
|
||||
run: npm run build --workspaces --if-present
|
||||
|
||||
# macOS: Import certificate and configure signing
|
||||
- name: Import macOS signing certificate
|
||||
if: matrix.platform == 'darwin'
|
||||
env:
|
||||
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
|
||||
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
|
||||
run: |
|
||||
# Create temporary keychain
|
||||
KEYCHAIN_PATH=$RUNNER_TEMP/build.keychain
|
||||
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
|
||||
|
||||
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
|
||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||
|
||||
# Import certificate
|
||||
echo "$MACOS_CERTIFICATE" | base64 --decode > $RUNNER_TEMP/certificate.p12
|
||||
security import $RUNNER_TEMP/certificate.p12 \
|
||||
-k "$KEYCHAIN_PATH" \
|
||||
-P "$MACOS_CERTIFICATE_PASSWORD" \
|
||||
-T /usr/bin/codesign \
|
||||
-T /usr/bin/security
|
||||
|
||||
# Allow codesign to access keychain
|
||||
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||
security list-keychains -d user -s "$KEYCHAIN_PATH" login.keychain
|
||||
|
||||
# Verify certificate
|
||||
security find-identity -v -p codesigning "$KEYCHAIN_PATH"
|
||||
|
||||
# Windows: Import certificate (if signing enabled)
|
||||
- name: Import Windows signing certificate
|
||||
if: matrix.platform == 'win32' && secrets.WINDOWS_CERTIFICATE != ''
|
||||
env:
|
||||
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
run: |
|
||||
echo "$env:WINDOWS_CERTIFICATE" | Out-File -FilePath certificate.b64
|
||||
certutil -decode certificate.b64 certificate.pfx
|
||||
# Certificate will be used by electron-builder automatically
|
||||
shell: pwsh
|
||||
|
||||
# Build Electron app
|
||||
- name: Build Electron app
|
||||
env:
|
||||
# macOS signing
|
||||
CSC_LINK: ${{ matrix.platform == 'darwin' && secrets.MACOS_CERTIFICATE || '' }}
|
||||
CSC_KEY_PASSWORD: ${{ matrix.platform == 'darwin' && secrets.MACOS_CERTIFICATE_PASSWORD || '' }}
|
||||
|
||||
# macOS notarization
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
# Windows signing (optional)
|
||||
WIN_CSC_LINK: ${{ matrix.platform == 'win32' && secrets.WINDOWS_CERTIFICATE || '' }}
|
||||
WIN_CSC_KEY_PASSWORD: ${{ matrix.platform == 'win32' && secrets.WINDOWS_CERTIFICATE_PASSWORD || '' }}
|
||||
|
||||
run: |
|
||||
cd packages/noodl-editor
|
||||
npx electron-builder --${{ matrix.platform == 'darwin' && 'mac' || matrix.platform == 'win32' && 'win' || 'linux' }} --${{ matrix.arch }} --publish always
|
||||
|
||||
# Upload artifacts to GitHub Release
|
||||
- name: Upload artifacts
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: |
|
||||
packages/noodl-editor/dist/*.dmg
|
||||
packages/noodl-editor/dist/*.zip
|
||||
packages/noodl-editor/dist/*.exe
|
||||
packages/noodl-editor/dist/*.AppImage
|
||||
packages/noodl-editor/dist/*.deb
|
||||
packages/noodl-editor/dist/*.yml
|
||||
packages/noodl-editor/dist/*.yaml
|
||||
draft: false
|
||||
prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'alpha') }}
|
||||
```
|
||||
|
||||
### Phase 2: Configure GitHub Secrets
|
||||
|
||||
Navigate to: Repository → Settings → Secrets and variables → Actions
|
||||
|
||||
**Required Secrets:**
|
||||
|
||||
| Secret | Description | How to Get |
|
||||
|--------|-------------|------------|
|
||||
| `MACOS_CERTIFICATE` | Base64-encoded .p12 file | `base64 -i certificate.p12` |
|
||||
| `MACOS_CERTIFICATE_PASSWORD` | Password for .p12 | Your certificate password |
|
||||
| `APPLE_ID` | Apple Developer email | Your Apple ID |
|
||||
| `APPLE_APP_SPECIFIC_PASSWORD` | App-specific password | appleid.apple.com → Security |
|
||||
| `APPLE_TEAM_ID` | Team ID | Y35J975HXR (from certificate) |
|
||||
|
||||
**Optional Secrets (for Windows signing):**
|
||||
|
||||
| Secret | Description |
|
||||
|--------|-------------|
|
||||
| `WINDOWS_CERTIFICATE` | Base64-encoded .pfx file |
|
||||
| `WINDOWS_CERTIFICATE_PASSWORD` | Certificate password |
|
||||
|
||||
### Phase 3: Export macOS Certificate for CI
|
||||
|
||||
```bash
|
||||
# 1. Export from Keychain Access
|
||||
# - Open Keychain Access
|
||||
# - Find "Developer ID Application: Osborne Solutions"
|
||||
# - Right-click → Export
|
||||
# - Save as .p12 with strong password
|
||||
|
||||
# 2. Base64 encode
|
||||
base64 -i "Developer ID Application.p12" | pbcopy
|
||||
# Paste into MACOS_CERTIFICATE secret
|
||||
|
||||
# 3. Generate app-specific password
|
||||
# - Go to appleid.apple.com
|
||||
# - Sign In & Security → App-Specific Passwords
|
||||
# - Generate new password labeled "nodegex-ci"
|
||||
# - Copy to APPLE_APP_SPECIFIC_PASSWORD secret
|
||||
```
|
||||
|
||||
### Phase 4: Update package.json for CI
|
||||
|
||||
```json
|
||||
{
|
||||
"build": {
|
||||
"publish": {
|
||||
"provider": "github",
|
||||
"owner": "the-low-code-foundation",
|
||||
"repo": "opennoodl",
|
||||
"releaseType": "release"
|
||||
},
|
||||
|
||||
"mac": {
|
||||
"target": [
|
||||
{ "target": "dmg", "arch": ["x64", "arm64"] },
|
||||
{ "target": "zip", "arch": ["x64", "arm64"] }
|
||||
]
|
||||
},
|
||||
|
||||
"win": {
|
||||
"target": [
|
||||
{ "target": "nsis", "arch": ["x64"] }
|
||||
]
|
||||
},
|
||||
|
||||
"linux": {
|
||||
"target": [
|
||||
{ "target": "AppImage", "arch": ["x64"] },
|
||||
{ "target": "deb", "arch": ["x64"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 5: Release Process
|
||||
|
||||
**Step 5.1: Create release**
|
||||
|
||||
```bash
|
||||
# Update version in package.json
|
||||
npm version patch # or minor, major
|
||||
|
||||
# Push with tags
|
||||
git push && git push --tags
|
||||
```
|
||||
|
||||
**Step 5.2: Monitor workflow**
|
||||
|
||||
Go to: Repository → Actions → Release workflow
|
||||
|
||||
Each platform builds in parallel (~15-30 minutes total).
|
||||
|
||||
**Step 5.3: Verify release**
|
||||
|
||||
1. Check GitHub Releases for all artifacts
|
||||
2. Download and test on each platform
|
||||
3. Verify auto-update works from previous version
|
||||
|
||||
### Phase 6: Version Management
|
||||
|
||||
**Semantic Versioning:**
|
||||
- `v1.0.0` → Stable release
|
||||
- `v1.1.0-beta.1` → Beta release
|
||||
- `v1.1.0-alpha.1` → Alpha release
|
||||
|
||||
**Pre-release handling:**
|
||||
|
||||
Tags containing `beta` or `alpha` automatically create pre-releases that don't trigger auto-update for stable users.
|
||||
|
||||
### Phase 7: Update Manifests
|
||||
|
||||
electron-builder automatically generates:
|
||||
|
||||
```yaml
|
||||
# latest-mac.yml
|
||||
version: 1.2.0
|
||||
files:
|
||||
- url: Nodegex-1.2.0-arm64.dmg
|
||||
sha512: abc123...
|
||||
size: 150000000
|
||||
- url: Nodegex-1.2.0-x64.dmg
|
||||
sha512: def456...
|
||||
size: 155000000
|
||||
path: Nodegex-1.2.0-arm64.dmg
|
||||
sha512: abc123...
|
||||
releaseDate: '2024-01-15T10:30:00.000Z'
|
||||
```
|
||||
|
||||
These files tell electron-updater which version is latest and where to download.
|
||||
|
||||
## Workflow Customizations
|
||||
|
||||
### Manual Trigger
|
||||
|
||||
Add workflow_dispatch for manual runs:
|
||||
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to build'
|
||||
required: true
|
||||
```
|
||||
|
||||
### Build on PR (Testing)
|
||||
|
||||
Create separate workflow for PR testing:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/build-test.yml
|
||||
name: Build Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'packages/noodl-editor/**'
|
||||
- '.github/workflows/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- run: npm ci
|
||||
- run: npm run build --workspaces
|
||||
- run: cd packages/noodl-editor && npx electron-builder --linux --dir
|
||||
```
|
||||
|
||||
### Caching
|
||||
|
||||
Speed up builds with dependency caching:
|
||||
|
||||
```yaml
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.npm
|
||||
~/.cache/electron
|
||||
~/.cache/electron-builder
|
||||
key: ${{ runner.os }}-build-${{ hashFiles('**/package-lock.json') }}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### macOS: "No identity found"
|
||||
|
||||
Certificate not imported correctly:
|
||||
```yaml
|
||||
# Debug step
|
||||
- name: Debug certificates
|
||||
if: matrix.platform == 'darwin'
|
||||
run: |
|
||||
security list-keychains
|
||||
security find-identity -v -p codesigning
|
||||
```
|
||||
|
||||
### macOS: Notarization timeout
|
||||
|
||||
Apple's servers can be slow. Increase timeout or retry:
|
||||
```javascript
|
||||
// macos-notarize.js
|
||||
await notarize({
|
||||
// ...
|
||||
timeout: 1800000 // 30 minutes
|
||||
});
|
||||
```
|
||||
|
||||
### Windows: SmartScreen warning
|
||||
|
||||
Without EV certificate, SmartScreen shows warning for first ~1000 downloads. Solutions:
|
||||
1. Purchase EV code signing certificate (~$400/year)
|
||||
2. Accept warnings initially (reputation builds over time)
|
||||
|
||||
### Linux: AppImage won't run
|
||||
|
||||
Missing FUSE. Document in release notes:
|
||||
```markdown
|
||||
## Linux Users
|
||||
AppImage requires FUSE:
|
||||
\`\`\`bash
|
||||
sudo apt install libfuse2
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
## Files to Create/Modify
|
||||
|
||||
| File | Action |
|
||||
|------|--------|
|
||||
| `.github/workflows/release.yml` | Create |
|
||||
| `.github/workflows/build-test.yml` | Create (optional) |
|
||||
| `packages/noodl-editor/package.json` | Add publish config |
|
||||
| `packages/noodl-editor/build/macos-notarize.js` | Update for CI |
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. ✅ `git tag v1.2.0 && git push --tags` triggers workflow
|
||||
2. ✅ All 4 build targets complete successfully
|
||||
3. ✅ GitHub Release created with all artifacts
|
||||
4. ✅ Update manifests (latest*.yml) present
|
||||
5. ✅ Existing users see "Update Available" within 1 minute
|
||||
6. ✅ Build time < 30 minutes total
|
||||
|
||||
## Cost Considerations
|
||||
|
||||
GitHub Actions is free for public repositories.
|
||||
|
||||
For private repos:
|
||||
- 2,000 minutes/month free
|
||||
- Each release uses ~60-90 minutes (all platforms combined)
|
||||
- ~20-30 releases/month possible on free tier
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Secrets Protection**: Never log secrets or expose in artifacts
|
||||
2. **Certificate Security**: Rotate certificates before expiration
|
||||
3. **Tag Protection**: Consider requiring reviews for tags
|
||||
4. **Artifact Integrity**: SHA512 checksums in update manifests
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
1. **Changelog Generation**: Auto-generate from merged PRs
|
||||
2. **Slack/Discord Notifications**: Post release announcements
|
||||
3. **Download Statistics**: Track per-platform adoption
|
||||
4. **Rollback Mechanism**: Quick revert to previous version
|
||||
Reference in New Issue
Block a user