Add desktop packaging workflows#1
Conversation
Add Electron desktop packaging support and a GitHub Actions workflow so v4.0.0 can produce Windows desktop bundles and unsigned macOS DMG artifacts.
There was a problem hiding this comment.
6 issues found across 14 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="scripts/build-electron-macos.js">
<violation number="1" location="scripts/build-electron-macos.js:187">
P2: DMG-only check too weak. Build can pass with missing backend files. Add required backend file checks before declaring package ready.</violation>
</file>
<file name="scripts/build-electron.js">
<violation number="1" location="scripts/build-electron.js:37">
P1: Windows npm/npx call builds one unquoted shell string. Paths with spaces break arg parsing. Pass args separately to spawnSync.</violation>
<violation number="2" location="scripts/build-electron.js:148">
P2: `--no-node-runtime` path always fails later. Readiness check still hard-requires `node/node.exe`. Make that check conditional.</violation>
</file>
<file name="frontend/src/utils/desktop.ts">
<violation number="1" location="frontend/src/utils/desktop.ts:44">
P1: Do not trust URL param for desktop detection. Easy spoof, login gate bypass. Remove query-string fallback.</violation>
</file>
<file name="electron/main.js">
<violation number="1" location="electron/main.js:172">
P2: Health check accepts 4xx as ready. Wrong process on that port can pass startup and app loads broken target. Accept only 2xx.</violation>
<violation number="2" location="electron/main.js:213">
P2: Settings file path uses app backend install dir. User prefs can be lost on update and writes are fragile on packaged installs. Store prefs in userData.</violation>
</file>
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
| const env = { ...process.env, CSC_IDENTITY_AUTO_DISCOVERY: 'false' }; | ||
| const useWindowsShell = process.platform === 'win32' && (command === 'npm' || command === 'npx'); | ||
| const result = useWindowsShell | ||
| ? spawnSync([command, ...commandArgs].join(' '), { cwd, stdio: 'inherit', shell: true, env }) |
There was a problem hiding this comment.
P1: Windows npm/npx call builds one unquoted shell string. Paths with spaces break arg parsing. Pass args separately to spawnSync.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/build-electron.js, line 37:
<comment>Windows npm/npx call builds one unquoted shell string. Paths with spaces break arg parsing. Pass args separately to spawnSync.</comment>
<file context>
@@ -0,0 +1,241 @@
+ const env = { ...process.env, CSC_IDENTITY_AUTO_DISCOVERY: 'false' };
+ const useWindowsShell = process.platform === 'win32' && (command === 'npm' || command === 'npx');
+ const result = useWindowsShell
+ ? spawnSync([command, ...commandArgs].join(' '), { cwd, stdio: 'inherit', shell: true, env })
+ : spawnSync(command, commandArgs, { cwd, stdio: 'inherit', env });
+
</file context>
| } | ||
|
|
||
| export function isDesktopRuntime(): boolean { | ||
| return Boolean(tauriCore() || electronBridge() || window.__ONESHELL_RUNTIME__ === 'desktop' || new URLSearchParams(window.location.search).get('runtime') === 'desktop'); |
There was a problem hiding this comment.
P1: Do not trust URL param for desktop detection. Easy spoof, login gate bypass. Remove query-string fallback.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/utils/desktop.ts, line 44:
<comment>Do not trust URL param for desktop detection. Easy spoof, login gate bypass. Remove query-string fallback.</comment>
<file context>
@@ -0,0 +1,78 @@
+}
+
+export function isDesktopRuntime(): boolean {
+ return Boolean(tauriCore() || electronBridge() || window.__ONESHELL_RUNTIME__ === 'desktop' || new URLSearchParams(window.location.search).get('runtime') === 'desktop');
+}
+
</file context>
| } | ||
|
|
||
| function assertMacOutputReady() { | ||
| const dmgPath = path.join(outputDir, `1Shell-v${version}-mac-${macArch}.dmg`); |
There was a problem hiding this comment.
P2: DMG-only check too weak. Build can pass with missing backend files. Add required backend file checks before declaring package ready.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/build-electron-macos.js, line 187:
<comment>DMG-only check too weak. Build can pass with missing backend files. Add required backend file checks before declaring package ready.</comment>
<file context>
@@ -0,0 +1,204 @@
+}
+
+function assertMacOutputReady() {
+ const dmgPath = path.join(outputDir, `1Shell-v${version}-mac-${macArch}.dmg`);
+ if (!fs.existsSync(dmgPath)) {
+ throw new Error(`macOS desktop package is incomplete, missing: ${path.relative(ROOT, dmgPath)}`);
</file context>
| const requiredFiles = [ | ||
| '1Shell.cmd', | ||
| 'server.js', | ||
| path.join('node', 'node.exe'), |
There was a problem hiding this comment.
P2: --no-node-runtime path always fails later. Readiness check still hard-requires node/node.exe. Make that check conditional.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/build-electron.js, line 148:
<comment>`--no-node-runtime` path always fails later. Readiness check still hard-requires `node/node.exe`. Make that check conditional.</comment>
<file context>
@@ -0,0 +1,241 @@
+ const requiredFiles = [
+ '1Shell.cmd',
+ 'server.js',
+ path.join('node', 'node.exe'),
+ path.join('node_modules', 'dotenv', 'package.json'),
+ path.join('node_modules', 'better-sqlite3', 'package.json'),
</file context>
| path.join('node', 'node.exe'), | |
| ...(hasFlag('--no-node-runtime') ? [] : [path.join('node', 'node.exe')]), |
| } | ||
|
|
||
| function getDesktopSettingsPath() { | ||
| return path.join(backendRoot, 'data', 'desktop-settings.json'); |
There was a problem hiding this comment.
P2: Settings file path uses app backend install dir. User prefs can be lost on update and writes are fragile on packaged installs. Store prefs in userData.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At electron/main.js, line 213:
<comment>Settings file path uses app backend install dir. User prefs can be lost on update and writes are fragile on packaged installs. Store prefs in userData.</comment>
<file context>
@@ -0,0 +1,397 @@
+}
+
+function getDesktopSettingsPath() {
+ return path.join(backendRoot, 'data', 'desktop-settings.json');
+}
+
</file context>
| function probe() { | ||
| const req = http.get(healthUrl, res => { | ||
| res.resume(); | ||
| if (res.statusCode && res.statusCode >= 200 && res.statusCode < 500) { |
There was a problem hiding this comment.
P2: Health check accepts 4xx as ready. Wrong process on that port can pass startup and app loads broken target. Accept only 2xx.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At electron/main.js, line 172:
<comment>Health check accepts 4xx as ready. Wrong process on that port can pass startup and app loads broken target. Accept only 2xx.</comment>
<file context>
@@ -0,0 +1,397 @@
+ function probe() {
+ const req = http.get(healthUrl, res => {
+ res.resume();
+ if (res.statusCode && res.statusCode >= 200 && res.statusCode < 500) {
+ resolve();
+ return;
</file context>
Summary
Test plan