Skip to content

Commit 608b014

Browse files
committed
refactor(server): replace resolveRuntimePath with env-var-first path resolution
Remove the fragile multi-candidate path-probing helper. Both runtime environments (local repo and Docker /workspace) have the same dist/ depth, so a single APP_ROOT anchor (4 hops up from dist/services/) is enough. Adds SERVER_CONFIG_PATH env var alongside the existing TF_STATE_PATH so operators can override either path at runtime — the same pattern the integration test server already uses for TF_STATE_PATH. https://claude.ai/code/session_01UAVwJvuBtUaDYFXtJRheww
1 parent caf2899 commit 608b014

1 file changed

Lines changed: 17 additions & 33 deletions

File tree

app/packages/server/src/services/ConfigService.ts

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,17 @@ import { EMBEDDED_TFSTATE } from '../generated/tfstate.js';
77

88
const __dirname = dirname(fileURLToPath(import.meta.url));
99

10-
/**
11-
* Probes candidate relative paths from __dirname in order and returns the
12-
* first that exists on disk. Falls back to the first candidate when none
13-
* exist so callers get a deterministic (missing-file) path rather than
14-
* undefined behaviour.
15-
*
16-
* Needed because the depth from __dirname to the repo/workspace root differs
17-
* between environments:
18-
* - local source tree (src/services or dist/services inside repo): 5 levels up → repo root
19-
* - Docker image (dist/services inside /app): 4 levels up → /app
20-
*/
21-
function resolveRuntimePath(...relativeCandidates: string[]): string {
22-
for (const rel of relativeCandidates) {
23-
const resolved = join(__dirname, rel);
24-
if (existsSync(resolved)) return resolved;
25-
}
26-
return join(__dirname, relativeCandidates[0]);
27-
}
10+
// dist/services/ → 4 hops up = app root (repo: app/, Docker: /workspace/app/)
11+
// 5 hops up = repo/workspace root, where terraform/ lives
12+
const APP_ROOT = join(__dirname, '..', '..', '..', '..');
13+
14+
const TF_STATE_PATH =
15+
process.env['TF_STATE_PATH'] ??
16+
join(APP_ROOT, '..', 'terraform', 'terraform.tfstate');
2817

29-
const TF_STATE_PATH = process.env['TF_STATE_PATH'] ??
30-
resolveRuntimePath(
31-
'../../../../../terraform/terraform.tfstate',
32-
'../../../../terraform/terraform.tfstate',
33-
);
34-
const CONFIG_PATH = resolveRuntimePath(
35-
'../../../../../app/server_config.json',
36-
'../../../../server_config.json',
37-
);
18+
const SERVER_CONFIG_PATH =
19+
process.env['SERVER_CONFIG_PATH'] ??
20+
join(APP_ROOT, 'server_config.json');
3821

3922
/**
4023
* Shape of the subset of Terraform root outputs the management app consumes.
@@ -83,7 +66,8 @@ const DEFAULT_CONFIG: WatchdogConfig = {
8366
* lazily and cached in-memory until {@link ConfigService.invalidateCache}
8467
* is called.
8568
* - `server_config.json` — the user-editable file holding the watchdog
86-
* tunables and the optional API bearer token.
69+
* tunables and the optional API bearer token. Path resolved via
70+
* `SERVER_CONFIG_PATH` env var, defaulting to `<app-root>/server_config.json`.
8771
* - A handful of process env vars (`AWS_DEFAULT_REGION`, `API_TOKEN`).
8872
*
8973
* Every other service injects this one instead of touching `process.env` or
@@ -197,9 +181,9 @@ export class ConfigService {
197181
if (env !== undefined) {
198182
return env.length > 0 ? env : null;
199183
}
200-
if (!existsSync(CONFIG_PATH)) return null;
184+
if (!existsSync(SERVER_CONFIG_PATH)) return null;
201185
try {
202-
const raw = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')) as { api_token?: unknown };
186+
const raw = JSON.parse(readFileSync(SERVER_CONFIG_PATH, 'utf-8')) as { api_token?: unknown };
203187
return typeof raw.api_token === 'string' && raw.api_token.length > 0 ? raw.api_token : null;
204188
} catch (err) {
205189
logger.warn('Could not read api_token from config file', { err });
@@ -226,9 +210,9 @@ export class ConfigService {
226210
* fresh object on every call — safe for callers to mutate.
227211
*/
228212
getConfig(): WatchdogConfig {
229-
if (!existsSync(CONFIG_PATH)) return { ...DEFAULT_CONFIG };
213+
if (!existsSync(SERVER_CONFIG_PATH)) return { ...DEFAULT_CONFIG };
230214
try {
231-
const saved = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')) as Partial<WatchdogConfig>;
215+
const saved = JSON.parse(readFileSync(SERVER_CONFIG_PATH, 'utf-8')) as Partial<WatchdogConfig>;
232216
return { ...DEFAULT_CONFIG, ...saved };
233217
} catch (err) {
234218
logger.warn('Could not read config file, using defaults', { err });
@@ -242,7 +226,7 @@ export class ConfigService {
242226
* save here is not effective until the next `terraform apply`.
243227
*/
244228
saveConfig(config: WatchdogConfig): void {
245-
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
229+
writeFileSync(SERVER_CONFIG_PATH, JSON.stringify(config, null, 2));
246230
logger.info('Config saved', config);
247231
}
248232
}

0 commit comments

Comments
 (0)