Add support for across Step modifier
See: https://github.com/spring-projects/sts4/issues/752
This commit is contained in:
@@ -26,6 +26,8 @@ public class ValueParsers {
|
||||
};
|
||||
|
||||
public static final ValueParser POS_INTEGER = integerRange(0, null);
|
||||
|
||||
public static final ValueParser STRICTLY_POS_INTEGER = integerAtLeast(1);
|
||||
|
||||
public static ValueParser integerAtLeast(final Integer lowerBound) {
|
||||
return integerRange(lowerBound, null);
|
||||
|
||||
@@ -15,7 +15,6 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -38,10 +37,8 @@ import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
import org.springframework.ide.vscode.commons.util.SimpleGlob;
|
||||
import org.springframework.ide.vscode.commons.util.SimpleGlob.Match;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractUnionType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanAndSequenceUnion;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraint;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints;
|
||||
import org.springframework.ide.vscode.commons.yaml.snippet.TypeBasedSnippetProvider;
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.springframework.ide.vscode.commons.util.RegexpParser;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParseException;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParsers;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.SchemaContextAware;
|
||||
|
||||
@@ -96,6 +97,23 @@ public class ConcourseValueParsers {
|
||||
+ "you should use location instead."
|
||||
);
|
||||
|
||||
public static final ValueParser IDENTIFIER = new RegexpParser(
|
||||
"(\\d|[\\p{L}&&[^\\p{Lu}]]|\\-|_|\\.)*",
|
||||
"Identifier",
|
||||
"An identifier is a string value comprised of Unicode lowercase letters, Decimal numbers, hyphens (`-`)" +
|
||||
" underscores (`_`) and periods (`.`)");
|
||||
|
||||
public static final ValueParser MAX_IN_FLIGHT_OR_ALL = (str) -> {
|
||||
if (str.equals("all")) {
|
||||
return str;
|
||||
}
|
||||
try {
|
||||
return ValueParsers.STRICTLY_POS_INTEGER.parse(str);
|
||||
} catch (Exception e ) {
|
||||
throw new IllegalArgumentException("Expecting either 'all' or an integer greater than 0");
|
||||
}
|
||||
};
|
||||
|
||||
private static String createTimeRegexp() {
|
||||
String hours = "([0-2]?[0-9])";
|
||||
String minutes = "([0-6][0-9])";
|
||||
|
||||
@@ -94,6 +94,8 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
public final YType t_string = f.yatomic("String");
|
||||
public final YType t_ne_string = f.yatomic("String")
|
||||
.parseWith(ValueParsers.NE_STRING);
|
||||
public final YType t_identifier = f.yatomic("Identifier")
|
||||
.parseWith(ConcourseValueParsers.IDENTIFIER);
|
||||
public final YType t_opt_string = f.yatomic("String")
|
||||
.parseWith(YamlSchemaValueParsers.OPT_STRING);
|
||||
|
||||
@@ -381,7 +383,7 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
addProp(setPipelineStep, "var_files", t_strings);
|
||||
|
||||
YBeanType loadVarStep = f.ybean("LoadVarStep");
|
||||
addProp(loadVarStep, "load_var", t_ne_string); //TODO: t_identifier: see https://concourse-ci.org/config-basics.html#schema.identifier
|
||||
addProp(loadVarStep, "load_var", t_identifier);
|
||||
addProp(loadVarStep, "file", t_ne_string).isRequired(true);
|
||||
addProp(loadVarStep, "format", f.yenum("LoadVarFormat", "json", "yaml", "yml", "trim", "raw"));
|
||||
addProp(loadVarStep, "reveal", t_boolean);
|
||||
@@ -418,6 +420,12 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
addProp(doStep, "do", f.yseq(step));
|
||||
addProp(tryStep, "try", step);
|
||||
|
||||
YBeanType acrossVar = f.ybean("AcrossVar");
|
||||
addProp(acrossVar, "var", t_identifier).isPrimary(true);
|
||||
addProp(acrossVar, "values", f.yseq(t_any)).isRequired(true);
|
||||
addProp(acrossVar, "max_in_flight", f.yatomic("AcrossVarMaxInFlight").parseWith(ConcourseValueParsers.MAX_IN_FLIGHT_OR_ALL));
|
||||
addProp(acrossVar, "fail_fast", t_boolean);
|
||||
|
||||
// shared properties applicable for any subtype of Step:
|
||||
for (AbstractType subStep : stepTypes) {
|
||||
addProp(step, subStep, "on_success", step);
|
||||
@@ -427,6 +435,9 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
addProp(step, subStep, "tags", t_strings);
|
||||
addProp(step, subStep, "timeout", t_duration);
|
||||
addProp(step, subStep, "attempts", t_strictly_pos_integer);
|
||||
if (subStep!=getStep && subStep!=putStep) { // doc says 'across' doesn't work with put and get
|
||||
addProp(step, subStep, "across", f.yseq(acrossVar)); //TODO: create proper schema
|
||||
}
|
||||
}
|
||||
models.setStepType(step);
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Default `false`. When enabled, the `across` step will fail fast by returning as soon as any sub-step fails. This means that running steps will be interrupted and pending steps will no longer be scheduled.
|
||||
@@ -0,0 +1,2 @@
|
||||
Default 1. If set to `all`, the substep will run with all combinations of the current var in parallel. If set to a positive integer, only that number of substeps may run in parallel.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
The list of values that the [var](https://concourse-ci.org/across-step.html#schema.across_var.var) will iterate over when running the substep. If multiple vars are configured, all combinations of values across all vars will run.
|
||||
|
||||
The list of values may also be interpolated. For instance, you may use the [`load_var` step](https://concourse-ci.org/load-var-step.html) to first load a list of [*value* schema](https://concourse-ci.org/config-basics.html#schema.value) into a [local var](https://concourse-ci.org/vars.html#local-vars), and then iterate across that dynamic list of values.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
The name of the variable that will be added to the ["." var source](https://concourse-ci.org/vars.html#local-vars). This variable will only be accessible in the scope of the step - each iteration of the step gets its own scope.
|
||||
|
||||
If a variable of the same name already exists in the parent scope, a warning will be printed.
|
||||
@@ -0,0 +1,3 @@
|
||||
Run a step multiple times with different combinations of variable values.
|
||||
|
||||
See [Across Step Modifier](https://concourse-ci.org/across-step.html#across-step)
|
||||
@@ -74,6 +74,42 @@ public class ConcourseEditorTest {
|
||||
serverInitializer.setMaxCompletions(100);
|
||||
}
|
||||
|
||||
@Test public void GH_752_accross_step_modifier() throws Exception {
|
||||
//See: https://github.com/spring-projects/sts4/issues/752
|
||||
//See: https://concourse-ci.org/across-step.html#schema.across
|
||||
Editor editor = harness.newEditor(
|
||||
"jobs:\n" +
|
||||
"- name: job\n" +
|
||||
" plan:\n" +
|
||||
" - across:\n" +
|
||||
" - var: some-text\n" +
|
||||
" values: some_values\n" +
|
||||
" max_in_flight: bad_flight\n" +
|
||||
" fail_fast: is_fail_fast\n" +
|
||||
" task: running-((.:some-text))\n" +
|
||||
" config:\n" +
|
||||
" platform: linux\n" +
|
||||
" image_resource:\n" +
|
||||
" type: docker-image\n" +
|
||||
" source:\n" +
|
||||
" repository: ubuntu\n" +
|
||||
" run:\n" +
|
||||
" path: echo\n" +
|
||||
" args: [\"((.:some-text))\"]"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"some_values|Expecting a 'Sequence'",
|
||||
"bad_flight|'all' or an integer greater than 0",
|
||||
"is_fail_fast|boolean"
|
||||
);
|
||||
|
||||
editor.assertHoverContains("across", "Run a step multiple times with different combinations of variable values");
|
||||
editor.assertHoverContains("var", "The name of the variable that will be added");
|
||||
editor.assertHoverContains("values", "will iterate over");
|
||||
editor.assertHoverContains("max_in_flight", "number of substeps may run in parallel");
|
||||
editor.assertHoverContains("fail_fast", "steps will be interrupted and pending steps");
|
||||
}
|
||||
|
||||
@Test public void GH_752_repo_mirror() throws Exception {
|
||||
//See:
|
||||
// - https://github.com/concourse/registry-image-resource#:~:text=registry_mirror%3A%20Optional.
|
||||
@@ -4569,6 +4605,7 @@ public class ConcourseEditorTest {
|
||||
//"plan", exists
|
||||
//"public", exists
|
||||
//Completions for nested context (i.e. task step)
|
||||
"→ across",
|
||||
"→ attempts",
|
||||
"→ config",
|
||||
"→ ensure",
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2022 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.concourse;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.*;
|
||||
|
||||
public class IdentifierParserTest {
|
||||
|
||||
private ValueParser parser = ConcourseValueParsers.IDENTIFIER;
|
||||
|
||||
@Test
|
||||
public void goodExamples() throws Exception {
|
||||
parser.parse("identifier123.ha-boo_lalala");
|
||||
parser.parse("simple_ident");
|
||||
parser.parse("anything-with-dashes-123");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void badExamples() {
|
||||
does_not_parse("spaces are bad");
|
||||
does_not_parse("upperCaseisBad");
|
||||
does_not_parse("strange@symbols");
|
||||
does_not_parse("strange!symbols");
|
||||
does_not_parse("strange:symbols");
|
||||
}
|
||||
|
||||
private void does_not_parse(String string) {
|
||||
try {
|
||||
parser.parse(string);
|
||||
fail("Should have failed parsing!");
|
||||
} catch (Exception e) {
|
||||
assertContains("Identifier", ExceptionUtil.getMessage(e));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
jobs:
|
||||
- name: job
|
||||
plan:
|
||||
- across:
|
||||
- var: some-text
|
||||
max_in_flight: 1
|
||||
fail_fast: alala
|
||||
task: running-((.:some-text))
|
||||
config:
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: docker-image
|
||||
source:
|
||||
repository: ubuntu
|
||||
run:
|
||||
path: echo
|
||||
args: ["((.:some-text))"]
|
||||
Reference in New Issue
Block a user