『 纸上得来终觉浅,绝知此事要躬行 』


自建 ArchLinux 个人源

2021.08.06 | 本文总阅读量

wps-office 因为分发许可的原因移出 archlinuxcn 源之后,自建一个 arch repo 的需求就越来越强烈,我之前也搞过 github action 来打 archlinux 的包,但并不能完全满足我的需求,于是我准备大刀阔斧改上一波,并写点脚本辅助自动化打包流程。

这篇文章可能会有点长,因为我打算把我的踩坑过程也写进去,没时间看的可以直接到文末我的仓库链接查看具体实现。

1 需求分析

需求 预计解决方案
定时打包 github action cron
打完的时候通知 telegram bot api
能够打有依赖在 AUR 的包 aur helper yay
打自己修改过的 PKGBUILD 保证目录形式以兼容 aurpublish,以文件夹的形式上传
有一个 web 页面提供单独的下载,因为用户不一定都愿意导入你的 key 目前选用keybase

2 具体思路

首先想到的当然是依云写的 lilac 啦,但我看了看,貌似远远高于我的需求,我只是想打几个类似于 wps-office 这样的包应该用 github action 就能搞定,不过完全可以借鉴思路。

这里顺便提一下两位群友的方案:GitHub Actions 打造 AUR 打包下载一条龙服务利用 GitHub Actions 编译 AUR 包并建立自己的软件源,我接下来的操作和他们有很大的相似之处,只有一些具体细节并不相同。

2.1 解决 docker 权限问题

makepkgyay 因为安全原因不允许在 root 下运行。这是有点糟心的,docker 默认不就是 root 么,群友 DuckSoft 提供了一个 dirty hack 来让 makepkgroot 下运行

sed -i '/E_ROOT/d' /usr/bin/makepkg

这下好了,可以打没有依赖在 AUR 的包了,就像下面三行就完事了

# entrypoint.sh
git clone "https://aur.archlinux.org/$1.git"
cd "$1"
makepkg -sf --noconfirm

但是 yay 还是不能在 root 下运行,于是乎想到新建一个没有密码的普通用户然后通过解析 .SRCINFO 来递归安装依赖,这是 edlanglois/pkgbuild-action 的做法。

# Extract dependencies from .SRCINFO (depends or depends_x86_64) and install
mapfile -t PKGDEPS < \
	<(sed -n -e 's/^[[:space:]]*\(make\)\?depends\(_x86_64\)\? = \([[:alnum:][:punct:]]*\)[[:space:]]*$/\3/p' .SRCINFO)
sudo -H -u builder yay --sync --noconfirm "${PKGDEPS[@]}"

我具体看了看他的实现,感觉做法过于复杂了,而且在 root 下采用 sudo -u 还要额外考虑传递环境变量的问题。难道就没有一个内置 yay 并默认是普通用户的 docker 上游吗?

嗯,终于等到你 martynas/archlinux,它内置了 yay 用来构建 AUR 包并且有个默认的名为 buildpasswordless 用户,事情瞬间变得简单多了,这里是我写的 pkgbuild-aur

2.2 同时管理自己修改过的 PKGBUILD 和 AUR

为了能够同时方便的使用 aurpublish 来管理我自己手里的 AUR 包,我采用了以下的目录要求

# there should be a subdirectory with the same name of pkgname to satisfy `aurpublish`
if [[ ! -d $pkgbuild_dir ]]; then
    echo "$pkgbuild_dir should be a directory."
    exit 1
fi

if [[ ! -e $pkgbuild_dir/PKGBUILD ]]; then
    echo "$pkgbuild_dir does not contain a PKGBUILD file."
    exit 1
fi

if [[ ! -e $pkgbuild_dir/.SRCINFO ]]; then
    echo "$pkgbuild_dir does not contain a .SRCINFO file."
    exit 1
fi

需要在项目根目录下放置与包同名的文件夹,同时里面要包含 PKGBUILD.SRCINFO,后者是 AUR web 的必须要求同时也用来解析依赖,可以通过 makepkg --printsrcinfo > .SRCINFO 来生成。

同时要注意最好先改一下目录权限,据说 /github/workspace 默认 ownerroot,所以推荐如下改动。

# fix directory permissions
sudo chown -R build "$pkgbuild_dir"
sudo chown -R build /github/home

2.3 定时打包以及完成之后通知我

首先 github action 就支持 cron 定时,详见文档,作为一名良心资本家,我允许它周末休息,遵循 996 每天只干两次活。

on:
  push:
    branches:
      - master
  schedule:
    - cron: '0 9,21 * * 1-6' # build pkgs via 996 rules (;′⌒`)
  workflow_dispatch: # allow manual trigger

当然我开启了每次 push 运行和手动开启运行 CI 以便随时压榨(

github action 失败的话会有邮件提醒,但为了维护自己的个人源在 build 成功的时候我也需要提醒。这时候 oldherl 提醒了我,为什么不试试 telegram bot api 呢?这着实是个好主意,我翻查了一下文档,究极简单,以后提醒不用邮件了,全改用 telegram,顺便推荐比较完善的 telegram-action

2.4 打包之后的存放位置

首选当然是 github release 啦,我预想是将 github release 当成一个 archive repo,里面存放着前几个版本的包。Arch 是滚动更新的,自己的个人源自然只放最新的版本包,我目前选用的是 keybase,因为它提供 250 G 免费直链网盘和一个免费二级域名(这不就巧了么,正好符合需求),于是我写了个 repo-helper 方便使用

#!/usr/bin/bash

# Usage:
# ./repo-helper add <pkg_file_name>     # add package
# ./repo-helper remove <pkgname>        # remove package
# ./repo-helper list                    # list packages in repo
# ./repo-helper pull                    # download packages from personal github release

repo_name="zjuyk"
repo_ext=".db.tar.gz"
repo_dir="/run/user/1000/keybase/kbfs/public/$repo_name/x86_64/"
github_release_url="https://api.github.com/repos/zjuyk/repo/releases/latest"

db_path=$repo_dir$repo_name$repo_ext

case $1 in
        pull)
                echo "Pull update from github release..."
                curl -s $github_release_url | \
                        grep browser_download_url | \
                        cut -d : -f 2,3 | \
                        tr -d \" | \
                        xargs -n 1 curl -O -sSL -o $repo_dir -C -
                repo-add $db_path $repo_dir/*.zst --remove --sign --quiet
                index-generator --human $repo_dir
                echo "Done!"
                ;;
        add)
                echo "Add $2 to $repo_name..."
                mv -n ./$2 $repo_dir
                repo-add $db_path $repo_dir/$2 --remove --sign --quiet
                index-generator --human $repo_dir
                echo "Done!"
                ;;
        remove)
                echo "Remove $2 from $repo_name..."
                repo-remove $db_path $2
                index-generator --human $repo_dir
                echo "Done!"
                ;;
        list)
                ls $repo_dir/*.zst | sed 's/-x86_64.pkg.tar.zst//g' | awk -F "/" '{print $NF}'
                ;;
        *)
                echo "args should be one of 'pull', 'add', 'remove' and 'list'!"
esac

脚本中的 index-generator兔兔 他们在几年前用 Jinja 写的,我打算有时间的话用 rusttera 库重构一个。

repo

2.5 维护更新

说到检查版本首先想到的就是依云的 nvchecker 写个配置文件跑一跑,然后改一下包信息就完事了

# bump pkg version in PKGBUILD
# update checksums
$ updpkgsums
# update .SRCINFO
$ makepkg --printsrcinfo > .SRCINFO

3 不足与改进计划

  • 因为 keybase 非常注重安全性,所以我没办法直接从 github 将产物推到 keybase 上(也许可以,但也会十分复杂,需要专用的工具)。这导致我目前只能半自动化,需要手动 pull release 到运行着 keybase 的机器上同步。虽然可以定时运行,但 keybase 本身的运行会占用一定的资源,我正在考虑是否直接塞到 github repo 里然后开 github pages(属于对 github 的究极滥用,实际上我不会考虑这么做)

  • ArchLinux 除了 x86_64 架构外还额外划分了 any,这个我目前没有考虑,因为目前我并没有维护 any 的包,或许以后会加入

  • 同步是需要消耗流量的,出于未来保留若干版本包用于回滚的考虑,我目前是用 api 把所有的 release 全下下来了,没有做筛选,如果代理流量不够的话建议开一个 cloudflare worker 做反代

4 链接

发表评论