Skip to content

Commit 796a3a5

Browse files
codydeclaude
andauthored
fix(iac): render preDeploy as a first-class field in migrate and pull (#1167)
Config as Code migrations were emitting the pre-deploy command as a comment ('// preDeployCommand from CaC: ...'), silently dropping working migration config even though the IaC SDK and apply path fully support the field. config pull buried it in the raw deploy block for the same reason. Emit 'preDeploy:' as a real field in both, unwrapping the single-command array to a bare string. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 8984abb commit 796a3a5

2 files changed

Lines changed: 38 additions & 6 deletions

File tree

src/commands/config/migrate.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,16 @@ fn emit_service_fields(cac: &CacFile) -> Vec<String> {
613613
} else if let Some(region) = &cac.deploy.region {
614614
fields.push(format!(" replicas: {{ {}: 1 }},", js_string(region)));
615615
}
616+
if let Some(pre) = &cac.deploy.pre_deploy_command {
617+
let rendered = match pre {
618+
JsonValue::Array(items) if items.len() == 1 && items[0].is_string() => {
619+
js_string(items[0].as_str().unwrap_or_default())
620+
}
621+
JsonValue::String(cmd) => js_string(cmd),
622+
other => json_to_ts(other),
623+
};
624+
fields.push(format!(" preDeploy: {rendered},"));
625+
}
616626
if let Some(dockerfile) = &cac.build.dockerfile_path {
617627
fields.push(format!(
618628
" // dockerfilePath from CaC: {}",
@@ -625,12 +635,6 @@ fn emit_service_fields(cac: &CacFile) -> Vec<String> {
625635
if let Some(cron) = &cac.deploy.cron_schedule {
626636
fields.push(format!(" // cronSchedule from CaC: {}", js_string(cron)));
627637
}
628-
if let Some(pre) = &cac.deploy.pre_deploy_command {
629-
fields.push(format!(
630-
" // preDeployCommand from CaC: {}",
631-
json_to_ts(pre)
632-
));
633-
}
634638
if let Some(watch) = &cac.build.watch_patterns {
635639
let arr = watch
636640
.iter()
@@ -847,6 +851,22 @@ mod tests {
847851
assert!(go.contains("const Partial = \"api\""));
848852
}
849853

854+
#[test]
855+
fn emits_pre_deploy_as_a_real_field() {
856+
let cac = CacFile {
857+
deploy: CacDeploy {
858+
start_command: Some("node index.js".into()),
859+
pre_deploy_command: Some(serde_json::json!(["npx prisma migrate deploy"])),
860+
..Default::default()
861+
},
862+
..Default::default()
863+
};
864+
let services = [svc("api", cac)];
865+
let out = emit_railway_ts("api", &services, true);
866+
assert!(out.contains("preDeploy: \"npx prisma migrate deploy\""));
867+
assert!(!out.contains("// preDeployCommand from CaC"));
868+
}
869+
850870
#[test]
851871
fn merges_multiple_services_without_a_partial() {
852872
let web = svc(

src/commands/config/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,18 @@ fn render_deploy(
13071307
if let Some(timeout) = remaining.remove("healthcheckTimeout") {
13081308
lines.push(lang.config_field("healthcheckTimeout", &code_value(&timeout, lang)));
13091309
}
1310+
if let Some(pre) = remaining.remove("preDeployCommand") {
1311+
if !pre.is_null() {
1312+
let rendered = match &pre {
1313+
serde_json::Value::Array(items) if items.len() == 1 && items[0].is_string() => {
1314+
format!("{:?}", items[0].as_str().unwrap_or_default())
1315+
}
1316+
serde_json::Value::String(command) => format!("{:?}", command),
1317+
other => code_value(other, lang),
1318+
};
1319+
lines.push(lang.config_field("preDeploy", &rendered));
1320+
}
1321+
}
13101322
if let Some(regions) = remaining.remove("multiRegionConfig") {
13111323
lines.push(lang.config_field("replicas", &render_replicas(&regions, lang)));
13121324
}

0 commit comments

Comments
 (0)