Skip to content

welcome on dashboard #15

welcome on dashboard

welcome on dashboard #15

Workflow file for this run

name: build-os
on:
workflow_call:
inputs:
name:
description: 'The name of the OS image to build'
required: true
type: string
base_release_name:
description: 'The release name of the RPi OS base image (bullseye, bookworm)'
required: true
type: string
base_image_variant:
description: 'The name of the RPi OS base image variant (lite, desktop, or full)'
required: true
type: string
base_release_date:
description: 'The release date of the RPi OS base image'
required: true
type: string
base_file_release_date:
description: 'The release date of the RPi OS base image file, if different from the group release date'
required: false
type: string
arch:
description: 'The CPU architecture of the OS (armhf, arm64)'
required: true
type: string
jobs:
build-os-image:
name: Build image
runs-on: ubuntu-24.04-arm
env:
SETUP_USER: pi
permissions:
contents: read
packages: write
id-token: write
timeout-minutes: 30
steps:
- name: Get actual repo & commit SHA
run: |
# Get the actual repo name on PRs from external forks of the repo:
if [ -n "${{ github.event.pull_request.head.repo.full_name }}" ]; then
echo "REPO=${{ github.event.pull_request.head.repo.full_name }}" >> $GITHUB_ENV
else
echo "REPO=${{ github.repository }}" >> $GITHUB_ENV
fi
# Get the SHA of the actual commit (not the fake merge commit) on PR-triggered runs
# (refer to https://stackoverflow.com/a/68068674/23202949):
if [ -n "${{ github.event.pull_request.head.sha }}" ]; then
echo "ACTUAL_SHA=${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV
else
# Note: we want to have a SHA to check out even in merge queues, so we always fall back
# to a SHA even if it's a fictional SHA detached from any branches:
echo "ACTUAL_SHA=${{ github.sha }}" >> $GITHUB_ENV
fi
- name: Checkout repository
uses: actions/checkout@v4
with:
# PR-triggered workflow runs have a merge commit which is detached from existing
# branches and messes up pseudoversion determination when we record installer versioning
# information, so instead we check out the actual commit (and fetch all tags so we can
# determine a pseudoversion string):
repository: ${{ env.REPO }}
ref: ${{ env.ACTUAL_SHA }}
fetch-depth: 0
fetch-tags: true
filter: 'blob:none'
submodules: recursive
# GET BASE IMAGE
- name: Determine Raspberry Pi OS base image URL
id: rpi-os-image
run: |
case '${{ inputs.base_release_name }}' in
'bookworm')
IMAGE_RELEASE_CHANNEL='raspios'
;;
'bullseye')
IMAGE_RELEASE_CHANNEL='raspios_oldstable'
;;
*)
echo "Unknown release name: ${{ inputs.base_release_name }}"
exit 1
;;
esac
IMAGE_REPO_GROUP="$IMAGE_RELEASE_CHANNEL"
if [[ "${{ inputs.base_image_variant }}" != "desktop" ]]; then
IMAGE_REPO_GROUP="${IMAGE_REPO_GROUP}_${{ inputs.base_image_variant }}"
fi
IMAGE_REPO_GROUP="${IMAGE_REPO_GROUP}_${{ inputs.arch }}"
if [[ -z "${{ inputs.base_file_release_date }}" ]]; then
BASE_FILE_RELEASE_DATE="${{ inputs.base_release_date }}"
else
BASE_FILE_RELEASE_DATE="${{ inputs.base_file_release_date }}"
fi
IMAGE_FILENAME="$BASE_FILE_RELEASE_DATE-raspios-${{ inputs.base_release_name }}-${{ inputs.arch }}"
if [[ "${{ inputs.base_image_variant }}" != "desktop" ]]; then
IMAGE_FILENAME="${IMAGE_FILENAME}-${{ inputs.base_image_variant }}"
fi
IMAGE_FILENAME="${IMAGE_FILENAME}.img.xz"
IMAGE_URL="https://downloads.raspberrypi.com/$IMAGE_REPO_GROUP/images/$IMAGE_REPO_GROUP-${{ inputs.base_release_date }}/$IMAGE_FILENAME"
echo "RPi OS image filename: $IMAGE_FILENAME"
echo "image_filename=$IMAGE_FILENAME" >> $GITHUB_OUTPUT
echo "RPi OS image URL: $IMAGE_URL"
echo "image_url=$IMAGE_URL" >> $GITHUB_OUTPUT
- name: Download and cache base image
id: download-base
uses: ethanjli/cached-download-action@v0.1.2
with:
url: ${{ steps.rpi-os-image.outputs.image_url }}
destination: /tmp/${{ steps.rpi-os-image.outputs.image_filename }}
- name: Decompress & grow base image
id: expand-image
uses: ethanjli/pigrow-action@v0.1.1
with:
image: ${{ steps.download-base.outputs.destination }}
mode: to
size: 16G
# RECORD INSTALLER VERSIONING INFORMATION
# The user-driven setup script (install.planktoscope.community/distro.sh) records version info
# which is read by the PlanktoScope's Python backend and Node-RED dashboard, so we must
# record the same information here.
- name: Record OS installation versioning information
uses: ethanjli/pinspawn-action@v0.1.5
with:
image: ${{ steps.expand-image.outputs.destination }}
user: ${{ env.SETUP_USER }}
args: >-
--bind "$(pwd)":/run/os-setup
--machine=raspberrypi
run: |
export DEBIAN_FRONTEND=noninteractive
sudo apt-get update -y -o Dpkg::Progress-Fancy=0
sudo apt-get install -y -o Dpkg::Progress-Fancy=0 git
# chown is needed to suppress a git error about dubious repo ownership:
sudo chown -R $USER "/run/os-setup"
set -x
ACTUAL_SHA="$(printf "%.7s" "${{ env.ACTUAL_SHA }}")"
/run/os-setup/os/ci-record-version.sh \
"github.com/${{ env.REPO }}" "$ACTUAL_SHA" "hash" \
"/run/os-setup"
echo "installer-config.yml:"
cat /usr/share/planktoscope/installer-config.yml
echo "installer-versioning.yml:"
cat /usr/share/planktoscope/installer-versioning.yml
# If we don't do this, then the post-job cleanup for the actions/checkout step will emit a
# warning:
- name: Reset owner of Git repo
run: |
sudo chown -R $USER .
# Note: the following step isn't strictly necessary, but it's nice to separate the GitHub
# Actions log outputs for setting up pinspawn-action for the first time from the logs for
# pre-downloading container images
- name: Install pinspawn-action dependencies
uses: ethanjli/pinspawn-action@v0.1.5
with:
image: ${{ steps.expand-image.outputs.destination }}
run: echo "Done!"
# RUN OS SETUP SCRIPTS
- name: Run OS setup scripts in an unbooted container
uses: ethanjli/pinspawn-action@v0.1.5
with:
image: ${{ steps.expand-image.outputs.destination }}
user: ${{ env.SETUP_USER }}
# Note: CAP_NET_ADMIN is needed for iptables, which is needed for Docker (at least in an
# unbooted container). Setting the machine ID (and therefore hostname) to `raspberrypi`
# resolves noisy (but harmless) error messages from sudo.
args: >-
--bind "$(pwd)":/run/os-setup
--capability=CAP_NET_ADMIN
--machine=raspberrypi
run: |
echo "Running setup scripts..."
export DEBIAN_FRONTEND=noninteractive
/run/os-setup/os/setup.sh
echo "Done!"
- name: Prepare for a headless first boot on bare metal
uses: ethanjli/pinspawn-action@v0.1.5
env:
DEFAULT_PASSWORD: copepode
DEFAULT_KEYBOARD_LAYOUT: us
with:
image: ${{ steps.expand-image.outputs.destination }}
args: --machine=raspberrypi
run: |
# Change default settings for the SD card to enable headless & keyboardless first boot
# Note: we could change the username by making a `userconf.txt` file with the new
# username and an encrypted representation of the password (and un-disabling and
# unmasking `userconfig.service`), but we don't need to do that for now.
# See https://github.com/RPi-Distro/userconf-pi/blob/bookworm/userconf-service and
# https://www.raspberrypi.com/documentation/computers/configuration.html#configuring-a-user
# and the "firstrun"-related and "cloudinit"-related lines of
# https://github.com/raspberrypi/rpi-imager/blob/qml/src/OptionsPopup.qml and
# the RPi SD card image's `/usr/lib/raspberrypi-sys-mods/firstboot` and
# `/usr/lib/raspberrypi-sys-mods/imager_custom` scripts
echo "pi:${{ env.DEFAULT_PASSWORD }}" | chpasswd
sed -i \
-e "s~^XKBLAYOUT=.*~XKBLAYOUT=\"${{ env.DEFAULT_KEYBOARD_LAYOUT }}\"~" \
/etc/default/keyboard
systemctl disable userconfig.service
# This is needed to have the login prompt on tty1 (so that a user with a keyboard can
# log in without switching away from the default tty), because we disabled
# userconfig.service. See
# https://forums.raspberrypi.com/viewtopic.php?p=2032694#p2032694
systemctl enable getty@tty1
# RUN TESTS
- name: Run tests
- uses: ethanjli/pinspawn-action@v0.1.5
run: |
cd /home/pi/PlanktoScope
just ci
# UPLOAD OS IMAGE
- name: Determine output image name
env:
TAG_PREFIX: 'software/v'
run: |
# Determine the version for the image name
if [[ -n "${{ github.event.pull_request.head.sha }}" ]]; then
# We're in a pull request
version="$(\
printf "pr-%s-%.7s" \
"${{ github.event.pull_request.number }}" \
"${{ github.event.pull_request.head.sha }}" \
)"
elif [[ "${{ github.ref_type }}" == "tag" && "${{ github.ref }}" == refs/tags/$TAG_PREFIX* ]]; then
version="${{ github.ref }}"
version="v${version#"refs/tags/$TAG_PREFIX"}"
else
version="$(printf "%.7s" "${{ github.sha }}")"
fi
variant=""
if [[ "${{ inputs.base_release_name }}" != "bookworm" ]]; then
variant="${{ inputs.base_release_name }}"
fi
if [[ "${{ inputs.arch }}" != "arm64" ]]; then
variant="$variant.${{ inputs.arch }}"
fi
if [[ "${{ inputs.base_image_variant }}" != "lite" ]]; then
variant="$variant.${{ inputs.base_image_variant }}"
fi
# Assemble the image name
output_name="${{ inputs.name }}-$version"
if [[ $variant != "" ]]; then
output_name="$output_name.$variant"
fi
echo "OUTPUT_IMAGE_NAME=$output_name" >> $GITHUB_ENV
- name: Shrink the OS image
uses: ethanjli/pishrink-action@v0.1.4
env:
PISHRINK_XZ: -T0 ${{ github.ref_type == 'tag' && '-9' || '-1' }}
with:
image: ${{ steps.expand-image.outputs.destination }}
destination: ${{ env.OUTPUT_IMAGE_NAME }}.img
compress: xz
compress-parallel: true
- name: Upload image to Job Artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ env.OUTPUT_IMAGE_NAME }}
path: ${{ env.OUTPUT_IMAGE_NAME }}.img.xz
if-no-files-found: error
retention-days: 0
compression-level: 0
overwrite: true