Skip to content

Commit e239761

Browse files
committed
fix(cli): mount the config from an absolute --config path correctly
`cb init` built the config bind-mount source as `./{config_path}`. The prefix is needed for a relative path, which compose would otherwise read as a named volume, but an absolute path became `.//abs/path`: compose resolves that against the project directory, docker creates a root-owned directory there, and the sidecar starts with a DIRECTORY mounted at /cb-config.toml, dying with `Unable to find config file: "/cb-config.toml": Is a directory`. The prefix is now added only when the path needs it. Every other mount in the generated file already passed its path through unchanged. The init integration test already ran with an absolute --config but asserted only the mount's container side, so the bug rendered under it; it now pins the host side too.
1 parent 78e0a46 commit e239761

2 files changed

Lines changed: 35 additions & 5 deletions

File tree

‎bin/tests/binary.rs‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,16 @@ fn test_init_compose_file_pbs_service_structure() {
130130
assert_eq!(pbs["image"].as_str(), Some("ghcr.io/commit-boost/commit-boost:latest"), "image");
131131
assert_eq!(pbs["container_name"].as_str(), Some("cb_pbs"), "container_name");
132132

133-
// Config file must be mounted inside the container.
133+
// Config file must be mounted inside the container, from the path it was
134+
// read from. `--config` here is ABSOLUTE, so the source must be that path
135+
// verbatim: a `./` prefix would make compose resolve it against the project
136+
// directory and docker would create a directory there instead.
134137
let volumes = pbs["volumes"].as_sequence().expect("volumes is a list");
138+
let expected_mount = format!("{}:/cb-config.toml:ro", config.display());
135139
assert!(
136-
volumes.iter().any(|v| v.as_str().map_or(false, |s| s.ends_with(":/cb-config.toml:ro"))),
137-
"config must be mounted at /cb-config.toml"
140+
volumes.iter().any(|v| v.as_str() == Some(expected_mount.as_str())),
141+
"config must be mounted at /cb-config.toml from {}, got {volumes:?}",
142+
config.display()
138143
);
139144

140145
// Required environment variables must be present.

‎crates/cli/src/docker_init.rs‎

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@ use docker_compose_types::{
3131
use eyre::Result;
3232
use indexmap::IndexMap;
3333

34+
/// A host path as a compose bind-mount source. Compose reads a source with no
35+
/// `/` or `./` prefix as a NAMED VOLUME, so a relative path needs the prefix;
36+
/// an absolute one must be passed through, or it is resolved against the
37+
/// project directory and docker creates a root-owned directory there.
38+
fn compose_bind_source(path: &Path) -> String {
39+
let source = path.display().to_string();
40+
if path.is_absolute() || source.starts_with("./") || source.starts_with("../") {
41+
source
42+
} else {
43+
format!("./{source}")
44+
}
45+
}
46+
3447
/// Name of the docker compose file
3548
pub const CB_COMPOSE_FILE: &str = "cb.docker-compose.yml";
3649
/// Name of the envs file
@@ -91,8 +104,8 @@ pub async fn handle_docker_init(config_path: PathBuf, output_dir: PathBuf) -> Re
91104
let mut service_config = ServiceCreationInfo {
92105
config_info: CommitBoostConfigInfo {
93106
config_volume: Volumes::Simple(format!(
94-
"./{}:{}:ro",
95-
config_path.display(),
107+
"{}:{}:ro",
108+
compose_bind_source(&config_path),
96109
CONFIG_DEFAULT
97110
)),
98111
cb_config: CommitBoostConfig::from_file(&config_path)?,
@@ -954,6 +967,18 @@ mod tests {
954967

955968
// --- get_env_val ---
956969

970+
/// Compose reads a bare source as a named volume, so a relative path keeps
971+
/// the `./` prefix; an absolute one must NOT get it, or compose resolves
972+
/// `.//abs/path` against the project directory and docker creates a
973+
/// root-owned directory there instead of mounting the file.
974+
#[test]
975+
fn test_compose_bind_source_prefixes_only_relative_paths() {
976+
assert_eq!(compose_bind_source(Path::new("cb-config.toml")), "./cb-config.toml");
977+
assert_eq!(compose_bind_source(Path::new("./cb-config.toml")), "./cb-config.toml");
978+
assert_eq!(compose_bind_source(Path::new("../cb-config.toml")), "../cb-config.toml");
979+
assert_eq!(compose_bind_source(Path::new("/etc/cb/config.toml")), "/etc/cb/config.toml");
980+
}
981+
957982
#[test]
958983
fn test_get_env_val_returns_string_pair() {
959984
let (key, val) = get_env_val("MY_KEY", "my_value");

0 commit comments

Comments
 (0)