Files
review/tools/swagger-detect.sh
T
jiantw83 57ac9cdcd9 feat(swagger-detect): 新增判斷專案有沒有啟用 Swagger 的工具
What:新增 tools/swagger-detect.sh,判斷一個專案有沒有真的啟用 Swagger 文件。
涵蓋 dotnet、nodejs、python 三種技術棧,輸出 support=、stack=、package=、
config= 四類欄位;結束碼 0 支援、1 不支援、2 參數個數不對或專案路徑不存在。

Why:API 文件稽核只對產得出 Swagger 文件的專案有意義。支不支援如果交給 agent
自己看程式碼判斷,同一個專案可能這次說支援、下次說不支援。判定寫成腳本,呼叫端
讀結束碼分支就好,不必自己猜。

How:雙重確認,套件與設定缺一不算支援。第一關在套件宣告檔裡找已知的 Swagger
套件,第二關在原始碼裡找真的把 Swagger 接上去的呼叫或裝飾子。設定關鍵字一律
挑接線動作,不挑 import 或 require——光是引入套件不代表文件真的掛上去了。裝了
套件卻沒啟用的專案很常見,只看套件會誤判,讓稽核跑在一個根本產不出文件的專案
上。輸出刻意做成一行一個 key=value,呼叫端逐行讀就好。

Who:jsc-review 的 api-doc 技能,以及 jsc-sdlc 實作階段的收尾稽核。
2026-08-27 15:41:38 +08:00

171 lines
6.3 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env sh
# swagger-detect.sh — 判斷一個專案有沒有真的啟用 Swagger(OpenAPI)文件。
# 用法:swagger-detect.sh [專案路徑](預設目前目錄)
#
# 規則(雙重確認,缺一不算支援):
# 1. 套件:在套件宣告檔裡找得到已知的 Swagger 套件。
# 2. 設定:在原始碼裡找得到實際把 Swagger 接上去的呼叫或裝飾子。
# 只找到套件不算支援。裝了沒啟用的專案很常見,只看套件會誤判,
# 讓 api-doc 稽核跑在一個根本產不出文件的專案上。
# 設定關鍵字一律挑「接線動作」,不挑 import 或 require。光是引入套件
# 不代表文件真的掛上去了。
#
# 涵蓋範圍:
# dotnet 套件 Swashbuckle.AspNetCore、NSwag.AspNetCore
# 設定 AddSwaggerGen、UseSwagger、AddOpenApiDocument、UseOpenApi
# nodejs 套件 swagger-ui-express、@nestjs/swagger、fastify-swagger(含 @fastify/swagger)
# 設定 SwaggerModule.setup、swaggerUi.setup、swaggerUi.serve、
# register 進 fastify 的 swagger 外掛
# python 套件 fastapi、flasgger、drf-spectacular
# 設定 FastAPI( 建立 app、Swagger( 掛上 flasgger、SPECTACULAR_SETTINGS、
# SpectacularAPIView
#
# 輸出(純文字,一行一個 key=value,呼叫端逐行讀就好):
# 第一行永遠是 support=yes 或 support=no。
# 之後每一組偵測到套件的技術棧輸出三行:
# stack=dotnet|nodejs|python
# package=命中的套件名,多個以半形逗號相連
# config=命中的設定關鍵字,多個以半形逗號相連;沒命中就是空字串
# 一個套件都沒找到時只有 support=no 一行。
# 範例:
# support=yes
# stack=dotnet
# package=Swashbuckle.AspNetCore
# config=AddSwaggerGen,UseSwagger
#
# 結束碼:
# 0 支援(至少一個技術棧同時命中套件與設定)
# 1 不支援(沒有任何技術棧同時命中)
# 2 參數個數不對、專案路徑不存在,或環境缺 grep
#
# 護欄:
# 掃描一律跳過 node_modules、.git、bin、obj、dist、build、venv、.venv、
# __pycache__、packages、vendor,避免把相依套件自己的原始碼當成專案設定。
# 錯誤訊息一律印繁中到 stderr,正常輸出只走 stdout。
set -u
if [ "$#" -gt 1 ]; then
echo "用法:swagger-detect.sh [專案路徑]" >&2
exit 2
fi
DIR="${1:-.}"
[ -d "$DIR" ] || { echo "錯誤:找不到專案路徑 $DIR。請確認路徑後重試。" >&2; exit 2; }
command -v grep >/dev/null 2>&1 || { echo "錯誤:環境缺 grep,無法掃描。" >&2; exit 2; }
EX1=--exclude-dir=node_modules
EX2=--exclude-dir=.git
EX3=--exclude-dir=bin
EX4=--exclude-dir=obj
EX5=--exclude-dir=dist
EX6=--exclude-dir=build
EX7=--exclude-dir=venv
EX8=--exclude-dir=.venv
EX9=--exclude-dir=__pycache__
EX10=--exclude-dir=packages
EX11=--exclude-dir=vendor
# scan <延伸正規表示式> <副檔名樣式…>:命中回 0,沒命中回 1。
scan() {
pattern="$1"
shift
for inc in "$@"; do
if grep -R -l -E -i \
"$EX1" "$EX2" "$EX3" "$EX4" "$EX5" "$EX6" \
"$EX7" "$EX8" "$EX9" "$EX10" "$EX11" \
--include="$inc" -e "$pattern" "$DIR" >/dev/null 2>&1; then
return 0
fi
done
return 1
}
# append <既有清單> <新項目>:以半形逗號相連後印出。
append() {
if [ -z "$1" ]; then
printf '%s' "$2"
else
printf '%s,%s' "$1" "$2"
fi
}
SUPPORT=no
RECORDS=""
# collect <技術棧> <套件清單> <設定清單>:有套件才留紀錄,兩者都有才算支援。
collect() {
[ -n "$2" ] || return 0
RECORDS="${RECORDS}stack=$1
package=$2
config=$3
"
[ -n "$3" ] && SUPPORT=yes
return 0
}
# ── dotnet ────────────────────────────────────────────────────────────────
DOTNET_PKG=""
DOTNET_CFG=""
for pkg in 'Swashbuckle\.AspNetCore:Swashbuckle.AspNetCore' 'NSwag\.AspNetCore:NSwag.AspNetCore'; do
if scan "${pkg%%:*}" '*.csproj' '*.fsproj' '*.vbproj' '*.props' 'packages.config'; then
DOTNET_PKG=$(append "$DOTNET_PKG" "${pkg##*:}")
fi
done
if [ -n "$DOTNET_PKG" ]; then
for cfg in AddSwaggerGen UseSwagger AddOpenApiDocument UseOpenApi; do
if scan "$cfg" '*.cs' '*.fs' '*.vb'; then
DOTNET_CFG=$(append "$DOTNET_CFG" "$cfg")
fi
done
fi
collect dotnet "$DOTNET_PKG" "$DOTNET_CFG"
# ── nodejs ────────────────────────────────────────────────────────────────
NODE_PKG=""
NODE_CFG=""
for pkg in swagger-ui-express @nestjs/swagger fastify-swagger @fastify/swagger; do
if scan "\"$pkg\"" 'package.json'; then
NODE_PKG=$(append "$NODE_PKG" "$pkg")
fi
done
if [ -n "$NODE_PKG" ]; then
# 每一項都是「接線動作」:掛路由或註冊外掛,不是 import。
for cfg in 'SwaggerModule\.setup:SwaggerModule.setup' \
'swaggerUi\.setup:swaggerUi.setup' \
'swaggerUi\.serve:swaggerUi.serve' \
'register\(.*swagger:register(swagger)'; do
if scan "${cfg%%:*}" '*.js' '*.mjs' '*.cjs' '*.ts'; then
NODE_CFG=$(append "$NODE_CFG" "${cfg##*:}")
fi
done
fi
collect nodejs "$NODE_PKG" "$NODE_CFG"
# ── python ────────────────────────────────────────────────────────────────
PY_PKG=""
PY_CFG=""
for pkg in fastapi flasgger drf-spectacular; do
if scan "$pkg" 'requirements*.txt' 'pyproject.toml' 'Pipfile' 'setup.py' 'setup.cfg'; then
PY_PKG=$(append "$PY_PKG" "$pkg")
fi
done
if [ -n "$PY_PKG" ]; then
for cfg in 'FastAPI\(:FastAPI(' \
'Swagger\(:Swagger(' \
'SPECTACULAR_SETTINGS:SPECTACULAR_SETTINGS' \
'SpectacularAPIView:SpectacularAPIView'; do
if scan "${cfg%%:*}" '*.py'; then
PY_CFG=$(append "$PY_CFG" "${cfg##*:}")
fi
done
fi
collect python "$PY_PKG" "$PY_CFG"
# ── 輸出 ──────────────────────────────────────────────────────────────────
echo "support=$SUPPORT"
[ -n "$RECORDS" ] && printf '%s' "$RECORDS"
[ "$SUPPORT" = yes ] && exit 0
exit 1