pray-cli 1.16.0

Package manager for the language placed before inference — Prayfile CLI (binary: pray)
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Output};

pub struct UpdateFixture {
    pub root: PathBuf,
    pub consumer: PathBuf,
}

impl UpdateFixture {
    pub fn new(constraint: &str) -> Self {
        let stamp = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_nanos();
        let root = std::env::temp_dir().join(format!("pray-update-{stamp}"));
        let consumer = root.join("consumer");
        for directory in ["source/package/exports", "distribution", "consumer/rules"] {
            fs::create_dir_all(root.join(directory)).unwrap();
        }
        let fixture = Self { root, consumer };
        fs::write(
            fixture.root.join("source/Prayfile"),
            "prayfile \"1\"\npray \"sample/files\", \">= 1.0\", path: \"package\"\n",
        )
        .unwrap();
        fixture.git(&["init", "-b", "main"]);
        fixture.git(&["config", "user.name", "Pray Test"]);
        fixture.git(&["config", "user.email", "pray@example.invalid"]);
        fixture.publish("1.0.0");
        fs::write(
            fixture.consumer.join("rules/rules.prayspec"),
            r#"
Package::Specification.new do |spec|
  spec.name = "sample/rules"
  spec.version = "1.0.0"
  spec.files = ["rules.md"]
  spec.exports = { "rules" => { type: "fragment", path: "rules.md" } }
end
"#,
        )
        .unwrap();
        fs::write(fixture.consumer.join("rules/rules.md"), "old rules\n").unwrap();
        fs::write(
            fixture.consumer.join("Prayfile"),
            format!(
                r#"
prayfile "1"
source "dist", "git+file://{}"
tree "skills" do
  pray "sample/files", "{constraint}", source: "dist"
end
compose "INSTRUCTIONS.md" do
  pray "sample/rules", "~> 1.0", path: "rules"
end
"#,
                fixture.root.join("distribution").display()
            ),
        )
        .unwrap();
        fixture.success(&["install"]);
        fixture
    }

    pub fn publish(&self, version: &str) {
        fs::write(
            self.root.join("source/package/files.prayspec"),
            format!(
                r#"
Package::Specification.new do |spec|
  spec.name = "sample/files"
  spec.version = "{version}"
  spec.summary = "fixture"
  spec.files = ["exports/a.md", "exports/b.md"]
  spec.exports = {{ "files" => {{ type: "folder", path: "exports" }} }}
end
"#
            ),
        )
        .unwrap();
        for name in ["a.md", "b.md"] {
            fs::write(self.root.join("source/package/exports").join(name), version).unwrap();
        }
        let result = self.run_at(
            &self.root.join("source"),
            &[
                "publish",
                "--root",
                self.root.join("distribution/prayers").to_str().unwrap(),
            ],
        );
        assert!(
            result.status.success(),
            "{}",
            String::from_utf8_lossy(&result.stderr)
        );
        self.git(&["add", "-A"]);
        self.git(&["commit", "-m", version]);
    }

    pub fn omit_ledger(&self) {
        let path = self.consumer.join("Prayfile.lock");
        let mut lockfile = pray_core::lockfile::read_lockfile(&path).unwrap();
        lockfile.provisioned.clear();
        lockfile.generated_by = "pray 1.9.1".into();
        pray_core::lockfile::write_lockfile(&path, &lockfile).unwrap();
    }

    pub fn run(&self, arguments: &[&str]) -> Output {
        self.run_at(&self.consumer, arguments)
    }

    pub fn success(&self, arguments: &[&str]) -> Output {
        let output = self.run(arguments);
        assert!(
            output.status.success(),
            "{}",
            String::from_utf8_lossy(&output.stderr)
        );
        output
    }

    fn run_at(&self, directory: &Path, arguments: &[&str]) -> Output {
        Command::new(env!("CARGO_BIN_EXE_pray"))
            .args(arguments)
            .current_dir(directory)
            .env_remove("PRAY_PATH")
            .env_remove("PRAY_FILE_PATH")
            .env_remove("PRAY_ENV")
            .env("PRAY_HOME", self.root.join("home"))
            .env("PRAY_CACHE", self.root.join("cache"))
            .env("PRAY_TRUST_ASSUME_YES", "1")
            .output()
            .unwrap()
    }

    fn git(&self, arguments: &[&str]) {
        let output = Command::new("git")
            .args([
                "-c",
                "commit.gpgsign=false",
                "-c",
                "core.hooksPath=/dev/null",
            ])
            .args(arguments)
            .current_dir(self.root.join("distribution"))
            .output()
            .unwrap();
        assert!(
            output.status.success(),
            "{}",
            String::from_utf8_lossy(&output.stderr)
        );
    }
}

impl Drop for UpdateFixture {
    fn drop(&mut self) {
        let _ = fs::remove_dir_all(&self.root);
    }
}