Build a container image using the local Docker daemon. Creates a tar archive of `context_dir`, sends it to Docker with the specified Dockerfile path and tag, and streams build output to `on_log`.
(
dockerfile_path: &Path,
tag: &str,
context_dir: &Path,
build_args: &HashMap<String, String>,
on_log: &mut impl FnMut(String),
)
| 166 | /// Creates a tar archive of `context_dir`, sends it to Docker with the |
| 167 | /// specified Dockerfile path and tag, and streams build output to `on_log`. |
| 168 | async fn build_image( |
| 169 | dockerfile_path: &Path, |
| 170 | tag: &str, |
| 171 | context_dir: &Path, |
| 172 | build_args: &HashMap<String, String>, |
| 173 | on_log: &mut impl FnMut(String), |
| 174 | ) -> Result<()> { |
| 175 | let docker = Docker::connect_with_local_defaults() |
| 176 | .into_diagnostic() |
| 177 | .wrap_err("failed to connect to local Docker daemon")?; |
| 178 | // BUILD* always comes from daemon info. A TARGETPLATFORM override only |
| 179 | // changes the output image platform. |
| 180 | let build_platform = docker_daemon_platform(&docker).await?; |
| 181 | let target_platform = build_args |
| 182 | .get("TARGETPLATFORM") |
| 183 | .map_or_else(|| build_platform.clone(), Clone::clone); |
| 184 | let mut effective_build_args = build_args.clone(); |
| 185 | insert_implicit_platform_build_args( |
| 186 | &mut effective_build_args, |
| 187 | &target_platform, |
| 188 | &build_platform, |
| 189 | )?; |
| 190 | |
| 191 | // Compute the relative path of the Dockerfile within the context. |
| 192 | let dockerfile_relative = dockerfile_path |
| 193 | .strip_prefix(context_dir) |
| 194 | .unwrap_or(dockerfile_path); |
| 195 | let dockerfile_str = dockerfile_relative |
| 196 | .to_str() |
| 197 | .ok_or_else(|| miette::miette!("Dockerfile path is not valid UTF-8"))?; |
| 198 | |
| 199 | // Create a tar archive of the build context, respecting .dockerignore. |
| 200 | let context_tar = create_build_context_tar(context_dir)?; |
| 201 | |
| 202 | let mut builder = BuildImageOptionsBuilder::default() |
| 203 | .dockerfile(dockerfile_str) |
| 204 | .t(tag) |
| 205 | .rm(true) |
| 206 | .platform(&target_platform); |
| 207 | |
| 208 | // Pass build args to Docker. |
| 209 | if !effective_build_args.is_empty() { |
| 210 | builder = builder.buildargs(&effective_build_args); |
| 211 | } |
| 212 | |
| 213 | let options = builder.build(); |
| 214 | |
| 215 | let body = bollard::body_full(bytes::Bytes::from(context_tar)); |
| 216 | let mut stream = docker.build_image(options, None, Some(body)); |
| 217 | let no_progress_secs: u64 = std::env::var("OPENSHELL_BUILD_NO_PROGRESS_TIMEOUT_SECS") |
| 218 | .ok() |
| 219 | .and_then(|s| s.parse().ok()) |
| 220 | .filter(|&n| n > 0) |
| 221 | .unwrap_or(DEFAULT_BUILD_NO_PROGRESS_TIMEOUT_SECS); |
| 222 | let no_progress_timeout = Duration::from_secs(no_progress_secs); |
| 223 | |
| 224 | loop { |
| 225 | let next = match timeout(no_progress_timeout, stream.next()).await { |
no test coverage detected