Compare commits

...

49 Commits

Author SHA1 Message Date
csh db75d0ebc5 `gitea`的`runner`参数化,可以通过`docker-compose.yml`进行配置
Ubuntu System Information / show-system-info (push) Successful in 39s Details
🐛 修复`Dockerfile`缺少系统包
2025-12-10 14:42:20 +08:00
csh 1b5611cf3e 🐛 修复贡献者过滤自定义bot名称
Ubuntu System Information / show-system-info (push) Successful in 38s Details
2025-12-09 17:51:48 +08:00
ci[bot] 44e92ab89a 📝 Auto update CHANGELOG for 0.0.4-rc1 [skip ci]
Ubuntu System Information / show-system-info (push) Successful in 38s Details
2025-12-09 08:37:04 +00:00
csh eaedcd3bb2 🐛 Bug修复
Ubuntu System Information / show-system-info (push) Successful in 38s Details
2025-12-09 16:23:17 +08:00
csh 0e89b21c41 🐛 修复`lsb_release`命令报错
Ubuntu System Information / show-system-info (push) Failing after 38s Details
2025-12-09 16:17:22 +08:00
csh 39820e656a ♻️ 重构代码和`docker-runner`项目结构
Ubuntu System Information / show-system-info (push) Failing after 38s Details
 支持`docker`部署`archlinux`,预装`clang`跨平台编译工具链
2025-12-09 16:06:25 +08:00
csh 4829434408 🚀 优化bot名字,更具辨识度
Hello from ImmortalWrt / say-hello (push) Successful in 1s Details
2025-11-17 18:19:34 +08:00
ci-bot 9c572b64d1 📝 Auto update CHANGELOG for 0.0.3-rc3 [skip ci]
Hello from ImmortalWrt / say-hello (push) Successful in 1s Details
2025-11-17 10:08:49 +00:00
csh 49f11fb2d8 🔧 工作流的bot名字变更为`ci-bot`
Hello from ImmortalWrt / say-hello (push) Successful in 1s Details
2025-11-17 18:07:24 +08:00
csh 5ec0af5883 Merge branch 'main' of https://git.mytsl.cn/csh/actions-template
Hello from ImmortalWrt / say-hello (push) Successful in 1s Details
2025-11-17 17:52:36 +08:00
csh c81aac970c 🔧 `docker`的默认配置变更 2025-11-17 17:52:24 +08:00
github-actions[bot] 566a1754a7 📝 Auto update CHANGELOG for 0.0.3-rc2 [skip ci]
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 06:29:52 +00:00
csh 1b7eed5d93 🐛 不行就先放弃了
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 14:29:15 +08:00
csh 3a17dc4b52 🐛 ok了嘛 2025-11-03 14:21:19 +08:00
csh e71d9d01fd 🐛 修复过滤转义失败
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 14:08:57 +08:00
csh 160c5ebade 🐛 匹配逻辑
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 12:04:12 +08:00
csh fb4e7f3b1f Merge branch 'main' of https://git.mytsl.cn/csh/actions-template
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 12:00:29 +08:00
csh 9a472a4271 🐛 修复匹配正则问题 2025-11-03 12:00:14 +08:00
github-actions[bot] 0d3ccadfef 📝 Auto update CHANGELOG for 0.0.3-rc1 [skip ci]
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 03:38:53 +00:00
csh fa21511d1a 🐛 fix bug
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 11:38:30 +08:00
csh 7373553270 更新 CHANGELOG.md [skip ci]
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 11:28:25 +08:00
github-actions[bot] 3308b4354c 📝 Auto update CHANGELOG for 0.0.3-rc1 [skip ci]
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 03:25:11 +00:00
csh f422a75c1c Merge branch 'main' of https://git.mytsl.cn/csh/actions-template
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 11:24:45 +08:00
csh fc76a1f625 🐛 fix bug 2025-11-03 11:24:41 +08:00
csh 4ed8099158 更新 CHANGELOG.md
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 11:22:06 +08:00
csh bf6698ab3e 🐛 修复commit信息提取错误
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 11:19:46 +08:00
github-actions[bot] b57c0cc9d6 📝 Auto update CHANGELOG for 0.0.3-rc1 [skip ci]
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 02:44:03 +00:00
csh 1f535e0675 ♻️ 重构`changelog_and_release.yml`
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-03 10:40:26 +08:00
csh 9961226704 🐛 移除默认上传`CHANGELOG.md`
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-02 23:17:07 +08:00
github-actions[bot] b4c47c4085 📝 Auto update CHANGELOG for 0.0.2-rc1 [skip ci]
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-02 15:15:01 +00:00
csh ec5785cc6a 🎨 代码格式化
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
:fix: 修复识别`CHANGELOG`标题问题
2025-11-02 23:13:48 +08:00
csh 84e2751668 🐛 修复tag获取错误问题
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-02 23:03:22 +08:00
csh 0bb627fd34 🐛 修复bug
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-02 22:51:04 +08:00
csh 9cde246b20 ♻️ 重构`changelog_and_release`
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-02 22:39:12 +08:00
csh aaeed0d919 🐛 修复日期
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-02 22:11:41 +08:00
csh f57e6984c0 🐛 修复`update_state_badge.yml`自动生成的`README`日期错误
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-02 22:08:17 +08:00
csh 2ac94aff1c 📝 更新`WORKFLOW`说明
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-02 21:56:34 +08:00
csh d7a0c7a85b 🐛 help me
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-02 21:50:27 +08:00
csh 948bcfa787 🐛 继续修修
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-02 21:33:52 +08:00
csh 23a7fb5a28 🐛 继续修复测试
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-02 21:13:51 +08:00
csh b616573bfd 🐛 修`update_stats_badge.yml`,还没跑起来
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
Update Stats Badege / update-stats (push) Failing after 0s Details
2025-11-02 21:07:01 +08:00
csh 5b3629fb73 🐛 修复`update_stats_badge.yml` 克隆bug
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
Update Stats Badege / update-stats (push) Failing after 2s Details
♻️ 重构`changelog_and_release.yml`部分代码
2025-11-02 20:58:07 +08:00
csh 38e6c82394 🐛 修复token配置错误
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
Update Stats Badege / update-stats (push) Failing after 0s Details
2025-11-02 20:41:03 +08:00
csh 376c1a2866 🐛 修复runs-on错误
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
Update Stats Badege / update-stats (push) Failing after 0s Details
2025-11-02 20:39:27 +08:00
csh d5a7d5506e 新增:代码统计徽章 action: `update_stats_badge.yml`
Update Stats Badege / update-stats (push) Waiting to run Details
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
📝 新增:`update_stats_badge`的简要说明
2025-11-02 20:35:04 +08:00
csh c0aef0f531 :demo: 文档更新 2025-11-02 19:40:26 +08:00
csh a21adb9fcc 📦 workflows的文件进行更名,并且调整后缀为`yml`
♻️ `changelog_and_release`代码重构,把配置项全部移动到`env`
2025-11-02 19:38:04 +08:00
csh 8971cbffc3 🗑️ 移除发布Release时候自动上传CHANGELOG.md
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-01 13:48:29 +08:00
github-actions[bot] cae1f4d4a7 📝 Auto update CHANGELOG for 0.0.1-rc1 [skip ci]
Hello from ImmortalWrt / say-hello (push) Successful in 0s Details
2025-11-01 02:09:05 +00:00
26 changed files with 3137 additions and 4256 deletions

View File

@ -1,935 +0,0 @@
name: 📦 Tag Release Automation
on:
push:
tags:
- "[0-9]*"
jobs:
changelog-and-release:
runs-on: ubuntu-22.04
steps:
- name: ⚙️ Configuration
id: config
run: |
echo "======================================"
echo "⚙️ Loading workflow configuration"
echo "======================================"
# ========================================
# 📝 自定义配置区域 - 在此修改所有配置
# ========================================
# 📌 语义版本号规范 (SemVer)
# 格式: MAJOR.MINOR.PATCH[-prerelease]
#
# 开发版本示例:
# - 1.0.0-beta1, 1.0.0-beta2, 1.0.0-beta10 等
# - 1.2.0-alpha1, 1.2.0-rc1 等
#
# 正式版本示例:
# - 1.0.0 (首个稳定版PATCH必须从0开始)
# - 1.1.0 (新增功能,向后兼容)
# - 1.1.1 (Bug修复)
# - 2.0.0 (破坏性变更,不向后兼容)
#
# ⚠️ 重要规则:
# 1. 开发版本都以 .0-beta[N] 或 .0-alpha[N] 结尾
# 2. 正式发布必须从 .0 开始(如 1.0.0, 1.1.0, 2.0.0
# 3. 多个开发版本(如1.0.0-beta1, 1.0.0-beta2)会累加到同一个CHANGELOG区域(1.0.0)
# 4. CHANGELOG内容是叠加而非覆盖
# Gitea 服务器地址(用于生成头像链接)
GITEA_SERVER="https://git.mytsl.cn"
# CHANGELOG 配置
CHANGELOG_SECTION_TITLE="### :pencil: What's Changed"
CHANGELOG_CONTRIBUTORS_TITLE="### :busts_in_silhouette: Contributors"
# CHANGELOG 版本号处理方式
# - "full": 使用完整 tag 名称2025.10.31-1-beta → ## :bookmark: 2025.10.31-1-beta
# - "strip": 去除 pre-release 后缀1.0.0-beta1 → ## :bookmark: 1.0.0
# ⚠️ 推荐使用 "strip" 模式以支持多个开发版本叠加到同一CHANGELOG区域
CHANGELOG_VERSION_MODE="strip"
# Release 配置
RELEASE_TITLE="{version}" # 可用变量: {version}
# Pre-release 配置
# - "false": 正式版本(会被标记为 latest
# - "true": 预发布版本(会被标记为 pre-release
# - "auto": 自动判断(根据 tag 名称中是否包含 alpha/beta/rc
RELEASE_PRERELEASE_MODE="auto"
# Draft 配置(是否创建为草稿)
RELEASE_IS_DRAFT="false" # true 或 false
# 需要忽略的提交模式(支持正则表达式)
IGNORE_PATTERNS=(
"^Merge "
"^:memo: Auto update CHANGELOG"
"\[skip ci\]"
"\[ci skip\]"
"^Bump version"
"^Release v"
"^chore: update config"
"^style: "
"^chore: format"
"^chore: lint"
"^Initial commit"
)
# Git 提交消息格式
COMMIT_MESSAGE=":memo: Auto update CHANGELOG for {version} [skip ci]"
# 额外要上传到 Release 的文件(空格分隔)
ADDITIONAL_RELEASE_FILES="" # 例如: "README.md LICENSE docs/guide.pdf"
# ========================================
# 以下为自动处理,无需修改
# ========================================
VERSION="${{ github.ref_name }}"
# 处理 CHANGELOG 版本号
if [[ "$CHANGELOG_VERSION_MODE" == "strip" ]]; then
# 去除 pre-release 后缀(-beta, -rc1, -alpha 等)
CHANGELOG_VERSION=$(echo "$VERSION" | sed -E 's/-(alpha|beta|rc|pre|preview|dev|test)[0-9]*$//')
echo "📝 CHANGELOG version mode: strip"
echo " Tag: $VERSION → CHANGELOG: $CHANGELOG_VERSION"
else
# 使用完整 tag 名称
CHANGELOG_VERSION="$VERSION"
echo "📝 CHANGELOG version mode: full"
echo " Tag: $VERSION → CHANGELOG: $CHANGELOG_VERSION"
fi
# 处理 Release 标题
RELEASE_TITLE="${RELEASE_TITLE//\{version\}/$VERSION}"
# 处理提交消息
COMMIT_MSG="${COMMIT_MESSAGE//\{version\}/$VERSION}"
# 智能判断 pre-release
if [[ "$RELEASE_PRERELEASE_MODE" == "auto" ]]; then
if [[ "$VERSION" =~ (alpha|beta|rc|pre|preview|dev|test) ]]; then
RELEASE_IS_PRERELEASE="true"
PRERELEASE_DETECTED="yes (auto-detected: $VERSION contains pre-release keyword)"
else
RELEASE_IS_PRERELEASE="false"
PRERELEASE_DETECTED="no (auto-detected: stable release)"
fi
else
RELEASE_IS_PRERELEASE="$RELEASE_PRERELEASE_MODE"
PRERELEASE_DETECTED="$RELEASE_PRERELEASE_MODE (manually configured)"
fi
# 导出配置到环境变量
echo "GITEA_SERVER=$GITEA_SERVER" >> $GITHUB_ENV
echo "CHANGELOG_SECTION_TITLE=$CHANGELOG_SECTION_TITLE" >> $GITHUB_ENV
echo "CHANGELOG_CONTRIBUTORS_TITLE=$CHANGELOG_CONTRIBUTORS_TITLE" >> $GITHUB_ENV
echo "CHANGELOG_VERSION=$CHANGELOG_VERSION" >> $GITHUB_ENV
echo "RELEASE_TITLE=$RELEASE_TITLE" >> $GITHUB_ENV
echo "RELEASE_IS_PRERELEASE=$RELEASE_IS_PRERELEASE" >> $GITHUB_ENV
echo "RELEASE_IS_DRAFT=$RELEASE_IS_DRAFT" >> $GITHUB_ENV
echo "COMMIT_MSG=$COMMIT_MSG" >> $GITHUB_ENV
echo "ADDITIONAL_RELEASE_FILES=$ADDITIONAL_RELEASE_FILES" >> $GITHUB_ENV
# 导出忽略模式(转换为 JSON 数组格式便于后续处理)
IGNORE_JSON="["
for pattern in "${IGNORE_PATTERNS[@]}"; do
IGNORE_JSON="${IGNORE_JSON}\"${pattern}\","
done
IGNORE_JSON="${IGNORE_JSON%,}]"
echo "IGNORE_PATTERNS_JSON=$IGNORE_JSON" >> $GITHUB_ENV
# 显示配置摘要
echo ""
echo "📋 Configuration Summary:"
echo " 🏷️ Tag version: $VERSION"
echo " 📝 CHANGELOG version: $CHANGELOG_VERSION"
echo " 🌐 Gitea Server: $GITEA_SERVER"
echo " 📄 CHANGELOG Title: $CHANGELOG_SECTION_TITLE"
echo " 🚀 Release Title: $RELEASE_TITLE"
echo " 🏷️ Pre-release: $PRERELEASE_DETECTED"
echo " 📄 Draft: $RELEASE_IS_DRAFT"
echo " 💬 Commit Message: $COMMIT_MSG"
echo " 📎 Additional Files: ${ADDITIONAL_RELEASE_FILES:-none}"
echo ""
echo "✅ Configuration loaded successfully"
echo ""
- name: 📂 Checkout repository
run: |
echo "======================================"
echo "🚀 Starting repository checkout"
echo "======================================"
REPO_NAME="${{ github.event.repository.name }}"
REPO_DIR="/home/workspace/$REPO_NAME"
echo "📁 Repository: $REPO_NAME"
echo "📍 Target directory: $REPO_DIR"
if [ -d "$REPO_DIR/.git" ]; then
echo "✓ Repository exists, updating..."
cd "$REPO_DIR"
git clean -fdx
git reset --hard
git fetch --all --tags --force
echo "✓ Repository updated"
else
echo "📥 Cloning repository..."
mkdir -p /home/workspace
git clone https://oauth2:${{ secrets.WORKFLOW }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git "$REPO_DIR"
cd "$REPO_DIR"
echo "✓ Repository cloned"
fi
echo "🏷️ Checking out tag: ${{ github.ref_name }}"
git checkout -f ${{ github.ref_name }}
echo "REPO_DIR=$REPO_DIR" >> $GITHUB_ENV
echo "✅ Checkout completed"
echo ""
- name: 🔍 Check if bot commit
id: check_bot
run: |
echo "======================================"
echo "🤖 Checking commit author"
echo "======================================"
cd ${{ env.REPO_DIR }}
LAST_AUTHOR=$(git log -1 --pretty=format:'%an')
echo "👤 Last commit author: $LAST_AUTHOR"
if [[ "$LAST_AUTHOR" == "github-actions[bot]" ]]; then
echo "⚠️ Bot commit detected - skipping to prevent loop"
echo "is_bot_commit=true" >> $GITHUB_OUTPUT
else
echo "✓ Non-bot commit - continuing workflow"
echo "is_bot_commit=false" >> $GITHUB_OUTPUT
fi
echo ""
- name: 📋 Check if version exists in CHANGELOG
if: steps.check_bot.outputs.is_bot_commit == 'false'
id: check_version
run: |
echo "======================================"
echo "🔎 Checking CHANGELOG for existing version"
echo "======================================"
cd ${{ env.REPO_DIR }}
CHANGELOG_VERSION="${{ env.CHANGELOG_VERSION }}"
echo "🏷️ CHANGELOG version to check: $CHANGELOG_VERSION"
git checkout main || git checkout master
git pull origin main || git pull origin master || true
if [ -f "CHANGELOG.md" ]; then
echo "✓ CHANGELOG.md found"
if grep -q "## :bookmark: ${CHANGELOG_VERSION}" CHANGELOG.md; then
echo " Version ${CHANGELOG_VERSION} already exists in CHANGELOG"
echo "📝 Will append new commits to existing entry (content accumulation mode)"
echo "version_exists=true" >> $GITHUB_OUTPUT
else
echo "✓ Version ${CHANGELOG_VERSION} not found - will generate new entry"
echo "version_exists=false" >> $GITHUB_OUTPUT
fi
else
echo " CHANGELOG.md does not exist - will create new file"
echo "version_exists=false" >> $GITHUB_OUTPUT
fi
git checkout ${{ github.ref_name }}
echo ""
- name: 📝 Generate CHANGELOG
if: steps.check_bot.outputs.is_bot_commit == 'false'
id: changelog
run: |
echo "======================================"
echo "📝 Generating CHANGELOG"
echo "======================================"
cd ${{ env.REPO_DIR }}
VERSION="${{ github.ref_name }}"
CHANGELOG_VERSION="${{ env.CHANGELOG_VERSION }}"
VERSION_EXISTS="${{ steps.check_version.outputs.version_exists }}"
PREVIOUS_TAG=$(git tag --sort=-creatordate | sed -n '2p')
echo "🏷️ Tag version: $VERSION"
echo "📝 CHANGELOG version: $CHANGELOG_VERSION"
echo "🏷️ Previous version: ${PREVIOUS_TAG:-none (first release)}"
if [ "$VERSION_EXISTS" = "true" ]; then
echo " Updating existing CHANGELOG entry"
else
echo " Creating new CHANGELOG entry"
fi
# 从环境变量加载忽略模式
IGNORE_PATTERNS=(
"^Merge "
"^:memo: Auto update CHANGELOG"
"\[skip ci\]"
"\[ci skip\]"
"^Bump version"
"^Release v"
"^chore: update config"
"^style: "
"^chore: format"
"^chore: lint"
)
echo "🔍 Scanning commits..."
TEMP_COMMITS=$(mktemp)
if [ -z "$PREVIOUS_TAG" ]; then
COMMIT_RANGE="HEAD"
else
COMMIT_RANGE="${PREVIOUS_TAG}..${VERSION}"
fi
git log $COMMIT_RANGE --no-merges --reverse --format="COMMIT_START%n%h%n%an%n%s%nBODY_START%n%b%nBODY_END" > "$TEMP_COMMITS"
declare -a COMMITS_HASH
declare -a COMMITS_AUTHOR
declare -a COMMITS_MESSAGE
declare -a COMMITS_BODY
COMMIT_INDEX=0
CURRENT_HASH=""
CURRENT_AUTHOR=""
CURRENT_MESSAGE=""
CURRENT_BODY=""
IN_BODY=false
while IFS= read -r line; do
if [[ "$line" == "COMMIT_START" ]]; then
if [ -n "$CURRENT_HASH" ]; then
SHOULD_SKIP=false
for pattern in "${IGNORE_PATTERNS[@]}"; do
if echo "$CURRENT_MESSAGE" | grep -qiE "$pattern"; then
SHOULD_SKIP=true
break
fi
done
if [ "$SHOULD_SKIP" = false ]; then
COMMITS_HASH[$COMMIT_INDEX]="$CURRENT_HASH"
COMMITS_AUTHOR[$COMMIT_INDEX]="$CURRENT_AUTHOR"
COMMITS_MESSAGE[$COMMIT_INDEX]="$CURRENT_MESSAGE"
COMMITS_BODY[$COMMIT_INDEX]="$CURRENT_BODY"
COMMIT_INDEX=$((COMMIT_INDEX + 1))
fi
fi
CURRENT_HASH=""
CURRENT_AUTHOR=""
CURRENT_MESSAGE=""
CURRENT_BODY=""
IN_BODY=false
STATE="HASH"
elif [[ "$line" == "BODY_START" ]]; then
IN_BODY=true
CURRENT_BODY=""
elif [[ "$line" == "BODY_END" ]]; then
IN_BODY=false
elif [ "$IN_BODY" = true ]; then
if [ -n "$CURRENT_BODY" ]; then
CURRENT_BODY="${CURRENT_BODY}\n${line}"
else
CURRENT_BODY="$line"
fi
else
if [ -z "$CURRENT_HASH" ]; then
CURRENT_HASH="$line"
elif [ -z "$CURRENT_AUTHOR" ]; then
CURRENT_AUTHOR="$line"
elif [ -z "$CURRENT_MESSAGE" ]; then
CURRENT_MESSAGE="$line"
fi
fi
done < "$TEMP_COMMITS"
if [ -n "$CURRENT_HASH" ]; then
SHOULD_SKIP=false
for pattern in "${IGNORE_PATTERNS[@]}"; do
if echo "$CURRENT_MESSAGE" | grep -qiE "$pattern"; then
SHOULD_SKIP=true
break
fi
done
if [ "$SHOULD_SKIP" = false ]; then
COMMITS_HASH[$COMMIT_INDEX]="$CURRENT_HASH"
COMMITS_AUTHOR[$COMMIT_INDEX]="$CURRENT_AUTHOR"
COMMITS_MESSAGE[$COMMIT_INDEX]="$CURRENT_MESSAGE"
COMMITS_BODY[$COMMIT_INDEX]="$CURRENT_BODY"
COMMIT_INDEX=$((COMMIT_INDEX + 1))
fi
fi
rm -f "$TEMP_COMMITS"
if [ $COMMIT_INDEX -eq 0 ]; then
echo "⚠️ No valid commits found to add"
echo "changelog_updated=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "✓ Found ${COMMIT_INDEX} valid commits"
echo ""
echo "📄 Building CHANGELOG entry..."
# 使用配置的标题
NEW_ENTRY="## :bookmark: ${CHANGELOG_VERSION}\n\n"
NEW_ENTRY="${NEW_ENTRY}${CHANGELOG_SECTION_TITLE}\n\n"
REPO_URL="${{ github.server_url }}/${{ github.repository }}"
for ((i=0; i<$COMMIT_INDEX; i++)); do
hash="${COMMITS_HASH[$i]}"
author="${COMMITS_AUTHOR[$i]}"
message="${COMMITS_MESSAGE[$i]}"
body="${COMMITS_BODY[$i]}"
echo " [$((i+1))/$COMMIT_INDEX] ${hash:0:7}: ${message:0:60}..."
COMMIT_LINK="([${hash}](${REPO_URL}/commit/${hash}))"
if [ -n "$body" ]; then
FULL_MESSAGE="${message}\n${body}"
LINE_COUNT=$(echo -e "$FULL_MESSAGE" | sed '/^$/d' | wc -l)
CURRENT_LINE=1
FORMATTED_MESSAGE=""
while IFS= read -r line; do
[ -z "$line" ] && continue
if [ $CURRENT_LINE -eq $LINE_COUNT ]; then
FORMATTED_MESSAGE="${FORMATTED_MESSAGE}${line} ${COMMIT_LINK}"
if [ -n "$author" ]; then
FORMATTED_MESSAGE="${FORMATTED_MESSAGE} by @${author}"
fi
else
FORMATTED_MESSAGE="${FORMATTED_MESSAGE}${line} \n"
fi
CURRENT_LINE=$((CURRENT_LINE + 1))
done <<< "$(echo -e "$FULL_MESSAGE" | sed '/^$/d')"
NEW_ENTRY="${NEW_ENTRY}- ${FORMATTED_MESSAGE}\n"
else
if [ -n "$author" ]; then
NEW_ENTRY="${NEW_ENTRY}- ${message} ${COMMIT_LINK} by @${author}\n"
else
NEW_ENTRY="${NEW_ENTRY}- ${message} ${COMMIT_LINK}\n"
fi
fi
done
echo ""
echo "👥 Collecting contributors..."
if [ -z "$PREVIOUS_TAG" ]; then
CONTRIBUTORS=$(git log --pretty=format:"%an" --no-merges | grep -v "github-actions\[bot\]" | grep -v "dependabot\[bot\]" | grep -v "renovate\[bot\]" | sort -u)
else
CONTRIBUTORS=$(git log ${PREVIOUS_TAG}..${VERSION} --pretty=format:"%an" --no-merges | grep -v "github-actions\[bot\]" | grep -v "dependabot\[bot\]" | grep -v "renovate\[bot\]" | sort -u)
fi
if [ -n "$CONTRIBUTORS" ]; then
CONTRIBUTOR_COUNT=$(echo "$CONTRIBUTORS" | wc -l)
echo "✓ Found ${CONTRIBUTOR_COUNT} contributors"
# 使用配置的标题和服务器地址
NEW_ENTRY="${NEW_ENTRY}\n${CHANGELOG_CONTRIBUTORS_TITLE}\n\n"
while IFS= read -r name; do
NEW_ENTRY="${NEW_ENTRY}<a href=\"${GITEA_SERVER}/${name}\">\n"
NEW_ENTRY="${NEW_ENTRY} <img src=\"${GITEA_SERVER}/${name}.png\" alt=\"${name}\" width=\"35\" height=\"35\" style=\"border-radius: 50%;\" onerror=\"this.src='${GITEA_SERVER}/assets/img/avatar_default.png'\" />\n"
NEW_ENTRY="${NEW_ENTRY}</a>\n"
done <<< "$CONTRIBUTORS"
fi
NEW_ENTRY="${NEW_ENTRY}\n---\n"
echo ""
echo "💾 Writing to CHANGELOG.md..."
git checkout main || git checkout master
if [ -f CHANGELOG.md ]; then
if [ "$VERSION_EXISTS" = "true" ]; then
# 版本已存在,叠加新的条目(不是替换)
echo "🔄 Appending new commits to existing entry for $CHANGELOG_VERSION"
# 提取现有版本区域的内容
TEMP_OLD_CONTENT=$(mktemp)
TEMP_NEW_FILE=$(mktemp)
# 提取旧版本区域的所有内容
IN_VERSION=false
while IFS= read -r line; do
if [[ "$line" =~ ^##[[:space:]]:bookmark:[[:space:]]${CHANGELOG_VERSION}$ ]]; then
IN_VERSION=true
elif [[ "$line" =~ ^##[[:space:]]:bookmark: ]] && [ "$IN_VERSION" = true ]; then
IN_VERSION=false
fi
if [ "$IN_VERSION" = true ]; then
echo "$line" >> "$TEMP_OLD_CONTENT"
fi
done < CHANGELOG.md
# 提取旧的提交记录(在 What's Changed 和 Contributors 之间的内容)
OLD_COMMITS=""
if [ -f "$TEMP_OLD_CONTENT" ]; then
OLD_COMMITS=$(awk '
/^### :pencil: What'"'"'s Changed/,/^### :busts_in_silhouette: Contributors/ {
if ($0 !~ /^### :pencil: What/ && $0 !~ /^### :busts_in_silhouette:/) {
if ($0 ~ /^-/) print $0
}
}
' "$TEMP_OLD_CONTENT")
fi
# 提取旧的贡献者(在 Contributors 部分的内容)
OLD_CONTRIBUTORS=""
if [ -f "$TEMP_OLD_CONTENT" ]; then
OLD_CONTRIBUTORS=$(awk '
/^### :busts_in_silhouette: Contributors/,/^---/ {
if ($0 ~ /<a href=/) {
getline; img=$0
getline; close_tag=$0
print prev ORS img ORS close_tag
prev=""
}
prev=$0
}
' "$TEMP_OLD_CONTENT")
fi
# 构建新的完整条目(叠加模式)
MERGED_ENTRY="## :bookmark: ${CHANGELOG_VERSION}\n\n"
MERGED_ENTRY="${MERGED_ENTRY}${CHANGELOG_SECTION_TITLE}\n\n"
# 先添加旧的提交(如果有)
if [ -n "$OLD_COMMITS" ]; then
MERGED_ENTRY="${MERGED_ENTRY}${OLD_COMMITS}\n"
fi
# 再添加新的提交从NEW_ENTRY中提取
NEW_COMMITS=$(echo -e "$NEW_ENTRY" | awk '
/^### :pencil: What/,/^### :busts_in_silhouette:/ {
if ($0 !~ /^### :pencil: What/ && $0 !~ /^### :busts_in_silhouette:/) {
if ($0 ~ /^-/) print $0
}
}
')
MERGED_ENTRY="${MERGED_ENTRY}${NEW_COMMITS}\n"
# 合并贡献者(去重)
MERGED_ENTRY="${MERGED_ENTRY}\n${CHANGELOG_CONTRIBUTORS_TITLE}\n\n"
# 使用数组来去重贡献者
declare -A SEEN_CONTRIBUTORS
# 处理旧的贡献者
if [ -n "$OLD_CONTRIBUTORS" ]; then
while IFS= read -r line; do
if [[ "$line" =~ href=\"[^\"]+/([^\"]+)\" ]]; then
username="${BASH_REMATCH[1]}"
if [ -z "${SEEN_CONTRIBUTORS[$username]}" ]; then
SEEN_CONTRIBUTORS[$username]=1
MERGED_ENTRY="${MERGED_ENTRY}${line}\n"
read -r img_line
MERGED_ENTRY="${MERGED_ENTRY}${img_line}\n"
read -r close_line
MERGED_ENTRY="${MERGED_ENTRY}${close_line}\n"
fi
fi
done <<< "$OLD_CONTRIBUTORS"
fi
# 处理新的贡献者
NEW_CONTRIBUTORS=$(echo -e "$NEW_ENTRY" | awk '
/^### :busts_in_silhouette: Contributors/,/^---/ {
if ($0 ~ /<a href=/) print $0
else if ($0 ~ /<img src=/) print $0
else if ($0 ~ /<\/a>/) print $0
}
')
if [ -n "$NEW_CONTRIBUTORS" ]; then
while IFS= read -r line; do
if [[ "$line" =~ href=\"[^\"]+/([^\"]+)\" ]]; then
username="${BASH_REMATCH[1]}"
if [ -z "${SEEN_CONTRIBUTORS[$username]}" ]; then
SEEN_CONTRIBUTORS[$username]=1
MERGED_ENTRY="${MERGED_ENTRY}${line}\n"
read -r img_line
MERGED_ENTRY="${MERGED_ENTRY}${img_line}\n"
read -r close_line
MERGED_ENTRY="${MERGED_ENTRY}${close_line}\n"
fi
fi
done <<< "$NEW_CONTRIBUTORS"
fi
MERGED_ENTRY="${MERGED_ENTRY}\n---\n"
# 替换CHANGELOG中的对应版本区域
IN_VERSION=false
while IFS= read -r line; do
if [[ "$line" =~ ^##[[:space:]]:bookmark:[[:space:]]${CHANGELOG_VERSION}$ ]]; then
echo -e "${MERGED_ENTRY}" >> "$TEMP_NEW_FILE"
IN_VERSION=true
elif [[ "$line" =~ ^##[[:space:]]:bookmark: ]] && [ "$IN_VERSION" = true ]; then
IN_VERSION=false
echo "$line" >> "$TEMP_NEW_FILE"
elif [ "$IN_VERSION" = false ]; then
echo "$line" >> "$TEMP_NEW_FILE"
fi
done < CHANGELOG.md
mv "$TEMP_NEW_FILE" CHANGELOG.md
rm -f "$TEMP_OLD_CONTENT"
echo "✓ Appended new commits to existing CHANGELOG entry"
else
# 版本不存在,添加新条目
echo " Adding new entry for $CHANGELOG_VERSION"
TEMP_FILE=$(mktemp)
head -n 1 CHANGELOG.md > "$TEMP_FILE"
echo "" >> "$TEMP_FILE"
echo -e "${NEW_ENTRY}" >> "$TEMP_FILE"
tail -n +3 CHANGELOG.md >> "$TEMP_FILE"
mv "$TEMP_FILE" CHANGELOG.md
echo "✓ Added new CHANGELOG entry"
fi
else
echo "# CHANGELOG" > CHANGELOG.md
echo "" >> CHANGELOG.md
echo -e "${NEW_ENTRY}" >> CHANGELOG.md
echo "✓ Created new CHANGELOG.md"
fi
echo "changelog_updated=true" >> $GITHUB_OUTPUT
echo "✅ CHANGELOG generation completed"
echo ""
- name: 📤 Commit and push CHANGELOG
if: steps.changelog.outputs.changelog_updated == 'true'
run: |
echo "======================================"
echo "📤 Committing and pushing CHANGELOG"
echo "======================================"
cd ${{ env.REPO_DIR }}
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add CHANGELOG.md
if git diff --staged --quiet; then
echo " No changes to commit"
exit 0
fi
echo "📝 Creating commit..."
echo "💬 Commit message: $COMMIT_MSG"
git commit -m "$COMMIT_MSG"
echo "⬆️ Pushing to remote..."
git push origin main
echo "✅ CHANGELOG pushed to main branch"
echo ""
- name: 🗑️ Delete old release
if: steps.changelog.outputs.changelog_updated == 'true'
run: |
echo "======================================"
echo "🔍 Checking for existing release"
echo "======================================"
OLD_ID=$(curl -s -H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/tags/${{ github.ref_name }}" \
| python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('id', ''))" 2>/dev/null || echo "")
if [ -n "$OLD_ID" ]; then
echo "🗑️ Found existing release (ID: $OLD_ID)"
echo "🔄 Deleting old release..."
curl -s -X DELETE -H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/$OLD_ID"
echo "✓ Old release deleted"
sleep 2
else
echo " No existing release found"
fi
echo ""
- name: 📋 Generate release notes
if: steps.changelog.outputs.changelog_updated == 'true'
run: |
echo "======================================"
echo "📋 Generating release notes"
echo "======================================"
cd ${{ env.REPO_DIR }}
python3 << 'PYSCRIPT'
import re
import os
tag = "${{ github.ref_name }}"
changelog_version = "${{ env.CHANGELOG_VERSION }}"
print(f"🏷️ Tag version: {tag}")
print(f"📝 CHANGELOG version: {changelog_version}")
changelog_path = 'CHANGELOG.md'
if not os.path.exists(changelog_path):
print("❌ ERROR: CHANGELOG.md not found")
exit(1)
print("📖 Reading CHANGELOG.md...")
with open(changelog_path, 'r', encoding='utf-8') as f:
changelog = f.read()
print(f"🔍 Searching for version entry: {changelog_version}")
pattern = rf'##\s+:bookmark:\s+{re.escape(changelog_version)}\s*\n(.*?)(?=^##\s+:bookmark:|\Z)'
match = re.search(pattern, changelog, re.DOTALL | re.MULTILINE)
if match:
version_content = match.group(1).strip()
version_content = re.sub(r'\n\s*\n\s*\n+', '\n\n', version_content).strip()
version_content = re.sub(r'^-{3,}\s*\n', '', version_content)
print(f"✓ Found changelog for version {changelog_version}")
print(f"📊 Content length: {len(version_content)} characters")
else:
version_content = "⚠️ No changelog found for this version"
print(f"⚠️ WARNING: Version {changelog_version} not found in CHANGELOG")
with open('/tmp/release-body.txt', 'w', encoding='utf-8') as f:
f.write(version_content)
print("✅ Release notes generated successfully")
PYSCRIPT
echo ""
- name: 🚀 Create release
if: steps.changelog.outputs.changelog_updated == 'true'
run: |
echo "======================================"
echo "🚀 Creating GitHub/Gitea release"
echo "======================================"
cd ${{ env.REPO_DIR }}
echo "📦 Preparing release payload..."
echo "📋 Release title: $RELEASE_TITLE"
echo "🏷️ Pre-release: $RELEASE_IS_PRERELEASE"
echo "📄 Draft: $RELEASE_IS_DRAFT"
# 转换 bash 布尔值为 Python 布尔值
if [ "$RELEASE_IS_DRAFT" = "true" ]; then
PY_DRAFT="True"
else
PY_DRAFT="False"
fi
if [ "$RELEASE_IS_PRERELEASE" = "true" ]; then
PY_PRERELEASE="True"
else
PY_PRERELEASE="False"
fi
echo "🔄 Converting: draft=$RELEASE_IS_DRAFT → $PY_DRAFT, prerelease=$RELEASE_IS_PRERELEASE → $PY_PRERELEASE"
python3 << EOF
import json
with open('/tmp/release-body.txt', 'r') as f:
body = f.read()
payload = {
"tag_name": "${{ github.ref_name }}",
"name": "$RELEASE_TITLE",
"body": body,
"draft": $PY_DRAFT,
"prerelease": $PY_PRERELEASE
}
with open('/tmp/payload.json', 'w', encoding='utf-8') as f:
json.dump(payload, f, ensure_ascii=False, indent=2)
print("✓ Payload prepared")
EOF
echo "🌐 Sending API request..."
HTTP_CODE=$(curl -s -o /tmp/release_response.json -w "%{http_code}" -X POST \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-H "Content-Type: application/json" \
-d @/tmp/payload.json \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases")
echo "📡 API Response (HTTP $HTTP_CODE):"
echo "---"
cat /tmp/release_response.json | python3 -m json.tool 2>/dev/null || cat /tmp/release_response.json
echo "---"
if [ "$HTTP_CODE" != "201" ] && [ "$HTTP_CODE" != "200" ]; then
echo "❌ ERROR: Failed to create release"
exit 1
fi
echo "✅ Release created successfully"
echo ""
- name: 📎 Upload CHANGELOG attachment
if: steps.changelog.outputs.changelog_updated == 'true'
run: |
echo "======================================"
echo "📎 Uploading release attachments"
echo "======================================"
cd ${{ env.REPO_DIR }}
echo "🔍 Extracting release ID..."
RELEASE_ID=$(python3 << 'PYSCRIPT'
import json
import sys
try:
with open('/tmp/release_response.json', 'r') as f:
data = json.load(f)
if 'id' not in data:
print("❌ ERROR: No 'id' field in response", file=sys.stderr)
sys.exit(1)
print(data['id'])
except Exception as e:
print(f"❌ ERROR: {e}", file=sys.stderr)
sys.exit(1)
PYSCRIPT
)
if [ -z "$RELEASE_ID" ]; then
echo "❌ ERROR: Failed to get release ID"
exit 1
fi
echo "✓ Release ID: $RELEASE_ID"
echo ""
# 上传 CHANGELOG.md
echo "📤 [1/1] Uploading CHANGELOG.md..."
HTTP_CODE=$(curl -s -o /tmp/upload_response.json -w "%{http_code}" -X POST \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-F "attachment=@CHANGELOG.md" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/$RELEASE_ID/assets?name=CHANGELOG.md")
if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "200" ]; then
echo "✅ CHANGELOG.md uploaded successfully"
else
echo "⚠️ WARNING: Upload failed (HTTP $HTTP_CODE)"
cat /tmp/upload_response.json
fi
# 上传额外的文件
if [ -n "$ADDITIONAL_RELEASE_FILES" ]; then
echo ""
echo "📦 Uploading additional files..."
FILE_INDEX=2
TOTAL_FILES=$((1 + $(echo "$ADDITIONAL_RELEASE_FILES" | wc -w)))
for file in $ADDITIONAL_RELEASE_FILES; do
if [ -f "$file" ]; then
echo "📤 [$FILE_INDEX/$TOTAL_FILES] Uploading $(basename $file)..."
HTTP_CODE=$(curl -s -o /tmp/upload_response_${FILE_INDEX}.json -w "%{http_code}" -X POST \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-F "attachment=@${file}" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/$RELEASE_ID/assets?name=$(basename $file)")
if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "200" ]; then
echo "✅ $(basename $file) uploaded successfully"
else
echo "⚠️ WARNING: $(basename $file) upload failed (HTTP $HTTP_CODE)"
fi
FILE_INDEX=$((FILE_INDEX + 1))
else
echo "⚠️ WARNING: File not found: $file"
fi
done
fi
echo ""
echo "✅ All attachments processed"
echo ""
- name: 📊 Workflow summary
if: always()
run: |
echo "======================================"
echo "📊 Generating workflow summary"
echo "======================================"
echo "## 📦 Workflow Execution Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**🏷️ Tag:** \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY
echo "**📂 Repository:** \`${{ github.repository }}\`" >> $GITHUB_STEP_SUMMARY
echo "**🕒 Time:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### ⚙️ Configuration" >> $GITHUB_STEP_SUMMARY
echo "- **Gitea Server:** \`$GITEA_SERVER\`" >> $GITHUB_STEP_SUMMARY
echo "- **Release Title:** \`$RELEASE_TITLE\`" >> $GITHUB_STEP_SUMMARY
if [ "$RELEASE_IS_PRERELEASE" = "true" ]; then
echo "- **Release Type:** 🏷️ Pre-release" >> $GITHUB_STEP_SUMMARY
else
echo "- **Release Type:** ✅ Latest (Stable)" >> $GITHUB_STEP_SUMMARY
fi
if [ "$RELEASE_IS_DRAFT" = "true" ]; then
echo "- **Draft:** 📄 Yes" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.check_bot.outputs.is_bot_commit }}" = "true" ]; then
echo "⏭️ **Status:** Skipped (bot commit detected)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "The workflow was skipped to prevent infinite loops." >> $GITHUB_STEP_SUMMARY
elif [ "${{ steps.check_version.outputs.version_exists }}" = "true" ]; then
echo "⏭️ **Status:** Skipped (version already exists)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Version \`${{ github.ref_name }}\` already exists in CHANGELOG.md" >> $GITHUB_STEP_SUMMARY
elif [ "${{ steps.changelog.outputs.changelog_updated }}" = "true" ]; then
echo "✅ **Status:** Success" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Completed Actions" >> $GITHUB_STEP_SUMMARY
echo "- ✅ CHANGELOG.md updated" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Changes pushed to main branch" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Release created" >> $GITHUB_STEP_SUMMARY
echo "- ✅ CHANGELOG.md attached to release" >> $GITHUB_STEP_SUMMARY
if [ -n "$ADDITIONAL_RELEASE_FILES" ]; then
echo "- ✅ Additional files uploaded" >> $GITHUB_STEP_SUMMARY
fi
else
echo " **Status:** No updates" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "No valid commits found between tags." >> $GITHUB_STEP_SUMMARY
fi
echo ""
echo "✅ Workflow summary generated"

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +0,0 @@
name: Hello from ImmortalWrt
on:
push:
branches:
- main
jobs:
say-hello:
runs-on: company
steps:
- name: 打印问候
run: echo "Hello form ImmortalWrt Router!"
- name: 显示系统信息
run: |
echo "===== 系统信息 ====="
uname -a
cat /etc/openwrt_release || echo "不是 OpenWrt 环境"
- name: 显示 CPU 和内存
run: |
echo "===== CPU 信息 ====="
cat /proc/cpuinfo | grep "model name" | head -1 || echo "ARM 架构"
echo "===== 内存信息 ====="
free -h
- name: 显示磁盘空间
run: df -h
- name: 列出当前目录
run: ls -la

View File

@ -0,0 +1,102 @@
name: Ubuntu System Information
on:
push:
branches:
- main
workflow_dispatch: # 允许手动触发
jobs:
show-system-info:
runs-on: ubuntu-22.04
steps:
- name: 打印问候
run: |
echo "====================================="
echo " Ubuntu System Information Check "
echo "====================================="
echo ""
- name: 显示系统信息
run: |
echo "===== 系统版本 ====="
cat /etc/os-release
echo ""
echo "===== 内核信息 ====="
uname -a
echo ""
echo "===== 主机名 ====="
hostname
echo ""
echo "===== 系统启动时间 ====="
uptime
- name: 显示 CPU 信息
run: |
echo "===== CPU 详细信息 ====="
lscpu
echo ""
echo "===== CPU 型号 ====="
cat /proc/cpuinfo | grep "model name" | head -1
echo ""
echo "===== CPU 核心数 ====="
nproc
- name: 显示内存信息
run: |
echo "===== 内存使用情况 ====="
free -h
echo ""
echo "===== 内存详细信息 ====="
cat /proc/meminfo | grep -E 'MemTotal|MemFree|MemAvailable'
- name: 显示磁盘信息
run: |
echo "===== 磁盘使用情况 ====="
df -h
echo ""
echo "===== 磁盘分区信息 ====="
lsblk
- name: 显示网络信息
run: |
echo "===== 网络接口 ====="
cat /proc/net/dev
echo ""
echo "===== 路由表 ====="
cat /proc/net/route
echo ""
echo "===== DNS 配置 ====="
cat /etc/resolv.conf
- name: 显示已安装的软件
run: |
echo "===== Python 版本 ====="
python3 --version
echo ""
echo "===== Node.js 版本 ====="
node --version || echo "Node.js 未安装"
echo ""
echo "===== Docker 版本 ====="
docker --version || echo "Docker 未安装"
echo ""
echo "===== Git 版本 ====="
git --version
- name: 显示环境变量
run: |
echo "===== 重要环境变量 ====="
echo "PATH: $PATH"
echo "HOME: $HOME"
echo "USER: $USER"
echo "SHELL: $SHELL"
echo "PWD: $PWD"
- name: 显示当前目录
run: |
echo "===== 当前工作目录 ====="
pwd
echo ""
echo "===== 目录内容 ====="
ls -lah

View File

@ -0,0 +1,976 @@
name: 📊 代码统计徽章
on:
push:
branches: [main, master]
workflow_dispatch:
# ==========================================
# 🔧 配置区域 - 根据你的项目修改
# ==========================================
env:
# ===== Token 配置 =====
# 建议在 Settings -> Secrets 中配置 STATS_TOKEN 以获得更好的权限控制
ACCESS_TOKEN: ${{ secrets.WORKFLOW }}
# ===== 工作区配置 =====
# 完整克隆的工作目录
WORKSPACE_DIR: "/home/workspace"
# ===== 分支配置 =====
# 徽章数据存储分支(可配置)
BADGE_BRANCH: "stats"
# 徽章文件存储目录
BADGE_DIR: "badges"
# ===== 排除配置 =====
# 全局排除的目录(这些目录会被完全忽略)
EXCLUDE_DIRS: "node_modules,dist,build,out,target,vendor,.venv,venv,__pycache__,.git,.github"
# 特殊包含规则(即使在排除目录中,也统计这些扩展名的文件)
# 格式: 目录:扩展名列表
# 例如: 'dist:js,css' 表示即使 dist 被排除,也统计其中的 .js 和 .css 文件
SPECIAL_INCLUDES: ""
# 示例: 'dist:js,css|vendor:go,mod'
# ===== 徽章颜色配置 =====
COLOR_TOTAL: "blue"
COLOR_FILES: "green"
COLOR_DEFAULT: "brightgreen"
# ===== 徽章样式配置 =====
BADGE_STYLE: "flat" # 可选: flat, flat-square, plastic, for-the-badge, social
# ===== 输出配置 =====
# 是否生成详细报告
GENERATE_DETAILED_REPORT: "true"
# 是否输出到 workflow summary
OUTPUT_TO_SUMMARY: "true"
# 最小代码行数阈值(低于此值的语言不生成徽章)
MIN_LINES_THRESHOLD: "10"
# ===== Git 配置 =====
GIT_USER_NAME: "ci[bot]"
GIT_USER_EMAIL: "ci[bot]@tinysoft.com.cn"
# ===== 平台配置 =====
# 平台类型: github 或 gitea
PLATFORM: "gitea"
# Git 服务器 URLGitea 示例: https://gitea.example.com
GIT_SERVER_URL: "https://git.mytsl.cn"
# 仓库路径(格式: 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: 'https://git.mytsl.cn/${{ github.repository }}/raw/branch'
# ==========================================
# 🎨 语言分组配置
# 格式: 组名:后缀列表:显示名称:颜色:图标(可选)
# 图标使用 simple-icons 的名称,如 cplusplus, typescript 等
# ==========================================
LANGUAGE_GROUPS: |
cpp:hpp,cpp,cxx,cc,h,c:C/C++:00599C:cplusplus
python:py:Python:3572A5:python
typescript:ts,tsx:TypeScript:3178c6:typescript
javascript:js,jsx:JavaScript:F7DF1E:javascript
java:java:Java:007396:java
go:go:Go:00ADD8:go
rust:rs:Rust:CE412B:rust
shell:sh,bash:Shell:4EAA25:gnubash
yaml:yml,yaml:YAML:CB171E
tsf:tsl,tsf:TSF:9945FF
jobs:
update-stats:
runs-on: ubuntu-22.04
permissions:
contents: write
steps:
- name: 🔍 验证 Token 配置
id: validate_token
run: |
echo "🔐 验证访问令牌..."
if [ -z "${{ env.ACCESS_TOKEN }}" ]; then
echo "❌ 错误: 未配置访问令牌"
echo "请在 Settings -> Secrets 中配置 STATS_TOKEN 或确保 GITHUB_TOKEN 可用"
exit 1
fi
# 检测使用的是哪个 token
if [ -n "${{ secrets.STATS_TOKEN }}" ]; then
echo "✅ 使用自定义 STATS_TOKEN"
echo "token_type=STATS_TOKEN" >> $GITHUB_OUTPUT
else
echo "✅ 使用默认 GITHUB_TOKEN"
echo "token_type=GITHUB_TOKEN" >> $GITHUB_OUTPUT
fi
echo "🔗 仓库: ${{ github.repository }}"
echo "🌿 分支: ${{ github.ref_name }}"
echo "📝 提交: ${GITHUB_SHA:0:7}"
- name: 🔧 检查必需工具
id: check_deps
run: |
echo "======================================"
echo "🔍 检查依赖工具"
echo "======================================"
MISSING_TOOLS=()
# 检查 bc用于数字计算
if ! command -v bc &> /dev/null; then
echo "⚠️ bc 未安装"
MISSING_TOOLS+=("bc")
else
echo "✓ bc 已安装"
fi
# 检查 jq用于 JSON 处理)
if ! command -v jq &> /dev/null; then
echo "⚠️ jq 未安装"
MISSING_TOOLS+=("jq")
else
echo "✓ jq 已安装"
fi
# 如果有缺失的工具,安装它们
if [ ${#MISSING_TOOLS[@]} -gt 0 ]; then
echo ""
echo "📦 安装缺失的工具: ${MISSING_TOOLS[*]}"
# 🔧 智能判断是否需要 sudo
# Docker 环境通常以 root 运行,不需要 sudo
if [ "$EUID" -eq 0 ] || [ "$(id -u)" -eq 0 ]; then
# 当前是 root 用户,直接执行
echo "🔑 检测到 root 权限,直接安装"
apt-get update -qq
apt-get install -y -qq "${MISSING_TOOLS[@]}"
elif command -v sudo &> /dev/null; then
# 有 sudo 命令,使用 sudo
echo "🔑 使用 sudo 安装"
sudo apt-get update -qq
sudo apt-get install -y -qq "${MISSING_TOOLS[@]}"
else
# 既不是 root 也没有 sudo
echo "❌ 错误:没有足够权限安装工具"
echo "请在 Docker 镜像中预装以下工具:"
echo " ${MISSING_TOOLS[*]}"
echo ""
echo "方案1: 使用包含这些工具的镜像"
echo "方案2: 在 Dockerfile 中添加:"
echo " RUN apt-get update && apt-get install -y bc jq"
exit 1
fi
echo "installed=true" >> $GITHUB_OUTPUT
echo "✅ 工具安装完成"
else
echo "installed=false" >> $GITHUB_OUTPUT
echo "✅ 所有工具已就绪"
fi
echo "======================================"
echo ""
- name: 📥 克隆主仓库
id: clone_main
run: |
echo "======================================"
echo "🚀 开始准备主仓库"
echo "======================================"
REPO_NAME="${{ github.event.repository.name }}"
REPO_DIR="${{ env.WORKSPACE_DIR }}/$REPO_NAME"
echo "📁 仓库名称: $REPO_NAME"
echo "📍 目标目录: $REPO_DIR"
echo "🌐 服务器: ${GITHUB_SERVER_URL}"
echo "🌿 分支: ${{ github.ref_name }}"
echo ""
# 检查仓库状态
if [ -d "$REPO_DIR" ]; then
echo "📂 目录已存在,检查 Git 仓库状态..."
if [ -d "$REPO_DIR/.git" ]; then
# 目录存在且是有效的 Git 仓库
echo "✓ 发现有效的 Git 仓库,执行增量更新..."
cd "$REPO_DIR"
# 清理工作区
git clean -fdx
git reset --hard
# 获取最新代码
echo "📥 拉取最新代码..."
git fetch --all --tags --force
echo "✓ 仓库已更新"
else
# 目录存在但不是 Git 仓库(可能之前运行失败)
echo "⚠️ 目录存在但不是有效的 Git 仓库"
echo "🧹 清理损坏的目录..."
rm -rf "$REPO_DIR"
echo "✓ 已清理"
# 重新克隆
echo "📥 克隆仓库..."
mkdir -p "${{ env.WORKSPACE_DIR }}"
git clone \
https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git \
"$REPO_DIR"
if [ $? -ne 0 ]; then
echo "❌ 克隆失败"
exit 1
fi
cd "$REPO_DIR"
echo "✓ 仓库已克隆"
fi
else
# 目录不存在,首次克隆
echo "📥 克隆仓库(首次)..."
mkdir -p "${{ env.WORKSPACE_DIR }}"
git clone \
https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git \
"$REPO_DIR"
if [ $? -ne 0 ]; then
echo "❌ 克隆失败"
exit 1
fi
cd "$REPO_DIR"
echo "✓ 仓库已克隆"
fi
# 切换到目标分支
echo "🏷️ 切换到分支: ${{ github.ref_name }}"
git checkout -f ${{ github.ref_name }}
echo "✅ 仓库准备成功"
echo ""
# 配置 Git 用户信息
echo "⚙️ 配置 Git 用户信息..."
git config user.name "${{ env.GIT_USER_NAME }}"
git config user.email "${{ env.GIT_USER_EMAIL }}"
# 显示仓库信息
echo "📊 仓库信息:"
echo " - 当前分支: $(git branch --show-current)"
echo " - 最新提交: $(git log -1 --oneline)"
echo " - 远程地址: $(git remote get-url origin | sed 's/oauth2:.*@/oauth2:***@/')"
echo ""
# 导出环境变量
echo "REPO_DIR=$REPO_DIR" >> $GITHUB_ENV
echo "REPO_NAME=$REPO_NAME" >> $GITHUB_ENV
echo "✅ 主仓库准备完成"
echo "======================================"
echo ""
- name: ⚙️ 准备统计分支
id: prepare_stats_branch
run: |
echo "======================================"
echo "🔧 准备统计分支"
echo "======================================"
cd "${{ env.REPO_DIR }}"
# 获取所有远程分支
git fetch origin --prune
# 检查统计分支是否存在
if git ls-remote --heads origin ${{ env.BADGE_BRANCH }} | grep -q ${{ env.BADGE_BRANCH }}; then
echo "✅ 统计分支 '${{ env.BADGE_BRANCH }}' 已存在"
# 检出统计分支
git fetch origin ${{ env.BADGE_BRANCH }}:${{ env.BADGE_BRANCH }}
git checkout ${{ env.BADGE_BRANCH }}
echo "📂 当前分支内容:"
ls -la
# 返回主分支
git checkout ${{ github.ref_name }}
echo "branch_exists=true" >> $GITHUB_OUTPUT
else
echo "🆕 统计分支 '${{ env.BADGE_BRANCH }}' 不存在,将创建"
echo "branch_exists=false" >> $GITHUB_OUTPUT
fi
echo "======================================"
echo ""
- name: 🌱 创建统计分支
if: steps.prepare_stats_branch.outputs.branch_exists == 'false'
run: |
echo "======================================"
echo "🆕 创建新的统计分支"
echo "======================================"
cd "${{ env.REPO_DIR }}"
# 🔴 安全检查:确认在正确的目录
echo "🔍 当前目录: $(pwd)"
echo "🔍 .git 状态: $([ -d .git ] && echo '✓ 存在' || echo '✗ 不存在')"
if [ ! -d .git ]; then
echo "❌ 错误:.git 目录不存在,无法创建分支"
exit 1
fi
# 创建孤立分支orphan branch
# 📝 说明:孤立分支与主分支没有共同的提交历史,适合存储独立的数据
echo "🌱 创建孤立分支..."
git checkout --orphan ${{ env.BADGE_BRANCH }}
# 🔴 再次确认 .git 仍然存在
if [ ! -d .git ]; then
echo "❌ 严重错误:.git 目录丢失!"
exit 1
fi
# 🧹 清空所有文件(但保留 .git 目录)
# ❓ 为什么要删除?
# - stats 分支是独立的数据存储分支,不需要主分支的代码文件
# - 只需要存储徽章 JSON 文件和统计报告
# - 保持分支干净,避免混淆
#
# ✅ 能否复用仓库?
# - 可以!.git 目录包含所有分支的完整信息
# - 切换到主分支时,主分支的文件会恢复
# - 各分支独立,互不影响
echo "🧹 清理文件(保护 .git 和 .gitea..."
# 步骤 1: 使用 git rm 清理已跟踪的文件
git rm -rf . 2>/dev/null || true
# 步骤 2: 安全地删除剩余文件
# 使用更严格的保护,确保不会误删 .git
echo "🔍 查找需要删除的文件..."
find . -maxdepth 1 -type f ! -name '.gitignore' -delete 2>/dev/null || true
find . -maxdepth 1 -type d ! -name '.' ! -name '..' ! -name '.git' ! -name '.gitea' -exec rm -rf {} + 2>/dev/null || true
# 🔴 最终检查:确认 .git 仍然完好
echo "🔍 最终检查..."
if [ ! -d .git ]; then
echo "❌ 致命错误:.git 目录在清理过程中被删除!"
echo "这不应该发生,请检查 find 命令"
exit 1
fi
echo "✓ .git 目录完好"
echo "✓ 文件清理完成"
echo ""
# 创建初始 README
cat > README.md << 'EOF'
# 📊 代码统计徽章数据
> 此分支由 GitHub Actions 自动生成和维护
>
> ⚠️ **请勿手动修改此分支的内容!**
## 📁 目录结构
```
badges/
├── total-lines.json # 总代码行数徽章
├── total-files.json # 总文件数徽章
├── {language}-lines.json # 各语言代码行数徽章
├── {language}-files.json # 各语言文件数徽章
└── README.md # 详细统计报告
```
## 🔄 更新机制
- ✅ 每次 push 到主分支时自动更新
- ✅ 可通过 workflow_dispatch 手动触发
## 📊 数据来源
所有统计数据基于主分支的最新代码自动生成。
---
*🤖 由 GitHub Actions 自动维护*
EOF
# 创建徽章目录
mkdir -p ${{ env.BADGE_DIR }}
# 添加并提交
git add .
git commit -m "chore: 初始化统计分支"
# 推送到远程
git push https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git ${{ env.BADGE_BRANCH }}
echo "✅ 统计分支创建完成"
echo "======================================"
echo ""
# 🔴 重要:返回主分支,后续统计需要在主分支进行
echo "🔄 切换回主分支: ${{ github.ref_name }}"
git checkout ${{ github.ref_name }}
echo "✓ 已切换回主分支"
echo ""
- name: 📊 统计总代码量
id: total
run: |
echo "======================================"
echo "📊 统计总代码量"
echo "======================================"
cd "${{ env.REPO_DIR }}"
# 🔴 重要:确保在主分支进行统计(不要统计 stats 分支)
echo "🔍 确认当前分支..."
CURRENT_BRANCH=$(git branch --show-current)
if [ "$CURRENT_BRANCH" != "${{ github.ref_name }}" ]; then
echo "⚠️ 当前在分支: $CURRENT_BRANCH切换到主分支: ${{ github.ref_name }}"
git checkout ${{ github.ref_name }}
else
echo "✓ 已在主分支: $CURRENT_BRANCH"
fi
echo ""
# 构建排除参数
EXCLUDE_PARAMS=""
IFS=',' read -ra EXCLUDES <<< "${{ env.EXCLUDE_DIRS }}"
for dir in "${EXCLUDES[@]}"; do
EXCLUDE_PARAMS="$EXCLUDE_PARAMS -not -path '*/$dir/*'"
done
# 获取所有文件
echo "🔍 扫描文件..."
# 统计总文件数
TOTAL_FILES=$(eval "find . -type f $EXCLUDE_PARAMS" | 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}')
echo "📊 统计结果:"
echo " - 总文件数: $TOTAL_FILES"
echo " - 总代码行: $TOTAL_CODE"
# 格式化数字(添加千分位)
FORMATTED_FILES=$(printf "%'d" $TOTAL_FILES)
FORMATTED_CODE=$(printf "%'d" $TOTAL_CODE)
# 输出到环境变量
echo "total_files=$TOTAL_FILES" >> $GITHUB_OUTPUT
echo "total_code=$TOTAL_CODE" >> $GITHUB_OUTPUT
echo "formatted_files=$FORMATTED_FILES" >> $GITHUB_OUTPUT
echo "formatted_code=$FORMATTED_CODE" >> $GITHUB_OUTPUT
echo "======================================"
echo ""
- name: 📋 统计各语言代码量
id: languages
run: |
echo "======================================"
echo "📋 统计各语言代码量"
echo "======================================"
cd "${{ env.REPO_DIR }}"
# 🔴 重要:确保在主分支进行统计
echo "🔍 确认当前分支..."
CURRENT_BRANCH=$(git branch --show-current)
if [ "$CURRENT_BRANCH" != "${{ github.ref_name }}" ]; then
echo "⚠️ 当前在分支: $CURRENT_BRANCH切换到主分支: ${{ github.ref_name }}"
git checkout ${{ github.ref_name }}
else
echo "✓ 已在主分支: $CURRENT_BRANCH"
fi
echo ""
# 创建临时目录
mkdir -p /tmp/lang_stats
# 构建排除参数
EXCLUDE_PARAMS=""
IFS=',' read -ra EXCLUDES <<< "${{ env.EXCLUDE_DIRS }}"
for dir in "${EXCLUDES[@]}"; do
EXCLUDE_PARAMS="$EXCLUDE_PARAMS -not -path '*/$dir/*'"
done
LANGUAGE_COUNT=0
# 读取语言配置并统计
echo "${{ env.LANGUAGE_GROUPS }}" | while IFS= read -r line; do
[ -z "$line" ] && continue
# 解析配置行: 组名:后缀列表:显示名称:颜色:图标
IFS=':' read -r lang_id extensions display_name color icon <<< "$line"
echo "🔍 统计 $display_name..."
# 构建扩展名查找条件
FIND_CONDITIONS=""
IFS=',' read -ra EXTS <<< "$extensions"
for ext in "${EXTS[@]}"; do
if [ -z "$FIND_CONDITIONS" ]; then
FIND_CONDITIONS="-name '*.$ext'"
else
FIND_CONDITIONS="$FIND_CONDITIONS -o -name '*.$ext'"
fi
done
# 统计文件数
FILE_COUNT=$(eval "find . -type f \( $FIND_CONDITIONS \) $EXCLUDE_PARAMS" | wc -l)
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}')
# 只保存超过阈值的语言
if [ "$CODE_LINES" -ge "${{ env.MIN_LINES_THRESHOLD }}" ]; then
FORMATTED_LINES=$(printf "%'d" $CODE_LINES)
echo " - 文件数: $FILE_COUNT"
echo " - 代码行: $FORMATTED_LINES"
# 保存到临时文件
echo "$lang_id|$display_name|$CODE_LINES|$FORMATTED_LINES|$FILE_COUNT|$color|$icon" >> /tmp/lang_stats/${lang_id}.txt
LANGUAGE_COUNT=$((LANGUAGE_COUNT + 1))
else
echo " ⏩ 跳过(少于 ${{ env.MIN_LINES_THRESHOLD }} 行)"
fi
else
echo " ⏩ 未找到文件"
fi
echo ""
done
# 汇总所有语言数据
cat /tmp/lang_stats/*.txt 2>/dev/null | sort -t'|' -k3 -nr > /tmp/lang_summary.txt || touch /tmp/lang_summary.txt
# 计算实际找到的语言数
ACTUAL_COUNT=$(wc -l < /tmp/lang_summary.txt)
echo "language_count=$ACTUAL_COUNT" >> $GITHUB_OUTPUT
echo "✅ 找到 $ACTUAL_COUNT 种语言"
echo "======================================"
echo ""
- name: 🎨 生成徽章数据
id: generate_badges
run: |
echo "======================================"
echo "🎨 生成徽章数据"
echo "======================================"
cd "${{ env.REPO_DIR }}"
# 确保在统计分支
git checkout ${{ env.BADGE_BRANCH }}
# 确保徽章目录存在
mkdir -p ${{ env.BADGE_DIR }}
GENERATED_COUNT=0
# 生成总代码行数徽章
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 "generated_count=$GENERATED_COUNT" >> $GITHUB_OUTPUT
echo "✅ 已生成 $GENERATED_COUNT 个徽章"
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** 行 | ![代码行数](https://img.shields.io/endpoint?url=RAW_URL_PLACEHOLDER/BRANCH_PLACEHOLDER/BADGE_DIR_PLACEHOLDER/total-lines.json) |
| 📁 总文件数 | **TOTAL_FILES_PLACEHOLDER** 个 | ![文件数](https://img.shields.io/endpoint?url=RAW_URL_PLACEHOLDER/BRANCH_PLACEHOLDER/BADGE_DIR_PLACEHOLDER/total-files.json) |
| 🌐 语言种类 | **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}% | ![${display_name}](https://img.shields.io/endpoint?url=${{ env.RAW_URL_BASE }}/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }}/${lang_id}-lines.json) |
EOFLANG
done < /tmp/lang_summary.txt
fi
# 添加配置说明
cat >> ${{ env.BADGE_DIR }}/README.md << EOFMD
---
## ⚙️ 配置说明
### Token 配置
- 当前使用: **${{ steps.validate_token.outputs.token_type }}**
- 推荐配置自定义 `STATS_TOKEN` 以获得更好的权限控制
### 排除规则
当前排除的目录:
```
${{ 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 ""
- name: 📤 提交并推送到统计分支
id: commit
run: |
echo "======================================"
echo "📤 提交并推送更新"
echo "======================================"
cd "${{ env.REPO_DIR }}"
# 确保在统计分支
git checkout ${{ env.BADGE_BRANCH }}
# 配置 Git 用户信息
git config user.name "${{ env.GIT_USER_NAME }}"
git config user.email "${{ env.GIT_USER_EMAIL }}"
# 添加所有更改
git add ${{ env.BADGE_DIR }}/
# 检查是否有变更
if git diff --staged --quiet; then
echo "📌 没有变更,跳过提交"
echo "changed=false" >> $GITHUB_OUTPUT
echo ""
else
echo "📝 准备提交..."
# 生成提交信息
COMMIT_MSG="chore: 更新代码统计 [$(date '+%Y-%m-%d %H:%M')]
📊 统计摘要:
- 总代码: ${{ steps.total.outputs.formatted_code }} 行
- 总文件: ${{ steps.total.outputs.formatted_files }} 个
- 语言数: ${{ steps.languages.outputs.language_count }} 种
- 徽章数: ${{ steps.generate_badges.outputs.generated_count }} 个
🔗 触发提交: ${GITHUB_SHA:0:7}
🤖 由 GitHub Actions 自动生成"
git commit -m "$COMMIT_MSG"
echo "📤 推送到远程 ${{ env.BADGE_BRANCH }} 分支..."
# 推送到远程(使用 TOKEN
git push https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git ${{ env.BADGE_BRANCH }}
if [ $? -eq 0 ]; then
echo "✅ 推送成功"
echo "changed=true" >> $GITHUB_OUTPUT
else
echo "❌ 推送失败"
exit 1
fi
fi
echo "======================================"
echo ""
- name: 📈 生成 Workflow Summary
if: env.OUTPUT_TO_SUMMARY == 'true' && success()
run: |
cat >> $GITHUB_STEP_SUMMARY << 'EOFSUMMARY'
# 📊 代码统计完成
## ✅ 执行结果
| 项目 | 结果 |
|------|------|
| 🔐 Token 类型 | **${{ steps.validate_token.outputs.token_type }}** |
| 🔧 依赖检查 | ${{ steps.check_deps.outputs.installed == 'true' && '✅ 已安装缺失工具' || '✅ 所有工具就绪' }} |
| 📊 总代码行数 | **${{ steps.total.outputs.formatted_code }}** 行 |
| 📁 总文件数 | **${{ steps.total.outputs.formatted_files }}** 个 |
| 🌐 语言种类 | **${{ steps.languages.outputs.language_count }}** 种 |
| 🎨 生成徽章 | **${{ steps.generate_badges.outputs.generated_count }}** 个 |
| 📤 提交状态 | ${{ steps.commit.outputs.changed == 'true' && '✅ 已更新' || '📌 无变更' }} |
## 🔗 快速链接
- 📊 [查看详细统计报告](${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 }}/${{ env.BADGE_DIR }})
- 🔧 [查看 Workflow 配置](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/blob/${{ github.ref_name }}/.github/workflows/update_stats_badge.yaml)
## 📝 语言分布
EOFSUMMARY
# 添加语言分布表格
if [ -f /tmp/lang_summary.txt ] && [ -s /tmp/lang_summary.txt ]; then
cat >> $GITHUB_STEP_SUMMARY << 'EOFTABLE'
| 语言 | 代码行数 | 文件数 | 占比 |
|------|----------|--------|------|
EOFTABLE
TOTAL_CODE=${{ steps.total.outputs.total_code }}
sort -t'|' -k3 -nr /tmp/lang_summary.txt | head -10 | 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
echo "| **${display_name}** | ${formatted_lines} | ${file_count} | ${PERCENT}% |" >> $GITHUB_STEP_SUMMARY
done
else
echo "" >> $GITHUB_STEP_SUMMARY
echo "> ⚠️ 未找到符合条件的语言文件" >> $GITHUB_STEP_SUMMARY
fi
# 添加工作区信息
cat >> $GITHUB_STEP_SUMMARY << 'EOFWS'
## 📂 工作区信息
| 项目 | 值 |
|------|-----|
| 📁 仓库名称 | `${{ env.REPO_NAME }}` |
| 📍 工作目录 | `${{ env.REPO_DIR }}` |
| 🌿 统计分支 | `${{ env.BADGE_BRANCH }}` |
| 📂 徽章目录 | `${{ env.BADGE_DIR }}` |
EOFWS
echo "" >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "*⏱️ 执行时间: $(date '+%Y-%m-%d %H:%M:%S %Z')*" >> $GITHUB_STEP_SUMMARY
- name: 💡 输出使用说明
if: success()
run: |
echo "================================================"
echo "✅ 代码统计任务执行完成!"
echo "================================================"
echo ""
echo "🔐 Token 信息:"
echo " - 使用: ${{ steps.validate_token.outputs.token_type }}"
echo ""
# 根据实际结果输出
if [ "${{ steps.commit.outputs.changed }}" == "true" ]; then
echo "📊 统计结果:"
echo " - 总代码: ${{ steps.total.outputs.formatted_code }} 行"
echo " - 总文件: ${{ steps.total.outputs.formatted_files }} 个"
echo " - 语言数: ${{ steps.languages.outputs.language_count }} 种"
echo " - 徽章数: ${{ steps.generate_badges.outputs.generated_count }} 个"
echo ""
echo "📤 已推送更新到分支: ${{ env.BADGE_BRANCH }}"
else
echo "📌 统计数据无变更,未产生新的提交"
fi
echo ""
echo "📂 工作区信息:"
echo " - 仓库目录: ${{ env.REPO_DIR }}"
echo " - 统计分支: ${{ env.BADGE_BRANCH }}"
echo ""
echo "🔗 查看详细报告:"
echo " ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }}/README.md"
echo ""
echo "🎨 徽章目录:"
echo " ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }}"
echo ""
echo "================================================"
# 如果有警告或特殊情况,也输出
if [ "${{ steps.check_deps.outputs.installed }}" == "true" ]; then
echo ""
echo " 本次运行安装了缺失的依赖工具"
fi
if [ "${{ steps.languages.outputs.language_count }}" == "0" ]; then
echo ""
echo "⚠️ 警告: 未找到符合条件的语言文件"
echo " 请检查 LANGUAGE_GROUPS 配置和 MIN_LINES_THRESHOLD 设置"
fi
# 清理工作区(可选)
if [ "${{ env.CLEANUP_WORKSPACE }}" == "true" ]; then
echo ""
echo "🧹 清理工作区..."
rm -rf "${{ env.WORKSPACE_DIR }}"
echo "✅ 清理完成"
fi
- name: 🧹 清理工作区
if: always()
run: |
echo "🧹 清理临时文件..."
rm -rf /tmp/lang_stats /tmp/lang_summary.txt /tmp/total_stats*.json
echo "✅ 清理完成"

118
CHANGELOG.md Normal file
View File

@ -0,0 +1,118 @@
# CHANGELOG
## :bookmark: 0.0.4
### :pencil: What's Changed
- :rocket: 优化bot名字更具辨识度 ([4829434](https://git.mytsl.cn/csh/actions-template/commit/4829434408f46ebfad41293c46c9de1d94b157f3)) by @csh
- :recycle: 重构代码和`docker-runner`项目结构
:sparkles: 支持`docker`部署`archlinux`,预装`clang`跨平台编译工具链
([39820e6](https://git.mytsl.cn/csh/actions-template/commit/39820e656a9822e3b31df93d0abc33c02b2e56d1)) by @csh
- :bug: 修复`lsb_release`命令报错 ([0e89b21](https://git.mytsl.cn/csh/actions-template/commit/0e89b21c415bb62e02bace18038195c82d6374c7)) by @csh
- :bug: Bug修复 ([eaedcd3](https://git.mytsl.cn/csh/actions-template/commit/eaedcd3bb21e9ec8ed58e03b63ad85761a77cb48)) by @csh
### :busts_in_silhouette: Contributors
<a href="https://git.mytsl.cn/ci-bot">
<img src="https://git.mytsl.cn/ci-bot.png" alt="ci-bot" width="35" height="35" style="border-radius: 50%;" onerror="this.src='https://git.mytsl.cn/assets/img/avatar_default.png'" />
</a>
<a href="https://git.mytsl.cn/csh">
<img src="https://git.mytsl.cn/csh.png" alt="csh" width="35" height="35" style="border-radius: 50%;" onerror="this.src='https://git.mytsl.cn/assets/img/avatar_default.png'" />
</a>
---
## :bookmark: 0.0.3
### :pencil: What's Changed
- :bug: 移除默认上传`CHANGELOG.md` ([9961226](https://git.mytsl.cn/csh/actions-template/commit/99612267047e4d5c48d55888eeb0d05ba12c5da1)) by @csh
- :recycle: 重构`changelog_and_release.yml` ([1f535e0](https://git.mytsl.cn/csh/actions-template/commit/1f535e0675efe166b5d38f0f0778f40d8663d318)) by @csh
- :bug: 修复commit信息提取错误 ([bf6698a](https://git.mytsl.cn/csh/actions-template/commit/bf6698ab3e7f2020dcee899f6a06166b0e6a5168)) by @csh
- 更新 CHANGELOG.md ([4ed8099](https://git.mytsl.cn/csh/actions-template/commit/4ed8099158f0f753b53a526ccba7dfc7aaad739a)) by @csh
- :bug: fix bug ([fc76a1f](https://git.mytsl.cn/csh/actions-template/commit/fc76a1f625c4c6cb42e50cadb487b7607a8d2f0a)) by @csh
- 更新 CHANGELOG.md [skip ci] ([7373553](https://git.mytsl.cn/csh/actions-template/commit/7373553270d20357a225aaf41334919086f7687d)) by @csh
- :bug: fix bug ([fa21511](https://git.mytsl.cn/csh/actions-template/commit/fa21511d1ab98e415036046bc6950f8a3b322f25)) by @csh
- :bug: 移除默认上传`CHANGELOG.md` ([9961226](https://git.mytsl.cn/csh/actions-template/commit/99612267047e4d5c48d55888eeb0d05ba12c5da1)) by @csh
- :recycle: 重构`changelog_and_release.yml` ([1f535e0](https://git.mytsl.cn/csh/actions-template/commit/1f535e0675efe166b5d38f0f0778f40d8663d318)) by @csh
- :bug: 修复commit信息提取错误 ([bf6698a](https://git.mytsl.cn/csh/actions-template/commit/bf6698ab3e7f2020dcee899f6a06166b0e6a5168)) by @csh
- 更新 CHANGELOG.md ([4ed8099](https://git.mytsl.cn/csh/actions-template/commit/4ed8099158f0f753b53a526ccba7dfc7aaad739a)) by @csh
- :bug: fix bug ([fc76a1f](https://git.mytsl.cn/csh/actions-template/commit/fc76a1f625c4c6cb42e50cadb487b7607a8d2f0a)) by @csh
- :bug: fix bug ([fa21511](https://git.mytsl.cn/csh/actions-template/commit/fa21511d1ab98e415036046bc6950f8a3b322f25)) by @csh
- :bug: 修复匹配正则问题 ([9a472a4](https://git.mytsl.cn/csh/actions-template/commit/9a472a42711746ecb739eee4475b4a4125c75312)) by @csh
- :bug: 匹配逻辑 ([160c5eb](https://git.mytsl.cn/csh/actions-template/commit/160c5ebade5bf9e1a1e99412026ccd7d1cca6996)) by @csh
- :bug: 修复过滤转义失败 ([e71d9d0](https://git.mytsl.cn/csh/actions-template/commit/e71d9d01fdbbd19c24dcf57382ec52fb42e228be)) by @csh
- :bug: ok了嘛 ([3a17dc4](https://git.mytsl.cn/csh/actions-template/commit/3a17dc4b52163fbea3c2fe60c617acfe7d8cd19c)) by @csh
- :bug: 不行就先放弃了 ([1b7eed5](https://git.mytsl.cn/csh/actions-template/commit/1b7eed5d93af889b7d8ed7d862899e36cbddaaf1)) by @csh
- :bug: 移除默认上传`CHANGELOG.md` ([9961226](https://git.mytsl.cn/csh/actions-template/commit/99612267047e4d5c48d55888eeb0d05ba12c5da1)) by @csh
- :recycle: 重构`changelog_and_release.yml` ([1f535e0](https://git.mytsl.cn/csh/actions-template/commit/1f535e0675efe166b5d38f0f0778f40d8663d318)) by @csh
- :bug: 修复commit信息提取错误 ([bf6698a](https://git.mytsl.cn/csh/actions-template/commit/bf6698ab3e7f2020dcee899f6a06166b0e6a5168)) by @csh
- 更新 CHANGELOG.md ([4ed8099](https://git.mytsl.cn/csh/actions-template/commit/4ed8099158f0f753b53a526ccba7dfc7aaad739a)) by @csh
- :bug: fix bug ([fc76a1f](https://git.mytsl.cn/csh/actions-template/commit/fc76a1f625c4c6cb42e50cadb487b7607a8d2f0a)) by @csh
- :bug: fix bug ([fa21511](https://git.mytsl.cn/csh/actions-template/commit/fa21511d1ab98e415036046bc6950f8a3b322f25)) by @csh
- :bug: 修复匹配正则问题 ([9a472a4](https://git.mytsl.cn/csh/actions-template/commit/9a472a42711746ecb739eee4475b4a4125c75312)) by @csh
- :bug: 匹配逻辑 ([160c5eb](https://git.mytsl.cn/csh/actions-template/commit/160c5ebade5bf9e1a1e99412026ccd7d1cca6996)) by @csh
- :bug: 修复过滤转义失败 ([e71d9d0](https://git.mytsl.cn/csh/actions-template/commit/e71d9d01fdbbd19c24dcf57382ec52fb42e228be)) by @csh
- :bug: ok了嘛 ([3a17dc4](https://git.mytsl.cn/csh/actions-template/commit/3a17dc4b52163fbea3c2fe60c617acfe7d8cd19c)) by @csh
- :bug: 不行就先放弃了 ([1b7eed5](https://git.mytsl.cn/csh/actions-template/commit/1b7eed5d93af889b7d8ed7d862899e36cbddaaf1)) by @csh
- :wrench: `docker`的默认配置变更 ([c81aac9](https://git.mytsl.cn/csh/actions-template/commit/c81aac970cd50f7a3a08ccb4d7790a374efae721)) by @csh
- :wrench: 工作流的bot名字变更为`ci-bot` ([49f11fb](https://git.mytsl.cn/csh/actions-template/commit/49f11fb2d814e1fc59f8b05120bdd57fa44b9bad)) by @csh
### :busts_in_silhouette: Contributors
<a href="https://git.mytsl.cn/csh">
<img src="https://git.mytsl.cn/csh.png" alt="csh" width="35" height="35" style="border-radius: 50%;" onerror="this.src='https://git.mytsl.cn/assets/img/avatar_default.png'" />
</a>
---
## :bookmark: 0.0.2
*Released on 2025-11-02*
### :pencil: What's Changed
- :art: 代码格式化 ([`ec5785c`](https://github.com/csh/actions-template/commit/ec5785cc6af3267285623090e19b5c215d89d37f))
- :bug: 修复tag获取错误问题 ([`84e2751`](https://github.com/csh/actions-template/commit/84e275166844d9fd410a7aba3e32807683784970))
- :bug: 修复bug ([`0bb627f`](https://github.com/csh/actions-template/commit/0bb627fd34ce47e2baaf9b3856d12564da2423d9))
- :recycle: 重构`changelog_and_release` ([`9cde246`](https://github.com/csh/actions-template/commit/9cde246b208afe25ac6ecca67ecf4e7ad2e56ecc))
- :bug: 修复日期 ([`aaeed0d`](https://github.com/csh/actions-template/commit/aaeed0d9197af8f9c732ab9e4a2ae5dc99a40c00))
- :bug: 修复`update_state_badge.yml`自动生成的`README`日期错误 ([`f57e698`](https://github.com/csh/actions-template/commit/f57e6984c048f6dbfc5e8083dd68c8910a923487))
- :memo: 更新`WORKFLOW`说明 ([`2ac94af`](https://github.com/csh/actions-template/commit/2ac94aff1c499ca12bbe29652532bd4a8bef425d))
- :bug: help me ([`d7a0c7a`](https://github.com/csh/actions-template/commit/d7a0c7a85b04a3177790a8a74ebb1c5154e136de))
- :bug: 继续修修 ([`948bcfa`](https://github.com/csh/actions-template/commit/948bcfa7875b0276b649851c7445367b13c95438))
- :bug: 继续修复测试 ([`23a7fb5`](https://github.com/csh/actions-template/commit/23a7fb5a285c3f6f16071c82f6ba24137507a10d))
- :bug: 修`update_stats_badge.yml`,还没跑起来 ([`b616573`](https://github.com/csh/actions-template/commit/b616573bfd8374a8b007e8066cfcd053191d9717))
- :bug: 修复`update_stats_badge.yml` 克隆bug ([`5b3629f`](https://github.com/csh/actions-template/commit/5b3629fb732ecfdd78496736f1f861ec0f8d9eee))
- :bug: 修复token配置错误 ([`38e6c82`](https://github.com/csh/actions-template/commit/38e6c823948662e18e60bdc483112a2957352ffb))
- :bug: 修复runs-on错误 ([`376c1a2`](https://github.com/csh/actions-template/commit/376c1a28666378ff11c7d0669a61c4c980ca6261))
- :sparkles: 新增:代码统计徽章 action: `update_stats_badge.yml` ([`d5a7d55`](https://github.com/csh/actions-template/commit/d5a7d5506ec0f7d128c60f908bbd17198308c587))
- :demo: 文档更新 ([`c0aef0f`](https://github.com/csh/actions-template/commit/c0aef0f5316aa0fba1ae5686b8d7cc8c770e816e))
- :package: workflows的文件进行更名并且调整后缀为`yml` ([`a21adb9`](https://github.com/csh/actions-template/commit/a21adb9fcc79839ddd717f862abe69e7e232fbca))
- :wastebasket: 移除发布Release时候自动上传CHANGELOG.md ([`8971cbf`](https://github.com/csh/actions-template/commit/8971cbffc3023891852a14d9917c8b9b2b7f8272))
---
## :bookmark: 0.0.1
### :pencil: What's Changed
- :wastebasket: 删除CHANGELOG让工作流自动执行 ([d50ee5d](https://git.mytsl.cn/csh/actions-template/commit/d50ee5d)) by @csh
- :wrench: gitignore新增CHANGELOG.md ([29cd19e](https://git.mytsl.cn/csh/actions-template/commit/29cd19e)) by @csh
- :bug: 修复生成的CHANGELOG格式不对
:sparkles: 过滤commit新增`Initial commit`
:art: 代码格式化 ([b651daf](https://git.mytsl.cn/csh/actions-template/commit/b651daf)) by @csh
- :wastebasket: gitignore移除CHANGELOG.md会导致工作流提交失败 ([27f35a6](https://git.mytsl.cn/csh/actions-template/commit/27f35a6)) by @csh
### :busts_in_silhouette: Contributors
<a href="https://git.mytsl.cn/csh">
<img src="https://git.mytsl.cn/csh.png" alt="csh" width="35" height="35" style="border-radius: 50%;" onerror="this.src='https://git.mytsl.cn/assets/img/avatar_default.png'" />
</a>
---

File diff suppressed because it is too large Load Diff

View File

@ -39,11 +39,11 @@
包含内容:
- 🔄 `changelog_and_release.yml` - 自动更新 CHANGELOG和自动创建Release
- 💡 工作流配置说明
- 🔧 如何使用和定制
- 🔄 案例:`changelog_and_release.yml` - 自动更新 CHANGELOG 和自动创建 Release
👉 **两个实用的 Actions 工作流,可直接复制使用**
👉 **实用的 Actions 工作流,可直接复制使用**
---

View File

@ -1,880 +1,163 @@
# 📦 Tag Release Automation
# Workflow
自动生成 CHANGELOG 并创建 Release 的一体化工作流,**支持语义版本号SemVer和内容叠加**
本目录包含 Gitea Actions 的自动化工作流示例,展示如何使用 Gitea Actions 实现 CI/CD 自动化
---
## 📂 文件位置
## 📂 文件结构
```txt
.gitea/workflows/
└── changelog-and-release.yaml # CHANGELOG 生成 + Release 创建
├── changelog-and-release.yml # CHANGELOG 生成 + Release 创建
├── update_stats_badge.yml # 代码行数的统计
└── ... # 更多 workflow 待添加
```
---
## 🚀 快速开始
## 🎯 功能列表
### 1⃣ 配置 Gitea Token
### ✅ 已实现
#### 生成 Token
#### 1. 📦 自动发布工作流 (`changelog-and-release.yml`)
1. 登录 Gitea → 右上角头像 → **设置 (Settings)** → **应用 (Applications)**
2. 点击 **生成新令牌 (Generate New Token)**
3. 配置权限:
- ✅ **repo** - 完整的仓库访问权限(必需)
4. 复制生成的 Token 只显示一次)
**功能**:在推送 tag 时自动生成 CHANGELOG 并创建 Release
#### 添加 Secret
**特性**
**推荐方式:仓库级别配置**
- 🏷️ 自动识别语义版本号SemVer
- 📝 智能生成 CHANGELOG 条目
- 🔄 支持多个开发版本内容叠加
- 👥 自动提取贡献者列表
- 🚀 创建 GitHub/Gitea Release
- 📎 上传附件到 Release
- 🏷️ 自动识别 Pre-release 标记
1. 进入仓库 → **设置 (Settings)****Actions** → **Secrets**
2. 点击 **新建 Secret (New Secret)**
3. 填写:
- **名称**`GITEA_TOKEN`
- **值**:粘贴刚才的 token
4. 点击 **保存**
💡 **提示**:如果有多个仓库需要使用,可以在个人设置中添加全局 Secret
### 2⃣ 部署 Workflow
**触发方式**
```bash
# 创建目录
mkdir -p .gitea/workflows
# 复制文件(将文件重命名为 changelog-and-release.yaml
cp changelog-and-release.yaml .gitea/workflows/
# 提交
git add .gitea/workflows/
git commit -m "ci: add changelog and release automation"
git push origin main
```
### 3⃣ 自定义配置
打开 `changelog-and-release.yaml`,找到 **⚙️ Configuration** 步骤(约第 10-60 行),所有配置都在这里:
```yaml
- name: ⚙️ Configuration
id: config
run: |
# ========================================
# 📝 自定义配置区域 - 在此修改所有配置
# ========================================
# 📌 语义版本号规范 (SemVer)
# 格式: MAJOR.MINOR.PATCH[-prerelease]
#
# 示例: 1.0.0, 1.1.0, 1.1.1, 1.0.0-beta1, 2.0.0-rc1
#
# ⚠️ 重要规则:
# 1. 开发版本以 .0 开始 (如 1.0.0-beta1, 不是 1.0.1-beta1)
# 2. 多个开发版本会累加到同一 CHANGELOG 区域 (推荐使用 strip 模式)
# 3. CHANGELOG 内容是叠加而非覆盖
#
# 📖 详细规范请参考: CONVENTIONS.md
# Gitea 服务器地址(用于生成头像链接)
GITEA_SERVER="https://git.mytsl.cn"
# CHANGELOG 配置
CHANGELOG_SECTION_TITLE="### :pencil: What's Changed"
CHANGELOG_CONTRIBUTORS_TITLE="### :busts_in_silhouette: Contributors"
# CHANGELOG 版本号处理方式
# - "full": 使用完整 tag 名称1.0.0-beta → ## :bookmark: 1.0.0-beta
# - "strip": 去除 pre-release 后缀1.0.0-beta1 → ## :bookmark: 1.0.0
# ⚠️ 推荐使用 "strip" 模式以支持多个开发版本叠加到同一CHANGELOG区域
CHANGELOG_VERSION_MODE="strip"
# Release 配置
RELEASE_TITLE="{version}" # 可用变量: {version}
# Pre-release 配置
# - "false": 正式版本(会被标记为 latest
# - "true": 预发布版本(会被标记为 pre-release
# - "auto": 自动判断(根据 tag 名称中是否包含 alpha/beta/rc
RELEASE_PRERELEASE_MODE="auto"
# Draft 配置(是否创建为草稿)
RELEASE_IS_DRAFT="false" # true 或 false
# 需要忽略的提交模式
IGNORE_PATTERNS=(
"^Merge "
"^:memo: Auto update CHANGELOG"
"\[skip ci\]"
"\[ci skip\]"
"^style: "
"^chore: format"
)
# Git 提交消息格式
COMMIT_MESSAGE=":memo: Auto update CHANGELOG for {version} [skip ci]"
# 额外要上传到 Release 的文件
ADDITIONAL_RELEASE_FILES="" # 例如: "README.md LICENSE"
```
#### 常见配置示例
**修改 Gitea 服务器地址**
```bash
GITEA_SERVER="https://your-gitea.com"
```
**修改 CHANGELOG 标题为中文**
```bash
CHANGELOG_SECTION_TITLE="### :pencil: 变更内容"
CHANGELOG_CONTRIBUTORS_TITLE="### :busts_in_silhouette: 贡献者"
```
**配置 CHANGELOG 版本号处理方式**
```bash
# ⭐ 推荐strip 模式(支持内容叠加)
CHANGELOG_VERSION_MODE="strip"
# 所有相同前缀的版本共享同一个 CHANGELOG 区域,内容累积
# 1.0.0-beta1 → ## :bookmark: 1.0.0
# 1.0.0-beta2 → ## :bookmark: 1.0.0 (追加内容)
# 1.0.0 → ## :bookmark: 1.0.0 (追加内容)
# 方式 2: full 模式(每个 tag 独立条目)
CHANGELOG_VERSION_MODE="full"
# 每个 tag 都有独立的 CHANGELOG 条目
# 1.0.0-beta1 → ## :bookmark: 1.0.0-beta1
# 1.0.0-beta2 → ## :bookmark: 1.0.0-beta2
# 1.0.0 → ## :bookmark: 1.0.0
```
**推荐使用 `strip` 模式的原因**
1. ✅ **支持内容叠加** - 多个开发版本的提交累积在一起
2. ✅ **避免重复** - 所有版本的改动集中在一个区域
3. ✅ **完整历史** - 正式发布时 CHANGELOG 已包含所有开发阶段的提交
4. ✅ **符合最佳实践** - 大多数开源项目的做法
**strip 模式工作流程示例**
```bash
# 1. 推送第一个 beta 版本
git push origin 1.0.0-beta1
# → CHANGELOG: ## :bookmark: 1.0.0(创建新区域,添加 beta1 的提交)
# → Release: 1.0.0-beta1 (Pre-release 🏷️)
# 2. 推送第二个 beta 版本
git push origin 1.0.0-beta2
# → CHANGELOG: ## :bookmark: 1.0.0(叠加 beta2 的提交到现有区域)
# → Release: 1.0.0-beta2 (Pre-release 🏷️)
# 3. 推送 rc 版本
git push origin 1.0.0-rc1
# → CHANGELOG: ## :bookmark: 1.0.0(继续叠加 rc1 的提交)
# → Release: 1.0.0-rc1 (Pre-release 🏷️)
# 4. 推送正式版本
git push origin 1.0.0
# → CHANGELOG: ## :bookmark: 1.0.0(叠加正式版的提交,现在包含完整历史)
# → Release: 1.0.0 (Latest ✅)
```
**生成的 CHANGELOG 示例**(叠加模式):
```markdown
## :bookmark: 1.0.0
### :pencil: What's Changed
- :sparkles: Add user authentication (来自 beta1)
- :sparkles: Add profile page (来自 beta1)
- :bug: Fix login redirect (来自 beta2)
- :sparkles: Add password reset (来自 beta2)
- :memo: Update documentation (来自 rc1)
- :bug: Fix validation error (来自正式版)
### :busts_in_silhouette: Contributors
[所有版本的贡献者,自动去重]
---
```
**修改 Release 标题**
```bash
RELEASE_TITLE="{version}" # → 1.0.0
RELEASE_TITLE="v{version}" # → v1.0.0
RELEASE_TITLE="Release {version}" # → Release 1.0.0
RELEASE_TITLE="🎉 {version}" # → 🎉 1.0.0
```
**配置 Pre-release 标记**
```bash
# 方式 1: 自动判断(推荐)
RELEASE_PRERELEASE_MODE="auto"
# tag 包含 alpha/beta/rc/pre/preview/dev/test → Pre-release 🏷️
# 其他 tag → Latest (Stable) ✅
# 方式 2: 始终标记为正式版本
RELEASE_PRERELEASE_MODE="false"
# 所有 Release 都标记为 Latest ✅
# 方式 3: 始终标记为预发布版本
RELEASE_PRERELEASE_MODE="true"
# 所有 Release 都标记为 Pre-release 🏷️
```
**示例效果**
```bash
# auto 模式下:
1.0.0 → Latest ✅
1.0.0-beta1 → Pre-release 🏷️
1.0.0-rc1 → Pre-release 🏷️
1.0.0-alpha1 → Pre-release 🏷️
1.0.0.dev → Pre-release 🏷️
```
**创建为草稿**
```bash
RELEASE_IS_DRAFT="true" # Release 创建后不会立即发布,需要手动发布
```
**添加更多忽略的提交类型**
```bash
IGNORE_PATTERNS=(
"^Merge "
"^:memo: Auto update CHANGELOG"
"\[skip ci\]"
"^test: " # 添加:忽略测试
"^docs: " # 添加:忽略文档
"^refactor: " # 添加:忽略重构
)
```
**上传额外的文件到 Release**
```bash
ADDITIONAL_RELEASE_FILES="README.md LICENSE docs/guide.pdf"
```
**自定义提交消息**
```bash
COMMIT_MESSAGE="docs: update CHANGELOG for {version}"
COMMIT_MESSAGE="chore(release): update CHANGELOG [{version}]"
```
### 4⃣ 创建 Release
#### 完整开发流程示例
```bash
# === 第一阶段Alpha 内部测试 ===
git commit -m ":sparkles: Add user authentication"
git tag 1.0.0-alpha1
git push origin 1.0.0-alpha1
git commit -m ":sparkles: Add user profile"
git tag 1.0.0-alpha2
git push origin 1.0.0-alpha2
# === 第二阶段Beta 公开测试 ===
git commit -m ":bug: Fix login redirect"
git tag 1.0.0-beta1
git push origin 1.0.0-beta1
git commit -m ":sparkles: Add password reset"
git tag 1.0.0-beta2
git push origin 1.0.0-beta2
# === 第三阶段RC 发布候选 ===
git commit -m ":memo: Update documentation"
git tag 1.0.0-rc1
git push origin 1.0.0-rc1
# === 第四阶段:正式发布 ===
git tag 1.0.0
git push origin 1.0.0
# === 后续版本 ===
# 新增功能(向后兼容)
git commit -m ":sparkles: Add email notifications"
git tag 1.1.0
git push origin 1.1.0
# Bug 修复
git commit -m ":bug: Fix email sending"
git tag 1.1.1
git push origin 1.1.1
# 破坏性变更
git commit -m ":boom: Change API format"
git tag 2.0.0
git push origin 2.0.0
```
🎉 完成!工作流会自动:
**文件**[changelog-and-release.yml](.gitea/workflows/changelog_and_release.yml)
1. ✅ 加载自定义配置
2. ✅ 生成/叠加 CHANGELOG 条目
3. ✅ 合并并去重贡献者
4. ✅ 提交并推送到 main 分支
5. ✅ 创建 Release自动识别 Pre-release
6. ✅ 上传 CHANGELOG.md 及额外文件
💡 **详细配置**:查看 `changelog-and-release.yml` 文件顶部的 `env` 区域,所有配置项都有详细注释说明
---
## 📋 工作原理
#### 2. 📊 代码统计徽章工作流 (`update-stats-badge.yml`)
### 触发条件
**功能**:自动统计代码行数并生成徽章数据
**推送数字开头的 tag**
**特性**
- 📈 统计总代码行数和文件数
- 🌐 按语言分组统计
- 🎨 自动生成徽章 JSON 数据
- 📊 生成统计摘要JSON 格式)
- 🚫 灵活的目录排除配置
**触发方式**
```bash
# ✅ 会触发
git push origin 1.0.0
git push origin 1.1.0-beta1
git push origin 2.0.0-rc1
# 推送到主分支时自动触发
git push origin main
# ❌ 不会触发(不是数字开头)
git push origin v1.0.0
git push origin release-1.0
# 手动触发
# 仓库 → Actions → Update Code Statistics Badge → Run workflow
```
### 执行流程
**配置文件**[update-stats-badge.yml](.gitea/workflows/update_stats_badge.yaml)
```txt
Tag Push (1.0.0-beta1)
⚙️ 加载配置参数
📂 检出代码
🤖 检查是否为 bot 提交
📋 检查版本是否已存在1.0.0
↓ 已存在 → 叠加模式
↓ 不存在 → 创建新条目
📝 生成 CHANGELOG 条目
├─ 提取旧的提交记录(如果存在)
├─ 提取旧的贡献者(如果存在)
├─ 追加新的提交记录
├─ 合并并去重贡献者
└─ 生成完整的条目
📤 提交并推送
🗑️ 删除旧 Release
🚀 创建新 Release
├─ 使用配置的标题格式
├─ 自动识别 Pre-release
└─ 上传配置的附件列表
✅ 完成
```
**markdown引用格式**: `![C++](https://img.shields.io/endpoint?url=https://你的gitea/用户名/仓库/raw/branch/分支/badges/cpp-lines.json)`
### 版本号处理逻辑
#### Strip 模式(推荐)
```txt
Tag: 1.0.0-beta1
↓ strip 处理
CHANGELOG Version: 1.0.0
检查 1.0.0 是否存在?
├─ 不存在 → 创建新区域
└─ 已存在 → 叠加内容
Tag: 1.0.0-beta2
↓ strip 处理
CHANGELOG Version: 1.0.0
检查 1.0.0 是否存在?
└─ 已存在 → 叠加内容(保留 beta1 的提交)
Tag: 1.0.0
↓ strip 处理
CHANGELOG Version: 1.0.0
检查 1.0.0 是否存在?
└─ 已存在 → 叠加内容(保留所有之前的提交)
```
### 配置加载日志示例
执行时会显示配置摘要:
```txt
======================================
⚙️ Loading workflow configuration
======================================
📋 Configuration Summary:
🏷️ Tag version: 1.0.0-beta1
📝 CHANGELOG version: 1.0.0
🌐 Gitea Server: https://git.mytsl.cn
📄 CHANGELOG Title: ### :pencil: What's Changed
🚀 Release Title: 1.0.0-beta1
🏷️ Pre-release: yes (auto-detected: 1.0.0-beta1 contains pre-release keyword)
📄 Draft: false
💬 Commit Message: :memo: Auto update CHANGELOG for 1.0.0-beta1 [skip ci]
📎 Additional Files: README.md LICENSE
✅ Configuration loaded successfully
```
### 生成的内容示例
#### CHANGELOG.md叠加模式
```markdown
## :bookmark: 1.0.0
### :pencil: What's Changed
- :sparkles: Add user authentication ([a1b2c3d](链接)) by @developer1
- :sparkles: Add user profile ([e4f5g6h](链接)) by @developer1
- :bug: Fix login redirect ([i7j8k9l](链接)) by @developer2
- :sparkles: Add password reset ([m0n1o2p](链接)) by @developer2
### :busts_in_silhouette: Contributors
<a href="https://git.mytsl.cn/developer1">
<img src="https://git.mytsl.cn/developer1.png" alt="developer1" width="35" height="35" />
</a>
<a href="https://git.mytsl.cn/developer2">
<img src="https://git.mytsl.cn/developer2.png" alt="developer2" width="35" height="35" />
</a>
---
```
#### Release
- **标题**:根据 `RELEASE_TITLE` 生成(例如:`1.0.0-beta1` 或 `v1.0.0-beta1`
- **标记**
- `RELEASE_PRERELEASE_MODE="auto"` → 根据 tag 名称自动判断
- `RELEASE_PRERELEASE_MODE="false"` → Latest ✅
- `RELEASE_PRERELEASE_MODE="true"` → Pre-release 🏷️
- **内容**CHANGELOG 中该版本的完整内容(包含所有叠加的提交)
- **附件**`CHANGELOG.md` + `ADDITIONAL_RELEASE_FILES` 中的文件
**Latest Release 示例**
```txt
✅ Latest
1.0.0 ← RELEASE_TITLE
### :pencil: What's Changed
- :sparkles: Add user authentication ([a1b2c3d](链接))
- :sparkles: Add user profile ([e4f5g6h](链接))
- :bug: Fix login redirect ([i7j8k9l](链接))
- :sparkles: Add password reset ([m0n1o2p](链接))
### :busts_in_silhouette: Contributors
[所有贡献者头像]
```
**Pre-release 示例**
```txt
🏷️ Pre-release
1.0.0-beta1 ← RELEASE_TITLE
### :pencil: What's Changed
- :sparkles: Add user authentication ([a1b2c3d](链接))
- :sparkles: Add user profile ([e4f5g6h](链接))
```
💡 **详细配置**:查看 `update-stats-badge.yml` 文件顶部的 `env` 区域,包含语言分组、颜色、排除目录等配置
---
## 🎨 配置示例库
### 🔜 待扩展
### 示例 1标准 SemVer 配置(推荐)
更多自动化场景:
```bash
GITEA_SERVER="https://git.example.com"
CHANGELOG_SECTION_TITLE="### :pencil: What's Changed"
CHANGELOG_CONTRIBUTORS_TITLE="### :busts_in_silhouette: Contributors"
CHANGELOG_VERSION_MODE="strip" # 支持内容叠加
RELEASE_TITLE="{version}"
RELEASE_PRERELEASE_MODE="auto" # 自动识别 beta/rc 等
COMMIT_MESSAGE=":memo: Auto update CHANGELOG for {version} [skip ci]"
```
### 示例 2中文环境
```bash
GITEA_SERVER="https://git.example.com"
CHANGELOG_SECTION_TITLE="### :pencil: 变更内容"
CHANGELOG_CONTRIBUTORS_TITLE="### :busts_in_silhouette: 贡献者"
CHANGELOG_VERSION_MODE="strip"
RELEASE_TITLE="版本 {version}"
RELEASE_PRERELEASE_MODE="auto"
COMMIT_MESSAGE="文档: 更新 CHANGELOG [{version}] [skip ci]"
```
### 示例 3详细记录
```bash
GITEA_SERVER="https://git.example.com"
CHANGELOG_SECTION_TITLE="### :clipboard: What's Changed in This Version"
CHANGELOG_CONTRIBUTORS_TITLE="### :heart: Special Thanks To"
CHANGELOG_VERSION_MODE="strip"
RELEASE_TITLE="Release {version}"
RELEASE_PRERELEASE_MODE="auto"
RELEASE_IS_DRAFT="false"
COMMIT_MESSAGE=":memo: Automated CHANGELOG update for version {version} [skip ci]"
ADDITIONAL_RELEASE_FILES="README.md LICENSE CHANGELOG.md docs/MIGRATION.md"
IGNORE_PATTERNS=(
"^Merge "
"^:memo: Auto update CHANGELOG"
"\[skip ci\]"
"^Bump version"
"^style: "
"^chore: format"
"^chore: lint"
"^test: "
"^docs: update readme"
)
```
### 示例 4开发分支始终 Pre-release
```bash
GITEA_SERVER="https://git.example.com"
CHANGELOG_SECTION_TITLE="### :test_tube: Development Changes"
CHANGELOG_CONTRIBUTORS_TITLE="### :busts_in_silhouette: Contributors"
CHANGELOG_VERSION_MODE="strip"
RELEASE_TITLE="Dev Build {version}"
RELEASE_PRERELEASE_MODE="true" # 始终标记为 pre-release
COMMIT_MESSAGE=":test_tube: Update CHANGELOG for dev build {version} [skip ci]"
```
### 示例 5草稿模式
```bash
GITEA_SERVER="https://git.example.com"
CHANGELOG_SECTION_TITLE="### :pencil: What's Changed"
CHANGELOG_CONTRIBUTORS_TITLE="### :busts_in_silhouette: Contributors"
CHANGELOG_VERSION_MODE="strip"
RELEASE_TITLE="{version}"
RELEASE_PRERELEASE_MODE="auto"
RELEASE_IS_DRAFT="true" # 创建为草稿
COMMIT_MESSAGE=":memo: Auto update CHANGELOG for {version} [skip ci]"
```
---
## 📖 版本号最佳实践
### 快速参考
**版本演进示例**
```txt
0.1.0 → 0.2.0 → 1.0.0 (首个稳定版) → 1.1.0 (新功能) → 1.1.1 (Bug修复) → 2.0.0 (破坏性变更)
```
**何时递增版本号**
- **MAJOR**:不兼容的 API 修改、破坏性变更
- **MINOR**:新增功能但保持向后兼容
- **PATCH**Bug 修复、安全补丁
**Pre-release 版本命名**
```bash
1.0.0-alpha1 # 内部测试,可能不稳定
1.0.0-beta1 # 公开测试,功能基本完成
1.0.0-rc1 # 发布候选,准备正式发布
```
**版本号检查清单**
- [ ] 格式:`MAJOR.MINOR.PATCH` (不要 `v` 前缀)
- [ ] 开发版本以 `.0` 开始 (如 `1.0.0-beta1`)
- [ ] Pre-release 后缀完整 (`beta1` 不是 `b1`)
- [ ] `CHANGELOG_VERSION_MODE` 设置为 `"strip"`
📖 **详细说明请参考**[CONVENTIONS.md](./CONVENTIONS.md) - 包含完整的 SemVer 规范、更新规则和示例
- 🧪 **自动测试** - 推送代码时运行单元测试
- 🏗️ **自动构建** - 构建并发布 Docker 镜像
- 📊 **代码质量** - 运行代码检查和静态分析
- 🌐 **自动部署** - 部署到生产环境
- 📧 **通知集成** - 发送构建结果到 Slack/邮件
- 🔐 **安全扫描** - 依赖漏洞扫描
---
## ❓ 常见问题
### Q1: 为什么推荐使用 strip 模式
### Q1: 如何配置 Token
**原因**
**步骤**
1. **支持内容叠加** - 多个开发版本的提交会累积在同一个 CHANGELOG 区域
2. **完整的开发历史** - 正式发布时CHANGELOG 已包含所有开发阶段的提交
3. **避免重复** - 不需要为每个 beta/rc 版本创建独立的 CHANGELOG 条目
4. **便于阅读** - 所有相关的变更集中在一起
1. Gitea → 设置 → 应用 → 生成新令牌
2. 权限:勾选 **repo**
3. 仓库 → Settings → Actions → Secrets → 添加 Secret
4. 名称:`WORKFLOW`,值:你的 Token
**对比**
```bash
# Strip 模式
1.0.0-beta1, 1.0.0-beta2, 1.0.0 → 所有提交在 ## :bookmark: 1.0.0
# Full 模式
1.0.0-beta1 → ## :bookmark: 1.0.0-beta1
1.0.0-beta2 → ## :bookmark: 1.0.0-beta2
1.0.0 → ## :bookmark: 1.0.0
```
💡 **提示**:一个 Token 即可完成所有操作Git + API
---
### Q2: 如何修改配置?
**所有配置都在第一个步骤中**,只需编辑 `changelog-and-release.yaml`**⚙️ Configuration** 步骤即可。
修改后提交推送:
```bash
git add .gitea/workflows/changelog-and-release.yaml
git commit -m "ci: update workflow configuration"
git push
```
下次推送 tag 时会使用新配置。
---
### Q3: 版本号格式错误会怎样?
**常见错误**
```bash
❌ v1.0.0 # 不应该有 v 前缀
❌ 1.0.1-beta1 # 开发版本应该从 .0 开始
❌ 1.0.0beta1 # 缺少连字符
❌ 1.0.0-b1 # 后缀应该完整写 beta1
```
**影响**
- 工作流仍会执行
- 但可能无法正确识别为 Pre-release
- CHANGELOG 版本号可能不符合预期
---
### Q4: 如何查看 CHANGELOG 是否正确叠加?
**检查步骤**
1. 查看 CHANGELOG.md 文件
2. 找到对应的版本区域(如 `## :bookmark: 1.0.0`
3. 确认包含所有 beta/rc 版本的提交
**示例**
```markdown
## :bookmark: 1.0.0
### :pencil: What's Changed
- 提交 1来自 1.0.0-beta1
- 提交 2来自 1.0.0-beta1
- 提交 3来自 1.0.0-beta2
- 提交 4来自 1.0.0-rc1
- 提交 5来自 1.0.0
```
---
### Q5: 贡献者重复怎么办?
**不用担心!** 工作流会自动去重贡献者。
如果同一个人在多个版本都有贡献,他们的头像只会出现一次。
---
### Q6: Workflow 没有触发?
### Q2: `changelog_and_release` 没有触发?
**检查清单**
- [ ] Workflow 文件在 `.gitea/workflows/` 目录
- [ ] 文件名`changelog-and-release.yaml`(或 `.yml`
- [ ] Tag 格式正确(数字开头,如 `1.0.0``1.0.0-beta1`
- [ ] Runner 已部署并正常运行
- [ ] 仓库已启用 Actions
- [ ] GITEA_TOKEN 已正确配置
- [ ] Workflow 文件在 `.gitea/workflows/` 目录
- [ ] 文件名正确(如 `changelog-and-release.yml`
- [ ] Token 已正确配置Secret 名称为 `WORKFLOW`
- [ ] Tag 格式正确(数字开头,如 `1.0.0`
- [ ] Gitea Actions 已启用
- [ ] Runner 正常运行
---
### Q7: 提示 "GITEA_TOKEN not found"
### Q3: `changelog_and_release` 版本号格式有要求吗?
**原因**Token 未正确配置
**解决方法**
1. 确认已添加名为 `GITEA_TOKEN` 的 Secret
2. 确认 Token 具有 `repo` 权限
3. 尝试重新生成并配置 Token
---
### Q8: CHANGELOG 没有更新?
**可能原因**
1. 没有新的有效提交
2. 所有提交都匹配了 `IGNORE_PATTERNS` 中的规则
3. 版本已存在且是 bot 提交
**检查方法**
推荐使用**语义化版本号SemVer**
```bash
# 查看两个 tag 之间的提交
git log $(git describe --tags --abbrev=0 @^)..@ --oneline --no-merges
✅ 推荐格式:
1.0.0 # 正式版本
1.0.0-beta1 # 预发布版本
# 检查 workflow 日志中的 "Found X valid commits"
❌ 不推荐:
v1.0.0 # 不要使用 v 前缀
1.0.0-b1 # 后缀要完整
```
详细规范请参考 workflow 文件中的注释说明。
---
### Q9: 如何跳过自动化
### Q4: 如何查看执行日志?
在 commit 消息中添加 `[skip ci]`
```bash
git commit -m "docs: update readme [skip ci]"
```
**注意**Tag 推送无法跳过,因为 workflow 在 tag 推送时触发
仓库 → Actions → 点击对应的 workflow 运行记录 → 查看详细日志
---
### Q10: 什么时候使用 full 模式
### Q5: Token 权限不足怎么办?
**使用场景**
确保 Token 具有以下权限:
- 你希望每个 tag 都有独立的 CHANGELOG 条目
- 不需要累积开发版本的提交
- 每个版本的变更需要完全独立
- ✅ **repo** - 完整仓库访问权限
- 用于读取代码
- 用于推送提交
- 用于创建 Release
**大多数情况下,推荐使用 strip 模式。**
如果还是失败,尝试重新生成 Token。
---
### Q11: Release 标记Latest/Pre-release是如何工作的
## 🤝 贡献
**GitHub/Gitea 的自动标记规则**
1. **Latest最新稳定版**
- 自动标记给最新的非 pre-release 版本
- 显示绿色的 "Latest" 标签 ✅
2. **Pre-release预发布版**
- 通过 API 设置 `prerelease: true`
- 显示橙色的 "Pre-release" 标签 🏷️
- 不会被标记为 Latest
**auto 模式识别的关键词**
- `alpha`, `beta`, `rc`, `pre`, `preview`, `dev`, `test`
**示例**
```bash
1.0.0 → Latest ✅
1.0.0-beta1 → Pre-release 🏷️
1.0.0-rc1 → Pre-release 🏷️
1.1.0 → Latest ✅ (1.0.0 变为旧版本)
```
欢迎提交更多实用的 Workflow 示例!
---
## 🔍 调试技巧
### 查看配置加载情况
每次执行都会在日志开头显示配置摘要:
```
======================================
⚙️ Loading workflow configuration
======================================
📋 Configuration Summary:
🏷️ Tag version: 1.0.0-beta1
📝 CHANGELOG version: 1.0.0
🌐 Gitea Server: https://git.mytsl.cn
📝 CHANGELOG Title: ### :pencil: What's Changed
🚀 Release Title: 1.0.0-beta1
🏷️ Pre-release: yes (auto-detected)
💬 Commit Message: :memo: Auto update CHANGELOG for 1.0.0-beta1 [skip ci]
📎 Additional Files: none
```
### 本地测试配置
提取 Configuration 步骤中的 bash 代码,在本地运行测试:
```bash
#!/bin/bash
# 模拟环境变量
export github_ref_name="1.0.0-beta1"
# 复制你的配置
GITEA_SERVER="https://git.mytsl.cn"
CHANGELOG_VERSION_MODE="strip"
RELEASE_TITLE="{version}"
COMMIT_MESSAGE=":memo: Auto update CHANGELOG for {version} [skip ci]"
# 测试版本处理
VERSION="$github_ref_name"
if [[ "$CHANGELOG_VERSION_MODE" == "strip" ]]; then
CHANGELOG_VERSION=$(echo "$VERSION" | sed -E 's/-(alpha|beta|rc|pre|preview|dev|test)[0-9]*$//')
echo "Tag: $VERSION → CHANGELOG: $CHANGELOG_VERSION"
fi
# 测试变量替换
RELEASE_TITLE="${RELEASE_TITLE//\{version\}/$VERSION}"
COMMIT_MSG="${COMMIT_MESSAGE//\{version\}/$VERSION}"
echo "Release Title: $RELEASE_TITLE"
echo "Commit Message: $COMMIT_MSG"
```
### 验证版本号格式
```bash
# 测试版本号是否符合规范
version="1.0.0-beta1"
# 检查是否数字开头
if [[ "$version" =~ ^[0-9] ]]; then
echo "✅ 格式正确"
else
echo "❌ 版本号必须以数字开头"
fi
# 检查是否包含 pre-release 标识
if [[ "$version" =~ (alpha|beta|rc|pre|preview|dev|test) ]]; then
echo "🏷️ 将被识别为 Pre-release"
else
echo "✅ 将被识别为 Latest"
fi
```
---
## 📄 许可 MIT

View File

@ -1,28 +0,0 @@
services:
gitea-runner:
build: .
container_name: gitea-runner
restart: unless-stopped
privileged: true
volumes:
- ./runner-data:/data
- ./setup.sh:/data/setup.sh:ro
- ./register.sh:/data/register.sh:ro
- ./manage.sh:/data/manage.sh:ro
- ./entrypoint.sh:/data/entrypoint.sh:ro
- /var/run/docker.sock:/var/run/docker.sock
environment:
- TZ=Asia/Shanghai
# 如果需要使用代理,取消下面的注释并修改为你的代理地址
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
- http_proxy=http://host.docker.internal:20122
- https_proxy=http://host.docker.internal:20122
- HTTP_PROXY=http://host.docker.internal:20122
- HTTPS_PROXY=http://host.docker.internal:20122
# - no_proxy=localhost,127.0.0.1
# Linux 系统需要取消下面的注释以支持 host.docker.internal
# extra_hosts:
# - "host.docker.internal:host-gateway"

View File

@ -1,321 +0,0 @@
#!/bin/bash
set -e
echo "=========================================="
echo " Gitea Runner Registration Script "
echo "=========================================="
echo ""
# 检查 act_runner 是否安装
if ! command -v act_runner &> /dev/null; then
echo "✗ act_runner is not installed!"
echo ""
echo "Please run the setup script first:"
echo " docker-compose exec gitea-runner /data/setup.sh"
exit 1
fi
echo "✓ act_runner found: $(act_runner --version)"
echo ""
# 获取注册信息并验证
while true; do
read -p "Enter Gitea instance URL (e.g., https://gitea.example.com): " GITEA_INSTANCE
# 验证 URL 格式
if [[ ! "$GITEA_INSTANCE" =~ ^https?:// ]]; then
echo "✗ Error: URL must start with http:// or https://"
echo ""
continue
fi
# 移除末尾的斜杠
GITEA_INSTANCE="${GITEA_INSTANCE%/}"
echo "✓ URL validated: $GITEA_INSTANCE"
break
done
read -p "Enter registration token: " GITEA_TOKEN
if [ -z "$GITEA_TOKEN" ]; then
echo "✗ Error: Token cannot be empty!"
exit 1
fi
read -p "Enter runner name (default: docker-runner): " RUNNER_NAME
RUNNER_NAME=${RUNNER_NAME:-docker-runner}
# 多个 label逗号分隔无空格
# ubuntu-22.04:host://ubuntu:22.04,ubuntu-20.04:host://ubuntu:20.04,node:docker://node:18
read -p "Enter runner labels (default: ubuntu-22.04:docker://ubuntu:22.04): " RUNNER_LABELS
RUNNER_LABELS=${RUNNER_LABELS:-ubuntu-22.04:host://ubuntu:22.04}
# 创建 runner 目录
RUNNER_DIR="/data/runners/${RUNNER_NAME}"
mkdir -p "$RUNNER_DIR"
cd "$RUNNER_DIR"
echo ""
echo "Registration Information:"
echo " Instance: $GITEA_INSTANCE"
echo " Name: $RUNNER_NAME"
echo " Labels: $RUNNER_LABELS"
echo " Directory: $RUNNER_DIR"
echo ""
# 检查是否已经注册
if [ -f ".runner" ] || [ -f "config.yaml" ]; then
echo "⚠ Runner already exists in this directory!"
read -p "Do you want to re-register? This will overwrite existing configuration. (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Registration cancelled."
exit 0
fi
echo "Cleaning up existing runner..."
# 停止现有 runner
echo "Stopping existing runner..."
supervisorctl stop "runner-${RUNNER_NAME}" 2>/dev/null || true
# 删除 supervisor 配置
rm -f "/etc/supervisor/conf.d/runner-${RUNNER_NAME}.conf"
# 重新加载 supervisor
supervisorctl reread 2>/dev/null || true
supervisorctl update 2>/dev/null || true
# 删除日志
rm -f "/var/log/supervisor/runner-${RUNNER_NAME}".*.log*
# 删除旧配置和缓存
rm -f .runner config.yaml
rm -rf cache
fi
# 执行注册
echo ""
echo "Registering runner..."
act_runner register \
--instance "$GITEA_INSTANCE" \
--token "$GITEA_TOKEN" \
--name "$RUNNER_NAME" \
--labels "$RUNNER_LABELS" \
--no-interactive
if [ ! -f ".runner" ]; then
echo ""
echo "✗ Registration failed! .runner file not created."
exit 1
fi
echo "✓ Registration successful!"
# 生成配置文件
echo ""
echo "Generating config.yaml..."
act_runner generate-config > config.yaml
echo "✓ Configuration file generated!"
# 创建缓存目录
mkdir -p cache
# 使用 Python 修改配置(最可靠的方法)
echo ""
echo "Configuring runner settings..."
if command -v python3 &> /dev/null; then
python3 << PYEOF
import yaml
import sys
try:
# 读取生成的配置
with open('config.yaml', 'r') as f:
config = yaml.safe_load(f)
# 读取 .runner 获取实际注册的 labels
import json
with open('.runner', 'r') as f:
runner_data = json.load(f)
# 使用 .runner 中的 labels这是实际注册的
registered_labels = runner_data.get('labels', [])
# 修改配置
if 'runner' not in config:
config['runner'] = {}
# 使用实际注册的 labels
config['runner']['labels'] = registered_labels
config['runner']['capacity'] = 2
# 启用缓存
if 'cache' not in config:
config['cache'] = {}
config['cache']['enabled'] = True
config['cache']['dir'] = './cache'
# 保存配置
with open('config.yaml', 'w') as f:
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
print("✓ Configuration updated using Python")
print(f" - Labels: {registered_labels}")
print(f" - Capacity: 2")
print(f" - Cache enabled: ./cache")
sys.exit(0)
except Exception as e:
print(f"✗ Python configuration failed: {e}", file=sys.stderr)
sys.exit(1)
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: 2/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: 2/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 "Validating configuration..."
# 检查 YAML 语法
if command -v python3 &> /dev/null; then
python3 << PYEOF
import yaml
import sys
try:
with open('config.yaml', 'r') as f:
yaml.safe_load(f)
print("✓ config.yaml syntax is valid")
sys.exit(0)
except Exception as e:
print(f"✗ config.yaml syntax error: {e}", file=sys.stderr)
sys.exit(1)
PYEOF
if [ $? -ne 0 ]; then
echo ""
echo "✗ Configuration file has syntax errors!"
echo " Backup available at: config.yaml.bak"
exit 1
fi
fi
# 显示配置摘要
echo ""
echo "Configuration Summary:"
echo "-------------------------------------------"
echo ".runner labels:"
cat .runner | grep -A 10 '"labels"' | head -15
echo ""
echo "config.yaml labels:"
grep -A 5 "^ labels:" config.yaml | head -10
# 创建 supervisor 配置
echo ""
echo "Creating supervisor configuration..."
cat > "/etc/supervisor/conf.d/runner-${RUNNER_NAME}.conf" <<EOF
[program:runner-${RUNNER_NAME}]
command=/usr/local/bin/act_runner daemon --config ${RUNNER_DIR}/config.yaml
directory=${RUNNER_DIR}
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/runner-${RUNNER_NAME}.err.log
stdout_logfile=/var/log/supervisor/runner-${RUNNER_NAME}.out.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=10
user=root
environment=HOME="/root"
EOF
echo "✓ Supervisor configuration created"
# 重新加载 supervisor
echo ""
echo "Reloading supervisor..."
supervisorctl reread
supervisorctl update
echo "Starting runner..."
sleep 2
# 启动 runner
supervisorctl restart "runner-${RUNNER_NAME}" 2>/dev/null || \
supervisorctl start "runner-${RUNNER_NAME}"
# 等待启动
sleep 3
# 显示状态
RUNNER_STATUS=$(supervisorctl status "runner-${RUNNER_NAME}" 2>/dev/null || echo "UNKNOWN")
echo ""
echo "=========================================="
echo "✓ Runner registered and started!"
echo "=========================================="
echo ""
echo "Runner Information:"
echo " Name: $RUNNER_NAME"
echo " Directory: $RUNNER_DIR"
echo " Status: $RUNNER_STATUS"
echo ""
echo "Configuration files:"
echo " .runner: $(ls -lh .runner 2>/dev/null | awk '{print $5}' || echo 'N/A')"
echo " config.yaml: $(ls -lh config.yaml 2>/dev/null | awk '{print $5}' || echo 'N/A')"
echo ""
echo "Useful commands:"
echo " View logs: docker-compose exec gitea-runner /data/manage.sh logs ${RUNNER_NAME}"
echo " Follow logs: docker-compose exec gitea-runner /data/manage.sh follow ${RUNNER_NAME}"
echo " Check status: docker-compose exec gitea-runner /data/manage.sh status"
echo " Restart: docker-compose exec gitea-runner /data/manage.sh restart ${RUNNER_NAME}"
echo ""
# 显示最近的日志
if [ -f "/var/log/supervisor/runner-${RUNNER_NAME}.out.log" ]; then
echo "Recent logs:"
echo "-------------------------------------------"
tail -n 20 "/var/log/supervisor/runner-${RUNNER_NAME}.out.log" 2>/dev/null || echo "No logs yet"
echo ""
fi
# 检查是否有错误
if [ -f "/var/log/supervisor/runner-${RUNNER_NAME}.err.log" ]; then
# 只查找 error/fatal/panic 级别的日志
ERR_CONTENT=$(grep -E 'level=(error|fatal|panic)' \
"/var/log/supervisor/runner-${RUNNER_NAME}.err.log" | tail -n 5 2>/dev/null)
if [ -n "$ERR_CONTENT" ]; then
echo "❌ Recent errors detected:"
echo "-------------------------------------------"
echo "$ERR_CONTENT"
echo ""
echo "Check full error log with:"
echo " docker-compose exec gitea-runner cat /var/log/supervisor/runner-${RUNNER_NAME}.err.log"
fi
fi

View File

@ -10,111 +10,134 @@ PERSISTENT_BIN="/data/bin"
RUNNER_PATH="$PERSISTENT_BIN/act_runner"
SYSTEM_LINK="/usr/local/bin/act_runner"
# 读取环境变量配置
ENABLE_BUILDX="${ENABLE_BUILDX:-false}"
BINFMT_METHOD="${BINFMT_METHOD:-update-binfmts}" # update-binfmts 或 tonistiigi
# 创建必要目录
mkdir -p /data/runners
mkdir -p "$PERSISTENT_BIN"
mkdir -p /data/buildx
mkdir -p /var/log/supervisor
mkdir -p /var/run
# ============================================
# 初始化 Docker Buildx 支持
# 初始化 Docker Buildx 支持(可选)
# ============================================
echo ""
echo "Initializing Docker Buildx..."
# 启动 Docker 守护进程(如果使用主机 socket 则跳过)
if [ -S /var/run/docker.sock ]; then
echo "✓ Using host Docker socket"
else
echo "Starting Docker daemon..."
dockerd > /var/log/dockerd.log 2>&1 &
sleep 5
fi
# 等待 Docker 就绪
echo "Waiting for Docker daemon..."
for i in {1..30}; do
if docker info > /dev/null 2>&1; then
echo "✓ Docker daemon is ready"
break
fi
if [ $i -eq 30 ]; then
echo "✗ Docker daemon failed to start"
[ -f /var/log/dockerd.log ] && cat /var/log/dockerd.log
exit 1
fi
sleep 1
done
# 注册 QEMU binfmt
echo ""
echo "Registering QEMU binary formats..."
update-binfmts --enable 2>/dev/null || {
echo "⚠ binfmt_misc not available, trying to mount..."
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc 2>/dev/null || true
update-binfmts --enable
}
# 验证多架构支持
echo "Verifying multi-arch support..."
if docker run --rm arm64v8/alpine uname -m > /dev/null 2>&1; then
echo " ✓ arm64 support verified"
else
echo " ⚠ arm64 verification failed"
fi
if docker run --rm amd64/alpine uname -m > /dev/null 2>&1; then
echo " ✓ amd64 support verified"
else
echo " ⚠ amd64 verification failed"
fi
# 配置 Buildx
if [ ! -f "/data/buildx/.configured" ]; then
if [ "$ENABLE_BUILDX" = "true" ]; then
echo ""
echo "Setting up Buildx for the first time..."
echo "Initializing Docker Buildx..."
mkdir -p /data/buildx
# 创建 BuildKit 配置
cat > /data/buildx/buildkitd.toml <<EOF
# 启动 Docker 守护进程(如果使用主机 socket 则跳过)
if [ -S /var/run/docker.sock ]; then
echo "✓ Using host Docker socket"
else
echo "Starting Docker daemon..."
dockerd > /var/log/dockerd.log 2>&1 &
sleep 5
fi
# 等待 Docker 就绪
echo "Waiting for Docker daemon..."
for i in {1..30}; do
if docker info > /dev/null 2>&1; then
echo "✓ Docker daemon is ready"
break
fi
if [ $i -eq 30 ]; then
echo "✗ Docker daemon failed to start"
[ -f /var/log/dockerd.log ] && cat /var/log/dockerd.log
exit 1
fi
sleep 1
done
# 注册 QEMU binfmt
echo ""
echo "Registering QEMU binary formats..."
if [ "$BINFMT_METHOD" = "tonistiigi" ]; then
# 使用 tonistiigi/binfmt更可靠
if docker run --privileged --rm tonistiigi/binfmt --install arm64,amd64 > /var/log/binfmt-install.log 2>&1; then
echo "✓ Installed binfmt via tonistiigi/binfmt"
grep -m1 "Installed" /var/log/binfmt-install.log || true
else
echo "⚠ binfmt install via tonistiigi/binfmt failed, trying local tools..."
if command -v update-binfmts > /dev/null 2>&1; then
update-binfmts --enable 2>/dev/null || true
else
echo "⚠ update-binfmts not available; multi-arch emulation may be limited"
fi
fi
else
# 使用 update-binfmts传统方法
update-binfmts --enable 2>/dev/null || {
echo "⚠ binfmt_misc not available, trying to mount..."
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc 2>/dev/null || true
update-binfmts --enable
}
fi
# 验证多架构支持
echo "Verifying multi-arch support..."
if docker run --rm arm64v8/alpine uname -m > /dev/null 2>&1; then
echo " ✓ arm64 support verified"
else
echo " ⚠ arm64 verification failed"
fi
if docker run --rm amd64/alpine uname -m > /dev/null 2>&1; then
echo " ✓ amd64 support verified"
else
echo " ⚠ amd64 verification failed"
fi
# 配置 Buildx
if [ ! -f "/data/buildx/.configured" ]; then
echo ""
echo "Setting up Buildx for the first time..."
# 创建 BuildKit 配置
cat > /data/buildx/buildkitd.toml <<EOF
[worker.oci]
max-parallelism = 4
EOF
# 创建 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
# 验证
echo "Verifying Buildx..."
docker buildx inspect --bootstrap > /dev/null 2>&1
# 标记为已配置
touch /data/buildx/.configured
echo "✓ Buildx configured successfully!"
docker buildx inspect | grep "Platforms:" | head -1
else
echo "✓ Buildx already configured"
# 确保 builder 可用
docker buildx use gitea-multiarch 2>/dev/null || {
echo "⚠ Recreating Buildx builder..."
docker buildx rm gitea-multiarch 2>/dev/null || true
# 创建 Buildx builder
docker buildx create \
--name gitea-multiarch \
--driver docker-container \
--bootstrap \
--use 2>/dev/null
}
fi
--use \
--config /data/buildx/buildkitd.toml 2>/dev/null || \
docker buildx use gitea-multiarch 2>/dev/null
echo ""
# 验证
echo "Verifying Buildx..."
docker buildx inspect --bootstrap > /dev/null 2>&1
# 标记为已配置
touch /data/buildx/.configured
echo "✓ Buildx configured successfully!"
docker buildx inspect | grep "Platforms:" | head -1
else
echo "✓ Buildx already configured"
# 确保 builder 可用
docker buildx use gitea-multiarch 2>/dev/null || {
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
echo ""
fi
# ============================================
# 检查 act_runner 安装

View File

@ -20,12 +20,17 @@ echo ""
# 获取注册信息并验证
while true; do
read -p "Enter Gitea instance URL (e.g., https://gitea.example.com): " GITEA_INSTANCE
if [ -n "${GITEA_INSTANCE:-}" ]; then
echo "Using Gitea instance from env: $GITEA_INSTANCE"
else
read -p "Enter Gitea instance URL (e.g., https://gitea.example.com): " GITEA_INSTANCE
fi
# 验证 URL 格式
if [[ ! "$GITEA_INSTANCE" =~ ^https?:// ]]; then
echo "✗ Error: URL must start with http:// or https://"
echo ""
unset GITEA_INSTANCE
continue
fi
@ -35,20 +40,26 @@ while true; do
break
done
read -p "Enter registration token: " GITEA_TOKEN
if [ -n "${GITEA_TOKEN:-}" ]; then
echo "Using registration token from env."
else
read -p "Enter registration token: " GITEA_TOKEN
fi
if [ -z "$GITEA_TOKEN" ]; then
echo "✗ Error: Token cannot be empty!"
exit 1
fi
read -p "Enter runner name (default: docker-runner): " RUNNER_NAME
RUNNER_NAME=${RUNNER_NAME:-docker-runner}
DEFAULT_NAME="${DEFAULT_RUNNER_NAME:-docker-runner}"
read -p "Enter runner name (default: $DEFAULT_NAME): " RUNNER_NAME
RUNNER_NAME=${RUNNER_NAME:-$DEFAULT_NAME}
# 多个 label逗号分隔无空格
# ubuntu-22.04:host://ubuntu:22.04,ubuntu-20.04:host://ubuntu:20.04,node:docker://node:18
read -p "Enter runner labels (default: ubuntu-22.04:docker://ubuntu:22.04): " RUNNER_LABELS
RUNNER_LABELS=${RUNNER_LABELS:-ubuntu-22.04:host://ubuntu:22.04}
DEFAULT_LABEL="${DEFAULT_RUNNER_LABEL:-ubuntu-22.04:host://ubuntu:22.04}"
read -p "Enter runner labels (default: $DEFAULT_LABEL): " RUNNER_LABELS
RUNNER_LABELS=${RUNNER_LABELS:-$DEFAULT_LABEL}
# 创建 runner 目录
RUNNER_DIR="/data/runners/${RUNNER_NAME}"

View File

@ -0,0 +1,74 @@
FROM archlinux:latest
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# 初始化密钥和软件源
RUN pacman-key --init && \
pacman-key --populate archlinux && \
sed -i 's/^#ParallelDownloads/ParallelDownloads/' /etc/pacman.conf && \
printf "\n[archlinuxcn]\nServer = https://mirrors.ustc.edu.cn/archlinuxcn/\$arch\n" >> /etc/pacman.conf && \
pacman -Syy --noconfirm
# 安装基础工具、Buildx 支持和开发环境
RUN pacman -Syu --noconfirm && \
pacman -S --noconfirm --disable-download-timeout archlinuxcn-keyring && \
pacman -S --noconfirm --needed \
base-devel \
ca-certificates \
clang \
lld \
libc++ \
libc++abi \
cmake \
ninja \
curl \
git \
nodejs \
npm \
python \
python-pyyaml \
python-pip \
paru \
sudo \
vim \
supervisor \
qemu-user-static \
qemu-user-static-binfmt \
fakeroot \
docker \
docker-buildx \
&& pacman -Scc --noconfirm
# AUR 用户paru 需非 root 运行)
RUN useradd -m aur && \
echo "aur ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/aur && \
chmod 0440 /etc/sudoers.d/aur
# 安装额外的 AUR 工具
USER aur
WORKDIR /home/aur
RUN paru -S --noconfirm --needed --noprogressbar conan-bin llvm-mingw-w64-toolchain-ucrt-bin && \
paru -Scc --noconfirm && \
rm -rf ~/.cache/paru
# 切换回 root
USER root
WORKDIR /root
# 交叉工具链可用:确保 windres 在 PATH避免 CMake 查找失败
ENV PATH="/opt/llvm-mingw/llvm-mingw-ucrt/bin:${PATH}"
ENV RC="/opt/llvm-mingw/llvm-mingw-ucrt/bin/x86_64-w64-mingw32-windres"
# 验证 qemu 和 docker 安装
RUN qemu-aarch64-static --version && \
qemu-x86_64-static --version && \
docker --version
# 创建必要目录
RUN mkdir -p /data /etc/supervisor/conf.d /var/log/supervisor
# 设置工作目录
WORKDIR /data
# 使用自定义入口点
ENTRYPOINT ["/data/entrypoint.sh"]

View File

@ -0,0 +1,38 @@
services:
gitea-runner:
build: .
container_name: gitea-runner-buildx-archlinux
restart: unless-stopped
network_mode: "host" # 使用宿主机网络
privileged: true # Buildx 需要 privileged 模式
volumes:
- ./runner-data:/data
- ../../common/setup.sh:/data/setup.sh:ro
- ../../common/register.sh:/data/register.sh:ro
- ../../common/manage.sh:/data/manage.sh:ro
- ../../common/check_crlf.sh:/data/check_crlf.sh:ro
- ../../common/entrypoint.sh:/data/entrypoint.sh:ro
- /var/run/docker.sock:/var/run/docker.sock
environment:
- TZ=Asia/Shanghai
# Arch Linux Buildx 配置 - 使用 tonistiigi/binfmt
- ENABLE_BUILDX=true
- BINFMT_METHOD=tonistiigi
- GITEA_INSTANCE=https://git.mytsl.cn
- GITEA_TOKEN=
- DEFAULT_RUNNER_NAME=buildx-archlinux
- DEFAULT_RUNNER_LABEL=archlinux:host://archlinux:latest,company-server:host://archlinux:latest,buildx-archlinux:host://archlinux:latest
# 如果需要使用代理,取消下面的注释并修改为你的代理地址
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
# - http_proxy=http://host.docker.internal:20122
# - https_proxy=http://host.docker.internal:20122
# - HTTP_PROXY=http://host.docker.internal:20122
# - HTTPS_PROXY=http://host.docker.internal:20122
# - no_proxy=localhost,127.0.0.1
# Linux 系统需要取消下面的注释以支持 host.docker.internal
# extra_hosts:
# - "host.docker.internal:host-gateway"

View File

@ -15,12 +15,24 @@ RUN apt-get update && apt-get install -y \
lsb-release \
qemu-user-static \
binfmt-support \
apt-transport-https \
&& rm -rf /var/lib/apt/lists/*
# 安装 Docker包含 Buildx 插件)
RUN curl -fsSL https://get.docker.com -o get-docker.sh && \
sh get-docker.sh && \
rm get-docker.sh
RUN install -m 0755 -d /etc/apt/keyrings && \
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
chmod a+r /etc/apt/keyrings/docker.gpg && \
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list && \
apt-get update && \
apt-get install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin \
&& rm -rf /var/lib/apt/lists/*
# 验证安装
RUN docker --version && \

View File

@ -0,0 +1,37 @@
services:
gitea-runner:
build: .
container_name: gitea-runner-buildx-ubuntu-22
restart: unless-stopped
network_mode: "host" # 使用宿主机网络
privileged: true # Buildx 需要 privileged 模式
volumes:
- ./runner-data:/data
- ../../common/setup.sh:/data/setup.sh:ro
- ../../common/register.sh:/data/register.sh:ro
- ../../common/manage.sh:/data/manage.sh:ro
- ../../common/check_crlf.sh:/data/check_crlf.sh:ro
- ../../common/entrypoint.sh:/data/entrypoint.sh:ro
- /var/run/docker.sock:/var/run/docker.sock
environment:
- TZ=Asia/Shanghai
# Buildx 配置 - 启用多架构构建
- ENABLE_BUILDX=true
- GITEA_INSTANCE=https://git.mytsl.cn
- 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
# 如果需要使用代理,取消下面的注释并修改为你的代理地址
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
# - http_proxy=http://host.docker.internal:20122
# - https_proxy=http://host.docker.internal:20122
# - HTTP_PROXY=http://host.docker.internal:20122
# - HTTPS_PROXY=http://host.docker.internal:20122
# - no_proxy=localhost,127.0.0.1
# Linux 系统需要取消下面的注释以支持 host.docker.internal
# extra_hosts:
# - "host.docker.internal:host-gateway"

View File

@ -18,6 +18,7 @@ RUN apt-get update && apt-get install -y \
jq \
python3 \
python3-yaml \
lsb-release \
supervisor \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

View File

@ -0,0 +1,35 @@
services:
gitea-runner:
build: .
container_name: gitea-runner-standard-ubuntu-22
restart: unless-stopped
network_mode: "host" # 使用宿主机网络
volumes:
- ./runner-data:/data
- ../../common/setup.sh:/data/setup.sh:ro
- ../../common/register.sh:/data/register.sh:ro
- ../../common/manage.sh:/data/manage.sh:ro
- ../../common/entrypoint.sh:/data/entrypoint.sh:ro
- /var/run/docker.sock:/var/run/docker.sock
environment:
- TZ=Asia/Shanghai
# Standard 配置 - 不启用 Buildx
- ENABLE_BUILDX=false
- GITEA_INSTANCE=https://git.mytsl.cn
- 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
# 如果需要使用代理,取消下面的注释并修改为你的代理地址
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
# - http_proxy=http://host.docker.internal:20122
# - https_proxy=http://host.docker.internal:20122
# - HTTP_PROXY=http://host.docker.internal:20122
# - HTTPS_PROXY=http://host.docker.internal:20122
# - no_proxy=localhost,127.0.0.1
# Linux 系统需要取消下面的注释以支持 host.docker.internal
# extra_hosts:
# - "host.docker.internal:host-gateway"

View File

@ -1,30 +0,0 @@
services:
gitea-runner:
build: .
container_name: gitea-runner
restart: unless-stopped
volumes:
- ./runner-data:/data
- ./setup.sh:/data/setup.sh:ro
- ./register.sh:/data/register.sh:ro
- ./manage.sh:/data/manage.sh:ro
- ./entrypoint.sh:/data/entrypoint.sh:ro
- /var/run/docker.sock:/var/run/docker.sock
environment:
- TZ=Asia/Shanghai
# 如果需要使用代理,取消下面的注释并修改为你的代理地址
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
- http_proxy=http://host.docker.internal:20122
- https_proxy=http://host.docker.internal:20122
- HTTP_PROXY=http://host.docker.internal:20122
- HTTPS_PROXY=http://host.docker.internal:20122
# - no_proxy=localhost,127.0.0.1
# Linux 系统需要取消下面的注释以支持 host.docker.internal
# extra_hosts:
# - "host.docker.internal:host-gateway"
# 如果需要使用主机网络,取消下面的注释
# network_mode: host

View File

@ -1,134 +0,0 @@
#!/bin/bash
set -e
echo "==================================="
echo "Gitea Runner Container Starting..."
echo "==================================="
# 定义路径
PERSISTENT_BIN="/data/bin"
RUNNER_PATH="$PERSISTENT_BIN/act_runner"
SYSTEM_LINK="/usr/local/bin/act_runner"
# 创建必要目录
mkdir -p /data/runners
mkdir -p "$PERSISTENT_BIN"
mkdir -p /var/log/supervisor
mkdir -p /var/run
# 创建主 supervisor 配置文件
cat > /etc/supervisor/supervisord.conf <<EOF
[supervisord]
nodaemon=true
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
[unix_http_server]
file=/var/run/supervisor.sock
chmod=0700
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[include]
files=/etc/supervisor/conf.d/*.conf
EOF
# 检查持久化目录中的 act_runner
if [ -f "$RUNNER_PATH" ]; then
echo "✓ Found act_runner in persistent storage"
echo " Location: $RUNNER_PATH"
# 创建软链接到系统路径(如果不存在)
if [ ! -L "$SYSTEM_LINK" ] || [ ! -e "$SYSTEM_LINK" ]; then
ln -sf "$RUNNER_PATH" "$SYSTEM_LINK"
echo " Created system link: $SYSTEM_LINK -> $RUNNER_PATH"
fi
# 验证版本
RUNNER_VERSION=$("$SYSTEM_LINK" --version 2>/dev/null || echo "unknown")
echo " Version: $RUNNER_VERSION"
elif [ -f "$SYSTEM_LINK" ]; then
# 旧版本可能在系统路径,迁移到持久化目录
echo "⚠ Found act_runner in system path, migrating to persistent storage..."
cp "$SYSTEM_LINK" "$RUNNER_PATH"
chmod +x "$RUNNER_PATH"
ln -sf "$RUNNER_PATH" "$SYSTEM_LINK"
echo " ✓ Migrated to: $RUNNER_PATH"
else
# 没有找到 act_runner
echo "⚠ act_runner not installed yet!"
echo ""
echo "Please run the setup script first:"
echo " docker-compose exec gitea-runner /data/setup.sh"
echo ""
echo "This will download and install the Gitea Runner to persistent storage."
echo "Container is waiting..."
# 等待 act_runner 安装
while [ ! -f "$RUNNER_PATH" ] && [ ! -f "$SYSTEM_LINK" ]; do
sleep 10
done
# 再次检查并创建链接
if [ -f "$RUNNER_PATH" ]; then
ln -sf "$RUNNER_PATH" "$SYSTEM_LINK"
echo "✓ act_runner detected and linked!"
elif [ -f "$SYSTEM_LINK" ]; then
cp "$SYSTEM_LINK" "$RUNNER_PATH"
ln -sf "$RUNNER_PATH" "$SYSTEM_LINK"
echo "✓ act_runner detected and migrated!"
fi
fi
# 为每个已注册的 runner 创建 supervisor 配置
echo ""
echo "Scanning for registered runners..."
RUNNER_COUNT=0
if [ -d "/data/runners" ]; then
for runner_dir in /data/runners/*/; do
if [ -d "$runner_dir" ]; then
runner_name=$(basename "$runner_dir")
if [ -f "$runner_dir/.runner" ] && [ -f "$runner_dir/config.yaml" ]; then
echo "Found runner: $runner_name"
# 创建该 runner 的 supervisor 配置
cat > "/etc/supervisor/conf.d/runner-${runner_name}.conf" <<EOF
[program:runner-${runner_name}]
command=/usr/local/bin/act_runner daemon --config ${runner_dir}/config.yaml
directory=${runner_dir}
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/runner-${runner_name}.err.log
stdout_logfile=/var/log/supervisor/runner-${runner_name}.out.log
user=root
environment=HOME="/root"
EOF
RUNNER_COUNT=$((RUNNER_COUNT + 1))
fi
fi
done
fi
if [ $RUNNER_COUNT -eq 0 ]; then
echo "⚠ No runners registered yet!"
echo ""
echo "Please run the register script to add a runner:"
echo " docker-compose exec gitea-runner /data/register.sh"
echo ""
echo "Container will continue running and wait for runners..."
fi
echo "Total runners configured: $RUNNER_COUNT"
echo ""
echo "==================================="
echo "Starting Supervisor..."
echo "==================================="
# 启动 supervisord使用主配置文件
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf

View File

@ -1,288 +0,0 @@
#!/bin/bash
echo "=========================================="
echo " Gitea Runner Management Tool "
echo "=========================================="
echo ""
# 函数:列出所有 runners
list_runners() {
echo "Registered Runners:"
echo "-------------------------------------------"
if [ ! -d "/data/runners" ] || [ -z "$(ls -A /data/runners 2>/dev/null)" ]; then
echo "No runners registered yet."
return
fi
printf "%-20s %-15s %-30s\n" "Name" "Status" "Log File"
echo "-------------------------------------------"
for runner_dir in /data/runners/*/; do
if [ -d "$runner_dir" ]; then
runner_name=$(basename "$runner_dir")
if [ -f "$runner_dir/.runner" ]; then
# 获取状态
status=$(supervisorctl status "runner-${runner_name}" 2>/dev/null | awk '{print $2}')
[ -z "$status" ] && status="NOT_LOADED"
log_file="/var/log/supervisor/runner-${runner_name}.out.log"
printf "%-20s %-15s %-30s\n" "$runner_name" "$status" "$log_file"
fi
fi
done
echo ""
}
# 函数:查看 runner 详细信息
show_runner() {
local runner_name=$1
local runner_dir="/data/runners/${runner_name}"
if [ ! -d "$runner_dir" ]; then
echo "✗ Runner '$runner_name' not found!"
return 1
fi
echo "Runner Details: $runner_name"
echo "-------------------------------------------"
echo "Directory: $runner_dir"
if [ -f "$runner_dir/config.yaml" ]; then
echo ""
echo "Configuration:"
cat "$runner_dir/config.yaml"
fi
echo ""
echo "Status:"
supervisorctl status "runner-${runner_name}" 2>/dev/null | awk '{print $2}'
echo ""
}
# 函数:删除 runner
delete_runner() {
local runner_name=$1
local runner_dir="/data/runners/${runner_name}"
if [ ! -d "$runner_dir" ]; then
echo "✗ Runner '$runner_name' not found!"
return 1
fi
echo "⚠ Warning: This will permanently delete runner '$runner_name'"
read -p "Are you sure? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Deletion cancelled."
return 0
fi
echo "Stopping runner..."
supervisorctl stop "runner-${runner_name}" 2>/dev/null || true
echo "Removing configuration..."
rm -f "/etc/supervisor/conf.d/runner-${runner_name}.conf"
echo "Deleting runner directory..."
rm -rf "$runner_dir"
echo "Updating supervisor..."
supervisorctl reread
supervisorctl update
echo ""
echo "✓ Runner '$runner_name' deleted successfully!"
}
# 函数:查看 runner 日志
logs_runner() {
local runner_name=$1
local lines=${2:-50}
local log_file="/var/log/supervisor/runner-${runner_name}.out.log"
if [ ! -f "$log_file" ]; then
echo "✗ Log file not found for runner '$runner_name'"
return 1
fi
echo "Showing last $lines lines of '$runner_name' logs:"
echo "-------------------------------------------"
tail -n "$lines" "$log_file"
}
# 函数:实时查看日志
follow_logs() {
local runner_name=$1
local log_file="/var/log/supervisor/runner-${runner_name}.out.log"
if [ ! -f "$log_file" ]; then
echo "✗ Log file not found for runner '$runner_name'"
return 1
fi
echo "Following logs for '$runner_name' (Press Ctrl+C to exit):"
echo "-------------------------------------------"
tail -f "$log_file"
}
# 函数:启动/停止/重启 runner
control_runner() {
local action=$1
local runner_name=$2
case $action in
start|stop|restart)
echo "${action^}ing runner '$runner_name'..."
supervisorctl "$action" "runner-${runner_name}"
;;
*)
echo "✗ Invalid action: $action"
return 1
;;
esac
}
# 函数:显示所有 runner 状态
status_all() {
echo "All Runners Status:"
echo "-------------------------------------------"
supervisorctl status | grep "^runner-" || echo "No runners running."
echo ""
}
# 主菜单
show_menu() {
echo "Choose an action:"
echo " 1) List all runners"
echo " 2) Show runner details"
echo " 3) Add new runner"
echo " 4) Delete runner"
echo " 5) Start runner"
echo " 6) Stop runner"
echo " 7) Restart runner"
echo " 8) View runner logs"
echo " 9) Follow runner logs (real-time)"
echo " 10) Show all runners status"
echo " 0) Exit"
echo ""
}
# 主程序
if [ $# -eq 0 ]; then
# 交互模式
while true; do
show_menu
read -p "Enter your choice: " choice
echo ""
case $choice in
1)
list_runners
;;
2)
read -p "Enter runner name: " runner_name
show_runner "$runner_name"
;;
3)
echo "Starting registration process..."
/data/register.sh
;;
4)
read -p "Enter runner name to delete: " runner_name
delete_runner "$runner_name"
;;
5)
read -p "Enter runner name to start: " runner_name
control_runner start "$runner_name"
;;
6)
read -p "Enter runner name to stop: " runner_name
control_runner stop "$runner_name"
;;
7)
read -p "Enter runner name to restart: " runner_name
control_runner restart "$runner_name"
;;
8)
read -p "Enter runner name: " runner_name
read -p "Number of lines (default 50): " lines
logs_runner "$runner_name" "${lines:-50}"
;;
9)
read -p "Enter runner name: " runner_name
follow_logs "$runner_name"
;;
10)
status_all
;;
0)
echo "Goodbye!"
exit 0
;;
*)
echo "Invalid choice!"
;;
esac
echo ""
read -p "Press Enter to continue..."
clear
done
else
# 命令行模式
case $1 in
list|ls)
list_runners
;;
show|info)
show_runner "$2"
;;
add|register)
/data/register.sh
;;
delete|rm|remove)
delete_runner "$2"
;;
start)
control_runner start "$2"
;;
stop)
control_runner stop "$2"
;;
restart)
control_runner restart "$2"
;;
logs)
logs_runner "$2" "${3:-50}"
;;
follow)
follow_logs "$2"
;;
status)
status_all
;;
*)
echo "Usage: $0 [command] [runner_name]"
echo ""
echo "Commands:"
echo " list - List all runners"
echo " show <name> - Show runner details"
echo " add - Add new runner"
echo " delete <name> - Delete a runner"
echo " start <name> - Start a runner"
echo " stop <name> - Stop a runner"
echo " restart <name> - Restart a runner"
echo " logs <name> [lines] - View runner logs"
echo " follow <name> - Follow runner logs (real-time)"
echo " status - Show all runners status"
echo ""
echo "Or run without arguments for interactive mode."
exit 1
;;
esac
fi

View File

@ -1,141 +0,0 @@
#!/bin/bash
echo "=========================================="
echo " Gitea Runner Installation Script "
echo "=========================================="
echo ""
# 持久化安装路径
INSTALL_PATH="/data/bin/act_runner"
SYSTEM_PATH="/usr/local/bin/act_runner"
# 创建目录
mkdir -p /data/bin
# 检查是否已安装
if [ -f "$INSTALL_PATH" ]; then
CURRENT_VERSION=$($INSTALL_PATH --version 2>/dev/null | grep -oP 'version \K[0-9.]+' || echo "unknown")
echo "⚠ act_runner already installed (version: $CURRENT_VERSION)"
echo " Location: $INSTALL_PATH"
echo ""
read -p "Do you want to reinstall/upgrade? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Installation cancelled."
exit 0
fi
rm -f "$INSTALL_PATH" "$SYSTEM_PATH"
fi
# 获取要安装的版本
echo "Available versions: https://dl.gitea.com/act_runner/"
echo ""
read -p "Enter version to install (default: 0.2.13): " RUNNER_VERSION
RUNNER_VERSION=${RUNNER_VERSION:-0.2.13}
ARCH=$(uname -m)
case "$ARCH" in
x86_64)
RUNNER_ARCH="amd64"
;;
aarch64|arm64)
RUNNER_ARCH="arm64"
;;
armv7l)
RUNNER_ARCH="arm-7"
;;
*)
echo "⚠ Unknown architecture: $ARCH"
RUNNER_ARCH="arm64"
;;
esac
# 确认架构
echo ""
echo "Detected architecture: $ARCH -> $RUNNER_ARCH"
read -p "Is this correct? (Y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Nn]$ ]]; then
echo ""
echo "Available architectures:"
echo " 1. amd64 (x86_64)"
echo " 2. arm64 (aarch64)"
echo " 3. arm-7 (armv7l)"
read -p "Select architecture (1-3): " ARCH_CHOICE
case "$ARCH_CHOICE" in
1) RUNNER_ARCH="amd64" ;;
2) RUNNER_ARCH="arm64" ;;
3) RUNNER_ARCH="arm-7" ;;
*)
echo "Invalid choice. Exiting."
exit 1
;;
esac
fi
DOWNLOAD_URL="https://dl.gitea.com/act_runner/${RUNNER_VERSION}/act_runner-${RUNNER_VERSION}-linux-${RUNNER_ARCH}"
echo ""
echo "Download Configuration:"
echo " Version: $RUNNER_VERSION"
echo " Architecture: $RUNNER_ARCH"
echo " URL: $DOWNLOAD_URL"
echo " Install Location: $INSTALL_PATH (persistent)"
echo ""
read -p "Proceed with download? (Y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Nn]$ ]]; then
echo "Installation cancelled."
exit 0
fi
echo ""
echo "Downloading act_runner..."
echo ""
# 下载到持久化目录
if curl -L "$DOWNLOAD_URL" -o "$INSTALL_PATH"; then
chmod +x "$INSTALL_PATH"
# 同时创建软链接到系统路径
ln -sf "$INSTALL_PATH" "$SYSTEM_PATH"
# 验证安装
if $INSTALL_PATH --version; then
echo ""
echo "=========================================="
echo "✓ act_runner installed successfully!"
echo "=========================================="
echo ""
echo "Version: $($INSTALL_PATH --version)"
echo "Location: $INSTALL_PATH (persistent storage)"
echo ""
echo "Next steps:"
echo "1. Register the runner:"
echo " docker-compose exec gitea-runner /data/register.sh"
echo ""
echo "2. Restart the container:"
echo " docker-compose restart"
echo ""
echo "Note: act_runner is saved in persistent storage"
echo " and will survive container restarts."
echo ""
else
echo ""
echo "✗ Installation verification failed!"
rm -f "$INSTALL_PATH" "$SYSTEM_PATH"
exit 1
fi
else
echo ""
echo "✗ Download failed!"
echo "Please check:"
echo " - Internet connection"
echo " - Version number is correct: $RUNNER_VERSION"
echo " - Architecture is correct: $RUNNER_ARCH"
echo " - URL is accessible: $DOWNLOAD_URL"
echo ""
echo "You can check available versions at:"
echo " https://dl.gitea.com/act_runner/"
exit 1
fi