diff --git a/.github/workflows/build-test-publish.yml b/.github/workflows/build-test-publish.yml new file mode 100644 index 0000000..c48038d --- /dev/null +++ b/.github/workflows/build-test-publish.yml @@ -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--" + 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" diff --git a/.github/workflows/openwrt-master-build.yml b/.github/workflows/openwrt-master-build.yml index f1acc13..ca6292e 100644 --- a/.github/workflows/openwrt-master-build.yml +++ b/.github/workflows/openwrt-master-build.yml @@ -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 diff --git a/.github/workflows/openwrt-stable-build.yml b/.github/workflows/openwrt-stable-build.yml index d31a6ff..0a6ca45 100644 --- a/.github/workflows/openwrt-stable-build.yml +++ b/.github/workflows/openwrt-stable-build.yml @@ -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