Moved commons and concourse editor to 'headless-services'

This commit is contained in:
Kris De Volder
2017-04-06 16:27:12 -07:00
parent 3abf189512
commit bf8f8cffa9
608 changed files with 600 additions and 51 deletions

View File

@@ -0,0 +1,88 @@
/*******************************************************************************
* Copyright (c) 2017 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.reconcile.ITypeCollector;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YAtomicType;
import org.yaml.snakeyaml.nodes.Node;
import com.google.common.collect.ImmutableMap;
/**
* An implementation of {@link ITypeCollector} which keeps track of the
* types of 'interesting' nodes in the ast.
*
* @author Kris De Volder
*/
public class ASTTypeCache implements ITypeCollector {
/**
* Set upon commencing a reconciler session.
*/
private YamlFileAST currentAst = null;
/**
* Collects types for the current session.
*/
private ImmutableMap.Builder<Node, YType> currentTypes = null;
private final Set<YType> interestingTypes = new HashSet<>();
private final Map<String, ImmutableMap<Node, YType>> typeIndex = new HashMap<>();
@Override
public void beginCollecting(YamlFileAST ast) {
Assert.isNull("A session is already active. Concurrency isn't supported by ITypeCollector protocol", currentTypes);
this.currentAst = ast;
this.currentTypes = ImmutableMap.builder();
}
@Override
public void endCollecting(YamlFileAST ast) {
Assert.isLegal(currentAst==ast);
String uri = ast.getDocument().getUri();
typeIndex.put(uri, currentTypes.build());
this.currentAst = null;
this.currentTypes = null;
}
@Override
public void accept(Node node, YType type) {
if (interestingTypes.contains(type)) {
currentTypes.put(node, type);
}
}
public YType getType(YamlFileAST ast, Node node) {
ImmutableMap<Node, YType> types = typeIndex.get(ast.getDocument().getUri());
if (types!=null) {
return types.get(node);
}
return null;
}
/**
* Declares a given YType as 'interesting'. This means that nodes of this type will be
* added to the index.
*/
public void addInterestingType(YType type) {
this.interestingTypes.add(type);
}
}

View File

@@ -0,0 +1,108 @@
/*******************************************************************************
* Copyright (c) 2017 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.springframework.ide.vscode.commons.languageserver.definition.SimpleDefinitionFinder;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.yaml.snakeyaml.nodes.Node;
import reactor.core.publisher.Flux;
public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<ConcourseLanguageServer> {
@FunctionalInterface
private interface Handler {
Flux<Location> handle(Node refNode, TextDocument doc, YamlFileAST ast);
}
private final ConcourseModel models;
private ASTTypeCache astTypes;
private Map<YType, Handler> handlers = new HashMap<>();
public ConcourseDefinitionFinder(ConcourseLanguageServer server, ConcourseModel models, PipelineYmlSchema schema) {
super(server);
this.models = models;
this.astTypes = models.getAstTypeCache();
findByPath(schema.t_resource_name, ConcourseModel.RESOURCE_NAMES_PATH);
findByPath(schema.t_job_name, ConcourseModel.JOB_NAMES_PATH);
findByPath(schema.t_resource_type_name, ConcourseModel.RESOURCE_TYPE_NAMES_PATH);
}
/**
* Add a handler that finds the definitions for a target node within the same document
* by following a {@link YamlPath} to find candidate nodes.
*
* @param refType the type inferred by the reconciler for the target node.
* @param definitionsPath Path that points to all nodes within the same file corresponding
* to definitions of nodes of the given type.
*/
private void findByPath(YType refType, YamlPath definitionsPath) {
astTypes.addInterestingType(refType);
Handler handler = (Node refNode, TextDocument doc, YamlFileAST ast) -> {
String name = NodeUtil.asScalar(refNode);
if (name!=null) {
return Flux.fromStream(definitionsPath.traverseAmbiguously(ast))
.filter((node) -> name.equals(NodeUtil.asScalar(node)))
.map((node) -> toLocation(doc, node))
.filter(Optional::isPresent)
.map(Optional::get);
}
return Flux.empty();
};
handlers.put(refType, handler);
}
@Override
protected Flux<Location> findDefinitions(TextDocumentPositionParams params) {
try {
TextDocument doc = server.getTextDocumentService().get(params);
if (doc!=null) {
YamlFileAST ast = models.getSafeAst(doc, false);
Node refNode = ast.findNode(doc.toOffset(params.getPosition()));
if (refNode!=null) {
YType type = astTypes.getType(ast, refNode);
if (type!=null) {
Handler handler = handlers.get(type);
if (handler!=null) {
return handler.handle(refNode, doc, ast);
}
}
}
}
} catch (Exception e) {
Log.log(e);
}
return Flux.empty();
}
Optional<Location> toLocation(TextDocument doc, Node node) {
int start = node.getStartMark().getIndex();
int end = node.getEndMark().getIndex();
try {
return Optional.of(new Location(doc.getUri(), doc.toRange(start, end-start)));
} catch (BadLocationException e) {
Log.log(e);
return Optional.empty();
}
}
}

View File

@@ -0,0 +1,164 @@
/*******************************************************************************
* Copyright (c) 2016 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.CompletionOptions;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.TextDocumentSyncKind;
import org.springframework.ide.vscode.commons.languageserver.LanguageIds;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngine;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider;
import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEngineAdapter;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
import org.springframework.ide.vscode.commons.yaml.completion.SchemaBasedYamlAssistContextProvider;
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngine;
import org.springframework.ide.vscode.commons.yaml.hover.YamlHoverInfoProvider;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaBasedReconcileEngine;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
import com.google.common.collect.ImmutableList;
public class ConcourseLanguageServer extends SimpleLanguageServer {
YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT;
SimpleTextDocumentService documents = getTextDocumentService();
ConcourseModel models = new ConcourseModel(documents);
YamlASTProvider currentAsts = models.getAstProvider(false);
private SchemaSpecificPieces forPipelines;
private SchemaSpecificPieces forTasks;
private class SchemaSpecificPieces {
final VscodeCompletionEngineAdapter completionEngine;
final VscodeHoverEngineAdapter hoverEngine;
final YamlSchemaBasedReconcileEngine reconcileEngine;
SchemaSpecificPieces(YamlSchema schema) {
SchemaBasedYamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema);
YamlCompletionEngine yamlCompletionEngine = new YamlCompletionEngine(structureProvider, contextProvider);
this.completionEngine = new VscodeCompletionEngineAdapter(ConcourseLanguageServer.this, yamlCompletionEngine);
HoverInfoProvider infoProvider = new YamlHoverInfoProvider(currentAsts, structureProvider, contextProvider);
this.hoverEngine = new VscodeHoverEngineAdapter(ConcourseLanguageServer.this, infoProvider);
this.reconcileEngine = new YamlSchemaBasedReconcileEngine(currentAsts, schema);
reconcileEngine.setTypeCollector(models.getAstTypeCache());
}
public void setMaxCompletions(int max) {
completionEngine.setMaxCompletionsNumber(max);
}
}
public ConcourseLanguageServer() {
PipelineYmlSchema pipelineSchema = new PipelineYmlSchema(models);
this.forPipelines = new SchemaSpecificPieces(pipelineSchema);
this.forTasks = new SchemaSpecificPieces(pipelineSchema.getTaskSchema());
ConcourseDefinitionFinder definitionFinder = new ConcourseDefinitionFinder(this, models, pipelineSchema);
// SimpleWorkspaceService workspace = getWorkspaceService();
documents.onDidChangeContent(params -> {
TextDocument doc = params.getDocument();
if (LanguageIds.CONCOURSE_PIPELINE.equals(doc.getLanguageId())) {
validateWith(doc, forPipelines.reconcileEngine);
} else if (LanguageIds.CONCOURSE_TASK.equals(doc.getLanguageId())) {
validateWith(doc, forTasks.reconcileEngine);
} else {
validateWith(doc, IReconcileEngine.NULL);
}
});
// workspace.onDidChangeConfiguraton(settings -> {
// System.out.println("Config changed: "+params);
// Integer val = settings.getInt("languageServerExample", "maxNumberOfProblems");
// if (val!=null) {
// maxProblems = ((Number) val).intValue();
// for (TextDocument doc : documents.getAll()) {
// validateDocument(documents, doc);
// }
// }
// });
documents.onCompletion(params -> {
TextDocument doc = documents.get(params);
if (doc!=null) {
if (LanguageIds.CONCOURSE_PIPELINE.equals(doc.getLanguageId())) {
return forPipelines.completionEngine.getCompletions(params);
} else if (LanguageIds.CONCOURSE_TASK.equals(doc.getLanguageId())) {
return forTasks.completionEngine.getCompletions(params);
}
}
return CompletableFuture.completedFuture(new CompletionList(false, ImmutableList.of()));
});
documents.onCompletionResolve(params -> {
//this is a bogus implementation. But its not currently used.
throw new IllegalStateException("Not implemented");
});
documents.onHover(params -> {
TextDocument doc = documents.get(params);
if (doc!=null) {
if (LanguageIds.CONCOURSE_PIPELINE.equals(doc.getLanguageId())) {
return forPipelines.hoverEngine.getHover(params);
} else if (LanguageIds.CONCOURSE_TASK.equals(doc.getLanguageId())) {
return forTasks.hoverEngine.getHover(params);
}
}
return SimpleTextDocumentService.NO_HOVER;
});
documents.onDefinition(definitionFinder);
}
@Override
protected DiagnosticSeverity getDiagnosticSeverity(ReconcileProblem problem) {
ProblemType type = problem.getType();
if (YamlSchemaProblems.PROPERTY_CONSTRAINT.contains(type)) {
return DiagnosticSeverity.Warning;
}
return super.getDiagnosticSeverity(problem);
}
public SimpleLanguageServer setMaxCompletions(int max) {
forPipelines.setMaxCompletions(max);
forTasks.setMaxCompletions(max);
return this;
}
@Override
protected ServerCapabilities getServerCapabilities() {
ServerCapabilities c = new ServerCapabilities();
c.setTextDocumentSync(TextDocumentSyncKind.Incremental);
c.setHoverProvider(true);
CompletionOptions completionProvider = new CompletionOptions();
completionProvider.setResolveProvider(false);
c.setCompletionProvider(completionProvider);
c.setDefinitionProvider(true);
return c;
}
}

View File

@@ -0,0 +1,282 @@
/*******************************************************************************
* Copyright (c) 2016 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import static org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.anyChild;
import static org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.valueAt;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Function;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.languageserver.util.TextDocumentContentChange;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.ast.YamlParser;
import org.springframework.ide.vscode.commons.yaml.path.ASTRootCursor;
import org.springframework.ide.vscode.commons.yaml.path.NodeCursor;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory;
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
import org.springframework.ide.vscode.concourse.util.CollectorUtil;
import org.springframework.ide.vscode.concourse.util.StaleFallbackCache;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableMultiset.Builder;
import com.google.common.collect.Multiset;
/**
* ConcourseModels is responsible for extracting various bits of information
* out of .yml documents and caching them for use by various tools (reconcile engine
* and completion engine).
*/
public class ConcourseModel {
/**
* Wraps around a Node in the AST that represents a 'step' and
* provides methods for accessing information from the node.
*/
public static class StepModel {
private final String stepType;
private final MappingNode step;
public StepModel(String stepType, MappingNode step) {
this.stepType = stepType;
this.step = step;
}
public Node getResourceNameNode() {
Node node = NodeUtil.getProperty(step, "resource");
return node!=null ? node : NodeUtil.getProperty(step, stepType);
}
public String getResourceName() {
return NodeUtil.asScalar(getResourceNameNode());
}
}
public static class ResourceModel {
private final Node resource;
public ResourceModel(Node resource) {
this.resource = resource;
}
public String getType() {
return NodeUtil.getScalarProperty(resource, "type");
}
public boolean hasSourceProperty(String propName) {
YamlPath path = new YamlPath(YamlPathSegment.valueAt("source"), YamlPathSegment.keyAt(propName));
return path.traverseAmbiguously(resource).findFirst().isPresent();
}
}
public static final YamlPath JOB_NAMES_PATH = new YamlPath(
anyChild(),
valueAt("jobs"),
anyChild(),
valueAt("name")
);
public static final YamlPath RESOURCE_TYPE_NAMES_PATH = new YamlPath(
anyChild(),
valueAt("resource_types"),
anyChild(),
valueAt("name")
);
public static final YamlPath RESOURCES_PATH = new YamlPath(
anyChild(), // skip over the root node which contains multiple doces
valueAt("resources"),
anyChild()
);
public static final YamlPath RESOURCE_NAMES_PATH = RESOURCES_PATH.append(
valueAt("name")
);
private final YamlParser parser;
private final StaleFallbackCache<String, YamlFileAST> asts = new StaleFallbackCache<>();
private final ASTTypeCache astTypes = new ASTTypeCache();
public ConcourseModel(SimpleTextDocumentService documents) {
Yaml yaml = new Yaml();
this.parser = new YamlParser(yaml);
documents.onDidChangeContent(this::documentChanged);
}
private void documentChanged(TextDocumentContentChange changeEvent) {
String uri = changeEvent.getDocument().getUri();
if (uri!=null) {
asts.invalidate(uri);
}
}
/**
* Returns the resource names that are defined by given IDocument. If the contents
* of IDocument is not currently parseable then this may return stale information
* retained from a previous successful parse.
* <p>
* It may also return null if its not currently possible to obtain the list of resource
* names (e.g. because there hasn't been a successful parse yet and current document contents
* can not be parsed).
*/
public Multiset<String> getResourceNames(IDocument doc) {
return getStringsFromAst(doc, RESOURCE_NAMES_PATH);
}
/**
* Get the resource type tag associated with a given resourceName in the given document.
* <p>
* If the content of IDocument is not currently parseable then this may return stale information
* retained from a previous successful parse.
* <p>
* It may also return null if its not currently possible to obtain type of the resource. E.g
* because there is no such resource, the resource has no valid type tag, or the document
* was never successfully parsed.
*/
public String getResourceType(IDocument doc, String resourceName) {
ResourceModel resource = getResource(doc, resourceName);
if (resource!=null) {
return resource.getType();
}
return null;
}
public ResourceModel getResource(IDocument doc, String resourceName) {
return getFromAst(doc, (ast) -> {
Node resource = RESOURCES_PATH.traverseAmbiguously(new ASTRootCursor(ast))
.map((cursor) -> ((NodeCursor)cursor).getNode())
.filter((resourceNode) -> resourceName.equals(NodeUtil.getScalarProperty(resourceNode, "name")))
.findFirst().orElse(null);
if (resource!=null) {
return new ResourceModel(resource);
}
return null;
});
}
/**
* Returns the job names that are defined by given IDocument. If the contents
* of IDocument is not currently parseable then this may return stale information
* retained from a previous successful parse.
* <p>
* It may also return null if its not currently possible to obtain the list of resource
* names (e.g. because there hasn't been a successful parse yet and current document contents
* can not be parsed).
*/
public Multiset<String> getJobNames(IDocument doc) {
return getStringsFromAst(doc, JOB_NAMES_PATH);
}
private Multiset<String> getStringsFromAst(IDocument doc, YamlPath path) {
return getFromAst(doc, (ast) -> {
return path
.traverseAmbiguously(ast)
.map(NodeUtil::asScalar)
.filter((string) -> string!=null)
.collect(CollectorUtil.toMultiset());
});
}
public Multiset<String> getResourceTypeNames(IDocument doc) {
Collection<YValueHint> hints = getResourceTypeNameHints(doc);
if (hints!=null) {
return ImmutableMultiset.copyOf(YTypeFactory.values(hints));
}
return null;
}
public Collection<YValueHint> getResourceTypeNameHints(IDocument doc) {
Multiset<String> userDefined = getStringsFromAst(doc, RESOURCE_TYPE_NAMES_PATH);
if (userDefined!=null) {
Builder<YValueHint> builder = ImmutableMultiset.builder();
builder.addAll(YTypeFactory.hints(userDefined));
builder.addAll(Arrays.asList(PipelineYmlSchema.BUILT_IN_RESOURCE_TYPES));
return builder.build();
}
return null;
}
private <T> T getFromAst(IDocument doc, Function<YamlFileAST, T> astFunction) {
try {
if (doc!=null) {
String uri = doc.getUri();
if (uri!=null) {
YamlFileAST ast = getAst(doc, true);
return astFunction.apply(ast);
}
}
} catch (YAMLException e) {
// ignore: found garbage in the doc. Can't compute stuff and that's to be expected.
} catch (Exception e) {
Log.log(e);
}
return null;
}
public YamlFileAST getSafeAst(IDocument doc) {
return getSafeAst(doc, true);
}
public YamlFileAST getAst(IDocument doc, boolean allowStaleAst) throws Exception {
return getAstProvider(allowStaleAst).getAST(doc);
}
public YamlASTProvider getAstProvider(boolean allowStaleAsts) {
return (IDocument doc) -> {
String uri = doc.getUri();
if (uri!=null) {
return asts.get(uri, allowStaleAsts, () -> {
return parser.getAST(doc);
});
}
return null;
};
}
public YamlFileAST getSafeAst(IDocument doc, boolean allowStaleAst) {
if (doc!=null) {
try {
return getAst(doc, allowStaleAst);
} catch (Exception e) {
//ignored
}
}
return null;
}
public ASTTypeCache getAstTypeCache() {
return astTypes;
}
public StepModel newStep(String stepType, MappingNode stepNode) {
return new StepModel(stepType, stepNode);
}
}

View File

@@ -0,0 +1,97 @@
/*******************************************************************************
* Copyright (c) 2016 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import java.util.function.Function;
import org.springframework.ide.vscode.commons.util.RegexpParser;
import org.springframework.ide.vscode.commons.util.ValueParser;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.yaml.schema.SchemaContextAware;
import com.google.common.collect.Multiset;
/**
* Methods and constants to create/get parsers for some atomic types
* used in manifest yml schema.
*
* @author Kris De Volder
*/
public class ConcourseValueParsers {
// public static final SchemaContextAware<ValueParser> resourceTypeName(ConcourseModel models) {
// return (dc) -> {
// return new EnumValueParser("ResourceType Name", models.getResourceTypeNames(dc.getDocument())) {
// @Override
// protected String createErrorMessage(String value, Collection<String> validValues) {
// return "The '"+value+"' Resource Type does not exist. Existing resource types: "+validValues;
// }
// };
// };
// };
public static final SchemaContextAware<ValueParser> resourceNameDef(ConcourseModel models) {
return acceptOnlyUniqueNames(models::getResourceNames, "resource name");
}
public static final SchemaContextAware<ValueParser> jobNameDef(ConcourseModel models) {
return acceptOnlyUniqueNames(models::getJobNames, "job name");
}
public static SchemaContextAware<ValueParser> resourceTypeNameDef(ConcourseModel models) {
return acceptOnlyUniqueNames(models::getResourceTypeNames, "resource-type name");
}
public static SchemaContextAware<ValueParser> acceptOnlyUniqueNames(
Function<IDocument, Multiset<String>> getDefinedNameCounts,
String typeName
) {
return (dc) -> {
Multiset<String> resourceNames = getDefinedNameCounts.apply(dc.getDocument());
return (String input) -> {
if (resourceNames.count(input)<=1) {
//okay
return resourceNames;
}
throw new IllegalArgumentException("Duplicate "+typeName+" '"+input+"'");
};
};
};
public static ValueParser DURATION = new RegexpParser(
"^(([0-9]+(.[0-9]+)?)(ns|us|µs|ms|s|h|m))+$",
"Duration",
" A duration string is a sequence of decimal numbers, each with "
+ "optional fraction and a unit suffix, such as '300ms', '1.5h' or"
+ " '2h45m'. Valid time units are 'ns', 'us' (or 'µs'), 'ms', 's', "
+ "'m', 'h'."
);
public static final ValueParser TIME_OF_DAY = new RegexpParser(
createTimeRegexp(),
"Time",
"Supported time formats are: 3:04 PM, 3PM, 3 PM, 15:04, and 1504. "
+ "Deprecation: an offset may be appended, e.g. +0700 or -0400, but "
+ "you should use location instead."
);
private static String createTimeRegexp() {
String hours = "([0-2]?[0-9])";
String minutes = "([0-6][0-9])";
String time = hours+"((:"+minutes+")|"+minutes+")?";
String pm = "(\\s?[AP]M)?";
String zone = "(\\s(\\+|\\-)[0-9][0-9][0-9][0-9])?";
return time + pm + zone;
}
}

View File

@@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2016-2017 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import java.io.IOException;
import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
public class Main {
SimpleLanguageServer server = new ConcourseLanguageServer();
public static void main(String[] args) throws IOException, InterruptedException {
LaunguageServerApp.start(ConcourseLanguageServer::new);
}
}

View File

@@ -0,0 +1,706 @@
/*******************************************************************************
* Copyright (c) 2016 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import java.time.ZoneId;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.ide.vscode.commons.languageserver.LanguageIds;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.util.MimeTypes;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
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.util.text.IDocument;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
import org.springframework.ide.vscode.commons.yaml.schema.BasicYValueHint;
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
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.YTypeFactory.YTypedPropertyImpl;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints;
import org.springframework.ide.vscode.concourse.ConcourseModel.ResourceModel;
import org.springframework.ide.vscode.concourse.ConcourseModel.StepModel;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import reactor.core.publisher.Flux;
/**
* @author Kris De Volder
*/
public class PipelineYmlSchema implements YamlSchema {
//TODO: the infos for composing this should probably be integrated somehow in the ResourceTypeRegistry so
// we only have a list of built-in resource types in a single place.
public static final YValueHint[] BUILT_IN_RESOURCE_TYPES = {
hint("git", "The 'git' resource can pull and push to git repositories."),
hint("hg", "The 'hg' resource can pull and push to Mercurial repositories."),
hint("time", "The 'time' resource can start jobs on a schedule or timestamp outputs."),
hint("s3", "The 's3' resource can fetch from and upload to S3 buckets."),
hint("archive", "The 'archive' resource can fetch and extract .tar.gz archives."),
hint("semver", "The 'semver' resource can set or bump version numbers."),
hint("github-release", "The 'github-release' resource can fetch and publish versioned GitHub resources."),
hint("docker-image", "The 'docker-image' resource can fetch, build, and push Docker images."),
hint("tracker", "The 'tracker' resource can deliver stories and bugs on Pivotal Tracker."),
hint("pool", "The 'pool' resource allows you to configure how to serialize use of an external system. "
+ "This lets you prevent test interference or overwork on shared systems."),
hint("cf", "The cf resource can deploy an application to Cloud Foundry."),
hint("bosh-io-release", "The bosh-io-release resource can track and fetch new BOSH releases from bosh.io."),
hint("bosh-io-stemcell", "The bosh-io-stemcell resource can track and fetch new BOSH stemcells from bosh.io."),
hint("bosh-deployment", "The bosh-deployment resource can deploy BOSH stemcells and releases."),
hint("vagrant-cloud", "The vagrant-cloud resource can fetch and publish Vagrant boxes to Atlas.")
};
public static final Set<String> BUILT_IN_RESOURCE_TYPE_NAMES = Flux.fromArray(PipelineYmlSchema.BUILT_IN_RESOURCE_TYPES)
.map(YValueHint::getValue)
.collect(Collectors.toSet())
.block();
private final AbstractType TOPLEVEL_TYPE;
private final YTypeUtil TYPE_UTIL;
public final YTypeFactory f = new YTypeFactory();
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_strings = f.yseq(t_string);
public final YType t_pair = f.ybean("NameValuePair",
f.yprop("name", t_string),
f.yprop("value", t_string)
);
public final YType t_pair_list = f.yseq(t_pair);
public final YAtomicType t_boolean = f.yenum("boolean", "true", "false");
public final YType t_any = f.yany("Object");
public final YType t_params = f.ymap(t_string, t_any);
public final YType t_string_params = f.ymap(t_string, t_string);
public final YType t_pos_integer = f.yatomic("Positive Integer")
.parseWith(ValueParsers.POS_INTEGER);
public final YType t_strictly_pos_integer = f.yatomic("Strictly Positive Integer")
.parseWith(ValueParsers.integerAtLeast(1));
public final YAtomicType t_resource_name;
public final YAtomicType t_job_name;
public final YAtomicType t_resource_type_name;
public final YType t_mime_type = f.yatomic("MimeType")
.parseWith(ValueParsers.NE_STRING)
.addHints(MimeTypes.getKnownMimeTypes());
public final YType t_duration = f.yatomic("Duration")
.parseWith(ConcourseValueParsers.DURATION);
public final YType t_time_of_day = f.yatomic("TimeOfDay")
.parseWith(ConcourseValueParsers.TIME_OF_DAY);
public final YType t_location = f.yatomic("Location")
//Note: we could have used f.yenum here too. But it saves memory if we don't keep the large set of ValueHints in memory.
// That's why we attach custom hint provider and parser here that do essentially the same thing.
.addHintProvider(() -> {
return ZoneId.getAvailableZoneIds().stream()
.map(BasicYValueHint::new)
.collect(Collectors.toList());
})
.parseWith(ValueParser.of((zoneId) -> {
if (!ZoneId.getAvailableZoneIds().contains(zoneId)) {
throw new ValueParseException("Unknown 'Location'. See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones");
}
return zoneId;
}));
public final AbstractType task;
private final ResourceTypeRegistry resourceTypes = new ResourceTypeRegistry();
private final ConcourseModel models;
public final YType t_semver = f.yatomic("Semver")
.parseWith(ValueParsers.NE_STRING); //TODO: use real semver parser.
public final YType t_s3_region = f.yenum("S3Region",
//See: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUT.html
"us-west-1", "us-west-2",
"ca-central-1", "EU", "eu-west-1",
"eu-west-2", "eu-central-1",
"ap-south-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", "ap-northeast-2",
"sa-east-1",
"us-east-2"
);
public final YType t_day = f.yenum("Day",
//See https://github.com/concourse/time-resource#source-configuration
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
);
public PipelineYmlSchema(ConcourseModel models) {
this.models = models;
TYPE_UTIL = f.TYPE_UTIL;
// define schema types
TOPLEVEL_TYPE = f.ybean("Pipeline");
YAtomicType t_version = f.yatomic("Version");
t_version.addHints("latest", "every");
YAtomicType t_image_type = f.yatomic("ImageType");
t_image_type.addHints("docker_image");
t_resource_type_name = f.yenumFromHints("ResourceType Name",
(parseString, validValues) -> {
return "The '"+parseString+"' Resource Type does not exist. Existing types: "+validValues;
},
(DynamicSchemaContext dc) -> {
return models.getResourceTypeNameHints(dc.getDocument());
}
);
t_resource_name = f.yenum("Resource Name",
(parseString, validValues) -> {
return "The '"+parseString+"' resource does not exist. Existing resources: "+validValues;
},
(DynamicSchemaContext dc) -> {
return (models.getResourceNames(dc.getDocument()));
}
);
t_job_name = f.yenum("Job Name",
(parseString, validValues) -> {
return "The '"+parseString+"' Job does not exist. Existing jobs: "+validValues;
},
(DynamicSchemaContext dc) -> {
return models.getJobNames(dc.getDocument());
}
);
YAtomicType resourceNameDef = f.yatomic("Resource Name");
resourceNameDef.parseWith(ConcourseValueParsers.resourceNameDef(models));
YAtomicType jobNameDef = f.yatomic("Job Name");
jobNameDef.parseWith(ConcourseValueParsers.jobNameDef(models));
YAtomicType resourceTypeNameDef = f.yatomic("ResourceType Name");
resourceTypeNameDef.parseWith(ConcourseValueParsers.resourceTypeNameDef(models));
YType resourceSource = f.contextAware("ResourceSource", (dc) ->
resourceTypes.getSourceType(getResourceTypeTag(models, dc))
);
AbstractType t_resource = f.ybean("Resource");
addProp(t_resource, "name", resourceNameDef).isRequired(true);
addProp(t_resource, "type", t_resource_type_name).isRequired(true);
addProp(t_resource, "source", resourceSource);
addProp(t_resource, "check_every", t_duration);
AbstractType t_image_resource = f.ybean("ImageResource");
for (YTypedProperty p : t_resource.getProperties()) {
if (!"name".equals(p.getName())) {
t_image_resource.addProperty(p);
}
}
YAtomicType t_platform = f.yenum("Platform", "windows", "linux", "darwin");
t_platform.parseWith(ValueParsers.NE_STRING); //no errors because in theory platform are just strings.
AbstractType t_input = f.ybean("TaskInput");
addProp(t_input, "name", t_ne_string).isRequired(true);
addProp(t_input, "path", t_ne_string);
AbstractType t_output = f.ybean("TaskOutput");
addProp(t_output, "name", t_ne_string).isRequired(true);
addProp(t_output, "path", t_ne_string);
AbstractType t_command = f.ybean("Command");
addProp(t_command, "path", t_ne_string).isRequired(true);
addProp(t_command, "args", t_strings);
addProp(t_command, "dir", t_ne_string);
addProp(t_command, "user", t_string);
task = f.ybean("TaskConfig");
addProp(task, "platform", t_platform).isRequired(true);
addProp(task, "image_resource", t_image_resource);
addProp(task, "image", t_ne_string);
addProp(task, "inputs", f.yseq(t_input));
addProp(task, "outputs", f.yseq(t_output));
addProp(task, "run", t_command).isRequired(true);
addProp(task, "params", t_string_params);
task.require((dc) -> {
String languageId = dc.getDocument().getLanguageId();
if (LanguageIds.CONCOURSE_PIPELINE.equals(languageId)) {
Node parentImageDef = getParentPropertyNode("image", models, dc);
if (parentImageDef==null) {
return Constraints.requireOneOf("image_resource", "image");
} else {
return Constraints.deprecated((name) ->
"Deprecated: This attribute in the task config will be ignored! "+
"The 'image' attribute on the task itself takes precedence.",
"image_resource", "image"
);
}
} else {
return Constraints.requireAtMostOneOf("image_resource", "image");
}
});
AbstractType t_put_get_name = f.contextAware("Name", (dc) -> {
if (getParentPropertyNode("resource", models, dc)!=null) {
return null;
} else {
return t_resource_name;
}
})
.treatAsAtomic()
.parseWith(ValueParsers.NE_STRING);
YBeanType getStep = f.ybean("GetStep");
addProp(getStep, "get", t_put_get_name);
addProp(getStep, "resource", t_resource_name);
addProp(getStep, "version", t_version);
addProp(getStep, "passed", f.yseq(t_job_name));
addProp(getStep, "params", f.contextAware("GetParams", (dc) ->
resourceTypes.getInParamsType(getResourceType("get", models, dc))
));
addProp(getStep, "trigger", t_boolean);
YBeanType putStep = f.ybean("PutStep");
addProp(putStep, "put", t_put_get_name);
addProp(putStep, "resource", t_resource_name);
addProp(putStep, "params", f.contextAware("PutParams", (dc) ->
resourceTypes.getOutParamsType(getResourceType("put", models, dc))
));
addProp(putStep, "get_params", f.contextAware("GetParams", (dc) ->
resourceTypes.getInParamsType(getResourceType("put", models, dc))
));
putStep.require((dc) -> (IDocument doc, Node parent, MappingNode map, YType type, Set<String> foundProps, IProblemCollector problems) -> {
StepModel step = models.newStep("put", map);
String resourceName = step.getResourceName();
if (resourceName!=null) {
ResourceModel resource = models.getResource(doc, resourceName);
if (resource!=null) {
if ("git".equals(resource.getType()) && !resource.hasSourceProperty("branch")) {
problems.accept(YamlSchemaProblems.schemaProblem(
"Resource of type 'git' is used in a 'put' step, so it should define 'branch' attribute in its 'source', but it doesn't.",
step.getResourceNameNode()
));
}
}
}
});
YBeanType taskStep = f.ybean("TaskStep");
addProp(taskStep, "task", t_ne_string);
addProp(taskStep, "file", t_string);
addProp(taskStep, "config", task);
addProp(taskStep, "privileged", t_boolean);
addProp(taskStep, "params", t_params);
addProp(taskStep, "image", t_resource_name);
addProp(taskStep, "input_mapping", f.ymap(t_ne_string, t_resource_name));
addProp(taskStep, "output_mapping", t_string_params);
taskStep.requireOneOf("config", "file");
YBeanType aggregateStep = f.ybean("AggregateStep");
YBeanType doStep = f.ybean("DoStep");
YBeanType tryStep = f.ybean("TryStep");
YBeanType[] stepTypes = {
getStep,
putStep,
taskStep,
aggregateStep,
doStep,
tryStep
};
YBeanUnionType step = f.yunion("Step", stepTypes);
addProp(aggregateStep, "aggregate", f.yseq(step));
addProp(doStep, "do", f.yseq(step));
addProp(tryStep, "try", step);
// shared properties applicable for any subtype of Step:
for (AbstractType subStep : stepTypes) {
addProp(step, subStep, "on_success", step);
addProp(step, subStep, "on_failure", step);
addProp(step, subStep, "ensure", step);
addProp(step, subStep, "attempts", t_strictly_pos_integer);
addProp(step, subStep, "tags", t_strings);
addProp(step, subStep, "timeout", t_duration);
}
AbstractType job = f.ybean("Job");
addProp(job, "name", jobNameDef).isRequired(true);
addProp(job, "plan", f.yseq(step)).isRequired(true);
addProp(job, "serial", t_boolean);
addProp(job, "build_logs_to_retain", t_pos_integer);
addProp(job, "serial_groups", t_strings);
addProp(job, "max_in_flight", t_pos_integer);
addProp(job, "public", t_boolean);
addProp(job, "disable_manual_trigger", t_boolean);
AbstractType resourceType = f.ybean("ResourceType");
addProp(resourceType, "name", resourceTypeNameDef).isRequired(true);
addProp(resourceType, "type", t_image_type).isRequired(true);
addProp(resourceType, "source", resourceSource);
AbstractType group = f.ybean("Group");
addProp(group, "name", t_ne_string).isRequired(true);
addProp(group, "resources", f.yseq(t_resource_name));
addProp(group, "jobs", f.yseq(t_job_name));
addProp(TOPLEVEL_TYPE, "resources", f.yseq(t_resource));
addProp(TOPLEVEL_TYPE, "jobs", f.yseq(job));
addProp(TOPLEVEL_TYPE, "resource_types", f.yseq(resourceType));
addProp(TOPLEVEL_TYPE, "groups", f.yseq(group));
initializeDefaultResourceTypes();
}
private static YValueHint hint(String value, String description) {
return YTypeFactory.hint(value, value + " - " + description);
}
private void initializeDefaultResourceTypes() {
// git :
{
AbstractType source = f.ybean("GitSource");
addProp(source, "uri", t_string).isRequired(true);
addProp(source, "branch", t_ne_string); //It's more complicated than that! Its only required in 'put' step. So we'll check this as a contrain in put steps!
addProp(source, "private_key", t_ne_string);
addProp(source, "username", t_ne_string);
addProp(source, "password", t_string);
addProp(source, "paths", t_strings);
addProp(source, "ignore_paths", t_strings);
addProp(source, "skip_ssl_verification", t_boolean);
addProp(source, "tag_filter", t_string);
addProp(source, "git_config", t_pair_list);
addProp(source, "disable_ci_skip", t_boolean);
addProp(source, "commit_verification_keys", t_strings);
addProp(source, "commit_verification_key_ids", t_strings);
addProp(source, "gpg_keyserver", t_string);
AbstractType get = f.ybean("GitGetParams");
addProp(get, "depth", t_pos_integer);
addProp(get, "submodules", f.yany("GitSubmodules").addHints("all", "none"));
addProp(get, "disable_git_lfs", t_boolean);
AbstractType put = f.ybean("GitPutParams");
addProp(put, "repository", t_ne_string).isRequired(true);
addProp(put, "rebase", t_boolean);
addProp(put, "tag", t_ne_string);
addProp(put, "only_tag", t_boolean);
addProp(put, "tag_prefix", t_string);
addProp(put, "force", t_boolean);
addProp(put, "annotate", t_ne_string);
resourceTypes.def("git", source, get, put);
}
//docker-image:
{
AbstractType source = f.ybean("DockerImageSource");
addProp(source, "repository", t_ne_string).isRequired(true);
addProp(source, "tag", t_ne_string);
addProp(source, "username", t_ne_string);
addProp(source, "password", t_ne_string);
addProp(source, "aws_access_key_id", t_ne_string);
addProp(source, "aws_secret_access_key", t_ne_string);
addProp(source, "insecure_registries", t_strings);
addProp(source, "registry_mirror", t_ne_string);
addProp(source, "ca_certs", f.yseq(f.ybean("CaCertsEntry",
f.yprop("domain", t_ne_string),
f.yprop("cert", t_ne_string)
)));
addProp(source, "client_certs", f.yseq(f.ybean("ClientCertsEntry",
f.yprop("domain", t_ne_string),
f.yprop("key", t_ne_string),
f.yprop("cert", t_ne_string)
)));
AbstractType get = f.ybean("DockerImageGetParams");
addProp(get, "save", t_boolean);
addProp(get, "rootfs", t_boolean);
addProp(get, "skip_download", t_boolean);
AbstractType put = f.ybean("DockerImagePutParams");
addProp(put, "build", t_ne_string);
addProp(put, "load", t_ne_string);
addProp(put, "dockerfile", t_ne_string);
addProp(put, "cache", t_boolean);
addProp(put, "cache_tag", t_ne_string);
addProp(put, "load_base", t_ne_string);
addProp(put, "load_file", t_ne_string);
addProp(put, "load_repository", t_ne_string);
addProp(put, "load_tag", t_ne_string);
addProp(put, "import_file", t_ne_string);
addProp(put, "pull_repository", t_ne_string).isDeprecated(true);
addProp(put, "pull_tag", t_ne_string).isDeprecated(true);
addProp(put, "tag", t_ne_string);
addProp(put, "tag_prefix", t_ne_string);
addProp(put, "tag_as_latest", t_boolean);
addProp(put, "build_args", t_string_params);
addProp(put, "build_args_file", t_ne_string);
resourceTypes.def("docker-image", source, get, put);
}
//s3
{
YType t_canned_acl = f.yenum("S3CannedAcl",
//See http://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl
"private", "public-read", "public-read-write", "aws-exec-read",
"authenticated-read", "bucket-owner-read", "bucket-owner-full-control",
"log-delivery-write"
);
AbstractType source = f.ybean("S3Source");
addProp(source, "bucket", t_ne_string).isRequired(true);
addProp(source, "access_key_id", t_ne_string);
addProp(source, "secret_access_key", t_ne_string);
addProp(source, "region_name", t_s3_region);
addProp(source, "private", t_boolean);
addProp(source, "cloudfront_url", t_ne_string);
addProp(source, "endpoint", t_ne_string);
addProp(source, "disable_ssl", t_boolean);
addProp(source, "server_side_encryption", t_ne_string);
addProp(source, "sse_kms_key_id", t_ne_string);
addProp(source, "use_v2_signing", t_boolean);
addProp(source, "regexp", t_ne_string);
addProp(source, "versioned_file", t_ne_string);
source.requireOneOf("regexp", "versioned_file");
AbstractType get = f.ybean("S3GetParams");
//Note: S3GetParams intentionally has no properties since no params are expected according to the docs.
AbstractType put = f.ybean("S3PutParams");
addProp(put, "file", t_ne_string).isRequired(true);
addProp(put, "acl", t_canned_acl);
addProp(put, "content_type", t_mime_type);
resourceTypes.def("s3", source, get, put);
}
//pool
{
AbstractType source = f.ybean("PoolSource");
addProp(source, "uri", t_ne_string).isRequired(true);
addProp(source, "branch", t_ne_string).isRequired(true);
addProp(source, "pool", t_ne_string).isRequired(true);
addProp(source, "private_key", t_ne_string);
addProp(source, "username", t_ne_string);
addProp(source, "password", t_string);
addProp(source, "retry_delay", t_duration);
AbstractType get = f.ybean("PoolGetParams");
//get params deliberately left empty
AbstractType put = f.ybean("PoolPutParams");
addProp(put, "acquire", t_boolean);
addProp(put, "claim", t_ne_string);
addProp(put, "release", t_ne_string);
addProp(put, "add", t_ne_string);
addProp(put, "add_claimed", t_ne_string);
addProp(put, "remove", t_ne_string);
put.requireOneOf(put.getPropertyNames());
resourceTypes.def("pool", source, get, put);
}
//semver
{
AbstractType git_source = f.ybean("GitSemverSource");
addProp(git_source, "uri", t_ne_string).isRequired(true);
addProp(git_source, "branch", t_ne_string).isRequired(true);
addProp(git_source, "file", t_ne_string).isRequired(true);
addProp(git_source, "private_key", t_ne_string);
addProp(git_source, "username", t_ne_string);
addProp(git_source, "password", t_ne_string);
addProp(git_source, "git_user", t_ne_string);
AbstractType s3_source = f.ybean("S3SemverSource");
addProp(s3_source, "bucket", t_ne_string).isRequired(true);
addProp(s3_source, "key", t_ne_string).isRequired(true);
addProp(s3_source, "access_key_id", t_ne_string).isRequired(true);
addProp(s3_source, "secret_access_key", t_ne_string).isRequired(true);
addProp(s3_source, "region_name", t_s3_region);
addProp(s3_source, "endpoint", t_ne_string);
addProp(s3_source, "disable_ssl", t_boolean);
AbstractType swift_source = f.ybean("SwiftSemverSource");
addProp(swift_source, "openstack", t_any).isRequired(true);
AbstractType[] driverSpecificSources = {
git_source, s3_source, swift_source
};
AbstractType source = f.contextAware("SemverSource", (dc) -> {
switch (getSemverDriverName(dc)) {
case "git":
return git_source;
case "s3":
return s3_source;
case "swift":
return swift_source;
default:
return null;
}
}).treatAsBean();
addProp(source, "initial_version", t_semver);
addProp(source, "driver", f.yenum("SemverDriver", "git", "s3", "swift"));
for (AbstractType s : driverSpecificSources) {
for (YTypedProperty p : source.getProperties()) {
s.addProperty(p);
}
}
AbstractType get = f.ybean("SemverGetParams");
addProp(get, "bump", f.yenum("SemverBump", "major", "minor", "patch", "final"));
addProp(get, "pre", t_ne_string);
AbstractType put = f.ybean("SemverPutParams");
for (YTypedProperty p : get.getProperties()) {
put.addProperty(p);
}
addProp(put, "file", t_ne_string);
resourceTypes.def("semver", source, get, put);
}
//time:
{
AbstractType source = f.ybean("TimeSource");
addProp(source, "interval", t_duration);
addProp(source, "location", t_location);
addProp(source, "start", t_time_of_day);
addProp(source, "stop", t_time_of_day);
addProp(source, "days", f.yseq(t_day));
AbstractType get = f.ybean("TimeGetParams");
//get params deliberately left empty
AbstractType put = f.ybean("TimePutParams");
//put params deliberately left empty
resourceTypes.def("time", source, get, put);
}
}
private String getSemverDriverName(DynamicSchemaContext dc) {
String driver = getSiblingPropertyValue(dc, "driver");
return driver!=null ? driver : "s3";
}
private Node getResourceNameNode(String resourceNameProp, DynamicSchemaContext dc) {
Node resourceName = getParentPropertyNode("resource", models, dc);
if (resourceName==null) {
resourceName = getParentPropertyNode(resourceNameProp, models, dc);
}
return resourceName;
}
private String getResourceName(String resourceNameProp, DynamicSchemaContext dc) {
Node resourceName = getResourceNameNode(resourceNameProp, dc);
return NodeUtil.asScalar(resourceName);
}
private String getResourceType(String resourceNameProp, ConcourseModel models, DynamicSchemaContext dc) {
String resourceName = getResourceName(resourceNameProp, dc);
if (resourceName!=null) {
return models.getResourceType(dc.getDocument(), resourceName);
}
return null;
}
private String getResourceTypeTag(ConcourseModel models, DynamicSchemaContext dc) {
return getParentPropertyValue("type", models, dc);
}
private String getParentPropertyValue(String propName, ConcourseModel models, DynamicSchemaContext dc) {
return NodeUtil.asScalar(getParentPropertyNode(propName, models, dc));
}
private String getSiblingPropertyValue(DynamicSchemaContext dc, String propName) {
YamlPath path = dc.getPath();
if (path!=null) {
YamlFileAST root = models.getSafeAst(dc.getDocument());
if (root!=null) {
return NodeUtil.asScalar(path.append(YamlPathSegment.valueAt(propName)).traverseToNode(root));
}
}
return null;
}
private Node getParentPropertyNode(String propName, ConcourseModel models, DynamicSchemaContext dc) {
YamlPath path = dc.getPath();
if (path!=null) {
YamlFileAST root = models.getSafeAst(dc.getDocument());
if (root!=null) {
return path.dropLast().append(YamlPathSegment.valueAt(propName)).traverseToNode(root);
}
}
return null;
}
private YTypedPropertyImpl prop(AbstractType beanType, String name, YType type) {
YTypedPropertyImpl prop = f.yprop(name, type);
prop.setDescriptionProvider(descriptionFor(beanType, name));
return prop;
}
private YTypedPropertyImpl addProp(AbstractType superType, AbstractType bean, String name, YType type) {
YTypedPropertyImpl p = prop(superType, name, type);
bean.addProperty(p);
return p;
}
private YTypedPropertyImpl addProp(AbstractType bean, String name, YType type) {
return addProp(bean, bean, name, type);
}
public static Renderable descriptionFor(YType owner, String propName) {
String typeName = owner.toString();
return Renderables.fromClasspath(PipelineYmlSchema.class, "/desc/"+typeName+"/"+propName);
}
@Override
public AbstractType getTopLevelType() {
return TOPLEVEL_TYPE;
}
@Override
public YTypeUtil getTypeUtil() {
return TYPE_UTIL;
}
public YamlSchema getTaskSchema() {
return new YamlSchema() {
@Override
public YTypeUtil getTypeUtil() {
return TYPE_UTIL;
}
@Override
public YType getTopLevelType() {
return task;
}
@Override
public String toString() {
return "TaskYamlSchema";
}
};
}
}

View File

@@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright (c) 2017 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import java.util.HashMap;
import java.util.Map;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType;
/**
* Keeps track of known resource types.
*
* @author Kris De Volder
*/
public class ResourceTypeRegistry {
private static class ResourceTypeInfo {
private final AbstractType source;
private final AbstractType in;
private final AbstractType out;
public ResourceTypeInfo(AbstractType source, AbstractType in, AbstractType out) {
super();
this.source = source;
this.in = in;
this.out = out;
}
public AbstractType getSource() {
return source;
}
public AbstractType getIn() {
return in;
}
public AbstractType getOut() {
return out;
}
}
private Map<String, ResourceTypeInfo> resourceTypes = new HashMap<>();
public ResourceTypeRegistry() {
}
public void def(String resourceTypeName, AbstractType source, AbstractType in, AbstractType out) {
Assert.isLegal(!resourceTypes.containsKey(resourceTypeName), "Multiple definitions for '"+resourceTypeName+"'");
resourceTypes.put(resourceTypeName, new ResourceTypeInfo(source, in, out));
}
public YType getSourceType(String typeTag) {
if (typeTag!=null) {
ResourceTypeInfo v = resourceTypes.get(typeTag);
if (v!=null) {
return v.getSource();
}
}
return null;
}
public YType getInParamsType(String typeTag) {
if (typeTag!=null) {
ResourceTypeInfo v = resourceTypes.get(typeTag);
if (v!=null) {
return v.getIn();
}
}
return null;
}
public YType getOutParamsType(String typeTag) {
if (typeTag!=null) {
ResourceTypeInfo v = resourceTypes.get(typeTag);
if (v!=null) {
return v.getOut();
}
}
return null;
}
}

View File

@@ -0,0 +1,67 @@
/*******************************************************************************
* Copyright (c) 2016 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.concourse.util;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
/**
* Stuff missing from {@link Collectors} that we implement ourself.
*/
public class CollectorUtil {
/**
* Collects elements into a ImmutableMutiset (the set is converted to an immutable one
* at the end. Accumulating / combining is done with a mutable Multiset because that involves
* less copying.)
*/
public static <T> Collector<T, HashMultiset<T>, ImmutableMultiset<T>> toMultiset() {
return new Collector<T, HashMultiset<T>, ImmutableMultiset<T>>() {
@Override
public Supplier<HashMultiset<T>> supplier() {
return HashMultiset::create;
}
@Override
public BiConsumer<HashMultiset<T>, T> accumulator() {
return (a, e) -> a.add(e);
}
@Override
public BinaryOperator<HashMultiset<T>> combiner() {
return (a1, a2) -> {
a1.addAll(a2);
return a1;
};
}
@Override
public Function<HashMultiset<T>, ImmutableMultiset<T>> finisher() {
return ImmutableMultiset::copyOf;
}
@Override
public Set<Collector.Characteristics> characteristics() {
return ImmutableSet.of(Collector.Characteristics.UNORDERED);
}
};
}
}

View File

@@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright (c) 2016 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.concourse.util;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
/**
* Simple cache implementation that falls back on 'stale' cache entry if
* a new entry can not be computed. The api is loosely modeled after
* guava's Cache interface (but only the subset we use is implemented to reduce the
* complexity of its implementation).
*/
public class StaleFallbackCache<K, V>{
Map<K, V> staleEntries = new HashMap<>();
Cache<K, CompletableFuture<V>> validEntries = CacheBuilder.newBuilder().build();
public synchronized V get(K key, boolean allowStaleEntries, Callable<? extends V> valueLoader) throws Exception {
CompletableFuture<V> valid = validEntries.get(key, () -> load(valueLoader));
if (!allowStaleEntries) {
return future_get(valid);
} else {
if (valid.isCompletedExceptionally()) {
V staleValue = staleEntries.get(key);
if (staleValue!=null) {
return staleValue;
}
}
return future_get(valid);
}
}
public synchronized void invalidate(K key) {
CompletableFuture<V> staleEntry = validEntries.getIfPresent(key);
if (staleEntry!=null) {
validEntries.invalidate(key);
try {
staleEntries.put(key, future_get(staleEntry));
} catch (Exception e) {
//ignore. Don't overwrite stale entry if current entry represents an error.
// We only keep 'good quality' stale entries not failed attempts to compute a value.
// as it is kind of the point to fall back on a 'good' old entry when the current
// entry is unavailable because of a problem (e.g. problems parsing the AST).
}
}
}
private V future_get(CompletableFuture<V> f) throws Exception {
try {
return f.get();
} catch (InterruptedException e) {
throw e;
} catch (ExecutionException e) {
throw ExceptionUtil.exception(e.getCause());
}
}
private CompletableFuture<V> load(Callable<? extends V> valueLoader) {
CompletableFuture<V> future = new CompletableFuture<V>();
try {
V value = valueLoader.call();
Assert.isNotNull(value);
future.complete(value);
} catch (Throwable e) {
future.completeExceptionally(e);
}
return future;
}
}

View File

@@ -0,0 +1,19 @@
<p>Performs the given steps in parallel.</p><p>If any sub-steps in an aggregate result in an error, the aggregate step as a
whole is considered to have errored.</p><p>Similarly, when aggregating <a href="task-step.html"><code>task</code></a> steps, if any
<em>fail</em>, the aggregate step will fail. This is useful for build matrixes:</p><div class="highlight"><pre class="verbatim"><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">plan</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">some-repo</span><span class="t">
</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">aggregate</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">task</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">unit-windows</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">file</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">some-repo/ci/windows.yml</span><span class="t">
</span><span class="t"> </span><span class="nv">-</span><span class="sp"> </span><span class="py">task</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">unit-linux</span><span class="t">
</span><span class="t"> </span><span class="py">file</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">some-repo/ci/linux.yml</span><span class="t">
</span><span class="t"> </span><span class="nv">-</span><span class="sp"> </span><span class="py">task</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">unit-darwin</span><span class="t">
</span><span class="t"> </span><span class="py">file</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">some-repo/ci/darwin.yml</span></pre></div><p>The <code>aggregate</code> 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:</p><div class="highlight"><pre class="verbatim"><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">plan</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">aggregate</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">component-a</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">component-b</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">integration-suite</span><span class="t">
</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">task</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">integration</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">file</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">integration-suite/task.yml</span></pre></div>

View File

@@ -0,0 +1 @@
*Optional.* Arguments to pass to the command. Note that when executed with `fly`, any arguments passed to `fly` are appended to this array.

View File

@@ -0,0 +1 @@
*Optional.* A directory, relative to the initial working directory, to set as the working directory when running the script.

View File

@@ -0,0 +1 @@
*Required.* The command to execute, relative to the task's working directory. For a script living in a resource's repo, you must specify the full path to the resource, i.e. `my-resource/scripts/test`.

View File

@@ -0,0 +1 @@
*Optional.* Explicitly set the user to run as. If not specified, this defaults to the user configured by the task's image. If not specified there, it's up to the Garden backend, and may be e.g. `root` on Linux.

View File

@@ -0,0 +1,14 @@
Run steps in series.
do: [step]
Simply performs the given steps serially, with the same semantics as if they were at the top level step listing.
This can be used to perform multiple steps serially in the branch of an `aggregate` step:
plan:
- aggregate:
- task: unit
- do:
- get: something-else
- task: something-else-unit

View File

@@ -0,0 +1 @@
*Optional.* Place a `.tar` file of the image in the destination.

View File

@@ -0,0 +1 @@
*Optional.* Place a `docker save`d image in the destination.

View File

@@ -0,0 +1,2 @@
*Optional.* Skip `docker pull` of image. Artifacts based
on the image will not be present.

View File

@@ -0,0 +1 @@
*Optional.* The path of a directory containing a `Dockerfile` to build.

View File

@@ -0,0 +1,10 @@
*Optional.* A map of Docker build arguments.
Example:
```yaml
build_args:
do_thing: true
how_many_things: 2
email: me@yopmail.com
```

View File

@@ -0,0 +1,8 @@
Optional.* Path to a JSON file containing Docker build
arguments.
Example file contents:
```yaml
{ "email": "me@yopmail.com", "how_many_things": 1, "do_thing": false }
```

View File

@@ -0,0 +1,10 @@
*Optional.* Default `false`. When the `build` parameter is set,
first pull `image:tag` from the Docker registry (so as to use cached
intermediate images when building). This will cause the resource to fail
if it is set to `true` and the image does not exist yet.
Note: Since docker 1.10 docker images [do not contain all necessary metadata to
restore the build cache](https://github.com/docker/docker/issues/20316).
Additional metadata needs to be saved and re-applied after a docker pull to have
subsequent builds skip identical intermediate layers. This additional
metadata is stored as a very small separate image (`image:${cache_tag}-buildcache`)
in the repository of this resource.

View File

@@ -0,0 +1,5 @@
*Optional.* Default `tag`. The specific tag to pull before
building when `cache` parameter is set. Instead of pulling the same tag
that's going to be built, this allows picking a different tag like
`latest` or the previous version. This will cause the resource to fail
if it is set to a tag that does not exist yet.

View File

@@ -0,0 +1,2 @@
*Optional.* The path of the `Dockerfile` in the directory if
it's not at the root of the directory.

View File

@@ -0,0 +1 @@
*Optional.* A path to a file to `docker import` and then push.

View File

@@ -0,0 +1,2 @@
*Optional.* The path of a directory containing an image that was
fetched using this same resource with `save: true`.

View File

@@ -0,0 +1,3 @@
*Optional.* A path to a directory containing an image to `docker load`
before running `docker build`. The directory must have `image`,
`image-id`, `repository`, and `tag` present, i.e. the tree produced by `/in`.

View File

@@ -0,0 +1,2 @@
*Optional.* A path to a file to `docker load` and then push.
Requires `load_repository`.

View File

@@ -0,0 +1 @@
*Optional.* The repository of the image loaded from `load_file`.

View File

@@ -0,0 +1 @@
*Optional.* Default `latest`. The tag of image loaded from `load_file`.

View File

@@ -0,0 +1,2 @@
*Optional.* **DEPRECATED. Use `get` and `load` instead.** A
path to a repository to pull down, and then push to this resource.

View File

@@ -0,0 +1,2 @@
*Optional.* **DEPRECATED. Use `get` and `load` instead.** Default
`latest`. The tag of the repository to pull down via `pull_repository`.

View File

@@ -0,0 +1,2 @@
*Optional.* The value should be a path to a file containing the name
of the tag.

View File

@@ -0,0 +1,2 @@
*Optional.* Default `false`. If true, the pushed image will
be tagged as `latest` in addition to whatever other tag was specified.

View File

@@ -0,0 +1,3 @@
*Optional.* If specified, the tag read from the file will be
prepended with this string. This is useful for adding `v` in front of version
numbers.

View File

@@ -0,0 +1,2 @@
*Optional.* AWS access key to use for acquiring ECR
credentials.

View File

@@ -0,0 +1,2 @@
*Optional.* AWS secret key to use for acquiring ECR
credentials.

View File

@@ -0,0 +1,25 @@
*Optional.* An array of objects with the following format:
```yaml
ca_certs:
- domain: example.com:443
cert: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
- domain: 10.244.6.2:443
cert: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
```
Each entry specifies the x509 CA certificate for the trusted docker registry
residing at the specified domain. This is used to validate the certificate of
the docker registry when the registry's certificate is signed by a custom
authority (or itself).
The domain should match the first component of `repository`, including the
port. If the registry specified in `repository` does not use a custom cert,
adding `ca_certs` will break the check script. This option is overridden by
entries in `insecure_registries` with the same address or a matching CIDR.

View File

@@ -0,0 +1,27 @@
*Optional.* An array of objects with the following format:
```yaml
client_certs:
- domain: example.com:443
cert: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
key: |
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
- domain: 10.244.6.2:443
cert: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
key: |
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
```
Each entry specifies the x509 certificate and key to use for authenticating
against the docker registry residing at the specified domain. The domain
should match the first component of `repository`, including the port.

View File

@@ -0,0 +1,3 @@
*Optional.* An array of CIDRs or `host:port` addresses
to whitelist for insecure access (either `http` or unverified `https`).
This option overrides any entries in `ca_certs` with the same address.

View File

@@ -0,0 +1 @@
*Optional.* The password to use when authenticating.

View File

@@ -0,0 +1 @@
*Optional.* A URL pointing to a docker registry mirror service.

View File

@@ -0,0 +1,2 @@
*Required.* The name of the repository, e.g.
`concourse/docker-image-resource`.

View File

@@ -0,0 +1 @@
*Optional.* The tag to track. Defaults to `latest`.

View File

@@ -0,0 +1 @@
*Optional.* The username to authenticate with when pushing.

View File

@@ -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).

View File

@@ -0,0 +1,3 @@
<p><em>Optional.</em> A map of arbitrary configuration to forward to the
resource. Refer to the resource type's documentation to see what it
supports.</p>

View File

@@ -0,0 +1,15 @@
<p><em>Optional.</em> When specified, only the versions of the resource that
made it through the given list of jobs will be considered when triggering
and fetching.</p><p>Note that if multiple <code>get</code>s are configured with <code>passed</code>
constraints, all of the mentioned jobs are correlated. That is, with the
following set of inputs:</p><div class="highlight"><pre class="verbatim"><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">plan</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">a</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">passed</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="pi">[</span><span class="nv"></span><span class="nv">a-unit</span><span class="t"></span><span class="pi">,</span><span class="t"> </span><span class="nv"></span><span class="nv">integration</span><span class="t"></span><span class="pi">]</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">b</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">passed</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="pi">[</span><span class="nv"></span><span class="nv">b-unit</span><span class="t"></span><span class="pi">,</span><span class="t"> </span><span class="nv"></span><span class="nv">integration</span><span class="t"></span><span class="pi">]</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">x</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">passed</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="pi">[</span><span class="nv"></span><span class="nv">integration</span><span class="t"></span><span class="pi">]</span></pre></div><p>This means "give me the versions of <code>a</code>, <code>b</code>, and <code>x</code> that
have passed the <em>same build</em> of <code>integration</code>, with the same
version of <code>a</code> passing <code>a-unit</code> and the same version of
<code>b</code> passing <code>b-unit</code>."</p><p>This is crucial to being able to implement safe "fan-in" semantics as
things progress through a pipeline.</p>

View File

@@ -0,0 +1,2 @@
<p><em>Optional. Defaults to <code>name</code>.</em> The resource to fetch, as
configured in <a href="configuring-resources.html"><code>resources</code></a>.</p>

View File

@@ -0,0 +1,4 @@
<p><em>Optional. Default <code>false</code>.</em> Set to <code>true</code> to auto-trigger
new builds of the plan's job whenever this step has new versions available,
as specified by the <code>resource</code> and any <code>passed</code> constraints.</p><p>Otherwise, if no <code>get</code> steps set this to <code>true</code>, the job can only
be manually triggered.</p>

View File

@@ -0,0 +1,11 @@
<p><em>Optional. Defaults to <code>latest</code>.</em> The version of the resource to
fetch.</p><p>If set to <code>latest</code>, scheduling will just find the latest available
version of a resource and use it, allowing versions to be skipped. This is
usually what you want, e.g. if someone pushes 100 git commits.</p><p>If set to <code>every</code>, builds will walk through all available versions of
the resource. Note that if <code>passed</code> is also configured, it will only
step through the versions satisfying the constraints.</p><p>If set to a specific version (e.g. <code>{ref: abcdef123}</code>), only that
version will be used. Note that the version must be available and detected by
the resource, otherwise the input will never be satisfied. You may want to
use <a href="fly-check-resource.html"><code>check-resource</code></a> to force detection of resource versions,
if you need to use an older one that was never detected (as all newly
configured resources start from the latest version).</p>

View File

@@ -0,0 +1,3 @@
*Optional.* If a positive integer is given, *shallow* clone the
repository using the `--depth` option. Using this flag voids your warranty.
Some things will stop working unless we have the entire history.

View File

@@ -0,0 +1 @@
*Optional.* If `true`, will not fetch Git LFS files.

View File

@@ -0,0 +1,3 @@
*Optional.* If `none`, submodules will not be fetched. If specified as
a list of paths, only the given paths will be fetched. If not specified,
or if `all` is explicitly specified, all submodules are fetched.

View File

@@ -0,0 +1,5 @@
*Optional.* If specified the tag will be an
[annotated](https://git-scm.com/book/en/v2/Git-Basics-Tagging#Annotated-Tags)
tag rather than a
[lightweight](https://git-scm.com/book/en/v2/Git-Basics-Tagging#Lightweight-Tags)
tag. The value should be a path to a file containing the annotation message.

View File

@@ -0,0 +1,2 @@
*Optional.* When set to 'true' this will force the branch to be
pushed regardless of the upstream state.

View File

@@ -0,0 +1 @@
*Optional.* When set to 'true' push only the tags of a repo.

View File

@@ -0,0 +1,2 @@
*Optional.* If pushing fails with non-fast-forward, continuously
attempt rebasing and pushing.

View File

@@ -0,0 +1 @@
*Required.* The path of the repository to push to the source.

View File

@@ -0,0 +1,2 @@
*Optional.* If this is set then HEAD will be tagged. The value should be
a path to a file containing the name of the tag.

View File

@@ -0,0 +1,3 @@
*Optional.* If specified, the tag read from the file will be
prepended with this string. This is useful for adding `v` in front of
version numbers.

View File

@@ -0,0 +1 @@
*Required.* The branch the file lives on.

View File

@@ -0,0 +1 @@
*Required.* The name of the file in the repository.

View File

@@ -0,0 +1,2 @@
*Optional.* The git identity to use when pushing to the
repository support RFC 5322 address of the form "Gogh Fir \<gf@example.com\>" or "foo@example.com".

View File

@@ -0,0 +1 @@
*Optional.* Password for HTTP(S) auth when pulling/pushing.

View File

@@ -0,0 +1 @@
*Optional.* The SSH private key to use when pulling from/pushing to to the repository.

View File

@@ -0,0 +1 @@
*Required.* The repository URL.

View File

@@ -0,0 +1,3 @@
*Optional.* Username for HTTP(S) auth when pulling/pushing.
This is needed when only HTTP/HTTPS protocol for git is available (which does not support private key auth)
and auth is required.

View File

@@ -0,0 +1,3 @@
The branch to track. This is *optional* if the resource is
only used in `get` steps (default value in this case is `master`).
However, it is *required* when used in a `put` step.

View File

@@ -0,0 +1,9 @@
*Optional.* Array of GPG public key ids that
the resource will check against to verify the commit (details below). The
corresponding keys will be fetched from the key server specified in
`gpg_keyserver`. The ids can be short id, long id or fingerprint.
If `commit_verification_keys` or `commit_verification_key_ids` is specified in
the source configuration, it will additionally verify that the resulting commit
has been GPG signed by one of the specified keys. It will error if this is not
the case.

View File

@@ -0,0 +1,7 @@
*Optional.* Array of GPG public keys that the
resource will check against to verify the commit.
If `commit_verification_keys` or `commit_verification_key_ids` is specified in
the source configuration, it will additionally verify that the resulting commit
has been GPG signed by one of the specified keys. It will error if this is not
the case.

View File

@@ -0,0 +1,2 @@
*Optional.* Allows for commits that have been labeled with `[ci skip]` or `[skip ci]`
previously to be discovered by the resource.

View File

@@ -0,0 +1,7 @@
*Optional.* If specified as (list of pairs `name` and `value`)
it will configure git global options, setting each name with each value.
This can be useful to set options like `credential.helper` or similar.
See the [`git-config(1)` manual page](https://www.kernel.org/pub/software/scm/git/docs/git-config.html)
for more information and documentation of existing git options.

View File

@@ -0,0 +1,2 @@
*Optional.* GPG keyserver to download the public keys from.
Defaults to `hkp:///keys.gnupg.net/`.

View File

@@ -0,0 +1,10 @@
*Optional.* A list of glob patterns. The inverse of `paths`; changes
to the specified files are ignored.
Note that if you want to push commits that change these files via a `put`,
the commit will still be "detected", as [`check` and `put` both introduce
versions](https://concourse.ci/pipeline-mechanics.html#collecting-versions).
To avoid this you should define a second resource that you use for commits
that change files that you don't want to feed back into your pipeline - think
of one as read-only (with `ignore_paths`) and one as write-only (which
shouldn't need it).

View File

@@ -0,0 +1,3 @@
*Optional.* Password for HTTP(S) auth when pulling/pushing.
Note: You can also use pipeline templating to hide this password in source control. (For more information: https://concourse.ci/fly-set-pipeline.html)

View File

@@ -0,0 +1,2 @@
*Optional.* If specified (as a list of glob patterns), only changes
to the specified files will yield new versions from `check`.

View File

@@ -0,0 +1,12 @@
*Optional.* Private key to use when pulling/pushing.
Example:
private_key: |
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAtCS10/f7W7lkQaSgD/mVeaSOvSF9ql4hf/zfMwfVGgHWjj+W
<Lots more text>
DWiJL+OFeg9kawcUL6hQ8JeXPhlImG6RTUffma9+iGQyyBMCGd1l
-----END RSA PRIVATE KEY-----
Note: You can also use pipeline templating to hide this private key in source control. (For more information: https://concourse.ci/fly-set-pipeline.html)

View File

@@ -0,0 +1,2 @@
*Optional.* Skips git ssl verification by exporting
`GIT_SSL_NO_VERIFY=true`.

View File

@@ -0,0 +1,4 @@
`tag_filter`: *Optional.* If specified, the resource will only detect commits
that have a tag matching the specified expression. Patterns are
[glob(7)](http://man7.org/linux/man-pages/man7/glob.7.html) compatible (as
in, bash compatible).

View File

@@ -0,0 +1 @@
*Required.* The location of the repository.

View File

@@ -0,0 +1,3 @@
*Optional.* Username for HTTP(S) auth when pulling/pushing.
This is needed when only HTTP/HTTPS protocol for git is available (which does not support private key auth)
and auth is required.

View File

@@ -0,0 +1 @@
*Optional.* A list of jobs that should appear in this group. A job may appear in multiple groups. Neighbours of jobs in the current group will also appear on the same page in order to give context of the location of the group in the pipeline.

View File

@@ -0,0 +1 @@
*Required.* The name of the group. This should be short and simple as it will be used as the tab name for navigation.

View File

@@ -0,0 +1 @@
*Optional.* A list of resources that should appear in this group. Resources that are inputs or outputs of jobs in the group are automatically added; they do not have to be explicitly listed here.

View File

@@ -0,0 +1,10 @@
<p><em>Optional.</em> If configured, only the last specified number of builds
will have their build logs persisted. This is useful if you have a job that
runs periodically but after some amount of time the logs aren't worth keeping
around.</p><p>Example:</p><div class="highlight"><pre class="verbatim"><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">jobs</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">name</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">smoke-tests</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">build_logs_to_retain</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="mi">100</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">plan</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="mi">10</span><span class="nv"></span><span class="nv">m</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">task</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">smoke-tests</span><span class="t">
</span><span class="t"> </span><span class="nv">#</span><span class="sp"> </span><span class="nv">...</span></pre></div>

View File

@@ -0,0 +1,3 @@
<p><em>Optional. Default <code>false</code>.</em> If set to <code>true</code>, manual
triggering of the job (via the web UI or <a href="fly-trigger-job.html"><code>trigger-job</code></a>) will be
disabled.</p>

View File

@@ -0,0 +1,3 @@
<p><em>Optional.</em> If set, specifies a maximum number of builds to run at a
time. If <code>serial</code> or <code>serial_groups</code> are set, they take precedence
and force this value to be <code>1</code>.</p>

View File

@@ -0,0 +1,2 @@
<p><em>Required.</em> The name of the job. This should be short; it will show up
in URLs.</p>

View File

@@ -0,0 +1,60 @@
<p>Each <a href="concepts.html#jobs">Job</a> has a single build plan. When a build of a job is
created, the plan determines what happens.</p><p>A build plan is a sequence of <em>steps</em> to execute. These steps may fetch
down or update <a href="concepts.html#resources">Resources</a>, or execute
<a href="concepts.html#tasks">Tasks</a>.</p><p>A new build of the job is scheduled whenever <a href="get-step.html"><code>get</code></a> steps with
<code>trigger: true</code> have new versions available.</p><p>To visualize the job in the pipeline, resources that appear as <code>get</code>
steps are drawn as inputs, and resources that appear in <code>put</code> steps
appear as outputs.</p><p>A simple unit test job may look something like:</p><div class="highlight"><pre class="verbatim"><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">name</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">banana-unit</span><span class="t">
</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">plan</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">banana</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">trigger</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="kc">true</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">task</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">unit</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">file</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">banana/task.yml</span></pre></div><p>This job says: <a href="get-step.html"><code>get</code></a> the <code>banana</code> resource,
and run a <a href="task-step.html"><code>task</code></a> step called <code>unit</code>, using
the configuration from the <code>task.yml</code> file fetched from the <code>banana</code>
step.</p><p>When new versions of <code>banana</code> are detected, a new build of
<code>banana-unit</code> will be scheduled, because we've set <code>trigger: true</code>.</p><p>Jobs can depend on resources that are produced by or pass through upstream
jobs, by configuring <code>passed: [job-a, job-b]</code> on the
<a href="get-step.html"><code>get</code></a> step.</p><p>Putting these pieces together, if we were to propagate <code>banana</code> from
the above example into an integration suite with another <code>apple</code>
component (pretending we also defined its <code>apple-unit</code> job), the
configuration for the integration job may look something like:</p><div class="highlight"><pre class="verbatim"><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">name</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">fruit-basket-integration</span><span class="t">
</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">plan</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">aggregate</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">banana</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">trigger</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="kc">true</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">passed</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="pi">[</span><span class="nv"></span><span class="nv">banana-unit</span><span class="t"></span><span class="pi">]</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">apple</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">trigger</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="kc">true</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">passed</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="pi">[</span><span class="nv"></span><span class="nv">apple-unit</span><span class="t"></span><span class="pi">]</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">integration-suite</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">trigger</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="kc">true</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">task</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">integration</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">file</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">integration-suite/task.yml</span></pre></div><p>Note the use of the <a href="aggregate-step.html"><code>aggregate</code></a> step to
collect multiple inputs at once.</p><p>With this example we've configured a tiny pipeline that will automatically
run unit tests for two components, and continuously run integration tests
against whichever versions pass both unit tests.</p><p>This can be further chained into later "stages" of your pipeline; for
example, you may want to continuously deliver an artifact built from
whichever components pass <code>fruit-basket-integration</code>.</p><p>To push artifacts, you would use a <a href="put-step.html"><code>put</code></a> step
that targets the destination resource. For example:</p><div class="highlight"><pre class="verbatim"><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">name</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">deliver-food</span><span class="t">
</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">plan</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">aggregate</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">banana</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">trigger</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="kc">true</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">passed</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="pi">[</span><span class="nv"></span><span class="nv">fruit-basket-integration</span><span class="t"></span><span class="pi">]</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">apple</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">trigger</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="kc">true</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">passed</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="pi">[</span><span class="nv"></span><span class="nv">fruit-basket-integration</span><span class="t"></span><span class="pi">]</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">baggy</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">trigger</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="kc">true</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">task</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">shrink-wrap</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">file</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">baggy/shrink-wrap.yml</span><span class="t">
</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">put</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">bagged-food</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">params</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">bag</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">shrink-wrap/bagged.tgz</span></pre></div><p>This presumes that there's a <code>bagged-food</code>
<a href="concepts.html#resources">resource</a> defined, which understands that the
<code>bag</code> parameter points to a file to ship up to the resource's location.</p><p>Note that both <code>banana</code> and <code>apple</code> list the same job as an
upstream dependency. This guarantees that <code>deliver-food</code> will only
trigger when a version of both of these dependencies pass through the same
build of the integration job (and transitively, their individual unit jobs).
This prevents bad apples or bruised bananas from being delivered. (I'm sorry.)</p>

View File

@@ -0,0 +1,5 @@
<p><em>Optional. Default <code>false</code>.</em> If set to <code>true</code>, the build log
of this job will be viewable by unauthenticated users. Unauthenticated users
will always be able to see the inputs, outputs, and build status history of a
job. This is useful if you would like to expose your pipeline publicly without
showing sensitive information in the build log.</p>

View File

@@ -0,0 +1,2 @@
<p><em>Optional. Default <code>false</code>.</em> If set to <code>true</code>, builds will
queue up and execute one-by-one, rather than executing in parallel.</p>

View File

@@ -0,0 +1,12 @@
<p><em>Optional. Default <code>[]</code>.</em> When set to an array of arbitrary
tag-like strings, builds of this job and other jobs referencing the same
tags will be serialized.</p><p>This can be used to ensure that certain jobs do not run at the same time,
like so:</p><div class="highlight"><pre class="verbatim"><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">jobs</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">name</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">job-a</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">serial_groups</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="pi">[</span><span class="nv"></span><span class="nv">some-tag</span><span class="t"></span><span class="pi">]</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">name</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">job-b</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">serial_groups</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="pi">[</span><span class="nv"></span><span class="nv">some-tag</span><span class="t"></span><span class="pi">,</span><span class="t"> </span><span class="nv"></span><span class="nv">some-other-tag</span><span class="t"></span><span class="pi">]</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">name</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">job-c</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">serial_groups</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="pi">[</span><span class="nv"></span><span class="nv">some-other-tag</span><span class="t"></span><span class="pi">]</span></pre></div><p>In this example, <code>job-a</code> and <code>job-c</code> can run concurrently, but
neither job can run builds at the same time as <code>job-b</code>.</p><p>The builds are executed in their order of creation, across all jobs with
common tags.</p>

View File

@@ -0,0 +1,21 @@
Splitting up your pipeline into sections.
A pipeline may optionally contain a section called `groups`. As more resources and jobs are added to a pipeline it can become difficult to navigate. Pipeline groups allow you to group jobs together under a header and have them show on different tabs in the user interface. Groups have no functional effect on your pipeline.
A simple grouping for the pipeline above may look like:
groups:
- name: tests
jobs:
- controller-mysql
- controller-postgres
- worker
- integration
- name: deploy
jobs:
- deploy
This would display two tabs at the top of the home page: "tests" and "deploy". Once you have added groups to your pipeline then all jobs must be in a group otherwise they will not be visible.
For a real world example of how groups can be used to simplify navigation and provide logical grouping, see the groups used at the top of the page in the [Concourse pipeline](https://ci.concourse.ci/).

View File

@@ -0,0 +1,16 @@
<p>At a high level, a job describes some actions to perform when dependent
resources change (or when manually triggered). For example, you may define a
job that runs your unit tests whenever new code is pushed to a repository.</p><p>Jobs can be thought of as functions with inputs and outputs, that
automatically run when new inputs are available. A job can depend on the
outputs of upstream jobs, which is the root of pipeline functionality.</p><p>The definition of actions to perform is done via a
<a href="build-plans.html">Build Plan</a>, which is a very powerful
composition-based DSL that can express anything from running simple unit
tests to running a matrix of tasks and aggregating the result.</p><div class="section" id="section_job-builds"><h3><a name="job-builds"></a>Builds</h3><p>An instance of execution of a job's plan is called a <em>build</em>. A build
can either succeed or fail, or error if something unrelated to your code
goes wrong (i.e. if one of your workers falls off the face of the earth).</p><p>When a build runs, the job's plan is realized. Each step described by the
job's plan is executed, and so long as all <a href="concepts.html#tasks">Tasks</a> succeed, the
build succeeds. If a task fails, the build fails, and its resources do not
propagate to the rest of the pipeline.</p><p>The containers running in a build can be accessed while they're running
(and also shortly after they finish) via
<a href="fly-intercept.html"><code>fly intercept</code></a>, which can greatly help in
debugging.</p>

View File

@@ -0,0 +1,52 @@
<p>Additional resource types used by your pipeline.</p>
<p>Each resource in a pipeline has a <code>type</code>. The resource's type determines
what versions are detected, the bits that are fetched when used for a
<a href="get-step.html"><code>get</code></a> step, and the side effect that occurs when used for a
<a href="put-step.html"><code>put</code></a> step.</p><p>Out of the box, Concourse comes with a few resource types to cover common CI
use cases like dealing with Git repositories and S3 buckets.</p><p>Beyond these core types, each pipeline can configure its own custom types by
specifying <code>resource_types</code> at the top level. Each custom resource type is
itself defined as a resource that provides the container image for the custom
resource type (see <a href="implementing-resources.html">Implementing a Resource</a>). You will almost always
be using the
<a href="https://github.com/concourse/docker-image-resource"><code>docker-image</code>
resource type</a> when doing this.</p><p>The following example extends a Concourse pipeline to support use of the
<a href="https://github.com/jtarchie/pullrequest-resource"><code>pull-request</code>
resource type</a> and then uses it within the pipeline:</p><div class="highlight"><pre class="verbatim"><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">resource_types</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">name</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">pull-request</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">type</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">docker-image</span><span class="t">
</span><span class="t"> </span><span class="py">source</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">repository</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">jtarchie/pr</span><span class="t">
</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">resources</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">name</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">atomy-pr</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">type</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">pull-request</span><span class="t">
</span><span class="t"> </span><span class="py">source</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">repo</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">vito/atomy</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">access_token</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="pi">{</span><span class="pi">{</span><span class="nv"></span><span class="nv">access-token</span><span class="t"></span><span class="pi">}</span><span class="pi">}</span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">jobs</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">name</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">atomy-pr-unit</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">plan</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">get</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">atomy-pr</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="pi">-</span><span class="t"></span><span class="t"> </span><span class="t"></span><span class="nv"></span><span class="py">put</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">atomy-pr</span><span class="t">
</span><span class="t"> </span><span class="py">params</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">path</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">atomy-pr</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">status</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">pending</span><span class="t">
</span><span class="t"> </span><span class="nv">-</span><span class="sp"> </span><span class="py">task</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">unit</span><span class="t">
</span><span class="t"> </span><span class="py">file</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">atomy-pr/ci/unit.yml</span><span class="t">
</span><span class="t"> </span><span class="py">on_success</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">put</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">atomy-pr</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">params</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">path</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">atomy-pr</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">status</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">success</span><span class="t">
</span><span class="t"> </span><span class="py">on_failure</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">put</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">atomy-pr</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">params</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">path</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">atomy-pr</span><span class="t">
</span><span class="t"> </span><span class="t"></span><span class="t"></span><span class="t"></span><span class="t"></span><span class="nv"></span><span class="py">status</span><span class="t"></span><span class="pi">:</span><span class="t"></span><span class="t"> </span><span class="nv"></span><span class="nv">failure</span></pre></div><p>Custom resource types can override the core resource types, and can be defined
in terms of each other. Also, a custom resource type can use the core type that
it's overriding. This is useful if you want to e.g. provide your own custom
<code>docker-image</code> resource, by overriding the core one (and using it one last
time for the override itself), and then using it for all other custom resource
types.</p>

View File

@@ -0,0 +1,14 @@
<p>A resource is any entity that can be checked for new versions, pulled down
at a specific version, and/or pushed up to idempotently create new versions.
A common example would be a git repository, but it can also represent more
abstract things like
<a href="https://github.com/concourse/time-resource">time itself</a>.</p><p>At its core, Concourse knows nothing about things like <code>git</code>. Instead,
it consumes a generic interface implemented by <em>resource types</em>. This
allows Concourse to be extended by configuring workers with resource type
implementations.</p><p>This abstraction is immensely powerful, as it does not limit Concourse to
whatever things its authors thought to integrate with. Instead, as a user of
Concourse you can just reuse resource type implementations, or
<a href="implementing-resources.html">implement your own</a>.</p><p>To use resources, configure them in your pipeline via the
<a href="configuring-resources.html"><code>resources</code></a> section, and use them in
your <a href="build-plans.html">Build Plans</a> via the <a href="get-step.html"><code>get</code></a> and
<a href="put-step.html"><code>put</code></a> steps.</p>

View File

@@ -0,0 +1,3 @@
If true, we will attempt to move a randomly chosen lock from the
pool's unclaimed directory to the claimed directory. Acquiring will retry
until a lock becomes available.

View File

@@ -0,0 +1,4 @@
If set, we will add a new lock to the pool in the unclaimed state. The
value is the path to a directory containing the files `name` and `metadata`
which should contain the name of your new lock and the contents you would like
in the lock, respectively.

Some files were not shown because too many files have changed in this diff Show More