Compare commits
6 Commits
0579c2dc97
...
1259495cbb
| Author | SHA1 | Date |
|---|---|---|
|
|
1259495cbb | |
|
|
d4fa498a4a | |
|
|
8445c6a363 | |
|
|
ebd0f94053 | |
|
|
40fa747a94 | |
|
|
e5db752c55 |
|
|
@ -50,14 +50,6 @@ build_mirror_path() {
|
||||||
printf '%s/%s/%s.git\n' "${mirror_root}" "${owner}" "${repo_name}"
|
printf '%s/%s/%s.git\n' "${mirror_root}" "${owner}" "${repo_name}"
|
||||||
}
|
}
|
||||||
|
|
||||||
build_mirror_lock_path() {
|
|
||||||
local mirror_root=$1
|
|
||||||
local owner=$2
|
|
||||||
local repo_name=$3
|
|
||||||
|
|
||||||
printf '%s.lock\n' "$(build_mirror_path "${mirror_root}" "${owner}" "${repo_name}")"
|
|
||||||
}
|
|
||||||
|
|
||||||
build_job_workspace_root() {
|
build_job_workspace_root() {
|
||||||
local workspace_root=$1
|
local workspace_root=$1
|
||||||
local owner=$2
|
local owner=$2
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,243 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import unicodedata
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
LABEL_BG = "#555555"
|
||||||
|
DEFAULT_TOTAL_COLOR = "#1f6feb"
|
||||||
|
DEFAULT_FILES_COLOR = "#2da44e"
|
||||||
|
DEFAULT_LANGUAGE_COUNT_COLOR = "#8250df"
|
||||||
|
TEXT_COLOR = "#ffffff"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LanguageStat:
|
||||||
|
lang_id: str
|
||||||
|
display_name: str
|
||||||
|
code_lines: int
|
||||||
|
formatted_lines: str
|
||||||
|
file_count: int
|
||||||
|
color: str
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--output-dir", required=True)
|
||||||
|
parser.add_argument("--readme-path", required=True)
|
||||||
|
parser.add_argument("--repo", required=True)
|
||||||
|
parser.add_argument("--generated-by", required=True)
|
||||||
|
parser.add_argument("--updated-at", required=True)
|
||||||
|
parser.add_argument("--total-color", default=DEFAULT_TOTAL_COLOR)
|
||||||
|
parser.add_argument("--files-color", default=DEFAULT_FILES_COLOR)
|
||||||
|
parser.add_argument("--language-count-color", default=DEFAULT_LANGUAGE_COUNT_COLOR)
|
||||||
|
parser.add_argument("--total-code", required=True, type=int)
|
||||||
|
parser.add_argument("--formatted-total-code", required=True)
|
||||||
|
parser.add_argument("--total-files", required=True, type=int)
|
||||||
|
parser.add_argument("--formatted-total-files", required=True)
|
||||||
|
parser.add_argument("--language-count", required=True, type=int)
|
||||||
|
parser.add_argument("--exclude-dirs", required=True)
|
||||||
|
parser.add_argument("--min-lines-threshold", required=True, type=int)
|
||||||
|
parser.add_argument("--lang-summary", required=True)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def escape_xml(value: str) -> str:
|
||||||
|
return (
|
||||||
|
value.replace("&", "&")
|
||||||
|
.replace("<", "<")
|
||||||
|
.replace(">", ">")
|
||||||
|
.replace('"', """)
|
||||||
|
.replace("'", "'")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def display_units(value: str) -> int:
|
||||||
|
units = 0
|
||||||
|
for char in value:
|
||||||
|
if unicodedata.east_asian_width(char) in {"F", "W"}:
|
||||||
|
units += 2
|
||||||
|
else:
|
||||||
|
units += 1
|
||||||
|
return units
|
||||||
|
|
||||||
|
|
||||||
|
def text_width(value: str) -> int:
|
||||||
|
return display_units(value) * 7 + 20
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_color(value: str, fallback: str) -> str:
|
||||||
|
if not value:
|
||||||
|
return fallback
|
||||||
|
return value if value.startswith("#") else f"#{value}"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_language_summary(path: Path) -> List[LanguageStat]:
|
||||||
|
stats: List[LanguageStat] = []
|
||||||
|
|
||||||
|
if not path.exists():
|
||||||
|
return stats
|
||||||
|
|
||||||
|
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
parts = line.split("|")
|
||||||
|
if len(parts) < 6:
|
||||||
|
continue
|
||||||
|
|
||||||
|
while len(parts) < 7:
|
||||||
|
parts.append("")
|
||||||
|
|
||||||
|
lang_id, display_name, code_lines, formatted_lines, file_count, color, _icon = parts[:7]
|
||||||
|
stats.append(
|
||||||
|
LanguageStat(
|
||||||
|
lang_id=lang_id,
|
||||||
|
display_name=display_name,
|
||||||
|
code_lines=int(code_lines),
|
||||||
|
formatted_lines=formatted_lines,
|
||||||
|
file_count=int(file_count),
|
||||||
|
color=normalize_color(color, DEFAULT_TOTAL_COLOR),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return stats
|
||||||
|
|
||||||
|
|
||||||
|
def badge_svg(label: str, message: str, color: str) -> str:
|
||||||
|
height = 20
|
||||||
|
left_width = max(46, text_width(label))
|
||||||
|
right_width = max(46, text_width(message))
|
||||||
|
total_width = left_width + right_width
|
||||||
|
left_text_x = left_width / 2
|
||||||
|
right_text_x = left_width + right_width / 2
|
||||||
|
|
||||||
|
return f"""<svg xmlns="http://www.w3.org/2000/svg" width="{total_width}" height="{height}" role="img" aria-label="{escape_xml(label)}: {escape_xml(message)}">
|
||||||
|
<title>{escape_xml(label)}: {escape_xml(message)}</title>
|
||||||
|
<rect width="{left_width}" height="{height}" fill="{LABEL_BG}" />
|
||||||
|
<rect x="{left_width}" width="{right_width}" height="{height}" fill="{escape_xml(color)}" />
|
||||||
|
<rect width="{total_width}" height="{height}" rx="3" fill="transparent" />
|
||||||
|
<g fill="{TEXT_COLOR}" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||||
|
<text x="{left_text_x:.1f}" y="14">{escape_xml(label)}</text>
|
||||||
|
<text x="{right_text_x:.1f}" y="14">{escape_xml(message)}</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def write_badge(path: Path, label: str, message: str, color: str) -> None:
|
||||||
|
path.write_text(badge_svg(label, message, color), encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def render_readme(args: argparse.Namespace, languages: List[LanguageStat], badge_dir_name: str) -> str:
|
||||||
|
language_rows = []
|
||||||
|
|
||||||
|
for stat in languages:
|
||||||
|
share = (stat.code_lines * 100 / args.total_code) if args.total_code else 0.0
|
||||||
|
language_rows.append(
|
||||||
|
f"| **{stat.display_name}** | {stat.formatted_lines} | {stat.file_count} | {share:.1f}% | "
|
||||||
|
f" |"
|
||||||
|
)
|
||||||
|
|
||||||
|
if language_rows:
|
||||||
|
language_table = "\n".join(language_rows)
|
||||||
|
else:
|
||||||
|
language_table = "| _未找到符合条件的语言_ | - | - | - | - |"
|
||||||
|
|
||||||
|
return f"""# 📊 代码统计详细报告
|
||||||
|
|
||||||
|
> 此分支由 {args.generated_by} 自动生成和维护
|
||||||
|
>
|
||||||
|
> ⚠️ **请勿手动修改此分支的内容!**
|
||||||
|
>
|
||||||
|
> 🤖 由 {args.generated_by} 自动生成
|
||||||
|
>
|
||||||
|
> 📅 更新时间: {args.updated_at}
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
## 📈 总体统计
|
||||||
|
|
||||||
|
| 统计项 | 数值 | 徽章 |
|
||||||
|
|--------|------|------|
|
||||||
|
| 💻 总代码行数 | **{args.formatted_total_code}** 行 |  |
|
||||||
|
| 📁 总文件数 | **{args.formatted_total_files}** 个 |  |
|
||||||
|
| 🌐 语言种类 | **{args.language_count}** 种 |  |
|
||||||
|
|
||||||
|
## 🌈 语言分布
|
||||||
|
|
||||||
|
| 语言 | 代码行数 | 文件数 | 占比 | 徽章 |
|
||||||
|
|------|----------|--------|------|------|
|
||||||
|
{language_table}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ 配置说明
|
||||||
|
|
||||||
|
### Token 配置
|
||||||
|
|
||||||
|
- 当前使用: **WORKFLOW**
|
||||||
|
- 需要在 Settings -> Actions -> Secrets 中配置 `WORKFLOW`
|
||||||
|
|
||||||
|
### 排除规则
|
||||||
|
|
||||||
|
当前排除的目录:
|
||||||
|
```
|
||||||
|
{args.exclude_dirs}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 阈值设置
|
||||||
|
|
||||||
|
- 最小代码行数阈值: **{args.min_lines_threshold}** 行
|
||||||
|
- 低于此阈值的语言不会生成徽章
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*📊 统计仓库:`{args.repo}`*
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
args = parse_args()
|
||||||
|
output_dir = Path(args.output_dir)
|
||||||
|
readme_path = Path(args.readme_path)
|
||||||
|
|
||||||
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
readme_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
languages = parse_language_summary(Path(args.lang_summary))
|
||||||
|
badge_dir_name = output_dir.name
|
||||||
|
|
||||||
|
write_badge(
|
||||||
|
output_dir / "total-lines.svg",
|
||||||
|
"代码",
|
||||||
|
f"{args.formatted_total_code} 行",
|
||||||
|
normalize_color(args.total_color, DEFAULT_TOTAL_COLOR),
|
||||||
|
)
|
||||||
|
write_badge(
|
||||||
|
output_dir / "total-files.svg",
|
||||||
|
"文件",
|
||||||
|
f"{args.formatted_total_files} 个",
|
||||||
|
normalize_color(args.files_color, DEFAULT_FILES_COLOR),
|
||||||
|
)
|
||||||
|
write_badge(
|
||||||
|
output_dir / "language-count.svg",
|
||||||
|
"语言",
|
||||||
|
f"{args.language_count} 种",
|
||||||
|
normalize_color(args.language_count_color, DEFAULT_LANGUAGE_COUNT_COLOR),
|
||||||
|
)
|
||||||
|
|
||||||
|
for stat in languages:
|
||||||
|
write_badge(output_dir / f"{stat.lang_id}-lines.svg", stat.display_name, f"{stat.formatted_lines} 行", stat.color)
|
||||||
|
|
||||||
|
readme_path.write_text(render_readme(args, languages, badge_dir_name), encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -35,17 +35,12 @@ env:
|
||||||
SPECIAL_INCLUDES: ""
|
SPECIAL_INCLUDES: ""
|
||||||
# 示例: 'dist:js,css|vendor:go,mod'
|
# 示例: 'dist:js,css|vendor:go,mod'
|
||||||
|
|
||||||
# ===== 徽章颜色配置 =====
|
# ===== SVG 徽章颜色配置 =====
|
||||||
COLOR_TOTAL: "blue"
|
COLOR_TOTAL: "blue"
|
||||||
COLOR_FILES: "green"
|
COLOR_FILES: "green"
|
||||||
COLOR_DEFAULT: "brightgreen"
|
COLOR_LANGUAGE_COUNT: "purple"
|
||||||
|
|
||||||
# ===== 徽章样式配置 =====
|
|
||||||
BADGE_STYLE: "flat" # 可选: flat, flat-square, plastic, for-the-badge, social
|
|
||||||
|
|
||||||
# ===== 输出配置 =====
|
# ===== 输出配置 =====
|
||||||
# 是否生成详细报告
|
|
||||||
GENERATE_DETAILED_REPORT: "true"
|
|
||||||
# 是否输出到 workflow summary
|
# 是否输出到 workflow summary
|
||||||
OUTPUT_TO_SUMMARY: "true"
|
OUTPUT_TO_SUMMARY: "true"
|
||||||
# 最小代码行数阈值(低于此值的语言不生成徽章)
|
# 最小代码行数阈值(低于此值的语言不生成徽章)
|
||||||
|
|
@ -55,18 +50,6 @@ env:
|
||||||
GIT_USER_NAME: "ci[bot]"
|
GIT_USER_NAME: "ci[bot]"
|
||||||
GIT_USER_EMAIL: "ci[bot]@tinysoft.com.cn"
|
GIT_USER_EMAIL: "ci[bot]@tinysoft.com.cn"
|
||||||
|
|
||||||
# ===== 平台配置 =====
|
|
||||||
# 平台类型: github 或 gitea
|
|
||||||
PLATFORM: "gitea"
|
|
||||||
# Git 服务器 URL(Gitea 示例: https://gitea.example.com)
|
|
||||||
GIT_SERVER_URL: "${{ github.server_url }}"
|
|
||||||
# 仓库路径(格式: owner/repo)
|
|
||||||
REPO_PATH: "${{ github.repository }}"
|
|
||||||
# Raw 文件基础 URL
|
|
||||||
# GitHub: https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}
|
|
||||||
# Gitea: https://gitea.example.com/{owner}/{repo}/raw/branch/{branch}/{path}
|
|
||||||
RAW_URL_BASE: '${{ github.server_url }}/${{ github.repository }}/raw/branch'
|
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 🎨 语言分组配置
|
# 🎨 语言分组配置
|
||||||
# 格式: 组名:后缀列表:显示名称:颜色:图标(可选)
|
# 格式: 组名:后缀列表:显示名称:颜色:图标(可选)
|
||||||
|
|
@ -363,19 +346,19 @@ jobs:
|
||||||
cat > README.md << 'EOF'
|
cat > README.md << 'EOF'
|
||||||
# 📊 代码统计徽章数据
|
# 📊 代码统计徽章数据
|
||||||
|
|
||||||
> 此分支由 GitHub Actions 自动生成和维护
|
> 此分支由 Gitea Actions 自动生成和维护
|
||||||
>
|
>
|
||||||
> ⚠️ **请勿手动修改此分支的内容!**
|
> ⚠️ **请勿手动修改此分支的内容!**
|
||||||
|
|
||||||
## 📁 目录结构
|
## 📁 目录结构
|
||||||
|
|
||||||
```
|
```
|
||||||
|
README.md # 详细统计报告
|
||||||
badges/
|
badges/
|
||||||
├── total-lines.json # 总代码行数徽章
|
├── total-lines.svg # 总代码行数徽章
|
||||||
├── total-files.json # 总文件数徽章
|
├── total-files.svg # 总文件数徽章
|
||||||
├── {language}-lines.json # 各语言代码行数徽章
|
├── language-count.svg # 语言种类徽章
|
||||||
├── {language}-files.json # 各语言文件数徽章
|
└── {language}-lines.svg # 各语言代码行数徽章
|
||||||
└── README.md # 详细统计报告
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🔄 更新机制
|
## 🔄 更新机制
|
||||||
|
|
@ -389,7 +372,7 @@ jobs:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*🤖 由 GitHub Actions 自动维护*
|
*🤖 由 Gitea Actions 自动维护*
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# 创建徽章目录
|
# 创建徽章目录
|
||||||
|
|
@ -433,20 +416,20 @@ jobs:
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# 构建排除参数
|
# 构建排除参数
|
||||||
EXCLUDE_PARAMS=""
|
EXCLUDE_FIND_ARGS=()
|
||||||
IFS=',' read -ra EXCLUDES <<< "${{ env.EXCLUDE_DIRS }}"
|
IFS=',' read -ra EXCLUDES <<< "${{ env.EXCLUDE_DIRS }}"
|
||||||
for dir in "${EXCLUDES[@]}"; do
|
for dir in "${EXCLUDES[@]}"; do
|
||||||
EXCLUDE_PARAMS="$EXCLUDE_PARAMS -not -path '*/$dir/*'"
|
EXCLUDE_FIND_ARGS+=(-not -path "*/$dir/*")
|
||||||
done
|
done
|
||||||
|
|
||||||
# 获取所有文件
|
# 获取所有文件
|
||||||
echo "🔍 扫描文件..."
|
echo "🔍 扫描文件..."
|
||||||
|
|
||||||
# 统计总文件数
|
# 统计总文件数
|
||||||
TOTAL_FILES=$(eval "find . -type f $EXCLUDE_PARAMS" | wc -l)
|
TOTAL_FILES=$(find . -type f "${EXCLUDE_FIND_ARGS[@]}" | wc -l)
|
||||||
|
|
||||||
# 统计总代码行数(排除空行)
|
# 统计总代码行数(排除空行)
|
||||||
TOTAL_CODE=$(eval "find . -type f $EXCLUDE_PARAMS -exec grep -cHv '^[[:space:]]*$' {} + 2>/dev/null" | awk -F: '{sum+=$2} END {print sum+0}')
|
TOTAL_CODE=$(find . -type f "${EXCLUDE_FIND_ARGS[@]}" -exec grep -cHv '^[[:space:]]*$' {} + 2>/dev/null | awk -F: '{sum+=$2} END {print sum+0}')
|
||||||
|
|
||||||
echo "📊 统计结果:"
|
echo "📊 统计结果:"
|
||||||
echo " - 总文件数: $TOTAL_FILES"
|
echo " - 总文件数: $TOTAL_FILES"
|
||||||
|
|
@ -489,10 +472,10 @@ jobs:
|
||||||
mkdir -p /tmp/lang_stats
|
mkdir -p /tmp/lang_stats
|
||||||
|
|
||||||
# 构建排除参数
|
# 构建排除参数
|
||||||
EXCLUDE_PARAMS=""
|
EXCLUDE_FIND_ARGS=()
|
||||||
IFS=',' read -ra EXCLUDES <<< "${{ env.EXCLUDE_DIRS }}"
|
IFS=',' read -ra EXCLUDES <<< "${{ env.EXCLUDE_DIRS }}"
|
||||||
for dir in "${EXCLUDES[@]}"; do
|
for dir in "${EXCLUDES[@]}"; do
|
||||||
EXCLUDE_PARAMS="$EXCLUDE_PARAMS -not -path '*/$dir/*'"
|
EXCLUDE_FIND_ARGS+=(-not -path "*/$dir/*")
|
||||||
done
|
done
|
||||||
|
|
||||||
LANGUAGE_COUNT=0
|
LANGUAGE_COUNT=0
|
||||||
|
|
@ -507,22 +490,22 @@ jobs:
|
||||||
echo "🔍 统计 $display_name..."
|
echo "🔍 统计 $display_name..."
|
||||||
|
|
||||||
# 构建扩展名查找条件
|
# 构建扩展名查找条件
|
||||||
FIND_CONDITIONS=""
|
LANG_FIND_ARGS=()
|
||||||
IFS=',' read -ra EXTS <<< "$extensions"
|
IFS=',' read -ra EXTS <<< "$extensions"
|
||||||
for ext in "${EXTS[@]}"; do
|
for ext in "${EXTS[@]}"; do
|
||||||
if [ -z "$FIND_CONDITIONS" ]; then
|
if [ "${#LANG_FIND_ARGS[@]}" -eq 0 ]; then
|
||||||
FIND_CONDITIONS="-name '*.$ext'"
|
LANG_FIND_ARGS+=(-name "*.$ext")
|
||||||
else
|
else
|
||||||
FIND_CONDITIONS="$FIND_CONDITIONS -o -name '*.$ext'"
|
LANG_FIND_ARGS+=(-o -name "*.$ext")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# 统计文件数
|
# 统计文件数
|
||||||
FILE_COUNT=$(eval "find . -type f \( $FIND_CONDITIONS \) $EXCLUDE_PARAMS" | wc -l)
|
FILE_COUNT=$(find . -type f \( "${LANG_FIND_ARGS[@]}" \) "${EXCLUDE_FIND_ARGS[@]}" | wc -l)
|
||||||
|
|
||||||
if [ "$FILE_COUNT" -gt 0 ]; then
|
if [ "$FILE_COUNT" -gt 0 ]; then
|
||||||
# 统计代码行数
|
# 统计代码行数
|
||||||
CODE_LINES=$(eval "find . -type f \( $FIND_CONDITIONS \) $EXCLUDE_PARAMS -exec grep -cHv '^[[:space:]]*$' {} + 2>/dev/null" | awk -F: '{sum+=$2} END {print sum+0}')
|
CODE_LINES=$(find . -type f \( "${LANG_FIND_ARGS[@]}" \) "${EXCLUDE_FIND_ARGS[@]}" -exec grep -cHv '^[[:space:]]*$' {} + 2>/dev/null | awk -F: '{sum+=$2} END {print sum+0}')
|
||||||
|
|
||||||
# 只保存超过阈值的语言
|
# 只保存超过阈值的语言
|
||||||
if [ "$CODE_LINES" -ge "${{ env.MIN_LINES_THRESHOLD }}" ]; then
|
if [ "$CODE_LINES" -ge "${{ env.MIN_LINES_THRESHOLD }}" ]; then
|
||||||
|
|
@ -555,212 +538,50 @@ jobs:
|
||||||
echo "======================================"
|
echo "======================================"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
- name: 🎨 生成徽章数据
|
- name: 🎨 生成 SVG 徽章与统计报告
|
||||||
id: generate_badges
|
id: generate_badges
|
||||||
run: |
|
run: |
|
||||||
echo "======================================"
|
echo "======================================"
|
||||||
echo "🎨 生成徽章数据"
|
echo "🎨 生成 SVG 徽章与统计报告"
|
||||||
echo "======================================"
|
echo "======================================"
|
||||||
|
|
||||||
cd "${{ env.REPO_DIR }}"
|
cd "${{ env.REPO_DIR }}"
|
||||||
|
|
||||||
# 确保在统计分支
|
# 在主分支渲染产物,避免依赖 stats 分支中的脚本文件
|
||||||
|
git checkout ${{ github.ref_name }}
|
||||||
|
|
||||||
|
RENDER_ROOT=$(mktemp -d)
|
||||||
|
UPDATED_AT=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
||||||
|
|
||||||
|
python3 .gitea/ci/render_stats_svgs.py \
|
||||||
|
--output-dir "$RENDER_ROOT/${{ env.BADGE_DIR }}" \
|
||||||
|
--readme-path "$RENDER_ROOT/README.md" \
|
||||||
|
--repo "${{ github.repository }}" \
|
||||||
|
--generated-by "Gitea Actions" \
|
||||||
|
--updated-at "$UPDATED_AT" \
|
||||||
|
--total-color "${{ env.COLOR_TOTAL }}" \
|
||||||
|
--files-color "${{ env.COLOR_FILES }}" \
|
||||||
|
--language-count-color "${{ env.COLOR_LANGUAGE_COUNT }}" \
|
||||||
|
--total-code "${{ steps.total.outputs.total_code }}" \
|
||||||
|
--formatted-total-code "${{ steps.total.outputs.formatted_code }}" \
|
||||||
|
--total-files "${{ steps.total.outputs.total_files }}" \
|
||||||
|
--formatted-total-files "${{ steps.total.outputs.formatted_files }}" \
|
||||||
|
--language-count "${{ steps.languages.outputs.language_count }}" \
|
||||||
|
--exclude-dirs "${{ env.EXCLUDE_DIRS }}" \
|
||||||
|
--min-lines-threshold "${{ env.MIN_LINES_THRESHOLD }}" \
|
||||||
|
--lang-summary /tmp/lang_summary.txt
|
||||||
|
|
||||||
git checkout ${{ env.BADGE_BRANCH }}
|
git checkout ${{ env.BADGE_BRANCH }}
|
||||||
|
rm -rf "${{ env.BADGE_DIR }}"
|
||||||
# 确保徽章目录存在
|
mkdir -p "${{ env.BADGE_DIR }}"
|
||||||
mkdir -p ${{ env.BADGE_DIR }}
|
cp -f "$RENDER_ROOT/README.md" README.md
|
||||||
|
cp -f "$RENDER_ROOT/${{ env.BADGE_DIR }}"/* "${{ env.BADGE_DIR }}/"
|
||||||
GENERATED_COUNT=0
|
GENERATED_COUNT=$(find "$RENDER_ROOT/${{ env.BADGE_DIR }}" -maxdepth 1 -type f | wc -l | tr -d ' ')
|
||||||
|
rm -rf "$RENDER_ROOT"
|
||||||
# 生成总代码行数徽章
|
|
||||||
echo "📊 生成总代码行数徽章..."
|
|
||||||
cat > ${{ env.BADGE_DIR }}/total-lines.json << EOF
|
|
||||||
{
|
|
||||||
"schemaVersion": 1,
|
|
||||||
"label": "代码",
|
|
||||||
"message": "${{ steps.total.outputs.formatted_code }} 行",
|
|
||||||
"color": "${{ env.COLOR_TOTAL }}",
|
|
||||||
"style": "${{ env.BADGE_STYLE }}"
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
GENERATED_COUNT=$((GENERATED_COUNT + 1))
|
|
||||||
|
|
||||||
# 生成总文件数徽章
|
|
||||||
echo "📁 生成总文件数徽章..."
|
|
||||||
cat > ${{ env.BADGE_DIR }}/total-files.json << EOF
|
|
||||||
{
|
|
||||||
"schemaVersion": 1,
|
|
||||||
"label": "文件",
|
|
||||||
"message": "${{ steps.total.outputs.formatted_files }} 个",
|
|
||||||
"color": "${{ env.COLOR_FILES }}",
|
|
||||||
"style": "${{ env.BADGE_STYLE }}"
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
GENERATED_COUNT=$((GENERATED_COUNT + 1))
|
|
||||||
|
|
||||||
# 生成各语言徽章
|
|
||||||
if [ -f /tmp/lang_summary.txt ] && [ -s /tmp/lang_summary.txt ]; then
|
|
||||||
echo ""
|
|
||||||
echo "🌐 生成语言徽章..."
|
|
||||||
|
|
||||||
while IFS='|' read -r lang_id display_name code_lines formatted_lines file_count color icon; do
|
|
||||||
[ -z "$lang_id" ] && continue
|
|
||||||
|
|
||||||
echo " - $display_name"
|
|
||||||
|
|
||||||
# 生成代码行数徽章(使用 heredoc)
|
|
||||||
if [ -n "$icon" ]; then
|
|
||||||
# 带图标的徽章
|
|
||||||
cat > ${{ env.BADGE_DIR }}/${lang_id}-lines.json << EOFJSON
|
|
||||||
{
|
|
||||||
"schemaVersion": 1,
|
|
||||||
"label": "$display_name",
|
|
||||||
"message": "$formatted_lines 行",
|
|
||||||
"color": "$color",
|
|
||||||
"style": "${{ env.BADGE_STYLE }}",
|
|
||||||
"namedLogo": "$icon"
|
|
||||||
}
|
|
||||||
EOFJSON
|
|
||||||
else
|
|
||||||
# 不带图标的徽章
|
|
||||||
cat > ${{ env.BADGE_DIR }}/${lang_id}-lines.json << EOFJSON
|
|
||||||
{
|
|
||||||
"schemaVersion": 1,
|
|
||||||
"label": "$display_name",
|
|
||||||
"message": "$formatted_lines 行",
|
|
||||||
"color": "$color",
|
|
||||||
"style": "${{ env.BADGE_STYLE }}"
|
|
||||||
}
|
|
||||||
EOFJSON
|
|
||||||
fi
|
|
||||||
GENERATED_COUNT=$((GENERATED_COUNT + 1))
|
|
||||||
|
|
||||||
# 生成文件数徽章
|
|
||||||
cat > ${{ env.BADGE_DIR }}/${lang_id}-files.json << EOFJSON
|
|
||||||
{
|
|
||||||
"schemaVersion": 1,
|
|
||||||
"label": "$display_name 文件",
|
|
||||||
"message": "$file_count 个",
|
|
||||||
"color": "$color",
|
|
||||||
"style": "${{ env.BADGE_STYLE }}"
|
|
||||||
}
|
|
||||||
EOFJSON
|
|
||||||
GENERATED_COUNT=$((GENERATED_COUNT + 1))
|
|
||||||
done < /tmp/lang_summary.txt
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "generated_count=$GENERATED_COUNT" >> $GITHUB_OUTPUT
|
echo "generated_count=$GENERATED_COUNT" >> $GITHUB_OUTPUT
|
||||||
echo "✅ 已生成 $GENERATED_COUNT 个徽章"
|
echo "✅ 已生成 $GENERATED_COUNT 个 SVG 徽章"
|
||||||
echo "======================================"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
- name: 📝 生成详细统计报告
|
|
||||||
if: env.GENERATE_DETAILED_REPORT == 'true'
|
|
||||||
run: |
|
|
||||||
echo "======================================"
|
|
||||||
echo "📝 生成详细统计报告"
|
|
||||||
echo "======================================"
|
|
||||||
|
|
||||||
cd "${{ env.REPO_DIR }}"
|
|
||||||
|
|
||||||
# 确保在统计分支
|
|
||||||
git checkout ${{ env.BADGE_BRANCH }}
|
|
||||||
|
|
||||||
# 生成 README
|
|
||||||
cat > ${{ env.BADGE_DIR }}/README.md << 'EOFMD'
|
|
||||||
# 📊 代码统计详细报告
|
|
||||||
|
|
||||||
> 🤖 由 GitHub Actions 自动生成
|
|
||||||
>
|
|
||||||
> 📅 更新时间: TIMESTAMP_PLACEHOLDER
|
|
||||||
|
|
||||||
## 📈 总体统计
|
|
||||||
|
|
||||||
| 统计项 | 数值 | 徽章 |
|
|
||||||
|--------|------|------|
|
|
||||||
| 💻 总代码行数 | **TOTAL_CODE_PLACEHOLDER** 行 |  |
|
|
||||||
| 📁 总文件数 | **TOTAL_FILES_PLACEHOLDER** 个 |  |
|
|
||||||
| 🌐 语言种类 | **LANG_COUNT_PLACEHOLDER** 种 | - |
|
|
||||||
|
|
||||||
## 🌈 语言分布
|
|
||||||
|
|
||||||
EOFMD
|
|
||||||
|
|
||||||
# 替换占位符
|
|
||||||
sed -i "s|TIMESTAMP_PLACEHOLDER|$(date -u '+%Y-%m-%d %H:%M:%S UTC')|g" ${{ env.BADGE_DIR }}/README.md
|
|
||||||
sed -i "s|TOTAL_CODE_PLACEHOLDER|${{ steps.total.outputs.formatted_code }}|g" ${{ env.BADGE_DIR }}/README.md
|
|
||||||
sed -i "s|TOTAL_FILES_PLACEHOLDER|${{ steps.total.outputs.formatted_files }}|g" ${{ env.BADGE_DIR }}/README.md
|
|
||||||
sed -i "s|LANG_COUNT_PLACEHOLDER|${{ steps.languages.outputs.language_count }}|g" ${{ env.BADGE_DIR }}/README.md
|
|
||||||
sed -i "s|REPO_PLACEHOLDER|${{ github.repository }}|g" ${{ env.BADGE_DIR }}/README.md
|
|
||||||
sed -i "s|BRANCH_PLACEHOLDER|${{ env.BADGE_BRANCH }}|g" ${{ env.BADGE_DIR }}/README.md
|
|
||||||
sed -i "s|BADGE_DIR_PLACEHOLDER|${{ env.BADGE_DIR }}|g" ${{ env.BADGE_DIR }}/README.md
|
|
||||||
sed -i "s|RAW_URL_PLACEHOLDER|${{ env.RAW_URL_BASE }}|g" ${{ env.BADGE_DIR }}/README.md
|
|
||||||
|
|
||||||
# 添加语言统计表格
|
|
||||||
if [ -f /tmp/lang_summary.txt ] && [ -s /tmp/lang_summary.txt ]; then
|
|
||||||
cat >> ${{ env.BADGE_DIR }}/README.md << 'EOFTABLE'
|
|
||||||
| 语言 | 代码行数 | 文件数 | 占比 | 徽章 |
|
|
||||||
|------|----------|--------|------|------|
|
|
||||||
EOFTABLE
|
|
||||||
|
|
||||||
TOTAL_CODE=${{ steps.total.outputs.total_code }}
|
|
||||||
while IFS='|' read -r lang_id display_name code_lines formatted_lines file_count color icon; do
|
|
||||||
[ -z "$lang_id" ] && continue
|
|
||||||
|
|
||||||
if [ "$TOTAL_CODE" -gt 0 ]; then
|
|
||||||
PERCENT=$(echo "scale=1; $code_lines * 100 / $TOTAL_CODE" | bc)
|
|
||||||
else
|
|
||||||
PERCENT="0.0"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat >> ${{ env.BADGE_DIR }}/README.md << EOFLANG
|
|
||||||
| **${display_name}** | ${formatted_lines} | ${file_count} | ${PERCENT}% |  |
|
|
||||||
EOFLANG
|
|
||||||
done < /tmp/lang_summary.txt
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 添加配置说明
|
|
||||||
cat >> ${{ env.BADGE_DIR }}/README.md << EOFMD
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ⚙️ 配置说明
|
|
||||||
|
|
||||||
### Token 配置
|
|
||||||
|
|
||||||
- 当前使用: **${{ steps.validate_token.outputs.token_type }}**
|
|
||||||
- 需要在 Settings -> Secrets 中配置 `WORKFLOW`
|
|
||||||
|
|
||||||
### 排除规则
|
|
||||||
|
|
||||||
当前排除的目录:
|
|
||||||
```
|
|
||||||
${{ env.EXCLUDE_DIRS }}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 阈值设置
|
|
||||||
|
|
||||||
- 最小代码行数阈值: **${{ env.MIN_LINES_THRESHOLD }}** 行
|
|
||||||
- 低于此阈值的语言将不会生成徽章
|
|
||||||
|
|
||||||
### 徽章样式
|
|
||||||
|
|
||||||
- 当前样式: **${{ env.BADGE_STYLE }}**
|
|
||||||
- 可选样式: flat, flat-square, plastic, for-the-badge, social
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
|
|
||||||
*📊 由 [GitHub Actions](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions) 自动生成和更新*
|
|
||||||
|
|
||||||
*🤖 生成时间: $(date -u '+%Y-%m-%d %H:%M:%S UTC')*
|
|
||||||
|
|
||||||
</div>
|
|
||||||
EOFMD
|
|
||||||
|
|
||||||
echo "✅ 详细统计报告已生成"
|
|
||||||
echo "======================================"
|
echo "======================================"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
|
@ -781,7 +602,7 @@ jobs:
|
||||||
git config user.email "${{ env.GIT_USER_EMAIL }}"
|
git config user.email "${{ env.GIT_USER_EMAIL }}"
|
||||||
|
|
||||||
# 添加所有更改
|
# 添加所有更改
|
||||||
git add ${{ env.BADGE_DIR }}/
|
git add README.md ${{ env.BADGE_DIR }}/
|
||||||
|
|
||||||
# 检查是否有变更
|
# 检查是否有变更
|
||||||
if git diff --staged --quiet; then
|
if git diff --staged --quiet; then
|
||||||
|
|
@ -801,7 +622,7 @@ jobs:
|
||||||
- 徽章数: ${{ steps.generate_badges.outputs.generated_count }} 个
|
- 徽章数: ${{ steps.generate_badges.outputs.generated_count }} 个
|
||||||
|
|
||||||
🔗 触发提交: ${GITHUB_SHA:0:7}
|
🔗 触发提交: ${GITHUB_SHA:0:7}
|
||||||
🤖 由 GitHub Actions 自动生成"
|
🤖 由 Gitea Actions 自动生成"
|
||||||
|
|
||||||
git commit -m "$COMMIT_MSG"
|
git commit -m "$COMMIT_MSG"
|
||||||
|
|
||||||
|
|
@ -842,7 +663,7 @@ jobs:
|
||||||
|
|
||||||
## 🔗 快速链接
|
## 🔗 快速链接
|
||||||
|
|
||||||
- 📊 [查看详细统计报告](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }}/README.md)
|
- 📊 [查看详细统计报告](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/README.md)
|
||||||
- 🎨 [浏览徽章文件](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }})
|
- 🎨 [浏览徽章文件](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }})
|
||||||
- 🔧 [查看 Workflow 配置](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ github.ref_name }}/.gitea/workflows/update_stats_badge.yaml)
|
- 🔧 [查看 Workflow 配置](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ github.ref_name }}/.gitea/workflows/update_stats_badge.yaml)
|
||||||
|
|
||||||
|
|
@ -924,7 +745,7 @@ jobs:
|
||||||
echo " - 统计分支: ${{ env.BADGE_BRANCH }}"
|
echo " - 统计分支: ${{ env.BADGE_BRANCH }}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "🔗 查看详细报告:"
|
echo "🔗 查看详细报告:"
|
||||||
echo " ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }}/README.md"
|
echo " ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/README.md"
|
||||||
echo ""
|
echo ""
|
||||||
echo "🎨 徽章目录:"
|
echo "🎨 徽章目录:"
|
||||||
echo " ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }}"
|
echo " ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }}"
|
||||||
|
|
@ -957,7 +778,7 @@ jobs:
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
echo "🧹 清理临时文件..."
|
echo "🧹 清理临时文件..."
|
||||||
rm -rf /tmp/lang_stats /tmp/lang_summary.txt /tmp/total_stats*.json
|
rm -rf /tmp/lang_stats /tmp/lang_summary.txt
|
||||||
if [ -n "${{ env.JOB_WORKSPACE }}" ] && [ "${{ env.JOB_WORKSPACE }}" != "/" ]; then
|
if [ -n "${{ env.JOB_WORKSPACE }}" ] && [ "${{ env.JOB_WORKSPACE }}" != "/" ]; then
|
||||||
echo "🧹 清理 Job 工作区: ${{ env.JOB_WORKSPACE }}"
|
echo "🧹 清理 Job 工作区: ${{ env.JOB_WORKSPACE }}"
|
||||||
rm -rf "${{ env.JOB_WORKSPACE }}"
|
rm -rf "${{ env.JOB_WORKSPACE }}"
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@
|
||||||
|
|
||||||
## 📂 文档导航
|
## 📂 文档导航
|
||||||
|
|
||||||
### 🚀 Runner
|
### 🚀 [DEPLOYMENT.md](./DEPLOYMENT.md)
|
||||||
|
|
||||||
#### [DEPLOYMENT.md](./DEPLOYMENT.md)
|
|
||||||
|
|
||||||
**Gitea Runner Docker 部署完整教程**
|
**Gitea Runner Docker 部署完整教程**
|
||||||
|
|
||||||
|
|
|
||||||
20
WORKFLOW.md
20
WORKFLOW.md
|
|
@ -63,14 +63,14 @@ git push origin 1.0.0
|
||||||
|
|
||||||
#### 2. 📊 代码统计徽章工作流 (`update_stats_badge.yaml`)
|
#### 2. 📊 代码统计徽章工作流 (`update_stats_badge.yaml`)
|
||||||
|
|
||||||
**功能**:自动统计代码行数并生成徽章数据
|
**功能**:自动统计代码行数并生成 SVG 徽章与统计报告
|
||||||
|
|
||||||
**特性**:
|
**特性**:
|
||||||
|
|
||||||
- 📈 统计总代码行数和文件数
|
- 📈 统计总代码行数和文件数
|
||||||
- 🌐 按语言分组统计
|
- 🌐 按语言分组统计
|
||||||
- 🎨 自动生成徽章 JSON 数据
|
- 🎨 自动生成仓库内自托管的 SVG 徽章
|
||||||
- 📊 生成统计摘要(JSON 格式)
|
- 📊 在 `stats` 分支根目录生成统计报告 README
|
||||||
- 🚫 灵活的目录排除配置
|
- 🚫 灵活的目录排除配置
|
||||||
|
|
||||||
**触发方式**:
|
**触发方式**:
|
||||||
|
|
@ -85,7 +85,19 @@ git push origin main
|
||||||
|
|
||||||
**配置文件**:[update_stats_badge.yaml](.gitea/workflows/update_stats_badge.yaml)
|
**配置文件**:[update_stats_badge.yaml](.gitea/workflows/update_stats_badge.yaml)
|
||||||
|
|
||||||
**markdown引用格式**: ``
|
**markdown引用格式**: ``
|
||||||
|
|
||||||
|
**产物结构**:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
stats
|
||||||
|
├── README.md
|
||||||
|
└── badges/
|
||||||
|
├── total-lines.svg
|
||||||
|
├── total-files.svg
|
||||||
|
├── language-count.svg
|
||||||
|
└── <language>-lines.svg
|
||||||
|
```
|
||||||
|
|
||||||
💡 **详细配置**:查看 `update_stats_badge.yaml` 文件顶部的 `env` 区域,包含语言分组、颜色、排除目录等配置
|
💡 **详细配置**:查看 `update_stats_badge.yaml` 文件顶部的 `env` 区域,包含语言分组、颜色、排除目录等配置
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,29 +90,6 @@ echo ""
|
||||||
|
|
||||||
if [ $FIXED_COUNT -gt 0 ]; then
|
if [ $FIXED_COUNT -gt 0 ]; then
|
||||||
echo -e "${GREEN}✓ 所有问题已自动修复!${NC}"
|
echo -e "${GREEN}✓ 所有问题已自动修复!${NC}"
|
||||||
echo ""
|
|
||||||
echo "建议执行以下命令重启容器:"
|
|
||||||
echo " docker compose down"
|
|
||||||
echo " docker compose build --no-cache"
|
|
||||||
echo " docker compose up -d"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# 询问是否立即重启
|
|
||||||
read -p "是否立即重启容器? (y/N): " -n 1 -r
|
|
||||||
echo ""
|
|
||||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
||||||
echo "正在重启容器..."
|
|
||||||
docker compose down
|
|
||||||
docker compose build --no-cache
|
|
||||||
docker compose up -d
|
|
||||||
sleep 3
|
|
||||||
echo ""
|
|
||||||
echo "容器状态:"
|
|
||||||
docker compose ps
|
|
||||||
echo ""
|
|
||||||
echo "查看日志:"
|
|
||||||
docker logs gitea-runner --tail=30
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo -e "${GREEN}✓ 所有文件格式正确,无需修复!${NC}"
|
echo -e "${GREEN}✓ 所有文件格式正确,无需修复!${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,24 @@ mkdir -p "$PERSISTENT_BIN"
|
||||||
mkdir -p /var/log/supervisor
|
mkdir -p /var/log/supervisor
|
||||||
mkdir -p /var/run
|
mkdir -p /var/run
|
||||||
|
|
||||||
|
create_buildx_builder() {
|
||||||
|
docker buildx create \
|
||||||
|
--name gitea-multiarch \
|
||||||
|
--driver docker-container \
|
||||||
|
--bootstrap \
|
||||||
|
--use \
|
||||||
|
--config /data/buildx/buildkitd.toml 2>/dev/null || \
|
||||||
|
docker buildx use gitea-multiarch 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_buildx_builder() {
|
||||||
|
docker buildx use gitea-multiarch 2>/dev/null || {
|
||||||
|
echo "⚠ Recreating Buildx builder..."
|
||||||
|
docker buildx rm gitea-multiarch 2>/dev/null || true
|
||||||
|
create_buildx_builder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# 初始化 Docker Buildx 支持(可选)
|
# 初始化 Docker Buildx 支持(可选)
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
@ -80,13 +98,13 @@ if [ "$ENABLE_BUILDX" = "true" ]; then
|
||||||
|
|
||||||
# 验证多架构支持
|
# 验证多架构支持
|
||||||
echo "Verifying multi-arch support..."
|
echo "Verifying multi-arch support..."
|
||||||
if docker run --rm arm64v8/alpine uname -m > /dev/null 2>&1; then
|
if docker run --rm --platform linux/arm64 alpine uname -m > /dev/null 2>&1; then
|
||||||
echo " ✓ arm64 support verified"
|
echo " ✓ arm64 support verified"
|
||||||
else
|
else
|
||||||
echo " ⚠ arm64 verification failed"
|
echo " ⚠ arm64 verification failed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if docker run --rm amd64/alpine uname -m > /dev/null 2>&1; then
|
if docker run --rm --platform linux/amd64 alpine uname -m > /dev/null 2>&1; then
|
||||||
echo " ✓ amd64 support verified"
|
echo " ✓ amd64 support verified"
|
||||||
else
|
else
|
||||||
echo " ⚠ amd64 verification failed"
|
echo " ⚠ amd64 verification failed"
|
||||||
|
|
@ -104,13 +122,7 @@ if [ "$ENABLE_BUILDX" = "true" ]; then
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# 创建 Buildx builder
|
# 创建 Buildx builder
|
||||||
docker buildx create \
|
create_buildx_builder
|
||||||
--name gitea-multiarch \
|
|
||||||
--driver docker-container \
|
|
||||||
--bootstrap \
|
|
||||||
--use \
|
|
||||||
--config /data/buildx/buildkitd.toml 2>/dev/null || \
|
|
||||||
docker buildx use gitea-multiarch 2>/dev/null
|
|
||||||
|
|
||||||
# 验证
|
# 验证
|
||||||
echo "Verifying Buildx..."
|
echo "Verifying Buildx..."
|
||||||
|
|
@ -125,15 +137,7 @@ EOF
|
||||||
echo "✓ Buildx already configured"
|
echo "✓ Buildx already configured"
|
||||||
|
|
||||||
# 确保 builder 可用
|
# 确保 builder 可用
|
||||||
docker buildx use gitea-multiarch 2>/dev/null || {
|
ensure_buildx_builder
|
||||||
echo "⚠ Recreating Buildx builder..."
|
|
||||||
docker buildx rm gitea-multiarch 2>/dev/null || true
|
|
||||||
docker buildx create \
|
|
||||||
--name gitea-multiarch \
|
|
||||||
--driver docker-container \
|
|
||||||
--bootstrap \
|
|
||||||
--use 2>/dev/null
|
|
||||||
}
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
|
||||||
|
|
@ -133,12 +133,11 @@ echo "✓ Configuration file generated!"
|
||||||
# 创建缓存目录
|
# 创建缓存目录
|
||||||
mkdir -p cache
|
mkdir -p cache
|
||||||
|
|
||||||
# 使用 Python 修改配置(最可靠的方法)
|
# 使用 Python 修改配置
|
||||||
echo ""
|
echo ""
|
||||||
echo "Configuring runner settings..."
|
echo "Configuring runner settings..."
|
||||||
|
|
||||||
if command -v python3 &> /dev/null; then
|
python3 << PYEOF
|
||||||
python3 << PYEOF
|
|
||||||
import yaml
|
import yaml
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
@ -184,32 +183,6 @@ except Exception as e:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
PYEOF
|
PYEOF
|
||||||
|
|
||||||
PYTHON_EXIT=$?
|
|
||||||
|
|
||||||
if [ $PYTHON_EXIT -ne 0 ]; then
|
|
||||||
echo ""
|
|
||||||
echo "⚠ Python configuration failed, using basic sed..."
|
|
||||||
|
|
||||||
# 基本的 sed 修改(只修改简单的值,不动 labels)
|
|
||||||
sed -i 's/capacity: 1/capacity: 4/g' config.yaml || true
|
|
||||||
sed -i 's/enabled: false/enabled: true/g' config.yaml || true
|
|
||||||
sed -i 's|dir: ""|dir: ./cache|g' config.yaml || true
|
|
||||||
|
|
||||||
echo "✓ Basic configuration applied"
|
|
||||||
echo " Note: Please manually verify labels in config.yaml match .runner"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "⚠ Python3 not found, applying basic configuration..."
|
|
||||||
|
|
||||||
# 基本的 sed 修改
|
|
||||||
sed -i 's/capacity: 1/capacity: 4/g' config.yaml || true
|
|
||||||
sed -i 's/enabled: false/enabled: true/g' config.yaml || true
|
|
||||||
sed -i 's|dir: ""|dir: ./cache|g' config.yaml || true
|
|
||||||
|
|
||||||
echo "✓ Basic configuration applied"
|
|
||||||
echo " Note: Labels will use act_runner defaults"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 验证配置文件
|
# 验证配置文件
|
||||||
echo ""
|
echo ""
|
||||||
echo "Validating configuration..."
|
echo "Validating configuration..."
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,8 @@ SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
UPGRADE_HELPER="${SCRIPT_DIR}/upgrade.sh"
|
UPGRADE_HELPER="${SCRIPT_DIR}/upgrade.sh"
|
||||||
DEFAULT_RUNNER_VERSION_FALLBACK="${DEFAULT_RUNNER_VERSION_FALLBACK:-0.2.13}"
|
DEFAULT_RUNNER_VERSION_FALLBACK="${DEFAULT_RUNNER_VERSION_FALLBACK:-0.2.13}"
|
||||||
|
|
||||||
if [ -f "${UPGRADE_HELPER}" ]; then
|
# shellcheck source=/dev/null
|
||||||
# shellcheck source=/dev/null
|
source "${UPGRADE_HELPER}"
|
||||||
source "${UPGRADE_HELPER}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo " Gitea Runner Installation Script "
|
echo " Gitea Runner Installation Script "
|
||||||
|
|
@ -42,15 +40,13 @@ echo "Available versions: https://dl.gitea.com/act_runner/"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
DEFAULT_RUNNER_VERSION="${DEFAULT_RUNNER_VERSION_FALLBACK}"
|
DEFAULT_RUNNER_VERSION="${DEFAULT_RUNNER_VERSION_FALLBACK}"
|
||||||
if declare -F resolve_latest_version_or_fallback >/dev/null 2>&1; then
|
DEFAULT_RUNNER_VERSION=$(resolve_latest_version_or_fallback "${DEFAULT_RUNNER_VERSION_FALLBACK}")
|
||||||
DEFAULT_RUNNER_VERSION=$(resolve_latest_version_or_fallback "${DEFAULT_RUNNER_VERSION_FALLBACK}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Default install version: ${DEFAULT_RUNNER_VERSION}"
|
echo "Default install version: ${DEFAULT_RUNNER_VERSION}"
|
||||||
read -p "Enter version to install (default: ${DEFAULT_RUNNER_VERSION}): " RUNNER_VERSION
|
read -p "Enter version to install (default: ${DEFAULT_RUNNER_VERSION}): " RUNNER_VERSION
|
||||||
RUNNER_VERSION=${RUNNER_VERSION:-$DEFAULT_RUNNER_VERSION}
|
RUNNER_VERSION=${RUNNER_VERSION:-$DEFAULT_RUNNER_VERSION}
|
||||||
|
|
||||||
if declare -F validate_version >/dev/null 2>&1 && ! validate_version "${RUNNER_VERSION}"; then
|
if ! validate_version "${RUNNER_VERSION}"; then
|
||||||
echo "✗ Invalid version format: ${RUNNER_VERSION}"
|
echo "✗ Invalid version format: ${RUNNER_VERSION}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -118,6 +114,7 @@ echo ""
|
||||||
# 下载到持久化目录
|
# 下载到持久化目录
|
||||||
if curl -L "$DOWNLOAD_URL" -o "$INSTALL_PATH"; then
|
if curl -L "$DOWNLOAD_URL" -o "$INSTALL_PATH"; then
|
||||||
chmod +x "$INSTALL_PATH"
|
chmod +x "$INSTALL_PATH"
|
||||||
|
validate_binary_arch_or_fail "$INSTALL_PATH" "$RUNNER_ARCH"
|
||||||
|
|
||||||
# 同时创建软链接到系统路径
|
# 同时创建软链接到系统路径
|
||||||
ln -sf "$INSTALL_PATH" "$SYSTEM_PATH"
|
ln -sf "$INSTALL_PATH" "$SYSTEM_PATH"
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,73 @@ detect_runner_arch() {
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detect_binary_arch_from_file() {
|
||||||
|
local binary_path=$1
|
||||||
|
local magic=""
|
||||||
|
local low_byte=""
|
||||||
|
local high_byte=""
|
||||||
|
local machine_hex=""
|
||||||
|
|
||||||
|
if [ ! -f "${binary_path}" ]; then
|
||||||
|
echo "unknown"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
magic=$(od -An -t x1 -N 4 "${binary_path}" 2>/dev/null | tr -d ' \n')
|
||||||
|
if [ "${magic}" != "7f454c46" ]; then
|
||||||
|
echo "unknown"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r low_byte high_byte <<<"$(od -An -t x1 -j 18 -N 2 "${binary_path}" 2>/dev/null)"
|
||||||
|
if [ -z "${low_byte:-}" ] || [ -z "${high_byte:-}" ]; then
|
||||||
|
echo "unknown"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
machine_hex="${high_byte}${low_byte}"
|
||||||
|
|
||||||
|
case "${machine_hex}" in
|
||||||
|
003e)
|
||||||
|
echo "amd64"
|
||||||
|
;;
|
||||||
|
00b7)
|
||||||
|
echo "arm64"
|
||||||
|
;;
|
||||||
|
0028)
|
||||||
|
echo "arm-7"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "unknown"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
binary_arch_matches_target() {
|
||||||
|
local binary_path=$1
|
||||||
|
local expected_arch=$2
|
||||||
|
local detected_arch=""
|
||||||
|
|
||||||
|
detected_arch=$(detect_binary_arch_from_file "${binary_path}")
|
||||||
|
[ "${detected_arch}" = "${expected_arch}" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_binary_arch_or_fail() {
|
||||||
|
local binary_path=$1
|
||||||
|
local expected_arch=$2
|
||||||
|
local detected_arch=""
|
||||||
|
|
||||||
|
detected_arch=$(detect_binary_arch_from_file "${binary_path}")
|
||||||
|
|
||||||
|
if [ "${detected_arch}" = "unknown" ]; then
|
||||||
|
fail "Unable to detect downloaded binary architecture: ${binary_path}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${detected_arch}" != "${expected_arch}" ]; then
|
||||||
|
fail "Downloaded binary architecture mismatch: expected ${expected_arch}, got ${detected_arch}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
get_installed_version() {
|
get_installed_version() {
|
||||||
local output=""
|
local output=""
|
||||||
|
|
||||||
|
|
@ -102,6 +169,16 @@ get_installed_version() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_installed_binary_arch() {
|
||||||
|
if [ -x "${INSTALL_PATH}" ]; then
|
||||||
|
detect_binary_arch_from_file "${INSTALL_PATH}"
|
||||||
|
elif command -v act_runner >/dev/null 2>&1; then
|
||||||
|
detect_binary_arch_from_file "$(command -v act_runner)"
|
||||||
|
else
|
||||||
|
echo "unknown"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
list_registered_runners() {
|
list_registered_runners() {
|
||||||
local runner_dir
|
local runner_dir
|
||||||
|
|
||||||
|
|
@ -154,6 +231,7 @@ download_and_install() {
|
||||||
log "Downloading ${url}"
|
log "Downloading ${url}"
|
||||||
curl -fL "${url}" -o "${tmp_path}"
|
curl -fL "${url}" -o "${tmp_path}"
|
||||||
chmod +x "${tmp_path}"
|
chmod +x "${tmp_path}"
|
||||||
|
validate_binary_arch_or_fail "${tmp_path}" "${arch}"
|
||||||
|
|
||||||
downloaded_version=$("${tmp_path}" --version 2>/dev/null | extract_version_number)
|
downloaded_version=$("${tmp_path}" --version 2>/dev/null | extract_version_number)
|
||||||
if [ "${downloaded_version}" != "${version}" ]; then
|
if [ "${downloaded_version}" != "${version}" ]; then
|
||||||
|
|
@ -205,6 +283,7 @@ main() {
|
||||||
local target_arch=""
|
local target_arch=""
|
||||||
local auto_confirm="false"
|
local auto_confirm="false"
|
||||||
local current_version=""
|
local current_version=""
|
||||||
|
local current_arch="unknown"
|
||||||
local runner_names=()
|
local runner_names=()
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
|
|
@ -252,13 +331,18 @@ main() {
|
||||||
target_arch=$(detect_runner_arch)
|
target_arch=$(detect_runner_arch)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "${current_version}" ] && [ "${current_version}" = "${target_version}" ]; then
|
current_arch=$(get_installed_binary_arch || true)
|
||||||
log "act_runner is already at the latest version: ${current_version}"
|
|
||||||
|
if [ -n "${current_version}" ] && \
|
||||||
|
[ "${current_version}" = "${target_version}" ] && \
|
||||||
|
[ "${current_arch}" = "${target_arch}" ]; then
|
||||||
|
log "act_runner is already at the requested version and architecture: ${current_version} (${current_arch})"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Upgrade summary:"
|
log "Upgrade summary:"
|
||||||
log " Current version: ${current_version:-not installed}"
|
log " Current version: ${current_version:-not installed}"
|
||||||
|
log " Current arch: ${current_arch:-unknown}"
|
||||||
log " Target version: ${target_version}"
|
log " Target version: ${target_version}"
|
||||||
log " Architecture: ${target_arch}"
|
log " Architecture: ${target_arch}"
|
||||||
log " Install path: ${INSTALL_PATH}"
|
log " Install path: ${INSTALL_PATH}"
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
GITEA_INSTANCE=https://git.mytsl.cn
|
GITEA_INSTANCE=https://git.mytsl.cn
|
||||||
GITEA_TOKEN=
|
GITEA_TOKEN=
|
||||||
|
DEFAULT_RUNNER_NAME=buildx-archlinux
|
||||||
|
DEFAULT_RUNNER_LABEL=archlinux:host://archlinux:latest,company-server:host://archlinux:latest,buildx-archlinux:host://archlinux:latest
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ services:
|
||||||
- BINFMT_METHOD=tonistiigi
|
- BINFMT_METHOD=tonistiigi
|
||||||
- GITEA_INSTANCE=${GITEA_INSTANCE}
|
- GITEA_INSTANCE=${GITEA_INSTANCE}
|
||||||
- GITEA_TOKEN=${GITEA_TOKEN}
|
- GITEA_TOKEN=${GITEA_TOKEN}
|
||||||
- DEFAULT_RUNNER_NAME=buildx-archlinux
|
- DEFAULT_RUNNER_NAME=${DEFAULT_RUNNER_NAME:-buildx-archlinux}
|
||||||
- DEFAULT_RUNNER_LABEL=archlinux:host://archlinux:latest,company-server:host://archlinux:latest,buildx-archlinux:host://archlinux:latest
|
- DEFAULT_RUNNER_LABEL=${DEFAULT_RUNNER_LABEL:-archlinux:host://archlinux:latest,company-server:host://archlinux:latest,buildx-archlinux:host://archlinux:latest}
|
||||||
|
|
||||||
# 如果需要使用代理,取消下面的注释并修改为你的代理地址
|
# 如果需要使用代理,取消下面的注释并修改为你的代理地址
|
||||||
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
|
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
GITEA_INSTANCE=https://git.mytsl.cn
|
GITEA_INSTANCE=https://git.mytsl.cn
|
||||||
GITEA_TOKEN=
|
GITEA_TOKEN=
|
||||||
|
DEFAULT_RUNNER_NAME=buildx-ubuntu-22
|
||||||
|
DEFAULT_RUNNER_LABEL=ubuntu-22.04:host://ubuntu:22.04,company-server:host://ubuntu:22.04,buildx-ubuntu-22:host://ubuntu:22.04
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ services:
|
||||||
- ENABLE_BUILDX=true
|
- ENABLE_BUILDX=true
|
||||||
- GITEA_INSTANCE=${GITEA_INSTANCE}
|
- GITEA_INSTANCE=${GITEA_INSTANCE}
|
||||||
- GITEA_TOKEN=${GITEA_TOKEN}
|
- GITEA_TOKEN=${GITEA_TOKEN}
|
||||||
- DEFAULT_RUNNER_NAME=buildx-ubuntu-22
|
- DEFAULT_RUNNER_NAME=${DEFAULT_RUNNER_NAME:-buildx-ubuntu-22}
|
||||||
- DEFAULT_RUNNER_LABEL=ubuntu-22.04:host://ubuntu:22.04,company-server:host://ubuntu:22.04,buildx-ubuntu-22:host://ubuntu:22.04
|
- DEFAULT_RUNNER_LABEL=${DEFAULT_RUNNER_LABEL:-ubuntu-22.04:host://ubuntu:22.04,company-server:host://ubuntu:22.04,buildx-ubuntu-22:host://ubuntu:22.04}
|
||||||
|
|
||||||
# 如果需要使用代理,取消下面的注释并修改为你的代理地址
|
# 如果需要使用代理,取消下面的注释并修改为你的代理地址
|
||||||
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
|
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
GITEA_INSTANCE=https://git.mytsl.cn
|
GITEA_INSTANCE=https://git.mytsl.cn
|
||||||
GITEA_TOKEN=
|
GITEA_TOKEN=
|
||||||
|
DEFAULT_RUNNER_NAME=standard-ubuntu-22
|
||||||
|
DEFAULT_RUNNER_LABEL=ubuntu-22.04:host://ubuntu:22.04,company-server:host://ubuntu:22.04,standard-ubuntu-22:host://ubuntu:22.04
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ services:
|
||||||
- ENABLE_BUILDX=false
|
- ENABLE_BUILDX=false
|
||||||
- GITEA_INSTANCE=${GITEA_INSTANCE}
|
- GITEA_INSTANCE=${GITEA_INSTANCE}
|
||||||
- GITEA_TOKEN=${GITEA_TOKEN}
|
- GITEA_TOKEN=${GITEA_TOKEN}
|
||||||
- DEFAULT_RUNNER_NAME=standard-ubuntu-22
|
- DEFAULT_RUNNER_NAME=${DEFAULT_RUNNER_NAME:-standard-ubuntu-22}
|
||||||
- DEFAULT_RUNNER_LABEL=ubuntu-22.04:host://ubuntu:22.04,company-server:host://ubuntu:22.04,standard-ubuntu-22:host://ubuntu:22.04
|
- DEFAULT_RUNNER_LABEL=${DEFAULT_RUNNER_LABEL:-ubuntu-22.04:host://ubuntu:22.04,company-server:host://ubuntu:22.04,standard-ubuntu-22:host://ubuntu:22.04}
|
||||||
|
|
||||||
# 如果需要使用代理,取消下面的注释并修改为你的代理地址
|
# 如果需要使用代理,取消下面的注释并修改为你的代理地址
|
||||||
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
|
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,10 @@ test_check_crlf_works_from_preset_directory() {
|
||||||
)
|
)
|
||||||
|
|
||||||
! rg -q "文件不存在" "${output_file}" || fail "check_crlf.sh should inspect sibling common scripts even when invoked from preset directory"
|
! rg -q "文件不存在" "${output_file}" || fail "check_crlf.sh should inspect sibling common scripts even when invoked from preset directory"
|
||||||
|
! rg -q "是否立即重启容器" "${output_file}" || fail "check_crlf.sh should not prompt to restart containers"
|
||||||
|
! rg -q "docker compose down" "${output_file}" || fail "check_crlf.sh should not include compose restart commands"
|
||||||
|
! rg -q "docker compose build --no-cache" "${output_file}" || fail "check_crlf.sh should not take responsibility for rebuilding containers"
|
||||||
|
! rg -q "docker compose up -d" "${output_file}" || fail "check_crlf.sh should not take responsibility for starting containers"
|
||||||
|
|
||||||
for file_name in entrypoint.sh setup.sh upgrade.sh register.sh manage.sh; do
|
for file_name in entrypoint.sh setup.sh upgrade.sh register.sh manage.sh; do
|
||||||
! has_crlf "${common_dir}/${file_name}" || fail "${file_name} should have CRLF fixed"
|
! has_crlf "${common_dir}/${file_name}" || fail "${file_name} should have CRLF fixed"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
REPO_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd)
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
echo "FAIL: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_root=$(mktemp -d)
|
||||||
|
lang_summary="${temp_root}/lang_summary.txt"
|
||||||
|
output_dir="${temp_root}/badges"
|
||||||
|
readme_path="${temp_root}/README.md"
|
||||||
|
|
||||||
|
cat > "${lang_summary}" <<'EOF'
|
||||||
|
python|Python|1200|1,200|12|3572A5|python
|
||||||
|
typescript|TypeScript|800|800|9|3178c6|typescript
|
||||||
|
yaml|YAML|120|120|5|CB171E|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
python3 "${REPO_ROOT}/.gitea/ci/render_stats_svgs.py" \
|
||||||
|
--output-dir "${output_dir}" \
|
||||||
|
--readme-path "${readme_path}" \
|
||||||
|
--repo "csh/actions-template" \
|
||||||
|
--generated-by "Gitea Actions" \
|
||||||
|
--updated-at "2026-05-22 12:00:00 UTC" \
|
||||||
|
--total-code 2120 \
|
||||||
|
--formatted-total-code "2,120" \
|
||||||
|
--total-files 26 \
|
||||||
|
--formatted-total-files "26" \
|
||||||
|
--language-count 3 \
|
||||||
|
--exclude-dirs "node_modules,dist,.git" \
|
||||||
|
--min-lines-threshold 10 \
|
||||||
|
--lang-summary "${lang_summary}"
|
||||||
|
|
||||||
|
test -f "${output_dir}/total-lines.svg" || fail "renderer should create total-lines.svg"
|
||||||
|
test -f "${output_dir}/total-files.svg" || fail "renderer should create total-files.svg"
|
||||||
|
test -f "${output_dir}/language-count.svg" || fail "renderer should create language-count.svg"
|
||||||
|
test -f "${output_dir}/python-lines.svg" || fail "renderer should create per-language svg badges"
|
||||||
|
test -f "${readme_path}" || fail "renderer should create root README.md"
|
||||||
|
|
||||||
|
grep -q '<svg' "${output_dir}/total-lines.svg" || fail "svg badges should contain svg markup"
|
||||||
|
grep -q '2,120' "${output_dir}/total-lines.svg" || fail "total-lines badge should include formatted total code"
|
||||||
|
grep -q 'Python' "${output_dir}/python-lines.svg" || fail "language badge should include display name"
|
||||||
|
grep -q '\./badges/total-lines.svg' "${readme_path}" || fail "README should reference badges via relative svg paths"
|
||||||
|
grep -q '\./badges/python-lines.svg' "${readme_path}" || fail "README should reference language badges via relative svg paths"
|
||||||
|
grep -q 'Gitea Actions' "${readme_path}" || fail "README should mention Gitea Actions"
|
||||||
|
grep -q '此分支由 Gitea Actions 自动生成和维护' "${readme_path}" || fail "README should explain that stats branch is generated"
|
||||||
|
grep -q '请勿手动修改此分支的内容' "${readme_path}" || fail "README should warn against manual edits"
|
||||||
|
! grep -q 'GitHub Actions' "${readme_path}" || fail "README should not mention GitHub Actions"
|
||||||
|
! find "${temp_root}" -name '*.json' | grep -q . || fail "renderer should not create json badge payloads"
|
||||||
|
|
||||||
|
rm -rf "${temp_root}"
|
||||||
|
|
||||||
|
echo "stats_svg_render_test.sh: PASS"
|
||||||
|
|
@ -60,10 +60,12 @@ test_workflow_doc_describes_workspace_architecture() {
|
||||||
|
|
||||||
file="${REPO_ROOT}/WORKFLOW.md"
|
file="${REPO_ROOT}/WORKFLOW.md"
|
||||||
|
|
||||||
grep -q 'Git bare 镜像 + 工作副本' "${file}" || fail "WORKFLOW.md should explicitly describe the bare mirror plus worktree-style model"
|
grep -Eq '^## .*运行模型' "${file}" || fail "WORKFLOW.md should include a run model section"
|
||||||
grep -q '共享仓库缓存' "${file}" || fail "WORKFLOW.md should describe the shared repository cache model"
|
grep -q 'bare' "${file}" || fail "WORKFLOW.md should describe the bare mirror model"
|
||||||
grep -q '独立工作副本' "${file}" || fail "WORKFLOW.md should describe isolated per-job work copies"
|
grep -q '工作副本' "${file}" || fail "WORKFLOW.md should describe isolated work copies"
|
||||||
grep -q '任务结束后' "${file}" || fail "WORKFLOW.md should mention cleanup after workflow completion"
|
grep -Eq '共享.*缓存|缓存.*共享' "${file}" || fail "WORKFLOW.md should describe the shared cache model"
|
||||||
|
grep -Eq '并发|隔离' "${file}" || fail "WORKFLOW.md should mention concurrency or isolation tradeoffs"
|
||||||
|
grep -Eq '结束后.*清理|清理.*工作副本' "${file}" || fail "WORKFLOW.md should mention cleanup after workflow completion"
|
||||||
! rg -q 'bootstrap_workspace\.sh' "${file}" || fail "WORKFLOW.md should describe architecture rather than helper implementation"
|
! rg -q 'bootstrap_workspace\.sh' "${file}" || fail "WORKFLOW.md should describe architecture rather than helper implementation"
|
||||||
! rg -q '/data/git-mirrors|/home/workspace/jobs' "${file}" || fail "WORKFLOW.md should avoid implementation-specific workspace paths"
|
! rg -q '/data/git-mirrors|/home/workspace/jobs' "${file}" || fail "WORKFLOW.md should avoid implementation-specific workspace paths"
|
||||||
}
|
}
|
||||||
|
|
@ -88,13 +90,13 @@ test_readme_has_project_intro_and_navigation() {
|
||||||
|
|
||||||
file="${REPO_ROOT}/README.md"
|
file="${REPO_ROOT}/README.md"
|
||||||
|
|
||||||
grep -q '^## 📖 项目简介$' "${file}" || fail "README.md should provide a project intro section"
|
grep -Eq '^## .*项目简介' "${file}" || fail "README.md should provide a project intro section"
|
||||||
grep -q '^## 📂 文档导航$' "${file}" || fail "README.md should provide a document navigation section"
|
grep -Eq '^## .*文档导航' "${file}" || fail "README.md should provide a document navigation section"
|
||||||
grep -q '\[DEPLOYMENT.md\](\./DEPLOYMENT.md)' "${file}" || fail "README.md should link to DEPLOYMENT.md"
|
grep -q '\[DEPLOYMENT.md\](\./DEPLOYMENT.md)' "${file}" || fail "README.md should link to DEPLOYMENT.md"
|
||||||
grep -q '\[WORKFLOW.md\](\./WORKFLOW.md)' "${file}" || fail "README.md should link to WORKFLOW.md"
|
grep -q '\[WORKFLOW.md\](\./WORKFLOW.md)' "${file}" || fail "README.md should link to WORKFLOW.md"
|
||||||
grep -q '具体部署步骤.*\[DEPLOYMENT.md\](\./DEPLOYMENT.md)' "${file}" || fail "README.md should delegate deployment details to DEPLOYMENT.md"
|
|
||||||
! rg -q '^## 🚀 快速提示$' "${file}" || fail "README.md should not carry concrete deployment tip sections"
|
! rg -q '^## 🚀 快速提示$' "${file}" || fail "README.md should not carry concrete deployment tip sections"
|
||||||
! rg -q '^## ⚙️ 当前默认行为$' "${file}" || fail "README.md should not carry default behavior details"
|
! rg -q '^## ⚙️ 当前默认行为$' "${file}" || fail "README.md should not carry default behavior details"
|
||||||
|
! rg -q 'docker compose (build|up|exec)' "${file}" || fail "README.md should stay high-level and leave compose commands to deployment docs"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_deployment_doc_stays_runner_focused() {
|
test_deployment_doc_stays_runner_focused() {
|
||||||
|
|
@ -124,9 +126,111 @@ test_preset_env_examples_exist() {
|
||||||
test -f "${file}" || fail "missing env example: ${file}"
|
test -f "${file}" || fail "missing env example: ${file}"
|
||||||
grep -q '^GITEA_INSTANCE=https://git.mytsl.cn$' "${file}" || fail "${file} should include company default instance"
|
grep -q '^GITEA_INSTANCE=https://git.mytsl.cn$' "${file}" || fail "${file} should include company default instance"
|
||||||
grep -q '^GITEA_TOKEN=$' "${file}" || fail "${file} should include empty token placeholder"
|
grep -q '^GITEA_TOKEN=$' "${file}" || fail "${file} should include empty token placeholder"
|
||||||
|
grep -q '^DEFAULT_RUNNER_NAME=' "${file}" || fail "${file} should define DEFAULT_RUNNER_NAME override example"
|
||||||
|
grep -q '^DEFAULT_RUNNER_LABEL=' "${file}" || fail "${file} should define DEFAULT_RUNNER_LABEL override example"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_preset_compose_supports_runner_identity_overrides() {
|
||||||
|
grep -q 'DEFAULT_RUNNER_NAME=${DEFAULT_RUNNER_NAME:-standard-ubuntu-22}' \
|
||||||
|
"${REPO_ROOT}/docker-runner/presets/standard-ubuntu-22/docker-compose.yml" || \
|
||||||
|
fail "standard preset should allow DEFAULT_RUNNER_NAME override via env"
|
||||||
|
grep -q 'DEFAULT_RUNNER_LABEL=${DEFAULT_RUNNER_LABEL:-ubuntu-22.04:host://ubuntu:22.04,company-server:host://ubuntu:22.04,standard-ubuntu-22:host://ubuntu:22.04}' \
|
||||||
|
"${REPO_ROOT}/docker-runner/presets/standard-ubuntu-22/docker-compose.yml" || \
|
||||||
|
fail "standard preset should allow DEFAULT_RUNNER_LABEL override via env"
|
||||||
|
|
||||||
|
grep -q 'DEFAULT_RUNNER_NAME=${DEFAULT_RUNNER_NAME:-buildx-ubuntu-22}' \
|
||||||
|
"${REPO_ROOT}/docker-runner/presets/buildx-ubuntu-22/docker-compose.yml" || \
|
||||||
|
fail "buildx ubuntu preset should allow DEFAULT_RUNNER_NAME override via env"
|
||||||
|
grep -q 'DEFAULT_RUNNER_LABEL=${DEFAULT_RUNNER_LABEL:-ubuntu-22.04:host://ubuntu:22.04,company-server:host://ubuntu:22.04,buildx-ubuntu-22:host://ubuntu:22.04}' \
|
||||||
|
"${REPO_ROOT}/docker-runner/presets/buildx-ubuntu-22/docker-compose.yml" || \
|
||||||
|
fail "buildx ubuntu preset should allow DEFAULT_RUNNER_LABEL override via env"
|
||||||
|
|
||||||
|
grep -q 'DEFAULT_RUNNER_NAME=${DEFAULT_RUNNER_NAME:-buildx-archlinux}' \
|
||||||
|
"${REPO_ROOT}/docker-runner/presets/buildx-archlinux/docker-compose.yml" || \
|
||||||
|
fail "buildx arch preset should allow DEFAULT_RUNNER_NAME override via env"
|
||||||
|
grep -q 'DEFAULT_RUNNER_LABEL=${DEFAULT_RUNNER_LABEL:-archlinux:host://archlinux:latest,company-server:host://archlinux:latest,buildx-archlinux:host://archlinux:latest}' \
|
||||||
|
"${REPO_ROOT}/docker-runner/presets/buildx-archlinux/docker-compose.yml" || \
|
||||||
|
fail "buildx arch preset should allow DEFAULT_RUNNER_LABEL override via env"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_register_requires_python_yaml_path() {
|
||||||
|
local file
|
||||||
|
|
||||||
|
file="${REPO_ROOT}/docker-runner/common/register.sh"
|
||||||
|
|
||||||
|
! rg -q 'Python configuration failed, using basic sed' "${file}" || fail "register.sh should not fall back to basic sed when Python config fails"
|
||||||
|
! rg -q 'Python3 not found, applying basic configuration' "${file}" || fail "register.sh should not continue with basic configuration when Python3 is missing"
|
||||||
|
! rg -q "sed -i 's/capacity: 1/capacity: 4/g' config.yaml" "${file}" || fail "register.sh should not mutate config.yaml via sed fallback"
|
||||||
|
grep -q "python3 << PYEOF" "${file}" || fail "register.sh should keep Python-based config generation"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_setup_requires_upgrade_helper() {
|
||||||
|
local file
|
||||||
|
|
||||||
|
file="${REPO_ROOT}/docker-runner/common/setup.sh"
|
||||||
|
|
||||||
|
grep -q '^# shellcheck source=/dev/null$' "${file}" || fail "setup.sh should source upgrade.sh directly"
|
||||||
|
grep -q '^source "\${UPGRADE_HELPER}"$' "${file}" || fail "setup.sh should require sourcing upgrade.sh"
|
||||||
|
! grep -Fq 'if [ -f "${UPGRADE_HELPER}" ]; then' "${file}" || fail "setup.sh should not treat upgrade helper as optional"
|
||||||
|
! rg -q 'declare -F resolve_latest_version_or_fallback' "${file}" || fail "setup.sh should not guard helper functions with declare -F"
|
||||||
|
! rg -q 'declare -F validate_version' "${file}" || fail "setup.sh should not guard validate_version with declare -F"
|
||||||
|
! rg -q 'declare -F validate_binary_arch_or_fail' "${file}" || fail "setup.sh should not guard binary validation with declare -F"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_entrypoint_uses_shared_buildx_builder_creation() {
|
||||||
|
local file
|
||||||
|
|
||||||
|
file="${REPO_ROOT}/docker-runner/common/entrypoint.sh"
|
||||||
|
|
||||||
|
grep -q '^create_buildx_builder() {$' "${file}" || fail "entrypoint.sh should extract Buildx builder creation into a helper"
|
||||||
|
grep -q '^ensure_buildx_builder() {$' "${file}" || fail "entrypoint.sh should extract Buildx builder selection into a helper"
|
||||||
|
grep -q '^[[:space:]]*create_buildx_builder$' "${file}" || fail "entrypoint.sh should use shared builder creation during initial setup"
|
||||||
|
grep -q '^[[:space:]]*ensure_buildx_builder$' "${file}" || fail "entrypoint.sh should use shared builder selection during reuse"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_stats_workflow_avoids_eval_find() {
|
||||||
|
local file
|
||||||
|
|
||||||
|
file="${REPO_ROOT}/.gitea/workflows/update_stats_badge.yaml"
|
||||||
|
|
||||||
|
! rg -q 'eval "find ' "${file}" || fail "stats workflow should avoid eval when building find commands"
|
||||||
|
grep -q 'EXCLUDE_FIND_ARGS=()' "${file}" || fail "stats workflow should build reusable find exclusion arrays"
|
||||||
|
grep -q 'LANG_FIND_ARGS=()' "${file}" || fail "stats workflow should build language-specific find arguments via arrays"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_stats_workflow_uses_svg_badges() {
|
||||||
|
local file workflow_doc
|
||||||
|
|
||||||
|
file="${REPO_ROOT}/.gitea/workflows/update_stats_badge.yaml"
|
||||||
|
workflow_doc="${REPO_ROOT}/WORKFLOW.md"
|
||||||
|
|
||||||
|
grep -q 'total-lines.svg' "${file}" || fail "stats workflow should generate total-lines.svg"
|
||||||
|
grep -q 'total-files.svg' "${file}" || fail "stats workflow should generate total-files.svg"
|
||||||
|
grep -q 'language-count.svg' "${file}" || fail "stats workflow should generate language-count.svg"
|
||||||
|
grep -Fq '{language}-lines.svg' "${file}" || fail "stats workflow should describe per-language svg badges"
|
||||||
|
grep -q '/src/branch/${{ env.BADGE_BRANCH }}/README.md' "${file}" || fail "stats workflow summary should link to stats branch root README"
|
||||||
|
! rg -q '/src/branch/\$\{\{ env.BADGE_BRANCH \}\}/\$\{\{ env.BADGE_DIR \}\}/README\.md' "${file}" || fail "stats workflow should not place README.md under the badge directory"
|
||||||
|
! rg -q 'img\.shields\.io/endpoint' "${file}" || fail "stats workflow should not depend on Shields endpoint badges"
|
||||||
|
! rg -q 'total-lines\.json|total-files\.json|\$\{lang_id\}-lines\.json|\$\{lang_id\}-files\.json' "${file}" || fail "stats workflow should not generate json badge payloads"
|
||||||
|
! rg -q 'GitHub Actions' "${file}" || fail "stats workflow should not mention GitHub Actions"
|
||||||
|
grep -q 'Gitea Actions' "${file}" || fail "stats workflow should mention Gitea Actions in generated content"
|
||||||
|
|
||||||
|
grep -q 'badges/cpp-lines.svg' "${workflow_doc}" || fail "WORKFLOW.md should show svg badge usage examples"
|
||||||
|
! rg -q 'img\.shields\.io/endpoint|cpp-lines\.json' "${workflow_doc}" || fail "WORKFLOW.md should not document json endpoint badge usage"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_entrypoint_uses_platform_aware_multiarch_verification() {
|
||||||
|
local file
|
||||||
|
|
||||||
|
file="${REPO_ROOT}/docker-runner/common/entrypoint.sh"
|
||||||
|
|
||||||
|
grep -q 'docker run --rm --platform linux/arm64 alpine uname -m' "${file}" || fail "entrypoint.sh should verify arm64 support with an explicit platform"
|
||||||
|
grep -q 'docker run --rm --platform linux/amd64 alpine uname -m' "${file}" || fail "entrypoint.sh should verify amd64 support with an explicit platform"
|
||||||
|
! rg -q 'docker run --rm arm64v8/alpine uname -m' "${file}" || fail "entrypoint.sh should not use arm64v8/alpine without an explicit platform"
|
||||||
|
! rg -q 'docker run --rm amd64/alpine uname -m' "${file}" || fail "entrypoint.sh should not use amd64/alpine without an explicit platform"
|
||||||
|
}
|
||||||
|
|
||||||
test_preset_compose_uses_env_for_instance
|
test_preset_compose_uses_env_for_instance
|
||||||
test_workflows_do_not_hardcode_company_server
|
test_workflows_do_not_hardcode_company_server
|
||||||
test_stats_workflow_uses_workflow_secret_consistently
|
test_stats_workflow_uses_workflow_secret_consistently
|
||||||
|
|
@ -138,5 +242,12 @@ test_readme_has_project_intro_and_navigation
|
||||||
test_deployment_doc_stays_runner_focused
|
test_deployment_doc_stays_runner_focused
|
||||||
test_presets_define_expected_hostname
|
test_presets_define_expected_hostname
|
||||||
test_preset_env_examples_exist
|
test_preset_env_examples_exist
|
||||||
|
test_preset_compose_supports_runner_identity_overrides
|
||||||
|
test_register_requires_python_yaml_path
|
||||||
|
test_setup_requires_upgrade_helper
|
||||||
|
test_entrypoint_uses_shared_buildx_builder_creation
|
||||||
|
test_stats_workflow_avoids_eval_find
|
||||||
|
test_stats_workflow_uses_svg_badges
|
||||||
|
test_entrypoint_uses_platform_aware_multiarch_verification
|
||||||
|
|
||||||
echo "template_defaults_test.sh: PASS"
|
echo "template_defaults_test.sh: PASS"
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,31 @@ assert_eq() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
make_fake_elf() {
|
||||||
|
local path=$1
|
||||||
|
local arch=$2
|
||||||
|
local machine_bytes=""
|
||||||
|
|
||||||
|
case "${arch}" in
|
||||||
|
amd64)
|
||||||
|
machine_bytes='\076\000'
|
||||||
|
;;
|
||||||
|
arm64)
|
||||||
|
machine_bytes='\267\000'
|
||||||
|
;;
|
||||||
|
arm-7)
|
||||||
|
machine_bytes='\050\000'
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "FAIL: unsupported fake ELF arch ${arch}" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf '\177ELF\002\001\001\000\000\000\000\000\000\000\000\000\002\000%b' \
|
||||||
|
"${machine_bytes}" > "${path}"
|
||||||
|
}
|
||||||
|
|
||||||
test_extract_versions_from_listing() {
|
test_extract_versions_from_listing() {
|
||||||
local html
|
local html
|
||||||
html=$(cat <<'EOF'
|
html=$(cat <<'EOF'
|
||||||
|
|
@ -90,10 +115,61 @@ test_resolve_latest_version_or_fallback_uses_fallback() {
|
||||||
"resolve_latest_version_or_fallback should fall back when detected version is invalid"
|
"resolve_latest_version_or_fallback should fall back when detected version is invalid"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_detect_binary_arch_from_file() {
|
||||||
|
local temp_root amd64_file arm64_file arm7_file text_file
|
||||||
|
|
||||||
|
temp_root=$(mktemp -d)
|
||||||
|
amd64_file="${temp_root}/act_runner-amd64"
|
||||||
|
arm64_file="${temp_root}/act_runner-arm64"
|
||||||
|
arm7_file="${temp_root}/act_runner-arm7"
|
||||||
|
text_file="${temp_root}/not-elf"
|
||||||
|
|
||||||
|
make_fake_elf "${amd64_file}" "amd64"
|
||||||
|
make_fake_elf "${arm64_file}" "arm64"
|
||||||
|
make_fake_elf "${arm7_file}" "arm-7"
|
||||||
|
printf 'not an elf\n' > "${text_file}"
|
||||||
|
|
||||||
|
assert_eq "amd64" "$(detect_binary_arch_from_file "${amd64_file}")" \
|
||||||
|
"detect_binary_arch_from_file should recognize amd64 ELF binaries"
|
||||||
|
assert_eq "arm64" "$(detect_binary_arch_from_file "${arm64_file}")" \
|
||||||
|
"detect_binary_arch_from_file should recognize arm64 ELF binaries"
|
||||||
|
assert_eq "arm-7" "$(detect_binary_arch_from_file "${arm7_file}")" \
|
||||||
|
"detect_binary_arch_from_file should recognize arm-7 ELF binaries"
|
||||||
|
assert_eq "unknown" "$(detect_binary_arch_from_file "${text_file}")" \
|
||||||
|
"detect_binary_arch_from_file should reject non-ELF files"
|
||||||
|
|
||||||
|
rm -rf "${temp_root}"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_binary_arch_matches_target() {
|
||||||
|
local temp_root amd64_file arm64_file
|
||||||
|
|
||||||
|
temp_root=$(mktemp -d)
|
||||||
|
amd64_file="${temp_root}/act_runner-amd64"
|
||||||
|
arm64_file="${temp_root}/act_runner-arm64"
|
||||||
|
|
||||||
|
make_fake_elf "${amd64_file}" "amd64"
|
||||||
|
make_fake_elf "${arm64_file}" "arm64"
|
||||||
|
|
||||||
|
if ! binary_arch_matches_target "${amd64_file}" "amd64"; then
|
||||||
|
echo "FAIL: binary_arch_matches_target should accept matching architectures" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if binary_arch_matches_target "${arm64_file}" "amd64"; then
|
||||||
|
echo "FAIL: binary_arch_matches_target should reject mismatched architectures" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf "${temp_root}"
|
||||||
|
}
|
||||||
|
|
||||||
test_extract_versions_from_listing
|
test_extract_versions_from_listing
|
||||||
test_pick_latest_version
|
test_pick_latest_version
|
||||||
test_extract_version_number_accepts_v_prefix
|
test_extract_version_number_accepts_v_prefix
|
||||||
test_resolve_latest_version_or_fallback_prefers_latest
|
test_resolve_latest_version_or_fallback_prefers_latest
|
||||||
test_resolve_latest_version_or_fallback_uses_fallback
|
test_resolve_latest_version_or_fallback_uses_fallback
|
||||||
|
test_detect_binary_arch_from_file
|
||||||
|
test_binary_arch_matches_target
|
||||||
|
|
||||||
echo "upgrade_test.sh: PASS"
|
echo "upgrade_test.sh: PASS"
|
||||||
|
|
|
||||||
|
|
@ -53,14 +53,6 @@ test_sanitize_job_name() {
|
||||||
assert_eq "release-notes-job" "${actual}" "job names should be filesystem-safe"
|
assert_eq "release-notes-job" "${actual}" "job names should be filesystem-safe"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_build_lock_path() {
|
|
||||||
local actual
|
|
||||||
|
|
||||||
actual=$(build_mirror_lock_path "/data/git-mirrors" "csh" "actions-template")
|
|
||||||
|
|
||||||
assert_eq "/data/git-mirrors/csh/actions-template.git.lock" "${actual}" "lock path should sit beside the bare mirror"
|
|
||||||
}
|
|
||||||
|
|
||||||
test_repo_owner_and_name_parsing() {
|
test_repo_owner_and_name_parsing() {
|
||||||
local actual
|
local actual
|
||||||
|
|
||||||
|
|
@ -167,7 +159,6 @@ test_presets_do_not_mount_workspace_helper() {
|
||||||
test_repo_path_layout
|
test_repo_path_layout
|
||||||
test_job_identity_prefers_run_metadata
|
test_job_identity_prefers_run_metadata
|
||||||
test_sanitize_job_name
|
test_sanitize_job_name
|
||||||
test_build_lock_path
|
|
||||||
test_repo_owner_and_name_parsing
|
test_repo_owner_and_name_parsing
|
||||||
test_prepare_and_cleanup_workspace
|
test_prepare_and_cleanup_workspace
|
||||||
test_register_default_capacity_is_four
|
test_register_default_capacity_is_four
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue