e2e: move backend/dex/frontend from 8080 to 18080 so runners don't collide
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 23s
ci / rust (push) Successful in 3m43s
ci / e2e (push) Failing after 14s
ci / publish (push) Successful in 36s

Two of the fleet's Gitea Actions runners (morpheus, architect) already
had 8080 permanently bound by unrelated services (nginx on morpheus,
envio-hasura on architect) — every e2e run scheduled there died at
playwright's webServer preflight with "http://127.0.0.1:8080/healthz is
already used". 18080 is unused across morpheus/tank/architect.

Swap 8080 → 18080 in the eight e2e-scoped sites: clawmates.e2e.toml
(listen_addr + slack base_url + oauth redirect_base), dex.yaml (client
redirect URIs must match backend), playwright.config.ts + tests
(p4-slack, p6-oauth), the http.ts dev-fallback origin, and the two
shell scripts (e2e-backend safety check, rehearse-install healthz probe).

Prod compose (/opt/clawmates/docker-compose.yml on gw-04) is untouched;
prod continues to expose the server on 8080 internally on the compose
network (that's per-network, not host-shared).
This commit is contained in:
Omar Sobh
2026-07-05 20:25:27 -07:00
parent 8f29cf8e44
commit 298eb8e20e
8 changed files with 14 additions and 14 deletions
+3 -3
View File
@@ -2,7 +2,7 @@
# CLAWMATES_MODE=e2e, which applies the deterministic seed at boot. # CLAWMATES_MODE=e2e, which applies the deterministic seed at boot.
deploy_target = "air_gapped" deploy_target = "air_gapped"
listen_addr = "127.0.0.1:8080" listen_addr = "127.0.0.1:18080"
[database] [database]
url = "postgres://postgres:[email protected]:54330/clawmates_e2e" url = "postgres://postgres:[email protected]:54330/clawmates_e2e"
@@ -19,10 +19,10 @@ mode = "local"
socket_path = "/tmp/clawmates-e2e-broker.sock" socket_path = "/tmp/clawmates-e2e-broker.sock"
[slack] [slack]
base_url = "http://127.0.0.1:8080/__slack" base_url = "http://127.0.0.1:18080/__slack"
[oauth] [oauth]
issuer_url = "http://127.0.0.1:54556/dex" issuer_url = "http://127.0.0.1:54556/dex"
client_id = "clawmates" client_id = "clawmates"
client_secret = "tc-e2e-oauth-secret" client_secret = "tc-e2e-oauth-secret"
redirect_base = "http://127.0.0.1:8080" redirect_base = "http://127.0.0.1:18080"
+1 -1
View File
@@ -11,7 +11,7 @@ staticClients:
secret: tc-e2e-oauth-secret secret: tc-e2e-oauth-secret
name: Clawmates name: Clawmates
redirectURIs: redirectURIs:
- http://127.0.0.1:8080/api/apps/oauth/callback - http://127.0.0.1:18080/api/apps/oauth/callback
enablePasswordDB: true enablePasswordDB: true
staticPasswords: staticPasswords:
- email: [email protected] - email: [email protected]
+2 -2
View File
@@ -19,7 +19,7 @@ export default defineConfig({
webServer: [ webServer: [
{ {
command: "bash ../scripts/e2e-backend.sh", command: "bash ../scripts/e2e-backend.sh",
url: "http://127.0.0.1:8080/healthz", url: "http://127.0.0.1:18080/healthz",
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
timeout: 180_000, timeout: 180_000,
}, },
@@ -28,7 +28,7 @@ export default defineConfig({
url: "http://127.0.0.1:3100/login", url: "http://127.0.0.1:3100/login",
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
timeout: 180_000, timeout: 180_000,
env: { API_ORIGIN: "http://127.0.0.1:8080" }, env: { API_ORIGIN: "http://127.0.0.1:18080" },
}, },
], ],
}); });
+1 -1
View File
@@ -10,7 +10,7 @@ export { TOKEN_COOKIE } from "@/lib/auth/bearer";
/** Origin of the Rust backend, reachable from the Next server process. */ /** Origin of the Rust backend, reachable from the Next server process. */
export function apiOrigin(): string { export function apiOrigin(): string {
return process.env.API_ORIGIN ?? "http://127.0.0.1:8080"; return process.env.API_ORIGIN ?? "http://127.0.0.1:18080";
} }
/** Thrown when there is no session or the backend rejects it; callers /** Thrown when there is no session or the backend rejects it; callers
+4 -4
View File
@@ -46,14 +46,14 @@ test("connect Slack, then an outbound post is gated and broker-executed", async
await expect(card.getByText(/wants to:/)).toContainText("Post to Slack #general"); await expect(card.getByText(/wants to:/)).toContainText("Post to Slack #general");
// Provably blocked: the sink saw nothing. // Provably blocked: the sink saw nothing.
const before = await request.get("http://127.0.0.1:8080/__slack/posts"); const before = await request.get("http://127.0.0.1:18080/__slack/posts");
expect(await before.json()).toHaveLength(0); expect(await before.json()).toHaveLength(0);
// Approve → the broker posts exactly once. // Approve → the broker posts exactly once.
await card.getByRole("button", { name: "Approve" }).click(); await card.getByRole("button", { name: "Approve" }).click();
await expect(page.getByText(/Posted to #general/)).toBeVisible(); await expect(page.getByText(/Posted to #general/)).toBeVisible();
const after = await request.get("http://127.0.0.1:8080/__slack/posts"); const after = await request.get("http://127.0.0.1:18080/__slack/posts");
const posts = (await after.json()) as { channel: string; text: string }[]; const posts = (await after.json()) as { channel: string; text: string }[];
expect(posts).toHaveLength(1); expect(posts).toHaveLength(1);
expect(posts[0].channel).toBe("#general"); expect(posts[0].channel).toBe("#general");
@@ -74,7 +74,7 @@ test("connect Slack, then an outbound post is gated and broker-executed", async
createHmac("sha256", "e2e-signing-secret") createHmac("sha256", "e2e-signing-secret")
.update(`v0:${timestamp}:${mention}`) .update(`v0:${timestamp}:${mention}`)
.digest("hex"); .digest("hex");
const inbound = await request.post("http://127.0.0.1:8080/api/slack/events", { const inbound = await request.post("http://127.0.0.1:18080/api/slack/events", {
headers: { headers: {
"x-slack-request-timestamp": timestamp, "x-slack-request-timestamp": timestamp,
"x-slack-signature": signature, "x-slack-signature": signature,
@@ -96,7 +96,7 @@ test("connect Slack, then an outbound post is gated and broker-executed", async
await expect(page.getByText(/All clear/)).toBeVisible(); await expect(page.getByText(/All clear/)).toBeVisible();
await expect await expect
.poll(async () => { .poll(async () => {
const res = await request.get("http://127.0.0.1:8080/__slack/posts"); const res = await request.get("http://127.0.0.1:18080/__slack/posts");
return ((await res.json()) as { text: string }[]).map((p) => p.text); return ((await res.json()) as { text: string }[]).map((p) => p.text);
}) })
.toContain("On it!"); .toContain("On it!");
+1 -1
View File
@@ -6,7 +6,7 @@ import { expect, test } from "@playwright/test";
const OWNER_EMAIL = "[email protected]"; const OWNER_EMAIL = "[email protected]";
const OWNER_PASSWORD = "e2e-password"; const OWNER_PASSWORD = "e2e-password";
const BACKEND = "http://127.0.0.1:8080"; const BACKEND = "http://127.0.0.1:18080";
test("connecting an app via OAuth walks the real dex login", async ({ test("connecting an app via OAuth walks the real dex login", async ({
page, page,
+1 -1
View File
@@ -30,7 +30,7 @@ done
# Refuse to run against someone else's 8080 (e.g. the compose stack): # Refuse to run against someone else's 8080 (e.g. the compose stack):
# reuseExistingServer would silently point every journey at the wrong # reuseExistingServer would silently point every journey at the wrong
# backend with the wrong seed. # backend with the wrong seed.
if curl -fsS -o /dev/null --max-time 2 http://127.0.0.1:8080/healthz 2>/dev/null; then if curl -fsS -o /dev/null --max-time 2 http://127.0.0.1:18080/healthz 2>/dev/null; then
echo "e2e-backend: port 8080 is already serving — stop the other stack first" >&2 echo "e2e-backend: port 8080 is already serving — stop the other stack first" >&2
echo " (docker compose -f deploy/compose/docker-compose.yml down)" >&2 echo " (docker compose -f deploy/compose/docker-compose.yml down)" >&2
exit 1 exit 1
+1 -1
View File
@@ -56,7 +56,7 @@ docker compose --project-directory "$CLAWMATES_HOME" up -d --no-build
echo "==> Waiting for the platform" echo "==> Waiting for the platform"
for _ in $(seq 1 60); do for _ in $(seq 1 60); do
if curl -fsS http://127.0.0.1:8080/healthz >/dev/null 2>&1; then if curl -fsS http://127.0.0.1:18080/healthz >/dev/null 2>&1; then
echo "==> Server is healthy" echo "==> Server is healthy"
if curl -fsS http://127.0.0.1:3000/login | grep -q clawmates; then if curl -fsS http://127.0.0.1:3000/login | grep -q clawmates; then
echo "==> Frontend serves the login page" echo "==> Frontend serves the login page"