124 lines
4.8 KiB
Rust
124 lines
4.8 KiB
Rust
|
|
use anyhow::{Context, Result, bail};
|
||
|
|
use std::path::{Path, PathBuf};
|
||
|
|
use std::process::Command;
|
||
|
|
|
||
|
|
/// Copies the built package into `repo_dir` and runs `repo-add` against
|
||
|
|
/// `<repo_name>.db.tar.gz` there.
|
||
|
|
///
|
||
|
|
/// `repo_dir`/`repo_name` are expected to already be a real, registered
|
||
|
|
/// pacman repo (see SPEC.md > Architecture > Publisher and
|
||
|
|
/// `/etc/pacman.conf`'s `[custom]` section on this box) — pkgwatch doesn't
|
||
|
|
/// create the repo or touch pacman.conf, only adds packages to an
|
||
|
|
/// already-registered one. Getting the new version into an installed
|
||
|
|
/// system is a separate, deliberate `pacman -Syu`/`pacman -S` step left to
|
||
|
|
/// the operator, not run automatically here.
|
||
|
|
pub fn publish(package_path: &Path, repo_dir: &Path, repo_name: &str) -> Result<PathBuf> {
|
||
|
|
publish_with(Path::new("repo-add"), package_path, repo_dir, repo_name)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// `repo_add_bin` is injectable so tests can point it at a stub script
|
||
|
|
/// instead of the real `repo-add` (or a mutated global `PATH`, which would
|
||
|
|
/// race with `cargo test`'s parallel test threads).
|
||
|
|
fn publish_with(
|
||
|
|
repo_add_bin: &Path,
|
||
|
|
package_path: &Path,
|
||
|
|
repo_dir: &Path,
|
||
|
|
repo_name: &str,
|
||
|
|
) -> Result<PathBuf> {
|
||
|
|
std::fs::create_dir_all(repo_dir)
|
||
|
|
.with_context(|| format!("creating repo dir {}", repo_dir.display()))?;
|
||
|
|
let file_name = package_path
|
||
|
|
.file_name()
|
||
|
|
.context("built package path has no filename")?;
|
||
|
|
let dest = repo_dir.join(file_name);
|
||
|
|
std::fs::copy(package_path, &dest)
|
||
|
|
.with_context(|| format!("copying {} to {}", package_path.display(), dest.display()))?;
|
||
|
|
|
||
|
|
let db_path = repo_dir.join(format!("{repo_name}.db.tar.gz"));
|
||
|
|
let status = Command::new(repo_add_bin)
|
||
|
|
.arg(&db_path)
|
||
|
|
.arg(&dest)
|
||
|
|
.status()
|
||
|
|
.with_context(|| {
|
||
|
|
format!(
|
||
|
|
"running {} (is pacman-contrib installed?)",
|
||
|
|
repo_add_bin.display()
|
||
|
|
)
|
||
|
|
})?;
|
||
|
|
if !status.success() {
|
||
|
|
bail!("repo-add failed for {}", dest.display());
|
||
|
|
}
|
||
|
|
Ok(dest)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use super::*;
|
||
|
|
|
||
|
|
fn write_stub(dir: &Path, name: &str, script: &str) -> PathBuf {
|
||
|
|
let path = dir.join(name);
|
||
|
|
std::fs::write(&path, format!("#!/bin/sh\n{script}\n")).unwrap();
|
||
|
|
let mut perms = std::fs::metadata(&path).unwrap().permissions();
|
||
|
|
std::os::unix::fs::PermissionsExt::set_mode(&mut perms, 0o755);
|
||
|
|
std::fs::set_permissions(&path, perms).unwrap();
|
||
|
|
path
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn publish_copies_package_and_invokes_repo_add() {
|
||
|
|
let stub_dir = tempfile::tempdir().unwrap();
|
||
|
|
let log_path = stub_dir.path().join("invoked_with.txt");
|
||
|
|
let repo_add = write_stub(
|
||
|
|
stub_dir.path(),
|
||
|
|
"fake-repo-add",
|
||
|
|
&format!("echo \"$@\" > {}", log_path.display()),
|
||
|
|
);
|
||
|
|
|
||
|
|
let src_dir = tempfile::tempdir().unwrap();
|
||
|
|
let package_path = src_dir.path().join("thing-1.0.0-1-x86_64.pkg.tar.zst");
|
||
|
|
std::fs::write(&package_path, b"pkg-bytes").unwrap();
|
||
|
|
|
||
|
|
let repo_dir = tempfile::tempdir().unwrap();
|
||
|
|
let dest = publish_with(&repo_add, &package_path, repo_dir.path(), "custom").unwrap();
|
||
|
|
|
||
|
|
assert_eq!(
|
||
|
|
dest,
|
||
|
|
repo_dir.path().join("thing-1.0.0-1-x86_64.pkg.tar.zst")
|
||
|
|
);
|
||
|
|
assert_eq!(std::fs::read(&dest).unwrap(), b"pkg-bytes");
|
||
|
|
|
||
|
|
let invoked_with = std::fs::read_to_string(&log_path).unwrap();
|
||
|
|
assert!(invoked_with.contains("custom.db.tar.gz"));
|
||
|
|
assert!(invoked_with.contains("thing-1.0.0-1-x86_64.pkg.tar.zst"));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn publish_errors_when_repo_add_fails() {
|
||
|
|
let stub_dir = tempfile::tempdir().unwrap();
|
||
|
|
let repo_add = write_stub(stub_dir.path(), "fake-repo-add-fail", "exit 1");
|
||
|
|
|
||
|
|
let src_dir = tempfile::tempdir().unwrap();
|
||
|
|
let package_path = src_dir.path().join("thing-1.0.0-1-x86_64.pkg.tar.zst");
|
||
|
|
std::fs::write(&package_path, b"pkg-bytes").unwrap();
|
||
|
|
|
||
|
|
let repo_dir = tempfile::tempdir().unwrap();
|
||
|
|
let err = publish_with(&repo_add, &package_path, repo_dir.path(), "custom").unwrap_err();
|
||
|
|
assert!(err.to_string().contains("repo-add failed"));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn publish_creates_repo_dir_if_missing() {
|
||
|
|
let stub_dir = tempfile::tempdir().unwrap();
|
||
|
|
let repo_add = write_stub(stub_dir.path(), "fake-repo-add-ok", "exit 0");
|
||
|
|
|
||
|
|
let src_dir = tempfile::tempdir().unwrap();
|
||
|
|
let package_path = src_dir.path().join("thing-1.0.0-1-x86_64.pkg.tar.zst");
|
||
|
|
std::fs::write(&package_path, b"pkg-bytes").unwrap();
|
||
|
|
|
||
|
|
let parent = tempfile::tempdir().unwrap();
|
||
|
|
let repo_dir = parent.path().join("nested/repo");
|
||
|
|
publish_with(&repo_add, &package_path, &repo_dir, "custom").unwrap();
|
||
|
|
assert!(repo_dir.join("thing-1.0.0-1-x86_64.pkg.tar.zst").exists());
|
||
|
|
}
|
||
|
|
}
|