wsb 0.275.7622

Workspace tools for every project under $PRJ: git across submodules, template stamping, GitHub, and the workbench
Documentation
//! `wsb update` for one repository, as the daemon runs it: write the version
//! and render every template that is the repository's or names it.

use crate::prelude::*;

use wsb_prj::settings::RepoSettings;

use crate::state::update_version_file_in_repo;

#[derive(Debug, Clone)]
pub struct RepoUpdateResult {
    pub version: String,
    pub rendered_wstemplate_files: Vec<String>,
}

/// Update the repository wsb manages at `repo_root`.
pub fn run_repo_update(repo_root: &Path) -> Result<RepoUpdateResult> {
    let settings = RepoSettings::require(repo_root)?;
    let cache = crate::state::RunCache::new();
    let version_info = crate::state::stamped_version(repo_root, &settings, &cache, None)?;

    update_version_file_in_repo(&version_info, &settings.version, repo_root)?;

    let engine = crate::state::wstemplate::WstemplateEngine::for_repo(&settings, version_info.clone(), repo_root, cache);
    let rendered_wstemplate_files = engine
        .render_relevant()?
        .into_iter()
        .map(|item| {
            item.output_path
                .strip_prefix(repo_root)
                .unwrap_or(item.output_path.as_path())
                .display()
                .to_string()
        })
        .collect::<Vec<_>>();

    Ok(RepoUpdateResult {
        version: version_info.full_version,
        rendered_wstemplate_files,
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;
    use tempfile::TempDir;

    fn run_git(repo_root: &Path, args: &[&str]) {
        let repo = match args {
            ["init"] => crate::git_backend::init_repository(repo_root).unwrap(),
            _ => crate::git_backend::open_repo(repo_root).unwrap(),
        };

        match args {
            ["init"] => {}
            ["config", "user.name", name] => {
                let email = repo
                    .config_snapshot()
                    .string("user.email")
                    .map(|value| value.to_string())
                    .unwrap_or_else(|| "test@example.com".to_string());
                crate::git_backend::configure_local_user(&repo, name, &email).unwrap();
            }
            ["config", "user.email", email] => {
                let name = repo
                    .config_snapshot()
                    .string("user.name")
                    .map(|value| value.to_string())
                    .unwrap_or_else(|| "Test User".to_string());
                crate::git_backend::configure_local_user(&repo, &name, email).unwrap();
            }
            ["add", path] => {
                crate::git_backend::stage_paths(&repo, &[path.to_string()]).unwrap();
            }
            ["commit", "-m", message] => {
                crate::git_backend::commit_all(&repo, message).unwrap();
            }
            _ => panic!("unsupported git test helper command: {args:?}"),
        }
    }

    /// An update writes the version and the renders into the repository it
    /// is given, at the major its settings commit — not into the process's
    /// working directory.
    #[test]
    fn run_repo_update_writes_into_the_repo_it_is_given() -> Result<()> {
        let temp_dir = TempDir::new().unwrap();
        let repo_root = temp_dir.path();

        run_git(repo_root, &["init"]);
        run_git(repo_root, &["config", "user.name", "Test User"]);
        run_git(repo_root, &["config", "user.email", "test@example.com"]);

        RepoSettings {
            name: "Example".to_string(),
            alias: "example".to_string(),
            templates_root: ".".to_string(),
            version: wsb_prj::settings::VersionSettings::at_major(5),
        }
        .save(repo_root)?;
        fs::write(repo_root.join("README.md"), "hello\n").unwrap();
        fs::write(repo_root.join("out.txt.wstemplate"), "{{ project.version }}\n").unwrap();
        run_git(repo_root, &["add", "README.md"]);
        run_git(repo_root, &["add", ".wsb/repo.json"]);
        run_git(repo_root, &["add", "out.txt.wstemplate"]);
        run_git(repo_root, &["commit", "-m", "initial"]);
        fs::write(repo_root.join("README.md"), "changed\n").unwrap();

        let original_dir = std::env::current_dir().unwrap();
        let unrelated_dir = TempDir::new().unwrap();
        std::env::set_current_dir(unrelated_dir.path()).unwrap();
        let result = run_repo_update(repo_root);
        std::env::set_current_dir(original_dir).unwrap();
        let result = result?;

        // One commit and a change of the tree's own: the version is the one
        // the commit about to be made will have — the second commit, with the
        // first's three files and the change it makes behind it.
        assert_eq!(result.version, "5.1.4");
        assert_eq!(fs::read_to_string(repo_root.join("version.txt")).unwrap().trim(), "5.1.4");
        assert_eq!(fs::read_to_string(repo_root.join("out.txt")).unwrap().trim(), "5.1.4");
        assert_eq!(result.rendered_wstemplate_files, vec!["out.txt".to_string()]);
        Ok(())
    }
}