mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-20 00:11:34 +00:00
191 lines
6.1 KiB
Rust
191 lines
6.1 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use crate::features::FeatureLayer;
|
|
|
|
/// Generate a combined Dockerfile from base + features + env + user.
|
|
pub fn generate(
|
|
base_dockerfile: &str,
|
|
feature_layers: &[FeatureLayer],
|
|
container_env: &HashMap<String, String>,
|
|
remote_user: Option<&str>,
|
|
) -> String {
|
|
let mut sections: Vec<String> = Vec::new();
|
|
|
|
sections.push("# Generated by arc-devcontainer".to_string());
|
|
sections.push(base_dockerfile.to_string());
|
|
|
|
for layer in feature_layers {
|
|
sections.push(layer.dockerfile_snippet.clone());
|
|
}
|
|
|
|
if !container_env.is_empty() {
|
|
let mut keys: Vec<&String> = container_env.keys().collect();
|
|
keys.sort();
|
|
let env_lines: Vec<String> = keys
|
|
.iter()
|
|
.map(|k| format!("ENV {}={}", k, container_env[*k]))
|
|
.collect();
|
|
sections.push(env_lines.join("\n"));
|
|
}
|
|
|
|
if let Some(user) = remote_user {
|
|
sections.push(format!("USER {}", user));
|
|
}
|
|
|
|
let mut result = sections.join("\n\n");
|
|
result.push('\n');
|
|
result
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
fn make_layer(id: &str, dir_name: &str, snippet: &str) -> FeatureLayer {
|
|
FeatureLayer {
|
|
id: id.to_string(),
|
|
dir_name: dir_name.to_string(),
|
|
dockerfile_snippet: snippet.to_string(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn base_image_only() {
|
|
let result = generate("FROM ubuntu:22.04", &[], &HashMap::new(), None);
|
|
assert_eq!(
|
|
result,
|
|
"# Generated by arc-devcontainer\n\nFROM ubuntu:22.04\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn base_dockerfile_preserved_as_is() {
|
|
let base = "FROM ubuntu:22.04\nRUN apt-get update\nRUN apt-get install -y curl";
|
|
let result = generate(base, &[], &HashMap::new(), None);
|
|
assert_eq!(
|
|
result,
|
|
"# Generated by arc-devcontainer\n\nFROM ubuntu:22.04\nRUN apt-get update\nRUN apt-get install -y curl\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn with_feature_layers() {
|
|
let layers = vec![
|
|
make_layer("node", "node-1", "RUN install-node.sh"),
|
|
make_layer("python", "python-1", "RUN install-python.sh"),
|
|
];
|
|
let result = generate("FROM ubuntu:22.04", &layers, &HashMap::new(), None);
|
|
assert_eq!(
|
|
result,
|
|
"# Generated by arc-devcontainer\n\n\
|
|
FROM ubuntu:22.04\n\n\
|
|
RUN install-node.sh\n\n\
|
|
RUN install-python.sh\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn with_env_sorted() {
|
|
let mut env = HashMap::new();
|
|
env.insert("ZEBRA".to_string(), "stripes".to_string());
|
|
env.insert("APPLE".to_string(), "red".to_string());
|
|
env.insert("MANGO".to_string(), "yellow".to_string());
|
|
let result = generate("FROM alpine", &[], &env, None);
|
|
assert_eq!(
|
|
result,
|
|
"# Generated by arc-devcontainer\n\n\
|
|
FROM alpine\n\n\
|
|
ENV APPLE=red\n\
|
|
ENV MANGO=yellow\n\
|
|
ENV ZEBRA=stripes\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn with_remote_user() {
|
|
let result = generate("FROM alpine", &[], &HashMap::new(), Some("vscode"));
|
|
assert_eq!(
|
|
result,
|
|
"# Generated by arc-devcontainer\n\n\
|
|
FROM alpine\n\n\
|
|
USER vscode\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn all_combined() {
|
|
let layers = vec![make_layer("node", "node-1", "RUN install-node.sh")];
|
|
let mut env = HashMap::new();
|
|
env.insert("PATH".to_string(), "/usr/local/bin".to_string());
|
|
env.insert("HOME".to_string(), "/home/vscode".to_string());
|
|
let result = generate("FROM ubuntu:22.04", &layers, &env, Some("vscode"));
|
|
assert_eq!(
|
|
result,
|
|
"# Generated by arc-devcontainer\n\n\
|
|
FROM ubuntu:22.04\n\n\
|
|
RUN install-node.sh\n\n\
|
|
ENV HOME=/home/vscode\n\
|
|
ENV PATH=/usr/local/bin\n\n\
|
|
USER vscode\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn empty_feature_layers_no_extra_blank_lines() {
|
|
let result = generate("FROM alpine", &[], &HashMap::new(), Some("dev"));
|
|
assert_eq!(
|
|
result,
|
|
"# Generated by arc-devcontainer\n\n\
|
|
FROM alpine\n\n\
|
|
USER dev\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn empty_env_map_treated_as_none() {
|
|
let env = HashMap::new();
|
|
let result = generate("FROM alpine", &[], &env, None);
|
|
assert_eq!(result, "# Generated by arc-devcontainer\n\nFROM alpine\n");
|
|
}
|
|
|
|
#[test]
|
|
fn multiline_base_dockerfile() {
|
|
let base = "FROM ubuntu:22.04 AS builder\n\
|
|
RUN apt-get update && apt-get install -y build-essential\n\
|
|
COPY . /app\n\
|
|
RUN make\n\
|
|
\n\
|
|
FROM ubuntu:22.04\n\
|
|
COPY --from=builder /app/bin /usr/local/bin";
|
|
let result = generate(base, &[], &HashMap::new(), None);
|
|
assert!(result.starts_with("# Generated by arc-devcontainer\n\n"));
|
|
assert!(result.contains("FROM ubuntu:22.04 AS builder"));
|
|
assert!(result.contains("COPY --from=builder /app/bin /usr/local/bin"));
|
|
assert!(result.ends_with('\n'));
|
|
}
|
|
|
|
#[test]
|
|
fn container_env_only() {
|
|
let mut cenv = HashMap::new();
|
|
cenv.insert("DEBIAN_FRONTEND".to_string(), "noninteractive".to_string());
|
|
let result = generate("FROM alpine", &[], &cenv, None);
|
|
assert_eq!(
|
|
result,
|
|
"# Generated by arc-devcontainer\n\n\
|
|
FROM alpine\n\n\
|
|
ENV DEBIAN_FRONTEND=noninteractive\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn container_env_with_multiple_keys() {
|
|
let mut cenv = HashMap::new();
|
|
cenv.insert("ALPHA".to_string(), "first".to_string());
|
|
cenv.insert("BETA".to_string(), "second".to_string());
|
|
cenv.insert("GAMMA".to_string(), "third".to_string());
|
|
let result = generate("FROM alpine", &[], &cenv, None);
|
|
assert!(result.contains("ENV ALPHA=first"));
|
|
assert!(result.contains("ENV BETA=second"));
|
|
assert!(result.contains("ENV GAMMA=third"));
|
|
}
|
|
}
|