Files
wehub-resource-sync f99010fae1
Desktop Artifacts / Desktop Build (Linux) (push) Waiting to run
Desktop Artifacts / Desktop Build (Windows) (push) Waiting to run
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Waiting to run
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

568 lines
23 KiB
Makefile

.DEFAULT_GOAL := help
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
BUILD_DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
LDFLAGS := -X main.version=$(VERSION) \
-X main.commit=$(COMMIT) \
-X main.buildDate=$(BUILD_DATE)
LDFLAGS_RELEASE := $(LDFLAGS) -s -w
DESKTOP_DIST_DIR := dist/desktop
GOLANGCI_LINT_VERSION ?= v2.11.4
# Isolate each checkout from stale sibling-worktree fixes and issue positions.
GOLANGCI_LINT_CACHE ?= $(CURDIR)/.golangci-cache
export GOLANGCI_LINT_CACHE
CUSTOM_GCL := ./custom-gcl
PRICING_SNAPSHOT_FILE := internal/pricing/snapshot/litellm_snapshot.json.gz
# sqlite-vec's cgo bindings #include "sqlite3.h". Without an override the
# compiler falls back to the system header (the macOS SDK one marks
# sqlite3_auto_extension deprecated, warning on every build) which can also
# drift from the amalgamation mattn/go-sqlite3 statically links. Compile
# against the bundled amalgamation's own header instead, mirroring the
# Linux release workflow in .github/workflows/release.yml.
# Setting CGO_CFLAGS replaces Go's built-in default of "-O2 -g", so restate
# it explicitly or the SQLite amalgamation compiles unoptimized (2-3x slower
# queries, caught by the bench gate). Caller-provided flags stay last so
# they can still override.
SQLITE_INCLUDE_DIR := .sqlite-include
export CGO_CFLAGS := -O2 -g -I$(CURDIR)/$(SQLITE_INCLUDE_DIR) $(CGO_CFLAGS)
GOPATH_FIRST := $(shell go env GOPATH | cut -d: -f1)
AIR_BIN := $(shell if command -v air >/dev/null 2>&1; then command -v air; \
elif [ -n "$$(go env GOBIN)" ] && [ -x "$$(go env GOBIN)/air" ]; then printf "%s" "$$(go env GOBIN)/air"; \
elif [ -x "$(GOPATH_FIRST)/bin/air" ]; then printf "%s" "$(GOPATH_FIRST)/bin/air"; \
fi)
.PHONY: build build-release install frontend frontend-dev dev check-air air-install desktop-dev desktop-build desktop-macos-app desktop-macos-dmg desktop-windows-installer desktop-linux-appimage desktop-app docs-install docs-build docs-serve docs-check docs-screenshots docs-assets-branch docs-generated-assets-branch docs-deploy-staging docs-deploy test test-short test-evalingest bench-backends bench-gate bench-gate-config test-postgres test-postgres-ci test-s3 postgres-up postgres-down test-ssh test-ssh-ci ssh-up ssh-down e2e e2e-duckdb vet lint lint-ci lint-golangci lint-golangci-ci nilaway nilaway-golangci-build lint-tools tidy clean release release-darwin-arm64 release-darwin-amd64 release-linux-amd64 install-hooks ensure-embed-dir pricing-snapshot sqlite-vec-header dev-snapshot help
# Ensure go:embed has at least one file (no-op if frontend is built)
ensure-embed-dir:
@mkdir -p internal/web/dist
@test -f internal/web/dist/.keep \
|| printf '%s\n' \
'keep embed dir for generated frontend assets' \
> internal/web/dist/.keep
# Copy the go-sqlite3 amalgamation header where CGO_CFLAGS points.
# Chained from pricing-snapshot so every Go-compiling target stages it
# without lengthening each prerequisite list.
sqlite-vec-header:
@go mod download github.com/mattn/go-sqlite3
@mkdir -p $(SQLITE_INCLUDE_DIR)
@install -m 0644 \
"$$(go list -m -f '{{.Dir}}' github.com/mattn/go-sqlite3)/sqlite3-binding.h" \
$(SQLITE_INCLUDE_DIR)/sqlite3.h
# Restore the generated LiteLLM fallback snapshot from its artifact branch.
# Pinned ref, SHA256, and branch are compiled into the snapshot tool.
pricing-snapshot: sqlite-vec-header
go run ./internal/pricing/cmd/litellm-snapshot -restore
# Build the binary (debug, with embedded pricing snapshot and frontend)
build: pricing-snapshot frontend
CGO_ENABLED=1 go build -tags fts5 -ldflags="$(LDFLAGS)" -o agentsview ./cmd/agentsview
@chmod +x agentsview
# Build with optimizations (release)
build-release: pricing-snapshot frontend
CGO_ENABLED=1 go build -tags fts5 -ldflags="$(LDFLAGS_RELEASE)" -trimpath -o agentsview ./cmd/agentsview
@chmod +x agentsview
# Install to ~/.local/bin, $GOBIN, or $GOPATH/bin.
# Copy to a temp file in the destination directory, then rename into place.
# Rename is atomic and produces a fresh inode, so overwriting the binary while
# an old agentsview is still running does not leave the kernel validating exec
# against stale code-signature pages (which SIGKILLs the new process on macOS).
install: build-release
@if [ -d "$(HOME)/.local/bin" ]; then \
INSTALL_DIR="$(HOME)/.local/bin"; \
else \
INSTALL_DIR="$${GOBIN:-$$(go env GOBIN)}"; \
if [ -z "$$INSTALL_DIR" ]; then \
GOPATH_FIRST="$$(go env GOPATH | cut -d: -f1)"; \
INSTALL_DIR="$$GOPATH_FIRST/bin"; \
fi; \
mkdir -p "$$INSTALL_DIR"; \
fi; \
echo "Installing to $$INSTALL_DIR/agentsview"; \
tmp="$$(mktemp "$$INSTALL_DIR/agentsview.tmp.XXXXXX")" || exit $$?; \
cleanup() { rm -f "$$tmp"; }; \
trap cleanup EXIT HUP INT TERM; \
cp agentsview "$$tmp" && chmod 755 "$$tmp" && mv -f "$$tmp" "$$INSTALL_DIR/agentsview"; \
status=$$?; \
trap - EXIT HUP INT TERM; \
cleanup; \
exit $$status
# Build frontend SPA and copy into embed directory
frontend:
cd frontend && npm ci && npm run build
rm -rf internal/web/dist
cp -r frontend/dist internal/web/dist
printf '%s\n' \
'keep embed dir for generated frontend assets' \
> internal/web/dist/.keep
# Run Vite+ dev server (use alongside `make dev`)
frontend-dev:
cd frontend && npm run dev
# Build and run agentsview against a fresh snapshot of the prod SQLite DB.
# Prod DB is never written; sqlite3 .backup is WAL-safe even with prod running.
# Prod config.toml is NOT copied, so remote PG push is disabled in the snapshot.
# Overrides:
# PROD_DATA_DIR - source data dir (default: $$HOME/.agentsview)
# SNAPSHOT_DIR - destination dir (default: tmp/prod-snapshot)
# RESNAPSHOT=0 - reuse existing snapshot instead of re-cloning
PROD_DATA_DIR ?= $(HOME)/.agentsview
SNAPSHOT_DIR ?= tmp/prod-snapshot
# Resolve SNAPSHOT_DIR so relative and absolute paths both work.
SNAPSHOT_ABS := $(abspath $(SNAPSHOT_DIR))
# Sentinel file written into a snapshot directory the first time
# we populate it. Subsequent runs require this marker before
# touching any contents, so pointing SNAPSHOT_DIR at an
# unrelated existing directory cannot accidentally delete
# someone's files. The previous version used rm -rf on the
# whole directory; that left dangerous edge cases (e.g.
# SNAPSHOT_DIR=tmp wiping unrelated tmp/ contents) even with a
# denylist, so we now only ever delete a small set of files we
# know we wrote.
SNAPSHOT_MARKER := .agentsview-snapshot
dev-snapshot: build
@if [ ! -f "$(PROD_DATA_DIR)/sessions.db" ]; then \
echo "error: prod sessions.db not found at $(PROD_DATA_DIR)/sessions.db" >&2; \
exit 1; \
fi
@if [ -z "$(SNAPSHOT_ABS)" ]; then \
echo "error: SNAPSHOT_DIR resolved to empty path" >&2; \
exit 1; \
fi
@if [ -d "$(SNAPSHOT_ABS)" ] && [ ! -f "$(SNAPSHOT_ABS)/$(SNAPSHOT_MARKER)" ]; then \
if [ -n "$$(ls -A "$(SNAPSHOT_ABS)" 2>/dev/null)" ]; then \
echo "error: $(SNAPSHOT_ABS) is non-empty and missing the $(SNAPSHOT_MARKER) marker" >&2; \
echo " refusing to touch a directory we did not create" >&2; \
echo " remove the directory manually if you want to reuse this path" >&2; \
exit 1; \
fi; \
fi
@mkdir -p "$(SNAPSHOT_ABS)"
@touch "$(SNAPSHOT_ABS)/$(SNAPSHOT_MARKER)"
@if [ "$${RESNAPSHOT:-1}" = "1" ] || [ ! -f "$(SNAPSHOT_ABS)/sessions.db" ]; then \
echo "Snapshotting $(PROD_DATA_DIR)/sessions.db -> $(SNAPSHOT_ABS)/sessions.db"; \
for f in sessions.db sessions.db-wal sessions.db-shm \
config.toml config.json config.json.bak \
debug.log; do \
rm -f "$(SNAPSHOT_ABS)/$$f"; \
done; \
sqlite3 "$(PROD_DATA_DIR)/sessions.db" \
".backup $(SNAPSHOT_ABS)/sessions.db"; \
else \
echo "Reusing existing snapshot at $(SNAPSHOT_ABS)/sessions.db"; \
fi
AGENTSVIEW_DATA_DIR="$(SNAPSHOT_ABS)" ./agentsview serve --port 0
# Ensure air is installed for backend live reload
check-air:
@if [ -z "$(AIR_BIN)" ]; then \
echo "air not found. Install with: make air-install" >&2; \
exit 1; \
fi
# Install air for backend live reload
air-install:
go install github.com/air-verse/air@latest
# Run Go server in dev mode with live reload (use with frontend-dev).
# Edits to .go files trigger a rebuild + restart via air.
dev: pricing-snapshot ensure-embed-dir check-air
"$(AIR_BIN)" -c .air.toml -- $(ARGS)
# Run the Tauri desktop wrapper in development mode
desktop-dev:
cd desktop && npm ci && npm run tauri:dev
# Build desktop app bundles via Tauri
desktop-build:
cd desktop && npm ci && npm run tauri:build
# Build only the macOS .app bundle (skip DMG packaging).
# Skips updater artifact signing when TAURI_SIGNING_PRIVATE_KEY
# is not set so local builds succeed without release keys.
desktop-macos-app:
cd desktop && npm ci && npm run tauri:build:macos-app \
$(if $(TAURI_SIGNING_PRIVATE_KEY),,-- --config '{"bundle":{"createUpdaterArtifacts":false}}')
mkdir -p $(DESKTOP_DIST_DIR)/macos
rm -rf $(DESKTOP_DIST_DIR)/macos/AgentsView.app
cp -R desktop/src-tauri/target/release/bundle/macos/AgentsView.app \
$(DESKTOP_DIST_DIR)/macos/AgentsView.app
@echo "macOS app bundle copied to $(DESKTOP_DIST_DIR)/macos/AgentsView.app"
# Build macOS DMG installer
desktop-macos-dmg:
cd desktop && npm ci && npm run tauri:build:macos-dmg
mkdir -p $(DESKTOP_DIST_DIR)/macos
rm -f $(DESKTOP_DIST_DIR)/macos/*.dmg
@dmg_count=$$(find desktop/src-tauri/target/release/bundle/dmg \
-maxdepth 1 -type f -name '*.dmg' | wc -l | tr -d ' '); \
if [ "$$dmg_count" -eq 0 ]; then \
echo "error: no DMG installer found in bundle output" >&2; \
exit 1; \
fi; \
find desktop/src-tauri/target/release/bundle/dmg \
-maxdepth 1 -type f -name '*.dmg' \
-exec cp {} $(DESKTOP_DIST_DIR)/macos/ \;; \
echo "Copied $$dmg_count DMG installer(s) to $(DESKTOP_DIST_DIR)/macos/"
# Build Windows NSIS installer bundle (.exe)
# Run on Windows runner/host.
desktop-windows-installer:
cd desktop && npm ci && npm run tauri:build:windows
mkdir -p $(DESKTOP_DIST_DIR)/windows
rm -f $(DESKTOP_DIST_DIR)/windows/*.exe
@bundle_dir="desktop/src-tauri/target/release/bundle/nsis"; \
target_triple="$${TAURI_ENV_TARGET_TRIPLE:-$${CARGO_BUILD_TARGET:-}}"; \
if [ -z "$$target_triple" ] && command -v rustc >/dev/null 2>&1; then \
host_triple=$$(rustc -vV | awk '/^host: /{print $$2}'); \
if [ "$$host_triple" = "aarch64-pc-windows-msvc" ]; then \
target_triple="$$host_triple"; \
fi; \
fi; \
if [ -n "$$target_triple" ] && \
[ -d "desktop/src-tauri/target/$$target_triple/release/bundle/nsis" ]; then \
bundle_dir="desktop/src-tauri/target/$$target_triple/release/bundle/nsis"; \
fi; \
if [ ! -d "$$bundle_dir" ]; then \
echo "error: Windows bundle output directory not found: $$bundle_dir" >&2; \
exit 1; \
fi; \
exe_count=$$(find "$$bundle_dir" \
-maxdepth 1 -type f -name '*.exe' | wc -l | tr -d ' '); \
if [ "$$exe_count" -eq 0 ]; then \
echo "error: no Windows installer (.exe) found in $$bundle_dir" >&2; \
exit 1; \
fi; \
find "$$bundle_dir" \
-maxdepth 1 -type f -name '*.exe' \
-exec cp {} $(DESKTOP_DIST_DIR)/windows/ \;; \
echo "Copied $$exe_count Windows installer(s) to $(DESKTOP_DIST_DIR)/windows/"
# Build Linux AppImage bundle
# Run on a Linux host.
desktop-linux-appimage:
cd desktop && npm ci && npm run tauri:build:linux \
$(if $(TAURI_SIGNING_PRIVATE_KEY),,-- --config '{"bundle":{"createUpdaterArtifacts":false}}')
cd desktop && bash scripts/repair-appimage-diricon.sh \
src-tauri/target/release/bundle/appimage/*.AppImage
mkdir -p $(DESKTOP_DIST_DIR)/linux
rm -f $(DESKTOP_DIST_DIR)/linux/*.AppImage
@ai_count=$$(find desktop/src-tauri/target/release/bundle/appimage \
-maxdepth 1 -type f -name '*.AppImage' | wc -l | tr -d ' '); \
if [ "$$ai_count" -eq 0 ]; then \
echo "error: no AppImage found in bundle output" >&2; \
exit 1; \
fi; \
find desktop/src-tauri/target/release/bundle/appimage \
-maxdepth 1 -type f -name '*.AppImage' \
-exec cp {} $(DESKTOP_DIST_DIR)/linux/ \;; \
echo "Copied $$ai_count AppImage(s) to $(DESKTOP_DIST_DIR)/linux/"
# Backward-compatible alias (macOS .app)
desktop-app: desktop-macos-app
# Run tests
test: pricing-snapshot ensure-embed-dir
go test -tags "fts5" ./... -v -count=1
# Run fast tests only
test-short: pricing-snapshot ensure-embed-dir
go test -tags "fts5" ./... -short -count=1
# Run the quarantined eval-ingest endpoint tests under their build tag.
test-evalingest: pricing-snapshot ensure-embed-dir
CGO_ENABLED=1 go test -tags "fts5,evalingest" \
./internal/db ./internal/server -v -count=1
# Compare db.Store read-query performance across SQLite, DuckDB, and PostgreSQL.
# Requires Docker because the PostgreSQL backend is started with testcontainers.
BENCH_BACKENDS_FLAGS ?= -bench . -run '^$$' -benchmem
BENCH_BACKENDS_SESSIONS ?= 1000
BENCH_BACKENDS_MESSAGES_PER_SESSION ?= 64
bench-backends: pricing-snapshot ensure-embed-dir
AGENTSVIEW_BENCH_SESSIONS=$(BENCH_BACKENDS_SESSIONS) \
AGENTSVIEW_BENCH_MESSAGES_PER_SESSION=$(BENCH_BACKENDS_MESSAGES_PER_SESSION) \
CGO_ENABLED=1 go test -tags "fts5,benchdb" ./internal/backendbench $(BENCH_BACKENDS_FLAGS)
# Hot-path benchmark gate. Runs every benchmark in the gated packages
# (sync engine warm/cold/append, message write paths, usage
# aggregation, secret scanning). This target is the single source of
# truth for the gate configuration: CI's bench.yml runs it on both
# the PR head and the merge base, then compares the outputs with
# `go run ./cmd/benchgate -old old.txt -new new.txt`. Run it before
# and after touching a sync or DB hot path.
BENCH_GATE_PACKAGES ?= ./internal/sync ./internal/db ./internal/secrets
# Count must stay >= 5: benchgate's time gate needs at least 5
# candidate samples for its significance test.
BENCH_GATE_COUNT ?= 6
# Fixed iterations, not a duration: some gated benchmarks grow their
# fixture as they iterate, so baseline and candidate must run the
# same iteration count to measure identical workloads.
BENCH_GATE_TIME ?= 20x
bench-gate: pricing-snapshot ensure-embed-dir
CGO_ENABLED=1 go test -tags "fts5" -run '^$$' \
-bench . -benchmem \
-count $(BENCH_GATE_COUNT) -benchtime $(BENCH_GATE_TIME) \
-timeout 25m $(BENCH_GATE_PACKAGES)
# Prints the gate's sample/iteration configuration in shell-evalable
# form. CI evaluates this on the PR head and passes the values into
# the merge-base `make bench-gate` invocation, so both sides measure
# identical workloads even when a PR changes the defaults above (the
# package list intentionally stays per-side).
bench-gate-config:
@echo "BENCH_GATE_COUNT=$(BENCH_GATE_COUNT) BENCH_GATE_TIME=$(BENCH_GATE_TIME)"
# Start test PostgreSQL container
postgres-up:
docker compose -f docker-compose.test.yml up -d --wait
# Stop test PostgreSQL container
postgres-down:
docker compose -f docker-compose.test.yml down
# Run PostgreSQL integration tests (starts postgres automatically)
test-postgres: pricing-snapshot ensure-embed-dir postgres-up
@echo "Waiting for postgres to be ready..."
@sleep 2
TEST_PG_URL="postgres://agentsview_test:agentsview_test_password@localhost:5433/agentsview_test?sslmode=disable" \
CGO_ENABLED=1 go test -tags "fts5,pgtest" -v ./internal/postgres/... ./internal/activity/... -count=1
# PostgreSQL integration tests for CI (postgres already running as service)
test-postgres-ci: pricing-snapshot ensure-embed-dir
CGO_ENABLED=1 go test -tags "fts5,pgtest" -v ./internal/postgres/... ./internal/activity/... -count=1
# S3 discovery integration tests. testcontainers starts and tears down a
# rustfs (S3-compatible) container automatically, so only a working Docker
# daemon is required.
test-s3: pricing-snapshot ensure-embed-dir
CGO_ENABLED=1 go test -tags "fts5,s3test" -v ./internal/sync/... -run TestS3 -count=1
# Start test SSH container
ssh-up:
docker compose -f docker-compose.test.yml up -d --build --wait sshd
docker cp "$$(docker compose -f docker-compose.test.yml ps -q sshd)":/tmp/test_ssh_key testdata/ssh/test_key
chmod 600 testdata/ssh/test_key
# Stop test SSH container
ssh-down:
docker compose -f docker-compose.test.yml down sshd
# Run SSH integration tests (starts sshd automatically)
test-ssh: pricing-snapshot ensure-embed-dir ssh-up
TEST_SSH_HOST=localhost TEST_SSH_PORT=2222 TEST_SSH_USER=testuser \
TEST_SSH_KEY=$(CURDIR)/testdata/ssh/test_key \
CGO_ENABLED=1 go test -tags "fts5,sshtest" -v ./internal/ssh/... -count=1
# SSH integration tests for CI (sshd already running)
test-ssh-ci: pricing-snapshot ensure-embed-dir
CGO_ENABLED=1 go test -tags "fts5,sshtest" -v ./internal/ssh/... -count=1
# Run Playwright E2E tests
e2e:
cd frontend && npx playwright test
# Run focused Playwright smoke tests against duckdb serve.
e2e-duckdb:
cd frontend && AGENTSVIEW_E2E_BACKEND=duckdb npx playwright test \
e2e/duckdb-backend.spec.ts e2e/session-list.spec.ts --project=chromium
# Vet
vet: pricing-snapshot ensure-embed-dir
go vet -tags fts5 ./...
# Lint Go code and auto-fix where possible (local development)
lint: lint-golangci nilaway
# Run golangci-lint with auto-fixes for local development.
lint-golangci: pricing-snapshot ensure-embed-dir
@if ! command -v golangci-lint >/dev/null 2>&1; then \
echo "golangci-lint not found. Install with: make lint-tools" >&2; \
exit 1; \
fi
golangci-lint run --fix ./...
# Lint Go code without fixing (for CI)
lint-ci: lint-golangci-ci nilaway
# Run golangci-lint without auto-fixes for CI.
lint-golangci-ci: pricing-snapshot ensure-embed-dir
@if ! command -v golangci-lint >/dev/null 2>&1; then \
echo "golangci-lint not found. Install with: make lint-tools" >&2; \
exit 1; \
fi
golangci-lint run ./...
# Build a custom golangci-lint binary with the NilAway module plugin.
# Strip every repo-local Git env var (GIT_DIR, GIT_INDEX_FILE,
# GIT_CONFIG_PARAMETERS, etc.) and disable VCS stamping so the inner
# `git clone` and `go build` don't inherit the parent repo's state.
# When `make nilaway` runs from a pre-commit hook, git exports those
# vars pointing at the parent repo, which makes the clone and the
# VCS-stamped build fail with exit 128. The list comes from
# `git rev-parse --local-env-vars` so it tracks whatever Git considers
# repo-local at runtime.
nilaway-golangci-build:
@if ! command -v golangci-lint >/dev/null 2>&1; then \
echo "golangci-lint not found. Install with: make lint-tools" >&2; \
exit 1; \
fi
@unset_args=$$(git rev-parse --local-env-vars 2>/dev/null | sed 's/^/-u /' | tr '\n' ' '); \
env $$unset_args GOFLAGS=-buildvcs=false \
golangci-lint custom --version "$(GOLANGCI_LINT_VERSION)" --name custom-gcl
# Run NilAway through the custom golangci-lint module plugin.
nilaway: pricing-snapshot ensure-embed-dir nilaway-golangci-build
@set -e; \
root=$$(pwd); \
dirs=$$(go list -f '{{.Dir}}' ./...); \
for dir in $$dirs; do \
if [ "$$dir" = "$$root" ]; then \
pkg="."; \
else \
pkg="./$${dir#$$root/}"; \
fi; \
echo "$(CUSTOM_GCL) run --config .golangci.nilaway.yml $$pkg"; \
GOMAXPROCS=$${GOMAXPROCS:-1} GOGC=$${GOGC:-10} GOMEMLIMIT=$${GOMEMLIMIT:-512MiB} \
$(CUSTOM_GCL) run --config .golangci.nilaway.yml "$$pkg"; \
done
# Install pinned local lint tools.
lint-tools:
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)
# Tidy dependencies
tidy: pricing-snapshot
go mod tidy
# Clean build artifacts
clean:
rm -f agentsview agentsv
rm -f $(PRICING_SNAPSHOT_FILE)
rm -rf internal/web/dist dist/ tmp/ $(SQLITE_INCLUDE_DIR)
mkdir -p internal/web/dist
printf '%s\n' \
'keep embed dir for generated frontend assets' \
> internal/web/dist/.keep
docs-install:
cd docs && uv sync --frozen --no-dev
docs-build:
cd docs && uv run --frozen bash ./vercel-build.sh
docs-serve:
cd docs && bash assets/hydrate-assets.sh && uv run bash ./zensical-docs.sh serve
docs-check:
bash scripts/check-docs.sh
docs-screenshots:
bash docs/screenshots/run.sh
docs-assets-branch:
bash docs/assets/update-static-assets-branch.sh
docs-generated-assets-branch:
bash docs/screenshots/update-generated-assets-branch.sh
docs-deploy-staging:
vercel deploy docs
docs-deploy:
vercel deploy docs --prod
# Build release binary for current platform (CGO required for sqlite3)
release: pricing-snapshot frontend
mkdir -p dist
CGO_ENABLED=1 go build -tags fts5 \
-ldflags="$(LDFLAGS_RELEASE)" -trimpath \
-o dist/agentsview-$$(go env GOOS)-$$(go env GOARCH) ./cmd/agentsview
# Cross-compile targets (require CC set to target cross-compiler)
release-darwin-arm64: pricing-snapshot frontend
mkdir -p dist
GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go build -tags fts5 \
-ldflags="$(LDFLAGS_RELEASE)" -trimpath \
-o dist/agentsview-darwin-arm64 ./cmd/agentsview
release-darwin-amd64: pricing-snapshot frontend
mkdir -p dist
GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build -tags fts5 \
-ldflags="$(LDFLAGS_RELEASE)" -trimpath \
-o dist/agentsview-darwin-amd64 ./cmd/agentsview
release-linux-amd64: pricing-snapshot frontend
mkdir -p dist
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -tags fts5 \
-ldflags="$(LDFLAGS_RELEASE)" -trimpath \
-o dist/agentsview-linux-amd64 ./cmd/agentsview
# Install pre-commit and pre-push hooks via prek
install-hooks:
@if ! command -v prek >/dev/null 2>&1; then \
echo "prek not found. Install with: brew install prek" >&2; \
exit 1; \
fi
prek install -f
# Show help
help:
@echo "agentsview build targets:"
@echo ""
@echo " build - Build with embedded frontend"
@echo " build-release - Release build (optimized, stripped)"
@echo " pricing-snapshot - Restore LiteLLM snapshot from artifact branch"
@echo " install - Build and install to ~/.local/bin or GOPATH"
@echo ""
@echo " dev - Run Go server with live reload via air (use with frontend-dev)"
@echo " dev-snapshot - Run agentsview against a fresh snapshot of prod sessions.db"
@echo " air-install - Install air for backend live reload"
@echo " frontend - Build frontend SPA"
@echo " frontend-dev - Run Vite+ dev server"
@echo " desktop-dev - Run Tauri desktop wrapper in dev mode"
@echo " desktop-build - Build Tauri desktop app bundles"
@echo " desktop-macos-app - Build macOS .app bundle only"
@echo " desktop-macos-dmg - Build macOS DMG installer"
@echo " desktop-windows-installer - Build Windows NSIS installer"
@echo " desktop-linux-appimage - Build Linux AppImage"
@echo " desktop-app - Alias for desktop-macos-app"
@echo ""
@echo " test - Run all tests"
@echo " test-short - Run fast tests only"
@echo " bench-backends - Benchmark SQLite, DuckDB, and PostgreSQL stores"
@echo " bench-gate - Run the hot-path benchmarks CI gates PRs on"
@echo " test-postgres - Run PostgreSQL integration tests"
@echo " test-s3 - Run S3 discovery integration tests (Docker)"
@echo " postgres-up - Start test PostgreSQL container"
@echo " postgres-down - Stop test PostgreSQL container"
@echo " test-ssh - Run SSH integration tests"
@echo " ssh-up - Start test SSH container"
@echo " ssh-down - Stop test SSH container"
@echo " e2e - Run Playwright E2E tests"
@echo " e2e-duckdb - Run DuckDB-backed Playwright smoke tests"
@echo " vet - Run go vet"
@echo " lint - Run golangci-lint and NilAway (auto-fix golangci issues)"
@echo " lint-ci - Run golangci-lint and NilAway (no fix, for CI)"
@echo " lint-golangci - Run golangci-lint with auto-fix"
@echo " nilaway - Run NilAway through custom golangci-lint"
@echo " lint-tools - Install pinned lint tools"
@echo " tidy - Tidy go.mod"
@echo ""
@echo " release - Release build for current platform"
@echo " clean - Remove build artifacts"
@echo " install-hooks - Install pre-commit and pre-push git hooks"