Skip to content

Commit

Permalink
Merge pull request #10 from cvent/git-file-paths
Browse files Browse the repository at this point in the history
Support git file paths, also bump crate versions
  • Loading branch information
jonathanmorley authored Sep 28, 2018
2 parents b20dc74 + db67f9b commit a48c909
Show file tree
Hide file tree
Showing 15 changed files with 1,353 additions and 1,127 deletions.
909 changes: 414 additions & 495 deletions Cargo.lock

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "hogan"
version = "0.2.5"
version = "0.2.6"
authors = ["Jonathan Morley <[email protected]>"]

[[bin]]
Expand All @@ -11,7 +11,7 @@ doc = false
[dependencies]
failure = "0.1"
git2 = "0.7"
handlebars = "0.32"
handlebars = "1"
itertools = "0.7"
json-patch = "0.2"
log = "0.4"
Expand All @@ -29,6 +29,7 @@ walkdir = "2"
zip = "0.4"

[dev-dependencies]
assert_cli = "0.5.4"
dir-diff = "0.3.1"
assert_cmd = "0.9"
dir-diff = "0.3"
fs_extra = "1"
predicates = "0.9"
278 changes: 246 additions & 32 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,88 @@
use failure::Error;
use find_file_paths;
use git;
use git2::Repository;
use json_patch::merge;
use regex::Regex;
use serde_json::{self, Value};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::str::{self, FromStr};
use tempfile::{self, TempDir};
use url::{ParseError, Url};
use walkdir::WalkDir;
use git;

#[derive(Debug, PartialEq)]
pub enum ConfigUrl {
File {
path: PathBuf,
},
Git {
url: Url,
branch: Option<String>,
internal_path: PathBuf,
},
}

impl FromStr for ConfigUrl {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match Url::parse(&s) {
Ok(url) => {
if url.scheme() == "file" {
Ok(ConfigUrl::File {
path: PathBuf::from(s.trim_left_matches("file://")),
})
} else {
let mut path_segments = url
.path_segments()
.ok_or_else(|| format_err!("Url cannot be a base"))?
.map(|segment| segment.to_owned())
.collect::<Vec<String>>();

match path_segments
.iter()
.position(|s| s.ends_with(".git"))
.map(|index| index + 1)
{
Some(git_index) => {
let mut git_url = url.clone();
git_url.set_fragment(None);

let internal_path = if git_index > path_segments.len() {
PathBuf::new()
} else {
let (base_segments, rest) = path_segments.split_at(git_index);

git_url
.path_segments_mut()
.map_err(|_| format_err!("Url cannot be a base"))?
.clear()
.extend(base_segments);

rest.iter().collect()
};

Ok(ConfigUrl::Git {
url: git_url,
branch: url.fragment().map(|f| f.to_owned()),
internal_path,
})
}
None => bail!("Config Url not a file path, and not a .git URL"),
}
}
}
Err(ParseError::RelativeUrlWithoutBase) => if s.contains(".git") {
format!("ssh://{}", str::replace(s, ":", "/"))
} else {
format!("file://{}", s)
}.parse(),
Err(e) => Err(e.into()),
}
}
}

pub enum ConfigDir {
File {
Expand All @@ -23,34 +96,34 @@ pub enum ConfigDir {
}

impl ConfigDir {
pub fn new(src: String, ssh_key_path: &Path) -> Result<ConfigDir, Error> {
let config_dir = if src.contains(".git") {
let git_url = git::GitUrl::new(&src);
let temp_dir = tempfile::tempdir()?;

let git_repo = git_url.clone(temp_dir.path(), Some(ssh_key_path))?;

let directory = match git_repo.workdir() {
Some(workdir) => workdir.join(git_url.internal_path),
None => bail!("No working directory found for git repository"),
};

Ok(ConfigDir::Git {
git_repo,
temp_dir,
directory,
})
} else {
match Url::parse(&src) {
Ok(url) => match url.scheme() {
"file" => ConfigDir::new(src.replacen("file://", "", 1), ssh_key_path),
scheme => bail!("URL scheme {} not yet supported", scheme),
},
Err(ParseError::RelativeUrlWithoutBase) => Ok(ConfigDir::File {
directory: PathBuf::from(src),
}),
Err(e) => Err(e.into()),
pub fn new(url: ConfigUrl, ssh_key_path: &Path) -> Result<ConfigDir, Error> {
let config_dir = match url {
ConfigUrl::Git {
url,
branch,
internal_path,
} => {
let temp_dir = tempfile::tempdir()?;

let git_repo = git::clone(
url.as_str(),
branch.as_ref().map(|x| &**x),
temp_dir.path(),
Some(ssh_key_path),
)?;

let directory = match git_repo.workdir() {
Some(workdir) => workdir.join(internal_path),
None => bail!("No working directory found for git repository"),
};

Ok(ConfigDir::Git {
git_repo,
temp_dir,
directory,
})
}
ConfigUrl::File { path } => Ok(ConfigDir::File { directory: path }),
};

if let &Ok(ref config_dir) = &config_dir {
Expand Down Expand Up @@ -109,8 +182,7 @@ impl ConfigDir {

environment.config_data = config_data;
environment
})
.collect()
}).collect()
}

fn find_environments(&self, filter: Regex) -> Box<Iterator<Item = Environment>> {
Expand Down Expand Up @@ -186,6 +258,148 @@ struct EnvironmentType {
mod tests {
use super::*;
use regex::RegexBuilder;
use std::path::Path;

#[test]
fn test_github_url() {
assert_eq!(
"[email protected]:foo/bar.git".parse::<ConfigUrl>().unwrap(),
ConfigUrl::Git {
url: Url::parse("ssh://[email protected]/foo/bar.git").unwrap(),
branch: None,
internal_path: PathBuf::from(""),
}
);

assert_eq!(
"[email protected]:foo/bar.git/internal/path#branch"
.parse::<ConfigUrl>()
.unwrap(),
ConfigUrl::Git {
url: Url::parse("ssh://[email protected]/foo/bar.git").unwrap(),
branch: Some(String::from("branch")),
internal_path: PathBuf::from("internal/path"),
}
);

assert_eq!(
"https://github.com/foo/bar.git"
.parse::<ConfigUrl>()
.unwrap(),
ConfigUrl::Git {
url: Url::parse("https://github.com/foo/bar.git").unwrap(),
branch: None,
internal_path: PathBuf::from(""),
}
);

assert_eq!(
"https://github.com/foo/bar.git/internal/path#branch"
.parse::<ConfigUrl>()
.unwrap(),
ConfigUrl::Git {
url: Url::parse("https://github.com/foo/bar.git").unwrap(),
branch: Some(String::from("branch")),
internal_path: PathBuf::from("internal/path"),
}
);
}

#[test]
fn test_bitbucket_git_url() {
assert_eq!(
"ssh://[email protected]/foo/bar.git"
.parse::<ConfigUrl>()
.unwrap(),
ConfigUrl::Git {
url: Url::parse("ssh://[email protected]/foo/bar.git").unwrap(),
branch: None,
internal_path: PathBuf::from(""),
}
);

assert_eq!(
"ssh://[email protected]/foo/bar.git/internal/path#branch"
.parse::<ConfigUrl>()
.unwrap(),
ConfigUrl::Git {
url: Url::parse("ssh://[email protected]/foo/bar.git").unwrap(),
branch: Some(String::from("branch")),
internal_path: PathBuf::from("internal/path"),
}
);

assert_eq!(
"https://[email protected]/scm/foo/bar.git"
.parse::<ConfigUrl>()
.unwrap(),
ConfigUrl::Git {
url: Url::parse("https://[email protected]/scm/foo/bar.git").unwrap(),
branch: None,
internal_path: PathBuf::from(""),
}
);

assert_eq!(
"https://[email protected]/scm/foo/bar.git/internal/path#branch"
.parse::<ConfigUrl>()
.unwrap(),
ConfigUrl::Git {
url: Url::parse("https://[email protected]/scm/foo/bar.git").unwrap(),
branch: Some(String::from("branch")),
internal_path: PathBuf::from("internal/path"),
}
);
}

#[test]
fn test_local_path() {
assert_eq!(
"foo/bar/baz".parse::<ConfigUrl>().unwrap(),
ConfigUrl::File {
path: PathBuf::from("foo/bar/baz"),
}
);

assert_eq!(
"/foo/bar/baz".parse::<ConfigUrl>().unwrap(),
ConfigUrl::File {
path: PathBuf::from("/foo/bar/baz"),
}
);

assert_eq!(
"foo/bar.git".parse::<ConfigUrl>().unwrap(),
ConfigUrl::Git {
url: Url::parse("ssh://foo/bar.git").unwrap(),
branch: None,
internal_path: PathBuf::from("")
}
);

assert_eq!(
"foo/bar.git/baz".parse::<ConfigUrl>().unwrap(),
ConfigUrl::Git {
url: Url::parse("ssh://foo/bar.git").unwrap(),
branch: None,
internal_path: PathBuf::from("baz")
}
);

assert_eq!(
"file://foo/bar/baz".parse::<ConfigUrl>().unwrap(),
ConfigUrl::File {
path: PathBuf::from("foo/bar/baz"),
}
);

assert_eq!(
"file://foo/bar.git/baz".parse::<ConfigUrl>().unwrap(),
ConfigUrl::File {
path: PathBuf::from("foo/bar.git/baz"),
}
);
}

#[test]
fn test_basic_triple_merge() {
Expand Down Expand Up @@ -223,7 +437,7 @@ mod tests {
#[test]
fn test_find_all_configs() {
let config_dir = ConfigDir::new(
String::from("file://./tests/fixtures/configs"),
"file://./tests/fixtures/configs".parse().unwrap(),
Path::new(""),
).unwrap();
let environments = config_dir.find(
Expand All @@ -238,7 +452,7 @@ mod tests {
#[test]
fn test_find_subset_configs() {
let config_dir = ConfigDir::new(
String::from("file://./tests/fixtures/configs"),
"file://./tests/fixtures/configs".parse().unwrap(),
Path::new(""),
).unwrap();
let environments = config_dir.find(
Expand Down
Loading

0 comments on commit a48c909

Please sign in to comment.