Deduplicate CI workflows via a reusable workflow

Move the shared build, SBOM, test and publish jobs into
build-test-publish.yml and turn the master and stable workflows into
thin callers. Drop the 24.10-SNAPSHOT workflow.

Also fix the stable SBOM job scanning the previously published :latest
image instead of the freshly built CI image, and use the checked-out
mount script instead of downloading it from master.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Albrecht Lohofener
2026-10-19 10:09:20 +02:00
co-authored by Claude Opus 5.5
parent db8cd43068
commit dede682981
3 changed files with 403 additions and 693 deletions
+386
View File
@@ -0,0 +1,386 @@
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 launch docker --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: |
# By default portainer is running at port 9000. But we need port 9000
multipass exec router-vm -- docker stop portainer
# 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 ens4
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 ens3 -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 ens4
multipass exec lan-vm -- sudo ip addr add 172.31.22.2/24 dev ens4
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 ens4
multipass exec router-vm -- sudo ip addr add 172.31.22.99/24 dev ens5
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"
+8 -350
View File
@@ -1,4 +1,3 @@
name: OpenWrt master build
on:
@@ -8,353 +7,12 @@ on:
schedule:
- cron: '0 20 * * *'
env:
OPENWRT_VERSION: master
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: |
albrechtloh/openwrt-docker:ci-openwrt-master-snapshot
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:
context: . # TODO: Just for testing
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
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=$OPENWRT_VERSION.$DATETIME-$GIT_HASH_DECIMAL
APP_VERSION=0.0.0.$DATETIME-$GIT_HASH_DECIMAL # TODO just for SNAPSHOT
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: albrechtloh/openwrt-docker:ci-openwrt-master-snapshot
upload-artifact: false
format: cyclonedx-json
output-file: alpine_sbom.json
- name: Prepare OpenWrt rootfs
run: |
# Use automated generated image
sed -i 's/image: albrechtloh\/openwrt-docker:latest/image: albrechtloh\/openwrt-docker:ci-openwrt-master-snapshot/g' 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
wget https://raw.githubusercontent.com/AlbrechtL/openwrt-docker/refs/heads/master/src/mount_openwrt_squashfs_combined.sh
chmod +x mount_openwrt_squashfs_combined.sh
sudo ./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
runs-on: ubuntu-latest
needs: build
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 launch docker --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: |
# By default portainer is running at port 9000. But we need port 9000
multipass exec router-vm -- docker stop portainer
# 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 ens4
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 ens3 -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 ens4
multipass exec lan-vm -- sudo ip addr add 172.31.22.2/24 dev ens4
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 ens4
multipass exec router-vm -- sudo ip addr add 172.31.22.99/24 dev ens5
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: Prepare tests
run: |
# Use local image instead of hub.docker.com
sed -i 's/image: albrechtloh\/openwrt-docker:latest/image: openwrt-docker/g' test/tests/docker-compose.yml.template
cat test/tests/docker-compose.yml.template
- 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: Login into Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Tag image at Docker Hub
run: |
docker buildx imagetools create \
--tag albrechtloh/openwrt-docker:openwrt-master-snapshot \
albrechtloh/openwrt-docker:ci-openwrt-master-snapshot
build-test-publish:
uses: ./.github/workflows/build-test-publish.yml
with:
openwrt_version: master
ci_tag: ci-openwrt-master-snapshot
app_version_prefix: '0.0.0'
publish_tags: openwrt-master-snapshot
secrets: inherit
+9 -343
View File
@@ -5,347 +5,13 @@ on:
branches:
- master
env:
OPENWRT_VERSION: 25.12.6
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: |
albrechtloh/openwrt-docker:ci-openwrt-stable
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
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=$OPENWRT_VERSION.$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: albrechtloh/openwrt-docker:ci-openwrt-stable
upload-artifact: false
format: cyclonedx-json
output-file: alpine_sbom.json
- name: Prepare OpenWrt rootfs
run: |
# Run OpenWrt container for some time to install additional IPKs
set +e # Enable that the next command can fail
timeout --preserve-status 30 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
wget https://raw.githubusercontent.com/AlbrechtL/openwrt-docker/refs/heads/master/src/mount_openwrt_squashfs_combined.sh
chmod +x mount_openwrt_squashfs_combined.sh
sudo ./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 launch docker --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: |
# By default portainer is running at port 9000. But we need port 9000
multipass exec router-vm -- docker stop portainer
# 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 ens4
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 ens3 -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 ens4
multipass exec lan-vm -- sudo ip addr add 172.31.22.2/24 dev ens4
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 ens4
multipass exec router-vm -- sudo ip addr add 172.31.22.99/24 dev ens5
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: |
# 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
run: |
GIT_HASH=`git rev-parse --short HEAD`
docker buildx imagetools create \
--tag albrechtloh/openwrt-docker:openwrt-${{ env.OPENWRT_VERSION }}-$GIT_HASH \
--tag albrechtloh/openwrt-docker:latest \
albrechtloh/openwrt-docker:ci-openwrt-stable
build-test-publish:
uses: ./.github/workflows/build-test-publish.yml
with:
openwrt_version: 25.12.6
ci_tag: ci-openwrt-stable
app_version_prefix: 25.12.6
publish_tags: latest
publish_versioned_tag: true
secrets: inherit