Skip to content

Commit 43075d0

Browse files
Alex Holmbergclaude
authored andcommitted
fix(deploy): correct dockerfile path derivation for subdirectory deployments
When deploying a service from a subdirectory (e.g., path: "services/contact-intelligence"), the dockerfile path and build context must be relative to the repo root, not the analyzed subdirectory. Before: dockerfile="Dockerfile", context="." (relative to subdirectory) After: dockerfile="services/contact-intelligence/Dockerfile", context="services/contact-intelligence" Changes: - Extract dockerfile filename, then prepend the subpath to construct repo-relative paths - Follow the same pattern as orchestrator.rs (commit 3cb8698) - Add docker_config to deployment response for visibility - Add debug logging for path derivation troubleshooting Fixes cloud runner failing with "unable to evaluate symlinks in Dockerfile path: lstat /workspace/source/Dockerfile: no such file or directory" when deploying monorepo services. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent af30a70 commit 43075d0

1 file changed

Lines changed: 50 additions & 9 deletions

File tree

src/agent/tools/platform/deploy_service.rs

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -561,27 +561,64 @@ User: "deploy this service"
561561

562562
// Build deployment config request
563563
// Derive dockerfile path and build context from DockerfileInfo
564+
// NOTE: When analyzing a subdirectory (args.path), paths must be relative to repo root
564565
let (dockerfile_path, build_context) = analysis.docker_analysis
565566
.as_ref()
566567
.and_then(|d| d.dockerfiles.first())
567568
.map(|df| {
568-
// Get the dockerfile path relative to project root
569-
let df_path = df.path.strip_prefix(&analysis_path)
570-
.map(|p| p.to_string_lossy().to_string())
571-
.unwrap_or_else(|_| df.path.to_string_lossy().to_string());
572-
573-
// Build context is the parent directory of the Dockerfile
574-
let context = df.path.parent()
569+
// Get dockerfile filename (e.g., "Dockerfile" or "Dockerfile.prod")
570+
let dockerfile_name = df.path.file_name()
571+
.map(|n| n.to_string_lossy().to_string())
572+
.unwrap_or_else(|| "Dockerfile".to_string());
573+
574+
// Derive build context from dockerfile path relative to analysis_path
575+
// The parent directory of the Dockerfile is typically the build context
576+
let analysis_relative_context = df.path.parent()
575577
.and_then(|p| p.strip_prefix(&analysis_path).ok())
576578
.map(|p| {
577579
let s = p.to_string_lossy().to_string();
578580
if s.is_empty() { ".".to_string() } else { s }
579581
})
580582
.unwrap_or_else(|| ".".to_string());
581583

582-
(df_path, context)
584+
// If we analyzed a subdirectory (args.path), prepend it to get repo-root-relative paths
585+
// Otherwise, the context from analyzer is already relative to project root
586+
let repo_relative_context = if let Some(ref subpath) = args.path {
587+
// Combine subpath with the analysis-relative build context
588+
if analysis_relative_context == "." {
589+
subpath.clone()
590+
} else {
591+
format!("{}/{}", subpath, analysis_relative_context)
592+
}
593+
} else {
594+
analysis_relative_context
595+
};
596+
597+
// Construct dockerfile path: context/Dockerfile
598+
// Following orchestrator.rs pattern (see commit 3cb8698)
599+
let df_path = if repo_relative_context == "." || repo_relative_context.is_empty() {
600+
dockerfile_name
601+
} else {
602+
format!("{}/{}", repo_relative_context, dockerfile_name)
603+
};
604+
605+
(df_path, repo_relative_context)
583606
})
584-
.unwrap_or_else(|| ("Dockerfile".to_string(), ".".to_string()));
607+
.unwrap_or_else(|| {
608+
// No dockerfile found - use subpath if provided, else defaults
609+
if let Some(ref subpath) = args.path {
610+
(format!("{}/Dockerfile", subpath), subpath.clone())
611+
} else {
612+
("Dockerfile".to_string(), ".".to_string())
613+
}
614+
});
615+
616+
tracing::debug!(
617+
"Deploy service docker config: dockerfile_path={}, build_context={}, subpath={:?}",
618+
dockerfile_path,
619+
build_context,
620+
args.path
621+
);
585622

586623
let cloud_runner_config = build_cloud_runner_config(
587624
&final_provider,
@@ -644,6 +681,10 @@ User: "deploy this service"
644681
"machine_type": final_machine,
645682
"region": final_region,
646683
"port": final_port,
684+
"docker_config": {
685+
"dockerfile_path": dockerfile_path,
686+
"build_context": build_context,
687+
},
647688
"message": format!(
648689
"NEW deployment started for '{}' on {} environment. Task ID: {}",
649690
service_name, resolved_env_name, response.backstage_task_id

0 commit comments

Comments
 (0)