Add a little more smarts to the parser for finding required directives

This commit also adds some failing test cases
This commit is contained in:
R Tyler Croy 2020-12-19 12:11:42 -08:00
parent 4eb6bfe406
commit 112e7f617a
10 changed files with 225 additions and 3 deletions

153
Cargo.lock generated
View File

@ -1,5 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "block-buffer"
version = "0.7.3"
@ -48,6 +68,19 @@ dependencies = [
"generic-array",
]
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
@ -89,6 +122,24 @@ dependencies = [
"syn",
]
[[package]]
name = "hermit-abi"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
dependencies = [
"libc",
]
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]]
name = "jdp"
version = "0.1.0"
@ -98,8 +149,21 @@ dependencies = [
"log",
"pest",
"pest_derive",
"pretty_env_logger",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
[[package]]
name = "log"
version = "0.4.11"
@ -115,6 +179,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "memchr"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "opaque-debug"
version = "0.2.3"
@ -164,6 +234,16 @@ dependencies = [
"sha-1",
]
[[package]]
name = "pretty_env_logger"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
dependencies = [
"env_logger",
"log",
]
[[package]]
name = "proc-macro2"
version = "1.0.24"
@ -173,6 +253,12 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.7"
@ -182,6 +268,24 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
[[package]]
name = "sha-1"
version = "0.8.2"
@ -205,6 +309,24 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
]
[[package]]
name = "typenum"
version = "1.12.0"
@ -222,3 +344,34 @@ name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -18,6 +18,7 @@ gumdrop = "~0.8.0"
log = "~0.4.11"
pest = "~2.1.3"
pest_derive = "~2.1.0"
pretty_env_logger = "~0.4.0"
[dev-dependencies]
glob = "0.3"

View File

@ -21,3 +21,20 @@ This parser is **not** a Groovy syntax parser, and as such any advanced or wacky
groovy that is littered around a `Jenkinsfile` should largely be ignored. This
includes the `script` step which is basically checked to make sure that there is
a `script { }` block, but anything within it is explicitly ignored.
== Development
Development of `jdp` is all driven through the use of `cargo`, e.g. `cargo test`.
It's relatively straightforward to use a local test Jenkins environment to validate
the `Jenkinsfile` located in `data/`. There is a major caveat in that many files
will fail to validate if certain plugins are not installed on the Jenkins.
Once a local Jenkins is created, be sure to set a fixed sshd port in the
`/configureSecurity` view (e.g. `2022`).
[source]
----
find data/valid -iname Jenkinsfile -exec ./scripts/declarative-linter {} \;
----

View File

@ -0,0 +1,3 @@
pipeline {
// Gotta have stages!
}

0
data/invalid/empty/Jenkinsfile vendored Normal file
View File

3
data/invalid/no-stages/Jenkinsfile vendored Normal file
View File

@ -0,0 +1,3 @@
pipeline {
agent any
}

5
scripts/declarative-linter Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
echo ">> Linting $1 against a local Jenkins"
ssh -p 2022 admin@localhost declarative-linter < $1
echo "---"

View File

@ -2,6 +2,7 @@ extern crate pest;
#[macro_use]
extern crate pest_derive;
use log::*;
use pest::error::Error as PestError;
use pest::Parser;
use std::path::PathBuf;
@ -23,7 +24,42 @@ pub fn parse_file(path: &PathBuf) -> Result<(), pest::error::Error<Rule>> {
}
pub fn parse_pipeline_string(buffer: &str) -> Result<(), PestError<Rule>> {
let _parser = PipelineParser::parse(Rule::pipeline, buffer)?;
use pest::error::ErrorVariant;
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 {
warn!("Did I just see two agent directives?");
}
agents = true;
},
Rule::stagesDecl => {
if stages {
warn!("Did I just see two stages directives?");
}
stages = true;
},
_ => {},
}
}
/*
* 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(())
}

View File

@ -29,6 +29,7 @@ struct CheckOpts {
}
fn main() {
pretty_env_logger::init();
let opts = JdpOptions::parse_args_default_or_exit();
if opts.command.is_none() {

View File

@ -8,16 +8,19 @@ pipeline = _{ SOI ~ shebang? ~
opening_brace ~
// I am pretty sure this grammar is wrong and there has to
// a better way to allow these declarations anywhere within
// the block, but still only allow one of each
// the block, but still only allow one of each.
// For now the parser code will have to inspect this itself
(
// An agent declaration at the root level is required
agentDecl
// A stages declaration is also required
| stagesDecl
| environmentDecl
| optionsDecl
| parametersDecl
| postDecl
| toolsDecl
| triggersDecl
| stagesDecl{1}
)*
~
closing_brace ~