use config::{Config, ConfigError, Environment, File, FileFormat};
use std::env;
use std::ffi::OsString;
use structopt::StructOpt;
#[derive(StructOpt, Debug)]
pub enum CommandOpt {
#[structopt(name = "search")]
Search {
pattern: String,
#[structopt(long = "tmpl", short = "t", default_value = "{{ name }}\t{{ color }}")]
template: String,
},
#[structopt(name = "job")]
Job {
name: String,
#[structopt(long = "tmpl", short = "t",
default_value = "{{ name }} - {{ color }} (#{{ lastBuild.number }})")]
template: String,
},
#[structopt(name = "build")]
Build {
name: String,
number: Option<u32>,
#[structopt(long = "tmpl", short = "t",
default_value = "{{ fullDisplayName}} {{ result }} {{ timestamp }} ({{duration}}ms)")]
template: String,
},
#[structopt(name = "trigger")]
Trigger {
name: String,
#[structopt(long = "wait-start")]
wait_start: bool,
#[structopt(long = "wait-finish")]
wait_finish: bool,
#[structopt(long = "polling", default_value = "10")]
polling: u64,
#[structopt(long = "tmpl", short = "t",
default_value = "{{ queueItem.task.name }} {{#if queueItem.why}}{{ queueItem.why }}{{/if}}{{#if queueItem.executable}}{{ build.displayName }} {{ build.result }} {{build.elapsed}}s (est. {{ build.estimatedDuration }}ms){{/if}}")]
template: String,
},
#[structopt(name = "views")]
Views {
pattern: Option<String>,
#[structopt(long = "tmpl", short = "t", default_value = "{{ name }}")]
template: String,
},
#[structopt(name = "view")]
View {
name: String,
#[structopt(long = "tmpl", short = "t",
default_value = "{{ name }}\t{{ color }}\t(#{{ lastBuild.number }})")]
template: String,
},
}
#[derive(StructOpt, Debug)]
#[structopt(name = "jencli", author = "",
after_help = r#"
About Templates
Templates are defined using handlebars syntax. To view all fields available for a template, set jencli logs to debug with RUST_LOG=jencli=debug
A few helpers are available:
* colored: add color to build result and job status
* date: transform timestamps to UTC dates
About Configuration
Jenkins configuration (url, user, password, depth) can be overriden in a number of way, by decreasing order of priority:
* values passed as options
* values in environment variables
* .jencli.yaml file in path
* .jencli.yaml file in user home directory
"#)]
pub struct ParamsOpt {
#[structopt(env = "JENKINS_URL", long = "url")]
pub url: String,
#[structopt(env = "JENKINS_USER", long = "user")]
pub user: Option<String>,
#[structopt(env = "JENKINS_PASSWORD", long = "password")]
pub password: Option<String>,
#[structopt(env = "JENKINS_DEPTH", long = "depth", default_value = "1")]
pub depth: u8,
#[structopt(flatten)]
pub command: CommandOpt,
}
#[derive(Debug, Deserialize)]
pub struct JenkinsSettings {
pub url: Option<String>,
pub user: Option<String>,
pub password: Option<String>,
pub depth: Option<u8>,
}
impl JenkinsSettings {
pub fn new() -> Result<Self, ConfigError> {
let mut config = Config::new();
config.merge(File::new("/Users/francoism/.jencli.yaml", FileFormat::Yaml).required(false))?;
let mut current_dir = env::current_dir().unwrap();
let mut pathes = vec![current_dir.clone().into_os_string()];
while current_dir.pop() {
pathes.push(current_dir.clone().into_os_string());
}
for dir in pathes.iter().rev() {
let mut file = dir.clone();
file.push(OsString::from("/.jencli.yaml"));
let file_path = file.to_str().unwrap();
config.merge(File::new(file_path, FileFormat::Yaml).required(false))?;
}
config.merge(Environment::with_prefix("jenkins"))?;
config.try_into()
}
}
pub fn load() -> Result<ParamsOpt, ConfigError> {
let jenkins_settings = JenkinsSettings::new()?;
if let Some(url) = jenkins_settings.url {
env::set_var("JENKINS_URL", &url);
}
if let Some(user) = jenkins_settings.user {
env::set_var("JENKINS_USER", &user);
}
if let Some(password) = jenkins_settings.password {
env::set_var("JENKINS_PASSWORD", &password);
}
if let Some(depth) = jenkins_settings.depth {
env::set_var("JENKINS_DEPTH", &depth.to_string());
}
Ok(ParamsOpt::from_args())
}