Files
openwrt-docker/.github/workflows/build-test-publish.yml
T
Albrecht LohofenerandClaude Opus 5.5 5af163b06f Adapt CI to eth* NIC names of multipass 1.17 VMs
Multipass 1.17 VMs name their NICs eth0/eth1/eth2 instead of
ens3/ens4/ens5, so 'ip addr add ... dev ens4' fails on wan-vm. Update the
interface names in the test job and the WAN_IF/LAN_IF test parameters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:04:41 +02:00

387 lines
14 KiB
YAML

name: Build, test and publish
on:
workflow_call:
inputs:
openwrt_version:
description: OpenWrt version passed as OPENWRT_VERSION build argument (e.g. "master" or "25.12.6")
required: true
type: string
ci_tag:
description: Docker Hub tag used for the intermediate CI image (e.g. "ci-openwrt-stable")
required: true
type: string
app_version_prefix:
description: Prefix of the u-OS app version. uc-aom-packager accepts decimal numbers only.
required: true
type: string
publish_tags:
description: Space-separated Docker Hub tags that are set after the tests passed
required: true
type: string
publish_versioned_tag:
description: Additionally publish the tag "openwrt-<openwrt_version>-<git hash>"
required: false
type: boolean
default: false
env:
OPENWRT_VERSION: ${{ inputs.openwrt_version }}
CI_IMAGE: albrechtloh/openwrt-docker:${{ inputs.ci_tag }}
jobs:
build:
name: Build
runs-on: ubuntu-26.04
services:
registry:
image: registry:2
ports:
- 5000:5000
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set Swap Space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 10
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
with:
driver-opts: network=host # Important to access local Docker registry
- name: Login into Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build Docker image
uses: docker/build-push-action@v7
env:
DOCKER_BUILD_RECORD_UPLOAD: false
with:
context: .
push: true
provenance: false
platforms: |
linux/x86_64
linux/arm64
build-args: |
OPENWRT_VERSION=${{ env.OPENWRT_VERSION }}
BUILDKIT_MULTI_PLATFORM=1
tags: |
${{ env.CI_IMAGE }}
localhost:5000/local/openwrt-docker:latest
- name: Inspect
run: |
docker buildx imagetools inspect localhost:5000/local/openwrt-docker:latest
- name: Build and export
uses: docker/build-push-action@v7
env:
DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false
with:
tags: openwrt-docker:latest
build-args: OPENWRT_VERSION=${{ env.OPENWRT_VERSION }}
allow: security.insecure,network.host
outputs: type=docker,dest=/tmp/openwrt-docker-image.tar
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: OpenWrt Docker Image
path: /tmp/openwrt-docker-image.tar
- name: Login to Weidmueller wmucdev
uses: docker/login-action@v4
with:
registry: wmucdev.azurecr.io
username: ${{ secrets.WMUCDEV_USERNAME }}
password: ${{ secrets.WMUCDEV_TOKEN }}
- name: Create Weidmueller u-OS app
id: create_u-os-app
env:
APP_VERSION_PREFIX: ${{ inputs.app_version_prefix }}
run: |
# uc-aom-packager accept only version that has to be greater than the previous one. Lets use the datetime to ensure that.
DATETIME=$(date "+%Y%m%d%H%M%S")
# uc-aom-packager allow only decimal numbers as version number for an unkonwn reason
# see https://github.com/weidmueller/uc-addon/blob/main/uc-manifest.schema-doc.md#version)
# So lets convert the git hash into a decimal value
GIT_HASH=`git rev-parse --short HEAD`
GIT_HASH_DECIMAL=$((16#$GIT_HASH))
# Create app version
APP_VERSION=$APP_VERSION_PREFIX.$DATETIME-$GIT_HASH_DECIMAL
echo "***** Create manifest from template *****"
mv u-os-app/manifest.json.template u-os-app/manifest.json
sed -i 's/APPVERSION_TO_INSERT/'"$APP_VERSION"'/g' u-os-app/manifest.json
cat u-os-app/manifest.json | grep version
echo "***** Run uc-aom-packager to create the u-OS app *****"
# uc-aom-packager accepts only multiarch images. "--build-arg BUILDKIT_MULTI_PLATFORM=1" has to set to ensure that.
# uc-aom-packager needs to Docker registry credentials in a clear text file. Because of security concerns we use a local registry without credentials here.
docker run --rm --network=host --pull=always \
--mount src=$(pwd)/u-os-app,target=/tmp/u-os-app-openwrt,type=bind wmucdev.azurecr.io/u-control/uc-aom-packager:0 \
uc-aom-packager push \
-m /tmp/u-os-app-openwrt \
-s /tmp/u-os-app-openwrt/source-credentials.json \
-t /tmp/u-os-app-openwrt/target-credentials.json \
-v
echo "***** Create SWU app file*****"
docker run --rm --network=host --pull=always \
--mount src=$(pwd)/u-os-app,target=/tmp/u-os-app-openwrt,type=bind wmucdev.azurecr.io/u-control/uc-aom-packager:0 \
uc-aom-packager \
export \
-t /tmp/u-os-app-openwrt/target-credentials.json \
--version $APP_VERSION \
-o /tmp/u-os-app-openwrt/u-os-app-openwrt_$APP_VERSION.swu \
-v
find u-os-app/
- name: Archive artifacts (OpenWrt u-OS app)
if: always() && steps.create_u-os-app.outcome == 'success'
uses: actions/upload-artifact@v7
with:
name: OpenWrt u-OS app
path: |
u-os-app/u-os-app-openwrt_*.swu/linux-arm64/*.swu
u-os-app/u-os-app-openwrt_*.swu/linux-amd64/*.swu
if-no-files-found: error
sbom:
name: SBOM generation
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Generate alpine SBOM
uses: anchore/sbom-action@v0
with:
image: ${{ env.CI_IMAGE }}
upload-artifact: false
format: cyclonedx-json
output-file: alpine_sbom.json
- name: Prepare OpenWrt rootfs
run: |
# Use automated generated image (commented "#image:" lines are not touched)
sed -i "s|^\(\s*\)image: .*|\1image: $CI_IMAGE|" docker-compose.yml
grep -n "image:" docker-compose.yml
# Run OpenWrt container for some time to install additional IPKs
set +e # Enable that the next command can fail
timeout --preserve-status 60 docker compose up
set -e # Revert set +e
# Get image from volume
sudo find /var/lib/docker/volumes/
IMAGE=$(sudo cat /var/lib/docker/volumes/openwrt-docker_data/_data/current_version)
echo $IMAGE
# Mount volume
sudo ./src/mount_openwrt_squashfs_combined.sh "/var/lib/docker/volumes/openwrt-docker_data/_data/${IMAGE}"
- name: Generate OpenWrt SBOM
uses: anchore/sbom-action@v0
with:
path: /mnt
upload-artifact: false
format: cyclonedx-json
output-file: openwrt_sbom.json
- name: Generate web-frontend SBOM
uses: anchore/sbom-action@v0
with:
path: web-frontend
upload-artifact: false
format: cyclonedx-json
output-file: web-frontend_sbom.json
- name: Generate web-backend SBOM
uses: anchore/sbom-action@v0
with:
path: web-backend
upload-artifact: false
format: cyclonedx-json
output-file: web-backend_sbom.json
- name: Archive artifacts (SBOMs)
uses: actions/upload-artifact@v7
with:
name: Software Bill of Material (SBOM)
path: ./*.json
if-no-files-found: error
test:
name: Test
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Download artifact
uses: actions/download-artifact@v8
with:
name: OpenWrt Docker Image
path: .
- name: Install VMs
run: |
set -x
sudo snap install multipass # need multipass >= 1.15 because of networks support
# Workaround for "multipass_root_cert.pem': No such file or directory" issue, see https://github.com/canonical/multipass/issues/5015
sleep 180
# Workaround for 'multipass socket access denied'
sudo chmod a+w /var/snap/multipass/common/multipass_socket
multipass find
# Set up some bridges for the VMs
sudo ip link add lannetvm type bridge
sudo ip link add wannetvm type bridge
sudo ip link set dev lannetvm up
sudo ip link set dev wannetvm up
ip addr
# We need to disable the firewall otherwise the tap devices are not working properly
sudo ufw disable
# Start VMs
# Multipass and the OpenWrt container are using the same MAC QUI 52:54:00 which creates error. Let's use differnet MAC addresses here
# Multipass >= 1.17 dropped blueprints (formerly "multipass launch docker"), so install Docker via cloud-init
printf '#cloud-config\nruncmd:\n - curl -fsSL https://get.docker.com | sh\n - adduser ubuntu docker\n' | \
multipass launch 24.04 --cpus 2 --memory 4G --disk 40G --timeout 1800 --cloud-init - \
--network name=wannetvm,mac=06:00:00:11:22:33 --network name=lannetvm,mac=06:00:00:44:55:66 --name router-vm
multipass mount $GITHUB_WORKSPACE router-vm
multipass launch --network wannetvm --name wan-vm
multipass launch --network lannetvm --name lan-vm
- name: Show VM and and environments variables
run: |
export
multipass exec router-vm -- lsb_release -a
multipass info
- name: Load image
run: |
multipass exec router-vm -- docker load --input openwrt-docker-image.tar
multipass exec router-vm -- docker image ls -a
- name: Prepare VMs
run: |
# Install python
multipass exec router-vm -- sudo apt-get -y install python3-pip python-is-python3 python3-venv
# Install everthing for pytest
multipass exec router-vm -- find .
multipass exec router-vm -- bash -c 'python -m venv ~/.venv'
multipass exec router-vm -- bash -c 'source ~/.venv/bin/activate && pip install -r test/requirements.txt'
echo '******** router-vm IPs ********'
multipass exec router-vm -- ip addr
# Prepare WAN VM
echo '******** wan-vm IPs ********'
# Add IP address
multipass exec wan-vm -- sudo ip addr add 192.168.22.1/24 dev eth1
multipass exec wan-vm -- ip addr
# Install DHCP server
multipass exec wan-vm -- sudo sh -c 'apt-get update && apt-get install -y isc-dhcp-server && echo "default-lease-time 600;\nmax-lease-time 7200;\nsubnet 192.168.22.0 netmask 255.255.255.0 {\n range 192.168.22.100 192.168.22.200;\n option routers 192.168.22.1;\n option domain-name-servers 8.8.8.8, 8.8.4.4;\n option domain-name \"example.org\";\n}" > /etc/dhcp/dhcpd.conf && systemctl enable isc-dhcp-server && systemctl restart isc-dhcp-server'
multipass exec wan-vm -- cat /etc/dhcp/dhcpd.conf
# Enable NAT
multipass exec wan-vm -- sudo modprobe iptable_nat
multipass exec wan-vm -- sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
multipass exec wan-vm -- sudo sysctl -w net.ipv4.ip_forward=1
# Prepare LAN VM
echo '******** lan-vm IPs ********'
multipass exec lan-vm -- sudo ip addr add 172.31.1.2/24 dev eth1
multipass exec lan-vm -- sudo ip addr add 172.31.22.2/24 dev eth1
multipass exec lan-vm -- ip addr
# Double check if virtual networks are working
echo '******** host IPs ********'
ip addr
sudo ufw status
echo '******** router-vm IPs ********'
multipass exec router-vm -- sudo ip addr add 192.168.22.99/24 dev eth1
multipass exec router-vm -- sudo ip addr add 172.31.22.99/24 dev eth2
multipass exec router-vm -- ip addr
echo '******** Test connection between router-vm, lan-vm and wan-vm ********'
multipass exec router-vm -- ping -c 1 192.168.22.1 # to wan-vm
multipass exec router-vm -- ping -c 1 172.31.22.2 # to lan-vm
- name: Run tests
run: |
cat test/tests/docker-compose.yml.template
# Run pytest
multipass exec router-vm -- bash -c 'source ~/.venv/bin/activate && cd test/ && pytest -v -s --html=report.html --self-contained-html --reruns 3'
- name: Archive test report
if: always()
uses: actions/upload-artifact@v7
with:
name: Test report
path: test/*.html
if-no-files-found: error
publish:
name: Publish
needs: test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Login into Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Tag image at Docker Hub
env:
PUBLISH_TAGS: ${{ inputs.publish_tags }}
PUBLISH_VERSIONED_TAG: ${{ inputs.publish_versioned_tag }}
run: |
TAGS=()
for TAG in $PUBLISH_TAGS; do
TAGS+=(--tag "albrechtloh/openwrt-docker:$TAG")
done
if [ "$PUBLISH_VERSIONED_TAG" = "true" ]; then
GIT_HASH=`git rev-parse --short HEAD`
TAGS+=(--tag "albrechtloh/openwrt-docker:openwrt-$OPENWRT_VERSION-$GIT_HASH")
fi
echo "Publishing $CI_IMAGE as: ${TAGS[*]}"
docker buildx imagetools create "${TAGS[@]}" "$CI_IMAGE"