From fdab91e54de8f181acdcf9b00775a5fc5e45acf0 Mon Sep 17 00:00:00 2001 From: kdletters Date: Tue, 21 Apr 2026 01:48:33 +0800 Subject: [PATCH] build: add server-rs check scripts --- .../01_M0_M2_FOUNDATION_AND_AUTH.md | 3 +- ...M_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md | 1 + server-rs/README.md | 7 +- server-rs/apps/api-server/README.md | 8 +++ server-rs/apps/api-server/src/api_response.rs | 20 ++++-- server-rs/apps/api-server/src/app.rs | 15 ++++- .../apps/api-server/src/error_middleware.rs | 7 +- server-rs/apps/api-server/src/health.rs | 4 +- server-rs/apps/api-server/src/main.rs | 2 +- .../apps/api-server/src/response_headers.rs | 10 ++- server-rs/scripts/check.ps1 | 67 +++++++++++++++++++ server-rs/scripts/check.sh | 60 +++++++++++++++++ 12 files changed, 178 insertions(+), 26 deletions(-) create mode 100644 server-rs/scripts/check.ps1 create mode 100644 server-rs/scripts/check.sh diff --git a/backend-rewrite-tasklist/01_M0_M2_FOUNDATION_AND_AUTH.md b/backend-rewrite-tasklist/01_M0_M2_FOUNDATION_AND_AUTH.md index f7798f2e..63979533 100644 --- a/backend-rewrite-tasklist/01_M0_M2_FOUNDATION_AND_AUTH.md +++ b/backend-rewrite-tasklist/01_M0_M2_FOUNDATION_AND_AUTH.md @@ -119,7 +119,8 @@ 交付物:[../server-rs/scripts/dev.ps1](../server-rs/scripts/dev.ps1)、[../server-rs/scripts/dev.sh](../server-rs/scripts/dev.sh) - [x] 新增测试脚本 交付物:[../server-rs/scripts/test.ps1](../server-rs/scripts/test.ps1)、[../server-rs/scripts/test.sh](../server-rs/scripts/test.sh) -- [ ] 新增 lint / fmt / clippy / check 脚本 +- [x] 新增 lint / fmt / clippy / check 脚本 + 交付物:[../server-rs/scripts/check.ps1](../server-rs/scripts/check.ps1)、[../server-rs/scripts/check.sh](../server-rs/scripts/check.sh) - [ ] 新增 smoke 脚本 - [ ] 新增 SpacetimeDB 本地开发脚本 diff --git a/docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md b/docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md index 40d96346..7ca9ea45 100644 --- a/docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md +++ b/docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md @@ -243,6 +243,7 @@ server-rs/ │ └─ tests-support/ # 集成测试、contract 测试、smoke 支撑 └─ scripts/ ├─ dev.sh / dev.ps1 + ├─ check.sh / check.ps1 ├─ spacetime-publish.sh └─ smoke.sh ``` diff --git a/server-rs/README.md b/server-rs/README.md index 8b080765..792c9992 100644 --- a/server-rs/README.md +++ b/server-rs/README.md @@ -14,7 +14,7 @@ ## 2. 当前阶段说明 -当前目录已经完成以下二十八项初始化: +当前目录已经完成以下三十项初始化: 1. 为新后端预留正式目录并把路径固定到仓库结构中。 2. 创建虚拟 workspace `Cargo.toml`,后续 package 会逐项挂入。 @@ -44,10 +44,13 @@ 26. 创建 `scripts/dev.sh`,固定 Unix-like 本地开发入口。 27. 创建 `scripts/test.ps1`,固定 Windows 本地测试入口。 28. 创建 `scripts/test.sh`,固定 Unix-like 本地测试入口。 +29. 创建 `scripts/check.ps1`,固定 Windows 本地统一检查入口。 +30. 创建 `scripts/check.sh`,固定 Unix-like 本地统一检查入口。 后续任务会继续在本目录内按顺序补齐: -1. lint、smoke 与 SpacetimeDB 本地脚本 +1. smoke 脚本 +2. SpacetimeDB 本地开发脚本 ## 3. 已冻结边界 diff --git a/server-rs/apps/api-server/README.md b/server-rs/apps/api-server/README.md index 714d1df2..aac1f3b2 100644 --- a/server-rs/apps/api-server/README.md +++ b/server-rs/apps/api-server/README.md @@ -13,6 +13,7 @@ 5. `/healthz`、`/api/*`、SSE 与静态资源兼容层装配 6. 由 `../../scripts/dev.ps1` 与 `../../scripts/dev.sh` 驱动的本地开发启动链路 7. 由 `../../scripts/test.ps1` 与 `../../scripts/test.sh` 驱动的本地测试链路 +8. 由 `../../scripts/check.ps1` 与 `../../scripts/check.sh` 驱动的本地统一检查链路 ## 2. 当前阶段说明 @@ -73,6 +74,13 @@ 3. 当请求携带 `x-genarrative-response-envelope` 时,`/healthz` 会返回标准 success envelope。 4. `x-request-id`、`x-api-version`、`x-route-version`、`x-response-time-ms` 会在 `/healthz` 响应中一并回写。 +当前本地检查链路约定: + +1. `../../scripts/check.ps1` 与 `../../scripts/check.sh` 统一串联 `cargo fmt --all --check`、`cargo clippy`、`cargo check`、`cargo test`。 +2. 默认检查整个 `server-rs` workspace,确保后续多 package 扩容时仍然保持统一口径。 +3. 当只需聚焦单个 package 时,可通过 `-Package` 或 `SERVER_RS_CHECK_PACKAGE` 收窄 `clippy / check / test` 目标。 +4. `cargo fmt --all --check` 仍固定覆盖整个 workspace,避免多 package 下格式基线漂移。 + ## 3. 边界约束 1. `api-server` 负责 HTTP、SSE、Cookie、Header、路由与协议装配。 diff --git a/server-rs/apps/api-server/src/api_response.rs b/server-rs/apps/api-server/src/api_response.rs index b22988d0..534648f1 100644 --- a/server-rs/apps/api-server/src/api_response.rs +++ b/server-rs/apps/api-server/src/api_response.rs @@ -99,8 +99,14 @@ mod tests { assert_eq!(body["ok"], Value::Bool(true)); assert_eq!(body["data"]["ok"], Value::String("value".to_string())); - assert_eq!(body["meta"]["requestId"], Value::String("req-test".to_string())); - assert_eq!(body["meta"]["routeVersion"], Value::String(API_VERSION.to_string())); + assert_eq!( + body["meta"]["requestId"], + Value::String("req-test".to_string()) + ); + assert_eq!( + body["meta"]["routeVersion"], + Value::String(API_VERSION.to_string()) + ); } #[test] @@ -122,8 +128,14 @@ mod tests { }; let body = json_error_body(Some(&request_context), &error).0; - assert_eq!(body["error"]["code"], Value::String("NOT_FOUND".to_string())); - assert_eq!(body["meta"]["requestId"], Value::String("req-test".to_string())); + assert_eq!( + body["error"]["code"], + Value::String("NOT_FOUND".to_string()) + ); + assert_eq!( + body["meta"]["requestId"], + Value::String("req-test".to_string()) + ); assert!(body.get("ok").is_none()); } } diff --git a/server-rs/apps/api-server/src/app.rs b/server-rs/apps/api-server/src/app.rs index 3b27eb65..514fd776 100644 --- a/server-rs/apps/api-server/src/app.rs +++ b/server-rs/apps/api-server/src/app.rs @@ -77,15 +77,24 @@ mod tests { assert_eq!(response.status(), StatusCode::OK); assert_eq!( - response.headers().get("x-request-id").and_then(|value| value.to_str().ok()), + response + .headers() + .get("x-request-id") + .and_then(|value| value.to_str().ok()), Some("req-health-legacy") ); assert_eq!( - response.headers().get("x-api-version").and_then(|value| value.to_str().ok()), + response + .headers() + .get("x-api-version") + .and_then(|value| value.to_str().ok()), Some("2026-04-08") ); assert_eq!( - response.headers().get("x-route-version").and_then(|value| value.to_str().ok()), + response + .headers() + .get("x-route-version") + .and_then(|value| value.to_str().ok()), Some("2026-04-08") ); assert!(response.headers().contains_key("x-response-time-ms")); diff --git a/server-rs/apps/api-server/src/error_middleware.rs b/server-rs/apps/api-server/src/error_middleware.rs index a974f167..0af5bf51 100644 --- a/server-rs/apps/api-server/src/error_middleware.rs +++ b/server-rs/apps/api-server/src/error_middleware.rs @@ -1,9 +1,4 @@ -use axum::{ - extract::Request, - http::header::CONTENT_TYPE, - middleware::Next, - response::Response, -}; +use axum::{extract::Request, http::header::CONTENT_TYPE, middleware::Next, response::Response}; use tracing::{error, warn}; use crate::{ diff --git a/server-rs/apps/api-server/src/health.rs b/server-rs/apps/api-server/src/health.rs index 2fa1a1e1..8ab27ab5 100644 --- a/server-rs/apps/api-server/src/health.rs +++ b/server-rs/apps/api-server/src/health.rs @@ -3,9 +3,7 @@ use serde_json::{json, Value}; use crate::{api_response::json_success_body, request_context::RequestContext}; -pub async fn health_check( - Extension(request_context): Extension, -) -> Json { +pub async fn health_check(Extension(request_context): Extension) -> Json { json_success_body( Some(&request_context), json!({ diff --git a/server-rs/apps/api-server/src/main.rs b/server-rs/apps/api-server/src/main.rs index 61cb0909..9ea35687 100644 --- a/server-rs/apps/api-server/src/main.rs +++ b/server-rs/apps/api-server/src/main.rs @@ -1,5 +1,5 @@ -mod app; mod api_response; +mod app; mod config; mod error_middleware; mod health; diff --git a/server-rs/apps/api-server/src/response_headers.rs b/server-rs/apps/api-server/src/response_headers.rs index 34935db1..1e41a9ec 100644 --- a/server-rs/apps/api-server/src/response_headers.rs +++ b/server-rs/apps/api-server/src/response_headers.rs @@ -28,12 +28,10 @@ pub async fn propagate_request_id_header(request: Request, next: Next) -> Respon } if let Ok(header_value) = HeaderValue::from_str(API_VERSION) { - response - .headers_mut() - .insert( - HeaderName::from_static(API_VERSION_HEADER), - header_value.clone(), - ); + response.headers_mut().insert( + HeaderName::from_static(API_VERSION_HEADER), + header_value.clone(), + ); response .headers_mut() .insert(HeaderName::from_static(ROUTE_VERSION_HEADER), header_value); diff --git a/server-rs/scripts/check.ps1 b/server-rs/scripts/check.ps1 new file mode 100644 index 00000000..6187c486 --- /dev/null +++ b/server-rs/scripts/check.ps1 @@ -0,0 +1,67 @@ +[CmdletBinding()] +param( + [Alias("h")] + [switch]$Help, + [string]$Package = "" +) + +$ErrorActionPreference = "Stop" + +function Write-Usage { + @( + 'Usage:' + ' ./server-rs/scripts/check.ps1' + ' ./server-rs/scripts/check.ps1 -Package api-server' + '' + 'Notes:' + ' 1. Run cargo fmt --all --check for the whole server-rs workspace' + ' 2. Run clippy/check/test for the whole workspace by default' + ' 3. Use -Package to target one workspace package for clippy/check/test' + ) -join [Environment]::NewLine +} + +if ($Help) { + Write-Usage + exit 0 +} + +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$serverRsDir = Split-Path -Parent $scriptDir +$manifestPath = Join-Path $serverRsDir "Cargo.toml" + +if (-not (Test-Path $manifestPath)) { + throw "Missing server-rs/Cargo.toml, cannot start check script." +} + +Write-Host "[server-rs:check] working dir: $serverRsDir" +Write-Host "[server-rs:check] step: cargo fmt --all --check" + +Push-Location $serverRsDir +try { + cargo fmt --all --check --manifest-path $manifestPath + + if ([string]::IsNullOrWhiteSpace($Package)) { + Write-Host "[server-rs:check] step: cargo clippy --workspace --all-targets --all-features -D warnings" + cargo clippy --workspace --manifest-path $manifestPath --all-targets --all-features -- -D warnings + + Write-Host "[server-rs:check] step: cargo check --workspace" + cargo check --workspace --manifest-path $manifestPath + + Write-Host "[server-rs:check] step: cargo test --workspace" + cargo test --workspace --manifest-path $manifestPath + } + else { + Write-Host "[server-rs:check] target package: $Package" + Write-Host "[server-rs:check] step: cargo clippy -p $Package --all-targets --all-features -D warnings" + cargo clippy -p $Package --manifest-path $manifestPath --all-targets --all-features -- -D warnings + + Write-Host "[server-rs:check] step: cargo check -p $Package" + cargo check -p $Package --manifest-path $manifestPath + + Write-Host "[server-rs:check] step: cargo test -p $Package" + cargo test -p $Package --manifest-path $manifestPath + } +} +finally { + Pop-Location +} diff --git a/server-rs/scripts/check.sh b/server-rs/scripts/check.sh new file mode 100644 index 00000000..34e8f9e2 --- /dev/null +++ b/server-rs/scripts/check.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# 统一串联 Rust workspace 的格式、lint、编译与测试校验,保证本地和 CI 使用同一条检查链路。 + +usage() { + cat <<'EOF' +用法: + ./server-rs/scripts/check.sh + SERVER_RS_CHECK_PACKAGE=api-server ./server-rs/scripts/check.sh + +说明: + 1. 先执行整个 `server-rs` workspace 的 `cargo fmt --all --check` + 2. 默认继续执行整个 workspace 的 `cargo clippy`、`cargo check`、`cargo test` + 3. 可通过 `SERVER_RS_CHECK_PACKAGE` 将 clippy/check/test 收窄到单个 package + 4. `cargo fmt --all --check` 始终覆盖整个 workspace,避免多 package 下格式口径漂移 +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + usage + exit 0 +fi + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +SERVER_RS_DIR="$(cd -- "${SCRIPT_DIR}/.." && pwd)" +MANIFEST_PATH="${SERVER_RS_DIR}/Cargo.toml" + +if [[ ! -f "${MANIFEST_PATH}" ]]; then + echo "[server-rs:check] 未找到 ${MANIFEST_PATH},无法启动检查脚本。" >&2 + exit 1 +fi + +echo "[server-rs:check] 工作目录: ${SERVER_RS_DIR}" +echo "[server-rs:check] 步骤: cargo fmt --all --check" + +cd "${SERVER_RS_DIR}" +cargo fmt --all --check --manifest-path "${MANIFEST_PATH}" + +if [[ -n "${SERVER_RS_CHECK_PACKAGE:-}" ]]; then + echo "[server-rs:check] 目标 package: ${SERVER_RS_CHECK_PACKAGE}" + echo "[server-rs:check] 步骤: cargo clippy -p ${SERVER_RS_CHECK_PACKAGE} --all-targets --all-features -D warnings" + cargo clippy -p "${SERVER_RS_CHECK_PACKAGE}" --manifest-path "${MANIFEST_PATH}" --all-targets --all-features -- -D warnings + + echo "[server-rs:check] 步骤: cargo check -p ${SERVER_RS_CHECK_PACKAGE}" + cargo check -p "${SERVER_RS_CHECK_PACKAGE}" --manifest-path "${MANIFEST_PATH}" + + echo "[server-rs:check] 步骤: cargo test -p ${SERVER_RS_CHECK_PACKAGE}" + cargo test -p "${SERVER_RS_CHECK_PACKAGE}" --manifest-path "${MANIFEST_PATH}" +else + echo "[server-rs:check] 步骤: cargo clippy --workspace --all-targets --all-features -D warnings" + cargo clippy --workspace --manifest-path "${MANIFEST_PATH}" --all-targets --all-features -- -D warnings + + echo "[server-rs:check] 步骤: cargo check --workspace" + cargo check --workspace --manifest-path "${MANIFEST_PATH}" + + echo "[server-rs:check] 步骤: cargo test --workspace" + cargo test --workspace --manifest-path "${MANIFEST_PATH}" +fi