Skip to content

Commit 00a7f36

Browse files
authored
Fix env var config roundtrip through build.rs TOML serialization (#362)
1 parent 08634ca commit 00a7f36

5 files changed

Lines changed: 200 additions & 182 deletions

File tree

crates/common/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ serde = { workspace = true }
4141
serde_json = { workspace = true }
4242
sha2 = { workspace = true }
4343
tokio = { workspace = true }
44+
toml = { workspace = true }
4445
trusted-server-js = { path = "../js" }
4546
url = { workspace = true }
4647
urlencoding = { workspace = true }

crates/common/build.rs

Lines changed: 14 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,82 +9,36 @@ mod auction_config_types;
99
#[path = "src/settings.rs"]
1010
mod settings;
1111

12-
use serde_json::Value;
13-
use std::collections::HashSet;
1412
use std::fs;
1513
use std::path::Path;
1614

1715
const TRUSTED_SERVER_INIT_CONFIG_PATH: &str = "../../trusted-server.toml";
1816
const TRUSTED_SERVER_OUTPUT_CONFIG_PATH: &str = "../../target/trusted-server-out.toml";
1917

2018
fn main() {
21-
merge_toml();
22-
rerun_if_changed();
23-
}
24-
25-
fn rerun_if_changed() {
26-
// Watch the root trusted-server.toml file for changes
27-
println!("cargo:rerun-if-changed={}", TRUSTED_SERVER_INIT_CONFIG_PATH);
28-
29-
// Create a default Settings instance and convert to JSON to discover all fields
30-
let default_settings = settings::Settings::default();
31-
let settings_json = serde_json::to_value(&default_settings).unwrap();
32-
33-
let mut env_vars = HashSet::new();
34-
collect_env_vars(&settings_json, &mut env_vars, &[]);
35-
36-
// Print rerun-if-env-changed for each variable
37-
let mut sorted_vars: Vec<_> = env_vars.into_iter().collect();
38-
sorted_vars.sort();
39-
40-
for var in sorted_vars {
41-
println!("cargo:rerun-if-env-changed={}", var);
42-
}
43-
}
44-
45-
fn merge_toml() {
46-
// Get the OUT_DIR where we'll copy the config file
47-
let dest_path = Path::new(TRUSTED_SERVER_OUTPUT_CONFIG_PATH);
19+
// Always rerun build.rs: integration settings are stored in a flat
20+
// HashMap<String, JsonValue>, so we cannot enumerate all possible env
21+
// var keys ahead of time. Emitting rerun-if-changed for a nonexistent
22+
// file forces cargo to always rerun the build script.
23+
println!("cargo:rerun-if-changed=_always_rebuild_sentinel_");
4824

4925
// Read init config
5026
let init_config_path = Path::new(TRUSTED_SERVER_INIT_CONFIG_PATH);
5127
let toml_content = fs::read_to_string(init_config_path)
52-
.unwrap_or_else(|_| panic!("Failed to read {:?}", init_config_path));
28+
.unwrap_or_else(|_| panic!("Failed to read {init_config_path:?}"));
5329

54-
// For build time: use from_toml to parse with environment variables
55-
let settings = settings::Settings::from_toml(&toml_content)
30+
// Merge base TOML with environment variable overrides and write output
31+
let settings = settings::Settings::from_toml_and_env(&toml_content)
5632
.expect("Failed to parse settings at build time");
5733

58-
// Write the merged settings to the output directory as TOML
5934
let merged_toml =
6035
toml::to_string_pretty(&settings).expect("Failed to serialize settings to TOML");
6136

62-
fs::write(dest_path, merged_toml).unwrap_or_else(|_| panic!("Failed to write {:?}", dest_path));
63-
}
64-
65-
fn collect_env_vars(value: &Value, env_vars: &mut HashSet<String>, path: &[String]) {
66-
if let Value::Object(map) = value {
67-
for (key, val) in map {
68-
let mut new_path = path.to_owned();
69-
new_path.push(key.to_uppercase());
70-
71-
match val {
72-
Value::String(_) | Value::Number(_) | Value::Bool(_) => {
73-
// Leaf node - create environment variable
74-
let env_var = format!(
75-
"{}{}{}",
76-
settings::ENVIRONMENT_VARIABLE_PREFIX,
77-
settings::ENVIRONMENT_VARIABLE_SEPARATOR,
78-
new_path.join(settings::ENVIRONMENT_VARIABLE_SEPARATOR)
79-
);
80-
env_vars.insert(env_var);
81-
}
82-
Value::Object(_) => {
83-
// Recurse into nested objects
84-
collect_env_vars(val, env_vars, &new_path);
85-
}
86-
_ => {}
87-
}
88-
}
37+
// Only write when content changes to avoid unnecessary recompilation.
38+
let dest_path = Path::new(TRUSTED_SERVER_OUTPUT_CONFIG_PATH);
39+
let current = fs::read_to_string(dest_path).unwrap_or_default();
40+
if current != merged_toml {
41+
fs::write(dest_path, merged_toml)
42+
.unwrap_or_else(|_| panic!("Failed to write {dest_path:?}"));
8943
}
9044
}

0 commit comments

Comments
 (0)