From 52212615062ee279195c32b70326081844ac48f6 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Mon, 27 Jul 2020 16:55:28 +0100 Subject: [PATCH 01/37] beats 2.0 pipeline --- vars/beats.groovy | 96 +++++++++++++++++++++++++++++++++++++++++++++++ vars/beats.txt | 17 +++++++++ 2 files changed, 113 insertions(+) create mode 100644 vars/beats.groovy create mode 100644 vars/beats.txt diff --git a/vars/beats.groovy b/vars/beats.groovy new file mode 100644 index 000000000..46b987e00 --- /dev/null +++ b/vars/beats.groovy @@ -0,0 +1,96 @@ +#!/usr/bin/env groovy + +/** +* Given the YAML definition and the changeset global macros +* then it verifies if the project or stage should be enabled. +*/ +Boolean when(Map args = [:]) { + def project = args.project + def content = args.content + def patterns = args.changeset + def ret = false + + if (whenComments(args)) { ret = true } + if (whenLabels(args)) { ret = true } + if (whenParameters(args)) { ret = true } + if (whenBranches(args)) { ret = true } + if (whenTags(args)) { ret = true } + + return ret +} + +private Boolean whenBranches(Map args = [:]) { + def ret = false + + if (content['branches'] && env.BRANCH_NAME?.trim()) { + ret = true + markdownReason(project: project, reason: 'Branch is enabled and matches with the pattern.') + } + return ret +} + +private Boolean whenComments(Map args = [:]) { + def ret = false + if (args.content['comments']?.trim() && env.GITHUB_COMMENT?.trim()) { + if (args.content['comments']?.any { env.GITHUB_COMMENT?.toLowerCase()?.contains(it?.toLowerCase()) }) { + ret = true + markdownReason(project: args.project, reason: 'Comment is enabled and matches with the pattern.') + } else { + markdownReason(project: args.project, reason: 'Comment is enabled and does not match with the pattern.') + } + } + return ret +} + +private Boolean whenLabels(Map args = [:]) { + def ret = false + if (content['labels']) { + if (content['labels']?.any { matchesPrLabel(label: it) }) { + ret = true + markdownReason(project: project, reason: 'Label is enabled and matches with the pattern.') + } else { + markdownReason(project: project, reason: 'Label is enabled and does not match with the pattern.') + } + } + return ret +} + +private Boolean whenLabels(Map args = [:]) { + def ret = false + if (content['labels']) { + if (content['labels']?.any { matchesPrLabel(label: it) }) { + ret = true + markdownReason(project: project, reason: 'Label is enabled and matches with the pattern.') + } else { + markdownReason(project: project, reason: 'Label is enabled and does not match with the pattern.') + } + } + return ret +} + +private Boolean whenParameters(Map args = [:]) { + def ret = false + if (content['parameters']) { + if (content['parameters']?.any { params.[it] }) { + ret = true + markdownReason(project: project, reason: 'Parameter is enabled and matches with the pattern.') + } else { + markdownReason(project: project, reason: 'Parameter is enabled and does not match with the pattern.') + } + } + return ret +} + +private Boolean whenTags(Map args = [:]) { + def ret = false + if (content['tags'] && env.TAG_NAME?.trim()) { + ret = true + markdownReason(project: project, reason: 'Tag is enabled and matches with the pattern.') + } + return ret +} + +private void markdownReason(Map args = [:]) { + echo "${args.project} - ${args.reason}" + // TODO create markdown +} diff --git a/vars/beats.txt b/vars/beats.txt new file mode 100644 index 000000000..0ffb9bcdf --- /dev/null +++ b/vars/beats.txt @@ -0,0 +1,17 @@ +beats.when() + +

+ fooo + + The list of step's params and the related default values are: +

+

+ + +
+
+
+
From bd050f6c8301ae58859d450050342b498d0c6903 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Mon, 27 Jul 2020 17:06:22 +0100 Subject: [PATCH 02/37] UTs: whenBranches --- src/test/groovy/BeatsStepTests.groovy | 55 +++++++++++++++++++++++++++ vars/beats.groovy | 20 ++-------- 2 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 src/test/groovy/BeatsStepTests.groovy diff --git a/src/test/groovy/BeatsStepTests.groovy b/src/test/groovy/BeatsStepTests.groovy new file mode 100644 index 000000000..c82ffd343 --- /dev/null +++ b/src/test/groovy/BeatsStepTests.groovy @@ -0,0 +1,55 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import org.junit.Before +import org.junit.After +import org.junit.Test +import static org.junit.Assert.assertFalse +import static org.junit.Assert.assertTrue + +class BeatsStepTests extends ApmBasePipelineTest { + String scriptName = 'vars/beats.groovy' + + @Override + @Before + void setUp() throws Exception { + super.setUp() + } + + @Test + void test_whenBranches_and_no_environment_variable() throws Exception { + def script = loadScript(scriptName) + def ret = script.whenBranches() + assertFalse(ret) + } + + @Test + void test_whenBranches_and_environment_variable_but_no_data() throws Exception { + def script = loadScript(scriptName) + env.BRANCH_NAME = 'branch' + def ret = script.whenBranches(content: [:]) + assertFalse(ret) + } + + @Test + void test_whenBranches_and_environment_variable_with_data() throws Exception { + def script = loadScript(scriptName) + env.BRANCH_NAME = 'branch' + def ret = script.whenBranches(content: [ branches: true]) + assertTrue(ret) + } +} diff --git a/vars/beats.groovy b/vars/beats.groovy index 46b987e00..c689c6fd3 100644 --- a/vars/beats.groovy +++ b/vars/beats.groovy @@ -21,10 +21,9 @@ Boolean when(Map args = [:]) { private Boolean whenBranches(Map args = [:]) { def ret = false - - if (content['branches'] && env.BRANCH_NAME?.trim()) { + if (env.BRANCH_NAME?.trim() && args.content?.get('branches')) { ret = true - markdownReason(project: project, reason: 'Branch is enabled and matches with the pattern.') + markdownReason(project: args.project, reason: 'Branch is enabled and matches with the pattern.') } return ret } @@ -55,23 +54,10 @@ private Boolean whenLabels(Map args = [:]) { return ret } -private Boolean whenLabels(Map args = [:]) { - def ret = false - if (content['labels']) { - if (content['labels']?.any { matchesPrLabel(label: it) }) { - ret = true - markdownReason(project: project, reason: 'Label is enabled and matches with the pattern.') - } else { - markdownReason(project: project, reason: 'Label is enabled and does not match with the pattern.') - } - } - return ret -} - private Boolean whenParameters(Map args = [:]) { def ret = false if (content['parameters']) { - if (content['parameters']?.any { params.[it] }) { + if (content['parameters']?.any { params[it] }) { ret = true markdownReason(project: project, reason: 'Parameter is enabled and matches with the pattern.') } else { From 769eca1315a254de751db351298fb601d9895e00 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Mon, 27 Jul 2020 17:13:24 +0100 Subject: [PATCH 03/37] UTs: whenTags and whenParameters --- src/test/groovy/BeatsStepTests.groovy | 51 +++++++++++++++++++++++++++ vars/beats.groovy | 16 ++++----- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/test/groovy/BeatsStepTests.groovy b/src/test/groovy/BeatsStepTests.groovy index c82ffd343..aae59003c 100644 --- a/src/test/groovy/BeatsStepTests.groovy +++ b/src/test/groovy/BeatsStepTests.groovy @@ -52,4 +52,55 @@ class BeatsStepTests extends ApmBasePipelineTest { def ret = script.whenBranches(content: [ branches: true]) assertTrue(ret) } + + @Test + void test_whenParameters_and_no_params() throws Exception { + def script = loadScript(scriptName) + def ret = script.whenParameters() + assertFalse(ret) + } + + @Test + void test_whenParameters_and_params_without_match() throws Exception { + def script = loadScript(scriptName) + def ret = script.whenParameters(content: [ parameters : [ 'foo', 'bar']]) + assertFalse(ret) + } + + void test_whenParameters_and_params_with_match() throws Exception { + def script = loadScript(scriptName) + params.bar = true + def ret = script.whenParameters(content: [ parameters : [ 'foo', 'bar']]) + assertTrue(ret) + } + + void test_whenParameters_and_params_with_match_but_disabled() throws Exception { + def script = loadScript(scriptName) + params.bar = false + def ret = script.whenParameters(content: [ parameters : [ 'foo', 'bar']]) + assertFalse(ret) + } + + @Test + void test_whenTags_and_no_environment_variable() throws Exception { + def script = loadScript(scriptName) + def ret = script.whenTags() + assertFalse(ret) + } + + @Test + void test_whenTags_and_environment_variable_but_no_data() throws Exception { + def script = loadScript(scriptName) + env.TAG_NAME = 'tag' + def ret = script.whenTags(content: [:]) + assertFalse(ret) + } + + @Test + void test_whenTags_and_environment_variable_with_data() throws Exception { + def script = loadScript(scriptName) + env.TAG_NAME = 'tag' + def ret = script.whenTags(content: [ tags: true]) + assertTrue(ret) + } } diff --git a/vars/beats.groovy b/vars/beats.groovy index c689c6fd3..24083b7bb 100644 --- a/vars/beats.groovy +++ b/vars/beats.groovy @@ -46,9 +46,9 @@ private Boolean whenLabels(Map args = [:]) { if (content['labels']) { if (content['labels']?.any { matchesPrLabel(label: it) }) { ret = true - markdownReason(project: project, reason: 'Label is enabled and matches with the pattern.') + markdownReason(project: args.project, reason: 'Label is enabled and matches with the pattern.') } else { - markdownReason(project: project, reason: 'Label is enabled and does not match with the pattern.') + markdownReason(project: args.project, reason: 'Label is enabled and does not match with the pattern.') } } return ret @@ -56,12 +56,12 @@ private Boolean whenLabels(Map args = [:]) { private Boolean whenParameters(Map args = [:]) { def ret = false - if (content['parameters']) { - if (content['parameters']?.any { params[it] }) { + if (args.content?.get('parameters')) { + if (args.content?.get('parameters')?.any { params[it] }) { ret = true - markdownReason(project: project, reason: 'Parameter is enabled and matches with the pattern.') + markdownReason(project: args.project, reason: 'Parameter is enabled and matches with the pattern.') } else { - markdownReason(project: project, reason: 'Parameter is enabled and does not match with the pattern.') + markdownReason(project: args.project, reason: 'Parameter is enabled and does not match with the pattern.') } } return ret @@ -69,9 +69,9 @@ private Boolean whenParameters(Map args = [:]) { private Boolean whenTags(Map args = [:]) { def ret = false - if (content['tags'] && env.TAG_NAME?.trim()) { + if (env.TAG_NAME?.trim() && args.content?.get('tags')) { ret = true - markdownReason(project: project, reason: 'Tag is enabled and matches with the pattern.') + markdownReason(project: args.project, reason: 'Tag is enabled and matches with the pattern.') } return ret } From 6278ea0fecb8cc15e135d1e9f0fb1d471d09cb7a Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Mon, 27 Jul 2020 17:17:47 +0100 Subject: [PATCH 04/37] UTs: whenComments --- src/test/groovy/BeatsStepTests.groovy | 31 +++++++++++++++++++++++++++ vars/beats.groovy | 8 +++---- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/test/groovy/BeatsStepTests.groovy b/src/test/groovy/BeatsStepTests.groovy index aae59003c..5a155d39a 100644 --- a/src/test/groovy/BeatsStepTests.groovy +++ b/src/test/groovy/BeatsStepTests.groovy @@ -53,6 +53,37 @@ class BeatsStepTests extends ApmBasePipelineTest { assertTrue(ret) } + @Test + void test_whenComments_and_no_environment_variable() throws Exception { + def script = loadScript(scriptName) + def ret = script.whenComments() + assertFalse(ret) + } + + @Test + void test_whenComments_and_environment_variable_but_no_data() throws Exception { + def script = loadScript(scriptName) + env.GITHUB_COMMENT = 'branch' + def ret = script.whenComments(content: [:]) + assertFalse(ret) + } + + @Test + void test_whenComments_and_environment_variable_with_match() throws Exception { + def script = loadScript(scriptName) + env.GITHUB_COMMENT = '/test foo' + def ret = script.whenComments(content: [ comments: ['/test foo']]) + assertTrue(ret) + } + + @Test + void test_whenComments_and_environment_variable_without_match() throws Exception { + def script = loadScript(scriptName) + env.GITHUB_COMMENT = '/test foo' + def ret = script.whenComments(content: [ comments: ['/run bla', '/test bar']]) + assertFalse(ret) + } + @Test void test_whenParameters_and_no_params() throws Exception { def script = loadScript(scriptName) diff --git a/vars/beats.groovy b/vars/beats.groovy index 24083b7bb..b65d8fdd2 100644 --- a/vars/beats.groovy +++ b/vars/beats.groovy @@ -30,8 +30,8 @@ private Boolean whenBranches(Map args = [:]) { private Boolean whenComments(Map args = [:]) { def ret = false - if (args.content['comments']?.trim() && env.GITHUB_COMMENT?.trim()) { - if (args.content['comments']?.any { env.GITHUB_COMMENT?.toLowerCase()?.contains(it?.toLowerCase()) }) { + if (args.content?.get('comments') && env.GITHUB_COMMENT?.trim()) { + if (args.content?.get('comments')?.any { env.GITHUB_COMMENT?.toLowerCase()?.contains(it?.toLowerCase()) }) { ret = true markdownReason(project: args.project, reason: 'Comment is enabled and matches with the pattern.') } else { @@ -43,8 +43,8 @@ private Boolean whenComments(Map args = [:]) { private Boolean whenLabels(Map args = [:]) { def ret = false - if (content['labels']) { - if (content['labels']?.any { matchesPrLabel(label: it) }) { + if (args.content?.get('labels')) { + if (args.content?.get('labels')?.any { matchesPrLabel(label: it) }) { ret = true markdownReason(project: args.project, reason: 'Label is enabled and matches with the pattern.') } else { From c65f13060bd9e895801cd63ee80fad2c12eb4d31 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Mon, 27 Jul 2020 17:21:53 +0100 Subject: [PATCH 05/37] UTs: whenLabels --- src/test/groovy/BeatsStepTests.groovy | 23 +++++++++++++++++++++++ vars/beats.groovy | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/test/groovy/BeatsStepTests.groovy b/src/test/groovy/BeatsStepTests.groovy index 5a155d39a..4b14c35a2 100644 --- a/src/test/groovy/BeatsStepTests.groovy +++ b/src/test/groovy/BeatsStepTests.groovy @@ -84,6 +84,29 @@ class BeatsStepTests extends ApmBasePipelineTest { assertFalse(ret) } + @Test + void test_whenLabels_and_no_data() throws Exception { + def script = loadScript(scriptName) + def ret = script.whenLabels(content: [:]) + assertFalse(ret) + } + + @Test + void test_whenLabels_with_match() throws Exception { + def script = loadScript(scriptName) + helper.registerAllowedMethod('matchesPrLabel', [Map.class], { true }) + def ret = script.whenLabels(content: [ labels: ['foo']]) + assertTrue(ret) + } + + @Test + void test_whenLabels_without_match() throws Exception { + def script = loadScript(scriptName) + helper.registerAllowedMethod('matchesPrLabel', [Map.class], { false }) + def ret = script.whenLabels(content: [ labels: ['foo']]) + assertFalse(ret) + } + @Test void test_whenParameters_and_no_params() throws Exception { def script = loadScript(scriptName) diff --git a/vars/beats.groovy b/vars/beats.groovy index b65d8fdd2..0d8961c8d 100644 --- a/vars/beats.groovy +++ b/vars/beats.groovy @@ -15,7 +15,7 @@ Boolean when(Map args = [:]) { if (whenParameters(args)) { ret = true } if (whenBranches(args)) { ret = true } if (whenTags(args)) { ret = true } - + // TODO: changeset validation return ret } From b2940bc27e1db8a00a9791aa4ad7dd8167de4b94 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Mon, 27 Jul 2020 17:23:55 +0100 Subject: [PATCH 06/37] add license header --- ...pTests.groovy => BeatsWhenStepTests.groovy} | 4 ++-- vars/{beats.groovy => beatsWhen.groovy} | 18 +++++++++++++++++- vars/{beats.txt => beatsWhen.txt} | 0 3 files changed, 19 insertions(+), 3 deletions(-) rename src/test/groovy/{BeatsStepTests.groovy => BeatsWhenStepTests.groovy} (97%) rename vars/{beats.groovy => beatsWhen.groovy} (75%) rename vars/{beats.txt => beatsWhen.txt} (100%) diff --git a/src/test/groovy/BeatsStepTests.groovy b/src/test/groovy/BeatsWhenStepTests.groovy similarity index 97% rename from src/test/groovy/BeatsStepTests.groovy rename to src/test/groovy/BeatsWhenStepTests.groovy index 4b14c35a2..b1b51a06d 100644 --- a/src/test/groovy/BeatsStepTests.groovy +++ b/src/test/groovy/BeatsWhenStepTests.groovy @@ -21,8 +21,8 @@ import org.junit.Test import static org.junit.Assert.assertFalse import static org.junit.Assert.assertTrue -class BeatsStepTests extends ApmBasePipelineTest { - String scriptName = 'vars/beats.groovy' +class BeatsWhenStepTests extends ApmBasePipelineTest { + String scriptName = 'vars/beatsWhen.groovy' @Override @Before diff --git a/vars/beats.groovy b/vars/beatsWhen.groovy similarity index 75% rename from vars/beats.groovy rename to vars/beatsWhen.groovy index 0d8961c8d..d9dc31d47 100644 --- a/vars/beats.groovy +++ b/vars/beatsWhen.groovy @@ -1,10 +1,26 @@ #!/usr/bin/env groovy +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. /** * Given the YAML definition and the changeset global macros * then it verifies if the project or stage should be enabled. */ -Boolean when(Map args = [:]) { +Boolean call(Map params = [:]){ def project = args.project def content = args.content def patterns = args.changeset diff --git a/vars/beats.txt b/vars/beatsWhen.txt similarity index 100% rename from vars/beats.txt rename to vars/beatsWhen.txt From f71a6fcdeec604e2c949d83a6816ee7fdd9d6e9d Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Mon, 27 Jul 2020 17:31:07 +0100 Subject: [PATCH 07/37] ITs: beatsWhen --- local/configs/jenkins.yaml | 4 +++- src/test/resources/folders/beats.dsl | 4 ++++ src/test/resources/jobs/beats/beatsWhen.dsl | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/folders/beats.dsl create mode 100644 src/test/resources/jobs/beats/beatsWhen.dsl diff --git a/local/configs/jenkins.yaml b/local/configs/jenkins.yaml index 2cd679674..cdb59542a 100644 --- a/local/configs/jenkins.yaml +++ b/local/configs/jenkins.yaml @@ -103,9 +103,11 @@ unclassified: globalConfigName: username globalConfigEmail: username@example.com jobs: - - file: "/var/pipeline-library/src/test/resources/folders/it.dsl" + - file: "/var/pipeline-library/src/test/resources/folders/beats.dsl" - file: "/var/pipeline-library/src/test/resources/folders/getBuildInfoJsonFiles.dsl" + - file: "/var/pipeline-library/src/test/resources/folders/it.dsl" - file: "/var/pipeline-library/src/test/resources/folders/timeout.dsl" + - file: "/var/pipeline-library/src/test/resources/jobs/beats/when.dsl" - file: "/var/pipeline-library/src/test/resources/jobs/cancelPreviousRunningBuilds.dsl" - file: "/var/pipeline-library/src/test/resources/jobs/cmd.dsl" - file: "/var/pipeline-library/src/test/resources/jobs/dockerLogin.dsl" diff --git a/src/test/resources/folders/beats.dsl b/src/test/resources/folders/beats.dsl new file mode 100644 index 000000000..94a2c6d0f --- /dev/null +++ b/src/test/resources/folders/beats.dsl @@ -0,0 +1,4 @@ +folder('it/beats') { + displayName('beats') + description('beats ITs for the APM shared library') +} diff --git a/src/test/resources/jobs/beats/beatsWhen.dsl b/src/test/resources/jobs/beats/beatsWhen.dsl new file mode 100644 index 000000000..fe4dc9a85 --- /dev/null +++ b/src/test/resources/jobs/beats/beatsWhen.dsl @@ -0,0 +1,21 @@ +NAME = 'it/beats/beatsWhen' +DSL = '''pipeline { + agent none + stages { + stage('linux') { + agent { label 'linux && immutable' } + steps { + beatsWhen(project: 'test', + content: 'TBD') + } + } + } +}''' + +pipelineJob(NAME) { + definition { + cps { + script(DSL.stripIndent()) + } + } +} From feae2ad18dc7116fcdf0aab8ada7daca722061f2 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Mon, 27 Jul 2020 17:32:46 +0100 Subject: [PATCH 08/37] Add beats meta step --- vars/beats.groovy | 25 +++++++++++++++++++++++++ vars/beats.txt | 17 +++++++++++++++++ vars/beatsWhen.groovy | 2 +- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 vars/beats.groovy create mode 100644 vars/beats.txt diff --git a/vars/beats.groovy b/vars/beats.groovy new file mode 100644 index 000000000..61a431c8b --- /dev/null +++ b/vars/beats.groovy @@ -0,0 +1,25 @@ +#!/usr/bin/env groovy +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/** +* Given the YAML definition and the changeset global macros +* then it verifies if the project or stage should be enabled. +*/ +Boolean when(Map args = [:]){ + return beatsWhen(args) +} diff --git a/vars/beats.txt b/vars/beats.txt new file mode 100644 index 000000000..0ffb9bcdf --- /dev/null +++ b/vars/beats.txt @@ -0,0 +1,17 @@ +beats.when() + +

+ fooo + + The list of step's params and the related default values are: +

    +
  • foo:
  • +
  • bar: Defaults to `8`
  • +
+

+ + +
+
+
+
diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index d9dc31d47..b29b0ba19 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -20,7 +20,7 @@ * Given the YAML definition and the changeset global macros * then it verifies if the project or stage should be enabled. */ -Boolean call(Map params = [:]){ +Boolean call(Map args = [:]){ def project = args.project def content = args.content def patterns = args.changeset From d4bf9a351c8b1d980b07f8b404cdc93132afcbe3 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Tue, 28 Jul 2020 11:14:44 +0100 Subject: [PATCH 09/37] ITs --- local/configs/jenkins.yaml | 2 +- src/test/resources/jobs/beats/beatsWhen.dsl | 79 +++++++++++++++++++-- vars/beats.groovy | 4 ++ vars/beats.txt | 18 +++++ vars/beatsStages.groovy | 25 +++++++ vars/beatsStages.txt | 15 ++++ 6 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 vars/beatsStages.groovy create mode 100644 vars/beatsStages.txt diff --git a/local/configs/jenkins.yaml b/local/configs/jenkins.yaml index cdb59542a..02c72c8fd 100644 --- a/local/configs/jenkins.yaml +++ b/local/configs/jenkins.yaml @@ -107,7 +107,7 @@ jobs: - file: "/var/pipeline-library/src/test/resources/folders/getBuildInfoJsonFiles.dsl" - file: "/var/pipeline-library/src/test/resources/folders/it.dsl" - file: "/var/pipeline-library/src/test/resources/folders/timeout.dsl" - - file: "/var/pipeline-library/src/test/resources/jobs/beats/when.dsl" + - file: "/var/pipeline-library/src/test/resources/jobs/beats/beatsWhen.dsl" - file: "/var/pipeline-library/src/test/resources/jobs/cancelPreviousRunningBuilds.dsl" - file: "/var/pipeline-library/src/test/resources/jobs/cmd.dsl" - file: "/var/pipeline-library/src/test/resources/jobs/dockerLogin.dsl" diff --git a/src/test/resources/jobs/beats/beatsWhen.dsl b/src/test/resources/jobs/beats/beatsWhen.dsl index fe4dc9a85..09c6940c6 100644 --- a/src/test/resources/jobs/beats/beatsWhen.dsl +++ b/src/test/resources/jobs/beats/beatsWhen.dsl @@ -1,16 +1,83 @@ NAME = 'it/beats/beatsWhen' DSL = '''pipeline { - agent none + agent { label 'linux && immutable' } + environment { + PIPELINE_LOG_LEVEL = 'DEBUG' + } + parameters { + booleanParam(name: 'auditbeat', defaultValue: 'true', description: '') + } stages { - stage('linux') { - agent { label 'linux && immutable' } + stage('prepare') { steps { - beatsWhen(project: 'test', - content: 'TBD') + deleteDir() + + writeYaml(file: 'branches.yaml', data: readYaml(text: """ +when: + branches: true +""").when) + + writeYaml(file: 'comments.yaml', data: readYaml(text: """ +when: + comments: + - "/test auditbeat" + - "foo" +""").when) + + writeYaml(file: 'labels.yaml', data: readYaml(text: """ +when: + labels: + - "auditbeat" + - "foo" +""").when) + + writeYaml(file: 'parameters.yaml', data: readYaml(text: """ +when: + parameters: + - "auditbeat" + - "foo" +""").when) + + writeYaml(file: 'tags.yaml', data: readYaml(text: """ +when: + tags: true +""").when) } } + stage('branches') { + environment { BRANCH_NAME = 'foo' } + steps { verify('branches.yaml') } + } + stage('comment') { + environment { GITHUB_COMMENT = 'foo' } + steps { verify('comments.yaml') } + } + stage('labels') { + // labels work only for MBPs + steps { verifyFalse('labels.yaml') } + } + stage('parameters') { + steps { verify('parameters.yaml') } + } + stage('tags') { + environment { TAG_NAME = 'foo' } + steps { verify('tags.yaml') } + } + } +} + +def verify(fileName) { + whenFalse(beatsWhen(project: 'test', content: readYaml(file: fileName))) { + error 'Assert failed' } -}''' +} + +def verifyFalse(fileName) { + whenTrue(beatsWhen(project: 'test', content: readYaml(file: fileName))) { + error 'Assert failed' + } +} +''' pipelineJob(NAME) { definition { diff --git a/vars/beats.groovy b/vars/beats.groovy index 61a431c8b..dc5bf5cd1 100644 --- a/vars/beats.groovy +++ b/vars/beats.groovy @@ -23,3 +23,7 @@ Boolean when(Map args = [:]){ return beatsWhen(args) } + +Map generateStages(Map args = [:]) { + // TODO +} diff --git a/vars/beats.txt b/vars/beats.txt index 0ffb9bcdf..1ab0cb222 100644 --- a/vars/beats.txt +++ b/vars/beats.txt @@ -14,4 +14,22 @@
 
 
+
+ +beats.generateStages() + +

+ fooo + + The list of step's params and the related default values are: +

    +
  • foo:
  • +
  • bar: Defaults to `8`
  • +
+

+ + +
+
+
 
diff --git a/vars/beatsStages.groovy b/vars/beatsStages.groovy new file mode 100644 index 000000000..ec07e700f --- /dev/null +++ b/vars/beatsStages.groovy @@ -0,0 +1,25 @@ +#!/usr/bin/env groovy +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +Boolean call(Map args = [:]){ + def project = args.project + def content = args.content + def patterns = args.changeset + + // TODO +} diff --git a/vars/beatsStages.txt b/vars/beatsStages.txt new file mode 100644 index 000000000..8d43b0e9e --- /dev/null +++ b/vars/beatsStages.txt @@ -0,0 +1,15 @@ +

+ fooo + + The list of step's params and the related default values are: +

    +
  • foo:
  • +
  • bar: Defaults to `8`
  • +
+

+ + +
+
+
+
From 47879e9c82a6f2f782b85ccc4202cb87ba9a09a5 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Tue, 28 Jul 2020 12:05:49 +0100 Subject: [PATCH 10/37] beatsStages --- local/configs/jenkins.yaml | 1 + src/test/resources/jobs/beats/beatsStages.dsl | 106 ++++++++++++++++++ vars/beatsStages.groovy | 36 +++++- vars/beatsStages.txt | 13 ++- 4 files changed, 148 insertions(+), 8 deletions(-) create mode 100644 src/test/resources/jobs/beats/beatsStages.dsl diff --git a/local/configs/jenkins.yaml b/local/configs/jenkins.yaml index 02c72c8fd..7c34dbb08 100644 --- a/local/configs/jenkins.yaml +++ b/local/configs/jenkins.yaml @@ -107,6 +107,7 @@ jobs: - file: "/var/pipeline-library/src/test/resources/folders/getBuildInfoJsonFiles.dsl" - file: "/var/pipeline-library/src/test/resources/folders/it.dsl" - file: "/var/pipeline-library/src/test/resources/folders/timeout.dsl" + - file: "/var/pipeline-library/src/test/resources/jobs/beats/beatsStages.dsl" - file: "/var/pipeline-library/src/test/resources/jobs/beats/beatsWhen.dsl" - file: "/var/pipeline-library/src/test/resources/jobs/cancelPreviousRunningBuilds.dsl" - file: "/var/pipeline-library/src/test/resources/jobs/cmd.dsl" diff --git a/src/test/resources/jobs/beats/beatsStages.dsl b/src/test/resources/jobs/beats/beatsStages.dsl new file mode 100644 index 000000000..e456217d5 --- /dev/null +++ b/src/test/resources/jobs/beats/beatsStages.dsl @@ -0,0 +1,106 @@ +NAME = 'it/beats/beatsStages' +DSL = '''pipeline { + agent { label 'linux && immutable' } + environment { + PIPELINE_LOG_LEVEL = 'DEBUG' + } + stages { + stage('prepare') { + steps { + deleteDir() + + writeYaml(file: 'simple.yaml', data: readYaml(text: """ +platform: + - "linux && ubuntu-16" +stages: + simple: + command: + - "mage build test" +""")) + + writeYaml(file: 'two.yaml', data: readYaml(text: """ +platform: + - "linux && ubuntu-16" +stages: + one: + command: + - "mage build test" + two: + command: + - "make -C auditbeat crosscompile" +""")) + + writeYaml(file: 'complex.yaml', data: readYaml(text: """ +platform: + - "linux && ubuntu-16" +stages: + windows: + command: + - "cd auditbeat && mage build unitTest" + platforms: + - "windows-2019" + - "windows-2016" + when: + comments: + - "/test auditbeat for windows" + parameters: + - "windows" +""")) + } + } + stage('simple') { + steps { + script { + def ret = beatsStages(project: 'test', content: readYaml(file: 'simple.yaml')) + whenFalse(ret.size() == 1) { + error 'Assert failed. There should be just one entry.' + } + ret.each { k,v -> + whenFalse(k.equals('test-simple')) { + error 'Assert failed. Name of the stage does not match.' + } + } + } + } + } + stage('two') { + steps { + script { + def ret = beatsStages(project: 'test', content: readYaml(file: 'two.yaml')) + whenFalse(ret.size() == 2) { + error 'Assert failed. There should be just one entry.' + } + ret.each { k,v -> + whenFalse(k.equals('test-one') || k.equals('test-two')) { + error 'Assert failed. Name of the stage does not match.' + } + } + } + } + } + stage('complex') { + steps { + script { + def ret = beatsStages(project: 'test', content: readYaml(file: 'complex.yaml')) + whenFalse(ret.size() == 2) { + error 'Assert failed. There should be just one entry.' + } + ret.each { k,v -> + whenFalse(k.equals('test-windows-windows-2016') || k.equals('test-windows-windows-2019')) { + error 'Assert failed. Name of the stage does not match.' + } + } + } + } + } + } +} +''' + +pipelineJob(NAME) { + definition { + cps { + script(DSL.stripIndent()) + } + } +} diff --git a/vars/beatsStages.groovy b/vars/beatsStages.groovy index ec07e700f..e6b82d270 100644 --- a/vars/beatsStages.groovy +++ b/vars/beatsStages.groovy @@ -16,10 +16,40 @@ // specific language governing permissions and limitations // under the License. -Boolean call(Map args = [:]){ +/** +* Given the YAML definition then it creates all the stages +*/ +Map call(Map args = [:]){ def project = args.project def content = args.content - def patterns = args.changeset - // TODO + def mapOfStages = [:] + + def defaultNode = content.containsKey('platform') ? content.platform : '' + + content?.stages?.each { name, value -> + if (value.containsKey('platforms')) { + value.platforms.each { platform -> + def stageName = "${project}-${name}-${platform}" + log(level: 'DEBUG', text: "stage: ${stageName}") + mapOfStages[stageName] = generateStage(label: platform, content: value) + } + } else { + def stageName = "${project}-${name}" + log(level: 'DEBUG', text: "stage: ${stageName}") + mapOfStages["${stageName}"] = generateStage(label: defaultNode, content: value) + } + } + + return mapOfStages +} + +private generateStage(Map args = [:]) { + def content = args.content + def label = args.label + return { + // TODO TBDnode(label) { + echo "${content.command} in ${label}" + //} + } } diff --git a/vars/beatsStages.txt b/vars/beatsStages.txt index 8d43b0e9e..406eb1105 100644 --- a/vars/beatsStages.txt +++ b/vars/beatsStages.txt @@ -1,15 +1,18 @@

- fooo + Given the YAML definition then it creates all the stages The list of step's params and the related default values are:

    -
  • foo:
  • -
  • bar: Defaults to `8`
  • +
  • project: the name of the project
  • +
  • content: the content with all the stages and commands to be transformed

-
-
+    script {
+        def mapParallelTasks = [:]
+        beatsWhen(project: 'auditbeat', content: readYaml(file: 'auditbeat/Jenkinsfile.yml'))
+        parallel(mapParallelTasks)
+    }
 
From 3e15f57abf85d96a4e7f0a388800580be6c5e779 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Tue, 28 Jul 2020 12:20:13 +0100 Subject: [PATCH 11/37] beatsStages support with beatsWhen --- src/test/resources/jobs/beats/beatsStages.dsl | 47 +++++++++++++++++-- vars/beatsStages.groovy | 37 +++++++++++---- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/src/test/resources/jobs/beats/beatsStages.dsl b/src/test/resources/jobs/beats/beatsStages.dsl index e456217d5..9d833bb0b 100644 --- a/src/test/resources/jobs/beats/beatsStages.dsl +++ b/src/test/resources/jobs/beats/beatsStages.dsl @@ -4,6 +4,9 @@ DSL = '''pipeline { environment { PIPELINE_LOG_LEVEL = 'DEBUG' } + parameters { + booleanParam(name: 'macos', defaultValue: 'true', description: '') + } stages { stage('prepare') { steps { @@ -30,7 +33,19 @@ stages: - "make -C auditbeat crosscompile" """)) - writeYaml(file: 'complex.yaml', data: readYaml(text: """ + writeYaml(file: 'platforms.yaml', data: readYaml(text: """ +platform: + - "linux && ubuntu-16" +stages: + windows: + command: + - "cd auditbeat && mage build unitTest" + platforms: + - "windows-2019" + - "windows-2016" +""")) + + writeYaml(file: 'when.yaml', data: readYaml(text: """ platform: - "linux && ubuntu-16" stages: @@ -78,10 +93,10 @@ stages: } } } - stage('complex') { + stage('platforms') { steps { script { - def ret = beatsStages(project: 'test', content: readYaml(file: 'complex.yaml')) + def ret = beatsStages(project: 'test', content: readYaml(file: 'platforms.yaml')) whenFalse(ret.size() == 2) { error 'Assert failed. There should be just one entry.' } @@ -93,6 +108,32 @@ stages: } } } + stage('when-with-comment-match') { + environment { + GITHUB_COMMENT = '/test auditbeat for windows' + } + steps { + script { + def ret = beatsStages(project: 'test', content: readYaml(file: 'when.yaml')) + whenFalse(ret.size() == 2) { + error 'Assert failed. There should be just 2 entries.' + } + } + } + } + stage('when-without-comment-match') { + environment { + GITHUB_COMMENT = '/foo' + } + steps { + script { + def ret = beatsStages(project: 'test', content: readYaml(file: 'when.yaml')) + whenFalse(ret.size() == 0) { + error 'Assert failed. There should be just 0 entries.' + } + } + } + } } } ''' diff --git a/vars/beatsStages.groovy b/vars/beatsStages.groovy index e6b82d270..038ed30a9 100644 --- a/vars/beatsStages.groovy +++ b/vars/beatsStages.groovy @@ -27,23 +27,42 @@ Map call(Map args = [:]){ def defaultNode = content.containsKey('platform') ? content.platform : '' - content?.stages?.each { name, value -> - if (value.containsKey('platforms')) { - value.platforms.each { platform -> - def stageName = "${project}-${name}-${platform}" - log(level: 'DEBUG', text: "stage: ${stageName}") - mapOfStages[stageName] = generateStage(label: platform, content: value) + content?.stages?.each { stageName, value -> + def tempMapOfStages = [:] + if (value.containsKey('when')) { + if (beatsWhen(project: project, content: value.when)) { + tempMapOfStages = generateStages(content: value, project: project, name: stageName, defaultNode: defaultNode) } } else { - def stageName = "${project}-${name}" - log(level: 'DEBUG', text: "stage: ${stageName}") - mapOfStages["${stageName}"] = generateStage(label: defaultNode, content: value) + tempMapOfStages = generateStages(content: value, project: project, stageName: stageName, defaultNode: defaultNode) } + tempMapOfStages.each { k,v -> mapOfStages["${k}"] = v } } return mapOfStages } +private generateStages(Map args = [:]) { + def content = args.content + def project = args.project + def stageName = args.stageName + def defaultNode = args.defaultNode + + def mapOfStages = [:] + if (content.containsKey('platforms')) { + content.platforms.each { platform -> + def id = "${project}-${stageName}-${platform}" + log(level: 'DEBUG', text: "stage: ${id}") + mapOfStages[id] = generateStage(label: platform, content: content) + } + } else { + def id = "${project}-${stageName}" + log(level: 'DEBUG', text: "stage: ${id}") + mapOfStages["${id}"] = generateStage(label: defaultNode, content: content) + } + return mapOfStages +} + private generateStage(Map args = [:]) { def content = args.content def label = args.label From afd55fc1e841e929cd6ed308365825ce775bedac Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Tue, 28 Jul 2020 12:56:27 +0100 Subject: [PATCH 12/37] beatsStages UTs --- src/test/groovy/ApmBasePipelineTest.groovy | 1 + src/test/groovy/BeatsStagesStepTests.groovy | 191 ++++++++++++++++++ src/test/resources/jobs/beats/beatsStages.dsl | 12 +- vars/beatsStages.groovy | 13 +- 4 files changed, 203 insertions(+), 14 deletions(-) create mode 100644 src/test/groovy/BeatsStagesStepTests.groovy diff --git a/src/test/groovy/ApmBasePipelineTest.groovy b/src/test/groovy/ApmBasePipelineTest.groovy index 1ab37a1f8..1d304f8c3 100644 --- a/src/test/groovy/ApmBasePipelineTest.groovy +++ b/src/test/groovy/ApmBasePipelineTest.groovy @@ -346,6 +346,7 @@ class ApmBasePipelineTest extends DeclarativePipelineTest { return script.call(m) }) helper.registerAllowedMethod('base64encode', [Map.class], { return "YWRtaW46YWRtaW4xMjMK" }) + helper.registerAllowedMethod('beatsWhen', [Map.class], null) helper.registerAllowedMethod('cancelPreviousRunningBuilds', [Map.class], null) helper.registerAllowedMethod('cmd', [Map.class], { m -> def script = loadScript('vars/cmd.groovy') diff --git a/src/test/groovy/BeatsStagesStepTests.groovy b/src/test/groovy/BeatsStagesStepTests.groovy new file mode 100644 index 000000000..c73ef8311 --- /dev/null +++ b/src/test/groovy/BeatsStagesStepTests.groovy @@ -0,0 +1,191 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import org.junit.Before +import org.junit.After +import org.junit.Test +import static org.junit.Assert.assertFalse +import static org.junit.Assert.assertTrue + +class BeatsStagesStepTests extends ApmBasePipelineTest { + String scriptName = 'vars/beatsStages.groovy' + + @Override + @Before + void setUp() throws Exception { + super.setUp() + } + + @Test + void test_with_no_data() throws Exception { + def script = loadScript(scriptName) + try { + script.call() + } catch (e) { + // NOOP + } + printCallStack() + assertTrue(assertMethodCallContainsPattern('error', 'project param is required')) + assertJobStatusFailure() + } + + @Test + void test_with_no_project() throws Exception { + def script = loadScript(scriptName) + try { + script.call(project: 'foo') + } catch (e) { + // NOOP + } + printCallStack() + assertTrue(assertMethodCallContainsPattern('error', 'content param is required')) + assertJobStatusFailure() + } + + @Test + void test_with_no_platform() throws Exception { + def script = loadScript(scriptName) + try { + script.call(project: 'foo', content: [:]) + } catch (e) { + // NOOP + } + printCallStack() + assertTrue(assertMethodCallContainsPattern('error', 'platform entry in the content is required')) + assertJobStatusFailure() + } + + @Test + void test_missing_command() throws Exception { + def script = loadScript(scriptName) + def data = [ + "platform" : "linux && ubuntu-16", + "stages": [ + "simple" : [ + "foo" : [ "bar" ] + ] + ] + ] + + try { + script.call(project: 'foo', content: data) + } catch (e) { + // NOOP + } + printCallStack() + assertTrue(assertMethodCallContainsPattern('error', 'command entry in the stage is required')) + assertJobStatusFailure() + } + + @Test + void test_simple() throws Exception { + def script = loadScript(scriptName) + def ret = script.call(project: 'foo', content: [ + "platform" : [ "linux && ubuntu-16" ], + "stages": [ + "simple" : [ + "command" : [ "foo" ] + ] + ] + ]) + printCallStack() + assertTrue(ret.size() == 1) + assertTrue(assertMethodCallContainsPattern('log', 'stage: foo-simple')) + assertJobStatusSuccess() + } + + @Test + void test_multiple() throws Exception { + def script = loadScript(scriptName) + def ret = script.call(project: 'foo', content: [ + "platform" : [ "linux && ubuntu-16" ], + "stages": [ + "simple" : [ + "command" : [ "foo" ] + ], + "multi" : [ + "command" : [ "foo" ], + "platforms" : [ 'windows-2019', 'windows-2016' ] + ] + ] + ]) + printCallStack() + assertTrue(ret.size() == 3) + assertTrue(assertMethodCallContainsPattern('log', 'stage: foo-simple')) + assertTrue(assertMethodCallContainsPattern('log', 'stage: foo-multi-windows-2019')) + assertTrue(assertMethodCallContainsPattern('log', 'stage: foo-multi-windows-2016')) + assertJobStatusSuccess() + } + + @Test + void test_multiple_when_without_match() throws Exception { + def script = loadScript(scriptName) + helper.registerAllowedMethod('beatsWhen', [Map.class], {return false}) + def ret = script.call(project: 'foo', content: [ + "platform" : [ "linux && ubuntu-16" ], + "stages": [ + "simple" : [ + "command" : [ "foo" ] + ], + "multi" : [ + "command" : [ "foo" ], + "platforms" : [ 'windows-2019' ] + ], + "multi-when" : [ + "command" : [ "foo" ], + "platforms" : [ 'windows-2016' ], + "when" : [ + "comments" : [ "/test auditbeat for windows" ] + ] + ] + ] + ]) + printCallStack() + assertTrue(ret.size() == 2) + assertFalse(assertMethodCallContainsPattern('log', 'stage: foo-multi-when')) + assertJobStatusSuccess() + } + + @Test + void test_multiple_when_with_match() throws Exception { + def script = loadScript(scriptName) + helper.registerAllowedMethod('beatsWhen', [Map.class], { return true }) + def ret = script.call(project: 'foo', content: [ + "platform" : [ "linux && ubuntu-16" ], + "stages": [ + "simple" : [ + "command" : [ "foo" ] + ], + "multi" : [ + "command" : [ "foo" ], + "platforms" : [ 'windows-2019' ] + ], + "multi-when" : [ + "command" : [ "foo" ], + "platforms" : [ 'windows-2016' ], + "when" : [ + "comments" : [ "/test auditbeat for windows" ] + ] + ] + ] + ]) + printCallStack() + assertTrue(ret.size() == 3) + assertTrue(assertMethodCallContainsPattern('log', 'stage: foo-multi-when')) + assertJobStatusSuccess() + } +} diff --git a/src/test/resources/jobs/beats/beatsStages.dsl b/src/test/resources/jobs/beats/beatsStages.dsl index 9d833bb0b..bc43073ec 100644 --- a/src/test/resources/jobs/beats/beatsStages.dsl +++ b/src/test/resources/jobs/beats/beatsStages.dsl @@ -13,8 +13,7 @@ DSL = '''pipeline { deleteDir() writeYaml(file: 'simple.yaml', data: readYaml(text: """ -platform: - - "linux && ubuntu-16" +platform: "linux && ubuntu-16" stages: simple: command: @@ -22,8 +21,7 @@ stages: """)) writeYaml(file: 'two.yaml', data: readYaml(text: """ -platform: - - "linux && ubuntu-16" +platform: "linux && ubuntu-16" stages: one: command: @@ -34,8 +32,7 @@ stages: """)) writeYaml(file: 'platforms.yaml', data: readYaml(text: """ -platform: - - "linux && ubuntu-16" +platform: "linux && ubuntu-16" stages: windows: command: @@ -46,8 +43,7 @@ stages: """)) writeYaml(file: 'when.yaml', data: readYaml(text: """ -platform: - - "linux && ubuntu-16" +platform: "linux && ubuntu-16" stages: windows: command: diff --git a/vars/beatsStages.groovy b/vars/beatsStages.groovy index 038ed30a9..f8c5617be 100644 --- a/vars/beatsStages.groovy +++ b/vars/beatsStages.groovy @@ -20,18 +20,17 @@ * Given the YAML definition then it creates all the stages */ Map call(Map args = [:]){ - def project = args.project - def content = args.content - - def mapOfStages = [:] + def project = args.containsKey('project') ? args.project : error('beatsStages: project param is required') + def content = args.containsKey('content') ? args.content : error('beatsStages: content param is required') + def defaultNode = content.containsKey('platform') ? content.platform : error('beatsStages: platform entry in the content is required.') - def defaultNode = content.containsKey('platform') ? content.platform : '' + def mapOfStages = [:] content?.stages?.each { stageName, value -> def tempMapOfStages = [:] if (value.containsKey('when')) { if (beatsWhen(project: project, content: value.when)) { - tempMapOfStages = generateStages(content: value, project: project, name: stageName, defaultNode: defaultNode) + tempMapOfStages = generateStages(content: value, project: project, stageName: stageName, defaultNode: defaultNode) } } else { tempMapOfStages = generateStages(content: value, project: project, stageName: stageName, defaultNode: defaultNode) @@ -66,6 +65,8 @@ private generateStages(Map args = [:]) { private generateStage(Map args = [:]) { def content = args.content def label = args.label + + def command = content?.containsKey('command') ? content.command : error('beatsStages: command entry in the stage is required.') return { // TODO TBDnode(label) { echo "${content.command} in ${label}" From 0faa2f5c286a481e893d48c2a4a88068baccef5d Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Tue, 28 Jul 2020 13:03:33 +0100 Subject: [PATCH 13/37] Add docs and UTs for the mandatory entries --- src/test/groovy/BeatsWhenStepTests.groovy | 26 +++++++++++++++++++++++ vars/beatsStages.txt | 6 +++--- vars/beatsWhen.groovy | 4 ++-- vars/beatsWhen.txt | 15 +++++++------ 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/test/groovy/BeatsWhenStepTests.groovy b/src/test/groovy/BeatsWhenStepTests.groovy index b1b51a06d..9d60ea96d 100644 --- a/src/test/groovy/BeatsWhenStepTests.groovy +++ b/src/test/groovy/BeatsWhenStepTests.groovy @@ -30,6 +30,32 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { super.setUp() } + @Test + void test_with_no_data() throws Exception { + def script = loadScript(scriptName) + try { + script.call() + } catch (e) { + // NOOP + } + printCallStack() + assertTrue(assertMethodCallContainsPattern('error', 'project param is required')) + assertJobStatusFailure() + } + + @Test + void test_with_no_project() throws Exception { + def script = loadScript(scriptName) + try { + script.call(project: 'foo') + } catch (e) { + // NOOP + } + printCallStack() + assertTrue(assertMethodCallContainsPattern('error', 'content param is required')) + assertJobStatusFailure() + } + @Test void test_whenBranches_and_no_environment_variable() throws Exception { def script = loadScript(scriptName) diff --git a/vars/beatsStages.txt b/vars/beatsStages.txt index 406eb1105..040d6c0b4 100644 --- a/vars/beatsStages.txt +++ b/vars/beatsStages.txt @@ -3,8 +3,8 @@ The list of step's params and the related default values are:
    -
  • project: the name of the project
  • -
  • content: the content with all the stages and commands to be transformed
  • +
  • project: the name of the project. Mandatory
  • +
  • content: the content with all the stages and commands to be transformed. Mandatory

@@ -12,7 +12,7 @@
     script {
         def mapParallelTasks = [:]
-        beatsWhen(project: 'auditbeat', content: readYaml(file: 'auditbeat/Jenkinsfile.yml'))
+        beatsStages(project: 'auditbeat', content: readYaml(file: 'auditbeat/Jenkinsfile.yml'))
         parallel(mapParallelTasks)
     }
 
diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index b29b0ba19..dc01daa69 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -21,8 +21,8 @@ * then it verifies if the project or stage should be enabled. */ Boolean call(Map args = [:]){ - def project = args.project - def content = args.content + def project = args.containsKey('project') ? args.project : error('beatsWhen: project param is required') + def content = args.containsKey('content') ? args.content : error('beatsWhen: content param is required') def patterns = args.changeset def ret = false diff --git a/vars/beatsWhen.txt b/vars/beatsWhen.txt index 0ffb9bcdf..7058c62ac 100644 --- a/vars/beatsWhen.txt +++ b/vars/beatsWhen.txt @@ -1,17 +1,18 @@ -beats.when() -

- fooo + Given the YAML definition and the changeset global macros + then it verifies if the project or stage should be enabled. The list of step's params and the related default values are:

    -
  • foo:
  • -
  • bar: Defaults to `8`
  • +
  • project: the name of the project. Mandatory
  • +
  • content: the content with the when section. Mandatory
  • +
  • changeset: the global changeset. Optional

-
-
+    whenTrue(beatsWhen(project: 'auditbeat', content: readYaml(file: 'auditbeat/Jenkinsfile.yml')))
+        ...
+    }
 
From 778bff60718108cdda650e0337ebac0e7a7ff914 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Tue, 28 Jul 2020 13:11:11 +0100 Subject: [PATCH 14/37] Add TODO and generate markdwon with the when reasons --- vars/beatsWhen.groovy | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index dc01daa69..1e959b2a6 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -31,7 +31,8 @@ Boolean call(Map args = [:]){ if (whenParameters(args)) { ret = true } if (whenBranches(args)) { ret = true } if (whenTags(args)) { ret = true } - // TODO: changeset validation + if (whenChangeset(args)) { ret = true } + return ret } @@ -44,6 +45,12 @@ private Boolean whenBranches(Map args = [:]) { return ret } +private Boolean whenChangeset(Map args = [:]) { + def ret = false + // TODO + return ret +} + private Boolean whenComments(Map args = [:]) { def ret = false if (args.content?.get('comments') && env.GITHUB_COMMENT?.trim()) { @@ -93,6 +100,10 @@ private Boolean whenTags(Map args = [:]) { } private void markdownReason(Map args = [:]) { - echo "${args.project} - ${args.reason}" - // TODO create markdown + def fileName = "build-${args.project}.md" + def data = '' + if(fileExists(fileName)) { + data = readFile(file: "${fileName}") + } + writeFile file: 'build.sbt', text: "${data}\r\n${args.reason}" } From 61912a6283e6a1cf601f7daed9d62990d2ecdc99 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Tue, 28 Jul 2020 15:21:04 +0100 Subject: [PATCH 15/37] Support changeset with macros --- src/test/groovy/BeatsWhenStepTests.groovy | 53 +++++++++++++++++++++++ vars/beatsWhen.groovy | 43 +++++++++++++++++- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/src/test/groovy/BeatsWhenStepTests.groovy b/src/test/groovy/BeatsWhenStepTests.groovy index 9d60ea96d..d91e79e80 100644 --- a/src/test/groovy/BeatsWhenStepTests.groovy +++ b/src/test/groovy/BeatsWhenStepTests.groovy @@ -79,6 +79,59 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { assertTrue(ret) } + @Test + void test_whenChangeset_and_no_data() throws Exception { + def script = loadScript(scriptName) + def ret = script.whenChangeset() + assertFalse(ret) + } + + @Test + void test_whenChangeset_and_content() throws Exception { + def script = loadScript(scriptName) + def changeset = 'Jenkinsfile' + helper.registerAllowedMethod('readFile', [String.class], { return changeset }) + def ret = script.whenChangeset(content: [ changeset: ['^.ci']]) + assertFalse(ret) + } + + @Test + void test_whenChangeset_and_content_with_match() throws Exception { + def script = loadScript(scriptName) + def changeset = 'Jenkinsfile' + helper.registerAllowedMethod('readFile', [String.class], { return changeset }) + def ret = script.whenChangeset(content: [ changeset: ['^Jenkinsfile']]) + assertTrue(ret) + } + + @Test + void test_whenChangeset_content_and_macro() throws Exception { + def script = loadScript(scriptName) + def ret = script.whenChangeset(content: [ changeset: ['^.ci', '@oss']], + changeset: [ oss: [ '^oss'] ]) + assertFalse(ret) + } + + @Test + void test_whenChangeset_content_and_macro_with_match() throws Exception { + def script = loadScript(scriptName) + def changeset = 'oss' + helper.registerAllowedMethod('readFile', [String.class], { return changeset }) + def ret = script.whenChangeset(content: [ changeset: ['^.ci', '@oss']], + changeset: [ oss: [ '^oss'] ]) + assertTrue(ret) + } + + @Test + void test_whenChangeset_content_and_macro_without_match() throws Exception { + def script = loadScript(scriptName) + def changeset = 'oss' + helper.registerAllowedMethod('readFile', [String.class], { return changeset }) + def ret = script.whenChangeset(content: [ changeset: ['^.ci', '@osss']], + changeset: [ oss: [ '^oss'] ]) + assertFalse(ret) + } + @Test void test_whenComments_and_no_environment_variable() throws Exception { def script = loadScript(scriptName) diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index 1e959b2a6..bd87f7b77 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -23,7 +23,7 @@ Boolean call(Map args = [:]){ def project = args.containsKey('project') ? args.project : error('beatsWhen: project param is required') def content = args.containsKey('content') ? args.content : error('beatsWhen: content param is required') - def patterns = args.changeset + def changeset = args.changeset def ret = false if (whenComments(args)) { ret = true } @@ -47,7 +47,46 @@ private Boolean whenBranches(Map args = [:]) { private Boolean whenChangeset(Map args = [:]) { def ret = false - // TODO + + if (args.content?.get('changeset')) { + // Gather macro changeset entries + def macro = [:] + args?.changeset?.each { k,v -> + macro[k] = v + } + + // Create list of changeset patterns to be searched. + def patterns = [] + args.content.changeset.each { + if (it.startsWith('@')){ + def search = it.replaceAll('@', '') + macro[search].each { macroEntry -> + patterns << macroEntry + } + } else { + patterns << it + } + } + + // TODO: to be refactored with isGitRegionMatch.isPartialPatternMatch() + + // Gather the diff between the target branch and the current commit. + def gitDiffFile = 'git-diff.txt' + def from = env.CHANGE_TARGET?.trim() ? "origin/${env.CHANGE_TARGET}" : env.GIT_PREVIOUS_COMMIT + sh(script: "git diff --name-only ${from}...${env.GIT_BASE_COMMIT} > ${gitDiffFile}", returnStdout: true) + + // Search for any pattern that matches that particular + def fileContent = readFile(gitDiffFile) + ret = patterns?.any { pattern -> + fileContent?.split('\n').any { line -> line ==~ pattern } + } + if (ret) { + markdownReason(project: args.project, reason: 'Changeset is enabled and matches with the pattern.') + } else { + markdownReason(project: args.project, reason: 'Changeset is enabled and does not match with the pattern.') + } + } + return ret } From 4e0d4dc4682f086da42d1c21d981ea9ff3669e51 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Tue, 28 Jul 2020 18:14:23 +0100 Subject: [PATCH 16/37] Enable function to be passed from the pipeline itself --- src/test/groovy/BeatsStagesStepTests.groovy | 47 +++++++++---------- src/test/resources/jobs/beats/beatsStages.dsl | 41 +++++++++++----- vars/beatsStages.groovy | 18 +++---- vars/beatsStages.txt | 8 +++- 4 files changed, 65 insertions(+), 49 deletions(-) diff --git a/src/test/groovy/BeatsStagesStepTests.groovy b/src/test/groovy/BeatsStagesStepTests.groovy index c73ef8311..cda1c61a3 100644 --- a/src/test/groovy/BeatsStagesStepTests.groovy +++ b/src/test/groovy/BeatsStagesStepTests.groovy @@ -24,6 +24,10 @@ import static org.junit.Assert.assertTrue class BeatsStagesStepTests extends ApmBasePipelineTest { String scriptName = 'vars/beatsStages.groovy' + def runCommand(Map args = [:]) { + echo "${args.label}" + } + @Override @Before void setUp() throws Exception { @@ -60,7 +64,7 @@ class BeatsStagesStepTests extends ApmBasePipelineTest { void test_with_no_platform() throws Exception { def script = loadScript(scriptName) try { - script.call(project: 'foo', content: [:]) + script.call(project: 'foo', content: [:], function: this.&runCommand) } catch (e) { // NOOP } @@ -70,24 +74,15 @@ class BeatsStagesStepTests extends ApmBasePipelineTest { } @Test - void test_missing_command() throws Exception { + void test_with_no_function() throws Exception { def script = loadScript(scriptName) - def data = [ - "platform" : "linux && ubuntu-16", - "stages": [ - "simple" : [ - "foo" : [ "bar" ] - ] - ] - ] - try { - script.call(project: 'foo', content: data) + script.call(project: 'foo', content: [:]) } catch (e) { // NOOP } printCallStack() - assertTrue(assertMethodCallContainsPattern('error', 'command entry in the stage is required')) + assertTrue(assertMethodCallContainsPattern('error', 'function param is required')) assertJobStatusFailure() } @@ -98,10 +93,10 @@ class BeatsStagesStepTests extends ApmBasePipelineTest { "platform" : [ "linux && ubuntu-16" ], "stages": [ "simple" : [ - "command" : [ "foo" ] + "mage" : [ "foo" ] ] ] - ]) + ], function: this.&runCommand) printCallStack() assertTrue(ret.size() == 1) assertTrue(assertMethodCallContainsPattern('log', 'stage: foo-simple')) @@ -115,14 +110,14 @@ class BeatsStagesStepTests extends ApmBasePipelineTest { "platform" : [ "linux && ubuntu-16" ], "stages": [ "simple" : [ - "command" : [ "foo" ] + "make" : [ "foo" ] ], "multi" : [ - "command" : [ "foo" ], + "mage" : [ "foo" ], "platforms" : [ 'windows-2019', 'windows-2016' ] ] ] - ]) + ], function: this.&runCommand) printCallStack() assertTrue(ret.size() == 3) assertTrue(assertMethodCallContainsPattern('log', 'stage: foo-simple')) @@ -139,21 +134,21 @@ class BeatsStagesStepTests extends ApmBasePipelineTest { "platform" : [ "linux && ubuntu-16" ], "stages": [ "simple" : [ - "command" : [ "foo" ] + "make" : [ "foo" ] ], "multi" : [ - "command" : [ "foo" ], + "make" : [ "foo" ], "platforms" : [ 'windows-2019' ] ], "multi-when" : [ - "command" : [ "foo" ], + "mage" : [ "foo" ], "platforms" : [ 'windows-2016' ], "when" : [ "comments" : [ "/test auditbeat for windows" ] ] ] ] - ]) + ], function: this.&runCommand) printCallStack() assertTrue(ret.size() == 2) assertFalse(assertMethodCallContainsPattern('log', 'stage: foo-multi-when')) @@ -168,21 +163,21 @@ class BeatsStagesStepTests extends ApmBasePipelineTest { "platform" : [ "linux && ubuntu-16" ], "stages": [ "simple" : [ - "command" : [ "foo" ] + "make" : [ "foo" ] ], "multi" : [ - "command" : [ "foo" ], + "mage" : [ "foo" ], "platforms" : [ 'windows-2019' ] ], "multi-when" : [ - "command" : [ "foo" ], + "mage" : [ "foo" ], "platforms" : [ 'windows-2016' ], "when" : [ "comments" : [ "/test auditbeat for windows" ] ] ] ] - ]) + ], function: this.&runCommand) printCallStack() assertTrue(ret.size() == 3) assertTrue(assertMethodCallContainsPattern('log', 'stage: foo-multi-when')) diff --git a/src/test/resources/jobs/beats/beatsStages.dsl b/src/test/resources/jobs/beats/beatsStages.dsl index bc43073ec..9ea54d51f 100644 --- a/src/test/resources/jobs/beats/beatsStages.dsl +++ b/src/test/resources/jobs/beats/beatsStages.dsl @@ -16,7 +16,7 @@ DSL = '''pipeline { platform: "linux && ubuntu-16" stages: simple: - command: + mage: - "mage build test" """)) @@ -24,10 +24,10 @@ stages: platform: "linux && ubuntu-16" stages: one: - command: + mage: - "mage build test" two: - command: + make: - "make -C auditbeat crosscompile" """)) @@ -35,8 +35,8 @@ stages: platform: "linux && ubuntu-16" stages: windows: - command: - - "cd auditbeat && mage build unitTest" + mage: + - "mage build unitTest" platforms: - "windows-2019" - "windows-2016" @@ -46,8 +46,8 @@ stages: platform: "linux && ubuntu-16" stages: windows: - command: - - "cd auditbeat && mage build unitTest" + mage: + - "mage build unitTest" platforms: - "windows-2019" - "windows-2016" @@ -62,7 +62,7 @@ stages: stage('simple') { steps { script { - def ret = beatsStages(project: 'test', content: readYaml(file: 'simple.yaml')) + def ret = beatsStages(project: 'test', content: readYaml(file: 'simple.yaml'), function: this.&runCommand) whenFalse(ret.size() == 1) { error 'Assert failed. There should be just one entry.' } @@ -71,13 +71,14 @@ stages: error 'Assert failed. Name of the stage does not match.' } } + parallel(ret) } } } stage('two') { steps { script { - def ret = beatsStages(project: 'test', content: readYaml(file: 'two.yaml')) + def ret = beatsStages(project: 'test', content: readYaml(file: 'two.yaml'), function: this.&runCommand) whenFalse(ret.size() == 2) { error 'Assert failed. There should be just one entry.' } @@ -86,13 +87,14 @@ stages: error 'Assert failed. Name of the stage does not match.' } } + parallel(ret) } } } stage('platforms') { steps { script { - def ret = beatsStages(project: 'test', content: readYaml(file: 'platforms.yaml')) + def ret = beatsStages(project: 'test', content: readYaml(file: 'platforms.yaml'), function: this.&runCommand) whenFalse(ret.size() == 2) { error 'Assert failed. There should be just one entry.' } @@ -101,6 +103,7 @@ stages: error 'Assert failed. Name of the stage does not match.' } } + parallel(ret) } } } @@ -110,10 +113,11 @@ stages: } steps { script { - def ret = beatsStages(project: 'test', content: readYaml(file: 'when.yaml')) + def ret = beatsStages(project: 'test', content: readYaml(file: 'when.yaml'), function: this.&runCommand) whenFalse(ret.size() == 2) { error 'Assert failed. There should be just 2 entries.' } + parallel(ret) } } } @@ -123,15 +127,28 @@ stages: } steps { script { - def ret = beatsStages(project: 'test', content: readYaml(file: 'when.yaml')) + def ret = beatsStages(project: 'test', content: readYaml(file: 'when.yaml'), function: this.&runCommand) whenFalse(ret.size() == 0) { error 'Assert failed. There should be just 0 entries.' } + parallel(ret) } } } } } + +// TODO expected to call org.codehaus.groovy.runtime.MethodClosure.call but wound up catching WorkflowScript.runCommand; see: https://jenkins.io/redirect/pipeline-cps-method-mismatches/ +def runCommand(Map args = [:]) { + if (args?.content?.mage) { + dir(args.project) { + echo "mage ${args.label}" + } + } + if (args?.content?.make) { + echo "make ${args.label}" + } +} ''' pipelineJob(NAME) { diff --git a/vars/beatsStages.groovy b/vars/beatsStages.groovy index f8c5617be..ce8037cbf 100644 --- a/vars/beatsStages.groovy +++ b/vars/beatsStages.groovy @@ -22,6 +22,7 @@ Map call(Map args = [:]){ def project = args.containsKey('project') ? args.project : error('beatsStages: project param is required') def content = args.containsKey('content') ? args.content : error('beatsStages: content param is required') + def function = args.containsKey('function') ? args.function : error('beatsStages: function param is required') def defaultNode = content.containsKey('platform') ? content.platform : error('beatsStages: platform entry in the content is required.') def mapOfStages = [:] @@ -30,10 +31,10 @@ Map call(Map args = [:]){ def tempMapOfStages = [:] if (value.containsKey('when')) { if (beatsWhen(project: project, content: value.when)) { - tempMapOfStages = generateStages(content: value, project: project, stageName: stageName, defaultNode: defaultNode) + tempMapOfStages = generateStages(content: value, project: project, stageName: stageName, defaultNode: defaultNode, function: function) } } else { - tempMapOfStages = generateStages(content: value, project: project, stageName: stageName, defaultNode: defaultNode) + tempMapOfStages = generateStages(content: value, project: project, stageName: stageName, defaultNode: defaultNode, function: function) } tempMapOfStages.each { k,v -> mapOfStages["${k}"] = v } } @@ -46,30 +47,29 @@ private generateStages(Map args = [:]) { def project = args.project def stageName = args.stageName def defaultNode = args.defaultNode + def function = args.function def mapOfStages = [:] if (content.containsKey('platforms')) { content.platforms.each { platform -> def id = "${project}-${stageName}-${platform}" log(level: 'DEBUG', text: "stage: ${id}") - mapOfStages[id] = generateStage(label: platform, content: content) + mapOfStages[id] = generateStage(project: project, label: platform, content: content, function: function) } } else { def id = "${project}-${stageName}" log(level: 'DEBUG', text: "stage: ${id}") - mapOfStages["${id}"] = generateStage(label: defaultNode, content: content) + mapOfStages["${id}"] = generateStage(project: project, label: defaultNode, content: content, function: function) } return mapOfStages } private generateStage(Map args = [:]) { + def project = args.project def content = args.content def label = args.label - - def command = content?.containsKey('command') ? content.command : error('beatsStages: command entry in the stage is required.') + def function = args.function return { - // TODO TBDnode(label) { - echo "${content.command} in ${label}" - //} + function(args) } } diff --git a/vars/beatsStages.txt b/vars/beatsStages.txt index 040d6c0b4..fb81a1857 100644 --- a/vars/beatsStages.txt +++ b/vars/beatsStages.txt @@ -5,14 +5,18 @@
  • project: the name of the project. Mandatory
  • content: the content with all the stages and commands to be transformed. Mandatory
  • +
  • function: the function to be called. Mandatory

-
     script {
         def mapParallelTasks = [:]
-        beatsStages(project: 'auditbeat', content: readYaml(file: 'auditbeat/Jenkinsfile.yml'))
+        beatsStages(project: 'auditbeat', content: readYaml(file: 'auditbeat/Jenkinsfile.yml'), function: this.&myFunction)
         parallel(mapParallelTasks)
     }
+
+    def myFunction(Map args = [:]) {
+        ...
+    }
 
From 6f55dbdfec80358b633b6a95435751fca5c36cb2 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Tue, 28 Jul 2020 18:17:20 +0100 Subject: [PATCH 17/37] Fix license header --- vars/beats.groovy | 29 ----------------------------- vars/beats.txt | 35 ----------------------------------- vars/beatsStages.groovy | 1 - vars/beatsWhen.groovy | 1 - 4 files changed, 66 deletions(-) delete mode 100644 vars/beats.groovy delete mode 100644 vars/beats.txt diff --git a/vars/beats.groovy b/vars/beats.groovy deleted file mode 100644 index dc5bf5cd1..000000000 --- a/vars/beats.groovy +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env groovy -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -/** -* Given the YAML definition and the changeset global macros -* then it verifies if the project or stage should be enabled. -*/ -Boolean when(Map args = [:]){ - return beatsWhen(args) -} - -Map generateStages(Map args = [:]) { - // TODO -} diff --git a/vars/beats.txt b/vars/beats.txt deleted file mode 100644 index 1ab0cb222..000000000 --- a/vars/beats.txt +++ /dev/null @@ -1,35 +0,0 @@ -beats.when() - -

- fooo - - The list of step's params and the related default values are: -

    -
  • foo:
  • -
  • bar: Defaults to `8`
  • -
-

- - -
-
-
-
- -beats.generateStages() - -

- fooo - - The list of step's params and the related default values are: -

    -
  • foo:
  • -
  • bar: Defaults to `8`
  • -
-

- - -
-
-
-
diff --git a/vars/beatsStages.groovy b/vars/beatsStages.groovy index ce8037cbf..8d5cec2b8 100644 --- a/vars/beatsStages.groovy +++ b/vars/beatsStages.groovy @@ -1,4 +1,3 @@ -#!/usr/bin/env groovy // Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index bd87f7b77..217939621 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -1,4 +1,3 @@ -#!/usr/bin/env groovy // Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright From da8b06859a22a79522f556efb0dd90207a7d988a Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 29 Jul 2020 08:15:58 +0100 Subject: [PATCH 18/37] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- vars/beatsWhen.groovy | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index 217939621..2b6785192 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -36,12 +36,11 @@ Boolean call(Map args = [:]){ } private Boolean whenBranches(Map args = [:]) { - def ret = false if (env.BRANCH_NAME?.trim() && args.content?.get('branches')) { - ret = true markdownReason(project: args.project, reason: 'Branch is enabled and matches with the pattern.') + return true } - return ret + return false } private Boolean whenChangeset(Map args = [:]) { @@ -90,16 +89,14 @@ private Boolean whenChangeset(Map args = [:]) { } private Boolean whenComments(Map args = [:]) { - def ret = false if (args.content?.get('comments') && env.GITHUB_COMMENT?.trim()) { if (args.content?.get('comments')?.any { env.GITHUB_COMMENT?.toLowerCase()?.contains(it?.toLowerCase()) }) { - ret = true markdownReason(project: args.project, reason: 'Comment is enabled and matches with the pattern.') - } else { - markdownReason(project: args.project, reason: 'Comment is enabled and does not match with the pattern.') + return true } + markdownReason(project: args.project, reason: 'Comment is enabled and does not match with the pattern.') } - return ret + return false } private Boolean whenLabels(Map args = [:]) { From 873d0272421b03d5924567dde7f6c500c9ee4cef Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 29 Jul 2020 08:24:40 +0100 Subject: [PATCH 19/37] As suggested in the code review --- vars/beatsWhen.groovy | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index 2b6785192..7b9847e01 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -25,12 +25,12 @@ Boolean call(Map args = [:]){ def changeset = args.changeset def ret = false + if (whenBranches(args)) { ret = true } + if (whenChangeset(args)) { ret = true } if (whenComments(args)) { ret = true } if (whenLabels(args)) { ret = true } if (whenParameters(args)) { ret = true } - if (whenBranches(args)) { ret = true } if (whenTags(args)) { ret = true } - if (whenChangeset(args)) { ret = true } return ret } @@ -44,8 +44,6 @@ private Boolean whenBranches(Map args = [:]) { } private Boolean whenChangeset(Map args = [:]) { - def ret = false - if (args.content?.get('changeset')) { // Gather macro changeset entries def macro = [:] @@ -80,12 +78,13 @@ private Boolean whenChangeset(Map args = [:]) { } if (ret) { markdownReason(project: args.project, reason: 'Changeset is enabled and matches with the pattern.') + return true } else { markdownReason(project: args.project, reason: 'Changeset is enabled and does not match with the pattern.') } } - return ret + return false } private Boolean whenComments(Map args = [:]) { @@ -100,38 +99,35 @@ private Boolean whenComments(Map args = [:]) { } private Boolean whenLabels(Map args = [:]) { - def ret = false if (args.content?.get('labels')) { if (args.content?.get('labels')?.any { matchesPrLabel(label: it) }) { - ret = true markdownReason(project: args.project, reason: 'Label is enabled and matches with the pattern.') + return true } else { markdownReason(project: args.project, reason: 'Label is enabled and does not match with the pattern.') } } - return ret + return false } private Boolean whenParameters(Map args = [:]) { - def ret = false if (args.content?.get('parameters')) { if (args.content?.get('parameters')?.any { params[it] }) { - ret = true markdownReason(project: args.project, reason: 'Parameter is enabled and matches with the pattern.') + return true } else { markdownReason(project: args.project, reason: 'Parameter is enabled and does not match with the pattern.') } } - return ret + return false } private Boolean whenTags(Map args = [:]) { - def ret = false if (env.TAG_NAME?.trim() && args.content?.get('tags')) { - ret = true markdownReason(project: args.project, reason: 'Tag is enabled and matches with the pattern.') + return true } - return ret + return false } private void markdownReason(Map args = [:]) { From 71fe04ce93d2b81b4acf9865310a503251b85d8b Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 29 Jul 2020 08:50:21 +0100 Subject: [PATCH 20/37] As suggested in the code review Add parameters to the call that are required rather than everything --- src/test/groovy/BeatsWhenStepTests.groovy | 52 ++++++++-------- vars/beatsWhen.groovy | 76 ++++++++++++++--------- 2 files changed, 74 insertions(+), 54 deletions(-) diff --git a/src/test/groovy/BeatsWhenStepTests.groovy b/src/test/groovy/BeatsWhenStepTests.groovy index d91e79e80..3e6a1b152 100644 --- a/src/test/groovy/BeatsWhenStepTests.groovy +++ b/src/test/groovy/BeatsWhenStepTests.groovy @@ -59,7 +59,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { @Test void test_whenBranches_and_no_environment_variable() throws Exception { def script = loadScript(scriptName) - def ret = script.whenBranches() + def ret = script.whenBranches(project: 'foo') assertFalse(ret) } @@ -67,7 +67,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenBranches_and_environment_variable_but_no_data() throws Exception { def script = loadScript(scriptName) env.BRANCH_NAME = 'branch' - def ret = script.whenBranches(content: [:]) + def ret = script.whenBranches(project: 'foo') assertFalse(ret) } @@ -75,14 +75,14 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenBranches_and_environment_variable_with_data() throws Exception { def script = loadScript(scriptName) env.BRANCH_NAME = 'branch' - def ret = script.whenBranches(content: [ branches: true]) + def ret = script.whenBranches(isBranch: true, project: 'foo') assertTrue(ret) } @Test void test_whenChangeset_and_no_data() throws Exception { def script = loadScript(scriptName) - def ret = script.whenChangeset() + def ret = script.whenChangeset(project: 'foo') assertFalse(ret) } @@ -91,7 +91,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) def changeset = 'Jenkinsfile' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) - def ret = script.whenChangeset(content: [ changeset: ['^.ci']]) + def ret = script.whenChangeset(changeset: ['^.ci'], project: 'foo') assertFalse(ret) } @@ -100,15 +100,15 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) def changeset = 'Jenkinsfile' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) - def ret = script.whenChangeset(content: [ changeset: ['^Jenkinsfile']]) + def ret = script.whenChangeset(changeset: ['^Jenkinsfile'], project: 'foo') assertTrue(ret) } @Test void test_whenChangeset_content_and_macro() throws Exception { def script = loadScript(scriptName) - def ret = script.whenChangeset(content: [ changeset: ['^.ci', '@oss']], - changeset: [ oss: [ '^oss'] ]) + def ret = script.whenChangeset(changeset: ['^.ci', '@oss'], + macros: [ oss: [ '^oss'] ], project: 'foo') assertFalse(ret) } @@ -117,8 +117,8 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) def changeset = 'oss' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) - def ret = script.whenChangeset(content: [ changeset: ['^.ci', '@oss']], - changeset: [ oss: [ '^oss'] ]) + def ret = script.whenChangeset(changeset: ['^.ci', '@oss'], + macros: [ oss: [ '^oss'] ], project: 'foo') assertTrue(ret) } @@ -127,15 +127,15 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) def changeset = 'oss' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) - def ret = script.whenChangeset(content: [ changeset: ['^.ci', '@osss']], - changeset: [ oss: [ '^oss'] ]) + def ret = script.whenChangeset(changeset: ['^.ci', '@osss'], + macros: [ oss: [ '^oss'] ], project: 'foo') assertFalse(ret) } @Test void test_whenComments_and_no_environment_variable() throws Exception { def script = loadScript(scriptName) - def ret = script.whenComments() + def ret = script.whenComments(project: 'foo') assertFalse(ret) } @@ -143,7 +143,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenComments_and_environment_variable_but_no_data() throws Exception { def script = loadScript(scriptName) env.GITHUB_COMMENT = 'branch' - def ret = script.whenComments(content: [:]) + def ret = script.whenComments(project: 'foo') assertFalse(ret) } @@ -151,7 +151,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenComments_and_environment_variable_with_match() throws Exception { def script = loadScript(scriptName) env.GITHUB_COMMENT = '/test foo' - def ret = script.whenComments(content: [ comments: ['/test foo']]) + def ret = script.whenComments(comments: ['/test foo'], project: 'foo') assertTrue(ret) } @@ -159,14 +159,14 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenComments_and_environment_variable_without_match() throws Exception { def script = loadScript(scriptName) env.GITHUB_COMMENT = '/test foo' - def ret = script.whenComments(content: [ comments: ['/run bla', '/test bar']]) + def ret = script.whenComments(comments: ['/run bla', '/test bar'], project: 'foo') assertFalse(ret) } @Test void test_whenLabels_and_no_data() throws Exception { def script = loadScript(scriptName) - def ret = script.whenLabels(content: [:]) + def ret = script.whenLabels(project: 'foo') assertFalse(ret) } @@ -174,7 +174,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenLabels_with_match() throws Exception { def script = loadScript(scriptName) helper.registerAllowedMethod('matchesPrLabel', [Map.class], { true }) - def ret = script.whenLabels(content: [ labels: ['foo']]) + def ret = script.whenLabels(labels: ['bar'], project: 'foo') assertTrue(ret) } @@ -182,42 +182,42 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenLabels_without_match() throws Exception { def script = loadScript(scriptName) helper.registerAllowedMethod('matchesPrLabel', [Map.class], { false }) - def ret = script.whenLabels(content: [ labels: ['foo']]) + def ret = script.whenLabels(labels: ['bar'], project: 'foo') assertFalse(ret) } @Test void test_whenParameters_and_no_params() throws Exception { def script = loadScript(scriptName) - def ret = script.whenParameters() + def ret = script.whenParameters(project: 'foo') assertFalse(ret) } @Test void test_whenParameters_and_params_without_match() throws Exception { def script = loadScript(scriptName) - def ret = script.whenParameters(content: [ parameters : [ 'foo', 'bar']]) + def ret = script.whenParameters(parameters : [ 'foo', 'bar'], project: 'foo') assertFalse(ret) } void test_whenParameters_and_params_with_match() throws Exception { def script = loadScript(scriptName) params.bar = true - def ret = script.whenParameters(content: [ parameters : [ 'foo', 'bar']]) + def ret = script.whenParameters(parameters : [ 'foo', 'bar'], project: 'foo') assertTrue(ret) } void test_whenParameters_and_params_with_match_but_disabled() throws Exception { def script = loadScript(scriptName) params.bar = false - def ret = script.whenParameters(content: [ parameters : [ 'foo', 'bar']]) + def ret = script.whenParameters(parameters : [ 'foo', 'bar'], project: 'foo') assertFalse(ret) } @Test void test_whenTags_and_no_environment_variable() throws Exception { def script = loadScript(scriptName) - def ret = script.whenTags() + def ret = script.whenTags(project: 'foo') assertFalse(ret) } @@ -225,7 +225,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenTags_and_environment_variable_but_no_data() throws Exception { def script = loadScript(scriptName) env.TAG_NAME = 'tag' - def ret = script.whenTags(content: [:]) + def ret = script.whenTags(project: 'foo') assertFalse(ret) } @@ -233,7 +233,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenTags_and_environment_variable_with_data() throws Exception { def script = loadScript(scriptName) env.TAG_NAME = 'tag' - def ret = script.whenTags(content: [ tags: true]) + def ret = script.whenTags(isTag: true, project: 'foo') assertTrue(ret) } } diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index 7b9847e01..e3638ed74 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -22,38 +22,50 @@ Boolean call(Map args = [:]){ def project = args.containsKey('project') ? args.project : error('beatsWhen: project param is required') def content = args.containsKey('content') ? args.content : error('beatsWhen: content param is required') - def changeset = args.changeset + def macros = args.get('changeset', [:]) def ret = false - if (whenBranches(args)) { ret = true } - if (whenChangeset(args)) { ret = true } - if (whenComments(args)) { ret = true } - if (whenLabels(args)) { ret = true } - if (whenParameters(args)) { ret = true } - if (whenTags(args)) { ret = true } + def changeset = content.get('changeset', []) + def comments = content.get('comments', []) + def isBranch = content.get('branches', false) + def isTag = content.get('tags', false) + def labels = content.get('labels', []) + def parameters = content.get('parameters', []) + if (whenBranches(isBranch: isBranch, project: project)) { ret = true } + if (whenChangeset(changeset: changeset, macros: macros, project: project)) { ret = true } + if (whenComments(comments: comments, project: project)) { ret = true } + if (whenLabels(labels: labels, project: project)) { ret = true } + if (whenParameters(parameters: parameters, project: project)) { ret = true } + if (whenTags(isTag: isTag, project: project)) { ret = true } return ret } private Boolean whenBranches(Map args = [:]) { - if (env.BRANCH_NAME?.trim() && args.content?.get('branches')) { - markdownReason(project: args.project, reason: 'Branch is enabled and matches with the pattern.') + def isBranch = args.get('isBranch', false) + def project = args.project + if (env.BRANCH_NAME?.trim() && isBranch) { + markdownReason(project: project, reason: 'Branch is enabled and matches with the pattern.') return true } return false } private Boolean whenChangeset(Map args = [:]) { - if (args.content?.get('changeset')) { + def macros = args.get('macros', [:]) + def changeset = args.get('changeset', []) + def project = args.project + + if (changeset) { // Gather macro changeset entries def macro = [:] - args?.changeset?.each { k,v -> + macros.each { k,v -> macro[k] = v } // Create list of changeset patterns to be searched. def patterns = [] - args.content.changeset.each { + changeset.each { if (it.startsWith('@')){ def search = it.replaceAll('@', '') macro[search].each { macroEntry -> @@ -77,10 +89,10 @@ private Boolean whenChangeset(Map args = [:]) { fileContent?.split('\n').any { line -> line ==~ pattern } } if (ret) { - markdownReason(project: args.project, reason: 'Changeset is enabled and matches with the pattern.') + markdownReason(project: project, reason: 'Changeset is enabled and matches with the pattern.') return true } else { - markdownReason(project: args.project, reason: 'Changeset is enabled and does not match with the pattern.') + markdownReason(project: project, reason: 'Changeset is enabled and does not match with the pattern.') } } @@ -88,43 +100,51 @@ private Boolean whenChangeset(Map args = [:]) { } private Boolean whenComments(Map args = [:]) { - if (args.content?.get('comments') && env.GITHUB_COMMENT?.trim()) { - if (args.content?.get('comments')?.any { env.GITHUB_COMMENT?.toLowerCase()?.contains(it?.toLowerCase()) }) { - markdownReason(project: args.project, reason: 'Comment is enabled and matches with the pattern.') + def comments = args.get('comments', []) + def project = args.project + if (comments && env.GITHUB_COMMENT?.trim()) { + if (comments.any { env.GITHUB_COMMENT?.toLowerCase()?.contains(it?.toLowerCase()) }) { + markdownReason(project: project, reason: 'Comment is enabled and matches with the pattern.') return true } - markdownReason(project: args.project, reason: 'Comment is enabled and does not match with the pattern.') + markdownReason(project: project, reason: 'Comment is enabled and does not match with the pattern.') } return false } private Boolean whenLabels(Map args = [:]) { - if (args.content?.get('labels')) { - if (args.content?.get('labels')?.any { matchesPrLabel(label: it) }) { - markdownReason(project: args.project, reason: 'Label is enabled and matches with the pattern.') + def labels = args.get('labels', []) + def project = args.project + if (labels) { + if (labels.any { matchesPrLabel(label: it) }) { + markdownReason(project: project, reason: 'Label is enabled and matches with the pattern.') return true } else { - markdownReason(project: args.project, reason: 'Label is enabled and does not match with the pattern.') + markdownReason(project: project, reason: 'Label is enabled and does not match with the pattern.') } } return false } private Boolean whenParameters(Map args = [:]) { - if (args.content?.get('parameters')) { - if (args.content?.get('parameters')?.any { params[it] }) { - markdownReason(project: args.project, reason: 'Parameter is enabled and matches with the pattern.') + def parameters = args.get('parameters', []) + def project = args.project + if (parameters) { + if (parameters.any { params[it] }) { + markdownReason(project: project, reason: 'Parameter is enabled and matches with the pattern.') return true } else { - markdownReason(project: args.project, reason: 'Parameter is enabled and does not match with the pattern.') + markdownReason(project: project, reason: 'Parameter is enabled and does not match with the pattern.') } } return false } private Boolean whenTags(Map args = [:]) { - if (env.TAG_NAME?.trim() && args.content?.get('tags')) { - markdownReason(project: args.project, reason: 'Tag is enabled and matches with the pattern.') + def isTag = args.get('isTag', false) + def project = args.project + if (env.TAG_NAME?.trim() && isTag) { + markdownReason(project: project, reason: 'Tag is enabled and matches with the pattern.') return true } return false From 799a0e2a2ff2f82620b836d2bfd175ccf3aae9d7 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 29 Jul 2020 10:17:00 +0100 Subject: [PATCH 21/37] Revert "As suggested in the code review" This reverts commit 71fe04ce93d2b81b4acf9865310a503251b85d8b. --- src/test/groovy/BeatsWhenStepTests.groovy | 52 ++++++++-------- vars/beatsWhen.groovy | 76 +++++++++-------------- 2 files changed, 54 insertions(+), 74 deletions(-) diff --git a/src/test/groovy/BeatsWhenStepTests.groovy b/src/test/groovy/BeatsWhenStepTests.groovy index 3e6a1b152..d91e79e80 100644 --- a/src/test/groovy/BeatsWhenStepTests.groovy +++ b/src/test/groovy/BeatsWhenStepTests.groovy @@ -59,7 +59,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { @Test void test_whenBranches_and_no_environment_variable() throws Exception { def script = loadScript(scriptName) - def ret = script.whenBranches(project: 'foo') + def ret = script.whenBranches() assertFalse(ret) } @@ -67,7 +67,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenBranches_and_environment_variable_but_no_data() throws Exception { def script = loadScript(scriptName) env.BRANCH_NAME = 'branch' - def ret = script.whenBranches(project: 'foo') + def ret = script.whenBranches(content: [:]) assertFalse(ret) } @@ -75,14 +75,14 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenBranches_and_environment_variable_with_data() throws Exception { def script = loadScript(scriptName) env.BRANCH_NAME = 'branch' - def ret = script.whenBranches(isBranch: true, project: 'foo') + def ret = script.whenBranches(content: [ branches: true]) assertTrue(ret) } @Test void test_whenChangeset_and_no_data() throws Exception { def script = loadScript(scriptName) - def ret = script.whenChangeset(project: 'foo') + def ret = script.whenChangeset() assertFalse(ret) } @@ -91,7 +91,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) def changeset = 'Jenkinsfile' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) - def ret = script.whenChangeset(changeset: ['^.ci'], project: 'foo') + def ret = script.whenChangeset(content: [ changeset: ['^.ci']]) assertFalse(ret) } @@ -100,15 +100,15 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) def changeset = 'Jenkinsfile' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) - def ret = script.whenChangeset(changeset: ['^Jenkinsfile'], project: 'foo') + def ret = script.whenChangeset(content: [ changeset: ['^Jenkinsfile']]) assertTrue(ret) } @Test void test_whenChangeset_content_and_macro() throws Exception { def script = loadScript(scriptName) - def ret = script.whenChangeset(changeset: ['^.ci', '@oss'], - macros: [ oss: [ '^oss'] ], project: 'foo') + def ret = script.whenChangeset(content: [ changeset: ['^.ci', '@oss']], + changeset: [ oss: [ '^oss'] ]) assertFalse(ret) } @@ -117,8 +117,8 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) def changeset = 'oss' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) - def ret = script.whenChangeset(changeset: ['^.ci', '@oss'], - macros: [ oss: [ '^oss'] ], project: 'foo') + def ret = script.whenChangeset(content: [ changeset: ['^.ci', '@oss']], + changeset: [ oss: [ '^oss'] ]) assertTrue(ret) } @@ -127,15 +127,15 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) def changeset = 'oss' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) - def ret = script.whenChangeset(changeset: ['^.ci', '@osss'], - macros: [ oss: [ '^oss'] ], project: 'foo') + def ret = script.whenChangeset(content: [ changeset: ['^.ci', '@osss']], + changeset: [ oss: [ '^oss'] ]) assertFalse(ret) } @Test void test_whenComments_and_no_environment_variable() throws Exception { def script = loadScript(scriptName) - def ret = script.whenComments(project: 'foo') + def ret = script.whenComments() assertFalse(ret) } @@ -143,7 +143,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenComments_and_environment_variable_but_no_data() throws Exception { def script = loadScript(scriptName) env.GITHUB_COMMENT = 'branch' - def ret = script.whenComments(project: 'foo') + def ret = script.whenComments(content: [:]) assertFalse(ret) } @@ -151,7 +151,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenComments_and_environment_variable_with_match() throws Exception { def script = loadScript(scriptName) env.GITHUB_COMMENT = '/test foo' - def ret = script.whenComments(comments: ['/test foo'], project: 'foo') + def ret = script.whenComments(content: [ comments: ['/test foo']]) assertTrue(ret) } @@ -159,14 +159,14 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenComments_and_environment_variable_without_match() throws Exception { def script = loadScript(scriptName) env.GITHUB_COMMENT = '/test foo' - def ret = script.whenComments(comments: ['/run bla', '/test bar'], project: 'foo') + def ret = script.whenComments(content: [ comments: ['/run bla', '/test bar']]) assertFalse(ret) } @Test void test_whenLabels_and_no_data() throws Exception { def script = loadScript(scriptName) - def ret = script.whenLabels(project: 'foo') + def ret = script.whenLabels(content: [:]) assertFalse(ret) } @@ -174,7 +174,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenLabels_with_match() throws Exception { def script = loadScript(scriptName) helper.registerAllowedMethod('matchesPrLabel', [Map.class], { true }) - def ret = script.whenLabels(labels: ['bar'], project: 'foo') + def ret = script.whenLabels(content: [ labels: ['foo']]) assertTrue(ret) } @@ -182,42 +182,42 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenLabels_without_match() throws Exception { def script = loadScript(scriptName) helper.registerAllowedMethod('matchesPrLabel', [Map.class], { false }) - def ret = script.whenLabels(labels: ['bar'], project: 'foo') + def ret = script.whenLabels(content: [ labels: ['foo']]) assertFalse(ret) } @Test void test_whenParameters_and_no_params() throws Exception { def script = loadScript(scriptName) - def ret = script.whenParameters(project: 'foo') + def ret = script.whenParameters() assertFalse(ret) } @Test void test_whenParameters_and_params_without_match() throws Exception { def script = loadScript(scriptName) - def ret = script.whenParameters(parameters : [ 'foo', 'bar'], project: 'foo') + def ret = script.whenParameters(content: [ parameters : [ 'foo', 'bar']]) assertFalse(ret) } void test_whenParameters_and_params_with_match() throws Exception { def script = loadScript(scriptName) params.bar = true - def ret = script.whenParameters(parameters : [ 'foo', 'bar'], project: 'foo') + def ret = script.whenParameters(content: [ parameters : [ 'foo', 'bar']]) assertTrue(ret) } void test_whenParameters_and_params_with_match_but_disabled() throws Exception { def script = loadScript(scriptName) params.bar = false - def ret = script.whenParameters(parameters : [ 'foo', 'bar'], project: 'foo') + def ret = script.whenParameters(content: [ parameters : [ 'foo', 'bar']]) assertFalse(ret) } @Test void test_whenTags_and_no_environment_variable() throws Exception { def script = loadScript(scriptName) - def ret = script.whenTags(project: 'foo') + def ret = script.whenTags() assertFalse(ret) } @@ -225,7 +225,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenTags_and_environment_variable_but_no_data() throws Exception { def script = loadScript(scriptName) env.TAG_NAME = 'tag' - def ret = script.whenTags(project: 'foo') + def ret = script.whenTags(content: [:]) assertFalse(ret) } @@ -233,7 +233,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenTags_and_environment_variable_with_data() throws Exception { def script = loadScript(scriptName) env.TAG_NAME = 'tag' - def ret = script.whenTags(isTag: true, project: 'foo') + def ret = script.whenTags(content: [ tags: true]) assertTrue(ret) } } diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index e3638ed74..7b9847e01 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -22,50 +22,38 @@ Boolean call(Map args = [:]){ def project = args.containsKey('project') ? args.project : error('beatsWhen: project param is required') def content = args.containsKey('content') ? args.content : error('beatsWhen: content param is required') - def macros = args.get('changeset', [:]) + def changeset = args.changeset def ret = false - def changeset = content.get('changeset', []) - def comments = content.get('comments', []) - def isBranch = content.get('branches', false) - def isTag = content.get('tags', false) - def labels = content.get('labels', []) - def parameters = content.get('parameters', []) + if (whenBranches(args)) { ret = true } + if (whenChangeset(args)) { ret = true } + if (whenComments(args)) { ret = true } + if (whenLabels(args)) { ret = true } + if (whenParameters(args)) { ret = true } + if (whenTags(args)) { ret = true } - if (whenBranches(isBranch: isBranch, project: project)) { ret = true } - if (whenChangeset(changeset: changeset, macros: macros, project: project)) { ret = true } - if (whenComments(comments: comments, project: project)) { ret = true } - if (whenLabels(labels: labels, project: project)) { ret = true } - if (whenParameters(parameters: parameters, project: project)) { ret = true } - if (whenTags(isTag: isTag, project: project)) { ret = true } return ret } private Boolean whenBranches(Map args = [:]) { - def isBranch = args.get('isBranch', false) - def project = args.project - if (env.BRANCH_NAME?.trim() && isBranch) { - markdownReason(project: project, reason: 'Branch is enabled and matches with the pattern.') + if (env.BRANCH_NAME?.trim() && args.content?.get('branches')) { + markdownReason(project: args.project, reason: 'Branch is enabled and matches with the pattern.') return true } return false } private Boolean whenChangeset(Map args = [:]) { - def macros = args.get('macros', [:]) - def changeset = args.get('changeset', []) - def project = args.project - - if (changeset) { + if (args.content?.get('changeset')) { // Gather macro changeset entries def macro = [:] - macros.each { k,v -> + args?.changeset?.each { k,v -> macro[k] = v } // Create list of changeset patterns to be searched. def patterns = [] - changeset.each { + args.content.changeset.each { if (it.startsWith('@')){ def search = it.replaceAll('@', '') macro[search].each { macroEntry -> @@ -89,10 +77,10 @@ private Boolean whenChangeset(Map args = [:]) { fileContent?.split('\n').any { line -> line ==~ pattern } } if (ret) { - markdownReason(project: project, reason: 'Changeset is enabled and matches with the pattern.') + markdownReason(project: args.project, reason: 'Changeset is enabled and matches with the pattern.') return true } else { - markdownReason(project: project, reason: 'Changeset is enabled and does not match with the pattern.') + markdownReason(project: args.project, reason: 'Changeset is enabled and does not match with the pattern.') } } @@ -100,51 +88,43 @@ private Boolean whenChangeset(Map args = [:]) { } private Boolean whenComments(Map args = [:]) { - def comments = args.get('comments', []) - def project = args.project - if (comments && env.GITHUB_COMMENT?.trim()) { - if (comments.any { env.GITHUB_COMMENT?.toLowerCase()?.contains(it?.toLowerCase()) }) { - markdownReason(project: project, reason: 'Comment is enabled and matches with the pattern.') + if (args.content?.get('comments') && env.GITHUB_COMMENT?.trim()) { + if (args.content?.get('comments')?.any { env.GITHUB_COMMENT?.toLowerCase()?.contains(it?.toLowerCase()) }) { + markdownReason(project: args.project, reason: 'Comment is enabled and matches with the pattern.') return true } - markdownReason(project: project, reason: 'Comment is enabled and does not match with the pattern.') + markdownReason(project: args.project, reason: 'Comment is enabled and does not match with the pattern.') } return false } private Boolean whenLabels(Map args = [:]) { - def labels = args.get('labels', []) - def project = args.project - if (labels) { - if (labels.any { matchesPrLabel(label: it) }) { - markdownReason(project: project, reason: 'Label is enabled and matches with the pattern.') + if (args.content?.get('labels')) { + if (args.content?.get('labels')?.any { matchesPrLabel(label: it) }) { + markdownReason(project: args.project, reason: 'Label is enabled and matches with the pattern.') return true } else { - markdownReason(project: project, reason: 'Label is enabled and does not match with the pattern.') + markdownReason(project: args.project, reason: 'Label is enabled and does not match with the pattern.') } } return false } private Boolean whenParameters(Map args = [:]) { - def parameters = args.get('parameters', []) - def project = args.project - if (parameters) { - if (parameters.any { params[it] }) { - markdownReason(project: project, reason: 'Parameter is enabled and matches with the pattern.') + if (args.content?.get('parameters')) { + if (args.content?.get('parameters')?.any { params[it] }) { + markdownReason(project: args.project, reason: 'Parameter is enabled and matches with the pattern.') return true } else { - markdownReason(project: project, reason: 'Parameter is enabled and does not match with the pattern.') + markdownReason(project: args.project, reason: 'Parameter is enabled and does not match with the pattern.') } } return false } private Boolean whenTags(Map args = [:]) { - def isTag = args.get('isTag', false) - def project = args.project - if (env.TAG_NAME?.trim() && isTag) { - markdownReason(project: project, reason: 'Tag is enabled and matches with the pattern.') + if (env.TAG_NAME?.trim() && args.content?.get('tags')) { + markdownReason(project: args.project, reason: 'Tag is enabled and matches with the pattern.') return true } return false From 97a40651d66cc7bc9f066307530e83cfd2c64aa2 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 29 Jul 2020 11:51:33 +0100 Subject: [PATCH 22/37] Add markdown reasons --- src/test/groovy/BeatsWhenStepTests.groovy | 23 ++++++++++++++ vars/beatsWhen.groovy | 37 +++++++++++++++-------- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/test/groovy/BeatsWhenStepTests.groovy b/src/test/groovy/BeatsWhenStepTests.groovy index d91e79e80..9216f0d0c 100644 --- a/src/test/groovy/BeatsWhenStepTests.groovy +++ b/src/test/groovy/BeatsWhenStepTests.groovy @@ -60,6 +60,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenBranches_and_no_environment_variable() throws Exception { def script = loadScript(scriptName) def ret = script.whenBranches() + printCallStack() assertFalse(ret) } @@ -68,6 +69,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) env.BRANCH_NAME = 'branch' def ret = script.whenBranches(content: [:]) + printCallStack() assertFalse(ret) } @@ -76,6 +78,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) env.BRANCH_NAME = 'branch' def ret = script.whenBranches(content: [ branches: true]) + printCallStack() assertTrue(ret) } @@ -83,6 +86,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenChangeset_and_no_data() throws Exception { def script = loadScript(scriptName) def ret = script.whenChangeset() + printCallStack() assertFalse(ret) } @@ -92,6 +96,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def changeset = 'Jenkinsfile' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) def ret = script.whenChangeset(content: [ changeset: ['^.ci']]) + printCallStack() assertFalse(ret) } @@ -101,6 +106,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def changeset = 'Jenkinsfile' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) def ret = script.whenChangeset(content: [ changeset: ['^Jenkinsfile']]) + printCallStack() assertTrue(ret) } @@ -109,6 +115,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) def ret = script.whenChangeset(content: [ changeset: ['^.ci', '@oss']], changeset: [ oss: [ '^oss'] ]) + printCallStack() assertFalse(ret) } @@ -119,6 +126,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { helper.registerAllowedMethod('readFile', [String.class], { return changeset }) def ret = script.whenChangeset(content: [ changeset: ['^.ci', '@oss']], changeset: [ oss: [ '^oss'] ]) + printCallStack() assertTrue(ret) } @@ -129,6 +137,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { helper.registerAllowedMethod('readFile', [String.class], { return changeset }) def ret = script.whenChangeset(content: [ changeset: ['^.ci', '@osss']], changeset: [ oss: [ '^oss'] ]) + printCallStack() assertFalse(ret) } @@ -136,6 +145,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenComments_and_no_environment_variable() throws Exception { def script = loadScript(scriptName) def ret = script.whenComments() + printCallStack() assertFalse(ret) } @@ -144,6 +154,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) env.GITHUB_COMMENT = 'branch' def ret = script.whenComments(content: [:]) + printCallStack() assertFalse(ret) } @@ -152,6 +163,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) env.GITHUB_COMMENT = '/test foo' def ret = script.whenComments(content: [ comments: ['/test foo']]) + printCallStack() assertTrue(ret) } @@ -160,6 +172,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) env.GITHUB_COMMENT = '/test foo' def ret = script.whenComments(content: [ comments: ['/run bla', '/test bar']]) + printCallStack() assertFalse(ret) } @@ -167,6 +180,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenLabels_and_no_data() throws Exception { def script = loadScript(scriptName) def ret = script.whenLabels(content: [:]) + printCallStack() assertFalse(ret) } @@ -175,6 +189,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) helper.registerAllowedMethod('matchesPrLabel', [Map.class], { true }) def ret = script.whenLabels(content: [ labels: ['foo']]) + printCallStack() assertTrue(ret) } @@ -183,6 +198,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) helper.registerAllowedMethod('matchesPrLabel', [Map.class], { false }) def ret = script.whenLabels(content: [ labels: ['foo']]) + printCallStack() assertFalse(ret) } @@ -190,6 +206,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenParameters_and_no_params() throws Exception { def script = loadScript(scriptName) def ret = script.whenParameters() + printCallStack() assertFalse(ret) } @@ -197,6 +214,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenParameters_and_params_without_match() throws Exception { def script = loadScript(scriptName) def ret = script.whenParameters(content: [ parameters : [ 'foo', 'bar']]) + printCallStack() assertFalse(ret) } @@ -204,6 +222,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) params.bar = true def ret = script.whenParameters(content: [ parameters : [ 'foo', 'bar']]) + printCallStack() assertTrue(ret) } @@ -211,6 +230,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) params.bar = false def ret = script.whenParameters(content: [ parameters : [ 'foo', 'bar']]) + printCallStack() assertFalse(ret) } @@ -218,6 +238,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { void test_whenTags_and_no_environment_variable() throws Exception { def script = loadScript(scriptName) def ret = script.whenTags() + printCallStack() assertFalse(ret) } @@ -226,6 +247,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) env.TAG_NAME = 'tag' def ret = script.whenTags(content: [:]) + printCallStack() assertFalse(ret) } @@ -234,6 +256,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def script = loadScript(scriptName) env.TAG_NAME = 'tag' def ret = script.whenTags(content: [ tags: true]) + printCallStack() assertTrue(ret) } } diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index 7b9847e01..2a5db97c7 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -40,6 +40,7 @@ private Boolean whenBranches(Map args = [:]) { markdownReason(project: args.project, reason: 'Branch is enabled and matches with the pattern.') return true } + markdownReason(project: args.project, reason: 'Branch is disabled') return false } @@ -82,42 +83,51 @@ private Boolean whenChangeset(Map args = [:]) { } else { markdownReason(project: args.project, reason: 'Changeset is enabled and does not match with the pattern.') } + } else { + markdownReason(project: args.project, reason: 'Changeset is disabled') } - return false } private Boolean whenComments(Map args = [:]) { if (args.content?.get('comments') && env.GITHUB_COMMENT?.trim()) { - if (args.content?.get('comments')?.any { env.GITHUB_COMMENT?.toLowerCase()?.contains(it?.toLowerCase()) }) { - markdownReason(project: args.project, reason: 'Comment is enabled and matches with the pattern.') + def match = args.content.get('comments').find { env.GITHUB_COMMENT?.toLowerCase()?.contains(it?.toLowerCase()) } + if (match) { + markdownReason(project: args.project, reason: "Comment is enabled and matches with the pattern '${match}.") return true } - markdownReason(project: args.project, reason: 'Comment is enabled and does not match with the pattern.') + markdownReason(project: args.project, reason: "Comment is enabled and does not match with the pattern '${args.content.get('comments').toString()}.") + } else { + markdownReason(project: args.project, reason: 'Comment is disabled') } return false } private Boolean whenLabels(Map args = [:]) { if (args.content?.get('labels')) { - if (args.content?.get('labels')?.any { matchesPrLabel(label: it) }) { - markdownReason(project: args.project, reason: 'Label is enabled and matches with the pattern.') + def match = args.content.get('labels').find { matchesPrLabel(label: it) } + if (match) { + markdownReason(project: args.project, reason: "Label is enabled and matches with the pattern '${match}.") return true - } else { - markdownReason(project: args.project, reason: 'Label is enabled and does not match with the pattern.') } + markdownReason(project: args.project, reason: "Label is enabled and does not match with the pattern '${args.content.get('labels').toString()}.") + } else { + markdownReason(project: args.project, reason: 'Label is disabled') } return false } private Boolean whenParameters(Map args = [:]) { if (args.content?.get('parameters')) { - if (args.content?.get('parameters')?.any { params[it] }) { - markdownReason(project: args.project, reason: 'Parameter is enabled and matches with the pattern.') + def match = args.content.get('parameters').find { params[it] } + if (match) { + markdownReason(project: args.project, reason: "Parameter is enabled and matches with the pattern '${match}.") return true } else { - markdownReason(project: args.project, reason: 'Parameter is enabled and does not match with the pattern.') + markdownReason(project: args.project, reason: "Parameter is enabled and does not match with the pattern '${args.content.get('parameters').toString()}.") } + } else { + markdownReason(project: args.project, reason: 'Parameter is disabled') } return false } @@ -127,14 +137,15 @@ private Boolean whenTags(Map args = [:]) { markdownReason(project: args.project, reason: 'Tag is enabled and matches with the pattern.') return true } + markdownReason(project: args.project, reason: 'Tag is disabled') return false } private void markdownReason(Map args = [:]) { - def fileName = "build-${args.project}.md" + def fileName = "build-${args.project?.trim() ?: ''}-reasons.md" def data = '' if(fileExists(fileName)) { data = readFile(file: "${fileName}") } - writeFile file: 'build.sbt', text: "${data}\r\n${args.reason}" + writeFile(file: fileName, text: "${data}\r\n${args.reason}") } From dbcf3374fbcc2cc1946d993ef568e8269cca52b8 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 29 Jul 2020 13:17:50 +0100 Subject: [PATCH 23/37] Generate one master markdown with the build reasons --- vars/beatsWhen.groovy | 51 ++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index 2a5db97c7..cbdcdb886 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -25,22 +25,24 @@ Boolean call(Map args = [:]){ def changeset = args.changeset def ret = false + markdownReason(project: project, reason: "## Build reasons for `${project}`") if (whenBranches(args)) { ret = true } if (whenChangeset(args)) { ret = true } if (whenComments(args)) { ret = true } if (whenLabels(args)) { ret = true } if (whenParameters(args)) { ret = true } if (whenTags(args)) { ret = true } + markdownReason(project: project, reason: '') return ret } private Boolean whenBranches(Map args = [:]) { if (env.BRANCH_NAME?.trim() && args.content?.get('branches')) { - markdownReason(project: args.project, reason: 'Branch is enabled and matches with the pattern.') + markdownReason(project: args.project, reason: '* Branch is enabled and matches with the pattern.') return true } - markdownReason(project: args.project, reason: 'Branch is disabled') + markdownReason(project: args.project, reason: '* Branch is disabled.') return false } @@ -74,17 +76,17 @@ private Boolean whenChangeset(Map args = [:]) { // Search for any pattern that matches that particular def fileContent = readFile(gitDiffFile) - ret = patterns?.any { pattern -> + match = patterns?.find { pattern -> fileContent?.split('\n').any { line -> line ==~ pattern } } - if (ret) { - markdownReason(project: args.project, reason: 'Changeset is enabled and matches with the pattern.') + if (match) { + markdownReason(project: args.project, reason: "* Changeset is enabled and matches with the pattern '${match}'.") return true } else { - markdownReason(project: args.project, reason: 'Changeset is enabled and does not match with the pattern.') + markdownReason(project: args.project, reason: "* Changeset is enabled and does not match with the pattern '${fileContent}'.") } } else { - markdownReason(project: args.project, reason: 'Changeset is disabled') + markdownReason(project: args.project, reason: '* Changeset is disabled.') } return false } @@ -93,12 +95,12 @@ private Boolean whenComments(Map args = [:]) { if (args.content?.get('comments') && env.GITHUB_COMMENT?.trim()) { def match = args.content.get('comments').find { env.GITHUB_COMMENT?.toLowerCase()?.contains(it?.toLowerCase()) } if (match) { - markdownReason(project: args.project, reason: "Comment is enabled and matches with the pattern '${match}.") + markdownReason(project: args.project, reason: "* Comment is enabled and matches with the pattern '${match}'.") return true } - markdownReason(project: args.project, reason: "Comment is enabled and does not match with the pattern '${args.content.get('comments').toString()}.") + markdownReason(project: args.project, reason: "* Comment is enabled and does not match with the pattern '${args.content.get('comments').toString()}'.") } else { - markdownReason(project: args.project, reason: 'Comment is disabled') + markdownReason(project: args.project, reason: '* Comment is disabled.') } return false } @@ -107,12 +109,12 @@ private Boolean whenLabels(Map args = [:]) { if (args.content?.get('labels')) { def match = args.content.get('labels').find { matchesPrLabel(label: it) } if (match) { - markdownReason(project: args.project, reason: "Label is enabled and matches with the pattern '${match}.") + markdownReason(project: args.project, reason: "* Label is enabled and matches with the pattern '${match}'.") return true } - markdownReason(project: args.project, reason: "Label is enabled and does not match with the pattern '${args.content.get('labels').toString()}.") + markdownReason(project: args.project, reason: "* Label is enabled and does not match with the pattern '${args.content.get('labels').toString()}'.") } else { - markdownReason(project: args.project, reason: 'Label is disabled') + markdownReason(project: args.project, reason: '* Label is disabled.') } return false } @@ -121,31 +123,34 @@ private Boolean whenParameters(Map args = [:]) { if (args.content?.get('parameters')) { def match = args.content.get('parameters').find { params[it] } if (match) { - markdownReason(project: args.project, reason: "Parameter is enabled and matches with the pattern '${match}.") + markdownReason(project: args.project, reason: "* Parameter is enabled and matches with the pattern '${match}'.") return true } else { - markdownReason(project: args.project, reason: "Parameter is enabled and does not match with the pattern '${args.content.get('parameters').toString()}.") + markdownReason(project: args.project, reason: "* Parameter is enabled and does not match with the pattern '${args.content.get('parameters').toString()}'.") } } else { - markdownReason(project: args.project, reason: 'Parameter is disabled') + markdownReason(project: args.project, reason: '* Parameter is disabled.') } return false } private Boolean whenTags(Map args = [:]) { if (env.TAG_NAME?.trim() && args.content?.get('tags')) { - markdownReason(project: args.project, reason: 'Tag is enabled and matches with the pattern.') + markdownReason(project: args.project, reason: '* Tag is enabled and matches with the pattern.') return true } - markdownReason(project: args.project, reason: 'Tag is disabled') + markdownReason(project: args.project, reason: '* Tag is disabled') return false } private void markdownReason(Map args = [:]) { - def fileName = "build-${args.project?.trim() ?: ''}-reasons.md" - def data = '' - if(fileExists(fileName)) { - data = readFile(file: "${fileName}") + dir('build-reasons') { + def fileName = 'build.md' + def data = '' + if(fileExists(fileName)) { + data = readFile(file: "${fileName}") + } + def content = "${data}\r\n${args.reason}" + writeFile(file: fileName, text: "${content}") } - writeFile(file: fileName, text: "${data}\r\n${args.reason}") } From 519e93e56031b0818c75cde0e8d75dfd793725d9 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 29 Jul 2020 14:32:42 +0100 Subject: [PATCH 24/37] [markdown] easy to show the reasons --- vars/beatsWhen.groovy | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index cbdcdb886..6eb2a36cc 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -32,17 +32,17 @@ Boolean call(Map args = [:]){ if (whenLabels(args)) { ret = true } if (whenParameters(args)) { ret = true } if (whenTags(args)) { ret = true } - markdownReason(project: project, reason: '') + markdownReason(project: project, reason: "### ${ret ? '✅ enabled' : '❕disabled'}") return ret } private Boolean whenBranches(Map args = [:]) { if (env.BRANCH_NAME?.trim() && args.content?.get('branches')) { - markdownReason(project: args.project, reason: '* Branch is enabled and matches with the pattern.') + markdownReason(project: args.project, reason: '* ✅ Branch is enabled .') return true } - markdownReason(project: args.project, reason: '* Branch is disabled.') + markdownReason(project: args.project, reason: '* ❕Branch is `disabled`.') return false } @@ -80,13 +80,13 @@ private Boolean whenChangeset(Map args = [:]) { fileContent?.split('\n').any { line -> line ==~ pattern } } if (match) { - markdownReason(project: args.project, reason: "* Changeset is enabled and matches with the pattern '${match}'.") + markdownReason(project: args.project, reason: "* ✅ Changeset is `enabled` and matches with the pattern `${match}`.") return true } else { - markdownReason(project: args.project, reason: "* Changeset is enabled and does not match with the pattern '${fileContent}'.") + markdownReason(project: args.project, reason: "* ❕Changeset is `enabled` and does **NOT** match with the pattern `${fileContent}`.") } } else { - markdownReason(project: args.project, reason: '* Changeset is disabled.') + markdownReason(project: args.project, reason: '* ❕Changeset is `disabled`.') } return false } @@ -95,12 +95,12 @@ private Boolean whenComments(Map args = [:]) { if (args.content?.get('comments') && env.GITHUB_COMMENT?.trim()) { def match = args.content.get('comments').find { env.GITHUB_COMMENT?.toLowerCase()?.contains(it?.toLowerCase()) } if (match) { - markdownReason(project: args.project, reason: "* Comment is enabled and matches with the pattern '${match}'.") + markdownReason(project: args.project, reason: "* ✅ Comment is `enabled` and matches with the pattern `${match}`.") return true } - markdownReason(project: args.project, reason: "* Comment is enabled and does not match with the pattern '${args.content.get('comments').toString()}'.") + markdownReason(project: args.project, reason: "* ❕Comment is `enabled` and does **NOT** match with the pattern `${args.content.get('comments').toString()}`.") } else { - markdownReason(project: args.project, reason: '* Comment is disabled.') + markdownReason(project: args.project, reason: '* ❕Comment is `disabled`.') } return false } @@ -109,12 +109,12 @@ private Boolean whenLabels(Map args = [:]) { if (args.content?.get('labels')) { def match = args.content.get('labels').find { matchesPrLabel(label: it) } if (match) { - markdownReason(project: args.project, reason: "* Label is enabled and matches with the pattern '${match}'.") + markdownReason(project: args.project, reason: "* ✅ Label is `enabled` and matches with the pattern `${match}`.") return true } - markdownReason(project: args.project, reason: "* Label is enabled and does not match with the pattern '${args.content.get('labels').toString()}'.") + markdownReason(project: args.project, reason: "* ❕Label is `enabled` and does **NOT** match with the pattern `${args.content.get('labels').toString()}`.") } else { - markdownReason(project: args.project, reason: '* Label is disabled.') + markdownReason(project: args.project, reason: '* ❕Label is `disabled`.') } return false } @@ -123,23 +123,23 @@ private Boolean whenParameters(Map args = [:]) { if (args.content?.get('parameters')) { def match = args.content.get('parameters').find { params[it] } if (match) { - markdownReason(project: args.project, reason: "* Parameter is enabled and matches with the pattern '${match}'.") + markdownReason(project: args.project, reason: "* ✅ Parameter is `enabled` and matches with the pattern `${match}`.") return true } else { - markdownReason(project: args.project, reason: "* Parameter is enabled and does not match with the pattern '${args.content.get('parameters').toString()}'.") + markdownReason(project: args.project, reason: "* ❕Parameter is `enabled` and does **NOT** match with the pattern `${args.content.get('parameters').toString()}`.") } } else { - markdownReason(project: args.project, reason: '* Parameter is disabled.') + markdownReason(project: args.project, reason: '* ❕Parameter is `disabled`.') } return false } private Boolean whenTags(Map args = [:]) { if (env.TAG_NAME?.trim() && args.content?.get('tags')) { - markdownReason(project: args.project, reason: '* Tag is enabled and matches with the pattern.') + markdownReason(project: args.project, reason: '* ✅ Tag is `enabled`.') return true } - markdownReason(project: args.project, reason: '* Tag is disabled') + markdownReason(project: args.project, reason: '* ❕Tag is `disabled`.') return false } From f96444627a1ea9ac9d622e30f54fa227733e10e2 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 29 Jul 2020 15:52:26 +0100 Subject: [PATCH 25/37] Add description to diff the when condition is the global one or the specific one in the stage --- src/test/groovy/BeatsWhenStepTests.groovy | 9 +++++++++ vars/beatsStages.groovy | 2 +- vars/beatsWhen.groovy | 6 +++--- vars/beatsWhen.txt | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/test/groovy/BeatsWhenStepTests.groovy b/src/test/groovy/BeatsWhenStepTests.groovy index 9216f0d0c..aded66722 100644 --- a/src/test/groovy/BeatsWhenStepTests.groovy +++ b/src/test/groovy/BeatsWhenStepTests.groovy @@ -56,6 +56,15 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { assertJobStatusFailure() } + @Test + void test_with_description() throws Exception { + def script = loadScript(scriptName) + def ret = script.call(project: 'foo', description: 'bar', content: [:]) + printCallStack() + assertFalse(ret) + assertTrue(assertMethodCallContainsPattern('writeFile', 'Stages for `foo bar`')) + } + @Test void test_whenBranches_and_no_environment_variable() throws Exception { def script = loadScript(scriptName) diff --git a/vars/beatsStages.groovy b/vars/beatsStages.groovy index 8d5cec2b8..61410f1df 100644 --- a/vars/beatsStages.groovy +++ b/vars/beatsStages.groovy @@ -29,7 +29,7 @@ Map call(Map args = [:]){ content?.stages?.each { stageName, value -> def tempMapOfStages = [:] if (value.containsKey('when')) { - if (beatsWhen(project: project, content: value.when)) { + if (beatsWhen(project: project, content: value.when, description: stageName)) { tempMapOfStages = generateStages(content: value, project: project, stageName: stageName, defaultNode: defaultNode, function: function) } } else { diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index 6eb2a36cc..f62cb318a 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -22,17 +22,17 @@ Boolean call(Map args = [:]){ def project = args.containsKey('project') ? args.project : error('beatsWhen: project param is required') def content = args.containsKey('content') ? args.content : error('beatsWhen: content param is required') - def changeset = args.changeset + def description = args.get('description', '') def ret = false - markdownReason(project: project, reason: "## Build reasons for `${project}`") + markdownReason(project: project, reason: "## Build reasons for `${project}` ${description}") if (whenBranches(args)) { ret = true } if (whenChangeset(args)) { ret = true } if (whenComments(args)) { ret = true } if (whenLabels(args)) { ret = true } if (whenParameters(args)) { ret = true } if (whenTags(args)) { ret = true } - markdownReason(project: project, reason: "### ${ret ? '✅ enabled' : '❕disabled'}") + markdownReason(project: project, reason: "* Stages for `${project} ${description}` have been ${ret ? '✅ enabled' : '❕disabled'}") return ret } diff --git a/vars/beatsWhen.txt b/vars/beatsWhen.txt index 7058c62ac..00697ea61 100644 --- a/vars/beatsWhen.txt +++ b/vars/beatsWhen.txt @@ -7,10 +7,10 @@
  • project: the name of the project. Mandatory
  • content: the content with the when section. Mandatory
  • changeset: the global changeset. Optional
  • +
  • description: the description to be used in the markdown generation with the build reasons. Optional
  • -
         whenTrue(beatsWhen(project: 'auditbeat', content: readYaml(file: 'auditbeat/Jenkinsfile.yml')))
             ...
    
    From 8d00b3b28a66024f02354e23c6e783f55f19f4d0 Mon Sep 17 00:00:00 2001
    From: Victor Martinez 
    Date: Thu, 30 Jul 2020 11:32:39 +0100
    Subject: [PATCH 26/37] Support disabled flag to easily disable a whole project
     or stage if required
    
    ---
     src/test/groovy/BeatsWhenStepTests.groovy | 32 +++++++++++++++++++++++
     vars/beatsWhen.groovy                     | 18 ++++++++-----
     2 files changed, 44 insertions(+), 6 deletions(-)
    
    diff --git a/src/test/groovy/BeatsWhenStepTests.groovy b/src/test/groovy/BeatsWhenStepTests.groovy
    index aded66722..0d38aae42 100644
    --- a/src/test/groovy/BeatsWhenStepTests.groovy
    +++ b/src/test/groovy/BeatsWhenStepTests.groovy
    @@ -185,6 +185,38 @@ class BeatsWhenStepTests extends ApmBasePipelineTest {
         assertFalse(ret)
       }
     
    +  @Test
    +  void test_whenEnabled_without_data() throws Exception {
    +    def script = loadScript(scriptName)
    +    def ret = script.whenEnabled()
    +    printCallStack()
    +    assertTrue(ret)
    +  }
    +
    +  @Test
    +  void test_whenEnabled_with_data() throws Exception {
    +    def script = loadScript(scriptName)
    +    def ret = script.whenEnabled(content: [:])
    +    printCallStack()
    +    assertTrue(ret)
    +  }
    +
    +  @Test
    +  void test_whenEnabled_with_disabled() throws Exception {
    +    def script = loadScript(scriptName)
    +    def ret = script.whenEnabled(content: [ disabled: true])
    +    printCallStack()
    +    assertFalse(ret)
    +  }
    +
    +  @Test
    +  void test_whenEnabled_with_no_disabled() throws Exception {
    +    def script = loadScript(scriptName)
    +    def ret = script.whenEnabled(content: [ disabled: false])
    +    printCallStack()
    +    assertTrue(ret)
    +  }
    +
       @Test
       void test_whenLabels_and_no_data() throws Exception {
         def script = loadScript(scriptName)
    diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy
    index f62cb318a..c934aeca9 100644
    --- a/vars/beatsWhen.groovy
    +++ b/vars/beatsWhen.groovy
    @@ -26,12 +26,14 @@ Boolean call(Map args = [:]){
       def ret = false
     
       markdownReason(project: project, reason: "## Build reasons for `${project}` ${description}")
    -  if (whenBranches(args)) { ret = true }
    -  if (whenChangeset(args)) { ret = true }
    -  if (whenComments(args)) { ret = true }
    -  if (whenLabels(args)) { ret = true }
    -  if (whenParameters(args)) { ret = true }
    -  if (whenTags(args)) { ret = true }
    +  if (whenEnabled(args)) {
    +    if (whenBranches(args)) { ret = true }
    +    if (whenChangeset(args)) { ret = true }
    +    if (whenComments(args)) { ret = true }
    +    if (whenLabels(args)) { ret = true }
    +    if (whenParameters(args)) { ret = true }
    +    if (whenTags(args)) { ret = true }
    +  }
       markdownReason(project: project, reason: "* Stages for `${project} ${description}` have been ${ret ? '✅ enabled' : '❕disabled'}")
     
       return ret
    @@ -105,6 +107,10 @@ private Boolean whenComments(Map args = [:]) {
       return false
     }
     
    +private boolean whenEnabled(Map args = [:]) {
    +  return !args.content?.get('disabled')
    +}
    +
     private Boolean whenLabels(Map args = [:]) {
       if (args.content?.get('labels')) {
         def match = args.content.get('labels').find { matchesPrLabel(label: it) }
    
    From 8fb05e422324b0ef5d89e134cd4f4aada288227b Mon Sep 17 00:00:00 2001
    From: Victor Martinez 
    Date: Thu, 30 Jul 2020 15:39:43 +0100
    Subject: [PATCH 27/37] Use the stage name as the context for the GH check
    
    ---
     vars/beatsStages.groovy | 7 ++-----
     1 file changed, 2 insertions(+), 5 deletions(-)
    
    diff --git a/vars/beatsStages.groovy b/vars/beatsStages.groovy
    index 61410f1df..82268936c 100644
    --- a/vars/beatsStages.groovy
    +++ b/vars/beatsStages.groovy
    @@ -53,20 +53,17 @@ private generateStages(Map args = [:]) {
         content.platforms.each { platform ->
           def id = "${project}-${stageName}-${platform}"
           log(level: 'DEBUG', text: "stage: ${id}")
    -      mapOfStages[id] = generateStage(project: project, label: platform, content: content, function: function)
    +      mapOfStages[id] = generateStage(context: id, project: project, label: platform, content: content, function: function)
         }
       } else {
         def id = "${project}-${stageName}"
         log(level: 'DEBUG', text: "stage: ${id}")
    -    mapOfStages["${id}"] = generateStage(project: project, label: defaultNode, content: content, function: function)
    +    mapOfStages["${id}"] = generateStage(context: id, project: project, label: defaultNode, content: content, function: function)
       }
       return mapOfStages
     }
     
     private generateStage(Map args = [:]) {
    -  def project = args.project
    -  def content = args.content
    -  def label = args.label
       def function = args.function
       return {
         function(args)
    
    From c9d7c0a9c060e3bb4f7bc0bf768615de12b35245 Mon Sep 17 00:00:00 2001
    From: Victor Martinez 
    Date: Thu, 30 Jul 2020 17:29:15 +0100
    Subject: [PATCH 28/37] Support changesetFunction for the beatsWhen
    
    ---
     src/test/groovy/BeatsWhenStepTests.groovy | 26 +++++++++++++++++++++++
     vars/beatsWhen.groovy                     |  6 ++++++
     vars/beatsWhen.txt                        |  8 ++++++-
     3 files changed, 39 insertions(+), 1 deletion(-)
    
    diff --git a/src/test/groovy/BeatsWhenStepTests.groovy b/src/test/groovy/BeatsWhenStepTests.groovy
    index 0d38aae42..81eba169f 100644
    --- a/src/test/groovy/BeatsWhenStepTests.groovy
    +++ b/src/test/groovy/BeatsWhenStepTests.groovy
    @@ -24,6 +24,10 @@ import static org.junit.Assert.assertTrue
     class BeatsWhenStepTests extends ApmBasePipelineTest {
       String scriptName = 'vars/beatsWhen.groovy'
     
    +  def getProjectDependencies(Map args = [:]) {
    +    return [ '^projectA/.*', '^projectB' ]
    +  }
    +
       @Override
       @Before
       void setUp() throws Exception {
    @@ -150,6 +154,28 @@ class BeatsWhenStepTests extends ApmBasePipelineTest {
         assertFalse(ret)
       }
     
    +  @Test
    +  void test_whenChangeset_content_and_function_with_match() throws Exception {
    +    def script = loadScript(scriptName)
    +    def changeset = 'projectA/Jenkinsfile'
    +    helper.registerAllowedMethod('readFile', [String.class], { return changeset })
    +    def ret = script.whenChangeset(content: [ changeset: ['^Jenkinsfile']],
    +                                   changesetFunction: this.&getProjectDependencies)
    +    printCallStack()
    +    assertTrue(ret)
    +  }
    +
    +  @Test
    +  void test_whenChangeset_content_and_function_without_match() throws Exception {
    +    def script = loadScript(scriptName)
    +    def changeset = 'foo/Jenkinsfile'
    +    helper.registerAllowedMethod('readFile', [String.class], { return changeset })
    +    def ret = script.whenChangeset(content: [ changeset: ['^Jenkinsfile']],
    +                                   changesetFunction: this.&getProjectDependencies)
    +    printCallStack()
    +    assertFalse(ret)
    +  }
    +
       @Test
       void test_whenComments_and_no_environment_variable() throws Exception {
         def script = loadScript(scriptName)
    diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy
    index c934aeca9..d1f4ddab1 100644
    --- a/vars/beatsWhen.groovy
    +++ b/vars/beatsWhen.groovy
    @@ -69,6 +69,12 @@ private Boolean whenChangeset(Map args = [:]) {
           }
         }
     
    +    // If function then calculate the project dependencies on the fly.
    +    if (args.get('changesetFunction')) {
    +      calculatedPatterns = args.changesetFunction(args)
    +      patterns.addAll(calculatedPatterns)
    +    }
    +
         // TODO: to be refactored  with isGitRegionMatch.isPartialPatternMatch()
     
         // Gather the diff between the target branch and the current commit.
    diff --git a/vars/beatsWhen.txt b/vars/beatsWhen.txt
    index 00697ea61..59be37bed 100644
    --- a/vars/beatsWhen.txt
    +++ b/vars/beatsWhen.txt
    @@ -8,11 +8,17 @@
             
  • content: the content with the when section. Mandatory
  • changeset: the global changeset. Optional
  • description: the description to be used in the markdown generation with the build reasons. Optional
  • +
  • changesetFunction: the function to be called. Optional
  • -    whenTrue(beatsWhen(project: 'auditbeat', content: readYaml(file: 'auditbeat/Jenkinsfile.yml')))
    +    whenTrue(beatsWhen(project: 'auditbeat', changesetFunction: this.&getProjectDependencies
    +                       content: readYaml(file: 'auditbeat/Jenkinsfile.yml')))
    +        ...
    +    }
    +
    +    def getProjectDependencies(Map args = [:]) {
             ...
         }
     
    From 8b025e562a4786eb271df25e408fbe2aa977d266 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 30 Jul 2020 17:29:50 +0100 Subject: [PATCH 29/37] Docs --- vars/README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/vars/README.md b/vars/README.md index 46245928f..a26edbff4 100644 --- a/vars/README.md +++ b/vars/README.md @@ -39,6 +39,56 @@ Encode a text to base64 base64encode(text: "text to encode", encoding: "UTF-8") ``` +## beatsStages +

    + Given the YAML definition then it creates all the stages + + The list of step's params and the related default values are: +

      +
    • project: the name of the project. Mandatory
    • +
    • content: the content with all the stages and commands to be transformed. Mandatory
    • +
    • function: the function to be called. Mandatory
    • +
    +

    + +
    +    script {
    +        def mapParallelTasks = [:]
    +        beatsStages(project: 'auditbeat', content: readYaml(file: 'auditbeat/Jenkinsfile.yml'), function: this.&myFunction)
    +        parallel(mapParallelTasks)
    +    }
    +
    +    def myFunction(Map args = [:]) {
    +        ...
    +    }
    +
    + +## beatsWhen +

    + Given the YAML definition and the changeset global macros + then it verifies if the project or stage should be enabled. + + The list of step's params and the related default values are: +

      +
    • project: the name of the project. Mandatory
    • +
    • content: the content with the when section. Mandatory
    • +
    • changeset: the global changeset. Optional
    • +
    • description: the description to be used in the markdown generation with the build reasons. Optional
    • +
    • changesetFunction: the function to be called. Optional
    • +
    +

    + +
    +    whenTrue(beatsWhen(project: 'auditbeat', changesetFunction: this.&getProjectDependencies
    +                       content: readYaml(file: 'auditbeat/Jenkinsfile.yml')))
    +        ...
    +    }
    +
    +    def getProjectDependencies(Map args = [:]) {
    +        ...
    +    }
    +
    + ## build Override the `build` step to highlight in BO the URL to the downstream job. From e9e9e617e694cfa44f939c0cb072067c5e049723 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Fri, 31 Jul 2020 08:16:11 +0100 Subject: [PATCH 30/37] Add markdown collapse --- vars/beatsWhen.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index d1f4ddab1..68ccf5ba0 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -27,12 +27,14 @@ Boolean call(Map args = [:]){ markdownReason(project: project, reason: "## Build reasons for `${project}` ${description}") if (whenEnabled(args)) { + markdownReason(project: project, reason: "
    Expand to view the reasons

    ") if (whenBranches(args)) { ret = true } if (whenChangeset(args)) { ret = true } if (whenComments(args)) { ret = true } if (whenLabels(args)) { ret = true } if (whenParameters(args)) { ret = true } if (whenTags(args)) { ret = true } + markdownReason(project: project, reason: "

    ") } markdownReason(project: project, reason: "* Stages for `${project} ${description}` have been ${ret ? '✅ enabled' : '❕disabled'}") From 4db5fcff974a7c1a2dc52135723165e7bb042939 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Fri, 31 Jul 2020 08:25:27 +0100 Subject: [PATCH 31/37] Support beatsWhen with special token for some specific project dependencies --- src/test/groovy/BeatsWhenStepTests.groovy | 11 +++++++++++ vars/beatsWhen.groovy | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/src/test/groovy/BeatsWhenStepTests.groovy b/src/test/groovy/BeatsWhenStepTests.groovy index 81eba169f..21f60af45 100644 --- a/src/test/groovy/BeatsWhenStepTests.groovy +++ b/src/test/groovy/BeatsWhenStepTests.groovy @@ -165,6 +165,17 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { assertTrue(ret) } + @Test + void test_whenChangeset_content_with_project_dependency_and_function_with_match() throws Exception { + def script = loadScript(scriptName) + def changeset = 'projectA/Jenkinsfile' + helper.registerAllowedMethod('readFile', [String.class], { return changeset }) + def ret = script.whenChangeset(content: [ changeset: ['#generator/common/beatgen']], + changesetFunction: this.&getProjectDependencies) + printCallStack() + assertTrue(ret) + } + @Test void test_whenChangeset_content_and_function_without_match() throws Exception { def script = loadScript(scriptName) diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index 68ccf5ba0..d56de380b 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -75,6 +75,15 @@ private Boolean whenChangeset(Map args = [:]) { if (args.get('changesetFunction')) { calculatedPatterns = args.changesetFunction(args) patterns.addAll(calculatedPatterns) + + // Search for some other project dependencies that are explicitly + // sett with the pattern # + args.content.changeset.findAll { it.startsWith('#') }.each { + Map newArgs = args + newArgs.project = it.replaceAll('#', '') + calculatedPatterns = args.changesetFunction(args) + patterns.addAll(calculatedPatterns) + } } // TODO: to be refactored with isGitRegionMatch.isPartialPatternMatch() From 25f3a0b48afb5d7962177c4ada1eade5fff6c227 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Fri, 31 Jul 2020 11:59:59 +0100 Subject: [PATCH 32/37] Cosmetic change in the markdown --- vars/beatsWhen.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index d56de380b..d8d0ec15c 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -25,9 +25,9 @@ Boolean call(Map args = [:]){ def description = args.get('description', '') def ret = false - markdownReason(project: project, reason: "## Build reasons for `${project}` ${description}") + markdownReason(project: project, reason: "## Build reasons for `${project} ${description}`") if (whenEnabled(args)) { - markdownReason(project: project, reason: "
    Expand to view the reasons

    ") + markdownReason(project: project, reason: "

    Expand to view the reasons

    \n") if (whenBranches(args)) { ret = true } if (whenChangeset(args)) { ret = true } if (whenComments(args)) { ret = true } @@ -36,7 +36,7 @@ Boolean call(Map args = [:]){ if (whenTags(args)) { ret = true } markdownReason(project: project, reason: "

    ") } - markdownReason(project: project, reason: "* Stages for `${project} ${description}` have been ${ret ? '✅ enabled' : '❕disabled'}") + markdownReason(project: project, reason: "#### Stages for `${project} ${description}` have been ${ret ? '✅ enabled' : '❕disabled'}\n") return ret } From c3b029bdbd344f537c0ede767d77700005b468aa Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 6 Aug 2020 08:01:24 +0100 Subject: [PATCH 33/37] Pass the unique ID to be consumed for the stashed map that's required to identify the stage, since STAGE_NAME env variable is not available any more when creating stages dynamically --- vars/beatsStages.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vars/beatsStages.groovy b/vars/beatsStages.groovy index 82268936c..021221b77 100644 --- a/vars/beatsStages.groovy +++ b/vars/beatsStages.groovy @@ -53,12 +53,12 @@ private generateStages(Map args = [:]) { content.platforms.each { platform -> def id = "${project}-${stageName}-${platform}" log(level: 'DEBUG', text: "stage: ${id}") - mapOfStages[id] = generateStage(context: id, project: project, label: platform, content: content, function: function) + mapOfStages[id] = generateStage(context: id, project: project, label: platform, content: content, function: function, id: id) } } else { def id = "${project}-${stageName}" log(level: 'DEBUG', text: "stage: ${id}") - mapOfStages["${id}"] = generateStage(context: id, project: project, label: defaultNode, content: content, function: function) + mapOfStages["${id}"] = generateStage(context: id, project: project, label: defaultNode, content: content, function: function, id: id) } return mapOfStages } From cb7b8942542d628431a3030441a6656ac194a069 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 3 Sep 2020 14:19:57 +0100 Subject: [PATCH 34/37] Fix wound up catching method mismatch with this.& by using a Class expected to call org.codehaus.groovy.runtime.MethodClosure.call but wound up catching WorkflowScript.runCommand; see: https://jenkins.io/redirect/pipeline-cps-method-mismatches/ was caused when using the this.& function pointer This should help to avoid any kind of issues in the future in case the mismatch is forced to fail --- src/co/elastic/beats/BeatsFunction.groovy | 35 +++++++++++++++++++ src/test/groovy/BeatsStagesStepTests.groovy | 15 ++++---- src/test/groovy/BeatsWhenStepTests.groovy | 11 +++--- .../mock/beats/GetProjectDependencies.groovy | 30 ++++++++++++++++ .../co/elastic/mock/beats/RunCommand.groovy | 30 ++++++++++++++++ src/test/resources/jobs/beats/beatsStages.dsl | 30 +++++++++------- vars/beatsStages.groovy | 2 +- vars/beatsStages.txt | 2 +- vars/beatsWhen.groovy | 5 +-- vars/beatsWhen.txt | 2 +- 10 files changed, 128 insertions(+), 34 deletions(-) create mode 100644 src/co/elastic/beats/BeatsFunction.groovy create mode 100644 src/test/groovy/co/elastic/mock/beats/GetProjectDependencies.groovy create mode 100644 src/test/groovy/co/elastic/mock/beats/RunCommand.groovy diff --git a/src/co/elastic/beats/BeatsFunction.groovy b/src/co/elastic/beats/BeatsFunction.groovy new file mode 100644 index 000000000..b5de20867 --- /dev/null +++ b/src/co/elastic/beats/BeatsFunction.groovy @@ -0,0 +1,35 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package co.elastic.beats + +/** + Base class to implement specific functions for the beats 2.0 pipeline. +*/ +class BeatsFunction { + /** object to access to pipeline steps */ + public steps + + public BeatsFunction(Map args){ + this.steps = args.steps + } + + /** + This method should be overwritten by the target pipeline. + */ + protected run(Map args){ } +} diff --git a/src/test/groovy/BeatsStagesStepTests.groovy b/src/test/groovy/BeatsStagesStepTests.groovy index cda1c61a3..5818702ac 100644 --- a/src/test/groovy/BeatsStagesStepTests.groovy +++ b/src/test/groovy/BeatsStagesStepTests.groovy @@ -20,14 +20,11 @@ import org.junit.After import org.junit.Test import static org.junit.Assert.assertFalse import static org.junit.Assert.assertTrue +import co.elastic.mock.beats.RunCommand class BeatsStagesStepTests extends ApmBasePipelineTest { String scriptName = 'vars/beatsStages.groovy' - def runCommand(Map args = [:]) { - echo "${args.label}" - } - @Override @Before void setUp() throws Exception { @@ -64,7 +61,7 @@ class BeatsStagesStepTests extends ApmBasePipelineTest { void test_with_no_platform() throws Exception { def script = loadScript(scriptName) try { - script.call(project: 'foo', content: [:], function: this.&runCommand) + script.call(project: 'foo', content: [:], function: new RunCommand(steps: this)) } catch (e) { // NOOP } @@ -96,7 +93,7 @@ class BeatsStagesStepTests extends ApmBasePipelineTest { "mage" : [ "foo" ] ] ] - ], function: this.&runCommand) + ], function: new RunCommand(steps: this)) printCallStack() assertTrue(ret.size() == 1) assertTrue(assertMethodCallContainsPattern('log', 'stage: foo-simple')) @@ -117,7 +114,7 @@ class BeatsStagesStepTests extends ApmBasePipelineTest { "platforms" : [ 'windows-2019', 'windows-2016' ] ] ] - ], function: this.&runCommand) + ], function: new RunCommand(steps: this)) printCallStack() assertTrue(ret.size() == 3) assertTrue(assertMethodCallContainsPattern('log', 'stage: foo-simple')) @@ -148,7 +145,7 @@ class BeatsStagesStepTests extends ApmBasePipelineTest { ] ] ] - ], function: this.&runCommand) + ], function: new RunCommand(steps: this)) printCallStack() assertTrue(ret.size() == 2) assertFalse(assertMethodCallContainsPattern('log', 'stage: foo-multi-when')) @@ -177,7 +174,7 @@ class BeatsStagesStepTests extends ApmBasePipelineTest { ] ] ] - ], function: this.&runCommand) + ], function: new RunCommand(steps: this)) printCallStack() assertTrue(ret.size() == 3) assertTrue(assertMethodCallContainsPattern('log', 'stage: foo-multi-when')) diff --git a/src/test/groovy/BeatsWhenStepTests.groovy b/src/test/groovy/BeatsWhenStepTests.groovy index 21f60af45..f09ed6e5e 100644 --- a/src/test/groovy/BeatsWhenStepTests.groovy +++ b/src/test/groovy/BeatsWhenStepTests.groovy @@ -20,14 +20,11 @@ import org.junit.After import org.junit.Test import static org.junit.Assert.assertFalse import static org.junit.Assert.assertTrue +import co.elastic.mock.beats.GetProjectDependencies class BeatsWhenStepTests extends ApmBasePipelineTest { String scriptName = 'vars/beatsWhen.groovy' - def getProjectDependencies(Map args = [:]) { - return [ '^projectA/.*', '^projectB' ] - } - @Override @Before void setUp() throws Exception { @@ -160,7 +157,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def changeset = 'projectA/Jenkinsfile' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) def ret = script.whenChangeset(content: [ changeset: ['^Jenkinsfile']], - changesetFunction: this.&getProjectDependencies) + changesetFunction: new GetProjectDependencies()) printCallStack() assertTrue(ret) } @@ -171,7 +168,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def changeset = 'projectA/Jenkinsfile' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) def ret = script.whenChangeset(content: [ changeset: ['#generator/common/beatgen']], - changesetFunction: this.&getProjectDependencies) + changesetFunction: new GetProjectDependencies()) printCallStack() assertTrue(ret) } @@ -182,7 +179,7 @@ class BeatsWhenStepTests extends ApmBasePipelineTest { def changeset = 'foo/Jenkinsfile' helper.registerAllowedMethod('readFile', [String.class], { return changeset }) def ret = script.whenChangeset(content: [ changeset: ['^Jenkinsfile']], - changesetFunction: this.&getProjectDependencies) + changesetFunction: new GetProjectDependencies()) printCallStack() assertFalse(ret) } diff --git a/src/test/groovy/co/elastic/mock/beats/GetProjectDependencies.groovy b/src/test/groovy/co/elastic/mock/beats/GetProjectDependencies.groovy new file mode 100644 index 000000000..cd87507e7 --- /dev/null +++ b/src/test/groovy/co/elastic/mock/beats/GetProjectDependencies.groovy @@ -0,0 +1,30 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package co.elastic.mock.beats + +/** + * Mock class for the Beats 2.0 beatsWhen step + */ +class GetProjectDependencies extends co.elastic.beats.BeatsFunction { + public GetProjectDependencies(Map args = [:]){ + super(args) + } + public run(Map args = [:]){ + return [ '^projectA/.*', '^projectB' ] + } +} diff --git a/src/test/groovy/co/elastic/mock/beats/RunCommand.groovy b/src/test/groovy/co/elastic/mock/beats/RunCommand.groovy new file mode 100644 index 000000000..a758a0e09 --- /dev/null +++ b/src/test/groovy/co/elastic/mock/beats/RunCommand.groovy @@ -0,0 +1,30 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package co.elastic.mock.beats + +/** + * Mock class for the Beats 2.0 beatsStages step + */ +class RunCommand extends co.elastic.beats.BeatsFunction { + public RunCommand(Map args = [:]){ + super(args) + } + public run(Map args = [:]) { + steps.echo "-------------${args.label} ---- ${args.context}" + } +} diff --git a/src/test/resources/jobs/beats/beatsStages.dsl b/src/test/resources/jobs/beats/beatsStages.dsl index 9ea54d51f..952382d49 100644 --- a/src/test/resources/jobs/beats/beatsStages.dsl +++ b/src/test/resources/jobs/beats/beatsStages.dsl @@ -62,7 +62,7 @@ stages: stage('simple') { steps { script { - def ret = beatsStages(project: 'test', content: readYaml(file: 'simple.yaml'), function: this.&runCommand) + def ret = beatsStages(project: 'test', content: readYaml(file: 'simple.yaml'), function: new RunCommand(steps: this)) whenFalse(ret.size() == 1) { error 'Assert failed. There should be just one entry.' } @@ -78,7 +78,7 @@ stages: stage('two') { steps { script { - def ret = beatsStages(project: 'test', content: readYaml(file: 'two.yaml'), function: this.&runCommand) + def ret = beatsStages(project: 'test', content: readYaml(file: 'two.yaml'), function: new RunCommand(steps: this)) whenFalse(ret.size() == 2) { error 'Assert failed. There should be just one entry.' } @@ -94,7 +94,7 @@ stages: stage('platforms') { steps { script { - def ret = beatsStages(project: 'test', content: readYaml(file: 'platforms.yaml'), function: this.&runCommand) + def ret = beatsStages(project: 'test', content: readYaml(file: 'platforms.yaml'), function: new RunCommand(steps: this)) whenFalse(ret.size() == 2) { error 'Assert failed. There should be just one entry.' } @@ -113,7 +113,7 @@ stages: } steps { script { - def ret = beatsStages(project: 'test', content: readYaml(file: 'when.yaml'), function: this.&runCommand) + def ret = beatsStages(project: 'test', content: readYaml(file: 'when.yaml'), function: new RunCommand(steps: this)) whenFalse(ret.size() == 2) { error 'Assert failed. There should be just 2 entries.' } @@ -127,7 +127,7 @@ stages: } steps { script { - def ret = beatsStages(project: 'test', content: readYaml(file: 'when.yaml'), function: this.&runCommand) + def ret = beatsStages(project: 'test', content: readYaml(file: 'when.yaml'), function: new RunCommand(steps: this)) whenFalse(ret.size() == 0) { error 'Assert failed. There should be just 0 entries.' } @@ -138,15 +138,19 @@ stages: } } -// TODO expected to call org.codehaus.groovy.runtime.MethodClosure.call but wound up catching WorkflowScript.runCommand; see: https://jenkins.io/redirect/pipeline-cps-method-mismatches/ -def runCommand(Map args = [:]) { - if (args?.content?.mage) { - dir(args.project) { - echo "mage ${args.label}" - } +class RunCommand extends co.elastic.beats.BeatsFunction { + public RunCommand(Map args = [:]){ + super(args) } - if (args?.content?.make) { - echo "make ${args.label}" + public run(Map args = [:]){ + if (args?.content?.mage) { + steps.dir(args.project) { + steps.echo "mage ${args.label}" + } + } + if (args?.content?.make) { + steps.echo "make ${args.label}" + } } } ''' diff --git a/vars/beatsStages.groovy b/vars/beatsStages.groovy index 021221b77..e901fd54c 100644 --- a/vars/beatsStages.groovy +++ b/vars/beatsStages.groovy @@ -66,6 +66,6 @@ private generateStages(Map args = [:]) { private generateStage(Map args = [:]) { def function = args.function return { - function(args) + function.run(args) } } diff --git a/vars/beatsStages.txt b/vars/beatsStages.txt index fb81a1857..2cd13032d 100644 --- a/vars/beatsStages.txt +++ b/vars/beatsStages.txt @@ -5,7 +5,7 @@
    • project: the name of the project. Mandatory
    • content: the content with all the stages and commands to be transformed. Mandatory
    • -
    • function: the function to be called. Mandatory
    • +
    • function: the function to be called. Should implement the class BeatsFunction. Mandatory

    diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index d8d0ec15c..1e62918ba 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -73,7 +73,8 @@ private Boolean whenChangeset(Map args = [:]) { // If function then calculate the project dependencies on the fly. if (args.get('changesetFunction')) { - calculatedPatterns = args.changesetFunction(args) + def changesetFunction = args.changesetFunction + calculatedPatterns = changesetFunction.run(args) patterns.addAll(calculatedPatterns) // Search for some other project dependencies that are explicitly @@ -81,7 +82,7 @@ private Boolean whenChangeset(Map args = [:]) { args.content.changeset.findAll { it.startsWith('#') }.each { Map newArgs = args newArgs.project = it.replaceAll('#', '') - calculatedPatterns = args.changesetFunction(args) + calculatedPatterns = changesetFunction.run(args) patterns.addAll(calculatedPatterns) } } diff --git a/vars/beatsWhen.txt b/vars/beatsWhen.txt index 59be37bed..781fc3e5a 100644 --- a/vars/beatsWhen.txt +++ b/vars/beatsWhen.txt @@ -8,7 +8,7 @@
  • content: the content with the when section. Mandatory
  • changeset: the global changeset. Optional
  • description: the description to be used in the markdown generation with the build reasons. Optional
  • -
  • changesetFunction: the function to be called. Optional
  • +
  • changesetFunction: the function to be called. Should implement the class BeatsFunction. Optional
  • From fa26c0f8daf25efb2b521e695d6ca139c7d780c6 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 3 Sep 2020 14:37:28 +0100 Subject: [PATCH 35/37] Fix super-linter --- .ci/Jenkinsfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index bfbad6186..057c22cce 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -151,6 +151,9 @@ pipeline { } } stage('Super-linter') { + options { + warnError('Super-linter failed, unstable and move forward') + } steps { withGithubNotify(context: 'Check super-linter', tab: 'tests') { deleteDir() From 28f514e22a22e1265873a357f034645e28903769 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 3 Sep 2020 16:38:21 +0100 Subject: [PATCH 36/37] Update vars/beatsWhen.groovy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- vars/beatsWhen.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vars/beatsWhen.groovy b/vars/beatsWhen.groovy index 1e62918ba..c32357500 100644 --- a/vars/beatsWhen.groovy +++ b/vars/beatsWhen.groovy @@ -46,7 +46,7 @@ private Boolean whenBranches(Map args = [:]) { markdownReason(project: args.project, reason: '* ✅ Branch is enabled .') return true } - markdownReason(project: args.project, reason: '* ❕Branch is `disabled`.') + markdownReason(project: args.project, reason: '* ❗Branch is `disabled`.') return false } From 72bbcac93a9cb9ec2cc50e5763e7d53f24fc0741 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 3 Sep 2020 16:51:05 +0100 Subject: [PATCH 37/37] Revert "Fix super-linter" This reverts commit fa26c0f8daf25efb2b521e695d6ca139c7d780c6. --- .ci/Jenkinsfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index 057c22cce..bfbad6186 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -151,9 +151,6 @@ pipeline { } } stage('Super-linter') { - options { - warnError('Super-linter failed, unstable and move forward') - } steps { withGithubNotify(context: 'Check super-linter', tab: 'tests') { deleteDir()