build(vm): rebuild bookworm vm #51
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: minimal virtual machine | |
# auto-task.start-build-time = ? | |
env: | |
# DEBIAN_FRONTEND: noninteractive | |
DEB_ENV: "DEBIAN_FRONTEND=noninteractive" | |
DEV_DIR: assets/vm-minimal | |
SSH_VM: "ssh -F ssh/vm.sshconf vm" | |
# | |
# | |
# | |
# RELEASE_TAG: unstable | |
# CODENAME: sid | |
# ROOTFS_REPO: "debian-sid:" | |
# | |
RELEASE_TAG: 12 | |
CODENAME: "bookworm" | |
ROOTFS_REPO: "debian:bookworm-" | |
# | |
FETCH: "aria2c -s5 -x5 -k1m --no-conf" | |
new_task: "tmux new-session -d" | |
on: | |
push: | |
# branches: ["build"] | |
paths: | |
- .github/workflows/vm-minimal.yml | |
jobs: | |
build: | |
strategy: | |
fail-fast: false | |
matrix: | |
include: | |
# sid: | |
# - arch: rv64gc | |
# deb_arch: riscv64 | |
# qemu_pkg_arch: misc | |
# tty: ttyS0 | |
# net: eth0 | |
# - arch: loong64 | |
# deb_arch: loong64 | |
# qemu_pkg_arch: misc | |
# tty: ttyS0 | |
# net: enp0s1 | |
# # ------------------ | |
- arch: arm64 | |
deb_arch: arm64 | |
qemu_pkg_arch: arm | |
tty: ttyAMA0 | |
net: enp0s1 | |
- arch: armv7a | |
deb_arch: armhf | |
qemu_pkg_arch: arm | |
tty: ttyAMA0 | |
net: enp0s1 | |
- arch: x64 | |
deb_arch: amd64 | |
qemu_pkg_arch: x86 | |
tty: ttyS0 | |
net: enp0s2 | |
- arch: x86 | |
deb_arch: i386 | |
qemu_pkg_arch: x86 | |
tty: ttyS0 | |
net: ens3 | |
- arch: ppc64le | |
deb_arch: ppc64el | |
qemu_pkg_arch: ppc | |
tty: hev0 | |
net: enp0s0 | |
- arch: s390x | |
deb_arch: s390x | |
qemu_pkg_arch: misc | |
tty: ttysclp0 | |
# enP1p0s0 | |
net: eno1 | |
- arch: mips64le | |
deb_arch: mips64el | |
qemu_pkg_arch: mips | |
tty: ttyS0 | |
net: enp0s3 | |
# ------------------------ | |
# BOOKWORM | |
- arch: mipsle | |
deb_arch: mipsel | |
qemu_pkg_arch: mips | |
tty: ttyS0 | |
net: enp0s18 | |
runs-on: ubuntu-latest | |
env: | |
non_x86: ${{ !contains(fromJSON('["x64", "x86"]'), matrix.arch) }} | |
neither_x86_nor_loong: ${{ !contains(fromJSON('["x64", "x86", "loong64"]'), matrix.arch) }} | |
non_loong: ${{ matrix.arch != 'loong64' }} | |
is_loong: ${{ matrix.arch == 'loong64' }} | |
deb_main_supported: ${{ contains(fromJSON('["x64", "x86", "arm64", "armv7a", "rv64gc", "ppc64le", "s390x", "mips64le", "mipsle"]'), matrix.arch) }} | |
DEBUG_MODE: false | |
# | |
# debug: | |
# non_loong: ${{ matrix.arch != 'x64' }} | |
# is_loong: ${{ matrix.arch == 'x64' }} | |
# deb_main_supported: ${{ contains(fromJSON('["x86", "arm64", "rv64gc", "ppc64le", "s390x", "mips64le"]'), matrix.arch) }} | |
defaults: | |
run: | |
shell: zsh --pipefail -fex {0} | |
steps: | |
- name: install zsh | |
if: runner.arch == 'X64' | |
shell: sh -e {0} | |
run: ${{ vars.INSTALL_ZSH }} | |
- uses: actions/checkout@v4 | |
- name: export rootfs | |
id: export_rootfs | |
env: | |
scripts: "${{env.DEV_DIR}}/scripts/*" | |
run: | | |
sudo cp -pv ${{env.scripts}} /usr/local/bin | |
pkgs=(aria2 qemu-utils) | |
if { ${{env.non_loong}} } { | |
pkgs+=(systemd-container) | |
} | |
apt-install $pkgs & | |
task=$! | |
image=ghcr.io/2cd/${{env.ROOTFS_REPO}}${{matrix.arch}} | |
docker pull $image | |
name=$(docker create $image) | |
docker export $name -o rootfs.tar | |
sudo mkdir -pv rootfs | |
sudo tar -C rootfs --overwrite -xf rootfs.tar | |
unlink rootfs.tar | |
sudo cp -pv ${{env.scripts}} rootfs/usr/local/bin | |
docker rm $name | |
wait $task | |
- name: fetch zsh & busybox for loong64 | |
if: fromJSON(env.is_loong) | |
env: | |
url: "https://github.com/2moe/zsh-static-docker/releases/download/latest/zsh-${{matrix.arch}}.tar.zst" | |
run: | | |
${{env.new_task}} "apt-install qemu-system-x86" | |
${{env.FETCH}} -o a.tzst $url | |
mkdir -p zsh-tmp | |
tar -C zsh-tmp -xf a.tzst ||: | |
for bin (zsh-tmp/opt/bin/{zsh,busybox}) { | |
sudo install -Dm755 $bin rootfs/bin/${bin:t} | |
} | |
sudo rm -rf zsh-tmp a.tzst | |
- name: install qemu-user | |
if: fromJson(env.neither_x86_nor_loong) | |
run: apt-install qemu-user-static | |
- name: fetch zsh for non-loong64 | |
if: fromJson(env.non_loong) | |
env: | |
img: ghcr.io/2moe/zsh-static:${{matrix.arch}} | |
run: docker run --rm -v $PWD/rootfs/bin/:/host $img cp zsh busybox /host | |
- name: update mirror src | |
if: fromJSON(env.deb_main_supported) | |
env: | |
mirror_src: "rootfs/etc/apt/sources.list.d/mirror.sources" | |
run: | | |
awk_args=( | |
-v true=1 | |
-v false=0 | |
-v mirror_url='https://cloudflaremirrors.com/debian/' | |
'/experimental/ { | |
cond = true | |
} | |
/-debug/ { | |
cond = false | |
} | |
/snapshot\.debian/ { | |
if ($1 == "URIs:") { | |
sub($0, sprintf("%s %s", $1, mirror_url)) | |
} | |
} | |
cond && /Enabled:/ { | |
sub("no", "yes") | |
cond = false | |
} | |
{ print }' | |
${mirror_src}.bak | |
) | |
sudo cp -vf $mirror_src ${mirror_src}.bak | |
awk $awk_args | sudo tee ${mirror_src} | |
- name: setup ssh server | |
run: | | |
sudo rm -vf rootfs/etc/resolv.conf ||: | |
uuu_keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg | |
if [[ -e $uuu_keyring ]] { | |
sudo cp -vf $uuu_keyring rootfs$uuu_keyring | |
} | |
single_arg=$(<<'CTR_ARG' | |
for i (zsh busybox) { | |
ln -vf /bin/$i /bin/${i}-static | |
} | |
apt-install | |
apt-get upgrade -y | |
apt-install openssh-server | |
ssh-keygen -A ||: | |
# allow root to login without passwd in TTY | |
sed -E '/^root:/ s@(root:)[^:]+(:)@\1\2@' -i /etc/shadow | |
chsh -s /bin/zsh | |
CTR_ARG | |
) | |
print -R "$single_arg" > install-sshd.zsh | |
if { ${{env.non_loong}} } { | |
sudo systemd-nspawn -E "${{env.DEB_ENV}}" -D rootfs zsh -fexc "$single_arg" | |
} | |
cp -va ${{env.DEV_DIR}}/ssh . | |
key=ssh/vm.ed25519 | |
ssh-keygen -t ed25519 -f $key -N "" | |
ssh_client_d=rootfs/root/.ssh | |
for i ($ssh_client_d) { | |
sudo install -Dm600 ${key}.pub $i/authorized_keys | |
sudo chmod 700 -v $i | |
} | |
sshd_cfg=ssh/vm.sshd.conf | |
sudo install -Dm600 $sshd_cfg rootfs/etc/ssh/sshd_config.d/vm.conf | |
unlink $sshd_cfg | |
sudo cp -v ${{env.DEV_DIR}}/systemd/* rootfs/etc/systemd/system/ | |
for f (${{env.DEV_DIR}}/libexec/*) { | |
sudo install -Dm700 $f rootfs/usr/libexec/tmm/qemu/${f:t} | |
} | |
- name: install kernel | |
if: fromJSON(env.non_loong) | |
run: | | |
case ${{matrix.arch}} { | |
(arm64|x64) kernel=cloud-${{matrix.deb_arch}} ;; | |
(armv7a) kernel=armmp-lpae ;; | |
(ppc64le) kernel=powerpc64le ;; | |
(x86) kernel=686 ;; | |
# | |
# loongson-3, mips64r2el, octeon | |
(mips64le) kernel=loongson-3 ;; | |
# loongson-3, mips32r2el, octeon, 4kc-malta | |
(mipsle) kernel=4kc-malta ;; | |
(*) kernel=${{matrix.deb_arch}} ;; | |
} | |
sudo cp -a rootfs kernel_r | |
sudo systemd-nspawn -D kernel_r sh -c "apt-install linux-image-${kernel}" | |
- name: setup loongarch environment | |
if: fromJSON(env.is_loong) | |
timeout-minutes: 30 | |
env: | |
url: "https://github.com/2cd/debian-museum/releases/download/unstable/vm-dev_sid_x64.tar.zst" | |
run: | | |
${{env.FETCH}} -o a.tzst $url | |
mkdir -pv dev | |
tar -C dev -xvf a.tzst | |
qemu-img create -f raw dev/data.raw 2G | |
sudo mkfs.ext4 -FL data dev/data.raw | |
sudo mkdir -p data | |
sudo mount -v dev/data.raw data | |
sudo cp -a rootfs/* install-sshd.zsh data | |
sudo umount -lvf data | |
sed_args=( | |
-E | |
# dev/vdb | |
-e 's@^(\s{2,})(disk.qcow2)$@\1\2\n\1data.raw@' | |
-e 's@^(hostfwd)(_rules=)@\1_map+=(127.0.0.1:9022 9022)\n\1\2@' | |
-i dev/run | |
) | |
sed $sed_args | |
cd dev | |
while {! sudo apt-get install} { | |
sleep 3 | |
} | |
sudo chmod 666 -v /dev/kvm | |
${{env.new_task}} ./run | |
sleep 3 | |
sed -E 's@(IdentityFile)\s.*@\1 dev/ssh/vm.ed25519@' ssh/vm.sshconf > dev.conf | |
cat dev.conf | |
while {! ./connect-to-ssh exit} { | |
sleep 1 | |
} | |
- name: install sshd & kernel for loongarch | |
if: fromJSON(env.is_loong) | |
uses: 2moe/local-ssh-action@v0 | |
with: | |
host: vm | |
args: | | |
-F | |
dev/dev.conf | |
run: | | |
cd /root | |
mkdir -p rootfs | |
mount /dev/vdb rootfs | |
if { ${{env.DEBUG_MODE}} } { | |
src=/etc/apt/sources.list.d/mirror.sources | |
sed_cmd=(sed -E 's@(https://).*@\1cloudflaremirrors.com/debian/@g' -i $src) | |
systemd-nspawn -E "${{env.DEB_ENV}}" -D rootfs $sed_cmd | |
} | |
systemd-nspawn -E "${{env.DEB_ENV}}" -D rootfs zsh -fex /install-sshd.zsh | |
systemd-nspawn -D rootfs unlink /install-sshd.zsh | |
cp -a rootfs no-kernel | |
systemd-nspawn -D rootfs sh -c "apt-install linux-image-${{matrix.deb_arch}}" | |
cd rootfs | |
ls -lh . boot | |
tar -cf /root/kernel.tar boot initrd.* vmlinu* | |
${{env.new_task}} 'systemd-nspawn -bD ../no-kernel' | |
- name: scp loongarch kernel.tar & unpack | |
if: fromJSON(env.is_loong) | |
run: | | |
mkdir -p kernel_r | |
ls -lh dev | |
./dev/get-file-from-vm /root/kernel.tar | |
sudo tar -C kernel_r -xf Downloads/kernel.tar | |
- name: copy kernel | |
run: | | |
cp -a kernel_r/boot . | |
for f (vmlinuz 'initrd.img') { | |
k_file=kernel_r/$f | |
if [[ ! -e $k_file ]] { | |
# fix ppc64le | |
case $f { | |
(vmlinuz) k_file=kernel_r/vmlinux ;; | |
(*) exit 2 ;; | |
} | |
} | |
real_path=$(realpath $k_file) | |
real_fname=${real_path:t} | |
cp -v $real_path boot/$f | |
if [[ $f != $real_fname ]] { | |
ln -svf $f boot/$real_fname | |
} | |
} | |
for f (config System) { | |
cp -v kernel_r/boot/${f}* boot/ ||: | |
} | |
${{env.new_task}} 'sudo systemd-nspawn -bD rootfs' | |
sleep 2 | |
# sudo rm -rf kernel_r | |
while {! ${{env.SSH_VM}} exit} { | |
sleep 1 | |
} | |
- name: setup dhcp-network & serial | |
uses: 2moe/local-ssh-action@v0 | |
with: | |
host: vm | |
args: | | |
-F | |
ssh/vm.sshconf | |
run: | | |
set -fexo pipefail | |
apt-install ifupdown udev fdisk | |
apt-install qemu-guest-agent dosfstools ||: | |
network_iface=${{matrix.net}} | |
tty_serial=${{matrix.tty}} | |
printf "%s\n" "auto $network_iface" "iface $network_iface inet dhcp" >/etc/network/interfaces.d/qemu-nat.conf | |
cd $(mktemp -d) | |
printf "%s\n" 'nameserver 10.0.2.3' 'nameserver 1.1.1.1' > resolv.conf | |
install -m644 resolv.conf /etc/resolv.conf | |
# serial | |
cp /usr/lib/systemd/system/serial-getty@.service /etc/systemd/system/serial-getty@${tty_serial}.service | |
sed '/ExecStart=/ s@- $TERM@--autologin root --local-line - xterm@' -i /etc/systemd/system/serial-getty@${tty_serial}.service | |
systemctl disable systemd-networkd-wait-online.service | |
systemctl set-default multi-user.target | |
for service ( | |
add-cloudflare-dns.service | |
unsafe-resize-partition.service | |
) { | |
systemctl enable $service | |
} | |
case ${{matrix.arch}} { | |
(mips64le) systemctl enable force-sync-time.service ;; | |
} | |
timeout 2 timedatectl set-ntp yes ||: | |
timeout 3 timedatectl show ||: | |
apt-get autopurge -y | |
apt-get clean | |
ls -lh /var/cache/apt | |
rm -vf /var/lib/apt/lists/*.* ||: | |
df -Th | |
journalctl --rotate ||: | |
journalctl --vacuum-time=1s ||: | |
- name: quit systemd-container | |
run: | | |
${{env.SSH_VM}} poweroff ||: | |
- name: copy no-kernel rootfs to vdb | |
if: fromJSON(env.is_loong) | |
uses: 2moe/local-ssh-action@v0 | |
with: | |
host: vm | |
args: | | |
-F | |
dev/dev.conf | |
run: | | |
chmod 777 -R rootfs | |
rm -rf rootfs/* ||: | |
umount -lvf /dev/vdb | |
mkfs.ext4 -FL data /dev/vdb | |
mkdir data | |
mount /dev/vdb data | |
ls -lah data | |
cp -a no-kernel/* data | |
- name: Shutdown DEV-VM | |
if: fromJSON(env.is_loong) | |
run: | | |
ssh -F dev/dev.conf vm poweroff ||: | |
- name: mount loongarch data to rootfs | |
if: fromJSON(env.is_loong) | |
run: | | |
while {lsof dev/data.raw} { | |
sleep 1 | |
} | |
sudo chmod 777 -R rootfs | |
sudo rm -rf rootfs | |
mkdir -p rootfs | |
sudo mount -v dev/data.raw rootfs | |
- name: disk partition | |
timeout-minutes: 30 | |
id: disk_partition | |
run: | | |
while {! sudo apt-get install} { | |
sleep 2 | |
} | |
qemu-img --version | |
qemu-img create -f raw disk.img 1G | |
print -R ,,L | sudo sfdisk disk.img | |
loop_dev=$(sudo losetup -f) | |
sudo losetup $loop_dev disk.img | |
sudo losetup --all | |
sudo kpartx -va $loop_dev | |
p1=/dev/mapper/${loop_dev:t}p1 | |
outputs=( | |
"dev=$loop_dev" | |
# "p1=$p1" | |
) | |
printf "%s\n" $outputs >> $GITHUB_OUTPUT | |
# | |
sudo mkfs.ext4 -FL rootfs $p1 | |
sudo mkdir -p disk | |
# | |
sudo mount -v $p1 disk | |
lsblk | |
# | |
sudo cp -a rootfs/* disk | |
sudo umount -lvf disk | |
cp -vf 2>/dev/null ${{env.DEV_DIR}}/* . ||: | |
- if: matrix.arch != 'x64' | |
run: | | |
sed_args=( | |
-E | |
-e 's@^(qemu_arch)=.*@\1=${{matrix.arch}}@' | |
-i run | |
) | |
sed $sed_args | |
- name: download EFI | |
if: fromJson(env.non_x86) | |
env: | |
efi_file: boot/EFI.fd | |
run: | | |
unset url | |
case ${{matrix.arch}} { | |
(arm64) url='https://retrage.github.io/edk2-nightly/bin/RELEASEAARCH64_QEMU_EFI.fd' ;; | |
(armv7a) url='https://retrage.github.io/edk2-nightly/bin/RELEASEARM_QEMU_EFI.fd' ;; | |
(loong64) url='https://mirrors.pku.edu.cn/loongarch/archlinux/images/QEMU_EFI_8.1.fd' ;; | |
} | |
if (($+url)) { | |
${{env.FETCH}} -o "$efi_file" $url | |
size=$(stat -c %s "$efi_file") | |
if ((size <= 4096)) { | |
exit 1 | |
} | |
} | |
- name: pack vm | |
run: | | |
cat run | |
loop_dev=${{steps.disk_partition.outputs.dev}} | |
sudo kpartx -dv $loop_dev ||: | |
sudo losetup --detach $loop_dev ||: | |
apt-install b3sum | |
files=( | |
*.img | |
run | |
Readme.md | |
connect-to-ssh | |
get-file-from-vm | |
send-file-to-vm | |
) | |
for f ($files ssh/* boot/*) { | |
b3sum $f >> blake3.txt | |
} | |
for f (ssh boot blake3.txt) { | |
files+=$f | |
} | |
args=( | |
--posix | |
--use-compress-program='zstdmt --long -18v' | |
-cf vm-minimal_${{env.CODENAME}}_${{matrix.arch}}.tar.zst | |
$files | |
) | |
tar $args | |
- name: release | |
uses: softprops/action-gh-release@v2 | |
with: | |
fail_on_unmatched_files: true | |
tag_name: ${{env.RELEASE_TAG}} | |
files: | | |
*.tar.zst |