Skip to content

Commit 45cce89

Browse files
gilesknapclaude
andauthored
Add CI workflow to build Raspberry Pi SD card image (#10)
* Add CI workflow to build Raspberry Pi SD card image Add a manually triggered GitHub Actions workflow that uses pi-gen to build a Raspberry Pi OS Lite image with the agent, pico-mac-sender, USB/IP, and read-only overlay pre-configured. The resulting image is attached to the GitHub Release for the specified tag. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove root index.html redirect This duplicate redirect was accidentally added in a4d968f and still points to master. The correct redirect lives in .github/pages/index.html. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Document how to trigger the SD card image build Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Simplify SD card build instructions to UI only Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a265bed commit 45cce89

File tree

10 files changed

+212
-14
lines changed

10 files changed

+212
-14
lines changed

.github/workflows/sdcard.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Build SD Card Image
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
agent-version:
7+
description: "Release tag to install (e.g. v0.3.0)"
8+
required: true
9+
type: string
10+
11+
permissions:
12+
contents: write
13+
14+
jobs:
15+
build-image:
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
22+
- name: Build Pi image
23+
id: build
24+
uses: usimd/pi-gen-action@v1
25+
with:
26+
image-name: raspi-agent-server-${{ inputs.agent-version }}
27+
stage-list: stage0 stage1 stage2 ./pi-gen-stage
28+
pi-gen-version: arm64
29+
hostname: raspberrypi
30+
username: local
31+
password: local
32+
locale: en_GB.UTF-8
33+
timezone: Europe/London
34+
enable-ssh: 1
35+
compression: xz
36+
compression-level: 6
37+
export-last-stage-only: true
38+
increase-runner-disk-size: true
39+
env:
40+
AGENT_VERSION: ${{ inputs.agent-version }}
41+
42+
- name: Attach image to release
43+
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
44+
with:
45+
tag_name: ${{ inputs.agent-version }}
46+
files: ${{ steps.build.outputs.image-path }}
47+
env:
48+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

docs/how-to/new-raspi-image.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ pre-built image. See {doc}`/tutorials/commission-raspi`.
88
This page is only needed if you want to create a completely new SD card
99
image, perhaps because a new version of Raspberry Pi OS has been released.
1010

11+
:::{tip}
12+
Images can also be built automatically using the **Build SD Card Image**
13+
GitHub Actions workflow. The resulting image will be attached to the
14+
corresponding [GitHub Release](https://github.com/DiamondLightSource/dra-usbip-driver/releases).
15+
16+
To trigger the build, go to **Actions → Build SD Card Image → Run
17+
workflow**, enter the release tag (e.g. `v0.3.0`), and click **Run
18+
workflow**.
19+
:::
20+
1121
If you just want to update the agent version on an existing image, see
1222
{doc}`update-raspi-image`.
1323

docs/tutorials/commission-raspi.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ card is suitable.
1515

1616
## Step 2: Flash the Agent Server Image
1717

18-
<!-- TODO: host a pre-built image and add download link here -->
19-
20-
1. Download the pre-built agent server image.
18+
1. Download the latest `raspi-agent-server-*.img.xz` from the
19+
[GitHub Releases](https://github.com/DiamondLightSource/dra-usbip-driver/releases)
20+
page and decompress it:
21+
```bash
22+
unxz raspi-agent-server-*.img.xz
23+
```
2124

2225
1. Insert a microSD card of at least 16GB capacity into a card reader
2326
connected to your computer.

index.html

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
usbip
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/bin/bash
2+
set -eu
3+
4+
# Configure USB/IP kernel modules to load at boot
5+
cat > /etc/modules-load.d/usbip.conf <<EOF
6+
usbip-core
7+
usbip-host
8+
EOF
9+
10+
# Create systemd service for usbipd
11+
cat > /etc/systemd/system/usbipd.service <<EOF
12+
[Unit]
13+
Description=USB/IP Daemon
14+
After=network.target
15+
16+
[Service]
17+
Type=forking
18+
ExecStart=/usr/sbin/usbipd -D
19+
Restart=on-failure
20+
21+
[Install]
22+
WantedBy=multi-user.target
23+
EOF
24+
25+
systemctl enable usbipd.service
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/bash
2+
set -eu
3+
4+
REPO="DiamondLightSource/dra-usbip-driver"
5+
INSTALL_DIR="/usr/local/bin"
6+
SERVICE_NAME="dra-usbip-agent"
7+
BINARY_NAME="agent"
8+
GOARCH="arm64"
9+
10+
if [ -z "${AGENT_VERSION:-}" ]; then
11+
echo "Error: AGENT_VERSION must be set" >&2
12+
exit 1
13+
fi
14+
15+
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${AGENT_VERSION}/${BINARY_NAME}-linux-${GOARCH}"
16+
17+
echo "Installing ${SERVICE_NAME} ${AGENT_VERSION} (linux/${GOARCH})..."
18+
19+
curl -fsSL -o "${INSTALL_DIR}/${BINARY_NAME}" "$DOWNLOAD_URL"
20+
chmod +x "${INSTALL_DIR}/${BINARY_NAME}"
21+
22+
cat > /etc/systemd/system/${SERVICE_NAME}.service <<EOF
23+
[Unit]
24+
Description=DRA USB/IP Agent
25+
After=network.target
26+
27+
[Service]
28+
ExecStart=${INSTALL_DIR}/${BINARY_NAME}
29+
Restart=on-failure
30+
RestartSec=5
31+
32+
[Install]
33+
WantedBy=multi-user.target
34+
EOF
35+
36+
systemctl enable "${SERVICE_NAME}"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/bash
2+
set -eu
3+
4+
REPO="DiamondLightSource/dra-usbip-driver"
5+
INSTALL_DIR="/usr/local/bin"
6+
SERVICE_NAME="pico-mac-sender"
7+
BINARY_NAME="pico-mac-sender"
8+
GOARCH="arm64"
9+
10+
if [ -z "${AGENT_VERSION:-}" ]; then
11+
echo "Error: AGENT_VERSION must be set" >&2
12+
exit 1
13+
fi
14+
15+
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${AGENT_VERSION}/${BINARY_NAME}-linux-${GOARCH}"
16+
17+
echo "Installing ${SERVICE_NAME} ${AGENT_VERSION} (linux/${GOARCH})..."
18+
19+
curl -fsSL -o "${INSTALL_DIR}/${BINARY_NAME}" "$DOWNLOAD_URL"
20+
chmod +x "${INSTALL_DIR}/${BINARY_NAME}"
21+
22+
cat > /etc/systemd/system/${SERVICE_NAME}.service <<EOF
23+
[Unit]
24+
Description=Send MAC address to Raspberry Pi Pico OLED display
25+
After=multi-user.target
26+
27+
[Service]
28+
ExecStart=${INSTALL_DIR}/${BINARY_NAME}
29+
Restart=on-failure
30+
RestartSec=5
31+
32+
[Install]
33+
WantedBy=multi-user.target
34+
EOF
35+
36+
systemctl enable "${SERVICE_NAME}"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
cryptsetup
2+
cryptsetup-bin
3+
overlayroot
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/bin/bash
2+
set -eu
3+
4+
# Create a run-once service that enables read-only overlay on first boot
5+
cat > /etc/systemd/system/runonce.service <<EOF
6+
[Unit]
7+
Description=Run script once on next boot
8+
ConditionPathExists=/var/local/runonce.sh
9+
After=multi-user.target
10+
11+
[Service]
12+
Type=oneshot
13+
ExecStart=/bin/bash /var/local/runonce.sh
14+
15+
[Install]
16+
WantedBy=multi-user.target
17+
EOF
18+
19+
systemctl enable runonce.service
20+
21+
# Create the runonce script
22+
cat > /var/local/runonce.sh <<'SCRIPT'
23+
#!/bin/bash
24+
set -x
25+
26+
# Disable this script from running again
27+
mv /var/local/runonce.sh /var/local/runonce.sh.done
28+
29+
# Disable services that will report errors when in RO mode
30+
systemctl mask dphys-swapfile.service
31+
systemctl mask systemd-zram-setup@zram0.service
32+
33+
# Enable read-only overlay mode
34+
raspi-config nonint do_overlayfs 0
35+
36+
# Reboot to pick up the change
37+
sync; reboot
38+
SCRIPT
39+
40+
chmod +x /var/local/runonce.sh
41+
42+
# Disable WiFi and Bluetooth for production
43+
cat >> /boot/firmware/config.txt <<EOF
44+
45+
dtoverlay=disable-wifi
46+
dtoverlay=disable-bt
47+
EOF

0 commit comments

Comments
 (0)