1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use anyhow::{anyhow, Result};
use tokio::process;

/// Execute a git command
async fn git(args: &[&str]) -> Result<String> {
    println!("Running $ git {}", args.join(" "));
    let output = process::Command::new("git").args(args).output().await?;
    if output.status.success() {
        Ok(String::from_utf8(output.stdout)?)
    } else {
        Err(anyhow!(
            "Running git {} failed: {}",
            args.join(" "),
            output
                .status
                .code()
                .map_or_else(|| "unknown".to_string(), |c| c.to_string())
        ))
    }
}

/// Get the sha1 of the specified ref
async fn sha1(ref_: &str) -> Result<String> {
    git(&["rev-parse", ref_])
        .await
        .map(|s| s.trim().to_string())
}

/// What are the changed files between two commits?
async fn changes(first: &str, second: &str) -> Result<Vec<String>> {
    let res = tokio::try_join!(sha1(first), sha1(second));
    let (first_sha1, second_sha1) = match res {
        Ok((first_sha1, second_sha1)) => (first_sha1, second_sha1),
        Err(e) => return Err(e),
    };
    Ok(git(&["diff", "--name-only", &first_sha1, &second_sha1])
        .await?
        .trim()
        .split('\n')
        .map(|s| s.to_string())
        .collect())
}

/// Fetch updates to the config repo, identify which are changes
/// to channels and then actually pull it.
pub async fn pull() -> Result<Vec<String>> {
    // Fetch remote updates
    git(&["fetch"]).await?;
    // Identify changes to channel configs
    let changed = changes("HEAD", "origin/master")
        .await?
        .iter()
        // Turn "channels/foo.toml" -> "#foo"
        .filter_map(|file| {
            if file.starts_with("channels/") && file.ends_with(".toml") {
                Some(format!(
                    "#{}",
                    file.trim_start_matches("channels/")
                        .trim_end_matches(".toml")
                ))
            } else {
                None
            }
        })
        .collect();
    // Now actually pull the repo!
    // TODO: race condition if a commit is merged between fetch and pull?
    git(&["pull"]).await?;
    Ok(changed)
}