Skip to content

Commit 94d5ed2

Browse files
authored
Merge pull request #7 from loganintech/feature/6
Added support for setting remote after repo creation. Adds support for ssh formatting remotes.
2 parents a884c2c + a54b313 commit 94d5ed2

File tree

9 files changed

+117
-11
lines changed

9 files changed

+117
-11
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Changelog
22

3-
## [0.4.0] - 2019-05-07
3+
## [0.4.0] - 2019-05-11
4+
### Added
5+
* Added `--ssh_remote_format`, `--set_remote`, and `--remote_name` commands. These are global commands and must be used before the subcommand. Example: `gitpub --ssh_remote_format github -n "name"` Check README for more info.
6+
47
### Changed
58

69
* This version encompasses changes to the CLI interface. It was re-written from structopt to use a clap app.

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,23 @@ Logan Saso <[email protected]>
1212
A small program to create remote git repositories from the command line.
1313
1414
USAGE:
15-
gitpub [OPTIONS] <SUBCOMMAND>
15+
gitpub [FLAGS] [OPTIONS] <SUBCOMMAND>
1616
1717
FLAGS:
18-
-h, --help Prints help information
19-
-V, --version Prints version information
18+
-h, --help Prints help information
19+
--set_remote Sets the remote of the local dir after successful creation.
20+
--ssh_remote_format Attempts to convert the git remote url into ssh format. If it fails (the provider doesn't support ssh format), the remote isn't set.
21+
-V, --version Prints version information
2022
2123
OPTIONS:
22-
--endpoint <endpoint>
24+
--endpoint <endpoint> Sets a custom endpoint to POST to, useful if you want a private instance and know the api matches one gitpub supports.
25+
--remote_name <remote_name> Designates a custom name for setting remote. Defaults to origin.
2326
2427
SUBCOMMANDS:
2528
bitbucket Create a repo on bitbucket.
2629
github Create a repo on github.
2730
gitlab Create a repo on gitlab.
2831
help Prints this message or the help of the given subcommand(s)
29-
3032
```
3133

3234
## Github Setup

src/cli.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,30 @@ pub fn get_app() -> App<'static, 'static> {
4545
.author("Logan Saso <[email protected]>")
4646
.about("A small program to create remote git repositories from the command line.")
4747
.version(env!("CARGO_PKG_VERSION"))
48+
.subcommand(github::subcommand())
49+
.subcommand(gitlab::subcommand())
50+
.subcommand(bitbucket::subcommand())
4851
.arg(
4952
Arg::with_name("endpoint")
5053
.long("endpoint")
51-
.takes_value(true),
54+
.takes_value(true)
55+
.help("Sets a custom endpoint to POST to, useful if you want a private instance and know the api matches one gitpub supports.")
56+
.conflicts_with("set_remote"),
57+
)
58+
.arg(
59+
Arg::with_name("set_remote")
60+
.long("set_remote")
61+
.help("Sets the remote of the local dir after successful creation."),
62+
).arg(
63+
Arg::with_name("remote_name")
64+
.long("remote_name")
65+
.help("Designates a custom name for setting remote. Defaults to origin.")
66+
.default_value("origin")
67+
.hide_default_value(true),
68+
).arg(
69+
Arg::with_name("ssh_remote_format")
70+
.long("ssh_remote_format")
71+
.help("Attempts to convert the git remote url into ssh format. If it fails (the provider doesn't support ssh format), the remote isn't set.")
72+
.conflicts_with("endpoint"),
5273
)
53-
.subcommand(github::subcommand())
54-
.subcommand(gitlab::subcommand())
55-
.subcommand(bitbucket::subcommand())
5674
}

src/git.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use std::env::current_dir;
2+
use std::process::Command;
3+
4+
pub fn add_remote(name: &str, url: &str) -> bool {
5+
if can_create_remote(name) {
6+
if let Ok(dir) = current_dir() {
7+
if dir.ancestors().any(|p| p.join(".git").exists()) {
8+
return Command::new("git")
9+
.arg("remote")
10+
.arg("add")
11+
.arg(name)
12+
.arg(url)
13+
.output()
14+
.is_ok();
15+
}
16+
}
17+
}
18+
19+
false
20+
}
21+
22+
fn can_create_remote(name: &str) -> bool {
23+
if let Ok(out) = Command::new("git")
24+
.arg("remote")
25+
.arg("get-url")
26+
.arg(name)
27+
.output()
28+
{
29+
if let Ok(err) = String::from_utf8(out.stderr) {
30+
return err.starts_with("fatal"); //If it starts with fatal, that means the remote doesn't exist and we can create one
31+
}
32+
}
33+
34+
false
35+
}

src/main.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ use cli::Gitpo;
22
use reqwest::StatusCode;
33
use std::process::exit;
44
mod cli;
5+
mod git;
56
mod provider;
67

8+
use git::add_remote;
9+
710
fn main() -> Result<(), Box<dyn std::error::Error>> {
811
let matches = cli::get_app().get_matches();
912
let config = Gitpo::from_matches(&matches);
@@ -26,7 +29,23 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
2629
match status {
2730
StatusCode::OK | StatusCode::CREATED => {
2831
let apiloc = config.extract_url(&headers);
32+
let (remote_url, can_use_ssh) = match (
33+
config.ssh_url(&headers),
34+
matches.is_present("ssh_remote_format"),
35+
) {
36+
(Some(url), true) => (url, true),
37+
_ => (config.extract_url(&headers), false),
38+
};
2939
println!("Repo created: {}", apiloc);
40+
let remote_name = matches
41+
.value_of("remote_name")
42+
.expect("This should default to origin, so something is wrong.");
43+
if matches.is_present("set_remote")
44+
&& ((matches.is_present("ssh_remote_format") && can_use_ssh) || !can_use_ssh)
45+
&& !add_remote(remote_name, &remote_url)
46+
{
47+
eprintln!("Couldn't set remote.");
48+
}
3049
}
3150
StatusCode::UNPROCESSABLE_ENTITY | StatusCode::BAD_REQUEST => {
3251
eprintln!("The provider had an issue processing this request. Perhaps the repository already exists, or you're using an unsupported option. e.g. Enabling projects on a repo in an org that has them disabled.");

src/provider/bitbucket.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ impl<'a> Provider for BitbucketArgs<'a> {
4949
fn auth_header(&self) -> String {
5050
"Authorization".to_string()
5151
}
52+
53+
fn ssh_url(&self, _: &reqwest::header::HeaderMap) -> Option<String> {
54+
Some(format!(
55+
"[email protected]:{}/{}.git",
56+
&self.username, &self.name
57+
))
58+
}
5259
}
5360

5461
pub fn subcommand() -> App<'static, 'static> {

src/provider/github.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,20 @@ impl<'a> Provider for GithubArgs<'a> {
6060
fn auth_header(&self) -> String {
6161
"Authorization".to_string()
6262
}
63+
64+
fn ssh_url(&self, headers: &reqwest::header::HeaderMap) -> Option<String> {
65+
headers
66+
.get("location")
67+
.and_then(|x| x.to_str().ok())
68+
.map(|src| {
69+
Some({
70+
let mut res = src.replace("https://api.github.com/repos/", "[email protected]:");
71+
res.push_str(".git");
72+
res
73+
})
74+
})
75+
.unwrap_or(None)
76+
}
6377
}
6478

6579
pub fn subcommand() -> App<'static, 'static> {

src/provider/gitlab.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ impl<'a> Provider for GitlabArgs<'a> {
8787
fn auth_header(&self) -> String {
8888
"Private-Token".to_string()
8989
}
90+
91+
fn ssh_url(&self, _: &reqwest::header::HeaderMap) -> Option<String> {
92+
match std::env::var("GITLAB_USERNAME") {
93+
Ok(u) => Some(format!("[email protected]:{}/{}.git", u, self.project_name())),
94+
_ => None,
95+
}
96+
}
9097
}
9198

9299
pub fn subcommand() -> App<'static, 'static> {

src/provider/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ pub mod gitlab;
55
pub trait Provider {
66
fn payload(&self) -> String;
77
fn endpoint(&self) -> String;
8-
fn extract_url(&self, src: &reqwest::header::HeaderMap) -> String;
8+
fn extract_url(&self, _: &reqwest::header::HeaderMap) -> String;
99
fn token(&self) -> String;
1010
fn auth_header(&self) -> String;
11+
fn ssh_url(&self, _: &reqwest::header::HeaderMap) -> Option<String> { None }
1112
}

0 commit comments

Comments
 (0)