Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 133 additions & 30 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,37 +1,140 @@
name: Build Image

on: push
on:
repository_dispatch:
push:

jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- board: raspberrypiarmhf
arch: armhf
- board: raspberrypiarm64
arch: arm64
steps:
- name: Update apt
run: sudo apt-get update
- name: Install Dependencies
run: sudo apt install coreutils p7zip-full qemu-user-static python3-git
- name: Checkout CustomPiOS
uses: actions/checkout@v2
with:
repository: 'guysoft/CustomPiOS'
path: CustomPiOS
- name: Checkout Project Repository
uses: actions/checkout@v2
with:
repository: ${{ github.repository }}
path: repository
submodules: true
- name: Download Raspbian Image
run: cd repository/src/image && wget -q -c --trust-server-names 'https://downloads.raspberrypi.org/raspios_lite_armhf_latest'
- name: Update CustomPiOS Paths
run: cd repository/src && ../../CustomPiOS/src/update-custompios-paths
- name: Build Image
run: sudo modprobe loop && cd repository/src && sudo bash -x ./build_dist
- name: Copy Output
run: cp ${{ github.workspace }}/repository/src/workspace/*-raspios-*-lite.img build.img
- name: Zip Output
run: gzip build.img
- uses: actions/upload-artifact@v4
with:
name: build.img.gz
path: build.img.gz
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y coreutils p7zip-full qemu-user-static \
python3-git python3-yaml

- name: Checkout CustomPiOS
uses: actions/checkout@v4
with:
repository: 'guysoft/CustomPiOS'
ref: feature/e2e
path: CustomPiOS

- name: Checkout Project Repository
uses: actions/checkout@v4
with:
path: repository
submodules: true

- name: Update CustomPiOS Paths
run: |
cd repository/src
../../CustomPiOS/src/update-custompios-paths

- name: Download Base Image
run: |
cd repository/src
export DIST_PATH=$(pwd)
export CUSTOM_PI_OS_PATH=$(cat custompios_path)
export BASE_BOARD=${{ matrix.board }}
$CUSTOM_PI_OS_PATH/base_image_downloader_wrapper.sh

- name: Build Image
run: |
sudo modprobe loop
cd repository/src
sudo BASE_BOARD=${{ matrix.board }} bash -x ./build_dist

- name: Copy output
id: copy
run: |
source repository/src/config
NOW=$(date +"%Y-%m-%d-%H%M")
IMAGE="${NOW}-fullpageos-${DIST_VERSION}-${{ matrix.arch }}"
cp repository/src/workspace/*.img ${IMAGE}.img
echo "image=${IMAGE}" >> $GITHUB_OUTPUT

- uses: actions/upload-artifact@v4
with:
name: fullpageos-${{ matrix.arch }}
path: ${{ steps.copy.outputs.image }}.img

e2e-test:
needs: build
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4

- name: Checkout CustomPiOS
uses: actions/checkout@v4
with:
repository: 'guysoft/CustomPiOS'
ref: feature/e2e
path: CustomPiOS

- name: Download arm64 image from build
uses: actions/download-artifact@v4
with:
name: fullpageos-arm64
path: image/

- name: Prepare testing context
run: |
mkdir -p testing/custompios
cp -r CustomPiOS/src/distro_testing/scripts testing/custompios/scripts
cp -r CustomPiOS/src/distro_testing/tests testing/custompios/tests

- name: Build test Docker image
run: DOCKER_BUILDKIT=0 docker build -t fullpageos-e2e-test ./testing/

- name: Start E2E test container
run: |
mkdir -p artifacts
IMG=$(find image/ -name '*.img' | head -1)
docker run -d --name fullpageos-test \
-v "$PWD/artifacts:/output" \
-v "$(realpath $IMG):/input/image.img:ro" \
-e ARTIFACTS_DIR=/output \
-e DISTRO_NAME="FullPageOS" \
-e QEMU_EXTRA_ARGS="-device virtio-gpu-pci" \
-e KEEP_ALIVE=true \
fullpageos-e2e-test

- name: Wait for tests to complete
run: |
for i in $(seq 1 180); do
[ -f artifacts/exit-code ] && break
sleep 5
done
if [ ! -f artifacts/exit-code ]; then
echo "ERROR: Tests did not complete within 15 minutes"
docker logs fullpageos-test 2>&1 | tail -80
exit 1
fi
echo "Tests finished with exit code: $(cat artifacts/exit-code)"
cat artifacts/test-results.txt 2>/dev/null || true

- name: Collect logs and stop container
if: always()
run: |
docker logs fullpageos-test > artifacts/container.log 2>&1 || true
docker stop fullpageos-test 2>/dev/null || true

- name: Check test result
run: exit "$(cat artifacts/exit-code 2>/dev/null || echo 1)"

- uses: actions/upload-artifact@v4
if: always()
with:
name: e2e-test-results
path: artifacts/
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ flags=(
)

# Standard behavior - runs chromium
chromium-browser "${flags[@]}" --app=$(/opt/custompios/scripts/get_url)
chromium "${flags[@]}" --app=$(/opt/custompios/scripts/get_url)
exit;

# Remove the two lines above to enable signage mode - refresh the browser whenever errors are seen in log

chromium-browser --enable-logging --log-level=2 --v=0 "${flags[@]}" --app=$(/opt/custompios/scripts/get_url) &
chromium --enable-logging --log-level=2 --v=0 "${flags[@]}" --app=$(/opt/custompios/scripts/get_url) &

export logfile="/home/$(id -nu 1000)/.config/chromium/chrome_debug.log"

Expand Down
2 changes: 1 addition & 1 deletion src/modules/fullpageos/start_chroot_script
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ apt-get -y --force-yes install git screen checkinstall avahi-daemon libavahi-com

if [ "$FULLPAGEOS_INCLUDE_CHROMIUM" == "yes" ]
then
apt-get install -y --force-yes chromium-browser
apt-get install -y --force-yes chromium
sed -i 's@%BROWSER_START_SCRIPT%@/opt/custompios/scripts/start_chromium_browser@g' /opt/custompios/scripts/run_onepageos
fi

Expand Down
1 change: 1 addition & 0 deletions testing/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
images/
3 changes: 3 additions & 0 deletions testing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
images/
custompios/
*.png
20 changes: 20 additions & 0 deletions testing/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM ptrsr/pi-ci:latest

ENV LIBGUESTFS_BACKEND=direct

RUN apt-get update && apt-get install -y --no-install-recommends \
sshpass openssh-client curl socat imagemagick \
&& rm -rf /var/lib/apt/lists/*

# Shared framework from CustomPiOS (copied into build context by CI)
COPY custompios/scripts/ /test/scripts/
COPY custompios/tests/ /test/tests/

# FullPageOS-specific tests and hooks
COPY tests/ /test/tests/
COPY hooks/ /test/hooks/

RUN chmod +x /test/scripts/*.sh /test/tests/*.sh; \
chmod +x /test/hooks/*.sh 2>/dev/null || true

ENTRYPOINT ["/test/scripts/entrypoint.sh"]
37 changes: 37 additions & 0 deletions testing/hooks/post-boot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/bash
set -e
SSH_HOST="${1:-localhost}"
SSH_PORT="${2:-2222}"

SSH_CMD="sshpass -p raspberry ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p $SSH_PORT pi@$SSH_HOST"

echo "Installing Xvfb and configuring virtual display..."

# Install xvfb and x11-apps (for xwd screenshot capture)
$SSH_CMD "sudo apt-get update -qq && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq xvfb x11-apps 2>&1 | tail -3"

# In QEMU virt, logind's seat0 has CanGraphical=no (no real display device),
# so lightdm won't start an X session. Start Xvfb and the GUI session directly.
echo "Starting Xvfb virtual display..."
$SSH_CMD "sudo Xvfb :0 -screen 0 1280x720x24 &"
sleep 2

echo "Starting GUI session (matchbox + chromium kiosk)..."
$SSH_CMD "sudo -u pi bash -c 'export DISPLAY=:0; export HOME=/home/pi; /opt/custompios/scripts/start_gui' &"

# Wait for Chromium to appear on the display
echo "Waiting for Chromium to start..."
for i in $(seq 1 30); do
PGREP=$($SSH_CMD "pgrep -f 'chromium.*--kiosk' || true" 2>/dev/null)
if [ -n "$PGREP" ]; then
echo " Chromium running on display (pid: $PGREP)"
break
fi
sleep 5
done

# Give the page time to render
echo " Waiting for page to load..."
sleep 15

echo "Post-boot setup complete"
51 changes: 51 additions & 0 deletions testing/hooks/prepare-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/bash
set -e
IMAGE_FILE="${1:?Usage: $0 <image.qcow2>}"

export LIBGUESTFS_BACKEND=direct
export LIBGUESTFS_DEBUG=0
export LIBGUESTFS_TRACE=0

echo '=== FullPageOS-specific image patches ==='

guestfish -a "$IMAGE_FILE" <<GFEOF
run
mount /dev/sda2 /

# Remove fbturbo X11 config that conflicts with virtio-gpu
-rm /usr/share/X11/xorg.conf.d/99-fbturbo.conf
-rm /usr/share/X11/xorg.conf.d/99-fbturbo.~

# Disable services that timeout waiting for Pi-specific hardware in QEMU:
# - zram (no /dev/zram0 in virt)
# - rpi-eeprom-update (no Pi EEPROM)
# - copy-network-manager config for wlan0 (no wifi)
# - DRI device dependencies (virtio-gpu may not create card0/renderD128 in time)
-rm /etc/systemd/system/multi-user.target.wants/dphys-swapfile.service
-rm /etc/systemd/system/swap.target.wants/mkswap.service
-rm /etc/systemd/system/swap.target.wants/sys-subsystem-net-devices-wlan0.device
-rm /lib/systemd/system/rpi-eeprom-update.service
-rm /etc/systemd/system/multi-user.target.wants/rpi-eeprom-update.service

# Mask services that block boot waiting for Pi hardware
ln-sf /dev/null /etc/systemd/system/systemd-zram-setup@.service
ln-sf /dev/null /etc/systemd/system/copy-network-manager-conf@.service

# Mask DRI device wait units -- virtio-gpu may not create /dev/dri/* in QEMU virt
ln-sf /dev/null /etc/systemd/system/dev-dri-card0.device
ln-sf /dev/null /etc/systemd/system/dev-dri-renderD128.device

# Pre-create /etc/gpu_enabled so enable_gpu script skips its reboot
touch /etc/gpu_enabled

# Disable the enable_gpu_first_boot service entirely in QEMU
ln-sf /dev/null /etc/systemd/system/enable_gpu_first_boot.service

# Xvfb will be installed and configured via post-boot.sh hook (needs apt-get).
# Pre-create the lightdm conf directory so the hook can write to it.
mkdir-p /etc/lightdm/lightdm.conf.d

umount /
GFEOF

echo 'FullPageOS patches applied'
56 changes: 56 additions & 0 deletions testing/hooks/screenshot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/bin/bash
set -e
SSH_HOST="${1:-localhost}"
SSH_PORT="${2:-2222}"
ARTIFACTS_DIR="${3:-/output}"

SSH_CMD="sshpass -p raspberry ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p $SSH_PORT pi@$SSH_HOST"
SCP_CMD="sshpass -p raspberry scp -o StrictHostKeyChecking=no -P $SSH_PORT"

echo "Capturing X display screenshot from the running desktop..."

# Wait for Chromium to be rendering on the display (give the page time to load)
echo " Waiting for Chromium to settle..."
sleep 10

# Capture the actual X display using xwd (part of x11-apps, installed with xterm)
DISPLAY_SCREENSHOT=false
$SSH_CMD "DISPLAY=:0 XAUTHORITY=/var/run/lightdm/root/:0 xwd -root -out /tmp/display.xwd" 2>/dev/null && DISPLAY_SCREENSHOT=true

if [ "$DISPLAY_SCREENSHOT" = false ]; then
# Try alternative Xauthority paths
$SSH_CMD "DISPLAY=:0 xwd -root -out /tmp/display.xwd" 2>/dev/null && DISPLAY_SCREENSHOT=true
fi

if [ "$DISPLAY_SCREENSHOT" = true ] && $SSH_CMD "test -s /tmp/display.xwd" 2>/dev/null; then
# Convert xwd to png inside the guest (ImageMagick's convert may not be there, try import)
$SSH_CMD "convert /tmp/display.xwd /tmp/display-screenshot.png 2>/dev/null || cp /tmp/display.xwd /tmp/display-screenshot.xwd" || true

if $SSH_CMD "test -f /tmp/display-screenshot.png" 2>/dev/null; then
$SCP_CMD "pi@${SSH_HOST}:/tmp/display-screenshot.png" "$ARTIFACTS_DIR/screenshot.png"
echo "Display screenshot saved (PNG)"
elif $SSH_CMD "test -f /tmp/display-screenshot.xwd" 2>/dev/null; then
$SCP_CMD "pi@${SSH_HOST}:/tmp/display-screenshot.xwd" "$ARTIFACTS_DIR/screenshot.xwd"
# Convert on the host side if imagemagick is available
if command -v convert &>/dev/null; then
convert "$ARTIFACTS_DIR/screenshot.xwd" "$ARTIFACTS_DIR/screenshot.png" 2>/dev/null && \
rm -f "$ARTIFACTS_DIR/screenshot.xwd" && \
echo "Display screenshot saved (converted to PNG on host)" || \
echo "Display screenshot saved (XWD format)"
else
echo "Display screenshot saved (XWD format)"
fi
fi
else
echo " xwd capture failed, falling back to Chromium headless..."
SCREENSHOT_URL="${SCREENSHOT_URL:-http://localhost/FullPageDashboard}"
$SSH_CMD "chromium --headless --disable-gpu --no-sandbox --screenshot=/tmp/chromium-screenshot.png --window-size=1280,720 '$SCREENSHOT_URL'" 2>/dev/null || true
sleep 2
if $SSH_CMD "test -f /tmp/chromium-screenshot.png" 2>/dev/null; then
$SCP_CMD "pi@${SSH_HOST}:/tmp/chromium-screenshot.png" "$ARTIFACTS_DIR/screenshot.png"
echo "Chromium headless screenshot saved (fallback)"
else
echo "All screenshot methods failed"
exit 1
fi
fi
Loading
Loading