-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathenv.dart
More file actions
113 lines (102 loc) · 3.46 KB
/
env.dart
File metadata and controls
113 lines (102 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/// Loads environment configuration from `.env` files.
///
/// Searches for `.env` at the project root (walking up from cwd to find
/// a directory containing a `pubspec.yaml` with `workspace:`, or
/// any `.env` file along the way).
library;
import 'dart:io';
import 'package:dotenv/dotenv.dart' as dotenv;
import 'package:path/path.dart' as p;
/// Well-known environment variable keys used by the CLI.
abstract final class EnvKeys {
static const gcsBucket = 'GCS_BUCKET';
static const gcpProjectId = 'GCP_PROJECT_ID';
static const googleApplicationCredentials = 'GOOGLE_APPLICATION_CREDENTIALS';
static const geminiApiKey = 'GEMINI_API_KEY';
static const anthropicApiKey = 'ANTHROPIC_API_KEY';
static const openaiApiKey = 'OPENAI_API_KEY';
/// All keys that `loadEnv` will look for.
static const all = [
gcsBucket,
gcpProjectId,
googleApplicationCredentials,
geminiApiKey,
anthropicApiKey,
openaiApiKey,
];
}
/// Loads environment configuration, merging `.env` file values with
/// system environment variables. System env vars take precedence.
///
/// Returns a map of resolved environment key-value pairs.
Map<String, String> loadEnv() {
final envFile = _findEnvFile();
final env = <String, String>{};
// Load from .env file if found
dotenv.DotEnv? dotEnv;
if (envFile != null) {
dotEnv = dotenv.DotEnv(includePlatformEnvironment: false)..load([envFile]);
}
// For each known key, check .env first, then system env overrides
for (final key in EnvKeys.all) {
// .env file value (using public [] operator)
if (dotEnv != null && dotEnv.isDefined(key)) {
final value = dotEnv[key];
if (value != null && value.isNotEmpty) {
env[key] = value;
}
}
// System environment variables override .env file values
final systemValue = Platform.environment[key];
if (systemValue != null && systemValue.isNotEmpty) {
env[key] = systemValue;
}
}
return env;
}
/// Resolves a value with the following precedence:
/// 1. Explicit CLI flag value
/// 2. Environment variable (from .env or system)
/// 3. Default value (if provided)
///
/// Throws [StateError] if no value is found and no default is given.
String resolveEnvValue({
String? flagValue,
required String envKey,
required Map<String, String> env,
String? defaultValue,
}) {
if (flagValue != null && flagValue.isNotEmpty) return flagValue;
final envValue = env[envKey];
if (envValue != null && envValue.isNotEmpty) return envValue;
if (defaultValue != null) return defaultValue;
throw StateError(
'Missing required configuration: $envKey. '
'Set it in .env, as an environment variable, or pass it as a CLI flag.',
);
}
/// Walks up from the current directory to find a `.env` file
/// at or below the repo root.
String? _findEnvFile() {
var dir = Directory.current.absolute;
// Walk up at most 10 levels
for (var i = 0; i < 10; i++) {
final envPath = p.join(dir.path, '.env');
if (File(envPath).existsSync()) {
return envPath;
}
// Check if this looks like the repo root
final pubspecPath = p.join(dir.path, 'pubspec.yaml');
if (File(pubspecPath).existsSync()) {
final contents = File(pubspecPath).readAsStringSync();
if (contents.contains('workspace:')) {
// This is the repo root — no .env file here
return null;
}
}
final parent = dir.parent;
if (parent.path == dir.path) break; // reached filesystem root
dir = parent;
}
return null;
}