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.
212 lines
5.8 KiB
Rust
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, ©_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, ©_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/{}/podman/podman.sock", uid))?;
|
|
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(())
|
|
}
|
|
}
|