chore(deps): bump docker/setup-qemu-action from 4.0.0 to 4.1.0 #101
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Integration Tests | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| jobs: | |
| integration: | |
| name: integration-tests | |
| runs-on: ubuntu-24.04 | |
| services: | |
| nats: | |
| image: nats:latest | |
| ports: | |
| - 4222:4222 | |
| # nats:latest is a scratch image — no shell, so no healthcheck. | |
| # Tests use wait_until() to retry connections instead. | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y ninja-build python3 python3-pip | |
| pip3 install --user conan | |
| - name: Configure Conan | |
| run: conan profile detect --force | |
| - name: Install Conan dependencies (gcc/release) | |
| run: | | |
| conan install . \ | |
| --output-folder=build/ci \ | |
| --build=missing \ | |
| --lockfile=conan.lock \ | |
| -s build_type=Release \ | |
| -s compiler=gcc \ | |
| -s compiler.version=14 \ | |
| -s compiler.libcxx=libstdc++11 \ | |
| -s compiler.cppstd=20 | |
| - name: Configure | |
| run: cmake --preset ci | |
| - name: Build | |
| run: cmake --build --preset ci | |
| - name: Start mock Agamemnon | |
| # Bind to an ephemeral port (port 0) so parallel jobs on the same | |
| # runner cannot collide on a fixed port (issue #91). The script | |
| # prints the actual bound port to stdout; we tee it to a log file | |
| # and extract the port in the next step. | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "$RUNNER_TEMP/mock-agamemnon" | |
| ( python3 scripts/mock-agamemnon.py \ | |
| > "$RUNNER_TEMP/mock-agamemnon/stdout.log" 2>&1 ) & | |
| echo $! > "$RUNNER_TEMP/mock-agamemnon/pid" | |
| env: | |
| PYTHONUNBUFFERED: "1" | |
| - name: Wait for mock Agamemnon to be ready | |
| run: | | |
| set -euo pipefail | |
| log="$RUNNER_TEMP/mock-agamemnon/stdout.log" | |
| # Wait for the "listening on :<port>" line to appear (up to ~10s). | |
| port="" | |
| for i in $(seq 1 40); do | |
| if [ -s "$log" ]; then | |
| port=$(grep -oE 'listening on :[0-9]+' "$log" \ | |
| | head -n1 \ | |
| | sed 's/^listening on ://') || port="" | |
| if [ -n "$port" ]; then | |
| break | |
| fi | |
| fi | |
| sleep 0.25 | |
| done | |
| if [ -z "$port" ]; then | |
| echo "mock-agamemnon never printed bound port" | |
| if [ -f "$log" ]; then cat "$log"; fi | |
| exit 1 | |
| fi | |
| echo "MOCK_AGAMEMNON_PORT=$port" >> "$GITHUB_ENV" | |
| echo "AGAMEMNON_URL=http://localhost:$port" >> "$GITHUB_ENV" | |
| for i in $(seq 1 20); do | |
| if curl -sf "http://localhost:$port/v1/health" | grep -q '"ok"'; then | |
| echo "mock-agamemnon ready on :$port"; exit 0 | |
| fi | |
| sleep 0.5 | |
| done | |
| echo "mock-agamemnon did not become ready in time" | |
| if [ -f "$log" ]; then cat "$log"; fi | |
| exit 1 | |
| - name: Verify NATS connectivity | |
| # Fail fast if NATS is unreachable, so REQUIRES_NATS tests can't | |
| # silently skip the NATS code paths (#92). A real NATS server greets | |
| # every new TCP connection with an INFO line; require it before tests. | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| probe_nats() { | |
| # Open a TCP connection to NATS and read the first line. Returns | |
| # 0 only when the server emits a valid INFO greeting; nonzero on | |
| # connection failure, timeout, or unexpected payload. | |
| local line | |
| set +e | |
| line=$(timeout 2 bash -c \ | |
| 'exec 3<>/dev/tcp/localhost/4222 && head -n 1 <&3' \ | |
| 2>/dev/null) | |
| local rc=$? | |
| set -e | |
| if [ "$rc" -ne 0 ]; then return 1; fi | |
| printf '%s' "$line" | grep -q '^INFO ' | |
| } | |
| for i in $(seq 1 30); do | |
| if probe_nats; then | |
| echo "NATS reachable after ${i}s" | |
| exit 0 | |
| fi | |
| sleep 1 | |
| done | |
| echo "ERROR: NATS server at nats://localhost:4222 did not greet within 30s" | |
| exit 1 | |
| - name: Run integration tests | |
| env: | |
| NATS_URL: nats://localhost:4222 | |
| # AGAMEMNON_URL is exported by the previous step via $GITHUB_ENV | |
| run: ctest --preset integration --output-junit integration-results.xml | |
| - name: Assert REQUIRES_NATS tests actually executed | |
| # Guard against the integration preset matching zero tests (e.g. if the | |
| # REQUIRES_NATS label is renamed/removed). ctest exits 0 when no tests | |
| # match the filter, which would silently bypass the NATS suite (#92). | |
| run: | | |
| set -euo pipefail | |
| xml=build/ci/integration-results.xml | |
| if [ ! -f "$xml" ]; then | |
| echo "ERROR: $xml not produced — ctest did not run" | |
| exit 1 | |
| fi | |
| count=$(python3 -c "import xml.etree.ElementTree as ET, sys; \ | |
| root = ET.parse('$xml').getroot(); \ | |
| print(sum(1 for _ in root.iter('testcase')))") | |
| echo "integration testcases executed: $count" | |
| if [ "$count" -lt 1 ]; then | |
| echo "ERROR: zero REQUIRES_NATS tests ran — NATS integration silently skipped (#92)" | |
| exit 1 | |
| fi | |
| - name: Upload ctest logs on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ctest-logs | |
| path: | | |
| build/ci/Testing/Temporary/LastTest.log | |
| build/ci/Testing/Temporary/LastTestsFailed.log | |
| build/ci/Testing/Temporary/CTestCostData.txt | |
| if-no-files-found: ignore | |
| retention-days: 7 |