diff --git a/.github/actions/setup-podman/action.yml b/.github/actions/setup-podman/action.yml index 954140f..2814e2b 100644 --- a/.github/actions/setup-podman/action.yml +++ b/.github/actions/setup-podman/action.yml @@ -101,6 +101,14 @@ runs: systemctl --user enable --now podman.socket echo "DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock" >> $GITHUB_ENV + - name: Expose Podman socket as /var/run/docker.sock + shell: bash + run: | + # GitHub Actions container-based actions mount /var/run/docker.sock + # hardcoded. Symlink it to the Podman socket so those actions work. + sudo ln -sf "$XDG_RUNTIME_DIR/podman/podman.sock" /var/run/docker.sock + sudo chmod 666 "$XDG_RUNTIME_DIR/podman/podman.sock" + - name: Verify Podman and podman-compose shell: bash run: | diff --git a/.github/workflows/_robot-tests.yml b/.github/workflows/_robot-tests.yml new file mode 100644 index 0000000..2d95f5f --- /dev/null +++ b/.github/workflows/_robot-tests.yml @@ -0,0 +1,66 @@ +name: Robot Tests + +on: + workflow_call: + inputs: + release_version: + required: true + type: string + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + robot-tests: + runs-on: ubuntu-24.04 + + steps: + - name: Checkout repository + uses: actions/checkout@v7 + + - name: Set up Podman + uses: ./.github/actions/setup-podman + + - name: Login to ghcr.io + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | \ + podman login ghcr.io \ + -u "${{ github.actor }}" \ + --password-stdin + + - name: Download runtime image (amd64) + uses: actions/download-artifact@v8 + with: + name: runtime-image-amd64 + + - name: Load runtime image + run: | + zstd -d runtime.tar.zst -o runtime.tar + podman load < runtime.tar + podman images + podman tag \ + ghcr.io/${{ github.repository }}:latest-amd64 \ + packtly-builder:latest + + - name: Run Robot Framework tests + run: ./test/robot/run.sh + + - name: Fix step summary permissions for Robot Reporter + if: always() + run: chmod o+w "$GITHUB_STEP_SUMMARY" + + - name: Robot Reporter + if: always() && github.event_name == 'pull_request' + uses: joonvena/robotframework-reporter-action@v2.5 + with: + gh_access_token: ${{ secrets.GITHUB_TOKEN }} + report_path: test/robot/results + + - name: Upload Robot results + if: always() + uses: actions/upload-artifact@v7 + with: + name: robot-results + path: test/robot/results + if-no-files-found: warn + retention-days: 7 diff --git a/.github/workflows/_tooling.yml b/.github/workflows/_tooling.yml index 26d1db4..f8a7629 100644 --- a/.github/workflows/_tooling.yml +++ b/.github/workflows/_tooling.yml @@ -45,13 +45,20 @@ jobs: command: just build release-version: ${{ inputs.release_version }} - - name: Publish test results - uses: dorny/test-reporter@v3 + - name: Publish Unit Test Results + uses: mikepenz/action-junit-report@v6 if: always() with: - name: Pytest Results - path: packtly-builder/tooling/python-test-results.xml - reporter: python-xunit + comment: true + detailed_summary: true + group_suite: true + include_passed: true + include_time_in_summary: true + job_summary: true + report_paths: packtly-builder/tooling/python-test-results.xml + simplified_summary: false + skip_success_summary: false + updateComment: true - name: Coverage summary uses: orgoro/coverage@ca0c362dc1a4f100447309405e6dfea47e251495 diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 969ac8d..8211b86 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -63,3 +63,13 @@ jobs: permissions: packages: write + robot-tests: + needs: + - runtime-manifest + uses: ./.github/workflows/_robot-tests.yml + with: + release_version: ${{ needs.version.outputs.release_version }} + secrets: inherit + permissions: + checks: write + pull-requests: write diff --git a/.gitignore b/.gitignore index bbbe7f2..c1ddb98 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,11 @@ cover/ python-test-results.xml test-output.xml *.log + +# Robot Framework artifacts +output.xml +report.html +log.html +xunit.xml +debugfile.txt +test/robot/results/ diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 18d15b8..02306a4 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -13,6 +13,14 @@ "cwd": "${workspaceFolder}/packtly-builder/tooling" }, "problemMatcher": [] + }, + { + "label": "run robot tests", + "type": "shell", + "command": "${workspaceFolder}/test/robot/run.sh", + "problemMatcher": [ + "$python" + ] } ], "inputs": [ diff --git a/test/robot/keywords/aptly.robot b/test/robot/keywords/aptly.robot new file mode 100644 index 0000000..253005b --- /dev/null +++ b/test/robot/keywords/aptly.robot @@ -0,0 +1,11 @@ +*** Settings *** +Library OperatingSystem + +*** Keywords *** +Generate Aptly Credentials + [Arguments] + ... ${path} + ... ${username}=admin + ... ${password}=password + + Create File ${path} username=${username}\npassword=${password} diff --git a/test/robot/keywords/git.robot b/test/robot/keywords/git.robot new file mode 100644 index 0000000..3340f17 --- /dev/null +++ b/test/robot/keywords/git.robot @@ -0,0 +1,18 @@ +*** Settings *** +Library Process + +*** Keywords *** +Checkout Repository + [Arguments] ${url} ${path} ${branch}=main + + ${result}= Run Process + ... git + ... clone + ... --depth + ... 1 + ... --branch + ... ${branch} + ... ${url} + ... ${path} + + Should Be Equal As Integers ${result.rc} 0 ${result.stderr} diff --git a/test/robot/keywords/gpg.robot b/test/robot/keywords/gpg.robot new file mode 100644 index 0000000..74c339a --- /dev/null +++ b/test/robot/keywords/gpg.robot @@ -0,0 +1,48 @@ +*** Settings *** +Library Process +Library String +Library OperatingSystem + +*** Variables *** +${GEN_GPG_SCRIPT} ${EMPTY} + +*** Keywords *** +Get Gen GPG Script + ${result}= Run Process git rev-parse --show-toplevel + Should Be Equal As Integers ${result.rc} 0 ${result.stderr} + ${topdir}= Strip String ${result.stdout} + RETURN ${topdir}/packtly-builder/containers/scripts/gen_gpg + +Generate GPG Keys + [Arguments] + ... ${keys_dir} + ... ${image} + ... ${name}=packtly-test + ... ${email}=test@packtly.local + ... ${pass}=test1234 + + ${gpg_out}= Set Variable ${keys_dir}/_gpg_out + ${gen_gpg}= Get Gen GPG Script + + Create Directory ${keys_dir}/public + Create Directory ${keys_dir}/private + Create Directory ${gpg_out} + + ${result}= Run Process + ... podman run --rm + ... --entrypoint /usr/local/bin/gen_gpg + ... -v ${gen_gpg}:/usr/local/bin/gen_gpg:Z,ro + ... -v ${gpg_out}:/opt/keys/gpg:Z + ... ${image} + ... --name ${name} + ... --email ${email} + ... --pass ${pass} + ... stdout=PIPE stderr=PIPE + + Should Be Equal As Integers ${result.rc} 0 ${result.stderr} + + Move File ${gpg_out}/repo_signing.key ${keys_dir}/public/repo_signing.key + Move File ${gpg_out}/repo_signing_private.key ${keys_dir}/private/repo_signing_private.key + Move File ${gpg_out}/repo_signing_private_pass ${keys_dir}/private/repo_signing_private_pass + + Remove Directory ${gpg_out} recursive=True diff --git a/test/robot/requirements.txt b/test/robot/requirements.txt new file mode 100644 index 0000000..357978a --- /dev/null +++ b/test/robot/requirements.txt @@ -0,0 +1,4 @@ +robotframework +robotframework-requests +robotframework-jsonlibrary +robotframework-pythonlibcore diff --git a/test/robot/run.sh b/test/robot/run.sh new file mode 100755 index 0000000..bd305c9 --- /dev/null +++ b/test/robot/run.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Install robotframework if not already installed +if ! pipx list --short | grep -q '^robotframework '; then + pipx install \ + --pip-args="-r ${ROOT}/requirements.txt" \ + robotframework +fi + +# Tests ausführen +robot \ + --outputdir "${ROOT}/results" \ + "${ROOT}/suites" + diff --git a/test/robot/suites/hello_world.robot b/test/robot/suites/hello_world.robot new file mode 100644 index 0000000..6cbba8d --- /dev/null +++ b/test/robot/suites/hello_world.robot @@ -0,0 +1,39 @@ +*** Settings *** +Documentation Hello World – Robot Framework Einstiegsbeispiel +... Zeigt grundlegende Keywords, Variablen und Log-Ausgaben. + +*** Variables *** +${GREETING} Hello, Robot World! +${NAME} Packtly Builder + +*** Test Cases *** + +Hello World Ausgabe + [Documentation] Gibt eine einfache Begrüßung aus. + Log ${GREETING} + Log To Console ${GREETING} + +Variablen verwenden + [Documentation] Demonstriert die Nutzung von Robot-Variablen. + ${message}= Set Variable Hello from ${NAME}! + Should Be Equal ${message} Hello from ${NAME}! + Log ${message} + +Einfache Assertion + [Documentation] Prüft arithmetische Gleichheit via BuiltIn. + ${result}= Evaluate 2 + 2 + Should Be Equal As Integers ${result} 4 + +Schleifen Demo + [Documentation] Iteriert über eine Liste und loggt jeden Eintrag. + @{items}= Create List alpha beta gamma + FOR ${item} IN @{items} + Log Item: ${item} + END + +*** Keywords *** + +Benutze Gruss + [Arguments] ${name} + [Documentation] Gibt einen personalisierten Gruß zurück. + RETURN Hello, ${name}! diff --git a/test/robot/suites/test_build_systemd.robot b/test/robot/suites/test_build_systemd.robot new file mode 100644 index 0000000..ff4cd6b --- /dev/null +++ b/test/robot/suites/test_build_systemd.robot @@ -0,0 +1,99 @@ +*** Settings *** +Library OperatingSystem +Library Process +Resource ../keywords/git.robot +Resource ../keywords/gpg.robot +Resource ../keywords/aptly.robot + +Suite Setup Prepare Workspace +Suite Teardown Remove Directory ${WORKSPACE} recursive=True + +*** Variables *** +${SYSTEMD_URL} https://salsa.debian.org/systemd-team/systemd.git +${SYSTEMD_BRANCH} debian/master +${CONTAINER_IMAGE} packtly-builder:latest +${APTLY_CREDENTIALS} ${EMPTY} +${WORKSPACE} ${EMPTY} +${KEYS_DIR} ${EMPTY} + +*** Keywords *** +Prepare Workspace + Set Suite Variable ${WORKSPACE} ${TEMPDIR}/systemd-build + Set Suite Variable ${KEYS_DIR} ${WORKSPACE}/keys + Set Suite Variable ${APTLY_CREDENTIALS} ${WORKSPACE}/aptly-credentials + Create Directory ${WORKSPACE} + Create Directory ${WORKSPACE}/logs + Generate GPG Keys ${KEYS_DIR} ${CONTAINER_IMAGE} + Generate Aptly Credentials ${APTLY_CREDENTIALS} + +Run Systemd build + [Arguments] + ... ${workspace} + ... ${keys_dir} + ... ${aptly_credentials} + ... ${image} + ... ${dist}=trixie-apollo + ... ${component}=main + + ${result}= Run Process + ... podman run --rm + ... -v ${workspace}:/workspace:Z + ... -v ${keys_dir}/public/repo_signing.key:/opt/keys/gpg/repo_signing.key:Z,ro + ... -v ${keys_dir}/private/repo_signing_private.key:/opt/keys/gpg/repo_signing_private.key:Z,ro + ... -v ${keys_dir}/private/repo_signing_private_pass:/opt/keys/gpg/repo_signing_private_pass:Z,ro + ... -v ${aptly_credentials}:/run/secrets/aptly-credentials:Z,ro + ... -v ${workspace}/logs:/logs:Z + ... -e APTLYHOST\=http://localhost:8080 + ... -e DEB_BUILD_OPTIONS\=nocheck + ... --cap-add SYS_ADMIN + ... --cap-add SYS_PTRACE + ... --network\=host + ... ${image} + ... /workspace/systemd + ... --log-file /logs/build.log + ... --dist ${dist} + ... --component ${component} + + Log File ${workspace}/logs/build.log + Copy File ${workspace}/logs/build.log ${CURDIR}/../results/systemd_build.log + Should Be Equal As Integers ${result.rc} 0 + ... Build failed with rc ${result.rc}. See stdout/stderr and build.log above. + + +Verify Build Artifacts + [Documentation] Verify a successful build produced valid .changes and .deb artifacts. + [Arguments] ${workspace} ${image} + + # A successful debuild leaves the .changes and .deb files in the workspace. + ${changes}= List Files In Directory ${workspace} *.changes absolute=True + Should Not Be Empty ${changes} No .changes file produced - build did not complete + ${debs}= List Files In Directory ${workspace} *.deb absolute=True + Should Not Be Empty ${debs} No .deb packages produced + + # Validate each .deb is a structurally valid Debian package. + FOR ${deb} IN @{debs} + ${info}= Run Process + ... podman run --rm + ... --entrypoint dpkg-deb + ... -v ${workspace}:/workspace:Z,ro + ... ${image} + ... --info /workspace/${deb.rsplit('/', 1)[1]} + ... stdout=PIPE stderr=PIPE + Log ${info.stdout} + Should Be Equal As Integers ${info.rc} 0 + ... dpkg-deb rejected ${deb}: ${info.stderr} + END + + + +*** Test Cases *** +Test Systemd Build + [Documentation] Test building systemd package with packtly-builder + [Tags] systemd build + Checkout Repository ${SYSTEMD_URL} ${WORKSPACE}/systemd ${SYSTEMD_BRANCH} + Run Systemd build + ... ${WORKSPACE} + ... ${KEYS_DIR} + ... ${APTLY_CREDENTIALS} + ... image=${CONTAINER_IMAGE} + Verify Build Artifacts ${WORKSPACE} ${CONTAINER_IMAGE}