name: quality-gate on: push: branches: - main - codex/** pull_request: workflow_dispatch: jobs: quality: runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: Check out repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 - name: Install .NET SDK uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1 with: dotnet-version: | 8.0.128 10.0.301 - name: Restore locked dependencies run: dotnet restore Rendezvous.slnx --locked-mode - name: Verify dependency licenses and reviewed transport pin run: python3 eng/release_artifacts.py policy --root . - name: Reject vulnerable direct or transitive packages shell: bash run: | set -euo pipefail dotnet package list --project Rendezvous.slnx \ --vulnerable --include-transitive --no-restore --format json \ >"${RUNNER_TEMP}/nuget-vulnerabilities.json" python3 eng/release_artifacts.py audit \ --input "${RUNNER_TEMP}/nuget-vulnerabilities.json" - name: Enforce compatibility version bumps run: ./scripts/check-compatibility.sh origin/main - name: Verify formatting and analyzers run: dotnet format Rendezvous.slnx --verify-no-changes --no-restore - name: Test dependency-free diagnostic dashboard run: ./scripts/test-diagnostic-dashboard.sh - name: Test observability dashboard provisioning run: ./scripts/test-observability-assets.sh - name: Build run: dotnet build Rendezvous.slnx --configuration Release --no-restore - name: Verify generated API contract run: git diff --exit-code -- docs/api - name: Test run: dotnet test Rendezvous.slnx --configuration Release --no-build - name: Run quick capacity and resilience gate run: ./scripts/run-capacity-gate.sh - name: Test privileged Linux namespace topology when available shell: bash run: | set -euo pipefail probe="rendezvous-probe-$$" suffix="$(( $$ % 100000 ))" bridge="rvb${suffix}" veth_root="rvr${suffix}" veth_peer="rvp${suffix}" cleanup_probe() { if [[ -n "$veth_root" ]]; then ip link delete "$veth_root" >/dev/null 2>&1 || true fi if [[ -n "$bridge" ]]; then ip link delete "$bridge" >/dev/null 2>&1 || true fi if [[ -n "$probe" ]]; then ip netns delete "$probe" >/dev/null 2>&1 || true fi } trap cleanup_probe EXIT if command -v ip >/dev/null 2>&1 \ && command -v iptables >/dev/null 2>&1 \ && command -v sysctl >/dev/null 2>&1 \ && ip netns add "$probe" 2>/dev/null \ && ip link add "$bridge" type bridge \ && ip link add "$veth_root" type veth peer name "$veth_peer" \ && ip link set "$veth_root" master "$bridge" \ && ip link set "$veth_peer" netns "$probe" \ && ip netns exec "$probe" sysctl -q -w net.ipv4.ip_forward=1 \ && ip netns exec "$probe" iptables -t nat -A POSTROUTING -o "$veth_peer" -j MASQUERADE \ && ip netns exec "$probe" iptables -A FORWARD -i "$veth_peer" -o lo \ -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT; then ip link delete "$veth_root" veth_root="" ip link delete "$bridge" bridge="" ip netns delete "$probe" probe="" results="${RUNNER_TEMP:-/tmp}/rendezvous-netns-results" mkdir -p "$results" RENDEZVOUS_RUN_NETNS_TESTS=1 dotnet test Rendezvous.slnx \ --configuration Release \ --no-build \ --filter FullyQualifiedName~PrivilegedLinuxNatNamespacesCompleteDirectTrafficAcrossSeparateObservedEndpoints \ --logger "trx;LogFileName=netns.trx" \ --results-directory "$results" grep -q 'testName="[^"]*\.PrivilegedLinuxNatNamespacesCompleteDirectTrafficAcrossSeparateObservedEndpoints"' \ "$results/netns.trx" else echo "Network namespaces/NAT tooling unavailable; deterministic loopback topology remains the required gate." fi container: needs: quality runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: Check out repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install .NET SDK uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1 with: dotnet-version: | 8.0.128 10.0.301 - name: Build deployment diagnostic run: | dotnet restore src/FinalFactory.Rendezvous.TestClient/FinalFactory.Rendezvous.TestClient.csproj --locked-mode dotnet build src/FinalFactory.Rendezvous.TestClient/FinalFactory.Rendezvous.TestClient.csproj --configuration Release --no-restore - name: Build and exercise hardened container shell: bash run: | set -euo pipefail compose_file="deploy/compose/compose.yaml" secret="deploy/compose/secrets/signing-key" cleanup() { RENDEZVOUS_UID=1654 RENDEZVOUS_GID=1654 \ docker compose -f "$compose_file" down --volumes >/dev/null 2>&1 || true rm -f "$secret" } trap cleanup EXIT install -d -m 0700 deploy/compose/secrets openssl rand -out "$secret" 32 chmod 0444 "$secret" export RENDEZVOUS_UID=1654 export RENDEZVOUS_GID=1654 port_suffix="$(( ${GITHUB_RUN_ID:-$$} % 10000 ))" export RENDEZVOUS_HTTP_HOST_PORT="$(( 20000 + port_suffix ))" export RENDEZVOUS_UDP_HOST_PORT="$(( 40000 + port_suffix ))" runner_network="$(docker inspect "$HOSTNAME" | jq -er \ '.[0].NetworkSettings.Networks | keys[0]')" runner_workspace_source="$(docker inspect "$HOSTNAME" | jq -er \ --arg destination "$GITHUB_WORKSPACE" \ '.[0].Mounts[] | select(.Destination == $destination) | .Source')" export RENDEZVOUS_CONFIG_SOURCE="$runner_workspace_source/deploy/compose/appsettings.Production.json" export RENDEZVOUS_SECRET_SOURCE="$runner_workspace_source/deploy/compose/secrets/signing-key" export SOURCE_REVISION_ID="$GITHUB_SHA" docker compose -f "$compose_file" build \ --build-arg SOURCE_REVISION_ID="$SOURCE_REVISION_ID" docker compose -f "$compose_file" up --no-build --detach container_id="$(docker compose -f "$compose_file" ps -q rendezvous)" test -n "$container_id" docker network connect "$runner_network" "$container_id" service_ip="$(docker inspect "$container_id" | jq -er \ --arg network "$runner_network" \ '.[0].NetworkSettings.Networks[$network].IPAddress')" export RENDEZVOUS_SMOKE_HTTP_URL="http://${service_ip}:8080/" export RENDEZVOUS_SMOKE_UDP_ENDPOINT="${service_ip}:9050" test "$(docker inspect --format '{{.Config.User}}' "$container_id")" = "1654:1654" test "$(docker inspect --format '{{.HostConfig.ReadonlyRootfs}}' "$container_id")" = "true" test "$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/app/appsettings.Production.json"}}{{.RW}}{{end}}{{end}}' "$container_id")" = "false" test "$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/run/secrets/rendezvous-signing-key"}}{{.RW}}{{end}}{{end}}' "$container_id")" = "false" for attempt in {1..100}; do if curl --fail --silent "${RENDEZVOUS_SMOKE_HTTP_URL%/}/health/ready" >/dev/null 2>&1; then break fi if (( attempt == 100 )); then docker compose -f "$compose_file" logs rendezvous exit 1 fi sleep 0.1 done ./scripts/smoke-deployment.sh docker compose -f "$compose_file" stop --timeout 40 rendezvous test "$(docker inspect --format '{{.State.Running}}' "$container_id")" = "false" test "$(docker inspect --format '{{.State.ExitCode}}' "$container_id")" = "0"