Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Build - Linux

on:
push:
tags:
- 'v*'
workflow_dispatch:

permissions:
contents: write

jobs:
build-linux:
name: Build Linux (Ubuntu)
runs-on: ubuntu-latest

Comment thread
coderabbitai[bot] marked this conversation as resolved.
strategy:
matrix:
arch: [x64]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Install Linux build dependencies
run: |
sudo apt-get update
sudo apt-get install -y libarchive-tools

- name: Build Electron app for Linux
run: npm run electron:build:linux
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Verify build artifacts
run: |
echo "=== Checking deb ==="
shopt -s nullglob
DEBS=(release/*.deb)
if [ ${#DEBS[@]} -eq 0 ]; then echo "ERROR: No .deb found"; exit 1; fi
for DEB in "${DEBS[@]}"; do
echo "deb: $DEB ($(stat --format=%s "$DEB") bytes)"

echo "=== Checking deb metadata ==="
dpkg-deb --info "$DEB"

echo "=== Checking deb contains dorothy binary ==="
MAIN_BIN=$(dpkg-deb --contents "$DEB" | grep -m1 "opt/Dorothy/dorothy$")
if [ -z "$MAIN_BIN" ]; then echo "ERROR: dorothy binary not found in deb"; exit 1; fi
BIN_SIZE=$(echo "$MAIN_BIN" | awk '{print $3}')
echo "Main binary: $(echo "$MAIN_BIN" | awk '{print $NF}') ($BIN_SIZE bytes)"
if [ "$BIN_SIZE" -eq 0 ]; then echo "ERROR: dorothy binary is zero-size"; exit 1; fi
done

echo "=== All checks passed ==="

- name: Upload deb artifact
uses: actions/upload-artifact@v4
with:
name: Dorothy-Linux-deb-${{ matrix.arch }}
path: release/*.deb
if-no-files-found: warn

- name: Upload to GitHub Release
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v2
with:
files: |
release/*.deb
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
6 changes: 6 additions & 0 deletions build/linux/after-install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
# Set SUID bit on chrome-sandbox for Electron's sandbox to work.
# Required for Chromium's SUID sandbox on Linux (avoids --no-sandbox).
# Idempotent: safe to run multiple times.
chown root:root /opt/Dorothy/chrome-sandbox
chmod 4755 /opt/Dorothy/chrome-sandbox
6 changes: 6 additions & 0 deletions build/linux/before-remove.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
# Remove SUID bit from chrome-sandbox before package removal.
# Ensures no world-executable SUID root binary is left behind.
if [ -f /opt/Dorothy/chrome-sandbox ]; then
chmod 0755 /opt/Dorothy/chrome-sandbox
fi
12 changes: 12 additions & 0 deletions electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';

if (process.platform === 'linux') {
// Ignore EPIPE errors on stdout/stderr (happens when launched from desktop entries)
process.stdout.on('error', (err: NodeJS.ErrnoException) => {
if (err.code === 'EPIPE') return;
throw err;
});
process.stderr.on('error', (err: NodeJS.ErrnoException) => {
if (err.code === 'EPIPE') return;
throw err;
});
}

// Types
import type { AppSettings, AgentStatus } from './types';

Expand Down
33 changes: 32 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"name": "dorothy",
"version": "1.2.5",
"description": "Agent Control Center - manage AI coding agents from a single desktop app",
"author": "Charlie85270",
"private": true,
"main": "electron/dist/main.js",
"scripts": {
Expand All @@ -15,7 +17,9 @@
"electron:dev": "concurrently \"npm run dev\" \"npm run electron:start\"",
"electron:start": "wait-on http://localhost:3000 && tsc -p electron/tsconfig.json && NODE_ENV=development electron .",
"electron:build": "bash -c 'set -e; mv src/app/api src/app/_api_backup; mv src/app/icon.tsx src/app/_icon_backup.tsx 2>/dev/null || true; trap \"mv src/app/_api_backup src/app/api 2>/dev/null; mv src/app/_icon_backup.tsx src/app/icon.tsx 2>/dev/null\" EXIT; ELECTRON_BUILD=1 next build; tsc -p electron/tsconfig.json; cd mcp-orchestrator && npm install && npm run build && cd ..; cd mcp-telegram && npm install && npm run build && cd ..; cd mcp-kanban && npm install && npm run build && cd ..; cd mcp-vault && npm install && npm run build && cd ..; cd mcp-socialdata && npm install && npm run build && cd ..; cd mcp-x && npm install && npm run build && cd ..; cd mcp-world && npm install && npm run build && cd ..; electron-builder --mac'",
"electron:pack": "tsc -p electron/tsconfig.json && cd mcp-orchestrator && npm install && npm run build && cd .. && cd mcp-telegram && npm install && npm run build && cd .. && cd mcp-kanban && npm install && npm run build && cd .. && cd mcp-vault && npm install && npm run build && cd .. && cd mcp-socialdata && npm install && npm run build && cd .. && cd mcp-x && npm install && npm run build && cd .. && cd mcp-world && npm install && npm run build && cd .. && electron-builder --dir --mac"
"electron:build:linux": "bash -c 'set -e; mv src/app/api src/app/_api_backup; mv src/app/icon.tsx src/app/_icon_backup.tsx 2>/dev/null || true; trap \"mv src/app/_api_backup src/app/api 2>/dev/null; mv src/app/_icon_backup.tsx src/app/icon.tsx 2>/dev/null\" EXIT; ELECTRON_BUILD=1 next build; tsc -p electron/tsconfig.json; cd mcp-orchestrator && npm install && npm run build && cd ..; cd mcp-telegram && npm install && npm run build && cd ..; cd mcp-kanban && npm install && npm run build && cd ..; cd mcp-vault && npm install && npm run build && cd ..; cd mcp-socialdata && npm install && npm run build && cd ..; cd mcp-x && npm install && npm run build && cd ..; cd mcp-world && npm install && npm run build && cd ..; electron-builder --linux'",
"electron:pack": "tsc -p electron/tsconfig.json && cd mcp-orchestrator && npm install && npm run build && cd .. && cd mcp-telegram && npm install && npm run build && cd .. && cd mcp-kanban && npm install && npm run build && cd .. && cd mcp-vault && npm install && npm run build && cd .. && cd mcp-socialdata && npm install && npm run build && cd .. && cd mcp-x && npm install && npm run build && cd .. && cd mcp-world && npm install && npm run build && cd .. && electron-builder --dir --mac",
"electron:pack:linux": "tsc -p electron/tsconfig.json && cd mcp-orchestrator && npm install && npm run build && cd .. && cd mcp-telegram && npm install && npm run build && cd .. && cd mcp-kanban && npm install && npm run build && cd .. && cd mcp-vault && npm install && npm run build && cd .. && cd mcp-socialdata && npm install && npm run build && cd .. && cd mcp-x && npm install && npm run build && cd .. && cd mcp-world && npm install && npm run build && cd .. && electron-builder --dir --linux"
},
"build": {
"appId": "io.dorothy.app",
Expand Down Expand Up @@ -117,6 +121,33 @@
"zip"
]
},
"linux": {
"category": "Development",
"icon": "public/icon-512.png",
"target": [
"deb"
],
"desktop": {
"StartupNotify": "true",
"Categories": "Development;IDE;"
}
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"deb": {
"maintainer": "Charlie85270",
"fpm": ["--after-install", "build/linux/after-install.sh", "--before-remove", "build/linux/before-remove.sh"],
"depends": [
"libgtk-3-0",
"libnotify4",
"libnss3",
"libxss1",
"libxtst6",
"xdg-utils",
"libatspi2.0-0",
"libuuid1",
"libsecret-1-0"
],
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"artifactName": "Dorothy-${version}-${arch}.deb"
},
"dmg": {
"title": "Dorothy",
"contents": [
Expand Down