extern crate toml;
extern crate serde;
extern crate zip;
extern crate walkdir;
extern crate structopt;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate release_manager;
use std::path::Path;
use std::fs::{self, File};
use std::io::{Read, Write};
use std::process::exit;
use std::env;
use toml::Value;
use walkdir::WalkDir;
use zip::ZipWriter;
use zip::write::FileOptions;
use structopt::StructOpt;
use log::{LogLevelFilter, LogRecord};
use env_logger::LogBuilder;
use release_manager::{Config, ConfigState, ConfigTrait, Error, Opt, StatusWrapper};
use release_manager::{parse_toml, publish, table_str};
fn zip_directory(result_path: &str, zip_contents_path: &str, zip_name: &str) -> Result<(), Error> {
if !Path::new(zip_contents_path).is_dir() {
return Err(Error::ZipPath);
}
let path_str = format!("{}/{}", result_path, zip_name);
let path = Path::new(&path_str);
let file = File::create(&path)?;
let mut zip = ZipWriter::new(file);
let options = FileOptions::default()
.compression_method(zip::CompressionMethod::Deflated)
.unix_permissions(0o755);
let walkdir = WalkDir::new(zip_contents_path);
for dent in walkdir.into_iter().filter_map(|e| e.ok()) {
let path = dent.path();
let name = path.strip_prefix(Path::new(zip_contents_path))?
.to_str()
.ok_or(Error::ZipPath)?;
if path.is_file() {
zip.start_file(name, options)?;
let mut f = File::open(path)?;
let mut buffer = Vec::new();
f.read_to_end(&mut buffer)?;
zip.write_all(&*buffer)?;
}
}
zip.finish()?;
Ok(())
}
fn do_main() -> Result<(), Error> {
let opt = Opt::from_args();
let mut log_builder = LogBuilder::new();
log_builder.format(|record: &LogRecord| format!("{}", record.args()));
log_builder.filter(
Some("release_manager"),
if opt.verbose() {
LogLevelFilter::Debug
} else {
LogLevelFilter::Info
},
);
if let Ok(ref rust_log) = env::var("RUST_LOG") {
log_builder.parse(rust_log);
}
log_builder.init().unwrap();
let rc = opt.release_config();
let release_path = if let Some(ref rc) = rc {
Path::new(rc)
} else {
Path::new("Release.toml")
};
let cargo_path = Path::new("Cargo.toml");
let (config, state) = Config::new(&release_path)?;
if state == ConfigState::Upgraded {
config.save(&release_path)?;
}
let cargo: Value = parse_toml(&cargo_path)?;
let sf = opt.status_file();
let status_path = if let Some(ref sf) = sf {
Path::new(sf)
} else {
Path::new("Status.toml")
};
let mut status = StatusWrapper::new(&status_path);
let _ = status.read();
let package = cargo.get("package").ok_or(Error::PackageMissing)?;
let name = table_str(package, "name", Error::NameMissing)?;
let version = table_str(package, "version", Error::VersionMissing)?;
if status.is_published(version) && opt.publish() {
return Err(Error::RePublish);
}
let full_release_path_string = format!("{}/{}/{}", &config.release_path, &name, version);
let full_release_path = Path::new(&full_release_path_string);
fs::create_dir_all(&full_release_path)?;
let targets = config.targets();
let dir = env::current_dir()?;
let dir_str = dir.to_str().ok_or(Error::PathString)?;
status.clear_missing_targets(
version,
targets
.iter()
.map(|t| t.output_str())
.collect::<Vec<_>>()
.as_ref(),
);
status.write()?;
if opt.force_compile() {
status.reset_all(version);
}
for target in targets {
if !status.needs_compile(&target.output_str(), version) {
info!("Skipping: {}, already compiled", target.output_str());
continue;
}
let build_dir = &format!("{}/target/{}", dir_str, target.target_str());
if !opt.skip_dependencies() && fs::metadata(build_dir).is_ok() {
fs::remove_dir_all(build_dir)?;
}
let proc_status = target.compile(version, &mut status)?;
if proc_status.success() {
let build_path = format!("{}/release/{}", build_dir, &name);
let dest_dir = format!("{}/{}", &full_release_path_string, target.output_str());
fs::create_dir_all(&dest_dir)?;
for file in config.included_files() {
let file_path = format!("{}/{}", dir_str, &file);
fs::copy(file_path, format!("{}/{}", dest_dir, &file))?;
}
if fs::metadata(&build_path).is_ok() {
let dest_path = format!("{}/{}", dest_dir, &name);
fs::copy(build_path, dest_path)?;
} else {
let build_path = format!("{}.exe", build_path);
if fs::metadata(&build_path).is_ok() {
let dest_path =
format!(
"{}/{}.exe",
dest_dir,
&name,
);
fs::copy(build_path, dest_path)?;
}
}
zip_directory(
&full_release_path_string,
&dest_dir,
&format!("{}.zip", target.output_str()),
)?;
}
}
status.write()?;
if !status.all_clear(version) {
return Err(Error::FailedBuilds);
}
if opt.publish() {
publish().map(|exit_status| if exit_status.success() {
status.publish(version);
})?;
status.write()?;
}
Ok(())
}
fn main() {
exit(match do_main() {
Ok(_) => 0,
Err(e) => {
error!("{}", e);
1
}
});
}