Skip to content

Commit 836b9db

Browse files
committed
chore: add build:app and run:app scripts for desktop AppImage workflow
1 parent 42c3e30 commit 836b9db

3 files changed

Lines changed: 201 additions & 1 deletion

File tree

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
"db:seed": "pnpm --filter @openlinear/api exec tsx ../../packages/db/prisma/seed.ts",
1313
"db:studio": "pnpm --filter db db:studio",
1414
"build:sidecar": "./scripts/build-sidecar.sh",
15-
"build:desktop": "pnpm --filter @openlinear/desktop tauri build"
15+
"build:desktop": "pnpm --filter @openlinear/desktop tauri build",
16+
"build:app": "./scripts/build-app.sh",
17+
"build:app:fast": "./scripts/build-app.sh --skip-sidecar",
18+
"run:app": "./scripts/run-app.sh",
19+
"run:app:fresh": "./scripts/run-app.sh --build"
1620
},
1721
"devDependencies": {
1822
"turbo": "^2.8.9",

scripts/build-app.sh

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/usr/bin/env bash
2+
# build-app.sh — Build the full OpenLinear desktop app (AppImage/dmg/deb)
3+
#
4+
# Usage:
5+
# ./scripts/build-app.sh # build for current platform
6+
# ./scripts/build-app.sh --skip-sidecar # skip sidecar rebuild (faster if unchanged)
7+
# ./scripts/build-app.sh --target appimage # explicit target
8+
set -euo pipefail
9+
10+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11+
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
12+
BINARIES_DIR="$ROOT_DIR/apps/desktop/src-tauri/binaries"
13+
14+
GREEN='\033[0;32m'
15+
CYAN='\033[0;36m'
16+
YELLOW='\033[1;33m'
17+
RED='\033[0;31m'
18+
NC='\033[0m'
19+
20+
step() { echo -e "\n${CYAN}==>${NC} $1"; }
21+
ok() { echo -e "${GREEN}${NC} $1"; }
22+
warn() { echo -e "${YELLOW}${NC} $1"; }
23+
fail() { echo -e "${RED}${NC} $1"; exit 1; }
24+
25+
SKIP_SIDECAR=false
26+
BUNDLE_TARGET=""
27+
28+
for arg in "$@"; do
29+
case "$arg" in
30+
--skip-sidecar) SKIP_SIDECAR=true ;;
31+
--target) shift; BUNDLE_TARGET="$1" ;;
32+
--target=*) BUNDLE_TARGET="${arg#--target=}" ;;
33+
esac
34+
done
35+
36+
OS="$(uname -s)"
37+
ARCH="$(uname -m)"
38+
39+
echo ""
40+
echo -e "${GREEN}OpenLinear — Desktop Build${NC}"
41+
echo -e "Platform: ${CYAN}$OS / $ARCH${NC}"
42+
echo ""
43+
44+
cd "$ROOT_DIR"
45+
46+
# ── Step 1: Check binaries directory ─────────────────────────────
47+
step "Checking binaries directory..."
48+
mkdir -p "$BINARIES_DIR"
49+
50+
# Check opencode binary
51+
case "$OS" in
52+
Linux) OPENCODE_BIN="$BINARIES_DIR/opencode-x86_64-unknown-linux-gnu" ;;
53+
Darwin)
54+
if [ "$ARCH" = "arm64" ]; then
55+
OPENCODE_BIN="$BINARIES_DIR/opencode-aarch64-apple-darwin"
56+
else
57+
OPENCODE_BIN="$BINARIES_DIR/opencode-x86_64-apple-darwin"
58+
fi
59+
;;
60+
*) fail "Unsupported OS: $OS" ;;
61+
esac
62+
63+
if [ ! -f "$OPENCODE_BIN" ]; then
64+
warn "opencode binary not found at $OPENCODE_BIN"
65+
if [ -f "$SCRIPT_DIR/download-opencode.sh" ]; then
66+
step "Downloading opencode binary..."
67+
bash "$SCRIPT_DIR/download-opencode.sh"
68+
ok "opencode downloaded"
69+
else
70+
fail "No opencode binary and no download-opencode.sh. Add opencode to $BINARIES_DIR manually."
71+
fi
72+
else
73+
ok "opencode binary present"
74+
fi
75+
76+
# ── Step 2: Build sidecar native binary ──────────────────────────
77+
if [ "$SKIP_SIDECAR" = true ]; then
78+
warn "Skipping sidecar build (--skip-sidecar)"
79+
case "$OS" in
80+
Linux) SIDECAR_BIN="$BINARIES_DIR/openlinear-sidecar-x86_64-unknown-linux-gnu" ;;
81+
Darwin)
82+
if [ "$ARCH" = "arm64" ]; then
83+
SIDECAR_BIN="$BINARIES_DIR/openlinear-sidecar-aarch64-apple-darwin"
84+
else
85+
SIDECAR_BIN="$BINARIES_DIR/openlinear-sidecar-x86_64-apple-darwin"
86+
fi
87+
;;
88+
esac
89+
if [ ! -f "$SIDECAR_BIN" ]; then
90+
fail "No sidecar binary at $SIDECAR_BIN. Run without --skip-sidecar first."
91+
fi
92+
ok "Using existing sidecar binary"
93+
else
94+
step "Building sidecar binary..."
95+
bash "$SCRIPT_DIR/build-sidecar.sh"
96+
ok "Sidecar binary built"
97+
fi
98+
99+
# ── Step 3: Install dependencies ─────────────────────────────────
100+
step "Installing dependencies..."
101+
pnpm install --frozen-lockfile 2>/dev/null || pnpm install
102+
ok "Dependencies ready"
103+
104+
# ── Step 4: Build Tauri app ───────────────────────────────────────
105+
step "Building Tauri desktop app..."
106+
if [ -n "$BUNDLE_TARGET" ]; then
107+
pnpm --filter @openlinear/desktop tauri build --bundles "$BUNDLE_TARGET"
108+
else
109+
pnpm --filter @openlinear/desktop tauri build
110+
fi
111+
ok "Tauri build complete"
112+
113+
# ── Step 5: Report output ─────────────────────────────────────────
114+
step "Build artifacts:"
115+
BUNDLE_DIR="$ROOT_DIR/apps/desktop/src-tauri/target/release/bundle"
116+
117+
if [ -d "$BUNDLE_DIR" ]; then
118+
find "$BUNDLE_DIR" -type f \( -name "*.AppImage" -o -name "*.dmg" -o -name "*.deb" \) | while read -r f; do
119+
SIZE=$(du -sh "$f" | cut -f1)
120+
echo -e " ${GREEN}${NC} $(basename "$f") ${CYAN}($SIZE)${NC}"
121+
echo -e " ${YELLOW}${NC} $f"
122+
done
123+
else
124+
warn "Bundle directory not found at $BUNDLE_DIR"
125+
fi
126+
127+
echo ""
128+
echo -e "${GREEN}Build complete!${NC} Run with: ${CYAN}pnpm run:app${NC}"

scripts/run-app.sh

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env bash
2+
# run-app.sh — Find and launch the built OpenLinear AppImage (Linux) or .app (macOS)
3+
#
4+
# Usage:
5+
# ./scripts/run-app.sh # auto-detect latest build
6+
# ./scripts/run-app.sh --build # build first, then run
7+
set -euo pipefail
8+
9+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10+
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
11+
BUNDLE_DIR="$ROOT_DIR/apps/desktop/src-tauri/target/release/bundle"
12+
13+
GREEN='\033[0;32m'
14+
CYAN='\033[0;36m'
15+
YELLOW='\033[1;33m'
16+
RED='\033[0;31m'
17+
NC='\033[0m'
18+
19+
ok() { echo -e "${GREEN}${NC} $1"; }
20+
warn() { echo -e "${YELLOW}${NC} $1"; }
21+
fail() { echo -e "${RED}${NC} $1"; exit 1; }
22+
23+
BUILD_FIRST=false
24+
for arg in "$@"; do
25+
case "$arg" in
26+
--build|-b) BUILD_FIRST=true ;;
27+
esac
28+
done
29+
30+
if [ "$BUILD_FIRST" = true ]; then
31+
echo -e "${CYAN}==> Building app first...${NC}"
32+
bash "$SCRIPT_DIR/build-app.sh"
33+
fi
34+
35+
OS="$(uname -s)"
36+
37+
case "$OS" in
38+
Linux)
39+
APP=$(find "$BUNDLE_DIR/appimage" -name "*.AppImage" 2>/dev/null | sort -V | tail -1)
40+
if [ -z "$APP" ]; then
41+
fail "No AppImage found in $BUNDLE_DIR/appimage — run: pnpm build:app"
42+
fi
43+
chmod +x "$APP"
44+
ok "Launching: $(basename "$APP")"
45+
echo -e " ${YELLOW}${NC} $APP"
46+
echo ""
47+
exec "$APP"
48+
;;
49+
Darwin)
50+
APP=$(find "$BUNDLE_DIR" -name "*.app" -maxdepth 3 2>/dev/null | sort -V | tail -1)
51+
if [ -z "$APP" ]; then
52+
APP=$(find "$BUNDLE_DIR/dmg" -name "*.dmg" 2>/dev/null | sort -V | tail -1)
53+
if [ -z "$APP" ]; then
54+
fail "No .app or .dmg found in $BUNDLE_DIR — run: pnpm build:app"
55+
fi
56+
ok "Opening DMG: $(basename "$APP")"
57+
open "$APP"
58+
else
59+
ok "Launching: $(basename "$APP")"
60+
echo -e " ${YELLOW}${NC} $APP"
61+
echo ""
62+
open "$APP"
63+
fi
64+
;;
65+
*)
66+
fail "Unsupported OS: $OS"
67+
;;
68+
esac

0 commit comments

Comments
 (0)