Add GitHub Actions workflow to build Raspberry Pi image - #286
Conversation
Fixes DouglasHalse#165 Added automated workflow that: - Creates a production-ready Raspberry Pi OS Lite image - Pre-installs SnackAttack and all dependencies - Configures auto-login and auto-start on boot - Enables SSH for remote access - Can be triggered manually or on release - Uploads compressed image as artifact The image boots directly into SnackAttack without any desktop environment.
There was a problem hiding this comment.
Pull request overview
This PR adds a GitHub Actions workflow to automate the creation of pre-configured Raspberry Pi OS images with SnackAttack pre-installed and configured for automatic startup.
- Introduces workflow triggered manually or on release publication
- Uses pi-gen to build custom Raspberry Pi OS Lite images with SnackAttack
- Configures auto-login, SSH access, and automatic application launch
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| on_chroot << CHROOT | ||
| # Clone SnackAttack repository | ||
| cd /home/pi | ||
| git clone https://github.com/${{ github.repository }}.git snackAttackTrack |
There was a problem hiding this comment.
Using 'git clone --depth 1' without specifying a branch or tag means the image will always contain the latest code from the default branch at build time, even for release builds. For release workflows, consider pinning to a specific commit SHA or tag to ensure reproducible builds and that release images contain the exact code version being released.
| git clone https://github.com/${{ github.repository }}.git snackAttackTrack | |
| git clone --depth 1 https://github.com/${{ github.repository }}.git snackAttackTrack | |
| cd snackAttackTrack | |
| git fetch --depth 1 origin ${{ github.sha }} | |
| git checkout ${{ github.sha }} | |
| cd .. |
| cat > /etc/systemd/system/getty@tty1.service.d/autologin.conf << EOF | ||
| [Service] | ||
| ExecStart= | ||
| ExecStart=-/sbin/agetty --autologin pi --noclear %I \\\$TERM |
There was a problem hiding this comment.
The escape sequence '\$TERM' may not be correctly interpreted within the nested heredoc structure. This could result in the wrong value being written to the autologin configuration file, potentially breaking the auto-login functionality. Verify that the final configuration file contains the literal '$TERM' as intended.
| ExecStart=-/sbin/agetty --autologin pi --noclear %I \\\$TERM | |
| ExecStart=-/sbin/agetty --autologin pi --noclear %I \$TERM |
| echo "IMAGE_PATH=pi-gen/deploy/${IMAGE_FILE}.xz" >> $GITHUB_ENV | ||
| echo "Image compressed: ${IMAGE_FILE}.xz" | ||
| else | ||
| echo "Looking for zip file..." | ||
| ZIP_FILE=$(ls *.zip 2>/dev/null | head -1) | ||
| if [ -n "$ZIP_FILE" ]; then | ||
| echo "IMAGE_PATH=pi-gen/deploy/$ZIP_FILE" >> $GITHUB_ENV |
There was a problem hiding this comment.
The IMAGE_PATH environment variable is set but never used in subsequent steps. The artifact and release upload steps use glob patterns instead. Either remove this unused variable assignment or update the upload steps to reference it for more explicit and maintainable file path handling.
| cp ../rpi-setup/quick-deploy.sh stage2/99-snackattack/files/ | ||
| cp ../rpi-setup/setup-autostart.sh stage2/99-snackattack/files/ | ||
| cp ../rpi-setup/setup-production.sh stage2/99-snackattack/files/ | ||
|
|
There was a problem hiding this comment.
The workflow attempts to copy setup scripts from a non-existent 'rpi-setup' directory. These files (quick-deploy.sh, setup-autostart.sh, setup-production.sh) are not present in the repository, which will cause the build to fail immediately when executing these copy commands. Either remove these copy commands if the files are not needed, or ensure the required files are added to the repository before this workflow can function.
| cp ../rpi-setup/quick-deploy.sh stage2/99-snackattack/files/ | |
| cp ../rpi-setup/setup-autostart.sh stage2/99-snackattack/files/ | |
| cp ../rpi-setup/setup-production.sh stage2/99-snackattack/files/ | |
| pi_version: | ||
| description: 'Target Raspberry Pi version' | ||
| required: false | ||
| default: 'pi4' | ||
| type: choice | ||
| options: | ||
| - pi3 | ||
| - pi4 | ||
| - pi5 |
There was a problem hiding this comment.
The workflow defines a 'pi_version' input parameter but never uses it in the build process. All Raspberry Pi models (Pi 3, 4, and 5) would receive the same image regardless of the selection. If different hardware configurations are needed, the parameter should be used to configure pi-gen appropriately, or it should be removed to avoid confusion.
| pi_version: | |
| description: 'Target Raspberry Pi version' | |
| required: false | |
| default: 'pi4' | |
| type: choice | |
| options: | |
| - pi3 | |
| - pi4 | |
| - pi5 |
| FIRST_USER_PASS="snackattack" | ||
| ENABLE_SSH=1 |
There was a problem hiding this comment.
The default password 'snackattack' is publicly documented in the PR description and hardcoded in the workflow. This creates a significant security risk as anyone can access the SSH service with known credentials. Consider implementing a secure password generation mechanism, prompting users to change the password on first boot, or at minimum documenting this as a critical security configuration step.
| ZIP_FILE=$(ls *.zip 2>/dev/null | head -1) | ||
| if [ -n "$ZIP_FILE" ]; then | ||
| echo "IMAGE_PATH=pi-gen/deploy/$ZIP_FILE" >> $GITHUB_ENV | ||
| echo "Found zip: $ZIP_FILE" |
There was a problem hiding this comment.
The 'Compress image' step does not handle the case where no image or zip file is found. If the build fails silently or produces output in an unexpected location, the step will complete successfully without setting IMAGE_PATH, and the subsequent upload steps will fail without a clear error message. Add error handling to fail explicitly if no image file is found.
| echo "Found zip: $ZIP_FILE" | |
| echo "Found zip: $ZIP_FILE" | |
| else | |
| echo "ERROR: No .img or .zip file found in pi-gen/deploy. Build failed." >&2 | |
| exit 1 |
| git clone https://github.com/${{ github.repository }}.git snackAttackTrack | ||
| chown -R 1000:1000 snackAttackTrack | ||
|
|
||
| # Install Python dependencies | ||
| cd snackAttackTrack |
There was a problem hiding this comment.
The repository URL is dynamically constructed using github.repository, but the cloned directory name is hardcoded as 'snackAttackTrack'. If this workflow is ever used in a fork with a different repository name, the paths in subsequent steps (like line 181) would be incorrect. Consider using a consistent variable or the actual repository name from the context.
| git clone https://github.com/${{ github.repository }}.git snackAttackTrack | |
| chown -R 1000:1000 snackAttackTrack | |
| # Install Python dependencies | |
| cd snackAttackTrack | |
| REPO_NAME="$(basename "${{ github.repository }}")" | |
| git clone https://github.com/${{ github.repository }}.git "$REPO_NAME" | |
| chown -R 1000:1000 "$REPO_NAME" | |
| # Install Python dependencies | |
| cd "$REPO_NAME" |
Summary
This PR adds a GitHub Actions workflow to automatically build a pre-configured Raspberry Pi image.
Fixes #165
Changes
Added
.github/workflows/build-rpi-image.ymlthat:Features
How to use
Default credentials
The image boots directly into SnackAttack without any desktop environment, as requested in the issue.