From 2c401f7ae6825eb4081d54fc5e98eb3ab6c5947a Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Tue, 28 Apr 2026 17:44:23 +0800 Subject: [PATCH] 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. --- Makefile | 310 +++++++----------- README.md | 87 ++--- backend/Makefile | 97 ++++++ backend/README.md | 30 +- openspec/specs/database-migration/spec.md | 10 +- openspec/specs/mysql-testing/spec.md | 12 +- openspec/specs/test-coverage/spec.md | 17 +- .../specs/workspace-command-flows/spec.md | 114 +++++++ 8 files changed, 411 insertions(+), 266 deletions(-) create mode 100644 backend/Makefile create mode 100644 openspec/specs/workspace-command-flows/spec.md diff --git a/Makefile b/Makefile index acf5c27..cdc3b95 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ -.PHONY: all dev build test lint clean \ +.PHONY: \ + lint test clean \ version-sync version-check \ - backend-build backend-run backend-dev backend-test backend-test-all backend-test-unit backend-test-integration backend-test-coverage \ - backend-lint backend-clean backend-deps backend-generate \ - backend-db-up backend-db-down backend-db-status backend-db-create \ - test-mysql-up test-mysql-down test-mysql test-mysql-quick \ - frontend-build frontend-dev frontend-test frontend-test-watch frontend-test-coverage frontend-test-e2e frontend-lint frontend-clean \ - desktop-build desktop-build-mac desktop-build-win desktop-build-linux \ - desktop-dev desktop-test desktop-clean \ - desktop-prepare-frontend desktop-prepare-embedfs desktop-prepare-windows-resource \ - release-assets-linux release-assets-windows release-assets-macos + 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 \ + _backend-lint _backend-test _backend-clean _backend-build \ + _frontend-install _frontend-build _frontend-check _frontend-test _frontend-dev _frontend-clean \ + _desktop-test _desktop-clean _desktop-prepare-frontend _desktop-prepare-embedfs _desktop-prepare-windows-resource \ + _server-run-backend _server-run-frontend VERSION := $(shell go run ./backend/cmd/versionctl print) 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) # ============================================ -# 顶层便捷命令 +# 全局命令 # ============================================ -dev: - @echo "🚀 Starting development environment..." - @$(MAKE) -j2 backend-dev frontend-dev +lint: _backend-lint _frontend-check + @printf 'Lint complete\n' -build: backend-build frontend-build - @echo "✅ Build complete" +test: _backend-test _frontend-test _desktop-test + @printf 'All tests passed\n' -test: backend-test desktop-test frontend-test - @echo "✅ All tests passed" - -lint: backend-lint frontend-lint - @echo "✅ Lint complete" - -all: build test lint +clean: _backend-clean _frontend-clean _desktop-clean + @printf 'Clean complete\n' # ============================================ # 版本管理 @@ -54,136 +48,89 @@ version-check: go run ./backend/cmd/versionctl check # ============================================ -# 后端 +# Server 模式 # ============================================ -backend-build: version-check - cd backend && go build -ldflags "$(GO_LDFLAGS)" -o bin/server ./cmd/server +server-run: + @$(MAKE) -j2 _server-run-backend _server-run-frontend -backend-run: version-check - cd backend && go run -ldflags "$(GO_LDFLAGS)" ./cmd/server +server-build: version-check _backend-build _frontend-build + @printf 'Server build complete\n' -backend-dev: version-check - cd backend && go run -ldflags "$(GO_LDFLAGS)" ./cmd/server +server-lint: _backend-lint _frontend-check + @printf 'Server lint complete\n' -backend-test: - cd backend && go test ./internal/... ./pkg/... ./tests/... ./cmd/server/... -v +server-test: _backend-test _frontend-test + @printf 'Server tests passed\n' -backend-test-all: - cd backend && go test ./... -v +server-clean: _backend-clean _frontend-clean + @printf 'Server artifacts cleaned\n' -backend-test-unit: - cd backend && go test ./internal/... ./pkg/... -v +_server-run-backend: + @$(MAKE) -C backend run -backend-test-integration: - cd backend && go test ./tests/... -v - -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 +_server-run-frontend: _frontend-install + cd frontend && bun run dev # ============================================ -# MySQL 专项测试 +# Desktop 模式 # ============================================ -test-mysql-up: - @echo "Starting MySQL test container..." - cd backend/tests/mysql && docker-compose up -d - @echo "Waiting for MySQL to be ready..." - @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 \ - echo "MySQL is ready!"; \ - exit 0; \ - fi; \ - echo "Waiting... ($$i/30)"; \ - sleep 1; \ - done; \ - echo "MySQL failed to start"; \ - exit 1 +desktop-build-mac: version-check _desktop-prepare-frontend _desktop-prepare-embedfs + @printf 'Building macOS desktop...\n' + 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 + @printf 'Packaging macOS app bundle...\n' + 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 \ + 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; \ + go run ./backend/cmd/versionctl macos-plist "$$MIN_MACOS_VERSION" > build/Nex.app/Contents/Info.plist + chmod +x build/Nex.app/Contents/MacOS/nex + @printf 'macOS desktop build complete\n' -test-mysql-down: - @echo "Stopping MySQL test container..." - cd backend/tests/mysql && docker-compose down -v +desktop-build-win: version-check _desktop-prepare-frontend _desktop-prepare-embedfs _desktop-prepare-windows-resource + @printf 'Building Windows desktop...\n' +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 - @echo "Running MySQL tests..." - cd backend && go test -tags=mysql ./tests/mysql/... -v -count=1 - $(MAKE) test-mysql-down +desktop-build-linux: version-check _desktop-prepare-frontend _desktop-prepare-embedfs + @printf 'Building Linux desktop...\n' + cd backend && CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-linux-amd64 ./cmd/desktop + @printf 'Linux desktop build complete\n' -test-mysql-quick: - @echo "Running MySQL tests (without container management)..." - cd backend && go test -tags=mysql ./tests/mysql/... -v -count=1 +desktop-lint: _backend-lint _frontend-check + @printf 'Desktop lint complete\n' -# ============================================ -# 前端 -# ============================================ +desktop-test: _desktop-test + @printf 'Desktop tests passed\n' -frontend-install: - cd frontend && bun install +desktop-clean: _desktop-clean + @printf 'Desktop artifacts cleaned\n' -frontend-build: frontend-install version-sync - cd frontend && bun run build +_desktop-test: + cd backend && go test ./cmd/desktop/... -v -frontend-dev: frontend-install version-sync - cd frontend && bun dev +_desktop-clean: + rm -rf build/ embedfs/assets embedfs/frontend-dist backend/cmd/desktop/rsrc_windows_amd64.syso -frontend-test: frontend-install - cd frontend && bun run test - -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..." +_desktop-prepare-frontend: _frontend-install + @printf 'Preparing frontend for desktop...\n' ifeq ($(OS),Windows_NT) powershell -NoProfile -Command "Copy-Item -LiteralPath 'frontend/.env.desktop' -Destination 'frontend/.env.production.local' -Force" cd frontend && bun run build @@ -194,8 +141,8 @@ else rm -f frontend/.env.production.local endif -desktop-prepare-embedfs: - @echo "📦 Preparing embedded filesystem..." +_desktop-prepare-embedfs: + @printf 'Preparing embedded filesystem...\n' 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" else @@ -204,8 +151,8 @@ else cp -r frontend/dist embedfs/frontend-dist endif -desktop-prepare-windows-resource: - @echo "📦 Preparing Windows executable icon..." +_desktop-prepare-windows-resource: + @printf 'Preparing Windows executable icon...\n' ifeq ($(OS),Windows_NT) cd backend/cmd/desktop && windres -O coff -F pe-x86-64 -i icon_windows.rc -o rsrc_windows_amd64.syso else @@ -214,74 +161,34 @@ else 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; \ else \ - echo "❌ 未找到 windres,无法生成 Windows exe 图标资源"; \ + printf 'Missing windres for Windows icon resource generation\n'; \ exit 1; \ fi 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)" 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 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 -release-assets-windows: version-sync version-check desktop-build-win +release-assets-windows: version-check desktop-build-win 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" 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-win-amd64.exe' -DestinationPath '$(RELEASE_DIR)/$(DESKTOP_WINDOWS_ASSET)' -Force" else - @echo "❌ release-assets-windows 需要在 Windows 环境执行" + @printf 'release-assets-windows requires Windows\n' @exit 1 endif -release-assets-macos: version-sync version-check desktop-build-mac +release-assets-macos: version-check desktop-build-mac rm -rf "$(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 @@ -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 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 - @echo "✅ Clean complete" +_backend-build: + @$(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 diff --git a/README.md b/README.md index fcb83ee..ddaf52c 100644 --- a/README.md +++ b/README.md @@ -109,9 +109,6 @@ make desktop-build-win # Linux make desktop-build-linux - -# 构建所有平台 -make desktop-build ``` **使用桌面应用**: @@ -132,32 +129,28 @@ make desktop-build - Xfce: 需要 libappindicator - 其他支持 StatusNotifierItem 规范的环境 -### CLI 模式 - -#### 后端 +### Server 模式(前后端分离) ```bash -cd backend -go mod download -go run cmd/server/main.go +make server-run ``` -后端服务将在 `http://localhost:9826` 启动。首次启动会自动: +`make server-run` 会并行启动: +- 后端服务:`http://localhost:9826` +- 前端开发服务器:`http://localhost:5173` + +前端请求会继续通过 Vite proxy 转发到后端。后端首次启动会自动: - 创建配置文件 `~/.nex/config.yaml` - 初始化数据库 `~/.nex/config.db` - 运行数据库迁移 - 创建日志目录 `~/.nex/log/` -### 前端 +**构建 server 模式产物**: ```bash -cd frontend -bun install -bun dev +make server-build ``` -前端开发服务器将在 `http://localhost:5173` 启动,API 请求通过 Vite proxy 转发到后端。 - ## API 接口 ### 代理接口(对外部应用) @@ -279,51 +272,41 @@ export NEX_DATABASE_DBNAME=nex ## 测试 ```bash -# 顶层便捷命令 -make test # 运行所有测试 +# 全局默认测试(不含 MySQL 和前端 E2E) +make test -# 后端测试 -make backend-test # 后端测试 -make backend-test-coverage # 后端覆盖率 -make backend-test-unit # 后端单元测试 -make backend-test-integration # 后端集成测试 - -# 前端测试 -make frontend-test # 前端测试 -make frontend-test-e2e # 前端 E2E 测试 -make frontend-test-coverage # 前端覆盖率 +# 产品级测试 +make server-test +make desktop-test ``` +backend 分类测试、MySQL 专项测试和前端 E2E 测试请分别查看 `backend/README.md` 与 `frontend/README.md`。 + ## 开发 ```bash # 首次克隆后安装 Git hooks lefthook install -# 顶层便捷命令 -make dev # 启动开发环境(并行启动后端和前端) -make build # 构建所有产物 -make lint # 检查所有代码 -make clean # 清理所有构建产物 +# 全局命令 +make lint # 前后端共享检查 +make test # 默认全量测试(不含 MySQL/E2E) +make clean # 清理所有构建产物和测试报告 -# 后端开发 -make backend-build # 构建后端 -make backend-run # 运行后端 -make backend-dev # 后端开发模式 -make backend-lint # 后端代码检查 -make backend-clean # 清理后端构建产物 +# server 模式 +make server-run # 并行启动后端和前端开发服务 +make server-build # 构建 backend/bin/server 和 frontend/dist +make server-lint # server 模式检查 +make server-test # server 模式测试 +make server-clean # 清理 server 模式产物 -# 数据库操作 -make backend-db-up # 数据库迁移 -make backend-db-down # 数据库回滚 -make backend-db-status # 数据库迁移状态 -make backend-db-create # 创建新迁移 - -# 前端开发 -make frontend-build # 构建前端 -make frontend-dev # 前端开发模式 -make frontend-lint # 前端代码检查 -make frontend-clean # 清理前端构建产物 +# desktop 模式 +make desktop-build-mac # 构建 macOS 桌面应用 +make desktop-build-win # 构建 Windows 桌面应用 +make desktop-build-linux # 构建 Linux 桌面应用 +make desktop-lint # desktop 模式检查 +make desktop-test # desktop 专属测试 +make desktop-clean # 清理 desktop 产物 ``` ## 版本与发布 @@ -339,13 +322,13 @@ make frontend-clean # 清理前端构建产物 2. 同步镜像文件: ```bash -go run ./backend/cmd/versionctl sync +make version-sync ``` 3. 校验版本一致性: ```bash -go run ./backend/cmd/versionctl check +make version-check ``` 4. 提交版本变更后,创建发布 tag: diff --git a/backend/Makefile b/backend/Makefile new file mode 100644 index 0000000..9e6c7dd --- /dev/null +++ b/backend/Makefile @@ -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 diff --git a/backend/README.md b/backend/README.md index ec810d9..9168aff 100644 --- a/backend/README.md +++ b/backend/README.md @@ -437,24 +437,37 @@ docker run -d -e NEX_SERVER_PORT=9000 -e NEX_LOG_LEVEL=info nex-server ## 测试 ```bash -# 运行所有测试 +# 运行 backend 默认测试 make test +# 分类测试 +make test-unit +make test-integration + # 生成覆盖率报告 make test-coverage + +# MySQL 专项测试 +make mysql-up +make mysql-down +make mysql-test +make mysql-test-quick ``` ## 数据库迁移 ```bash # 使用 Makefile -make migrate-up DB_PATH=~/.nex/config.db -make migrate-down DB_PATH=~/.nex/config.db -make migrate-status DB_PATH=~/.nex/config.db +make migrate-up DB_DSN=~/.nex/config.db +make migrate-down DB_DSN=~/.nex/config.db +make migrate-status DB_DSN=~/.nex/config.db # 创建新迁移 make migrate-create +# MySQL 迁移 +make migrate-up DB_DRIVER=mysql DB_DSN='user:pass@tcp(localhost:3306)/nex' + # 或直接使用 goose goose -dir migrations sqlite3 ~/.nex/config.db up ``` @@ -571,9 +584,12 @@ GET /anthropic/v1/models ## 开发 ```bash -make build # 构建 -make lint # 代码检查 -make deps # 整理依赖 +make build # 构建 backend/bin/server +make run # 运行后端服务 +make lint # 代码检查 +make clean # 清理 backend 构建产物 +go mod tidy # 整理依赖 +go generate ./... # 刷新 mock 等生成代码 ``` 环境要求:Go 1.26 或更高版本 diff --git a/openspec/specs/database-migration/spec.md b/openspec/specs/database-migration/spec.md index ff13042..fe22417 100644 --- a/openspec/specs/database-migration/spec.md +++ b/openspec/specs/database-migration/spec.md @@ -67,11 +67,11 @@ ### Requirement: 迁移命令集成 -迁移 SHALL 集成到 Makefile。 +迁移 SHALL 集成到 `backend/Makefile`,而不是根目录 `Makefile`。 #### Scenario: 迁移 up 命令 -- **WHEN** 执行 `make backend-migrate-up` +- **WHEN** 在 `backend/` 目录执行 `make migrate-up` - **THEN** SHALL 执行所有待执行的迁移 - **THEN** SHALL 使用 `DB_DRIVER` 变量选择方言目录(默认 `sqlite3`) - **THEN** SHALL 使用 `DB_DSN` 变量作为数据库连接串 @@ -79,20 +79,20 @@ #### Scenario: 迁移 down 命令 -- **WHEN** 执行 `make backend-migrate-down` +- **WHEN** 在 `backend/` 目录执行 `make migrate-down` - **THEN** SHALL 回滚最后一个迁移 - **THEN** SHALL 使用 `DB_DRIVER` 和 `DB_DSN` 变量 - **THEN** SHALL 显示回滚进度 #### Scenario: 迁移状态命令 -- **WHEN** 执行 `make backend-migrate-status` +- **WHEN** 在 `backend/` 目录执行 `make migrate-status` - **THEN** SHALL 显示当前迁移状态 - **THEN** SHALL 显示已执行和待执行的迁移 #### Scenario: 创建迁移命令 -- **WHEN** 执行 `make backend-migrate-create` +- **WHEN** 在 `backend/` 目录执行 `make migrate-create` - **THEN** SHALL 同时在 `migrations/sqlite/` 和 `migrations/mysql/` 两个目录创建新的迁移文件模板 - **THEN** SHALL 使用递增的版本号 diff --git a/openspec/specs/mysql-testing/spec.md b/openspec/specs/mysql-testing/spec.md index 5d6ecad..e5f9d6b 100644 --- a/openspec/specs/mysql-testing/spec.md +++ b/openspec/specs/mysql-testing/spec.md @@ -8,16 +8,16 @@ ### Requirement: MySQL 测试环境可启动 -系统 SHALL 提供 Docker Compose 配置以启动 MySQL 8.0 测试环境。 +系统 SHALL 提供 Docker Compose 配置和 backend 局部 make 命令以启动 MySQL 8.0 测试环境。 #### Scenario: 启动 MySQL 测试容器 -- **WHEN** 执行 `make test-mysql-up` +- **WHEN** 在 `backend/` 目录执行 `make mysql-up` - **THEN** 启动 MySQL 8.0 容器,端口 13306 - **AND** 创建数据库 `nex_test` - **AND** 容器数据存储在内存盘(tmpfs) #### Scenario: 销毁 MySQL 测试容器 -- **WHEN** 执行 `make test-mysql-down` +- **WHEN** 在 `backend/` 目录执行 `make mysql-down` - **THEN** 停止并删除容器 - **AND** 所有数据被销毁 @@ -90,15 +90,15 @@ MySQL 测试 SHALL 验证并发写入不丢失数据。 ### Requirement: MySQL 测试命令完整 -Makefile SHALL 提供完整的 MySQL 测试命令。 +`backend/Makefile` SHALL 提供完整的 MySQL 测试命令。 #### Scenario: 完整测试流程 -- **WHEN** 执行 `make test-mysql` +- **WHEN** 在 `backend/` 目录执行 `make mysql-test` - **THEN** 启动 Docker MySQL - **AND** 等待 MySQL 就绪 - **AND** 运行所有 MySQL 测试 - **AND** 销毁容器 #### Scenario: 快速测试(容器已运行) -- **WHEN** 执行 `make test-mysql-quick` +- **WHEN** 在 `backend/` 目录执行 `make mysql-test-quick` - **THEN** 直接运行测试,不管理容器生命周期 diff --git a/openspec/specs/test-coverage/spec.md b/openspec/specs/test-coverage/spec.md index 1777506..2b02bd1 100644 --- a/openspec/specs/test-coverage/spec.md +++ b/openspec/specs/test-coverage/spec.md @@ -157,26 +157,33 @@ ### Requirement: 集成到构建流程 -测试 SHALL 集成到构建流程中。 +测试 SHALL 同时集成到根目录公共流程和 backend 局部流程中。 #### Scenario: 运行测试命令 - **WHEN** 执行 `make test` 命令 -- **THEN** SHALL 运行所有单元测试和集成测试 +- **THEN** SHALL 运行 backend 核心测试、frontend 的 Vitest 单元/组件测试和 desktop 专属测试 +- **THEN** SHALL NOT 运行 MySQL 专项测试和 frontend E2E 测试 - **THEN** SHALL 显示测试结果 - **THEN** SHALL 在测试失败时返回非零退出码 +#### Scenario: 运行 backend 局部测试命令 + +- **WHEN** 在 `backend/` 目录执行 `make test` 命令 +- **THEN** SHALL 运行 backend 核心测试 +- **THEN** SHALL NOT 运行 frontend 的 Vitest 单元/组件测试、desktop 专属测试、MySQL 专项测试或 frontend E2E 测试 + #### Scenario: 分类测试命令 -- **WHEN** 执行 `make test-unit` 命令 +- **WHEN** 在 `backend/` 目录执行 `make test-unit` 命令 - **THEN** SHALL 仅运行 `./internal/...` 和 `./pkg/...` 下的单元测试 -- **WHEN** 执行 `make test-integration` 命令 +- **WHEN** 在 `backend/` 目录执行 `make test-integration` 命令 - **THEN** SHALL 仅运行 `./tests/...` 下的集成测试 #### Scenario: 覆盖率检查命令 -- **WHEN** 执行 `make test-coverage` 命令 +- **WHEN** 在 `backend/` 目录执行 `make test-coverage` 命令 - **THEN** SHALL 运行测试并生成覆盖率报告 - **THEN** SHALL 检查覆盖率是否达标 - **THEN** SHALL 在覆盖率不足时返回非零退出码 diff --git a/openspec/specs/workspace-command-flows/spec.md b/openspec/specs/workspace-command-flows/spec.md new file mode 100644 index 0000000..9a3b89c --- /dev/null +++ b/openspec/specs/workspace-command-flows/spec.md @@ -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 提供等价的公共命令别名