Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1867838583 | ||
|
|
5a855cf5cb | ||
|
|
fa26f066b8 | ||
|
|
9e1e30c7be | ||
|
|
2cab970059 | ||
|
|
51605b09ea | ||
|
|
2da96a5fe0 | ||
|
|
40b6fc84d1 | ||
|
|
d0ccf7ab43 | ||
|
|
a1f1e94b79 | ||
|
|
3dfe0895e1 | ||
|
|
affe0c8553 | ||
|
|
03d1f1d79b | ||
|
|
c21d28c2cc | ||
|
|
74b359a8f5 | ||
|
|
cfe4c1e7c5 | ||
|
|
7d36cfc6ac | ||
|
|
ee375ccd2e | ||
|
|
4cf3b4aacd | ||
|
|
927b7813f6 | ||
|
|
f3800fea94 | ||
|
|
3a87156c5b | ||
|
|
f199b588bd | ||
|
|
d46add8a7e | ||
|
|
1662980fa2 | ||
|
|
6780d2b32b | ||
|
|
d85479a77c | ||
|
|
0b6b090d1f | ||
|
|
432872a810 | ||
|
|
badb04d99f | ||
|
|
69b175bf2f | ||
|
|
1569ab34d2 | ||
|
|
3a0db85929 | ||
|
|
fbbd4ac22b | ||
|
|
d6fe823eae | ||
|
|
85ccf9331e | ||
|
|
7f55625ec9 | ||
|
|
fe6cf4a651 | ||
|
|
e6559154c5 | ||
|
|
b9e7b91c43 | ||
|
|
b5efc787c1 | ||
|
|
45fd822e62 | ||
|
|
023e6d3788 | ||
|
|
5910765704 | ||
|
|
a5a69e8725 | ||
|
|
23e72dcf8d | ||
|
|
9dba1ccf7c | ||
|
|
d4ba21c460 | ||
|
|
5956724489 | ||
|
|
e143b650aa | ||
|
|
a4ce4ce085 | ||
|
|
602905fbcf | ||
|
|
3b8ff34915 | ||
|
|
1074718eeb | ||
|
|
e0c5466ee6 | ||
|
|
6b4634e8ce | ||
|
|
b7b6747126 | ||
|
|
bb5759aa0e | ||
|
|
4273223cff | ||
|
|
23cf44e584 | ||
|
|
f6fa064b1f | ||
|
|
9407e4a134 | ||
|
|
cfddd28be4 | ||
|
|
e69490f7b6 | ||
|
|
1f81d3a887 | ||
|
|
8b84c5aa6a |
@@ -1,14 +0,0 @@
|
||||
# Triage Findings
|
||||
|
||||
When the task is to triage review findings, follow this workflow:
|
||||
|
||||
1. Merge all findings into one list.
|
||||
2. Remove duplicates.
|
||||
3. Sort by severity: `critical` -> `warning` -> `info`.
|
||||
4. Renumber from 1 after sorting.
|
||||
5. Fix real issues with the smallest safe change.
|
||||
6. Add false positives to `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
|
||||
7. Add or update tests when behavior changes.
|
||||
8. Re-check the issue after each fix.
|
||||
|
||||
Use the repo-local `triage-findings` skill for the same workflow when running in Codex.
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
name: triage-findings
|
||||
description: Triage findings, fix real issues, and exclude false positives.
|
||||
---
|
||||
|
||||
# Triage Findings
|
||||
|
||||
## Use
|
||||
|
||||
直接輸入:`triage-findings 問題原始檔(文字或截圖)`
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Merge all findings.
|
||||
2. Sort by severity:
|
||||
- critical
|
||||
- warning
|
||||
- info
|
||||
3. Renumber from 1.
|
||||
4. Fix real issues.
|
||||
5. Put false positives into `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
|
||||
6. Add tests when behavior changes.
|
||||
|
||||
## Output Rules
|
||||
|
||||
- Keep the final list short.
|
||||
- Keep numbering contiguous.
|
||||
- Preserve file path, location, and fix.
|
||||
- When writing exclusions, prefer the original issue text over paraphrased rewrites.
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
name: triage-findings
|
||||
description: Merge code-review findings, sort and renumber them by severity, resolve real issues, and move false positives into exclusions.
|
||||
---
|
||||
|
||||
# Triage Findings
|
||||
|
||||
## When To Use
|
||||
|
||||
Use this skill when you receive multiple review findings, screenshots, comments, or issue lists that need to become one final triaged list.
|
||||
It is also used when some findings are false positives and should be moved into the exclusions list.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Collect all findings into one list.
|
||||
2. Merge duplicates into a single finding when they describe the same issue.
|
||||
3. Sort the final list by severity:
|
||||
- critical
|
||||
- warning
|
||||
- info
|
||||
4. Renumber the sorted list from 1 upward.
|
||||
5. Rewrite each finding concisely so the final list reads cleanly and consistently.
|
||||
6. If a finding is a false positive, do not keep it in the final list.
|
||||
7. Add false positives to the exclusions list using the existing schema in the repo or task context, and preserve the original finding wording as much as possible, including language and semantics.
|
||||
|
||||
## Resolution Flow
|
||||
|
||||
After the list is merged and ordered, resolve the remaining findings one by one.
|
||||
|
||||
1. Start from the highest severity item.
|
||||
2. Identify the root cause in the relevant file or context.
|
||||
3. Apply the smallest safe change that fixes the issue.
|
||||
4. Add or update tests when behavior changes.
|
||||
5. Re-check the issue after the change.
|
||||
6. If the item is confirmed false positive, move it to exclusions instead of changing code.
|
||||
7. Continue until the list is either fixed or explicitly excluded.
|
||||
|
||||
## Output Rules
|
||||
|
||||
- Keep the final findings list in severity order, then by any stable secondary order needed to make it readable.
|
||||
- Keep numbering contiguous after filtering and merging.
|
||||
- Preserve useful details like file path, location, and suggested fix.
|
||||
- Keep exclusions entries minimal and consistent with the project schema.
|
||||
- When writing exclusions, prefer the original issue text and language; only paraphrase if needed to fit the schema.
|
||||
- If the source already provides a severity or title, keep it unless it conflicts with the final ordering.
|
||||
@@ -1,4 +0,0 @@
|
||||
interface:
|
||||
display_name: "Triage Findings"
|
||||
short_description: "Triage, sort, fix, and exclude review findings"
|
||||
default_prompt: "Use $triage-findings to merge review findings, sort and renumber them by severity, resolve real issues one by one, and add false positives to exclusions."
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
name: triage-findings
|
||||
description: Triage findings, fix real issues, and exclude false positives.
|
||||
---
|
||||
|
||||
# Triage Findings
|
||||
|
||||
## Use
|
||||
|
||||
直接輸入:`triage-findings 問題原始檔(文字或截圖)`
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Merge all findings.
|
||||
2. Sort by severity:
|
||||
- critical
|
||||
- warning
|
||||
- info
|
||||
3. Renumber from 1.
|
||||
4. Fix real issues.
|
||||
5. Put false positives into `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
|
||||
6. Add tests when behavior changes.
|
||||
|
||||
## Output Rules
|
||||
|
||||
- Keep the final list short.
|
||||
- Keep numbering contiguous.
|
||||
- Preserve file path, location, and fix.
|
||||
- When writing exclusions, prefer the original issue text over paraphrased rewrites.
|
||||
@@ -14,9 +14,9 @@ jobs:
|
||||
with:
|
||||
GITEA_TOKEN: ${{ secrets.RUNNER_TOKEN }}
|
||||
GITEA_COMMENT_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }},${{ secrets.GEMINI_API_KEY_1 }},${{ secrets.GEMINI_API_KEY_2 }},${{ secrets.GEMINI_API_KEY_3 }},${{ secrets.GEMINI_API_KEY_4 }},${{ secrets.GEMINI_API_KEY_5 }},${{ secrets.GEMINI_API_KEY_6 }},${{ secrets.GEMINI_API_KEY_7 }},${{ secrets.GEMINI_API_KEY_8 }},${{ secrets.GEMINI_API_KEY_9 }},${{ secrets.GEMINI_API_KEY_10 }},${{ secrets.GEMINI_API_KEY_11 }},${{ secrets.GEMINI_API_KEY_12 }},${{ secrets.GEMINI_API_KEY_13 }},${{ secrets.GEMINI_API_KEY_14 }},${{ secrets.GEMINI_API_KEY_15 }},${{ secrets.GEMINI_API_KEY_16 }},${{ secrets.GEMINI_API_KEY_17 }},${{ secrets.GEMINI_API_KEY_18 }},${{ secrets.GEMINI_API_KEY_19 }}
|
||||
GEMINI_BASE_URL: https://generativelanguage.googleapis.com/v1beta
|
||||
GEMINI_MODEL: ${{ vars.GEMINI_MODEL }}
|
||||
OPENCODE_BASE_URL: ${{ vars.OPENCODE_BASE_URL }}
|
||||
OPENCODE_PROVIDER: ${{ vars.OPENCODE_PROVIDER }}
|
||||
OPENCODE_MODEL: ${{ vars.GEMINI_MODEL }}
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# Triage Findings
|
||||
|
||||
Use the triage-finding workflow for review issue lists:
|
||||
|
||||
1. Merge findings into one list.
|
||||
2. Remove duplicates.
|
||||
3. Sort by severity: `critical` -> `warning` -> `info`.
|
||||
4. Renumber from 1.
|
||||
5. Fix real issues with the smallest safe change.
|
||||
6. Put false positives into `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
|
||||
7. Add or update tests when behavior changes.
|
||||
8. Re-check after each fix.
|
||||
|
||||
The full reusable skill lives in `.claude/skills/triage-findings/SKILL.md`.
|
||||
@@ -1,14 +0,0 @@
|
||||
# Triage Findings
|
||||
|
||||
Use the triage-finding workflow for review issue lists:
|
||||
|
||||
1. Merge findings into one list.
|
||||
2. Remove duplicates.
|
||||
3. Sort by severity: `critical` -> `warning` -> `info`.
|
||||
4. Renumber from 1.
|
||||
5. Fix real issues with the smallest safe change.
|
||||
6. Put false positives into `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
|
||||
7. Add or update tests when behavior changes.
|
||||
8. Re-check after each fix.
|
||||
|
||||
The reusable skill lives in `.gemini/skills/triage-findings/SKILL.md`.
|
||||
@@ -1,16 +0,0 @@
|
||||
# Triage Findings
|
||||
|
||||
When the task is to triage review findings, follow this workflow:
|
||||
|
||||
1. Merge all findings into one list.
|
||||
2. Remove duplicates.
|
||||
3. Sort by severity: `critical` -> `warning` -> `info`.
|
||||
4. Renumber from 1 after sorting.
|
||||
5. Fix real issues with the smallest safe change.
|
||||
6. Add false positives to `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
|
||||
7. Add or update tests when behavior changes.
|
||||
8. Re-check the issue after each fix.
|
||||
|
||||
Use the repo-local `triage-findings` skill for the same workflow when running in Codex.
|
||||
|
||||
Trigger it with `/triage-findings`.
|
||||
@@ -1,14 +0,0 @@
|
||||
# Triage Findings
|
||||
|
||||
Use the triage-finding workflow for review issue lists:
|
||||
|
||||
1. Merge findings into one list.
|
||||
2. Remove duplicates.
|
||||
3. Sort by severity: `critical` -> `warning` -> `info`.
|
||||
4. Renumber from 1.
|
||||
5. Fix real issues with the smallest safe change.
|
||||
6. Put false positives into `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
|
||||
7. Add or update tests when behavior changes.
|
||||
8. Re-check after each fix.
|
||||
|
||||
The reusable skill lives in `.gemini/skills/triage-findings/SKILL.md`.
|
||||
+74
-19
@@ -4,6 +4,7 @@ set -euo pipefail
|
||||
|
||||
readonly LINE="=================================================="
|
||||
readonly SUBLINE="--------------------------------------------------"
|
||||
readonly RELEASES_PER_PAGE=10
|
||||
|
||||
section() {
|
||||
printf '\n%s\n%s\n%s\n' "$LINE" "$1" "$SUBLINE"
|
||||
@@ -53,13 +54,17 @@ latest_stable_version() {
|
||||
fi
|
||||
|
||||
printf '%s' "$release_json" | jq -r '
|
||||
if type == "array" then
|
||||
[ .[] | select(.tag_name? and (.tag_name | test("-beta\\.") | not)) | .tag_name ][0]
|
||||
// "v0.0.0"
|
||||
else
|
||||
"v0.0.0"
|
||||
end
|
||||
( if type == "array" then . else [] end )
|
||||
| [ .[]
|
||||
| select(.tag_name? and (.tag_name | test("-beta\\.") | not))
|
||||
| .tag_name
|
||||
| sub("^v"; "")
|
||||
| select(test("^[0-9]+(\\.[0-9]+)*$"))
|
||||
| split(".")
|
||||
| map(tonumber)
|
||||
]
|
||||
| if length == 0 then [0, 0, 0] else (sort | last) end
|
||||
| "\(.[0] // 0).\(.[1] // 0).\(.[2] // 0)"
|
||||
'
|
||||
}
|
||||
|
||||
@@ -124,13 +129,17 @@ calculate_version() {
|
||||
|
||||
printf '%s' "$release_json" | jq -r --arg is_beta "$is_beta" '
|
||||
def stable_version:
|
||||
if type == "array" then
|
||||
[ .[] | select(.tag_name? and (.tag_name | test("-beta\\.") | not)) | .tag_name ][0]
|
||||
// "v0.0.0"
|
||||
else
|
||||
"v0.0.0"
|
||||
end
|
||||
| sub("^v"; "");
|
||||
( if type == "array" then . else [] end )
|
||||
| [ .[]
|
||||
| select(.tag_name? and (.tag_name | test("-beta\\.") | not))
|
||||
| .tag_name
|
||||
| sub("^v"; "")
|
||||
| select(test("^[0-9]+(\\.[0-9]+)*$"))
|
||||
| split(".")
|
||||
| map(tonumber)
|
||||
]
|
||||
| if length == 0 then [0, 0, 0] else (sort | last) end
|
||||
| "\(.[0] // 0).\(.[1] // 0).\(.[2] // 0)";
|
||||
|
||||
def next_release($latest):
|
||||
($latest | split(".") | map(tonumber? // 0)) as $parts
|
||||
@@ -166,6 +175,56 @@ calculate_version() {
|
||||
'
|
||||
}
|
||||
|
||||
fetch_releases() {
|
||||
local base_url="$1"
|
||||
local -a auth_args=()
|
||||
|
||||
if [ -n "${RUNNER_TOKEN:-}" ] && [ "${RUNNER_TOKEN:-}" != "null" ]; then
|
||||
info "使用授權 token 取得 release" >&2
|
||||
auth_args=(-H "Authorization: token $RUNNER_TOKEN")
|
||||
else
|
||||
info "使用匿名請求取得 release" >&2
|
||||
fi
|
||||
|
||||
local page=1
|
||||
local combined="[]"
|
||||
local page_json count
|
||||
|
||||
while :; do
|
||||
if ! page_json="$(curl -fsS ${auth_args[@]+"${auth_args[@]}"} "${base_url}?limit=${RELEASES_PER_PAGE}&page=${page}")"; then
|
||||
fail "release API 請求失敗 (page=${page})"
|
||||
fi
|
||||
|
||||
# 空字串或 null 代表已無更多資料
|
||||
if [ -z "$page_json" ] || [ "$page_json" = "null" ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
if ! count="$(printf '%s' "$page_json" | jq 'if type == "array" then length else -1 end')"; then
|
||||
fail "release API 回傳資料無法解析 (page=${page})"
|
||||
fi
|
||||
|
||||
if [ "$count" = "-1" ]; then
|
||||
fail "release API 回傳非陣列資料 (page=${page})"
|
||||
fi
|
||||
|
||||
info "第 ${page} 頁取得 ${count} 筆 release" >&2
|
||||
|
||||
if ! combined="$(jq -n --argjson acc "$combined" --argjson page "$page_json" '$acc + $page')"; then
|
||||
fail "合併 release 資料失敗 (page=${page})"
|
||||
fi
|
||||
|
||||
# 不足一頁代表已取完
|
||||
if [ "$count" -lt "$RELEASES_PER_PAGE" ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
page=$((page + 1))
|
||||
done
|
||||
|
||||
printf '%s' "$combined"
|
||||
}
|
||||
|
||||
main() {
|
||||
section "參數檢查"
|
||||
|
||||
@@ -186,12 +245,8 @@ main() {
|
||||
RELEASE_URL="$GITEA_SERVER_URL/api/v1/repos/$GITEA_REPOSITORY/releases"
|
||||
info "RELEASE_URL=$RELEASE_URL"
|
||||
|
||||
if [ -n "${RUNNER_TOKEN:-}" ] && [ "${RUNNER_TOKEN:-}" != "null" ]; then
|
||||
info "使用授權 token 取得 release"
|
||||
RELEASE_JSON="$(curl -fsS -H "Authorization: token $RUNNER_TOKEN" "$RELEASE_URL")"
|
||||
else
|
||||
info "使用匿名請求取得 release"
|
||||
RELEASE_JSON="$(curl -fsS "$RELEASE_URL")"
|
||||
if ! RELEASE_JSON="$(fetch_releases "$RELEASE_URL")"; then
|
||||
fail "取得 release 資料失敗"
|
||||
fi
|
||||
|
||||
VERSION_INFO="$(calculate_version "$RELEASE_JSON" "$IS_BETA")"
|
||||
|
||||
+149
-44
@@ -33,7 +33,6 @@ assert_eq() {
|
||||
|
||||
make_mock_curl() {
|
||||
local bin_dir="$1"
|
||||
local response_file="$2"
|
||||
|
||||
cat >"$bin_dir/curl" <<'EOF'
|
||||
#!/bin/sh
|
||||
@@ -45,6 +44,26 @@ if [ "${FAKE_CURL_STATUS:-0}" != "0" ]; then
|
||||
exit "$FAKE_CURL_STATUS"
|
||||
fi
|
||||
|
||||
# 分頁模式:依 URL 上的 page=N 回傳對應的 page 檔,超出範圍回傳空陣列
|
||||
if [ -n "${FAKE_CURL_PAGES_DIR:-}" ]; then
|
||||
page=1
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
*page=*)
|
||||
page=${arg##*page=}
|
||||
page=${page%%&*}
|
||||
;;
|
||||
esac
|
||||
done
|
||||
page_file="$FAKE_CURL_PAGES_DIR/$page.json"
|
||||
if [ -f "$page_file" ]; then
|
||||
cat "$page_file"
|
||||
else
|
||||
printf '[]'
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cat "${FAKE_CURL_RESPONSE_FILE:?}"
|
||||
EOF
|
||||
chmod +x "$bin_dir/curl"
|
||||
@@ -55,42 +74,63 @@ make_mock_jq() {
|
||||
|
||||
cat >"$bin_dir/jq" <<'EOF'
|
||||
#!/bin/sh
|
||||
is_beta=""
|
||||
query=""
|
||||
python3 -c '
|
||||
import json, re, sys
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
-r)
|
||||
shift
|
||||
;;
|
||||
--arg)
|
||||
if [ "$2" = "is_beta" ]; then
|
||||
is_beta="$3"
|
||||
fi
|
||||
shift 3
|
||||
;;
|
||||
*)
|
||||
query="$1"
|
||||
shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
args = sys.argv[1:]
|
||||
named = {}
|
||||
use_stdin = True
|
||||
i = 0
|
||||
while i < len(args):
|
||||
a = args[i]
|
||||
if a == "-r":
|
||||
i += 1
|
||||
elif a == "-n":
|
||||
use_stdin = False
|
||||
i += 1
|
||||
elif a in ("--arg", "--argjson"):
|
||||
value = args[i + 2]
|
||||
named[args[i + 1]] = json.loads(value) if a == "--argjson" else value
|
||||
i += 3
|
||||
else:
|
||||
i += 1
|
||||
|
||||
python3 -c 'import json, sys
|
||||
query = sys.argv[1]
|
||||
is_beta = sys.argv[2]
|
||||
payload = sys.stdin.read()
|
||||
try:
|
||||
data = json.loads(payload)
|
||||
except Exception:
|
||||
payload = sys.stdin.read() if use_stdin else ""
|
||||
|
||||
def parse_or_exit(text):
|
||||
try:
|
||||
return json.loads(text)
|
||||
except Exception:
|
||||
sys.exit(4)
|
||||
|
||||
# merge 模式:$acc + $page
|
||||
if "acc" in named and "page" in named:
|
||||
sys.stdout.write(json.dumps(named["acc"] + named["page"]))
|
||||
sys.exit(0)
|
||||
|
||||
# count 模式:陣列長度,非陣列回 -1
|
||||
if "is_beta" not in named:
|
||||
data = parse_or_exit(payload)
|
||||
sys.stdout.write(str(len(data)) if isinstance(data, list) else "-1")
|
||||
sys.exit(0)
|
||||
|
||||
# calculate 模式
|
||||
data = parse_or_exit(payload)
|
||||
is_beta = named.get("is_beta", "")
|
||||
|
||||
def to_tuple(ver):
|
||||
nums = []
|
||||
for part in ver.split("."):
|
||||
try:
|
||||
nums.append(int(part))
|
||||
except Exception:
|
||||
nums.append(0)
|
||||
while len(nums) < 3:
|
||||
nums.append(0)
|
||||
return tuple(nums[:3])
|
||||
|
||||
def next_version(latest):
|
||||
parts = [int(p or 0) for p in latest.split(".")]
|
||||
while len(parts) < 3:
|
||||
parts.append(0)
|
||||
major, minor, patch = parts[:3]
|
||||
major, minor, patch = to_tuple(latest)
|
||||
patch += 1
|
||||
if patch >= 10:
|
||||
patch = 0
|
||||
@@ -98,18 +138,17 @@ def next_version(latest):
|
||||
if minor >= 10:
|
||||
minor = 0
|
||||
major += 1
|
||||
return f"{major}.{minor}.{patch}"
|
||||
return "{}.{}.{}".format(major, minor, patch)
|
||||
|
||||
def beta_max(data, prefix):
|
||||
def beta_max(items, prefix):
|
||||
values = []
|
||||
for item in data:
|
||||
for item in items:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
tag = item.get("tag_name")
|
||||
if isinstance(tag, str) and tag.startswith(prefix):
|
||||
suffix = tag[len(prefix):]
|
||||
try:
|
||||
values.append(int(suffix))
|
||||
values.append(int(tag[len(prefix):]))
|
||||
except Exception:
|
||||
pass
|
||||
return max(values) if values else 0
|
||||
@@ -117,22 +156,24 @@ def beta_max(data, prefix):
|
||||
if not isinstance(data, list):
|
||||
base = "0.0.0"
|
||||
else:
|
||||
base = "0.0.0"
|
||||
stable = []
|
||||
for item in data:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
tag = item.get("tag_name")
|
||||
if isinstance(tag, str) and "-beta." not in tag:
|
||||
base = tag[1:] if tag.startswith("v") else tag
|
||||
break
|
||||
ver = tag[1:] if tag.startswith("v") else tag
|
||||
if re.match(r"^[0-9]+(\.[0-9]+)*$", ver):
|
||||
stable.append(ver)
|
||||
base = max(stable, key=to_tuple) if stable else "0.0.0"
|
||||
|
||||
next_ver = next_version(base)
|
||||
if is_beta == "true":
|
||||
beta = beta_max(data, f"v{next_ver}-beta.") + 1
|
||||
sys.stdout.write(f"{base}\t{next_ver}-beta.{beta}")
|
||||
beta = beta_max(data, "v{}-beta.".format(next_ver)) + 1
|
||||
sys.stdout.write("{}\t{}-beta.{}".format(base, next_ver, beta))
|
||||
else:
|
||||
sys.stdout.write(f"{base}\t{next_ver}")
|
||||
' "$query" "$is_beta"
|
||||
sys.stdout.write("{}\t{}".format(base, next_ver))
|
||||
' "$@"
|
||||
EOF
|
||||
chmod +x "$bin_dir/jq"
|
||||
}
|
||||
@@ -281,6 +322,69 @@ test_null_release_payload() {
|
||||
assert_eq "version=0.0.1" "$(cat "$output_file")" "null release payload"
|
||||
}
|
||||
|
||||
test_pagination_collects_all_pages() {
|
||||
local workdir
|
||||
local pages_dir
|
||||
local bin_dir
|
||||
local output_file
|
||||
local stdout_file
|
||||
local stderr_file
|
||||
local curl_args
|
||||
local i
|
||||
|
||||
workdir="$(mktemp -d)"
|
||||
CLEANUP_PATHS+=("$workdir")
|
||||
pages_dir="$workdir/pages"
|
||||
mkdir -p "$pages_dir"
|
||||
|
||||
# 第 1 頁滿載 10 筆,且全部是 beta(重現「最新一整頁都是 beta」的情境)
|
||||
{
|
||||
printf '['
|
||||
for i in $(seq 1 10); do
|
||||
if [ "$i" -gt 1 ]; then printf ','; fi
|
||||
printf '{"tag_name":"v1.9.0-beta.%s"}' "$i"
|
||||
done
|
||||
printf ']'
|
||||
} >"$pages_dir/1.json"
|
||||
|
||||
# 第 2 頁才出現穩定版,且故意讓最大值不是第一筆(驗證 semver 取最大)
|
||||
cat >"$pages_dir/2.json" <<'JSON'
|
||||
[
|
||||
{"tag_name":"v1.2.3"},
|
||||
{"tag_name":"v1.3.1"},
|
||||
{"tag_name":"v1.2.9"}
|
||||
]
|
||||
JSON
|
||||
|
||||
bin_dir="$workdir/bin"
|
||||
mkdir -p "$bin_dir"
|
||||
make_mock_curl "$bin_dir"
|
||||
make_mock_jq "$bin_dir"
|
||||
|
||||
output_file="$workdir/github_output"
|
||||
stdout_file="$workdir/stdout"
|
||||
stderr_file="$workdir/stderr"
|
||||
|
||||
FAKE_CURL_STATUS=0 \
|
||||
FAKE_CURL_PAGES_DIR="$pages_dir" \
|
||||
FAKE_CURL_LOG_FILE="$workdir/curl_args" \
|
||||
PATH="$bin_dir:$PATH" \
|
||||
GITEA_SERVER_URL="https://gitea.example.com" \
|
||||
GITEA_REPOSITORY="org/repo" \
|
||||
IS_BETA="false" \
|
||||
GITHUB_OUTPUT="$output_file" \
|
||||
bash "$ROOT_DIR/entrypoint.sh" >"$stdout_file" 2>"$stderr_file"
|
||||
|
||||
# 穩定版在第 2 頁、最大為 1.3.1 -> 下一版 1.3.2
|
||||
assert_eq "version=1.3.2" "$(cat "$output_file")" "pagination + semver max output"
|
||||
|
||||
# 確認確實翻到第 2 頁
|
||||
curl_args="$workdir/curl_args"
|
||||
if ! grep -q 'page=2' "$curl_args"; then
|
||||
fail "pagination: expected a request for page=2"
|
||||
fi
|
||||
}
|
||||
|
||||
test_token_auth_header() {
|
||||
local workdir
|
||||
local response_file
|
||||
@@ -392,6 +496,7 @@ test_beta_release_flow
|
||||
test_empty_release_list
|
||||
test_only_beta_releases
|
||||
test_null_release_payload
|
||||
test_pagination_collects_all_pages
|
||||
test_token_auth_header
|
||||
test_malformed_release_payload
|
||||
test_curl_failure
|
||||
|
||||
Reference in New Issue
Block a user