运行手册

GitHub Actions 每周反向链接快照:运行手册

目标:每周一,工作流将当天的引用域名和锚文本写入仓库。仅此而已。差异对比将在后续步骤中进行。

横跨河流的铁桥,象征可靠的定时工作流基础设施
开始之前

你需要一个仓库、一个存储为秘密 LZ_KEY 的 Linkzoid 密钥,以及一个你拥有的域名。预算:每次运行每个目标约消耗 6 个积分,即 $0.0006。快照是对某个月份的事实记录,因此每周运行通常会重新获取相同的索引。以这种方式设置检查反向链接的方法,只需一个晚上即可完成。

运行手册步骤

01

1. 添加秘密

进入仓库设置,然后选择“秘密和变量”,再选择“Actions”。将其命名为 LZ_KEY。如果你将其粘贴到工作流文件中,请立即轮换密钥。

02

2. 提交脚本

将下面的 Python 代码保存为 snap.py。它将写入 data/YYYY-MM-DD/refdomains.json,并在响应 ok 为 false 时失败。

03

3. 提交工作流

使用下面的 YAML 文件。使用 workflow_dispatch 触发器手动运行一次,并在信任定时计划之前查看日志。

04

4. 检查第一个文件

打开 JSON 文件。将行数与 total 进行核对,并将该数字与免费 overview 调用针对你的域名报告的数量进行比较。

05

5. 添加警报

作业失败时默认会向仓库所有者发送邮件。保持此设置不变。作业静默停止是此类设置失效的常见方式。

snap.py

标准库。循环在达到计数后结束,而不是基于猜测。

python
import json, os, sys, datetime, pathlib, urllib.request as u
KEY = os.environ['LZ_KEY']
TARGET = sys.argv[1]
def get(**p):
    q = '&'.join(f'{k}={v}' for k, v in p.items())
    url = f'https://linkzoid.pro/api.php?{q}&key={KEY}'
    with u.urlopen(url, timeout=30) as r:
        return json.load(r)
rows, page = [], 1
while True:
    d = get(action='refdomains', q=TARGET, page=page, per=1000)
    if not d['ok']:
        sys.exit(f'bad response: {d}')
    rows += d['rows']
    if not d['rows'] or len(rows) >= d['total']:
        break
    page += 1
day = datetime.date.today().isoformat()
out = pathlib.Path('data') / day
out.mkdir(parents=True, exist_ok=True)
(out / 'refdomains.json').write_text(json.dumps(rows))
print(len(rows), 'rows, balance', d['_credits'])

.github/workflows/snapshot.yml

GitHub 中的 Cron 在 UTC 时间运行,当运行器繁忙时可能会延迟启动。不要构建依赖于具体分钟的任务。

yaml
name: backlink-snapshot
on:
  schedule:
    - cron: '17 6 * * 1'
  workflow_dispatch:
jobs:
  snap:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: python snap.py example.com
        env:
          LZ_KEY: ${{ secrets.LZ_KEY }}
      - run: |
          git config user.name snapshot-bot
          git config user.email "snapshot-bot@localhost"
          git add data
          git commit -m "snapshot $(date -u +%F)" || exit 0
          git push
修剪幼树的园丁,代表开始计划前的准备工作

在开启计划之前

  • 密钥存储在仓库秘密中,YAML 仅通过名称引用它。
  • 工作流文件位于默认分支,因为这是计划读取的副本。
  • 使用 workflow_dispatch 的手动运行已完成,并生成了一个按日期命名的文件夹。
  • 文件中的行数与 API 为该调用返回的总数匹配。
  • 作业在响应不良时以非零退出码退出,因此失败会使运行显示为红色。

错误与修复

定时反向链接作业中首先出现的问题
错误你看到的现象修复方法
密钥粘贴在 YAML 中密钥在仓库历史中可见轮换密钥,并将其移至 secrets.LZ_KEY
奇数分钟,如 0 0运行延迟 20 分钟开始选择 17 6;整点时段通常较为拥挤
仓库闲置 60 天GitHub 暂停计划提交一些内容,或在 Actions 选项卡中重新启用它
循环仅在空页时停止在页面上限时无限运行当 len(rows) >= total 时也停止
退出码被忽略绿色勾号,但文件夹为空在 ok: false 时使用 sys.exit
无更改时提交失败无意义的红色运行在提交后添加 || exit 0

保护密钥和仓库

密钥是该设置中唯一需要保护的内容。将其视为密码,因为任何持有它的人都可以消耗你的余额。

定时作业会静默泄露。密钥粘贴到日志中、工作流复制到公共仓库,或者拥有写权限的前同事,这些都是常见的泄露途径。每季度检查一次。

人员离职时轮换密钥

当拥有访问权限的同事离职时,创建新密钥,更新秘密并停用旧密钥。然后确认下一次运行通过。变更后的一次绿色运行是你全年所做的最便宜的测试。在仓库笔记中记录轮换日期,以便下次轮换时不会感到意外。

限制谁可以编辑工作流

任何可以编辑 YAML 的人都可以运行中打印秘密。保持写权限范围尽可能小,并以审查代码的方式审查工作流文件的更改。对于小型仓库,两人拥有写权限通常就足够了。

永远不要打印请求 URL

打印余额和行数,但不要打印请求 URL。密钥位于查询字符串中,因此打印 URL 的调试行会将密钥写入日志。如果必须在问题中共享请求,请先用占位符替换密钥。

指南针和盆栽植物,表示稳步进展和早期问题检测

快照是什么,不是什么

你获得的是月度快照。我们的爬虫每月刷新一次索引,因此同一个月内的两个周一运行通常会返回相同的行。这是预期内的现象。

每周的频率是可行的。它保持了习惯的延续,并且你可以在七天内而不是三十天内发现作业故障。如果你在周二建立的链接在周一缺失,请耐心等待下一次刷新,不要惊慌。

当你拥有三到四个按日期命名的文件夹后,可以转向 SQLite 差异对比。如果你卡在顽固的响应代码上,速率限制表 列出了每个状态对循环的含义。

保留第一个文件夹作为基准。每次后续比较都指向它,按日期命名的文件夹比记忆去年春季的数字外观更值得信赖。保持一致性。

添加锚文本和顶级页面

每个文件一个操作可使差异对比更易读。复制 snap.py 中的循环,将 refdomains 更改为 anchors 或 toppages,并写入第二个文件名。每个操作消耗 2 个积分。

合并文件会隐藏哪部分发生了更改,而审查者在滚动拉取请求时希望每个差异只展示一个概念。保持分离。如果你跟踪三个域名,在工作流中使用矩阵循环遍历短列表,以便每个目标都有自己独立的作业和独立的失败状态。

锚文本是第二个文件,在差异对比中有自己的上限,因此应将其与引用域名分开审查。每次运行成本低廉且可重复,这正是其核心思想。

阅读运行日志

打开第一次运行的日志。在信任计划之前务必查看。查找行数、余额和提交行。如果其中任何一项缺失,说明作业执行的内容少于其声称的数量。同时记录每次运行的余额,以便在不打开仪表板的情况下即可看到月度支出。

绿色勾号说明不了多少。它仅表示进程干净地退出。它并不表示文件包含正确的行,这就是脚本打印自身摘要的原因。在脚本的不同版本之间保持这些摘要行不变,以便两个日志的差异仅显示真正的更改。

健康日志的样子

健康的运行会打印一行包含行数和信用余额,然后是一次带有当天日期的提交。任何将这些行淹没在额外噪音中的情况都值得再次查看。

发现静默故障

注意空文件夹、从未出现的提交或行数与总数不符的情况。这些都指向循环或解析问题,而非 API 本身。保存第一个健康日志作为后续比较的参考。

某些故障会留下一个文件夹,其中的行数少于往常。在庆祝绿色运行之前,将计数与上周进行比较,因为静默截断看起来与静默的月份完全相同。

安全地添加第二个目标

第二个域名是一个矩阵条目,而不是整个工作流的副本。为每个目标分配独立的工作,以便一个故障不会掩盖另一个故障。保持每个作业独立。检查路径是否包含域名名称,以便两次运行不会写入同一个文件。

先从第二个目标开始。一旦其文件夹看起来正确,再添加其余部分。中途失败的矩阵比两个简单的运行更难阅读。

按目标命名文件夹

在路径中包含域名,例如 data/<domain>/YYYY-MM-DD,即使两个作业在同一分钟运行,文件夹也能保持分离。旧文件夹可以保留在原处,因为路径中的日期使它们易于查找。

对每个目标使用相同的命名规则。这样脚本就可以列出文件夹而无需特殊处理。

首先规划信用预算

将每个目标的调用次数乘以目标数量,然后在第一次运行之前将总数与余额进行比较。矩阵使支出像作业数量一样快速增加。在工作流旁边的仓库中记录预期的月度总额。当余额下降速度快于预期时,循环可能正在重新获取页面,你可以在下一次运行之前停止它。

第一周会出现的问题

我可以在私有仓库上免费运行此程序吗?
通常可以,在 GitHub 免费账户提供的每月分钟数范围内。快照作业运行时间远低于一分钟,因此每周计划使用的分钟数很少。检查你当前计划的配额,因为 GitHub 会不时更改它。
如果有多人推送,密钥应该放在哪里?
在组织或仓库秘密中,永远不要放在文件中。任何可以编辑工作流的人都可以将秘密打印到日志中,因此限制拥有写权限的人员,并在有人离开项目时轮换密钥。
为什么我的计划运行没有触发?
三个常见原因。工作流文件不在默认分支上,仓库闲置了 60 天导致 GitHub 暂停了计划,或者 cron 字符串有拼写错误。使用 workflow_dispatch 手动运行一次以排除脚本本身的问题。
数据文件夹会变大吗?
很小。一千个引用域名仅占几百千字节的 JSON。一个域名一年的每周文件总大小远低于一百兆字节,这对 git 来说没问题,但如果介意,你可以压缩旧文件夹。