Appearance
Daily News Refresh 工作流深度拆解
归类:DevOps / GitHub Actions / news-base
日期:2026-04-07
状态:✅ 已优化落地
一、背景
news-base 是一个基于 VitePress 的新闻聚合站点,需要每日自动完成以下链路:
GitHub Actions 定时触发
→ pnpm crawl(抓取新闻 JSON)
→ pnpm build(生成静态站点)
→ git commit + push(提交数据产物)
→ Cloudflare Pages 自动部署二、最终工作流全文(带注释)
yaml
name: Daily News Refresh
on:
workflow_dispatch:
schedule:
# UTC 22:00 = 北京时间 06:00,提前 3h 触发,补偿免费 Runner 队列延迟
# 预期实际执行时间:北京时间 09:00 左右
- cron: '0 22 * * *'
# 防止同名工作流并发执行,避免重复抓取与冲突提交
concurrency:
group: daily-news-refresh
cancel-in-progress: false
permissions:
contents: write
issues: write
jobs:
refresh-news:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
# 提前迁移 Actions JS 运行时到 Node.js 24,消除 Node.js 20 弃用警告
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc' # 读取 .nvmrc,当前为 22.22.0
- name: Enable Corepack
run: |
corepack enable
corepack prepare pnpm@9.15.9 --activate
pnpm --version
# 获取 pnpm store 路径,供缓存 key 使用
- name: Get pnpm store directory
id: pnpm-cache
run: echo "store=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
# 缓存 pnpm store,命中时 install 可从几十秒降至几秒
- name: Cache pnpm store
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.store }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Install dependencies
run: pnpm install --frozen-lockfile
# 抓取失败时自动重试,最多 3 次,每次间隔 60s,应对网络抖动或源站限流
- name: Refresh news data (with retry)
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 60
command: pnpm run crawl
- name: Build site
run: pnpm run build
- name: Commit refreshed API data
run: |
if git diff --quiet -- data/daily public/api docs/public; then
echo "No API changes to commit."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add data/daily public/api docs/public
git commit -m "chore: refresh daily news $(date +'%Y-%m-%d')"
git push origin HEAD:main
# 任意步骤失败后,通过创建 GitHub Issue 留存故障记录
- name: Create failure issue on error
if: failure()
uses: actions/github-script@v7
with:
script: |
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['ci-failure'],
state: 'open',
});
const today = new Date().toISOString().slice(0, 10);
const exists = issues.some(i => i.title.includes(today));
if (exists) return;
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `[CI 失败] Daily News Refresh 工作流异常 ${today}`,
body: [
'## 工作流执行失败',
`- **Run**: [#${context.runNumber}](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})`,
`- **触发方式**: ${context.eventName}`,
`- **时间 (UTC)**: ${new Date().toISOString()}`,
'请检查 Actions 日志排查原因。',
].join('\n'),
labels: ['ci-failure'],
});三、每步设计意图
Step 1:actions/checkout@v4
fetch-depth: 0:保留完整 git 历史,避免后续git push时出现 shallow clone 错误。
Step 2:actions/setup-node@v4
- 使用
node-version-file: '.nvmrc'读取项目声明版本,与本地保持一致。 - 不要硬编码版本号(如
node-version: '22'),升级时只需改.nvmrc。
Step 3:Enable Corepack
- corepack 是 Node.js 官方 pnpm/yarn 版本管理工具,不需要额外 install pnpm。
pnpm@9.15.9 --activate确保版本与packageManager字段一致。
Step 4~5:pnpm 缓存
- 通过
pnpm store path获取缓存目录(不同系统路径不同,不能硬编码)。 hashFiles('**/pnpm-lock.yaml')确保依赖变化时自动失效旧缓存。
Step 6:pnpm install --frozen-lockfile
--frozen-lockfile:CI 环境中不允许更新 lock 文件,发现不一致直接报错,避免隐式依赖变更。
Step 7:Crawl with retry
- 新闻源稳定性参差不齐,网络抖动会导致偶发失败。
- 3 次重试 + 60s 间隔可覆盖绝大多数临时限流场景。
Step 8:Build site
pnpm run build触发 VitePress 静态站点构建,生成dist/。
Step 9:Commit(条件提交)
git diff --quiet先检查是否有实际变化,无变化直接退出,避免无意义提交。$(date +'%Y-%m-%d')在 commit message 中附加日期,便于 git log 定位。git push origin HEAD:main比git push更稳健,明确推送目标分支。
Step 10:失败 Issue 通知
if: failure()在任意前置步骤失败时才触发。- 查重逻辑避免同一天重复创建 Issue(如重试多次后都失败)。
- 使用内置的
GITHUB_TOKEN,无需额外配置 Secrets。
四、遇到的问题与修复
问题 1:实际执行时间远晚于预期
现象:cron 设 10 1 * * *(北京 09:10),实际执行为 12:08~12:17,延迟约 3 小时。
原因:GitHub 免费共享 Runner 在流量高峰时排队长达 2~3 小时。
修复:将 cron 提前至 0 22 * * *(北京 06:00),以补偿队列延迟,使实际执行目标为北京 09:00。
问题 2:Node.js 20 弃用警告
现象:
Node.js 20 actions are deprecated. actions/checkout@v4, actions/setup-node@v4
will be forced to run with Node.js 24 by default starting June 2nd, 2026.原因:Actions 内部 JS 运行时(与项目 Node 版本无关)使用了 Node.js 20。
修复:
- 在 job 级添加
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true环境变量 - 将
.nvmrc从20.19.0升级为22.22.0(与本地实际版本对齐) - 更新
package.json的engines.node为22.x
问题 3:push 被远端拒绝
现象:
! [rejected] main -> main (fetch first)
error: failed to push some refs原因:本地有未同步的本地提交,而远端(GitHub Actions 自动提交)已新增 commit。
修复:
bash
git pull --rebase origin main
git push origin mainRebase 模式比 merge 更干净,不会产生多余的 merge commit。
五、预防建议
- 定时任务 cron 预留缓冲时间:免费 Runner 排队不可控,目标执行时间前至少预留 3 小时。
- 依赖缓存是基础优化:pnpm/npm store 缓存可以把 install 时间从 30s+ 降至秒级,几乎零成本。
- 网络密集步骤一定要加重试:爬虫类工作流必须加 retry,否则偶发失败率会很高。
- 用条件提交代替无条件提交:
git diff --quiet先检查,无变化不 commit,避免产出无意义的 git 历史。 - 使用
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24提前适配:不要等到 GitHub 强制切换再处理,届时可能有 breaking change。