build(vm-minimal): rm run-vm
step
#11
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: | |
DEV_DIR: assets/vm-minimal | |
VM_SHUTDOWN: "ssh -F ssh/vm.sshconf vm poweroff ||:" | |
DEB_ENV: "DEBIAN_FRONTEND=noninteractive" | |
# DEBIAN_FRONTEND: noninteractive | |
on: | |
push: | |
# branches: ["build"] | |
paths: | |
- .github/workflows/vm-minimal.yml | |
jobs: | |
build: | |
strategy: | |
fail-fast: true | |
matrix: | |
include: | |
- arch: arm64 | |
deb_arch: arm64 | |
qemu_pkg_arch: arm | |
# - arch: x64 | |
# deb_arch: amd64 | |
# qemu_pkg_arch: x86 | |
runs-on: ubuntu-latest | |
env: | |
NOT_X86: ${{ !contains(fromJson('["x64", "x86"]'), 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 | |
run: | | |
sudo mkdir -pv rootfs/usr/local/bin/ | |
for p ('/' 'rootfs/') { | |
sudo cp -pv ${DEV_DIR}/scripts/* ${p}usr/local/bin | |
} | |
apt-install systemd-container & | |
task=$! | |
image=ghcr.io/2cd/debian-sid:${{matrix.arch}} | |
docker pull $image | |
name=$(docker create $image) | |
docker export $name -o rootfs.tar | |
sudo tar -C rootfs --overwrite -xf rootfs.tar | |
unlink rootfs.tar | |
docker rm $name | |
wait $task | |
- name: install qemu-user | |
if: env.NOT_X86 | |
run: apt-install qemu-user-static | |
- name: setup ssh server | |
env: | |
mirror_src: "rootfs/etc/apt/sources.list.d/mirror.sources" | |
run: | | |
tmux new-session -d -s install_qemu 'apt-install qemu-system-${{matrix.qemu_pkg_arch}}' | |
sudo cp $mirror_src ${mirror_src}.bak | |
awk_args=( | |
-v true=1 | |
-v false=0 | |
'/experimental/{ | |
cond = true | |
} | |
/-debug/{ | |
cond = false | |
} | |
/snapshot\.debian/ { | |
if ($1 == "URIs:") { | |
sub($0, sprintf("%s https://cloudflaremirrors.com/debian/", $1)) | |
} | |
} | |
cond && /Enabled:/{ | |
sub("no", "yes") | |
cond = false | |
} | |
{ print }' | |
${mirror_src}.bak | |
) | |
awk $awk_args | sudo tee ${mirror_src} | |
sudo rm -vf rootfs/etc/resolv.conf | |
for i (zsh busybox) { | |
file=$commands[$i] | |
bin=/usr/local/bin/$i | |
if [[ -e $bin ]] { | |
file=$bin | |
} | |
sudo cp -vf $file rootfs/bin/ | |
} | |
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 | |
) | |
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/ | |
sudo install -Dm700 ${{env.DEV_DIR}}/libexec/resize-partition rootfs/usr/libexec/tmm/qemu/resize-partition | |
sudo cp -a rootfs kernel_r | |
sudo systemd-nspawn -D kernel_r sh -c 'apt-install linux-image-cloud-${{matrix.deb_arch}}' | |
- name: copy kernel | |
run: | | |
mkdir -p boot | |
for f (vmlinuz 'initrd.img') { | |
real_path=$(realpath kernel_r/$f) | |
real_fname=${real_path:t} | |
cp -v $real_path boot/$f | |
ln -svf $f boot/$real_fname | |
} | |
for f (config System) { | |
cp -v kernel_r/boot/${f}* boot/ ||: | |
} | |
tmux new-session -d -s nspawn 'sudo systemd-nspawn -bD rootfs' | |
sleep 2 | |
# sudo rm -rf kernel_r | |
while {! ssh -F ssh/vm.sshconf vm exit} { | |
sleep 1 | |
} | |
- name: set network & serial name | |
id: set_serial_name | |
run: | | |
case ${{matrix.arch}} { | |
(x64|x86) | |
network_iface=enp0s2 | |
tty_serial=ttyS0 | |
;; | |
(*) | |
network_iface=enp0s1 | |
tty_serial=ttyAMA0 | |
;; | |
} | |
outputs=( | |
"network=$network_iface" | |
"tty=$tty_serial" | |
) | |
printf "%s\n" $outputs >> $GITHUB_OUTPUT | |
- 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 qemu-guest-agent udev fdisk dosfstools | |
network_iface=${{steps.set_serial_name.outputs.network}} | |
tty_serial=${{steps.set_serial_name.outputs.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 | |
} | |
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: disk partition | |
id: disk_partition | |
run: | | |
${{env.VM_SHUTDOWN}} | |
if ((! $+commands[qemu-img])) { | |
apt-install | |
} | |
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 | |
- name: configure kvm | |
run: | | |
cp -vf 2>/dev/null ${{env.DEV_DIR}}/* . ||: | |
sudo adduser $USER kvm ||: | |
sudo chmod 666 /dev/kvm ||: | |
- if: matrix.arch != 'x64' | |
run: | | |
enable_efi=true | |
tty_serial=${{steps.set_serial_name.outputs.tty}} | |
if [[ ${{matrix.arch}} == x86 ]] { | |
enable_efi=false | |
} | |
sed_args=( | |
-E | |
-e 's@^(qemu_arch)=.*@\1=${{matrix.arch}}@' | |
) | |
if {$enable_efi} { | |
sed_args+=( | |
-e 's@^(enable_efi)=.*@\1=true@' | |
-e "s@( console)=ttyS0,@\1=${tty_serial},@" | |
) | |
} | |
sed_args+=( -i run ) | |
sed $sed_args | |
- name: download EFI | |
if: env.NOT_X86 | |
run: | | |
unset url | |
case ${{matrix.arch}} { | |
(arm64) url='https://retrage.github.io/edk2-nightly/bin/RELEASEAARCH64_QEMU_EFI.fd' ;; | |
# (riscv64) url='https://retrage.github.io/edk2-nightly/bin/RELEASERISCV64_VIRT.fd' ;; | |
} | |
if (($+url)) { | |
curl -Lo boot/EFI.fd $url | |
exit | |
} | |
cat run | |
- name: pack vm | |
run: | | |
loop_dev=${{steps.disk_partition.outputs.dev}} | |
sudo kpartx -dv $loop_dev ||: | |
sudo losetup --detach $loop_dev ||: | |
apt-install b3sum | |
while {lsof disk.img} { | |
sleep 1 | |
} | |
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_${{matrix.arch}}.tar.zst | |
$files | |
) | |
tar $args | |
- name: release | |
uses: softprops/action-gh-release@v2 | |
with: | |
fail_on_unmatched_files: true | |
tag_name: unstable | |
files: | | |
*.tar.zst |