-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 139dba2
Showing
9 changed files
with
338 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
target |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "cargo-release" | ||
version = "0.1.0-pre" | ||
authors = ["Ning Sun <[email protected]>"] | ||
|
||
[dependencies] | ||
toml = "0.1.27" | ||
semver = "0.2.3" | ||
quick-error = "0.1.3" | ||
regex = "0.1.55" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
use cmd::call; | ||
use error::FatalError; | ||
|
||
pub fn publish() -> Result<bool, FatalError> { | ||
call(vec!["cargo", "publish"]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
use std::process::{Command}; | ||
|
||
use error::FatalError; | ||
|
||
pub fn call(command: Vec<&str>) -> Result<bool, FatalError> { | ||
let mut iter = command.iter(); | ||
let cmd_name = iter.next().unwrap(); | ||
|
||
let mut cmd = Command::new(cmd_name); | ||
|
||
for arg in iter { | ||
cmd.arg(arg); | ||
} | ||
|
||
let mut child = try!(cmd.spawn().map_err(FatalError::from)); | ||
let result = try!(child.wait().map_err(FatalError::from)); | ||
|
||
Ok(result.success()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
use std::io::prelude::*; | ||
use std::io; | ||
use std::io::BufReader; | ||
use std::fs::{self, File}; | ||
use std::path::Path; | ||
|
||
use toml::{Parser, Value, Table}; | ||
use semver::Version; | ||
use regex::Regex; | ||
|
||
use error::FatalError; | ||
|
||
fn load_from_file(path: &Path) -> io::Result<String> { | ||
let mut file = try!(File::open(path)); | ||
let mut s = String::new(); | ||
try!(file.read_to_string(&mut s)); | ||
Ok(s) | ||
} | ||
|
||
fn save_to_file(path: &Path, content: &str) -> io::Result<()> { | ||
let mut file = try!(File::create(path)); | ||
try!(file.write_all(&content.as_bytes())); | ||
Ok(()) | ||
} | ||
|
||
pub fn parse_cargo_config() -> Result<Table, FatalError> { | ||
let cargo_file_path = Path::new("Cargo.toml"); | ||
|
||
let cargo_file_content = try!(load_from_file(&cargo_file_path) | ||
.map_err(FatalError::from)); | ||
|
||
let mut parser = Parser::new(&cargo_file_content); | ||
|
||
match parser.parse() { | ||
Some(toml) => Ok(toml), | ||
None => Err(FatalError::InvalidCargoFileFormat) | ||
} | ||
} | ||
|
||
pub fn save_cargo_config(config: &Table) -> Result<(), FatalError> { | ||
let cargo_file_path = Path::new("Cargo.toml"); | ||
|
||
let serialized_data = format!("{}", Value::Table(config.clone())); | ||
|
||
try!(save_to_file(&cargo_file_path, &serialized_data).map_err(FatalError::from)); | ||
Ok(()) | ||
} | ||
|
||
pub fn rewrite_cargo_version(version: &str) -> Result<(), FatalError> { | ||
{ | ||
let section_matcher = Regex::new("^\\[.+\\]").unwrap(); | ||
let file_in = try!(File::open("Cargo.toml").map_err(FatalError::from)); | ||
let mut bufreader = BufReader::new(file_in); | ||
let mut line = String::new(); | ||
|
||
let mut file_out = try!(File::create("Cargo.toml.work").map_err(FatalError::from)); | ||
let mut in_package = false; | ||
|
||
loop { | ||
let b = try!(bufreader.read_line(&mut line).map_err(FatalError::from)); | ||
if b <= 0 { | ||
break; | ||
} | ||
|
||
if section_matcher.is_match(&line) { | ||
in_package = line.trim() == "[package]"; | ||
} | ||
|
||
if in_package && line.starts_with("version") { | ||
line = format!("version = \"{}\"\n", version); | ||
} | ||
|
||
try!(file_out.write_all(line.as_bytes()).map_err(FatalError::from)); | ||
line.clear(); | ||
} | ||
} | ||
|
||
try!(fs::rename("Cargo.toml.work", "Cargo.toml")); | ||
|
||
Ok(()) | ||
} | ||
|
||
pub fn parse_version(version: &str) -> Result<Version, FatalError> { | ||
Version::parse(version).map_err(|e| FatalError::from(e)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use std::io::{Error as IOError}; | ||
use semver::SemVerError; | ||
|
||
quick_error! { | ||
#[derive(Debug)] | ||
pub enum FatalError { | ||
IOError(err: IOError) { | ||
from() | ||
cause(err) | ||
} | ||
InvalidCargoFileFormat { | ||
display("Invalid cargo file format") | ||
description("Invalid cargo file format") | ||
} | ||
SemVerError(err: SemVerError) { | ||
from() | ||
cause(err) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
use std::process::{Command}; | ||
|
||
use cmd::call; | ||
use error::FatalError; | ||
|
||
pub fn status() -> Result<bool, FatalError> { | ||
let output = try!(Command::new("git") | ||
.arg("diff").arg("--exit-code") | ||
.output().map_err(FatalError::from)); | ||
Ok(output.status.success()) | ||
} | ||
|
||
pub fn commit_all(msg: &str) -> Result<bool, FatalError> { | ||
call(vec!["git", "commit", "-am", msg]) | ||
} | ||
|
||
pub fn tag(name: &str) -> Result<bool, FatalError> { | ||
call(vec!["git", "tag", name]) | ||
} | ||
|
||
pub fn push() -> Result<bool, FatalError> { | ||
call(vec!["git", "push", "--follow-tags"]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
#![allow(dead_code)] | ||
|
||
#[macro_use] extern crate quick_error; | ||
extern crate regex; | ||
extern crate toml; | ||
extern crate semver; | ||
|
||
use std::process::exit; | ||
|
||
use semver::Identifier; | ||
|
||
mod config; | ||
mod error; | ||
mod cmd; | ||
mod git; | ||
mod cargo; | ||
|
||
fn execute() -> Result<i32, error::FatalError> { | ||
|
||
// STEP 0: Check if working directory is clean | ||
let git_clean = try!(git::status()); | ||
if git_clean { | ||
println!("Working directory is clean"); | ||
} else { | ||
println!("Uncommitted changes detected, please commit before release"); | ||
return Ok(128); | ||
} | ||
|
||
// STEP 1: Read version from Cargo.toml and remove | ||
let result = try!(config::parse_cargo_config()); | ||
|
||
let mut version = result.get("package") | ||
.and_then(|f| f.as_table()) | ||
.and_then(|f| f.get("version")) | ||
.and_then(|f| f.as_str()) | ||
.and_then(|f| config::parse_version(f).ok()) | ||
.unwrap(); | ||
|
||
// STEP 2: Remove pre extension, save and commit | ||
if version.is_prerelease() { | ||
version.pre.clear(); | ||
let new_version_string = version.to_string(); | ||
try!(config::rewrite_cargo_version(&new_version_string)); | ||
|
||
let commit_msg = format!("(cargo-release) version {}", new_version_string); | ||
if !try!(git::commit_all(&commit_msg)) { | ||
// commit failed, abort release | ||
return Ok(128); | ||
} | ||
} | ||
|
||
// STEP 3: Tag | ||
let current_version = version.to_string(); | ||
if !try!(git::tag(¤t_version)) { | ||
// tag failed, abort release | ||
return Ok(128); | ||
} | ||
|
||
// STEP 4: cargo publish | ||
if !try!(cargo::publish()) { | ||
return Ok(128); | ||
} | ||
|
||
// STEP 5: bump version | ||
version.increment_patch(); | ||
version.pre.push(Identifier::AlphaNumeric("pre".to_owned())); | ||
println!("Starting next development cycle {}", version); | ||
let updated_version_string = version.to_string(); | ||
try!(config::rewrite_cargo_version(&updated_version_string)); | ||
let commit_msg = format!("(cargo-release) start next development cycle {}", | ||
updated_version_string); | ||
if !try!(git::commit_all(&commit_msg)) { | ||
return Ok(128); | ||
} | ||
|
||
// STEP 6: git push | ||
if !try!(git::push()) { | ||
return Ok(128); | ||
} | ||
Ok(0) | ||
} | ||
|
||
fn main() { | ||
match execute() { | ||
Ok(code) => exit(code), | ||
Err(e) => { | ||
println!("Fatal: {}", e); | ||
exit(128); | ||
} | ||
} | ||
} |