build: add server-rs check scripts
This commit is contained in:
@@ -119,7 +119,8 @@
|
|||||||
交付物:[../server-rs/scripts/dev.ps1](../server-rs/scripts/dev.ps1)、[../server-rs/scripts/dev.sh](../server-rs/scripts/dev.sh)
|
交付物:[../server-rs/scripts/dev.ps1](../server-rs/scripts/dev.ps1)、[../server-rs/scripts/dev.sh](../server-rs/scripts/dev.sh)
|
||||||
- [x] 新增测试脚本
|
- [x] 新增测试脚本
|
||||||
交付物:[../server-rs/scripts/test.ps1](../server-rs/scripts/test.ps1)、[../server-rs/scripts/test.sh](../server-rs/scripts/test.sh)
|
交付物:[../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 脚本
|
- [ ] 新增 smoke 脚本
|
||||||
- [ ] 新增 SpacetimeDB 本地开发脚本
|
- [ ] 新增 SpacetimeDB 本地开发脚本
|
||||||
|
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ server-rs/
|
|||||||
│ └─ tests-support/ # 集成测试、contract 测试、smoke 支撑
|
│ └─ tests-support/ # 集成测试、contract 测试、smoke 支撑
|
||||||
└─ scripts/
|
└─ scripts/
|
||||||
├─ dev.sh / dev.ps1
|
├─ dev.sh / dev.ps1
|
||||||
|
├─ check.sh / check.ps1
|
||||||
├─ spacetime-publish.sh
|
├─ spacetime-publish.sh
|
||||||
└─ smoke.sh
|
└─ smoke.sh
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
## 2. 当前阶段说明
|
## 2. 当前阶段说明
|
||||||
|
|
||||||
当前目录已经完成以下二十八项初始化:
|
当前目录已经完成以下三十项初始化:
|
||||||
|
|
||||||
1. 为新后端预留正式目录并把路径固定到仓库结构中。
|
1. 为新后端预留正式目录并把路径固定到仓库结构中。
|
||||||
2. 创建虚拟 workspace `Cargo.toml`,后续 package 会逐项挂入。
|
2. 创建虚拟 workspace `Cargo.toml`,后续 package 会逐项挂入。
|
||||||
@@ -44,10 +44,13 @@
|
|||||||
26. 创建 `scripts/dev.sh`,固定 Unix-like 本地开发入口。
|
26. 创建 `scripts/dev.sh`,固定 Unix-like 本地开发入口。
|
||||||
27. 创建 `scripts/test.ps1`,固定 Windows 本地测试入口。
|
27. 创建 `scripts/test.ps1`,固定 Windows 本地测试入口。
|
||||||
28. 创建 `scripts/test.sh`,固定 Unix-like 本地测试入口。
|
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. 已冻结边界
|
## 3. 已冻结边界
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
5. `/healthz`、`/api/*`、SSE 与静态资源兼容层装配
|
5. `/healthz`、`/api/*`、SSE 与静态资源兼容层装配
|
||||||
6. 由 `../../scripts/dev.ps1` 与 `../../scripts/dev.sh` 驱动的本地开发启动链路
|
6. 由 `../../scripts/dev.ps1` 与 `../../scripts/dev.sh` 驱动的本地开发启动链路
|
||||||
7. 由 `../../scripts/test.ps1` 与 `../../scripts/test.sh` 驱动的本地测试链路
|
7. 由 `../../scripts/test.ps1` 与 `../../scripts/test.sh` 驱动的本地测试链路
|
||||||
|
8. 由 `../../scripts/check.ps1` 与 `../../scripts/check.sh` 驱动的本地统一检查链路
|
||||||
|
|
||||||
## 2. 当前阶段说明
|
## 2. 当前阶段说明
|
||||||
|
|
||||||
@@ -73,6 +74,13 @@
|
|||||||
3. 当请求携带 `x-genarrative-response-envelope` 时,`/healthz` 会返回标准 success envelope。
|
3. 当请求携带 `x-genarrative-response-envelope` 时,`/healthz` 会返回标准 success envelope。
|
||||||
4. `x-request-id`、`x-api-version`、`x-route-version`、`x-response-time-ms` 会在 `/healthz` 响应中一并回写。
|
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. 边界约束
|
## 3. 边界约束
|
||||||
|
|
||||||
1. `api-server` 负责 HTTP、SSE、Cookie、Header、路由与协议装配。
|
1. `api-server` 负责 HTTP、SSE、Cookie、Header、路由与协议装配。
|
||||||
|
|||||||
@@ -99,8 +99,14 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(body["ok"], Value::Bool(true));
|
assert_eq!(body["ok"], Value::Bool(true));
|
||||||
assert_eq!(body["data"]["ok"], Value::String("value".to_string()));
|
assert_eq!(body["data"]["ok"], Value::String("value".to_string()));
|
||||||
assert_eq!(body["meta"]["requestId"], Value::String("req-test".to_string()));
|
assert_eq!(
|
||||||
assert_eq!(body["meta"]["routeVersion"], Value::String(API_VERSION.to_string()));
|
body["meta"]["requestId"],
|
||||||
|
Value::String("req-test".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
body["meta"]["routeVersion"],
|
||||||
|
Value::String(API_VERSION.to_string())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -122,8 +128,14 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let body = json_error_body(Some(&request_context), &error).0;
|
let body = json_error_body(Some(&request_context), &error).0;
|
||||||
|
|
||||||
assert_eq!(body["error"]["code"], Value::String("NOT_FOUND".to_string()));
|
assert_eq!(
|
||||||
assert_eq!(body["meta"]["requestId"], Value::String("req-test".to_string()));
|
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());
|
assert!(body.get("ok").is_none());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,15 +77,24 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
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")
|
Some("req-health-legacy")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
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")
|
Some("2026-04-08")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
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")
|
Some("2026-04-08")
|
||||||
);
|
);
|
||||||
assert!(response.headers().contains_key("x-response-time-ms"));
|
assert!(response.headers().contains_key("x-response-time-ms"));
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
use axum::{
|
use axum::{extract::Request, http::header::CONTENT_TYPE, middleware::Next, response::Response};
|
||||||
extract::Request,
|
|
||||||
http::header::CONTENT_TYPE,
|
|
||||||
middleware::Next,
|
|
||||||
response::Response,
|
|
||||||
};
|
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ use serde_json::{json, Value};
|
|||||||
|
|
||||||
use crate::{api_response::json_success_body, request_context::RequestContext};
|
use crate::{api_response::json_success_body, request_context::RequestContext};
|
||||||
|
|
||||||
pub async fn health_check(
|
pub async fn health_check(Extension(request_context): Extension<RequestContext>) -> Json<Value> {
|
||||||
Extension(request_context): Extension<RequestContext>,
|
|
||||||
) -> Json<Value> {
|
|
||||||
json_success_body(
|
json_success_body(
|
||||||
Some(&request_context),
|
Some(&request_context),
|
||||||
json!({
|
json!({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
mod app;
|
|
||||||
mod api_response;
|
mod api_response;
|
||||||
|
mod app;
|
||||||
mod config;
|
mod config;
|
||||||
mod error_middleware;
|
mod error_middleware;
|
||||||
mod health;
|
mod health;
|
||||||
|
|||||||
@@ -28,9 +28,7 @@ pub async fn propagate_request_id_header(request: Request, next: Next) -> Respon
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(header_value) = HeaderValue::from_str(API_VERSION) {
|
if let Ok(header_value) = HeaderValue::from_str(API_VERSION) {
|
||||||
response
|
response.headers_mut().insert(
|
||||||
.headers_mut()
|
|
||||||
.insert(
|
|
||||||
HeaderName::from_static(API_VERSION_HEADER),
|
HeaderName::from_static(API_VERSION_HEADER),
|
||||||
header_value.clone(),
|
header_value.clone(),
|
||||||
);
|
);
|
||||||
|
|||||||
67
server-rs/scripts/check.ps1
Normal file
67
server-rs/scripts/check.ps1
Normal file
@@ -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
|
||||||
|
}
|
||||||
60
server-rs/scripts/check.sh
Normal file
60
server-rs/scripts/check.sh
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user