AUR 自动构建脚本设计与改进

最近折腾了一套 AUR 自动构建脚本,用来把常用的 AUR 包编译成自己的 pacman 仓库,并同步到 Cloudflare R2。

整体思路并不复杂:GitHub Actions / 容器负责构建,pikaur 负责拉取和编译 AUR 包,repo-add 负责生成仓库数据库,最后用 rclone 把产物上传到 R2。


设计目标

这套脚本主要解决几个问题:

  • 不想在本机反复编译大型 AUR 包
  • 多台 Arch 设备可以共用同一个二进制仓库
  • 构建产物需要带签名,方便 pacman 直接校验
  • 仓库文件要能低成本公开访问
  • 构建脚本和密钥配置要尽量自动化

最终效果是:只要脚本跑完,R2 上会生成完整的 pacman 仓库目录,客户端添加源之后就可以像官方源一样安装。


脚本结构

脚本大致分成几块:

1
2
3
4
5
6
script/
├── main.zsh # 初始化环境、构建仓库、上传产物
├── update_all.zsh # 批量更新需要构建的包
├── upload_script.zsh # 加密并上传脚本包
├── script.conf # 仓库名、GPG、R2 等配置
└── packages/ # 每个包的 PKGBUILD 或独立构建逻辑

核心入口是 main.zsh,它负责完成完整流水线:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
初始化 pacman / makepkg

创建专用构建用户

导入 GPG 私钥并配置签名

安装 pikaur 等构建工具

编译 AUR 包

清理旧包和 debug 包

重新生成 repo 数据库

同步到 Cloudflare R2

构建环境初始化

脚本会先启用 multilib,再把自定义的 MAKEPKG_CONF 写入 /etc/makepkg.conf

这里主要配置:

  • CFLAGS / CXXFLAGS:按机器架构优化
  • MAKEFLAGS:自动使用多线程编译
  • BUILDENV:开启包签名,关闭不需要的构建特性
  • PKGEXT:统一使用 pkg.tar.zst
  • COMPRESSZST:提高压缩效率

然后创建一个独立的 aur-build 用户,所有 AUR 构建都在这个用户下执行,避免直接用 root 跑 makepkg


包构建逻辑

普通 AUR 包可以直接放在 packages/包名/PKGBUILD 下,由 pikaur 处理依赖和构建。

对于需要特殊处理的包,则可以单独写 build.zsh。例如某些官方包需要改 PKGBUILD、禁用 strip 或调整 makepkg 配置,就可以在独立脚本里完成。

这种设计的好处是:

  • 普通包保持简单
  • 特殊包可以单独定制
  • 构建失败不会影响脚本结构
  • 后续添加新包只需要新增目录

仓库数据库生成

构建完成后,脚本不会简单地在旧数据库上继续追加,而是先清理再重建:

1
2
3
4
5
6
7
删除 debug 包

每个包只保留最新版本

删除旧的 db / files 数据库

扫描当前 pkg 目录重新 repo-add

这样可以避免几个常见问题:

  • 旧版本包长期堆积
  • 数据库里残留不存在的包
  • debug 包被意外加入仓库
  • 多次构建后仓库状态不一致

对个人仓库来说,这种“从当前产物重建数据库”的方式更可控。


上传到 Cloudflare R2

上传部分使用 rclone 的 S3 后端对接 Cloudflare R2。

配置不写入 rclone 配置文件,而是通过环境变量传入:

1
2
3
4
5
RCLONE_CONFIG_R2_TYPE=s3
RCLONE_CONFIG_R2_PROVIDER=Cloudflare
RCLONE_CONFIG_R2_ENDPOINT=...
RCLONE_CONFIG_R2_ACCESS_KEY_ID=...
RCLONE_CONFIG_R2_SECRET_ACCESS_KEY=...

这样有两个优点:

  • CI 环境里更容易注入密钥
  • 不会在命令参数里直接暴露 access key

脚本还会把 R2 endpoint 处理成 account-level endpoint,再把 bucket 名称放进路径里。这是为了避免 bucket-specific endpoint 在 list / delete / sync 场景下出现兼容问题。


相比原始方案的改进

这版脚本主要做了这些调整。

1️⃣ 上传方式改为 rclone + R2

原始方案更偏向固定对象存储上传流程,我这里统一改成 rclone sync

优点是:

  • 本地目录和远端仓库保持一致
  • 删除旧文件更自然
  • R2 / S3 兼容存储都容易迁移
  • CI 日志更容易看出同步了哪些文件

2️⃣ R2 配置集中到 script.conf

所有 R2 相关配置都放在同一个配置文件中,包括:

  • bucket 名称
  • endpoint
  • 仓库上传前缀
  • 脚本备份路径
  • access key
  • secret key

这样脚本本身只负责流程,具体环境差异都交给配置文件处理。


3️⃣ 兼容无密码 GPG 私钥

原始脚本会预设 GPG passphrase,适合带密码的私钥。

我的场景里使用的是无密码私钥,所以去掉了 gpg-preset-passphrase 相关逻辑,只保留:

1
2
3
4
5
pacman-key 导入并信任

gpg 导入到构建用户

导入后立即 shred 删除明文私钥

流程更短,也更符合当前使用方式。


4️⃣ 仓库数据库改为完全重建

直接在旧数据库上追加包,时间久了容易留下脏状态。

现在每次上传前都会:

  • 清理 debug 包
  • 使用 paccache -k1 保留最新版
  • 删除旧数据库文件
  • 对当前包目录重新执行 repo-add

这样 R2 上的仓库更像一次干净发布,而不是不断叠加的历史目录。


5️⃣ 构建脚本支持加密备份

upload_script.zsh 会用 7z 把脚本目录打包,并开启文件名加密:

1
7z a /tmp/script.7z -mhe=on -p"$PASSWORD" ./

然后把加密包上传到 R2 的指定路径。

这样即使 CI 环境临时丢失,也可以从对象存储里恢复脚本配置。当然,真正敏感的 token 和私钥仍然建议放在 CI secret 里,不要长期明文落盘。


客户端使用方式

仓库发布后,在客户端导入 GPG key,然后添加 pacman 源:

1
2
[custom]
Server = https://example.com/repo/x86_64

之后就可以直接安装:

1
2
sudo pacman -Syu
sudo pacman -S package-name

如果是私有仓库,也可以把 R2 前面接 Cloudflare Access 或者只在内网环境使用。


总结

这套脚本的重点不是“能不能构建 AUR”,而是把 AUR 构建变成一个稳定的发布流程:

  • 构建环境可重复
  • 包签名自动完成
  • 仓库数据库保持干净
  • 旧包自动清理
  • R2 上传可同步、可恢复
  • 配置和流程分离

对于只有几台 Arch 设备的个人用户来说,这种方式已经足够轻量;对于经常折腾 AUR 包、又不想在本机反复编译的人来说,也能省下不少时间。