You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
malachite/src/generate.rs

212 lines
5.8 KiB
Rust

use std::env::set_current_dir;
use std::fs;
use std::path::PathBuf;
use fs_extra::dir;
use futures_util::StreamExt;
use futures_util::TryStreamExt;
use glob::glob;
use gpgme::{Context, Protocol, SignMode};
use names::Generator;
use podman_api::opts::ContainerCreateOpts;
use podman_api::opts::ImageBuildOpts;
use podman_api::Podman;
use crate::config::SecurityConfig;
use crate::errors::AppResult;
use crate::utils::{uid, ShellCommand};
pub const GENERATEFILE: &str = include_str!("../Generatefile");
#[derive(Clone, Debug)]
pub enum GenerateKind {
Podman { image: String },
Host,
}
impl GenerateKind {
pub fn image(&self) -> Option<&str> {
match self {
Self::Podman { image } => Some(image),
Self::Host => None,
}
}
}
pub struct PacmanRepository {
pub kind: GenerateKind,
pub name: String,
pub tempdir: String,
pub out: PathBuf,
pub repo: PathBuf,
pub security: SecurityConfig,
}
impl PacmanRepository {
pub fn new<S: ToString, P: Into<PathBuf>>(
kind: GenerateKind,
name: S,
out: P,
repo: P,
security: SecurityConfig,
) -> Self {
let mut generator = Generator::with_naming(names::Name::Numbered);
Self {
kind,
name: name.to_string(),
tempdir: generator.next().unwrap(),
out: out.into(),
repo: repo.into(),
security,
}
}
pub async fn generate(&self) -> AppResult<()> {
fs::create_dir_all(&self.repo)?;
match &self.kind {
GenerateKind::Podman { .. } => self.podman_generate().await?,
GenerateKind::Host => self.host_generate()?,
}
if self.security.sign {
let packages: Vec<PathBuf> = glob(&format!("{}/*.pkg.tar.*", self.repo.display()))?
.filter_map(Result::ok)
.collect();
let mut gpg_ctx = Context::from_protocol(Protocol::OpenPgp)?;
gpg_ctx.set_armor(true);
for package in packages {
let contents = fs::read(&package)?;
let mut buffer = Vec::new();
gpg_ctx.sign(SignMode::Detached, &contents, &mut buffer)?;
let sigfile = format!("{}.sig", package.display());
fs::write(sigfile, buffer)?;
}
}
Ok(())
}
pub fn host_generate(&self) -> AppResult<()> {
if self.repo.exists() {
fs::remove_dir_all(&self.repo)?;
}
fs::create_dir_all(&self.repo)?;
let generate_path = std::env::temp_dir().join(&self.tempdir);
fs::create_dir_all(&generate_path)?;
let copy_opts = dir::CopyOptions {
content_only: true,
..Default::default()
};
dir::copy(&self.out, &generate_path, &copy_opts)?;
let mlc_dir = std::env::current_dir()?;
set_current_dir(&generate_path)?;
let files: Vec<String> = glob("*.pkg.tar.*")?
.filter_map(Result::ok)
.map(|p| p.display().to_string())
.collect();
ShellCommand::repo_add()
.arg(format!("{}.db.tar.gz", self.name))
.args(files)
.spawn()?
.wait()?;
set_current_dir(mlc_dir)?;
let copy_opts = dir::CopyOptions {
content_only: true,
..Default::default()
};
dir::copy(&generate_path, &self.repo, &copy_opts)?;
fs::remove_dir_all(&generate_path)?;
Ok(())
}
pub async fn podman_generate(&self) -> AppResult<()> {
let uid = uid();
let podman = Podman::new(format!("unix:///run/user/{uid}/podman/podman.sock"))?;
let image = self.kind.image().unwrap();
let generatefile = fs::read_to_string(".mlc/Generatefile")?;
let generatefile_template = liquid::ParserBuilder::with_stdlib()
.build()?
.parse(&generatefile)?;
let generatefile_values = liquid::object!({
"image": image,
"name": &self.name,
"repo": self.repo.file_name().unwrap().to_str().unwrap(),
});
let generatefile = generatefile_template.render(&generatefile_values)?;
let generate_path = std::env::temp_dir().join(&self.tempdir);
fs::create_dir_all(&generate_path)?;
fs::write(generate_path.join("Generatefile"), generatefile)?;
dir::copy(&self.out, &generate_path, &dir::CopyOptions::new())?;
let opts = &ImageBuildOpts::builder(generate_path.to_string_lossy())
.dockerfile("Generatefile")
.tag(&self.tempdir)
.build();
let images = podman.images();
match images.build(opts) {
Ok(mut generate_stream) => {
while let Some(chunk) = generate_stream.next().await {
match chunk {
Ok(chunk) => println!("{chunk:?}"),
Err(e) => eprintln!("{e}"),
}
}
}
Err(e) => eprintln!("{e}"),
};
podman
.containers()
.create(
&ContainerCreateOpts::builder()
.image(&self.tempdir)
.name(&self.tempdir)
.build(),
)
.await?;
let bytes = podman
.containers()
.get(&self.tempdir)
.copy_from(format!(
"/{}",
self.repo.file_name().unwrap().to_str().unwrap()
))
.try_concat()
.await?;
let mut archive = tar::Archive::new(&bytes[..]);
archive.unpack(self.repo.parent().unwrap())?;
podman.images().get(&self.tempdir).remove().await?;
fs::remove_dir_all(&generate_path)?;
Ok(())
}
}