Basic support for using glob pattern in concourse group declarations.
This commit is contained in:
Kris De Volder
2021-05-18 15:07:52 -07:00
parent a485c3247d
commit 6945fe18fc
7 changed files with 328 additions and 4 deletions

View File

@@ -16,7 +16,9 @@ import static org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.v
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
@@ -30,6 +32,8 @@ import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.SimpleGlob;
import org.springframework.ide.vscode.commons.util.SimpleGlob.Match;
import org.springframework.ide.vscode.commons.util.Streams;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.text.IDocument;
@@ -136,10 +140,17 @@ public class ConcourseModel {
public final void jobAssignmentIsComplete(DynamicSchemaContext dc, Node parent, Node node, YType type, IProblemCollector problems) {
Multiset<String> assignedJobs = getStringsFromAst(dc.getDocument(), JOBS_ASSIGNED_TO_GROUPS);
if (assignedJobs!=null && !assignedJobs.isEmpty()) {
Map<String, SimpleGlob> assignedJobMatchers = new HashMap<>();
for (String jobPattern : assignedJobs) {
if (!assignedJobMatchers.containsKey(jobPattern)) {
assignedJobMatchers.put(jobPattern, SimpleGlob.create(jobPattern));
}
}
getJobNameNodes(dc).forEach(jobDefName -> {
String name = NodeUtil.asScalar(jobDefName);
if (StringUtil.hasText(name)) { //'not assigned to a group' errors for empty names are a bit silly, so avoid that
if (!assignedJobs.contains(name)) {
if (!assignedJobMatchers.values().stream().anyMatch(jobPat -> jobPat.match(name)!=Match.FAIL)) {
problems.accept(YamlSchemaProblems.schemaProblem("'"+name+"' belongs to no group", jobDefName));
}
}

View File

@@ -217,6 +217,14 @@ public class PipelineYmlSchema implements YamlSchema {
return models.getJobNames(dc);
}
).require(models::passedJobHasInteractionWithResource);
AbstractType t_job_name_pattern = f.yGlobEnum("Job Name Pattern",
(parseString, validValues) -> {
return "The '"+parseString+"' Job does not match any existing job: "+validValues;
},
(DynamicSchemaContext dc) -> {
return models.getJobNames(dc);
}
);
YAtomicType t_resource_name_def = f.yatomic("Resource Name");
t_resource_name_def.parseWith(ConcourseValueParsers.resourceNameDef(models));
@@ -462,7 +470,7 @@ public class PipelineYmlSchema implements YamlSchema {
AbstractType group = f.ybean("Group");
addProp(group, "name", t_group_name_def).isPrimary(true);
addProp(group, "resources", f.yseq(t_resource_name));
addProp(group, "jobs", f.yseq(t_job_name));
addProp(group, "jobs", f.yseq(t_job_name_pattern));
YType t_background_image_def = f.yatomic("Background Image")
.parseWith(ValueParsers.NE_STRING);

View File

@@ -74,6 +74,82 @@ public class ConcourseEditorTest {
serverInitializer.setMaxCompletions(100);
}
@Test public void GH_639_globStar() throws Exception {
Editor editor = harness.newEditor(
"groups:\n" +
"- name: build\n" +
" jobs:\n" +
" - \"ci-*\"\n" +
"\n" +
"jobs:\n" +
"- name: ci-project-one\n" +
" plan:\n" +
" - task: gradle-build\n" +
" file: gradle-build-dcind.yml"
);
editor.assertProblems(/*NONE*/);
editor = harness.newEditor(
"groups:\n" +
"- name: build\n" +
" jobs:\n" +
" - one-*\n" +
" - two-*\n" +
"- name: scan\n" +
" jobs:\n" +
" - one-*\n" +
" - three-*\n" +
"\n" +
"jobs:\n" +
"- name: one-project\n" +
" plan: []\n" +
"- name: two-project\n" +
" plan: []\n" +
"- name: three-project\n" +
" plan: []\n" +
"- name: four-project\n" +
" plan: []\n"
);
editor.assertProblems("four-project|no group");
editor = harness.newEditor(
"groups:\n" +
"- name: build\n" +
" jobs:\n" +
" - ci-*\n" +
"\n" +
"jobs:\n" +
"- name: xx-project-one\n" +
" plan:\n" +
" - task: gradle-build\n" +
" file: gradle-build-dcind.yml"
);
editor.assertProblems(
"ci-*|does not match any existing job",
"xx-project-one|belongs to no group"
);
// No errors if patterns are too complex for our SimpleGlob to understand
editor = harness.newEditor(
"groups:\n" +
"- name: build\n" +
" jobs:\n" +
" - \"{one,two,three,four}-project*\"\n" +
"- name: scan\n" +
"\n" +
"jobs:\n" +
"- name: one-project\n" +
" plan: []\n" +
"- name: two-project\n" +
" plan: []\n" +
"- name: three-project\n" +
" plan: []\n" +
"- name: four-project\n" +
" plan: []\n"
);
editor.assertProblems(/*NONE*/);
}
@Test public void addSingleRequiredPropertiesQuickfix() throws Exception {
Editor editor = harness.newEditor(
"resources:\n" +
@@ -1327,7 +1403,7 @@ public class ConcourseEditorTest {
editor.assertProblems(
"build-artefact^ # <- bad|should define 'branch'",
"bogus-job|does not exist",
"bogus-job|does not match",
"not-a-resource|does not exist"
);
}