🎉 首次发布!!
Hello from ImmortalWrt / say-hello (push) Successful in 0s
Details
Hello from ImmortalWrt / say-hello (push) Successful in 0s
Details
📝 教程阅读`README`
This commit is contained in:
parent
082194acba
commit
4b34b9e0f3
|
|
@ -0,0 +1,934 @@
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
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\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"
|
||||||
|
|
||||||
|
# 先添加旧的提交(如果有)
|
||||||
|
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"
|
||||||
|
|
||||||
|
# 使用数组来去重贡献者
|
||||||
|
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\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"
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,357 @@
|
||||||
|
# CHANGELOG
|
||||||
|
|
||||||
|
## :bookmark: 0.0.1
|
||||||
|
|
||||||
|
### :pencil: What's Changed
|
||||||
|
- :cycle: refactor ([6739e6a](https://git.mytsl.cn/csh/actions/commit/6739e6a)) by @csh
|
||||||
|
- :memo: update md ([196df7d](https://git.mytsl.cn/csh/actions/commit/196df7d)) by @csh
|
||||||
|
- :memo: update readme ([a221ddd](https://git.mytsl.cn/csh/actions/commit/a221ddd)) 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: 2025.10.31-23
|
||||||
|
|
||||||
|
### :pencil: What's Changed
|
||||||
|
- :cycle: hello ([f76531c](https://git.mytsl.cn/csh/actions/commit/f76531c)) 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: 2025.10.31-22-beta
|
||||||
|
|
||||||
|
### :pencil: What's Changed
|
||||||
|
- sos
|
||||||
|
sos
|
||||||
|
sos ([ee64fe8](https://git.mytsl.cn/csh/actions/commit/ee64fe8)) 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: 2025.10.31-21-beta
|
||||||
|
|
||||||
|
### :pencil: What's Changed
|
||||||
|
- :bug: fixbug
|
||||||
|
:bug: fixbug ([3f21e1a](https://git.mytsl.cn/csh/actions/commit/3f21e1a)) 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: 2025.10.31-20-beta
|
||||||
|
|
||||||
|
### :pencil: What's Changed
|
||||||
|
- :help: helpme
|
||||||
|
:help: helpme ([69f1112](https://git.mytsl.cn/csh/actions/commit/69f1112)) 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: 2025.10.31-19-beta
|
||||||
|
|
||||||
|
### :pencil: What's Changed
|
||||||
|
- :cycle: 重大更新
|
||||||
|
:warning: 哈哈哈 ([d000080](https://git.mytsl.cn/csh/actions/commit/d000080)) 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: 2025.10.31-17
|
||||||
|
|
||||||
|
### :pencil: What's Changed
|
||||||
|
- :rocket: gogogo
|
||||||
|
:waring: aaaa ([f27a32c](https://git.mytsl.cn/csh/actions/commit/f27a32c)) 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: 2025.10.31-16
|
||||||
|
|
||||||
|
### :pencil: å˜æ›´å†…容
|
||||||
|
- [:rocket:] yes
|
||||||
|
[:rocket:] amdyes! ([e1f9e4a](https://git.mytsl.cn/csh/actions/commit/e1f9e4a)) by @csh
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-15
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- :bug: okok
|
||||||
|
:bug: okok ([2011842](https://git.mytsl.cn/csh/actions/commit/2011842)) by @csh
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-14
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- sos
|
||||||
|
sos ([6cd3c75](https://git.mytsl.cn/csh/actions/commit/6cd3c75) by @csh
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-13
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- :bug: continuetest1
|
||||||
|
:bug: continuetest2 ([9a66205](https://git.mytsl.cn/csh/actions/commit/9a66205))
|
||||||
|
- csh d0be624
|
||||||
|
:memo: Auto update CHANGELOG for 2025.10.31-12 [skip ci] ([](https://git.mytsl.cn/csh/actions/commit/)) by @|github-actions[bot]
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-12
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- :bug: bug1
|
||||||
|
:bug: bug2 ([e982410](https://git.mytsl.cn/csh/actions/commit/e982410))
|
||||||
|
- csh ([](https://git.mytsl.cn/csh/actions/commit/))
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-11
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- :bug: bug1
|
||||||
|
:bug: bug1 ([664f1a6](https://git.mytsl.cn/csh/actions/commit/664f1a6))
|
||||||
|
- csh ([](https://git.mytsl.cn/csh/actions/commit/))
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-10
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- :bug: bu1
|
||||||
|
:bug: bu2 ([bad1e99](https://git.mytsl.cn/csh/actions/commit/bad1e99))
|
||||||
|
- csh ([](https://git.mytsl.cn/csh/actions/commit/))
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-9
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- :bug: bu1
|
||||||
|
:bug: bu2 ([d37cc3c](https://git.mytsl.cn/csh/actions/commit/d37cc3c))
|
||||||
|
- csh ([](https://git.mytsl.cn/csh/actions/commit/))
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-8
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- :bug: 1 :bug: 2 ([f163913](https://git.mytsl.cn/csh/actions/commit/f163913))
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-7
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- :bug: t1 :bug: t2 ([ba7ede6](https://git.mytsl.cn/csh/actions/commit/ba7ede6))
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-6
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- :bug: 多行bug :bug: 多行测试 ([6f4f726](https://git.mytsl.cn/csh/actions/commit/6f4f726))
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-5
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- :sparkles: 支持多行commit :rocket: 加速测试 ([3b0c44d](https://git.mytsl.cn/csh/actions/commit/3b0c44d))
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-4
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- - :sparkles: `auto-changelog.yaml`过滤一些没用的提交信息 - :bug: 修复CHANGELOG标题不置顶 ([cc3a9ab](https://git.mytsl.cn/csh/actions/commit/cc3a9ab))
|
||||||
|
- 更新 CHANGELOG.md ([a71fa13](https://git.mytsl.cn/csh/actions/commit/a71fa13))
|
||||||
|
- 更新 CHANGELOG.md ([181ce32](https://git.mytsl.cn/csh/actions/commit/181ce32))
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-3
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- Merge branch 'main' of https://git.mytsl.cn/csh/actions ([c31da44](https://git.mytsl.cn/csh/actions/commit/c31da44))
|
||||||
|
- :bug: 样式错误 ([65a5b5a](https://git.mytsl.cn/csh/actions/commit/65a5b5a))
|
||||||
|
- :memo: 自动更新 CHANGELOG for 2025.10.31-2 [skip ci] ([3bdca3f](https://git.mytsl.cn/csh/actions/commit/3bdca3f))
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-2
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- :bug: emoji样式和贡献者图标 ([57eeddf](https://git.mytsl.cn/csh/actions/commit/57eeddf))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-1 [skip ci] ([42c4f2d](https://git.mytsl.cn/csh/actions/commit/42c4f2d))
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<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: 2025.10.31-1
|
||||||
|
|
||||||
|
### :pencil: 变更内容
|
||||||
|
- 删除 CHANGELOG.md ([5306159](https://git.mytsl.cn/csh/actions/commit/5306159))
|
||||||
|
- :bug:: fix cycle and bot ([84eb006](https://git.mytsl.cn/csh/actions/commit/84eb006))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([3582de4](https://git.mytsl.cn/csh/actions/commit/3582de4))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([94c8ab3](https://git.mytsl.cn/csh/actions/commit/94c8ab3))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([8f48c6d](https://git.mytsl.cn/csh/actions/commit/8f48c6d))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([cb31aad](https://git.mytsl.cn/csh/actions/commit/cb31aad))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([dea51a0](https://git.mytsl.cn/csh/actions/commit/dea51a0))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([4269113](https://git.mytsl.cn/csh/actions/commit/4269113))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([3dbdefb](https://git.mytsl.cn/csh/actions/commit/3dbdefb))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([59756ba](https://git.mytsl.cn/csh/actions/commit/59756ba))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([ecc43b4](https://git.mytsl.cn/csh/actions/commit/ecc43b4))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([4afb56b](https://git.mytsl.cn/csh/actions/commit/4afb56b))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([2ce5bef](https://git.mytsl.cn/csh/actions/commit/2ce5bef))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([89b2f77](https://git.mytsl.cn/csh/actions/commit/89b2f77))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([7b2f45b](https://git.mytsl.cn/csh/actions/commit/7b2f45b))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([9f083b8](https://git.mytsl.cn/csh/actions/commit/9f083b8))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([62cb5a2](https://git.mytsl.cn/csh/actions/commit/62cb5a2))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([7459b75](https://git.mytsl.cn/csh/actions/commit/7459b75))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([cd421b6](https://git.mytsl.cn/csh/actions/commit/cd421b6))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([5353b5c](https://git.mytsl.cn/csh/actions/commit/5353b5c))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([14695a7](https://git.mytsl.cn/csh/actions/commit/14695a7))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([52dc518](https://git.mytsl.cn/csh/actions/commit/52dc518))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([a0bef08](https://git.mytsl.cn/csh/actions/commit/a0bef08))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([7a533cd](https://git.mytsl.cn/csh/actions/commit/7a533cd))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([865970d](https://git.mytsl.cn/csh/actions/commit/865970d))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([6ad15b5](https://git.mytsl.cn/csh/actions/commit/6ad15b5))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([21a5243](https://git.mytsl.cn/csh/actions/commit/21a5243))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([f156b69](https://git.mytsl.cn/csh/actions/commit/f156b69))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([ef83267](https://git.mytsl.cn/csh/actions/commit/ef83267))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([9d99e65](https://git.mytsl.cn/csh/actions/commit/9d99e65))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([183011e](https://git.mytsl.cn/csh/actions/commit/183011e))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([6d15881](https://git.mytsl.cn/csh/actions/commit/6d15881))
|
||||||
|
- docs: 自动更新 CHANGELOG for 2025.10.31-5 ([48ac173](https://git.mytsl.cn/csh/actions/commit/48ac173))
|
||||||
|
|
||||||
|
### :busts_in_silhouette: 贡献者
|
||||||
|
<a href="https://git.mytsl.cn/csh">
|
||||||
|
<img src="https://git.mytsl.cn/avatars/csh" alt="csh" width="35" height="35" style="border-radius: 50%;" onerror="this.src='https://git.mytsl.cn/assets/img/avatar_default.png'" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
# 模板说明
|
||||||
|
|
||||||
|
## :label: 图例说明
|
||||||
|
|
||||||
|
| 图标 | 代码 | 说明 | 图标 | 代码 | 说明 |
|
||||||
|
| :-----------: | :-------------- | :------- | :-------------------: | :---------------------- | :------------ |
|
||||||
|
| :tada: | `:tada:` | 首次发布 | :sparkles: | `:sparkles:` | 新功能 |
|
||||||
|
| :bug: | `:bug:` | Bug 修复 | :rocket: | `:rocket:` | 性能优化 |
|
||||||
|
| :art: | `:art:` | 代码样式 | :recycle: | `:recycle:` | 代码重构 |
|
||||||
|
| :package: | `:package:` | 依赖更新 | :lock: | `:lock:` | 安全修复 |
|
||||||
|
| :warning: | `:warning:` | 废弃警告 | :wrench: | `:wrench:` | 配置变更 |
|
||||||
|
| :wastebasket: | `:wastebasket:` | 删除功能 | :busts_in_silhouette: | `:busts_in_silhouette:` | 贡献者 |
|
||||||
|
| :memo: | `:memo:` | 文档更新 | :bookmark: | `:bookmark:` | 发行/版本标签 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## :pushpin: 版本号规范
|
||||||
|
|
||||||
|
### SemVer - 语义版本号
|
||||||
|
|
||||||
|
#### 格式
|
||||||
|
|
||||||
|
```txt
|
||||||
|
MAJOR.MINOR.PATCH
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 字段说明
|
||||||
|
|
||||||
|
| 字段 | 说明 | 何时递增 |
|
||||||
|
| --------- | -------- | ------------------ |
|
||||||
|
| **MAJOR** | 主版本号 | 不兼容的 API 修改 |
|
||||||
|
| **MINOR** | 次版本号 | 向后兼容的功能新增 |
|
||||||
|
| **PATCH** | 修订号 | 向后兼容的问题修正 |
|
||||||
|
|
||||||
|
#### 更新规则
|
||||||
|
|
||||||
|
- **MAJOR(主版本)**:破坏性变更,不向后兼容。递增后 MINOR 和 PATCH 重置为 0
|
||||||
|
- **MINOR(次版本)**:新增功能但保持兼容。递增后 PATCH 重置为 0
|
||||||
|
- **PATCH(修订)**:仅修复 bug,不新增功能
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
|
||||||
|
```txt
|
||||||
|
1.0.0 # 首个稳定版
|
||||||
|
1.1.0 # 新增功能
|
||||||
|
1.1.1 # Bug 修复
|
||||||
|
2.0.0 # 破坏性变更
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pre-release 版本
|
||||||
|
|
||||||
|
```txt
|
||||||
|
1.0.0-alpha.1
|
||||||
|
1.0.0-beta
|
||||||
|
1.0.0-rc.1
|
||||||
|
1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 初始开发阶段
|
||||||
|
|
||||||
|
```txt
|
||||||
|
0.1.0 # 初始版本
|
||||||
|
0.2.0 # API 未稳定
|
||||||
|
1.0.0 # 首个稳定版 ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**:`0.x.x` 版本可以随时破坏兼容性
|
||||||
|
|
||||||
|
---
|
||||||
File diff suppressed because it is too large
Load Diff
63
README.md
63
README.md
|
|
@ -1,3 +1,62 @@
|
||||||
# runner-template
|
# 🎯 Gitea Runner Template
|
||||||
|
|
||||||
runner部署,actions工作流模板
|
[](https://www.docker.com/)
|
||||||
|
|
||||||
|
> 🚀 Gitea Runner Docker 部署模板 + Actions Workflow 示例 + 文档规范
|
||||||
|
|
||||||
|
## 📖 项目简介
|
||||||
|
|
||||||
|
这是一个完整的 Gitea Runner 模板项目,提供:
|
||||||
|
|
||||||
|
- 🐳 **Docker 部署方案**:开箱即用的 Runner 容器化部署
|
||||||
|
- 📝 **Workflow 示例**:自动化 CHANGELOG 和 Release 工作流
|
||||||
|
- 📚 **文档规范**:统一的文档格式和版本管理规范
|
||||||
|
- 🔧 **配置指南**:详细的配置说明和最佳实践
|
||||||
|
|
||||||
|
## 📂 文档导航
|
||||||
|
|
||||||
|
### 🚀 Runner
|
||||||
|
|
||||||
|
#### [DEPLOYMENT.md](./DEPLOYMENT.md)
|
||||||
|
|
||||||
|
**Gitea Runner Docker 部署完整教程**
|
||||||
|
|
||||||
|
包含内容:
|
||||||
|
|
||||||
|
- 📦 标准版 vs Buildx 多架构版选择
|
||||||
|
- 📝 Dockerfile 和 docker-compose.yml 配置
|
||||||
|
- 🔧 安装、注册、管理脚本详解
|
||||||
|
- ⚙️ 完整的部署流程
|
||||||
|
- ❓ 常见问题和故障排除
|
||||||
|
|
||||||
|
👉 **完整的 Runner 部署教程,从零开始搭建**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📋 [Workflow](./WORKFLOW.md)
|
||||||
|
|
||||||
|
**Gitea Actions 自动化工作流示例**
|
||||||
|
|
||||||
|
包含内容:
|
||||||
|
|
||||||
|
- 🔄 `changelog_and_release.yml` - 自动更新 CHANGELOG和自动创建Release
|
||||||
|
- 💡 工作流配置说明
|
||||||
|
- 🔧 如何使用和定制
|
||||||
|
|
||||||
|
👉 **两个实用的 Actions 工作流,可直接复制使用**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📝 [Conventions](./CONVENTIONS.md)
|
||||||
|
|
||||||
|
**文档编写规范和版本命名规则**
|
||||||
|
|
||||||
|
包含内容:
|
||||||
|
|
||||||
|
- 📊 Emoji 图标使用规范
|
||||||
|
- 🔢 版本号命名规则(SemVer)
|
||||||
|
- 📝 [CHANGELOG](./CHANGELOG.md) 模板
|
||||||
|
|
||||||
|
👉 **维护项目文档的统一规范,贡献者必读**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,880 @@
|
||||||
|
# 📦 Tag Release Automation
|
||||||
|
|
||||||
|
自动生成 CHANGELOG 并创建 Release 的一体化工作流,**支持语义版本号(SemVer)和内容叠加**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📂 文件位置
|
||||||
|
|
||||||
|
```txt
|
||||||
|
.gitea/workflows/
|
||||||
|
└── changelog-and-release.yaml # CHANGELOG 生成 + Release 创建
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
### 1️⃣ 配置 Gitea Token
|
||||||
|
|
||||||
|
#### 生成 Token
|
||||||
|
|
||||||
|
1. 登录 Gitea → 右上角头像 → **设置 (Settings)** → **应用 (Applications)**
|
||||||
|
2. 点击 **生成新令牌 (Generate New Token)**
|
||||||
|
3. 配置权限:
|
||||||
|
- ✅ **repo** - 完整的仓库访问权限(必需)
|
||||||
|
4. 复制生成的 Token(⚠️ 只显示一次)
|
||||||
|
|
||||||
|
#### 添加 Secret
|
||||||
|
|
||||||
|
**推荐方式:仓库级别配置**
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
🎉 完成!工作流会自动:
|
||||||
|
|
||||||
|
1. ✅ 加载自定义配置
|
||||||
|
2. ✅ 生成/叠加 CHANGELOG 条目
|
||||||
|
3. ✅ 合并并去重贡献者
|
||||||
|
4. ✅ 提交并推送到 main 分支
|
||||||
|
5. ✅ 创建 Release(自动识别 Pre-release)
|
||||||
|
6. ✅ 上传 CHANGELOG.md 及额外文件
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 工作原理
|
||||||
|
|
||||||
|
### 触发条件
|
||||||
|
|
||||||
|
**推送数字开头的 tag**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ✅ 会触发
|
||||||
|
git push origin 1.0.0
|
||||||
|
git push origin 1.1.0-beta1
|
||||||
|
git push origin 2.0.0-rc1
|
||||||
|
|
||||||
|
# ❌ 不会触发(不是数字开头)
|
||||||
|
git push origin v1.0.0
|
||||||
|
git push origin release-1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 执行流程
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Tag Push (1.0.0-beta1)
|
||||||
|
↓
|
||||||
|
⚙️ 加载配置参数
|
||||||
|
↓
|
||||||
|
📂 检出代码
|
||||||
|
↓
|
||||||
|
🤖 检查是否为 bot 提交
|
||||||
|
↓
|
||||||
|
📋 检查版本是否已存在(1.0.0)
|
||||||
|
↓ 已存在 → 叠加模式
|
||||||
|
↓ 不存在 → 创建新条目
|
||||||
|
📝 生成 CHANGELOG 条目
|
||||||
|
├─ 提取旧的提交记录(如果存在)
|
||||||
|
├─ 提取旧的贡献者(如果存在)
|
||||||
|
├─ 追加新的提交记录
|
||||||
|
├─ 合并并去重贡献者
|
||||||
|
└─ 生成完整的条目
|
||||||
|
↓
|
||||||
|
📤 提交并推送
|
||||||
|
↓
|
||||||
|
🗑️ 删除旧 Release
|
||||||
|
↓
|
||||||
|
🚀 创建新 Release
|
||||||
|
├─ 使用配置的标题格式
|
||||||
|
├─ 自动识别 Pre-release
|
||||||
|
└─ 上传配置的附件列表
|
||||||
|
↓
|
||||||
|
✅ 完成
|
||||||
|
```
|
||||||
|
|
||||||
|
### 版本号处理逻辑
|
||||||
|
|
||||||
|
#### 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](链接))
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 配置示例库
|
||||||
|
|
||||||
|
### 示例 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 规范、更新规则和示例
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❓ 常见问题
|
||||||
|
|
||||||
|
### Q1: 为什么推荐使用 strip 模式?
|
||||||
|
|
||||||
|
**原因**:
|
||||||
|
|
||||||
|
1. ✅ **支持内容叠加** - 多个开发版本的提交会累积在同一个 CHANGELOG 区域
|
||||||
|
2. ✅ **完整的开发历史** - 正式发布时,CHANGELOG 已包含所有开发阶段的提交
|
||||||
|
3. ✅ **避免重复** - 不需要为每个 beta/rc 版本创建独立的 CHANGELOG 条目
|
||||||
|
4. ✅ **便于阅读** - 所有相关的变更集中在一起
|
||||||
|
|
||||||
|
**对比**:
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 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 没有触发?
|
||||||
|
|
||||||
|
**检查清单**:
|
||||||
|
|
||||||
|
- [ ] Workflow 文件在 `.gitea/workflows/` 目录下
|
||||||
|
- [ ] 文件名为 `changelog-and-release.yaml`(或 `.yml`)
|
||||||
|
- [ ] Tag 格式正确(数字开头,如 `1.0.0` 或 `1.0.0-beta1`)
|
||||||
|
- [ ] Runner 已部署并正常运行
|
||||||
|
- [ ] 仓库已启用 Actions
|
||||||
|
- [ ] GITEA_TOKEN 已正确配置
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Q7: 提示 "GITEA_TOKEN not found"?
|
||||||
|
|
||||||
|
**原因**:Token 未正确配置
|
||||||
|
|
||||||
|
**解决方法**:
|
||||||
|
|
||||||
|
1. 确认已添加名为 `GITEA_TOKEN` 的 Secret
|
||||||
|
2. 确认 Token 具有 `repo` 权限
|
||||||
|
3. 尝试重新生成并配置 Token
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Q8: CHANGELOG 没有更新?
|
||||||
|
|
||||||
|
**可能原因**:
|
||||||
|
|
||||||
|
1. 没有新的有效提交
|
||||||
|
2. 所有提交都匹配了 `IGNORE_PATTERNS` 中的规则
|
||||||
|
3. 版本已存在且是 bot 提交
|
||||||
|
|
||||||
|
**检查方法**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看两个 tag 之间的提交
|
||||||
|
git log $(git describe --tags --abbrev=0 @^)..@ --oneline --no-merges
|
||||||
|
|
||||||
|
# 检查 workflow 日志中的 "Found X valid commits"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Q9: 如何跳过自动化?
|
||||||
|
|
||||||
|
在 commit 消息中添加 `[skip ci]`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "docs: update readme [skip ci]"
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**:Tag 推送无法跳过,因为 workflow 在 tag 推送时触发
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Q10: 什么时候使用 full 模式?
|
||||||
|
|
||||||
|
**使用场景**:
|
||||||
|
|
||||||
|
- 你希望每个 tag 都有独立的 CHANGELOG 条目
|
||||||
|
- 不需要累积开发版本的提交
|
||||||
|
- 每个版本的变更需要完全独立
|
||||||
|
|
||||||
|
**大多数情况下,推荐使用 strip 模式。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 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 变为旧版本)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 调试技巧
|
||||||
|
|
||||||
|
### 查看配置加载情况
|
||||||
|
|
||||||
|
每次执行都会在日志开头显示配置摘要:
|
||||||
|
|
||||||
|
```
|
||||||
|
======================================
|
||||||
|
⚙️ 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
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
|
# 设置环境变量避免交互式安装
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# 更新系统并安装必要软件
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
python3 \
|
||||||
|
python3-yaml \
|
||||||
|
supervisor \
|
||||||
|
ca-certificates \
|
||||||
|
gnupg \
|
||||||
|
lsb-release \
|
||||||
|
qemu-user-static \
|
||||||
|
binfmt-support \
|
||||||
|
&& 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 docker --version && \
|
||||||
|
qemu-aarch64-static --version && \
|
||||||
|
qemu-x86_64-static --version
|
||||||
|
|
||||||
|
# 创建必要目录
|
||||||
|
RUN mkdir -p /data /etc/supervisor/conf.d /var/log/supervisor
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /data
|
||||||
|
|
||||||
|
# 使用自定义入口点
|
||||||
|
ENTRYPOINT ["/data/entrypoint.sh"]
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo " 文件格式快速修复工具"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 颜色定义
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# 要检查的文件列表
|
||||||
|
SCRIPT_FILES=(
|
||||||
|
"entrypoint.sh"
|
||||||
|
"setup.sh"
|
||||||
|
"register.sh"
|
||||||
|
"manage.sh"
|
||||||
|
)
|
||||||
|
|
||||||
|
FIXED_COUNT=0
|
||||||
|
TOTAL_FILES=0
|
||||||
|
|
||||||
|
echo "🔍 检查并修复以下文件:"
|
||||||
|
printf '%s\n' "${SCRIPT_FILES[@]}" | sed 's/^/ - /'
|
||||||
|
echo ""
|
||||||
|
echo "----------------------------------------"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
for file in "${SCRIPT_FILES[@]}"; do
|
||||||
|
TOTAL_FILES=$((TOTAL_FILES + 1))
|
||||||
|
|
||||||
|
# 检查文件是否存在
|
||||||
|
if [ ! -f "$file" ]; then
|
||||||
|
echo -e "${RED}✗ $file - 文件不存在,跳过${NC}"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
NEEDS_FIX=false
|
||||||
|
|
||||||
|
# 检查换行符
|
||||||
|
HAS_CRLF=false
|
||||||
|
if file "$file" | grep -qi "CRLF\|with CR"; then
|
||||||
|
HAS_CRLF=true
|
||||||
|
NEEDS_FIX=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查权限
|
||||||
|
NEEDS_CHMOD=false
|
||||||
|
if [ ! -x "$file" ]; then
|
||||||
|
NEEDS_CHMOD=true
|
||||||
|
NEEDS_FIX=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 显示状态
|
||||||
|
echo -n "📄 $file ... "
|
||||||
|
|
||||||
|
if [ "$NEEDS_FIX" = false ]; then
|
||||||
|
echo -e "${GREEN}OK${NC}"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 修复换行符
|
||||||
|
if [ "$HAS_CRLF" = true ]; then
|
||||||
|
sed -i 's/\r$//' "$file" 2>/dev/null || sed -i '' 's/\r$//' "$file" 2>/dev/null
|
||||||
|
echo -n -e "${YELLOW}[换行符已修复]${NC} "
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 修复权限
|
||||||
|
if [ "$NEEDS_CHMOD" = true ]; then
|
||||||
|
chmod +x "$file"
|
||||||
|
echo -n -e "${YELLOW}[权限已修复]${NC} "
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓${NC}"
|
||||||
|
FIXED_COUNT=$((FIXED_COUNT + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo "修复完成"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "检查文件: $TOTAL_FILES"
|
||||||
|
echo "修复文件: $FIXED_COUNT"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ $FIXED_COUNT -gt 0 ]; then
|
||||||
|
echo -e "${GREEN}✓ 所有问题已自动修复!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "建议执行以下命令重启容器:"
|
||||||
|
echo " docker-compose down"
|
||||||
|
echo " docker-compose build --no-cache"
|
||||||
|
echo " docker-compose up -d"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 询问是否立即重启
|
||||||
|
read -p "是否立即重启容器? (y/N): " -n 1 -r
|
||||||
|
echo ""
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "正在重启容器..."
|
||||||
|
docker-compose down
|
||||||
|
docker-compose build --no-cache
|
||||||
|
docker-compose up -d
|
||||||
|
sleep 3
|
||||||
|
echo ""
|
||||||
|
echo "容器状态:"
|
||||||
|
docker-compose ps
|
||||||
|
echo ""
|
||||||
|
echo "查看日志:"
|
||||||
|
docker logs gitea-runner --tail=30
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}✓ 所有文件格式正确,无需修复!${NC}"
|
||||||
|
fi
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
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"
|
||||||
|
|
@ -0,0 +1,234 @@
|
||||||
|
#!/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 /data/buildx
|
||||||
|
mkdir -p /var/log/supervisor
|
||||||
|
mkdir -p /var/run
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 初始化 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
|
||||||
|
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
|
||||||
|
docker buildx create \
|
||||||
|
--name gitea-multiarch \
|
||||||
|
--driver docker-container \
|
||||||
|
--bootstrap \
|
||||||
|
--use 2>/dev/null
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 检查 act_runner 安装
|
||||||
|
# ============================================
|
||||||
|
# 创建主 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
|
||||||
|
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 "Container is waiting..."
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 配置已注册的 Runners
|
||||||
|
# ============================================
|
||||||
|
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"
|
||||||
|
|
||||||
|
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",PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
|
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 ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Total runners configured: $RUNNER_COUNT"
|
||||||
|
echo ""
|
||||||
|
echo "==================================="
|
||||||
|
echo "Starting Supervisor..."
|
||||||
|
echo "==================================="
|
||||||
|
|
||||||
|
# 启动 supervisord
|
||||||
|
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||||||
|
|
@ -0,0 +1,288 @@
|
||||||
|
#!/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
|
||||||
|
|
@ -0,0 +1,321 @@
|
||||||
|
#!/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
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
#!/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
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
|
# 设置环境变量避免交互式安装
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# RUN apt-get update && \
|
||||||
|
# apt-get install -y ca-certificates && \
|
||||||
|
# update-ca-certificates && \
|
||||||
|
# rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.list && \
|
||||||
|
# sed -i 's@//.*security.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.list
|
||||||
|
|
||||||
|
# 更新系统并安装必要软件
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
jq \
|
||||||
|
python3 \
|
||||||
|
python3-yaml \
|
||||||
|
supervisor \
|
||||||
|
ca-certificates \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 创建必要目录
|
||||||
|
RUN mkdir -p /data /etc/supervisor/conf.d /var/log/supervisor
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /data
|
||||||
|
|
||||||
|
# 使用自定义入口点
|
||||||
|
ENTRYPOINT ["/data/entrypoint.sh"]
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
#!/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
|
||||||
|
|
@ -0,0 +1,288 @@
|
||||||
|
#!/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
|
||||||
|
|
@ -0,0 +1,321 @@
|
||||||
|
#!/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
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
#!/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
|
||||||
Loading…
Reference in New Issue