Deploy to Production / probe-network (push) Skipped
Deploy to Production / pre-check (push) Successful in 1m9s
Deploy to Production / build (push) Failing after 39m20s
Deploy to Production / deploy-edgeone (push) Skipped
Deploy to Production / finalize (push) Skipped
Deploy to Production / notify-failure (push) Successful in 5s
实测 run #55:build job 在「Upload build artifact (public/)」失败: ::error::@actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ are not currently supported on GHES. Gitea 被 v4 识别为 GHES、未实现新 artifact API → 降回 v3(走旧 API)。 v3 无 include-hidden-files 选项、隐藏文件不保证进包,故: - build job 额外写非隐藏的 public/build_hash.txt 兜底 - 中转机 sync.sh 优先读 .build_hash,读不到退 build_hash.txt,最后才本地复算
1141 lines
55 KiB
YAML
1141 lines
55 KiB
YAML
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
|
||
# 写两份:
|
||
# .build_hash —— 历史沿用(也用于 upx put 到又拍云 /__build_hash)
|
||
# build_hash.txt —— 非隐藏。upload-artifact(v3/v4)对「以 . 开头的隐藏
|
||
# 文件」处理不一致,可能整个丢掉,非隐藏文件才稳。
|
||
# 中转机优先读 .build_hash,读不到退到 build_hash.txt,都没有才本地复算。
|
||
# 注意:本步的 find 在写这两个文件之前执行,所以 hash 不含它们自身。
|
||
echo "$BUILD_HASH" > public/.build_hash
|
||
echo "$BUILD_HASH" > public/build_hash.txt
|
||
|
||
- 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 下载」)
|
||
#
|
||
# ★ 必须用 v3,不能用 v4:实测 v4 在 Gitea 上直接失败 ——
|
||
# "@actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+
|
||
# are not currently supported on GHES."(Gitea 被识别为 GHES,未实现新版
|
||
# artifact 服务)。v3 走的是 Gitea 已实现的旧 artifact API。
|
||
# v3 也没有 include-hidden-files 选项;隐藏文件能不能进 artifact 不可靠,
|
||
# 所以 hash 另外写了一份非隐藏的 public/build_hash.txt 兜底。
|
||
uses: actions/upload-artifact@v3
|
||
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 零跨境、零费用)
|
||
# ★ 必须与上传端同为 v3:v4 在 Gitea(被识别为 GHES)上不受支持。
|
||
uses: actions/download-artifact@v3
|
||
with:
|
||
name: hugo-public
|
||
path: public/
|
||
|
||
- name: Normalize artifact layout
|
||
# upload-artifact 对「目录路径」的打包语义存在版本/实现差异:
|
||
# 解出来的可能是 public/index.html(内容直接在根),也可能是
|
||
# public/public/index.html(多一层目录)。这里统一拍平成 public/index.html,
|
||
# 免得 EdgeOne 部署时找不到入口、或站点多出一层 /public/ 前缀。
|
||
run: |
|
||
if [ ! -f public/index.html ] && [ -f public/public/index.html ]; then
|
||
echo "ℹ️ 检测到产物多一层 public/,已拍平"
|
||
mv public/public public__flat
|
||
rm -rf public
|
||
mv public__flat public
|
||
fi
|
||
if [ ! -f public/index.html ]; then
|
||
echo "❌ 产物结构异常:public/index.html 不存在"
|
||
find public -maxdepth 2 | head -20
|
||
exit 1
|
||
fi
|
||
echo "✅ public/index.html 就位,文件数 $(find public -type f | wc -l)"
|
||
|
||
- 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 |