テクニカルプア

備忘録と若干の補足

mkosi で実行される処理に AUR パッケージのビルドとインストールを含める

の続編。件の記事で挫折した内容が達成できたので書く。

mkosi によるイメージのビルド時に AUR パッケージのビルドとインストールもあわせてやる方法が実現できた。 これによって先の内容は無用となった。哀しい。

TL; DR

をみてもらうのが手っ取り早い。

解説

上の通りなのだがそれではブログに書く意味がないので言葉で説明する。

ビルド中のコンテナから外に出れない

mkosi.default[Packages] 内で WithNetwork=yes を与えるか mkosi 自体に --with-network を与える。 こうすることで mkosi.build スクリプトが実行される段階(mkosi の man 内容でいう development image)でコンテナから外に出れる。 man に書いてあったのを見落としていた。man はちゃんと読むべきだった。

ちなみに WithNetwork=yes をすると mkosi を実行しているホストのネットワークがそのまま使用され、そうでない場合は systemd-nspawn コマンドに --private-network が付与されるようだった。 ホストとコンテナとでネットワークまわりの namespace が共有されてしまうのはちょっと気持ち悪いが、まあいいか……。

ビルド対象が依存するパッケージをいれる

上の WithNetwork=yes でインターネットに出れるようになる為、 pacman -U する際の依存関係解決も pacman 側で任せられるようになる。 よって mkosi.default[Packages] 内に依存パッケージをズラズラ書いておく必要がなくなる。単に pacman -U --noconfirm とかすればよい。 ただデフォルトでは /etc/pacman.d/mirrorlist の内容が全部コメントアウトされているので、 mkosi.postinst あたりで適当なミラーをアンコメントするなり適当なファイルで書き換えるなりする処理がが必要。 私は後者を選んだ。

PKGBUILD をコンテナ内に持ち込む

あらかじめ AUR 上のパッケージを手元に git clone しておく。以下では git clone してできたディレクトリを aurpkg/ とする。 mkosi.default[Package] 内で以下のようにしてやればよい:

  • BuildSources=aurpkg を指定
  • SourceFileTransfer=mount を指定

こうすると mkosi するディレクトリの中にある aurpkg/ というディレクトリがビルド用コンテナ内から $SRCDIR (/root/src) で参照できる。

makepkg コマンドを叩く

makepkg コマンドは root では実行できない。しかし mkosi.build は root として実行される。どうするか。nobody ユーザとして makepkg を実行してやればよさそう。 具体的には sudo -u nobody -- bash -c "makepkg -sr" などとやればよい。

ただしこれを実行すると AUR パッケージが必要とするパッケージをインストールする段階にうつり、nobody ユーザが sudo で root になる為のパスワードをきかれてしまい悲しいことになる。 なので事前に PKGBUILD の dependsmakedepends に書かれてるものを pacman -S --noconfirm しておくのがよい。

以上を踏まえて私は以下のようにした:

source $SRCDIR/PKGBUILD
[[ -n "$depends" ]] && pacman -Sy --noconfirm "${depends[@]}"
[[ -n "$makedepends" ]] && pacman -Sy --noconfirm "${makedepends[@]}"

また makepkg は自身が使うディレクトリを一旦 mkdir -p してみる実装になっているようで 1$SRCDIR (/root/src/) や $BUILDDIR (/root/build/) を nobody ユーザで mkdir -p できるようになっていないとエラーを吐いてコケてしまう。これには /root/ で nobody ユーザが実行権限をもっている必要がある。 加えて $BUILDDIR$SRCDIR および後述する $DESTDIR (/root/dest/) それ自体も nobody ユーザから読み書きができるようになっていないと必要なファイルを参照したり作成できなくなってしまうので、これまたコケてしまう。 よって setfacl で以下のようにしてやる:

for DIR in $BUILDDIR $SRCDIR $DESTDIR; do
        setfacl -m u:nobody:rwx $DIR
        setfacl -d --set u:nobody:rwx,o::- $DIR
done
setfacl -m u:nobody:rwx /root
setfacl -d --set u:nobody:rwx,o::- /root

makepkg の結果をどうインストールするか

mkosi.build が実行される development image の段階で生成物を pacman -U しても最終的に得られるイメージには反映されない。development image はあくまで中間イメージであり、最終成果物を得る為の過程にすぎない為だ。 また mkosi.build を実行することで作成された内容は $DESTDIR(通常はコンテナ内 /root/dest/)に移しておかないと mkosi.build 終了時に中間イメージごと消滅してしまう。

mkosi.postinst は中間イメージ作成時と最終イメージ作成時の双方で実行されるので、 pacman -Umkosi.postinst で実行してあげればよい。 また mkosi.buildmakepkg -sr した結果作成されたパッケージを $DESTDIR 下に移すには makepkg する際に PKGDEST という環境変数$DESTDIR にしてやればよい。こうすることで作成されたパッケージは $DESTDIR 以下に出力される。 $DESTDIR に移された成果物は最終イメージ内 / 直下に配置されるので、 mkosi.postinst では / 下にあるパッケージを pacman -U してやればよい。

# mkosi.build
# mkosi.build は $SRCDIR (/root/src/) で実行されるので makepkg する前にディレクトリを移動する必要はない
# PKGEXT を変えているのは生成物を xz で圧縮させないようにさせるため
sudo -u nobody -- bash -c "PKGDEST=$DESTDIR PKGEXT='.pkg.tar' makepkg -sr"
# mkosi.postinst
for CANDIDATE in /*.pkg.tar; do
        if [ -e "$CANDIDATE" ]; then
                pacman -U --noconfirm "$CANDIDATE"
                rm -f "$CANDIDATE"
        fi
done

まとめ

とりあえずやりたいことはできるようになったのでよかった。なんかもっとシンプルにできそうな気もするが、今はこれが限界。

参考