Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c54e23b0a | |||
| a6fdb07302 | |||
| a9e957db63 | |||
| 8e278659c0 | |||
| 6192b2099d | |||
| 83da83560c | |||
| 3dc683b58d | |||
| 28e240ce56 | |||
| e6eaa224c6 | |||
| 739735ffc1 | |||
| d17d2614f8 | |||
| f7686a3b2f | |||
| 325bedfc34 | |||
| 76edcc83f0 | |||
| ac2ea2a9a5 | |||
| ffaea10f44 | |||
| cd0edec737 | |||
| 82d3f78536 | |||
| 9a90a92c0f | |||
| f7e5a13fcd | |||
| e125464a3c |
@@ -0,0 +1,14 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
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` as a top-level JSON array, preserving the original wording, language, and semantics as much as possible. Do not wrap the array in `exclusions` or `excluded_findings`.
|
||||
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, always output a top-level JSON array.
|
||||
- When writing exclusions, prefer the original issue text over paraphrased rewrites.
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
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 as a top-level JSON array in `.gitea/ai-review/exclusions.json`, and preserve the original finding wording as much as possible, including language and semantics. Do not wrap the array in `exclusions` or `excluded_findings`.
|
||||
|
||||
## 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, always output a top-level JSON array.
|
||||
- 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.
|
||||
@@ -0,0 +1,4 @@
|
||||
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 `.gitea/ai-review/exclusions.json` as a top-level JSON array."
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
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` as a top-level JSON array, preserving the original wording, language, and semantics as much as possible. Do not wrap the array in `exclusions` or `excluded_findings`.
|
||||
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, always output a top-level JSON array.
|
||||
- When writing exclusions, prefer the original issue text over paraphrased rewrites.
|
||||
@@ -0,0 +1,110 @@
|
||||
[
|
||||
{
|
||||
"level": "critical",
|
||||
"role": "Aria",
|
||||
"location": "Dockerfile",
|
||||
"suggestion": "檔案末尾應包含一個空行,以符合 POSIX 規範並避免工具處理問題。"
|
||||
},
|
||||
{
|
||||
"level": "warning",
|
||||
"role": "Zara",
|
||||
"location": "Dockerfile:10",
|
||||
"suggestion": "在 `apt-get install` 命令中加入 `--no-install-recommends` 參數是一個很好的優化,它能減少安裝不必要的推薦套件,進而縮小 Docker 映像檔的體積。較小的映像檔能加快建置、推送與拉取的速度,並節省儲存空間。"
|
||||
},
|
||||
{
|
||||
"level": "warning",
|
||||
"role": "Maya",
|
||||
"location": "entrypoint.sh:28-34",
|
||||
"suggestion": "新增了 `require_index` 函數來驗證 `RELEASE_INDEX` 必須是非負整數。這是一個很好的改進,但需要為此新增測試案例,例如傳入非數字字串或負數,確保腳本能正確地捕捉錯誤並退出。"
|
||||
},
|
||||
{
|
||||
"level": "warning",
|
||||
"role": "Maya",
|
||||
"location": "entrypoint.sh:83-87",
|
||||
"suggestion": "新增了檢查解壓縮後是否找到 `.nupkg` 檔案的邏輯。這是一個重要的邊界條件處理,請為此新增測試案例,確保當成品中不包含 `.nupkg` 檔案時,腳本能正確地報錯並退出。"
|
||||
},
|
||||
{
|
||||
"level": "info",
|
||||
"role": "Zara",
|
||||
"location": "entrypoint.sh:3",
|
||||
"suggestion": "使用 `set -Eeuo pipefail` 是一個良好的 Shell 腳本實踐。它能確保腳本在遇到錯誤時立即終止 (`-e`)、未定義變數時報錯 (`-u`),並在管道命令中捕捉錯誤 (`-o pipefail`)。這提升了腳本的健壯性和可靠性,間接避免因錯誤導致的資源浪費。"
|
||||
},
|
||||
{
|
||||
"level": "info",
|
||||
"role": "Zara",
|
||||
"location": "entrypoint.sh:40",
|
||||
"suggestion": "在 `curl` 命令中加入 `-f` (fail fast) 參數,可以在 HTTP 錯誤時立即終止腳本,避免處理無效資料,提高腳本的穩定性。同時,`-L` (follow redirects) 參數確保能正確處理重定向,提升可靠性。"
|
||||
},
|
||||
{
|
||||
"level": "info",
|
||||
"role": "Zara",
|
||||
"location": "entrypoint.sh:54",
|
||||
"suggestion": "在 `unzip` 命令中使用 `-q` (quiet) 參數可以抑制詳細輸出,減少控制台 I/O,使日誌更簡潔。這是一個輕微的效能優化和日誌清理。"
|
||||
},
|
||||
{
|
||||
"level": "info",
|
||||
"role": "Aria",
|
||||
"location": "entrypoint.sh",
|
||||
"suggestion": "腳本內部變數從 `UPPER_SNAKE_CASE` (例如 `RELEASE_HEADER`) 變更為 `snake_case` (例如 `release_header`),這與 shell 腳本的常見慣例更一致,有助於區分環境變數和內部變數。此為風格改善。"
|
||||
},
|
||||
{
|
||||
"level": "info",
|
||||
"role": "Maya",
|
||||
"location": "entrypoint.sh",
|
||||
"suggestion": "此腳本經過重構後,錯誤處理和參數驗證變得更加健壯(例如使用 `set -Eeuo pipefail` 和自定義驗證函數)。為了確保這些改進的穩定性,建議導入一個輕量級的 shell 腳本測試框架(如 `bats` 或 `shunit2`),為所有關鍵函數和端到端流程編寫單元測試和整合測試,以覆蓋各種成功和失敗情境。"
|
||||
},
|
||||
{
|
||||
"level": "critical",
|
||||
"role": "Rex",
|
||||
"location": "entrypoint.sh:L71 (舊版)",
|
||||
"suggestion": "舊版腳本直接使用 `unzip \"$RELEASE_NAME\" -d output` 解壓縮檔案,存在 Zip Slip (路徑遍歷) 漏洞風險。惡意壓縮檔可能包含 `../../etc/passwd` 等路徑,導致任意檔案被覆蓋或寫入。新版腳本新增了 `validate_release_archive` 函數來檢查壓縮檔內容,有效防範此類攻擊,這是非常關鍵的安全改進。"
|
||||
},
|
||||
{
|
||||
"level": "critical",
|
||||
"role": "Rex",
|
||||
"location": "entrypoint.sh:L90 (舊版)",
|
||||
"suggestion": "舊版腳本在推送 NuGet 套件時使用了 `--allow-insecure-connections` 旗標,這意味著 `RUNNER_TOKEN` 可能會透過未加密的 HTTP 連線傳輸,容易被中間人攻擊竊取。新版腳本移除了此旗標,強制使用安全的 HTTPS 連線,這是非常重要的安全強化。應始終優先使用加密連線來傳輸敏感資料。"
|
||||
},
|
||||
{
|
||||
"level": "critical",
|
||||
"role": "Rex",
|
||||
"location": "entrypoint.sh:L19-L26 (舊版), L69 (舊版)",
|
||||
"suggestion": "舊版腳本直接將敏感的 `RUNNER_TOKEN` 變數值輸出到標準輸出,這可能導致令牌洩漏到日誌或執行環境中。新版腳本透過 `require_value` 函數對敏感變數進行遮罩處理,是一個重要的安全改進。建議所有包含敏感資訊(如 API 金鑰、密碼、令牌)的變數在輸出時都應進行遮罩或避免直接輸出。"
|
||||
},
|
||||
{
|
||||
"level": "warning",
|
||||
"role": "Aria",
|
||||
"location": "a/entrypoint.sh:3,6,27,30,32,35,40,43,45,48,50,53,55,58,60,63,66,68,71,74",
|
||||
"suggestion": "腳本中的區塊標題格式不一致且手動維護。建議使用一個專門的函數來生成標準化的區塊標題,以提高一致性與可讀性。"
|
||||
},
|
||||
{
|
||||
"level": "warning",
|
||||
"role": "Aria",
|
||||
"location": "a/entrypoint.sh:44,47",
|
||||
"suggestion": "將變數內容傳遞給 `jq` 時,使用 `printf '%s'` 而非 `echo` 更為安全,因為 `echo` 在不同 shell 中對特殊字元(如反斜線)的處理可能不一致。"
|
||||
},
|
||||
{
|
||||
"level": "warning",
|
||||
"role": "Aria",
|
||||
"location": "a/entrypoint.sh:62",
|
||||
"suggestion": "`dotnet nuget push \"output/*.nupkg\"` 依賴 shell 的萬用字元展開,這在沒有匹配檔案或匹配檔案過多時可能導致錯誤。建議使用 `find` 命令安全地列出所有 `.nupkg` 檔案,並逐一推送,以提高腳本的健壯性。"
|
||||
},
|
||||
{
|
||||
"level": "info",
|
||||
"role": "Leo",
|
||||
"location": "entrypoint.sh",
|
||||
"suggestion": "腳本中包含多處硬編碼的繁體中文錯誤訊息(例如 `錯誤:...`)。若團隊成員或使用者可能來自不同語言背景,考慮將錯誤訊息國際化(例如,透過環境變數或外部設定檔提供不同語言版本),或統一使用英文以提高通用性,有助於長期維護和跨文化協作。"
|
||||
},
|
||||
{
|
||||
"level": "info",
|
||||
"role": "Aria",
|
||||
"location": "a/Dockerfile:5",
|
||||
"suggestion": "在 Dockerfile 或腳本中,建議使用 `apt-get` 而非 `apt` 進行套件管理操作,因為 `apt-get` 在非互動式環境中行為更穩定且可預測。"
|
||||
},
|
||||
{
|
||||
"level": "info",
|
||||
"role": "Aria",
|
||||
"location": "a/Dockerfile:9",
|
||||
"suggestion": "空行不應包含額外的縮排空格,以保持格式一致性。"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -1,10 +1,11 @@
|
||||
name: CD
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
version:
|
||||
name: "CD > 計算版本號"
|
||||
name: 計算版本號
|
||||
runs-on: ubuntu
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
@@ -13,14 +14,14 @@ jobs:
|
||||
id: version
|
||||
uses: https://gitea.jsc.idv.tw/actions/calculate-version@${{ vars.ACTION_CALCULATE_VERSION }}
|
||||
release:
|
||||
name: "CD > 發布專案"
|
||||
name: 發布專案
|
||||
runs-on: ubuntu
|
||||
needs: version
|
||||
steps:
|
||||
- name: 發布專案
|
||||
uses: akkuman/gitea-release-action@${{ vars.ACTION_RELEASE_VERSION }}
|
||||
with:
|
||||
tag_name: "v${{ needs.version.outputs.version }}"
|
||||
tag_name: v${{ needs.version.outputs.version }}
|
||||
- name: 清理成品
|
||||
uses: https://gitea.jsc.idv.tw/actions/cleanup-release@${{ vars.ACTION_CLEANUP_RELEASE_VERSION }}
|
||||
with:
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
name: AI
|
||||
on:
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- master
|
||||
types: [opened, synchronize]
|
||||
jobs:
|
||||
code-review:
|
||||
name: Code Review
|
||||
runs-on: ubuntu
|
||||
steps:
|
||||
- name: AI Code Review
|
||||
uses: https://gitea.jsc.idv.tw/actions/code-review@${{ vars.ACTION_CODE_REVIEW_VERSION }}
|
||||
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 }}
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
@@ -0,0 +1,14 @@
|
||||
# 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`.
|
||||
@@ -0,0 +1,14 @@
|
||||
# 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` as a top-level JSON array, preserving the original wording, language, and semantics as much as possible. Do not wrap the array in `exclusions` or `excluded_findings`.
|
||||
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`.
|
||||
@@ -0,0 +1,16 @@
|
||||
# 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`.
|
||||
+6
-5
@@ -1,12 +1,13 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0
|
||||
|
||||
# 更新並安裝必要的工具後清理暫存檔案以減小映像檔大小
|
||||
RUN apt update \
|
||||
&& apt install -y curl jq unzip \
|
||||
# 安裝必要工具,並清理 apt 快取以縮小映像檔
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl jq unzip \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# 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`.
|
||||
+4
-2
@@ -8,6 +8,8 @@ inputs:
|
||||
RELEASE_INDEX:
|
||||
description: '要推送的版本中第幾個成品,預設為 0'
|
||||
default: 0
|
||||
RUNNER_TOKEN:
|
||||
description: '存取權杖'
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
@@ -16,5 +18,5 @@ runs:
|
||||
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||
RELEASE_VERSION: ${{ inputs.RELEASE_VERSION }}
|
||||
RELEASE_INDEX: ${{ inputs.RELEASE_INDEX }}
|
||||
RUNNER_TOKEN: ${{ secrets.RUNNER_TOKEN }}
|
||||
NUGET_AUTHOR: ${{ gitea.author }}
|
||||
RUNNER_TOKEN: ${{ inputs.RUNNER_TOKEN || secrets.RUNNER_TOKEN || secrets.GITEA_TOKEN }}
|
||||
NUGET_AUTHOR: ${{ gitea.repository_owner }}
|
||||
+95
-42
@@ -1,73 +1,126 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=================================================="
|
||||
set -Eeuo pipefail
|
||||
|
||||
echo "參數檢查"
|
||||
section() {
|
||||
printf '\n==================================================\n'
|
||||
printf '%s\n' "$1"
|
||||
printf -- '--------------------------------------------------\n'
|
||||
}
|
||||
|
||||
echo "--------------------------------------------------"
|
||||
require_value() {
|
||||
local name="$1"
|
||||
local value="$2"
|
||||
local display_value="$value"
|
||||
|
||||
# 顯示 GITEA_SERVER_URL 參數,並檢查是否為空或 "null",如果是則輸出錯誤訊息並退出
|
||||
echo "GITEA_SERVER_URL=$GITEA_SERVER_URL" && ([ -z "$GITEA_SERVER_URL" ] || [ "$GITEA_SERVER_URL" = "null" ]) && exit 1
|
||||
case "$name" in
|
||||
*TOKEN*|*SECRET*|*PASSWORD*|*PASS*|*KEY*)
|
||||
display_value="***"
|
||||
;;
|
||||
esac
|
||||
|
||||
# 顯示 GITEA_REPOSITORY 參數,並檢查是否為空或 "null",如果是則輸出錯誤訊息並退出
|
||||
echo "GITEA_REPOSITORY=$GITEA_REPOSITORY" && ([ -z "$GITEA_REPOSITORY" ] || [ "$GITEA_REPOSITORY" = "null" ]) && exit 1
|
||||
printf '%s=%s\n' "$name" "$display_value"
|
||||
|
||||
# 顯示 RELEASE_VERSION 參數,並檢查是否為空或 "null",如果是則輸出錯誤訊息並退出
|
||||
echo "RELEASE_VERSION=$RELEASE_VERSION" && ([ -z "$RELEASE_VERSION" ] || [ "$RELEASE_VERSION" = "null" ]) && exit 1
|
||||
if [ -z "$value" ] || [ "$value" = "null" ]; then
|
||||
printf '錯誤:%s 不可為空\n' "$name" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 顯示 RELEASE_INDEX 參數,並檢查是否為空或 "null",如果是則輸出錯誤訊息並退出
|
||||
echo "RELEASE_INDEX=$RELEASE_INDEX" && ([ -z "$RELEASE_INDEX" ] || [ "$RELEASE_INDEX" = "null" ]) && exit 1
|
||||
validate_release_archive() {
|
||||
local archive="$1"
|
||||
local archive_entries
|
||||
local entry
|
||||
local normalized_entry
|
||||
|
||||
# 顯示 RUNNER_TOKEN 參數,並檢查是否為空或 "null",如果是則輸出錯誤訊息並退出
|
||||
echo "RUNNER_TOKEN=$RUNNER_TOKEN" && ([ -z "$RUNNER_TOKEN" ] || [ "$RUNNER_TOKEN" = "null" ]) && exit 1
|
||||
if ! archive_entries="$(unzip -Z1 "$archive")"; then
|
||||
printf '錯誤:無法讀取壓縮檔:%s\n' "$archive" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 顯示 NUGET_AUTHOR 參數,並檢查是否為空或 "null",如果是則輸出錯誤訊息並退出
|
||||
echo "NUGET_AUTHOR=$NUGET_AUTHOR" && ([ -z "$NUGET_AUTHOR" ] || [ "$NUGET_AUTHOR" = "null" ]) && exit 1
|
||||
while IFS= read -r entry; do
|
||||
[ -z "$entry" ] && continue
|
||||
|
||||
echo "=================================================="
|
||||
normalized_entry="${entry//\\//}"
|
||||
|
||||
echo "取得成品連結"
|
||||
case "$normalized_entry" in
|
||||
/*|../*|*/../*|*/..|..)
|
||||
printf '錯誤:壓縮檔包含不安全路徑:%s\n' "$entry" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done <<EOF
|
||||
$archive_entries
|
||||
EOF
|
||||
}
|
||||
|
||||
echo "--------------------------------------------------"
|
||||
require_index() {
|
||||
local value="$1"
|
||||
|
||||
# 組合 RELEASE_HEADER 參數,並顯示出來
|
||||
RELEASE_HEADER="Authorization: token $RUNNER_TOKEN" && echo "RELEASE_HEADER=$RELEASE_HEADER"
|
||||
if [[ ! "$value" =~ ^[0-9]+$ ]]; then
|
||||
printf '錯誤:RELEASE_INDEX 必須是非負整數\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 組合 RELEASE_URL 參數,並顯示出來
|
||||
RELEASE_URL="$GITEA_SERVER_URL/api/v1/repos/$GITEA_REPOSITORY/releases/tags/v$RELEASE_VERSION" && echo "RELEASE_URL=$RELEASE_URL"
|
||||
section "參數檢查"
|
||||
require_value "GITEA_SERVER_URL" "${GITEA_SERVER_URL:-}"
|
||||
require_value "GITEA_REPOSITORY" "${GITEA_REPOSITORY:-}"
|
||||
require_value "RELEASE_VERSION" "${RELEASE_VERSION:-}"
|
||||
require_value "RELEASE_INDEX" "${RELEASE_INDEX:-}"
|
||||
require_index "${RELEASE_INDEX:-}"
|
||||
require_value "RUNNER_TOKEN" "${RUNNER_TOKEN:-}"
|
||||
require_value "NUGET_AUTHOR" "${NUGET_AUTHOR:-}"
|
||||
|
||||
# 取得成品資訊
|
||||
RELEASE_JSON="$(curl -s -H "$RELEASE_HEADER" "$RELEASE_URL")"
|
||||
section "取得成品連結"
|
||||
|
||||
# 從成品資訊取得名稱與連結
|
||||
RELEASE_NAME="$(echo "$RELEASE_JSON" | jq -r ".assets[$RELEASE_INDEX].name")" && echo "RELEASE_NAME=$RELEASE_NAME"
|
||||
RELEASE_URL="$(echo "$RELEASE_JSON" | jq -r ".assets[$RELEASE_INDEX].browser_download_url")" && echo "RELEASE_URL=$RELEASE_URL"
|
||||
release_header="Authorization: token $RUNNER_TOKEN"
|
||||
release_api_url="$GITEA_SERVER_URL/api/v1/repos/$GITEA_REPOSITORY/releases/tags/v$RELEASE_VERSION"
|
||||
|
||||
echo "=================================================="
|
||||
printf 'RELEASE_API_URL=%s\n' "$release_api_url"
|
||||
|
||||
echo "下載成品"
|
||||
release_json="$(curl -fsSL -H "$release_header" "$release_api_url")"
|
||||
release_asset_path=".assets[$RELEASE_INDEX]"
|
||||
|
||||
echo "--------------------------------------------------"
|
||||
release_name="$(printf '%s' "$release_json" | jq -r "$release_asset_path.name")"
|
||||
require_value "RELEASE_NAME" "$release_name"
|
||||
|
||||
curl -H "$RELEASE_HEADER" "$RELEASE_URL" -o "$RELEASE_NAME"
|
||||
release_url="$(printf '%s' "$release_json" | jq -r "$release_asset_path.browser_download_url")"
|
||||
require_value "RELEASE_URL" "$release_url"
|
||||
|
||||
echo "=================================================="
|
||||
section "下載成品"
|
||||
|
||||
echo "解壓縮成品"
|
||||
curl -fsSL -H "$release_header" "$release_url" -o "$release_name"
|
||||
printf '已下載:%s\n' "$release_name"
|
||||
|
||||
echo "--------------------------------------------------"
|
||||
section "解壓縮成品"
|
||||
|
||||
unzip "$RELEASE_NAME" -d output
|
||||
rm -rf output
|
||||
mkdir -p output
|
||||
validate_release_archive "$release_name"
|
||||
unzip -q "$release_name" -d output
|
||||
printf '已解壓縮到:%s\n' "output"
|
||||
|
||||
echo "=================================================="
|
||||
section "推送 NUGET 套件"
|
||||
|
||||
echo "推送 NUGET 套件"
|
||||
nuget_source="$GITEA_SERVER_URL/api/packages/$NUGET_AUTHOR/nuget/index.json"
|
||||
printf 'NUGET_SOURCE=%s\n' "$nuget_source"
|
||||
|
||||
echo "--------------------------------------------------"
|
||||
mapfile -t nuget_packages < <(find output -type f -name '*.nupkg' | sort)
|
||||
|
||||
# 組合 NUGET_SOURCE 參數,並顯示出來
|
||||
NUGET_SOURCE="$GITEA_SERVER_URL/api/packages/$NUGET_AUTHOR/nuget/index.json" && echo "NUGET_SOURCE=$NUGET_SOURCE"
|
||||
if [ "${#nuget_packages[@]}" -eq 0 ]; then
|
||||
printf '錯誤:找不到 .nupkg 檔案\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dotnet nuget push "output/*.nupkg" --source "$NUGET_SOURCE" --api-key "$RUNNER_TOKEN" --skip-duplicate --allow-insecure-connections
|
||||
for nuget_package in "${nuget_packages[@]}"; do
|
||||
printf 'NUGET_PACKAGE=%s\n' "$nuget_package"
|
||||
|
||||
echo "=================================================="
|
||||
dotnet nuget push "$nuget_package" \
|
||||
--source "$nuget_source" \
|
||||
--api-key "$RUNNER_TOKEN" \
|
||||
--skip-duplicate \
|
||||
--allow-insecure-connections
|
||||
done
|
||||
|
||||
section "完成"
|
||||
|
||||
Reference in New Issue
Block a user