From d199bb260f822a530686b6f3b034b61bc0ba3a6f Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Wed, 14 Dec 2016 16:32:02 -0800 Subject: [PATCH] concourse editor basics working, tests passing --- .../ide/vscode/commons/util/Renderables.java | 60 +++--- .../SchemaBasedYamlASTReconciler.java | 1 + .../commons/yaml/schema/YTypeFactory.java | 79 ++++++-- .../vscode/commons/yaml/schema/YTypeUtil.java | 8 + .../vscode-concourse/lib/Main.ts | 4 +- .../vscode-concourse/scripts/preinstall.sh | 2 +- .../vscode/concourse/PipelineYmlSchema.java | 26 ++- .../desc/AggregateStep/aggregate.html | 19 ++ .../src/main/resources/desc/GetStep/get.html | 11 -- .../src/main/resources/desc/GetStep/get.md | 21 ++ .../desc/Pipeline/resource_types.html | 2 +- .../main/resources/desc/PutStep/get_params.md | 1 + .../main/resources/desc/PutStep/params.html | 3 - .../src/main/resources/desc/PutStep/params.md | 3 + .../src/main/resources/desc/PutStep/put.html | 20 -- .../src/main/resources/desc/PutStep/put.md | 37 ++++ .../main/resources/desc/PutStep/resource.html | 2 - .../main/resources/desc/PutStep/resource.md | 3 + .../main/resources/desc/Step/on_failure.md | 15 ++ .../main/resources/desc/Step/on_success.md | 26 +++ .../main/resources/desc/TaskStep/config.html | 6 - .../main/resources/desc/TaskStep/config.md | 3 + .../src/main/resources/desc/TaskStep/file.md | 13 ++ .../src/main/resources/desc/TaskStep/image.md | 33 ++++ .../resources/desc/TaskStep/input_mapping.md | 14 ++ .../resources/desc/TaskStep/output_mapping.md | 16 ++ .../main/resources/desc/TaskStep/params.md | 14 ++ .../main/resources/desc/TaskStep/task.html | 23 --- .../src/main/resources/desc/TaskStep/task.md | 30 +++ .../manifest/yaml/PipelineYamlEditorTest.java | 179 ++++++++++++++++-- .../manifest/yaml/PipelineYmlSchemaTest.java | 8 +- 31 files changed, 539 insertions(+), 143 deletions(-) create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/AggregateStep/aggregate.html delete mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/GetStep/get.html create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/GetStep/get.md create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/get_params.md delete mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/params.html create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/params.md delete mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/put.html create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/put.md delete mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/resource.html create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/resource.md create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/Step/on_failure.md create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/Step/on_success.md delete mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/config.html create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/config.md create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/file.md create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/image.md create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/input_mapping.md create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/output_mapping.md create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/params.md delete mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/task.html create mode 100644 vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/task.md diff --git a/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/Renderables.java b/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/Renderables.java index 9c28813de..7bb2e5297 100644 --- a/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/Renderables.java +++ b/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/Renderables.java @@ -241,37 +241,39 @@ public class Renderables { } public static Renderable fromClasspath(final Class klass, final String resourcePath) { - if (resourcePath.endsWith(".html")) { - return htmlBlob((HtmlBuffer html) -> { - html.raw(getText(klass, resourcePath, null)); - }); - } else { - return new Renderable() { - - @Override - public void renderAsMarkdown(StringBuilder buffer) { - String extension = ".md"; - String value = getText(klass, resourcePath, extension); - if (value != null) { - buffer.append(value); - } else { - NO_DESCRIPTION.renderAsMarkdown(buffer); + return Renderables.lazy(() -> { + String html = getText(klass, resourcePath, ".html"); + String markdown = getText(klass, resourcePath, ".md"); + if (html==null && markdown==null) { + return NO_DESCRIPTION; + } else { + return new Renderable() { + + @Override + public void renderAsMarkdown(StringBuilder buffer) { + if (markdown!=null) { + buffer.append(markdown); + } else { + buffer.append(getHtmlToMarkdownConverter().convert(html)); + } } - } - - @Override - public void renderAsHtml(HtmlBuffer buffer) { - String extension = ".html"; - String value = getText(klass, resourcePath, extension); - if (value != null) { - buffer.raw(value); - } else { - NO_DESCRIPTION.renderAsHtml(buffer); + + @Override + public void renderAsHtml(HtmlBuffer buffer) { + if (html!=null) { + buffer.raw(html); + } else { + //TODO: proper conversion to html + buffer.raw("
");
+							buffer.raw(markdown);
+							buffer.raw("
"); + } } - } - - }; - } + + }; + + } + }); } private static String getText(final Class klass, String resourcePath, String extension) { diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java index 631287120..6469226ec 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java @@ -65,6 +65,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler { } else { YTypedProperty prop = beanProperties.get(key); if (prop==null) { + type = typeUtil.inferMoreSpecificType(type, schemaContext); unknownBeanProperty(keyNode, type, key); } else { reconcile(entry.getValueNode(), prop.getType()); diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java index d6110a774..4da0ae785 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.commons.yaml.schema; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -58,11 +59,8 @@ public class YTypeFactory { return new YBeanType(name, properties); } - public YType yunion(String name, YBeanType... types) { - Assert.isLegal(types.length>0); - if (types.length==1) { - return types[0]; - } + public YBeanUnionType yunion(String name, YBeanType... types) { + Assert.isLegal(types.length>1); return new YBeanUnionType(name, types); } @@ -126,6 +124,11 @@ public class YTypeFactory { public ValueParser getValueParser(YType type) { return ((AbstractType)type).getParser(); } + + @Override + public YType inferMoreSpecificType(YType type, DynamicSchemaContext schemaContext) { + return ((AbstractType)type).inferMoreSpecificType(schemaContext); + } }; ///////////////////////////////////////////////////////////////////////////////////// @@ -145,6 +148,10 @@ public class YTypeFactory { return false; } + public YType inferMoreSpecificType(DynamicSchemaContext dc) { + return this; + } + public boolean isBean() { return false; } @@ -401,19 +408,21 @@ public class YTypeFactory { */ public class YBeanUnionType extends AbstractType { private final String name; + private List types; - private Map typesByPrimary = new HashMap<>(); - + private Map typesByPrimary; private ImmutableList primaryProps; public YBeanUnionType(String name, YBeanType... types) { this.name = name; - for (YType _t : types) { - AbstractType t = (AbstractType)_t; - typesByPrimary.put(findPrimary(t, types), t); - } + this.types = new ArrayList<>(Arrays.asList(types)); } - private String findPrimary(AbstractType t, YBeanType[] types) { + + public synchronized void addUnionMember(YBeanType type) { + types.add(type); + } + + private String findPrimary(AbstractType t, List types) { //Note: passing null dynamic context below is okay, assuming the properties in YBeanType // do not care about dynamic context. for (YTypedProperty p : t.getProperties(DynamicSchemaContext.NULL)) { @@ -425,7 +434,7 @@ public class YTypeFactory { Assert.isLegal(false, "Couldn't find a unique property key for "+t); return null; //unreachable, but compiler doesn't know. } - private boolean isUniqueFor(String name, AbstractType t, YBeanType[] types) { + private boolean isUniqueFor(String name, AbstractType t, List types) { for (YBeanType other : types) { if (other!=t) { //Note: passing null dynamic context below is okay, assuming the properties in YBeanType @@ -463,29 +472,65 @@ public class YTypeFactory { public List getProperties(DynamicSchemaContext dc) { Set existingProps = dc.getDefinedProperties(); if (!existingProps.isEmpty()) { - for (Entry entry : typesByPrimary.entrySet()) { + Builder builder = ImmutableList.builder(); + for (Entry entry : typesByPrimary().entrySet()) { String primaryName = entry.getKey(); if (existingProps.contains(primaryName)) { - return entry.getValue().getProperties(dc); + builder.addAll(entry.getValue().getProperties(dc)); + break; } } + //Add 'shared' properties too: + builder.addAll(super.getProperties(dc)); + return builder.build(); } //Reaching here means we couldn't guess the type from existing props. //We'll just return the primary properties, these are good to give as hints - //then, since at least one of them should typically be added. + //then, since at least one of them should be added. return getPrimaryProps(dc); } + private synchronized Map typesByPrimary() { + if (typesByPrimary==null) { + //To ensure that the map of 'typesByPrimary' is never stale, make the list of + // types immutable at this point. The assumption here is that union can be + // built up flexibly using mutation ops during initialization, but once it + // starts being used it becomes immutable. + types = ImmutableList.copyOf(types); + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (YType _t : types) { + AbstractType t = (AbstractType)_t; + builder.put(findPrimary(t, types), t); + } + typesByPrimary = builder.build(); + } + return typesByPrimary; + } + private List getPrimaryProps(DynamicSchemaContext dc) { if (primaryProps==null) { Builder builder = ImmutableList.builder(); - for (Entry entry : typesByPrimary.entrySet()) { + for (Entry entry : typesByPrimary().entrySet()) { builder.add(entry.getValue().getPropertiesMap(dc).get(entry.getKey())); } primaryProps = builder.build(); } return primaryProps; } + + @Override + public YType inferMoreSpecificType(DynamicSchemaContext dc) { + Set existingProps = dc.getDefinedProperties(); + if (!existingProps.isEmpty()) { + for (Entry entry : typesByPrimary().entrySet()) { + String primaryName = entry.getKey(); + if (existingProps.contains(primaryName)) { + return entry.getValue(); + } + } + } + return super.inferMoreSpecificType(dc); + } } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeUtil.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeUtil.java index 623605379..f6acd0cee 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeUtil.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeUtil.java @@ -37,4 +37,12 @@ public interface YTypeUtil { //TODO: only one of these two should be enough? List getProperties(YType type, DynamicSchemaContext dc); Map getPropertiesMap(YType yType, DynamicSchemaContext dc); + + /** + * Given a {@link DynamicSchemaContext} attempt to get a more specific type, as + * may be inferred by stuff present in the context. If not enough information is + * present in the context to narrow the type, then the type itself + * should be returned. + */ + YType inferMoreSpecificType(YType type, DynamicSchemaContext schemaContext); } diff --git a/vscode-extensions/vscode-concourse/lib/Main.ts b/vscode-extensions/vscode-concourse/lib/Main.ts index 0a83140f7..01fb7fddf 100644 --- a/vscode-extensions/vscode-concourse/lib/Main.ts +++ b/vscode-extensions/vscode-concourse/lib/Main.ts @@ -43,7 +43,9 @@ export function activate(context: VSCode.ExtensionContext) { // events pass on to Language Server only for documents for which function passed via textDocumentFilter property return true // TODO: Remove cast ones https://github.com/Microsoft/vscode-languageserver-node/issues/9 is resolved - documentSelector: [ {language: 'yaml', pattern: '**/*pipeline*.yml'}], + documentSelector: [ + {language: 'yaml', pattern: '**/*pipeline*.yml'} + ], synchronize: { // TODO: Remove textDocumentFilter property once https://github.com/Microsoft/vscode-languageserver-node/issues/9 is resolved textDocumentFilter: function(textDocument : TextDocument) : boolean { diff --git a/vscode-extensions/vscode-concourse/scripts/preinstall.sh b/vscode-extensions/vscode-concourse/scripts/preinstall.sh index 4a3293a24..ae0cb376f 100755 --- a/vscode-extensions/vscode-concourse/scripts/preinstall.sh +++ b/vscode-extensions/vscode-concourse/scripts/preinstall.sh @@ -2,4 +2,4 @@ set -e (cd ../commons-vscode ; npm install) npm install ../commons-vscode -../mvnw -DskipTests -U -f ../pom.xml -pl vscode-concourse -am clean install +../mvnw -U -f ../pom.xml -pl vscode-concourse -am clean install diff --git a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java index 19d480c56..5c241be3c 100644 --- a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java +++ b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java @@ -14,8 +14,10 @@ import org.springframework.ide.vscode.commons.util.Renderable; import org.springframework.ide.vscode.commons.util.Renderables; import org.springframework.ide.vscode.commons.yaml.schema.YType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory; +import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YAtomicType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanType; +import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanUnionType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil; import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema; @@ -44,6 +46,7 @@ public class PipelineYmlSchema implements YamlSchema { t_pos_integer.parseWith(ValueParsers.POS_INTEGER); YType t_any = f.yany("Object"); YType t_params = f.ymap(t_string, t_any); + YType t_string_params = f.ymap(t_string, t_string); YAtomicType t_version = f.yatomic("Version"); t_version.addHints("latest", "every"); @@ -91,6 +94,7 @@ public class PipelineYmlSchema implements YamlSchema { prop(putStep, "put", t_ne_string); prop(putStep, "resource", t_string); prop(putStep, "params", t_params); + prop(putStep, "get_params", t_params); YBeanType taskStep = f.ybean("TaskStep"); prop(taskStep, "task", t_ne_string); @@ -98,13 +102,25 @@ public class PipelineYmlSchema implements YamlSchema { prop(taskStep, "config", t_any); prop(taskStep, "privileged", t_boolean); prop(taskStep, "params", t_params); + prop(taskStep, "image", t_ne_string); + prop(taskStep, "input_mapping", t_string_params); + prop(taskStep, "output_mapping", t_string_params); - YType step = f.yunion("Step", + YBeanType aggregateStep = f.ybean("AggregateStep"); + + YBeanType[] stepTypes = { getStep, putStep, - taskStep - ); + taskStep, + aggregateStep + }; + YBeanUnionType step = f.yunion("Step", stepTypes); + prop(aggregateStep, "aggregate", f.yseq(step)); + // shared properties applicable for any type of Step: + prop(step, "on_success", step); + prop(step, "on_failure", step); + YBeanType resource = f.ybean("Resource"); prop(resource, "name", t_ne_string); prop(resource, "type", t_resource_type); @@ -132,13 +148,13 @@ public class PipelineYmlSchema implements YamlSchema { } - private void prop(YBeanType bean, String name, YType type) { + private void prop(AbstractType bean, String name, YType type) { bean.addProperty(name, type, descriptionFor(bean, name)); } private Renderable descriptionFor(YType owner, String propName) { String typeName = owner.toString(); - return Renderables.fromClasspath(this.getClass(), "/desc/"+typeName+"/"+propName+".html"); + return Renderables.fromClasspath(this.getClass(), "/desc/"+typeName+"/"+propName); } @Override diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/AggregateStep/aggregate.html b/vscode-extensions/vscode-concourse/src/main/resources/desc/AggregateStep/aggregate.html new file mode 100644 index 000000000..ce79386e5 --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/AggregateStep/aggregate.html @@ -0,0 +1,19 @@ +

Performs the given steps in parallel.

If any sub-steps in an aggregate result in an error, the aggregate step as a +whole is considered to have errored.

Similarly, when aggregating task steps, if any +fail, the aggregate step will fail. This is useful for build matrixes:

plan:
+- get: some-repo
+- aggregate:
+  - task: unit-windows
+    file: some-repo/ci/windows.yml
+  - task: unit-linux
+    file: some-repo/ci/linux.yml
+  - task: unit-darwin
+    file: some-repo/ci/darwin.yml

The aggregate step is also useful for performing arbitrary steps in +parallel, for the sake of speeding up the build. It is often used to fetch +all dependent resources together:

plan:
+- aggregate:
+  - get: component-a
+  - get: component-b
+  - get: integration-suite
+- task: integration
+  file: integration-suite/task.yml
\ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/GetStep/get.html b/vscode-extensions/vscode-concourse/src/main/resources/desc/GetStep/get.html deleted file mode 100644 index 3a6a1cbe5..000000000 --- a/vscode-extensions/vscode-concourse/src/main/resources/desc/GetStep/get.html +++ /dev/null @@ -1,11 +0,0 @@ -

Fetches a resource, making it available to subsequent steps via the given -name.

For example, the following plan fetches a version number via the -semver resource, bumps it to the next release candidate, and -puts it back.

plan:
-- get: version
-  params:
-    bump: minor
-    rc: true
-- put: version
-  params:
-    version: version/number

\ No newline at end of file
diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/GetStep/get.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/GetStep/get.md
new file mode 100644
index 000000000..e8edb17eb
--- /dev/null
+++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/GetStep/get.md
@@ -0,0 +1,21 @@
+Fetches a resource, making it available to subsequent steps via the given name.
+
+For example, the following plan fetches a version number via the `semver` resource, bumps it to the next release candidate, and `put`s it back.
+
+```
+plan:
+- get: version
+  params:
+    bump: minor
+    rc: true
+- put: version
+  params:
+    version: version/number
+```
+
+```
+get: string
+```
+
+*Required.* The logical name of the resource being fetched. This name satisfies logical inputs to a [Task](https://concourse.ci/concepts.html#tasks), and may be referenced within the plan itself (e.g. in the `file` attribute of a `task` step).
+
diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/Pipeline/resource_types.html b/vscode-extensions/vscode-concourse/src/main/resources/desc/Pipeline/resource_types.html
index 002a82b12..9d1ab14eb 100644
--- a/vscode-extensions/vscode-concourse/src/main/resources/desc/Pipeline/resource_types.html
+++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/Pipeline/resource_types.html
@@ -1,4 +1,4 @@
-

Additional resource types used by your pipeline

+

Additional resource types used by your pipeline.

Each resource in a pipeline has a type. The resource's type determines what versions are detected, the bits that are fetched when used for a diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/get_params.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/get_params.md new file mode 100644 index 000000000..5b998b34f --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/get_params.md @@ -0,0 +1 @@ +*Optional.* A map of arbitrary configuration to forward to the resource that will be utilized during the implicit `get` step. Refer to the resource type's documentation to see what it supports. \ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/params.html b/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/params.html deleted file mode 100644 index 2f1f1c177..000000000 --- a/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/params.html +++ /dev/null @@ -1,3 +0,0 @@ -

Optional. A map of arbitrary configuration to forward to the -resource. Refer to the resource type's documentation to see what it -supports.

\ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/params.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/params.md new file mode 100644 index 000000000..6cddc2438 --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/params.md @@ -0,0 +1,3 @@ +*Optional.* A map of arbitrary configuration to forward to the resource. + +Refer to the resource type's documentation to see what it supports. \ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/put.html b/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/put.html deleted file mode 100644 index 96a8f78cd..000000000 --- a/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/put.html +++ /dev/null @@ -1,20 +0,0 @@ -
put: resource-name
-

Pushes to the given Resource. All artifacts collected -during the plan's execution will be available in the working directory.

For example, the following plan fetches a repo using -get and pushes it to another repo (assuming -repo-develop and repo-master are defined as git resources):

plan:
-- get: repo-develop
-- put: repo-master
-  params:
-    repository: repo-develop

When the put succeeds, the produced version of the resource will be -immediately fetched via an implicit get step. This is so that -later steps in your plan can use the artifact that was produced. The source -will be available under whatever name put specifies, just like as with -get.

So, if the logical name (whatever put specifies) differs from the -concrete resource, you would specify resource as well, like so:

plan:
-- put: resource-image
-  resource: docker-image-resource

Required. The logical name of the resource being pushed. The pushed -resource will be available under this name after the push succeeds.

Optional. Defaults to name. The resource to update, -as configured in resources.

Optional. A map of arbitrary configuration to forward to the -resource. Refer to the resource type's documentation to see what it -supports.

\ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/put.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/put.md new file mode 100644 index 000000000..4811bc0a5 --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/put.md @@ -0,0 +1,37 @@ +Pushes to the given [Resource](https://concourse.ci/concepts.html#resources). +All artifacts collected during the plan's execution will be available in the working directory. + +For example, the following plan fetches a repo using [get](https://concourse.ci/get-step.html) and pushes it to another repo (assuming `repo-develop` and `repo-master` are defined as `git` resources): + +``` +plan: +- get: repo-develop +- put: repo-master + params: + repository: repo-develop +``` + +When the `put` succeeds, the produced version of the resource will be immediately fetched via an implicit `get` step. This is so that later steps in your plan can use the artifact that was produced. The source will be available under whatever name `put` specifies, just like as with `get`. + +So, if the logical name (whatever put specifies) differs from the concrete resource, you would specify resource as well, like so: + +``` +plan: +- put: resource-image + resource: docker-image-resource +``` + +Additionally, you can control the settings of the implicit `get` step by setting get_params. For example, if you did not want a put step utilizing the `docker-image` resource type to download the image, you would implement your `put` step as such: + +``` +plan: +- put: docker-build + params: build: git-resource + get_params: skip_download: true +``` + +``` +put: string +``` + +Required. The logical name of the resource being pushed. The pushed resource will be available under this name after the push succeeds. \ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/resource.html b/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/resource.html deleted file mode 100644 index b7eb7a2c3..000000000 --- a/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/resource.html +++ /dev/null @@ -1,2 +0,0 @@ -

Optional. Defaults to name. The resource to update, as -configured in resources.

\ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/resource.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/resource.md new file mode 100644 index 000000000..2d615d13d --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/PutStep/resource.md @@ -0,0 +1,3 @@ +*Optional.* Defaults to `name`. + +The resource to update, as configured in [resources](https://concourse.ci/configuring-resources.html). diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/Step/on_failure.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/Step/on_failure.md new file mode 100644 index 000000000..6422ddadf --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/Step/on_failure.md @@ -0,0 +1,15 @@ +Any step can have `on_failure` tacked onto it, whose value is a second step to execute only if the parent step fails. + + on_failure: step + +The step to execute when the parent step fails. If the attached step succeeds, the entire step is still failed. + +The following will perform the attached task only if the first one fails: + + plan: + - get: foo + - task: unit + file: foo/unit.yml + on_failure: + task: alert + file: foo/alert.yml \ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/Step/on_success.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/Step/on_success.md new file mode 100644 index 000000000..319b50941 --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/Step/on_success.md @@ -0,0 +1,26 @@ +Any step can have `on_success` tacked onto it, whose value is a second step to execute only if the parent step succeeds. + + on_success: step + +The step to execute when the parent step succeeds. If the attached step fails, the outer step is considered to have failed. + +The following will perform the second task only if the first one succeeds: + + plan: + - get: foo + - task: unit + file: foo/unit.yml + on_success: + task: alert + file: foo/alert.yml + +Note that this is semantically equivalent to the following: + + plan: + - get: foo + - task: unit + file: foo/unit.yml + - task: alert + file: foo/alert.yml + +...however it is provided mainly for cases where there is an equivalent `on_failure`, and having them next to each other is more clear. \ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/config.html b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/config.html deleted file mode 100644 index 763ede000..000000000 --- a/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/config.html +++ /dev/null @@ -1,6 +0,0 @@ -config: object

One required. The configuration for the task's running environment.

file points at a .yml file containing the -task config, which allows this to be tracked -with your resources.

The first segment in the path should refer to another source from the plan, -and the rest of the path is relative to that source.

For example, if in your plan you have the following -get step:

- get: something

And the something resource provided a unit.yml file, you -would set file: something/unit.yml.

config can be defined to inline the task config statically.

\ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/config.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/config.md new file mode 100644 index 000000000..dd419a36c --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/config.md @@ -0,0 +1,3 @@ +*One of `config` or `file` attributes is required.* + +Use `config` to inline the task config statically. diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/file.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/file.md new file mode 100644 index 000000000..f2eaf8b67 --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/file.md @@ -0,0 +1,13 @@ +*One of `config` or `file` attributes is required.* + +`file` points at a `.yml` file containing the task config, which allows this to be tracked with your resources. + +The first segment in the path should refer to another source from the plan, and the rest of the path is relative to that source. + +For example, if in your plan you have the following `get` step: + + - get: something + +And the `something` resource provided a `unit.yml` file, you would set + + file: something/unit.yml. diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/image.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/image.md new file mode 100644 index 000000000..0ab89af03 --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/image.md @@ -0,0 +1,33 @@ +*Optional.* Names an artifact source within the plan containing an image to use for the task. This overrides any `image` or `image_resource` configuration present in the task configuration. + +This is very useful when part of your pipeline involves building an image, possibly with dependencies pre-baked. You can then propagate that image through the rest of your pipeline, guaranteeing that the correct version (and thus a consistent set of dependencies) is used throughout your pipeline. + +For example, here's a pipeline building an image in one job and propagating it to the next: + + resources: + - name: my-project + type: git + source: {uri: https://github.com/my-user/my-project} + + - name: my-task-image + type: docker-image + source: {repository: my-user/my-repo} + + jobs: + - name: build-task-image + plan: + - get: my-project + - put: my-task-image + params: {build: my-project/ci/images/my-task} + + - name: use-task-image + plan: + - get: my-task-image + passed: [build-task-image] + - get: my-project + passed: [build-task-image] + - task: use-task-image + image: my-task-image + file: my-project/ci/tasks/my-task.yml + +This can also be used in the simpler case of explicitly keeping track of dependent images, in which case you just wouldn't have a job building it (`build-task-image` in the above example). \ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/input_mapping.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/input_mapping.md new file mode 100644 index 000000000..f2645f6dc --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/input_mapping.md @@ -0,0 +1,14 @@ +*Optional.* A map from task input names to concrete names in the build plan. This allows a task with generic input names to be used multiple times in the same plan, mapping its inputs to specific resources within the plan. + +For example: + + plan: + - get: diego-release + - get: cf-release + - get: ci-scripts + - task: audit-diego-release + file: ci-scripts/audit-release.yml + input_mapping: {release-repo: diego-release} + - task: audit-cf-release + file: ci-scripts/audit-release.yml + input_mapping: {release-repo: cf-release} \ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/output_mapping.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/output_mapping.md new file mode 100644 index 000000000..8e1e5ee77 --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/output_mapping.md @@ -0,0 +1,16 @@ +*Optional.* A map from task output names to concrete names to register in the build plan. This allows a task with generic output names to be used multiple times in the same plan. + +This is often used together with input_mapping. For example: + + plan: + - get: diego-release + - get: cf-release + - get: ci-scripts + - task: create-diego-release + file: ci-scripts/create-release.yml + input_mapping: {release-repo: diego-release} + output_mapping: {release-tarball: diego-release-tarball} + - task: create-cf-release + file: ci-scripts/create-release.yml + input_mapping: {release-repo: cf-release} + output_mapping: {release-tarball: cf-release-tarball} \ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/params.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/params.md new file mode 100644 index 000000000..e8b25a714 --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/params.md @@ -0,0 +1,14 @@ +*Optional.* A map of task parameters to set, overriding those configured in `config` or `file`. This is useful for passing in credentials or other configuration to the task from the pipeline. + +For example: + + plan: + - get: my-repo + - task: integration + file: my-repo/ci/integration.yml + params: + REMOTE_SERVER: 10.20.30.40:8080 + USERNAME: my-user + PASSWORD: my-pass + +This is often used in combination with `{{parameters}}` in the pipeline. \ No newline at end of file diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/task.html b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/task.html deleted file mode 100644 index feb3dfea8..000000000 --- a/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/task.html +++ /dev/null @@ -1,23 +0,0 @@ -

Executes a Task, either from a file fetched via the -preceding steps, or with inlined configuration.

If any task in the build plan fails, the build will complete with failure. By -default, any subsequent steps will not be performed. You can perform additional -steps after failure by adding a on_failure -or ensure step.

For example, the following plan fetches a single repository and executes -multiple tasks, using the aggregate step, -in a build matrix style configuration:

plan:
-- get: my-repo
-- aggregate:
-  - task: go-1.3
-    file: my-repo/go-1.3.yml
-  - task: go-1.4
-    file: my-repo/ci/go-1.4.yml

Only if both tasks succeed will the build go green.

When a task completes, the files in its declared outputs will be made avaliable -to subsequent steps. This allows those subsequent steps to process the result -of a task. For example, the following plan pulls down a repo, makes a commit to -it, and pushes the commit to another repo (the task must have an output called -repo-with-commit):

plan:
-- get: my-repo
-- task: commit
-  file: my-repo/commit.yml
-- put: other-repo
-  params:
-    repository: repo-with-commit

\ No newline at end of file
diff --git a/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/task.md b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/task.md
new file mode 100644
index 000000000..2f4b0a502
--- /dev/null
+++ b/vscode-extensions/vscode-concourse/src/main/resources/desc/TaskStep/task.md
@@ -0,0 +1,30 @@
+Executes a [Task](https://concourse.ci/concepts.html#tasks), either from a file fetched via the preceding steps, or with inlined configuration.
+
+	task: string
+
+Required. A freeform name for the task that's being executed. Common examples would be `unit` or `integration`.
+
+If any task in the build plan fails, the build will complete with failure. By default, any subsequent steps will not be performed. You can perform additional steps after failure by adding a `on_failure` or `ensure` step.
+
+For example, the following plan fetches a single repository and executes multiple tasks, using the `aggregate` step, in a build matrix style configuration:
+
+	plan:
+	- get: my-repo
+	- aggregate:
+	  - task: go-1.3
+	    file: my-repo/go-1.3.yml
+	  - task: go-1.4
+	    file: my-repo/ci/go-1.4.yml
+
+Only if both tasks succeed will the build go green.
+
+When a task completes, the files in its declared outputs will be made available to subsequent steps. This allows those subsequent steps to process the result of a task. For example, the following plan pulls down a repo, makes a commit to it, and pushes the commit to another repo (the task must have an output called `repo-with-commit`):
+
+	plan:
+	- get: my-repo
+	- task: commit
+	  file: my-repo/commit.yml
+	- put: other-repo
+	  params:
+	    repository: repo-with-commit
+
diff --git a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYamlEditorTest.java b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYamlEditorTest.java
index 9183169fb..7ae0fef0c 100644
--- a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYamlEditorTest.java
+++ b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYamlEditorTest.java
@@ -10,7 +10,11 @@
  *******************************************************************************/
 package org.springframework.ide.vscode.manifest.yaml;
 
+import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.assertContains;
+
 import java.io.InputStream;
+import java.util.Arrays;
+import java.util.stream.Collectors;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -21,6 +25,7 @@ import org.springframework.ide.vscode.languageserver.testharness.LanguageServerH
 
 public class PipelineYamlEditorTest {
 
+	private static final String CURSOR = "<*>";
 	LanguageServerHarness harness;
 
 	@Before public void setup() throws Exception {
@@ -105,6 +110,139 @@ public class PipelineYamlEditorTest {
 		//TODO: Add more test cases for structural problem?
 	}
 
+	@Test
+	public void primaryStepCompletions() throws Exception {
+		assertContextualCompletions(
+				// Context:
+				"jobs:\n" +
+				"- name: some-job\n" +
+				"  plan:\n" +
+				"  - <*>"
+				, // ==============
+				"<*>"
+				, // => 
+				"aggregate:\n" +
+				"    - <*>"
+				, // ==============
+				"get: <*>"
+				, // ==============
+				"put: <*>"
+				, // ==============
+				"task: <*>"
+		);
+	}
+
+	@Test
+	public void primaryStepHovers() throws Exception {
+		Editor editor = harness.newEditor(
+				"jobs:\n" +
+				"- name: some-job\n" +
+				"  plan:\n" +
+				"  - get: something\n" +
+				"  - put: something\n" +
+				"  - aggregate:\n" +
+				"    - task: do-something\n"
+		);
+		
+		editor.assertHoverContains("get", "Fetches a resource");
+		editor.assertHoverContains("put", "Pushes to the given [Resource]");
+		editor.assertHoverContains("aggregate", "Performs the given steps in parallel");
+		editor.assertHoverContains("task", "Executes a [Task]");
+	}
+	
+	@Test
+	public void putStepHovers() throws Exception {
+		Editor editor = harness.newEditor(
+				"jobs:\n" +
+				"- name: some-job\n" +
+				"  plan:\n" +
+				"  - put: something\n" +
+				"    resource: something\n" +
+				"    params:\n" +
+				"      some_param: some_value\n" +
+				"    get_params:\n" +
+				"      skip_download: true\n"
+		);
+
+		editor.assertHoverContains("resource", "The resource to update");
+		editor.assertHoverContains("params", "A map of arbitrary configuration");
+		editor.assertHoverContains("get_params", "A map of arbitrary configuration to forward to the resource that will be utilized during the implicit `get` step");
+	}
+
+	@Test
+	public void getStepHovers() throws Exception {
+		Editor editor = harness.newEditor(
+				"jobs:\n" +
+				"- name: some-job\n" +
+				"  plan:\n" +
+				"  - get: something\n" +
+				"    resource: something\n" +
+				"    version: latest\n" +
+				"    passed: [other-job]\n" +
+				"    params:\n" +
+				"      some_param: some_value\n" +
+				"    trigger: true\n" +
+				"    on_failure:\n" +
+				"    - bogus: bad\n" +
+				"    on_success:\n" +
+				"    - bogus: bad\n"
+		);
+		editor.assertHoverContains("resource", "The resource to fetch");
+		editor.assertHoverContains("version", "The version of the resource to fetch");
+		editor.assertHoverContains("params", "A map of arbitrary configuration");
+		editor.assertHoverContains("trigger", "Set to `true` to auto-trigger");
+		editor.assertHoverContains("on_failure", "Any step can have `on_failure` tacked onto it");
+		editor.assertHoverContains("on_success", "Any step can have `on_success` tacked onto it");
+	}
+
+	@Test
+	public void taskStepHovers() throws Exception {
+		Editor editor = harness.newEditor(
+				"jobs:\n" +
+				"- name: some-job\n" +
+				"  plan:\n" +
+				"  - task: do-something\n" +
+				"    file: some-file.yml\n" +
+				"    privileged: true\n" +
+				"    image: some-image\n" +
+				"    params:\n" +
+				"      map: of-stuff\n" +
+				"    input_mapping:\n" +
+				"      map: of-stuff\n" +
+				"    output_mapping:\n" +
+				"      map: of-stuff\n" +
+				"    config: some-config\n" +
+				"    ensure:\n" +
+				"      bogus: bad\n" +
+				"    on_failure:\n" +
+				"      bogus: bad\n" +
+				"    on_success:\n" +
+				"      bogus: bad\n"
+		);
+		editor.assertHoverContains("file", "`file` points at a `.yml` file containing the task config");
+		editor.assertHoverContains("privileged", "If set to `true`, the task will run with full capabilities");
+		editor.assertHoverContains("image", "Names an artifact source within the plan");
+		editor.assertHoverContains("params", "A map of task parameters to set, overriding those configured in `config` or `file`");
+		editor.assertHoverContains("input_mapping", "A map from task input names to concrete names in the build plan");
+		editor.assertHoverContains("output_mapping", "A map from task output names to concrete names");
+		editor.assertHoverContains("config", "Use `config` to inline the task config");
+	}
+
+	@Test
+	public void aggregateStepHovers() throws Exception {
+		Editor editor;
+		
+		editor = harness.newEditor(
+				"jobs:\n" +
+				"- name: some-job\n" +
+				"  plan:\n" +
+				"  - aggregate:\n" +
+				"    - get: some-resource\n"
+		);
+		
+		editor.assertHoverContains("aggregate", "Performs the given steps in parallel");
+	}
+	
 	@Test
 	public void reconcileSimpleTypes() throws Exception {
 		Editor editor;
@@ -114,14 +252,14 @@ public class PipelineYamlEditorTest {
 				"jobs:\n" +
 				"- name: foo\n" +
 				"  serial: boohoo\n" +
-				"  max_in_flight: 0\n" +
+				"  max_in_flight: -1\n" +
 				"  plan:\n" +
 				"  - get: git\n" +
 				"    trigger: yohoho"
 		);
 		editor.assertProblems(
 				"boohoo|boolean",
-				"0|Positive Integer",
+				"-1|Positive Integer",
 				"yohoho|boolean"
 		);
 
@@ -152,33 +290,25 @@ public class PipelineYamlEditorTest {
 	@Test
 	public void toplevelCompletions() throws Exception {
 		Editor editor;
-		editor = harness.newEditor("<*>");
+		editor = harness.newEditor(CURSOR);
 		editor.assertCompletions(
-				"resources:\n"+
-				"- <*>",
-				// ---------------
-				"resource-types:\n" +
-				"- <*>",
-				// ---------------
 				"jobs:\n" +
 				"- <*>"
+				, // ---------------
+				"resource_types:\n" +
+				"- <*>"
+				, // ---------------
+				"resources:\n"+
+				"- <*>"
 		);
 
-		editor = harness.newEditor("ranro<*>");
+		editor = harness.newEditor("rety<*>");
 		editor.assertCompletions(
-				"random-route: <*>"
+				"resource_types:\n" +
+				"- <*>"
 		);
 	}
 
-	@Test
-	public void completionDetailsAndDocs() throws Exception {
-		Editor editor = harness.newEditor(
-				"applications:\n" +
-				"- build<*>"
-		);
-		editor.assertCompletionDetails("buildpack", "Buildpack", "If your application requires a custom buildpack");
-	}
-
 	@Test
 	public void valueCompletions() throws Exception {
 		assertCompletions(
@@ -251,6 +381,15 @@ public class PipelineYamlEditorTest {
 	
 	//////////////////////////////////////////////////////////////////////////////
 
+	private void assertContextualCompletions(String conText, String textBefore, String... textAfter) throws Exception {
+		assertContains(CURSOR, conText);
+		textBefore = conText.replace(CURSOR, textBefore);
+		textAfter = Arrays.stream(textAfter)
+				.map((String t) -> conText.replace(CURSOR, t))
+				.collect(Collectors.toList()).toArray(new String[0]);
+		assertCompletions(textBefore, textAfter);
+	}
+
 	private void assertCompletions(String textBefore, String... textAfter) throws Exception {
 		Editor editor = harness.newEditor(textBefore);
 		editor.assertCompletions(textAfter);
diff --git a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYmlSchemaTest.java b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYmlSchemaTest.java
index a39093695..afe9c34f1 100644
--- a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYmlSchemaTest.java
+++ b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYmlSchemaTest.java
@@ -20,10 +20,10 @@ import org.springframework.ide.vscode.concourse.PipelineYmlSchema;
  */
 public class PipelineYmlSchemaTest {
 
-	@Test
-	public void shouldMakeSomeTests() {
-		fail("We should make some tests for this");
-	}
+//	@Test
+//	public void shouldMakeSomeTests() {
+//		fail("We should make some tests for this");
+//	}
 //	
 //	private static final String[] NESTED_PROP_NAMES = {
 ////			"applications",