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@v4 - name: Install .NET SDK uses: actions/setup-dotnet@v4 with: dotnet-version: 10.0.301 - name: Restore locked dependencies run: dotnet restore Rendezvous.slnx --locked-mode - name: Verify formatting and analyzers run: dotnet format Rendezvous.slnx --verify-no-changes --no-restore - 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: 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@v4 - name: Install .NET SDK uses: actions/setup-dotnet@v4 with: dotnet-version: 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 docker compose -f "$compose_file" up --build --detach container_id="$(docker compose -f "$compose_file" ps -q rendezvous)" test -n "$container_id" 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 http://127.0.0.1:8080/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"