Skip to content

Commit

Permalink
Parse more complex nested stages, etc
Browse files Browse the repository at this point in the history
This adds some more logic to the parser rather than the grammar to ensure that
the stage {} directive has the necessary children blocks

The errors are pretty sweet too:

    ❯ ./target/debug/jdp check data/invalid/no-steps-in-stage/Jenkinsfile

    data/invalid/no-steps-in-stage/Jenkinsfile
    ------------------------------------------
    0: pipeline {
    1:     agent any
    2:     stages {
    3:         stage('Build') {
      --------^

    Fail: A stage must have either steps{}, parallel{}, or nested stages {}
  • Loading branch information
rtyler committed Dec 20, 2020
1 parent 7f3e025 commit ad1c466
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 26 deletions.
50 changes: 50 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ extern crate pest_derive;
use pest::error::Error as PestError;
use pest::error::ErrorVariant;
use pest::Parser;
use pest::iterators::Pairs;
use std::path::PathBuf;

#[derive(Parser)]
Expand Down Expand Up @@ -41,6 +42,54 @@ pub fn parse_file(path: &PathBuf) -> Result<(), pest::error::Error<Rule>> {
}
}

/**
* Make sure that the stage has the required directives, otherwise throw
* out a CustomError
*/
fn parse_stage(parser: &mut Pairs<Rule>, span: pest::Span) -> Result<(), PestError<Rule>> {
let mut met_requirements = false;

while let Some(parsed) = parser.next() {
match parsed.as_rule() {
Rule::stepsDecl => {
met_requirements = true;
},
Rule::parallelDecl => {
met_requirements = true;
},
Rule::stagesDecl => {
met_requirements = true;
parse_stages(&mut parsed.into_inner())?;
}
_ => {},
}
}

if ! met_requirements {
Err(PestError::new_from_span(
ErrorVariant::CustomError {
message: "A stage must have either steps{}, parallel{}, or nested stages {}".to_string(),
}, span
))
}
else {
Ok(())
}
}

fn parse_stages(parser: &mut Pairs<Rule>) -> Result<(), PestError<Rule>> {
while let Some(parsed) = parser.next() {
match parsed.as_rule() {
Rule::stage => {
let span = parsed.as_span();
parse_stage(&mut parsed.into_inner(), span)?;
},
_ => {},
}
}
Ok(())
}

pub fn parse_pipeline_string(buffer: &str) -> Result<(), PestError<Rule>> {
let mut parser = PipelineParser::parse(Rule::pipeline, buffer)?;

Expand Down Expand Up @@ -70,6 +119,7 @@ pub fn parse_pipeline_string(buffer: &str) -> Result<(), PestError<Rule>> {
));
}
stages = true;
parse_stages(&mut parsed.into_inner())?;
}
_ => {}
}
Expand Down
60 changes: 34 additions & 26 deletions src/pipeline.pest
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,11 @@ agentBlock = {
| ("label" ~ string)) ~
closing_brace
}
k8sAgent = { "kubernetes" ~
opening_brace ~
("defaultContainer" ~ string)? ~
("yaml" ~ string)? ~
("yamlFile" ~ string)? ~
closing_brace
}

credentialProperty = { IDENT ~
"=" ~
"credentials" ~ opening_paren ~ string ~ closing_paren
}
dockerAgent = { "docker" ~
opening_brace ~
("reuseNode" ~ bool)? ~
Expand All @@ -62,6 +60,7 @@ dockerAgent = { "docker" ~
("customWorkspace" ~ string)? ~
closing_brace
}

dockerfileAgent = { "dockerfile" ~
opening_brace ~
("reuseNode" ~ bool)? ~
Expand All @@ -73,13 +72,6 @@ dockerfileAgent = { "dockerfile" ~
("customWorkspace" ~ string)? ~
closing_brace
}
nodeAgent = { "node" ~
opening_brace ~
("label" ~ string)? ~
("customWorkspace" ~ string)? ~
closing_brace
}


environmentDecl = { "environment" ~
opening_brace ~
Expand All @@ -90,10 +82,8 @@ envProperty = {
property
| credentialProperty
}
credentialProperty = { IDENT ~
"=" ~
"credentials" ~ opening_paren ~ string ~ closing_paren
}

failFast = { "failFast" ~ bool }

func = { IDENT ~ opening_paren? ~ (kwargs | args | func)? ~ closing_paren? }

Expand All @@ -105,6 +95,23 @@ inputDecl = { "input" ~
closing_brace
}

k8sAgent = { "kubernetes" ~
opening_brace ~
("defaultContainer" ~ string)? ~
("yaml" ~ string)? ~
("yamlFile" ~ string)? ~
closing_brace
}

nodeAgent = { "node" ~
opening_brace ~
("label" ~ string)? ~
("customWorkspace" ~ string)? ~
closing_brace
}



optionsDecl = { "options" ~
opening_brace ~
(func)* ~
Expand Down Expand Up @@ -159,14 +166,15 @@ stagesDecl = { "stages" ~

stage = { "stage(" ~ string ~ ")" ~
opening_brace ~
whenDecl? ~
optionsDecl? ~
agentDecl? ~
environmentDecl? ~
inputDecl? ~
toolsDecl? ~
(stepsDecl | parallelDecl) ~
postDecl? ~
(whenDecl
| failFast
| optionsDecl
| agentDecl
| environmentDecl
| inputDecl
| toolsDecl
| (stepsDecl | parallelDecl | stagesDecl)
| postDecl)* ~
closing_brace
}

Expand Down

0 comments on commit ad1c466

Please sign in to comment.