1
0

chore: streamline workspace make workflows

Clarify product-level server and desktop commands while moving backend-only maintenance tasks into backend/Makefile. This keeps root automation focused on core flows and aligns the main OpenSpec specs with the new command boundaries.
This commit is contained in:
2026-04-28 17:44:23 +08:00
parent a9972360c2
commit 2c401f7ae6
8 changed files with 411 additions and 266 deletions

308
Makefile
View File

@@ -1,14 +1,14 @@
.PHONY: all dev build test lint clean \ .PHONY: \
lint test clean \
version-sync version-check \ version-sync version-check \
backend-build backend-run backend-dev backend-test backend-test-all backend-test-unit backend-test-integration backend-test-coverage \ server-run server-build server-lint server-test server-clean \
backend-lint backend-clean backend-deps backend-generate \ desktop-build-mac desktop-build-win desktop-build-linux \
backend-db-up backend-db-down backend-db-status backend-db-create \ desktop-lint desktop-test desktop-clean \
test-mysql-up test-mysql-down test-mysql test-mysql-quick \ release-assets-linux release-assets-windows release-assets-macos \
frontend-build frontend-dev frontend-test frontend-test-watch frontend-test-coverage frontend-test-e2e frontend-lint frontend-clean \ _backend-lint _backend-test _backend-clean _backend-build \
desktop-build desktop-build-mac desktop-build-win desktop-build-linux \ _frontend-install _frontend-build _frontend-check _frontend-test _frontend-dev _frontend-clean \
desktop-dev desktop-test desktop-clean \ _desktop-test _desktop-clean _desktop-prepare-frontend _desktop-prepare-embedfs _desktop-prepare-windows-resource \
desktop-prepare-frontend desktop-prepare-embedfs desktop-prepare-windows-resource \ _server-run-backend _server-run-frontend
release-assets-linux release-assets-windows release-assets-macos
VERSION := $(shell go run ./backend/cmd/versionctl print) VERSION := $(shell go run ./backend/cmd/versionctl print)
GIT_COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || printf 'unknown') GIT_COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || printf 'unknown')
@@ -25,23 +25,17 @@ DESKTOP_WINDOWS_ASSET := $(shell go run ./backend/cmd/versionctl asset-name desk
DESKTOP_MACOS_ASSET := $(shell go run ./backend/cmd/versionctl asset-name desktop macos) DESKTOP_MACOS_ASSET := $(shell go run ./backend/cmd/versionctl asset-name desktop macos)
# ============================================ # ============================================
# 顶层便捷命令 # 全局命令
# ============================================ # ============================================
dev: lint: _backend-lint _frontend-check
@echo "🚀 Starting development environment..." @printf 'Lint complete\n'
@$(MAKE) -j2 backend-dev frontend-dev
build: backend-build frontend-build test: _backend-test _frontend-test _desktop-test
@echo "✅ Build complete" @printf 'All tests passed\n'
test: backend-test desktop-test frontend-test clean: _backend-clean _frontend-clean _desktop-clean
@echo "✅ All tests passed" @printf 'Clean complete\n'
lint: backend-lint frontend-lint
@echo "✅ Lint complete"
all: build test lint
# ============================================ # ============================================
# 版本管理 # 版本管理
@@ -54,136 +48,89 @@ version-check:
go run ./backend/cmd/versionctl check go run ./backend/cmd/versionctl check
# ============================================ # ============================================
# 后端 # Server 模式
# ============================================ # ============================================
backend-build: version-check server-run:
cd backend && go build -ldflags "$(GO_LDFLAGS)" -o bin/server ./cmd/server @$(MAKE) -j2 _server-run-backend _server-run-frontend
backend-run: version-check server-build: version-check _backend-build _frontend-build
cd backend && go run -ldflags "$(GO_LDFLAGS)" ./cmd/server @printf 'Server build complete\n'
backend-dev: version-check server-lint: _backend-lint _frontend-check
cd backend && go run -ldflags "$(GO_LDFLAGS)" ./cmd/server @printf 'Server lint complete\n'
backend-test: server-test: _backend-test _frontend-test
cd backend && go test ./internal/... ./pkg/... ./tests/... ./cmd/server/... -v @printf 'Server tests passed\n'
backend-test-all: server-clean: _backend-clean _frontend-clean
cd backend && go test ./... -v @printf 'Server artifacts cleaned\n'
backend-test-unit: _server-run-backend:
cd backend && go test ./internal/... ./pkg/... -v @$(MAKE) -C backend run
backend-test-integration: _server-run-frontend: _frontend-install
cd backend && go test ./tests/... -v cd frontend && bun run dev
backend-test-coverage:
cd backend && go test ./... -coverprofile=coverage.out
cd backend && go tool cover -html=coverage.out -o coverage.html
@echo "Coverage report generated: backend/coverage.html"
backend-lint:
cd backend && go tool golangci-lint run ./...
backend-clean:
rm -rf backend/bin/ backend/coverage.out backend/coverage.html
backend-deps:
cd backend && go mod tidy
backend-generate:
cd backend && go generate ./...
backend-db-up:
@echo "Running database migration up..."
cd backend && goose -dir migrations/sqlite sqlite3 "$(DB_PATH)" up
backend-db-down:
@echo "Running database migration down..."
cd backend && goose -dir migrations/sqlite sqlite3 "$(DB_PATH)" down
backend-db-status:
@echo "Checking database migration status..."
cd backend && goose -dir migrations/sqlite sqlite3 "$(DB_PATH)" status
backend-db-create:
@read -p "Migration name: " name; \
cd backend && goose -dir migrations/sqlite create $$name sql; \
cd backend && goose -dir migrations/mysql create $$name sql
# ============================================ # ============================================
# MySQL 专项测试 # Desktop 模式
# ============================================ # ============================================
test-mysql-up: desktop-build-mac: version-check _desktop-prepare-frontend _desktop-prepare-embedfs
@echo "Starting MySQL test container..." @printf 'Building macOS desktop...\n'
cd backend/tests/mysql && docker-compose up -d cd backend && CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-mac-arm64 ./cmd/desktop
@echo "Waiting for MySQL to be ready..." cd backend && CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-mac-amd64 ./cmd/desktop
@for i in $$(seq 1 30); do \ lipo -create build/nex-mac-arm64 build/nex-mac-amd64 -output build/nex-mac-universal
if docker exec nex-mysql-test mysqladmin ping -h localhost -u root -ptestpass --silent 2>/dev/null; then \ @printf 'Packaging macOS app bundle...\n'
echo "MySQL is ready!"; \ mkdir -p build/Nex.app/Contents/MacOS build/Nex.app/Contents/Resources
exit 0; \ cp build/nex-mac-universal build/Nex.app/Contents/MacOS/nex
@if [ -f assets/icon.icns ]; then \
cp assets/icon.icns build/Nex.app/Contents/Resources/; \
else \
printf 'Missing assets/icon.icns\n'; \
fi
@MIN_MACOS_VERSION=$$(vtool -show-build build/nex-mac-universal | awk '/minos / {print $$2; exit}'); \
if [ -z "$$MIN_MACOS_VERSION" ]; then \
printf 'Unable to read macOS minimum version\n'; \
exit 1; \
fi; \ fi; \
echo "Waiting... ($$i/30)"; \ go run ./backend/cmd/versionctl macos-plist "$$MIN_MACOS_VERSION" > build/Nex.app/Contents/Info.plist
sleep 1; \ chmod +x build/Nex.app/Contents/MacOS/nex
done; \ @printf 'macOS desktop build complete\n'
echo "MySQL failed to start"; \
exit 1
test-mysql-down: desktop-build-win: version-check _desktop-prepare-frontend _desktop-prepare-embedfs _desktop-prepare-windows-resource
@echo "Stopping MySQL test container..." @printf 'Building Windows desktop...\n'
cd backend/tests/mysql && docker-compose down -v ifeq ($(OS),Windows_NT)
powershell -NoProfile -Command "New-Item -ItemType Directory -Path 'build' -Force | Out-Null"
cd backend && set "CGO_ENABLED=1"&& set "GOOS=windows"&& set "GOARCH=amd64"&& go build -ldflags "$(GO_LDFLAGS_WIN)" -o ../build/nex-win-amd64.exe ./cmd/desktop
else
mkdir -p build
cd backend && CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS_WIN)" -o ../build/nex-win-amd64.exe ./cmd/desktop
endif
@printf 'Windows desktop build complete\n'
test-mysql: test-mysql-up desktop-build-linux: version-check _desktop-prepare-frontend _desktop-prepare-embedfs
@echo "Running MySQL tests..." @printf 'Building Linux desktop...\n'
cd backend && go test -tags=mysql ./tests/mysql/... -v -count=1 cd backend && CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-linux-amd64 ./cmd/desktop
$(MAKE) test-mysql-down @printf 'Linux desktop build complete\n'
test-mysql-quick: desktop-lint: _backend-lint _frontend-check
@echo "Running MySQL tests (without container management)..." @printf 'Desktop lint complete\n'
cd backend && go test -tags=mysql ./tests/mysql/... -v -count=1
# ============================================ desktop-test: _desktop-test
# 前端 @printf 'Desktop tests passed\n'
# ============================================
frontend-install: desktop-clean: _desktop-clean
cd frontend && bun install @printf 'Desktop artifacts cleaned\n'
frontend-build: frontend-install version-sync _desktop-test:
cd frontend && bun run build cd backend && go test ./cmd/desktop/... -v
frontend-dev: frontend-install version-sync _desktop-clean:
cd frontend && bun dev rm -rf build/ embedfs/assets embedfs/frontend-dist backend/cmd/desktop/rsrc_windows_amd64.syso
frontend-test: frontend-install _desktop-prepare-frontend: _frontend-install
cd frontend && bun run test @printf 'Preparing frontend for desktop...\n'
frontend-test-watch: frontend-install
cd frontend && bun run test:watch
frontend-test-coverage: frontend-install
cd frontend && bun run test:coverage
frontend-test-e2e: frontend-install
cd frontend && bun run test:e2e
frontend-lint: frontend-install
cd frontend && bun run lint
frontend-clean:
rm -rf frontend/dist frontend/.next frontend/node_modules frontend/coverage frontend/playwright-report frontend/test-results frontend/tsconfig.tsbuildinfo
# ============================================
# 桌面应用
# ============================================
desktop-build: desktop-build-mac desktop-build-win desktop-build-linux
@echo "✅ Desktop builds complete for all platforms"
desktop-prepare-frontend: frontend-install version-sync
@echo "📦 Preparing frontend for desktop..."
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
powershell -NoProfile -Command "Copy-Item -LiteralPath 'frontend/.env.desktop' -Destination 'frontend/.env.production.local' -Force" powershell -NoProfile -Command "Copy-Item -LiteralPath 'frontend/.env.desktop' -Destination 'frontend/.env.production.local' -Force"
cd frontend && bun run build cd frontend && bun run build
@@ -194,8 +141,8 @@ else
rm -f frontend/.env.production.local rm -f frontend/.env.production.local
endif endif
desktop-prepare-embedfs: _desktop-prepare-embedfs:
@echo "📦 Preparing embedded filesystem..." @printf 'Preparing embedded filesystem...\n'
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
powershell -NoProfile -Command "Remove-Item -LiteralPath 'embedfs/assets' -Recurse -Force -ErrorAction SilentlyContinue; Remove-Item -LiteralPath 'embedfs/frontend-dist' -Recurse -Force -ErrorAction SilentlyContinue; Copy-Item -LiteralPath 'assets' -Destination 'embedfs/assets' -Recurse; Copy-Item -LiteralPath 'frontend/dist' -Destination 'embedfs/frontend-dist' -Recurse" powershell -NoProfile -Command "Remove-Item -LiteralPath 'embedfs/assets' -Recurse -Force -ErrorAction SilentlyContinue; Remove-Item -LiteralPath 'embedfs/frontend-dist' -Recurse -Force -ErrorAction SilentlyContinue; Copy-Item -LiteralPath 'assets' -Destination 'embedfs/assets' -Recurse; Copy-Item -LiteralPath 'frontend/dist' -Destination 'embedfs/frontend-dist' -Recurse"
else else
@@ -204,8 +151,8 @@ else
cp -r frontend/dist embedfs/frontend-dist cp -r frontend/dist embedfs/frontend-dist
endif endif
desktop-prepare-windows-resource: _desktop-prepare-windows-resource:
@echo "📦 Preparing Windows executable icon..." @printf 'Preparing Windows executable icon...\n'
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
cd backend/cmd/desktop && windres -O coff -F pe-x86-64 -i icon_windows.rc -o rsrc_windows_amd64.syso cd backend/cmd/desktop && windres -O coff -F pe-x86-64 -i icon_windows.rc -o rsrc_windows_amd64.syso
else else
@@ -214,74 +161,34 @@ else
elif command -v windres >/dev/null 2>&1; then \ elif command -v windres >/dev/null 2>&1; then \
cd backend/cmd/desktop && windres -O coff -F pe-x86-64 -i icon_windows.rc -o rsrc_windows_amd64.syso; \ cd backend/cmd/desktop && windres -O coff -F pe-x86-64 -i icon_windows.rc -o rsrc_windows_amd64.syso; \
else \ else \
echo "❌ 未找到 windres,无法生成 Windows exe 图标资源"; \ printf 'Missing windres for Windows icon resource generation\n'; \
exit 1; \ exit 1; \
fi fi
endif endif
desktop-build-mac: desktop-prepare-frontend desktop-prepare-embedfs
@echo "🍎 Building macOS..."
cd backend && CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-mac-arm64 ./cmd/desktop
cd backend && CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-mac-amd64 ./cmd/desktop
lipo -create build/nex-mac-arm64 build/nex-mac-amd64 -output build/nex-mac-universal
@echo "📦 Packaging macOS .app..."
mkdir -p build/Nex.app/Contents/MacOS build/Nex.app/Contents/Resources
cp build/nex-mac-universal build/Nex.app/Contents/MacOS/nex
@if [ -f assets/icon.icns ]; then \
cp assets/icon.icns build/Nex.app/Contents/Resources/; \
else \
echo "⚠️ 未找到 assets/icon.icns"; \
fi
@MIN_MACOS_VERSION=$$(vtool -show-build build/nex-mac-universal | awk '/minos / {print $$2; exit}'); \
if [ -z "$$MIN_MACOS_VERSION" ]; then \
echo "❌ 无法读取 macOS 最低系统版本"; \
exit 1; \
fi; \
go run ./backend/cmd/versionctl macos-plist "$$MIN_MACOS_VERSION" > build/Nex.app/Contents/Info.plist
chmod +x build/Nex.app/Contents/MacOS/nex
@echo "✅ macOS app packaged: build/Nex.app"
desktop-build-win: desktop-prepare-frontend desktop-prepare-embedfs desktop-prepare-windows-resource
@echo "🪟 Building Windows..."
ifeq ($(OS),Windows_NT)
powershell -NoProfile -Command "New-Item -ItemType Directory -Path 'build' -Force | Out-Null"
cd backend && set "CGO_ENABLED=1"&& set "GOOS=windows"&& set "GOARCH=amd64"&& go build -ldflags "$(GO_LDFLAGS_WIN)" -o ../build/nex-win-amd64.exe ./cmd/desktop
else
mkdir -p build
cd backend && CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS_WIN)" -o ../build/nex-win-amd64.exe ./cmd/desktop
endif
desktop-build-linux: desktop-prepare-frontend desktop-prepare-embedfs
@echo "🐧 Building Linux..."
cd backend && CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-linux-amd64 ./cmd/desktop
desktop-dev: desktop-prepare-frontend desktop-prepare-embedfs
@echo "🖥️ Starting desktop app in dev mode..."
cd backend && go run -ldflags "$(GO_LDFLAGS)" ./cmd/desktop
# ============================================ # ============================================
# 发布资产 # 发布资产
# ============================================ # ============================================
release-assets-linux: version-sync version-check desktop-build-linux release-assets-linux: version-check desktop-build-linux
rm -rf "$(RELEASE_DIR)" rm -rf "$(RELEASE_DIR)"
mkdir -p "$(RELEASE_DIR)" mkdir -p "$(RELEASE_DIR)"
cd backend && CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-server-linux-amd64 ./cmd/server cd backend && CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-server-linux-amd64 ./cmd/server
tar -C build -czf "$(RELEASE_DIR)/$(SERVER_LINUX_ASSET)" nex-server-linux-amd64 tar -C build -czf "$(RELEASE_DIR)/$(SERVER_LINUX_ASSET)" nex-server-linux-amd64
tar -C build -czf "$(RELEASE_DIR)/$(DESKTOP_LINUX_ASSET)" nex-linux-amd64 tar -C build -czf "$(RELEASE_DIR)/$(DESKTOP_LINUX_ASSET)" nex-linux-amd64
release-assets-windows: version-sync version-check desktop-build-win release-assets-windows: version-check desktop-build-win
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
powershell -NoProfile -Command "Remove-Item -LiteralPath '$(RELEASE_DIR)' -Recurse -Force -ErrorAction SilentlyContinue; New-Item -ItemType Directory -Path '$(RELEASE_DIR)' -Force | Out-Null" powershell -NoProfile -Command "Remove-Item -LiteralPath '$(RELEASE_DIR)' -Recurse -Force -ErrorAction SilentlyContinue; New-Item -ItemType Directory -Path '$(RELEASE_DIR)' -Force | Out-Null"
cd backend && set "CGO_ENABLED=1"&& set "GOOS=windows"&& set "GOARCH=amd64"&& go build -ldflags "$(GO_LDFLAGS_WIN)" -o ../build/nex-server-win-amd64.exe ./cmd/server cd backend && set "CGO_ENABLED=1"&& set "GOOS=windows"&& set "GOARCH=amd64"&& go build -ldflags "$(GO_LDFLAGS_WIN)" -o ../build/nex-server-win-amd64.exe ./cmd/server
powershell -NoProfile -Command "Compress-Archive -LiteralPath 'build/nex-server-win-amd64.exe' -DestinationPath '$(RELEASE_DIR)/$(SERVER_WINDOWS_ASSET)' -Force" powershell -NoProfile -Command "Compress-Archive -LiteralPath 'build/nex-server-win-amd64.exe' -DestinationPath '$(RELEASE_DIR)/$(SERVER_WINDOWS_ASSET)' -Force"
powershell -NoProfile -Command "Compress-Archive -LiteralPath 'build/nex-win-amd64.exe' -DestinationPath '$(RELEASE_DIR)/$(DESKTOP_WINDOWS_ASSET)' -Force" powershell -NoProfile -Command "Compress-Archive -LiteralPath 'build/nex-win-amd64.exe' -DestinationPath '$(RELEASE_DIR)/$(DESKTOP_WINDOWS_ASSET)' -Force"
else else
@echo "❌ release-assets-windows 需要在 Windows 环境执行" @printf 'release-assets-windows requires Windows\n'
@exit 1 @exit 1
endif endif
release-assets-macos: version-sync version-check desktop-build-mac release-assets-macos: version-check desktop-build-mac
rm -rf "$(RELEASE_DIR)" rm -rf "$(RELEASE_DIR)"
mkdir -p "$(RELEASE_DIR)" mkdir -p "$(RELEASE_DIR)"
cd backend && CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-server-darwin-amd64 ./cmd/server cd backend && CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-server-darwin-amd64 ./cmd/server
@@ -290,15 +197,36 @@ release-assets-macos: version-sync version-check desktop-build-mac
tar -C build -czf "$(RELEASE_DIR)/$(SERVER_DARWIN_ARM64_ASSET)" nex-server-darwin-arm64 tar -C build -czf "$(RELEASE_DIR)/$(SERVER_DARWIN_ARM64_ASSET)" nex-server-darwin-arm64
ditto -c -k --keepParent build/Nex.app "$(RELEASE_DIR)/$(DESKTOP_MACOS_ASSET)" ditto -c -k --keepParent build/Nex.app "$(RELEASE_DIR)/$(DESKTOP_MACOS_ASSET)"
desktop-test:
cd backend && go test ./cmd/desktop/... -v
desktop-clean:
rm -rf build/ embedfs/assets embedfs/frontend-dist
# ============================================ # ============================================
# 清理 # 共享 helper targets
# ============================================ # ============================================
clean: backend-clean frontend-clean desktop-clean _backend-build:
@echo "✅ Clean complete" @$(MAKE) -C backend build
_backend-lint:
@$(MAKE) -C backend lint
_backend-test:
@$(MAKE) -C backend test
_backend-clean:
@$(MAKE) -C backend clean
_frontend-install:
cd frontend && bun install
_frontend-build: _frontend-install
cd frontend && bun run build
_frontend-check: _frontend-install
cd frontend && bun run check
_frontend-test: _frontend-install
cd frontend && bun run test
_frontend-dev: _frontend-install
cd frontend && bun run dev
_frontend-clean:
rm -rf frontend/dist frontend/.next frontend/coverage frontend/playwright-report frontend/test-results frontend/tsconfig.tsbuildinfo

View File

@@ -109,9 +109,6 @@ make desktop-build-win
# Linux # Linux
make desktop-build-linux make desktop-build-linux
# 构建所有平台
make desktop-build
``` ```
**使用桌面应用** **使用桌面应用**
@@ -132,32 +129,28 @@ make desktop-build
- Xfce: 需要 libappindicator - Xfce: 需要 libappindicator
- 其他支持 StatusNotifierItem 规范的环境 - 其他支持 StatusNotifierItem 规范的环境
### CLI 模式 ### Server 模式(前后端分离)
#### 后端
```bash ```bash
cd backend make server-run
go mod download
go run cmd/server/main.go
``` ```
后端服务将在 `http://localhost:9826` 启动。首次启动会自动: `make server-run` 会并行启动:
- 后端服务:`http://localhost:9826`
- 前端开发服务器:`http://localhost:5173`
前端请求会继续通过 Vite proxy 转发到后端。后端首次启动会自动:
- 创建配置文件 `~/.nex/config.yaml` - 创建配置文件 `~/.nex/config.yaml`
- 初始化数据库 `~/.nex/config.db` - 初始化数据库 `~/.nex/config.db`
- 运行数据库迁移 - 运行数据库迁移
- 创建日志目录 `~/.nex/log/` - 创建日志目录 `~/.nex/log/`
### 前端 **构建 server 模式产物**
```bash ```bash
cd frontend make server-build
bun install
bun dev
``` ```
前端开发服务器将在 `http://localhost:5173` 启动API 请求通过 Vite proxy 转发到后端。
## API 接口 ## API 接口
### 代理接口(对外部应用) ### 代理接口(对外部应用)
@@ -279,51 +272,41 @@ export NEX_DATABASE_DBNAME=nex
## 测试 ## 测试
```bash ```bash
# 顶层便捷命令 # 全局默认测试(不含 MySQL 和前端 E2E
make test # 运行所有测试 make test
# 后端测试 # 产品级测试
make backend-test # 后端测试 make server-test
make backend-test-coverage # 后端覆盖率 make desktop-test
make backend-test-unit # 后端单元测试
make backend-test-integration # 后端集成测试
# 前端测试
make frontend-test # 前端测试
make frontend-test-e2e # 前端 E2E 测试
make frontend-test-coverage # 前端覆盖率
``` ```
backend 分类测试、MySQL 专项测试和前端 E2E 测试请分别查看 `backend/README.md``frontend/README.md`
## 开发 ## 开发
```bash ```bash
# 首次克隆后安装 Git hooks # 首次克隆后安装 Git hooks
lefthook install lefthook install
# 顶层便捷命令 # 全局命令
make dev # 启动开发环境(并行启动后端和前端) make lint # 前后端共享检查
make build # 构建所有产物 make test # 默认全量测试(不含 MySQL/E2E
make lint # 检查所有代码 make clean # 清理所有构建产物和测试报告
make clean # 清理所有构建产物
# 后端开发 # server 模式
make backend-build # 构建后端 make server-run # 并行启动后端和前端开发服务
make backend-run # 运行后端 make server-build # 构建 backend/bin/server 和 frontend/dist
make backend-dev # 后端开发模式 make server-lint # server 模式检查
make backend-lint # 后端代码检查 make server-test # server 模式测试
make backend-clean # 清理后端构建产物 make server-clean # 清理 server 模式产物
# 数据库操作 # desktop 模式
make backend-db-up # 数据库迁移 make desktop-build-mac # 构建 macOS 桌面应用
make backend-db-down # 数据库回滚 make desktop-build-win # 构建 Windows 桌面应用
make backend-db-status # 数据库迁移状态 make desktop-build-linux # 构建 Linux 桌面应用
make backend-db-create # 创建新迁移 make desktop-lint # desktop 模式检查
make desktop-test # desktop 专属测试
# 前端开发 make desktop-clean # 清理 desktop 产物
make frontend-build # 构建前端
make frontend-dev # 前端开发模式
make frontend-lint # 前端代码检查
make frontend-clean # 清理前端构建产物
``` ```
## 版本与发布 ## 版本与发布
@@ -339,13 +322,13 @@ make frontend-clean # 清理前端构建产物
2. 同步镜像文件: 2. 同步镜像文件:
```bash ```bash
go run ./backend/cmd/versionctl sync make version-sync
``` ```
3. 校验版本一致性: 3. 校验版本一致性:
```bash ```bash
go run ./backend/cmd/versionctl check make version-check
``` ```
4. 提交版本变更后,创建发布 tag 4. 提交版本变更后,创建发布 tag

97
backend/Makefile Normal file
View File

@@ -0,0 +1,97 @@
.PHONY: \
build run \
test test-unit test-integration test-coverage \
lint clean \
migrate-up migrate-down migrate-status migrate-create \
mysql-up mysql-down mysql-test mysql-test-quick
VERSION := $(shell go run ./cmd/versionctl print)
GIT_COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || printf 'unknown')
BUILD_TIME ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
GO_LDFLAGS := -X nex/backend/pkg/buildinfo.version=$(VERSION) -X nex/backend/pkg/buildinfo.commit=$(GIT_COMMIT) -X nex/backend/pkg/buildinfo.buildTime=$(BUILD_TIME)
DB_DRIVER ?= sqlite3
DB_DSN ?= $(HOME)/.nex/config.db
ifeq ($(DB_DRIVER),mysql)
GOOSE_DIR := migrations/mysql
GOOSE_DRIVER := mysql
else ifeq ($(DB_DRIVER),sqlite3)
GOOSE_DIR := migrations/sqlite
GOOSE_DRIVER := sqlite3
else
$(error unsupported DB_DRIVER '$(DB_DRIVER)', use sqlite3 or mysql)
endif
build:
go build -ldflags "$(GO_LDFLAGS)" -o bin/server ./cmd/server
run:
go run -ldflags "$(GO_LDFLAGS)" ./cmd/server
test:
go test ./internal/... ./pkg/... ./tests/... ./cmd/server/... -v
test-unit:
go test ./internal/... ./pkg/... -v
test-integration:
go test ./tests/... -v
test-coverage:
go test ./... -coverprofile=coverage.out
go tool cover -html=coverage.out -o coverage.html
@printf 'Coverage report generated: backend/coverage.html\n'
lint:
go tool golangci-lint run ./...
clean:
rm -rf bin/ coverage.out coverage.html
migrate-up:
@printf 'Running database migration up...\n'
goose -dir $(GOOSE_DIR) $(GOOSE_DRIVER) "$(DB_DSN)" up
migrate-down:
@printf 'Running database migration down...\n'
goose -dir $(GOOSE_DIR) $(GOOSE_DRIVER) "$(DB_DSN)" down
migrate-status:
@printf 'Checking database migration status...\n'
goose -dir $(GOOSE_DIR) $(GOOSE_DRIVER) "$(DB_DSN)" status
migrate-create:
@printf 'Migration name: '; \
read name; \
goose -dir migrations/sqlite create $$name sql; \
goose -dir migrations/mysql create $$name sql
mysql-up:
@printf 'Starting MySQL test container...\n'
cd tests/mysql && docker-compose up -d
@printf 'Waiting for MySQL to be ready...\n'
@for i in $$(seq 1 30); do \
if docker exec nex-mysql-test mysqladmin ping -h localhost -u root -ptestpass --silent 2>/dev/null; then \
printf 'MySQL is ready\n'; \
exit 0; \
fi; \
printf 'Waiting... (%s/30)\n' $$i; \
sleep 1; \
done; \
printf 'MySQL failed to start\n'; \
exit 1
mysql-down:
@printf 'Stopping MySQL test container...\n'
cd tests/mysql && docker-compose down -v
mysql-test:
@set -e; \
$(MAKE) mysql-up; \
trap '$(MAKE) mysql-down' EXIT; \
go test -tags=mysql ./tests/mysql/... -v -count=1
mysql-test-quick:
@printf 'Running MySQL tests without container management...\n'
go test -tags=mysql ./tests/mysql/... -v -count=1

View File

@@ -437,24 +437,37 @@ docker run -d -e NEX_SERVER_PORT=9000 -e NEX_LOG_LEVEL=info nex-server
## 测试 ## 测试
```bash ```bash
# 运行所有测试 # 运行 backend 默认测试
make test make test
# 分类测试
make test-unit
make test-integration
# 生成覆盖率报告 # 生成覆盖率报告
make test-coverage make test-coverage
# MySQL 专项测试
make mysql-up
make mysql-down
make mysql-test
make mysql-test-quick
``` ```
## 数据库迁移 ## 数据库迁移
```bash ```bash
# 使用 Makefile # 使用 Makefile
make migrate-up DB_PATH=~/.nex/config.db make migrate-up DB_DSN=~/.nex/config.db
make migrate-down DB_PATH=~/.nex/config.db make migrate-down DB_DSN=~/.nex/config.db
make migrate-status DB_PATH=~/.nex/config.db make migrate-status DB_DSN=~/.nex/config.db
# 创建新迁移 # 创建新迁移
make migrate-create make migrate-create
# MySQL 迁移
make migrate-up DB_DRIVER=mysql DB_DSN='user:pass@tcp(localhost:3306)/nex'
# 或直接使用 goose # 或直接使用 goose
goose -dir migrations sqlite3 ~/.nex/config.db up goose -dir migrations sqlite3 ~/.nex/config.db up
``` ```
@@ -571,9 +584,12 @@ GET /anthropic/v1/models
## 开发 ## 开发
```bash ```bash
make build # 构建 make build # 构建 backend/bin/server
make run # 运行后端服务
make lint # 代码检查 make lint # 代码检查
make deps # 整理依赖 make clean # 清理 backend 构建产物
go mod tidy # 整理依赖
go generate ./... # 刷新 mock 等生成代码
``` ```
环境要求Go 1.26 或更高版本 环境要求Go 1.26 或更高版本

View File

@@ -67,11 +67,11 @@
### Requirement: 迁移命令集成 ### Requirement: 迁移命令集成
迁移 SHALL 集成到 Makefile 迁移 SHALL 集成到 `backend/Makefile`,而不是根目录 `Makefile`
#### Scenario: 迁移 up 命令 #### Scenario: 迁移 up 命令
- **WHEN** 执行 `make backend-migrate-up` - **WHEN** `backend/` 目录执行 `make migrate-up`
- **THEN** SHALL 执行所有待执行的迁移 - **THEN** SHALL 执行所有待执行的迁移
- **THEN** SHALL 使用 `DB_DRIVER` 变量选择方言目录(默认 `sqlite3` - **THEN** SHALL 使用 `DB_DRIVER` 变量选择方言目录(默认 `sqlite3`
- **THEN** SHALL 使用 `DB_DSN` 变量作为数据库连接串 - **THEN** SHALL 使用 `DB_DSN` 变量作为数据库连接串
@@ -79,20 +79,20 @@
#### Scenario: 迁移 down 命令 #### Scenario: 迁移 down 命令
- **WHEN** 执行 `make backend-migrate-down` - **WHEN** `backend/` 目录执行 `make migrate-down`
- **THEN** SHALL 回滚最后一个迁移 - **THEN** SHALL 回滚最后一个迁移
- **THEN** SHALL 使用 `DB_DRIVER``DB_DSN` 变量 - **THEN** SHALL 使用 `DB_DRIVER``DB_DSN` 变量
- **THEN** SHALL 显示回滚进度 - **THEN** SHALL 显示回滚进度
#### Scenario: 迁移状态命令 #### Scenario: 迁移状态命令
- **WHEN** 执行 `make backend-migrate-status` - **WHEN** `backend/` 目录执行 `make migrate-status`
- **THEN** SHALL 显示当前迁移状态 - **THEN** SHALL 显示当前迁移状态
- **THEN** SHALL 显示已执行和待执行的迁移 - **THEN** SHALL 显示已执行和待执行的迁移
#### Scenario: 创建迁移命令 #### Scenario: 创建迁移命令
- **WHEN** 执行 `make backend-migrate-create` - **WHEN** `backend/` 目录执行 `make migrate-create`
- **THEN** SHALL 同时在 `migrations/sqlite/``migrations/mysql/` 两个目录创建新的迁移文件模板 - **THEN** SHALL 同时在 `migrations/sqlite/``migrations/mysql/` 两个目录创建新的迁移文件模板
- **THEN** SHALL 使用递增的版本号 - **THEN** SHALL 使用递增的版本号

View File

@@ -8,16 +8,16 @@
### Requirement: MySQL 测试环境可启动 ### Requirement: MySQL 测试环境可启动
系统 SHALL 提供 Docker Compose 配置以启动 MySQL 8.0 测试环境。 系统 SHALL 提供 Docker Compose 配置和 backend 局部 make 命令以启动 MySQL 8.0 测试环境。
#### Scenario: 启动 MySQL 测试容器 #### Scenario: 启动 MySQL 测试容器
- **WHEN** 执行 `make test-mysql-up` - **WHEN** `backend/` 目录执行 `make mysql-up`
- **THEN** 启动 MySQL 8.0 容器,端口 13306 - **THEN** 启动 MySQL 8.0 容器,端口 13306
- **AND** 创建数据库 `nex_test` - **AND** 创建数据库 `nex_test`
- **AND** 容器数据存储在内存盘tmpfs - **AND** 容器数据存储在内存盘tmpfs
#### Scenario: 销毁 MySQL 测试容器 #### Scenario: 销毁 MySQL 测试容器
- **WHEN** 执行 `make test-mysql-down` - **WHEN** `backend/` 目录执行 `make mysql-down`
- **THEN** 停止并删除容器 - **THEN** 停止并删除容器
- **AND** 所有数据被销毁 - **AND** 所有数据被销毁
@@ -90,15 +90,15 @@ MySQL 测试 SHALL 验证并发写入不丢失数据。
### Requirement: MySQL 测试命令完整 ### Requirement: MySQL 测试命令完整
Makefile SHALL 提供完整的 MySQL 测试命令。 `backend/Makefile` SHALL 提供完整的 MySQL 测试命令。
#### Scenario: 完整测试流程 #### Scenario: 完整测试流程
- **WHEN** 执行 `make test-mysql` - **WHEN** `backend/` 目录执行 `make mysql-test`
- **THEN** 启动 Docker MySQL - **THEN** 启动 Docker MySQL
- **AND** 等待 MySQL 就绪 - **AND** 等待 MySQL 就绪
- **AND** 运行所有 MySQL 测试 - **AND** 运行所有 MySQL 测试
- **AND** 销毁容器 - **AND** 销毁容器
#### Scenario: 快速测试(容器已运行) #### Scenario: 快速测试(容器已运行)
- **WHEN** 执行 `make test-mysql-quick` - **WHEN** `backend/` 目录执行 `make mysql-test-quick`
- **THEN** 直接运行测试,不管理容器生命周期 - **THEN** 直接运行测试,不管理容器生命周期

View File

@@ -157,26 +157,33 @@
### Requirement: 集成到构建流程 ### Requirement: 集成到构建流程
测试 SHALL 集成到构建流程中。 测试 SHALL 同时集成到根目录公共流程和 backend 局部流程中。
#### Scenario: 运行测试命令 #### Scenario: 运行测试命令
- **WHEN** 执行 `make test` 命令 - **WHEN** 执行 `make test` 命令
- **THEN** SHALL 运行所有单元测试和集成测试 - **THEN** SHALL 运行 backend 核心测试、frontend 的 Vitest 单元/组件测试和 desktop 专属测试
- **THEN** SHALL NOT 运行 MySQL 专项测试和 frontend E2E 测试
- **THEN** SHALL 显示测试结果 - **THEN** SHALL 显示测试结果
- **THEN** SHALL 在测试失败时返回非零退出码 - **THEN** SHALL 在测试失败时返回非零退出码
#### Scenario: 运行 backend 局部测试命令
- **WHEN** 在 `backend/` 目录执行 `make test` 命令
- **THEN** SHALL 运行 backend 核心测试
- **THEN** SHALL NOT 运行 frontend 的 Vitest 单元/组件测试、desktop 专属测试、MySQL 专项测试或 frontend E2E 测试
#### Scenario: 分类测试命令 #### Scenario: 分类测试命令
- **WHEN** 执行 `make test-unit` 命令 - **WHEN** `backend/` 目录执行 `make test-unit` 命令
- **THEN** SHALL 仅运行 `./internal/...``./pkg/...` 下的单元测试 - **THEN** SHALL 仅运行 `./internal/...``./pkg/...` 下的单元测试
- **WHEN** 执行 `make test-integration` 命令 - **WHEN** `backend/` 目录执行 `make test-integration` 命令
- **THEN** SHALL 仅运行 `./tests/...` 下的集成测试 - **THEN** SHALL 仅运行 `./tests/...` 下的集成测试
#### Scenario: 覆盖率检查命令 #### Scenario: 覆盖率检查命令
- **WHEN** 执行 `make test-coverage` 命令 - **WHEN** `backend/` 目录执行 `make test-coverage` 命令
- **THEN** SHALL 运行测试并生成覆盖率报告 - **THEN** SHALL 运行测试并生成覆盖率报告
- **THEN** SHALL 检查覆盖率是否达标 - **THEN** SHALL 检查覆盖率是否达标
- **THEN** SHALL 在覆盖率不足时返回非零退出码 - **THEN** SHALL 在覆盖率不足时返回非零退出码

View File

@@ -0,0 +1,114 @@
# Workspace Command Flows
## Purpose
定义根目录 `Makefile``backend/Makefile` 的公开命令边界,明确全仓命令、产品级命令和 backend 局部维护命令的职责分层。
## Requirements
### Requirement: 根目录公开命令分层
根目录 `Makefile` SHALL 仅暴露全局命令、版本命令、server 产品命令、desktop 产品命令和 release 命令,不再作为 backend 局部维护命令或内部打包步骤的公开入口。
#### Scenario: 查看根目录公开命令
- **WHEN** 开发者查看根目录 `Makefile` 的公开 target
- **THEN** SHALL 仅看到 `lint``test``clean``version-sync``version-check``server-run``server-build``server-lint``server-test``server-clean``desktop-build-mac``desktop-build-win``desktop-build-linux``desktop-lint``desktop-test``desktop-clean``release-assets-linux``release-assets-windows``release-assets-macos` 这类公共入口
#### Scenario: 根目录不暴露局部和内部命令
- **WHEN** 开发者查看根目录 `Makefile` 的公开 target
- **THEN** SHALL NOT 暴露 `backend-*``frontend-*`、数据库迁移命令、MySQL 专项测试命令或 `desktop-prepare-*` 之类内部步骤
- **THEN** SHALL NOT 暴露 `dev``build``all``desktop-dev``desktop-build` 这类模糊或聚合式公共命令
### Requirement: 全局质量与清理命令
根目录 `Makefile` SHALL 提供 `lint``test``clean` 作为全仓默认入口。
#### Scenario: 执行全局 lint
- **WHEN** 执行 `make lint`
- **THEN** SHALL 运行 backend 的 Go lint 检查和 frontend 的 lint / format check
- **THEN** SHALL 在任一检查失败时返回非零退出码
#### Scenario: 执行全局 test
- **WHEN** 执行 `make test`
- **THEN** SHALL 运行 backend 核心测试、frontend 的 Vitest 单元/组件测试和 desktop 专属测试
- **THEN** SHALL NOT 运行 MySQL 专项测试和 frontend E2E 测试
- **THEN** SHALL 在任一测试失败时返回非零退出码
#### Scenario: 执行全局 clean
- **WHEN** 执行 `make clean`
- **THEN** SHALL 清理 server 与 desktop 相关的构建产物、发布产物和测试报告
- **THEN** SHALL NOT 删除 `frontend/node_modules`、Go module cache 或 bun cache 之类依赖目录和全局缓存
### Requirement: Server 产品命令
根目录 `Makefile` SHALL 提供面向前后端分离 server 模式的产品级命令。
#### Scenario: 启动 server 模式联调环境
- **WHEN** 执行 `make server-run`
- **THEN** SHALL 并行启动 Go 后端服务和前端 Vite 开发服务器
- **THEN** SHALL 使前端继续通过现有代理访问本地 backend 服务
#### Scenario: 构建 server 模式产物
- **WHEN** 执行 `make server-build`
- **THEN** SHALL 生成 `backend/bin/server`
- **AND** SHALL 生成 `frontend/dist`
- **AND** SHALL 在构建前校验版本一致性
- **AND** SHALL NOT 隐式执行 `version-sync` 或修改版本镜像文件
#### Scenario: 执行 server lint
- **WHEN** 执行 `make server-lint`
- **THEN** SHALL 运行 backend 的 Go lint 检查和 frontend 的 lint / format check
#### Scenario: 执行 server test
- **WHEN** 执行 `make server-test`
- **THEN** SHALL 运行 backend 核心测试和 frontend 的 Vitest 单元/组件测试
- **THEN** SHALL NOT 运行 MySQL 专项测试、frontend E2E 测试或 desktop 专属测试
#### Scenario: 执行 server clean
- **WHEN** 执行 `make server-clean`
- **THEN** SHALL 清理 `backend/bin/server``frontend/dist` 以及 server 模式相关测试报告与临时产物
### Requirement: Desktop 产品命令
根目录 `Makefile` SHALL 提供按平台拆分的 desktop 构建命令,以及 desktop 产品级 `lint``test``clean` 命令。
#### Scenario: 执行按平台拆分的 desktop 构建
- **WHEN** 执行 `make desktop-build-mac``make desktop-build-win``make desktop-build-linux`
- **THEN** SHALL 分别构建对应平台的 desktop 产物
- **THEN** SHALL 在构建前执行版本一致性校验
- **THEN** SHALL NOT 隐式执行 `version-sync` 或修改版本镜像文件
- **THEN** SHALL NOT 要求通过额外的 `desktop-build` 聚合命令触发构建
#### Scenario: 根目录不提供 desktop 开发聚合入口
- **WHEN** 开发者查看根目录 `Makefile`
- **THEN** SHALL NOT 提供 `desktop-dev` 作为公共命令
#### Scenario: 执行 desktop lint
- **WHEN** 执行 `make desktop-lint`
- **THEN** SHALL 运行 backend 的 Go lint 检查和 frontend 的 lint / format check
#### Scenario: 执行 desktop test
- **WHEN** 执行 `make desktop-test`
- **THEN** SHALL 运行 desktop 专属测试
#### Scenario: 执行 desktop clean
- **WHEN** 执行 `make desktop-clean`
- **THEN** SHALL 清理 desktop 构建目录、嵌入资源目录和 desktop 相关测试/打包产物
### Requirement: Release 命令沿用根目录入口
根目录 `Makefile` SHALL 继续提供 `release-assets-*` 作为发布资产入口,并与新的版本校验规则保持一致。
#### Scenario: 执行 release 资产命令
- **WHEN** 执行 `make release-assets-linux``make release-assets-windows``make release-assets-macos`
- **THEN** SHALL 在构建发布资产前执行版本一致性校验
- **THEN** SHALL NOT 隐式执行 `version-sync` 或修改版本镜像文件
### Requirement: Backend 局部命令下沉
数据库迁移、MySQL 专项测试以及其他 backend 局部维护命令 SHALL 由 `backend/Makefile` 提供,而不是由根目录 `Makefile` 公开。
#### Scenario: 执行 backend 局部维护命令
- **WHEN** 开发者需要执行数据库迁移或 MySQL 专项测试
- **THEN** SHALL 在 `backend/` 目录使用 backend 局部 make 命令完成操作
- **THEN** 根目录 `Makefile` SHALL NOT 提供等价的公共命令别名