From a8bcefe5beda5d8aae1d6681de03bca759e6edec Mon Sep 17 00:00:00 2001 From: Raymond Douglass Date: Tue, 17 Nov 2020 19:19:01 -0500 Subject: [PATCH 1/2] ENH Allow for lists as values in excludes mappings --- README.md | 41 +++++++++++++++++-- .../plugins/yamlaxis/YamlLoader.groovy | 11 ++++- .../YamlMatrixExecutionStrategy.groovy | 41 ++++++++++++++++++- .../YamlMatrixExecutionStrategySpec.groovy | 17 +++++--- .../yamlaxis/YamlTextLoaderSpock.groovy | 22 ++++++++++ 5 files changed, 121 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 01a1978..0dd9adb 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ Generate yaml based matrix and run job :muscle: ## Detail ### Excluding logic -Excluding pattern may be specified with `List` of `Map` (e.g. `List>`) - +Excluding pattern may be specified with `List` of `Map` (e.g. `List>`) +Elements in the `Map` may be a List to exclude multiple items for one key ```yaml # axis.yml exclude: @@ -71,7 +71,6 @@ exclude: - RUBY_VERSION: 2.3.0 DATABASE: oracle ``` - When specified 2 axes ![axis](doc/axis.png) @@ -86,3 +85,39 @@ This results in a 3x3 build matrix. * `RUBY_VERSION` value `2.1.8` and `DATABASE` value `oracle` is excluded * When specified `RUBY_VERSION` value `2.3.0` and `DATABASE` value `oracle`, 1 result is excluded * `RUBY_VERSION` value `2.3.0` and `DATABASE` value `oracle` is excluded + +#### Another example +```yaml +# axis2.yml +exclude: + - RUBY_VERSION: 2.1.8 + - RUBY_VERSION: 2.3.0 + DATABASE: + - oracle + - mysql +``` +* When specified `RUBY_VERSION` value `2.1.8`, 3 results are excluded + * `RUBY_VERSION` value `2.1.8` and `DATABASE` value `mysql` is excluded + * `RUBY_VERSION` value `2.1.8` and `DATABASE` value `postgres` is excluded + * `RUBY_VERSION` value `2.1.8` and `DATABASE` value `oracle` is excluded +* When specified `RUBY_VERSION` value `2.3.0`, 2 results are excluded + * `RUBY_VERSION` value `2.3.0` and `DATABASE` value `oracle` is excluded + * `RUBY_VERSION` value `2.3.0` and `DATABASE` value `mysql` is excluded + +#### Final example +Using multiple lists will exclude the cartesian product of those lists. +```yaml +# axis3.yml +exclude: + - RUBY_VERSION: + - 2.1.8 + - 2.3.0 + DATABASE: + - oracle + - mysql +``` +* 4 results are excluded + * `RUBY_VERSION` value `2.1.8` and `DATABASE` value `mysql` is excluded + * `RUBY_VERSION` value `2.1.8` and `DATABASE` value `oracle` is excluded + * `RUBY_VERSION` value `2.3.0` and `DATABASE` value `oracle` is excluded + * `RUBY_VERSION` value `2.3.0` and `DATABASE` value `mysql` is excluded diff --git a/src/main/groovy/org/jenkinsci/plugins/yamlaxis/YamlLoader.groovy b/src/main/groovy/org/jenkinsci/plugins/yamlaxis/YamlLoader.groovy index f5856d5..888c2f7 100644 --- a/src/main/groovy/org/jenkinsci/plugins/yamlaxis/YamlLoader.groovy +++ b/src/main/groovy/org/jenkinsci/plugins/yamlaxis/YamlLoader.groovy @@ -15,14 +15,21 @@ abstract class YamlLoader { * @param key * @return if key is not found, return null */ - List> loadMaps(String key){ + List> loadMaps(String key){ Map content = getContent() def values = content.get(key) if(values == null){ return null } values.collect { - it.collectEntries { k, v -> [k, v.toString()] } + it.collectEntries { k, v -> + if(v instanceof List){ + [k, v.collect { it.toString() }] + }else{ + [k, v.toString()] + } + } + } } diff --git a/src/main/groovy/org/jenkinsci/plugins/yamlaxis/YamlMatrixExecutionStrategy.groovy b/src/main/groovy/org/jenkinsci/plugins/yamlaxis/YamlMatrixExecutionStrategy.groovy index 38f9869..789a7f0 100644 --- a/src/main/groovy/org/jenkinsci/plugins/yamlaxis/YamlMatrixExecutionStrategy.groovy +++ b/src/main/groovy/org/jenkinsci/plugins/yamlaxis/YamlMatrixExecutionStrategy.groovy @@ -62,7 +62,7 @@ class YamlMatrixExecutionStrategy extends BaseMES { BuildUtils.log(execution, "[WARN] NotFound excludeKey ${excludeKey}") return [] } - values.collect { new Combination(it) } + collectExcludeCombinations(values) } catch (IOException e) { BuildUtils.log(execution, "[WARN] Can not read yamlFile: ${yamlFile}", e) @@ -70,6 +70,45 @@ class YamlMatrixExecutionStrategy extends BaseMES { } } + public static List collectExcludeCombinations(List> excludes) { + List> result = [] + for (value in excludes) { + List> combos = [] + boolean isList = false + for (Map.Entry entry in value) { + if (entry.value instanceof List) { + isList = true + List> newCombos = [] + for (def v in entry.value) { + if (combos) { + for (def c in combos) { + Map clone = new HashMap<>(c) + clone.put(entry.key, v) + newCombos.add(clone) + } + } else { + newCombos.add([(entry.key): v]) + } + } + combos = newCombos + } + } + if (isList) { + for (Map.Entry entry in value) { + if (entry.value instanceof String) { + for (def c in combos) { + c.put(entry.key, entry.value) + } + } + } + } else { + combos.add(value) + } + result.addAll(combos) + } + result.collect { new Combination(it) } + } + private YamlLoader getYamlLoader(MatrixBuild.MatrixBuildExecution execution){ switch(yamlType){ case YamlFileLoader.RADIO_VALUE: diff --git a/src/test/groovy/org/jenkinsci/plugins/yamlaxis/YamlMatrixExecutionStrategySpec.groovy b/src/test/groovy/org/jenkinsci/plugins/yamlaxis/YamlMatrixExecutionStrategySpec.groovy index 6a651d4..c3cddb1 100644 --- a/src/test/groovy/org/jenkinsci/plugins/yamlaxis/YamlMatrixExecutionStrategySpec.groovy +++ b/src/test/groovy/org/jenkinsci/plugins/yamlaxis/YamlMatrixExecutionStrategySpec.groovy @@ -30,7 +30,7 @@ class YamlMatrixExecutionStrategySpec extends Specification { def "run"() { setup: def matrixProject = configure() - List excludeCombinations = excludes.collect { new Combination(it) } + List excludeCombinations = YamlMatrixExecutionStrategy.collectExcludeCombinations(excludes) matrixProject.executionStrategy = new YamlMatrixExecutionStrategy(excludeCombinations) def build = matrixProject.scheduleBuild2(0).get() @@ -40,9 +40,16 @@ class YamlMatrixExecutionStrategySpec extends Specification { build.runs.size() == runsCount where: - excludes || runsCount - [] || 9 - [[axis1: "c", axis2: "z"]] || 8 - [[axis1: "c"]] || 6 + excludes || runsCount + [] || 9 + [[axis1: "c", axis2: "z"]] || 8 + [[axis1: "c", axis2: ["z"]]] || 8 + [[axis1: ["c"], axis2: "z"]] || 8 + [[axis1: ["c"], axis2: ["z"]]] || 8 + [[axis1: "c"]] || 6 + [[axis1: ["b", "c"]]] || 3 + [[axis1: "b"], [axis1: "c"]] || 3 + [[axis1: "b", axis2: ["x", "y"]]] || 7 + [[axis1: ["a", "b"], axis2: ["x", "y"]]] || 5 } } diff --git a/src/test/groovy/org/jenkinsci/plugins/yamlaxis/YamlTextLoaderSpock.groovy b/src/test/groovy/org/jenkinsci/plugins/yamlaxis/YamlTextLoaderSpock.groovy index d75251a..9688227 100644 --- a/src/test/groovy/org/jenkinsci/plugins/yamlaxis/YamlTextLoaderSpock.groovy +++ b/src/test/groovy/org/jenkinsci/plugins/yamlaxis/YamlTextLoaderSpock.groovy @@ -52,4 +52,26 @@ exclude: "exclude" || [[a: "1", b: "2"], [c: "3"]] "not_found" || null } + + def "loadValuesList"(){ + setup: + String yamlText = """ +exclude: + - a: 1 + b: + - 2 + - 3 + - c: 4 +""" + + def loader = new YamlTextLoader(yamlText: yamlText) + + expect: + loader.loadMaps(key) == expected + + where: + key || expected + "exclude" || [[a: "1", b: ["2", "3"]], [c: "4"]] + "not_found" || null + } } From c07726356ae7d2d1aeed8bfdfc976b196af96caf Mon Sep 17 00:00:00 2001 From: Ray Douglass <3107146+raydouglass@users.noreply.github.com> Date: Fri, 20 Nov 2020 11:57:18 -0500 Subject: [PATCH 2/2] Update README.md Co-authored-by: GO Sueyoshi --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0dd9adb..497313b 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ Generate yaml based matrix and run job :muscle: ## Detail ### Excluding logic Excluding pattern may be specified with `List` of `Map` (e.g. `List>`) + Elements in the `Map` may be a List to exclude multiple items for one key ```yaml # axis.yml