diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b1a0eb22..4d4ebe7f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,6 +16,7 @@ jobs: env: script: "./tmp/build/install_rubikpi3.sh" base_image: "https://people.canonical.com/~platform/images/qualcomm-iot/rubikpi3/ubuntu-server-24.04/x00/ubuntu-24.04-preinstalled-server-arm64+rubikpi3-20250912-127.yaml" + image_name: "rubikpi3" runs-on: ubuntu-24.04-arm @@ -31,7 +32,7 @@ jobs: - name: Build rubikpi3 with mounting run: | chmod +x ./mount_rubikpi3.sh - ./mount_rubikpi3.sh ${{ env.base_image }} ${{ env.script }} ${{ github.ref_name }} + ./mount_rubikpi3.sh ${{ env.base_image }} ${{ env.script }} - name: Compress built image run: | @@ -45,7 +46,7 @@ jobs: retention-days: 1 build: - runs-on: ubuntu-24.04 + runs-on: ubuntu-24.04-arm strategy: fail-fast: false @@ -95,6 +96,8 @@ jobs: base_image: https://github.com/Joshua-Riek/ubuntu-rockchip/releases/download/v2.4.0/ubuntu-24.04-preinstalled-server-arm64-rock-5c.img.xz name: "Build for ${{ matrix.name }}" + env: + image_name: "${{ matrix.name }}" steps: - uses: actions/checkout@v4.1.7 @@ -103,19 +106,11 @@ jobs: - name: Fetch tags run: git fetch --tags --force - - uses: pguyot/arm-runner-action@HEAD + - name: Install dependencies and build image id: install_deps - with: - image_additional_mb: 1500 - bind_mount_repository: true - base_image: ${{ matrix.base_image }} - commands: | - chmod +x ${{matrix.script}} - ${{ matrix.script }} - chmod +x ./install_common.sh - ./install_common.sh - mkdir -p /opt/photonvision/ - echo "${{ github.ref_name }};${{ matrix.name }}" > /opt/photonvision/image-version + run: | + sudo chmod +x ./mount_image.sh + sudo -E ./mount_image.sh ${{ matrix.base_image }} ${{ matrix.script }} 1000 2 - name: Compress built image run: | diff --git a/install.sh b/install.sh index 7ad328ef..6f8b91b8 100755 --- a/install.sh +++ b/install.sh @@ -42,7 +42,7 @@ install_if_missing() { debug "Installing $1..." if [[ -z $TEST ]]; then - apt-get install --yes "$1" + apt-get --yes install "$1" # Always mark our upstream apt deps as held back, which will prevent the package # from being automatically installed, upgraded or removed apt-mark manual "$1" @@ -245,7 +245,7 @@ fi debug "Updating package list..." if [[ -z $TEST ]]; then - apt-get update + apt-get -q update fi debug "Updated package list." diff --git a/install_common.sh b/install_common.sh index a85ff6ee..fb41c46a 100755 --- a/install_common.sh +++ b/install_common.sh @@ -1,5 +1,4 @@ -#!/bin/bash -v - +#!/bin/bash # Verbose and exit on errors set -ex @@ -29,3 +28,7 @@ echo "photon:vision" | chpasswd cp -f ./files/issue.txt /etc/issue cp -f /etc/issue /etc/issue.net sed -i 's/#Banner none/Banner \/etc\/issue.net/g' /etc/ssh/sshd_config + +# Add photon version file +mkdir -p /opt/photonvision/ +echo "${GITHUB_REF_NAME};${image_name}" > /opt/photonvision/image-version diff --git a/install_dev_pi.sh b/install_dev_pi.sh index 0c60f5bf..fef13348 100644 --- a/install_dev_pi.sh +++ b/install_dev_pi.sh @@ -2,6 +2,14 @@ # Verbose and exit on errors set -ex + +# silence log spam from dpkg +cat > /etc/apt/apt.conf.d/99dpkg.conf << EOF +Dpkg::Progress-Fancy "0"; +APT::Color "0"; +Dpkg::Use-Pty "0"; +EOF + # Run normal photon installer chmod +x ./install.sh ./install.sh --install-nm=yes --arch=aarch64 diff --git a/install_limelight.sh b/install_limelight.sh index 7f0ac5f4..c1c0a058 100755 --- a/install_limelight.sh +++ b/install_limelight.sh @@ -3,6 +3,13 @@ # Verbose and exit on errors set -ex +# silence log spam from dpkg +cat > /etc/apt/apt.conf.d/99dpkg.conf << EOF +Dpkg::Progress-Fancy "0"; +APT::Color "0"; +Dpkg::Use-Pty "0"; +EOF + # Run normal photon installer chmod +x ./install.sh ./install.sh --install-nm=yes --arch=aarch64 diff --git a/install_opi5.sh b/install_opi5.sh index aadf6a88..397d16cd 100755 --- a/install_opi5.sh +++ b/install_opi5.sh @@ -1,4 +1,4 @@ -#!/bin/bash -v +#!/bin/bash # Verbose and exit on errors set -ex @@ -15,7 +15,14 @@ else fi echo "pi:raspberry" | chpasswd -apt-get update --quiet +# silence log spam from dpkg +cat > /etc/apt/apt.conf.d/99dpkg.conf << EOF +Dpkg::Progress-Fancy "0"; +APT::Color "0"; +Dpkg::Use-Pty "0"; +EOF + +apt-get -q update before=$(df --output=used / | tail -n1) # clean up stuff @@ -24,13 +31,13 @@ before=$(df --output=used / | tail -n1) echo "Purging snaps" rm -rf /var/lib/snapd/seed/snaps/* rm -f /var/lib/snapd/seed/seed.yaml -apt-get purge --yes --quiet lxd-installer lxd-agent-loader -apt-get purge --yes --quiet snapd +apt-get --yes -q purge lxd-installer lxd-agent-loader +apt-get --yes -q purge snapd # remove bluetooth daemon -apt-get purge --yes --quiet bluez +apt-get --yes -q purge bluez -apt-get --yes --quiet autoremove +apt-get --yes -q autoremove # remove firmware that (probably) isn't needed rm -rf /usr/lib/firmware/mrvl @@ -49,16 +56,20 @@ chmod +x ./install.sh ./install.sh --install-nm=yes --arch=aarch64 echo "Installing additional things" -apt-get install --yes --quiet libc6 libstdc++6 +apt-get --yes -qq install libc6 libstdc++6 # let netplan create the config during cloud-init rm -f /etc/netplan/00-default-nm-renderer.yaml +mkdir --parents /mnt/CIDATA +mount "${loopdev}p1" /mnt/CIDATA # set NetworkManager as the renderer in cloud-init -cp -f ./OPi5_CIDATA/network-config /boot/network-config - +cp -f ./OPi5_CIDATA/network-config /mnt/CIDATA/network-config # add customized user-data file for cloud-init -cp -f ./OPi5_CIDATA/user-data /boot/user-data +cp -f ./OPi5_CIDATA/user-data /mnt/CIDATA/user-data + +umount /mnt/CIDATA +rmdir /mnt/CIDATA # modify photonvision.service to enable big cores sed -i 's/# AllowedCPUs=4-7/AllowedCPUs=4-7/g' /lib/systemd/system/photonvision.service @@ -86,7 +97,7 @@ for btservice in $btservices; do done rm -rf /var/lib/apt/lists/* -apt-get --yes --quiet clean +apt-get --yes -qq clean rm -rf /usr/share/doc rm -rf /usr/share/locale/ diff --git a/install_pi.sh b/install_pi.sh index 777de037..55418ec9 100755 --- a/install_pi.sh +++ b/install_pi.sh @@ -3,6 +3,13 @@ # Verbose and exit on errors set -ex +# silence log spam from dpkg +cat > /etc/apt/apt.conf.d/99dpkg.conf << EOF +Dpkg::Progress-Fancy "0"; +APT::Color "0"; +Dpkg::Use-Pty "0"; +EOF + # Run normal photon installer chmod +x ./install.sh ./install.sh --install-nm=yes --arch=aarch64 diff --git a/install_rubikpi3.sh b/install_rubikpi3.sh index 1ace4d57..9ab7d13e 100644 --- a/install_rubikpi3.sh +++ b/install_rubikpi3.sh @@ -60,7 +60,3 @@ rm -rf /usr/share/locale/ echo '=== Running install_common.sh ===' chmod +x ./install_common.sh ./install_common.sh -echo '=== Creating version file ===' -mkdir -p /opt/photonvision/ -echo '{$1};rubikpi3' > /opt/photonvision/image-version -echo '=== Installation complete ===' \ No newline at end of file diff --git a/mount_image.sh b/mount_image.sh new file mode 100755 index 00000000..a2b9937f --- /dev/null +++ b/mount_image.sh @@ -0,0 +1,162 @@ +#!/bin/bash +set -exuo pipefail + +rootdir="./rootfs" +rootdir="$(realpath ${rootdir})" +echo "Root directory will be: ${rootdir}" + +url=$1 +install_script=$2 +additional_mb=$3 +rootpartition=$4 + +if [[ $# -ge 5 ]]; then + bootpartition=$5 + if [[ "x$rootpartion" = "x$bootpartition" ]]; then + echo "Boot partition cannot be equal to root partition" + exit 1 + fi +else + bootpartition= +fi + +image="base_image.img" + +#### +# Download the image +#### +wget -nv -O ${image}.xz "${url}" +xz -T0 -d ${image}.xz + +#### +# Prepare and mount the image +#### + +if [[ ${additional_mb} -gt 0 ]]; then + dd if=/dev/zero bs=1M count=${additional_mb} >> ${image} +fi + +export loopdev=$(losetup --find --show --partscan ${image}) +# echo "loopdev=${loopdev}" >> $GITHUB_OUTPUT + +part_type=$(blkid -o value -s PTTYPE "${loopdev}") +echo "Image is using ${part_type} partition table" + +if [[ ${additional_mb} -gt 0 ]]; then + if [[ "${part_type}" == "gpt" ]]; then + sgdisk -e "${loopdev}" + fi + parted --script "${loopdev}" resizepart ${rootpartition} 100% + e2fsck -p -f "${loopdev}p${rootpartition}" + resize2fs "${loopdev}p${rootpartition}" + echo "Finished resizing disk image." +fi + +sync + +echo "Partitions in the mounted image:" +lsblk "${loopdev}" + +if [[ -n "$bootpartition" ]]; then + bootdev="${loopdev}p${bootpartition}" +else + bootdev= +fi +rootdev="${loopdev}p${rootpartition}" + +mkdir --parents ${rootdir} +# echo "rootdir=${rootdir}" >> "$GITHUB_OUTPUT" +mount "${rootdev}" "${rootdir}" +if [[ -n "$bootdev" ]]; then + mkdir --parents "${rootdir}/boot" + mount "${bootdev}" "${rootdir}/boot" +fi + +# Set up the environment +mount -t proc /proc "${rootdir}/proc" +mount -t sysfs /sys "${rootdir}/sys" +mount --rbind /dev "${rootdir}/dev" + +# Temporarily replace resolv.conf for networking +mv -v "${rootdir}/etc/resolv.conf" "${rootdir}/etc/resolv.conf.bak" +cp -v /etc/resolv.conf "${rootdir}/etc/resolv.conf" + +#### +# Modify the image in chroot +#### +chrootscriptdir=/tmp/build +scriptdir=${rootdir}${chrootscriptdir} +mkdir --parents "${scriptdir}" +mount --bind "$(pwd)" "${scriptdir}" + +cat >> "${scriptdir}/commands.sh" << EOF +set -ex +export DEBIAN_FRONTEND=noninteractive +cd "${chrootscriptdir}" +echo "Running ${install_script}" +chmod +x "${install_script}" +"./${install_script}" +echo "Running install_common.sh" +chmod +x "./install_common.sh" +"./install_common.sh" +EOF + +cat -n "${scriptdir}/commands.sh" +chmod +x "${scriptdir}/commands.sh" + +sudo -E chroot "${rootdir}" /bin/bash -c "${chrootscriptdir}/commands.sh" + +#### +# Clean up and shrink image +#### + +if [[ -e "${rootdir}/etc/resolv.conf.bak" ]]; then + mv "${rootdir}/etc/resolv.conf.bak" "${rootdir}/etc/resolv.conf" +fi + +echo "Zero filling empty space" +if mountpoint "${rootdir}/boot"; then + (cat /dev/zero > "${rootdir}/boot/zeros" 2>/dev/null || true); sync; rm "${rootdir}/boot/zeros"; +fi + +(cat /dev/zero > "${rootdir}/zeros" 2>/dev/null || true); sync; rm "${rootdir}/zeros"; + +umount --recursive "${rootdir}" + +echo "Resizing root filesystem to minimal size." +e2fsck -v -f -p -E discard "${rootdev}" +resize2fs -M "${rootdev}" +rootfs_blocksize=$(tune2fs -l ${rootdev} | grep "^Block size" | awk '{print $NF}') +rootfs_blockcount=$(tune2fs -l ${rootdev} | grep "^Block count" | awk '{print $NF}') + +echo "Resizing rootfs partition." +rootfs_partstart=$(parted -m --script "${loopdev}" unit B print | grep "^${rootpartition}:" | awk -F ":" '{print $2}' | tr -d 'B') +rootfs_partsize=$((${rootfs_blockcount} * ${rootfs_blocksize})) +rootfs_partend=$((${rootfs_partstart} + ${rootfs_partsize} - 1)) +rootfs_partoldend=$(parted -m --script "${loopdev}" unit B print | grep "^${rootpartition}:" | awk -F ":" '{print $3}' | tr -d 'B') +if [ "$rootfs_partoldend" -gt "$rootfs_partend" ]; then + echo y | parted ---pretend-input-tty "${loopdev}" unit B resizepart "${rootpartition}" "${rootfs_partend}" +else + echo "Rootfs partition not resized as it was not shrunk" +fi + +free_space=$(parted -m --script "${loopdev}" unit B print free | tail -1) +if [[ "${free_space}" =~ "free" ]]; then + initial_image_size=$(stat -L --printf="%s" "${image}") + image_size=$(echo "${free_space}" | awk -F ":" '{print $2}' | tr -d 'B') + if [[ "${part_type}" == "gpt" ]]; then + # for GPT partition table, leave space at the end for the secondary GPT + # it requires 33 sectors, which is 16896 bytes + image_size=$((image_size + 16896)) + fi + echo "Shrinking image from ${initial_image_size} to ${image_size} bytes." + truncate -s "${image_size}" "${image}" + if [[ "${part_type}" == "gpt" ]]; then + # use sgdisk to fix the secondary GPT after truncation + sgdisk -e "${image}" + fi +fi + +losetup --detach "${loopdev}" + +echo "image=${image}" >> "$GITHUB_OUTPUT" \ No newline at end of file diff --git a/mount_rubikpi3.sh b/mount_rubikpi3.sh old mode 100644 new mode 100755 index 55505579..1a4c549d --- a/mount_rubikpi3.sh +++ b/mount_rubikpi3.sh @@ -133,6 +133,8 @@ sudo mount -t sysfs sysfs rootfs/sys sudo mount -t tmpfs tmpfs rootfs/run sudo mount --bind /dev rootfs/dev +findmnt + # Setup DNS resolution in chroot echo "=== Setting up DNS in chroot ===" sudo rm -f rootfs/etc/resolv.conf @@ -149,7 +151,7 @@ sudo mkdir -p rootfs/tmp/build/ sudo mount --bind "$(pwd)" rootfs/tmp/build/ echo "=== Checking for sudo in chroot and running script ===" -sudo chroot rootfs /bin/bash -c " +sudo -E chroot rootfs /bin/bash -c " set -exv export DEBIAN_FRONTEND=noninteractive if ! command -v sudo &> /dev/null; then @@ -163,8 +165,10 @@ sudo chroot rootfs /bin/bash -c " echo '=== Making script executable ===' chmod +x ${script} - echo '=== Running ${script} with arguments: ${@:3} ===' - ./${script} ${@:3} + echo '=== Running ${script} ===' + ./${script} + echo '=== Zero filling empty space ===' + (cat /dev/zero > /zeros 2>/dev/null || true); sync; rm /zeros; " # Cleanup mounts