Skip to content

Commit 3328d79

Browse files
committed
Profiles override env vars and dependencies
Signed-off-by: itowlson <[email protected]>
1 parent ecae60d commit 3328d79

File tree

3 files changed

+106
-11
lines changed

3 files changed

+106
-11
lines changed

crates/environments/src/loader.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl ApplicationToValidate {
7777
spin_manifest::schema::v2::ComponentSpec::Inline(c) => (
7878
trigger.id.as_str(),
7979
&c.source(profile),
80-
&c.dependencies,
80+
&c.dependencies(profile),
8181
spin_loader::requires_service_chaining(c),
8282
),
8383
spin_manifest::schema::v2::ComponentSpec::Reference(r) => {
@@ -91,7 +91,7 @@ impl ApplicationToValidate {
9191
(
9292
id,
9393
&component.source(profile),
94-
&component.dependencies,
94+
&component.dependencies(profile),
9595
spin_loader::requires_service_chaining(component),
9696
)
9797
}

crates/loader/src/local.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,23 +176,23 @@ impl LocalLoader {
176176
})?;
177177

178178
let metadata = ValuesMapBuilder::new()
179-
.serializable("build", component.build(self.profile()))?
180-
.string("description", component.description)
179+
.string("description", component.description.clone())
181180
.string_array("allowed_outbound_hosts", allowed_outbound_hosts)
182-
.string_array("key_value_stores", component.key_value_stores)
183-
.string_array("databases", component.sqlite_databases)
184-
.string_array("ai_models", component.ai_models)
181+
.string_array("key_value_stores", component.key_value_stores.clone())
182+
.string_array("databases", component.sqlite_databases.clone())
183+
.string_array("ai_models", component.ai_models.clone())
184+
.serializable("build", component.build(self.profile()))?
185185
.take();
186186

187187
let dependencies = self
188188
.load_component_dependencies(
189189
id,
190190
component.dependencies_inherit_configuration,
191-
&component.dependencies,
191+
&component.dependencies(self.profile()),
192192
)
193193
.await?;
194194

195-
let env = component.environment.into_iter().collect();
195+
let env = component.environment(self.profile()).into_iter().collect();
196196

197197
let files = if component.files.is_empty() {
198198
vec![]

crates/manifest/src/schema/v2.rs

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ pub struct Component {
317317
///
318318
/// `environment = { DB_URL = "mysql://spin:spin@localhost/dev" }`
319319
#[serde(default, skip_serializing_if = "Map::is_empty")]
320-
pub environment: Map<String, String>,
320+
pub(crate) environment: Map<String, String>,
321321
/// The files the component is allowed to read. Each list entry is either:
322322
///
323323
/// - a glob pattern (e.g. "assets/**/*.jpg"); or
@@ -413,7 +413,7 @@ pub struct Component {
413413
///
414414
/// Learn more: https://spinframework.dev/writing-apps#using-component-dependencies
415415
#[serde(default, skip_serializing_if = "ComponentDependencies::is_empty")]
416-
pub dependencies: ComponentDependencies,
416+
pub(crate) dependencies: ComponentDependencies,
417417
/// Override values to use when building or running a named build profile.
418418
///
419419
/// Example: `profile.debug.build.command = "npm run build-debug"`
@@ -433,6 +433,22 @@ pub struct ComponentProfileOverride {
433433
#[serde(default, skip_serializing_if = "Option::is_none")]
434434
source: Option<ComponentSource>,
435435

436+
/// Environment variables for the Wasm module to be overridden in this profile.
437+
/// Environment variables specified in the default profile will still be set
438+
/// if not overridden here.
439+
///
440+
/// `environment = { DB_URL = "mysql://spin:spin@localhost/dev" }`
441+
#[serde(default, skip_serializing_if = "Map::is_empty")]
442+
environment: Map<String, String>,
443+
444+
/// Wasm Component Model imports to be overridden in this profile.
445+
/// Dependencies specified in the default profile will still be composed
446+
/// if not overridden here.
447+
///
448+
/// Learn more: https://spinframework.dev/writing-apps#using-component-dependencies
449+
#[serde(default, skip_serializing_if = "ComponentDependencies::is_empty")]
450+
dependencies: ComponentDependencies,
451+
436452
/// The command or commands for building the component in non-default profiles.
437453
/// If a component has no special build instructions for a profile, the
438454
/// default build command is used.
@@ -502,6 +518,36 @@ impl Component {
502518
None => &self.source,
503519
}
504520
}
521+
522+
/// The component's environment variables
523+
pub fn environment(&self, profile: Option<&str>) -> Map<String, String> {
524+
let mut environment = self.environment.clone();
525+
526+
let Some(overrides) = self.profile(profile).map(|o| o.environment.clone()) else {
527+
return environment;
528+
};
529+
530+
for (name, value) in overrides {
531+
environment.insert(name, value);
532+
}
533+
534+
environment
535+
}
536+
537+
/// The component's dependencies
538+
pub fn dependencies(&self, profile: Option<&str>) -> ComponentDependencies {
539+
let mut dependencies = self.dependencies.clone();
540+
541+
let Some(overrides) = self.profile(profile).map(|o| o.dependencies.clone()) else {
542+
return dependencies;
543+
};
544+
545+
for (itf, dep) in overrides.inner {
546+
dependencies.inner.insert(itf, dep);
547+
}
548+
549+
dependencies
550+
}
505551
}
506552

507553
/// Component dependencies
@@ -1225,4 +1271,53 @@ mod tests {
12251271
.commands()[1]
12261272
);
12271273
}
1274+
1275+
#[test]
1276+
fn profiles_override_env_vars() {
1277+
let manifest = AppManifest::deserialize(toml! {
1278+
spin_manifest_version = 2
1279+
[application]
1280+
name = "trigger-configs"
1281+
[[trigger.fake]]
1282+
component = "profile-test"
1283+
[component.profile-test]
1284+
source = "original"
1285+
environment = { DB_URL = "pg://production" }
1286+
[component.profile-test.profile.fancy]
1287+
environment = { DB_URL = "pg://fancy", FANCINESS = "1" }
1288+
})
1289+
.expect("manifest should be valid");
1290+
1291+
let id = KebabId::try_from("profile-test".to_owned())
1292+
.expect("profile-test should have been kebab");
1293+
let component = manifest
1294+
.components
1295+
.get(&id)
1296+
.expect("should have compopnent with id profile-test");
1297+
1298+
assert_eq!(1, component.environment(None).len());
1299+
assert_eq!(
1300+
"pg://production",
1301+
component
1302+
.environment(None)
1303+
.get("DB_URL")
1304+
.expect("DB_URL should have been set")
1305+
);
1306+
1307+
assert_eq!(2, component.environment(Some("fancy")).len());
1308+
assert_eq!(
1309+
"pg://fancy",
1310+
component
1311+
.environment(Some("fancy"))
1312+
.get("DB_URL")
1313+
.expect("DB_URL should have been set")
1314+
);
1315+
assert_eq!(
1316+
"1",
1317+
component
1318+
.environment(Some("fancy"))
1319+
.get("FANCINESS")
1320+
.expect("FANCINESS should have been set")
1321+
);
1322+
}
12281323
}

0 commit comments

Comments
 (0)