Skip to content

Commit

Permalink
feat: enable building base image with qemu-user-static
Browse files Browse the repository at this point in the history
  • Loading branch information
raphaelcoeffic committed Nov 5, 2024
1 parent 5fda0b0 commit 4e0e401
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 14 deletions.
17 changes: 13 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,31 @@ env:

jobs:
build:
name: Build - ${{ matrix.platform.os-name }}
name: Build - ${{ matrix.platform.arch }}
strategy:
matrix:
platform:
- os-name: Linux-x86_64
runs-on: ubuntu-latest
- arch: x86_64
target: x86_64-unknown-linux-musl
runs-on: ubuntu-latest

- arch: aarch64
target: aarch64-unknown-linux-musl
runs-on: ubuntu-latest

runs-on: ${{ matrix.platform.runs-on }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Build base image
env:
ARCH: ${{ matrix.platform.arch }}
run: |
cargo run --bin build-img
if [ "$ARCH" != "$(uname -m)" ]; then
apt-get install qemu-user-static
fi
cargo run --bin build-img -- -a $ARCH
cargo clean
- name: Upload artifacts - base image
Expand Down
28 changes: 20 additions & 8 deletions image_builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ impl BaseImageBuilder {
}

pub fn build_base(&self) -> Result<()> {
install_nix(&self.nix_dir)?;
self.build_base_with_arch(ARCH)
}

pub fn build_base_with_arch(&self, arch: &str) -> Result<()> {
install_nix(arch, &self.nix_dir)?;
log::info!("building base image");

let tmp = tempdir()?;
Expand Down Expand Up @@ -208,10 +212,17 @@ impl BaseImageBuilder {
tar_cmd.args(["-I", "xz -T0"]);
}

// prefer native tar over emulated one
let current_path = std::env::var_os("PATH")
.filter(|path| !path.is_empty())
.map(|path| path.to_string_lossy().into_owned() + ":")
.unwrap_or_default();
let path_env = format!("{current_path}/nix/.base/bin");

tar_cmd
.args([".bin", ".base", "etc", "var/nix"])
.args(nix_set.union(&base_set).map(|p| "store/".to_owned() + p))
.env("PATH", "/nix/.base/bin");
.env("PATH", path_env);

let tar_status = tar_cmd.status()?;

Expand All @@ -227,9 +238,9 @@ impl BaseImageBuilder {
}
}

fn nix_installer_url(version: &str) -> String {
fn nix_installer_url(version: &str, arch: &str) -> String {
const NIX_BASE_URL: &str = "https://releases.nixos.org/nix";
format!("{NIX_BASE_URL}/nix-{version}/nix-{version}-{ARCH}-linux.tar.xz")
format!("{NIX_BASE_URL}/nix-{version}/nix-{version}-{arch}-linux.tar.xz")
}

fn write_nix_paths(nix_dir: &Path) -> Result<(), io::Error> {
Expand Down Expand Up @@ -317,6 +328,7 @@ pub fn progress_bar(len: u64) -> ProgressBar {
/// Download and install Nix.
fn download_and_install_nix(
version: &str,
arch: &str,
url: &str,
dest: &Path,
) -> Result<()> {
Expand All @@ -331,7 +343,7 @@ fn download_and_install_nix(
fs::create_dir_all(&store_dir)?;

// unpack files
let tar_prefix = format!("nix-{version}-{ARCH}-linux");
let tar_prefix = format!("nix-{version}-{arch}-linux");
for file in ar.entries()? {
let mut f = file?;
let fpath = f.path()?;
Expand Down Expand Up @@ -370,7 +382,7 @@ fn find_nix(store_dir: &Path, version: &str) -> Result<PathBuf> {
}

/// Install Nix into `dest`
fn install_nix<P>(dest: P) -> Result<()>
fn install_nix<P>(arch: &str, dest: P) -> Result<()>
where
P: AsRef<Path>,
{
Expand All @@ -385,8 +397,8 @@ where
let nix_store = dest.join("store");
if !nix_store.exists() {
log::info!("installing Nix in {}", dest.display());
let nix_url = nix_installer_url(NIX_VERSION);
download_and_install_nix(NIX_VERSION, &nix_url, dest)?;
let nix_url = nix_installer_url(NIX_VERSION, arch);
download_and_install_nix(NIX_VERSION, arch, &nix_url, dest)?;
}

let nix_bin = dest.join(".bin");
Expand Down
26 changes: 24 additions & 2 deletions src/bin/build-img.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
path::{Path, PathBuf},
};

use anyhow::Result;
use anyhow::{bail, Result};
use clap::Parser;

use image_builder::*;
Expand All @@ -20,6 +20,10 @@ struct Args {
#[arg(short, long, env)]
flake_dir: Option<PathBuf>,

/// Architecture
#[arg(short, long, env)]
arch: Option<String>,

/// Compress base image
#[arg(short, long, env)]
uncompressed: bool,
Expand Down Expand Up @@ -71,6 +75,14 @@ impl Drop for BaseDir {
}
}

fn is_native_arch(arch: &str) -> bool {
arch == std::env::consts::ARCH
}

fn is_qemu_supported_arch(arch: &str) -> bool {
fs::exists(format!("/proc/sys/fs/binfmt_misc/qemu-{arch}")).is_ok_and(|x| x)
}

fn init_logging() {
env_logger::Builder::new()
.filter_level(log::LevelFilter::Info)
Expand All @@ -92,5 +104,15 @@ fn main() -> Result<()> {
base_builder.flake_dir(flake_dir);
}

base_builder.build_base()
if let Some(arch) = args.arch {
if !is_native_arch(&arch) && !is_qemu_supported_arch(&arch) {
bail!(
"{arch} does not seem to be supported. \
Try installing 'qemu-user-static' to enable support."
)
}
base_builder.build_base_with_arch(&arch)
} else {
base_builder.build_base()
}
}

0 comments on commit 4e0e401

Please sign in to comment.