Tag: github-action

4 notes found.

在 GHCR 里看到很多 untagged,不一定代表清理任务漏删了垃圾镜像。

如果一个 Docker 镜像是 multi-arch 构建,比如同时构建:

platforms: linux/amd64,linux/arm64

那么一个 tag 通常不是直接贴在某个具体平台镜像上,而是贴在一个“目录”上。这个目录的正式名字叫 OCI image index 或 manifest list。

可以把它理解成:

ghcr.io/org/app:sha-xxxxxxx
  -> 一个总入口 / 目录
      -> amd64 机器用的镜像
      -> arm64 机器用的镜像

用户拉镜像时还是用同一个 tag:

docker pull ghcr.io/org/app:sha-xxxxxxx

Docker 会根据本机架构自动挑正确的子镜像。amd64 机器拿 amd64,arm64 机器拿 arm64。

为什么会显示成 1 tagged + 4 untagged

以一个同时构建 linux/amd64linux/arm64,并且没有关掉 buildx 默认 provenance 的镜像为例,一次构建大致会产生:

1 个 tagged image index
2 个 untagged platform image manifest
2 个 untagged provenance attestation manifest

所以 GHCR 页面上看起来就是:

1 tagged + 4 untagged

这里的 2 个 platform manifest 是真正给不同 CPU 架构运行用的镜像。它们没有自己的 tag,因为 tag 贴在上层 image index 上。

另外 2 个 provenance attestation manifest 不是运行镜像,而是构建证明。它们记录“这个镜像是从哪个 repo、哪个 commit、哪个 Dockerfile、哪次 GitHub Actions run 构建出来的,以及用了哪些基础镜像/构建依赖”。常见格式是 in-toto statement,predicate 是 SLSA provenance。

这些 untagged 可以删吗

不要只看 GHCR UI 上写着 untagged 就手动删。

如果这些 untagged 是被某个保留 tag 引用的子 manifest,删掉以后可能会破坏 multi-arch 镜像,或者丢掉 provenance 证明。正确的清理逻辑应该理解 image index 和它下面的子 manifest,而不是把所有 UI 上的 untagged 都当成孤立垃圾。

dataaxiom/ghcr-cleanup-action 这类专门处理 GHCR 清理的工具,会把 multi-arch image、attestation、referrer 这些关系一起考虑。删除旧 tagged image 时,它会连同下面的子 manifest 一起处理;保留新 tagged image 时,被引用的 untagged 子 manifest 也应该留下。

GitHub 能避免这种显示吗

GHCR 目前没有一个开关可以说“不要把被 image index 引用的子 manifest 显示为 untagged”。它是按 registry 里的 manifest/version 展示,所以 UI 会显得比较吵。

能减少数量,但都有代价:

  • 关掉 provenance:每批少两个 attestation,代价是没有构建来源证明。
  • 只构建单平台:少一个平台镜像,代价是不再同时支持 amd64/arm64。
  • 单平台并关掉 provenance:最干净,但功能和供应链信息都缩水。

更实用的判断是:不要追求 GHCR 页面上完全没有 untagged,而是区分“孤立 untagged”和“被保留 tag 引用的 untagged 子 manifest”。前者可以清理,后者是镜像结构的一部分。

GitHub token 校验不要依赖固定长度,尤其不要写类似 ghs_[A-Za-z0-9]{36} 的正则。

GitHub 官方文档列出的 token 前缀包括:

  • ghp_: classic personal access token
  • github_pat_: fine-grained personal access token
  • gho_: OAuth access token
  • ghu_: GitHub App user access token
  • ghs_: GitHub App installation access token
  • ghr_: GitHub App refresh token

2026-04-27 起,GitHub App installation token 会逐步切到 stateless 格式,ghs_ token 会变成类似 ghs_APPID_JWT 的结构,长度约 520 字符且会变化。JWT 部分也不应该由客户端解析或校验语义。

如果业务只是想拦住明显错误的输入,可以只做轻量格式检查:

const githubTokenRe = /^(?:(?:ghp|gho|ghu|ghs|ghr)_|github_pat_)[A-Za-z0-9_.-]+$/;

这个校验只确认是 GitHub 已知 token 前缀,并允许 JWT 常见的 .-。真正有效性仍然应该交给 GitHub API 返回 401 Bad credentials 之类的结果判断。

更稳的原则:

  • token 当作 opaque string,不解析内部内容
  • 不限制固定长度
  • 存储字段至少能放下 520 字符以上
  • Actions 里优先使用内置 GITHUB_TOKEN,跨 repo 或特殊权限才使用 PAT 或 GitHub App

参考:

  1. GitHub Docs: GitHub’s token formats
  2. GitHub Changelog: Notice about upcoming new format for GitHub App installation tokens

一个 curl 请求的例子:

curl -X POST 'https://api.github.com/repos/zhaochunqi/til-pages/dispatches' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/vnd.github.v3+json' \
  --header 'Authorization: Bearer github_pat_xxxxx' \
  --header 'X-GitHub-Api-Version: 2022-11-28' \
  --data '{"event_type": "til-updated"}'

其中 github_pat_xxxxx 是 github 的 token,可以在 github token 页面生成。使用 fine-grained token, token 权限需要有 Contents: Read and write.

til-updated 是 github action 的事件类型,可以在 github action 的配置文件中定义。

on:
  repository_dispatch:
    types: [til-updated]

这样就可以通过 curl 请求触发 github action 了。

使用 https://github.com/marketplace/actions/create-pull-request 的时候遇到无法创建 pr 的问题,需要做如下配置:

1. 检查仓库设置

请访问:如:https://github.com/zhaochunqi/dns/settings/actions (替换成你自己的) 找到 “Workflow permissions” 部分,确保:

  • ✅ 选择 “Read and write permissions”
  • ✅ 勾选 “Allow GitHub Actions to create and approve pull requests”

2. workflow 中添加

permissions:
  contents: write
  pull-requests: write