This commit is contained in:
Vaica committed 2026-06-24 23:08:35 +08:00
1 parent 450a28c81d
commit 0be8aa9668
7 files changed
+588 -203

No files matched your search

+431 -27
View File
@@ -6,19 +6,257 @@ on:
- main
schedule:
- cron: '0 1 * * *' # UTC 01:00 = 北京时间 09:00
workflow_dispatch:
inputs:
force_font_subset:
description: 'Force font subset regeneration'
type: boolean
default: false
skip_font_subset:
description: 'Skip font subset step'
type: boolean
default: false
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
TZ: Asia/Shanghai
jobs:
deploy:
# ============================================================
# Job 0: Pre-Flight Checks (前置检查)
# ============================================================
pre-check:
runs-on: ubuntu-latest
timeout-minutes: 2
outputs:
can_deploy: ${{ steps.check.outputs.can_deploy }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Validate repository
id: repo-check
run: |
if [ "${{ github.repository }}" != "zqlit/blog" ]; then
echo "❌ 只能在主仓库执行部署"
echo "can_deploy=false" >> $GITHUB_OUTPUT
exit 1
fi
echo "✅ 仓库验证通过"
echo "can_deploy=true" >> $GITHUB_OUTPUT
- name: Validate environment variables
id: env-check
run: |
REQUIRED_SECRETS=(
"EDGEONE_API_TOKEN"
"UPYUN_BUCKET"
"UPYUN_OPERATOR"
"UPYUN_PASSWORD"
"DOGECLOUD_ACCESS_KEY"
"DOGECLOUD_SECRET_KEY"
"MAIL_USERNAME"
"MAIL_PASSWORD"
)
MISSING=""
for SECRET in "${REQUIRED_SECRETS[@]}"; do
if [ -z "${{ secrets[SECRET] }}" ]; then
MISSING="$MISSING $SECRET"
fi
done
if [ -n "$MISSING" ]; then
echo "❌ 缺少必要的环境变量: $MISSING"
exit 1
fi
echo "✅ 所有必要环境变量已配置"
- name: Validate commit (only for push events)
if: github.event_name == 'push'
id: commit-check
run: |
COMMIT_AUTHOR="${{ github.event.head_commit.author.name }}"
COMMIT_MESSAGE="${{ github.event.head_commit.message }}"
echo "📝 提交作者: $COMMIT_AUTHOR"
echo "📄 提交信息: $COMMIT_MESSAGE"
if echo "$COMMIT_MESSAGE" | grep -qE "^(feat|fix|chore|docs|style|refactor|test): "; then
echo "✅ 提交信息符合规范"
else
echo "⚠️ 提交信息格式建议: feat/fix/chore/docs/style/refactor/test: description"
fi
- name: Summary
run: |
echo "## 🔍 前置检查完成" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **仓库**: ${{ github.repository }}" >> $GITHUB_STEP_SUMMARY
echo "- **分支**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "- **触发方式**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
echo "- **Commit SHA**: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
# ============================================================
# Job 1: Font Subset (字体子集化 - 可选并行任务)
# ============================================================
subset-fonts:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
env:
TZ: Asia/Shanghai
needs: pre-check
if: |
needs.pre-check.outputs.can_deploy == 'true' &&
inputs.skip_font_subset != 'true'
outputs:
font_updated: ${{ steps.update.outputs.font_updated }}
char_count: ${{ steps.stats.outputs.char_count }}
original_size: ${{ steps.stats.outputs.original_size }}
subset_size: ${{ steps.stats.outputs.subset_size }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: pip install fonttools brotli zopfli
- name: Generate font subset
id: update
run: |
# 运行字体子集化脚本
# 会自动扫描:
# 1. 静态文件 (content, layouts, public)
# 2. 本地 JSON 数据 (友链、朋友圈)
# 3. 远程 API (api.usj.cc 的友链、订阅源、朋友圈)
python scripts/subset-font-safe.py
# 检查是否有变化
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"
git add themes/Ying/static/font/zql-v2-subset.woff2 themes/Ying/static/font/used_chars.txt
if ! git diff --staged --quiet; then
git commit -m "chore: update font subset [skip ci]"
git push origin HEAD:main
echo "font_updated=true" >> $GITHUB_OUTPUT
echo "✅ 字体子集已更新并推送"
else
echo "font_updated=false" >> $GITHUB_OUTPUT
echo "ℹ️ 字体子集无变化,跳过提交"
fi
- name: Collect stats
id: stats
if: always()
run: |
CHAR_COUNT=$(wc -m < themes/Ying/static/font/used_chars.txt | tr -d ' ')
ORIGINAL_SIZE=$(stat -c%s themes/Ying/static/font/zql-v2.woff2)
SUBSET_SIZE=$(stat -c%s themes/Ying/static/font/zql-v2-subset.woff2)
echo "char_count=$CHAR_COUNT" >> $GITHUB_OUTPUT
echo "original_size=$ORIGINAL_SIZE" >> $GITHUB_OUTPUT
echo "subset_size=$SUBSET_SIZE" >> $GITHUB_OUTPUT
- name: Create font subset summary
if: always()
run: |
ORIGINAL_SIZE_KB=$(echo "scale=1; ${{ steps.stats.outputs.original_size }} / 1024" | bc)
SUBSET_SIZE_KB=$(echo "scale=1; ${{ steps.stats.outputs.subset_size }} / 1024" | bc)
REDUCTION_KB=$(echo "scale=1; (${{ steps.stats.outputs.original_size }} - ${{ steps.stats.outputs.subset_size }}) / 1024" | bc)
PERCENTAGE=$(echo "scale=1; (${{ steps.stats.outputs.original_size }} - ${{ steps.stats.outputs.subset_size }}) * 100 / ${{ steps.stats.outputs.original_size }}" | bc)
echo "## 🔤 字体子集化" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.update.outputs.font_updated }}" = "true" ]; then
echo "✅ **字体子集已更新**" >> $GITHUB_STEP_SUMMARY
else
echo "ℹ️ **字体子集无变化**" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📊 统计信息" >> $GITHUB_STEP_SUMMARY
echo "- **字符数:** ${{ steps.stats.outputs.char_count }}" >> $GITHUB_STEP_SUMMARY
echo "- **原始大小:** ${ORIGINAL_SIZE_KB} KB" >> $GITHUB_STEP_SUMMARY
echo "- **子集大小:** ${SUBSET_SIZE_KB} KB" >> $GITHUB_STEP_SUMMARY
echo "- **节省空间:** ${REDUCTION_KB} KB (${PERCENTAGE}%)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 🌐 扫描的数据源" >> $GITHUB_STEP_SUMMARY
echo "- 静态文件: content, layouts, public" >> $GITHUB_STEP_SUMMARY
echo "- 本地 JSON: link_lite.json, friend_circle_data.json" >> $GITHUB_STEP_SUMMARY
echo "- 远程 API: api.usj.cc (links, feeds, articles)" >> $GITHUB_STEP_SUMMARY
- name: Send font subset notification
if: steps.update.outputs.font_updated == 'true'
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
FEISHU_WEBHOOK_URL: ${{ secrets.FEISHU_WEBHOOK_URL }}
run: |
ORIGINAL_SIZE_KB=$(echo "scale=1; ${{ steps.stats.outputs.original_size }} / 1024" | bc)
SUBSET_SIZE_KB=$(echo "scale=1; ${{ steps.stats.outputs.subset_size }} / 1024" | bc)
REDUCTION_KB=$(echo "scale=1; (${{ steps.stats.outputs.original_size }} - ${{ steps.stats.outputs.subset_size }}) / 1024" | bc)
PERCENTAGE=$(echo "scale=1; (${{ steps.stats.outputs.original_size }} - ${{ steps.stats.outputs.subset_size }}) * 100 / ${{ steps.stats.outputs.original_size }}" | bc)
if [ -n "$TELEGRAM_BOT_TOKEN" ] && [ -n "$TELEGRAM_CHAT_ID" ]; then
MESSAGE="✅ **字体子集化完成**
📦 项目: ${{ github.repository }}
🌿 分支: ${{ github.ref_name }}
📊 优化统计:
- 字符数: ${{ steps.stats.outputs.char_count }}
- 原始大小: ${ORIGINAL_SIZE_KB} KB
- 子集大小: ${SUBSET_SIZE_KB} KB
- 节省空间: ${REDUCTION_KB} KB (${PERCENTAGE}%)
🌐 数据源:
- 静态文件 + 本地JSON + 远程API"
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="${TELEGRAM_CHAT_ID}" \
-d text="${MESSAGE}" \
-d parse_mode="Markdown"
fi
if [ -n "$FEISHU_WEBHOOK_URL" ]; then
curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
-H "Content-Type: application/json" \
-d '{
"msg_type": "text",
"content": {
"text": "✅ **字体子集化完成**\n\n📦 项目: ${{ github.repository }}\n🌿 分支: ${{ github.ref_name }}\n\n📊 优化统计:\n- 字符数: ${{ steps.stats.outputs.char_count }}\n- 原始大小: '"${ORIGINAL_SIZE_KB}"' KB\n- 子集大小: '"${SUBSET_SIZE_KB}"' KB\n- 节省空间: '"${REDUCTION_KB}"' KB ('"${PERCENTAGE}"'%)\n\n🌐 数据源: 静态文件 + 本地JSON + 远程API"
}
}'
fi
# ============================================================
# Job 2: Build (构建)
# ============================================================
build:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
outputs:
build_hash: ${{ steps.hash.outputs.build_hash }}
needs: pre-check
if: needs.pre-check.outputs.can_deploy == 'true'
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -31,11 +269,6 @@ jobs:
hugo-version: '0.128.2'
extended: true
- name: Run Pre-Build Scripts
shell: pwsh
run: |
./scripts/add_draft_to_hidden.ps1
- name: Setup Node
uses: actions/setup-node@v4
with:
@@ -45,8 +278,23 @@ jobs:
- name: Install Node.js dependencies
run: npm ci
- name: Cache Hugo resources
uses: actions/cache@v4
with:
path: |
resources/_gen
/home/runner/.cache/hugo_cache
key: ${{ runner.os }}-hugo-${{ hashFiles('go.sum', 'package-lock.json', 'hugo.toml', 'config/_default/*.toml') }}
restore-keys: |
${{ runner.os }}-hugo-
- name: Run Pre-Build Scripts
shell: pwsh
run: ./scripts/add_draft_to_hidden.ps1
- name: Optimize Images
continue-on-error: true
timeout-minutes: 5
run: node scripts/optimize_images.js
- name: Commit Optimized Images
@@ -61,9 +309,56 @@ jobs:
echo "No changes to commit"
fi
- name: Build
- name: Build Hugo site
timeout-minutes: 10
run: rm -rf public && hugo --minify
- name: Generate build hash
id: hash
run: echo "build_hash=$(find public -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum | awk '{print $1}')" >> $GITHUB_OUTPUT
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: hugo-public
path: public/
retention-days: 1
- name: Push Image Optimizations
continue-on-error: true
run: |
git fetch origin main
git stash push -m "temp stash for rebase" || true
git rebase origin/main || true
git stash pop || true
if git log --oneline -1 | grep -q "\[skip ci\]"; then
git push origin HEAD:main
else
echo "No auto-commits to push"
fi
# ============================================================
# Job 3: Deploy to EdgeOne Pages (并行)
# ============================================================
deploy-edgeone:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
needs: build
steps:
- name: Checkout code (for scripts)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: hugo-public
path: public/
- name: Deploy to EdgeOne Pages
env:
EDGEONE_API_TOKEN: ${{ secrets.EDGEONE_API_TOKEN }}
@@ -71,6 +366,23 @@ jobs:
chmod +x scripts/deploy_edgeone.sh
./scripts/deploy_edgeone.sh hugo-blog ./public overseas
# ============================================================
# Job 4: Deploy to UpYun (并行)
# ============================================================
deploy-upyun:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
needs: build
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: hugo-public
path: public/
- name: Deploy to UpYun
uses: her-cat/upyun-deployer@v1.0.3
with:
@@ -80,39 +392,84 @@ jobs:
dir: 'public'
publish_dir: '/'
# ============================================================
# Job 5: CDN Refresh & Notifications (在所有部署完成后)
# ============================================================
finalize:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
needs: [deploy-edgeone, deploy-upyun]
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- name: Install Node.js dependencies
run: npm ci
- name: Refresh DogeCloud CDN
continue-on-error: true
env:
DOGECLOUD_ACCESS_KEY: ${{ secrets.DOGECLOUD_ACCESS_KEY }}
DOGECLOUD_SECRET_KEY: ${{ secrets.DOGECLOUD_SECRET_KEY }}
CDN_URL_LIST: ${{ secrets.CDN_URL_LIST }}
run: node scripts/refresh_cdn.js
- name: Push Image Optimizations
continue-on-error: true
run: |
git fetch origin main
# stash任何未提交的更改,避免rebase失败
git stash push -m "temp stash for rebase"
git rebase origin/main
# 恢复stash的更改
git stash pop || true
if ! git log --oneline -1 | grep -q "\[skip ci\]"; then
echo "No auto-commits to push"
else
git push origin HEAD:main
fi
- name: Generate Notification Report
continue-on-error: true
run: node scripts/generate_notification_report.js
- name: Send Telegram notification
if: secrets.TELEGRAM_BOT_TOKEN && secrets.TELEGRAM_CHAT_ID
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
run: |
MESSAGE="✅ **部署成功**
📦 项目: ${{ github.repository }}
🌿 分支: ${{ github.ref_name }}
🔗 Commit: ${{ github.sha }}
🕐 时间: $(date +'%Y-%m-%d %H:%M:%S')
📊 部署目标:
- EdgeOne Pages ✓
- UpYun ✓
- CDN 刷新 ✓"
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="${TELEGRAM_CHAT_ID}" \
-d text="${MESSAGE}" \
-d parse_mode="Markdown"
- name: Send Feishu notification
if: secrets.FEISHU_WEBHOOK_URL
env:
FEISHU_WEBHOOK_URL: ${{ secrets.FEISHU_WEBHOOK_URL }}
run: |
curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
-H "Content-Type: application/json" \
-d '{
"msg_type": "text",
"content": {
"text": "✅ **部署成功**\n\n📦 项目: ${{ github.repository }}\n🌿 分支: ${{ github.ref_name }}\n🔗 Commit: ${{ github.sha }}\n🕐 时间: '$(date +'%Y-%m-%d %H:%M:%S')'\n\n📊 部署目标:\n- EdgeOne Pages ✓\n- UpYun ✓\n- CDN 刷新 ✓"
}
}'
- name: Get current date (CN)
id: date
run: echo "date=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_OUTPUT
- name: Send deployment notification
if: success()
uses: dawidd6/action-send-mail@v3
with:
server_address: smtp.qq.com
@@ -122,6 +479,53 @@ jobs:
password: ${{ secrets.MAIL_PASSWORD }}
subject: 部署报告 - ${{ steps.date.outputs.date }}
to: ${{ secrets.MAIL_USERNAME }}
from: GitHub Actions <${{ secrets.MAIL_USERNAME }}>
from: GitHub Actions <${{ github.event_name }}>
body: |
${{ env.DEPLOY_REPORT }}
# ============================================================
# Job 6: Failure Notification (失败通知)
# ============================================================
notify-failure:
runs-on: ubuntu-latest
timeout-minutes: 3
permissions:
contents: read
needs: [build, deploy-edgeone, deploy-upyun, finalize]
if: failure()
steps:
- name: Send Telegram failure notification
if: secrets.TELEGRAM_BOT_TOKEN && secrets.TELEGRAM_CHAT_ID
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
run: |
MESSAGE="❌ **部署失败**
📦 项目: ${{ github.repository }}
🌿 分支: ${{ github.ref_name }}
🔗 Commit: ${{ github.sha }}
🕐 时间: $(date +'%Y-%m-%d %H:%M:%S')
📝 工作流: ${{ github.workflow }}
🔍 查看详情: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="${TELEGRAM_CHAT_ID}" \
-d text="${MESSAGE}" \
-d parse_mode="Markdown"
- name: Send Feishu failure notification
if: secrets.FEISHU_WEBHOOK_URL
env:
FEISHU_WEBHOOK_URL: ${{ secrets.FEISHU_WEBHOOK_URL }}
run: |
curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
-H "Content-Type: application/json" \
-d '{
"msg_type": "text",
"content": {
"text": "❌ **部署失败**\n\n📦 项目: ${{ github.repository }}\n🌿 分支: ${{ github.ref_name }}\n🔗 Commit: ${{ github.sha }}\n🕐 时间: '$(date +'%Y-%m-%d %H:%M:%S')'\n\n📝 工作流: ${{ github.workflow }}\n🔍 查看详情: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
}'