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
100 changes: 70 additions & 30 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,37 +1,77 @@
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: devel
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
uses: guysoft/CustomPiOS/.github/workflows/e2e-test.yml@feature/e2e
with:
image-artifact-name: fullpageos-arm64
distro-name: FullPageOS
timeout-minutes: 45
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/
2 changes: 2 additions & 0 deletions testing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
images/
*.png
21 changes: 21 additions & 0 deletions testing/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
ARG CUSTOMPIOS_TAG=devel
FROM ghcr.io/guysoft/custompios:${CUSTOMPIOS_TAG} AS custompios

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/*

COPY --from=custompios /CustomPiOS/distro_testing/scripts/ /test/scripts/
COPY --from=custompios /CustomPiOS/distro_testing/tests/ /test/tests/

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"]
63 changes: 63 additions & 0 deletions testing/hooks/post-boot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/bash
export E2E_SSH_HOST="${1:-localhost}"
export E2E_SSH_PORT="${2:-2222}"
source /test/scripts/ssh-helpers.sh

echo "Installing Xvfb and x11-apps..."
ssh_cmd "sudo apt-get update -qq && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq xvfb x11-apps 2>&1 | tail -5"

echo "Starting Xvfb virtual display..."
ssh_cmd "sudo nohup Xvfb :0 -screen 0 1280x720x24 -ac > /tmp/xvfb.log 2>&1 < /dev/null &"
sleep 3

echo "Verifying Xvfb is running..."
XVFB_PID=$(ssh_cmd "pgrep -f 'Xvfb :0' || true" 2>/dev/null)
if [ -z "$XVFB_PID" ]; then
echo " WARNING: Xvfb not running, checking log..."
ssh_cmd "cat /tmp/xvfb.log 2>/dev/null || true" 2>/dev/null
else
echo " Xvfb running (pid: $XVFB_PID)"
fi

echo "Checking lighttpd status..."
ssh_cmd "systemctl is-active lighttpd 2>/dev/null || true" 2>/dev/null
ssh_cmd "curl -s -o /dev/null -w 'HTTP %{http_code}' http://localhost/ || true" 2>/dev/null
echo ""

echo "Starting GUI session (matchbox + chromium kiosk)..."
ssh_cmd "sudo -u pi nohup bash -c 'export DISPLAY=:0; export HOME=/home/pi; /opt/custompios/scripts/start_gui' > /tmp/start_gui.log 2>&1 < /dev/null &"

echo "Waiting for Chromium to start..."
for i in $(seq 1 60); do
PGREP=$(ssh_cmd "pgrep -f 'chromium.*--kiosk' || true" 2>/dev/null)
if [ -n "$PGREP" ]; then
echo " Chromium running (pid: $PGREP) after ${i}x5s"
break
fi
if [ "$((i % 6))" -eq 0 ]; then
echo " ... still waiting (${i}x5s), diagnostics:"
ssh_cmd "pgrep -a Xvfb || echo ' Xvfb: NOT RUNNING'" 2>/dev/null || true
ssh_cmd "pgrep -a matchbox || echo ' matchbox: NOT RUNNING'" 2>/dev/null || true
ssh_cmd "pgrep -a chromium || echo ' chromium: NOT RUNNING'" 2>/dev/null || true
ssh_cmd "tail -5 /tmp/start_gui.log 2>/dev/null || true" 2>/dev/null || true
fi
sleep 5
done

if [ -z "$PGREP" ]; then
echo " WARNING: Chromium did not appear after 300s"
echo " start_gui log:"
ssh_cmd "cat /tmp/start_gui.log 2>/dev/null || true" 2>/dev/null || true
echo " xvfb log:"
ssh_cmd "cat /tmp/xvfb.log 2>/dev/null || true" 2>/dev/null || true
echo " Process list:"
ssh_cmd "ps aux | head -30" 2>/dev/null || true
fi

echo "Waiting for page to render..."
sleep 15

echo "Post-boot display state:"
ssh_cmd "DISPLAY=:0 xdotool search --onlyvisible --name . getwindowname 2>/dev/null || echo '(no visible windows)'" 2>/dev/null || true

echo "Post-boot setup complete"
40 changes: 40 additions & 0 deletions testing/hooks/prepare-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/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 FullPageOS-specific services not needed in QEMU:
# - dphys-swapfile/mkswap (no swap in qcow2)
# - wlan0 device (no wifi in QEMU)
-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

# 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

# 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'
49 changes: 49 additions & 0 deletions testing/hooks/screenshot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/bin/bash
set -e
export E2E_SSH_HOST="${1:-localhost}"
export E2E_SSH_PORT="${2:-2222}"
ARTIFACTS_DIR="${3:-/output}"
source /test/scripts/ssh-helpers.sh

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

echo " Waiting for Chromium to settle..."
sleep 10

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
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
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@${E2E_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@${E2E_SSH_HOST}:/tmp/display-screenshot.xwd" "$ARTIFACTS_DIR/screenshot.xwd"
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@${E2E_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
66 changes: 66 additions & 0 deletions testing/tests/test_chromium.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/bin/bash
set -e

export E2E_SSH_HOST="${1:-localhost}"
export E2E_SSH_PORT="${2:-2222}"
ARTIFACTS_DIR="${3:-}"
source /test/scripts/ssh-helpers.sh

echo "Test: Chromium is displaying FullPageDashboard"

echo " Checking for Chromium kiosk process..."
CHROMIUM_FOUND=0
for i in $(seq 1 60); do
PGREP=$(ssh_cmd "pgrep -f 'chromium.*--kiosk' || true" 2>/dev/null)
if [ -n "$PGREP" ]; then
CHROMIUM_FOUND=1
break
fi
printf "."
sleep 5
done
echo ""

if [ "$CHROMIUM_FOUND" -eq 0 ]; then
echo " FAIL: Chromium kiosk process not found after 300s"
ssh_cmd "ps aux" 2>/dev/null | tail -20
exit 1
fi
echo " Chromium kiosk running (pid: $PGREP)"

echo " Checking X display window title..."
WINDOW_TITLE=$(ssh_cmd "DISPLAY=:0 xdotool search --onlyvisible --name . getwindowname 2>/dev/null || true" 2>/dev/null)

if [ -n "$ARTIFACTS_DIR" ]; then
ssh_cmd "ps aux | grep -i chromium" > "$ARTIFACTS_DIR/chromium-processes.txt" 2>/dev/null || true
echo "$WINDOW_TITLE" > "$ARTIFACTS_DIR/window-title.txt" 2>/dev/null || true
fi

if [ -z "$WINDOW_TITLE" ]; then
echo " No windows found with --onlyvisible, trying without visibility filter..."
WINDOW_TITLE=$(ssh_cmd "DISPLAY=:0 xdotool search --name . getwindowname 2>&1 || true" 2>/dev/null)
if [ -n "$ARTIFACTS_DIR" ]; then
echo "$WINDOW_TITLE" > "$ARTIFACTS_DIR/window-title.txt" 2>/dev/null || true
fi
fi

if [ -z "$WINDOW_TITLE" ]; then
echo " Diagnosing X display access..."
ssh_cmd "DISPLAY=:0 xdpyinfo 2>&1 | head -5 || echo 'xdpyinfo failed'" 2>/dev/null || true
ssh_cmd "DISPLAY=:0 xdotool search --name . 2>&1 || echo 'xdotool search failed'" 2>/dev/null || true
echo " FAIL: No window on X display (display pipeline not working)"
exit 1
fi

echo " Window title: '$WINDOW_TITLE'"

# In Xvfb + matchbox kiosk, xdotool may see "matchbox" (the WM) rather than
# the Chromium page title, since matchbox doesn't propagate child names.
# Chromium process was already verified running with --kiosk --app=FullPageDashboard.
if echo "$WINDOW_TITLE" | grep -qi "Full Page Dashboard\|FullPageDashboard\|FullPageOS\|matchbox"; then
echo " PASS: Display pipeline active (window: '$WINDOW_TITLE'), Chromium kiosk running"
exit 0
else
echo " FAIL: Window title '$WINDOW_TITLE' does not indicate a working display"
exit 1
fi
Loading
Loading