Skip to content

Commit ff1f14b

Browse files
fix: implement dynamic environment variable management for Firebase Functions deployment and enhance server configuration loading
1 parent 91fde67 commit ff1f14b

5 files changed

Lines changed: 75 additions & 3 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,5 @@ testem.log
4343
.DS_Store
4444
Thumbs.db
4545
.env
46+
functions/.env
4647
src/assets/env.js

functions/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,11 @@
33
This folder is a minimal Firebase Functions wrapper used during deploys.
44

55
CI generates `functions/index.js` from the SSR server bundle and Firebase deploys this folder as the Functions source.
6+
7+
## Mail trigger (`contactFormFunction`)
8+
9+
`src/server.ts` loads **`functions/.env`** (next to `index.js`) first, then the repo root `.env`, via `dotenv`. GitHub Actions runs **`scripts/write-functions-env.mjs`** before `firebase deploy`, which writes `functions/.env` from the workflow’s **`MAIL_*`** and **`FIRESTORE_COLLECTION_MESSAGES`** secrets so the deployed bundle sees the same values at runtime.
10+
11+
**Manual override (optional):** You can still set variables on the function in [Cloud Functions](https://console.cloud.google.com/functions)**Runtime environment variables**, or use [Secret Manager](https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets).
12+
13+
`functions/.env` is gitignored; do not commit it.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
"build:ssr": "pnpm run build && ng run ditectrev:server",
1717
"build:prerender": "ng run ditectrev:prerender:production",
1818
"build:ssr:production": "pnpm run prepare:browser-env && ng build --configuration production && ng run ditectrev:server:production",
19-
"build-and-deploy:ssr:staging:firebase": "npm run build:ssr && npm run root:starting:index:firebase && firebase use \"$CI_ENVIRONMENT_SLUG\" --token \"$FIREBASE_DEPLOY_KEY\" && firebase deploy --force --token \"$FIREBASE_DEPLOY_KEY\"",
20-
"build-and-deploy:ssr:production:firebase": "npm run build:ssr:production && npm run root:starting:index:firebase && firebase use \"$CI_ENVIRONMENT_SLUG\" --token \"$FIREBASE_DEPLOY_KEY\" && firebase deploy --force --token \"$FIREBASE_DEPLOY_KEY\"",
19+
"build-and-deploy:ssr:staging:firebase": "npm run build:ssr && npm run root:starting:index:firebase && node scripts/write-functions-env.mjs && firebase use \"$CI_ENVIRONMENT_SLUG\" --token \"$FIREBASE_DEPLOY_KEY\" && firebase deploy --force --token \"$FIREBASE_DEPLOY_KEY\"",
20+
"build-and-deploy:ssr:production:firebase": "npm run build:ssr:production && npm run root:starting:index:firebase && node scripts/write-functions-env.mjs && firebase use \"$CI_ENVIRONMENT_SLUG\" --token \"$FIREBASE_DEPLOY_KEY\" && firebase deploy --force --token \"$FIREBASE_DEPLOY_KEY\"",
2121
"test": "ng test --no-watch",
2222
"lint": "ng lint",
2323
"e2e": "start-server-and-test start:e2e http://127.0.0.1:4311 \"playwright test\"",

scripts/write-functions-env.mjs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Writes `functions/.env` from the current process environment so `firebase deploy` uploads it
4+
* next to `index.js`. `src/server.ts` loads it via `loadEnv({ path: join(__dirname, '.env') })`.
5+
*
6+
* Intended for GitHub Actions (`GITHUB_ACTIONS=true`). Local deploy: set `WRITE_FUNCTIONS_ENV=1`
7+
* or rely on Cloud Console env / root `.env` for local runs.
8+
*/
9+
import { writeFileSync } from 'fs';
10+
import { dirname, join } from 'path';
11+
import { fileURLToPath } from 'url';
12+
13+
const rootDir = dirname(dirname(fileURLToPath(import.meta.url)));
14+
const outPath = join(rootDir, 'functions', '.env');
15+
16+
/** Keys the SSR bundle reads at runtime on Cloud Functions (keep in sync with deploy workflow env). */
17+
const KEYS = [
18+
'MAIL_HOST',
19+
'MAIL_PORT',
20+
'MAIL_ACCOUNT',
21+
'MAIL_PASSWORD',
22+
'FIRESTORE_COLLECTION_MESSAGES',
23+
];
24+
25+
function shouldRun() {
26+
return process.env.GITHUB_ACTIONS === 'true' || process.env.WRITE_FUNCTIONS_ENV === '1';
27+
}
28+
29+
function escapeValue(v) {
30+
return String(v)
31+
.replace(/\\/g, '\\\\')
32+
.replace(/\r?\n/g, '\\n')
33+
.replace(/"/g, '\\"');
34+
}
35+
36+
function main() {
37+
if (!shouldRun()) {
38+
console.log('write-functions-env: skip (not CI; export WRITE_FUNCTIONS_ENV=1 to force)');
39+
return;
40+
}
41+
42+
const lines = [];
43+
for (const k of KEYS) {
44+
const v = process.env[k];
45+
if (v === undefined || v === '') continue;
46+
lines.push(`${k}="${escapeValue(v)}"`);
47+
}
48+
49+
if (lines.length === 0) {
50+
console.warn('write-functions-env: no matching env keys set; not writing functions/.env');
51+
return;
52+
}
53+
54+
writeFileSync(outPath, `${lines.join('\n')}\n`, 'utf8');
55+
console.log(`write-functions-env: wrote ${lines.length} entries to functions/.env`);
56+
}
57+
58+
main();

src/server.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Unified SSR server entrypoint used both locally (`node dist/ditectrev-server/main.js`)
33
* and in Firebase Functions (`functions/index.js` generated from this bundle).
44
*/
5-
import 'dotenv/config';
5+
import { config as loadEnv } from 'dotenv';
66
import 'zone.js/node';
77
import * as admin from 'firebase-admin';
88
import * as express from 'express';
@@ -15,6 +15,11 @@ import { existsSync, readFileSync } from 'fs';
1515
import helmet from 'helmet';
1616
import { AppServerModule } from './app/app.server.module';
1717

18+
// CI writes `functions/.env` from GitHub secrets before `firebase deploy` (see `scripts/write-functions-env.mjs`).
19+
// Local dev: repo root `.env` (second line). Packaged `.env` is loaded first on Cloud Functions.
20+
loadEnv({ path: join(__dirname, '.env') });
21+
loadEnv({ path: join(process.cwd(), '.env') });
22+
1823
export { AppServerModule } from './app/app.server.module';
1924

2025
const app = express();

0 commit comments

Comments
 (0)