From cc6c9c69571aa10728d0c055af89e7e612395c2c Mon Sep 17 00:00:00 2001 From: Jarian Cottingham Date: Tue, 7 Jul 2026 23:27:12 +0000 Subject: [PATCH] feat: reserved CI port range 10000-10099, document convention --- .gitea/workflows/ci.yml | 11 +++++----- AGENTS.md | 45 +++++++++++++++++++++++++++++++++++++++++ run_archiver.py | 10 +++++---- 3 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 AGENTS.md diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index dc9350e..1f1ab61 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -114,17 +114,18 @@ jobs: id: port run: | sleep 2 - # Read shifted port from container logs (entrypoint prints [ci-port-shift]) + # CI port range 10000-10099: port = 10000 + CI_PORT_OFFSET + # NewsArchiverV2 uses offset 1, so port = 10001 CI_LOG=$(docker logs newsarchiver-e2e 2>&1 | grep "\[ci-port-shift\]" || echo "") if [ -n "$CI_LOG" ]; then APP_PORT=$(echo "$CI_LOG" | grep -oP 'to \K[0-9]+') echo "app_port=${APP_PORT}" >> $GITHUB_OUTPUT echo "$CI_LOG" - # Re-publish port now that we know the shifted port - echo "Discovered port ${APP_PORT} from CI logs" else - echo "app_port=5000" >> $GITHUB_OUTPUT - echo "No CI port shift detected, using default 5000" + # Fallback: compute from offset + OFFSET=${CI_PORT_OFFSET:-1} + echo "app_port=$((10000 + OFFSET))" >> $GITHUB_OUTPUT + echo "No CI port shift in logs, computed port $((10000 + OFFSET))" fi - name: Seed test data diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..359c678 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,45 @@ +# CI Port Convention + +## Reserved Range: 10000-10099 + +CI jobs never use production ports. Each service gets a fixed offset (1-99) within the 10000-10099 range. + +**Formula:** `CI_PORT = 10000 + CI_PORT_OFFSET` + +## Port Assignments + +| Offset | CI Port | Service | +|--------|---------|---------| +| 1 | 10001 | NewsArchiverV2 | +| 2 | 10002 | paste-bin | +| 3-99 | 10003-10099 | Reserved for future services | + +## Usage + +**CI workflow:** +```yaml +docker run -d --name myapp-e2e \ + --network test-network \ + -e CI=true \ + -e CI_PORT_OFFSET=1 \ + myapp:test \ + python main.py --serve --port 5000 +# App auto-shifts to port 10001 +``` + +**App code (any service):** +```python +if os.environ.get("CI") == "true" and os.environ.get("SKIP_PORT_SHIFT") != "1": + offset = int(os.environ.get("CI_PORT_OFFSET", "1")) + args.port = 10000 + offset + print(f"[ci-port-shift] Port shifted from {original} to {args.port}") +``` + +**Production:** Never sets `CI=true` or `CI_PORT_OFFSET`. Ports stay unchanged. + +## Rules + +1. CI jobs always set `CI=true` and the service's `CI_PORT_OFFSET` +2. Port discovery via log parsing: `docker logs | grep "\[ci-port-shift\]"` +3. Container-to-container traffic uses Docker networks, never host port publish +4. Production containers never have `CI=true` diff --git a/run_archiver.py b/run_archiver.py index 3acb66d..b558439 100644 --- a/run_archiver.py +++ b/run_archiver.py @@ -256,12 +256,14 @@ Examples: args = parser.parse_args() - # CI port shift: detect CI env, shift port to avoid conflicts with production + # CI port shift: detect CI env, map to reserved CI port range 10000-10099 + # CI_PORT_OFFSET (1-99) maps to 10001-10099. Each service gets a fixed offset. + # NewsArchiverV2=1, paste-bin=2, etc. See AGENTS.md for assignments. if os.environ.get("CI") == "true" and os.environ.get("SKIP_PORT_SHIFT") != "1": - offset = int(os.environ.get("CI_PORT_OFFSET", "1")) original_port = args.port - args.port = original_port + offset - print(f"[ci-port-shift] Port shifted from {original_port} to {args.port} (offset: {offset})") + offset = int(os.environ.get("CI_PORT_OFFSET", "1")) + args.port = 10000 + offset + print(f"[ci-port-shift] Port shifted from {original_port} to {args.port} (offset {offset}, CI range: 10000-10099)") logger = setup_logging(args.verbose)