Files
blog/.github/workflows/deploy.yml
T
zqlit 80ea1ff5b3
Deploy to Production / pre-check (push) Successful in 2m6s
Deploy to Production / build (push) Successful in 4m18s
Deploy to Production / deploy-edgeone (push) Successful in 7s
Deploy to Production / finalize (push) Successful in 16s
Deploy to Production / notify-failure (push) Skipped
fix(ci): 删除空壳 deploy-upyun job,UpYun 直传改为如实上报三态
deploy-upyun 是零上传动作的空壳 job,却无条件打印「UpYun 已同步最新内容」,其输出被 Telegram / 飞书 / 邮件通知当成 UpYun 部署成功的依据;真正的上传步骤标了 continue-on-error,失败也不影响 job 颜色。三层叠加,导致 7/25 起国内线路停更两个多月而通知始终显示成功。

改动:删除 deploy-upyun(finalize / notify-failure 的 needs 同步清理);Upload to UpYun 在退出时写 GITHUB_OUTPUT,如实上报 success / failure / skipped 三态与真实耗时(不用 steps.outcome,只依赖最通用的 output 传递);通知与 Step Summary 改为展示三态真实结果,移除「文件已在 Build 阶段同步」这类未经验证的说法。

又拍云直传失败仍不阻断流水线 —— 国内线路由腾讯云广州的 1Panel 计划任务「又拍云同步(国内中转)」每分钟兜底增量推送。
2026-10-01 16:47:05 +08:00

1040 lines
48 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.decide.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
# 注意:commit message 是用户可控的多行文本,必须经 env 传入。
# 直接写 ${{ github.event.head_commit.message }} 会把内容展开进脚本,
# 多行信息或含 $ / ` 的字符会导致 "bad substitution" 甚至命令注入。
env:
COMMIT_AUTHOR: ${{ github.event.head_commit.author.name }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
run: |
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: Decide whether to deploy
id: decide
run: |
# 基础决策来自仓库校验
CAN_DEPLOY="${{ steps.repo-check.outputs.can_deploy }}"
echo "📌 仓库校验结果: can_deploy=${CAN_DEPLOY}"
# 定时触发:若 1 小时内无新提交则跳过,避免重复构建
if [ "${{ github.event_name }}" = "schedule" ]; then
LAST_COMMIT_TIME=$(git log -1 --format=%ct)
CURRENT_TIME=$(date +%s)
DIFF_HOURS=$(( (CURRENT_TIME - LAST_COMMIT_TIME) / 3600 ))
echo "⏰ 最后一次提交时间: $(date -d @$LAST_COMMIT_TIME)"
echo "⏰ 当前时间: $(date)"
echo "⏰ 距离上次提交: ${DIFF_HOURS} 小时"
if [ "$DIFF_HOURS" -ge 1 ]; then
echo "⏭️ 超过 1 小时没有新提交,跳过定时构建"
CAN_DEPLOY="false"
else
echo "✅ 最近有提交,继续构建"
fi
fi
echo "can_deploy=${CAN_DEPLOY}" >> $GITHUB_OUTPUT
echo "## 🚦 部署决策: can_deploy=${CAN_DEPLOY}" >> $GITHUB_STEP_SUMMARY
- 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 }}
last_hash: ${{ steps.last-hash.outputs.last_hash }}
duration: ${{ steps.end-build.outputs.duration }}
file_count: ${{ steps.stats.outputs.file_count }}
total_size: ${{ steps.stats.outputs.total_size }}
need_upload: ${{ steps.check-upload.outputs.need_upload }}
# Upload to UpYun 的真实三态:success / failure / skipped(未执行)。
# 该步骤标了 continue-on-error,conclusion 恒为 success,不能用来判断成败。
# 这里刻意不用 steps.<id>.outcome,而是让步骤自己在退出时写 GITHUB_OUTPUT,
# 只依赖最通用的 output 传递机制,GitHub / Gitea 两边都稳。
upyun_status: ${{ steps.upload.outputs.upyun_status || 'skipped' }}
upyun_duration: ${{ steps.upload.outputs.duration }}
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
# Hugo 的 lastmod 用了 :git,必须保留完整提交历史(commit/tree),
# 但历史版本的文件内容(blob,多为图片)不需要。
# partial clone 只按需拉取当前工作区的 blob,实测本仓库可省约 200MB 传输。
filter: blob:none
- 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'
# Gitea 的 runner 缓存服务与 job 容器跨 bridge 网络不可达,
# 每次 restore/save 各死等约 4 分半后 ETIMEDOUT(纯浪费约 9 分钟),
# 因此仅在 GitHub 上启用缓存;Gitea 上传空值让 setup-node 跳过缓存。
cache: ${{ github.server_url == 'https://github.com' && 'npm' || '' }}
- name: Install Node.js dependencies
run: npm ci --no-audit --no-fund
- name: Cache Hugo resources
# 同 Setup Node:Gitea 环境缓存服务不可达,跳过以免每次白等
if: github.server_url == 'https://github.com'
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: bash
run: python3 scripts/add_draft_to_hidden.py
- 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 resources/_gen && hugo --minify --gc
- name: Generate build hash
id: hash
run: |
BUILD_HASH=$(find public -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum | awk '{print $1}')
echo "build_hash=${BUILD_HASH}" >> $GITHUB_OUTPUT
echo "$BUILD_HASH" > public/.build_hash
- 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: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install coscmd
run: pip install -U coscmd
- name: Configure coscmd
env:
COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }}
COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
run: coscmd config -a $COS_SECRET_ID -s $COS_SECRET_KEY -b $COS_BUCKET -r $COS_REGION -m 30
- name: Get last build hash from COS
id: last-hash
continue-on-error: true
run: |
echo "🔍 从腾讯云 COS 获取上次部署的 hash..."
coscmd download /__build_hash /tmp/last_hash 2>/dev/null || echo ""
LAST_HASH=$(cat /tmp/last_hash 2>/dev/null || echo "")
if [ -z "$LAST_HASH" ]; then
echo "⚠️ 没有找到上次部署的 hash(首次部署)"
else
echo "上次部署 hash: $LAST_HASH"
fi
echo "last_hash=${LAST_HASH}" >> $GITHUB_OUTPUT
- name: Check if upload is needed
id: check-upload
run: |
LAST_HASH="${{ steps.last-hash.outputs.last_hash }}"
CURRENT_HASH="${{ steps.hash.outputs.build_hash }}"
echo "上次部署 hash: $LAST_HASH"
echo "当前构建 hash: $CURRENT_HASH"
if [ -z "$LAST_HASH" ] || [ "$CURRENT_HASH" != "$LAST_HASH" ]; then
echo "🔄 构建产物有变化,需要上传"
echo "need_upload=true" >> $GITHUB_OUTPUT
else
echo "✅ 构建产物无变化,跳过上传"
echo "need_upload=false" >> $GITHUB_OUTPUT
fi
- name: Upload to Tencent COS
if: steps.check-upload.outputs.need_upload == 'true'
continue-on-error: true
run: |
echo "📤 打包并上传到腾讯云 COS..."
if command -v zstd; then
tar -cf - public/ | zstd -1 -T0 -o public.tar.zst
coscmd upload public.tar.zst /public.tar.zst
else
tar -czf public.tar.gz public/
coscmd upload public.tar.gz /public.tar.gz
fi
coscmd upload public/.build_hash /__build_hash
- name: Upload hash to COS (even if skipped)
if: steps.check-upload.outputs.need_upload == 'false'
run: |
echo "📤 更新 hash 文件到腾讯云 COS..."
coscmd upload public/.build_hash /__build_hash
- name: Upload to UpYun
if: steps.check-upload.outputs.need_upload == 'true'
timeout-minutes: 10
env:
UPYUN_SERVICE: ${{ secrets.UPYUN_BUCKET }}
UPYUN_OPERATOR: ${{ secrets.UPYUN_OPERATOR }}
UPYUN_PASSWORD: ${{ secrets.UPYUN_PASSWORD }}
id: upload
# 直传失败不阻断整条流水线 —— 国内线路已由腾讯云广州的中转机兜底
# (1Panel 计划任务「又拍云同步(国内中转)」每分钟比对 COS 上的 build hash,
# 有变化才增量推送)。但失败必须被如实上报,绝不能再伪装成成功。
continue-on-error: true
run: |
echo "📤 上传构建产物到 UpYun..."
START_TS=$(date +%s)
# 无论成功、失败还是中途退出,都在退出时如实上报结果与耗时。
# 这是"不再假绿"的核心:过去无论传没传上去,通知都显示成功。
on_exit() {
rc=$?
if [ "$rc" -eq 0 ]; then
echo "upyun_status=success" >> $GITHUB_OUTPUT
else
echo "upyun_status=failure" >> $GITHUB_OUTPUT
fi
echo "duration=$(($(date +%s) - START_TS))" >> $GITHUB_OUTPUT
}
trap on_exit EXIT
UPX_VERSION="0.4.9"
UPX_BIN="/tmp/upyun-upx"
# 主源用 GitHub Releases(实测 0.5s 稳定),
# 官方 collection.b0.upaiyun.com 实测 2/3 概率整站无响应,仅作兜底
UPX_URLS=(
"https://github.com/upyun/upx/releases/download/v${UPX_VERSION}/upx_${UPX_VERSION}_linux_amd64.tar.gz"
"https://collection.b0.upaiyun.com/softwares/upx/upx_${UPX_VERSION}_linux_amd64.tar.gz"
)
if [ ! -x "$UPX_BIN" ]; then
ok=false
for U in "${UPX_URLS[@]}"; do
echo "⬇️ 下载 upx: $U"
if curl -fsSL --connect-timeout 10 --max-time 60 --retry 2 --retry-delay 2 \
-o /tmp/upx.tar.gz "$U"; then
if tar -xzf /tmp/upx.tar.gz -C /tmp && [ -f /tmp/upx ]; then
mv /tmp/upx "$UPX_BIN" && chmod +x "$UPX_BIN" && ok=true
echo "✅ upx 就绪: $("$UPX_BIN" --version 2>&1 | head -1)"
break
fi
fi
echo "⚠️ 该源失败,尝试下一个..."
done
if [ "$ok" != "true" ]; then
echo "❌ 所有 upx 下载源均失败,跳过 UpYun 上传"
exit 1
fi
fi
for i in 1 2 3 4 5; do
echo "🔐 UpYun 登录 (尝试 $i/5)..."
if timeout 30 "$UPX_BIN" login "$UPYUN_SERVICE" "$UPYUN_OPERATOR" "$UPYUN_PASSWORD" 2>&1; then
echo "✅ 登录成功"
break
else
echo "⚠️ 登录失败,等待 10 秒后重试..."
sleep 10
fi
done
echo "🔄 开始上传到 UpYun..."
timeout 480 "$UPX_BIN" sync public/ / -w 10 --delete --strong
timeout 30 "$UPX_BIN" put public/.build_hash /__build_hash
echo "✅ UpYun 上传完成"
- 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
if [ "${{ github.server_url }}" = "https://github.com" ]; then
echo "- **Hugo 缓存**: ${{ steps.hugo-cache.outputs.cache-hit == 'true' && '✅ 命中' || '🔄 未命中' }}" >> $GITHUB_STEP_SUMMARY
else
echo "- **Hugo 缓存**: ⏭️ 已跳过 (Gitea 缓存服务跨网段不可达)" >> $GITHUB_STEP_SUMMARY
fi
# ============================================================
# Job 3: Deploy to EdgeOne Pages (并行)
# ============================================================
deploy-edgeone:
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
needs: build
outputs:
deployed: ${{ steps.check.outputs.deployed }}
duration: ${{ steps.end-edgeone.outputs.duration }}
download_duration: ${{ steps.download-files.outputs.download_duration }}
upload_duration: ${{ steps.end-edgeone-upload.outputs.duration }}
steps:
- name: Start timing
id: start-edgeone
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- name: Check if deployment is needed
id: check
run: |
CURRENT_HASH="${{ needs.build.outputs.build_hash }}"
LAST_HASH="${{ needs.build.outputs.last_hash }}"
echo "当前 build_hash: $CURRENT_HASH"
echo "上次部署 hash: $LAST_HASH"
if [ -z "$LAST_HASH" ] || [ "$CURRENT_HASH" != "$LAST_HASH" ]; then
echo "🔄 构建产物有变化,需要部署到 EdgeOne"
echo "deployed=true" >> $GITHUB_OUTPUT
else
echo "✅ 构建产物无变化,跳过 EdgeOne 部署"
echo "deployed=false" >> $GITHUB_OUTPUT
fi
- name: Setup Python
if: steps.check.outputs.deployed == 'true'
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install coscmd
if: steps.check.outputs.deployed == 'true'
run: pip install -U coscmd
- name: Configure coscmd
if: steps.check.outputs.deployed == 'true'
env:
COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }}
COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
run: coscmd config -a $COS_SECRET_ID -s $COS_SECRET_KEY -b $COS_BUCKET -r $COS_REGION
- name: Download files from COS
if: steps.check.outputs.deployed == 'true'
id: download-files
run: |
echo "📥 从腾讯云 COS 下载构建产物..."
DOWNLOAD_START=$(date +%s)
if command -v zstd; then
coscmd download /public.tar.zst /tmp/public.tar.zst
tar -I zstd -xf /tmp/public.tar.zst
else
coscmd download /public.tar.gz /tmp/public.tar.gz
tar -xzf /tmp/public.tar.gz
fi
DOWNLOAD_END=$(date +%s)
DOWNLOAD_DURATION=$((DOWNLOAD_END - DOWNLOAD_START))
echo "download_duration=${DOWNLOAD_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: |
# 内联 scripts/deploy_edgeone.sh —— 本步骤只需一个部署命令,
# 原先为此 checkout 整个仓库要花约 81 秒,不值得。
if [ -z "$EDGEONE_API_TOKEN" ]; then
echo "Error: EDGEONE_API_TOKEN is not set."
exit 1
fi
echo "Deploying to EdgeOne Pages..."
echo "Project: hugo-blog | Directory: ./public | Area: overseas"
# 国际站必须加 --area overseas
npx edgeone pages deploy ./public -n hugo-blog -t "$EDGEONE_API_TOKEN" --area 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: 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
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📊 部署统计" >> $GITHUB_STEP_SUMMARY
echo "- **下载: ${{ steps.download-files.outputs.download_duration }}s**" >> $GITHUB_STEP_SUMMARY
echo "- **上传部署**: ${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
# ============================================================
# 注意:这里原先有一个名为 deploy-upyun 的 job —— 它是纯空壳:
# 零上传动作,却无条件打印「UpYun 已同步最新内容」,且它的输出被
# Telegram / 飞书 / 邮件通知当成"UpYun 部署成功"的依据。
# 因它持续掩盖真实故障(7/25 起国内线路停更两个多月),已于 2026-10-01 删除。
# 又拍云的真实状态现在来自 build job 的 upyun_status 输出。
# ============================================================
# ============================================================
# Job 5: CDN Refresh & Notifications (after all deployments)
# ============================================================
finalize:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
needs: [pre-check, build, deploy-edgeone]
if: always() && needs.build.result == 'success' && needs.deploy-edgeone.result == 'success'
steps:
- name: Start timing
id: start-finalize
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22'
# 本 job 不再安装 npm 依赖(CDN 刷新脚本只用 node 内置模块),
# 因此不启用 npm 缓存 —— 也免得在没有 package-lock.json 时算缓存 key 出错。
- name: Determine deployment status
id: deploy-status
run: |
EDGEONE_DEPLOYED="${{ needs.deploy-edgeone.outputs.deployed }}"
UPYUN_STATUS="${{ needs.build.outputs.upyun_status }}"
echo "EdgeOne deployed: $EDGEONE_DEPLOYED"
echo "UpYun status: $UPYUN_STATUS"
if [ "$EDGEONE_DEPLOYED" = "true" ] || [ "$UPYUN_STATUS" = "success" ]; 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: |
# 内联 scripts/refresh_cdn.js(修改该文件时需同步这里)。
# 该脚本零外部依赖 —— 仅用 node 内置的 crypto 与 fetch ——
# 因此不必 checkout 整个仓库、也不必 npm ci(原先这两步合计约 211 秒)。
cat > /tmp/refresh_cdn.js <<'REFRESH_CDN_EOF'
const crypto = require("crypto");
async function dogecloudApi(apiPath, data = {}) {
const accessKey = process.env.DOGECLOUD_ACCESS_KEY;
const secretKey = process.env.DOGECLOUD_SECRET_KEY;
if (!accessKey || !secretKey) {
console.error("Error: DOGECLOUD_ACCESS_KEY or DOGECLOUD_SECRET_KEY not set.");
process.exit(1);
}
const body = JSON.stringify(data);
const signStr = apiPath + "\n" + body;
const sign = crypto.createHmac("sha1", secretKey).update(signStr).digest("hex");
const authorization = `TOKEN ${accessKey}:${sign}`;
const res = await fetch(`https://api.dogecloud.com${apiPath}`, {
method: "POST",
headers: {
Authorization: authorization,
"Content-Type": "application/json",
},
body,
});
return res.json();
}
async function main() {
const cdnUrlListStr = process.env.CDN_URL_LIST;
if (!cdnUrlListStr) {
console.log("Warning: CDN_URL_LIST not found or empty.");
return;
}
const urlList = cdnUrlListStr
.replace(/\n/g, ",")
.split(",")
.map((u) => u.trim())
.filter(Boolean);
if (urlList.length === 0) {
console.log("No URLs to refresh.");
return;
}
console.log(`Refreshing ${urlList.length} URLs...`);
const result = await dogecloudApi("/cdn/refresh/add.json", {
rtype: "path",
urls: JSON.stringify(urlList),
});
if (result && result.code === 200) {
console.log(`Refresh task submitted. Task ID: ${result.data.task_id}`);
} else {
console.error(`Failed to submit refresh task. Response: ${JSON.stringify(result)}`);
process.exit(1);
}
}
main();
REFRESH_CDN_EOF
node /tmp/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.build.outputs.upyun_duration }}"
UPYUN_STATUS_RAW="${{ needs.build.outputs.upyun_status }}"
FILE_COUNT="${{ needs.build.outputs.file_count }}"
TOTAL_SIZE="${{ needs.build.outputs.total_size }}"
EDGEONE_DETAIL=""
if [ "$EDGEONE_DEPLOYED" = "true" ]; then
EDGEONE_DOWNLOAD="${{ needs.deploy-edgeone.outputs.download_duration }}"
EDGEONE_UPLOAD="${{ needs.deploy-edgeone.outputs.upload_duration }}"
EDGEONE_DETAIL=$(printf "\n 📥 下载: %ss\n 📤 上传: %ss" "$EDGEONE_DOWNLOAD" "$EDGEONE_UPLOAD")
fi
# 三态映射:直传结果如实呈现,不再有"文件已在 Build 阶段同步"这种没验证过的说辞
case "$UPYUN_STATUS_RAW" in
success) UPYUN_TEXT="✅ 已直传";;
failure) UPYUN_TEXT="❌ 直传失败(国内中转机兜底)";;
skipped) UPYUN_TEXT="⏭️ 跳过(无变化)";;
*) UPYUN_TEXT="❔ 未执行";;
esac
if [ -n "$UPYUN_DUR" ]; then UPYUN_DUR_TEXT="${UPYUN_DUR}s"; else UPYUN_DUR_TEXT="—"; fi
if [ "${{ steps.deploy-status.outputs.any_deployed }}" = "true" ]; then
STATUS_ICON="🎉"
STATUS_TEXT="部署成功"
else
STATUS_ICON="⏭️"
STATUS_TEXT="无变化,跳过部署"
fi
EDGEONE_STATUS=$([ "$EDGEONE_DEPLOYED" = "true" ] && echo "✅ 已部署" || echo "⏭️ 跳过")
CURRENT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
MESSAGE="$STATUS_ICON *$STATUS_TEXT*"
MESSAGE="$MESSAGE"$'\n\n'
MESSAGE="$MESSAGE""📦 *项目*: \`${{ github.repository }}\`"$'\n'
MESSAGE="$MESSAGE""🌿 *分支*: \`${{ github.ref_name }}\`"$'\n'
MESSAGE="$MESSAGE""🔗 *Commit*: \`${{ github.sha }}\`"$'\n'
MESSAGE="$MESSAGE""⏰ *时间*: $CURRENT_TIME"$'\n'
MESSAGE="$MESSAGE""🚀 *触发方式*: ${{ github.event_name }}"$'\n\n'
MESSAGE="$MESSAGE""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE="$MESSAGE""📊 *构建统计*"$'\n'
MESSAGE="$MESSAGE""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE="$MESSAGE""📁 文件总数: $FILE_COUNT"$'\n'
MESSAGE="$MESSAGE""💾 总大小: $TOTAL_SIZE"$'\n\n'
MESSAGE="$MESSAGE""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE="$MESSAGE""⏱️ *耗时统计*"$'\n'
MESSAGE="$MESSAGE""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE="$MESSAGE""🔍 Pre-check: ${PRECHECK_DUR}s"$'\n'
MESSAGE="$MESSAGE""🏗️ Build: ${BUILD_DUR}s"$'\n'
MESSAGE="$MESSAGE""🌐 EdgeOne: ${EDGEONE_DUR}s$EDGEONE_DETAIL"$'\n'
MESSAGE="$MESSAGE""☁️ UpYun 直传: ${UPYUN_DUR_TEXT}"$'\n\n'
MESSAGE="$MESSAGE""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE="$MESSAGE""📌 *部署状态*"$'\n'
MESSAGE="$MESSAGE""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE="$MESSAGE""🌐 EdgeOne Pages: $EDGEONE_STATUS"$'\n'
MESSAGE="$MESSAGE""☁️ UpYun: $UPYUN_TEXT"$'\n\n'
if [ "${{ github.server_url }}" = "https://github.com" ]; then
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
else
RUN_URL="http://23.254.236.47:3001/${{ github.repository }}/actions/runs/${{ github.run_id }}"
fi
MESSAGE="$MESSAGE""🔗 [查看详情]($RUN_URL)"
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" \
-d disable_web_page_preview=true
- 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.build.outputs.upyun_duration }}"
UPYUN_STATUS_RAW="${{ needs.build.outputs.upyun_status }}"
FILE_COUNT="${{ needs.build.outputs.file_count }}"
TOTAL_SIZE="${{ needs.build.outputs.total_size }}"
EDGEONE_DETAIL=""
if [ "$EDGEONE_DEPLOYED" = "true" ]; then
EDGEONE_DOWNLOAD="${{ needs.deploy-edgeone.outputs.download_duration }}"
EDGEONE_UPLOAD="${{ needs.deploy-edgeone.outputs.upload_duration }}"
EDGEONE_DETAIL=$(printf "\n 📥 下载: %ss\n 📤 上传: %ss" "$EDGEONE_DOWNLOAD" "$EDGEONE_UPLOAD")
fi
# 三态映射:直传结果如实呈现,不再有"文件已在 Build 阶段同步"这种没验证过的说辞
case "$UPYUN_STATUS_RAW" in
success) UPYUN_TEXT="✅ 已直传";;
failure) UPYUN_TEXT="❌ 直传失败(国内中转机兜底)";;
skipped) UPYUN_TEXT="⏭️ 跳过(无变化)";;
*) UPYUN_TEXT="❔ 未执行";;
esac
if [ -n "$UPYUN_DUR" ]; then UPYUN_DUR_TEXT="${UPYUN_DUR}s"; else UPYUN_DUR_TEXT="—"; fi
if [ "${{ steps.deploy-status.outputs.any_deployed }}" = "true" ]; then
STATUS_ICON="🎉"
STATUS_TEXT="部署成功"
else
STATUS_ICON="⏭️"
STATUS_TEXT="无变化,跳过部署"
fi
EDGEONE_STATUS=$([ "$EDGEONE_DEPLOYED" = "true" ] && echo "✅ 已部署" || echo "⏭️ 跳过")
CURRENT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
MESSAGE_TEXT="$STATUS_ICON $STATUS_TEXT"
MESSAGE_TEXT="$MESSAGE_TEXT"$'\n\n'
MESSAGE_TEXT="$MESSAGE_TEXT""📦 项目: ${{ github.repository }}"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🌿 分支: ${{ github.ref_name }}"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🔗 Commit: ${{ github.sha }}"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""⏰ 时间: $CURRENT_TIME"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🚀 触发方式: ${{ github.event_name }}"$'\n\n'
MESSAGE_TEXT="$MESSAGE_TEXT""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""📊 构建统计"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""📁 文件总数: $FILE_COUNT"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""💾 总大小: $TOTAL_SIZE"$'\n\n'
MESSAGE_TEXT="$MESSAGE_TEXT""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""⏱️ 耗时统计"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🔍 Pre-check: ${PRECHECK_DUR}s"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🏗️ Build: ${BUILD_DUR}s"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🌐 EdgeOne: ${EDGEONE_DUR}s$EDGEONE_DETAIL"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""☁️ UpYun 直传: ${UPYUN_DUR_TEXT}"$'\n\n'
MESSAGE_TEXT="$MESSAGE_TEXT""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""📌 部署状态"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🌐 EdgeOne Pages: $EDGEONE_STATUS"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""☁️ UpYun: $UPYUN_TEXT"$'\n\n'
if [ "${{ github.server_url }}" = "https://github.com" ]; then
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
else
RUN_URL="http://23.254.236.47:3001/${{ github.repository }}/actions/runs/${{ github.run_id }}"
fi
MESSAGE_TEXT="$MESSAGE_TEXT""🔗 查看详情: $RUN_URL"
echo "📤 发送飞书通知..."
echo "Message length: ${#MESSAGE_TEXT}"
RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" -X POST "$FEISHU_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg text "$MESSAGE_TEXT" '{"msg_type":"text","content":{"text":$text}}')")
echo "飞书响应: $RESPONSE"
- 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
📥 下载 COS: ${{ needs.deploy-edgeone.outputs.download_duration }}s
📤 上传部署: ${{ needs.deploy-edgeone.outputs.upload_duration }}s
☁️ UpYun 直传: ${{ needs.build.outputs.upyun_status == 'success' && '✅ 成功' || (needs.build.outputs.upyun_status == 'skipped' && '⏭️ 跳过(无变化)' || '❌ 失败(国内中转机兜底)') }}
📌 部署状态
──────────────────────────────────
🌐 EdgeOne Pages: ${{ needs.deploy-edgeone.outputs.deployed == 'true' && '✅ 已部署' || '⏭️ 跳过(无变化)' }}
☁️ UpYun: ${{ needs.build.outputs.upyun_status == 'success' && '✅ 已直传' || (needs.build.outputs.upyun_status == 'skipped' && '⏭️ 跳过(无变化)' || '❌ 直传失败(中转机兜底)') }}
🔗 查看详情
──────────────────────────────────
${{ github.server_url == 'https://github.com' && 'https://github.com/zqlit/blog' || 'http://23.254.236.47:3001/zqlit/blog' }}/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.build.outputs.upyun_duration }}"
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
UPYUN_DUR_CELL="${UPYUN_DUR}s"; [ -z "$UPYUN_DUR" ] && UPYUN_DUR_CELL="—"
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_CELL} | - | ${UPYUN_DUR_CELL} |" >> $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.build.outputs.upyun_status == 'success' && '✅ 成功' || (needs.build.outputs.upyun_status == 'skipped' && '⏭️ 跳过(无变化)' || '❌ 失败(中转机兜底)') }} |" >> $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, finalize]
if: always() && (needs.pre-check.result == 'failure' || needs.build.result == 'failure' || needs.deploy-edgeone.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=$(printf "%s\n 🔴 Pre-check" "$FAILURES"); fi
if [ "${{ needs.build.result }}" = "failure" ]; then FAILURES=$(printf "%s\n 🔴 Build" "$FAILURES"); fi
if [ "${{ needs.deploy-edgeone.result }}" = "failure" ]; then FAILURES=$(printf "%s\n 🔴 Deploy EdgeOne" "$FAILURES"); fi
if [ "${{ needs.build.outputs.upyun_status }}" = "failure" ]; then FAILURES=$(printf "%s\n ⚠️ UpYun 直传失败(不阻断,国内中转机兜底)" "$FAILURES"); fi
if [ "${{ needs.finalize.result }}" = "failure" ]; then FAILURES=$(printf "%s\n 🔴 Finalize" "$FAILURES"); fi
PRECHECK_STATUS=$([ "${{ needs.pre-check.result }}" = "success" ] && echo "✅ 成功" || ([ "${{ needs.pre-check.result }}" = "failure" ] && echo "❌ 失败" || echo "⏭️ 跳过"))
BUILD_STATUS=$([ "${{ needs.build.result }}" = "success" ] && echo "✅ 成功" || ([ "${{ needs.build.result }}" = "failure" ] && echo "❌ 失败" || echo "⏭️ 跳过"))
EDGEONE_STATUS=$([ "${{ needs.deploy-edgeone.result }}" = "success" ] && echo "✅ 成功" || ([ "${{ needs.deploy-edgeone.result }}" = "failure" ] && echo "❌ 失败" || echo "⏭️ 跳过"))
UPYUN_STATUS=$([ "${{ needs.build.outputs.upyun_status }}" = "success" ] && echo "✅ 直传成功" || ([ "${{ needs.build.outputs.upyun_status }}" = "failure" ] && echo "❌ 直传失败(中转兜底)" || echo "⏭️ 跳过(无变化)"))
FINALIZE_STATUS=$([ "${{ needs.finalize.result }}" = "success" ] && echo "✅ 成功" || ([ "${{ needs.finalize.result }}" = "failure" ] && echo "❌ 失败" || echo "⏭️ 跳过"))
CURRENT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
MESSAGE="💥 *部署失败*"
MESSAGE="$MESSAGE"$'\n\n'
MESSAGE="$MESSAGE""📦 *项目*: \`${{ github.repository }}\`"$'\n'
MESSAGE="$MESSAGE""🌿 *分支*: \`${{ github.ref_name }}\`"$'\n'
MESSAGE="$MESSAGE""🔗 *Commit*: \`${{ github.sha }}\`"$'\n'
MESSAGE="$MESSAGE""⏰ *时间*: $CURRENT_TIME"$'\n'
MESSAGE="$MESSAGE""🚀 *触发方式*: ${{ github.event_name }}"$'\n\n'
MESSAGE="$MESSAGE""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE="$MESSAGE""⚠️ *失败阶段*"$'\n'
MESSAGE="$MESSAGE""━━━━━━━━━━━━━━━━━━━━━━$FAILURES"$'\n\n'
MESSAGE="$MESSAGE""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE="$MESSAGE""📊 *各阶段状态*"$'\n'
MESSAGE="$MESSAGE""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE="$MESSAGE""🔍 Pre-check: $PRECHECK_STATUS"$'\n'
MESSAGE="$MESSAGE""🏗️ Build: $BUILD_STATUS"$'\n'
MESSAGE="$MESSAGE""🌐 Deploy EdgeOne: $EDGEONE_STATUS"$'\n'
MESSAGE="$MESSAGE""☁️ UpYun 直传: $UPYUN_STATUS"$'\n'
MESSAGE="$MESSAGE""📤 Finalize: $FINALIZE_STATUS"$'\n\n'
if [ "${{ github.server_url }}" = "https://github.com" ]; then
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
else
RUN_URL="http://23.254.236.47:3001/${{ github.repository }}/actions/runs/${{ github.run_id }}"
fi
MESSAGE="$MESSAGE""🔗 [查看详情]($RUN_URL)"
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" \
-d disable_web_page_preview=true
- 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=$(printf "%s\n 🔴 Pre-check" "$FAILURES"); fi
if [ "${{ needs.build.result }}" = "failure" ]; then FAILURES=$(printf "%s\n 🔴 Build" "$FAILURES"); fi
if [ "${{ needs.deploy-edgeone.result }}" = "failure" ]; then FAILURES=$(printf "%s\n 🔴 Deploy EdgeOne" "$FAILURES"); fi
if [ "${{ needs.build.outputs.upyun_status }}" = "failure" ]; then FAILURES=$(printf "%s\n ⚠️ UpYun 直传失败(不阻断,国内中转机兜底)" "$FAILURES"); fi
if [ "${{ needs.finalize.result }}" = "failure" ]; then FAILURES=$(printf "%s\n 🔴 Finalize" "$FAILURES"); fi
PRECHECK_STATUS=$([ "${{ needs.pre-check.result }}" = "success" ] && echo "✅ 成功" || ([ "${{ needs.pre-check.result }}" = "failure" ] && echo "❌ 失败" || echo "⏭️ 跳过"))
BUILD_STATUS=$([ "${{ needs.build.result }}" = "success" ] && echo "✅ 成功" || ([ "${{ needs.build.result }}" = "failure" ] && echo "❌ 失败" || echo "⏭️ 跳过"))
EDGEONE_STATUS=$([ "${{ needs.deploy-edgeone.result }}" = "success" ] && echo "✅ 成功" || ([ "${{ needs.deploy-edgeone.result }}" = "failure" ] && echo "❌ 失败" || echo "⏭️ 跳过"))
UPYUN_STATUS=$([ "${{ needs.build.outputs.upyun_status }}" = "success" ] && echo "✅ 直传成功" || ([ "${{ needs.build.outputs.upyun_status }}" = "failure" ] && echo "❌ 直传失败(中转兜底)" || echo "⏭️ 跳过(无变化)"))
FINALIZE_STATUS=$([ "${{ needs.finalize.result }}" = "success" ] && echo "✅ 成功" || ([ "${{ needs.finalize.result }}" = "failure" ] && echo "❌ 失败" || echo "⏭️ 跳过"))
CURRENT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
MESSAGE_TEXT="💥 部署失败"
MESSAGE_TEXT="$MESSAGE_TEXT"$'\n\n'
MESSAGE_TEXT="$MESSAGE_TEXT""📦 项目: ${{ github.repository }}"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🌿 分支: ${{ github.ref_name }}"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🔗 Commit: ${{ github.sha }}"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""⏰ 时间: $CURRENT_TIME"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🚀 触发方式: ${{ github.event_name }}"$'\n\n'
MESSAGE_TEXT="$MESSAGE_TEXT""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""⚠️ 失败阶段"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""━━━━━━━━━━━━━━━━━━━━━━$FAILURES"$'\n\n'
MESSAGE_TEXT="$MESSAGE_TEXT""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""📊 各阶段状态"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""━━━━━━━━━━━━━━━━━━━━━━"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🔍 Pre-check: $PRECHECK_STATUS"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🏗️ Build: $BUILD_STATUS"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""🌐 Deploy EdgeOne: $EDGEONE_STATUS"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""☁️ UpYun 直传: $UPYUN_STATUS"$'\n'
MESSAGE_TEXT="$MESSAGE_TEXT""📤 Finalize: $FINALIZE_STATUS"$'\n\n'
if [ "${{ github.server_url }}" = "https://github.com" ]; then
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
else
RUN_URL="http://23.254.236.47:3001/${{ github.repository }}/actions/runs/${{ github.run_id }}"
fi
MESSAGE_TEXT="$MESSAGE_TEXT""🔗 查看详情: $RUN_URL"
curl -s -X POST "$FEISHU_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg text "$MESSAGE_TEXT" '{"msg_type":"text","content":{"text":$text}}')"