diff --git a/deploy/docker-compose.staging.yml b/deploy/docker-compose.staging.yml new file mode 100644 index 00000000..aab684bb --- /dev/null +++ b/deploy/docker-compose.staging.yml @@ -0,0 +1,8 @@ +services: + web: + networks: + - default + - app + +networks: + app: diff --git a/deploy/run-staging-deploy.sh b/deploy/run-staging-deploy.sh index 2a4b978e..3d69f44b 100755 --- a/deploy/run-staging-deploy.sh +++ b/deploy/run-staging-deploy.sh @@ -120,6 +120,7 @@ bash deploy/validate-staging-database-env.sh .env.staging.database compose=( docker compose -p jyotisha-staging --env-file .env.staging -f deploy/docker-compose.server.yml -f deploy/docker-compose.postgres.yml + -f deploy/docker-compose.staging.yml ) export APP_ENV_FILE='../.env.staging' export DATABASE_ENV_FILE='../.env.staging.database' @@ -196,7 +197,10 @@ if (!login?.ok) process.exit(1); const adminLogin = await fetch(`${process.env.STAGING_ADMIN_URL}/login`); if (!adminLogin.ok) process.exit(1); const adminRoot = await fetch(process.env.STAGING_ADMIN_URL, { redirect: "manual" }); -if (adminRoot.status !== 404) process.exit(1); +if ( + adminRoot.status !== 302 || + adminRoot.headers.get("location") !== "/admin/codes" +) process.exit(1); const adminSession = await fetch(`${process.env.STAGING_ADMIN_URL}/api/auth/get-session`); if (!adminSession.ok) process.exit(1); const account = await fetch(`${process.env.STAGING_URL}/api/account`); diff --git a/frontend/tests/database-topology.test.ts b/frontend/tests/database-topology.test.ts index 8e4b4136..1b2f6b88 100644 --- a/frontend/tests/database-topology.test.ts +++ b/frontend/tests/database-topology.test.ts @@ -5,9 +5,11 @@ import { startPostgresFixture } from "./helpers/postgres-fixture"; test("staging postgres is private and CI binds loopback only", () => { const staging = readFileSync("../deploy/docker-compose.postgres.yml", "utf8"); + const application = readFileSync("../deploy/docker-compose.staging.yml", "utf8"); const ci = readFileSync("../deploy/docker-compose.postgres-ci.yml", "utf8"); assert.match(staging, /image:\s*postgres:17-alpine/); assert.doesNotMatch(staging, /^\s+ports:/m); + assert.match(application, /web:\s*\n\s+networks:\s*\n\s+- default\s*\n\s+- app/); assert.match(ci, /127\.0\.0\.1:\$\{POSTGRES_HOST_PORT:-55432\}:5432/); }); diff --git a/frontend/tests/staging-backend-workflows.test.ts b/frontend/tests/staging-backend-workflows.test.ts index 787d41c2..6d356411 100644 --- a/frontend/tests/staging-backend-workflows.test.ts +++ b/frontend/tests/staging-backend-workflows.test.ts @@ -316,6 +316,7 @@ test("first immutable deployment rolls back to validated local image IDs", () => test("normal deployment checks migrations but never applies them", () => { const runner = read(deployScript); + assert.match(runner, /-f deploy\/docker-compose\.staging\.yml/); assertOrder(runner, [ "pull api web", "up -d --no-build --pull never --wait postgres", @@ -327,6 +328,8 @@ test("normal deployment checks migrations but never applies them", () => { assert.doesNotMatch(runner, /--profile migration run --rm migrator/); assert.doesNotMatch(runner, /npm\s+run\s+db:migrate(?!:check)/); assert.doesNotMatch(runner, /pull api web postgres/); + assert.match(runner, /adminRoot\.status !== 302/); + assert.match(runner, /adminRoot\.headers\.get\("location"\) !== "\/admin\/codes"/); }); test("manual migration uses only PostgreSQL and the digest-pinned migrator", () => {