Files
blog/.github/workflows/deploy.yml
T
2026-06-25 13:32:44 +08:00

798 lines
29 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Deploy to Production
on:
push:
branches:
- 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:
# ============================================================
# Job 0: Pre-Flight Checks (前置检查)
# ============================================================
pre-check:
runs-on: ubuntu-latest
timeout-minutes: 2
outputs:
can_deploy: ${{ steps.repo-check.outputs.can_deploy }}
steps:
- name: Start timing
id: start-pre-check
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- 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 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
- name: End timing
id: end-pre-check
run: |
end_time=$(date +%s)
start_time=${{ steps.start-pre-check.outputs.start_time }}
duration=$((end_time - start_time))
echo "duration=${duration}" >> $GITHUB_OUTPUT
echo "## ⏱️ Pre-check 耗时: ${duration}秒" >> $GITHUB_STEP_SUMMARY
# ============================================================
# Job 1: Font Subset (字体子集化 - 可选并行任务)
# ============================================================
subset-fonts:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
needs: pre-check
if: |
needs.pre-check.outputs.can_deploy == 'true' &&
inputs.skip_font_subset != 'true' &&
(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
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: Start timing
id: start-subset
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- 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
- name: End timing
id: end-subset
run: |
end_time=$(date +%s)
start_time=${{ steps.start-subset.outputs.start_time }}
duration=$((end_time - start_time))
echo "duration=${duration}" >> $GITHUB_OUTPUT
echo "## ⏱️ Subset-fonts 耗时: ${duration}秒" >> $GITHUB_STEP_SUMMARY
# ============================================================
# 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: Start timing
id: start-build
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: '0.128.2'
extended: true
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- 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
continue-on-error: true
run: |
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"
git add content/posts static
if ! git diff --staged --quiet; then
git commit -m "chore: auto-optimize images [skip ci]"
else
echo "No changes to commit"
fi
- 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
compression-level: 0
- 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
- name: End timing
id: end-build
run: |
end_time=$(date +%s)
start_time=${{ steps.start-build.outputs.start_time }}
duration=$((end_time - start_time))
echo "duration=${duration}" >> $GITHUB_OUTPUT
echo "## ⏱️ Build 耗时: ${duration}秒" >> $GITHUB_STEP_SUMMARY
# ============================================================
# Job 3: Deploy to EdgeOne Pages (并行)
# ============================================================
deploy-edgeone:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
needs: build
outputs:
deployed: ${{ steps.check.outputs.deployed }}
steps:
- name: Start timing
id: start-edgeone
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- name: Check build hash cache
id: cache-check
uses: actions/cache/restore@v4
with:
path: .last_deploy_hash
key: ${{ runner.os }}-deploy-edgeone-${{ needs.build.outputs.build_hash }}
restore-keys: |
${{ runner.os }}-deploy-edgeone-
- name: Check if deployment is needed
id: check
run: |
CURRENT_HASH="${{ needs.build.outputs.build_hash }}"
echo "当前 build_hash: $CURRENT_HASH"
if [ -f .last_deploy_hash ]; then
LAST_HASH=$(cat .last_deploy_hash)
echo "上次部署 hash: $LAST_HASH"
if [ "$CURRENT_HASH" = "$LAST_HASH" ]; then
echo "✅ 构建产物无变化,跳过 EdgeOne 部署"
echo "deployed=false" >> $GITHUB_OUTPUT
echo "skip_reason=hash_match" >> $GITHUB_OUTPUT
exit 0
fi
fi
echo "🔄 构建产物有变化,需要部署到 EdgeOne"
echo "deployed=true" >> $GITHUB_OUTPUT
- name: Checkout code (for scripts)
if: steps.check.outputs.deployed == 'true'
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download build artifact
if: steps.check.outputs.deployed == 'true'
uses: actions/download-artifact@v4
with:
name: hugo-public
path: public/
- name: Deploy to EdgeOne Pages
if: steps.check.outputs.deployed == 'true'
env:
EDGEONE_API_TOKEN: ${{ secrets.EDGEONE_API_TOKEN }}
run: |
chmod +x scripts/deploy_edgeone.sh
./scripts/deploy_edgeone.sh hugo-blog ./public overseas
- name: Save deploy hash
if: steps.check.outputs.deployed == 'true'
run: echo "${{ needs.build.outputs.build_hash }}" > .last_deploy_hash
- name: Save build hash cache
if: steps.check.outputs.deployed == 'true'
uses: actions/cache/save@v4
with:
path: .last_deploy_hash
key: ${{ runner.os }}-deploy-edgeone-${{ needs.build.outputs.build_hash }}
- name: End timing
id: end-edgeone
run: |
end_time=$(date +%s)
start_time=${{ steps.start-edgeone.outputs.start_time }}
duration=$((end_time - start_time))
echo "duration=${duration}" >> $GITHUB_OUTPUT
if [ "${{ steps.check.outputs.deployed }}" = "true" ]; then
echo "## ⏱️ Deploy EdgeOne 耗时: ${duration}秒" >> $GITHUB_STEP_SUMMARY
else
echo "## EdgeOne 部署已跳过(构建产物无变化)" >> $GITHUB_STEP_SUMMARY
echo "总耗时: ${duration}秒" >> $GITHUB_STEP_SUMMARY
fi
# ============================================================
# Job 4: Deploy to UpYun (并行)
# ============================================================
deploy-upyun:
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
needs: build
outputs:
deployed: ${{ steps.check.outputs.deployed }}
steps:
- name: Start timing
id: start-upyun
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- name: Check build hash cache
id: cache-check
uses: actions/cache/restore@v4
with:
path: .last_deploy_hash
key: ${{ runner.os }}-deploy-upyun-${{ needs.build.outputs.build_hash }}
restore-keys: |
${{ runner.os }}-deploy-upyun-
- name: Check if deployment is needed
id: check
run: |
CURRENT_HASH="${{ needs.build.outputs.build_hash }}"
echo "当前 build_hash: $CURRENT_HASH"
if [ -f .last_deploy_hash ]; then
LAST_HASH=$(cat .last_deploy_hash)
echo "上次部署 hash: $LAST_HASH"
if [ "$CURRENT_HASH" = "$LAST_HASH" ]; then
echo "✅ 构建产物无变化,跳过 UpYun 部署"
echo "deployed=false" >> $GITHUB_OUTPUT
echo "skip_reason=hash_match" >> $GITHUB_OUTPUT
exit 0
fi
fi
echo "🔄 构建产物有变化,需要部署到 UpYun"
echo "deployed=true" >> $GITHUB_OUTPUT
- name: Start artifact download timing
if: steps.check.outputs.deployed == 'true'
id: start-upyun-download
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- name: Download build artifact
if: steps.check.outputs.deployed == 'true'
uses: actions/download-artifact@v4
with:
name: hugo-public
path: public/
- name: End artifact download timing
if: steps.check.outputs.deployed == 'true'
id: end-upyun-download
run: |
end_time=$(date +%s)
start_time=${{ steps.start-upyun-download.outputs.start_time }}
duration=$((end_time - start_time))
echo "duration=${duration}" >> $GITHUB_OUTPUT
echo "## UpYun artifact download duration: ${duration}s" >> $GITHUB_STEP_SUMMARY
- name: Start UpYun upload timing
if: steps.check.outputs.deployed == 'true'
id: start-upyun-upload
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- name: Checkout code (for scripts)
if: steps.check.outputs.deployed == 'true'
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Deploy to UpYun
if: steps.check.outputs.deployed == 'true'
env:
UPYUN_SERVICE: ${{ secrets.UPYUN_BUCKET }}
UPYUN_OPERATOR: ${{ secrets.UPYUN_OPERATOR }}
UPYUN_PASSWORD: ${{ secrets.UPYUN_PASSWORD }}
run: |
chmod +x scripts/deploy_upyun.sh
./scripts/deploy_upyun.sh ./public / 20
- name: Save deploy hash
if: steps.check.outputs.deployed == 'true'
run: echo "${{ needs.build.outputs.build_hash }}" > .last_deploy_hash
- name: Save build hash cache
if: steps.check.outputs.deployed == 'true'
uses: actions/cache/save@v4
with:
path: .last_deploy_hash
key: ${{ runner.os }}-deploy-upyun-${{ needs.build.outputs.build_hash }}
- name: End timing
id: end-upyun
run: |
end_time=$(date +%s)
total_start_time=${{ steps.start-upyun.outputs.start_time }}
total_duration=$((end_time - total_start_time))
echo "duration=${total_duration}" >> $GITHUB_OUTPUT
if [ "${{ steps.check.outputs.deployed }}" = "true" ]; then
upload_start_time=${{ steps.start-upyun-upload.outputs.start_time }}
upload_duration=$((end_time - upload_start_time))
echo "upload_duration=${upload_duration}" >> $GITHUB_OUTPUT
echo "## Deploy UpYun total duration: ${total_duration}s" >> $GITHUB_STEP_SUMMARY
echo "## UpYun upload duration: ${upload_duration}s" >> $GITHUB_STEP_SUMMARY
else
echo "## UpYun 部署已跳过(构建产物无变化)" >> $GITHUB_STEP_SUMMARY
echo "总耗时: ${total_duration}s" >> $GITHUB_STEP_SUMMARY
fi
# ============================================================
# Job 5: CDN Refresh & Notifications (after all deployments)
# ============================================================
finalize:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
needs: [pre-check, subset-fonts, build, deploy-edgeone, deploy-upyun]
if: always() && needs.build.result == 'success' && needs.deploy-edgeone.result == 'success' && needs.deploy-upyun.result == 'success'
steps:
- name: Start timing
id: start-finalize
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- 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: Determine deployment status
id: deploy-status
run: |
EDGEONE_DEPLOYED="${{ needs.deploy-edgeone.outputs.deployed }}"
UPYUN_DEPLOYED="${{ needs.deploy-upyun.outputs.deployed }}"
echo "EdgeOne deployed: $EDGEONE_DEPLOYED"
echo "UpYun deployed: $UPYUN_DEPLOYED"
if [ "$EDGEONE_DEPLOYED" = "true" ] || [ "$UPYUN_DEPLOYED" = "true" ]; then
echo "any_deployed=true" >> $GITHUB_OUTPUT
else
echo "any_deployed=false" >> $GITHUB_OUTPUT
fi
- name: Refresh DogeCloud CDN
if: steps.deploy-status.outputs.any_deployed == '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: Generate Notification Report
if: steps.deploy-status.outputs.any_deployed == 'true'
continue-on-error: true
run: node scripts/generate_notification_report.js
- name: Send Telegram notification
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
run: |
if [ -z "$TELEGRAM_BOT_TOKEN" ] || [ -z "$TELEGRAM_CHAT_ID" ]; then
echo "⚠️ Telegram secrets 未配置,跳过通知"
exit 0
fi
EDGEONE_STATUS="${{ needs.deploy-edgeone.outputs.deployed == 'true' && '✅' || '⏭️ 跳过' }}"
UPYUN_STATUS="${{ needs.deploy-upyun.outputs.deployed == 'true' && '✅' || '⏭️ 跳过' }}"
CDN_STATUS="${{ steps.deploy-status.outputs.any_deployed == 'true' && '✅' || '⏭️ 跳过' }}"
if [ "${{ steps.deploy-status.outputs.any_deployed }}" = "true" ]; then
TITLE="✅ **部署成功**"
else
TITLE="ℹ️ **无变化,跳过部署**"
fi
MESSAGE="${TITLE}
📦 项目: ${{ github.repository }}
🌿 分支: ${{ github.ref_name }}
🔗 Commit: ${{ github.sha }}
🕐 时间: $(date +'%Y-%m-%d %H:%M:%S')
📊 部署目标:
- EdgeOne Pages ${EDGEONE_STATUS}
- UpYun ${UPYUN_STATUS}
- CDN 刷新 ${CDN_STATUS}"
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
env:
FEISHU_WEBHOOK_URL: ${{ secrets.FEISHU_WEBHOOK_URL }}
run: |
if [ -z "$FEISHU_WEBHOOK_URL" ]; then
echo "⚠️ 飞书 Webhook URL 未配置,跳过通知"
exit 0
fi
EDGEONE_STATUS="${{ needs.deploy-edgeone.outputs.deployed == 'true' && '✅' || '⏭️ 跳过' }}"
UPYUN_STATUS="${{ needs.deploy-upyun.outputs.deployed == 'true' && '✅' || '⏭️ 跳过' }}"
CDN_STATUS="${{ steps.deploy-status.outputs.any_deployed == 'true' && '✅' || '⏭️ 跳过' }}"
if [ "${{ steps.deploy-status.outputs.any_deployed }}" = "true" ]; then
TITLE="✅ **部署成功**"
else
TITLE="ℹ️ **无变化,跳过部署**"
fi
CURRENT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
-H "Content-Type: application/json" \
-d '{
"msg_type": "text",
"content": {
"text": "'"${TITLE}"'\n\n📦 项目: ${{ github.repository }}\n🌿 分支: ${{ github.ref_name }}\n🔗 Commit: ${{ github.sha }}\n🕐 时间: '"${CURRENT_TIME}"'\n\n📊 部署目标:\n- EdgeOne Pages '"${EDGEONE_STATUS}"'\n- UpYun '"${UPYUN_STATUS}"'\n- CDN 刷新 '"${CDN_STATUS}"'"
}
}'
- name: Get current date (CN)
if: steps.deploy-status.outputs.any_deployed == 'true'
id: date
run: echo "date=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_OUTPUT
- name: Send deployment notification
if: steps.deploy-status.outputs.any_deployed == 'true'
uses: dawidd6/action-send-mail@v3
with:
server_address: smtp.qq.com
server_port: 465
secure: true
username: ${{ secrets.MAIL_USERNAME }}
password: ${{ secrets.MAIL_PASSWORD }}
subject: 部署报告 - ${{ steps.date.outputs.date }}
to: ${{ secrets.MAIL_USERNAME }}
from: GitHub Actions <${{ github.event_name }}>
body: |
${{ env.DEPLOY_REPORT }}
- name: Generate timing summary
id: timing-summary
run: |
end_time=$(date +%s)
start_time=${{ steps.start-finalize.outputs.start_time }}
finalize_duration=$((end_time - start_time))
EDGEONE_DEPLOYED="${{ needs.deploy-edgeone.outputs.deployed }}"
UPYUN_DEPLOYED="${{ needs.deploy-upyun.outputs.deployed }}"
if [ "$EDGEONE_DEPLOYED" = "true" ]; then
EDGEONE_DISPLAY="✅ 已部署"
else
EDGEONE_DISPLAY="⏭️ 跳过(无变化)"
fi
if [ "$UPYUN_DEPLOYED" = "true" ]; then
UPYUN_DISPLAY="✅ 已部署"
else
UPYUN_DISPLAY="⏭️ 跳过(无变化)"
fi
echo "## ⏱️ 部署耗时统计" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| 阶段 | 状态 |" >> $GITHUB_STEP_SUMMARY
echo "|------|------|" >> $GITHUB_STEP_SUMMARY
echo "| Pre-check | ${{ needs.pre-check.result == 'success' && '✅ 成功' || needs.pre-check.result == 'skipped' && '⏭️ 跳过' || '❌ 失败' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Subset-fonts | ${{ needs.subset-fonts.result == 'success' && '✅ 成功' || needs.subset-fonts.result == 'skipped' && '⏭️ 跳过' || '❌ 失败' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Build | ${{ needs.build.result == 'success' && '✅ 成功' || '❌ 失败' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Deploy EdgeOne | ${EDGEONE_DISPLAY} |" >> $GITHUB_STEP_SUMMARY
echo "| Deploy UpYun | ${UPYUN_DISPLAY} |" >> $GITHUB_STEP_SUMMARY
echo "| Finalize | ✅ 成功 (${finalize_duration}秒) |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Finalize 阶段耗时:** ${finalize_duration}秒" >> $GITHUB_STEP_SUMMARY
# ============================================================
# Job 6: Failure Notification (失败通知)
# ============================================================
notify-failure:
runs-on: ubuntu-latest
timeout-minutes: 3
permissions:
contents: read
needs: [pre-check, subset-fonts, build, deploy-edgeone, deploy-upyun, finalize]
if: always() && (needs.pre-check.result == 'failure' || needs.build.result == 'failure' || needs.deploy-edgeone.result == 'failure' || needs.deploy-upyun.result == 'failure' || needs.finalize.result == 'failure')
steps:
- name: Send Telegram failure notification
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
run: |
if [ -z "$TELEGRAM_BOT_TOKEN" ] || [ -z "$TELEGRAM_CHAT_ID" ]; then
echo "⚠️ Telegram secrets 未配置,跳过失败通知"
exit 0
fi
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
env:
FEISHU_WEBHOOK_URL: ${{ secrets.FEISHU_WEBHOOK_URL }}
run: |
if [ -z "$FEISHU_WEBHOOK_URL" ]; then
echo "⚠️ 飞书 Webhook URL 未配置,跳过失败通知"
exit 0
fi
CURRENT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
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🕐 时间: '"${CURRENT_TIME}"'\n\n📝 工作流: ${{ github.workflow }}\n🔍 查看详情: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
}'