diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d4eac13..9e077de 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,6 +12,34 @@ concurrency: cancel-in-progress: true jobs: + rubik: + 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" + + runs-on: ubuntu-24.04 + + name: "Build for RubikPi3" + + steps: + - uses: actions/checkout@v4.1.7 + with: + fetch-depth: 0 + - name: Fetch tags + run: git fetch --tags --force + + - name: Build rubikpi3 with mounting + run: | + chmod +x ./mount_rubikpi3.sh + ./mount_rubikpi3.sh ${{ env.base_image }} ${{ env.script }} ${{ github.ref_name }} + + - uses: actions/upload-artifact@v4.3.4 + with: + name: photonvision_rubikpi3.tar.xz + path: photonvision_rubikpi3.tar.xz + if-no-files-found: error + retention-days: 1 + build: runs-on: ubuntu-24.04 @@ -95,7 +123,7 @@ jobs: retention-days: 1 release: - needs: [build] + needs: [build, rubik] runs-on: ubuntu-24.04 steps: # Download literally every single artifact @@ -111,17 +139,10 @@ jobs: **/*.xz if: github.event_name == 'push' # Upload all xz archives to GH tag if tagged - - uses: softprops/action-gh-release@v2.0.8 - with: - files: | - **/*opi5*.xz - if: startsWith(github.ref, 'refs/tags/v') - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: softprops/action-gh-release@v2.0.8 + - uses: softprops/action-gh-release@v2.4.0 with: files: | - **/!(*opi5*).xz + **.xz if: startsWith(github.ref, 'refs/tags/v') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/install_rubikpi3.sh b/install_rubikpi3.sh new file mode 100644 index 0000000..5714bf5 --- /dev/null +++ b/install_rubikpi3.sh @@ -0,0 +1,66 @@ +#!/bin/bash -v +# Verbose and exit on errors +set -ex + +cd /tmp/build +echo '=== Current directory: \$(pwd) ===' +echo '=== Files in current directory: ===' +ls -la + +REPO_ENTRY="deb http://apt.thundercomm.com/rubik-pi-3/noble ppa main" +HOST_ENTRY="151.106.120.85 apt.rubikpi.ai" # TODO: Remove legacy + +# First update the APT +apt-get update -y + + +# TODO: Remove legacy +sed -i "/$HOST_ENTRY/d" /etc/hosts || true +sed -i '/apt.rubikpi.ai ppa main/d' /etc/apt/sources.list || true + +if ! grep -q "^[^#]*$REPO_ENTRY" /etc/apt/sources.list; then + echo "$REPO_ENTRY" | tee -a /etc/apt/sources.list >/dev/null +fi + +# Add the GPG key for the RUBIK Pi PPA +wget -qO - https://thundercomm.s3.dualstack.ap-northeast-1.amazonaws.com/uploads/web/rubik-pi-3/tools/key.asc | tee /etc/apt/trusted.gpg.d/rubikpi3.asc + +apt update -y + +apt-get -y --allow-downgrades install libsqlite3-0=3.45.1-1ubuntu2 +apt-get -y install libqnn1 libsnpe1 tensorflow-lite-qcom-apps qcom-adreno1 + +ln -sf libOpenCL.so.1 /usr/lib/aarch64-linux-gnu/libOpenCL.so # Fix for snpe-tools + +# Run normal photon installer +chmod +x ./install.sh +./install.sh --install-nm=yes --arch=aarch64 + +# Enable ssh +systemctl enable ssh + + +# Remove extra packages too +echo "Purging extra things" +apt-get purge -y gdb gcc g++ linux-headers* libgcc*-dev +apt-get autoremove -y + +echo "Installing additional things" + +apt-get update -y + +apt-get install -y device-tree-compiler + +rm -rf /var/lib/apt/lists/* +apt-get clean + +rm -rf /usr/share/doc +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_rubikpi3.sh b/mount_rubikpi3.sh new file mode 100644 index 0000000..d4f8027 --- /dev/null +++ b/mount_rubikpi3.sh @@ -0,0 +1,216 @@ +# Set variables + +base_image=$1 +script=$2 + +set -exv + +# Install required packages +sudo apt-get update +sudo apt-get install -y qemu-user-static wget xz-utils +# If base_image ends with .yaml, treat it as a manifest and skip download +if [[ "$base_image" == *.yaml ]]; then + + # Download and process manifest + wget -O manifest.yaml "${base_image}" + + echo "=== Manifest contents ===" + cat manifest.yaml + echo "=========================" + + # Process each component using awk to extract URL and SHA256 + awk '/url:/ { + sub(/.*url:[[:space:]]*/,"",$0); + url=$0 + } + /sha256sum:/ { + sub(/.*sha256sum:[[:space:]]*/,"",$0); + print url, $0 + }' manifest.yaml | while read -r url sha; do + filename=$(basename "$url") + echo "Downloading: $filename from $url" + wget -nv -O "$filename" "$url" + echo "$sha $filename" | sha256sum -c - + done + + echo "=== Downloaded files ===" + ls -lh + echo "========================" +elif [[ "$base_image" == *.tar.xz ]]; then + # Directly download the tar.xz file + wget -nv -O base_image.tar.xz "${base_image}" + tar -xJf base_image.tar.xz +else + echo "Error: base_image must be a .yaml manifest or .tar.xz" + exit 1 +fi +# Find the rootfs image - look for the largest .img.xz file or one with "rootfs" in name +ROOTFS_IMG_XZ="" + +# First try to find a file with "rootfs" in the name +for file in *.img.xz; do + if [[ "$file" == *"rootfs"* ]]; then + ROOTFS_IMG_XZ="$file" + echo "Found rootfs image by name: $ROOTFS_IMG_XZ" + break + fi +done + +# If not found, use the largest .img.xz file +if [ -z "$ROOTFS_IMG_XZ" ]; then + ROOTFS_IMG_XZ=$(ls -S *.img.xz 2>/dev/null | head -n1) + echo "Using largest .img.xz file as rootfs: $ROOTFS_IMG_XZ" +fi + +if [ -z "$ROOTFS_IMG_XZ" ] || [ ! -f "$ROOTFS_IMG_XZ" ]; then + echo "Error: Could not find a suitable rootfs image file" + echo "Available files:" + ls -la + exit 1 +fi + +ROOTFS_IMG="${ROOTFS_IMG_XZ%.xz}" + +echo "Extracting rootfs image: $ROOTFS_IMG_XZ" +xz -d "$ROOTFS_IMG_XZ" + +if [ ! -f "$ROOTFS_IMG" ]; then + echo "Error: Failed to extract $ROOTFS_IMG" + exit 1 +fi + +echo "Using rootfs image: $ROOTFS_IMG" + +# This uses a fixed offset for Ubuntu preinstalled server images +echo "=== Mounting rootfs with fixed offset (rpiimager method) ===" +mkdir -p ./rootfs + +# Calculate offset: 4096 bytes/sector * 139008 sectors = 569,376,768 bytes +OFFSET=$((4096*139008)) +echo "Using offset: $OFFSET bytes (sector 139008)" + +sudo mount -o rw,loop,offset=$OFFSET "$ROOTFS_IMG" ./rootfs + +if [ $? -ne 0 ]; then + echo "Error: Failed to mount image with fixed offset" + exit 1 +fi + +echo "=== Mount successful ===" +ls -la ./rootfs | head -20 + +# Check if this is a Canonical or Modified image (from rpiimager logic) +if [ -f "rootfs/etc/ImgType" ]; then + cp rootfs/etc/ImgType ImgType +else + echo "Canonical" > ImgType +fi +read ImgType < ImgType +echo "Image type: $ImgType" + +# Expand image if it's a Canonical image (first time modification) +if [ "$ImgType" == "Canonical" ]; then + echo "=== Marking image as CustomIDE ===" + sudo chroot rootfs /bin/bash -c " + touch /etc/ImgType + echo 'CustomIDE' > /etc/ImgType + " + + echo "=== Unmounting for expansion ===" + sudo umount ./rootfs + + # Expand the image by 4GB (reduced from 10GB to fit GitHub Actions disk space) + echo "=== Expanding image by 4GB ===" + dd if=/dev/zero bs=1M count=4096 >> "$ROOTFS_IMG" + + # Remount after expansion + echo "=== Remounting after expansion ===" + sudo mount -o rw,loop,offset=$OFFSET "$ROOTFS_IMG" ./rootfs +elif [ "$ImgType" == "CustomIDE" ]; then + echo "Image already customized, no expansion needed" +fi + +rm -f ImgType + +echo "=== Filesystem ready ===" + +# Setup chroot environment +sudo mount -t proc proc rootfs/proc +sudo mount -t sysfs sysfs rootfs/sys +sudo mount -t tmpfs tmpfs rootfs/run +sudo mount --bind /dev rootfs/dev + +# Setup DNS resolution in chroot +echo "=== Setting up DNS in chroot ===" +sudo rm -f rootfs/etc/resolv.conf +sudo cp /etc/resolv.conf rootfs/etc/resolv.conf +sudo cp /etc/hosts rootfs/etc/hosts + +# Copy qemu static binaries for ARM emulation +sudo cp /usr/bin/qemu-arm-static rootfs/usr/bin/ || true +sudo cp /usr/bin/qemu-aarch64-static rootfs/usr/bin/ || true + +# DEPRECATED: using bind mount instead +# Copy repository into chroot (excluding mounted directories and problematic files) +# sudo mkdir -p rootfs/tmp/build/ +# sudo rsync -av --exclude=rootfs --exclude=.git --exclude=*.img --exclude=*.xz . rootfs/tmp/build/ + +# Mount and bind the current directory into /tmp/build in chroot +sudo mkdir -p rootfs/tmp/build/ +sudo mount --bind "$(pwd)" rootfs/tmp/build/ + +# Install sudo in the chroot environment (needed by install scripts) +echo "=== Installing sudo in chroot ===" +sudo chroot rootfs /usr/bin/qemu-aarch64-static /bin/bash -c "set -exv && DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y sudo" + +# Run the installation scripts in chroot with verbose output +echo "=== Running installation scripts in chroot ===" +sudo chroot rootfs /usr/bin/qemu-aarch64-static /bin/bash -c " + set -exv + export DEBIAN_FRONTEND=noninteractive + echo '=== Making script executable ===' + chmod +x ${script} + echo '=== Running ${script} with arguments: ${@:3} ===' + ./${script} ${@:3} +" + +# Cleanup mounts +sudo umount rootfs/dev || true +sudo umount rootfs/run || true +sudo umount rootfs/sys || true +sudo umount rootfs/proc || true +sudo umount rootfs/tmp/build/ || true +sudo umount rootfs || true + +# More aggressive loop device cleanup +if [ -n "$LOOP_DEV" ]; then + sudo losetup -d "$LOOP_DEV" || true +fi + +# Find and detach any remaining loop devices pointing to our image +sudo losetup -j "$ROOTFS_IMG" | cut -d: -f1 | xargs -r sudo losetup -d + +# Ensure all filesystem operations are complete +sync +sleep 3 + +# Assembly process for remaining files +mkdir -p photonvision_rubikpi3 +# Extract .tar.gz archive(s) directly into photonvision_rubikpi3 if they exist +if ls *.tar.gz 1>/dev/null 2>&1; then + tar -xzf *.tar.gz -C photonvision_rubikpi3 +fi +# Move all files (rawprogram, dtb, img) into photonvision_rubikpi3 +mv rawprogram*.xml photonvision_rubikpi3/ 2>/dev/null || true +mv dtb.bin photonvision_rubikpi3/ 2>/dev/null || true +mv *.img photonvision_rubikpi3/ 2>/dev/null || true + +# Flatten directory structure - move all files from subdirectories to photonvision_rubikpi3 root +find photonvision_rubikpi3 -mindepth 2 -type f -exec mv {} photonvision_rubikpi3/ \; +# Remove empty subdirectories +find photonvision_rubikpi3 -mindepth 1 -type d -empty -delete + +# Set output for later steps +# Save the rootfs image path for later steps +echo "rootfs_image=$ROOTFS_IMG" >> $GITHUB_ENV +tar -cJf photonvision_rubikpi3.tar.xz -C . photonvision_rubikpi3