Skip to content

Commit cf99192

Browse files
committed
fix: dedicated env var provider
1 parent 8259d43 commit cf99192

File tree

2 files changed

+80
-30
lines changed

2 files changed

+80
-30
lines changed

docs/cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ prek cache size [OPTIONS]
646646

647647
<dl class="cli-reference"><dt id="prek-cache-size--cd"><a href="#prek-cache-size--cd"><code>--cd</code></a>, <code>-C</code> <i>dir</i></dt><dd><p>Change to directory before running</p>
648648
</dd><dt id="prek-cache-size--color"><a href="#prek-cache-size--color"><code>--color</code></a> <i>color</i></dt><dd><p>Whether to use color in output</p>
649-
<p>May also be set with the <code>PREK_COLOR</code> environment variable.</p><p>[default: auto]</p><p>Possible values:</p>
649+
<p>Possible values:</p>
650650
<ul>
651651
<li><code>auto</code>: Enables colored output only when the output is going to a terminal or TTY with support</li>
652652
<li><code>always</code>: Enables colored output regardless of the detected environment</li>

src/settings.rs

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
use std::path::{Path, PathBuf};
1111
use std::sync::{LazyLock, RwLock};
1212

13-
use figment::providers::{Env, Serialized};
13+
use figment::providers::Serialized;
1414
use figment::{Figment, Profile, Provider, value::Map};
15+
use prek_consts::env_vars::EnvVars;
1516
use serde::{Deserialize, Serialize};
1617

1718
/// Global settings instance, initialized lazily.
@@ -162,20 +163,84 @@ impl Provider for PyProjectProvider {
162163
}
163164
}
164165

166+
/// Custom provider for PREK_* environment variables that handles
167+
/// special cases like comma-separated lists and boolean values.
168+
struct PrekEnvProvider;
169+
170+
impl Provider for PrekEnvProvider {
171+
fn metadata(&self) -> figment::Metadata {
172+
figment::Metadata::named("PREK_* environment variables")
173+
}
174+
175+
fn data(&self) -> Result<Map<Profile, figment::value::Dict>, figment::Error> {
176+
use figment::value::{Dict, Value};
177+
178+
let mut dict = Dict::new();
179+
180+
// PREK_SKIP or SKIP - comma-separated list
181+
if let Some(val) = EnvVars::var(EnvVars::PREK_SKIP)
182+
.ok()
183+
.or_else(|| EnvVars::var(EnvVars::SKIP).ok())
184+
{
185+
let items: Vec<Value> = val
186+
.split(',')
187+
.map(|s| s.trim().to_string())
188+
.filter(|s| !s.is_empty())
189+
.map(Value::from)
190+
.collect();
191+
dict.insert("skip".into(), Value::from(items));
192+
}
193+
194+
// PREK_HOME or PRE_COMMIT_HOME (EnvVars::var handles the fallback)
195+
if let Ok(val) = EnvVars::var(EnvVars::PREK_HOME) {
196+
dict.insert("home".into(), Value::from(val));
197+
}
198+
199+
if let Ok(val) = EnvVars::var(EnvVars::PREK_COLOR) {
200+
dict.insert("color".into(), Value::from(val));
201+
}
202+
203+
// PREK_ALLOW_NO_CONFIG - boolish values (EnvVars::var handles PRE_COMMIT fallback)
204+
if let Some(b) = EnvVars::var_as_bool(EnvVars::PREK_ALLOW_NO_CONFIG) {
205+
dict.insert("allow-no-config".into(), Value::from(b));
206+
}
207+
208+
// PREK_NO_CONCURRENCY - boolish values (EnvVars::var handles PRE_COMMIT fallback)
209+
if let Some(b) = EnvVars::var_as_bool(EnvVars::PREK_NO_CONCURRENCY) {
210+
dict.insert("no-concurrency".into(), Value::from(b));
211+
}
212+
213+
if let Some(b) = EnvVars::var_as_bool(EnvVars::PREK_NO_FAST_PATH) {
214+
dict.insert("no-fast-path".into(), Value::from(b));
215+
}
216+
217+
if let Ok(val) = EnvVars::var(EnvVars::PREK_UV_SOURCE) {
218+
dict.insert("uv-source".into(), Value::from(val));
219+
}
220+
221+
if let Some(b) = EnvVars::var_as_bool(EnvVars::PREK_NATIVE_TLS) {
222+
dict.insert("native-tls".into(), Value::from(b));
223+
}
224+
225+
if let Ok(val) = EnvVars::var(EnvVars::PREK_CONTAINER_RUNTIME) {
226+
dict.insert("container-runtime".into(), Value::from(val));
227+
}
228+
229+
let mut map = Map::new();
230+
map.insert(Profile::Default, dict);
231+
Ok(map)
232+
}
233+
}
234+
165235
impl Settings {
166236
/// Initialize settings from the given working directory.
167237
///
168238
/// This should be called early in `main()` before any settings are accessed.
169239
/// If not called, settings will use the current directory when first accessed.
170240
pub fn init(working_dir: &Path) -> Result<(), figment::Error> {
171241
let settings = Self::discover(working_dir)?;
172-
173-
let mut guard = SETTINGS.write().expect("settings lock poisoned");
174-
*guard = settings;
175-
176-
let mut init_guard = INITIALIZED.write().expect("initialized lock poisoned");
177-
*init_guard = true;
178-
242+
*SETTINGS.write().expect("settings lock poisoned") = settings;
243+
*INITIALIZED.write().expect("initialized lock poisoned") = true;
179244
Ok(())
180245
}
181246

@@ -187,15 +252,9 @@ impl Settings {
187252
cli_overrides: CliOverrides,
188253
) -> Result<(), figment::Error> {
189254
let figment = Self::build_figment(working_dir).merge(Serialized::defaults(cli_overrides));
190-
191255
let settings: Settings = figment.extract()?;
192-
193-
let mut guard = SETTINGS.write().expect("settings lock poisoned");
194-
*guard = settings;
195-
196-
let mut init_guard = INITIALIZED.write().expect("initialized lock poisoned");
197-
*init_guard = true;
198-
256+
*SETTINGS.write().expect("settings lock poisoned") = settings;
257+
*INITIALIZED.write().expect("initialized lock poisoned") = true;
199258
Ok(())
200259
}
201260

@@ -214,25 +273,16 @@ impl Settings {
214273
let _ = Self::init(&cwd);
215274
}
216275
}
217-
218-
let guard = SETTINGS.read().expect("settings lock poisoned");
219-
guard.clone()
276+
SETTINGS.read().expect("settings lock poisoned").clone()
220277
}
221278

222279
/// Build the figment for the given working directory.
223280
fn build_figment(working_dir: &Path) -> Figment {
224281
let mut figment = Figment::new()
225282
// Lowest precedence: built-in defaults
226283
.merge(Serialized::defaults(Settings::default()))
227-
// Next: environment variables
228-
.merge(
229-
Env::prefixed("PREK_")
230-
.map(|key| {
231-
// Convert PREK_FOO_BAR to foo-bar for serde
232-
key.as_str().to_lowercase().replace('_', "-").into()
233-
})
234-
.split(","), // Allow comma-separated lists for `skip`
235-
);
284+
// Next: environment variables (custom provider for special handling)
285+
.merge(PrekEnvProvider);
236286

237287
// Walk up to find pyproject.toml
238288
let mut current = Some(working_dir);
@@ -349,7 +399,7 @@ color = "always"
349399
fn test_env_var_loading() {
350400
figment::Jail::expect_with(|jail| {
351401
jail.set_env("PREK_SKIP", "hook1,hook2");
352-
jail.set_env("PREK_NO_CONCURRENCY", "true");
402+
jail.set_env("PREK_NO_CONCURRENCY", "1");
353403
jail.set_env("PREK_COLOR", "never");
354404

355405
let settings = Settings::discover(jail.directory())?;

0 commit comments

Comments
 (0)