Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ Notes:
`-b <path to pkg>` to `build-img`. The Makefile's `base-image` target always
adds it; to use `make` without it, override both variables:
`make base-image pkg_bin= build_img_args="-a x86_64"`.
- Extra packages can be added on top of the default ones (see `BASE_PACKAGES`
in `src/lib.rs`) with `-P`/`--add-package`, which can be repeated. Names are
[nixpkgs](https://search.nixos.org/packages) attribute names, e.g.
`cargo run --bin build-img -- -a x86_64 -P tshark -P python3`. With `make`,
append them to `build_img_args`:
`make base-image build_img_args="-a x86_64 -b <path to pkg> -P tshark"`.
Inside `dive`, `pkg list` shows them as built-in packages.
- `--features embedded_image` lets `dive` install the base image from its own
binary. Without it, `dive` builds the base image on first run, which requires
network access.
Expand Down
13 changes: 5 additions & 8 deletions src/bin/build-img.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ struct Args {
#[arg(short, long, env)]
persistent_base_dir: Option<PathBuf>,

/// Alternative flake directory
#[arg(short, long, env)]
flake_dir: Option<PathBuf>,

/// Add binary into base image
#[arg(short = 'b', long = "add-binary")]
binaries: Vec<PathBuf>,

/// Add package into base image (on top of the default packages)
#[arg(short = 'P', long = "add-package")]
packages: Vec<String>,

/// Architecture
#[arg(short, long, env)]
arch: Option<String>,
Expand Down Expand Up @@ -111,15 +111,12 @@ fn main() -> Result<()> {
let base_dir = BaseDir::new(args.persistent_base_dir)?;
let mut base_builder = BaseImageBuilder::new(base_dir.path());
base_builder.binaries(args.binaries);
base_builder.extra_packages(args.packages);

if !args.unpackaged {
base_builder.package(args.output, !args.uncompressed);
}

if let Some(flake_dir) = args.flake_dir {
base_builder.flake_dir(flake_dir);
}

if args.shell_exec {
base_builder.shell_exec(true);
}
Expand Down
52 changes: 34 additions & 18 deletions src/image_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ static STATIC_FILES: &[(&str, &str)] =

pub struct BaseImageBuilder {
nix_dir: PathBuf,
flake_dir: Option<PathBuf>,
binaries: Vec<PathBuf>,
extra_packages: Vec<String>,
shell_exec: bool,
package_output: Option<PathBuf>,
compress: bool,
Expand All @@ -61,27 +61,37 @@ impl BaseImageBuilder {
{
BaseImageBuilder {
nix_dir: nix_dir.as_ref().to_owned(),
flake_dir: None,
binaries: Vec::new(),
extra_packages: Vec::new(),
shell_exec: false,
package_output: None,
compress: false,
}
}

pub fn flake_dir<P>(&mut self, flake_dir: P) -> &mut Self
where
P: AsRef<Path>,
{
self.flake_dir.replace(flake_dir.as_ref().to_owned());
pub fn binaries(&mut self, binaries: Vec<PathBuf>) -> &mut Self {
self.binaries = binaries;
self
}

pub fn binaries(&mut self, binaries: Vec<PathBuf>) -> &mut Self {
self.binaries = binaries;
/// Packages added to the default package list
pub fn extra_packages(&mut self, packages: Vec<String>) -> &mut Self {
self.extra_packages = packages;
self
}

/// Default and extra packages, sorted and deduplicated
fn packages(&self) -> Vec<&str> {
let mut packages: Vec<&str> = crate::BASE_PACKAGES
.iter()
.copied()
.chain(self.extra_packages.iter().map(String::as_str))
.collect();
packages.sort();
packages.dedup();
packages
}

pub fn shell_exec(&mut self, shell_exec: bool) -> &mut Self {
self.shell_exec = shell_exec;
self
Expand Down Expand Up @@ -136,6 +146,14 @@ impl BaseImageBuilder {
return Self::POST_PROCESS_FAILED;
}

// package list, listed as built-in by `pkg`
let packages = self.packages().join("\n") + "\n";
hasher.update(&packages);
if let Err(err) = fs::write(crate::BASE_PACKAGES_FILE, packages) {
log::error!("failed to write package list: {err}");
return Self::POST_PROCESS_FAILED;
}

if let Err(err) = self.binaries.iter().try_for_each(|p| {
// TODO: hasher.update(content);
fs::copy(
Expand Down Expand Up @@ -217,15 +235,12 @@ impl BaseImageBuilder {

// build base flake
let flake_dir = Path::new(crate::CACHE_HOME).join("base-flake");
let store_path = match &self.flake_dir {
None => nixos::build_flake_from_package_list(
"debug-shell",
"A debug shell",
crate::BASE_PACKAGES,
&flake_dir,
)?,
Some(flake_dir) => nixos::build_flake(flake_dir)?,
};
let store_path = nixos::build_flake_from_package_list(
"debug-shell",
"A debug shell",
&self.packages(),
&flake_dir,
)?;
symlink_base(&store_path)?;
log::debug!("built base image");

Expand Down Expand Up @@ -280,6 +295,7 @@ impl BaseImageBuilder {
".bin",
".base",
".base.paths",
".base.packages",
".base.sha256",
".base.reginfo",
"etc",
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub(crate) const CONFIG_HOME: &str = "/nix/.config";

pub(crate) const USER_ENV_DIR: &str = "/nix/.env";
pub(crate) const BASE_DIR: &str = "/nix/.base";
pub(crate) const BASE_PACKAGES_FILE: &str = "/nix/.base.packages";
pub(crate) const ETC_DIR: &str = "/nix/etc";

pub(crate) const SSL_CERTS: &str = "/nix/.base/etc/ssl/certs/ca-bundle.crt";
Expand Down
17 changes: 11 additions & 6 deletions src/nixpkgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,16 +240,21 @@ pub fn installed_packages() -> Result<Packages> {
}
}

pub fn builtin_packages() -> Packages {
crate::BASE_PACKAGES
.iter()
.map(|s| Package::new_builtin(s))
.collect()
pub fn builtin_packages() -> Result<Packages> {
let content = match fs::read_to_string(crate::BASE_PACKAGES_FILE) {
Ok(content) => content,
// base images built before the package list was recorded
Err(err) if err.kind() == ErrorKind::NotFound => {
crate::BASE_PACKAGES.join("\n")
}
Err(err) => return Err(err.into()),
};
Ok(content.lines().map(Package::new_builtin).collect())
}

pub fn all_packages_sorted() -> Result<Packages> {
let installed_packages = installed_packages()?;
let builtin_packages = builtin_packages();
let builtin_packages = builtin_packages()?;

Ok(BinaryHeap::from_iter(
builtin_packages.into_iter().chain(installed_packages),
Expand Down
Loading