Files
blog/.github/workflows/deploy.yml
T
Workflow config file is invalid. Please check your config file: go-yaml load error in scanner at L600.C11: mapping values are not allowed in this context
2026-06-25 15:53:43 +08:00

842 lines
33 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:
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 }}
duration: ${{ steps.end-pre-check.outputs.duration }}
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: Build (构建)
# ============================================================
build:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
outputs:
build_hash: ${{ steps.hash.outputs.build_hash }}
duration: ${{ steps.end-build.outputs.duration }}
file_count: ${{ steps.stats.outputs.file_count }}
total_size: ${{ steps.stats.outputs.total_size }}
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
id: hugo-cache
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: Build statistics
id: stats
run: |
FILE_COUNT=$(find public -type f | wc -l)
TOTAL_SIZE=$(du -sh public | awk '{print $1}')
echo "file_count=${FILE_COUNT}" >> $GITHUB_OUTPUT
echo "total_size=${TOTAL_SIZE}" >> $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
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📊 构建统计" >> $GITHUB_STEP_SUMMARY
echo "- **文件总数**: ${{ steps.stats.outputs.file_count }}" >> $GITHUB_STEP_SUMMARY
echo "- **总大小**: ${{ steps.stats.outputs.total_size }}" >> $GITHUB_STEP_SUMMARY
echo "- **构建哈希**: ${{ steps.hash.outputs.build_hash }}" >> $GITHUB_STEP_SUMMARY
echo "- **Hugo 缓存**: ${{ steps.hugo-cache.outputs.cache-hit == 'true' && '✅ 命中' || '🔄 未命中' }}" >> $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 }}
duration: ${{ steps.end-edgeone.outputs.duration }}
download_duration: ${{ steps.end-edgeone.outputs.download_duration }}
upload_duration: ${{ steps.end-edgeone.outputs.upload_duration }}
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: Start download timing
if: steps.check.outputs.deployed == 'true'
id: start-edgeone-download
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: 0
- name: Download build artifact
if: steps.check.outputs.deployed == 'true'
uses: actions/download-artifact@v4
with:
name: hugo-public
path: public/
- name: End download timing
if: steps.check.outputs.deployed == 'true'
id: end-edgeone-download
run: |
end_time=$(date +%s)
start_time=${{ steps.start-edgeone-download.outputs.start_time }}
duration=$((end_time - start_time))
echo "duration=${duration}" >> $GITHUB_OUTPUT
- name: Start upload timing
if: steps.check.outputs.deployed == 'true'
id: start-edgeone-upload
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- 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: End upload timing
if: steps.check.outputs.deployed == 'true'
id: end-edgeone-upload
run: |
end_time=$(date +%s)
start_time=${{ steps.start-edgeone-upload.outputs.start_time }}
duration=$((end_time - start_time))
echo "duration=${duration}" >> $GITHUB_OUTPUT
- 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
download_duration=${{ steps.end-edgeone-download.outputs.duration }}
upload_duration=${{ steps.end-edgeone-upload.outputs.duration }}
echo "download_duration=${download_duration}" >> $GITHUB_OUTPUT
echo "upload_duration=${upload_duration}" >> $GITHUB_OUTPUT
echo "## ⏱️ Deploy EdgeOne 耗时: ${duration}秒" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📊 部署统计" >> $GITHUB_STEP_SUMMARY
echo "- **下载 Artifact**: ${download_duration}秒" >> $GITHUB_STEP_SUMMARY
echo "- **上传部署**: ${upload_duration}秒" >> $GITHUB_STEP_SUMMARY
echo "- **文件总数**: ${{ needs.build.outputs.file_count }}" >> $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 }}
duration: ${{ steps.end-upyun.outputs.duration }}
download_duration: ${{ steps.end-upyun.outputs.download_duration }}
upload_duration: ${{ steps.end-upyun.outputs.upload_duration }}
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 download timing
if: steps.check.outputs.deployed == 'true'
id: start-upyun-download
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: Download build artifact
if: steps.check.outputs.deployed == 'true'
uses: actions/download-artifact@v4
with:
name: hugo-public
path: public/
- name: End 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
- name: Start upload timing
if: steps.check.outputs.deployed == 'true'
id: start-upyun-upload
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- 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: End upload timing
if: steps.check.outputs.deployed == 'true'
id: end-upyun-upload
run: |
end_time=$(date +%s)
start_time=${{ steps.start-upyun-upload.outputs.start_time }}
duration=$((end_time - start_time))
echo "duration=${duration}" >> $GITHUB_OUTPUT
- 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
download_duration=${{ steps.end-upyun-download.outputs.duration }}
upload_duration=${{ steps.end-upyun-upload.outputs.duration }}
echo "download_duration=${download_duration}" >> $GITHUB_OUTPUT
echo "upload_duration=${upload_duration}" >> $GITHUB_OUTPUT
echo "## ⏱️ Deploy UpYun 耗时: ${total_duration}秒" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📊 部署统计" >> $GITHUB_STEP_SUMMARY
echo "- **下载 Artifact**: ${download_duration}秒" >> $GITHUB_STEP_SUMMARY
echo "- **上传部署**: ${upload_duration}秒" >> $GITHUB_STEP_SUMMARY
echo "- **文件总数**: ${{ needs.build.outputs.file_count }}" >> $GITHUB_STEP_SUMMARY
echo "- **总大小**: ${{ needs.build.outputs.total_size }}" >> $GITHUB_STEP_SUMMARY
else
echo "## UpYun 部署已跳过(构建产物无变化)" >> $GITHUB_STEP_SUMMARY
echo "总耗时: ${total_duration}秒" >> $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, 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: Send Telegram notification with detailed timing
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
PRECHECK_DUR="${{ needs.pre-check.outputs.duration }}"
BUILD_DUR="${{ needs.build.outputs.duration }}"
EDGEONE_DUR="${{ needs.deploy-edgeone.outputs.duration }}"
EDGEONE_DEPLOYED="${{ needs.deploy-edgeone.outputs.deployed }}"
UPYUN_DUR="${{ needs.deploy-upyun.outputs.duration }}"
UPYUN_DEPLOYED="${{ needs.deploy-upyun.outputs.deployed }}"
FILE_COUNT="${{ needs.build.outputs.file_count }}"
TOTAL_SIZE="${{ needs.build.outputs.total_size }}"
if [ "$EDGEONE_DEPLOYED" = "true" ]; then
EDGEONE_DOWNLOAD="${{ needs.deploy-edgeone.outputs.download_duration }}"
EDGEONE_UPLOAD="${{ needs.deploy-edgeone.outputs.upload_duration }}"
EDGEONE_DETAIL=$'\n - 下载: '"${EDGEONE_DOWNLOAD}"'s\n - 上传: '"${EDGEONE_UPLOAD}"'s'
else
EDGEONE_DETAIL=""
fi
if [ "$UPYUN_DEPLOYED" = "true" ]; then
UPYUN_DOWNLOAD="${{ needs.deploy-upyun.outputs.download_duration }}"
UPYUN_UPLOAD="${{ needs.deploy-upyun.outputs.upload_duration }}"
UPYUN_DETAIL=$'\n - 下载: '"${UPYUN_DOWNLOAD}"'s\n - 上传: '"${UPYUN_UPLOAD}"'s'
else
UPYUN_DETAIL=""
fi
if [ "${{ steps.deploy-status.outputs.any_deployed }}" = "true" ]; then
TITLE="✅ **部署成功**"
else
TITLE="ℹ️ **无变化,跳过部署**"
fi
CURRENT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
MESSAGE="$TITLE
📦 项目: ${{ github.repository }}
🌿 分支: ${{ github.ref_name }}
🔗 Commit: ${{ github.sha }}
🕐 时间: $CURRENT_TIME
📊 构建统计:
- 文件总数: $FILE_COUNT
- 总大小: $TOTAL_SIZE
⏱️ 耗时统计:
- Pre-check: ${PRECHECK_DUR}s
- Build: ${BUILD_DUR}s
- EdgeOne: ${EDGEONE_DUR}s${EDGEONE_DETAIL}
- UpYun: ${UPYUN_DUR}s${UPYUN_DETAIL}
🔍 查看详情: 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 notification with detailed timing
env:
FEISHU_WEBHOOK_URL: ${{ secrets.FEISHU_WEBHOOK_URL }}
run: |
if [ -z "$FEISHU_WEBHOOK_URL" ]; then
echo "⚠️ 飞书 Webhook URL 未配置,跳过通知"
exit 0
fi
PRECHECK_DUR="${{ needs.pre-check.outputs.duration }}"
BUILD_DUR="${{ needs.build.outputs.duration }}"
EDGEONE_DUR="${{ needs.deploy-edgeone.outputs.duration }}"
EDGEONE_DEPLOYED="${{ needs.deploy-edgeone.outputs.deployed }}"
UPYUN_DUR="${{ needs.deploy-upyun.outputs.duration }}"
UPYUN_DEPLOYED="${{ needs.deploy-upyun.outputs.deployed }}"
FILE_COUNT="${{ needs.build.outputs.file_count }}"
TOTAL_SIZE="${{ needs.build.outputs.total_size }}"
if [ "$EDGEONE_DEPLOYED" = "true" ]; then
EDGEONE_DOWNLOAD="${{ needs.deploy-edgeone.outputs.download_duration }}"
EDGEONE_UPLOAD="${{ needs.deploy-edgeone.outputs.upload_duration }}"
EDGEONE_DETAIL="\n - 下载: ${EDGEONE_DOWNLOAD}s\n - 上传: ${EDGEONE_UPLOAD}s"
else
EDGEONE_DETAIL=""
fi
if [ "$UPYUN_DEPLOYED" = "true" ]; then
UPYUN_DOWNLOAD="${{ needs.deploy-upyun.outputs.download_duration }}"
UPYUN_UPLOAD="${{ needs.deploy-upyun.outputs.upload_duration }}"
UPYUN_DETAIL="\n - 下载: ${UPYUN_DOWNLOAD}s\n - 上传: ${UPYUN_UPLOAD}s"
else
UPYUN_DETAIL=""
fi
if [ "${{ steps.deploy-status.outputs.any_deployed }}" = "true" ]; then
TITLE="✅ **部署成功**"
else
TITLE="ℹ️ **无变化,跳过部署**"
fi
CURRENT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
MESSAGE_TEXT="$TITLE"$'\n\n'"📦 项目: ${{ github.repository }}"$'\n'"🌿 分支: ${{ github.ref_name }}"$'\n'"🔗 Commit: ${{ github.sha }}"$'\n'"🕐 时间: ${CURRENT_TIME}"$'\n\n'"📊 构建统计:"$'\n'"- 文件总数: ${FILE_COUNT}"$'\n'"- 总大小: ${TOTAL_SIZE}"$'\n\n'"⏱️ 耗时统计:"$'\n'"- Pre-check: ${PRECHECK_DUR}s"$'\n'"- Build: ${BUILD_DUR}s"$'\n'"- EdgeOne: ${EDGEONE_DUR}s${EDGEONE_DETAIL}"$'\n'"- UpYun: ${UPYUN_DUR}s${UPYUN_DETAIL}"$'\n\n'"🔍 查看详情: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
-H "Content-Type: application/json" \
-d "{
\"msg_type\": \"text\",
\"content\": {
\"text\": \"${MESSAGE_TEXT}\"
}
}"
- 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 email notification with detailed timing
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: "[部署报告] ${{ github.repository }} - ${{ steps.date.outputs.date }}"
to: ${{ secrets.MAIL_USERNAME }}
from: GitHub Actions <${{ github.event_name }}>
body: |
===========================================
部署报告
===========================================
项目: ${{ github.repository }}
分支: ${{ github.ref_name }}
Commit: ${{ github.sha }}
时间: ${{ steps.date.outputs.date }}
触发方式: ${{ github.event_name }}
===========================================
构建统计
===========================================
文件总数: ${{ needs.build.outputs.file_count }}
总大小: ${{ needs.build.outputs.total_size }}
===========================================
耗时统计
===========================================
Pre-check: ${{ needs.pre-check.outputs.duration }}s
Build: ${{ needs.build.outputs.duration }}s
EdgeOne Pages: ${{ needs.deploy-edgeone.outputs.duration }}s
- 下载 Artifact: ${{ needs.deploy-edgeone.outputs.download_duration }}s
- 上传部署: ${{ needs.deploy-edgeone.outputs.upload_duration }}s
UpYun: ${{ needs.deploy-upyun.outputs.duration }}s
- 下载 Artifact: ${{ needs.deploy-upyun.outputs.download_duration }}s
- 上传部署: ${{ needs.deploy-upyun.outputs.upload_duration }}s
===========================================
部署状态
===========================================
EdgeOne Pages: ${{ needs.deploy-edgeone.outputs.deployed == 'true' && '已部署' || '跳过(无变化)' }}
UpYun: ${{ needs.deploy-upyun.outputs.deployed == 'true' && '已部署' || '跳过(无变化)' }}
===========================================
查看详情
===========================================
https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
- 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))
PRECHECK_DUR="${{ needs.pre-check.outputs.duration }}"
BUILD_DUR="${{ needs.build.outputs.duration }}"
EDGEONE_DUR="${{ needs.deploy-edgeone.outputs.duration }}"
EDGEONE_DEPLOYED="${{ needs.deploy-edgeone.outputs.deployed }}"
UPYUN_DUR="${{ needs.deploy-upyun.outputs.duration }}"
UPYUN_DEPLOYED="${{ needs.deploy-upyun.outputs.deployed }}"
if [ "$EDGEONE_DEPLOYED" = "true" ]; then
EDGEONE_DOWNLOAD="${{ needs.deploy-edgeone.outputs.download_duration }}"
EDGEONE_UPLOAD="${{ needs.deploy-edgeone.outputs.upload_duration }}"
else
EDGEONE_DOWNLOAD="0"
EDGEONE_UPLOAD="0"
fi
if [ "$UPYUN_DEPLOYED" = "true" ]; then
UPYUN_DOWNLOAD="${{ needs.deploy-upyun.outputs.download_duration }}"
UPYUN_UPLOAD="${{ needs.deploy-upyun.outputs.upload_duration }}"
else
UPYUN_DOWNLOAD="0"
UPYUN_UPLOAD="0"
fi
echo "## 📊 部署报告" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 基本信息" >> $GITHUB_STEP_SUMMARY
echo "- **项目**: ${{ github.repository }}" >> $GITHUB_STEP_SUMMARY
echo "- **分支**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "- **Commit**: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
echo "- **触发方式**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
echo "- **文件总数**: ${{ needs.build.outputs.file_count }}" >> $GITHUB_STEP_SUMMARY
echo "- **总大小**: ${{ needs.build.outputs.total_size }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### ⏱️ 耗时统计" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| 阶段 | 总耗时 | 下载 | 上传 |" >> $GITHUB_STEP_SUMMARY
echo "|------|--------|------|------|" >> $GITHUB_STEP_SUMMARY
echo "| Pre-check | ${PRECHECK_DUR}s | - | - |" >> $GITHUB_STEP_SUMMARY
echo "| Build | ${BUILD_DUR}s | - | - |" >> $GITHUB_STEP_SUMMARY
echo "| EdgeOne | ${EDGEONE_DUR}s | ${EDGEONE_DOWNLOAD}s | ${EDGEONE_UPLOAD}s |" >> $GITHUB_STEP_SUMMARY
echo "| UpYun | ${UPYUN_DUR}s | ${UPYUN_DOWNLOAD}s | ${UPYUN_UPLOAD}s |" >> $GITHUB_STEP_SUMMARY
echo "| Finalize | ${finalize_duration}s | - | - |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 🚀 部署状态" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| 目标 | 状态 |" >> $GITHUB_STEP_SUMMARY
echo "|------|------|" >> $GITHUB_STEP_SUMMARY
echo "| EdgeOne Pages | ${{ needs.deploy-edgeone.outputs.deployed == 'true' && '✅ 已部署' || '⏭️ 跳过(无变化)' }} |" >> $GITHUB_STEP_SUMMARY
echo "| UpYun | ${{ needs.deploy-upyun.outputs.deployed == 'true' && '✅ 已部署' || '⏭️ 跳过(无变化)' }} |" >> $GITHUB_STEP_SUMMARY
echo "| CDN 刷新 | ${{ steps.deploy-status.outputs.any_deployed == 'true' && '✅ 已刷新' || '⏭️ 跳过' }} |" >> $GITHUB_STEP_SUMMARY
# ============================================================
# Job 6: Failure Notification (失败通知)
# ============================================================
notify-failure:
runs-on: ubuntu-latest
timeout-minutes: 3
permissions:
contents: read
needs: [pre-check, 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
FAILURES=""
if [ "${{ needs.pre-check.result }}" = "failure" ]; then FAILURES="${FAILURES}"$'\n'"- ❌ Pre-check"; fi
if [ "${{ needs.build.result }}" = "failure" ]; then FAILURES="${FAILURES}"$'\n'"- ❌ Build"; fi
if [ "${{ needs.deploy-edgeone.result }}" = "failure" ]; then FAILURES="${FAILURES}"$'\n'"- ❌ Deploy EdgeOne"; fi
if [ "${{ needs.deploy-upyun.result }}" = "failure" ]; then FAILURES="${FAILURES}"$'\n'"- ❌ Deploy UpYun"; fi
if [ "${{ needs.finalize.result }}" = "failure" ]; then FAILURES="${FAILURES}"$'\n'"- ❌ Finalize"; fi
CURRENT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
MESSAGE="❌ **部署失败**
📦 项目: ${{ github.repository }}
🌿 分支: ${{ github.ref_name }}
🔗 Commit: ${{ github.sha }}
🕐 时间: $CURRENT_TIME
⚠️ 失败阶段:$FAILURES
🔍 查看详情: 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
FAILURES=""
if [ "${{ needs.pre-check.result }}" = "failure" ]; then FAILURES="${FAILURES}"$'\n'"- ❌ Pre-check"; fi
if [ "${{ needs.build.result }}" = "failure" ]; then FAILURES="${FAILURES}"$'\n'"- ❌ Build"; fi
if [ "${{ needs.deploy-edgeone.result }}" = "failure" ]; then FAILURES="${FAILURES}"$'\n'"- ❌ Deploy EdgeOne"; fi
if [ "${{ needs.deploy-upyun.result }}" = "failure" ]; then FAILURES="${FAILURES}"$'\n'"- ❌ Deploy UpYun"; fi
if [ "${{ needs.finalize.result }}" = "failure" ]; then FAILURES="${FAILURES}"$'\n'"- ❌ Finalize"; fi
CURRENT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
MESSAGE_TEXT="❌ **部署失败**"$'\n\n'"📦 项目: ${{ github.repository }}"$'\n'"🌿 分支: ${{ github.ref_name }}"$'\n'"🔗 Commit: ${{ github.sha }}"$'\n'"🕐 时间: ${CURRENT_TIME}"$'\n\n'"⚠️ 失败阶段:${FAILURES}"$'\n\n'"🔍 查看详情: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
-H "Content-Type: application/json" \
-d "{
\"msg_type\": \"text\",
\"content\": {
\"text\": \"${MESSAGE_TEXT}\"
}
}"