Commit fda88865f822e79e95b005a354a451c2b9226bfe
1 parent
b3977095
update
Showing
4 changed files
with
76 additions
and
12 deletions
skills/coding/mr-create/scripts/create-mr.sh
| @@ -76,14 +76,35 @@ awk -v report="$REPORT" ' | @@ -76,14 +76,35 @@ awk -v report="$REPORT" ' | ||
| 76 | mv .tmp/mr-desc.final "$DESC_FILE" | 76 | mv .tmp/mr-desc.final "$DESC_FILE" |
| 77 | 77 | ||
| 78 | # 7. 幂等守门:查已有 opened MR | 78 | # 7. 幂等守门:查已有 opened MR |
| 79 | -HTTP_CODE=$(curl -sS -o .tmp/existing.json -w '%{http_code}' \ | 79 | +CURL_META=$(curl -sS -o .tmp/existing.json -w '%{http_code}|%{url_effective}' \ |
| 80 | --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \ | 80 | --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \ |
| 81 | "${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID}/merge_requests?source_branch=${CURRENT_BRANCH}&state=opened") | 81 | "${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID}/merge_requests?source_branch=${CURRENT_BRANCH}&state=opened") |
| 82 | 82 | ||
| 83 | +HTTP_CODE=${CURL_META%%|*} | ||
| 84 | +EFFECTIVE_URL=${CURL_META#*|} | ||
| 85 | + | ||
| 83 | if [ "$HTTP_CODE" != "200" ]; then | 86 | if [ "$HTTP_CODE" != "200" ]; then |
| 84 | echo "[create-mr] ⚠️ 查询已有 MR 失败 (HTTP $HTTP_CODE)" >&2 | 87 | echo "[create-mr] ⚠️ 查询已有 MR 失败 (HTTP $HTTP_CODE)" >&2 |
| 85 | - jq -r '.message // .error // .' .tmp/existing.json | head -c 200 >&2 | ||
| 86 | - echo >&2 | 88 | + echo " effective URL: $EFFECTIVE_URL" >&2 |
| 89 | + FIRST=$(head -c 1 .tmp/existing.json 2>/dev/null || echo "") | ||
| 90 | + case "$FIRST" in | ||
| 91 | + '{'|'[') | ||
| 92 | + echo " 响应(JSON):" >&2 | ||
| 93 | + jq -r '.message // .error // .' .tmp/existing.json 2>/dev/null | head -c 200 >&2 | ||
| 94 | + echo >&2 | ||
| 95 | + ;; | ||
| 96 | + '<') | ||
| 97 | + echo " 响应是 HTML(很可能反代/路由失配,把 API 请求送进了 web 处理链):" >&2 | ||
| 98 | + head -c 200 .tmp/existing.json >&2 | ||
| 99 | + echo >&2 | ||
| 100 | + echo " 常见原因:GITLAB_API_URL 错 / GITLAB_PROJECT_ID 不是数字 ID / 反代规范化路径" >&2 | ||
| 101 | + ;; | ||
| 102 | + *) | ||
| 103 | + echo " 响应非 JSON 非 HTML(前 200 bytes):" >&2 | ||
| 104 | + head -c 200 .tmp/existing.json >&2 | ||
| 105 | + echo >&2 | ||
| 106 | + ;; | ||
| 107 | + esac | ||
| 87 | rm -f .tmp/existing.json "$DESC_FILE" | 108 | rm -f .tmp/existing.json "$DESC_FILE" |
| 88 | exit 1 | 109 | exit 1 |
| 89 | fi | 110 | fi |
skills/plan/downstream-gen/scripts/derive-gitlab.sh
| @@ -4,8 +4,9 @@ | @@ -4,8 +4,9 @@ | ||
| 4 | # 用法: bash derive-gitlab.sh [.env.local 路径,默认 .env.local] | 4 | # 用法: bash derive-gitlab.sh [.env.local 路径,默认 .env.local] |
| 5 | # | 5 | # |
| 6 | # 派生字段: | 6 | # 派生字段: |
| 7 | -# GITLAB_PROJECT_ID = origin 的 namespace/path(/ 编码成 %2F) | ||
| 8 | # GITLAB_API_URL = <scheme>://<host>/api/v3 | 7 | # GITLAB_API_URL = <scheme>://<host>/api/v3 |
| 8 | +# GITLAB_PROJECT_ID = 通过 GitLab API(GET /projects?search=...)解析得到的项目数字 ID | ||
| 9 | +# 要求 GITLAB_TOKEN 已填且有效;token 缺失 / 验证失败 / 未匹配时留 TBD | ||
| 9 | # | 10 | # |
| 10 | # 仅当字段值为 TBD(A5 自动补) 或空时回填;用户手填值不动。 | 11 | # 仅当字段值为 TBD(A5 自动补) 或空时回填;用户手填值不动。 |
| 11 | # 仅支持 http(s) origin URL;其他协议(ssh / git@)跳过。 | 12 | # 仅支持 http(s) origin URL;其他协议(ssh / git@)跳过。 |
| @@ -39,8 +40,8 @@ REST=${URL#${SCHEME}://} | @@ -39,8 +40,8 @@ REST=${URL#${SCHEME}://} | ||
| 39 | HOST=${REST%%/*} | 40 | HOST=${REST%%/*} |
| 40 | PATH_RAW=${REST#*/} | 41 | PATH_RAW=${REST#*/} |
| 41 | PATH_RAW=${PATH_RAW%.git} | 42 | PATH_RAW=${PATH_RAW%.git} |
| 43 | +REPO_NAME=$(basename "$PATH_RAW") | ||
| 42 | 44 | ||
| 43 | -PROJECT_ID=${PATH_RAW//\//%2F} | ||
| 44 | API_URL="${SCHEME}://${HOST}/api/v3" | 45 | API_URL="${SCHEME}://${HOST}/api/v3" |
| 45 | 46 | ||
| 46 | update_field() { | 47 | update_field() { |
| @@ -99,6 +100,36 @@ report_token() { | @@ -99,6 +100,36 @@ report_token() { | ||
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | echo "derive-gitlab.sh: 从 origin ($URL) 派生 GitLab 凭据:" | 102 | echo "derive-gitlab.sh: 从 origin ($URL) 派生 GitLab 凭据:" |
| 102 | -update_field GITLAB_PROJECT_ID "$PROJECT_ID" | ||
| 103 | update_field GITLAB_API_URL "$API_URL" | 103 | update_field GITLAB_API_URL "$API_URL" |
| 104 | + | ||
| 105 | +# GITLAB_PROJECT_ID:通过 GitLab API 解析数字 ID(要求 token 已填且有效) | ||
| 106 | +TOKEN_LINE=$(grep -E '^GITLAB_TOKEN=' "$ENV_FILE" | head -1) | ||
| 107 | +TOKEN_VAL=${TOKEN_LINE#GITLAB_TOKEN=} | ||
| 108 | +case "$TOKEN_VAL" in | ||
| 109 | + \'*\') TOKEN_VAL=${TOKEN_VAL#\'}; TOKEN_VAL=${TOKEN_VAL%\'} ;; | ||
| 110 | + \"*\") TOKEN_VAL=${TOKEN_VAL#\"}; TOKEN_VAL=${TOKEN_VAL%\"} ;; | ||
| 111 | +esac | ||
| 112 | + | ||
| 113 | +case "$TOKEN_VAL" in | ||
| 114 | + ""|"【人工填写:"*|"TBD"*) | ||
| 115 | + echo " GITLAB_PROJECT_ID = TBD [token 未填,跳过 API 解析;填完 token 后重跑此脚本]" | ||
| 116 | + ;; | ||
| 117 | + *) | ||
| 118 | + USER_HTTP=$(curl -sS -o /dev/null -w '%{http_code}' --header "PRIVATE-TOKEN: $TOKEN_VAL" "$API_URL/user" 2>/dev/null || echo "000") | ||
| 119 | + if [ "$USER_HTTP" != "200" ]; then | ||
| 120 | + echo " GITLAB_PROJECT_ID = TBD [token 验证失败 HTTP $USER_HTTP,留 TBD 待人工确认]" | ||
| 121 | + else | ||
| 122 | + SEARCH_RESP=$(curl -sS --header "PRIVATE-TOKEN: $TOKEN_VAL" \ | ||
| 123 | + "$API_URL/projects?search=$REPO_NAME&simple=true&per_page=50" 2>/dev/null || echo "[]") | ||
| 124 | + NUMERIC_ID=$(echo "$SEARCH_RESP" | jq -r --arg p "$PATH_RAW" \ | ||
| 125 | + '.[] | select(.path_with_namespace == $p) | .id' 2>/dev/null | head -1) | ||
| 126 | + if [ -n "$NUMERIC_ID" ]; then | ||
| 127 | + update_field GITLAB_PROJECT_ID "$NUMERIC_ID" | ||
| 128 | + else | ||
| 129 | + echo " GITLAB_PROJECT_ID = TBD [API 未匹配 path_with_namespace=$PATH_RAW,请到 GitLab 项目设置页查数字 ID 后填入]" | ||
| 130 | + fi | ||
| 131 | + fi | ||
| 132 | + ;; | ||
| 133 | +esac | ||
| 134 | + | ||
| 104 | report_token | 135 | report_token |
skills/plan/skeleton-gen/templates/env-local-template
| @@ -29,8 +29,9 @@ TEST_DB_ALLOWED_HOSTS= | @@ -29,8 +29,9 @@ TEST_DB_ALLOWED_HOSTS= | ||
| 29 | # 自动派生为 `<scheme>://<host>/api/v3`;如派生的 scheme 错(例如远程是 ssh 但实际 web 走 https),手动改掉即可 | 29 | # 自动派生为 `<scheme>://<host>/api/v3`;如派生的 scheme 错(例如远程是 ssh 但实际 web 走 https),手动改掉即可 |
| 30 | # - GITLAB_TOKEN:GitLab v3 的 **Private Token**(用户 Profile → Account → Private token 生成;HTTP 调用仍用 | 30 | # - GITLAB_TOKEN:GitLab v3 的 **Private Token**(用户 Profile → Account → Private token 生成;HTTP 调用仍用 |
| 31 | # `PRIVATE-TOKEN` 头。v3 token 是全权限、无细粒度 scope)。**无法派生,必须人工填** | 31 | # `PRIVATE-TOKEN` 头。v3 token 是全权限、无细粒度 scope)。**无法派生,必须人工填** |
| 32 | -# - GITLAB_PROJECT_ID:A5 从 origin 远程路径自动派生为 URL-encoded 形式(如 `zhuzc/test` → `zhuzc%2Ftest`); | ||
| 33 | -# 也可手动改成项目数字 ID(GitLab 项目设置页可见) | 32 | +# - GITLAB_PROJECT_ID:项目数字 ID(GitLab 项目设置 → General → 顶部 Project ID 字段,纯整数)。 |
| 33 | +# A5 `downstream-gen` 在 GITLAB_TOKEN 已填的前提下会调 API 自动解析数字 ID 写入; | ||
| 34 | +# token 未填 / API 解析失败时留 TBD,请人工到设置页查到后填入 | ||
| 34 | GITLAB_API_URL='TBD(A5 自动补)' | 35 | GITLAB_API_URL='TBD(A5 自动补)' |
| 35 | GITLAB_TOKEN=【人工填写:GitLab Private Token(Profile → Account → Private token)】 | 36 | GITLAB_TOKEN=【人工填写:GitLab Private Token(Profile → Account → Private token)】 |
| 36 | GITLAB_PROJECT_ID='TBD(A5 自动补)' | 37 | GITLAB_PROJECT_ID='TBD(A5 自动补)' |
skills/plan/skeleton-gen/templates/scripts-test-template.sh
| 1 | #!/usr/bin/env bash | 1 | #!/usr/bin/env bash |
| 2 | # scripts/test.sh —— 合并到默认分支(main / master)前的测试闸门。 | 2 | # scripts/test.sh —— 合并到默认分支(main / master)前的测试闸门。 |
| 3 | -# 顺序:setup-db → build → lint → unit+integration → e2e → reset-db | 3 | +# 顺序:detect → setup-db → build → lint → unit+integration → e2e → reset-db |
| 4 | # 由 .githooks/pre-push 和 test-gate skill(通过子会话)调用。 | 4 | # 由 .githooks/pre-push 和 test-gate skill(通过子会话)调用。 |
| 5 | 5 | ||
| 6 | set -euo pipefail | 6 | set -euo pipefail |
| @@ -8,17 +8,28 @@ set -euo pipefail | @@ -8,17 +8,28 @@ set -euo pipefail | ||
| 8 | PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" | 8 | PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" |
| 9 | cd "$PROJECT_ROOT" | 9 | cd "$PROJECT_ROOT" |
| 10 | 10 | ||
| 11 | +# Stack detection (runtime, mode-agnostic) | ||
| 12 | +HAS_BACKEND=0; [ -d backend ] && HAS_BACKEND=1 | ||
| 13 | +HAS_FRONTEND=0; [ -d frontend ] && HAS_FRONTEND=1 | ||
| 14 | +if [ $HAS_BACKEND -eq 0 ] && [ $HAS_FRONTEND -eq 0 ]; then | ||
| 15 | + echo "[test.sh] FATAL: neither backend/ nor frontend/ exists" >&2 | ||
| 16 | + exit 1 | ||
| 17 | +fi | ||
| 18 | + | ||
| 11 | echo "[test.sh] 1/6 setup test db" | 19 | echo "[test.sh] 1/6 setup test db" |
| 12 | ./scripts/setup-test-db.sh | 20 | ./scripts/setup-test-db.sh |
| 13 | 21 | ||
| 14 | echo "[test.sh] 2/6 build" | 22 | echo "[test.sh] 2/6 build" |
| 15 | -{{build_cmd}} | 23 | +if [ $HAS_BACKEND -eq 1 ]; then (cd backend && {{backend_build}}); else echo "[test.sh] skip backend build"; fi |
| 24 | +if [ $HAS_FRONTEND -eq 1 ]; then (cd frontend && {{frontend_build}}); else echo "[test.sh] skip frontend build"; fi | ||
| 16 | 25 | ||
| 17 | echo "[test.sh] 3/6 lint" | 26 | echo "[test.sh] 3/6 lint" |
| 18 | -{{lint_cmd}} | 27 | +if [ $HAS_BACKEND -eq 1 ]; then (cd backend && {{backend_lint}}); else echo "[test.sh] skip backend lint"; fi |
| 28 | +if [ $HAS_FRONTEND -eq 1 ]; then (cd frontend && {{frontend_lint}}); else echo "[test.sh] skip frontend lint"; fi | ||
| 19 | 29 | ||
| 20 | echo "[test.sh] 4/6 unit + integration" | 30 | echo "[test.sh] 4/6 unit + integration" |
| 21 | -{{test_cmd}} | 31 | +if [ $HAS_BACKEND -eq 1 ]; then (cd backend && {{backend_test}}); else echo "[test.sh] skip backend test"; fi |
| 32 | +if [ $HAS_FRONTEND -eq 1 ]; then (cd frontend && {{frontend_test}}); else echo "[test.sh] skip frontend test"; fi | ||
| 22 | 33 | ||
| 23 | echo "[test.sh] 5/6 E2E" | 34 | echo "[test.sh] 5/6 E2E" |
| 24 | {{e2e_cmd}} | 35 | {{e2e_cmd}} |