Skip to content

Commit 24d9ae1

Browse files
committed
Use single runtime env file and generic database URL - PR_26167_193-env-runtime-single-file-and-generic-db-url
1 parent 9081ebb commit 24d9ae1

10 files changed

Lines changed: 47 additions & 41 deletions

.env

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
TEST_VAR=hello_world
2-
API_URL=http://localhost:3000
1+
GAMEFOUNDRY_SUPABASE_URL=
2+
GAMEFOUNDRY_SUPABASE_ANON_KEY=
3+
GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY=
4+
GAMEFOUNDRY_DATABASE_URL=

.env.example

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# Game Foundry Studio local development environment
22

3-
# Configure the local API account and product-data connections here.
3+
# Configure the runtime auth and database connections here.
44
# Missing connection configuration reports diagnostics instead of falling back.
55

66
# Browser-safe account connection configuration.
7-
# Leave values empty in this example; local values belong in .env.local.
7+
# Leave values empty in this example; local values belong in user-managed copy files.
88
GAMEFOUNDRY_SUPABASE_URL=
99
GAMEFOUNDRY_SUPABASE_ANON_KEY=
1010

1111
# Server-only product-data and account administration connection configuration.
1212
# Do not expose these values to browser JavaScript, HTML, reports, or screenshots.
1313
GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY=
14-
GAMEFOUNDRY_SUPABASE_DATABASE_URL=
14+
GAMEFOUNDRY_DATABASE_URL=

.env.uat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
GAMEFOUNDRY_SUPABASE_URL=
77
GAMEFOUNDRY_SUPABASE_ANON_KEY=
88
GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY=
9-
GAMEFOUNDRY_SUPABASE_DATABASE_URL=
9+
GAMEFOUNDRY_DATABASE_URL=

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,5 @@ docs/dev/reports/playwright_v8_coverage_report.txt
3737
*.mp4
3838
*.mkv
3939

40+
.env
4041
.env.local

scripts/apply-supabase-dev-ddl.mjs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,13 @@ function envValue(key) {
8989
}
9090

9191
function parseDatabaseUrl() {
92-
const value = envValue("GAMEFOUNDRY_SUPABASE_DATABASE_URL");
92+
const value = envValue("GAMEFOUNDRY_DATABASE_URL");
9393
if (!value) {
94-
throw new Error("GAMEFOUNDRY_SUPABASE_DATABASE_URL is required to apply DEV DDL.");
94+
throw new Error("GAMEFOUNDRY_DATABASE_URL is required to apply DEV DDL.");
9595
}
9696
const databaseUrl = new URL(value);
9797
if (!["postgres:", "postgresql:"].includes(databaseUrl.protocol)) {
98-
throw new Error("GAMEFOUNDRY_SUPABASE_DATABASE_URL must use postgres:// or postgresql://.");
98+
throw new Error("GAMEFOUNDRY_DATABASE_URL must use postgres:// or postgresql://.");
9999
}
100100
return {
101101
database: decodeURIComponent(databaseUrl.pathname.replace(/^\/+/, "") || "postgres"),
@@ -431,7 +431,7 @@ async function main() {
431431
const envLoad = loadEnvLocal();
432432
const config = parseDatabaseUrl();
433433
if (!config.user || !config.password) {
434-
throw new Error("GAMEFOUNDRY_SUPABASE_DATABASE_URL must include username and password.");
434+
throw new Error("GAMEFOUNDRY_DATABASE_URL must include username and password.");
435435
}
436436
const socket = await openPostgresTlsConnection(config);
437437
try {

scripts/start-local-api-server.mjs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import { existsSync, readFileSync } from "node:fs";
33
import path from "node:path";
44
import { startLocalApiServer } from "../src/dev-runtime/server/local-api-server.mjs";
55

6-
function loadEnvLocal() {
7-
const envPath = path.resolve(process.cwd(), ".env.local");
6+
const RUNTIME_ENV_FILE = ".env";
7+
8+
function loadRuntimeEnv() {
9+
const envPath = path.resolve(process.cwd(), RUNTIME_ENV_FILE);
810
if (!existsSync(envPath)) {
911
return {
1012
loaded: false,
@@ -35,7 +37,7 @@ function loadEnvLocal() {
3537
};
3638
}
3739

38-
const envLocal = loadEnvLocal();
40+
const runtimeEnv = loadRuntimeEnv();
3941

4042
function connectionStatus(requiredKeys) {
4143
const missingKeys = requiredKeys.filter((key) => !String(process.env[key] || "").trim());
@@ -49,21 +51,22 @@ const accountConnection = connectionStatus([
4951
"GAMEFOUNDRY_SUPABASE_URL",
5052
"GAMEFOUNDRY_SUPABASE_ANON_KEY",
5153
]);
52-
const productDataConnection = connectionStatus([
54+
const databaseConnection = connectionStatus([
5355
"GAMEFOUNDRY_SUPABASE_URL",
5456
"GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY",
57+
"GAMEFOUNDRY_DATABASE_URL",
5558
]);
5659
const host = process.env.GAMEFOUNDRY_LOCAL_API_HOST || "127.0.0.1";
5760
const port = Number(process.env.GAMEFOUNDRY_LOCAL_API_PORT || 5501);
5861

5962
const localServer = await startLocalApiServer({ host, port });
6063

6164
console.log(`GameFoundry API runtime server running at ${localServer.baseUrl}/account/sign-in.html`);
62-
console.log(envLocal.loaded
63-
? `.env.local loaded for API runtime (${envLocal.loadedKeys} key(s) applied).`
64-
: ".env.local was not found for API runtime.");
65-
console.log(`Configured account connection: ${accountConnection.ready ? "configured" : `missing ${accountConnection.missingKeys.join(", ")}`}.`);
66-
console.log(`Configured product data connection: ${productDataConnection.ready ? "configured" : `missing ${productDataConnection.missingKeys.join(", ")}`}.`);
65+
console.log(runtimeEnv.loaded
66+
? `.env loaded for API runtime (${runtimeEnv.loadedKeys} key(s) applied).`
67+
: ".env was not found for API runtime.");
68+
console.log(`Configured auth connection: ${accountConnection.ready ? "configured" : `missing ${accountConnection.missingKeys.join(", ")}`}.`);
69+
console.log(`Configured database connection: ${databaseConnection.ready ? "configured" : `missing ${databaseConnection.missingKeys.join(", ")}`}.`);
6770
console.log("Press Ctrl+C to stop.");
6871

6972
for (const signal of ["SIGINT", "SIGTERM"]) {

scripts/validate-supabase-dev.mjs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@ const ENV_FILE = ".env.local";
1111
const REQUIRED_ENV = Object.freeze([
1212
{
1313
key: "GAMEFOUNDRY_SUPABASE_URL",
14-
label: "URL configured",
14+
label: "Auth connection URL configured",
1515
},
1616
{
1717
key: "GAMEFOUNDRY_SUPABASE_ANON_KEY",
18-
label: "Publishable key configured",
18+
label: "Auth anon key configured",
1919
},
2020
{
2121
key: "GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY",
22-
label: "Service role key configured",
22+
label: "Auth service role key configured",
2323
},
2424
{
25-
key: "GAMEFOUNDRY_SUPABASE_DATABASE_URL",
26-
label: "Database URL configured",
25+
key: "GAMEFOUNDRY_DATABASE_URL",
26+
label: "Database connection URL configured",
2727
},
2828
]);
2929

@@ -119,7 +119,7 @@ function sanitizeDiagnostic(value) {
119119
}
120120
});
121121
try {
122-
const databaseUrl = new URL(envValue("GAMEFOUNDRY_SUPABASE_DATABASE_URL"));
122+
const databaseUrl = new URL(envValue("GAMEFOUNDRY_DATABASE_URL"));
123123
if (databaseUrl.password) {
124124
text = text.split(databaseUrl.password).join(maskValue(databaseUrl.password));
125125
}
@@ -283,7 +283,7 @@ async function checkTlsValidation() {
283283
}
284284

285285
function parseDatabaseUrl() {
286-
const databaseUrl = new URL(envValue("GAMEFOUNDRY_SUPABASE_DATABASE_URL"));
286+
const databaseUrl = new URL(envValue("GAMEFOUNDRY_DATABASE_URL"));
287287
if (!["postgres:", "postgresql:"].includes(databaseUrl.protocol)) {
288288
throw new Error("Database URL must use postgres:// or postgresql://.");
289289
}
@@ -638,8 +638,8 @@ async function main() {
638638
const envLoad = loadEnvLocal();
639639
const results = [];
640640
results.push(envLoad.loaded
641-
? pass(".env.local loaded", `${envLoad.loadedKeys.length} key(s) loaded`)
642-
: fail(".env.local loaded", `${ENV_FILE} was not found`));
641+
? pass("Explicit .env.local DEV validation load", `${envLoad.loadedKeys.length} key(s) loaded`)
642+
: fail("Explicit .env.local DEV validation load", `${ENV_FILE} was not found`));
643643

644644
REQUIRED_ENV.forEach(({ key, label }) => {
645645
const value = envValue(key);

src/dev-runtime/auth/provider-contract-stubs.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ const BROWSER_SAFE_SUPABASE_ENV_KEYS = Object.freeze([
5151

5252
const SERVER_ONLY_SUPABASE_SECRET_KEYS = Object.freeze([
5353
"GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY",
54-
"GAMEFOUNDRY_SUPABASE_DATABASE_URL",
54+
"GAMEFOUNDRY_DATABASE_URL",
5555
]);
5656
const SUPABASE_POSTGRES_CONFIG_KEYS = Object.freeze([
5757
"GAMEFOUNDRY_SUPABASE_URL",
5858
"GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY",
5959
]);
6060
const SUPABASE_POSTGRES_SITE_SETUP_KEYS = Object.freeze([
6161
...SUPABASE_POSTGRES_CONFIG_KEYS,
62-
"GAMEFOUNDRY_SUPABASE_DATABASE_URL",
62+
"GAMEFOUNDRY_DATABASE_URL",
6363
]);
6464
export const SUPABASE_POSTGRES_IDENTITY_TABLES = Object.freeze(["users", "roles", "user_roles"]);
6565
export const SUPABASE_POSTGRES_PRODUCT_TABLES = Object.freeze([

tests/dev-runtime/SupabaseProviderContractStub.test.mjs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ test("Provider contract assigns identity and product data ownership to Supabase
522522
GAMEFOUNDRY_AUTH_PROVIDER: "supabase-auth",
523523
GAMEFOUNDRY_DB_PROVIDER: "supabase-postgres",
524524
GAMEFOUNDRY_SUPABASE_ANON_KEY: "browser-safe-anon-key",
525-
GAMEFOUNDRY_SUPABASE_DATABASE_URL: "server-only-database-url-placeholder",
525+
GAMEFOUNDRY_DATABASE_URL: "server-only-database-url-placeholder",
526526
GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY: "server-only-service-role-key",
527527
GAMEFOUNDRY_SUPABASE_URL: "https://supabase-dev.example.test",
528528
});
@@ -1371,7 +1371,7 @@ test("Supabase stubs do not expose server-only secret names or values through th
13711371
await withEnv({
13721372
GAMEFOUNDRY_AUTH_PROVIDER: "supabase-auth",
13731373
GAMEFOUNDRY_DB_PROVIDER: "supabase-postgres",
1374-
GAMEFOUNDRY_SUPABASE_DATABASE_URL: "server-only-database-url-placeholder",
1374+
GAMEFOUNDRY_DATABASE_URL: "server-only-database-url-placeholder",
13751375
GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY: "not-a-real-service-role-test-value",
13761376
}, async () => {
13771377
const server = await startApiServer();
@@ -1396,7 +1396,7 @@ test("Supabase stubs do not expose server-only secret names or values through th
13961396
assert.equal(providerText.includes("not-a-real-service-role-test-value"), false);
13971397
assert.equal(providerText.includes("server-only-database-url-placeholder"), false);
13981398
assert.equal(providerText.includes("GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY"), false);
1399-
assert.equal(providerText.includes("GAMEFOUNDRY_SUPABASE_DATABASE_URL"), false);
1399+
assert.equal(providerText.includes("GAMEFOUNDRY_DATABASE_URL"), false);
14001400
assert.equal(adapterText.includes("not-a-real-service-role-test-value"), false);
14011401
assert.equal(adapterText.includes("server-only-database-url-placeholder"), false);
14021402
} finally {
@@ -1490,7 +1490,7 @@ test("Supabase Postgres adapter supports identity tables and readiness when conf
14901490
const calls = [];
14911491
const database = new SupabasePostgresProviderAdapter({
14921492
env: {
1493-
GAMEFOUNDRY_SUPABASE_DATABASE_URL: "server-only-database-url-placeholder",
1493+
GAMEFOUNDRY_DATABASE_URL: "server-only-database-url-placeholder",
14941494
GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY: "not-a-real-service-role-test-value",
14951495
GAMEFOUNDRY_SUPABASE_URL: "https://supabase-dev.example.test/",
14961496
},
@@ -1539,7 +1539,7 @@ test("Supabase Postgres adapter supports DEV cleanup deletes and audit reassignm
15391539
const calls = [];
15401540
const database = new SupabasePostgresProviderAdapter({
15411541
env: {
1542-
GAMEFOUNDRY_SUPABASE_DATABASE_URL: "server-only-database-url-placeholder",
1542+
GAMEFOUNDRY_DATABASE_URL: "server-only-database-url-placeholder",
15431543
GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY: "not-a-real-service-role-test-value",
15441544
GAMEFOUNDRY_SUPABASE_URL: "https://supabase-dev.example.test/",
15451545
},
@@ -1604,7 +1604,7 @@ test("Supabase Postgres adapter initializes key-based users roles and user_roles
16041604
];
16051605
const database = new SupabasePostgresProviderAdapter({
16061606
env: {
1607-
GAMEFOUNDRY_SUPABASE_DATABASE_URL: "server-only-database-url-placeholder",
1607+
GAMEFOUNDRY_DATABASE_URL: "server-only-database-url-placeholder",
16081608
GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY: "not-a-real-service-role-test-value",
16091609
GAMEFOUNDRY_SUPABASE_URL: "https://supabase-dev.example.test/",
16101610
},
@@ -1686,7 +1686,7 @@ test("Supabase Postgres adapter reuses existing roleSlug and user_roles keys dur
16861686
const calls = [];
16871687
const database = new SupabasePostgresProviderAdapter({
16881688
env: {
1689-
GAMEFOUNDRY_SUPABASE_DATABASE_URL: "server-only-database-url-placeholder",
1689+
GAMEFOUNDRY_DATABASE_URL: "server-only-database-url-placeholder",
16901690
GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY: "not-a-real-service-role-test-value",
16911691
GAMEFOUNDRY_SUPABASE_URL: "https://supabase-dev.example.test/",
16921692
},
@@ -1781,7 +1781,7 @@ test("Supabase activation diagnostics report readiness for fixed providers", ()
17811781
GAMEFOUNDRY_AUTH_PROVIDER: "supabase-auth",
17821782
GAMEFOUNDRY_DB_PROVIDER: "supabase-postgres",
17831783
GAMEFOUNDRY_SUPABASE_ANON_KEY: "test-anon-key",
1784-
GAMEFOUNDRY_SUPABASE_DATABASE_URL: "server-only-database-url-placeholder",
1784+
GAMEFOUNDRY_DATABASE_URL: "server-only-database-url-placeholder",
17851785
GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY: "not-a-real-service-role-test-value",
17861786
GAMEFOUNDRY_SUPABASE_URL: "https://supabase-dev.example.test/",
17871787
});
@@ -1845,6 +1845,6 @@ test(".env.example documents Supabase DEV variables without values", async () =>
18451845
assert.match(contents, /^GAMEFOUNDRY_SUPABASE_URL=$/m);
18461846
assert.match(contents, /^GAMEFOUNDRY_SUPABASE_ANON_KEY=$/m);
18471847
assert.match(contents, /^GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY=$/m);
1848-
assert.match(contents, /^GAMEFOUNDRY_SUPABASE_DATABASE_URL=$/m);
1848+
assert.match(contents, /^GAMEFOUNDRY_DATABASE_URL=$/m);
18491849
assert.equal(/supabase\.co|sbp_|eyJ[A-Za-z0-9_-]{20,}/.test(contents), false);
18501850
});

tests/playwright/tools/GameWorkspaceMockRepository.spec.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { workspaceV2CoverageReporter } from "../../helpers/workspaceV2CoverageRe
77

88
const SUPABASE_ENV_KEYS = Object.freeze([
99
"GAMEFOUNDRY_SUPABASE_ANON_KEY",
10-
"GAMEFOUNDRY_SUPABASE_DATABASE_URL",
10+
"GAMEFOUNDRY_DATABASE_URL",
1111
"GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY",
1212
"GAMEFOUNDRY_SUPABASE_URL",
1313
]);
@@ -91,7 +91,7 @@ test.beforeAll(async () => {
9191
previousSupabaseEnv = Object.fromEntries(SUPABASE_ENV_KEYS.map((key) => [key, process.env[key]]));
9292
fakeSupabaseServer = await startFakeSupabaseServer();
9393
process.env.GAMEFOUNDRY_SUPABASE_ANON_KEY = "game-workspace-anon-key";
94-
process.env.GAMEFOUNDRY_SUPABASE_DATABASE_URL = "postgres://game-workspace:test@127.0.0.1:5432/game_workspace";
94+
process.env.GAMEFOUNDRY_DATABASE_URL = "postgres://game-workspace:test@127.0.0.1:5432/game_workspace";
9595
process.env.GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY = "game-workspace-service-role-key";
9696
process.env.GAMEFOUNDRY_SUPABASE_URL = fakeSupabaseServer.baseUrl;
9797
});

0 commit comments

Comments
 (0)