mirror of https://github.com/bohanyang/debi
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
415 lines
16 KiB
415 lines
16 KiB
name: Test Debian Installation Script |
|
|
|
on: |
|
push: |
|
branches: [ master, main ] |
|
pull_request: |
|
branches: [ master, main ] |
|
workflow_dispatch: |
|
|
|
jobs: |
|
test-debi: |
|
name: "Debian ${{ matrix.version }} | ${{ matrix.mirror }} | ethx:${{ matrix.ethx }} | user:${{ matrix.user }} | nc:${{ matrix.network_console }}" |
|
runs-on: ubuntu-22.04 |
|
timeout-minutes: 45 |
|
|
|
permissions: |
|
contents: read |
|
|
|
strategy: |
|
fail-fast: false |
|
matrix: |
|
# Target Debian version to install |
|
version: [10, 11, 12] |
|
# Mirror configuration |
|
mirror: ['default', 'ustc', 'cloudflare'] |
|
# Network interface naming |
|
ethx: [true, false] |
|
# User to create |
|
user: ['root', 'debian'] |
|
# Enable network console for remote installation |
|
network_console: [true] |
|
|
|
# Exclude combinations to keep matrix manageable (~12 tests) |
|
exclude: |
|
# Debian 10 - only test with default mirror and root user |
|
- version: 10 |
|
mirror: 'ustc' |
|
- version: 10 |
|
mirror: 'cloudflare' |
|
- version: 10 |
|
user: 'debian' |
|
- version: 10 |
|
ethx: false |
|
|
|
# For Debian 11 & 12, test key combinations |
|
# Skip debian user with ustc (redundant) |
|
- version: 11 |
|
mirror: 'ustc' |
|
user: 'debian' |
|
- version: 12 |
|
mirror: 'ustc' |
|
user: 'debian' |
|
|
|
# Skip cloudflare without ethx (less common) |
|
- mirror: 'cloudflare' |
|
ethx: false |
|
|
|
# Skip default mirror without ethx for Debian 12 |
|
- version: 12 |
|
mirror: 'default' |
|
ethx: false |
|
|
|
steps: |
|
- name: Checkout repository |
|
uses: actions/checkout@v4 |
|
|
|
- name: Enable KVM |
|
run: | |
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules |
|
sudo udevadm control --reload-rules |
|
sudo udevadm trigger --name-match=kvm |
|
|
|
- name: Cache APT packages |
|
uses: actions/cache@v4 |
|
id: cache-apt |
|
with: |
|
path: ~/apt-cache |
|
key: ${{ runner.os }}-apt-${{ hashFiles('.github/workflows/test.yml') }} |
|
restore-keys: | |
|
${{ runner.os }}-apt- |
|
|
|
- name: Restore APT cache |
|
if: steps.cache-apt.outputs.cache-hit == 'true' |
|
run: | |
|
if [ -d ~/apt-cache ] && [ "$(ls -A ~/apt-cache 2>/dev/null)" ]; then |
|
sudo cp ~/apt-cache/*.deb /var/cache/apt/archives/ 2>/dev/null || true |
|
fi |
|
|
|
- name: Install dependencies |
|
run: | |
|
sudo apt-get update |
|
sudo apt-get install -y qemu-kvm qemu-utils cloud-image-utils genisoimage expect sshpass |
|
|
|
- name: Save APT cache |
|
if: steps.cache-apt.outputs.cache-hit != 'true' |
|
run: | |
|
mkdir -p ~/apt-cache |
|
sudo cp /var/cache/apt/archives/*.deb ~/apt-cache/ 2>/dev/null || true |
|
sudo chown -R $USER:$USER ~/apt-cache |
|
|
|
- name: Check KVM availability |
|
run: | |
|
ls -la /dev/kvm |
|
kvm-ok || true |
|
|
|
- name: Cache Debian cloud image |
|
id: cache-debian-image |
|
uses: actions/cache@v4 |
|
with: |
|
path: /tmp/debian-11-base.qcow2 |
|
key: debian-11-cloud-image-${{ hashFiles('.github/workflows/test.yml') }} |
|
restore-keys: | |
|
debian-11-cloud-image- |
|
|
|
- name: Download Debian 11 cloud image |
|
if: steps.cache-debian-image.outputs.cache-hit != 'true' |
|
run: | |
|
wget -O /tmp/debian-11-base.qcow2 "https://cloud.debian.org/images/cloud/bullseye/latest/debian-11-generic-amd64.qcow2" |
|
|
|
- name: Create working disk image |
|
run: | |
|
qemu-img create -f qcow2 -F qcow2 -b /tmp/debian-11-base.qcow2 /tmp/test-disk.qcow2 20G |
|
qemu-img resize /tmp/test-disk.qcow2 20G |
|
|
|
- name: Create cloud-init configuration |
|
run: | |
|
mkdir -p /tmp/cloud-init |
|
|
|
# meta-data |
|
cat > /tmp/cloud-init/meta-data << 'EOF' |
|
instance-id: test-debi-vm |
|
local-hostname: test-debi |
|
EOF |
|
|
|
# user-data with root access |
|
cat > /tmp/cloud-init/user-data << 'EOF' |
|
#cloud-config |
|
users: |
|
- name: root |
|
lock_passwd: false |
|
plain_text_passwd: 'rootpass123' |
|
|
|
ssh_pwauth: true |
|
disable_root: false |
|
|
|
packages: |
|
- wget |
|
- curl |
|
|
|
runcmd: |
|
- sed -i 's/^#PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config |
|
- systemctl restart sshd |
|
EOF |
|
|
|
# Create ISO for cloud-init |
|
genisoimage -output /tmp/cloud-init.iso \ |
|
-volid cidata -rational-rock -joliet \ |
|
/tmp/cloud-init/user-data /tmp/cloud-init/meta-data |
|
|
|
- name: Start VM and wait for boot |
|
run: | |
|
# Start QEMU in background (daemonize mode doesn't support -nographic) |
|
sudo qemu-system-x86_64 \ |
|
-machine type=pc,accel=kvm \ |
|
-cpu host \ |
|
-m 2048 \ |
|
-display none \ |
|
-drive file=/tmp/test-disk.qcow2,format=qcow2,if=virtio \ |
|
-cdrom /tmp/cloud-init.iso \ |
|
-net nic,model=virtio \ |
|
-net user,hostfwd=tcp::2222-:22 \ |
|
-serial file:/tmp/serial.log \ |
|
-monitor unix:/tmp/qemu-monitor.sock,server,nowait \ |
|
-daemonize |
|
|
|
# Fix permissions on serial.log so it can be uploaded as artifact |
|
sudo chmod 644 /tmp/serial.log |
|
|
|
echo "Waiting for VM to boot and SSH to become available..." |
|
for i in {1..60}; do |
|
if sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 root@localhost 'echo SSH ready' 2>/dev/null; then |
|
echo "SSH is ready!" |
|
break |
|
fi |
|
echo "Attempt $i/60: Waiting for SSH..." |
|
sleep 5 |
|
done |
|
|
|
# Verify SSH is working |
|
sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 root@localhost 'uname -a' |
|
|
|
- name: Upload debi.sh to VM |
|
run: | |
|
sshpass -p 'rootpass123' scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P 2222 \ |
|
./debi.sh root@localhost:/root/debi.sh |
|
|
|
- name: Make script executable |
|
run: | |
|
sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 root@localhost \ |
|
'chmod +x /root/debi.sh' |
|
|
|
- name: Build debi.sh arguments from matrix parameters |
|
id: build-args |
|
run: | |
|
# Start with version |
|
ARGS="--version ${{ matrix.version }}" |
|
|
|
# Add mirror flag |
|
if [ "${{ matrix.mirror }}" = "ustc" ]; then |
|
ARGS="$ARGS --ustc" |
|
elif [ "${{ matrix.mirror }}" = "cloudflare" ]; then |
|
ARGS="$ARGS --cloudflare" |
|
fi |
|
# default mirror doesn't need a flag |
|
|
|
# Add ethx flag |
|
if [ "${{ matrix.ethx }}" = "true" ]; then |
|
ARGS="$ARGS --ethx" |
|
fi |
|
|
|
# Add network-console flag |
|
if [ "${{ matrix.network_console }}" = "true" ]; then |
|
ARGS="$ARGS --network-console" |
|
fi |
|
|
|
# Add user and password |
|
ARGS="$ARGS --user ${{ matrix.user }} --password newpass123" |
|
|
|
echo "args=$ARGS" >> $GITHUB_OUTPUT |
|
echo "Generated arguments: $ARGS" |
|
|
|
- name: Run debi.sh with test arguments |
|
run: | |
|
echo "Running debi.sh with arguments: ${{ steps.build-args.outputs.args }}" |
|
|
|
# Run the script with a pseudo-TTY to handle stty commands |
|
# Use 'script' command to provide a PTY for non-interactive SSH |
|
sshpass -p 'rootpass123' ssh -t -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 root@localhost \ |
|
"script -q -c 'cd /root && ./debi.sh ${{ steps.build-args.outputs.args }}' /tmp/debi-output.log; echo \"Script exit code: \$?\" >> /tmp/debi-output.log" || echo "Script execution finished with non-zero exit code, will validate via file checks" |
|
|
|
- name: Download and check debi.sh output |
|
run: | |
|
sshpass -p 'rootpass123' scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P 2222 \ |
|
root@localhost:/tmp/debi-output.log /tmp/debi-output.log || echo "Could not download output log" |
|
|
|
if [ -f /tmp/debi-output.log ]; then |
|
echo "=== debi.sh output ===" |
|
cat /tmp/debi-output.log |
|
echo "======================" |
|
fi |
|
|
|
- name: Verify installation preparation |
|
run: | |
|
echo "Checking if Debian installer files were downloaded..." |
|
|
|
# Check if installer directory was created |
|
sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 root@localhost \ |
|
'ls -la /boot/debian-* || echo "No installer directory found"' |
|
|
|
# Check for installer components |
|
sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 root@localhost \ |
|
'ls -lh /boot/debian-*/linux /boot/debian-*/initrd.gz 2>/dev/null || echo "Installer files not found"' |
|
|
|
# Check GRUB configuration |
|
sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 root@localhost \ |
|
'cat /etc/default/grub.d/zz-debi.cfg 2>/dev/null || echo "GRUB config not found"' |
|
|
|
# Check if GRUB was updated with installer entry |
|
sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 root@localhost \ |
|
'grep -A 5 "Debian Installer" /boot/grub/grub.cfg || echo "Debian Installer entry not found in GRUB"' |
|
|
|
- name: Validate installation files exist |
|
run: | |
|
# Verify key files exist |
|
sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 root@localhost \ |
|
'test -f /boot/debian-*/linux && test -f /boot/debian-*/initrd.gz && echo "Installation files verified!" || exit 1' |
|
|
|
# Verify GRUB config was updated |
|
sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 root@localhost \ |
|
'grep -q "Debian Installer" /boot/grub/grub.cfg && echo "GRUB config verified!" || exit 1' |
|
|
|
echo "✓ Script execution completed successfully" |
|
echo "✓ Installer files downloaded" |
|
echo "✓ GRUB configuration updated" |
|
echo "✓ System ready for reboot into Debian installer" |
|
|
|
- name: Reboot into Debian installer |
|
run: | |
|
echo "Rebooting VM to start Debian installation..." |
|
|
|
# Trigger reboot - the system should boot into Debian installer |
|
sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 root@localhost \ |
|
'reboot' 2>/dev/null || true |
|
|
|
echo "Reboot command sent. VM will now boot into Debian installer." |
|
echo "The installation process will take several minutes..." |
|
|
|
- name: Wait for Debian installation to complete |
|
run: | |
|
echo "Waiting for Debian installation to complete..." |
|
echo "This typically takes 10-20 minutes depending on network speed and mirror." |
|
echo "The installer will automatically reboot after completion." |
|
|
|
# Wait for the old system to go down |
|
sleep 30 |
|
|
|
# The installation process: |
|
# 1. VM boots into Debian installer (netboot) |
|
# 2. Installer runs unattended using preseed configuration |
|
# 3. System installs packages from mirror |
|
# 4. System reboots into newly installed Debian |
|
|
|
# We'll poll for SSH availability with the NEW password |
|
# This indicates the new system has been installed and booted |
|
|
|
MAX_WAIT=1800 # 30 minutes maximum wait |
|
POLL_INTERVAL=30 |
|
ELAPSED=0 |
|
|
|
echo "Polling for new system availability (max wait: ${MAX_WAIT}s)..." |
|
|
|
while [ $ELAPSED -lt $MAX_WAIT ]; do |
|
echo "Attempt at ${ELAPSED}s: Trying to connect with new credentials..." |
|
|
|
# Try to connect with the NEW password (set by debi.sh installation) |
|
if sshpass -p 'newpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 -p 2222 ${{ matrix.user }}@localhost 'echo "NEW SYSTEM CONNECTED!"' 2>/dev/null; then |
|
echo "✓ Successfully connected to newly installed system!" |
|
echo "✓ Installation completed successfully!" |
|
exit 0 |
|
fi |
|
|
|
# Also check if old system is still responding (installation not started yet) |
|
if sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -p 2222 root@localhost 'echo "old system"' 2>/dev/null; then |
|
echo " Old system still responding - installation may not have started" |
|
fi |
|
|
|
sleep $POLL_INTERVAL |
|
ELAPSED=$((ELAPSED + POLL_INTERVAL)) |
|
done |
|
|
|
echo "✗ Timeout: Could not connect to new system after ${MAX_WAIT}s" |
|
exit 1 |
|
|
|
- name: Verify new system installation |
|
run: | |
|
echo "Verifying the newly installed Debian system..." |
|
|
|
# Connect with new credentials and verify system |
|
sshpass -p 'newpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 ${{ matrix.user }}@localhost << 'VERIFY_EOF' |
|
echo "=== System Information ===" |
|
uname -a |
|
|
|
echo "" |
|
echo "=== OS Release ===" |
|
cat /etc/os-release |
|
|
|
echo "" |
|
echo "=== Disk Usage ===" |
|
df -h |
|
|
|
echo "" |
|
echo "=== Memory Info ===" |
|
free -h |
|
|
|
echo "" |
|
echo "=== Network Configuration ===" |
|
ip addr show |
|
|
|
echo "" |
|
echo "=== Hostname ===" |
|
hostname |
|
|
|
echo "" |
|
echo "✓ New Debian system is running successfully!" |
|
VERIFY_EOF |
|
|
|
- name: Collect debug information on failure |
|
if: failure() |
|
run: | |
|
echo "=== Serial console log ===" |
|
cat /tmp/serial.log || echo "No serial log available" |
|
|
|
echo "=== Trying to connect with old credentials ===" |
|
sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -p 2222 root@localhost \ |
|
'journalctl -n 100' 2>/dev/null || echo "Could not connect with old credentials" |
|
|
|
echo "=== Trying to connect with new credentials ===" |
|
sshpass -p 'newpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -p 2222 ${{ matrix.user }}@localhost \ |
|
'journalctl -n 100' 2>/dev/null || echo "Could not connect with new credentials" |
|
|
|
- name: Shutdown VM |
|
if: always() |
|
run: | |
|
# Try graceful shutdown with new credentials first (if new system is installed) |
|
sshpass -p 'newpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -p 2222 ${{ matrix.user }}@localhost \ |
|
'sudo poweroff' 2>/dev/null || true |
|
|
|
# Also try with old credentials (if still on old system) |
|
sshpass -p 'rootpass123' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -p 2222 root@localhost \ |
|
'poweroff' 2>/dev/null || true |
|
|
|
sleep 5 |
|
|
|
# Force kill QEMU if still running |
|
sudo pkill qemu-system-x86_64 || true |
|
|
|
- name: Upload logs as artifacts |
|
if: always() |
|
uses: actions/upload-artifact@v4 |
|
with: |
|
name: test-logs-v${{ matrix.version }}-${{ matrix.mirror }}-ethx${{ matrix.ethx }}-${{ matrix.user }}-nc${{ matrix.network_console }} |
|
path: | |
|
/tmp/debi-output.log |
|
/tmp/serial.log |
|
if-no-files-found: warn
|
|
|