diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 9a6e4c1..f01fca7 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -30,21 +30,25 @@ jobs: verify: runs-on: ubuntu-latest - # Postgres is started as a step rather than through `services:`. + # Postgres is started as a step rather than through `services:`, because + # this runner is configured with `container.network: host`. # - # Two approaches were tried and rejected before this one: + # That single setting explains three failed attempts, and is worth writing + # down so nobody repeats them: # - # `services:` — this runner does not attach service - # containers to the job network, giving - # "getaddrinfo EAI_AGAIN postgres". - # `--network container:$HOSTNAME` — /etc/hostname inside the job reports - # the HOST's name, not the container id, - # so the namespace join finds no such - # container. + # `services:` Service containers are not resolvable by + # name from a host-networked job, giving + # "getaddrinfo EAI_AGAIN postgres". + # `--network container:$HOSTNAME` /etc/hostname reports the HOST's name, + # not a container id, so the namespace + # join finds no such container. + # default-gateway addressing The wrong idea entirely: with host + # networking the default route is the + # real router, not a docker bridge. # - # What works without needing to know our own container id: publish the port - # on the host and reach it through the job container's default gateway. The - # port is derived from the run id so concurrent runs cannot collide. + # Because the job shares the host's network namespace, a published port is + # simply on 127.0.0.1. The port is derived from the run id so concurrent + # runs cannot collide. env: PG_CONTAINER: pig-ci-pg-${{ github.run_id }} @@ -57,29 +61,33 @@ jobs: - name: Start Postgres run: | - # A per-run port in the ephemeral range, so two runs never collide. PG_PORT=$(( 45000 + (${{ github.run_id }} % 15000) )) - GATEWAY=$(ip route | awk '/^default/ {print $3; exit}') - if [ -z "$GATEWAY" ]; then echo "Could not determine the gateway"; exit 1; fi - echo "Postgres will be published on ${GATEWAY}:${PG_PORT}" + echo "Publishing Postgres on 127.0.0.1:${PG_PORT}" docker rm -f "$PG_CONTAINER" 2>/dev/null || true docker run -d --name "$PG_CONTAINER" \ - -p "${PG_PORT}:5432" \ + -p "127.0.0.1:${PG_PORT}:5432" \ -e POSTGRES_USER=pig -e POSTGRES_PASSWORD=pig -e POSTGRES_DB=pig \ postgres:16-alpine + # pg_isready inside the container only proves the server started. + # What matters is that THIS job can reach it through the published + # port, so the readiness check is made from here, over TCP. for i in $(seq 1 60); do - if docker exec "$PG_CONTAINER" pg_isready -U pig -q; then - echo "Postgres ready after ${i}s" - # Export for every later step. - echo "DATABASE_URL=postgres://pig:pig@${GATEWAY}:${PG_PORT}/pig" >> "$GITHUB_ENV" + if node -e " + const net=require('net'); + const s=net.connect(${PG_PORT},'127.0.0.1'); + s.on('connect',()=>{s.end();process.exit(0)}); + s.on('error',()=>process.exit(1)); + " 2>/dev/null; then + echo "Reachable after ${i}s" + echo "DATABASE_URL=postgres://pig:pig@127.0.0.1:${PG_PORT}/pig" >> "$GITHUB_ENV" exit 0 fi sleep 1 done - echo "Postgres did not become ready" - docker logs "$PG_CONTAINER" | tail -30 + echo "Postgres never became reachable on 127.0.0.1:${PG_PORT}" + docker logs "$PG_CONTAINER" 2>&1 | tail -30 exit 1 - name: Install