linera_service/cli_wrappers/
docker.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::path::PathBuf;
5
6use anyhow::{Context, Result};
7use linera_base::command::{current_binary_parent, CommandExt};
8use pathdiff::diff_paths;
9use tokio::process::Command;
10
11use crate::cli_wrappers::local_kubernetes_net::BuildMode;
12
13pub struct DockerImage {
14    name: String,
15}
16
17impl DockerImage {
18    pub fn name(&self) -> &String {
19        &self.name
20    }
21
22    pub async fn build(
23        name: &str,
24        binaries: &BuildArg,
25        github_root: &PathBuf,
26        build_mode: &BuildMode,
27        dual_store: bool,
28    ) -> Result<Self> {
29        let build_arg = match binaries {
30            BuildArg::Directory(bin_path) => {
31                // Get the binaries from the specified path
32                let bin_path =
33                    diff_paths(bin_path, github_root).context("Getting relative path failed")?;
34                let bin_path_str = bin_path.to_str().context("Getting str failed")?;
35                format!("binaries={bin_path_str}")
36            }
37            BuildArg::ParentDirectory => {
38                // Get the binaries from current_binary_parent
39                let parent_path = current_binary_parent()
40                    .expect("Fetching current binaries path should not fail");
41                let bin_path =
42                    diff_paths(parent_path, github_root).context("Getting relative path failed")?;
43                let bin_path_str = bin_path.to_str().context("Getting str failed")?;
44                format!("binaries={bin_path_str}")
45            }
46            BuildArg::Build => {
47                // Build inside the Docker container
48                let arch = std::env::consts::ARCH;
49                // Translate architecture for Docker build arg
50                let docker_arch = match arch {
51                    "arm" => "aarch",
52                    _ => arch,
53                };
54                format!("target={}-unknown-linux-gnu", docker_arch)
55            }
56        };
57
58        let docker_image = Self {
59            name: name.to_owned(),
60        };
61        let mut command = Command::new("docker");
62        command
63            .current_dir(github_root)
64            .arg("build")
65            .args(["-f", "docker/Dockerfile"])
66            .args(["--build-arg", &build_arg]);
67
68        match build_mode {
69            // Release is the default, so no need to add any arguments
70            BuildMode::Release => {}
71            BuildMode::Debug => {
72                command.args(["--build-arg", "build_folder=debug"]);
73                command.args(["--build-arg", "build_flag="]);
74            }
75        }
76
77        if dual_store {
78            command.args(["--build-arg", "build_features=rocksdb,scylladb,metrics"]);
79        }
80
81        #[cfg(not(with_testing))]
82        command
83            .args([
84                "--build-arg",
85                &format!(
86                    "git_commit={}",
87                    linera_version::VersionInfo::get()?.git_commit
88                ),
89            ])
90            .args([
91                "--build-arg",
92                &format!(
93                    "build_date={}",
94                    // Same format as $(TZ=UTC date)
95                    chrono::Utc::now().format("%a %b %d %T UTC %Y")
96                ),
97            ]);
98
99        command.arg(".").args(["-t", name]).spawn_and_wait().await?;
100        Ok(docker_image)
101    }
102}
103
104/// Which binaries to use in the Docker container.
105#[derive(Clone)]
106pub enum BuildArg {
107    /// Build the binaries within the container.
108    Build,
109    /// Look for the binaries in the parent directory of the current binary.
110    ParentDirectory,
111    /// Look for the binaries in the specified path.
112    Directory(PathBuf),
113}
114
115impl From<Option<Option<PathBuf>>> for BuildArg {
116    fn from(arg: Option<Option<PathBuf>>) -> Self {
117        match arg {
118            None => BuildArg::Build,
119            Some(None) => BuildArg::ParentDirectory,
120            Some(Some(path)) => BuildArg::Directory(path),
121        }
122    }
123}