Use a regular expression for a quick sanity check to identify Scripted Pipelines

Fixes #16
This commit is contained in:
R Tyler Croy 2020-12-25 21:48:00 -08:00
parent daa04b0020
commit e0d13a696c
4 changed files with 109 additions and 51 deletions

2
Cargo.lock generated
View File

@ -146,10 +146,12 @@ version = "0.2.3"
dependencies = [
"glob",
"gumdrop",
"lazy_static",
"log",
"pest",
"pest_derive",
"pretty_env_logger",
"regex",
]
[[package]]

View File

@ -20,10 +20,12 @@ path = "src/main.rs"
[dependencies]
gumdrop = "~0.8.0"
lazy_static = "~1.4.0"
log = "~0.4.11"
pest = "~2.1.3"
pest_derive = "~2.1.0"
pretty_env_logger = "~0.4.0"
regex = "1"
[dev-dependencies]
glob = "0.3"

View File

@ -0,0 +1,6 @@
node {
echo 'This is a scripted pipeline'
// with groovy
['a','b'].each { echo it }
}

View File

@ -1,3 +1,5 @@
#[macro_use]
extern crate lazy_static;
extern crate pest;
#[macro_use]
extern crate pest_derive;
@ -6,6 +8,7 @@ use pest::error::Error as PestError;
use pest::error::ErrorVariant;
use pest::iterators::Pairs;
use pest::Parser;
use regex::Regex;
use std::path::PathBuf;
#[derive(Parser)]
@ -42,6 +45,76 @@ pub fn parse_file(path: &PathBuf) -> Result<(), pest::error::Error<Rule>> {
}
}
pub fn parse_pipeline_string(buffer: &str) -> Result<(), PestError<Rule>> {
if !is_declarative(buffer) {
return Err(PestError::new_from_pos(
ErrorVariant::CustomError {
message: "The buffer does not appear to be a Declarative Pipeline, I couldn't find pipeline { }".to_string(),
},
pest::Position::from_start(buffer),
));
}
let mut parser = PipelineParser::parse(Rule::pipeline, buffer)?;
let mut agents = false;
let mut stages = false;
while let Some(parsed) = parser.next() {
match parsed.as_rule() {
Rule::agentDecl => {
if agents {
return Err(PestError::new_from_span(
ErrorVariant::CustomError {
message: "Cannot have two top-level `agent` directives".to_string(),
},
parsed.as_span(),
));
}
agents = true;
}
Rule::stagesDecl => {
if stages {
return Err(PestError::new_from_span(
ErrorVariant::CustomError {
message: "Cannot have two top-level `stages` directives".to_string(),
},
parsed.as_span(),
));
}
stages = true;
parse_stages(&mut parsed.into_inner())?;
}
_ => {}
}
}
/*
* Both agents and stages are required, the lack thereof is an error
*/
if !agents || !stages {
let error = PestError::new_from_pos(
ErrorVariant::ParsingError {
positives: vec![],
negatives: vec![],
},
pest::Position::from_start(buffer),
);
return Err(error);
}
Ok(())
}
/**
* Run a quick sanity check to determine whether the given buffer appears to
* be a Declarative Pipeline or not.
*/
fn is_declarative(buffer: &str) -> bool {
lazy_static! {
static ref RE: Regex = Regex::new(r"pipeline(\s+)?\{").expect("Failed to make regex");
}
RE.is_match(buffer)
}
/**
* Make sure that the stage has the required directives, otherwise throw
* out a CustomError
@ -91,61 +164,36 @@ fn parse_stages(parser: &mut Pairs<Rule>) -> Result<(), PestError<Rule>> {
Ok(())
}
pub fn parse_pipeline_string(buffer: &str) -> Result<(), PestError<Rule>> {
let mut parser = PipelineParser::parse(Rule::pipeline, buffer)?;
let mut agents = false;
let mut stages = false;
while let Some(parsed) = parser.next() {
match parsed.as_rule() {
Rule::agentDecl => {
if agents {
return Err(PestError::new_from_span(
ErrorVariant::CustomError {
message: "Cannot have two top-level `agent` directives".to_string(),
},
parsed.as_span(),
));
}
agents = true;
}
Rule::stagesDecl => {
if stages {
return Err(PestError::new_from_span(
ErrorVariant::CustomError {
message: "Cannot have two top-level `stages` directives".to_string(),
},
parsed.as_span(),
));
}
stages = true;
parse_stages(&mut parsed.into_inner())?;
}
_ => {}
}
}
/*
* Both agents and stages are required, the lack thereof is an error
*/
if !agents || !stages {
let error = PestError::new_from_pos(
ErrorVariant::ParsingError {
positives: vec![],
negatives: vec![],
},
pest::Position::from_start(buffer),
);
return Err(error);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
/*
* This will test what parse_pipeline_string will do when faced with a
* Scripted Pipeline/
*/
#[test]
fn is_declarative_with_scripted() {
assert_eq!(false, is_declarative("node { sh 'env' }"));
}
#[test]
fn is_declarative_with_declarative() {
assert!(is_declarative(
"pipeline { agent any stages { stage('Build') { steps { sh 'printenv' } } } }"
));
}
/*
* This is just to help make sure the regex isn't too whitespace sensitive
*/
#[test]
fn is_declarative_with_declarative_no_spaces() {
assert!(is_declarative(
"pipeline{ agent any stages { stage('Build') { steps { sh 'printenv' }}}}"
));
}
#[test]
fn parse_string_single() {
let _str = PipelineParser::parse(Rule::string, r#"'hello world'"#)