Files
goose/ui/goose2/justfile
Matt Toohey e0f8e6b256 refactor(goose2): deduplicate backend_paths in justfile
Extract the duplicated BACKEND_PATHS shell variable from the `setup`
and `check-binary` recipes into a single top-level `just` variable
`backend_paths`, referenced via {{ backend_paths }} interpolation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
2026-04-21 11:59:16 +10:00

286 lines
10 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)"`
# Paths that affect the goose binary — used to filter git diff/status.
# NOTE: if you add a new crate dependency to goose-cli, add its path here.
backend_paths := "crates/goose-cli crates/goose crates/goose-acp crates/goose-acp-macros crates/goose-mcp crates/goose-server Cargo.toml Cargo.lock vendor"
# Default recipe
default:
@just --list
# ── Dev Environment ──────────────────────────────────────────
# Install dependencies and build workspace packages
setup:
#!/usr/bin/env bash
set -euo pipefail
cd ../ && pnpm install
cd sdk && pnpm build
cargo build --manifest-path ../../Cargo.toml -p goose-cli --bin goose
SHA=$(git rev-parse HEAD)
if [[ -n "$(git status --porcelain -- {{ backend_paths }} 2>/dev/null)" ]]; then
SHA="${SHA}-dirty"
fi
echo "${SHA}" > ../../target/debug/.goose-build-sha
# Check if the goose binary matches the current git state.
# Only flags staleness when paths that affect the binary have changed.
check-binary bin_path="":
#!/usr/bin/env bash
set -euo pipefail
GOOSE_BIN="{{ bin_path }}"
if [[ -z "${GOOSE_BIN}" ]]; then
REPO_ROOT=$(cd ../.. && pwd)
if [[ -x "${REPO_ROOT}/target/debug/goose" ]]; then
GOOSE_BIN="${REPO_ROOT}/target/debug/goose"
elif [[ -x "${REPO_ROOT}/target/release/goose" ]]; then
GOOSE_BIN="${REPO_ROOT}/target/release/goose"
else
echo "No local goose binary found; skipping build check"
exit 0
fi
fi
STAMP_FILE="${GOOSE_BIN%/*}/.goose-build-sha"
CURRENT_SHA=$(git rev-parse HEAD)
CURRENT_DIRTY=""
if [[ -n "$(git status --porcelain -- {{ backend_paths }} 2>/dev/null)" ]]; then
CURRENT_DIRTY="-dirty"
fi
if [[ ! -f "${STAMP_FILE}" ]]; then
echo "ERROR: no build stamp found — goose binary may be stale"
echo " Run: just goose2 setup"
exit 1
fi
BUILD_REF=$(cat "${STAMP_FILE}")
BUILD_BASE="${BUILD_REF%-dirty}"
BUILD_DIRTY=""
[[ "${BUILD_REF}" == *-dirty ]] && BUILD_DIRTY="-dirty"
if [[ "${BUILD_BASE}" != "${CURRENT_SHA}" ]]; then
# SHAs differ — check whether any backend-relevant paths changed.
if ! RELEVANT_CHANGES=$(git diff --name-only "${BUILD_BASE}" "${CURRENT_SHA}" -- {{ backend_paths }} 2>/dev/null); then
# Build SHA is no longer reachable (e.g. after rebase/force-push).
echo "ERROR: goose binary build SHA is no longer in history — binary may be stale"
echo " Binary built from: ${BUILD_REF}"
echo " Current HEAD: ${CURRENT_SHA}${CURRENT_DIRTY}"
echo " Run: just goose2 setup"
exit 1
fi
if [[ -n "${RELEVANT_CHANGES}" ]]; then
echo "ERROR: goose binary is stale — backend code changed since last build"
echo " Binary built from: ${BUILD_REF}"
echo " Current HEAD: ${CURRENT_SHA}${CURRENT_DIRTY}"
echo " Changed paths:"
echo "${RELEVANT_CHANGES}" | sed 's/^/ /'
echo " Run: just goose2 setup"
exit 1
fi
elif [[ "${BUILD_DIRTY}" != "${CURRENT_DIRTY}" ]]; then
echo "WARNING: goose binary dirty state differs from working tree"
echo " Binary built from: ${BUILD_REF}"
echo " Current HEAD: ${CURRENT_SHA}${CURRENT_DIRTY}"
echo " Consider running: just goose2 setup"
fi
# ── 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 && 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 && 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:
#!/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)
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}"
just check-binary "${GOOSE_BIN}"
else
echo "No local goose binary found under ${REPO_ROOT}/target; falling back to PATH"
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:
#!/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)
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}"
just check-binary "${GOOSE_BIN}"
else
echo "No local goose binary found under ${REPO_ROOT}/target; falling back to PATH"
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
# Kill the dev process (if running)
kill:
#!/usr/bin/env bash
set -euo pipefail
VITE_PORT={{ vite_port }}
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
if [[ "$PROC_NAME" != "node" ]]; then
echo "Process on port $VITE_PORT is '$PROC_NAME', not 'node' — refusing to kill"
exit 1
fi
echo "Killing node (PID $PID) on port $VITE_PORT"
kill -9 "$PID"
# ── Utilities ────────────────────────────────────────────────
# Clean build artifacts
clean:
cd src-tauri && cargo clean
rm -rf dist
rm -rf node_modules