Files
blog/.github/workflows/deploy.yml
T

1108 lines
53 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 }}
duration: ${{ steps.end-build.outputs.duration }}
file_count: ${{ steps.stats.outputs.file_count }}
total_size: ${{ steps.stats.outputs.total_size }}
# 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: Upload build artifact (public/)
# 2026-10-04 起产物不再上传腾讯云 COS,改用 Gitea artifact 传递:
# - deploy-edgeone 用 actions/download-artifact 直接拿(同 runner,零跨境)
# - 国内中转机用 Gitea API 下载 artifact zip(替代原先的「从 COS 下载」)
# hash 写在 public/.build_hash 里随 artifact 一起走,中转机解压后读它判断是否同步。
uses: actions/upload-artifact@v4
with:
name: hugo-public
path: public/
retention-days: 7
- name: Report deploy status (built)
# 上报「构建完成、产物已进 artifact」到 api.200181.xyz 的部署状态接口,
# 中转机同步完成后会上报 phase=synced —— 打开
# https://api.200181.xyz/api/deploy-status 即可看到国内线路是否同步完。
# 需要 Gitea 仓库 Secrets 里配置 RSS_API_TOKEN = rss-robot-cf 的站点口令;
# 未配置时静默跳过上报(不影响部署)。
continue-on-error: true
env:
REPORT_TOKEN: ${{ secrets.RSS_API_TOKEN }}
run: |
if [ -z "$REPORT_TOKEN" ]; then
echo "ℹ️ 未配置 RSS_API_TOKEN,跳过部署状态上报"
exit 0
fi
BUILD_HASH="${{ steps.hash.outputs.build_hash }}"
curl -s --max-time 20 -X POST \
"https://api.200181.xyz/api/deploy-status?token=${REPORT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"hash\":\"${BUILD_HASH}\",\"phase\":\"built\"}" \
&& echo "✅ 已上报 built: ${BUILD_HASH:0:12}"
- name: Upload to UpYun
# ┌─────────────────────────────────────────────────────────────────┐
# │ 2026-10-03 起永久跳过海外直传:本 runner 在境外,对 UpYun v0 接口 │
# │ 无论 -w 10/--strong 还是 -w 3 均为 70 秒左右 EOF,3 轮重试全败, │
# │ 每次部署白耗 5 分钟(2026-10-03 build #25 日志实锤)。 │
# │ 国内线路由腾讯云广州中转机兜底(1Panel 计划任务「又拍云同步(国内 │
# │ 中转)」每分钟比对 COS build hash → 下载 355M → upx sync → │
# │ 又拍云 purge → 多吉云刷新,实测 5 分钟内完成且它自己会刷 CDN)。 │
# │ 将来若把 runner 挪到国内,把下面的 if: false 删掉即可恢复。 │
# └─────────────────────────────────────────────────────────────────┘
if: false
timeout-minutes: 30
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..."
# ⚠️ 参数是踩过坑调出来的,别改回 -w 10 / --strong:
# 本 runner 在海外,UpYun 的 v0 接口对并发敏感。2026-10-02 连续两次失败,
# 现象是一批目录的 mkdir 全部 EOF:
# sync .../public/20210212 → /20210212 FAIL mkdir: Post "https://v0.api.upyun.com/...": EOF
# -w 10 + --strong(每文件强校验)把请求量放大到 UpYun 扛不住的量级。
# 现在:并发 3、去掉 --strong(默认按 mtime/size 增量比对)、
# 单次超时放宽到 25 分钟、整轮重试 3 次(sync 可重复执行,已传的会跳过)。
UPYUN_OK=false
for attempt in 1 2 3; do
echo "⬆️ 上传尝试 $attempt/3(并发 3,无 --strong)"
if timeout 1500 "$UPX_BIN" sync public/ / -w 3 --delete; then
UPYUN_OK=true
break
fi
echo "⚠️ 第 $attempt/3 次失败(多为 EOF 瞬断),20 秒后重试"
sleep 20
done
if [ "$UPYUN_OK" != "true" ]; then
echo "❌ UpYun 连续 3 次上传失败,国内源站仍是旧内容"
exit 1
fi
timeout 60 "$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 }}
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: |
# 2026-10-04 起去掉「比对 last_hash 跳过未变化部署」的优化:
# last_hash 原本存 COS,砍 COS 后无处持久化(Gitea 缓存服务跨网段不可达)。
# 且 push 才触发构建,本就意味着有内容变化,直接部署即可。
echo "🔄 构建完成,部署到 EdgeOne"
echo "deployed=true" >> $GITHUB_OUTPUT
- name: Download build artifact
id: download-files
# 从 Gitea artifact 下载 public/(build job 上传的,同 runner 零跨境、零费用)
uses: actions/download-artifact@v4
with:
name: hugo-public
path: public/
- name: Start upload timing
id: start-edgeone-upload
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- name: Deploy to EdgeOne Pages
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
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
echo "## ⏱️ Deploy EdgeOne 耗时: ${duration}秒" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📊 部署统计" >> $GITHUB_STEP_SUMMARY
echo "- **上传部署**: ${duration}秒" >> $GITHUB_STEP_SUMMARY
echo "- **文件总数**: ${{ needs.build.outputs.file_count }}" >> $GITHUB_STEP_SUMMARY
# ============================================================
# 注意:这里原先有一个名为 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
# 多吉云 CDN 只服务国内线路,回源是又拍云。
# 2026-10-03 起海外直传 UpYun 已永久跳过,国内线路由中转机同步,
# sync.sh 同步完成后会自己刷多吉云 —— 这里保持 false 即可。
if [ "$UPYUN_STATUS" = "success" ]; then
echo "cdn_refresh=true" >> $GITHUB_OUTPUT
else
echo "cdn_refresh=false" >> $GITHUB_OUTPUT
echo "ℹ️ 国内线路由中转机同步(sync.sh 完成后会自行刷新多吉云),CI 侧跳过"
fi
- name: Refresh DogeCloud CDN
if: steps.deploy-status.outputs.cdn_refresh == '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_UPLOAD="${{ needs.deploy-edgeone.outputs.upload_duration }}"
EDGEONE_DETAIL=$(printf "\n 📤 上传: %ss" "$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_UPLOAD="${{ needs.deploy-edgeone.outputs.upload_duration }}"
EDGEONE_DETAIL=$(printf "\n 📤 上传: %ss" "$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
📤 上传部署: ${{ 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_UPLOAD="${{ needs.deploy-edgeone.outputs.upload_duration }}"
else
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_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}}')"
# ============================================================
# Job 7: 网络链路探测(仅手动触发,不改任何线上数据)
# ============================================================
# 目的:评估「境外 runner → 国内源站/CDN」各条链路的真实耗时与稳定性,
# 为「境外 EdgeOne + 境内多吉云」架构决策提供数据,而不是拍脑袋。
#
# 触发方式:workflow_dispatch(手动),绝不随 push/schedule 自动跑。
# 安全:只做连通性/耗时探测,不下载、不上传任何真实产物,不刷新 CDN。
#
# 探测项:
# A. 又拍云 API(v0.api.upyun.com) —— 境外直传又拍云的入口,已知会 EOF
# B. 又拍云对象存储上传探针(upx login) —— 登录握手能否成功(不传文件)
# C. 腾讯云 COS(cos.ap-*.myqcloud.com) —— 现状中转,作为稳定对照
# D. 多吉云 API(api.dogecloud.com) —— CDN 刷新 API 跨境响应耗时
# E. 多吉云 CDN 边缘节点 —— 国内加速域名的连通耗时
# ============================================================
probe-network:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
if: github.event_name == 'workflow_dispatch'
steps:
- name: 探测结果汇总头
run: |
echo "## 🌐 境外 Runner → 国内链路探测" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| 探测项 | 结果 | 耗时 |" >> $GITHUB_STEP_SUMMARY
echo "|--------|------|------|" >> $GITHUB_STEP_SUMMARY
- name: A. 又拍云 API 连通性
run: |
echo "📡 探测又拍云 v0 API..."
T0=$(date +%s%3N)
CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 10 --max-time 20 "https://v0.api.upyun.com/" 2>&1 || echo "FAIL")
T1=$(date +%s%3N)
DUR=$(( (T1 - T0) / 1000 ))
echo " HTTP $CODE,耗时 ${DUR}s"
echo "| 又拍云 v0 API | $([ "$CODE" = "200" ] || [ "$CODE" = "401" ] || [ "$CODE" = "403" ] && echo '✅ 可达' || echo '⚠️ $CODE') | ${DUR}s |" >> $GITHUB_STEP_SUMMARY
- name: B. 又拍云 upx 登录握手(不传文件)
env:
UPYUN_SERVICE: ${{ secrets.UPYUN_BUCKET }}
UPYUN_OPERATOR: ${{ secrets.UPYUN_OPERATOR }}
UPYUN_PASSWORD: ${{ secrets.UPYUN_PASSWORD }}
run: |
echo "📡 探测又拍云 upx 登录握手..."
if [ -z "$UPYUN_SERVICE" ] || [ -z "$UPYUN_OPERATOR" ] || [ -z "$UPYUN_PASSWORD" ]; then
echo " ⚠️ UpYun secrets 未配置,跳过"
echo "| upx 登录握手 | ⏭️ 无密钥 | — |" >> $GITHUB_STEP_SUMMARY
exit 0
fi
UPX_VERSION="0.4.9"
curl -fsSL --connect-timeout 10 --max-time 60 -o /tmp/upx.tar.gz \
"https://github.com/upyun/upx/releases/download/v${UPX_VERSION}/upx_${UPX_VERSION}_linux_amd64.tar.gz" \
&& tar -xzf /tmp/upx.tar.gz -C /tmp && mv /tmp/upx /tmp/upyun-upx && chmod +x /tmp/upyun-upx
T0=$(date +%s%3N)
if timeout 30 /tmp/upyun-upx login "$UPYUN_SERVICE" "$UPYUN_OPERATOR" "$UPYUN_PASSWORD" 2>&1; then
R="✅ 握手成功"
else
R="❌ 握手失败"
fi
T1=$(date +%s%3N)
DUR=$(( (T1 - T0) / 1000 ))
echo " $R,耗时 ${DUR}s"
echo "| upx 登录握手 | $R | ${DUR}s |" >> $GITHUB_STEP_SUMMARY
- name: C. 腾讯云 COS 连通性(现状对照)
env:
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
run: |
echo "📡 探测腾讯云 COS..."
if [ -z "$COS_BUCKET" ]; then
echo " ⚠️ COS secrets 未配置,跳过"
echo "| 腾讯云 COS | ⏭️ 无密钥 | — |" >> $GITHUB_STEP_SUMMARY
exit 0
fi
HOST="${COS_BUCKET}.cos.${COS_REGION}.myqcloud.com"
T0=$(date +%s%3N)
CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 10 --max-time 20 "https://${HOST}/" 2>&1 || echo "FAIL")
T1=$(date +%s%3N)
DUR=$(( (T1 - T0) / 1000 ))
echo " ${HOST} → HTTP $CODE,耗时 ${DUR}s"
echo "| 腾讯云 COS | $([ "$CODE" != "FAIL" ] && [ "$CODE" != "000" ] && echo '✅ 可达' || echo '⚠️ $CODE') | ${DUR}s |" >> $GITHUB_STEP_SUMMARY
- name: D. 多吉云 API 跨境响应
env:
DOGECLOUD_ACCESS_KEY: ${{ secrets.DOGECLOUD_ACCESS_KEY }}
DOGECLOUD_SECRET_KEY: ${{ secrets.DOGECLOUD_SECRET_KEY }}
run: |
echo "📡 探测多吉云 API(不带签名,只测连通+延迟)..."
T0=$(date +%s%3N)
CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 10 --max-time 20 "https://api.dogecloud.com/cdn/refresh/add.json" 2>&1 || echo "FAIL")
T1=$(date +%s%3N)
DUR=$(( (T1 - T0) / 1000 ))
echo " HTTP $CODE,耗时 ${DUR}s"
echo "| 多吉云 API | $([ "$CODE" != "FAIL" ] && [ "$CODE" != "000" ] && echo '✅ 可达' || echo '⚠️ $CODE') | ${DUR}s |" >> $GITHUB_STEP_SUMMARY
- name: E. 多吉云 CDN 边缘节点连通性
run: |
echo "📡 探测多吉云 CDN 边缘节点..."
# 用当前博客域名(国内加速走多吉云)测连通 + 耗时
T0=$(date +%s%3N)
CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 10 --max-time 20 "https://usj.cc/" 2>&1 || echo "FAIL")
T1=$(date +%s%3N)
DUR=$(( (T1 - T0) / 1000 ))
echo " usj.cc → HTTP $CODE,耗时 ${DUR}s"
echo "| 博客站点(多吉云加速) | $([ "$CODE" = "200" ] && echo '✅ 可达' || echo '⚠️ $CODE') | ${DUR}s |" >> $GITHUB_STEP_SUMMARY
- name: 探测结果汇总尾
run: |
echo "" >> $GITHUB_STEP_SUMMARY
echo "> 结论判读:若「upx 登录握手」与「又拍云 v0 API」耗时远高于「COS / 多吉云 API」," >> $GITHUB_STEP_SUMMARY
echo "> 说明境外直传又拍云仍不稳定,国内线路应继续走中转机兜底,不宜改成境外直传。" >> $GITHUB_STEP_SUMMARY