280 lines
9.6 KiB
Makefile
280 lines
9.6 KiB
Makefile
# Derive a stable port from the working directory so the same worktree
|
|
# always gets the same port.
|
|
vite_port := `python3 -c "import hashlib,os; h=int(hashlib.sha256(os.getcwd().encode()).hexdigest(),16); print(10000 + h % 55000)"`
|
|
|
|
# Default recipe
|
|
default:
|
|
@just --list
|
|
|
|
# ── Dev Environment ──────────────────────────────────────────
|
|
|
|
# Install dependencies, build workspace packages, and activate git hooks
|
|
setup:
|
|
git config core.hooksPath .husky
|
|
cd ../ && pnpm install
|
|
cd ../sdk && pnpm build
|
|
cargo build --manifest-path ../../Cargo.toml -p goose-cli --bin goose
|
|
|
|
# ── Build & Check ────────────────────────────────────────────
|
|
|
|
# Run all checks (lint, format, typecheck, file sizes)
|
|
check:
|
|
pnpm check
|
|
pnpm typecheck
|
|
|
|
# Format code
|
|
fmt:
|
|
pnpm format
|
|
cd src-tauri && cargo fmt
|
|
|
|
# Check formatting without modifying
|
|
fmt-check:
|
|
pnpm exec biome format .
|
|
cd src-tauri && cargo fmt --check
|
|
|
|
# Run clippy on Tauri backend
|
|
clippy:
|
|
cd src-tauri && TAURI_CONFIG='{"bundle":{"externalBin":[]}}' cargo clippy -- -D warnings
|
|
|
|
# Build the frontend
|
|
build:
|
|
pnpm build
|
|
|
|
# Check Tauri Rust formatting
|
|
tauri-fmt-check:
|
|
cd src-tauri && cargo fmt --check
|
|
|
|
# Check Tauri Rust types
|
|
tauri-check:
|
|
cd src-tauri && TAURI_CONFIG='{"bundle":{"externalBin":[]}}' cargo check
|
|
|
|
# Full CI gate
|
|
ci: check clippy test build tauri-check
|
|
|
|
bundle:
|
|
pnpm tauri build
|
|
|
|
# ── Test ─────────────────────────────────────────────────────
|
|
|
|
# Run unit/component tests
|
|
test:
|
|
pnpm test
|
|
|
|
# Run tests in watch mode
|
|
test-watch:
|
|
pnpm test:watch
|
|
|
|
# Run tests with coverage
|
|
test-coverage:
|
|
pnpm test:coverage
|
|
|
|
# Run E2E smoke tests (builds first)
|
|
test-e2e:
|
|
pnpm test:e2e:smoke
|
|
|
|
# Run all E2E tests (builds first)
|
|
test-e2e-all:
|
|
pnpm test:e2e
|
|
|
|
# ── Run ──────────────────────────────────────────────────────
|
|
|
|
# Start the desktop app in dev mode
|
|
dev: setup
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
VITE_PORT={{ vite_port }}
|
|
export VITE_PORT
|
|
# Enable perf logs in the child `goose serve` process by default.
|
|
# Override with e.g. RUST_LOG=info just dev to disable.
|
|
export RUST_LOG="${RUST_LOG:-perf=debug,info}"
|
|
PROJECT_DIR=$(pwd)
|
|
REPO_ROOT=$(cd ../.. && pwd)
|
|
DISTRO_DIR="${PROJECT_DIR}/distro"
|
|
if [[ -z "${GOOSE_DISTRO_DIR:-}" && -d "${DISTRO_DIR}" ]]; then
|
|
export GOOSE_DISTRO_DIR="${DISTRO_DIR}"
|
|
fi
|
|
LOCAL_GOOSE_DEBUG="${REPO_ROOT}/target/debug/goose"
|
|
LOCAL_GOOSE_RELEASE="${REPO_ROOT}/target/release/goose"
|
|
if [[ -x "${LOCAL_GOOSE_DEBUG}" ]]; then
|
|
export GOOSE_BIN="${LOCAL_GOOSE_DEBUG}"
|
|
elif [[ -x "${LOCAL_GOOSE_RELEASE}" ]]; then
|
|
export GOOSE_BIN="${LOCAL_GOOSE_RELEASE}"
|
|
else
|
|
unset GOOSE_BIN
|
|
fi
|
|
EXTRA_CONFIG_ARGS=(--config "{\"build\":{\"devUrl\":\"http://localhost:${VITE_PORT}\",\"beforeDevCommand\":{\"script\":\"cd ${PROJECT_DIR} && exec pnpm exec vite --port ${VITE_PORT} --strictPort\",\"cwd\":\".\",\"wait\":false}}}")
|
|
|
|
if [[ -n "${GOOSE_BIN:-}" ]]; then
|
|
echo "Using local goose binary: ${GOOSE_BIN}"
|
|
else
|
|
echo "No local goose binary found under ${REPO_ROOT}/target; falling back to PATH"
|
|
fi
|
|
|
|
if [[ -n "${GOOSE_DISTRO_DIR:-}" ]]; then
|
|
echo "Using distro dir: ${GOOSE_DISTRO_DIR}"
|
|
fi
|
|
|
|
# In worktrees, generate a labeled icon so you can tell instances apart
|
|
if git rev-parse --is-inside-work-tree &>/dev/null; then
|
|
GIT_DIR=$(git rev-parse --git-dir)
|
|
if [[ "$GIT_DIR" == *".git/worktrees/"* ]]; then
|
|
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
|
|
WORKTREE_LABEL="${BRANCH_NAME##*/}"
|
|
|
|
ICON_DIR="$(pwd)/src-tauri/target/dev-icons"
|
|
mkdir -p "$ICON_DIR"
|
|
DEV_ICON="$ICON_DIR/icon.icns"
|
|
|
|
if swift scripts/generate-dev-icon.swift src-tauri/icons/icon.icns "$DEV_ICON" "$WORKTREE_LABEL"; then
|
|
echo "🌳 Worktree: ${WORKTREE_LABEL}"
|
|
EXTRA_CONFIG_ARGS+=(--config "{\"bundle\":{\"icon\":[\"$DEV_ICON\"]}}")
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
pnpm tauri dev --features app-test-driver --config src-tauri/tauri.dev.conf.json "${EXTRA_CONFIG_ARGS[@]}"
|
|
|
|
# Start the desktop app with dev config
|
|
dev-debug: setup
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
VITE_PORT={{ vite_port }}
|
|
export VITE_PORT
|
|
# Enable perf logs in the child `goose serve` process by default.
|
|
# Override with e.g. RUST_LOG=info just dev-debug to disable.
|
|
export RUST_LOG="${RUST_LOG:-perf=debug,info}"
|
|
REPO_ROOT=$(cd ../.. && pwd)
|
|
DISTRO_DIR="$(pwd)/distro"
|
|
if [[ -z "${GOOSE_DISTRO_DIR:-}" && -d "${DISTRO_DIR}" ]]; then
|
|
export GOOSE_DISTRO_DIR="${DISTRO_DIR}"
|
|
fi
|
|
LOCAL_GOOSE_DEBUG="${REPO_ROOT}/target/debug/goose"
|
|
LOCAL_GOOSE_RELEASE="${REPO_ROOT}/target/release/goose"
|
|
if [[ -x "${LOCAL_GOOSE_DEBUG}" ]]; then
|
|
export GOOSE_BIN="${LOCAL_GOOSE_DEBUG}"
|
|
elif [[ -x "${LOCAL_GOOSE_RELEASE}" ]]; then
|
|
export GOOSE_BIN="${LOCAL_GOOSE_RELEASE}"
|
|
else
|
|
unset GOOSE_BIN
|
|
fi
|
|
EXTRA_CONFIG_ARGS=(--config "{\"build\":{\"devUrl\":\"http://localhost:${VITE_PORT}\",\"beforeDevCommand\":{\"script\":\"exec ./node_modules/.bin/vite --port ${VITE_PORT} --strictPort\",\"cwd\":\"..\",\"wait\":false}}}")
|
|
|
|
if [[ -n "${GOOSE_BIN:-}" ]]; then
|
|
echo "Using local goose binary: ${GOOSE_BIN}"
|
|
else
|
|
echo "No local goose binary found under ${REPO_ROOT}/target; falling back to PATH"
|
|
fi
|
|
|
|
if [[ -n "${GOOSE_DISTRO_DIR:-}" ]]; then
|
|
echo "Using distro dir: ${GOOSE_DISTRO_DIR}"
|
|
fi
|
|
|
|
# In worktrees, generate a labeled icon so you can tell instances apart
|
|
if git rev-parse --is-inside-work-tree &>/dev/null; then
|
|
GIT_DIR=$(git rev-parse --git-dir)
|
|
if [[ "$GIT_DIR" == *".git/worktrees/"* ]]; then
|
|
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
|
|
WORKTREE_LABEL="${BRANCH_NAME##*/}"
|
|
|
|
ICON_DIR="$(pwd)/src-tauri/target/dev-icons"
|
|
mkdir -p "$ICON_DIR"
|
|
DEV_ICON="$ICON_DIR/icon.icns"
|
|
|
|
if swift scripts/generate-dev-icon.swift src-tauri/icons/icon.icns "$DEV_ICON" "$WORKTREE_LABEL"; then
|
|
echo "🌳 Worktree: ${WORKTREE_LABEL}"
|
|
EXTRA_CONFIG_ARGS+=(--config "{\"bundle\":{\"icon\":[\"$DEV_ICON\"]}}")
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
pnpm tauri dev --config src-tauri/tauri.dev.conf.json "${EXTRA_CONFIG_ARGS[@]}"
|
|
|
|
# Start only the frontend dev server
|
|
dev-frontend:
|
|
pnpm dev
|
|
|
|
# Parse `git worktree list --porcelain` into tab-separated "path\tbranch" lines.
|
|
# Uses sub() instead of $2 so worktree paths containing spaces are preserved.
|
|
# Detached-HEAD worktrees (no branch line) are silently skipped.
|
|
_worktree_awk := '/^worktree / { sub(/^worktree /, ""); wt=$0 } /^branch refs\/heads\// { b=$2; sub(/^refs\/heads\//, "", b); print wt "\t" b }'
|
|
|
|
# Compute a stable vite port from a directory path passed as $1.
|
|
_port_cmd := "import hashlib,sys; h=int(hashlib.sha256(sys.argv[1].encode()).hexdigest(),16); print(10000+h%55000)"
|
|
|
|
# List worktrees with an active dev server
|
|
running:
|
|
#!/usr/bin/env bash
|
|
git worktree list --porcelain | awk '{{ _worktree_awk }}' \
|
|
| while IFS=$'\t' read -r wt branch; do
|
|
dir="$wt/ui/goose2"
|
|
port=$(python3 -c '{{ _port_cmd }}' "$dir")
|
|
if lsof -ti :"$port" &>/dev/null; then
|
|
echo "$branch"
|
|
fi
|
|
done
|
|
|
|
# Kill the dev process (if running). Optionally pass a branch name to kill another worktree's process.
|
|
kill branch="":
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
if [[ -n "{{ branch }}" ]]; then
|
|
WORKTREE_PATH=$(git worktree list --porcelain \
|
|
| awk '{{ _worktree_awk }}' \
|
|
| awk -F'\t' -v branch="{{ branch }}" '$2 == branch { print $1; exit }')
|
|
if [[ -z "$WORKTREE_PATH" ]]; then
|
|
echo "No worktree found for branch '{{ branch }}'"
|
|
exit 1
|
|
fi
|
|
TARGET_DIR="$WORKTREE_PATH/ui/goose2"
|
|
VITE_PORT=$(python3 -c '{{ _port_cmd }}' "$TARGET_DIR")
|
|
else
|
|
VITE_PORT={{ vite_port }}
|
|
fi
|
|
|
|
PID=$(lsof -ti :"$VITE_PORT" 2>/dev/null | head -1) || true
|
|
|
|
if [[ -z "$PID" ]]; then
|
|
echo "No process found on port $VITE_PORT"
|
|
exit 0
|
|
fi
|
|
|
|
PROC_NAME=$(ps -p "$PID" -o comm= 2>/dev/null) || true
|
|
PROC_BASENAME="${PROC_NAME##*/}"
|
|
if [[ "$PROC_BASENAME" != "node" ]]; then
|
|
echo "Process on port $VITE_PORT is '$PROC_NAME', not 'node' — refusing to kill"
|
|
exit 1
|
|
fi
|
|
|
|
PGID=$(ps -p "$PID" -o pgid= 2>/dev/null | tr -d ' ')
|
|
if [[ -z "$PGID" || "$PGID" == "0" || "$PGID" == "1" ]]; then
|
|
echo "Killing node (PID $PID) on port $VITE_PORT"
|
|
kill -9 "$PID"
|
|
else
|
|
echo "Killing process group $PGID (found via node PID $PID on port $VITE_PORT)"
|
|
kill -9 -"$PGID" 2>/dev/null || true
|
|
fi
|
|
|
|
# Kill all dev processes across all worktrees
|
|
kill-all:
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
branches=$(just running)
|
|
if [[ -z "$branches" ]]; then
|
|
echo "No running dev servers found"
|
|
exit 0
|
|
fi
|
|
while read -r branch; do
|
|
just kill "$branch" || true
|
|
done <<< "$branches"
|
|
|
|
# ── Utilities ────────────────────────────────────────────────
|
|
|
|
# Clean build artifacts
|
|
clean:
|
|
cd src-tauri && cargo clean
|
|
rm -rf dist
|
|
rm -rf node_modules
|