From ec5785cc6af3267285623090e19b5c215d89d37f Mon Sep 17 00:00:00 2001 From: csh Date: Sun, 2 Nov 2025 23:13:48 +0800 Subject: [PATCH] =?UTF-8?q?:art:=20=E4=BB=A3=E7=A0=81=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :fix: 修复识别`CHANGELOG`标题问题 --- .gitea/workflows/changelog_and_release.yml | 120 +++++++++++++-------- .gitea/workflows/update_stats_badge.yaml | 84 +++++++-------- 2 files changed, 116 insertions(+), 88 deletions(-) diff --git a/.gitea/workflows/changelog_and_release.yml b/.gitea/workflows/changelog_and_release.yml index ee3c3e1..50f930b 100644 --- a/.gitea/workflows/changelog_and_release.yml +++ b/.gitea/workflows/changelog_and_release.yml @@ -80,11 +80,11 @@ env: # 需要忽略的提交模式(支持正则表达式) # 这些提交不会被添加到 CHANGELOG 中 # 使用 | 分隔多个模式 - # + # # ⚠️ 重要说明: # - 使用 ^ 表示行首匹配,$ 表示行尾匹配 # - [skip ci] 和 [ci skip] 仅匹配行尾,避免误过滤其他提交 - # - 如果要完全匹配某个字符串,使用 ^...$ + # - 如果要完全匹配某个字符串,使用 ^...$ IGNORE_PATTERNS: >- ^Merge | ^:memo: Auto update CHANGELOG| @@ -226,21 +226,21 @@ jobs: # 检查仓库状态 if [ -d "$REPO_DIR" ]; then echo "📂 目录已存在,检查 Git 仓库状态..." - + if [ -d "$REPO_DIR/.git" ]; then # 目录存在且是有效的 Git 仓库 echo "✓ 发现有效的 Git 仓库,执行增量更新..." cd "$REPO_DIR" - + # 清理工作区 git clean -fdx git reset --hard - + # 获取最新代码和标签,同时清理远程已删除的 tag echo "📥 拉取最新代码和标签..." git fetch --all --tags --force --prune --prune-tags echo "✓ 已同步远程状态(包括已删除的 tag)" - + echo "✓ 仓库已更新" else # 目录存在但不是 Git 仓库(可能之前运行失败) @@ -248,20 +248,20 @@ jobs: echo "🧹 清理损坏的目录..." rm -rf "$REPO_DIR" echo "✓ 已清理" - + # 重新克隆 echo "📥 克隆仓库..." mkdir -p "${{ env.WORKSPACE_DIR }}" - + git clone \ https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git \ "$REPO_DIR" - + if [ $? -ne 0 ]; then echo "❌ 克隆失败" exit 1 fi - + cd "$REPO_DIR" echo "✓ 仓库已克隆" fi @@ -269,11 +269,11 @@ jobs: # 目录不存在,首次克隆 echo "📥 克隆仓库(首次)..." mkdir -p "${{ env.WORKSPACE_DIR }}" - + git clone \ https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git \ "$REPO_DIR" - + if [ $? -eq 0 ]; then cd "$REPO_DIR" echo "✓ 仓库已克隆" @@ -362,7 +362,7 @@ jobs: else CHANGELOG_VERSION="${{ env.CHANGELOG_VERSION }}" echo "📝 检查版本: $CHANGELOG_VERSION" - + if grep -q "## :bookmark: $CHANGELOG_VERSION" CHANGELOG.md; then echo "⚠️ 版本 $CHANGELOG_VERSION 已存在于 CHANGELOG.md" echo "version_exists=true" >> $GITHUB_OUTPUT @@ -395,23 +395,23 @@ jobs: # 查找前一个 tag # 排除所有属于当前 CHANGELOG 版本的 tag(例如 0.0.2-rc1, 0.0.2-rc2 都属于 0.0.2) echo "🔍 查找前一个版本的 tag..." - + # 获取所有 tag ALL_TAGS=$(git tag --sort=-version:refname) - + # 遍历查找第一个不属于当前 CHANGELOG 版本的 tag PREVIOUS_TAG="" while IFS= read -r tag; do # 跳过空行 [ -z "$tag" ] && continue - + # 对每个 tag 执行 strip 操作(去除 pre-release 后缀) if [[ "${{ env.CHANGELOG_VERSION_MODE }}" == "strip" ]]; then tag_stripped=$(echo "$tag" | sed -E 's/-(alpha|beta|rc|pre|preview|dev|test)[0-9]*$//') else tag_stripped="$tag" fi - + # 如果这个 tag 的 CHANGELOG 版本不等于当前版本,就是我们要找的 if [ "$tag_stripped" != "$CHANGELOG_VERSION" ]; then PREVIOUS_TAG="$tag" @@ -424,13 +424,13 @@ jobs: COMMIT_RANGE="" else echo "📍 Previous tag: $PREVIOUS_TAG" - + # 对 previous tag 也执行 strip,显示对应的 CHANGELOG 版本 if [[ "${{ env.CHANGELOG_VERSION_MODE }}" == "strip" ]]; then PREV_CHANGELOG_VERSION=$(echo "$PREVIOUS_TAG" | sed -E 's/-(alpha|beta|rc|pre|preview|dev|test)[0-9]*$//') echo " (对应 CHANGELOG 版本: $PREV_CHANGELOG_VERSION)" fi - + COMMIT_RANGE="${PREVIOUS_TAG}..${VERSION}" fi @@ -477,9 +477,9 @@ jobs: line = line.strip() if not line or '|' not in line: continue - + commit_hash, message = line.split('|', 1) - + # 检查是否应该忽略 should_ignore = False matched_pattern = None @@ -488,7 +488,7 @@ jobs: should_ignore = True matched_pattern = pattern break - + if should_ignore: skipped_commits.append((message, matched_pattern)) else: @@ -549,11 +549,11 @@ jobs: # 找到版本区块,在其中追加新的提交 pattern = rf'(## :bookmark: {re.escape(changelog_version)}.*?\n{re.escape(section_title)}\n\n)(.*?)(\n---|\n## :bookmark: |\Z)' match = re.search(pattern, existing_content, re.DOTALL) - + if match: existing_commits = match.group(2).strip() new_commits = changelog_content.split(f"{section_title}\n\n")[1].split("\n---")[0].strip() - + # 合并提交,去重 all_commits_text = existing_commits + "\n" + new_commits # 简单去重(基于完整行) @@ -563,36 +563,64 @@ jobs: if line.strip() and line not in seen: unique_lines.append(line) seen.add(line) - + merged_commits = '\n'.join(unique_lines) - + # 重新组装版本区块 updated_section = match.group(1) + merged_commits + "\n\n---\n" new_content = existing_content[:match.start()] + updated_section + existing_content[match.end():] else: print("⚠️ Could not find version section, prepending new content") - new_content = existing_content.replace( - "# :memo: CHANGELOG\n\n", - f"# :memo: CHANGELOG\n\n{changelog_content}" - ) + # 尝试匹配各种可能的 CHANGELOG 标题格式 + header_pattern = r'^#\s*(?::[\w_]+:)?\s*CHANGELOG\s*\n' + match = re.search(header_pattern, existing_content, re.IGNORECASE | re.MULTILINE) + + if match: + insert_pos = match.end() + if not existing_content[insert_pos:insert_pos+1] == '\n': + new_content = existing_content[:insert_pos] + '\n' + changelog_content + existing_content[insert_pos:] + else: + new_content = existing_content[:insert_pos] + '\n' + changelog_content + existing_content[insert_pos+1:] + else: + # 没有找到标题,在开头插入 + new_content = f"# :memo: CHANGELOG\n\n{changelog_content}{existing_content}" else: # 插入新版本 - new_content = existing_content.replace( - "# :memo: CHANGELOG\n\n", - f"# :memo: CHANGELOG\n\n{changelog_content}" - ) + # 尝试匹配各种可能的 CHANGELOG 标题格式 + # 支持: # CHANGELOG, # :memo: CHANGELOG, # Changelog 等 + + # 尝试找到 CHANGELOG 标题(不区分大小写,可选 emoji) + header_pattern = r'^#\s*(?::[\w_]+:)?\s*CHANGELOG\s*\n' + match = re.search(header_pattern, existing_content, re.IGNORECASE | re.MULTILINE) + + if match: + # 找到了 CHANGELOG 标题,在其后插入 + insert_pos = match.end() + # 确保标题后有空行 + if not existing_content[insert_pos:insert_pos+1] == '\n': + new_content = existing_content[:insert_pos] + '\n' + changelog_content + existing_content[insert_pos:] + else: + new_content = existing_content[:insert_pos] + '\n' + changelog_content + existing_content[insert_pos+1:] + else: + # 没有找到 CHANGELOG 标题,在文件开头插入标题和内容 + if existing_content.strip(): + # 文件有内容但没有 CHANGELOG 标题 + new_content = f"# :memo: CHANGELOG\n\n{changelog_content}{existing_content}" + else: + # 文件为空 + new_content = f"# :memo: CHANGELOG\n\n{changelog_content}" # 检查内容是否真的改变 content_changed = (new_content != existing_content) version_exists = f"## :bookmark: {changelog_version}" in existing_content - + if content_changed: with open(changelog_file, 'w', encoding='utf-8') as f: f.write(new_content) - + print(f"✅ CHANGELOG updated successfully") print(f" Added {len(commits)} commits to version {changelog_version}") - + with open('/tmp/changelog_updated.txt', 'w') as f: f.write('true') with open('/tmp/content_changed.txt', 'w') as f: @@ -605,7 +633,7 @@ jobs: print(f" Version {changelog_version} already contains all {len(commits)} commits") print(f" This usually happens when recreating a deleted tag") print(f" Will still create Release from existing CHANGELOG content") - + with open('/tmp/changelog_updated.txt', 'w') as f: f.write('true') # 返回 true 以触发 Release 创建 with open('/tmp/content_changed.txt', 'w') as f: @@ -613,7 +641,7 @@ jobs: else: # 既没有新内容,版本也不存在(不应该发生) print(f"⚠️ Unexpected state: no content change and version doesn't exist") - + with open('/tmp/changelog_updated.txt', 'w') as f: f.write('false') with open('/tmp/content_changed.txt', 'w') as f: @@ -622,7 +650,7 @@ jobs: UPDATED=$(cat /tmp/changelog_updated.txt) CONTENT_CHANGED=$(cat /tmp/content_changed.txt 2>/dev/null || echo "false") - + echo "changelog_updated=$UPDATED" >> $GITHUB_OUTPUT echo "content_changed=$CONTENT_CHANGED" >> $GITHUB_OUTPUT @@ -660,10 +688,10 @@ jobs: else echo "📝 提交 CHANGELOG 更新..." git commit -m "${{ env.COMMIT_MSG }}" - + echo "📤 推送到远程 ${{ env.MAIN_BRANCH }} 分支..." git push https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git ${{ env.MAIN_BRANCH }} - + if [ $? -eq 0 ]; then echo "✅ 推送成功" else @@ -944,7 +972,7 @@ jobs: - ℹ️ CHANGELOG.md 无需更新(内容已存在) - ✅ Release 已创建 - ✅ CHANGELOG.md 已作为附件上传 - + 💡 **说明**: 检测到这是重建已删除的 tag,CHANGELOG 中已包含所有提交内容,因此直接从现有内容创建 Release。 EOFREBUILD fi @@ -963,7 +991,7 @@ jobs: 可能的原因: - 在标签之间未找到有效的提交记录 - 或者版本 \`${{ env.CHANGELOG_VERSION }}\` 已包含所有相关提交(常见于重新创建已删除的 tag) - + 💡 如果你删除了 tag 后重新创建,CHANGELOG 中的内容已经存在,无需重复添加。 EOFNOCOMMIT fi @@ -1017,7 +1045,7 @@ jobs: echo " - Release 标题: ${{ env.RELEASE_TITLE_PROCESSED }}" echo " - Pre-release: ${{ env.RELEASE_IS_PRERELEASE }}" echo "" - + if [ "${{ steps.changelog.outputs.content_changed }}" = "true" ]; then echo "✅ 已完成的操作:" echo " - CHANGELOG.md 已更新并推送到 ${{ env.MAIN_BRANCH }} 分支" @@ -1054,4 +1082,4 @@ jobs: run: | echo "🧹 清理临时文件..." rm -rf /tmp/commits.txt /tmp/changelog_updated.txt /tmp/content_changed.txt /tmp/release-body.txt /tmp/payload.json /tmp/release_response.json /tmp/upload_response_*.json - echo "✅ 清理完成" \ No newline at end of file + echo "✅ 清理完成" diff --git a/.gitea/workflows/update_stats_badge.yaml b/.gitea/workflows/update_stats_badge.yaml index de3ebab..a72f1e0 100644 --- a/.gitea/workflows/update_stats_badge.yaml +++ b/.gitea/workflows/update_stats_badge.yaml @@ -142,7 +142,7 @@ jobs: if [ ${#MISSING_TOOLS[@]} -gt 0 ]; then echo "" echo "📦 安装缺失的工具: ${MISSING_TOOLS[*]}" - + # 🔧 智能判断是否需要 sudo # Docker 环境通常以 root 运行,不需要 sudo if [ "$EUID" -eq 0 ] || [ "$(id -u)" -eq 0 ]; then @@ -166,7 +166,7 @@ jobs: echo " RUN apt-get update && apt-get install -y bc jq" exit 1 fi - + echo "installed=true" >> $GITHUB_OUTPUT echo "✅ 工具安装完成" else @@ -196,20 +196,20 @@ jobs: # 检查仓库状态 if [ -d "$REPO_DIR" ]; then echo "📂 目录已存在,检查 Git 仓库状态..." - + if [ -d "$REPO_DIR/.git" ]; then # 目录存在且是有效的 Git 仓库 echo "✓ 发现有效的 Git 仓库,执行增量更新..." cd "$REPO_DIR" - + # 清理工作区 git clean -fdx git reset --hard - + # 获取最新代码 echo "📥 拉取最新代码..." git fetch --all --tags --force - + echo "✓ 仓库已更新" else # 目录存在但不是 Git 仓库(可能之前运行失败) @@ -217,20 +217,20 @@ jobs: echo "🧹 清理损坏的目录..." rm -rf "$REPO_DIR" echo "✓ 已清理" - + # 重新克隆 echo "📥 克隆仓库..." mkdir -p "${{ env.WORKSPACE_DIR }}" - + git clone \ https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git \ "$REPO_DIR" - + if [ $? -ne 0 ]; then echo "❌ 克隆失败" exit 1 fi - + cd "$REPO_DIR" echo "✓ 仓库已克隆" fi @@ -238,16 +238,16 @@ jobs: # 目录不存在,首次克隆 echo "📥 克隆仓库(首次)..." mkdir -p "${{ env.WORKSPACE_DIR }}" - + git clone \ https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git \ "$REPO_DIR" - + if [ $? -ne 0 ]; then echo "❌ 克隆失败" exit 1 fi - + cd "$REPO_DIR" echo "✓ 仓库已克隆" fi @@ -294,17 +294,17 @@ jobs: # 检查统计分支是否存在 if git ls-remote --heads origin ${{ env.BADGE_BRANCH }} | grep -q ${{ env.BADGE_BRANCH }}; then echo "✅ 统计分支 '${{ env.BADGE_BRANCH }}' 已存在" - + # 检出统计分支 git fetch origin ${{ env.BADGE_BRANCH }}:${{ env.BADGE_BRANCH }} git checkout ${{ env.BADGE_BRANCH }} - + echo "📂 当前分支内容:" ls -la - + # 返回主分支 git checkout ${{ github.ref_name }} - + echo "branch_exists=true" >> $GITHUB_OUTPUT else echo "🆕 统计分支 '${{ env.BADGE_BRANCH }}' 不存在,将创建" @@ -348,7 +348,7 @@ jobs: # - stats 分支是独立的数据存储分支,不需要主分支的代码文件 # - 只需要存储徽章 JSON 文件和统计报告 # - 保持分支干净,避免混淆 - # + # # ✅ 能否复用仓库? # - 可以!.git 目录包含所有分支的完整信息 # - 切换到主分支时,主分支的文件会恢复 @@ -381,7 +381,7 @@ jobs: # 📊 代码统计徽章数据 > 此分支由 GitHub Actions 自动生成和维护 - > + > > ⚠️ **请勿手动修改此分支的内容!** ## 📁 目录结构 @@ -517,12 +517,12 @@ jobs: # 读取语言配置并统计 echo "${{ env.LANGUAGE_GROUPS }}" | while IFS= read -r line; do [ -z "$line" ] && continue - + # 解析配置行: 组名:后缀列表:显示名称:颜色:图标 IFS=':' read -r lang_id extensions display_name color icon <<< "$line" - + echo "🔍 统计 $display_name..." - + # 构建扩展名查找条件 FIND_CONDITIONS="" IFS=',' read -ra EXTS <<< "$extensions" @@ -533,21 +533,21 @@ jobs: FIND_CONDITIONS="$FIND_CONDITIONS -o -name '*.$ext'" fi done - + # 统计文件数 FILE_COUNT=$(eval "find . -type f \( $FIND_CONDITIONS \) $EXCLUDE_PARAMS" | wc -l) - + if [ "$FILE_COUNT" -gt 0 ]; then # 统计代码行数 CODE_LINES=$(eval "find . -type f \( $FIND_CONDITIONS \) $EXCLUDE_PARAMS -exec grep -cHv '^[[:space:]]*$' {} + 2>/dev/null" | awk -F: '{sum+=$2} END {print sum+0}') - + # 只保存超过阈值的语言 if [ "$CODE_LINES" -ge "${{ env.MIN_LINES_THRESHOLD }}" ]; then FORMATTED_LINES=$(printf "%'d" $CODE_LINES) - + echo " - 文件数: $FILE_COUNT" echo " - 代码行: $FORMATTED_LINES" - + # 保存到临时文件 echo "$lang_id|$display_name|$CODE_LINES|$FORMATTED_LINES|$FILE_COUNT|$color|$icon" >> /tmp/lang_stats/${lang_id}.txt LANGUAGE_COUNT=$((LANGUAGE_COUNT + 1)) @@ -619,12 +619,12 @@ jobs: if [ -f /tmp/lang_summary.txt ] && [ -s /tmp/lang_summary.txt ]; then echo "" echo "🌐 生成语言徽章..." - + while IFS='|' read -r lang_id display_name code_lines formatted_lines file_count color icon; do [ -z "$lang_id" ] && continue - + echo " - $display_name" - + # 生成代码行数徽章(使用 heredoc) if [ -n "$icon" ]; then # 带图标的徽章 @@ -651,7 +651,7 @@ jobs: EOFJSON fi GENERATED_COUNT=$((GENERATED_COUNT + 1)) - + # 生成文件数徽章 cat > ${{ env.BADGE_DIR }}/${lang_id}-files.json << EOFJSON { @@ -689,7 +689,7 @@ jobs: # 📊 代码统计详细报告 > 🤖 由 GitHub Actions 自动生成 - > + > > 📅 更新时间: TIMESTAMP_PLACEHOLDER ## 📈 总体统计 @@ -720,17 +720,17 @@ jobs: | 语言 | 代码行数 | 文件数 | 占比 | 徽章 | |------|----------|--------|------|------| EOFTABLE - + TOTAL_CODE=${{ steps.total.outputs.total_code }} while IFS='|' read -r lang_id display_name code_lines formatted_lines file_count color icon; do [ -z "$lang_id" ] && continue - + if [ "$TOTAL_CODE" -gt 0 ]; then PERCENT=$(echo "scale=1; $code_lines * 100 / $TOTAL_CODE" | bc) else PERCENT="0.0" fi - + cat >> ${{ env.BADGE_DIR }}/README.md << EOFLANG | **${display_name}** | ${formatted_lines} | ${file_count} | ${PERCENT}% | ![${display_name}](https://img.shields.io/endpoint?url=${{ env.RAW_URL_BASE }}/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }}/${lang_id}-lines.json) | EOFLANG @@ -807,7 +807,7 @@ jobs: echo "" else echo "📝 准备提交..." - + # 生成提交信息 COMMIT_MSG="chore: 更新代码统计 [$(date '+%Y-%m-%d %H:%M')] @@ -821,12 +821,12 @@ jobs: 🤖 由 GitHub Actions 自动生成" git commit -m "$COMMIT_MSG" - + echo "📤 推送到远程 ${{ env.BADGE_BRANCH }} 分支..." - + # 推送到远程(使用 TOKEN) git push https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git ${{ env.BADGE_BRANCH }} - + if [ $? -eq 0 ]; then echo "✅ 推送成功" echo "changed=true" >> $GITHUB_OUTPUT @@ -873,17 +873,17 @@ jobs: | 语言 | 代码行数 | 文件数 | 占比 | |------|----------|--------|------| EOFTABLE - + TOTAL_CODE=${{ steps.total.outputs.total_code }} sort -t'|' -k3 -nr /tmp/lang_summary.txt | head -10 | while IFS='|' read -r lang_id display_name code_lines formatted_lines file_count color icon; do [ -z "$lang_id" ] && continue - + if [ "$TOTAL_CODE" -gt 0 ]; then PERCENT=$(echo "scale=1; $code_lines * 100 / $TOTAL_CODE" | bc) else PERCENT="0.0" fi - + echo "| **${display_name}** | ${formatted_lines} | ${file_count} | ${PERCENT}% |" >> $GITHUB_STEP_SUMMARY done else