diff --git a/docs-old/README.md b/docs-old/README.md
new file mode 100644
index 00000000000..62a8e27886b
--- /dev/null
+++ b/docs-old/README.md
@@ -0,0 +1,17 @@
+# Table of Contents
+
+* [Read Me](../README.md)
+* [Introduction](./introduction/README.md)
+ * [Getting Started](./introduction/getting-started.md)
+ * [Motivation](./introduction/motivation.md)
+* [Guides](./guides/README.md)
+ * [Configuration](./guides/configuration.md)
+ * [Using Garden with Minikube](./guides/minikube.md)
+ * [Remote Kubernetes](./guides/remote-kubernetes.md)
+ * [Glossary](./guides/glossary.md)
+* [Reference](./reference/README.md)
+ * [Commands](./reference/commands.md)
+ * [Config](./reference/config.md)
+* [Examples](./examples/README.md)
+ * [Simple Project](./examples/simple-project.md)
+* [FAQs](./faqs.md)
\ No newline at end of file
diff --git a/docs-old/examples/README.md b/docs-old/examples/README.md
new file mode 100644
index 00000000000..1fcbf2b2ebf
--- /dev/null
+++ b/docs-old/examples/README.md
@@ -0,0 +1,6 @@
+# Examples
+
+The source code for the examples in this section can be found in our Github repository under the
+[examples directory](https://github.com/garden-io/garden/tree/master/examples).
+
+* [Simple Project](./simple-project.md)
\ No newline at end of file
diff --git a/docs-old/examples/simple-project.md b/docs-old/examples/simple-project.md
new file mode 100644
index 00000000000..60b9842968c
--- /dev/null
+++ b/docs-old/examples/simple-project.md
@@ -0,0 +1,309 @@
+# Simple Project
+
+In this guide, we'll walk you through configuring a simple project to run on the Garden framework. The project will consist of two Dockerized web services that communicate with one another, along with unit and integration tests.
+
+In what follows you'll learn how to:
+
+* [Configure the project](#project-wide-configuration)
+* [Configure individual modules](#module-configuration)
+* [Deploy the project locally](#deploying)
+* [Have the services communicate with one another](#inter-service-communication)
+* [Manage service dependencies](#dependencies)
+* [Test services](#testing)
+
+## Before you get started
+
+This tutorial assumes that you have already have a running [installation of Garden](../introduction/getting-started.md).
+
+## Clone the example repo
+
+The code for this tutorial can be found in our Github repository under the [examples directory](https://github.com/garden-io/garden/tree/master/examples). We'll use the [simple-project-start](https://github.com/garden-io/garden/tree/master/examples/simple-project-start/) example and work our way from there. The complete version is under [simple-project](https://github.com/garden-io/garden/tree/master/examples/simple-project).
+
+First, let's clone the examples repo, change into the directory, and take a look inside:
+```sh
+$ git clone https://github.com/garden-io/garden/examples.git
+$ cd garden/examples/simple-project-start
+$ tree .
+.
+└── services
+ ├── go-service
+ │ ├── Dockerfile
+ │ └── webserver
+ │ └── main.go
+ └── node-service
+ ├── Dockerfile
+ ├── app.js
+ ├── main.js
+ ├── package.json
+ └── test
+ └── integ.js
+
+5 directories, 7 files
+ ```
+
+As you can see the project consists of two super simple services and their accompanying Dockerfiles. One of the core tenets of multi-service backends is being able to pick the right tool for the job, and therefore we have a Node.js service and a Golang service, that we can pretend have different responsibilities.
+
+The task at hand is to configure these services so that they can run on the Garden framework.
+
+## Project-wide configuration
+
+To begin with, every project needs a project-wide `garden.yml` [configuration file](../guides/configuration.md#Config) at the root level. There we define, among other things, the name of the project, and the [providers](../guides/glossary.md#Provider) used for each [plugin](../guides/glossary.md#Plugin) the project requires.
+
+Let's go ahead and create one:
+
+```sh
+$ touch garden.yml
+```
+
+and add the following configuration:
+
+```yaml
+project:
+ name: simple-project
+ environments:
+ - name: local
+ providers:
+ - name: local-kubernetes
+```
+
+Above, we've specified the name of our project and configured it to use the local-kubernetes plugin for local development. Note, that this file must be located in the project root directory.
+
+## Module configuration
+
+Now, let's turn to our services. Services live inside [modules](../guides/glossary.md#Module), and each module has it's own `garden.yml` configuration file.
+
+We'll start with the module for the `node-service`:
+
+```sh
+$ touch services/node-service/garden.yml
+```
+
+and add the following:
+
+```yaml
+module:
+ description: Node service container
+ type: container
+```
+
+By running the `scan` command we can see that Garden detects our module config:
+
+```sh
+$ garden scan
+- name: node-service
+ type: container
+ path: /Users/eysi/code/simple-project/services/node-service
+ description: Node service container
+ version:
+ versionString: 2c8818986d-1528373640
+ latestCommit: 2c8818986d
+ dirtyTimestamp: 1528373640
+```
+
+Under the `module` directive of our `services/node-service/garden.yml` file we can now specify how to run our service:
+
+```yaml
+module:
+ description: Node service container
+ type: container
+ services:
+ - name: node-service
+ command: [npm, start]
+ ports:
+ - name: http
+ containerPort: 8080
+ endpoints:
+ - path: /hello-node
+ port: http
+```
+The [services](../guides/configuration.md#Services) directive is specific to container modules, and defines the services exposed by the module. In this case, our containerized Node.js server. The sub-directives tell Garden how to start the service and which endpoints to expose.
+
+## Deploying
+
+With this configuration we're almost ready to deploy. First, we'll need to create a user namespace for our environment with the login command:
+
+```sh
+$ garden login
+```
+
+Garden can now deploy our service to a local Kubernetes cluster:
+
+```sh
+$ garden deploy
+```
+
+To verify that everything is working, we can call the service at the `/hello-node` endpoint defined in `/services/node-service/app.js`:
+
+```sh
+$ garden call node-service/hello-node
+✔ Sending HTTP GET request to http://simple-project.local.app.garden/hello-node
+
+200 OK
+
+Hello from Node server!
+```
+
+In a similar manner, we create a config file for our `go-service`:
+
+```sh
+$ touch services/go-service/garden.yml
+```
+
+and add the following:
+
+```yaml
+module:
+ description: Go service container
+ type: container
+ services:
+ - name: go-service
+ ports:
+ - name: http
+ containerPort: 80
+ endpoints:
+ - path: /hello-go
+ port: http
+```
+
+Run the deploy command again, this time only for the `go-service`:
+
+```sh
+$ garden deploy go-service
+```
+
+Another way to verify that our services are up and running is to have a look at the service logs. We can either get an aggregate from all our services, by running `garden logs`, or we can specify a list of services. This time we're only interested in our `go-service`:
+
+```sh
+$ garden logs go-service
+go-service → 2018-06-07T12:52:41.075Z → Server running...
+```
+
+Looks good! Let's take stock:
+
+* We started out with a project consisting of multiple containerized services (really just two, but hey, it's a _simple_ project).
+* We added a project wide configuration at the root level, and a module configuration for each service.
+* We deployed our entire project with the `garden deploy` command
+* We saw how we could call our services and read their logs with the `garden call` and `garden logs` commands.
+
+## Inter-service communication
+
+Calling our `go-service` from our `node-service` is straightforward from within the application code. Crack open `services/node-service/app.js` with your favorite editor and add the following:
+
+```javascript
+const request = require('request-promise')
+
+// Unless configured otherwise, the hostname is simply the service name
+const goServiceEndpoint = `http://go-service/hello-go`;
+
+app.get('/call-go-service', (req, res) => {
+ // Query the go-service and return the response
+ request.get(goServiceEndpoint)
+ .then(message => {
+ res.json({
+ message,
+ })
+ })
+ .catch((err) => {
+ res.statusCode = 500
+ res.json({
+ error: err,
+ message: "Unable to reach service at " + goServiceEndpoint,
+ })
+ })
+})
+```
+
+Now let's re-deploy the `node-service` and try out our new endpoint:
+
+```sh
+$ garden deploy node-service
+$ garden call node-service/call-go-service
+✔ Sending HTTP GET request to http://simple-project.local.app.garden/call-go-service
+
+200 OK
+
+{
+ "message": "Hello from Go!"
+}
+```
+
+Nice!
+
+So far, we've seen how to configure a simple project and it's modules, how to deploy our services, and how these services can communicate. Next, let's take a look at how we can define dependencies and set up testing.
+
+## Dependencies
+
+An attentive reader will no doubt have noticed that our `node-service` depends on the `go-service` for it's `call-go-service` endpoint. We can express this in the `node-service` module configuration by adding `dependencies` under the `services` directive:
+
+```yaml
+module:
+ description: Node service container
+ ...
+ services:
+ - name: node-service
+ command: [npm, start]
+ ...
+ dependencies:
+ - go-service
+```
+
+This will ensure that our `go-service` will be deployed before the `node-service`.
+
+## Testing
+
+Finally, we'll update our `node-service` module configuration to tell Garden how to run our tests. Add the following test config under the `module` directive in `services/node-service/garden.yml`:
+
+```yaml
+module:
+ description: Node service container
+ ...
+ services:
+ - name: node-service
+ command: [npm, start]
+ ...
+ tests:
+ - name: unit
+ command: [npm, test]
+ - name: integ
+ command: [npm, run, integ]
+ dependencies:
+ - go-service
+```
+
+This allows us to run individual test groups by name or all of them at once with the test command:
+
+```sh
+$ garden test
+```
+
+Notice also that the integration test depends on the `go-service` being deployed.
+
+The entire module config should now look like this:
+
+```yaml
+module:
+ description: Node service container
+ type: container
+ services:
+ - name: node-service
+ command: [npm, start]
+ ports:
+ - name: http
+ containerPort: 8080
+ endpoints:
+ - path: /
+ port: http
+ dependencies:
+ - go-service
+ tests:
+ - name: unit
+ command: [npm, test]
+ - name: integ
+ command: [npm, run, integ]
+ dependencies:
+ - go-service
+```
+
+And that's it! Our services are up and running locally, dependencies are resolved, and tests are ready to run.
+
+Check out some of our other [Guides](../guides/README.md) for more of an in-depth look at the Garden framework.
\ No newline at end of file
diff --git a/docs-old/faqs.md b/docs-old/faqs.md
new file mode 100644
index 00000000000..9b92ecddb13
--- /dev/null
+++ b/docs-old/faqs.md
@@ -0,0 +1,6 @@
+# Frequently Asked Questions
+
+### When using garden inside tmux, colors look wonky. What gives?
+
+You need to set tmux to use 256 colors. As per the [official documentation](https://github.com/tmux/tmux/wiki/FAQ#how-do-i-use-a-256-colour-terminal), you
+can do that by adding `set -g default-terminal "screen-256color"` or `set -g default-terminal "tmux-256color"` to your `~/.tmux.conf` file.
diff --git a/docs/garden-banner-logotype-left-2.png b/docs-old/garden-banner-logotype-left-2.png
similarity index 100%
rename from docs/garden-banner-logotype-left-2.png
rename to docs-old/garden-banner-logotype-left-2.png
diff --git a/docs/guides/README.md b/docs-old/guides/README.md
similarity index 100%
rename from docs/guides/README.md
rename to docs-old/guides/README.md
diff --git a/docs/guides/configuration.md b/docs-old/guides/configuration.md
similarity index 100%
rename from docs/guides/configuration.md
rename to docs-old/guides/configuration.md
diff --git a/docs/guides/glossary.md b/docs-old/guides/glossary.md
similarity index 100%
rename from docs/guides/glossary.md
rename to docs-old/guides/glossary.md
diff --git a/docs/guides/minikube.md b/docs-old/guides/minikube.md
similarity index 100%
rename from docs/guides/minikube.md
rename to docs-old/guides/minikube.md
diff --git a/docs/guides/remote-kubernetes.md b/docs-old/guides/remote-kubernetes.md
similarity index 100%
rename from docs/guides/remote-kubernetes.md
rename to docs-old/guides/remote-kubernetes.md
diff --git a/docs/introduction/README.md b/docs-old/introduction/README.md
similarity index 100%
rename from docs/introduction/README.md
rename to docs-old/introduction/README.md
diff --git a/docs-old/introduction/getting-started.md b/docs-old/introduction/getting-started.md
new file mode 100644
index 00000000000..e0d58c1006d
--- /dev/null
+++ b/docs-old/introduction/getting-started.md
@@ -0,0 +1,42 @@
+# Getting Started
+
+This guide will walk you through setting up the Garden framework.
+
+Please follow the guide for your operating system:
+
+* [macOS](#macos)
+* [Windows](#windows)
+* [Linux (or manual installation on other platforms)](#linux-manual-installation)
+
+## Using the CLI
+
+With the CLI installed, we can now try out a few commands using the [hello-world](https://github.com/garden-io/garden/examples/tree/master/simple-project) project from our Github [examples repository](https://github.com/garden-io/garden/examples). The example consists of a a couple of simple services.
+
+_Note: check if Kubernetes is running with `kubectl version`. You should see both a `Client Version` and a `Server Version` in the response. If not, please start it up before proceeding._
+
+Clone the repo and change into the `hello-world` directory:
+
+```sh
+$ git clone https://github.com/garden-io/garden/examples.git
+$ cd garden/examples/hello-world
+```
+
+First, let's check the environment status by running the following from the project root:
+
+```sh
+$ garden status
+```
+
+The response tells us how the environment is configured and the status of the providers. Next, we'll deploy the services with:
+
+```sh
+$ garden deploy
+```
+
+And that's it! The services are now running on the Garden framework. You can see for yourself by querying the `/hello` endpoint of the container with:
+
+```sh
+$ garden call hello-container/hello
+```
+
+Check out our [Commands guide](../guides/commands.md) for other features like auto-reload, streaming service logs, running tests and lots more, or see how a Garden project is configured from scratch in our [Simple Project](../guides/simple-project.md) guide.
\ No newline at end of file
diff --git a/docs/introduction/motivation.md b/docs-old/introduction/motivation.md
similarity index 100%
rename from docs/introduction/motivation.md
rename to docs-old/introduction/motivation.md
diff --git a/docs-old/reference/README.md b/docs-old/reference/README.md
new file mode 100644
index 00000000000..aa067ae31dc
--- /dev/null
+++ b/docs-old/reference/README.md
@@ -0,0 +1,5 @@
+# Reference
+
+* [Commands](./commands.md)
+* [Config](./config.md)
+* [Template strings](./template-strings.md)
diff --git a/docs/reference/commands.md b/docs-old/reference/commands.md
similarity index 100%
rename from docs/reference/commands.md
rename to docs-old/reference/commands.md
diff --git a/docs/reference/config.md b/docs-old/reference/config.md
similarity index 100%
rename from docs/reference/config.md
rename to docs-old/reference/config.md
diff --git a/docs/reference/template-strings.md b/docs-old/reference/template-strings.md
similarity index 100%
rename from docs/reference/template-strings.md
rename to docs-old/reference/template-strings.md
diff --git a/docs/README.md b/docs/README.md
index d7b1654595f..8652c49885f 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,17 +1,87 @@
-# Table of Contents
-
-* [Read Me](../README.md)
-* [Introduction](./introduction/README.md)
- * [Getting Started](./introduction/getting-started.md)
- * [Motivation](./introduction/motivation.md)
-* [Guides](./guides/README.md)
- * [Configuration](./guides/configuration.md)
- * [Using Garden with Minikube](./guides/minikube.md)
- * [Remote Kubernetes](./guides/remote-kubernetes.md)
- * [Glossary](./guides/glossary.md)
-* [Reference](./reference/README.md)
- * [Commands](./reference/commands.md)
- * [Config](./reference/config.md)
-* [Examples](./examples/README.md)
- * [Simple Project](./examples/simple-project.md)
-* [FAQs](./faqs.md)
+[![CircleCI](https://circleci.com/gh/garden-io/garden/tree/master.svg?style=svg&circle-token=ac1ec9984d093f91e594e5a0a03b34cec2c2a093)](https://circleci.com/gh/garden-io/garden/tree/master)
+
+
+![](docs/garden-banner-logotype-left-2.png)
+
+*Welcome! Garden is a full-featured development framework for containers and serverless backends, designed to make
+it easy to develop and test distributed systems.*
+
+
+### Status
+
+The project is in _early alpha_ (or developer preview, if you prefer). This means APIs may well change (not drastically, but still), overall stability will improve and platform support is still limited.
+
+All that said, Garden can already be highly useful if the following applies to you:
+
+* **You're deploying to (or transitioning to) Kubernetes.**
+* **You really don't want to spend your precious hours building your own developer tooling!**
+
+If that sounds right for you, please give it a go and don't hesitate to report issues.
+
+
+## Features
+
+With Garden, you can...
+
+* Configure and deploy a fleet of services to a local Kubernetes cluster using simple declarations.
+* Use an integrated framework for building, testing and deploying services.
+* Easily run end-to-end tests across multiple services without waiting for a slow CI pipeline.
+* Automatically build, deploy and/or test when your code changes, using the `--watch` flag or the `garden dev` command.
+* Manage build and runtime dependencies across all your services.
+* Leverage a suite of commands and helpers to facilitate developing and running your stack.
+* _Write code the way you want, and run your production system however suits you! Garden does not impose any new libraries or languages aside from the config files._
+
+Garden is also designed to be pluggable and modular, with Kubernetes being just one plugin (albeit an important one). Over time we will add native support for a variety of platforms, including AWS (Lambda, ECS, Fargate and more), GCP, Heroku... and the list will continue growing.
+
+
+## Usage
+
+Head over to our [Quick Start guide](https://docs.garden.io/basics/getting-started/quick-start), and then look through our [Simple Project](https://docs.garden.io/examples/simple-project) guide to get a quick sense of how everything works.
+
+For a more in-depth approach, just keep reading this documentation.
+
+[![asciicast](https://asciinema.org/a/SKI7qe7DFVVHxvoaIVrLPb6Es.png)](https://asciinema.org/a/SKI7qe7DFVVHxvoaIVrLPb6Es?speed=2)
+
+## Contributing
+
+We welcome any and all contributions to Garden! What we're trying to achieve is a big task, and
+developers have a lot of diverse needs, so we need and appreciate your input, whether it's through
+code, docs, issues or developing plugins for your needs.
+
+For more detailed guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md).
+
+
+# Motivation
+
+The landscape of server-side development has changed immensely over the last decade.
+This has partly been driven by evolving needs — **scalability has become table-stakes for most
+projects and companies** — and also by the rapid development and proliferation of new technologies
+like containers.
+
+From an operations standpoint, all of this is fantastic. Scaling out is increasingly simple
+and cost-effective, and managing production systems is easier than ever. So much so, that the
+notion of DevOps has caught on — if ops is so easy, why not have the developers do it
+themselves?
+
+And the promise of it all is great. Microservices, immutable infrastructure, continuous
+integration and deployment, all that jazz. Trouble is, all this tends to come at the expense
+of application developer productivity. In embracing these new technologies and tools, we've
+_over-optimized for ops, and in turn made it more difficult and tedious to work on the actual
+application code_.
+
+Now, rather than lament and pine for the good ol' monolith days, we at Garden feel that this can
+be addressed by **a new generation of developer tooling**. So that's what we've set out to make.
+It's certainly not a trivial task, but we truly believe that it's possible to not only reclaim the
+rapid feedback loops we're used to when developing individual services, but to go further and
+leverage the benefits of modern backend platforms to make development easier and faster than ever.
+
+So think of Garden as the missing layer on top of Kubernetes, AWS, GCP, etc., that focuses purely
+on the **developer experience**, makes it trivial to work across multiple platforms, and closes the
+gap between infrastructure and application development.
+
+We do this by frameworking around the basic primitives of development — building, testing,
+debugging and deploying — and making the _how_ of each of those pluggable and configurable.
+This allows the framework to grow with you and adapt as your needs evolve in terms of how you
+architect and run your code in production, and allows us to easily tie together all the amazing
+open-source tools that are being developed in the ecosystem, into an **integrated, consistent
+and easy-to-use development framework**.
diff --git a/docs/basics/README.md b/docs/basics/README.md
new file mode 100644
index 00000000000..f0c272935ff
--- /dev/null
+++ b/docs/basics/README.md
@@ -0,0 +1,11 @@
+# Basics
+
+The following articles cover the basics of installing and using Garden:
+
+ * Installation instructions for your platform: [Installation](./basics/installation.md).
+ * A very brief guide on the main commands you should be familiar with, and an example project to test them in: [Quick Start](./basics/quick-start.md).
+ * An overview of the framework's main concepts, to help get you started using Garden with your own projects: [Concepts](./basics/concepts.md).
+
+If you're already familiar with the basics, feel free to move on to the next chapter: [Using Garden](./using-garden/README.md).
+
+Or dive right in by exploring our [Guides](./guides/README.md) and [Example projects](./examples/README.md).
diff --git a/docs/basics/concepts.md b/docs/basics/concepts.md
new file mode 100644
index 00000000000..3b9e2478d14
--- /dev/null
+++ b/docs/basics/concepts.md
@@ -0,0 +1,51 @@
+# How Garden works
+
+The mechanics for how Garden works are fundamentally straight forward:
+
+The main functionality is housed under what we call providers. We have, for example, a provider for containers, one for OpenFaaS, one for Kubernetes, and providers are how we control the behavior of these different types of tools.
+
+Garden projects, in turn, consist of modules. Each module in a project has a type (e.g. container, OpenFaaS), and the type then indicates which provider should deal with each specific module when it comes to building, deploying, and testing it.
+
+This information is conveyed through [configuration files](../using-garden/configuration-files.md), usually in YAML format, which live in the project root for project-wide settings, and in each module's directory (for module-specific settings).
+
+# Projects vs. modules vs. services
+
+Garden has three main organizational units: projects, modules, and services.
+
+A project is the largest unit, and it contains all the others. You can think of a project as a context: there aren't any hard rules or limitations as to how big or small your project should be, but it's advisable to keep all elements belonging to a same context inside the same project.
+
+Modules can be thought of as build units. So, for example, every container and every serverless function should, as a rule of thumb, have its own module.
+
+Lastly, services are units of deployment, or instances. They're *usually* one per module, but not necessarily: you might have, for example, two instances of the same container working on different queues or data streams.
+
+To sum it all up: A project consists of one or modules, and each module may deploy zero or more services.
+
+# The build → test → deploy sequence
+
+One of the main tools of Garden to make the development of distributed systems extremely agile is the developer framework. You can call with `garden dev`.
+
+It is a combination of the `build`, `deploy` and `test` commands, that is, it builds, deploys and tests all your modules and services, and re-builds, re-deploys and re-tests as you modify the code.
+
+The `build`, `deploy` and `test` commands, and by extension the `dev` command, are all dependency-aware. They will always build, test, and deploy modules in the right order so that all dependencies are respected.
+
+# How inter-service communication works
+
+Arguably the most important thing a distributed system needs to do is to allow its different parts to talk to one another. Garden makes inter-service communication extremely simple: a service's hostname is simply its name as declared in the configuration file.
+
+For example, if you have a service called `my-service`, you can access its `/feature` endpoint by simply calling `http://my-service/feature`.
+
+# Hot reload
+
+Hot reloading is updating a running service when its source files are changed, without re-building and re-deploying the whole thing.
+
+In the case of a container, for example, we would not destroy the container, change the files, and then re-deploy a new container. Instead, we would update the changed files without stopping the running container, thus potentially not losing the current state of the application.
+
+Hot reload is off for all modules by default, and it needs to be enabled with the `hotReload` field a module's configuration file. For more detailed information, see the [configuring hot reload](./guides/configuring-hot-reload.md) guide.
+
+# Projects with multiple and/or remote repositories
+
+Garden projects may include sources hosted in any number of local or remote repositories. Remote sources may be later linked to local directories for convenience or to work offline.
+
+You could have, for example, a project that has one local module, then one remote module from an external source, and then a second external source that contains, let's say, two more modules.
+
+For specifics see our [Remote sources project](../examples/remote-sources.md) example.
\ No newline at end of file
diff --git a/docs/basics/installation.md b/docs/basics/installation.md
new file mode 100644
index 00000000000..c30f0464cb7
--- /dev/null
+++ b/docs/basics/installation.md
@@ -0,0 +1,175 @@
+## Installation
+
+This guide will walk you through setting up the Garden framework.
+
+Please follow the guide for your operating system:
+
+* [macOS](#macos)
+* [Windows](#windows)
+* [Linux (Manual Installation)](#linux-manual-installation)
+
+And if you decide to use Minikube, please see our [Minikube Instructions](#minikube-instructions) further down this
+document.
+
+### macOS
+
+For Mac, we recommend the following steps to install Garden. You can also follow the manual installation
+steps below if you prefer.
+
+#### Step 1: Install homebrew
+
+If you haven't already set up homebrew, please follow [their instructions](https://brew.sh/) to set it up.
+
+#### Step 2: Docker and local Kubernetes
+
+To install Docker, Kubernetes and kubectl, we strongly recommend Docker for Mac (edge version).
+
+_Note: you need to install the **edge version** of Docker for Mac to enable Kubernetes support._
+
+Once installed, open the Docker for Mac preferences, go to the Kubernetes section,
+tick `Enable Kubernetes` and save. Please refer to their
+[installation guide](https://docs.docker.com/engine/installation/) for details.
+
+Alternatively, you can use Minikube. We generally find it less stable and more hassle to
+configure and use, but we do fully support it on Mac. Please look at the
+[Minikube Instructions](#minikube-instructions) section for details.
+
+#### Step 3: Install `garden-cli`
+
+We have a Homebrew tap and package that you can use to easily install `garden-cli` and all dependencies:
+
+```sh
+brew tap garden-io/garden
+brew install garden-cli
+```
+
+To later upgrade to the newest version, simply run `brew update` and then `brew upgrade garden-cli`
+(or `brew upgrade` to upgrade all your Homebrew packages).
+
+### Windows
+
+You can run Garden on Windows 10 Pro or Enterprise editions (unfortunately, the Home edition does not work, because it
+does not support virtualization). To install the Garden CLI, please use our _automated installation script_, which will
+check for dependencies, install missing dependencies if needed, and finally install the `garden-cli` npm package.
+
+The script will check for the following:
+
+* The [Chocolatey](https://chocolatey.org) package manager.
+* Whether you have Hyper-V enabled. This is required for _Docker for Windows_. If you do not already have it enabled,
+ the script will enable it (you will then need to restart your computer before starting Docker for Windows).
+* Docker - We strongly recommend using the _Edge version_ of
+ [Docker for Windows](https://www.docker.com/docker-windows), which has built-in support for Kubernetes. Garden also supports different configurations of Docker and Kubernetes, using Minikube for example, but
+ Docker for Windows is generally easier to install and configure, and is well supported. The script will check if Docker is
+ installed, and whether Kubernetes has been enabled as the default orchestrator.
+* Node.js - The script will install it via Chocolatey if it is missing. _If you already have Node.js
+ installed, please make sure it is version 8.x or newer._
+* Git and rsync - The script will install those if they are missing.
+
+To run the script, open PowerShell as an Administrator and run:
+
+```PowerShell
+Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/garden-io/garden/master/garden-service/support/install.ps1'))
+```
+
+To later upgrade to the newest version, run `npm install -g -U garden-cli`.
+
+### Linux (manual installation)
+
+You need the following dependencies on your local machine to use Garden:
+
+* Node.js >= 8.x
+* [Docker](https://docs.docker.com/)
+* Git
+* rsync
+* Local installation of Kubernetes and kubectl
+
+#### Step 1: Docker
+
+To install Docker, please follow the instructions in the [official documentation](https://docs.docker.com/install/).
+
+#### Step 2: Local Kubernetes
+
+For local Kubernetes, you can use [Minikube](https://github.com/kubernetes/minikube). Please see our
+[Minikube Instructions](#minikube-instructions).
+
+#### Step 3: Install other dependencies
+
+Use your preferred method or package manager to install `node` (version 8.x or higher), `git`, and `rsync`.
+
+On Ubuntu 18, you'd do `sudo apt install git rsync` for Git and rsync.
+
+To install Node, we recommend using nvm. You can install nvm e.g. by executing the following in a shell:
+```sh
+curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
+```
+and restarting your terminal. You can then install Node via `nvm install node`.
+
+#### Step 4: Install `garden-cli`
+
+Once you have the dependencies set up, install the Garden CLI via `npm`:
+
+```sh
+npm install -g garden-cli
+```
+
+To later upgrade to the newest version, run `npm install -g -U garden-cli`.
+
+
+# Minikube Instructions
+
+Garden can be used with [Minikube](https://github.com/kubernetes/minikube) on supported platforms.
+
+_NOTE: We highly recommend using Docker for Mac and Docker for Windows, for macOS and Windows respectively._
+
+## Installation
+
+For Minikube installation instructions, please see the [official guide](https://github.com/kubernetes/minikube#installation).
+
+You'll likely also need to install a driver to run the Minikube VM, please follow the
+[instructions here](https://github.com/kubernetes/minikube/blob/master/docs/drivers.md)
+and note the name of the driver you use. The driver you choose will likely vary depending on your
+OS/platform. We recommend [hyperkit](https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#hyperkit-driver)
+for macOS and [kvm2](https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#kvm2-driver) on most Linux
+platforms.
+
+Once Minikube and the appropriate driver for your OS is installed, you can start it by running:
+
+```sh
+minikube start --vm-driver= # e.g. hyperkit on macOS
+```
+
+You'll also need to have Docker (for macOS, we recommend [Docker for Mac](https://docs.docker.com/engine/installation/))
+and [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) installed.
+
+## Usage
+
+The `local-kubernetes` plugin attempts to automatically detect if it is installed and set the appropriate context
+for connecting to the local Kubernetes instance. In most cases you should not have to update your `garden.yml`,
+since it uses the `local-kubernetes` plugin by default, but you can configure it explicitly in your project-level
+`garden.yml` as follows:
+
+```yaml
+project:
+ environments:
+ - name: local
+ providers:
+ - name: local-kubernetes
+ context: minikube
+```
+
+If you happen to have installed both Minikube and a version of Docker for Mac with Kubernetes support enabled,
+`garden` will choose whichever one is configured as the current context in your `kubectl` configuration, and if neither
+is set as the current context, Docker for Mac is preferred by default.
+
+(If you're not yet familiar with Garden configuration files, see: [Configuration files](../using-garden/configuration-files.md))
+
+## Hostname
+
+Garden needs the Kubernetes instance to have a hostname. By default Garden will use `.nip.io`. If you'd
+like to use a custom hostname, you can specify it via the `ingressHostname` in the `local-kubernetes` provider config
+(see above).
+
+## Anything else?
+
+Once the above is set up, the `local-kubernetes` plugin will automatically configure everything else Garden needs to
+work. The built-in nginx ingress controller will be automatically enabled and used to route requests to services.
diff --git a/docs/basics/quick-start.md b/docs/basics/quick-start.md
new file mode 100644
index 00000000000..585b3c33080
--- /dev/null
+++ b/docs/basics/quick-start.md
@@ -0,0 +1,58 @@
+# Getting Started
+
+This guide will walk you through setting up the Garden framework. It assumes you already have Garden installed. If you don't, please check out our [installation guide](./installation.md).
+
+## Using the CLI
+
+With the CLI installed, we can now try out a few commands using the [Simple Project](../examples/simple-project.md) from our [example projects](../examples/README.md). The example project consists of a couple of simple modules, each defining one service.
+
+_Note: Check if Kubernetes is running with `kubectl version`. You should see both a `Client Version` and a `Server Version` in the response. If not, please start it up before proceeding._
+
+Clone the repo and change into the `simple-project` directory:
+
+```sh
+$ git clone https://github.com/garden-io/garden.git
+$ cd garden/examples/simple-project
+```
+
+First, let's check the environment status by running the following from the project root:
+
+```sh
+$ garden get status
+```
+
+The response tells us how the environment is configured and the status of the providers. Next, we'll build our modules with:
+
+```sh
+$ garden build
+```
+
+This builds Docker images for `go-service` and `node-service` respectively. Next, we'll deploy the services with:
+
+```sh
+$ garden deploy
+```
+
+And that's it! The `garden build` step above is actually unnecessary (only included here for clarity), since `garden deploy` will also rebuild modules as needed. The services are now running on the Garden framework. You can see for yourself by querying the `/hello` endpoint of `go-service`'s running container:
+
+```sh
+$ garden call go-service/hello-go
+```
+
+To run tests for all modules:
+
+```sh
+$ garden test
+```
+
+And if you prefer an interactive terminal that watches your project for changes and re-builds, re-deploys, and re-tests automatically, try:
+
+```sh
+$ garden dev
+```
+
+Go ahead, leave it running and change one of the files in the project, then watch it re-build.
+
+That's it for now. Check out our [Using Garden](../using-garden/README.md) section for other features like hot reload, remote clusters, integration tests, and lots more.
+
+To see how a Garden project is configured from scratch check, out the [Simple Project](../examples/simple-project.md) guide for a more in-depth presentation.
diff --git a/docs/contributing.md b/docs/contributing.md
new file mode 100644
index 00000000000..e10b3699789
--- /dev/null
+++ b/docs/contributing.md
@@ -0,0 +1,92 @@
+!!! WORK IN PROGRESS !!!
+
+# README.md
+
+This is the source code for the garden framework.
+
+Here's a high-level overview of how the whole thing works:
+
+![](schematic.jpg)
+
+The main components are plugins, projects, and the framework itself.
+
+### Projects
+
+Projects are your user's files. A project is composed of modules, which are in turn composed of services.
+
+Let's see our [hello-world](https://github.com/garden-io/garden/tree/master/examples/hello-world) example:
+ - It has two modules, `hello-container` and `hello-function`.
+ - `hello-container` contains one service, named `hello-container`.
+ - `hello-function` also contains only one service, named `hello-function`.
+
+Just as you may have many modules per project, you may have many services per module.
+
+Beware that no two services may have the same name in the project. (You could want to, for example, have a `logging` service on multiple modules. In this case you'll need a different name for each.)
+
+### Plugins
+
+Plugins are garden's way of knowing what to do about each module in a project. The way a container behaves is different than a serverless function, for example, and plugins are the way to define those different behaviours.
+
+Plugins interact with the project via the plugin interface. For more detailed information, see the README.md file in the plugin directory.
+
+### The Framework
+
+In the context of projects and plugins, what the framework does is:
+
+ 1. Watch project files for changes
+ 2. Provide plugins with context so they can operate on the project
+ 3. Keep track of state as multiple plugins work independently
+
+# Plugins
+
+To define a plugin you need to return a `GardenPlugin` object, and then declare that on the `builtinPlugins` object on the `garden/src/plugins/plugins.ts` file.
+
+`GardenPlugin` is an interface that lives on `garden/src/types/plugin/plugin.ts`:
+
+```typescript
+export interface GardenPlugin {
+ config?: object
+ configKeys?: string[]
+
+ modules?: string[]
+
+ actions?: Partial
+ moduleActions?: { [moduleType: string]: Partial }
+}
+```
+
+The way your plugin interacts with the rest of the world is via actions. These can be of three types: plugin actions, module actions, and service actions. They are:
+
+Plugin Actions:
+
+ - getEnvironmentStatus
+ - configureEnvironment
+ - destroyEnvironment
+ - getConfig
+ - setConfig
+ - deleteConfig
+ - getLoginStatus
+ - login
+ - logout
+
+Module Actions:
+
+ - parseModule
+ - getModuleBuildStatus
+ - buildModule
+ - pushModule
+ - runModule
+ - testModule
+ - getTestResult
+
+Service Actions:
+
+ - getServiceStatus
+ - deployService
+ - getServiceOutputs
+ - execInService
+ - getServiceLogs
+ - runService
+
+
+
diff --git a/docs/examples/README.md b/docs/examples/README.md
index 1fcbf2b2ebf..2d9f17faf9f 100644
--- a/docs/examples/README.md
+++ b/docs/examples/README.md
@@ -1,6 +1,17 @@
-# Examples
+# Example projects
-The source code for the examples in this section can be found in our Github repository under the
-[examples directory](https://github.com/garden-io/garden/tree/master/examples).
+## [Hello world](./hello-world.md)
-* [Simple Project](./simple-project.md)
\ No newline at end of file
+In this project we see in practice the basics of configuring dependencies, defining ports and endpoints, and setting up tests.
+
+## [Simple project](./simple-project.md)
+
+Here we see how to "gardenify" a pre-existing project, creating a project config and the individual module configs for every part.
+
+## [TLS project](./tls-project.md)
+
+This is an example project of how to set up TLS using the `mkcert` tool.
+
+## [Remote sources project](./remote-sources.md)
+
+This project demonstrates how to use multiple sources and repositories together into the same project. Although in this project we're using _remote_ sources, the same applies to using multiple _local_ repositories.
\ No newline at end of file
diff --git a/docs/examples/hello-world.md b/docs/examples/hello-world.md
new file mode 100644
index 00000000000..64ae919e71c
--- /dev/null
+++ b/docs/examples/hello-world.md
@@ -0,0 +1,127 @@
+# Hello World
+
+In this example, we'll have a practical look at the main characteristics of a Garden project:
+
+- Dependencies
+- Ports, endpoints, and health check settings
+- Tests
+
+This project contains four configuration files. [This one](https://github.com/garden-io/garden/tree/master/examples/hello-world/garden.yml) for project-wide settings, and three separate ones for each of the modules: [`hello-container`](https://github.com/garden-io/garden/tree/master/examples/hello-world/services/hello-container/garden.yml), [`hello-function`](https://github.com/garden-io/garden/tree/master/examples/hello-world/services/hello-function/garden.yml), and [`hello-npm-package`](https://github.com/garden-io/garden/tree/master/examples/hello-world/libraries/hello-npm-package/garden.yml).
+
+# Configuring dependencies
+
+There are three main types of dependencies we'll be dealing with: build dependencies, runtime dependencies, and test dependencies.
+
+You can think of build dependencies as libraries. For example, our `hello-world/services/hello-container/app.js` file in the `hello-container` module requires the `hello-npm-package` to be imported:
+
+```js
+const hello = require("./libraries/hello-npm-package")
+```
+
+For `hello-npm-package` to be imported by `hello-container`, of course, it needs to be built first. Thus, we specify it as a build dependency. Take a look at its `config.yml`:
+
+```yml
+ build:
+ dependencies:
+ - name: hello-npm-package
+ copy:
+ - source: "./"
+ target: libraries/hello-npm-package/
+```
+
+_Note: `source` refers to the path on the dependency module being specified, and `target` refers to where it will be accessed from by the dependant module (picture it as a mount directory)._
+
+Runtime dependencies, on the other hand, are irrelevant at build time, but required for execution. For example, as we can see on the `app.js` file, the `hello-container` module depends on `hello-function` being up and running:
+
+```js
+const functionEndpoint = process.env.GARDEN_SERVICES_HELLO_FUNCTION_ENDPOINT
+```
+
+So let's see how to make sure `hello-function` is running before `hello-container`:
+
+```yaml
+module:
+ description: Hello world container service
+ type: container
+ name: hello-container
+ services:
+ ...
+ dependencies:
+ - hello-function
+```
+
+Test dependencies will be covered further ahead.
+
+# Defining ports, endpoints, and health checks
+
+Before we can define our endpoints and health checks we'll to define the ports we'll be working with. For example, below we'll assign the name `http` to port number `8080`:
+
+```yml
+module:
+ description: Hello world container service
+ ...
+ services:
+ ...
+ ports:
+ - name: http
+ containerPort: 8080
+```
+
+Now let's use that port and a path to define an ingress endpoint for the service to expose:
+
+```yml
+module:
+ description: Hello world container service
+ ...
+ services:
+ ...
+ ports:
+ - name: http
+ containerPort: 8080
+ endpoints:
+ - path: /hello
+ port: http
+```
+
+Lastly, health checks currently have three possible types: `httpGet`, `command`, and `tcpPort`. They're specified in the [Config Files Reference](../reference/config-files-reference.md).
+
+For the Hello World project, we'll use the first one. This `healthCheck` endpoint will be pinged periodically to ensure that the service is still healthy. Here's what it looks like:
+
+```yml
+module:
+ description: Hello world container service
+ ...
+ services:
+ ...
+ ports:
+ - name: http
+ containerPort: 8080
+ endpoints:
+ - path: /hello
+ port: http
+ healthCheck:
+ httpGet:
+ path: /_ah/health
+ port: http
+```
+
+# Setting up tests
+
+Since Garden is language-agnostic, there aren't any low level requirements about how tests should be arranged. The only requirements are:
+
+- You must be able to execute a command to run your tests.
+- Any non-zero exit codes returned by the command mean your tests have failed, and zero indicates the tests are passing.
+
+The only difference between unit tests and integration tests, then, are that to run the latter you might need other services to be up and running as well. You can specify them as test dependencies.
+
+Here's what it looks like in practice:
+
+```yml
+ tests:
+ - name: unit
+ command: [npm, test]
+ - name: integ
+ command: [npm, run, integ]
+ dependencies:
+ - hello-function
+```
\ No newline at end of file
diff --git a/docs/examples/remote-sources.md b/docs/examples/remote-sources.md
new file mode 100644
index 00000000000..223ff010c02
--- /dev/null
+++ b/docs/examples/remote-sources.md
@@ -0,0 +1,106 @@
+# Remote sources example project
+
+This example demonstrates how you can import remote sources and remote modules into a Garden project.
+
+_Note: To use multiple local repositories—not remote, as this article describes—simply utilize `file:///my/other/project/path` in the `repositoryUrl` field described below._
+
+Important concepts:
+
+> Remote _source_: A collection of one or more Garden modules that live in a repository different from the main project repository. The `garden.yml` config files are co-located with the modules in the remote repository.
+
+> Remote _module_: The remote source code for a single Garden module. In this case, the `garden.yml` config file is stored in the main project repository while the module code itself is in the remote repository.
+
+## About
+
+This project is the same as the [multi-container example](https://github.com/garden-io/garden/tree/master/examples/multi-container)—except that in this case the services live in their own repositories. The repositories are:
+
+* [Database services](https://github.com/garden-io/garden-example-remote-sources-db-services) (contains the Postgres and Redis services)
+* [Web services](https://github.com/garden-io/garden-example-remote-sources-web-services) (contains the Python Vote web service and the Node.js Result web service)
+* [Java worker module](https://github.com/garden-io/garden-example-remote-module-jworker)
+
+_This split is pretty arbitrary and doesn't necessarily reflect how you would normally separate services into different repositories._
+
+## Usage
+
+This project doesn't require any setup and can be deployed right away. If this is your first time working with this project, Garden will start by fetching the remote source code:
+```sh
+garden deploy
+```
+Garden will continue to use the version originally downloaded. Use the `update-remote sources|modules|all` command to fetch the latest version of your remote sources and modules:
+```sh
+garden update-remote modules jworker
+```
+If you however change the repository URL of your remote source or module (e.g. switch to a different tag or branch), Garden will automatically fetch the correct version.
+
+It's also possible to link remote sources and modules to a local directory with the `link source|module` command. This is useful for when you want to try out changes to the remote source without having to push them to the remote repository. In this case, you clone the remote source to a local directory and link to its path:
+```sh
+garden link source web-services path/to/web-services
+```
+Now Garden will read the module from its local path, and changes you make will be visible immediately.
+
+Use the `unlink source|module` command to unlink it again, and revert to the module version the repository URL points to:
+```sh
+garden unlink source web-services
+```
+
+## Further reading
+
+### Project structure
+
+Looking at the project structure, you'll notice that the project doesn't contain any code outside the `garden.yml` config files. Rather, the config files themselves contain the URLs to the remote repositories.
+
+```sh
+$ tree
+.
+├── README.md
+├── garden.yml
+└── services
+ └── jworker
+ └── garden.yml
+
+2 directories, 3 files
+```
+
+### Configuring remote sources
+
+For this project, we want to import the database and web services as remote _sources_. This means that the entire source code gets embedded into the project and treated just like our other project files. As usual, Garden will scan the project for `garden.yml` files, and include all modules it finds.
+
+To import remote sources, we add them under the `sources` key in the top-level project configuration file:
+
+```yaml
+project:
+ name: remote-sources
+ sources:
+ - name: web-services
+ repositoryUrl: https://github.com/garden-io/garden-example-remote-sources-web-services.git#v0.1.0
+ - name: db-services
+ repositoryUrl: https://github.com/garden-io/garden-example-remote-sources-db-services.git#v0.1.0
+```
+
+> Remote repository URLs must contain a hash part that references a specific branch or tag, e.g. `https://github.com/org/repo.git/#my-tag-or-branch`. The remote repositories used in this example all contain the tag `v0.1.0`. Read more about Git tagging [here](https://git-scm.com/book/en/v2/Git-Basics-Tagging).
+
+### Configuring remote modules
+
+Additionally, we want to import the Java worker as a remote _module_. In that case, Garden assumes that the remote repository contains the source code for a single Garden module. Furthermore, the `garden.yml` config file for that module is kept in the main project repo:
+```sh
+$ tree services
+services
+└── jworker
+ └── garden.yml
+
+1 directory, 1 file
+```
+and the path to the repository URL is added under the `repositoryUrl` key like so:
+```yaml
+module:
+ description: worker
+ type: container
+ name: jworker
+ repositoryUrl: https://github.com/garden-io/garden-example-remote-module-jworker.git#v0.1.0
+ services:
+ - name: javaworker
+ dependencies:
+ - redis
+```
+
+Note that a project can contain its own modules and also import remote sources and modules.
\ No newline at end of file
diff --git a/docs/examples/simple-project.md b/docs/examples/simple-project.md
index 60b9842968c..cf004291f8e 100644
--- a/docs/examples/simple-project.md
+++ b/docs/examples/simple-project.md
@@ -1,6 +1,6 @@
# Simple Project
-In this guide, we'll walk you through configuring a simple project to run on the Garden framework. The project will consist of two Dockerized web services that communicate with one another, along with unit and integration tests.
+In this guide, we'll walk you through configuring a simple project to run on Garden. The project will consist of two Dockerized web services that communicate with one another, along with unit and integration tests.
In what follows you'll learn how to:
@@ -13,23 +13,23 @@ In what follows you'll learn how to:
## Before you get started
-This tutorial assumes that you have already have a running [installation of Garden](../introduction/getting-started.md).
+This tutorial assumes that you have already have a running [installation of Garden](../basics/installation.md).
## Clone the example repo
-The code for this tutorial can be found in our Github repository under the [examples directory](https://github.com/garden-io/garden/tree/master/examples). We'll use the [simple-project-start](https://github.com/garden-io/garden/tree/master/examples/simple-project-start/) example and work our way from there. The complete version is under [simple-project](https://github.com/garden-io/garden/tree/master/examples/simple-project).
+The code for this tutorial can be found in our Github repository under the [examples directory](https://github.com/garden-io/garden/tree/master/examples). We'll use the [simple-project-start](https://github.com/garden-io/garden/tree/master/examples/simple-project-start/) example and work our way from there. The final version is under [simple-project](https://github.com/garden-io/garden/tree/master/examples/simple-project).
First, let's clone the examples repo, change into the directory, and take a look inside:
```sh
-$ git clone https://github.com/garden-io/garden/examples.git
+$ git clone https://github.com/garden-io/garden.git
$ cd garden/examples/simple-project-start
$ tree .
.
└── services
├── go-service
- │ ├── Dockerfile
- │ └── webserver
- │ └── main.go
+ │ ├── Dockerfile
+ │ └── webserver
+ │ └── main.go
└── node-service
├── Dockerfile
├── app.js
@@ -41,13 +41,13 @@ $ tree .
5 directories, 7 files
```
-As you can see the project consists of two super simple services and their accompanying Dockerfiles. One of the core tenets of multi-service backends is being able to pick the right tool for the job, and therefore we have a Node.js service and a Golang service, that we can pretend have different responsibilities.
+As you can see, the project consists of two super simple services and their accompanying Dockerfiles. One of the core tenets of multi-service backends is being able to pick the right tool for the job, and therefore we have a Node.js service and a Go service, that we can pretend have different responsibilities.
The task at hand is to configure these services so that they can run on the Garden framework.
## Project-wide configuration
-To begin with, every project needs a project-wide `garden.yml` [configuration file](../guides/configuration.md#Config) at the root level. There we define, among other things, the name of the project, and the [providers](../guides/glossary.md#Provider) used for each [plugin](../guides/glossary.md#Plugin) the project requires.
+To begin with, every project needs a project-wide `garden.yml` [configuration file](../using-garden/configuration-files.md) at the root level. There we define, among other things, the name of the project, and the [providers](../reference/glossary.md#Provider) the project requires.
Let's go ahead and create one:
@@ -66,11 +66,11 @@ project:
- name: local-kubernetes
```
-Above, we've specified the name of our project and configured it to use the local-kubernetes plugin for local development. Note, that this file must be located in the project root directory.
+Above, we've specified the name of our project and configured it to use the local-kubernetes provider for local development. Note that this file must be located in the project root directory.
## Module configuration
-Now, let's turn to our services. Services live inside [modules](../guides/glossary.md#Module), and each module has it's own `garden.yml` configuration file.
+Now, let's turn to our services. Services live inside [modules](../reference/glossary.md#Module), and each module has it's own `garden.yml` configuration file.
We'll start with the module for the `node-service`:
@@ -92,7 +92,7 @@ By running the `scan` command we can see that Garden detects our module config:
$ garden scan
- name: node-service
type: container
- path: /Users/eysi/code/simple-project/services/node-service
+ path: /Users/username/code/simple-project/services/node-service
description: Node service container
version:
versionString: 2c8818986d-1528373640
@@ -112,18 +112,19 @@ module:
ports:
- name: http
containerPort: 8080
- endpoints:
+ ingresses:
- path: /hello-node
port: http
```
-The [services](../guides/configuration.md#Services) directive is specific to container modules, and defines the services exposed by the module. In this case, our containerized Node.js server. The sub-directives tell Garden how to start the service and which endpoints to expose.
+The [services](../using-garden/configuration-files.md#Services) directive is specific to container modules, and defines the services exposed by the module. In this case, our containerized Node.js server. The sub-directives tell Garden how to start the service and which ingress endpoints to expose.
## Deploying
-With this configuration we're almost ready to deploy. First, we'll need to create a user namespace for our environment with the login command:
+With this configuration we're almost ready to deploy. First, we'll need to make sure the environment is ready, by
+running the init command:
```sh
-$ garden login
+$ garden init
```
Garden can now deploy our service to a local Kubernetes cluster:
@@ -132,7 +133,7 @@ Garden can now deploy our service to a local Kubernetes cluster:
$ garden deploy
```
-To verify that everything is working, we can call the service at the `/hello-node` endpoint defined in `/services/node-service/app.js`:
+To verify that everything is working, we can call the service at the `/hello-node` ingress defined in `/services/node-service/app.js`:
```sh
$ garden call node-service/hello-node
@@ -143,7 +144,7 @@ $ garden call node-service/hello-node
Hello from Node server!
```
-In a similar manner, we create a config file for our `go-service`:
+In a similar manner, we create a config file for our `go-service` with
```sh
$ touch services/go-service/garden.yml
@@ -160,7 +161,7 @@ module:
ports:
- name: http
containerPort: 80
- endpoints:
+ ingresses:
- path: /hello-go
port: http
```
@@ -187,7 +188,7 @@ Looks good! Let's take stock:
## Inter-service communication
-Calling our `go-service` from our `node-service` is straightforward from within the application code. Crack open `services/node-service/app.js` with your favorite editor and add the following:
+Calling the `go-service` from the `node-service` is straightforward from within the application code. Open `services/node-service/app.js` with your favorite editor and add the following:
```javascript
const request = require('request-promise')
@@ -290,7 +291,7 @@ module:
ports:
- name: http
containerPort: 8080
- endpoints:
+ ingresses:
- path: /
port: http
dependencies:
@@ -306,4 +307,4 @@ module:
And that's it! Our services are up and running locally, dependencies are resolved, and tests are ready to run.
-Check out some of our other [Guides](../guides/README.md) for more of an in-depth look at the Garden framework.
\ No newline at end of file
+Check out some of our other [Example projects](./examples/README.md) for more of an in-depth look.
\ No newline at end of file
diff --git a/docs/examples/tls-project.md b/docs/examples/tls-project.md
new file mode 100644
index 00000000000..610c7f985a8
--- /dev/null
+++ b/docs/examples/tls-project.md
@@ -0,0 +1,58 @@
+# Local TLS example project
+
+This project shows how you can configure a TLS certificate to use for local development on Kubernetes.
+
+For the example to work you need to configure a local certificate authority (CA) on your computer for development. We'll use
+[mkcert](https://github.com/FiloSottile/mkcert) for this purpose.
+
+## Setup
+
+### Step 1 - Install mkcert
+
+If you don't have mkcert installed, follow the instructions [here](https://github.com/FiloSottile/mkcert#installation).
+
+### Step 2 - Generate a certificate
+
+After you've run `mkcert -install`, run
+
+```sh
+mkcert garden.dev '*.garden.dev'
+```
+
+_Note: You may choose another hostname if you prefer, but you'll need to update the project `garden.yml` accordingly._
+
+### Step 3 - Configure the certificate in your Kubernetes installation
+
+Create a Kubernetes Secret with your generated certificate and key.
+
+```sh
+kubectl create secret tls tls-garden-dev --key garden.dev+1-key.pem --cert garden.dev+1.pem
+```
+
+_The filenames above will be different if you used a different hostname._
+
+### Step 4 - Configure the hostname in your hosts file
+
+Add the `garden.dev` hostname to the hosts file on your machine, and have it point to the IP of your local cluster.
+If you use Docker for Desktop, the IP will be `127.0.0.1`. If you use minikube, you can get the IP by running
+`minikube ip`.
+
+We recommend using the [hosts](https://github.com/alphabetum/hosts) tool (or something similar) to modify your hosts
+file, but you may also edit it directly (it's at `/etc/hosts` on most platforms).
+
+## Usage
+
+Once you've completed the above, you can deploy the example project and the exposed ingress endpoints will be
+secured with TLS!
+
+Deploy the project:
+
+```sh
+garden deploy
+```
+
+And then try sending a simple request using:
+
+```sh
+garden call node-service/hello
+```
\ No newline at end of file
diff --git a/docs/introduction/getting-started.md b/docs/introduction/getting-started.md
deleted file mode 100644
index e583d55819a..00000000000
--- a/docs/introduction/getting-started.md
+++ /dev/null
@@ -1,162 +0,0 @@
-# Getting Started
-
-This guide will walk you through setting up the Garden framework.
-
-## Installation
-
-### macOS
-
-For Mac, we recommend the following steps to install Garden. You can also follow the manual installation
-steps below if you prefer.
-
-#### Step 1: Install homebrew
-
-If you haven't already set up homebrew, please follow [their instructions](https://brew.sh/) to set it up.
-
-#### Step 2: Docker and local Kubernetes
-
-To install Docker, Kubernetes and kubectl, we strongly recommend Docker for Mac (edge version).
-
-_Note: you need to install the **edge version** of Docker for Mac in
-order to enable Kubernetes support._
-
-Once installed, open the Docker for Mac preferences, go to the Kubernetes section,
-tick `Enable Kubernetes` and save. Please refer to their
-[installation guide](https://docs.docker.com/engine/installation/) for details.
-
-Alternatively, you can use Minikube. We generally find it less stable and more hassle to
-configure and use, but we do fully support it on Mac if you have it running. Please look at our
-[Minikube guide](../guides/minikube.md) for details.
-
-#### Step 3: Install `garden-cli`
-
-We have a Homebrew tap and package that you can use to easily install `garden-cli` and all dependencies:
-
-```sh
-brew tap garden-io/garden
-brew install garden-cli
-```
-
-To later upgrade to the newest version, simply run `brew update` and then `brew upgrade garden-cli`
-(or `brew upgrade` to upgrade all your Homebrew packages).
-
-### Windows
-
-You can run garden on Windows 10 Pro or Enterprise editions. Follow these instructions to get started.
-
-#### Step 1: Chocolatey
-
-If you haven't already, install the [Chocolatey](https://chocolatey.org) package manager.
-
-#### Step 2: Enable Hyper-V
-
-This is required for _Docker for Windows_ to run. Open PowerShell as an administrator and run:
-
-```powershell
-Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
-```
-
-This will require a restart.
-
-#### Step 3: Docker for Windows
-
-Install the Edge version of [Docker for Windows](https://www.docker.com/docker-windows).
-
-Once installed, open the Docker for Windows settings, go to the Kubernetes section,
-tick `Enable Kubernetes` and save. Please refer to their
-[installation guide](https://docs.docker.com/engine/installation/) for details.
-
-#### Step 4: Install dependencies
-
-Open PowerShell as an administrator and run:
-
-```powershell
-# install choco packages (note: python is needed to build some dependencies)
-choco install -y git nodejs rsync kubernetes-helm
-
-# install Stern (currently not available as a choco package)
-[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
-Invoke-WebRequest -Uri "https://github.com/wercker/stern/releases/download/1.7.0/stern_windows_amd64.exe" -OutFile "$Env:SystemRoot\system32\stern.exe"
-
-# install build tools so that node-gyp works
-npm install --global --production windows-build-tools
-npm config set msvs_version 2015 --global
-```
-
-#### Step 5: Install `garden-cli`
-
-Once you have the dependencies set up, open a new PowerShell and install the Garden CLI via `npm`:
-
-```powershell
-npm install -g garden-cli
-```
-
-To later upgrade to the newest version, run `npm install -g -U garden-cli`.
-
-### Linux / manual installation
-
-You need the following dependencies on your local machine to use Garden:
-
-* Node.js >= 8.x
-* [Docker](https://docs.docker.com/)
-* Git
-* rsync
-* [Helm](https://github.com/kubernetes/helm)
-* Local installation of Kubernetes and kubectl
-
-#### Step 1: Docker
-
-To install Docker, please follow the instructions in the [official documentation](https://docs.docker.com/install/).
-
-#### Step 2: Local Kubernetes
-
-For local Kubernetes, you can use [Minikube](https://github.com/kubernetes/minikube). Please see our
-[Minikube guide](../guides/minikube.md) for instructions.
-
-#### Step 3: Install other dependencies
-
-Use your preferred method or package manager to install `node` (version 8.x or higher), `git`, `rsync` and
-[Helm](https://github.com/kubernetes/helm).
-
-#### Step 4: Install `garden-cli`
-
-Once you have the dependencies set up, install the Garden CLI via `npm`:
-
-```sh
-npm install -g garden-cli
-```
-
-To later upgrade to the newest version, run `npm install -g -U garden-cli`.
-
-## Using the CLI
-
-With the CLI installed, we can now try out a few commands using the [hello-world](https://github.com/garden-io/garden/examples/tree/master/simple-project) project from our Github [examples repository](https://github.com/garden-io/garden/examples). The example consists of a a couple of simple services.
-
-_Note: check if Kubernetes is running with `kubectl version`. You should see both a `Client Version` and a `Server Version` in the response. If not, please start it up before proceeding._
-
-Clone the repo and change into the `hello-world` directory:
-
-```sh
-$ git clone https://github.com/garden-io/garden/examples.git
-$ cd garden/examples/hello-world
-```
-
-First, let's check the environment status by running the following from the project root:
-
-```sh
-$ garden status
-```
-
-The response tells us how the environment is configured and the status of the providers. Next, we'll deploy the services with:
-
-```sh
-$ garden deploy
-```
-
-And that's it! The services are now running on the Garden framework. You can see for yourself by querying the `/hello` endpoint of the container with:
-
-```sh
-$ garden call hello-container/hello
-```
-
-Check out our [Commands guide](../guides/commands.md) for other features like auto-reload, streaming service logs, running tests and lots more, or see how a Garden project is configured from scratch in our [Simple Project](../guides/simple-project.md) guide.
diff --git a/docs/outline.md b/docs/outline.md
new file mode 100644
index 00000000000..01e5899aef0
--- /dev/null
+++ b/docs/outline.md
@@ -0,0 +1,164 @@
+# DELETE THIS FILE ONCE NEW DOCS ARE COMPLETE
+
+# README
+
+- Logo
+- Status
+- Features
+- Usage
+- Contributing
+- Motivation
+- Acknowledgements
+
+# Basics
+
+- Chapter outline
+
+## Installation
+
+- Intro
+- macOS
+- Windows
+- Linux (manual installation)
+- Minikube Instructions
+
+## Quick Start
+
+- Using the CLI
+ - garden get status
+ - garden build
+ - garden deploy
+ - garden test
+ - garden dev
+ - garden call
+
+## Concepts
+
+- How Garden works
+- Projects vs. modules vs. services
+- The build → test → deploy sequence
+- How inter-service communication works
+- An overview of the providers system
+- Hot reload
+- Projects with multiple and/or remote repositories
+
+# Using Garden
+
+
+## Features and usage
+
+- File watching, building, and deploying
+- Testing
+- Hot reload
+- TLS
+- Remote Clusters
+- Multiple/remote repos
+
+## Configuration files
+
+- Introduction
+- Project configs
+- Module configs
+- Services
+- Tests
+
+## Providers
+
+- How providers work
+- What's expected of providers
+- Provider actions
+
+# Example projects
+
+
+## Hello world
+
+
+## Simple project
+
+
+## TLS project
+
+
+## Socks shop or Coolstore set-up
+
+
+## Something heavy on serverless
+
+
+# Guides
+
+
+## Projects with multiple remote and local repos
+
+
+## Configuring hot reload
+
+
+## Setting up TLS
+
+
+## How to "gardenify" an existing project
+
+
+## Migration guide from Docker Compose to Garden
+
+
+## Getting started with Kubernetes using the Garden framework
+
+
+# Contributing to Garden
+
+
+# Reference
+
+
+## Glossary
+
+- When you get here to through the relevant places in the docs and add links to the definitions.
+
+
+## Commands Reference
+
+
+## Config Files Reference
+
+
+## Provider Configurations
+
+
+### Container
+
+
+### Kubernetes
+
+
+### OpenFaaS
+
+
+### Local Kubernetes
+
+
+### Google Cloud Functions
+
+
+## Module Configurations
+
+
+### Container
+
+
+### Kubernetes
+
+
+### OpenFaaS
+
+
+### Local Kubernetes
+
+
+### Google Cloud Functions
+
+
+# FAQs
+
diff --git a/docs/reference/README.md b/docs/reference/README.md
index aa067ae31dc..f4caf28db90 100644
--- a/docs/reference/README.md
+++ b/docs/reference/README.md
@@ -1,5 +1,5 @@
# Reference
-* [Commands](./commands.md)
-* [Config](./config.md)
-* [Template strings](./template-strings.md)
+* [Glossary](./glossary.md)
+* [Commands Reference](./command-reference.md)
+* [Config Files Reference](./config-files-reference.md)
diff --git a/docs/reference/command-reference.md b/docs/reference/command-reference.md
new file mode 100644
index 00000000000..e3c4fde7668
--- /dev/null
+++ b/docs/reference/command-reference.md
@@ -0,0 +1,716 @@
+## Garden CLI commands
+
+Below is a list of Garden CLI commands and usage information.
+
+The commands should be run in a Garden project root, and are always scoped to that project.
+
+Note: You can get a list of commands in the CLI by running `garden -h/--help`,
+and detailed help for each command using `garden -h/--help`
+
+##### Global options
+
+The following option flags can be used with any of the CLI commands:
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--root` | `-r` | string | Override project root directory (defaults to working directory).
+ | `--silent` | `-s` | boolean | Suppress log output.
+ | `--env` | `-e` | string | The environment (and optionally namespace) to work against
+ | `--loglevel` | `-l` | `error` `warn` `info` `verbose` `debug` `silly` `0` `1` `2` `3` `4` `5` | Set logger level. Values can be either string or numeric and are prioritized from 0 to 5 (highest to lowest) as follows: error: 0, warn: 1, info: 2, verbose: 3, debug: 4, silly: 5
+ | `--output` | `-o` | `json` `yaml` | Output command result in specified format (note: disables progress logging).
+
+### garden build
+
+Build your modules.
+
+Builds all or specified modules, taking into account build dependency order.
+Optionally stays running and automatically builds modules if their source (or their dependencies' sources) change.
+
+Examples:
+
+ garden build # build all modules in the project
+ garden build my-module # only build my-module
+ garden build --force # force rebuild of modules
+ garden build --watch # watch for changes to code
+
+##### Usage
+
+ garden build [module] [options]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `module` | No | Specify module(s) to build. Use comma separator to specify multiple modules.
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--force` | | boolean | Force rebuild of module(s).
+ | `--watch` | `-w` | boolean | Watch for changes in module(s) and auto-build.
+
+### garden call
+
+Call a service ingress endpoint.
+
+This command resolves the deployed ingress endpoint for the given service and path, calls the given endpoint and
+outputs the result.
+
+Examples:
+
+ garden call my-container
+ garden call my-container/some-path
+
+Note: Currently only supports simple GET requests for HTTP/HTTPS ingresses.
+
+##### Usage
+
+ garden call
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `serviceAndPath` | Yes | The name of the service(s) to call followed by the ingress path (e.g. my-container/somepath).
+
+### garden create project
+
+Creates a new Garden project.
+
+The 'create project' command walks the user through setting up a new Garden project and
+generates scaffolding based on user input.
+
+Examples:
+
+ garden create project # creates a new Garden project in the current directory (project name defaults to
+ directory name)
+ garden create project my-project # creates a new Garden project in my-project directory
+ garden create project --module-dirs=path/to/modules1,path/to/modules2
+ # creates a new Garden project and looks for pre-existing modules in the modules1 and modules2 directories
+ garden create project --name my-project
+ # creates a new Garden project in the current directory and names it my-project
+
+##### Usage
+
+ garden create project [project-dir] [options]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `project-dir` | No | Directory of the project. (Defaults to current directory.)
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--module-dirs` | | array:path | Relative path to modules directory. Use comma as a separator to specify multiple directories
+ | `--name` | | string | Assigns a custom name to the project. (Defaults to name of the current directory.)
+
+### garden create module
+
+Creates a new Garden module.
+
+Creates a new Garden module of the given type
+
+Examples:
+
+ garden create module # creates a new module in the current directory (module name defaults to directory name)
+ garden create module my-module # creates a new module in my-module directory
+ garden create module --type=container # creates a new container module
+ garden create module --name=my-module # creates a new module in current directory and names it my-module
+
+##### Usage
+
+ garden create module [module-dir] [options]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `module-dir` | No | Directory of the module. (Defaults to current directory.)
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--name` | | string | Assigns a custom name to the module. (Defaults to name of the current directory.)
+ | `--type` | | `container` `google-cloud-function` `npm-package` | Type of module.
+
+### garden delete secret
+
+Delete a secret from the environment.
+
+Returns with an error if the provided key could not be found by the provider.
+
+Examples:
+
+ garden delete secret kubernetes somekey
+ garden del secret local-kubernetes some-other-key
+
+##### Usage
+
+ garden delete secret
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `provider` | Yes | The name of the provider to remove the secret from.
+ | `key` | Yes | The key of the configuration variable. Separate with dots to get a nested key (e.g. key.nested).
+
+### garden delete environment
+
+Deletes a running environment.
+
+This will trigger providers to clear up any deployments in a Garden environment and reset it.
+When you then run `garden init`, the environment will be reconfigured.
+
+This can be useful if you find the environment to be in an inconsistent state, or need/want to free up
+resources.
+
+##### Usage
+
+ garden delete environment
+
+### garden delete service
+
+Deletes a running service.
+
+Deletes (i.e. un-deploys) the specified services. Note that this command does not take into account any
+services depending on the deleted service, and might therefore leave the project in an unstable state.
+Running `garden deploy` will re-deploy any missing services.
+
+Examples:
+
+ garden delete service my-service # deletes my-service
+
+##### Usage
+
+ garden delete service
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `service` | Yes | The name of the service(s) to delete. Use comma as separator to specify multiple services.
+
+### garden deploy
+
+Deploy service(s) to your environment.
+
+
+ Deploys all or specified services, taking into account service dependency order.
+ Also builds modules and dependencies if needed.
+
+ Optionally stays running and automatically re-builds and re-deploys services if their module source
+ (or their dependencies' sources) change.
+
+ Examples:
+
+ garden deploy # deploy all modules in the project
+ garden deploy my-service # only deploy my-service
+ garden deploy --force # force re-deploy of modules, even if they're already deployed
+ garden deploy --watch # watch for changes to code
+ garden deploy --env stage # deploy your services to an environment called stage
+
+
+##### Usage
+
+ garden deploy [service] [options]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `service` | No | The name of the service(s) to deploy (skip to deploy all services). Use comma as separator to specify multiple services.
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--force` | | boolean | Force redeploy of service(s).
+ | `--force-build` | | boolean | Force rebuild of module(s).
+ | `--watch` | `-w` | boolean | Watch for changes in module(s) and auto-deploy.
+
+### garden dev
+
+Starts the garden development console.
+
+
+ The Garden dev console is a combination of the `build`, `deploy` and `test` commands.
+ It builds, deploys and tests all your modules and services, and re-builds, re-deploys and re-tests
+ as you modify the code.
+
+ Examples:
+
+ garden dev
+
+
+##### Usage
+
+ garden dev
+
+### garden exec
+
+Executes a command (such as an interactive shell) in a running service.
+
+Finds an active container for a deployed service and executes the given command within the container.
+Supports interactive shells.
+
+_NOTE: This command may not be supported for all module types._
+
+Examples:
+
+ garden exec my-service /bin/sh # runs a shell in the my-service container
+
+##### Usage
+
+ garden exec
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `service` | Yes | The service to exec the command in.
+ | `command` | Yes | The command to run.
+
+### garden get secret
+
+Get a secret from the environment.
+
+Returns with an error if the provided key could not be found.
+
+Examples:
+
+ garden get secret kubernetes somekey
+ garden get secret local-kubernetes some-other-key
+
+##### Usage
+
+ garden get secret
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `provider` | Yes | The name of the provider to read the secret from.
+ | `key` | Yes | The key of the configuration variable.
+
+### garden get status
+
+Outputs the status of your environment.
+
+
+##### Usage
+
+ garden get status
+
+### garden init
+
+Initialize system, environment or other runtime components.
+
+This command needs to be run before first deploying a Garden project, and occasionally after updating Garden,
+plugins or project configuration.
+
+Examples:
+
+ garden init
+ garden init --force # runs the init flows even if status checks report that the environment is ready
+
+##### Usage
+
+ garden init [options]
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--force` | | boolean | Force initalization of environment, ignoring the environment status check.
+
+### garden link source
+
+Link a remote source to a local directory.
+
+After linking a remote source, Garden will read it from its local directory instead of
+from the remote URL. Garden can only link remote sources that have been declared in the project
+level garden.yml config.
+
+Examples:
+
+ garden link source my-source path/to/my-source # links my-source to its local version at the given path
+
+##### Usage
+
+ garden link source
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `source` | Yes | Name of the source to link as declared in the project config.
+ | `path` | Yes | Path to the local directory that containes the source.
+
+### garden link module
+
+Link a module to a local directory.
+
+After linking a remote module, Garden will read the source from the module's local directory instead of from
+the remote URL. Garden can only link modules that have a remote source,
+i.e. modules that specifiy a repositoryUrl in their garden.yml config file.
+
+Examples:
+
+ garden link module my-module path/to/my-module # links my-module to its local version at the given path
+
+##### Usage
+
+ garden link module
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `module` | Yes | Name of the module to link.
+ | `path` | Yes | Path to the local directory that containes the module.
+
+### garden logs
+
+Retrieves the most recent logs for the specified service(s).
+
+Outputs logs for all or specified services, and optionally waits for news logs to come in.
+
+Examples:
+
+ garden logs # prints latest logs from all services
+ garden logs my-service # prints latest logs for my-service
+ garden logs -t # keeps running and streams all incoming logs to the console
+
+##### Usage
+
+ garden logs [service] [options]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `service` | No | The name of the service(s) to logs (skip to logs all services). Use comma as separator to specify multiple services.
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--tail` | `-t` | boolean | Continuously stream new logs from the service(s).
+
+### garden publish
+
+Build and publish module(s) to a remote registry.
+
+Publishes built module artifacts for all or specified modules.
+Also builds modules and dependencies if needed.
+
+Examples:
+
+ garden publish # publish artifacts for all modules in the project
+ garden publish my-container # only publish my-container
+ garden publish --force-build # force re-build of modules before publishing artifacts
+ garden publish --allow-dirty # allow publishing dirty builds (which by default triggers error)
+
+##### Usage
+
+ garden publish [module] [options]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `module` | No | The name of the module(s) to publish (skip to publish all modules). Use comma as separator to specify multiple modules.
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--force-build` | | boolean | Force rebuild of module(s) before publishing.
+ | `--allow-dirty` | | boolean | Allow publishing dirty builds (with untracked/uncommitted changes).
+
+### garden run module
+
+Run an ad-hoc instance of a module.
+
+This is useful for debugging or ad-hoc experimentation with modules.
+
+Examples:
+
+ garden run module my-container # run an ad-hoc instance of a my-container container and attach to it
+ garden run module my-container /bin/sh # run an interactive shell in a new my-container container
+ garden run module my-container --i=false /some/script # execute a script in my-container and return the output
+
+##### Usage
+
+ garden run module [command] [options]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `module` | Yes | The name of the module to run.
+ | `command` | No | The command to run in the module.
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--interactive` | | boolean | Set to false to skip interactive mode and just output the command result.
+ | `--force-build` | | boolean | Force rebuild of module before running.
+
+### garden run service
+
+Run an ad-hoc instance of the specified service
+
+This can be useful for debugging or ad-hoc experimentation with services.
+
+Examples:
+
+ garden run service my-service # run an ad-hoc instance of a my-service and attach to it
+
+##### Usage
+
+ garden run service [options]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `service` | Yes | The service to run
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--force-build` | | boolean | Force rebuild of module
+
+### garden run test
+
+Run the specified module test.
+
+This can be useful for debugging tests, particularly integration/end-to-end tests.
+
+Examples:
+
+ garden run test my-module integ # run the test named 'integ' in my-module
+ garden run test my-module integ --i=false # do not attach to the test run, just output results when completed
+
+##### Usage
+
+ garden run test [options]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `module` | Yes | The name of the module to run.
+ | `test` | Yes | The name of the test to run in the module.
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--interactive` | | boolean | Set to false to skip interactive mode and just output the command result.
+ | `--force-build` | | boolean | Force rebuild of module before running.
+
+### garden scan
+
+Scans your project and outputs an overview of all modules.
+
+
+##### Usage
+
+ garden scan
+
+### garden set secret
+
+Set a secret value for a provider in an environment.
+
+These secrets are handled by each provider, and may for example be exposed as environment
+variables for services or mounted as files, depending on how the provider is implemented
+and configured.
+
+_Note: The value is currently always stored as a string._
+
+Examples:
+
+ garden set secret kubernetes somekey myvalue
+ garden set secret local-kubernets somekey myvalue
+
+##### Usage
+
+ garden set secret
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `provider` | Yes | The name of the provider to store the secret with.
+ | `key` | Yes | A unique identifier for the secret.
+ | `value` | Yes | The value of the secret.
+
+### garden test
+
+Test all or specified modules.
+
+
+ Runs all or specified tests defined in the project. Also builds modules and dependencies,
+ and deploy service dependencies if needed.
+
+ Optionally stays running and automatically re-runs tests if their module source
+ (or their dependencies' sources) change.
+
+ Examples:
+
+ garden test # run all tests in the project
+ garden test my-module # run all tests in the my-module module
+ garden test -n integ # run all tests with the name 'integ' in the project
+ garden test --force # force tests to be re-run, even if they're already run successfully
+ garden test --watch # watch for changes to code
+
+
+##### Usage
+
+ garden test [module] [options]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `module` | No | The name of the module(s) to deploy (skip to test all modules). Use comma as separator to specify multiple modules.
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--name` | `-n` | string | Only run tests with the specfied name (e.g. unit or integ).
+ | `--force` | `-f` | boolean | Force re-test of module(s).
+ | `--force-build` | | boolean | Force rebuild of module(s).
+ | `--watch` | `-w` | boolean | Watch for changes in module(s) and auto-test.
+
+### garden unlink source
+
+Unlink a previously linked remote source from its local directory.
+
+After unlinking a remote source, Garden will go back to reading it from its remote URL instead
+of its local directory.
+
+Examples:
+
+ garden unlink source my-source # unlinks my-source
+ garden unlink source --all # unlinks all sources
+
+##### Usage
+
+ garden unlink source [source] [options]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `source` | No | Name of the source(s) to unlink. Use comma separator to specify multiple sources.
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--all` | `-a` | boolean | Unlink all sources.
+
+### garden unlink module
+
+Unlink a previously linked remote module from its local directory.
+
+After unlinking a remote module, Garden will go back to reading the module's source from
+its remote URL instead of its local directory.
+
+Examples:
+
+ garden unlink module my-module # unlinks my-module
+ garden unlink module --all # unlink all modules
+
+##### Usage
+
+ garden unlink module [module] [options]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `module` | No | Name of the module(s) to unlink. Use comma separator to specify multiple modules.
+
+##### Options
+
+| Argument | Alias | Type | Description |
+| -------- | ----- | ---- | ----------- |
+ | `--all` | `-a` | boolean | Unlink all modules.
+
+### garden update-remote sources
+
+Update remote sources.
+
+Update the remote sources declared in the project config.
+
+Examples:
+
+ garden update-remote sources # update all remote sources in the project config
+ garden update-remote sources my-source # update remote source my-source
+
+##### Usage
+
+ garden update-remote sources [source]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `source` | No | Name of the remote source(s) to update. Use comma separator to specify multiple sources.
+
+### garden update-remote modules
+
+Update remote modules.
+
+Remote modules are modules that have a repositoryUrl field
+in their garden.yml config that points to a remote repository.
+
+Examples:
+
+ garden update-remote modules # update all remote modules in the project
+ garden update-remote modules my-module # update remote module my-module
+
+##### Usage
+
+ garden update-remote modules [module]
+
+##### Arguments
+
+| Argument | Required | Description |
+| -------- | -------- | ----------- |
+ | `module` | No | Name of the remote module(s) to update. Use comma separator to specify multiple modules.
+
+### garden update-remote all
+
+Update all remote sources and modules.
+
+Examples:
+
+ garden update-remote all # update all remote sources and modules in the project
+
+##### Usage
+
+ garden update-remote all
+
+### garden validate
+
+Check your garden configuration for errors.
+
+Throws an error and exits with code 1 if something's not right in your garden.yml files.
+
+##### Usage
+
+ garden validate
diff --git a/docs/reference/config-files-reference.md b/docs/reference/config-files-reference.md
new file mode 100644
index 00000000000..d8404f87066
--- /dev/null
+++ b/docs/reference/config-files-reference.md
@@ -0,0 +1,645 @@
+## garden.yml reference
+
+Below is the full schema for the `garden.yml` configuration files. For an introduction,
+please look at our [configuration guide](../guides/configuration.md).
+
+Note that individual module types, e.g. `container`, add additional configuration keys. The built-in module types
+are listed in the [Built-in module types](#built-in-module-types) section. Please refer to those for more details
+on module configuration.
+
+```yaml
+
+# The schema version of the config file (currently not used).
+#
+# Required.
+# Allowed values: "0"
+version: 0
+
+# Configure a module whose sources are located in this directory.
+#
+# Optional.
+module:
+ # The type of this module.
+ #
+ # Example: "container"
+ #
+ # Required.
+ type:
+
+ # The name of this module.
+ #
+ # Example: "my-sweet-module"
+ #
+ # Required.
+ name:
+
+ description:
+
+ # A remote repository URL to fetch the module from. Garden will read the garden.yml config from
+ # the local module. Currently only supports git servers.
+ #
+ # Example: "# or
+ # git+https://github.com/organization/some-module.git#v2.0"
+ #
+ # Optional.
+ repositoryUrl:
+
+ # Variables that this module can reference and expose as environment variables.
+ #
+ # Example:
+ # my-variable: some-value
+ #
+ # Optional.
+ variables:
+ {}
+
+ # Set to false to disable pushing this module to remote registries.
+ #
+ # Optional.
+ allowPush: true
+
+ # Specify how to build the module. Note that plugins may specify additional keys on this object.
+ #
+ # Optional.
+ build:
+ # The command to run inside the module directory to perform the build.
+ #
+ # Example:
+ # - npm
+ # - run
+ # - build
+ #
+ # Optional.
+ command:
+ -
+
+ # A list of modules that must be built before this module is built.
+ #
+ # Example:
+ # - name: some-other-module-name
+ #
+ # Optional.
+ dependencies:
+ - # Module name to build ahead of this module
+ #
+ # Required.
+ name:
+
+ # Specify one or more files or directories to copy from the built dependency to this
+ # module.
+ #
+ # Optional.
+ copy:
+ - # POSIX-style path or filename of the directory or file(s) to copy to the target.
+ #
+ # Required.
+ source:
+
+ # POSIX-style path or filename to copy the directory or file(s) to (defaults to same
+ # as source path).
+ #
+ # Optional.
+ target:
+
+# The configuration for a Garden project. This should be specified in the garden.yml file in your
+# project root.
+#
+# Optional.
+project:
+ # The name of the project.
+ #
+ # Example: "my-sweet-project"
+ #
+ # Required.
+ name:
+
+ # The default environment to use when calling commands without the `--env` parameter.
+ #
+ # Optional.
+ defaultEnvironment:
+
+ # Default environment settings, that are inherited (but can be overridden) by each configured
+ # environment
+ #
+ # Example:
+ # providers: []
+ # variables: {}
+ #
+ # Optional.
+ environmentDefaults:
+ # Specify the provider that should store configuration variables for this environment. Use
+ # this when you configure multiple providers that can manage configuration.
+ #
+ # Optional.
+ configurationHandler:
+
+ # A list of providers that should be used for this environment, and their configuration.
+ # Please refer to individual plugins/providers for details on how to configure them.
+ #
+ # Optional.
+ providers:
+ - # The name of the provider plugin to configure.
+ #
+ # Example: "local-kubernetes"
+ #
+ # Required.
+ name:
+
+ # A key/value map of variables that modules can reference when using this environment.
+ #
+ # Optional.
+ variables:
+ {}
+
+ # A list of environments to configure for the project.
+ #
+ # Example:
+ # - name: local
+ # providers:
+ # - name: local-kubernetes
+ # variables: {}
+ #
+ # Optional.
+ environments:
+ - # Specify the provider that should store configuration variables for this environment. Use
+ # this when you configure multiple providers that can manage configuration.
+ #
+ # Optional.
+ configurationHandler:
+
+ # A list of providers that should be used for this environment, and their configuration.
+ # Please refer to individual plugins/providers for details on how to configure them.
+ #
+ # Optional.
+ providers:
+ - # The name of the provider plugin to configure.
+ #
+ # Example: "local-kubernetes"
+ #
+ # Required.
+ name:
+
+ # A key/value map of variables that modules can reference when using this environment.
+ #
+ # Optional.
+ variables:
+ {}
+
+ # Valid RFC1035/RFC1123 (DNS) label (may contain lowercase letters, numbers and dashes, must
+ # start with a letter, and cannot end with a dash) and additionally cannot contain
+ # consecutive dashes or be longer than 63 characters.
+ #
+ # Required.
+ name:
+
+ # A list of remote sources to import into project
+ #
+ # Optional.
+ sources:
+ - # The name of the source to import
+ #
+ # Required.
+ name:
+
+ # A remote respository URL. Currently only supports git servers. Use hash notation (#) to
+ # point to a specific branch or tag
+ #
+ # Example: "# or
+ # git+https://github.com/organization/some-module.git#v2.0"
+ #
+ # Required.
+ repositoryUrl:
+```
+
+## Built-in module types
+
+### generic
+
+```yaml
+
+# The module specification for a generic module.
+#
+# Required.
+module:
+ # The type of this module.
+ #
+ # Example: "container"
+ #
+ # Required.
+ type:
+
+ # The name of this module.
+ #
+ # Example: "my-sweet-module"
+ #
+ # Required.
+ name:
+
+ description:
+
+ # A remote repository URL to fetch the module from. Garden will read the garden.yml config from
+ # the local module. Currently only supports git servers.
+ #
+ # Example: "# or
+ # git+https://github.com/organization/some-module.git#v2.0"
+ #
+ # Optional.
+ repositoryUrl:
+
+ # Variables that this module can reference and expose as environment variables.
+ #
+ # Example:
+ # my-variable: some-value
+ #
+ # Optional.
+ variables:
+ {}
+
+ # Set to false to disable pushing this module to remote registries.
+ #
+ # Optional.
+ allowPush: true
+
+ # Specify how to build the module. Note that plugins may specify additional keys on this object.
+ #
+ # Optional.
+ build:
+ # The command to run inside the module directory to perform the build.
+ #
+ # Example:
+ # - npm
+ # - run
+ # - build
+ #
+ # Optional.
+ command:
+ -
+
+ # A list of modules that must be built before this module is built.
+ #
+ # Example:
+ # - name: some-other-module-name
+ #
+ # Optional.
+ dependencies:
+ - # Module name to build ahead of this module
+ #
+ # Required.
+ name:
+
+ # Specify one or more files or directories to copy from the built dependency to this
+ # module.
+ #
+ # Optional.
+ copy:
+ - # POSIX-style path or filename of the directory or file(s) to copy to the target.
+ #
+ # Required.
+ source:
+
+ # POSIX-style path or filename to copy the directory or file(s) to (defaults to same
+ # as source path).
+ #
+ # Optional.
+ target:
+
+ # Key/value map of environment variables. Keys must be valid POSIX environment variable names
+ # (must be uppercase, may not start with `GARDEN`) and values must be primitives.
+ #
+ # Optional.
+ env:
+ {}
+
+ # A list of tests to run in the module.
+ #
+ # Optional.
+ tests:
+ # The test specification of a generic module.
+ #
+ # Optional.
+ - # The name of the test.
+ #
+ # Required.
+ name:
+
+ # The names of services that must be running before the test is run.
+ #
+ # Optional.
+ dependencies:
+ -
+
+ # Maximum duration (in seconds) of the test run.
+ #
+ # Optional.
+ timeout: null
+
+ # The command to run in the module build context in order to test it.
+ #
+ # Optional.
+ command:
+ -
+
+ # Key/value map of environment variables. Keys must be valid POSIX environment variable
+ # names (must be uppercase, may not start with `GARDEN`) and values must be primitives.
+ #
+ # Optional.
+ env:
+ {}
+```
+
+### container
+
+```yaml
+
+# Configuration for a container module.
+#
+# Required.
+module:
+ # The type of this module.
+ #
+ # Example: "container"
+ #
+ # Required.
+ type:
+
+ # The name of this module.
+ #
+ # Example: "my-sweet-module"
+ #
+ # Required.
+ name:
+
+ description:
+
+ # A remote repository URL to fetch the module from. Garden will read the garden.yml config from
+ # the local module. Currently only supports git servers.
+ #
+ # Example: "# or
+ # git+https://github.com/organization/some-module.git#v2.0"
+ #
+ # Optional.
+ repositoryUrl:
+
+ # Variables that this module can reference and expose as environment variables.
+ #
+ # Example:
+ # my-variable: some-value
+ #
+ # Optional.
+ variables:
+ {}
+
+ # Set to false to disable pushing this module to remote registries.
+ #
+ # Optional.
+ allowPush: true
+
+ # Specify how to build the module. Note that plugins may specify additional keys on this object.
+ #
+ # Optional.
+ build:
+ # The command to run inside the module directory to perform the build.
+ #
+ # Example:
+ # - npm
+ # - run
+ # - build
+ #
+ # Optional.
+ command:
+ -
+
+ # A list of modules that must be built before this module is built.
+ #
+ # Example:
+ # - name: some-other-module-name
+ #
+ # Optional.
+ dependencies:
+ - # Module name to build ahead of this module
+ #
+ # Required.
+ name:
+
+ # Specify one or more files or directories to copy from the built dependency to this
+ # module.
+ #
+ # Optional.
+ copy:
+ - # POSIX-style path or filename of the directory or file(s) to copy to the target.
+ #
+ # Required.
+ source:
+
+ # POSIX-style path or filename to copy the directory or file(s) to (defaults to same
+ # as source path).
+ #
+ # Optional.
+ target:
+
+ # Specify build arguments when building the container image.
+ #
+ # Optional.
+ buildArgs:
+ {}
+
+ # Specify the image name for the container. Should be a valid docker image identifier. If
+ # specified and the module does not contain a Dockerfile, this image will be used to deploy the
+ # container services. If specified and the module does contain a Dockerfile, this identifier is
+ # used when pushing the built image.
+ #
+ # Optional.
+ image:
+
+ # List of services to deploy from this container module.
+ #
+ # Optional.
+ services:
+ # The required attributes of a service. This is generally further defined by plugins.
+ #
+ # Optional.
+ - # Valid RFC1035/RFC1123 (DNS) label (may contain lowercase letters, numbers and dashes, must
+ # start with a letter, and cannot end with a dash) and additionally cannot contain
+ # consecutive dashes or be longer than 63 characters.
+ #
+ # Required.
+ name:
+
+ # The names of services that this service depends on at runtime.
+ #
+ # Optional.
+ dependencies:
+ # Valid RFC1035/RFC1123 (DNS) label (may contain lowercase letters, numbers and dashes,
+ # must start with a letter, and cannot end with a dash) and additionally cannot contain
+ # consecutive dashes or be longer than 63 characters.
+ #
+ # Optional.
+ -
+
+ # Key/value map, keys must be valid identifiers.
+ #
+ # Optional.
+ outputs:
+ {}
+
+ # The arguments to run the container with when starting the service.
+ #
+ # Optional.
+ command:
+ -
+
+ # Whether to run the service as a daemon (to ensure only one runs per node).
+ #
+ # Optional.
+ daemon: false
+
+ # List of endpoints that the service exposes.
+ #
+ # Example:
+ # - path: /api
+ # port: http
+ #
+ # Optional.
+ endpoints:
+ - # The hostname that should route to this service. Defaults to the default hostname
+ # configured
+ # in the provider configuration.
+ #
+ # Note that if you're developing locally you may need to add this hostname to your hosts
+ # file.
+ #
+ # Optional.
+ hostname:
+
+ # The path which should be routed to the service.
+ #
+ # Optional.
+ path: /
+
+ # The name of the container port where the specified paths should be routed.
+ #
+ # Required.
+ port:
+
+ # Key/value map of environment variables. Keys must be valid POSIX environment variable
+ # names (must be uppercase, may not start with `GARDEN`) and values must be primitives.
+ #
+ # Optional.
+ env:
+ {}
+
+ # Specify how the service's health should be checked after deploying.
+ #
+ # Optional.
+ healthCheck:
+ # Set this to check the service's health by making an HTTP request
+ #
+ # Optional.
+ httpGet:
+ # The path of the service's health check endpoint.
+ #
+ # Required.
+ path:
+
+ # The name of the port where the service's health check endpoint should be available.
+ #
+ # Required.
+ port:
+
+ scheme: HTTP
+
+ # Set this to check the service's health by running a command in its container.
+ #
+ # Optional.
+ command:
+ -
+
+ # Set this to check the service's health by checking if this TCP port is accepting
+ # connections.
+ #
+ # Optional.
+ tcpPort:
+
+ # List of ports that the service container exposes.
+ #
+ # Optional.
+ ports:
+ #
+ # Required.
+ - # The name of the port (used when referencing the port elsewhere in the service
+ # configuration.
+ #
+ # Required.
+ name:
+
+ # The protocol of the service container port.
+ #
+ # Optional.
+ protocol: TCP
+
+ # The port number on the service container.
+ #
+ # Required.
+ containerPort:
+
+ hostPort:
+
+ # Set this to expose the service on the specified port on the host node (may not be
+ # supported by all providers).
+ #
+ # Optional.
+ nodePort:
+
+ # List of volumes that should be mounted when deploying the container.
+ #
+ # Optional.
+ volumes:
+ - # The name of the allocated volume.
+ #
+ # Required.
+ name:
+
+ # The path where the volume should be mounted in the container.
+ #
+ # Required.
+ containerPath:
+
+ hostPath:
+
+ # A list of tests to run in the module.
+ #
+ # Optional.
+ tests:
+ # The test specification of a generic module.
+ #
+ # Optional.
+ - # The name of the test.
+ #
+ # Required.
+ name:
+
+ # The names of services that must be running before the test is run.
+ #
+ # Optional.
+ dependencies:
+ -
+
+ # Maximum duration (in seconds) of the test run.
+ #
+ # Optional.
+ timeout: null
+
+ # The command to run in the module build context in order to test it.
+ #
+ # Optional.
+ command:
+ -
+
+ # Key/value map of environment variables. Keys must be valid POSIX environment variable
+ # names (must be uppercase, may not start with `GARDEN`) and values must be primitives.
+ #
+ # Optional.
+ env:
+ {}
+```
+
diff --git a/docs/reference/glossary.md b/docs/reference/glossary.md
new file mode 100644
index 00000000000..bf9998f6593
--- /dev/null
+++ b/docs/reference/glossary.md
@@ -0,0 +1,46 @@
+# Glossary
+
+#### Environment
+Represents the current configuration and status of any running services in the [project](#project), which may be
+inspected and modified via the Garden CLI's `environment` command.
+
+Several named environment configurations may be defined (e.g. _dev_, _testing_, ...) in the [project's
+`garden.yml`](../guides/configuration.md#project-configuration).
+
+#### Module
+The basic unit of configuration in Garden. A module is defined by its
+[`garden.yml` configuration file](../guides/configuration.md#module-configuration), located in the module's top-level
+directory,
+which
+is a subdirectory of the [project](#project) repository's top-level directory.
+
+Each module has a [plugin](#plugin) type, and may define one or more [services](#service).
+
+Essentially, a project is organized into modules at the granularity of its *build* steps. A module's build step may
+depend on one or more other modules, as specified in its `garden.yml`, in which case those modules will be built
+first, and their build output made available to the requiring module's build step.
+
+#### Provider
+A [module's](#module) plugin type defines its behavior when it is built, deployed, run and tested. Currently,
+`container` is the only stable plugin type, but plugin types for serverless functions and for build-only use cases
+(such as NPM modules) are under development.
+
+#### Project
+The top-level unit of organization in Garden. A project consists of one or more [modules](#module), along with a
+project-level [`garden.yml` configuration file](../guides/configuration.md#project-configuration).
+
+Garden CLI commands are run in the context of a project, and are aware of all its modules and services.
+
+Currently, Garden projects assume that all their modules are rooted in subdirectories of the same Git repository, with
+the project-level `garden.yml` located in the repository's top-level directory. In a future release, this mono-repo
+structure will be made optional.
+
+#### Provider
+An implementation of a [plugin type](#plugin) (e.g. `local-kubernetes` for the `container` plugin).
+
+#### Service
+The unit of deployment in Garden. Services are defined in their parent [module](#module)'s `garden.yml`, each
+exposing [one or more endpoints](../guides/configuration.md#services).
+
+Services may depend on services defined in other modules, in which case those services will be deployed first, and
+their deployment output made available to the requiring service's deploy step.
diff --git a/docs/reference/module-configs/README.md b/docs/reference/module-configs/README.md
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/docs/reference/module-configs/container.md b/docs/reference/module-configs/container.md
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/docs/reference/module-configs/google-cloud-functions.md b/docs/reference/module-configs/google-cloud-functions.md
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/docs/reference/module-configs/openfaas.md b/docs/reference/module-configs/openfaas.md
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/docs/reference/provider-configs/README.md b/docs/reference/provider-configs/README.md
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/docs/reference/provider-configs/container.md b/docs/reference/provider-configs/container.md
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/docs/reference/provider-configs/google-cloud-functions.md b/docs/reference/provider-configs/google-cloud-functions.md
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/docs/reference/provider-configs/kubernetes.md b/docs/reference/provider-configs/kubernetes.md
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/docs/reference/provider-configs/local-kubernetes.md b/docs/reference/provider-configs/local-kubernetes.md
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/docs/reference/provider-configs/openfaas.md b/docs/reference/provider-configs/openfaas.md
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/docs/schematic.jpg b/docs/schematic.jpg
new file mode 100644
index 00000000000..6225628018d
Binary files /dev/null and b/docs/schematic.jpg differ
diff --git a/docs/toc.md b/docs/toc.md
new file mode 100644
index 00000000000..05d69eecb28
--- /dev/null
+++ b/docs/toc.md
@@ -0,0 +1,22 @@
+# Table of Contents
+
+* [Table of Contents](./toc.md)
+* [Basics](./basics/README.md)
+ * [Installation](./basics/installation.md)
+ * [Quick Start](./basics/quick-start.md)
+ * [Concepts](./basics/concepts.md)
+* [Using Garden](./using-garden/README.md)
+ * [Features and usage](./using-garden/features-and-usage.md)
+ * [Configuration files](./using-garden/configuration-files.md)
+ * [Remote Clusters](./using-garden/remote-clusters.md)
+ * [Hot Reload](./using-garden/hot-reload.md)
+* [Example projects](./examples/README.md)
+ * [Hello world](./examples/hello-world.md)
+ * [Simple project](./examples/simple-project.md)
+ * [TLS project](./examples/tls-project.md)
+ * [Remote sources project](./examples/remote-sources.md)
+* [Reference](./reference/README.md)
+ * [Glossary](./reference/glossary.md)
+ * [Commands Reference](./reference/command-reference.md)
+ * [Config Files Reference](./reference/config-files-reference.md)
+* [FAQs](./faqs.md)
\ No newline at end of file
diff --git a/docs/using-garden/README.md b/docs/using-garden/README.md
new file mode 100644
index 00000000000..60640b4acab
--- /dev/null
+++ b/docs/using-garden/README.md
@@ -0,0 +1,17 @@
+# Using Garden
+
+## [Features and usage](./features-and-usage.md)
+
+In this article we discuss how to start a new project with `garden create`, the basic development workflow, how Garden's providers work, and the basics of testing and dependencies.
+
+## [Configuration files](./configuration-files.md)
+
+This one is all about Garden's configuration files. The difference between project and module configs, some significant specific fields, setting up services, and a primer on tests.
+
+## [Remote Clusters](./remote-clusters.md)
+
+Most of the time we want to develop locally, with our project running in Minikube or Docker. If you'd like to use a remote cluster though, check out this guide.
+
+## [Hot Reload](./hot-reload.md)
+
+This article discusses how to use hot reload, so that you can update files on the fly, without losing state and without having to destroy and re-create your container.
\ No newline at end of file
diff --git a/docs/using-garden/configuration-files.md b/docs/using-garden/configuration-files.md
new file mode 100644
index 00000000000..ad97f197e1a
--- /dev/null
+++ b/docs/using-garden/configuration-files.md
@@ -0,0 +1,201 @@
+# Configuration
+
+Garden is configured via `garden.yml` configuration files.
+
+The [project-wide](#project-configuration) `garden.yml` file should be located in the top-level directory of the
+project's Git repository.
+
+In addition, each of the project's [modules](../reference/glossary.md#module)' `garden.yml` should be located in that
+module's top-level directory.
+
+To get started, create a `garden.yml` file in the top-level directory of your repository, and a `garden.yml` file
+in the top-level directory of each of the modules you'd like to define for your project.
+
+To decide how to split your project up into modules, it's useful to consider what parts of it are built as a single
+step, and what the dependency relationships are between your build steps. For example, each container and each
+serverless function should be represented by its own module.
+
+Below, we'll be using examples from the
+[Hello world](../examples/hello-world.md) example project, which touches
+on many of the things you're likely to want to configure in a project.
+
+## Project Configuration
+
+We'll start by looking at the top-level [project configuration file](https://github.com/garden-io/garden/blob/master/examples/hello-world/garden.yml).
+
+```yaml
+# examples/hello-world/garden.yml
+project:
+ name: hello-world
+ environmentDefaults:
+ variables:
+ my-variable: hello-variable
+ environments:
+ - name: local
+ providers:
+ - name: local-kubernetes
+ - name: openfaas
+```
+
+The project-wide `garden.yml` defines the project's name, the default configuration used for each
+[environment](../reference/glossary.md#environment) (via the `environmentDefaults` directive), and
+environment-specific provider configuration. The above only configures a `local` environment, but you could add
+further environments, such as a remote Kubernetes environment.
+
+Here, project-wide configuration variables can also be specified (global, and/or environment-specific). These are
+then available for substitution in any string value in any module's `garden.yml`.
+
+For example, assuming the above project configuration, `"foo-${variables.my-variable}-bar"` would evaluate to
+`"foo-hello-variable-bar"` when used as a string value in a module's `garden.yml`.
+
+## Module Configuration
+
+Below, we'll use the module configurations of `hello-function` and `hello-container` from the
+[Hello world](../examples/hello-world.md) example project
+as examples to illustrate some of the primary module-level configuration options.
+
+The following is a snippet from `hello-container`'s module config:
+
+```yaml
+module:
+ name: hello-container
+ type: container
+ description: Hello world container service
+ ...
+ build:
+ dependencies:
+ - name: hello-npm-package
+ copy:
+ - source: "./"
+ target: libraries/hello-npm-package/
+```
+
+The first lines you'll find in all module configurations, and describe the module at a high level.
+
+The second part, the `build` key, demonstrates how Garden can serve as a build framework, managing build dependencies
+and even copying files between modules as they are built.
+
+Below is a run-down of the individual configuration keys, and what they represent:
+
+### name
+
+The module's name, used e.g. when referring to it from another module's configuration as a
+build dependency, or when building specific modules with `garden build`.
+
+Note that module names must be unique within a given project. An error will be thrown in any Garden CLI command if two
+modules use the same name.
+
+### type
+
+A [module](../reference/glossary.md#module)'s `type` specifies what kind of module this is, which will control how the
+module's code gets built, tested, deployed, etc. The module types are defined by _providers_. The built-in providers
+include `container` and `generic` (which basically provides a way to run commands locally).
+
+The example above is a `container` module, and the `hello-function` module is an `openfaas` module
+(which is one of many ways to run functions-as-a-service on Kubernetes).
+
+In this particular project, the `container` module type is deployed by the `local-kubernetes` provider, and the
+`openfaas` module is built and deployed by the corresponding `openfaas` provider.
+
+### build
+
+A module's build configuration is specified via the `build` directive, and the implementation of what `build` does varies depending on which provider is responsible for that module.
+
+Regardless of the implementation, a module's build command is executed
+with its working directory set to a copy of the module's top-level directory, located at
+`[project-root]/.garden/build/[module-name]`. This internal directory is referred to as the module's
+[build directory](../reference/glossary.md#build-directory).
+
+The `.garden` directory should not be modified by users, since this may lead to unexpected errors when the Garden CLI
+tools are used in the project.
+
+The `build.dependencies` subdirective lists the module's build dependencies, which need to be built ahead of this module.
+`name` is the required module's name. In many cases you only need to declare the build dependency name. For example,
+you simply need to build one container before another because it's used as a base image.
+
+In other cases, you may actually need files to be copied from one built module to another.
+The `copy` key indicates what files/folders, if any, should be copied from the required module's build directory to the
+module in question after the required module is built (`source`), and where they should be copied to (`target`).
+
+In the above example, we copy the entire contents of `hello-npm-package`'s build directory, after it has been built,
+into the `libraries/hello-npm-package/` in the `hello-container` build directory, _before the container is built_.
+
+## Services
+
+A module may contain zero or more _services_. Services are deployed when running `garden deploy` or `garden dev` as
+part of your runtime stack.
+
+How services are configured will depend on the module type. An `openfaas` module always contains a single service. A
+`container` module can contain any number of services (or none at all, if it's just used as a base image, for example).
+
+The following is a snippet from `hello-container`'s module config:
+
+```yaml
+module:
+ description: Hello world container service
+ type: container
+ services:
+ - name: hello-container
+ command: [npm, start]
+ ports:
+ - name: http
+ containerPort: 8080
+ endpoints:
+ - path: /hello
+ port: http
+ healthCheck:
+ httpGet:
+ path: /_ah/health
+ port: http
+ dependencies:
+ - hello-function
+ ...
+```
+
+Here the `services` directive defines the services exposed by the module. We only have one service in this example,
+but you may add another service, for example a background worker, that is started using a different
+`command`.
+
+For more details on how to configure services in a `container` module, please refer to the
+[Config Files Reference](../reference/config-files-reference.md).
+
+## Tests
+
+Each module can define one or more test suites. How these tests are specified, much like services, depends on the
+individual module type. However the concepts are most often the same; you specify one or more test suites, how to
+execute them, and in some cases which services need to be running for the tests to run successfully.
+
+For an example, here is another snippet from the `hello-container` module configuration:
+
+```yaml
+module:
+ description: Hello world container service
+ type: container
+ ...
+ tests:
+ - name: unit
+ command: [npm, test]
+ - name: integ
+ command: [npm, run, integ]
+ dependencies:
+ - hello-function
+```
+
+Here we define two types of tests. First are unit tests, which can be run on their own without any dependencies. The
+framework only needs to know which command to run, and the rest is handled by the module's code itself.
+
+The other test suite, `integ`, leverages the Garden framework's ability to manage runtime dependencies for tests. In
+this case, the integ test suite needs the `hello-function` service to be running for the tests to execute.
+
+This allows you write tests that actually call out to other services, rather than having to mock or stub those services
+in your tests.
+
+Tests can be run via `garden test`, as well as `garden dev`.
+
+## Next steps
+
+We highly recommend browsing through the [Example projects](../examples/README.md) to see different examples of how projects and modules can be configured.
+
+Also be sure to look at the [Config Files Reference](../reference/config-files-reference.md)
+ for more details on each of the available
+configuration keys.
diff --git a/docs/using-garden/features-and-usage.md b/docs/using-garden/features-and-usage.md
new file mode 100644
index 00000000000..23fe1a6b95e
--- /dev/null
+++ b/docs/using-garden/features-and-usage.md
@@ -0,0 +1,109 @@
+# Features and Usage
+
+Now that you've had a glimpse at the basic Garden commands on the [Quick Start](./basics/quick-start.md) guide, and a brief look at the main [Concepts](../basics/concepts.md) we'll be dealing with, let's go through what the typical usage of Garden looks like.
+
+## Starting a new project
+
+For starting a new project with Garden there are two options:
+
+- You can create all the configuration files by hand, and for that you should take a look at our [Configuration files](./configuration-files.md) document.
+- Or you can use the `garden create` command—a lot easier.
+
+### `garden create`
+
+The `garden create` command can be used to create either whole projects, or just modules. Essentially what it does is help you create configuration files so you don't have to do it by hand.
+
+The command `garden create project` will create a new project in the current directory and prompt you to add modules to it, which should each have a name and a type. It will then create the appropriate folders and the configuration files within them.
+
+If this is a pre-existing project and you want to "gardenify" code that's already there, you can try, for example, `garden create project --module-dirs=./services`. This will prompt you to create configuration files for every subfolder within the `./services` directory.
+
+To add individual modules later on you can use `garden create module`.
+
+```
+➜ test-project g create project
+
+Initializing new Garden project test-project
+---------
+? Would you like to add a module to your project? Yes
+? Enter module name my-module
+? Module type container
+? Add another module? (current modules: my-module) Yes
+? Enter module name my-module-2
+? Module type container
+? Add another module? (current modules: my-module, my-module-2) No
+---------
+✔ Setting up project
+✔ Writing config for my-module
+✔ Writing config for my-module-2
+✔ Writing config for test-project
+Project created! Be sure to check out our docs for how to get sarted!
+```
+
+For a practical example of "gardenifying" an existing project, check out the [Simple project](../examples/simple-project.md) example.
+
+## The development workflow
+
+Most of the time, the development workflow when using Garden after the configuration files are set is extremely simple: you leave `garden dev` running, and Garden will automatically re-build, re-deploy, and re-test your project as you work on it.
+
+Sometimes though you might prefer to skip the testing step, in which case you can simply use `garden deploy --watch`. This will watch for changes, then build and deploy them, but it'll skip testing.
+
+Another important topic to keep in mind is [inter-service communication](../basics/concepts.md#how-inter-service-communication-works). As previously discussed, your project has multiple services, and they need to talk to each other at some point. That's pretty simple: a service's hostname is simply its name. So a the hostname for a service called `my-service` is simply `http://my-service/`.
+
+For example, the following snippet calls a separate service in the project called `go-service`.
+
+```js
+request.get('http://go-service/hello-go').then(message => {res.json({message})})
+```
+
+Lastly, when things go wrong you should refer to the error logs. That's an `error.log` file in the project root, and the service logs that you can retrieve from the individual pods in your cluster.
+
+For the latter, you can use the `garden logs` command, followed by the name of the service you'd like to query. For example `garden logs go-service` would fetch the logs for the `go-service` service, while `garden logs go-service,node-service` would fetch the logs for both the `go-service` and the `node-service` services.
+
+The `garden logs` command is functionally equivalent to `kubectl logs`, but simpler to execute.
+
+## Providers
+
+Whenever "a module's type" is mentioned in the documentation, what's meant is "which provider will handle this module?" Providers, as [previously discussed](../basics/concepts.md), are responsible for implementing different behaviors for say containers and serverless functions, and they need to be specified in a module's configuration files.
+
+For a comprehensive list of providers available in Garden, check out the [References](../reference/README.md)
+
+
+## Testing and dependencies
+
+Both tests and dependencies are specified in the Garden configuration files.
+
+Dependencies are a field within the services declaration. Here's a snippet, from our [TLS project](../examples/tls-project.md) example:
+
+```yaml
+module:
+ name: node-service
+ description: Node service container
+ type: container
+ services:
+ - name: node-service
+ ...
+ dependencies:
+ - go-service
+```
+
+Tests should be specified the same way, and in the case of integration tests their dependencies should be present as well. Another snippet from the same file:
+
+```yaml
+tests:
+ - name: unit
+ command: [npm, test]
+ - name: integ
+ command: [npm, run, integ]
+ dependencies:
+ - go-service
+```
+
+Above we're using `npm test` and `npm run integ` for our tests, but they can be anything you'd like. The only constraint is that Garden follows the typical Unix exit codes convention: `0` means success, and any non-zero exit codes represent failures.
+
+## Advanced features
+
+For Garden's more advanced features, see the following docs:
+
+- [Hot Reload](./hot-reload.md), for how to update the files in a container without having to restart it and lose state.
+- [TLS project](../examples/tls-project.md), for—drumroll!—how to set up TLS with Garden.
+- [Remote sources project](../examples/remote-sources.md), for how to integrate multiple remote and local repositories within the same project.
diff --git a/docs/using-garden/hot-reload.md b/docs/using-garden/hot-reload.md
new file mode 100644
index 00000000000..4716a2a3330
--- /dev/null
+++ b/docs/using-garden/hot-reload.md
@@ -0,0 +1,49 @@
+# Hot Reload
+
+When the `local-kubernetes` provider is used, `container` modules can be configured to hot-reload their running services when the module's sources change (i.e. without redeploying). In essence, hot-reloading copies source files into the appropriate running containers when code is changed by the user.
+
+For example, services that can be run with a file system watcher that automatically update the running application process when sources change (e.g. nodemon, Django, Ruby on Rails, and many other web app frameworks) are a natural fit for this feature.
+
+# Usage
+
+Currently, modules configured for hot reloading are only deployed with hot reloading enabled when they're deployed via `garden deploy -w` or `garden dev`; and not, for example, when deployed via `garden deploy` without the `-w` flag.
+
+Subsequently deploying a service belonging to a module configured for hot reloading via `garden deploy` (without the watch flag) results in the service being redeployed in standard configuration. (See [this link](https://github.com/garden-io/garden/pull/291) for a more technical discussion.)
+
+Since hot reloading is triggered via Garden's file system watcher, hot reloading only occurs while a `garden deploy -w`, `garden build -w`, or `garden dev` command is running.
+
+# Quick example
+
+Following is a simple example of a module configured for hot reloading:
+
+```yaml
+module:
+ description: My Test Service
+ name: test-service
+ type: container
+ hotReload:
+ sync:
+ - target: /app/
+ services:
+ - name: test-service
+ command: [npm, start] # runs `node main.js`
+ hotReloadCommand: [npm, run, dev] # runs `nodemon main.js`
+```
+
+In the above, the `hotReload` field specifies the destination path inside the running container that the module's (top-level) directory (where its `garden.yml` resides) is synced to.
+
+Note that only files tracked in version control are synced, e.g. respecting `.gitignore`.
+
+If a `source` is specified along with `target`, that subpath in the module's directory is synced to the target instead of the default of syncing the module's top-level directory.
+
+You can configure several such `source`/`target` pairs, but note that the `source` path must be disjoint, i.e. a `source` path may not be a subdirectory of another `source` path within the same module. Here's an example:
+
+```yaml
+ sync:
+ - source: /foo
+ target: /app/foo
+ - source: /bar
+ target: /app/bar
+```
+
+Lastly, `hotReloadCommand` determines which command should be ran inside the container (when deployed with hot reloading enabled). If no `hotReloadCommand` is specified, `command` is also used in hot reload mode.
diff --git a/docs/using-garden/remote-clusters.md b/docs/using-garden/remote-clusters.md
new file mode 100644
index 00000000000..9f5740a1d7a
--- /dev/null
+++ b/docs/using-garden/remote-clusters.md
@@ -0,0 +1,130 @@
+# Using Garden with a remote Kubernetes cluster
+
+Below are some notes on steps you need to take before deploying Garden projects to a remote Kubernetes cluster.
+
+Many of the steps are not specific to Garden as such, so you may have already performed some of these steps
+and/or may need to follow the provided links in each section for details on how to perform the steps you have
+not yet completed.
+
+## Setup
+
+### Connecting to the cluster
+
+Start by making sure you have a [kubectl context](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/)
+set up on your development machine to access your cluster. How you set this up will vary by how and where you
+have deployed your cluster.
+
+Then configure the project and provider in your project `garden.yml`, along with the kubectl context you use to
+connect to your cluster.
+
+Example:
+
+```yaml
+project:
+ name: my-project
+ environments:
+ - name: dev
+ providers:
+ - name: kubernetes
+ context: my-dev-context # the name of the kubectl context for the cluster
+ ...
+ defaultEnvironment: dev
+```
+
+### Ingress, TLS and DNS
+
+The cluster needs to have a configured [nginx ingress controller](https://github.com/kubernetes/ingress-nginx).
+
+You'll also need to point one or more DNS entries to your cluster, and configure a TLS certificate for the hostnames
+you will expose for ingress.
+_How you configure DNS and prepare the certificates will depend on how you manage DNS and certificates in general,
+so we won't cover that in detail here._
+
+Once you have the certificates on hand (the `.crt` and `.key` files), create a
+[Secret](https://kubernetes.io/docs/concepts/configuration/secret/) for each cert in the cluster so that
+they can be referenced when deploying services:
+
+```sh
+kubectl create secret tls mydomain-tls-secret --key --cert
+```
+
+Then configure each certificate/secret in your `garden.yml` provider configuration:
+
+```yaml
+project:
+ name: my-project
+ environments:
+ - name: dev
+ providers:
+ - name: kubernetes
+ context: my-dev-context
+ tlsCertificates:
+ - name: main
+ # Optionally set particular hostnames to use this certificate for
+ # (useful if you have multiple certs for the same hostname).
+ hostnames: [mydomain.com]
+ secretRef:
+ # Change to whatever name you chose for the secret above.
+ name: my-tls-secret
+ # Change this if you store the secret in another namespace.
+ namespace: default
+ - name: wildcard
+ secretRef:
+ name: wildcard-tls-secret
+ namespace: default
+ ...
+ defaultEnvironment: dev
+```
+
+### Configuring a container registry
+
+When you deploy to the environment (via `garden deploy` or `garden dev`), containers are first built and then pushed
+to the configured _deployment registry_, where the K8s cluster will then pull the built images when deploying.
+This should generally be a _private_ container registry, or at least a private project in a public registry.
+
+Similarly to the above TLS configuration, you may also need to set up auth for the registry using K8s Secrets, in this
+case via the `kubectl create secret docker-registry` helper.
+
+_Note that you do not need to configure the authentication and imagePullSecrets when using GKE along with GCR,
+as long as your deployment registry is in the same project as the GKE cluster._
+
+The lovely folks at [Heptio](https://heptio.com) have prepared good guides on how to configure private registries
+for Kubernetes, which you can find [here](http://docs.heptio.com/content/private-registries.html).
+
+Once you've created the auth secret in the cluster, you can configure the registry and the secrets in your
+`garden.yml` project config like this:
+
+```yaml
+project:
+ name: my-project
+ environments:
+ - name: dev
+ providers:
+ - name: kubernetes
+ context: my-dev-context
+ ...
+ deploymentRegistry:
+ # The hostname of the registry, e.g. gcr.io for GCR (Google Container Registry)
+ hostname: my.registry.io
+ # Namespace (aka project ID) to use in the registry for this project.
+ # For GKE/GCR, use the project ID where your cluster is.
+ namespace: my-project-id
+ imagePullSecrets:
+ # The name of the secret you stored using `kubectl create secret docker-registry`
+ - name: my-registry-secret
+ # Change this if you store the secret in another namespace.
+ namespace: default
+ defaultEnvironment: dev
+```
+
+You also need to login to the `docker` CLI, so that images can be pushed to the registry. Please refer
+to your registry's documentation on how to do that (for Docker Hub you simply run `docker login`).
+
+### Permissions
+
+Note that you need to have permissions to create namespaces and to create deployments,
+daemonsets, services and ingresses within the namespaces created.
+
+The plugin will create two or more namespaces per user and project, one to run services, another to manage
+metadata and configuration (this is so that your environment can be reset without
+clearing your configuration variables), and potentially more to support specific plugins/providers.
\ No newline at end of file
diff --git a/garden-cli/garden-cli b/garden-cli/garden-cli
new file mode 100755
index 00000000000..f90024a76a5
Binary files /dev/null and b/garden-cli/garden-cli differ
diff --git a/garden-service/build/actions.d.ts b/garden-service/build/actions.d.ts
new file mode 100644
index 00000000000..e2091961f8a
--- /dev/null
+++ b/garden-service/build/actions.d.ts
@@ -0,0 +1,81 @@
+import { Garden } from "./garden";
+import { PrimitiveMap } from "./config/common";
+import { Module } from "./types/module";
+import { BuildResult, BuildStatus, DeleteSecretResult, EnvironmentStatusMap, ExecInServiceResult, GetSecretResult, GetServiceLogsResult, PushResult, RunResult, SetSecretResult, TestResult, PublishResult } from "./types/plugin/outputs";
+import { BuildModuleParams, DeleteSecretParams, DeployServiceParams, DeleteServiceParams, ExecInServiceParams, GetSecretParams, GetBuildStatusParams, GetServiceLogsParams, GetServiceOutputsParams, GetServiceStatusParams, GetTestResultParams, ModuleActionParams, PluginActionContextParams, PluginActionParams, PluginActionParamsBase, PluginServiceActionParamsBase, PushModuleParams, RunModuleParams, RunServiceParams, SetSecretParams, TestModuleParams, GetEnvironmentStatusParams, PluginModuleActionParamsBase, PublishModuleParams } from "./types/plugin/params";
+import { ServiceStatus } from "./types/service";
+import { Omit } from "./util/util";
+import { RuntimeContext } from "./types/service";
+import { ProcessResults } from "./process";
+import { LogEntry } from "./logger/log-entry";
+import { CleanupEnvironmentParams } from "./types/plugin/params";
+declare type TypeGuard = {
+ readonly [P in keyof (PluginActionParams | ModuleActionParams)]: (...args: any[]) => Promise;
+};
+export interface ContextStatus {
+ providers: EnvironmentStatusMap;
+ services: {
+ [name: string]: ServiceStatus;
+ };
+}
+export interface DeployServicesParams {
+ serviceNames?: string[];
+ force?: boolean;
+ forceBuild?: boolean;
+}
+declare type ActionHelperParams = Omit & {
+ pluginName?: string;
+};
+declare type ModuleActionHelperParams = Omit & {
+ pluginName?: string;
+};
+declare type ServiceActionHelperParams = Omit & {
+ runtimeContext?: RuntimeContext;
+ pluginName?: string;
+};
+declare type RequirePluginName = T & {
+ pluginName: string;
+};
+export declare class ActionHelper implements TypeGuard {
+ private garden;
+ constructor(garden: Garden);
+ getEnvironmentStatus({ pluginName }: ActionHelperParams): Promise;
+ /**
+ * Checks environment status and calls prepareEnvironment for each provider that isn't flagged as ready.
+ *
+ * If any of the getEnvironmentStatus handlers returns needUserInput=true, this throws and guides the user to
+ * run `garden init`
+ */
+ prepareEnvironment({ force, pluginName, logEntry, allowUserInput }: {
+ force?: boolean;
+ pluginName?: string;
+ logEntry?: LogEntry;
+ allowUserInput?: boolean;
+ }): Promise<{}>;
+ cleanupEnvironment({ pluginName }: ActionHelperParams): Promise;
+ getSecret(params: RequirePluginName>): Promise;
+ setSecret(params: RequirePluginName>): Promise;
+ deleteSecret(params: RequirePluginName>): Promise;
+ getBuildStatus(params: ModuleActionHelperParams>): Promise;
+ build(params: ModuleActionHelperParams>): Promise;
+ pushModule(params: ModuleActionHelperParams>): Promise;
+ publishModule(params: ModuleActionHelperParams>): Promise;
+ runModule(params: ModuleActionHelperParams>): Promise;
+ testModule(params: ModuleActionHelperParams>): Promise;
+ getTestResult(params: ModuleActionHelperParams>): Promise;
+ getServiceStatus(params: ServiceActionHelperParams): Promise;
+ deployService(params: ServiceActionHelperParams): Promise;
+ deleteService(params: ServiceActionHelperParams): Promise;
+ getServiceOutputs(params: ServiceActionHelperParams): Promise;
+ execInService(params: ServiceActionHelperParams): Promise;
+ getServiceLogs(params: ServiceActionHelperParams): Promise;
+ runService(params: ServiceActionHelperParams): Promise;
+ getStatus(): Promise;
+ deployServices({ serviceNames, force, forceBuild }: DeployServicesParams): Promise;
+ private commonParams;
+ private callActionHandler;
+ private callModuleHandler;
+ private callServiceHandler;
+}
+export {};
+//# sourceMappingURL=actions.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/actions.js b/garden-service/build/actions.js
new file mode 100644
index 00000000000..fee9eae75b1
--- /dev/null
+++ b/garden-service/build/actions.js
@@ -0,0 +1,319 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const Bluebird = require("bluebird");
+const chalk_1 = require("chalk");
+const service_1 = require("./types/service");
+const lodash_1 = require("lodash");
+const process_1 = require("./process");
+const deploy_1 = require("./tasks/deploy");
+const plugin_context_1 = require("./plugin-context");
+const exceptions_1 = require("./exceptions");
+class ActionHelper {
+ constructor(garden) {
+ this.garden = garden;
+ }
+ //===========================================================================
+ //region Environment Actions
+ //===========================================================================
+ getEnvironmentStatus({ pluginName }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const handlers = this.garden.getActionHandlers("getEnvironmentStatus", pluginName);
+ return Bluebird.props(lodash_1.mapValues(handlers, h => h(Object.assign({}, this.commonParams(h)))));
+ });
+ }
+ /**
+ * Checks environment status and calls prepareEnvironment for each provider that isn't flagged as ready.
+ *
+ * If any of the getEnvironmentStatus handlers returns needUserInput=true, this throws and guides the user to
+ * run `garden init`
+ */
+ prepareEnvironment({ force = false, pluginName, logEntry, allowUserInput = false }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const handlers = this.garden.getActionHandlers("prepareEnvironment", pluginName);
+ const statuses = yield this.getEnvironmentStatus({ pluginName });
+ const needUserInput = Object.entries(statuses)
+ .map(([name, status]) => (Object.assign({}, status, { name })))
+ .filter(status => status.needUserInput === true);
+ if (!allowUserInput && needUserInput.length > 0) {
+ const names = needUserInput.map(s => s.name).join(", ");
+ const msgPrefix = needUserInput.length === 1
+ ? `Plugin ${names} has been updated or hasn't been configured, and requires user input.`
+ : `Plugins ${names} have been updated or haven't been configured, and require user input.`;
+ throw new exceptions_1.ConfigurationError(`${msgPrefix}. Please run \`garden init\` and then re-run this command.`, { statuses });
+ }
+ const output = {};
+ // sequentially go through the preparation steps, to allow plugins to request user input
+ for (const [name, handler] of Object.entries(handlers)) {
+ const status = statuses[name] || { ready: false };
+ if (status.ready && !force) {
+ continue;
+ }
+ const envLogEntry = (logEntry || this.garden.log).info({
+ status: "active",
+ section: name,
+ msg: "Preparing environment...",
+ });
+ yield handler(Object.assign({}, this.commonParams(handler), { force, status, logEntry: envLogEntry }));
+ envLogEntry.setSuccess("Configured");
+ output[name] = true;
+ }
+ return output;
+ });
+ }
+ cleanupEnvironment({ pluginName }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const handlers = this.garden.getActionHandlers("cleanupEnvironment", pluginName);
+ yield Bluebird.each(lodash_1.values(handlers), h => h(Object.assign({}, this.commonParams(h))));
+ return this.getEnvironmentStatus({ pluginName });
+ });
+ }
+ getSecret(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const { pluginName } = params;
+ return this.callActionHandler({ actionType: "getSecret", pluginName, params: lodash_1.omit(params, ["pluginName"]) });
+ });
+ }
+ setSecret(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const { pluginName } = params;
+ return this.callActionHandler({ actionType: "setSecret", pluginName, params: lodash_1.omit(params, ["pluginName"]) });
+ });
+ }
+ deleteSecret(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const { pluginName } = params;
+ return this.callActionHandler({ actionType: "deleteSecret", pluginName, params: lodash_1.omit(params, ["pluginName"]) });
+ });
+ }
+ //endregion
+ //===========================================================================
+ //region Module Actions
+ //===========================================================================
+ getBuildStatus(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.callModuleHandler({
+ params,
+ actionType: "getBuildStatus",
+ defaultHandler: () => __awaiter(this, void 0, void 0, function* () { return ({ ready: false }); }),
+ });
+ });
+ }
+ build(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ yield this.garden.buildDir.syncDependencyProducts(params.module);
+ return this.callModuleHandler({ params, actionType: "build" });
+ });
+ }
+ pushModule(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.callModuleHandler({ params, actionType: "pushModule", defaultHandler: dummyPushHandler });
+ });
+ }
+ publishModule(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.callModuleHandler({ params, actionType: "publishModule", defaultHandler: dummyPublishHandler });
+ });
+ }
+ runModule(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.callModuleHandler({ params, actionType: "runModule" });
+ });
+ }
+ testModule(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.callModuleHandler({ params, actionType: "testModule" });
+ });
+ }
+ getTestResult(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.callModuleHandler({
+ params,
+ actionType: "getTestResult",
+ defaultHandler: () => __awaiter(this, void 0, void 0, function* () { return null; }),
+ });
+ });
+ }
+ //endregion
+ //===========================================================================
+ //region Service Actions
+ //===========================================================================
+ getServiceStatus(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.callServiceHandler({ params, actionType: "getServiceStatus" });
+ });
+ }
+ deployService(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.callServiceHandler({ params, actionType: "deployService" });
+ });
+ }
+ deleteService(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const logEntry = this.garden.log.info({
+ section: params.service.name,
+ msg: "Deleting...",
+ status: "active",
+ });
+ return this.callServiceHandler({
+ params: Object.assign({}, params, { logEntry }),
+ actionType: "deleteService",
+ defaultHandler: dummyDeleteServiceHandler,
+ });
+ });
+ }
+ getServiceOutputs(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.callServiceHandler({
+ params,
+ actionType: "getServiceOutputs",
+ defaultHandler: () => __awaiter(this, void 0, void 0, function* () { return ({}); }),
+ });
+ });
+ }
+ execInService(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.callServiceHandler({ params, actionType: "execInService" });
+ });
+ }
+ getServiceLogs(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.callServiceHandler({ params, actionType: "getServiceLogs", defaultHandler: dummyLogStreamer });
+ });
+ }
+ runService(params) {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.callServiceHandler({ params, actionType: "runService" });
+ });
+ }
+ //endregion
+ //===========================================================================
+ //region Helper Methods
+ //===========================================================================
+ getStatus() {
+ return __awaiter(this, void 0, void 0, function* () {
+ const envStatus = yield this.getEnvironmentStatus({});
+ const services = lodash_1.keyBy(yield this.garden.getServices(), "name");
+ const serviceStatus = yield Bluebird.props(lodash_1.mapValues(services, (service) => __awaiter(this, void 0, void 0, function* () {
+ const dependencies = yield this.garden.getServices(service.config.dependencies);
+ const runtimeContext = yield service_1.prepareRuntimeContext(this.garden, service.module, dependencies);
+ return this.getServiceStatus({ service, runtimeContext });
+ })));
+ return {
+ providers: envStatus,
+ services: serviceStatus,
+ };
+ });
+ }
+ deployServices({ serviceNames, force = false, forceBuild = false }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const services = yield this.garden.getServices(serviceNames);
+ return process_1.processServices({
+ services,
+ garden: this.garden,
+ watch: false,
+ handler: (module) => __awaiter(this, void 0, void 0, function* () {
+ return deploy_1.getDeployTasks({
+ garden: this.garden,
+ module,
+ serviceNames,
+ force,
+ forceBuild,
+ includeDependants: false,
+ });
+ }),
+ });
+ });
+ }
+ //endregion
+ // TODO: find a nicer way to do this (like a type-safe wrapper function)
+ commonParams(handler, logEntry) {
+ return {
+ ctx: plugin_context_1.createPluginContext(this.garden, handler["pluginName"]),
+ // TODO: find a better way for handlers to log during execution
+ logEntry,
+ };
+ }
+ callActionHandler({ params, actionType, pluginName, defaultHandler }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const handler = this.garden.getActionHandler({
+ actionType,
+ pluginName,
+ defaultHandler,
+ });
+ const handlerParams = Object.assign({}, this.commonParams(handler), params);
+ return handler(handlerParams);
+ });
+ }
+ callModuleHandler({ params, actionType, defaultHandler }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ // the type system is messing me up here, not sure why I need the any cast... - j.e.
+ const { module, pluginName } = params;
+ const handler = yield this.garden.getModuleActionHandler({
+ moduleType: module.type,
+ actionType,
+ pluginName,
+ defaultHandler,
+ });
+ const handlerParams = Object.assign({}, this.commonParams(handler), lodash_1.omit(params, ["module"]), { module: lodash_1.omit(module, ["_ConfigType"]) });
+ // TODO: figure out why this doesn't compile without the function cast
+ return handler(handlerParams);
+ });
+ }
+ callServiceHandler({ params, actionType, defaultHandler }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const { service } = params;
+ const module = service.module;
+ const handler = yield this.garden.getModuleActionHandler({
+ moduleType: module.type,
+ actionType,
+ pluginName: params.pluginName,
+ defaultHandler,
+ });
+ // TODO: figure out why this doesn't compile without the casts
+ const deps = yield this.garden.getServices(service.config.dependencies);
+ const runtimeContext = (params.runtimeContext || (yield service_1.prepareRuntimeContext(this.garden, module, deps)));
+ const handlerParams = Object.assign({}, this.commonParams(handler), params, { module,
+ runtimeContext });
+ return handler(handlerParams);
+ });
+ }
+}
+exports.ActionHelper = ActionHelper;
+const dummyLogStreamer = ({ service, logEntry }) => __awaiter(this, void 0, void 0, function* () {
+ logEntry && logEntry.warn({
+ section: service.name,
+ msg: chalk_1.default.yellow(`No handler for log retrieval available for module type ${service.module.type}`),
+ });
+ return {};
+});
+const dummyPushHandler = () => __awaiter(this, void 0, void 0, function* () {
+ return { pushed: false };
+});
+const dummyPublishHandler = ({ module }) => __awaiter(this, void 0, void 0, function* () {
+ return {
+ message: chalk_1.default.yellow(`No publish handler available for module type ${module.type}`),
+ published: false,
+ };
+});
+const dummyDeleteServiceHandler = ({ module, logEntry }) => __awaiter(this, void 0, void 0, function* () {
+ const msg = `No delete service handler available for module type ${module.type}`;
+ logEntry && logEntry.setError(msg);
+ return {};
+});
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,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QUFFLE9BQU8sRUFBRSxRQUFRLEVBQXdCLEVBQUUsRUFBRTtJQUM3RSxRQUFRLElBQUksUUFBUSxDQUFDLElBQUksQ0FBQztRQUN4QixPQUFPLEVBQUUsT0FBTyxDQUFDLElBQUk7UUFDckIsR0FBRyxFQUFFLGVBQUssQ0FBQyxNQUFNLENBQUMsMERBQTBELE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUM7S0FDbkcsQ0FBQyxDQUFBO0lBQ0YsT0FBTyxFQUFFLENBQUE7QUFDWCxDQUFDLENBQUEsQ0FBQTtBQUVELE1BQU0sZ0JBQWdCLEdBQUcsR0FBUyxFQUFFO0lBQ2xDLE9BQU8sRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUE7QUFDMUIsQ0FBQyxDQUFBLENBQUE7QUFFRCxNQUFNLG1CQUFtQixHQUFHLENBQU8sRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFO0lBQy9DLE9BQU87UUFDTCxPQUFPLEVBQUUsZUFBSyxDQUFDLE1BQU0sQ0FBQyxnREFBZ0QsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3BGLFNBQVMsRUFBRSxLQUFLO0tBQ2pCLENBQUE7QUFDSCxDQUFDLENBQUEsQ0FBQTtBQUVELE1BQU0seUJBQXlCLEdBQUcsQ0FBTyxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQXVCLEVBQUUsRUFBRTtJQUNwRixNQUFNLEdBQUcsR0FBRyx1REFBdUQsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFBO0lBQ2hGLFFBQVEsSUFBSSxRQUFRLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBQ2xDLE9BQU8sRUFBRSxDQUFBO0FBQ1gsQ0FBQyxDQUFBLENBQUEiLCJmaWxlIjoiYWN0aW9ucy5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgQmx1ZWJpcmQgPSByZXF1aXJlKFwiYmx1ZWJpcmRcIilcbmltcG9ydCBjaGFsayBmcm9tIFwiY2hhbGtcIlxuaW1wb3J0IHsgR2FyZGVuIH0gZnJvbSBcIi4vZ2FyZGVuXCJcbmltcG9ydCB7IFByaW1pdGl2ZU1hcCB9IGZyb20gXCIuL2NvbmZpZy9jb21tb25cIlxuaW1wb3J0IHsgTW9kdWxlIH0gZnJvbSBcIi4vdHlwZXMvbW9kdWxlXCJcbmltcG9ydCB7IE1vZHVsZUFjdGlvbnMsIFNlcnZpY2VBY3Rpb25zLCBQbHVnaW5BY3Rpb25zIH0gZnJvbSBcIi4vdHlwZXMvcGx1Z2luL3BsdWdpblwiXG5pbXBvcnQge1xuICBCdWlsZFJlc3VsdCxcbiAgQnVpbGRTdGF0dXMsXG4gIERlbGV0ZVNlY3JldFJlc3VsdCxcbiAgRW52aXJvbm1lbnRTdGF0dXNNYXAsXG4gIEV4ZWNJblNlcnZpY2VSZXN1bHQsXG4gIEdldFNlY3JldFJlc3VsdCxcbiAgR2V0U2VydmljZUxvZ3NSZXN1bHQsXG4gIE1vZHVsZUFjdGlvbk91dHB1dHMsXG4gIFB1c2hSZXN1bHQsXG4gIFJ1blJlc3VsdCxcbiAgU2VydmljZUFjdGlvbk91dHB1dHMsXG4gIFNldFNlY3JldFJlc3VsdCxcbiAgVGVzdFJlc3VsdCxcbiAgUGx1Z2luQWN0aW9uT3V0cHV0cyxcbiAgUHVibGlzaFJlc3VsdCxcbn0gZnJvbSBcIi4vdHlwZXMvcGx1Z2luL291dHB1dHNcIlxuaW1wb3J0IHtcbiAgQnVpbGRNb2R1bGVQYXJhbXMsXG4gIERlbGV0ZVNlY3JldFBhcmFtcyxcbiAgRGVwbG95U2VydmljZVBhcmFtcyxcbiAgRGVsZXRlU2VydmljZVBhcmFtcyxcbiAgRXhlY0luU2VydmljZVBhcmFtcyxcbiAgR2V0U2VjcmV0UGFyYW1zLFxuICBHZXRCdWlsZFN0YXR1c1BhcmFtcyxcbiAgR2V0U2VydmljZUxvZ3NQYXJhbXMsXG4gIEdldFNlcnZpY2VPdXRwdXRzUGFyYW1zLFxuICBHZXRTZXJ2aWNlU3RhdHVzUGFyYW1zLFxuICBHZXRUZXN0UmVzdWx0UGFyYW1zLFxuICBNb2R1bGVBY3Rpb25QYXJhbXMsXG4gIFBsdWdpbkFjdGlvbkNvbnRleHRQYXJhbXMsXG4gIFBsdWdpbkFjdGlvblBhcmFtcyxcbiAgUGx1Z2luQWN0aW9uUGFyYW1zQmFzZSxcbiAgUGx1Z2luU2VydmljZUFjdGlvblBhcmFtc0Jhc2UsXG4gIFB1c2hNb2R1bGVQYXJhbXMsXG4gIFJ1bk1vZHVsZVBhcmFtcyxcbiAgUnVuU2VydmljZVBhcmFtcyxcbiAgU2VydmljZUFjdGlvblBhcmFtcyxcbiAgU2V0U2VjcmV0UGFyYW1zLFxuICBUZXN0TW9kdWxlUGFyYW1zLFxuICBHZXRFbnZpcm9ubWVudFN0YXR1c1BhcmFtcyxcbiAgUGx1Z2luTW9kdWxlQWN0aW9uUGFyYW1zQmFzZSxcbiAgUHVibGlzaE1vZHVsZVBhcmFtcyxcbn0gZnJvbSBcIi4vdHlwZXMvcGx1Z2luL3BhcmFtc1wiXG5pbXBvcnQge1xuICBTZXJ2aWNlLFxuICBTZXJ2aWNlU3RhdHVzLFxuICBwcmVwYXJlUnVudGltZUNvbnRleHQsXG59IGZyb20gXCIuL3R5cGVzL3NlcnZpY2VcIlxuaW1wb3J0IHsgbWFwVmFsdWVzLCB2YWx1ZXMsIGtleUJ5LCBvbWl0IH0gZnJvbSBcImxvZGFzaFwiXG5pbXBvcnQgeyBPbWl0IH0gZnJvbSBcIi4vdXRpbC91dGlsXCJcbmltcG9ydCB7IFJ1bnRpbWVDb250ZXh0IH0gZnJvbSBcIi4vdHlwZXMvc2VydmljZVwiXG5pbXBvcnQgeyBwcm9jZXNzU2VydmljZXMsIFByb2Nlc3NSZXN1bHRzIH0gZnJvbSBcIi4vcHJvY2Vzc1wiXG5pbXBvcnQgeyBnZXREZXBsb3lUYXNrcyB9IGZyb20gXCIuL3Rhc2tzL2RlcGxveVwiXG5pbXBvcnQgeyBMb2dFbnRyeSB9IGZyb20gXCIuL2xvZ2dlci9sb2ctZW50cnlcIlxuaW1wb3J0IHsgY3JlYXRlUGx1Z2luQ29udGV4dCB9IGZyb20gXCIuL3BsdWdpbi1jb250ZXh0XCJcbmltcG9ydCB7IENsZWFudXBFbnZpcm9ubWVudFBhcmFtcyB9IGZyb20gXCIuL3R5cGVzL3BsdWdpbi9wYXJhbXNcIlxuaW1wb3J0IHsgQ29uZmlndXJhdGlvbkVycm9yIH0gZnJvbSBcIi4vZXhjZXB0aW9uc1wiXG5cbnR5cGUgVHlwZUd1YXJkID0ge1xuICByZWFkb25seSBbUCBpbiBrZXlvZiAoUGx1Z2luQWN0aW9uUGFyYW1zIHwgTW9kdWxlQWN0aW9uUGFyYW1zPGFueT4pXTogKC4uLmFyZ3M6IGFueVtdKSA9PiBQcm9taXNlPGFueT5cbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb250ZXh0U3RhdHVzIHtcbiAgcHJvdmlkZXJzOiBFbnZpcm9ubWVudFN0YXR1c01hcFxuICBzZXJ2aWNlczogeyBbbmFtZTogc3RyaW5nXTogU2VydmljZVN0YXR1cyB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRGVwbG95U2VydmljZXNQYXJhbXMge1xuICBzZXJ2aWNlTmFtZXM/OiBzdHJpbmdbXSxcbiAgZm9yY2U/OiBib29sZWFuXG4gIGZvcmNlQnVpbGQ/OiBib29sZWFuXG59XG5cbi8vIGF2b2lkIGhhdmluZyB0byBzcGVjaWZ5IGNvbW1vbiBwYXJhbXMgb24gZWFjaCBhY3Rpb24gaGVscGVyIGNhbGxcbnR5cGUgQWN0aW9uSGVscGVyUGFyYW1zPFQgZXh0ZW5kcyBQbHVnaW5BY3Rpb25QYXJhbXNCYXNlPiA9XG4gIE9taXQ8VCwga2V5b2YgUGx1Z2luQWN0aW9uQ29udGV4dFBhcmFtcz4gJiB7IHBsdWdpbk5hbWU/OiBzdHJpbmcgfVxudHlwZSBNb2R1bGVBY3Rpb25IZWxwZXJQYXJhbXM8VCBleHRlbmRzIFBsdWdpbk1vZHVsZUFjdGlvblBhcmFtc0Jhc2U+ID1cbiAgT21pdDxULCBrZXlvZiBQbHVnaW5BY3Rpb25Db250ZXh0UGFyYW1zPiAmIHsgcGx1Z2luTmFtZT86IHN0cmluZyB9XG4vLyBhZGRpdGlvbmFsbHkgbWFrZSBydW50aW1lQ29udGV4dCBwYXJhbSBvcHRpb25hbFxudHlwZSBTZXJ2aWNlQWN0aW9uSGVscGVyUGFyYW1zPFQgZXh0ZW5kcyBQbHVnaW5TZXJ2aWNlQWN0aW9uUGFyYW1zQmFzZT4gPVxuICBPbWl0PFQsIFwibW9kdWxlXCIgfCBcInJ1bnRpbWVDb250ZXh0XCIgfCBrZXlvZiBQbHVnaW5BY3Rpb25Db250ZXh0UGFyYW1zPlxuICAmIHsgcnVudGltZUNvbnRleHQ/OiBSdW50aW1lQ29udGV4dCwgcGx1Z2luTmFtZT86IHN0cmluZyB9XG5cbnR5cGUgUmVxdWlyZVBsdWdpbk5hbWU8VD4gPSBUICYgeyBwbHVnaW5OYW1lOiBzdHJpbmcgfVxuXG5leHBvcnQgY2xhc3MgQWN0aW9uSGVscGVyIGltcGxlbWVudHMgVHlwZUd1YXJkIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBnYXJkZW46IEdhcmRlbikgeyB9XG5cbiAgLy89PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy9yZWdpb24gRW52aXJvbm1lbnQgQWN0aW9uc1xuICAvLz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIGFzeW5jIGdldEVudmlyb25tZW50U3RhdHVzKFxuICAgIHsgcGx1Z2luTmFtZSB9OiBBY3Rpb25IZWxwZXJQYXJhbXM8R2V0RW52aXJvbm1lbnRTdGF0dXNQYXJhbXM+LFxuICApOiBQcm9taXNlPEVudmlyb25tZW50U3RhdHVzTWFwPiB7XG4gICAgY29uc3QgaGFuZGxlcnMgPSB0aGlzLmdhcmRlbi5nZXRBY3Rpb25IYW5kbGVycyhcImdldEVudmlyb25tZW50U3RhdHVzXCIsIHBsdWdpbk5hbWUpXG4gICAgcmV0dXJuIEJsdWViaXJkLnByb3BzKG1hcFZhbHVlcyhoYW5kbGVycywgaCA9PiBoKHsgLi4udGhpcy5jb21tb25QYXJhbXMoaCkgfSkpKVxuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrcyBlbnZpcm9ubWVudCBzdGF0dXMgYW5kIGNhbGxzIHByZXBhcmVFbnZpcm9ubWVudCBmb3IgZWFjaCBwcm92aWRlciB0aGF0IGlzbid0IGZsYWdnZWQgYXMgcmVhZHkuXG4gICAqXG4gICAqIElmIGFueSBvZiB0aGUgZ2V0RW52aXJvbm1lbnRTdGF0dXMgaGFuZGxlcnMgcmV0dXJucyBuZWVkVXNlcklucHV0PXRydWUsIHRoaXMgdGhyb3dzIGFuZCBndWlkZXMgdGhlIHVzZXIgdG9cbiAgICogcnVuIGBnYXJkZW4gaW5pdGBcbiAgICovXG4gIGFzeW5jIHByZXBhcmVFbnZpcm9ubWVudChcbiAgICB7IGZvcmNlID0gZmFsc2UsIHBsdWdpbk5hbWUsIGxvZ0VudHJ5LCBhbGxvd1VzZXJJbnB1dCA9IGZhbHNlIH06XG4gICAgICB7IGZvcmNlPzogYm9vbGVhbiwgcGx1Z2luTmFtZT86IHN0cmluZywgbG9nRW50cnk/OiBMb2dFbnRyeSwgYWxsb3dVc2VySW5wdXQ/OiBib29sZWFuIH0sXG4gICkge1xuICAgIGNvbnN0IGhhbmRsZXJzID0gdGhpcy5nYXJkZW4uZ2V0QWN0aW9uSGFuZGxlcnMoXCJwcmVwYXJlRW52aXJvbm1lbnRcIiwgcGx1Z2luTmFtZSlcbiAgICBjb25zdCBzdGF0dXNlcyA9IGF3YWl0IHRoaXMuZ2V0RW52aXJvbm1lbnRTdGF0dXMoeyBwbHVnaW5OYW1lIH0pXG5cbiAgICBjb25zdCBuZWVkVXNlcklucHV0ID0gT2JqZWN0LmVudHJpZXMoc3RhdHVzZXMpXG4gICAgICAubWFwKChbbmFtZSwgc3RhdHVzXSkgPT4gKHsgLi4uc3RhdHVzLCBuYW1lIH0pKVxuICAgICAgLmZpbHRlcihzdGF0dXMgPT4gc3RhdHVzLm5lZWRVc2VySW5wdXQgPT09IHRydWUpXG5cbiAgICBpZiAoIWFsbG93VXNlcklucHV0ICYmIG5lZWRVc2VySW5wdXQubGVuZ3RoID4gMCkge1xuICAgICAgY29uc3QgbmFtZXMgPSBuZWVkVXNlcklucHV0Lm1hcChzID0+IHMubmFtZSkuam9pbihcIiwgXCIpXG4gICAgICBjb25zdCBtc2dQcmVmaXggPSBuZWVkVXNlcklucHV0Lmxlbmd0aCA9PT0gMVxuICAgICAgICA/IGBQbHVnaW4gJHtuYW1lc30gaGFzIGJlZW4gdXBkYXRlZCBvciBoYXNuJ3QgYmVlbiBjb25maWd1cmVkLCBhbmQgcmVxdWlyZXMgdXNlciBpbnB1dC5gXG4gICAgICAgIDogYFBsdWdpbnMgJHtuYW1lc30gaGF2ZSBiZWVuIHVwZGF0ZWQgb3IgaGF2ZW4ndCBiZWVuIGNvbmZpZ3VyZWQsIGFuZCByZXF1aXJlIHVzZXIgaW5wdXQuYFxuXG4gICAgICB0aHJvdyBuZXcgQ29uZmlndXJhdGlvbkVycm9yKFxuICAgICAgICBgJHttc2dQcmVmaXh9LiBQbGVhc2UgcnVuIFxcYGdhcmRlbiBpbml0XFxgIGFuZCB0aGVuIHJlLXJ1biB0aGlzIGNvbW1hbmQuYCxcbiAgICAgICAgeyBzdGF0dXNlcyB9LFxuICAgICAgKVxuICAgIH1cblxuICAgIGNvbnN0IG91dHB1dCA9IHt9XG5cbiAgICAvLyBzZXF1ZW50aWFsbHkgZ28gdGhyb3VnaCB0aGUgcHJlcGFyYXRpb24gc3RlcHMsIHRvIGFsbG93IHBsdWdpbnMgdG8gcmVxdWVzdCB1c2VyIGlucHV0XG4gICAgZm9yIChjb25zdCBbbmFtZSwgaGFuZGxlcl0gb2YgT2JqZWN0LmVudHJpZXMoaGFuZGxlcnMpKSB7XG4gICAgICBjb25zdCBzdGF0dXMgPSBzdGF0dXNlc1tuYW1lXSB8fCB7IHJlYWR5OiBmYWxzZSB9XG5cbiAgICAgIGlmIChzdGF0dXMucmVhZHkgJiYgIWZvcmNlKSB7XG4gICAgICAgIGNvbnRpbnVlXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGVudkxvZ0VudHJ5ID0gKGxvZ0VudHJ5IHx8IHRoaXMuZ2FyZGVuLmxvZykuaW5mbyh7XG4gICAgICAgIHN0YXR1czogXCJhY3RpdmVcIixcbiAgICAgICAgc2VjdGlvbjogbmFtZSxcbiAgICAgICAgbXNnOiBcIlByZXBhcmluZyBlbnZpcm9ubWVudC4uLlwiLFxuICAgICAgfSlcblxuICAgICAgYXdhaXQgaGFuZGxlcih7IC4uLnRoaXMuY29tbW9uUGFyYW1zKGhhbmRsZXIpLCBmb3JjZSwgc3RhdHVzLCBsb2dFbnRyeTogZW52TG9nRW50cnkgfSlcblxuICAgICAgZW52TG9nRW50cnkuc2V0U3VjY2VzcyhcIkNvbmZpZ3VyZWRcIilcblxuICAgICAgb3V0cHV0W25hbWVdID0gdHJ1ZVxuICAgIH1cblxuICAgIHJldHVybiBvdXRwdXRcbiAgfVxuXG4gIGFzeW5jIGNsZWFudXBFbnZpcm9ubWVudChcbiAgICB7IHBsdWdpbk5hbWUgfTogQWN0aW9uSGVscGVyUGFyYW1zPENsZWFudXBFbnZpcm9ubWVudFBhcmFtcz4sXG4gICk6IFByb21pc2U8RW52aXJvbm1lbnRTdGF0dXNNYXA+IHtcbiAgICBjb25zdCBoYW5kbGVycyA9IHRoaXMuZ2FyZGVuLmdldEFjdGlvbkhhbmRsZXJzKFwiY2xlYW51cEVudmlyb25tZW50XCIsIHBsdWdpbk5hbWUpXG4gICAgYXdhaXQgQmx1ZWJpcmQuZWFjaCh2YWx1ZXMoaGFuZGxlcnMpLCBoID0+IGgoeyAuLi50aGlzLmNvbW1vblBhcmFtcyhoKSB9KSlcbiAgICByZXR1cm4gdGhpcy5nZXRFbnZpcm9ubWVudFN0YXR1cyh7IHBsdWdpbk5hbWUgfSlcbiAgfVxuXG4gIGFzeW5jIGdldFNlY3JldChwYXJhbXM6IFJlcXVpcmVQbHVnaW5OYW1lPEFjdGlvbkhlbHBlclBhcmFtczxHZXRTZWNyZXRQYXJhbXM+Pik6IFByb21pc2U8R2V0U2VjcmV0UmVzdWx0PiB7XG4gICAgY29uc3QgeyBwbHVnaW5OYW1lIH0gPSBwYXJhbXNcbiAgICByZXR1cm4gdGhpcy5jYWxsQWN0aW9uSGFuZGxlcih7IGFjdGlvblR5cGU6IFwiZ2V0U2VjcmV0XCIsIHBsdWdpbk5hbWUsIHBhcmFtczogb21pdChwYXJhbXMsIFtcInBsdWdpbk5hbWVcIl0pIH0pXG4gIH1cblxuICBhc3luYyBzZXRTZWNyZXQocGFyYW1zOiBSZXF1aXJlUGx1Z2luTmFtZTxBY3Rpb25IZWxwZXJQYXJhbXM8U2V0U2VjcmV0UGFyYW1zPj4pOiBQcm9taXNlPFNldFNlY3JldFJlc3VsdD4ge1xuICAgIGNvbnN0IHsgcGx1Z2luTmFtZSB9ID0gcGFyYW1zXG4gICAgcmV0dXJuIHRoaXMuY2FsbEFjdGlvbkhhbmRsZXIoeyBhY3Rpb25UeXBlOiBcInNldFNlY3JldFwiLCBwbHVnaW5OYW1lLCBwYXJhbXM6IG9taXQocGFyYW1zLCBbXCJwbHVnaW5OYW1lXCJdKSB9KVxuICB9XG5cbiAgYXN5bmMgZGVsZXRlU2VjcmV0KHBhcmFtczogUmVxdWlyZVBsdWdpbk5hbWU8QWN0aW9uSGVscGVyUGFyYW1zPERlbGV0ZVNlY3JldFBhcmFtcz4+KTogUHJvbWlzZTxEZWxldGVTZWNyZXRSZXN1bHQ+IHtcbiAgICBjb25zdCB7IHBsdWdpbk5hbWUgfSA9IHBhcmFtc1xuICAgIHJldHVybiB0aGlzLmNhbGxBY3Rpb25IYW5kbGVyKHsgYWN0aW9uVHlwZTogXCJkZWxldGVTZWNyZXRcIiwgcGx1Z2luTmFtZSwgcGFyYW1zOiBvbWl0KHBhcmFtcywgW1wicGx1Z2luTmFtZVwiXSkgfSlcbiAgfVxuXG4gIC8vZW5kcmVnaW9uXG5cbiAgLy89PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy9yZWdpb24gTW9kdWxlIEFjdGlvbnNcbiAgLy89PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICBhc3luYyBnZXRCdWlsZFN0YXR1czxUIGV4dGVuZHMgTW9kdWxlPihcbiAgICBwYXJhbXM6IE1vZHVsZUFjdGlvbkhlbHBlclBhcmFtczxHZXRCdWlsZFN0YXR1c1BhcmFtczxUPj4sXG4gICk6IFByb21pc2U8QnVpbGRTdGF0dXM+IHtcbiAgICByZXR1cm4gdGhpcy5jYWxsTW9kdWxlSGFuZGxlcih7XG4gICAgICBwYXJhbXMsXG4gICAgICBhY3Rpb25UeXBlOiBcImdldEJ1aWxkU3RhdHVzXCIsXG4gICAgICBkZWZhdWx0SGFuZGxlcjogYXN5bmMgKCkgPT4gKHsgcmVhZHk6IGZhbHNlIH0pLFxuICAgIH0pXG4gIH1cblxuICBhc3luYyBidWlsZDxUIGV4dGVuZHMgTW9kdWxlPihwYXJhbXM6IE1vZHVsZUFjdGlvbkhlbHBlclBhcmFtczxCdWlsZE1vZHVsZVBhcmFtczxUPj4pOiBQcm9taXNlPEJ1aWxkUmVzdWx0PiB7XG4gICAgYXdhaXQgdGhpcy5nYXJkZW4uYnVpbGREaXIuc3luY0RlcGVuZGVuY3lQcm9kdWN0cyhwYXJhbXMubW9kdWxlKVxuICAgIHJldHVybiB0aGlzLmNhbGxNb2R1bGVIYW5kbGVyKHsgcGFyYW1zLCBhY3Rpb25UeXBlOiBcImJ1aWxkXCIgfSlcbiAgfVxuXG4gIGFzeW5jIHB1c2hNb2R1bGU8VCBleHRlbmRzIE1vZHVsZT4ocGFyYW1zOiBNb2R1bGVBY3Rpb25IZWxwZXJQYXJhbXM8UHVzaE1vZHVsZVBhcmFtczxUPj4pOiBQcm9taXNlPFB1c2hSZXN1bHQ+IHtcbiAgICByZXR1cm4gdGhpcy5jYWxsTW9kdWxlSGFuZGxlcih7IHBhcmFtcywgYWN0aW9uVHlwZTogXCJwdXNoTW9kdWxlXCIsIGRlZmF1bHRIYW5kbGVyOiBkdW1teVB1c2hIYW5kbGVyIH0pXG4gIH1cblxuICBhc3luYyBwdWJsaXNoTW9kdWxlPFQgZXh0ZW5kcyBNb2R1bGU+KFxuICAgIHBhcmFtczogTW9kdWxlQWN0aW9uSGVscGVyUGFyYW1zPFB1Ymxpc2hNb2R1bGVQYXJhbXM8VD4+LFxuICApOiBQcm9taXNlPFB1Ymxpc2hSZXN1bHQ+IHtcbiAgICByZXR1cm4gdGhpcy5jYWxsTW9kdWxlSGFuZGxlcih7IHBhcmFtcywgYWN0aW9uVHlwZTogXCJwdWJsaXNoTW9kdWxlXCIsIGRlZmF1bHRIYW5kbGVyOiBkdW1teVB1Ymxpc2hIYW5kbGVyIH0pXG4gIH1cblxuICBhc3luYyBydW5Nb2R1bGU8VCBleHRlbmRzIE1vZHVsZT4ocGFyYW1zOiBNb2R1bGVBY3Rpb25IZWxwZXJQYXJhbXM8UnVuTW9kdWxlUGFyYW1zPFQ+Pik6IFByb21pc2U8UnVuUmVzdWx0PiB7XG4gICAgcmV0dXJuIHRoaXMuY2FsbE1vZHVsZUhhbmRsZXIoeyBwYXJhbXMsIGFjdGlvblR5cGU6IFwicnVuTW9kdWxlXCIgfSlcbiAgfVxuXG4gIGFzeW5jIHRlc3RNb2R1bGU8VCBleHRlbmRzIE1vZHVsZT4ocGFyYW1zOiBNb2R1bGVBY3Rpb25IZWxwZXJQYXJhbXM8VGVzdE1vZHVsZVBhcmFtczxUPj4pOiBQcm9taXNlPFRlc3RSZXN1bHQ+IHtcbiAgICByZXR1cm4gdGhpcy5jYWxsTW9kdWxlSGFuZGxlcih7IHBhcmFtcywgYWN0aW9uVHlwZTogXCJ0ZXN0TW9kdWxlXCIgfSlcbiAgfVxuXG4gIGFzeW5jIGdldFRlc3RSZXN1bHQ8VCBleHRlbmRzIE1vZHVsZT4oXG4gICAgcGFyYW1zOiBNb2R1bGVBY3Rpb25IZWxwZXJQYXJhbXM8R2V0VGVzdFJlc3VsdFBhcmFtczxUPj4sXG4gICk6IFByb21pc2U8VGVzdFJlc3VsdCB8IG51bGw+IHtcbiAgICByZXR1cm4gdGhpcy5jYWxsTW9kdWxlSGFuZGxlcih7XG4gICAgICBwYXJhbXMsXG4gICAgICBhY3Rpb25UeXBlOiBcImdldFRlc3RSZXN1bHRcIixcbiAgICAgIGRlZmF1bHRIYW5kbGVyOiBhc3luYyAoKSA9PiBudWxsLFxuICAgIH0pXG4gIH1cblxuICAvL2VuZHJlZ2lvblxuXG4gIC8vPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vcmVnaW9uIFNlcnZpY2UgQWN0aW9uc1xuICAvLz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIGFzeW5jIGdldFNlcnZpY2VTdGF0dXMocGFyYW1zOiBTZXJ2aWNlQWN0aW9uSGVscGVyUGFyYW1zPEdldFNlcnZpY2VTdGF0dXNQYXJhbXM+KTogUHJvbWlzZTxTZXJ2aWNlU3RhdHVzPiB7XG4gICAgcmV0dXJuIHRoaXMuY2FsbFNlcnZpY2VIYW5kbGVyKHsgcGFyYW1zLCBhY3Rpb25UeXBlOiBcImdldFNlcnZpY2VTdGF0dXNcIiB9KVxuICB9XG5cbiAgYXN5bmMgZGVwbG95U2VydmljZShwYXJhbXM6IFNlcnZpY2VBY3Rpb25IZWxwZXJQYXJhbXM8RGVwbG95U2VydmljZVBhcmFtcz4pOiBQcm9taXNlPFNlcnZpY2VTdGF0dXM+IHtcbiAgICByZXR1cm4gdGhpcy5jYWxsU2VydmljZUhhbmRsZXIoeyBwYXJhbXMsIGFjdGlvblR5cGU6IFwiZGVwbG95U2VydmljZVwiIH0pXG4gIH1cblxuICBhc3luYyBkZWxldGVTZXJ2aWNlKHBhcmFtczogU2VydmljZUFjdGlvbkhlbHBlclBhcmFtczxEZWxldGVTZXJ2aWNlUGFyYW1zPik6IFByb21pc2U8U2VydmljZVN0YXR1cz4ge1xuICAgIGNvbnN0IGxvZ0VudHJ5ID0gdGhpcy5nYXJkZW4ubG9nLmluZm8oe1xuICAgICAgc2VjdGlvbjogcGFyYW1zLnNlcnZpY2UubmFtZSxcbiAgICAgIG1zZzogXCJEZWxldGluZy4uLlwiLFxuICAgICAgc3RhdHVzOiBcImFjdGl2ZVwiLFxuICAgIH0pXG4gICAgcmV0dXJuIHRoaXMuY2FsbFNlcnZpY2VIYW5kbGVyKHtcbiAgICAgIHBhcmFtczogeyAuLi5wYXJhbXMsIGxvZ0VudHJ5IH0sXG4gICAgICBhY3Rpb25UeXBlOiBcImRlbGV0ZVNlcnZpY2VcIixcbiAgICAgIGRlZmF1bHRIYW5kbGVyOiBkdW1teURlbGV0ZVNlcnZpY2VIYW5kbGVyLFxuICAgIH0pXG4gIH1cblxuICBhc3luYyBnZXRTZXJ2aWNlT3V0cHV0cyhwYXJhbXM6IFNlcnZpY2VBY3Rpb25IZWxwZXJQYXJhbXM8R2V0U2VydmljZU91dHB1dHNQYXJhbXM+KTogUHJvbWlzZTxQcmltaXRpdmVNYXA+IHtcbiAgICByZXR1cm4gdGhpcy5jYWxsU2VydmljZUhhbmRsZXIoe1xuICAgICAgcGFyYW1zLFxuICAgICAgYWN0aW9uVHlwZTogXCJnZXRTZXJ2aWNlT3V0cHV0c1wiLFxuICAgICAgZGVmYXVsdEhhbmRsZXI6IGFzeW5jICgpID0+ICh7fSksXG4gICAgfSlcbiAgfVxuXG4gIGFzeW5jIGV4ZWNJblNlcnZpY2UocGFyYW1zOiBTZXJ2aWNlQWN0aW9uSGVscGVyUGFyYW1zPEV4ZWNJblNlcnZpY2VQYXJhbXM+KTogUHJvbWlzZTxFeGVjSW5TZXJ2aWNlUmVzdWx0PiB7XG4gICAgcmV0dXJuIHRoaXMuY2FsbFNlcnZpY2VIYW5kbGVyKHsgcGFyYW1zLCBhY3Rpb25UeXBlOiBcImV4ZWNJblNlcnZpY2VcIiB9KVxuICB9XG5cbiAgYXN5bmMgZ2V0U2VydmljZUxvZ3MocGFyYW1zOiBTZXJ2aWNlQWN0aW9uSGVscGVyUGFyYW1zPEdldFNlcnZpY2VMb2dzUGFyYW1zPik6IFByb21pc2U8R2V0U2VydmljZUxvZ3NSZXN1bHQ+IHtcbiAgICByZXR1cm4gdGhpcy5jYWxsU2VydmljZUhhbmRsZXIoeyBwYXJhbXMsIGFjdGlvblR5cGU6IFwiZ2V0U2VydmljZUxvZ3NcIiwgZGVmYXVsdEhhbmRsZXI6IGR1bW15TG9nU3RyZWFtZXIgfSlcbiAgfVxuXG4gIGFzeW5jIHJ1blNlcnZpY2UocGFyYW1zOiBTZXJ2aWNlQWN0aW9uSGVscGVyUGFyYW1zPFJ1blNlcnZpY2VQYXJhbXM+KTogUHJvbWlzZTxSdW5SZXN1bHQ+IHtcbiAgICByZXR1cm4gdGhpcy5jYWxsU2VydmljZUhhbmRsZXIoeyBwYXJhbXMsIGFjdGlvblR5cGU6IFwicnVuU2VydmljZVwiIH0pXG4gIH1cblxuICAvL2VuZHJlZ2lvblxuXG4gIC8vPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vcmVnaW9uIEhlbHBlciBNZXRob2RzXG4gIC8vPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgYXN5bmMgZ2V0U3RhdHVzKCk6IFByb21pc2U8Q29udGV4dFN0YXR1cz4ge1xuICAgIGNvbnN0IGVudlN0YXR1czogRW52aXJvbm1lbnRTdGF0dXNNYXAgPSBhd2FpdCB0aGlzLmdldEVudmlyb25tZW50U3RhdHVzKHt9KVxuICAgIGNvbnN0IHNlcnZpY2VzID0ga2V5QnkoYXdhaXQgdGhpcy5nYXJkZW4uZ2V0U2VydmljZXMoKSwgXCJuYW1lXCIpXG5cbiAgICBjb25zdCBzZXJ2aWNlU3RhdHVzID0gYXdhaXQgQmx1ZWJpcmQucHJvcHMobWFwVmFsdWVzKHNlcnZpY2VzLCBhc3luYyAoc2VydmljZTogU2VydmljZSkgPT4ge1xuICAgICAgY29uc3QgZGVwZW5kZW5jaWVzID0gYXdhaXQgdGhpcy5nYXJkZW4uZ2V0U2VydmljZXMoc2VydmljZS5jb25maWcuZGVwZW5kZW5jaWVzKVxuICAgICAgY29uc3QgcnVudGltZUNvbnRleHQgPSBhd2FpdCBwcmVwYXJlUnVudGltZUNvbnRleHQodGhpcy5nYXJkZW4sIHNlcnZpY2UubW9kdWxlLCBkZXBlbmRlbmNpZXMpXG4gICAgICByZXR1cm4gdGhpcy5nZXRTZXJ2aWNlU3RhdHVzKHsgc2VydmljZSwgcnVudGltZUNvbnRleHQgfSlcbiAgICB9KSlcblxuICAgIHJldHVybiB7XG4gICAgICBwcm92aWRlcnM6IGVudlN0YXR1cyxcbiAgICAgIHNlcnZpY2VzOiBzZXJ2aWNlU3RhdHVzLFxuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGRlcGxveVNlcnZpY2VzKFxuICAgIHsgc2VydmljZU5hbWVzLCBmb3JjZSA9IGZhbHNlLCBmb3JjZUJ1aWxkID0gZmFsc2UgfTogRGVwbG95U2VydmljZXNQYXJhbXMsXG4gICk6IFByb21pc2U8UHJvY2Vzc1Jlc3VsdHM+IHtcbiAgICBjb25zdCBzZXJ2aWNlcyA9IGF3YWl0IHRoaXMuZ2FyZGVuLmdldFNlcnZpY2VzKHNlcnZpY2VOYW1lcylcblxuICAgIHJldHVybiBwcm9jZXNzU2VydmljZXMoe1xuICAgICAgc2VydmljZXMsXG4gICAgICBnYXJkZW46IHRoaXMuZ2FyZGVuLFxuICAgICAgd2F0Y2g6IGZhbHNlLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKG1vZHVsZSkgPT4gZ2V0RGVwbG95VGFza3Moe1xuICAgICAgICBnYXJkZW46IHRoaXMuZ2FyZGVuLFxuICAgICAgICBtb2R1bGUsXG4gICAgICAgIHNlcnZpY2VOYW1lcyxcbiAgICAgICAgZm9yY2UsXG4gICAgICAgIGZvcmNlQnVpbGQsXG4gICAgICAgIGluY2x1ZGVEZXBlbmRhbnRzOiBmYWxzZSxcbiAgICAgIH0pLFxuICAgIH0pXG4gIH1cblxuICAvL2VuZHJlZ2lvblxuXG4gIC8vIFRPRE86IGZpbmQgYSBuaWNlciB3YXkgdG8gZG8gdGhpcyAobGlrZSBhIHR5cGUtc2FmZSB3cmFwcGVyIGZ1bmN0aW9uKVxuICBwcml2YXRlIGNvbW1vblBhcmFtcyhoYW5kbGVyLCBsb2dFbnRyeT86IExvZ0VudHJ5KTogUGx1Z2luQWN0aW9uUGFyYW1zQmFzZSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGN0eDogY3JlYXRlUGx1Z2luQ29udGV4dCh0aGlzLmdhcmRlbiwgaGFuZGxlcltcInBsdWdpbk5hbWVcIl0pLFxuICAgICAgLy8gVE9ETzogZmluZCBhIGJldHRlciB3YXkgZm9yIGhhbmRsZXJzIHRvIGxvZyBkdXJpbmcgZXhlY3V0aW9uXG4gICAgICBsb2dFbnRyeSxcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGNhbGxBY3Rpb25IYW5kbGVyPFQgZXh0ZW5kcyBrZXlvZiBQbHVnaW5BY3Rpb25zPihcbiAgICB7IHBhcmFtcywgYWN0aW9uVHlwZSwgcGx1Z2luTmFtZSwgZGVmYXVsdEhhbmRsZXIgfTpcbiAgICAgIHtcbiAgICAgICAgcGFyYW1zOiBBY3Rpb25IZWxwZXJQYXJhbXM8UGx1Z2luQWN0aW9uUGFyYW1zW1RdPixcbiAgICAgICAgYWN0aW9uVHlwZTogVCxcbiAgICAgICAgcGx1Z2luTmFtZT86IHN0cmluZyxcbiAgICAgICAgZGVmYXVsdEhhbmRsZXI/OiBQbHVnaW5BY3Rpb25zW1RdLFxuICAgICAgfSxcbiAgKTogUHJvbWlzZTxQbHVnaW5BY3Rpb25PdXRwdXRzW1RdPiB7XG4gICAgY29uc3QgaGFuZGxlciA9IHRoaXMuZ2FyZGVuLmdldEFjdGlvbkhhbmRsZXIoe1xuICAgICAgYWN0aW9uVHlwZSxcbiAgICAgIHBsdWdpbk5hbWUsXG4gICAgICBkZWZhdWx0SGFuZGxlcixcbiAgICB9KVxuICAgIGNvbnN0IGhhbmRsZXJQYXJhbXM6IFBsdWdpbkFjdGlvblBhcmFtc1tUXSA9IHtcbiAgICAgIC4uLnRoaXMuY29tbW9uUGFyYW1zKGhhbmRsZXIpLFxuICAgICAgLi4uPG9iamVjdD5wYXJhbXMsXG4gICAgfVxuICAgIHJldHVybiAoPEZ1bmN0aW9uPmhhbmRsZXIpKGhhbmRsZXJQYXJhbXMpXG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGNhbGxNb2R1bGVIYW5kbGVyPFQgZXh0ZW5kcyBrZXlvZiBPbWl0PE1vZHVsZUFjdGlvbnMsIFwiZGVzY3JpYmVUeXBlXCIgfCBcInZhbGlkYXRlXCI+PihcbiAgICB7IHBhcmFtcywgYWN0aW9uVHlwZSwgZGVmYXVsdEhhbmRsZXIgfTpcbiAgICAgIHsgcGFyYW1zOiBNb2R1bGVBY3Rpb25IZWxwZXJQYXJhbXM8TW9kdWxlQWN0aW9uUGFyYW1zW1RdPiwgYWN0aW9uVHlwZTogVCwgZGVmYXVsdEhhbmRsZXI/OiBNb2R1bGVBY3Rpb25zW1RdIH0sXG4gICk6IFByb21pc2U8TW9kdWxlQWN0aW9uT3V0cHV0c1tUXT4ge1xuICAgIC8vIHRoZSB0eXBlIHN5c3RlbSBpcyBtZXNzaW5nIG1lIHVwIGhlcmUsIG5vdCBzdXJlIHdoeSBJIG5lZWQgdGhlIGFueSBjYXN0Li4uIC0gai5lLlxuICAgIGNvbnN0IHsgbW9kdWxlLCBwbHVnaW5OYW1lIH0gPSA8YW55PnBhcmFtc1xuICAgIGNvbnN0IGhhbmRsZXIgPSBhd2FpdCB0aGlzLmdhcmRlbi5nZXRNb2R1bGVBY3Rpb25IYW5kbGVyKHtcbiAgICAgIG1vZHVsZVR5cGU6IG1vZHVsZS50eXBlLFxuICAgICAgYWN0aW9uVHlwZSxcbiAgICAgIHBsdWdpbk5hbWUsXG4gICAgICBkZWZhdWx0SGFuZGxlcixcbiAgICB9KVxuICAgIGNvbnN0IGhhbmRsZXJQYXJhbXM6IGFueSA9IHtcbiAgICAgIC4uLnRoaXMuY29tbW9uUGFyYW1zKGhhbmRsZXIpLFxuICAgICAgLi4ub21pdCg8b2JqZWN0PnBhcmFtcywgW1wibW9kdWxlXCJdKSxcbiAgICAgIG1vZHVsZTogb21pdChtb2R1bGUsIFtcIl9Db25maWdUeXBlXCJdKSxcbiAgICB9XG4gICAgLy8gVE9ETzogZmlndXJlIG91dCB3aHkgdGhpcyBkb2Vzbid0IGNvbXBpbGUgd2l0aG91dCB0aGUgZnVuY3Rpb24gY2FzdFxuICAgIHJldHVybiAoPEZ1bmN0aW9uPmhhbmRsZXIpKGhhbmRsZXJQYXJhbXMpXG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGNhbGxTZXJ2aWNlSGFuZGxlcjxUIGV4dGVuZHMga2V5b2YgU2VydmljZUFjdGlvbnM+KFxuICAgIHsgcGFyYW1zLCBhY3Rpb25UeXBlLCBkZWZhdWx0SGFuZGxlciB9OlxuICAgICAgeyBwYXJhbXM6IFNlcnZpY2VBY3Rpb25IZWxwZXJQYXJhbXM8U2VydmljZUFjdGlvblBhcmFtc1tUXT4sIGFjdGlvblR5cGU6IFQsIGRlZmF1bHRIYW5kbGVyPzogU2VydmljZUFjdGlvbnNbVF0gfSxcbiAgKTogUHJvbWlzZTxTZXJ2aWNlQWN0aW9uT3V0cHV0c1tUXT4ge1xuICAgIGNvbnN0IHsgc2VydmljZSB9ID0gPGFueT5wYXJhbXNcbiAgICBjb25zdCBtb2R1bGUgPSBzZXJ2aWNlLm1vZHVsZVxuXG4gICAgY29uc3QgaGFuZGxlciA9IGF3YWl0IHRoaXMuZ2FyZGVuLmdldE1vZHVsZUFjdGlvbkhhbmRsZXIoe1xuICAgICAgbW9kdWxlVHlwZTogbW9kdWxlLnR5cGUsXG4gICAgICBhY3Rpb25UeXBlLFxuICAgICAgcGx1Z2luTmFtZTogcGFyYW1zLnBsdWdpbk5hbWUsXG4gICAgICBkZWZhdWx0SGFuZGxlcixcbiAgICB9KVxuXG4gICAgLy8gVE9ETzogZmlndXJlIG91dCB3aHkgdGhpcyBkb2Vzbid0IGNvbXBpbGUgd2l0aG91dCB0aGUgY2FzdHNcbiAgICBjb25zdCBkZXBzID0gYXdhaXQgdGhpcy5nYXJkZW4uZ2V0U2VydmljZXMoc2VydmljZS5jb25maWcuZGVwZW5kZW5jaWVzKVxuICAgIGNvbnN0IHJ1bnRpbWVDb250ZXh0ID0gKCg8YW55PnBhcmFtcykucnVudGltZUNvbnRleHQgfHwgYXdhaXQgcHJlcGFyZVJ1bnRpbWVDb250ZXh0KHRoaXMuZ2FyZGVuLCBtb2R1bGUsIGRlcHMpKVxuXG4gICAgY29uc3QgaGFuZGxlclBhcmFtczogYW55ID0ge1xuICAgICAgLi4udGhpcy5jb21tb25QYXJhbXMoaGFuZGxlciksXG4gICAgICAuLi48b2JqZWN0PnBhcmFtcyxcbiAgICAgIG1vZHVsZSxcbiAgICAgIHJ1bnRpbWVDb250ZXh0LFxuICAgIH1cblxuICAgIHJldHVybiAoPEZ1bmN0aW9uPmhhbmRsZXIpKGhhbmRsZXJQYXJhbXMpXG4gIH1cbn1cblxuY29uc3QgZHVtbXlMb2dTdHJlYW1lciA9IGFzeW5jICh7IHNlcnZpY2UsIGxvZ0VudHJ5IH06IEdldFNlcnZpY2VMb2dzUGFyYW1zKSA9PiB7XG4gIGxvZ0VudHJ5ICYmIGxvZ0VudHJ5Lndhcm4oe1xuICAgIHNlY3Rpb246IHNlcnZpY2UubmFtZSxcbiAgICBtc2c6IGNoYWxrLnllbGxvdyhgTm8gaGFuZGxlciBmb3IgbG9nIHJldHJpZXZhbCBhdmFpbGFibGUgZm9yIG1vZHVsZSB0eXBlICR7c2VydmljZS5tb2R1bGUudHlwZX1gKSxcbiAgfSlcbiAgcmV0dXJuIHt9XG59XG5cbmNvbnN0IGR1bW15UHVzaEhhbmRsZXIgPSBhc3luYyAoKSA9PiB7XG4gIHJldHVybiB7IHB1c2hlZDogZmFsc2UgfVxufVxuXG5jb25zdCBkdW1teVB1Ymxpc2hIYW5kbGVyID0gYXN5bmMgKHsgbW9kdWxlIH0pID0+IHtcbiAgcmV0dXJuIHtcbiAgICBtZXNzYWdlOiBjaGFsay55ZWxsb3coYE5vIHB1Ymxpc2ggaGFuZGxlciBhdmFpbGFibGUgZm9yIG1vZHVsZSB0eXBlICR7bW9kdWxlLnR5cGV9YCksXG4gICAgcHVibGlzaGVkOiBmYWxzZSxcbiAgfVxufVxuXG5jb25zdCBkdW1teURlbGV0ZVNlcnZpY2VIYW5kbGVyID0gYXN5bmMgKHsgbW9kdWxlLCBsb2dFbnRyeSB9OiBEZWxldGVTZXJ2aWNlUGFyYW1zKSA9PiB7XG4gIGNvbnN0IG1zZyA9IGBObyBkZWxldGUgc2VydmljZSBoYW5kbGVyIGF2YWlsYWJsZSBmb3IgbW9kdWxlIHR5cGUgJHttb2R1bGUudHlwZX1gXG4gIGxvZ0VudHJ5ICYmIGxvZ0VudHJ5LnNldEVycm9yKG1zZylcbiAgcmV0dXJuIHt9XG59XG4iXX0=
diff --git a/garden-service/build/build-dir.d.ts b/garden-service/build/build-dir.d.ts
new file mode 100644
index 00000000000..0630809de39
--- /dev/null
+++ b/garden-service/build/build-dir.d.ts
@@ -0,0 +1,13 @@
+import { Module } from "./types/module";
+export declare class BuildDir {
+ private projectRoot;
+ buildDirPath: string;
+ constructor(projectRoot: string, buildDirPath: string);
+ static factory(projectRoot: string): Promise;
+ syncFromSrc(module: Module): Promise;
+ syncDependencyProducts(module: Module): Promise;
+ clear(): Promise;
+ buildPath(moduleName: string): Promise;
+ private sync;
+}
+//# sourceMappingURL=build-dir.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/build-dir.js b/garden-service/build/build-dir.js
new file mode 100644
index 00000000000..26899f1dadd
--- /dev/null
+++ b/garden-service/build/build-dir.js
@@ -0,0 +1,110 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const bluebird_1 = require("bluebird");
+const path_1 = require("path");
+const fs_extra_1 = require("fs-extra");
+const constants_1 = require("./constants");
+const exceptions_1 = require("./exceptions");
+const module_1 = require("./types/module");
+const lodash_1 = require("lodash");
+const execa = require("execa");
+const os_1 = require("os");
+const util_1 = require("./util/util");
+// Lazily construct a directory of modules inside which all build steps are performed.
+const buildDirRelPath = path_1.join(constants_1.GARDEN_DIR_NAME, "build");
+class BuildDir {
+ constructor(projectRoot, buildDirPath) {
+ this.projectRoot = projectRoot;
+ this.buildDirPath = buildDirPath;
+ }
+ static factory(projectRoot) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const buildDirPath = path_1.join(projectRoot, buildDirRelPath);
+ yield fs_extra_1.ensureDir(buildDirPath);
+ return new BuildDir(projectRoot, buildDirPath);
+ });
+ }
+ syncFromSrc(module) {
+ return __awaiter(this, void 0, void 0, function* () {
+ yield this.sync(path_1.resolve(this.projectRoot, module.path) + path_1.sep, yield this.buildPath(module.name));
+ });
+ }
+ syncDependencyProducts(module) {
+ return __awaiter(this, void 0, void 0, function* () {
+ yield this.syncFromSrc(module);
+ const buildPath = yield this.buildPath(module.name);
+ const buildDependencies = yield module.build.dependencies;
+ const dependencyConfigs = module.build.dependencies || [];
+ yield bluebird_1.map(lodash_1.zip(buildDependencies, dependencyConfigs), ([sourceModule, depConfig]) => __awaiter(this, void 0, void 0, function* () {
+ if (!sourceModule || !depConfig || !depConfig.copy) {
+ return;
+ }
+ const sourceBuildPath = yield this.buildPath(module_1.getModuleKey(sourceModule.name, sourceModule.plugin));
+ // Sync to the module's top-level dir by default.
+ yield bluebird_1.map(depConfig.copy, (copy) => {
+ if (path_1.isAbsolute(copy.source)) {
+ throw new exceptions_1.ConfigurationError(`Source path in build dependency copy spec must be a relative path`, {
+ copySpec: copy,
+ });
+ }
+ if (path_1.isAbsolute(copy.target)) {
+ throw new exceptions_1.ConfigurationError(`Target path in build dependency copy spec must be a relative path`, {
+ copySpec: copy,
+ });
+ }
+ const sourcePath = path_1.join(sourceBuildPath, copy.source);
+ const destinationPath = path_1.join(buildPath, copy.target);
+ return this.sync(sourcePath, destinationPath);
+ });
+ }));
+ });
+ }
+ clear() {
+ return __awaiter(this, void 0, void 0, function* () {
+ yield fs_extra_1.emptyDir(this.buildDirPath);
+ });
+ }
+ buildPath(moduleName) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const path = path_1.resolve(this.buildDirPath, moduleName);
+ yield fs_extra_1.ensureDir(path);
+ return path;
+ });
+ }
+ sync(sourcePath, destinationPath) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const destinationDir = path_1.parse(destinationPath).dir;
+ yield fs_extra_1.ensureDir(destinationDir);
+ if (os_1.platform() === "win32") {
+ // this is so that the cygwin-based rsync client can deal with the paths
+ sourcePath = util_1.toCygwinPath(sourcePath);
+ destinationPath = util_1.toCygwinPath(destinationPath);
+ }
+ // the correct way to copy all contents of a folder is using a trailing slash and not a wildcard
+ sourcePath = stripWildcard(sourcePath);
+ destinationPath = stripWildcard(destinationPath);
+ yield execa("rsync", ["-rptgo", sourcePath, destinationPath]);
+ });
+ }
+}
+exports.BuildDir = BuildDir;
+function stripWildcard(path) {
+ return path.endsWith("/*") ? path.slice(0, -1) : path;
+}
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,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QUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFBO0FBQ3ZELENBQUMiLCJmaWxlIjoiYnVpbGQtZGlyLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoQykgMjAxOCBHYXJkZW4gVGVjaG5vbG9naWVzLCBJbmMuIDxpbmZvQGdhcmRlbi5pbz5cbiAqXG4gKiBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljXG4gKiBMaWNlbnNlLCB2LiAyLjAuIElmIGEgY29weSBvZiB0aGUgTVBMIHdhcyBub3QgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzXG4gKiBmaWxlLCBZb3UgY2FuIG9idGFpbiBvbmUgYXQgaHR0cDovL21vemlsbGEub3JnL01QTC8yLjAvLlxuICovXG5cbmltcG9ydCB7IG1hcCBhcyBibHVlYmlyZE1hcCB9IGZyb20gXCJibHVlYmlyZFwiXG5pbXBvcnQge1xuICBpc0Fic29sdXRlLFxuICBqb2luLFxuICBwYXJzZSxcbiAgcmVzb2x2ZSxcbiAgc2VwLFxufSBmcm9tIFwicGF0aFwiXG5pbXBvcnQge1xuICBlbXB0eURpcixcbiAgZW5zdXJlRGlyLFxufSBmcm9tIFwiZnMtZXh0cmFcIlxuaW1wb3J0IHsgR0FSREVOX0RJUl9OQU1FIH0gZnJvbSBcIi4vY29uc3RhbnRzXCJcbmltcG9ydCB7IENvbmZpZ3VyYXRpb25FcnJvciB9IGZyb20gXCIuL2V4Y2VwdGlvbnNcIlxuaW1wb3J0IHtcbiAgQnVpbGRDb3B5U3BlYyxcbiAgTW9kdWxlLFxuICBnZXRNb2R1bGVLZXksXG59IGZyb20gXCIuL3R5cGVzL21vZHVsZVwiXG5pbXBvcnQgeyB6aXAgfSBmcm9tIFwibG9kYXNoXCJcbmltcG9ydCAqIGFzIGV4ZWNhIGZyb20gXCJleGVjYVwiXG5pbXBvcnQgeyBwbGF0Zm9ybSB9IGZyb20gXCJvc1wiXG5pbXBvcnQgeyB0b0N5Z3dpblBhdGggfSBmcm9tIFwiLi91dGlsL3V0aWxcIlxuXG4vLyBMYXppbHkgY29uc3RydWN0IGEgZGlyZWN0b3J5IG9mIG1vZHVsZXMgaW5zaWRlIHdoaWNoIGFsbCBidWlsZCBzdGVwcyBhcmUgcGVyZm9ybWVkLlxuXG5jb25zdCBidWlsZERpclJlbFBhdGggPSBqb2luKEdBUkRFTl9ESVJfTkFNRSwgXCJidWlsZFwiKVxuXG5leHBvcnQgY2xhc3MgQnVpbGREaXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHByb2plY3RSb290OiBzdHJpbmcsIHB1YmxpYyBidWlsZERpclBhdGg6IHN0cmluZykgeyB9XG5cbiAgc3RhdGljIGFzeW5jIGZhY3RvcnkocHJvamVjdFJvb3Q6IHN0cmluZykge1xuICAgIGNvbnN0IGJ1aWxkRGlyUGF0aCA9IGpvaW4ocHJvamVjdFJvb3QsIGJ1aWxkRGlyUmVsUGF0aClcbiAgICBhd2FpdCBlbnN1cmVEaXIoYnVpbGREaXJQYXRoKVxuICAgIHJldHVybiBuZXcgQnVpbGREaXIocHJvamVjdFJvb3QsIGJ1aWxkRGlyUGF0aClcbiAgfVxuXG4gIGFzeW5jIHN5bmNGcm9tU3JjKG1vZHVsZTogTW9kdWxlKSB7XG4gICAgYXdhaXQgdGhpcy5zeW5jKFxuICAgICAgcmVzb2x2ZSh0aGlzLnByb2plY3RSb290LCBtb2R1bGUucGF0aCkgKyBzZXAsXG4gICAgICBhd2FpdCB0aGlzLmJ1aWxkUGF0aChtb2R1bGUubmFtZSksXG4gICAgKVxuICB9XG5cbiAgYXN5bmMgc3luY0RlcGVuZGVuY3lQcm9kdWN0cyhtb2R1bGU6IE1vZHVsZSkge1xuICAgIGF3YWl0IHRoaXMuc3luY0Zyb21TcmMobW9kdWxlKVxuICAgIGNvbnN0IGJ1aWxkUGF0aCA9IGF3YWl0IHRoaXMuYnVpbGRQYXRoKG1vZHVsZS5uYW1lKVxuICAgIGNvbnN0IGJ1aWxkRGVwZW5kZW5jaWVzID0gYXdhaXQgbW9kdWxlLmJ1aWxkLmRlcGVuZGVuY2llc1xuICAgIGNvbnN0IGRlcGVuZGVuY3lDb25maWdzID0gbW9kdWxlLmJ1aWxkLmRlcGVuZGVuY2llcyB8fCBbXVxuXG4gICAgYXdhaXQgYmx1ZWJpcmRNYXAoemlwKGJ1aWxkRGVwZW5kZW5jaWVzLCBkZXBlbmRlbmN5Q29uZmlncyksIGFzeW5jIChbc291cmNlTW9kdWxlLCBkZXBDb25maWddKSA9PiB7XG4gICAgICBpZiAoIXNvdXJjZU1vZHVsZSB8fCAhZGVwQ29uZmlnIHx8ICFkZXBDb25maWcuY29weSkge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgY29uc3Qgc291cmNlQnVpbGRQYXRoID0gYXdhaXQgdGhpcy5idWlsZFBhdGgoZ2V0TW9kdWxlS2V5KHNvdXJjZU1vZHVsZS5uYW1lLCBzb3VyY2VNb2R1bGUucGx1Z2luKSlcblxuICAgICAgLy8gU3luYyB0byB0aGUgbW9kdWxlJ3MgdG9wLWxldmVsIGRpciBieSBkZWZhdWx0LlxuICAgICAgYXdhaXQgYmx1ZWJpcmRNYXAoZGVwQ29uZmlnLmNvcHksIChjb3B5OiBCdWlsZENvcHlTcGVjKSA9PiB7XG4gICAgICAgIGlmIChpc0Fic29sdXRlKGNvcHkuc291cmNlKSkge1xuICAgICAgICAgIHRocm93IG5ldyBDb25maWd1cmF0aW9uRXJyb3IoYFNvdXJjZSBwYXRoIGluIGJ1aWxkIGRlcGVuZGVuY3kgY29weSBzcGVjIG11c3QgYmUgYSByZWxhdGl2ZSBwYXRoYCwge1xuICAgICAgICAgICAgY29weVNwZWM6IGNvcHksXG4gICAgICAgICAgfSlcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChpc0Fic29sdXRlKGNvcHkudGFyZ2V0KSkge1xuICAgICAgICAgIHRocm93IG5ldyBDb25maWd1cmF0aW9uRXJyb3IoYFRhcmdldCBwYXRoIGluIGJ1aWxkIGRlcGVuZGVuY3kgY29weSBzcGVjIG11c3QgYmUgYSByZWxhdGl2ZSBwYXRoYCwge1xuICAgICAgICAgICAgY29weVNwZWM6IGNvcHksXG4gICAgICAgICAgfSlcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHNvdXJjZVBhdGggPSBqb2luKHNvdXJjZUJ1aWxkUGF0aCwgY29weS5zb3VyY2UpXG4gICAgICAgIGNvbnN0IGRlc3RpbmF0aW9uUGF0aCA9IGpvaW4oYnVpbGRQYXRoLCBjb3B5LnRhcmdldClcbiAgICAgICAgcmV0dXJuIHRoaXMuc3luYyhzb3VyY2VQYXRoLCBkZXN0aW5hdGlvblBhdGgpXG4gICAgICB9KVxuICAgIH0pXG4gIH1cblxuICBhc3luYyBjbGVhcigpIHtcbiAgICBhd2FpdCBlbXB0eURpcih0aGlzLmJ1aWxkRGlyUGF0aClcbiAgfVxuXG4gIGFzeW5jIGJ1aWxkUGF0aChtb2R1bGVOYW1lOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIGNvbnN0IHBhdGggPSByZXNvbHZlKHRoaXMuYnVpbGREaXJQYXRoLCBtb2R1bGVOYW1lKVxuICAgIGF3YWl0IGVuc3VyZURpcihwYXRoKVxuICAgIHJldHVybiBwYXRoXG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHN5bmMoc291cmNlUGF0aDogc3RyaW5nLCBkZXN0aW5hdGlvblBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGRlc3RpbmF0aW9uRGlyID0gcGFyc2UoZGVzdGluYXRpb25QYXRoKS5kaXJcbiAgICBhd2FpdCBlbnN1cmVEaXIoZGVzdGluYXRpb25EaXIpXG5cbiAgICBpZiAocGxhdGZvcm0oKSA9PT0gXCJ3aW4zMlwiKSB7XG4gICAgICAvLyB0aGlzIGlzIHNvIHRoYXQgdGhlIGN5Z3dpbi1iYXNlZCByc3luYyBjbGllbnQgY2FuIGRlYWwgd2l0aCB0aGUgcGF0aHNcbiAgICAgIHNvdXJjZVBhdGggPSB0b0N5Z3dpblBhdGgoc291cmNlUGF0aClcbiAgICAgIGRlc3RpbmF0aW9uUGF0aCA9IHRvQ3lnd2luUGF0aChkZXN0aW5hdGlvblBhdGgpXG4gICAgfVxuXG4gICAgLy8gdGhlIGNvcnJlY3Qgd2F5IHRvIGNvcHkgYWxsIGNvbnRlbnRzIG9mIGEgZm9sZGVyIGlzIHVzaW5nIGEgdHJhaWxpbmcgc2xhc2ggYW5kIG5vdCBhIHdpbGRjYXJkXG4gICAgc291cmNlUGF0aCA9IHN0cmlwV2lsZGNhcmQoc291cmNlUGF0aClcbiAgICBkZXN0aW5hdGlvblBhdGggPSBzdHJpcFdpbGRjYXJkKGRlc3RpbmF0aW9uUGF0aClcblxuICAgIGF3YWl0IGV4ZWNhKFwicnN5bmNcIiwgW1wiLXJwdGdvXCIsIHNvdXJjZVBhdGgsIGRlc3RpbmF0aW9uUGF0aF0pXG4gIH1cbn1cblxuZnVuY3Rpb24gc3RyaXBXaWxkY2FyZChwYXRoOiBzdHJpbmcpIHtcbiAgcmV0dXJuIHBhdGguZW5kc1dpdGgoXCIvKlwiKSA/IHBhdGguc2xpY2UoMCwgLTEpIDogcGF0aFxufVxuIl19
diff --git a/garden-service/build/cache.d.ts b/garden-service/build/cache.d.ts
new file mode 100644
index 00000000000..6ff4412c029
--- /dev/null
+++ b/garden-service/build/cache.d.ts
@@ -0,0 +1,68 @@
+export declare type CacheKey = string[];
+export declare type CacheContext = string[];
+export declare type CurriedKey = string;
+export declare type CacheValue = string | number | boolean | null | object;
+export declare type CacheValues = Map;
+/**
+ * A simple in-memory cache that additionally indexes keys in a tree by a seperate context key, so that keys
+ * can be invalidated based on surrounding context.
+ *
+ * For example, we can cache the version of a directory path, and then invalidate every cached key under a
+ * parent path:
+ *
+ * ```
+ * const cache = new TreeCache()
+ *
+ * # The context parameter (last parameter) here is the path to the module source
+ * cache.set(["modules", "my-module-a"], module, ["modules", "module-path-a"])
+ * cache.set(["modules", "my-module-b"], module, ["modules", "module-path-b"])
+ *
+ * # Invalidates the cache for module-a
+ * cache.invalidate(["modules", "module-path-a"])
+ *
+ * # Also invalidates the cache for module-a
+ * cache.invalidateUp(["modules", "module-path-a", "subdirectory"])
+ *
+ * # Invalidates the cache for both modules
+ * cache.invalidateDown(["modules"])
+ * ```
+ *
+ * This is useful, for example, when listening for filesystem events to make sure cached items stay in
+ * sync after making changes to sources.
+ *
+ * A single cache entry can also have multiple invalidation contexts, which is helpful when a cache key
+ * can be invalidated by changes to multiple contexts (say for a module version, which should also be
+ * invalidated when dependencies are updated).
+ *
+ */
+export declare class TreeCache {
+ private readonly cache;
+ private readonly contextTree;
+ constructor();
+ set(key: CacheKey, value: CacheValue, ...contexts: CacheContext[]): void;
+ get(key: CacheKey): CacheValue | undefined;
+ getOrThrow(key: CacheKey): CacheValue;
+ getByContext(context: CacheContext): CacheValues;
+ /**
+ * Delete a specific entry from the cache.
+ */
+ delete(key: CacheKey): void;
+ /**
+ * Invalidates all cache entries whose context equals `context`
+ */
+ invalidate(context: CacheContext): void;
+ /**
+ * Invalidates all cache entries where the given `context` starts with the entries' context
+ * (i.e. the whole path from the tree root down to the context leaf)
+ */
+ invalidateUp(context: CacheContext): void;
+ /**
+ * Invalidates all cache entries whose context _starts_ with the given `context`
+ * (i.e. the context node and the whole tree below it)
+ */
+ invalidateDown(context: CacheContext): void;
+ private getNode;
+ private clearNode;
+}
+export declare function pathToCacheContext(path: string): CacheContext;
+//# sourceMappingURL=cache.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/cache.js b/garden-service/build/cache.js
new file mode 100644
index 00000000000..e71b3e1a31f
--- /dev/null
+++ b/garden-service/build/cache.js
@@ -0,0 +1,213 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+Object.defineProperty(exports, "__esModule", { value: true });
+const lodash_1 = require("lodash");
+const path_1 = require("path");
+const exceptions_1 = require("./exceptions");
+/**
+ * A simple in-memory cache that additionally indexes keys in a tree by a seperate context key, so that keys
+ * can be invalidated based on surrounding context.
+ *
+ * For example, we can cache the version of a directory path, and then invalidate every cached key under a
+ * parent path:
+ *
+ * ```
+ * const cache = new TreeCache()
+ *
+ * # The context parameter (last parameter) here is the path to the module source
+ * cache.set(["modules", "my-module-a"], module, ["modules", "module-path-a"])
+ * cache.set(["modules", "my-module-b"], module, ["modules", "module-path-b"])
+ *
+ * # Invalidates the cache for module-a
+ * cache.invalidate(["modules", "module-path-a"])
+ *
+ * # Also invalidates the cache for module-a
+ * cache.invalidateUp(["modules", "module-path-a", "subdirectory"])
+ *
+ * # Invalidates the cache for both modules
+ * cache.invalidateDown(["modules"])
+ * ```
+ *
+ * This is useful, for example, when listening for filesystem events to make sure cached items stay in
+ * sync after making changes to sources.
+ *
+ * A single cache entry can also have multiple invalidation contexts, which is helpful when a cache key
+ * can be invalidated by changes to multiple contexts (say for a module version, which should also be
+ * invalidated when dependencies are updated).
+ *
+ */
+class TreeCache {
+ constructor() {
+ this.cache = new Map();
+ this.contextTree = makeContextNode([]);
+ }
+ set(key, value, ...contexts) {
+ if (key.length === 0) {
+ throw new exceptions_1.ParameterError(`Cache key must have at least one part`, { key, contexts });
+ }
+ if (contexts.length === 0) {
+ throw new exceptions_1.ParameterError(`Must specify at least one context`, { key, contexts });
+ }
+ const curriedKey = curry(key);
+ let entry = this.cache.get(curriedKey);
+ if (entry === undefined) {
+ entry = { key, value, contexts: {} };
+ this.cache.set(curriedKey, entry);
+ }
+ else {
+ // merge with the existing entry
+ entry.value = value;
+ }
+ contexts.forEach(c => entry.contexts[curry(c)] = c);
+ for (const context of Object.values(contexts)) {
+ let node = this.contextTree;
+ if (context.length === 0) {
+ throw new exceptions_1.ParameterError(`Context key must have at least one part`, { key, context });
+ }
+ const contextKey = [];
+ for (const part of context) {
+ contextKey.push(part);
+ if (node.children[part]) {
+ node = node.children[part];
+ }
+ else {
+ node = node.children[part] = makeContextNode(contextKey);
+ }
+ }
+ node.entries.add(curriedKey);
+ }
+ }
+ get(key) {
+ const entry = this.cache.get(curry(key));
+ return entry ? entry.value : undefined;
+ }
+ getOrThrow(key) {
+ const value = this.get(key);
+ if (value === undefined) {
+ throw new exceptions_1.NotFoundError(`Could not find key ${key} in cache`, { key });
+ }
+ return value;
+ }
+ getByContext(context) {
+ let pairs = [];
+ const node = this.getNode(context);
+ if (node) {
+ pairs = Array.from(node.entries).map(curriedKey => {
+ const entry = this.cache.get(curriedKey);
+ if (!entry) {
+ throw new exceptions_1.InternalError(`Invalid reference found in cache: ${curriedKey}`, { curriedKey });
+ }
+ return [entry.key, entry.value];
+ });
+ }
+ return new Map(pairs);
+ }
+ /**
+ * Delete a specific entry from the cache.
+ */
+ delete(key) {
+ const curriedKey = curry(key);
+ const entry = this.cache.get(curriedKey);
+ if (entry === undefined) {
+ return;
+ }
+ this.cache.delete(curriedKey);
+ // clear the entry from its contexts
+ for (const context of Object.values(entry.contexts)) {
+ const node = this.getNode(context);
+ node && node.entries.delete(curriedKey);
+ }
+ }
+ /**
+ * Invalidates all cache entries whose context equals `context`
+ */
+ invalidate(context) {
+ const node = this.getNode(context);
+ if (node) {
+ // clear all cache entries on the node
+ this.clearNode(node, false);
+ }
+ }
+ /**
+ * Invalidates all cache entries where the given `context` starts with the entries' context
+ * (i.e. the whole path from the tree root down to the context leaf)
+ */
+ invalidateUp(context) {
+ let node = this.contextTree;
+ for (const part of context) {
+ node = node.children[part];
+ if (!node) {
+ break;
+ }
+ this.clearNode(node, false);
+ }
+ }
+ /**
+ * Invalidates all cache entries whose context _starts_ with the given `context`
+ * (i.e. the context node and the whole tree below it)
+ */
+ invalidateDown(context) {
+ const node = this.getNode(context);
+ if (node) {
+ // clear all cache entries in the node and recursively through all child nodes
+ this.clearNode(node, true);
+ }
+ }
+ getNode(context) {
+ let node = this.contextTree;
+ for (const part of context) {
+ node = node.children[part];
+ if (!node) {
+ // no cache keys under the given context
+ return;
+ }
+ }
+ return node;
+ }
+ clearNode(node, clearChildNodes) {
+ for (const curriedKey of node.entries) {
+ const entry = this.cache.get(curriedKey);
+ if (entry === undefined) {
+ return;
+ }
+ // also clear the invalidated entry from its other contexts
+ for (const context of Object.values(entry.contexts)) {
+ if (!lodash_1.isEqual(context, node.key)) {
+ const otherNode = this.getNode(context);
+ otherNode && otherNode.entries.delete(curriedKey);
+ }
+ }
+ this.cache.delete(curriedKey);
+ }
+ node.entries = new Set();
+ if (clearChildNodes) {
+ for (const child of Object.values(node.children)) {
+ this.clearNode(child, true);
+ }
+ }
+ }
+}
+exports.TreeCache = TreeCache;
+function makeContextNode(key) {
+ return {
+ key,
+ children: {},
+ entries: new Set(),
+ };
+}
+function curry(key) {
+ return JSON.stringify(key);
+}
+function pathToCacheContext(path) {
+ const parsed = path_1.parse(path_1.normalize(path));
+ return ["path", ...parsed.dir.split(path_1.sep)];
+}
+exports.pathToCacheContext = pathToCacheContext;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,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SSxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRTtnQkFDaEQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUE7YUFDNUI7U0FDRjtJQUNILENBQUM7Q0FDRjtBQTVMRCw4QkE0TEM7QUFFRCxTQUFTLGVBQWUsQ0FBQyxHQUFpQjtJQUN4QyxPQUFPO1FBQ0wsR0FBRztRQUNILFFBQVEsRUFBRSxFQUFFO1FBQ1osT0FBTyxFQUFFLElBQUksR0FBRyxFQUFjO0tBQy9CLENBQUE7QUFDSCxDQUFDO0FBRUQsU0FBUyxLQUFLLENBQUMsR0FBNEI7SUFDekMsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFBO0FBQzVCLENBQUM7QUFFRCxTQUFnQixrQkFBa0IsQ0FBQyxJQUFZO0lBQzdDLE1BQU0sTUFBTSxHQUFHLFlBQUssQ0FBQyxnQkFBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUE7SUFDckMsT0FBTyxDQUFDLE1BQU0sRUFBRSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFVBQUcsQ0FBQyxDQUFDLENBQUE7QUFDM0MsQ0FBQztBQUhELGdEQUdDIiwiZmlsZSI6ImNhY2hlLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoQykgMjAxOCBHYXJkZW4gVGVjaG5vbG9naWVzLCBJbmMuIDxpbmZvQGdhcmRlbi5pbz5cbiAqXG4gKiBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljXG4gKiBMaWNlbnNlLCB2LiAyLjAuIElmIGEgY29weSBvZiB0aGUgTVBMIHdhcyBub3QgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzXG4gKiBmaWxlLCBZb3UgY2FuIG9idGFpbiBvbmUgYXQgaHR0cDovL21vemlsbGEub3JnL01QTC8yLjAvLlxuICovXG5cbmltcG9ydCB7XG4gIGlzRXF1YWwsXG59IGZyb20gXCJsb2Rhc2hcIlxuaW1wb3J0IHtcbiAgbm9ybWFsaXplLFxuICBwYXJzZSxcbiAgc2VwLFxufSBmcm9tIFwicGF0aFwiXG5pbXBvcnQge1xuICBJbnRlcm5hbEVycm9yLFxuICBOb3RGb3VuZEVycm9yLFxuICBQYXJhbWV0ZXJFcnJvcixcbn0gZnJvbSBcIi4vZXhjZXB0aW9uc1wiXG5cbmV4cG9ydCB0eXBlIENhY2hlS2V5ID0gc3RyaW5nW11cbmV4cG9ydCB0eXBlIENhY2hlQ29udGV4dCA9IHN0cmluZ1tdXG5leHBvcnQgdHlwZSBDdXJyaWVkS2V5ID0gc3RyaW5nXG5cbmV4cG9ydCB0eXBlIENhY2hlVmFsdWUgPSBzdHJpbmcgfCBudW1iZXIgfCBib29sZWFuIHwgbnVsbCB8IG9iamVjdFxuZXhwb3J0IHR5cGUgQ2FjaGVWYWx1ZXMgPSBNYXA8Q2FjaGVLZXksIENhY2hlVmFsdWU+XG5cbmludGVyZmFjZSBDYWNoZUVudHJ5IHtcbiAga2V5OiBDYWNoZUtleVxuICB2YWx1ZTogQ2FjaGVWYWx1ZVxuICBjb250ZXh0czogeyBbY3VycmllZENvbnRleHQ6IHN0cmluZ106IENhY2hlQ29udGV4dCB9XG59XG5cbnR5cGUgQ2FjaGVFbnRyaWVzID0gTWFwPEN1cnJpZWRLZXksIENhY2hlRW50cnk+XG5cbmludGVyZmFjZSBDb250ZXh0Tm9kZSB7XG4gIGtleTogQ2FjaGVDb250ZXh0XG4gIGNoaWxkcmVuOiB7IFtjb250ZXh0UGFydDogc3RyaW5nXTogQ29udGV4dE5vZGUgfVxuICBlbnRyaWVzOiBTZXQ8Q3VycmllZEtleT5cbn1cblxuLyoqXG4gKiAgQSBzaW1wbGUgaW4tbWVtb3J5IGNhY2hlIHRoYXQgYWRkaXRpb25hbGx5IGluZGV4ZXMga2V5cyBpbiBhIHRyZWUgYnkgYSBzZXBlcmF0ZSBjb250ZXh0IGtleSwgc28gdGhhdCBrZXlzXG4gKiAgY2FuIGJlIGludmFsaWRhdGVkIGJhc2VkIG9uIHN1cnJvdW5kaW5nIGNvbnRleHQuXG4gKlxuICogIEZvciBleGFtcGxlLCB3ZSBjYW4gY2FjaGUgdGhlIHZlcnNpb24gb2YgYSBkaXJlY3RvcnkgcGF0aCwgYW5kIHRoZW4gaW52YWxpZGF0ZSBldmVyeSBjYWNoZWQga2V5IHVuZGVyIGFcbiAqICBwYXJlbnQgcGF0aDpcbiAqXG4gKiAgYGBgXG4gKiAgY29uc3QgY2FjaGUgPSBuZXcgVHJlZUNhY2hlKClcbiAqXG4gKiAgIyBUaGUgY29udGV4dCBwYXJhbWV0ZXIgKGxhc3QgcGFyYW1ldGVyKSBoZXJlIGlzIHRoZSBwYXRoIHRvIHRoZSBtb2R1bGUgc291cmNlXG4gKiAgY2FjaGUuc2V0KFtcIm1vZHVsZXNcIiwgXCJteS1tb2R1bGUtYVwiXSwgbW9kdWxlLCBbXCJtb2R1bGVzXCIsIFwibW9kdWxlLXBhdGgtYVwiXSlcbiAqICBjYWNoZS5zZXQoW1wibW9kdWxlc1wiLCBcIm15LW1vZHVsZS1iXCJdLCBtb2R1bGUsIFtcIm1vZHVsZXNcIiwgXCJtb2R1bGUtcGF0aC1iXCJdKVxuICpcbiAqICAjIEludmFsaWRhdGVzIHRoZSBjYWNoZSBmb3IgbW9kdWxlLWFcbiAqICBjYWNoZS5pbnZhbGlkYXRlKFtcIm1vZHVsZXNcIiwgXCJtb2R1bGUtcGF0aC1hXCJdKVxuICpcbiAqICAjIEFsc28gaW52YWxpZGF0ZXMgdGhlIGNhY2hlIGZvciBtb2R1bGUtYVxuICogIGNhY2hlLmludmFsaWRhdGVVcChbXCJtb2R1bGVzXCIsIFwibW9kdWxlLXBhdGgtYVwiLCBcInN1YmRpcmVjdG9yeVwiXSlcbiAqXG4gKiAgIyBJbnZhbGlkYXRlcyB0aGUgY2FjaGUgZm9yIGJvdGggbW9kdWxlc1xuICogIGNhY2hlLmludmFsaWRhdGVEb3duKFtcIm1vZHVsZXNcIl0pXG4gKiAgYGBgXG4gKlxuICogIFRoaXMgaXMgdXNlZnVsLCBmb3IgZXhhbXBsZSwgd2hlbiBsaXN0ZW5pbmcgZm9yIGZpbGVzeXN0ZW0gZXZlbnRzIHRvIG1ha2Ugc3VyZSBjYWNoZWQgaXRlbXMgc3RheSBpblxuICogIHN5bmMgYWZ0ZXIgbWFraW5nIGNoYW5nZXMgdG8gc291cmNlcy5cbiAqXG4gKiAgQSBzaW5nbGUgY2FjaGUgZW50cnkgY2FuIGFsc28gaGF2ZSBtdWx0aXBsZSBpbnZhbGlkYXRpb24gY29udGV4dHMsIHdoaWNoIGlzIGhlbHBmdWwgd2hlbiBhIGNhY2hlIGtleVxuICogIGNhbiBiZSBpbnZhbGlkYXRlZCBieSBjaGFuZ2VzIHRvIG11bHRpcGxlIGNvbnRleHRzIChzYXkgZm9yIGEgbW9kdWxlIHZlcnNpb24sIHdoaWNoIHNob3VsZCBhbHNvIGJlXG4gKiAgaW52YWxpZGF0ZWQgd2hlbiBkZXBlbmRlbmNpZXMgYXJlIHVwZGF0ZWQpLlxuICpcbiAqL1xuZXhwb3J0IGNsYXNzIFRyZWVDYWNoZSB7XG4gIHByaXZhdGUgcmVhZG9ubHkgY2FjaGU6IENhY2hlRW50cmllc1xuICBwcml2YXRlIHJlYWRvbmx5IGNvbnRleHRUcmVlOiBDb250ZXh0Tm9kZVxuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMuY2FjaGUgPSBuZXcgTWFwPEN1cnJpZWRLZXksIENhY2hlRW50cnk+KClcbiAgICB0aGlzLmNvbnRleHRUcmVlID0gbWFrZUNvbnRleHROb2RlKFtdKVxuICB9XG5cbiAgc2V0KGtleTogQ2FjaGVLZXksIHZhbHVlOiBDYWNoZVZhbHVlLCAuLi5jb250ZXh0czogQ2FjaGVDb250ZXh0W10pIHtcbiAgICBpZiAoa2V5Lmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IFBhcmFtZXRlckVycm9yKGBDYWNoZSBrZXkgbXVzdCBoYXZlIGF0IGxlYXN0IG9uZSBwYXJ0YCwgeyBrZXksIGNvbnRleHRzIH0pXG4gICAgfVxuXG4gICAgaWYgKGNvbnRleHRzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IFBhcmFtZXRlckVycm9yKGBNdXN0IHNwZWNpZnkgYXQgbGVhc3Qgb25lIGNvbnRleHRgLCB7IGtleSwgY29udGV4dHMgfSlcbiAgICB9XG5cbiAgICBjb25zdCBjdXJyaWVkS2V5ID0gY3Vycnkoa2V5KVxuICAgIGxldCBlbnRyeSA9IHRoaXMuY2FjaGUuZ2V0KGN1cnJpZWRLZXkpXG5cbiAgICBpZiAoZW50cnkgPT09IHVuZGVmaW5lZCkge1xuICAgICAgZW50cnkgPSB7IGtleSwgdmFsdWUsIGNvbnRleHRzOiB7fSB9XG4gICAgICB0aGlzLmNhY2hlLnNldChjdXJyaWVkS2V5LCBlbnRyeSlcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gbWVyZ2Ugd2l0aCB0aGUgZXhpc3RpbmcgZW50cnlcbiAgICAgIGVudHJ5LnZhbHVlID0gdmFsdWVcbiAgICB9XG5cbiAgICBjb250ZXh0cy5mb3JFYWNoKGMgPT4gZW50cnkhLmNvbnRleHRzW2N1cnJ5KGMpXSA9IGMpXG5cbiAgICBmb3IgKGNvbnN0IGNvbnRleHQgb2YgT2JqZWN0LnZhbHVlcyhjb250ZXh0cykpIHtcbiAgICAgIGxldCBub2RlID0gdGhpcy5jb250ZXh0VHJlZVxuXG4gICAgICBpZiAoY29udGV4dC5sZW5ndGggPT09IDApIHtcbiAgICAgICAgdGhyb3cgbmV3IFBhcmFtZXRlckVycm9yKGBDb250ZXh0IGtleSBtdXN0IGhhdmUgYXQgbGVhc3Qgb25lIHBhcnRgLCB7IGtleSwgY29udGV4dCB9KVxuICAgICAgfVxuXG4gICAgICBjb25zdCBjb250ZXh0S2V5OiBDYWNoZUNvbnRleHQgPSBbXVxuXG4gICAgICBmb3IgKGNvbnN0IHBhcnQgb2YgY29udGV4dCkge1xuICAgICAgICBjb250ZXh0S2V5LnB1c2gocGFydClcblxuICAgICAgICBpZiAobm9kZS5jaGlsZHJlbltwYXJ0XSkge1xuICAgICAgICAgIG5vZGUgPSBub2RlLmNoaWxkcmVuW3BhcnRdXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbm9kZSA9IG5vZGUuY2hpbGRyZW5bcGFydF0gPSBtYWtlQ29udGV4dE5vZGUoY29udGV4dEtleSlcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBub2RlLmVudHJpZXMuYWRkKGN1cnJpZWRLZXkpXG4gICAgfVxuICB9XG5cbiAgZ2V0KGtleTogQ2FjaGVLZXkpOiBDYWNoZVZhbHVlIHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBlbnRyeSA9IHRoaXMuY2FjaGUuZ2V0KGN1cnJ5KGtleSkpXG4gICAgcmV0dXJuIGVudHJ5ID8gZW50cnkudmFsdWUgOiB1bmRlZmluZWRcbiAgfVxuXG4gIGdldE9yVGhyb3coa2V5OiBDYWNoZUtleSk6IENhY2hlVmFsdWUge1xuICAgIGNvbnN0IHZhbHVlID0gdGhpcy5nZXQoa2V5KVxuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgTm90Rm91bmRFcnJvcihgQ291bGQgbm90IGZpbmQga2V5ICR7a2V5fSBpbiBjYWNoZWAsIHsga2V5IH0pXG4gICAgfVxuICAgIHJldHVybiB2YWx1ZVxuICB9XG5cbiAgZ2V0QnlDb250ZXh0KGNvbnRleHQ6IENhY2hlQ29udGV4dCk6IENhY2hlVmFsdWVzIHtcbiAgICBsZXQgcGFpcnM6IFtDYWNoZUtleSwgQ2FjaGVWYWx1ZV1bXSA9IFtdXG5cbiAgICBjb25zdCBub2RlID0gdGhpcy5nZXROb2RlKGNvbnRleHQpXG5cbiAgICBpZiAobm9kZSkge1xuICAgICAgcGFpcnMgPSBBcnJheS5mcm9tKG5vZGUuZW50cmllcykubWFwKGN1cnJpZWRLZXkgPT4ge1xuICAgICAgICBjb25zdCBlbnRyeSA9IHRoaXMuY2FjaGUuZ2V0KGN1cnJpZWRLZXkpXG4gICAgICAgIGlmICghZW50cnkpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgSW50ZXJuYWxFcnJvcihgSW52YWxpZCByZWZlcmVuY2UgZm91bmQgaW4gY2FjaGU6ICR7Y3VycmllZEtleX1gLCB7IGN1cnJpZWRLZXkgfSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gPFtDYWNoZUtleSwgQ2FjaGVWYWx1ZV0+W2VudHJ5LmtleSwgZW50cnkudmFsdWVdXG4gICAgICB9KVxuICAgIH1cblxuICAgIHJldHVybiBuZXcgTWFwPENhY2hlS2V5LCBDYWNoZVZhbHVlPihwYWlycylcbiAgfVxuXG4gIC8qKlxuICAgKiBEZWxldGUgYSBzcGVjaWZpYyBlbnRyeSBmcm9tIHRoZSBjYWNoZS5cbiAgICovXG4gIGRlbGV0ZShrZXk6IENhY2hlS2V5KSB7XG4gICAgY29uc3QgY3VycmllZEtleSA9IGN1cnJ5KGtleSlcbiAgICBjb25zdCBlbnRyeSA9IHRoaXMuY2FjaGUuZ2V0KGN1cnJpZWRLZXkpXG5cbiAgICBpZiAoZW50cnkgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgdGhpcy5jYWNoZS5kZWxldGUoY3VycmllZEtleSlcblxuICAgIC8vIGNsZWFyIHRoZSBlbnRyeSBmcm9tIGl0cyBjb250ZXh0c1xuICAgIGZvciAoY29uc3QgY29udGV4dCBvZiBPYmplY3QudmFsdWVzKGVudHJ5LmNvbnRleHRzKSkge1xuICAgICAgY29uc3Qgbm9kZSA9IHRoaXMuZ2V0Tm9kZShjb250ZXh0KVxuICAgICAgbm9kZSAmJiBub2RlLmVudHJpZXMuZGVsZXRlKGN1cnJpZWRLZXkpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEludmFsaWRhdGVzIGFsbCBjYWNoZSBlbnRyaWVzIHdob3NlIGNvbnRleHQgZXF1YWxzIGBjb250ZXh0YFxuICAgKi9cbiAgaW52YWxpZGF0ZShjb250ZXh0OiBDYWNoZUNvbnRleHQpIHtcbiAgICBjb25zdCBub2RlID0gdGhpcy5nZXROb2RlKGNvbnRleHQpXG5cbiAgICBpZiAobm9kZSkge1xuICAgICAgLy8gY2xlYXIgYWxsIGNhY2hlIGVudHJpZXMgb24gdGhlIG5vZGVcbiAgICAgIHRoaXMuY2xlYXJOb2RlKG5vZGUsIGZhbHNlKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBJbnZhbGlkYXRlcyBhbGwgY2FjaGUgZW50cmllcyB3aGVyZSB0aGUgZ2l2ZW4gYGNvbnRleHRgIHN0YXJ0cyB3aXRoIHRoZSBlbnRyaWVzJyBjb250ZXh0XG4gICAqIChpLmUuIHRoZSB3aG9sZSBwYXRoIGZyb20gdGhlIHRyZWUgcm9vdCBkb3duIHRvIHRoZSBjb250ZXh0IGxlYWYpXG4gICAqL1xuICBpbnZhbGlkYXRlVXAoY29udGV4dDogQ2FjaGVDb250ZXh0KSB7XG4gICAgbGV0IG5vZGUgPSB0aGlzLmNvbnRleHRUcmVlXG5cbiAgICBmb3IgKGNvbnN0IHBhcnQgb2YgY29udGV4dCkge1xuICAgICAgbm9kZSA9IG5vZGUuY2hpbGRyZW5bcGFydF1cbiAgICAgIGlmICghbm9kZSkge1xuICAgICAgICBicmVha1xuICAgICAgfVxuICAgICAgdGhpcy5jbGVhck5vZGUobm9kZSwgZmFsc2UpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEludmFsaWRhdGVzIGFsbCBjYWNoZSBlbnRyaWVzIHdob3NlIGNvbnRleHQgX3N0YXJ0c18gd2l0aCB0aGUgZ2l2ZW4gYGNvbnRleHRgXG4gICAqIChpLmUuIHRoZSBjb250ZXh0IG5vZGUgYW5kIHRoZSB3aG9sZSB0cmVlIGJlbG93IGl0KVxuICAgKi9cbiAgaW52YWxpZGF0ZURvd24oY29udGV4dDogQ2FjaGVDb250ZXh0KSB7XG4gICAgY29uc3Qgbm9kZSA9IHRoaXMuZ2V0Tm9kZShjb250ZXh0KVxuXG4gICAgaWYgKG5vZGUpIHtcbiAgICAgIC8vIGNsZWFyIGFsbCBjYWNoZSBlbnRyaWVzIGluIHRoZSBub2RlIGFuZCByZWN1cnNpdmVseSB0aHJvdWdoIGFsbCBjaGlsZCBub2Rlc1xuICAgICAgdGhpcy5jbGVhck5vZGUobm9kZSwgdHJ1ZSlcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGdldE5vZGUoY29udGV4dDogQ2FjaGVDb250ZXh0KSB7XG4gICAgbGV0IG5vZGUgPSB0aGlzLmNvbnRleHRUcmVlXG5cbiAgICBmb3IgKGNvbnN0IHBhcnQgb2YgY29udGV4dCkge1xuICAgICAgbm9kZSA9IG5vZGUuY2hpbGRyZW5bcGFydF1cblxuICAgICAgaWYgKCFub2RlKSB7XG4gICAgICAgIC8vIG5vIGNhY2hlIGtleXMgdW5kZXIgdGhlIGdpdmVuIGNvbnRleHRcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG5vZGVcbiAgfVxuXG4gIHByaXZhdGUgY2xlYXJOb2RlKG5vZGU6IENvbnRleHROb2RlLCBjbGVhckNoaWxkTm9kZXM6IGJvb2xlYW4pIHtcbiAgICBmb3IgKGNvbnN0IGN1cnJpZWRLZXkgb2Ygbm9kZS5lbnRyaWVzKSB7XG4gICAgICBjb25zdCBlbnRyeSA9IHRoaXMuY2FjaGUuZ2V0KGN1cnJpZWRLZXkpXG5cbiAgICAgIGlmIChlbnRyeSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyBhbHNvIGNsZWFyIHRoZSBpbnZhbGlkYXRlZCBlbnRyeSBmcm9tIGl0cyBvdGhlciBjb250ZXh0c1xuICAgICAgZm9yIChjb25zdCBjb250ZXh0IG9mIE9iamVjdC52YWx1ZXMoZW50cnkuY29udGV4dHMpKSB7XG4gICAgICAgIGlmICghaXNFcXVhbChjb250ZXh0LCBub2RlLmtleSkpIHtcbiAgICAgICAgICBjb25zdCBvdGhlck5vZGUgPSB0aGlzLmdldE5vZGUoY29udGV4dClcbiAgICAgICAgICBvdGhlck5vZGUgJiYgb3RoZXJOb2RlLmVudHJpZXMuZGVsZXRlKGN1cnJpZWRLZXkpXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgdGhpcy5jYWNoZS5kZWxldGUoY3VycmllZEtleSlcbiAgICB9XG5cbiAgICBub2RlLmVudHJpZXMgPSBuZXcgU2V0PEN1cnJpZWRLZXk+KClcblxuICAgIGlmIChjbGVhckNoaWxkTm9kZXMpIHtcbiAgICAgIGZvciAoY29uc3QgY2hpbGQgb2YgT2JqZWN0LnZhbHVlcyhub2RlLmNoaWxkcmVuKSkge1xuICAgICAgICB0aGlzLmNsZWFyTm9kZShjaGlsZCwgdHJ1ZSlcbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuZnVuY3Rpb24gbWFrZUNvbnRleHROb2RlKGtleTogQ2FjaGVDb250ZXh0KTogQ29udGV4dE5vZGUge1xuICByZXR1cm4ge1xuICAgIGtleSxcbiAgICBjaGlsZHJlbjoge30sXG4gICAgZW50cmllczogbmV3IFNldDxDdXJyaWVkS2V5PigpLFxuICB9XG59XG5cbmZ1bmN0aW9uIGN1cnJ5KGtleTogQ2FjaGVLZXkgfCBDYWNoZUNvbnRleHQpIHtcbiAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KGtleSlcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhdGhUb0NhY2hlQ29udGV4dChwYXRoOiBzdHJpbmcpOiBDYWNoZUNvbnRleHQge1xuICBjb25zdCBwYXJzZWQgPSBwYXJzZShub3JtYWxpemUocGF0aCkpXG4gIHJldHVybiBbXCJwYXRoXCIsIC4uLnBhcnNlZC5kaXIuc3BsaXQoc2VwKV1cbn1cbiJdfQ==
diff --git a/garden-service/build/cli/cli.d.ts b/garden-service/build/cli/cli.d.ts
new file mode 100644
index 00000000000..fd6d91b967c
--- /dev/null
+++ b/garden-service/build/cli/cli.d.ts
@@ -0,0 +1,28 @@
+import { BooleanParameter, Command, ChoicesParameter, Parameter, StringParameter, EnvironmentOption } from "../commands/base";
+import { GardenError } from "../exceptions";
+import { GardenConfig } from "../config/base";
+export declare const MOCK_CONFIG: GardenConfig;
+export declare const GLOBAL_OPTIONS: {
+ root: StringParameter;
+ silent: BooleanParameter;
+ env: EnvironmentOption;
+ loglevel: ChoicesParameter;
+ output: ChoicesParameter;
+};
+export interface ParseResults {
+ argv: any;
+ code: number;
+ errors: (GardenError | Error)[];
+}
+export declare class GardenCli {
+ program: any;
+ commands: {
+ [key: string]: Command;
+ };
+ constructor();
+ addGlobalOption(key: string, option: Parameter): void;
+ addCommand(command: Command, program: any): void;
+ parse(): Promise;
+}
+export declare function run(): Promise;
+//# sourceMappingURL=cli.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/cli/cli.js b/garden-service/build/cli/cli.js
new file mode 100644
index 00000000000..17eeb036b22
--- /dev/null
+++ b/garden-service/build/cli/cli.js
@@ -0,0 +1,281 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const sywac = require("sywac");
+const lodash_1 = require("lodash");
+const path_1 = require("path");
+const js_yaml_1 = require("js-yaml");
+const commands_1 = require("../commands/commands");
+const stringify = require("json-stringify-safe");
+const util_1 = require("../util/util");
+const base_1 = require("../commands/base");
+const exceptions_1 = require("../exceptions");
+const garden_1 = require("../garden");
+const logger_1 = require("../logger/logger");
+const log_node_1 = require("../logger/log-node");
+const basic_terminal_writer_1 = require("../logger/writers/basic-terminal-writer");
+const fancy_terminal_writer_1 = require("../logger/writers/fancy-terminal-writer");
+const file_writer_1 = require("../logger/writers/file-writer");
+const helpers_1 = require("./helpers");
+const project_1 = require("../config/project");
+const constants_1 = require("../constants");
+const OUTPUT_RENDERERS = {
+ json: (data) => {
+ return stringify(data, null, 2);
+ },
+ yaml: (data) => {
+ return js_yaml_1.safeDump(data, { noRefs: true, skipInvalid: true });
+ },
+};
+const logLevelKeys = util_1.getEnumKeys(log_node_1.LogLevel);
+// Allow string or numeric log levels
+const logLevelChoices = [...logLevelKeys, ...lodash_1.range(logLevelKeys.length).map(String)];
+const getLogLevelFromArg = (level) => {
+ const lvl = parseInt(level, 10);
+ if (lvl) {
+ return lvl;
+ }
+ return log_node_1.LogLevel[level];
+};
+// For initializing garden without a project config
+exports.MOCK_CONFIG = {
+ version: "0",
+ dirname: "/",
+ path: process.cwd(),
+ project: {
+ name: "mock-project",
+ defaultEnvironment: "local",
+ environments: project_1.defaultEnvironments,
+ environmentDefaults: {
+ providers: [
+ {
+ name: "local-kubernetes",
+ },
+ ],
+ variables: {},
+ },
+ },
+};
+exports.GLOBAL_OPTIONS = {
+ root: new base_1.StringParameter({
+ alias: "r",
+ help: "Override project root directory (defaults to working directory).",
+ defaultValue: process.cwd(),
+ }),
+ silent: new base_1.BooleanParameter({
+ alias: "s",
+ help: "Suppress log output.",
+ defaultValue: false,
+ }),
+ env: new base_1.EnvironmentOption(),
+ loglevel: new base_1.ChoicesParameter({
+ alias: "l",
+ choices: logLevelChoices,
+ help: "Set logger level. Values can be either string or numeric and are prioritized from 0 to 5 " +
+ "(highest to lowest) as follows: error: 0, warn: 1, info: 2, verbose: 3, debug: 4, silly: 5",
+ hints: "[enum] [default: info] [error || 0, warn || 1, info || 2, verbose || 3, debug || 4, silly || 5]",
+ defaultValue: log_node_1.LogLevel[log_node_1.LogLevel.info],
+ }),
+ output: new base_1.ChoicesParameter({
+ alias: "o",
+ choices: Object.keys(OUTPUT_RENDERERS),
+ help: "Output command result in specified format (note: disables progress logging).",
+ }),
+};
+const GLOBAL_OPTIONS_GROUP_NAME = "Global options";
+const DEFAULT_CLI_LOGGER_TYPE = logger_1.LoggerType.fancy;
+class GardenCli {
+ constructor() {
+ this.commands = {};
+ const version = require("../../package.json").version;
+ this.program = sywac
+ .help("-h, --help", {
+ group: GLOBAL_OPTIONS_GROUP_NAME,
+ implicitCommand: false,
+ })
+ .version("-v, --version", {
+ version,
+ group: GLOBAL_OPTIONS_GROUP_NAME,
+ implicitCommand: false,
+ })
+ .showHelpByDefault()
+ .check((argv, _ctx) => {
+ // NOTE: Need to mutate argv!
+ lodash_1.merge(argv, helpers_1.falsifyConflictingParams(argv, exports.GLOBAL_OPTIONS));
+ })
+ .style(helpers_1.styleConfig);
+ const commands = commands_1.coreCommands;
+ const globalOptions = Object.entries(exports.GLOBAL_OPTIONS);
+ commands.forEach(command => this.addCommand(command, this.program));
+ globalOptions.forEach(([key, opt]) => this.addGlobalOption(key, opt));
+ }
+ addGlobalOption(key, option) {
+ this.program.option(helpers_1.getOptionSynopsis(key, option), Object.assign({}, helpers_1.prepareOptionConfig(option), { group: GLOBAL_OPTIONS_GROUP_NAME }));
+ }
+ addCommand(command, program) {
+ const fullName = command.getFullName();
+ if (this.commands[fullName]) {
+ // For now we don't allow multiple definitions of the same command. We may want to revisit this later.
+ throw new exceptions_1.PluginError(`Multiple definitions of command "${fullName}"`, {});
+ }
+ this.commands[fullName] = command;
+ const { arguments: args = {}, loggerType = DEFAULT_CLI_LOGGER_TYPE, options = {}, subCommands, } = command;
+ const argKeys = helpers_1.getKeys(args);
+ const optKeys = helpers_1.getKeys(options);
+ const globalKeys = helpers_1.getKeys(exports.GLOBAL_OPTIONS);
+ const dupKeys = lodash_1.intersection(optKeys, globalKeys);
+ if (dupKeys.length > 0) {
+ throw new exceptions_1.PluginError(`Global option(s) ${dupKeys.join(" ")} cannot be redefined`, {});
+ }
+ const action = (argv, cliContext) => __awaiter(this, void 0, void 0, function* () {
+ // Sywac returns positional args and options in a single object which we separate into args and opts
+ const parsedArgs = helpers_1.filterByKeys(argv, argKeys);
+ const parsedOpts = helpers_1.filterByKeys(argv, optKeys.concat(globalKeys));
+ const root = path_1.resolve(process.cwd(), parsedOpts.root);
+ const { env, loglevel, silent, output } = parsedOpts;
+ // Init logger
+ const level = getLogLevelFromArg(loglevel);
+ let writers = [];
+ if (!silent && !output && loggerType !== logger_1.LoggerType.quiet) {
+ if (loggerType === logger_1.LoggerType.fancy) {
+ writers.push(new fancy_terminal_writer_1.FancyTerminalWriter());
+ }
+ else if (loggerType === logger_1.LoggerType.basic) {
+ writers.push(new basic_terminal_writer_1.BasicTerminalWriter());
+ }
+ }
+ const logger = logger_1.Logger.initialize({ level, writers });
+ let garden;
+ let result;
+ do {
+ const contextOpts = { env, logger };
+ if (command.noProject) {
+ contextOpts.config = exports.MOCK_CONFIG;
+ }
+ garden = yield garden_1.Garden.factory(root, contextOpts);
+ // TODO: enforce that commands always output DeepPrimitiveMap
+ result = yield command.action({
+ garden,
+ args: parsedArgs,
+ opts: parsedOpts,
+ });
+ } while (result.restartRequired);
+ // We attach the action result to cli context so that we can process it in the parse method
+ cliContext.details.result = result;
+ });
+ // Command specific positional args and options are set inside the builder function
+ const setup = parser => {
+ subCommands.forEach(subCommandCls => this.addCommand(new subCommandCls(command), parser));
+ argKeys.forEach(key => parser.positional(helpers_1.getArgSynopsis(key, args[key]), helpers_1.prepareArgConfig(args[key])));
+ optKeys.forEach(key => parser.option(helpers_1.getOptionSynopsis(key, options[key]), helpers_1.prepareOptionConfig(options[key])));
+ // We only check for invalid flags for the last command since it might contain flags that
+ // the parent is unaware of, thus causing the check to fail for the parent
+ if (subCommands.length < 1) {
+ parser.check(helpers_1.failOnInvalidOptions);
+ }
+ return parser;
+ };
+ const commandConfig = {
+ setup,
+ aliases: command.alias,
+ desc: command.help,
+ run: action,
+ };
+ program.command(command.name, commandConfig);
+ }
+ parse() {
+ return __awaiter(this, void 0, void 0, function* () {
+ const parseResult = yield this.program.parse();
+ const { argv, details, errors, output: cliOutput } = parseResult;
+ const { result: commandResult } = details;
+ const { output } = argv;
+ let { code } = parseResult;
+ let logger;
+ // Note: Circumvents an issue where the process exits before the output is fully flushed.
+ // Needed for output renderers and Winston (see: https://github.com/winstonjs/winston/issues/228)
+ const waitForOutputFlush = () => util_1.sleep(100);
+ // Logger might not have been initialised if process exits early
+ try {
+ logger = logger_1.getLogger();
+ }
+ catch (_) {
+ logger = logger_1.Logger.initialize({
+ level: log_node_1.LogLevel.info,
+ writers: [new basic_terminal_writer_1.BasicTerminalWriter()],
+ });
+ }
+ // --help or --version options were called so we log the cli output and exit
+ if (cliOutput && errors.length < 1) {
+ logger.stop();
+ console.log(cliOutput);
+ // fix issue where sywac returns exit code 0 even when a command doesn't exist
+ if (!argv.h && !argv.help) {
+ code = 1;
+ }
+ process.exit(code);
+ }
+ const gardenErrors = errors
+ .map(exceptions_1.toGardenError)
+ .concat((commandResult && commandResult.errors) || []);
+ // --output option set
+ if (output) {
+ const renderer = OUTPUT_RENDERERS[output];
+ if (gardenErrors.length > 0) {
+ console.error(renderer({ success: false, errors: gardenErrors }));
+ }
+ else {
+ console.log(renderer(Object.assign({ success: true }, commandResult)));
+ }
+ yield waitForOutputFlush();
+ }
+ if (gardenErrors.length > 0) {
+ gardenErrors.forEach(error => logger.error({
+ msg: error.message,
+ error,
+ }));
+ if (logger.writers.find(w => w instanceof file_writer_1.FileWriter)) {
+ logger.info(`\nSee ${constants_1.ERROR_LOG_FILENAME} for detailed error message`);
+ yield waitForOutputFlush();
+ }
+ code = 1;
+ }
+ logger.stop();
+ return { argv, code, errors };
+ });
+ }
+}
+exports.GardenCli = GardenCli;
+function run() {
+ return __awaiter(this, void 0, void 0, function* () {
+ let code;
+ try {
+ const cli = new GardenCli();
+ const result = yield cli.parse();
+ code = result.code;
+ }
+ catch (err) {
+ console.log(err);
+ code = 1;
+ }
+ finally {
+ util_1.shutdown(code);
+ }
+ });
+}
+exports.run = run;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNsaS9jbGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7R0FNRzs7Ozs7Ozs7OztBQUVILCtCQUE4QjtBQUM5QixtQ0FBbUQ7QUFDbkQsK0JBQThCO0FBQzlCLHFDQUFrQztBQUNsQyxtREFBbUQ7QUFDbkQsaURBQWlEO0FBR2pELHVDQUlxQjtBQUNyQiwyQ0FReUI7QUFDekIsOENBSXNCO0FBQ3RCLHNDQUErQztBQUUvQyw2Q0FBZ0U7QUFDaEUsaURBQTZDO0FBQzdDLG1GQUE2RTtBQUM3RSxtRkFBNkU7QUFDN0UsK0RBQTBEO0FBRzFELHVDQVVrQjtBQUVsQiwrQ0FBdUQ7QUFDdkQsNENBQWlEO0FBRWpELE1BQU0sZ0JBQWdCLEdBQUc7SUFDdkIsSUFBSSxFQUFFLENBQUMsSUFBc0IsRUFBRSxFQUFFO1FBQy9CLE9BQU8sU0FBUyxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUE7SUFDakMsQ0FBQztJQUNELElBQUksRUFBRSxDQUFDLElBQXNCLEVBQUUsRUFBRTtRQUMvQixPQUFPLGtCQUFRLENBQUMsSUFBSSxFQUFFLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUM1RCxDQUFDO0NBQ0YsQ0FBQTtBQUVELE1BQU0sWUFBWSxHQUFHLGtCQUFXLENBQUMsbUJBQVEsQ0FBQyxDQUFBO0FBQzFDLHFDQUFxQztBQUNyQyxNQUFNLGVBQWUsR0FBRyxDQUFDLEdBQUcsWUFBWSxFQUFFLEdBQUcsY0FBSyxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQTtBQUVwRixNQUFNLGtCQUFrQixHQUFHLENBQUMsS0FBYSxFQUFFLEVBQUU7SUFDM0MsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQTtJQUMvQixJQUFJLEdBQUcsRUFBRTtRQUNQLE9BQU8sR0FBRyxDQUFBO0tBQ1g7SUFDRCxPQUFPLG1CQUFRLENBQUMsS0FBSyxDQUFDLENBQUE7QUFDeEIsQ0FBQyxDQUFBO0FBRUQsbURBQW1EO0FBQ3RDLFFBQUEsV0FBVyxHQUFpQjtJQUN2QyxPQUFPLEVBQUUsR0FBRztJQUNaLE9BQU8sRUFBRSxHQUFHO0lBQ1osSUFBSSxFQUFFLE9BQU8sQ0FBQyxHQUFHLEVBQUU7SUFDbkIsT0FBTyxFQUFFO1FBQ1AsSUFBSSxFQUFFLGNBQWM7UUFDcEIsa0JBQWtCLEVBQUUsT0FBTztRQUMzQixZQUFZLEVBQUUsNkJBQW1CO1FBQ2pDLG1CQUFtQixFQUFFO1lBQ25CLFNBQVMsRUFBRTtnQkFDVDtvQkFDRSxJQUFJLEVBQUUsa0JBQWtCO2lCQUN6QjthQUNGO1lBQ0QsU0FBUyxFQUFFLEVBQUU7U0FDZDtLQUNGO0NBQ0YsQ0FBQTtBQUVZLFFBQUEsY0FBYyxHQUFHO0lBQzVCLElBQUksRUFBRSxJQUFJLHNCQUFlLENBQUM7UUFDeEIsS0FBSyxFQUFFLEdBQUc7UUFDVixJQUFJLEVBQUUsa0VBQWtFO1FBQ3hFLFlBQVksRUFBRSxPQUFPLENBQUMsR0FBRyxFQUFFO0tBQzVCLENBQUM7SUFDRixNQUFNLEVBQUUsSUFBSSx1QkFBZ0IsQ0FBQztRQUMzQixLQUFLLEVBQUUsR0FBRztRQUNWLElBQUksRUFBRSxzQkFBc0I7UUFDNUIsWUFBWSxFQUFFLEtBQUs7S0FDcEIsQ0FBQztJQUNGLEdBQUcsRUFBRSxJQUFJLHdCQUFpQixFQUFFO0lBQzVCLFFBQVEsRUFBRSxJQUFJLHVCQUFnQixDQUFDO1FBQzdCLEtBQUssRUFBRSxHQUFHO1FBQ1YsT0FBTyxFQUFFLGVBQWU7UUFDeEIsSUFBSSxFQUNGLDJGQUEyRjtZQUMzRiw0RkFBNEY7UUFDOUYsS0FBSyxFQUNILGlHQUFpRztRQUNuRyxZQUFZLEVBQUUsbUJBQVEsQ0FBQyxtQkFBUSxDQUFDLElBQUksQ0FBQztLQUN0QyxDQUFDO0lBQ0YsTUFBTSxFQUFFLElBQUksdUJBQWdCLENBQUM7UUFDM0IsS0FBSyxFQUFFLEdBQUc7UUFDVixPQUFPLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQztRQUN0QyxJQUFJLEVBQUUsOEVBQThFO0tBQ3JGLENBQUM7Q0FDSCxDQUFBO0FBQ0QsTUFBTSx5QkFBeUIsR0FBRyxnQkFBZ0IsQ0FBQTtBQUNsRCxNQUFNLHVCQUF1QixHQUFHLG1CQUFVLENBQUMsS0FBSyxDQUFBO0FBYWhELE1BQWEsU0FBUztJQUlwQjtRQUZBLGFBQVEsR0FBK0IsRUFBRSxDQUFBO1FBR3ZDLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQTtRQUNyRCxJQUFJLENBQUMsT0FBTyxHQUFHLEtBQUs7YUFDakIsSUFBSSxDQUFDLFlBQVksRUFBRTtZQUNsQixLQUFLLEVBQUUseUJBQXlCO1lBQ2hDLGVBQWUsRUFBRSxLQUFLO1NBQ3ZCLENBQUM7YUFDRCxPQUFPLENBQUMsZUFBZSxFQUFFO1lBQ3hCLE9BQU87WUFDUCxLQUFLLEVBQUUseUJBQXlCO1lBQ2hDLGVBQWUsRUFBRSxLQUFLO1NBQ3ZCLENBQUM7YUFDRCxpQkFBaUIsRUFBRTthQUNuQixLQUFLLENBQUMsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLEVBQUU7WUFDcEIsNkJBQTZCO1lBQzdCLGNBQUssQ0FBQyxJQUFJLEVBQUUsa0NBQXdCLENBQUMsSUFBSSxFQUFFLHNCQUFjLENBQUMsQ0FBQyxDQUFBO1FBQzdELENBQUMsQ0FBQzthQUNELEtBQUssQ0FBQyxxQkFBVyxDQUFDLENBQUE7UUFFckIsTUFBTSxRQUFRLEdBQUcsdUJBQVksQ0FBQTtRQUU3QixNQUFNLGFBQWEsR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDLHNCQUFjLENBQUMsQ0FBQTtRQUVwRCxRQUFRLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUE7UUFDbkUsYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFBO0lBQ3ZFLENBQUM7SUFFRCxlQUFlLENBQUMsR0FBVyxFQUFFLE1BQXNCO1FBQ2pELElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLDJCQUFpQixDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUMsb0JBQzdDLDZCQUFtQixDQUFDLE1BQU0sQ0FBQyxJQUM5QixLQUFLLEVBQUUseUJBQXlCLElBQ2hDLENBQUE7SUFDSixDQUFDO0lBRUQsVUFBVSxDQUFDLE9BQWdCLEVBQUUsT0FBTztRQUNsQyxNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUE7UUFFdEMsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxFQUFFO1lBQzNCLHNHQUFzRztZQUN0RyxNQUFNLElBQUksd0JBQVcsQ0FBQyxvQ0FBb0MsUUFBUSxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUE7U0FDM0U7UUFFRCxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxHQUFHLE9BQU8sQ0FBQTtRQUVqQyxNQUFNLEVBQ0osU0FBUyxFQUFFLElBQUksR0FBRyxFQUFFLEVBQ3BCLFVBQVUsR0FBRyx1QkFBdUIsRUFDcEMsT0FBTyxHQUFHLEVBQUUsRUFDWixXQUFXLEdBQ1osR0FBRyxPQUFPLENBQUE7UUFFWCxNQUFNLE9BQU8sR0FBRyxpQkFBTyxDQUFDLElBQUksQ0FBQyxDQUFBO1FBQzdCLE1BQU0sT0FBTyxHQUFHLGlCQUFPLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDaEMsTUFBTSxVQUFVLEdBQUcsaUJBQU8sQ0FBQyxzQkFBYyxDQUFDLENBQUE7UUFDMUMsTUFBTSxPQUFPLEdBQWEscUJBQVksQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUE7UUFFM0QsSUFBSSxPQUFPLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtZQUN0QixNQUFNLElBQUksd0JBQVcsQ0FBQyxvQkFBb0IsT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsc0JBQXNCLEVBQUUsRUFBRSxDQUFDLENBQUE7U0FDdkY7UUFFRCxNQUFNLE1BQU0sR0FBRyxDQUFPLElBQUksRUFBRSxVQUFVLEVBQUUsRUFBRTtZQUN4QyxvR0FBb0c7WUFDcEcsTUFBTSxVQUFVLEdBQUcsc0JBQVksQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUE7WUFDOUMsTUFBTSxVQUFVLEdBQUcsc0JBQVksQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFBO1lBQ2pFLE1BQU0sSUFBSSxHQUFHLGNBQU8sQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEVBQUUsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFBO1lBQ3BELE1BQU0sRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxVQUFVLENBQUE7WUFFcEQsY0FBYztZQUNkLE1BQU0sS0FBSyxHQUFHLGtCQUFrQixDQUFDLFFBQVEsQ0FBQyxDQUFBO1lBQzFDLElBQUksT0FBTyxHQUFhLEVBQUUsQ0FBQTtZQUUxQixJQUFJLENBQUMsTUFBTSxJQUFJLENBQUMsTUFBTSxJQUFJLFVBQVUsS0FBSyxtQkFBVSxDQUFDLEtBQUssRUFBRTtnQkFDekQsSUFBSSxVQUFVLEtBQUssbUJBQVUsQ0FBQyxLQUFLLEVBQUU7b0JBQ25DLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSwyQ0FBbUIsRUFBRSxDQUFDLENBQUE7aUJBQ3hDO3FCQUFNLElBQUksVUFBVSxLQUFLLG1CQUFVLENBQUMsS0FBSyxFQUFFO29CQUMxQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksMkNBQW1CLEVBQUUsQ0FBQyxDQUFBO2lCQUN4QzthQUNGO1lBRUQsTUFBTSxNQUFNLEdBQUcsZUFBTSxDQUFDLFVBQVUsQ0FBQyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFBO1lBQ3BELElBQUksTUFBYyxDQUFBO1lBQ2xCLElBQUksTUFBTSxDQUFBO1lBQ1YsR0FBRztnQkFDRCxNQUFNLFdBQVcsR0FBZ0IsRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLENBQUE7Z0JBQ2hELElBQUksT0FBTyxDQUFDLFNBQVMsRUFBRTtvQkFDckIsV0FBVyxDQUFDLE1BQU0sR0FBRyxtQkFBVyxDQUFBO2lCQUNqQztnQkFDRCxNQUFNLEdBQUcsTUFBTSxlQUFNLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxXQUFXLENBQUMsQ0FBQTtnQkFDaEQsNkRBQTZEO2dCQUM3RCxNQUFNLEdBQUcsTUFBTSxPQUFPLENBQUMsTUFBTSxDQUFDO29CQUM1QixNQUFNO29CQUNOLElBQUksRUFBRSxVQUFVO29CQUNoQixJQUFJLEVBQUUsVUFBVTtpQkFDakIsQ0FBQyxDQUFBO2FBQ0gsUUFBUSxNQUFNLENBQUMsZUFBZSxFQUFDO1lBRWhDLDJGQUEyRjtZQUMzRixVQUFVLENBQUMsT0FBTyxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUE7UUFDcEMsQ0FBQyxDQUFBLENBQUE7UUFFRCxtRkFBbUY7UUFDbkYsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLEVBQUU7WUFDckIsV0FBVyxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxhQUFhLENBQUMsT0FBTyxDQUFDLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQTtZQUN6RixPQUFPLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyx3QkFBYyxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSwwQkFBZ0IsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUE7WUFDdEcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsMkJBQWlCLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLDZCQUFtQixDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQTtZQUU5Ryx5RkFBeUY7WUFDekYsMEVBQTBFO1lBQzFFLElBQUksV0FBVyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7Z0JBQzFCLE1BQU0sQ0FBQyxLQUFLLENBQUMsOEJBQW9CLENBQUMsQ0FBQTthQUNuQztZQUNELE9BQU8sTUFBTSxDQUFBO1FBQ2YsQ0FBQyxDQUFBO1FBRUQsTUFBTSxhQUFhLEdBQUc7WUFDcEIsS0FBSztZQUNMLE9BQU8sRUFBRSxPQUFPLENBQUMsS0FBSztZQUN0QixJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUk7WUFDbEIsR0FBRyxFQUFFLE1BQU07U0FDWixDQUFBO1FBRUQsT0FBTyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLGFBQWEsQ0FBQyxDQUFBO0lBQzlDLENBQUM7SUFFSyxLQUFLOztZQUNULE1BQU0sV0FBVyxHQUFzQixNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUE7WUFDakUsTUFBTSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsR0FBRyxXQUFXLENBQUE7WUFDaEUsTUFBTSxFQUFFLE1BQU0sRUFBRSxhQUFhLEVBQUUsR0FBRyxPQUFPLENBQUE7WUFDekMsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQTtZQUN2QixJQUFJLEVBQUUsSUFBSSxFQUFFLEdBQUcsV0FBVyxDQUFBO1lBQzFCLElBQUksTUFBYyxDQUFBO1lBRWxCLHlGQUF5RjtZQUN6RixpR0FBaUc7WUFDakcsTUFBTSxrQkFBa0IsR0FBRyxHQUFHLEVBQUUsQ0FBQyxZQUFLLENBQUMsR0FBRyxDQUFDLENBQUE7WUFFM0MsZ0VBQWdFO1lBQ2hFLElBQUk7Z0JBQ0YsTUFBTSxHQUFHLGtCQUFTLEVBQUUsQ0FBQTthQUNyQjtZQUFDLE9BQU8sQ0FBQyxFQUFFO2dCQUNWLE1BQU0sR0FBRyxlQUFNLENBQUMsVUFBVSxDQUFDO29CQUN6QixLQUFLLEVBQUUsbUJBQVEsQ0FBQyxJQUFJO29CQUNwQixPQUFPLEVBQUUsQ0FBQyxJQUFJLDJDQUFtQixFQUFFLENBQUM7aUJBQ3JDLENBQUMsQ0FBQTthQUNIO1lBRUQsNEVBQTRFO1lBQzVFLElBQUksU0FBUyxJQUFJLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO2dCQUNsQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUE7Z0JBQ2IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQTtnQkFFdEIsOEVBQThFO2dCQUM5RSxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUU7b0JBQ3pCLElBQUksR0FBRyxDQUFDLENBQUE7aUJBQ1Q7Z0JBRUQsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQTthQUNuQjtZQUVELE1BQU0sWUFBWSxHQUFrQixNQUFNO2lCQUN2QyxHQUFHLENBQUMsMEJBQWEsQ0FBQztpQkFDbEIsTUFBTSxDQUFDLENBQUMsYUFBYSxJQUFJLGFBQWEsQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQTtZQUV4RCxzQkFBc0I7WUFDdEIsSUFBSSxNQUFNLEVBQUU7Z0JBQ1YsTUFBTSxRQUFRLEdBQUcsZ0JBQWdCLENBQUMsTUFBTSxDQUFDLENBQUE7Z0JBQ3pDLElBQUksWUFBWSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7b0JBQzNCLE9BQU8sQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLENBQUMsQ0FBQyxDQUFBO2lCQUNsRTtxQkFBTTtvQkFDTCxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsaUJBQUcsT0FBTyxFQUFFLElBQUksSUFBSyxhQUFhLEVBQUcsQ0FBQyxDQUFBO2lCQUMzRDtnQkFDRCxNQUFNLGtCQUFrQixFQUFFLENBQUE7YUFDM0I7WUFFRCxJQUFJLFlBQVksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO2dCQUMzQixZQUFZLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztvQkFDekMsR0FBRyxFQUFFLEtBQUssQ0FBQyxPQUFPO29CQUNsQixLQUFLO2lCQUNOLENBQUMsQ0FBQyxDQUFBO2dCQUVILElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFlBQVksd0JBQVUsQ0FBQyxFQUFFO29CQUNyRCxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsOEJBQWtCLDZCQUE2QixDQUFDLENBQUE7b0JBQ3JFLE1BQU0sa0JBQWtCLEVBQUUsQ0FBQTtpQkFDM0I7Z0JBRUQsSUFBSSxHQUFHLENBQUMsQ0FBQTthQUNUO1lBRUQsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFBO1lBQ2IsT0FBTyxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLENBQUE7UUFDL0IsQ0FBQztLQUFBO0NBRUY7QUFwTUQsOEJBb01DO0FBRUQsU0FBc0IsR0FBRzs7UUFDdkIsSUFBSSxJQUFJLENBQUE7UUFDUixJQUFJO1lBQ0YsTUFBTSxHQUFHLEdBQUcsSUFBSSxTQUFTLEVBQUUsQ0FBQTtZQUMzQixNQUFNLE1BQU0sR0FBRyxNQUFNLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQTtZQUNoQyxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQTtTQUNuQjtRQUFDLE9BQU8sR0FBRyxFQUFFO1lBQ1osT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQTtZQUNoQixJQUFJLEdBQUcsQ0FBQyxDQUFBO1NBQ1Q7Z0JBQVM7WUFDUixlQUFRLENBQUMsSUFBSSxDQUFDLENBQUE7U0FDZjtJQUNILENBQUM7Q0FBQTtBQVpELGtCQVlDIiwiZmlsZSI6ImNsaS9jbGkuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChDKSAyMDE4IEdhcmRlbiBUZWNobm9sb2dpZXMsIEluYy4gPGluZm9AZ2FyZGVuLmlvPlxuICpcbiAqIFRoaXMgU291cmNlIENvZGUgRm9ybSBpcyBzdWJqZWN0IHRvIHRoZSB0ZXJtcyBvZiB0aGUgTW96aWxsYSBQdWJsaWNcbiAqIExpY2Vuc2UsIHYuIDIuMC4gSWYgYSBjb3B5IG9mIHRoZSBNUEwgd2FzIG5vdCBkaXN0cmlidXRlZCB3aXRoIHRoaXNcbiAqIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uXG4gKi9cblxuaW1wb3J0ICogYXMgc3l3YWMgZnJvbSBcInN5d2FjXCJcbmltcG9ydCB7IG1lcmdlLCBpbnRlcnNlY3Rpb24sIHJhbmdlIH0gZnJvbSBcImxvZGFzaFwiXG5pbXBvcnQgeyByZXNvbHZlIH0gZnJvbSBcInBhdGhcIlxuaW1wb3J0IHsgc2FmZUR1bXAgfSBmcm9tIFwianMteWFtbFwiXG5pbXBvcnQgeyBjb3JlQ29tbWFuZHMgfSBmcm9tIFwiLi4vY29tbWFuZHMvY29tbWFuZHNcIlxuaW1wb3J0IHN0cmluZ2lmeSA9IHJlcXVpcmUoXCJqc29uLXN0cmluZ2lmeS1zYWZlXCIpXG5cbmltcG9ydCB7IERlZXBQcmltaXRpdmVNYXAgfSBmcm9tIFwiLi4vY29uZmlnL2NvbW1vblwiXG5pbXBvcnQge1xuICBnZXRFbnVtS2V5cyxcbiAgc2h1dGRvd24sXG4gIHNsZWVwLFxufSBmcm9tIFwiLi4vdXRpbC91dGlsXCJcbmltcG9ydCB7XG4gIEJvb2xlYW5QYXJhbWV0ZXIsXG4gIENvbW1hbmQsXG4gIENob2ljZXNQYXJhbWV0ZXIsXG4gIFBhcmFtZXRlcixcbiAgU3RyaW5nUGFyYW1ldGVyLFxuICBFbnZpcm9ubWVudE9wdGlvbixcbiAgQ29tbWFuZFJlc3VsdCxcbn0gZnJvbSBcIi4uL2NvbW1hbmRzL2Jhc2VcIlxuaW1wb3J0IHtcbiAgR2FyZGVuRXJyb3IsXG4gIFBsdWdpbkVycm9yLFxuICB0b0dhcmRlbkVycm9yLFxufSBmcm9tIFwiLi4vZXhjZXB0aW9uc1wiXG5pbXBvcnQgeyBHYXJkZW4sIENvbnRleHRPcHRzIH0gZnJvbSBcIi4uL2dhcmRlblwiXG5cbmltcG9ydCB7IExvZ2dlciwgTG9nZ2VyVHlwZSwgZ2V0TG9nZ2VyIH0gZnJvbSBcIi4uL2xvZ2dlci9sb2dnZXJcIlxuaW1wb3J0IHsgTG9nTGV2ZWwgfSBmcm9tIFwiLi4vbG9nZ2VyL2xvZy1ub2RlXCJcbmltcG9ydCB7IEJhc2ljVGVybWluYWxXcml0ZXIgfSBmcm9tIFwiLi4vbG9nZ2VyL3dyaXRlcnMvYmFzaWMtdGVybWluYWwtd3JpdGVyXCJcbmltcG9ydCB7IEZhbmN5VGVybWluYWxXcml0ZXIgfSBmcm9tIFwiLi4vbG9nZ2VyL3dyaXRlcnMvZmFuY3ktdGVybWluYWwtd3JpdGVyXCJcbmltcG9ydCB7IEZpbGVXcml0ZXIgfSBmcm9tIFwiLi4vbG9nZ2VyL3dyaXRlcnMvZmlsZS13cml0ZXJcIlxuaW1wb3J0IHsgV3JpdGVyIH0gZnJvbSBcIi4uL2xvZ2dlci93cml0ZXJzL2Jhc2VcIlxuXG5pbXBvcnQge1xuICBmYWxzaWZ5Q29uZmxpY3RpbmdQYXJhbXMsXG4gIGZhaWxPbkludmFsaWRPcHRpb25zLFxuICBnZXRBcmdTeW5vcHNpcyxcbiAgZ2V0S2V5cyxcbiAgZ2V0T3B0aW9uU3lub3BzaXMsXG4gIGZpbHRlckJ5S2V5cyxcbiAgcHJlcGFyZUFyZ0NvbmZpZyxcbiAgcHJlcGFyZU9wdGlvbkNvbmZpZyxcbiAgc3R5bGVDb25maWcsXG59IGZyb20gXCIuL2hlbHBlcnNcIlxuaW1wb3J0IHsgR2FyZGVuQ29uZmlnIH0gZnJvbSBcIi4uL2NvbmZpZy9iYXNlXCJcbmltcG9ydCB7IGRlZmF1bHRFbnZpcm9ubWVudHMgfSBmcm9tIFwiLi4vY29uZmlnL3Byb2plY3RcIlxuaW1wb3J0IHsgRVJST1JfTE9HX0ZJTEVOQU1FIH0gZnJvbSBcIi4uL2NvbnN0YW50c1wiXG5cbmNvbnN0IE9VVFBVVF9SRU5ERVJFUlMgPSB7XG4gIGpzb246IChkYXRhOiBEZWVwUHJpbWl0aXZlTWFwKSA9PiB7XG4gICAgcmV0dXJuIHN0cmluZ2lmeShkYXRhLCBudWxsLCAyKVxuICB9LFxuICB5YW1sOiAoZGF0YTogRGVlcFByaW1pdGl2ZU1hcCkgPT4ge1xuICAgIHJldHVybiBzYWZlRHVtcChkYXRhLCB7IG5vUmVmczogdHJ1ZSwgc2tpcEludmFsaWQ6IHRydWUgfSlcbiAgfSxcbn1cblxuY29uc3QgbG9nTGV2ZWxLZXlzID0gZ2V0RW51bUtleXMoTG9nTGV2ZWwpXG4vLyBBbGxvdyBzdHJpbmcgb3IgbnVtZXJpYyBsb2cgbGV2ZWxzXG5jb25zdCBsb2dMZXZlbENob2ljZXMgPSBbLi4ubG9nTGV2ZWxLZXlzLCAuLi5yYW5nZShsb2dMZXZlbEtleXMubGVuZ3RoKS5tYXAoU3RyaW5nKV1cblxuY29uc3QgZ2V0TG9nTGV2ZWxGcm9tQXJnID0gKGxldmVsOiBzdHJpbmcpID0+IHtcbiAgY29uc3QgbHZsID0gcGFyc2VJbnQobGV2ZWwsIDEwKVxuICBpZiAobHZsKSB7XG4gICAgcmV0dXJuIGx2bFxuICB9XG4gIHJldHVybiBMb2dMZXZlbFtsZXZlbF1cbn1cblxuLy8gRm9yIGluaXRpYWxpemluZyBnYXJkZW4gd2l0aG91dCBhIHByb2plY3QgY29uZmlnXG5leHBvcnQgY29uc3QgTU9DS19DT05GSUc6IEdhcmRlbkNvbmZpZyA9IHtcbiAgdmVyc2lvbjogXCIwXCIsXG4gIGRpcm5hbWU6IFwiL1wiLFxuICBwYXRoOiBwcm9jZXNzLmN3ZCgpLFxuICBwcm9qZWN0OiB7XG4gICAgbmFtZTogXCJtb2NrLXByb2plY3RcIixcbiAgICBkZWZhdWx0RW52aXJvbm1lbnQ6IFwibG9jYWxcIixcbiAgICBlbnZpcm9ubWVudHM6IGRlZmF1bHRFbnZpcm9ubWVudHMsXG4gICAgZW52aXJvbm1lbnREZWZhdWx0czoge1xuICAgICAgcHJvdmlkZXJzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBuYW1lOiBcImxvY2FsLWt1YmVybmV0ZXNcIixcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgICB2YXJpYWJsZXM6IHt9LFxuICAgIH0sXG4gIH0sXG59XG5cbmV4cG9ydCBjb25zdCBHTE9CQUxfT1BUSU9OUyA9IHtcbiAgcm9vdDogbmV3IFN0cmluZ1BhcmFtZXRlcih7XG4gICAgYWxpYXM6IFwiclwiLFxuICAgIGhlbHA6IFwiT3ZlcnJpZGUgcHJvamVjdCByb290IGRpcmVjdG9yeSAoZGVmYXVsdHMgdG8gd29ya2luZyBkaXJlY3RvcnkpLlwiLFxuICAgIGRlZmF1bHRWYWx1ZTogcHJvY2Vzcy5jd2QoKSxcbiAgfSksXG4gIHNpbGVudDogbmV3IEJvb2xlYW5QYXJhbWV0ZXIoe1xuICAgIGFsaWFzOiBcInNcIixcbiAgICBoZWxwOiBcIlN1cHByZXNzIGxvZyBvdXRwdXQuXCIsXG4gICAgZGVmYXVsdFZhbHVlOiBmYWxzZSxcbiAgfSksXG4gIGVudjogbmV3IEVudmlyb25tZW50T3B0aW9uKCksXG4gIGxvZ2xldmVsOiBuZXcgQ2hvaWNlc1BhcmFtZXRlcih7XG4gICAgYWxpYXM6IFwibFwiLFxuICAgIGNob2ljZXM6IGxvZ0xldmVsQ2hvaWNlcyxcbiAgICBoZWxwOlxuICAgICAgXCJTZXQgbG9nZ2VyIGxldmVsLiBWYWx1ZXMgY2FuIGJlIGVpdGhlciBzdHJpbmcgb3IgbnVtZXJpYyBhbmQgYXJlIHByaW9yaXRpemVkIGZyb20gMCB0byA1IFwiICtcbiAgICAgIFwiKGhpZ2hlc3QgdG8gbG93ZXN0KSBhcyBmb2xsb3dzOiBlcnJvcjogMCwgd2FybjogMSwgaW5mbzogMiwgdmVyYm9zZTogMywgZGVidWc6IDQsIHNpbGx5OiA1XCIsXG4gICAgaGludHM6XG4gICAgICBcIltlbnVtXSBbZGVmYXVsdDogaW5mb10gW2Vycm9yIHx8IDAsIHdhcm4gfHwgMSwgaW5mbyB8fCAyLCB2ZXJib3NlIHx8IDMsIGRlYnVnIHx8IDQsIHNpbGx5IHx8IDVdXCIsXG4gICAgZGVmYXVsdFZhbHVlOiBMb2dMZXZlbFtMb2dMZXZlbC5pbmZvXSxcbiAgfSksXG4gIG91dHB1dDogbmV3IENob2ljZXNQYXJhbWV0ZXIoe1xuICAgIGFsaWFzOiBcIm9cIixcbiAgICBjaG9pY2VzOiBPYmplY3Qua2V5cyhPVVRQVVRfUkVOREVSRVJTKSxcbiAgICBoZWxwOiBcIk91dHB1dCBjb21tYW5kIHJlc3VsdCBpbiBzcGVjaWZpZWQgZm9ybWF0IChub3RlOiBkaXNhYmxlcyBwcm9ncmVzcyBsb2dnaW5nKS5cIixcbiAgfSksXG59XG5jb25zdCBHTE9CQUxfT1BUSU9OU19HUk9VUF9OQU1FID0gXCJHbG9iYWwgb3B0aW9uc1wiXG5jb25zdCBERUZBVUxUX0NMSV9MT0dHRVJfVFlQRSA9IExvZ2dlclR5cGUuZmFuY3lcblxuZXhwb3J0IGludGVyZmFjZSBQYXJzZVJlc3VsdHMge1xuICBhcmd2OiBhbnlcbiAgY29kZTogbnVtYmVyXG4gIGVycm9yczogKEdhcmRlbkVycm9yIHwgRXJyb3IpW11cbn1cblxuaW50ZXJmYWNlIFN5d2FjUGFyc2VSZXN1bHRzIGV4dGVuZHMgUGFyc2VSZXN1bHRzIHtcbiAgb3V0cHV0OiBzdHJpbmdcbiAgZGV0YWlsczogeyBsb2dnZXI6IExvZ2dlciwgcmVzdWx0PzogQ29tbWFuZFJlc3VsdCB9XG59XG5cbmV4cG9ydCBjbGFzcyBHYXJkZW5DbGkge1xuICBwcm9ncmFtOiBhbnlcbiAgY29tbWFuZHM6IHsgW2tleTogc3RyaW5nXTogQ29tbWFuZCB9ID0ge31cblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICBjb25zdCB2ZXJzaW9uID0gcmVxdWlyZShcIi4uLy4uL3BhY2thZ2UuanNvblwiKS52ZXJzaW9uXG4gICAgdGhpcy5wcm9ncmFtID0gc3l3YWNcbiAgICAgIC5oZWxwKFwiLWgsIC0taGVscFwiLCB7XG4gICAgICAgIGdyb3VwOiBHTE9CQUxfT1BUSU9OU19HUk9VUF9OQU1FLFxuICAgICAgICBpbXBsaWNpdENvbW1hbmQ6IGZhbHNlLFxuICAgICAgfSlcbiAgICAgIC52ZXJzaW9uKFwiLXYsIC0tdmVyc2lvblwiLCB7XG4gICAgICAgIHZlcnNpb24sXG4gICAgICAgIGdyb3VwOiBHTE9CQUxfT1BUSU9OU19HUk9VUF9OQU1FLFxuICAgICAgICBpbXBsaWNpdENvbW1hbmQ6IGZhbHNlLFxuICAgICAgfSlcbiAgICAgIC5zaG93SGVscEJ5RGVmYXVsdCgpXG4gICAgICAuY2hlY2soKGFyZ3YsIF9jdHgpID0+IHtcbiAgICAgICAgLy8gTk9URTogTmVlZCB0byBtdXRhdGUgYXJndiFcbiAgICAgICAgbWVyZ2UoYXJndiwgZmFsc2lmeUNvbmZsaWN0aW5nUGFyYW1zKGFyZ3YsIEdMT0JBTF9PUFRJT05TKSlcbiAgICAgIH0pXG4gICAgICAuc3R5bGUoc3R5bGVDb25maWcpXG5cbiAgICBjb25zdCBjb21tYW5kcyA9IGNvcmVDb21tYW5kc1xuXG4gICAgY29uc3QgZ2xvYmFsT3B0aW9ucyA9IE9iamVjdC5lbnRyaWVzKEdMT0JBTF9PUFRJT05TKVxuXG4gICAgY29tbWFuZHMuZm9yRWFjaChjb21tYW5kID0+IHRoaXMuYWRkQ29tbWFuZChjb21tYW5kLCB0aGlzLnByb2dyYW0pKVxuICAgIGdsb2JhbE9wdGlvbnMuZm9yRWFjaCgoW2tleSwgb3B0XSkgPT4gdGhpcy5hZGRHbG9iYWxPcHRpb24oa2V5LCBvcHQpKVxuICB9XG5cbiAgYWRkR2xvYmFsT3B0aW9uKGtleTogc3RyaW5nLCBvcHRpb246IFBhcmFtZXRlcjxhbnk+KTogdm9pZCB7XG4gICAgdGhpcy5wcm9ncmFtLm9wdGlvbihnZXRPcHRpb25TeW5vcHNpcyhrZXksIG9wdGlvbiksIHtcbiAgICAgIC4uLnByZXBhcmVPcHRpb25Db25maWcob3B0aW9uKSxcbiAgICAgIGdyb3VwOiBHTE9CQUxfT1BUSU9OU19HUk9VUF9OQU1FLFxuICAgIH0pXG4gIH1cblxuICBhZGRDb21tYW5kKGNvbW1hbmQ6IENvbW1hbmQsIHByb2dyYW0pOiB2b2lkIHtcbiAgICBjb25zdCBmdWxsTmFtZSA9IGNvbW1hbmQuZ2V0RnVsbE5hbWUoKVxuXG4gICAgaWYgKHRoaXMuY29tbWFuZHNbZnVsbE5hbWVdKSB7XG4gICAgICAvLyBGb3Igbm93IHdlIGRvbid0IGFsbG93IG11bHRpcGxlIGRlZmluaXRpb25zIG9mIHRoZSBzYW1lIGNvbW1hbmQuIFdlIG1heSB3YW50IHRvIHJldmlzaXQgdGhpcyBsYXRlci5cbiAgICAgIHRocm93IG5ldyBQbHVnaW5FcnJvcihgTXVsdGlwbGUgZGVmaW5pdGlvbnMgb2YgY29tbWFuZCBcIiR7ZnVsbE5hbWV9XCJgLCB7fSlcbiAgICB9XG5cbiAgICB0aGlzLmNvbW1hbmRzW2Z1bGxOYW1lXSA9IGNvbW1hbmRcblxuICAgIGNvbnN0IHtcbiAgICAgIGFyZ3VtZW50czogYXJncyA9IHt9LFxuICAgICAgbG9nZ2VyVHlwZSA9IERFRkFVTFRfQ0xJX0xPR0dFUl9UWVBFLFxuICAgICAgb3B0aW9ucyA9IHt9LFxuICAgICAgc3ViQ29tbWFuZHMsXG4gICAgfSA9IGNvbW1hbmRcblxuICAgIGNvbnN0IGFyZ0tleXMgPSBnZXRLZXlzKGFyZ3MpXG4gICAgY29uc3Qgb3B0S2V5cyA9IGdldEtleXMob3B0aW9ucylcbiAgICBjb25zdCBnbG9iYWxLZXlzID0gZ2V0S2V5cyhHTE9CQUxfT1BUSU9OUylcbiAgICBjb25zdCBkdXBLZXlzOiBzdHJpbmdbXSA9IGludGVyc2VjdGlvbihvcHRLZXlzLCBnbG9iYWxLZXlzKVxuXG4gICAgaWYgKGR1cEtleXMubGVuZ3RoID4gMCkge1xuICAgICAgdGhyb3cgbmV3IFBsdWdpbkVycm9yKGBHbG9iYWwgb3B0aW9uKHMpICR7ZHVwS2V5cy5qb2luKFwiIFwiKX0gY2Fubm90IGJlIHJlZGVmaW5lZGAsIHt9KVxuICAgIH1cblxuICAgIGNvbnN0IGFjdGlvbiA9IGFzeW5jIChhcmd2LCBjbGlDb250ZXh0KSA9PiB7XG4gICAgICAvLyBTeXdhYyByZXR1cm5zIHBvc2l0aW9uYWwgYXJncyBhbmQgb3B0aW9ucyBpbiBhIHNpbmdsZSBvYmplY3Qgd2hpY2ggd2Ugc2VwYXJhdGUgaW50byBhcmdzIGFuZCBvcHRzXG4gICAgICBjb25zdCBwYXJzZWRBcmdzID0gZmlsdGVyQnlLZXlzKGFyZ3YsIGFyZ0tleXMpXG4gICAgICBjb25zdCBwYXJzZWRPcHRzID0gZmlsdGVyQnlLZXlzKGFyZ3YsIG9wdEtleXMuY29uY2F0KGdsb2JhbEtleXMpKVxuICAgICAgY29uc3Qgcm9vdCA9IHJlc29sdmUocHJvY2Vzcy5jd2QoKSwgcGFyc2VkT3B0cy5yb290KVxuICAgICAgY29uc3QgeyBlbnYsIGxvZ2xldmVsLCBzaWxlbnQsIG91dHB1dCB9ID0gcGFyc2VkT3B0c1xuXG4gICAgICAvLyBJbml0IGxvZ2dlclxuICAgICAgY29uc3QgbGV2ZWwgPSBnZXRMb2dMZXZlbEZyb21BcmcobG9nbGV2ZWwpXG4gICAgICBsZXQgd3JpdGVyczogV3JpdGVyW10gPSBbXVxuXG4gICAgICBpZiAoIXNpbGVudCAmJiAhb3V0cHV0ICYmIGxvZ2dlclR5cGUgIT09IExvZ2dlclR5cGUucXVpZXQpIHtcbiAgICAgICAgaWYgKGxvZ2dlclR5cGUgPT09IExvZ2dlclR5cGUuZmFuY3kpIHtcbiAgICAgICAgICB3cml0ZXJzLnB1c2gobmV3IEZhbmN5VGVybWluYWxXcml0ZXIoKSlcbiAgICAgICAgfSBlbHNlIGlmIChsb2dnZXJUeXBlID09PSBMb2dnZXJUeXBlLmJhc2ljKSB7XG4gICAgICAgICAgd3JpdGVycy5wdXNoKG5ldyBCYXNpY1Rlcm1pbmFsV3JpdGVyKCkpXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgY29uc3QgbG9nZ2VyID0gTG9nZ2VyLmluaXRpYWxpemUoeyBsZXZlbCwgd3JpdGVycyB9KVxuICAgICAgbGV0IGdhcmRlbjogR2FyZGVuXG4gICAgICBsZXQgcmVzdWx0XG4gICAgICBkbyB7XG4gICAgICAgIGNvbnN0IGNvbnRleHRPcHRzOiBDb250ZXh0T3B0cyA9IHsgZW52LCBsb2dnZXIgfVxuICAgICAgICBpZiAoY29tbWFuZC5ub1Byb2plY3QpIHtcbiAgICAgICAgICBjb250ZXh0T3B0cy5jb25maWcgPSBNT0NLX0NPTkZJR1xuICAgICAgICB9XG4gICAgICAgIGdhcmRlbiA9IGF3YWl0IEdhcmRlbi5mYWN0b3J5KHJvb3QsIGNvbnRleHRPcHRzKVxuICAgICAgICAvLyBUT0RPOiBlbmZvcmNlIHRoYXQgY29tbWFuZHMgYWx3YXlzIG91dHB1dCBEZWVwUHJpbWl0aXZlTWFwXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IGNvbW1hbmQuYWN0aW9uKHtcbiAgICAgICAgICBnYXJkZW4sXG4gICAgICAgICAgYXJnczogcGFyc2VkQXJncyxcbiAgICAgICAgICBvcHRzOiBwYXJzZWRPcHRzLFxuICAgICAgICB9KVxuICAgICAgfSB3aGlsZSAocmVzdWx0LnJlc3RhcnRSZXF1aXJlZClcblxuICAgICAgLy8gV2UgYXR0YWNoIHRoZSBhY3Rpb24gcmVzdWx0IHRvIGNsaSBjb250ZXh0IHNvIHRoYXQgd2UgY2FuIHByb2Nlc3MgaXQgaW4gdGhlIHBhcnNlIG1ldGhvZFxuICAgICAgY2xpQ29udGV4dC5kZXRhaWxzLnJlc3VsdCA9IHJlc3VsdFxuICAgIH1cblxuICAgIC8vIENvbW1hbmQgc3BlY2lmaWMgcG9zaXRpb25hbCBhcmdzIGFuZCBvcHRpb25zIGFyZSBzZXQgaW5zaWRlIHRoZSBidWlsZGVyIGZ1bmN0aW9uXG4gICAgY29uc3Qgc2V0dXAgPSBwYXJzZXIgPT4ge1xuICAgICAgc3ViQ29tbWFuZHMuZm9yRWFjaChzdWJDb21tYW5kQ2xzID0+IHRoaXMuYWRkQ29tbWFuZChuZXcgc3ViQ29tbWFuZENscyhjb21tYW5kKSwgcGFyc2VyKSlcbiAgICAgIGFyZ0tleXMuZm9yRWFjaChrZXkgPT4gcGFyc2VyLnBvc2l0aW9uYWwoZ2V0QXJnU3lub3BzaXMoa2V5LCBhcmdzW2tleV0pLCBwcmVwYXJlQXJnQ29uZmlnKGFyZ3Nba2V5XSkpKVxuICAgICAgb3B0S2V5cy5mb3JFYWNoKGtleSA9PiBwYXJzZXIub3B0aW9uKGdldE9wdGlvblN5bm9wc2lzKGtleSwgb3B0aW9uc1trZXldKSwgcHJlcGFyZU9wdGlvbkNvbmZpZyhvcHRpb25zW2tleV0pKSlcblxuICAgICAgLy8gV2Ugb25seSBjaGVjayBmb3IgaW52YWxpZCBmbGFncyBmb3IgdGhlIGxhc3QgY29tbWFuZCBzaW5jZSBpdCBtaWdodCBjb250YWluIGZsYWdzIHRoYXRcbiAgICAgIC8vIHRoZSBwYXJlbnQgaXMgdW5hd2FyZSBvZiwgdGh1cyBjYXVzaW5nIHRoZSBjaGVjayB0byBmYWlsIGZvciB0aGUgcGFyZW50XG4gICAgICBpZiAoc3ViQ29tbWFuZHMubGVuZ3RoIDwgMSkge1xuICAgICAgICBwYXJzZXIuY2hlY2soZmFpbE9uSW52YWxpZE9wdGlvbnMpXG4gICAgICB9XG4gICAgICByZXR1cm4gcGFyc2VyXG4gICAgfVxuXG4gICAgY29uc3QgY29tbWFuZENvbmZpZyA9IHtcbiAgICAgIHNldHVwLFxuICAgICAgYWxpYXNlczogY29tbWFuZC5hbGlhcyxcbiAgICAgIGRlc2M6IGNvbW1hbmQuaGVscCxcbiAgICAgIHJ1bjogYWN0aW9uLFxuICAgIH1cblxuICAgIHByb2dyYW0uY29tbWFuZChjb21tYW5kLm5hbWUsIGNvbW1hbmRDb25maWcpXG4gIH1cblxuICBhc3luYyBwYXJzZSgpOiBQcm9taXNlPFBhcnNlUmVzdWx0cz4ge1xuICAgIGNvbnN0IHBhcnNlUmVzdWx0OiBTeXdhY1BhcnNlUmVzdWx0cyA9IGF3YWl0IHRoaXMucHJvZ3JhbS5wYXJzZSgpXG4gICAgY29uc3QgeyBhcmd2LCBkZXRhaWxzLCBlcnJvcnMsIG91dHB1dDogY2xpT3V0cHV0IH0gPSBwYXJzZVJlc3VsdFxuICAgIGNvbnN0IHsgcmVzdWx0OiBjb21tYW5kUmVzdWx0IH0gPSBkZXRhaWxzXG4gICAgY29uc3QgeyBvdXRwdXQgfSA9IGFyZ3ZcbiAgICBsZXQgeyBjb2RlIH0gPSBwYXJzZVJlc3VsdFxuICAgIGxldCBsb2dnZXI6IExvZ2dlclxuXG4gICAgLy8gTm90ZTogQ2lyY3VtdmVudHMgYW4gaXNzdWUgd2hlcmUgdGhlIHByb2Nlc3MgZXhpdHMgYmVmb3JlIHRoZSBvdXRwdXQgaXMgZnVsbHkgZmx1c2hlZC5cbiAgICAvLyBOZWVkZWQgZm9yIG91dHB1dCByZW5kZXJlcnMgYW5kIFdpbnN0b24gKHNlZTogaHR0cHM6Ly9naXRodWIuY29tL3dpbnN0b25qcy93aW5zdG9uL2lzc3Vlcy8yMjgpXG4gICAgY29uc3Qgd2FpdEZvck91dHB1dEZsdXNoID0gKCkgPT4gc2xlZXAoMTAwKVxuXG4gICAgLy8gTG9nZ2VyIG1pZ2h0IG5vdCBoYXZlIGJlZW4gaW5pdGlhbGlzZWQgaWYgcHJvY2VzcyBleGl0cyBlYXJseVxuICAgIHRyeSB7XG4gICAgICBsb2dnZXIgPSBnZXRMb2dnZXIoKVxuICAgIH0gY2F0Y2ggKF8pIHtcbiAgICAgIGxvZ2dlciA9IExvZ2dlci5pbml0aWFsaXplKHtcbiAgICAgICAgbGV2ZWw6IExvZ0xldmVsLmluZm8sXG4gICAgICAgIHdyaXRlcnM6IFtuZXcgQmFzaWNUZXJtaW5hbFdyaXRlcigpXSxcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgLy8gLS1oZWxwIG9yIC0tdmVyc2lvbiBvcHRpb25zIHdlcmUgY2FsbGVkIHNvIHdlIGxvZyB0aGUgY2xpIG91dHB1dCBhbmQgZXhpdFxuICAgIGlmIChjbGlPdXRwdXQgJiYgZXJyb3JzLmxlbmd0aCA8IDEpIHtcbiAgICAgIGxvZ2dlci5zdG9wKClcbiAgICAgIGNvbnNvbGUubG9nKGNsaU91dHB1dClcblxuICAgICAgLy8gZml4IGlzc3VlIHdoZXJlIHN5d2FjIHJldHVybnMgZXhpdCBjb2RlIDAgZXZlbiB3aGVuIGEgY29tbWFuZCBkb2Vzbid0IGV4aXN0XG4gICAgICBpZiAoIWFyZ3YuaCAmJiAhYXJndi5oZWxwKSB7XG4gICAgICAgIGNvZGUgPSAxXG4gICAgICB9XG5cbiAgICAgIHByb2Nlc3MuZXhpdChjb2RlKVxuICAgIH1cblxuICAgIGNvbnN0IGdhcmRlbkVycm9yczogR2FyZGVuRXJyb3JbXSA9IGVycm9yc1xuICAgICAgLm1hcCh0b0dhcmRlbkVycm9yKVxuICAgICAgLmNvbmNhdCgoY29tbWFuZFJlc3VsdCAmJiBjb21tYW5kUmVzdWx0LmVycm9ycykgfHwgW10pXG5cbiAgICAvLyAtLW91dHB1dCBvcHRpb24gc2V0XG4gICAgaWYgKG91dHB1dCkge1xuICAgICAgY29uc3QgcmVuZGVyZXIgPSBPVVRQVVRfUkVOREVSRVJTW291dHB1dF1cbiAgICAgIGlmIChnYXJkZW5FcnJvcnMubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zb2xlLmVycm9yKHJlbmRlcmVyKHsgc3VjY2VzczogZmFsc2UsIGVycm9yczogZ2FyZGVuRXJyb3JzIH0pKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc29sZS5sb2cocmVuZGVyZXIoeyBzdWNjZXNzOiB0cnVlLCAuLi5jb21tYW5kUmVzdWx0IH0pKVxuICAgICAgfVxuICAgICAgYXdhaXQgd2FpdEZvck91dHB1dEZsdXNoKClcbiAgICB9XG5cbiAgICBpZiAoZ2FyZGVuRXJyb3JzLmxlbmd0aCA+IDApIHtcbiAgICAgIGdhcmRlbkVycm9ycy5mb3JFYWNoKGVycm9yID0+IGxvZ2dlci5lcnJvcih7XG4gICAgICAgIG1zZzogZXJyb3IubWVzc2FnZSxcbiAgICAgICAgZXJyb3IsXG4gICAgICB9KSlcblxuICAgICAgaWYgKGxvZ2dlci53cml0ZXJzLmZpbmQodyA9PiB3IGluc3RhbmNlb2YgRmlsZVdyaXRlcikpIHtcbiAgICAgICAgbG9nZ2VyLmluZm8oYFxcblNlZSAke0VSUk9SX0xPR19GSUxFTkFNRX0gZm9yIGRldGFpbGVkIGVycm9yIG1lc3NhZ2VgKVxuICAgICAgICBhd2FpdCB3YWl0Rm9yT3V0cHV0Rmx1c2goKVxuICAgICAgfVxuXG4gICAgICBjb2RlID0gMVxuICAgIH1cblxuICAgIGxvZ2dlci5zdG9wKClcbiAgICByZXR1cm4geyBhcmd2LCBjb2RlLCBlcnJvcnMgfVxuICB9XG5cbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJ1bigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgbGV0IGNvZGVcbiAgdHJ5IHtcbiAgICBjb25zdCBjbGkgPSBuZXcgR2FyZGVuQ2xpKClcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBjbGkucGFyc2UoKVxuICAgIGNvZGUgPSByZXN1bHQuY29kZVxuICB9IGNhdGNoIChlcnIpIHtcbiAgICBjb25zb2xlLmxvZyhlcnIpXG4gICAgY29kZSA9IDFcbiAgfSBmaW5hbGx5IHtcbiAgICBzaHV0ZG93bihjb2RlKVxuICB9XG59XG4iXX0=
diff --git a/garden-service/build/cli/helpers.d.ts b/garden-service/build/cli/helpers.d.ts
new file mode 100644
index 00000000000..a993a259090
--- /dev/null
+++ b/garden-service/build/cli/helpers.d.ts
@@ -0,0 +1,45 @@
+import { ParameterValues, Parameter } from "../commands/base";
+export declare const styleConfig: {
+ usagePrefix: (str: any) => string;
+ usageCommandPlaceholder: (str: any) => string;
+ usagePositionals: (str: any) => string;
+ usageArgsPlaceholder: (str: any) => string;
+ usageOptionsPlaceholder: (str: any) => string;
+ group: (str: string) => string;
+ flags: (str: any, _type: any) => string;
+ hints: (str: any) => string;
+ groupError: (str: any) => string;
+ flagsError: (str: any) => string;
+ descError: (str: any) => string;
+ hintsError: (str: any) => string;
+ messages: (str: any) => string;
+};
+export declare const getKeys: (obj: any) => string[];
+export declare const filterByKeys: (obj: any, keys: string[]) => any;
+export declare type FalsifiedParams = {
+ [key: string]: false;
+};
+/**
+ * Returns the params that need to be overridden set to false
+ */
+export declare function falsifyConflictingParams(argv: any, params: ParameterValues): FalsifiedParams;
+export declare function getOptionSynopsis(key: string, { alias }: Parameter): string;
+export declare function getArgSynopsis(key: string, param: Parameter): string;
+export declare function prepareArgConfig(param: Parameter): {
+ desc: string;
+ params: SywacOptionConfig[];
+};
+export interface SywacOptionConfig {
+ desc: string | string[];
+ type: string;
+ defaultValue?: any;
+ coerce?: Function;
+ choices?: any[];
+ required?: boolean;
+ hints?: string;
+ strict: true;
+ mustExist: true;
+}
+export declare function prepareOptionConfig(param: Parameter): SywacOptionConfig;
+export declare function failOnInvalidOptions(argv: any, ctx: any): void;
+//# sourceMappingURL=helpers.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/cli/helpers.js b/garden-service/build/cli/helpers.js
new file mode 100644
index 00000000000..821aa57bdf9
--- /dev/null
+++ b/garden-service/build/cli/helpers.js
@@ -0,0 +1,133 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+Object.defineProperty(exports, "__esModule", { value: true });
+const chalk_1 = require("chalk");
+const lodash_1 = require("lodash");
+const exceptions_1 = require("../exceptions");
+// Parameter types T which map between the Parameter class and the Sywac cli library.
+// In case we add types that aren't supported natively by Sywac, see: http://sywac.io/docs/sync-config.html#custom
+const VALID_PARAMETER_TYPES = ["boolean", "number", "choice", "string", "array:string", "path", "array:path"];
+exports.styleConfig = {
+ usagePrefix: str => (`
+${chalk_1.default.bold(str.slice(0, 5).toUpperCase())}
+ ${chalk_1.default.italic(str.slice(7))}`),
+ usageCommandPlaceholder: str => chalk_1.default.blue(str),
+ usagePositionals: str => chalk_1.default.magenta(str),
+ usageArgsPlaceholder: str => chalk_1.default.magenta(str),
+ usageOptionsPlaceholder: str => chalk_1.default.yellow(str),
+ group: (str) => {
+ const cleaned = str.endsWith(":") ? str.slice(0, -1) : str;
+ return chalk_1.default.bold(cleaned.toUpperCase());
+ },
+ flags: (str, _type) => {
+ const style = str.startsWith("-") ? chalk_1.default.green : chalk_1.default.magenta;
+ return style(str);
+ },
+ hints: str => chalk_1.default.gray(str),
+ groupError: str => chalk_1.default.red.bold(str),
+ flagsError: str => chalk_1.default.red.bold(str),
+ descError: str => chalk_1.default.yellow.bold(str),
+ hintsError: str => chalk_1.default.red(str),
+ messages: str => chalk_1.default.red.bold(str),
+};
+// Helper functions
+exports.getKeys = (obj) => Object.keys(obj || {});
+exports.filterByKeys = (obj, keys) => {
+ return keys.reduce((memo, key) => {
+ if (obj[key]) {
+ memo[key] = obj[key];
+ }
+ return memo;
+ }, {});
+};
+/**
+ * Returns the params that need to be overridden set to false
+ */
+function falsifyConflictingParams(argv, params) {
+ return lodash_1.reduce(argv, (acc, val, key) => {
+ const param = params[key];
+ const overrides = (param || {}).overrides || [];
+ // argv always contains the "_" key which is irrelevant here
+ if (key === "_" || !param || !val || !(overrides.length > 0)) {
+ return acc;
+ }
+ const withAliases = overrides.reduce((_, keyToOverride) => {
+ if (!params[keyToOverride]) {
+ throw new exceptions_1.InternalError(`Cannot override non-existing parameter: ${keyToOverride}`, {
+ keyToOverride,
+ availableKeys: Object.keys(params),
+ });
+ }
+ return [keyToOverride, ...params[keyToOverride].alias];
+ }, []);
+ withAliases.forEach(keyToOverride => acc[keyToOverride] = false);
+ return acc;
+ }, {});
+}
+exports.falsifyConflictingParams = falsifyConflictingParams;
+// Sywac specific transformers and helpers
+function getOptionSynopsis(key, { alias }) {
+ if (alias && alias.length > 1) {
+ throw new exceptions_1.InternalError("Option aliases can only be a single character", {
+ optionName: key,
+ alias,
+ });
+ }
+ return alias ? `-${alias}, --${key}` : `--${key}`;
+}
+exports.getOptionSynopsis = getOptionSynopsis;
+function getArgSynopsis(key, param) {
+ return param.required ? `<${key}>` : `[${key}]`;
+}
+exports.getArgSynopsis = getArgSynopsis;
+function prepareArgConfig(param) {
+ return {
+ desc: param.help,
+ params: [prepareOptionConfig(param)],
+ };
+}
+exports.prepareArgConfig = prepareArgConfig;
+function prepareOptionConfig(param) {
+ const { coerce, defaultValue, help: desc, hints, required, type, } = param;
+ if (!VALID_PARAMETER_TYPES.includes(type)) {
+ throw new exceptions_1.InternalError(`Invalid parameter type for cli: ${type}`, {
+ type,
+ validParameterTypes: VALID_PARAMETER_TYPES,
+ });
+ }
+ let config = {
+ coerce,
+ defaultValue,
+ desc,
+ required,
+ type,
+ hints,
+ strict: true,
+ mustExist: true,
+ };
+ if (type === "choice") {
+ config.type = "enum";
+ config.choices = param.choices;
+ }
+ return config;
+}
+exports.prepareOptionConfig = prepareOptionConfig;
+function failOnInvalidOptions(argv, ctx) {
+ const validOptions = lodash_1.flatten(ctx.details.types
+ .filter(t => t.datatype !== "command")
+ .map(t => t.aliases));
+ const receivedOptions = Object.keys(argv);
+ const invalid = lodash_1.difference(receivedOptions, validOptions);
+ if (invalid.length > 0) {
+ ctx.cliMessage(`Received invalid flag(s): ${invalid.join(", ")}`);
+ }
+}
+exports.failOnInvalidOptions = failOnInvalidOptions;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,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QUMsNkJBQTZCLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFBO0tBQ2xFO0FBQ0gsQ0FBQztBQVhELG9EQVdDIiwiZmlsZSI6ImNsaS9oZWxwZXJzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoQykgMjAxOCBHYXJkZW4gVGVjaG5vbG9naWVzLCBJbmMuIDxpbmZvQGdhcmRlbi5pbz5cbiAqXG4gKiBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljXG4gKiBMaWNlbnNlLCB2LiAyLjAuIElmIGEgY29weSBvZiB0aGUgTVBMIHdhcyBub3QgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzXG4gKiBmaWxlLCBZb3UgY2FuIG9idGFpbiBvbmUgYXQgaHR0cDovL21vemlsbGEub3JnL01QTC8yLjAvLlxuICovXG5cbmltcG9ydCBjaGFsayBmcm9tIFwiY2hhbGtcIlxuaW1wb3J0IHsgZGlmZmVyZW5jZSwgZmxhdHRlbiwgcmVkdWNlIH0gZnJvbSBcImxvZGFzaFwiXG5pbXBvcnQge1xuICBDaG9pY2VzUGFyYW1ldGVyLFxuICBQYXJhbWV0ZXJWYWx1ZXMsXG4gIFBhcmFtZXRlcixcbn0gZnJvbSBcIi4uL2NvbW1hbmRzL2Jhc2VcIlxuaW1wb3J0IHtcbiAgSW50ZXJuYWxFcnJvcixcbn0gZnJvbSBcIi4uL2V4Y2VwdGlvbnNcIlxuXG4vLyBQYXJhbWV0ZXIgdHlwZXMgVCB3aGljaCBtYXAgYmV0d2VlbiB0aGUgUGFyYW1ldGVyPFQ+IGNsYXNzIGFuZCB0aGUgU3l3YWMgY2xpIGxpYnJhcnkuXG4vLyBJbiBjYXNlIHdlIGFkZCB0eXBlcyB0aGF0IGFyZW4ndCBzdXBwb3J0ZWQgbmF0aXZlbHkgYnkgU3l3YWMsIHNlZTogaHR0cDovL3N5d2FjLmlvL2RvY3Mvc3luYy1jb25maWcuaHRtbCNjdXN0b21cbmNvbnN0IFZBTElEX1BBUkFNRVRFUl9UWVBFUyA9IFtcImJvb2xlYW5cIiwgXCJudW1iZXJcIiwgXCJjaG9pY2VcIiwgXCJzdHJpbmdcIiwgXCJhcnJheTpzdHJpbmdcIiwgXCJwYXRoXCIsIFwiYXJyYXk6cGF0aFwiXVxuXG5leHBvcnQgY29uc3Qgc3R5bGVDb25maWcgPSB7XG4gIHVzYWdlUHJlZml4OiBzdHIgPT4gKFxuICAgIGBcbiR7Y2hhbGsuYm9sZChzdHIuc2xpY2UoMCwgNSkudG9VcHBlckNhc2UoKSl9XG4gICR7Y2hhbGsuaXRhbGljKHN0ci5zbGljZSg3KSl9YFxuICApLFxuICB1c2FnZUNvbW1hbmRQbGFjZWhvbGRlcjogc3RyID0+IGNoYWxrLmJsdWUoc3RyKSxcbiAgdXNhZ2VQb3NpdGlvbmFsczogc3RyID0+IGNoYWxrLm1hZ2VudGEoc3RyKSxcbiAgdXNhZ2VBcmdzUGxhY2Vob2xkZXI6IHN0ciA9PiBjaGFsay5tYWdlbnRhKHN0ciksXG4gIHVzYWdlT3B0aW9uc1BsYWNlaG9sZGVyOiBzdHIgPT4gY2hhbGsueWVsbG93KHN0ciksXG4gIGdyb3VwOiAoc3RyOiBzdHJpbmcpID0+IHtcbiAgICBjb25zdCBjbGVhbmVkID0gc3RyLmVuZHNXaXRoKFwiOlwiKSA/IHN0ci5zbGljZSgwLCAtMSkgOiBzdHJcbiAgICByZXR1cm4gY2hhbGsuYm9sZChjbGVhbmVkLnRvVXBwZXJDYXNlKCkpXG4gIH0sXG4gIGZsYWdzOiAoc3RyLCBfdHlwZSkgPT4ge1xuICAgIGNvbnN0IHN0eWxlID0gc3RyLnN0YXJ0c1dpdGgoXCItXCIpID8gY2hhbGsuZ3JlZW4gOiBjaGFsay5tYWdlbnRhXG4gICAgcmV0dXJuIHN0eWxlKHN0cilcbiAgfSxcbiAgaGludHM6IHN0ciA9PiBjaGFsay5ncmF5KHN0ciksXG4gIGdyb3VwRXJyb3I6IHN0ciA9PiBjaGFsay5yZWQuYm9sZChzdHIpLFxuICBmbGFnc0Vycm9yOiBzdHIgPT4gY2hhbGsucmVkLmJvbGQoc3RyKSxcbiAgZGVzY0Vycm9yOiBzdHIgPT4gY2hhbGsueWVsbG93LmJvbGQoc3RyKSxcbiAgaGludHNFcnJvcjogc3RyID0+IGNoYWxrLnJlZChzdHIpLFxuICBtZXNzYWdlczogc3RyID0+IGNoYWxrLnJlZC5ib2xkKHN0ciksIC8vIHRoZXNlIGFyZSBlcnJvciBtZXNzYWdlc1xufVxuXG4vLyBIZWxwZXIgZnVuY3Rpb25zXG5leHBvcnQgY29uc3QgZ2V0S2V5cyA9IChvYmopOiBzdHJpbmdbXSA9PiBPYmplY3Qua2V5cyhvYmogfHwge30pXG5leHBvcnQgY29uc3QgZmlsdGVyQnlLZXlzID0gKG9iajogYW55LCBrZXlzOiBzdHJpbmdbXSk6IGFueSA9PiB7XG4gIHJldHVybiBrZXlzLnJlZHVjZSgobWVtbywga2V5KSA9PiB7XG4gICAgaWYgKG9ialtrZXldKSB7XG4gICAgICBtZW1vW2tleV0gPSBvYmpba2V5XVxuICAgIH1cbiAgICByZXR1cm4gbWVtb1xuICB9LCB7fSlcbn1cblxuZXhwb3J0IHR5cGUgRmFsc2lmaWVkUGFyYW1zID0geyBba2V5OiBzdHJpbmddOiBmYWxzZSB9XG5cbi8qKlxuICogUmV0dXJucyB0aGUgcGFyYW1zIHRoYXQgbmVlZCB0byBiZSBvdmVycmlkZGVuIHNldCB0byBmYWxzZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZmFsc2lmeUNvbmZsaWN0aW5nUGFyYW1zKGFyZ3YsIHBhcmFtczogUGFyYW1ldGVyVmFsdWVzPGFueT4pOiBGYWxzaWZpZWRQYXJhbXMge1xuICByZXR1cm4gcmVkdWNlKGFyZ3YsIChhY2M6IHt9LCB2YWw6IGFueSwga2V5OiBzdHJpbmcpID0+IHtcbiAgICBjb25zdCBwYXJhbSA9IHBhcmFtc1trZXldXG4gICAgY29uc3Qgb3ZlcnJpZGVzID0gKHBhcmFtIHx8IHt9KS5vdmVycmlkZXMgfHwgW11cbiAgICAvLyBhcmd2IGFsd2F5cyBjb250YWlucyB0aGUgXCJfXCIga2V5IHdoaWNoIGlzIGlycmVsZXZhbnQgaGVyZVxuICAgIGlmIChrZXkgPT09IFwiX1wiIHx8ICFwYXJhbSB8fCAhdmFsIHx8ICEob3ZlcnJpZGVzLmxlbmd0aCA+IDApKSB7XG4gICAgICByZXR1cm4gYWNjXG4gICAgfVxuICAgIGNvbnN0IHdpdGhBbGlhc2VzID0gb3ZlcnJpZGVzLnJlZHVjZSgoXywga2V5VG9PdmVycmlkZTogc3RyaW5nKTogc3RyaW5nW10gPT4ge1xuICAgICAgaWYgKCFwYXJhbXNba2V5VG9PdmVycmlkZV0pIHtcbiAgICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoYENhbm5vdCBvdmVycmlkZSBub24tZXhpc3RpbmcgcGFyYW1ldGVyOiAke2tleVRvT3ZlcnJpZGV9YCwge1xuICAgICAgICAgIGtleVRvT3ZlcnJpZGUsXG4gICAgICAgICAgYXZhaWxhYmxlS2V5czogT2JqZWN0LmtleXMocGFyYW1zKSxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICAgIHJldHVybiBba2V5VG9PdmVycmlkZSwgLi4ucGFyYW1zW2tleVRvT3ZlcnJpZGVdLmFsaWFzXVxuICAgIH0sIFtdKVxuXG4gICAgd2l0aEFsaWFzZXMuZm9yRWFjaChrZXlUb092ZXJyaWRlID0+IGFjY1trZXlUb092ZXJyaWRlXSA9IGZhbHNlKVxuICAgIHJldHVybiBhY2NcbiAgfSwge30pXG59XG5cbi8vIFN5d2FjIHNwZWNpZmljIHRyYW5zZm9ybWVycyBhbmQgaGVscGVyc1xuZXhwb3J0IGZ1bmN0aW9uIGdldE9wdGlvblN5bm9wc2lzKGtleTogc3RyaW5nLCB7IGFsaWFzIH06IFBhcmFtZXRlcjxhbnk+KTogc3RyaW5nIHtcbiAgaWYgKGFsaWFzICYmIGFsaWFzLmxlbmd0aCA+IDEpIHtcbiAgICB0aHJvdyBuZXcgSW50ZXJuYWxFcnJvcihcIk9wdGlvbiBhbGlhc2VzIGNhbiBvbmx5IGJlIGEgc2luZ2xlIGNoYXJhY3RlclwiLCB7XG4gICAgICBvcHRpb25OYW1lOiBrZXksXG4gICAgICBhbGlhcyxcbiAgICB9KVxuICB9XG4gIHJldHVybiBhbGlhcyA/IGAtJHthbGlhc30sIC0tJHtrZXl9YCA6IGAtLSR7a2V5fWBcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEFyZ1N5bm9wc2lzKGtleTogc3RyaW5nLCBwYXJhbTogUGFyYW1ldGVyPGFueT4pIHtcbiAgcmV0dXJuIHBhcmFtLnJlcXVpcmVkID8gYDwke2tleX0+YCA6IGBbJHtrZXl9XWBcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHByZXBhcmVBcmdDb25maWcocGFyYW06IFBhcmFtZXRlcjxhbnk+KSB7XG4gIHJldHVybiB7XG4gICAgZGVzYzogcGFyYW0uaGVscCxcbiAgICBwYXJhbXM6IFtwcmVwYXJlT3B0aW9uQ29uZmlnKHBhcmFtKV0sXG4gIH1cbn1cblxuZXhwb3J0IGludGVyZmFjZSBTeXdhY09wdGlvbkNvbmZpZyB7XG4gIGRlc2M6IHN0cmluZyB8IHN0cmluZ1tdXG4gIHR5cGU6IHN0cmluZ1xuICBkZWZhdWx0VmFsdWU/OiBhbnlcbiAgY29lcmNlPzogRnVuY3Rpb25cbiAgY2hvaWNlcz86IGFueVtdXG4gIHJlcXVpcmVkPzogYm9vbGVhblxuICBoaW50cz86IHN0cmluZ1xuICBzdHJpY3Q6IHRydWVcbiAgbXVzdEV4aXN0OiB0cnVlIC8vIEZvciBwYXJhbWV0ZXJzIG9mIHBhdGggdHlwZVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcHJlcGFyZU9wdGlvbkNvbmZpZyhwYXJhbTogUGFyYW1ldGVyPGFueT4pOiBTeXdhY09wdGlvbkNvbmZpZyB7XG4gIGNvbnN0IHtcbiAgICBjb2VyY2UsXG4gICAgZGVmYXVsdFZhbHVlLFxuICAgIGhlbHA6IGRlc2MsXG4gICAgaGludHMsXG4gICAgcmVxdWlyZWQsXG4gICAgdHlwZSxcbiAgfSA9IHBhcmFtXG4gIGlmICghVkFMSURfUEFSQU1FVEVSX1RZUEVTLmluY2x1ZGVzKHR5cGUpKSB7XG4gICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoYEludmFsaWQgcGFyYW1ldGVyIHR5cGUgZm9yIGNsaTogJHt0eXBlfWAsIHtcbiAgICAgIHR5cGUsXG4gICAgICB2YWxpZFBhcmFtZXRlclR5cGVzOiBWQUxJRF9QQVJBTUVURVJfVFlQRVMsXG4gICAgfSlcbiAgfVxuICBsZXQgY29uZmlnOiBTeXdhY09wdGlvbkNvbmZpZyA9IHtcbiAgICBjb2VyY2UsXG4gICAgZGVmYXVsdFZhbHVlLFxuICAgIGRlc2MsXG4gICAgcmVxdWlyZWQsXG4gICAgdHlwZSxcbiAgICBoaW50cyxcbiAgICBzdHJpY3Q6IHRydWUsXG4gICAgbXVzdEV4aXN0OiB0cnVlLCAvLyBGb3IgcGFyYW1ldGVycyBvZiBwYXRoIHR5cGVcbiAgfVxuICBpZiAodHlwZSA9PT0gXCJjaG9pY2VcIikge1xuICAgIGNvbmZpZy50eXBlID0gXCJlbnVtXCJcbiAgICBjb25maWcuY2hvaWNlcyA9ICg8Q2hvaWNlc1BhcmFtZXRlcj5wYXJhbSkuY2hvaWNlc1xuICB9XG4gIHJldHVybiBjb25maWdcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZhaWxPbkludmFsaWRPcHRpb25zKGFyZ3YsIGN0eCkge1xuICBjb25zdCB2YWxpZE9wdGlvbnMgPSBmbGF0dGVuKFxuICAgIGN0eC5kZXRhaWxzLnR5cGVzXG4gICAgICAuZmlsdGVyKHQgPT4gdC5kYXRhdHlwZSAhPT0gXCJjb21tYW5kXCIpXG4gICAgICAubWFwKHQgPT4gdC5hbGlhc2VzKSxcbiAgKVxuICBjb25zdCByZWNlaXZlZE9wdGlvbnMgPSBPYmplY3Qua2V5cyhhcmd2KVxuICBjb25zdCBpbnZhbGlkID0gZGlmZmVyZW5jZShyZWNlaXZlZE9wdGlvbnMsIHZhbGlkT3B0aW9ucylcbiAgaWYgKGludmFsaWQubGVuZ3RoID4gMCkge1xuICAgIGN0eC5jbGlNZXNzYWdlKGBSZWNlaXZlZCBpbnZhbGlkIGZsYWcocyk6ICR7aW52YWxpZC5qb2luKFwiLCBcIil9YClcbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/base.d.ts b/garden-service/build/commands/base.d.ts
new file mode 100644
index 00000000000..b3565509889
--- /dev/null
+++ b/garden-service/build/commands/base.d.ts
@@ -0,0 +1,156 @@
+import { GardenError } from "../exceptions";
+import { TaskResults } from "../task-graph";
+import { LoggerType } from "../logger/logger";
+import { ProcessResults } from "../process";
+import { Garden } from "../garden";
+export declare class ValidationError extends Error {
+}
+export interface ParameterConstructor {
+ help: string;
+ required?: boolean;
+ alias?: string;
+ defaultValue?: T;
+ valueName?: string;
+ hints?: string;
+ overrides?: string[];
+}
+export declare abstract class Parameter {
+ abstract type: string;
+ _valueType: T;
+ defaultValue: T | undefined;
+ help: string;
+ required: boolean;
+ alias?: string;
+ hints?: string;
+ valueName: string;
+ overrides: string[];
+ constructor({ help, required, alias, defaultValue, valueName, overrides, hints }: ParameterConstructor);
+ coerce(input: T): T | undefined;
+ abstract validate(input: string): T;
+ autoComplete(): Promise;
+}
+export declare class StringParameter extends Parameter {
+ type: string;
+ validate(input: string): string;
+}
+export declare class StringOption extends Parameter {
+ type: string;
+ validate(input?: string): string | undefined;
+}
+export declare class StringsParameter extends Parameter {
+ type: string;
+ coerce(input: string[]): string[] | undefined;
+ validate(input: string): string[];
+}
+export declare class PathParameter extends Parameter {
+ type: string;
+ validate(input: string): string;
+}
+export declare class PathsParameter extends Parameter {
+ type: string;
+ validate(input: string): string[];
+}
+export declare class NumberParameter extends Parameter {
+ type: string;
+ validate(input: string): number;
+}
+export interface ChoicesConstructor extends ParameterConstructor {
+ choices: string[];
+}
+export declare class ChoicesParameter extends Parameter {
+ type: string;
+ choices: string[];
+ constructor(args: ChoicesConstructor);
+ validate(input: string): string;
+ autoComplete(): Promise;
+}
+export declare class BooleanParameter extends Parameter {
+ type: string;
+ validate(input: any): boolean;
+}
+export declare class EnvironmentOption extends StringParameter {
+ constructor({ help }?: {
+ help?: string | undefined;
+ });
+}
+export declare type Parameters = {
+ [key: string]: Parameter;
+};
+export declare type ParameterValues = {
+ [P in keyof T]: T[P]["_valueType"];
+};
+export interface CommandConstructor {
+ new (parent?: Command): Command;
+}
+export interface CommandResult {
+ result?: T;
+ restartRequired?: boolean;
+ errors?: GardenError[];
+}
+export interface CommandParams {
+ args: ParameterValues;
+ opts: ParameterValues;
+ garden: Garden;
+}
+export declare abstract class Command {
+ private parent?;
+ abstract name: string;
+ abstract help: string;
+ description?: string;
+ alias?: string;
+ loggerType?: LoggerType;
+ arguments?: T;
+ options?: U;
+ noProject: boolean;
+ subCommands: CommandConstructor[];
+ constructor(parent?: Command<{}, {}> | undefined);
+ getFullName(): any;
+ describe(): {
+ name: string;
+ fullName: any;
+ help: string;
+ description: string | undefined;
+ arguments: {
+ type: string;
+ _valueType: any;
+ defaultValue: any;
+ help: string;
+ required: boolean;
+ alias?: string | undefined;
+ hints?: string | undefined;
+ valueName: string;
+ overrides: string[];
+ name: string;
+ usageName: string;
+ }[] | undefined;
+ options: {
+ type: string;
+ _valueType: any;
+ defaultValue: any;
+ help: string;
+ required: boolean;
+ alias?: string | undefined;
+ hints?: string | undefined;
+ valueName: string;
+ overrides: string[];
+ name: string;
+ usageName: string;
+ }[] | undefined;
+ };
+ abstract action(params: CommandParams): Promise;
+}
+export declare function handleTaskResults(garden: Garden, taskType: string, results: ProcessResults): Promise>;
+export declare function describeParameters(args?: Parameters): {
+ type: string;
+ _valueType: any;
+ defaultValue: any;
+ help: string;
+ required: boolean;
+ alias?: string | undefined;
+ hints?: string | undefined;
+ valueName: string;
+ overrides: string[];
+ name: string;
+ usageName: string;
+}[] | undefined;
+//# sourceMappingURL=base.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/base.js b/garden-service/build/commands/base.js
new file mode 100644
index 00000000000..8115cef9c83
--- /dev/null
+++ b/garden-service/build/commands/base.js
@@ -0,0 +1,210 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const exceptions_1 = require("../exceptions");
+class ValidationError extends Error {
+}
+exports.ValidationError = ValidationError;
+class Parameter {
+ constructor({ help, required, alias, defaultValue, valueName, overrides, hints }) {
+ this.help = help;
+ this.required = required || false;
+ this.alias = alias;
+ this.hints = hints;
+ this.defaultValue = defaultValue;
+ this.valueName = valueName || "_valueType";
+ this.overrides = overrides || [];
+ }
+ coerce(input) {
+ return input;
+ }
+ autoComplete() {
+ return __awaiter(this, void 0, void 0, function* () {
+ return [];
+ });
+ }
+}
+exports.Parameter = Parameter;
+class StringParameter extends Parameter {
+ constructor() {
+ super(...arguments);
+ this.type = "string";
+ }
+ validate(input) {
+ return input;
+ }
+}
+exports.StringParameter = StringParameter;
+// Separating this from StringParameter for now because we can't set the output type based on the required flag
+// FIXME: Maybe use a Required type to enforce presence, rather that an option flag?
+class StringOption extends Parameter {
+ constructor() {
+ super(...arguments);
+ this.type = "string";
+ }
+ validate(input) {
+ return input;
+ }
+}
+exports.StringOption = StringOption;
+class StringsParameter extends Parameter {
+ constructor() {
+ super(...arguments);
+ this.type = "array:string";
+ }
+ // Sywac returns [undefined] if input is empty so we coerce that into undefined.
+ // This only applies to optional parameters since Sywac would throw if input is empty for a required parameter.
+ coerce(input) {
+ const filtered = input.filter(i => !!i);
+ if (filtered.length < 1) {
+ return undefined;
+ }
+ return filtered;
+ }
+ validate(input) {
+ return input.split(",");
+ }
+}
+exports.StringsParameter = StringsParameter;
+class PathParameter extends Parameter {
+ constructor() {
+ super(...arguments);
+ this.type = "path";
+ }
+ validate(input) {
+ return input;
+ }
+}
+exports.PathParameter = PathParameter;
+class PathsParameter extends Parameter {
+ constructor() {
+ super(...arguments);
+ this.type = "array:path";
+ }
+ validate(input) {
+ return input.split(",");
+ }
+}
+exports.PathsParameter = PathsParameter;
+class NumberParameter extends Parameter {
+ constructor() {
+ super(...arguments);
+ this.type = "number";
+ }
+ validate(input) {
+ try {
+ return parseInt(input, 10);
+ }
+ catch (_a) {
+ throw new ValidationError(`Could not parse "${input}" as number`);
+ }
+ }
+}
+exports.NumberParameter = NumberParameter;
+class ChoicesParameter extends Parameter {
+ constructor(args) {
+ super(args);
+ this.type = "choice";
+ this.choices = args.choices;
+ }
+ validate(input) {
+ if (this.choices.includes(input)) {
+ return input;
+ }
+ else {
+ throw new ValidationError(`"${input}" is not a valid argument`);
+ }
+ }
+ autoComplete() {
+ return __awaiter(this, void 0, void 0, function* () {
+ return this.choices;
+ });
+ }
+}
+exports.ChoicesParameter = ChoicesParameter;
+class BooleanParameter extends Parameter {
+ constructor() {
+ super(...arguments);
+ this.type = "boolean";
+ }
+ validate(input) {
+ return !!input;
+ }
+}
+exports.BooleanParameter = BooleanParameter;
+// TODO: maybe this should be a global option?
+class EnvironmentOption extends StringParameter {
+ constructor({ help = "The environment (and optionally namespace) to work against" } = {}) {
+ super({
+ help,
+ required: false,
+ alias: "e",
+ });
+ }
+}
+exports.EnvironmentOption = EnvironmentOption;
+class Command {
+ constructor(parent) {
+ this.parent = parent;
+ this.noProject = false;
+ this.subCommands = [];
+ }
+ getFullName() {
+ return !!this.parent ? `${this.parent.getFullName()} ${this.name}` : this.name;
+ }
+ describe() {
+ const { name, help, description } = this;
+ return {
+ name,
+ fullName: this.getFullName(),
+ help,
+ description,
+ arguments: describeParameters(this.arguments),
+ options: describeParameters(this.options),
+ };
+ }
+}
+exports.Command = Command;
+function handleTaskResults(garden, taskType, results) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const failed = Object.values(results.taskResults).filter(r => !!r.error).length;
+ if (failed) {
+ const error = new exceptions_1.RuntimeError(`${failed} ${taskType} task(s) failed!`, {
+ results,
+ });
+ return { errors: [error] };
+ }
+ garden.log.info("");
+ if (!results.restartRequired) {
+ garden.log.header({ emoji: "heavy_check_mark", command: `Done!` });
+ }
+ return {
+ result: results.taskResults,
+ restartRequired: results.restartRequired,
+ };
+ });
+}
+exports.handleTaskResults = handleTaskResults;
+function describeParameters(args) {
+ if (!args) {
+ return;
+ }
+ return Object.entries(args).map(([argName, arg]) => (Object.assign({ name: argName, usageName: arg.required ? `<${argName}>` : `[${argName}]` }, arg)));
+}
+exports.describeParameters = describeParameters;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,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Q0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFBO1FBQ25CLElBQUksQ0FBQyxPQUFPLENBQUMsZUFBZSxFQUFFO1lBQzVCLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsS0FBSyxFQUFFLGtCQUFrQixFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFBO1NBQ25FO1FBQ0QsT0FBTztZQUNMLE1BQU0sRUFBRSxPQUFPLENBQUMsV0FBVztZQUMzQixlQUFlLEVBQUUsT0FBTyxDQUFDLGVBQWU7U0FDekMsQ0FBQTtJQUNILENBQUM7Q0FBQTtBQXBCRCw4Q0FvQkM7QUFFRCxTQUFnQixrQkFBa0IsQ0FBQyxJQUFpQjtJQUNsRCxJQUFJLENBQUMsSUFBSSxFQUFFO1FBQUUsT0FBTTtLQUFFO0lBQ3JCLE9BQU8sTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLE9BQU8sRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLENBQUMsaUJBQ2xELElBQUksRUFBRSxPQUFPLEVBQ2IsU0FBUyxFQUFFLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUksT0FBTyxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksT0FBTyxHQUFHLElBQ3RELEdBQUcsRUFDTixDQUFDLENBQUE7QUFDTCxDQUFDO0FBUEQsZ0RBT0MiLCJmaWxlIjoiY29tbWFuZHMvYmFzZS5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQge1xuICBHYXJkZW5FcnJvcixcbiAgUnVudGltZUVycm9yLFxufSBmcm9tIFwiLi4vZXhjZXB0aW9uc1wiXG5pbXBvcnQgeyBUYXNrUmVzdWx0cyB9IGZyb20gXCIuLi90YXNrLWdyYXBoXCJcbmltcG9ydCB7IExvZ2dlclR5cGUgfSBmcm9tIFwiLi4vbG9nZ2VyL2xvZ2dlclwiXG5pbXBvcnQgeyBQcm9jZXNzUmVzdWx0cyB9IGZyb20gXCIuLi9wcm9jZXNzXCJcbmltcG9ydCB7IEdhcmRlbiB9IGZyb20gXCIuLi9nYXJkZW5cIlxuXG5leHBvcnQgY2xhc3MgVmFsaWRhdGlvbkVycm9yIGV4dGVuZHMgRXJyb3IgeyB9XG5cbmV4cG9ydCBpbnRlcmZhY2UgUGFyYW1ldGVyQ29uc3RydWN0b3I8VD4ge1xuICBoZWxwOiBzdHJpbmcsXG4gIHJlcXVpcmVkPzogYm9vbGVhbixcbiAgYWxpYXM/OiBzdHJpbmcsXG4gIGRlZmF1bHRWYWx1ZT86IFQsXG4gIHZhbHVlTmFtZT86IHN0cmluZyxcbiAgaGludHM/OiBzdHJpbmcsXG4gIG92ZXJyaWRlcz86IHN0cmluZ1tdLFxufVxuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgUGFyYW1ldGVyPFQ+IHtcbiAgYWJzdHJhY3QgdHlwZTogc3RyaW5nXG5cbiAgX3ZhbHVlVHlwZTogVFxuXG4gIGRlZmF1bHRWYWx1ZTogVCB8IHVuZGVmaW5lZFxuICBoZWxwOiBzdHJpbmdcbiAgcmVxdWlyZWQ6IGJvb2xlYW5cbiAgYWxpYXM/OiBzdHJpbmdcbiAgaGludHM/OiBzdHJpbmdcbiAgdmFsdWVOYW1lOiBzdHJpbmdcbiAgb3ZlcnJpZGVzOiBzdHJpbmdbXVxuXG4gIGNvbnN0cnVjdG9yKHsgaGVscCwgcmVxdWlyZWQsIGFsaWFzLCBkZWZhdWx0VmFsdWUsIHZhbHVlTmFtZSwgb3ZlcnJpZGVzLCBoaW50cyB9OiBQYXJhbWV0ZXJDb25zdHJ1Y3RvcjxUPikge1xuICAgIHRoaXMuaGVscCA9IGhlbHBcbiAgICB0aGlzLnJlcXVpcmVkID0gcmVxdWlyZWQgfHwgZmFsc2VcbiAgICB0aGlzLmFsaWFzID0gYWxpYXNcbiAgICB0aGlzLmhpbnRzID0gaGludHNcbiAgICB0aGlzLmRlZmF1bHRWYWx1ZSA9IGRlZmF1bHRWYWx1ZVxuICAgIHRoaXMudmFsdWVOYW1lID0gdmFsdWVOYW1lIHx8IFwiX3ZhbHVlVHlwZVwiXG4gICAgdGhpcy5vdmVycmlkZXMgPSBvdmVycmlkZXMgfHwgW11cbiAgfVxuXG4gIGNvZXJjZShpbnB1dDogVCk6IFQgfCB1bmRlZmluZWQge1xuICAgIHJldHVybiBpbnB1dFxuICB9XG5cbiAgYWJzdHJhY3QgdmFsaWRhdGUoaW5wdXQ6IHN0cmluZyk6IFRcblxuICBhc3luYyBhdXRvQ29tcGxldGUoKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICAgIHJldHVybiBbXVxuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBTdHJpbmdQYXJhbWV0ZXIgZXh0ZW5kcyBQYXJhbWV0ZXI8c3RyaW5nPiB7XG4gIHR5cGUgPSBcInN0cmluZ1wiXG5cbiAgdmFsaWRhdGUoaW5wdXQ6IHN0cmluZykge1xuICAgIHJldHVybiBpbnB1dFxuICB9XG59XG5cbi8vIFNlcGFyYXRpbmcgdGhpcyBmcm9tIFN0cmluZ1BhcmFtZXRlciBmb3Igbm93IGJlY2F1c2Ugd2UgY2FuJ3Qgc2V0IHRoZSBvdXRwdXQgdHlwZSBiYXNlZCBvbiB0aGUgcmVxdWlyZWQgZmxhZ1xuLy8gRklYTUU6IE1heWJlIHVzZSBhIFJlcXVpcmVkPFBhcmFtZXRlcj4gdHlwZSB0byBlbmZvcmNlIHByZXNlbmNlLCByYXRoZXIgdGhhdCBhbiBvcHRpb24gZmxhZz9cbmV4cG9ydCBjbGFzcyBTdHJpbmdPcHRpb24gZXh0ZW5kcyBQYXJhbWV0ZXI8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIHR5cGUgPSBcInN0cmluZ1wiXG5cbiAgdmFsaWRhdGUoaW5wdXQ/OiBzdHJpbmcpIHtcbiAgICByZXR1cm4gaW5wdXRcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgU3RyaW5nc1BhcmFtZXRlciBleHRlbmRzIFBhcmFtZXRlcjxzdHJpbmdbXSB8IHVuZGVmaW5lZD4ge1xuICB0eXBlID0gXCJhcnJheTpzdHJpbmdcIlxuXG4gIC8vIFN5d2FjIHJldHVybnMgW3VuZGVmaW5lZF0gaWYgaW5wdXQgaXMgZW1wdHkgc28gd2UgY29lcmNlIHRoYXQgaW50byB1bmRlZmluZWQuXG4gIC8vIFRoaXMgb25seSBhcHBsaWVzIHRvIG9wdGlvbmFsIHBhcmFtZXRlcnMgc2luY2UgU3l3YWMgd291bGQgdGhyb3cgaWYgaW5wdXQgaXMgZW1wdHkgZm9yIGEgcmVxdWlyZWQgcGFyYW1ldGVyLlxuICBjb2VyY2UoaW5wdXQ6IHN0cmluZ1tdKSB7XG4gICAgY29uc3QgZmlsdGVyZWQgPSBpbnB1dC5maWx0ZXIoaSA9PiAhIWkpXG4gICAgaWYgKGZpbHRlcmVkLmxlbmd0aCA8IDEpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWRcbiAgICB9XG4gICAgcmV0dXJuIGZpbHRlcmVkXG4gIH1cblxuICB2YWxpZGF0ZShpbnB1dDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGlucHV0LnNwbGl0KFwiLFwiKVxuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBQYXRoUGFyYW1ldGVyIGV4dGVuZHMgUGFyYW1ldGVyPHN0cmluZz4ge1xuICB0eXBlID0gXCJwYXRoXCJcblxuICB2YWxpZGF0ZShpbnB1dDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGlucHV0XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFBhdGhzUGFyYW1ldGVyIGV4dGVuZHMgUGFyYW1ldGVyPHN0cmluZ1tdPiB7XG4gIHR5cGUgPSBcImFycmF5OnBhdGhcIlxuXG4gIHZhbGlkYXRlKGlucHV0OiBzdHJpbmcpIHtcbiAgICByZXR1cm4gaW5wdXQuc3BsaXQoXCIsXCIpXG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIE51bWJlclBhcmFtZXRlciBleHRlbmRzIFBhcmFtZXRlcjxudW1iZXI+IHtcbiAgdHlwZSA9IFwibnVtYmVyXCJcblxuICB2YWxpZGF0ZShpbnB1dDogc3RyaW5nKSB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBwYXJzZUludChpbnB1dCwgMTApXG4gICAgfSBjYXRjaCB7XG4gICAgICB0aHJvdyBuZXcgVmFsaWRhdGlvbkVycm9yKGBDb3VsZCBub3QgcGFyc2UgXCIke2lucHV0fVwiIGFzIG51bWJlcmApXG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ2hvaWNlc0NvbnN0cnVjdG9yIGV4dGVuZHMgUGFyYW1ldGVyQ29uc3RydWN0b3I8c3RyaW5nPiB7XG4gIGNob2ljZXM6IHN0cmluZ1tdLFxufVxuXG5leHBvcnQgY2xhc3MgQ2hvaWNlc1BhcmFtZXRlciBleHRlbmRzIFBhcmFtZXRlcjxzdHJpbmc+IHtcbiAgdHlwZSA9IFwiY2hvaWNlXCJcbiAgY2hvaWNlczogc3RyaW5nW11cblxuICBjb25zdHJ1Y3RvcihhcmdzOiBDaG9pY2VzQ29uc3RydWN0b3IpIHtcbiAgICBzdXBlcihhcmdzKVxuXG4gICAgdGhpcy5jaG9pY2VzID0gYXJncy5jaG9pY2VzXG4gIH1cblxuICB2YWxpZGF0ZShpbnB1dDogc3RyaW5nKSB7XG4gICAgaWYgKHRoaXMuY2hvaWNlcy5pbmNsdWRlcyhpbnB1dCkpIHtcbiAgICAgIHJldHVybiBpbnB1dFxuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgVmFsaWRhdGlvbkVycm9yKGBcIiR7aW5wdXR9XCIgaXMgbm90IGEgdmFsaWQgYXJndW1lbnRgKVxuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGF1dG9Db21wbGV0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy5jaG9pY2VzXG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIEJvb2xlYW5QYXJhbWV0ZXIgZXh0ZW5kcyBQYXJhbWV0ZXI8Ym9vbGVhbj4ge1xuICB0eXBlID0gXCJib29sZWFuXCJcblxuICB2YWxpZGF0ZShpbnB1dDogYW55KSB7XG4gICAgcmV0dXJuICEhaW5wdXRcbiAgfVxufVxuXG4vLyBUT0RPOiBtYXliZSB0aGlzIHNob3VsZCBiZSBhIGdsb2JhbCBvcHRpb24/XG5leHBvcnQgY2xhc3MgRW52aXJvbm1lbnRPcHRpb24gZXh0ZW5kcyBTdHJpbmdQYXJhbWV0ZXIge1xuICBjb25zdHJ1Y3Rvcih7IGhlbHAgPSBcIlRoZSBlbnZpcm9ubWVudCAoYW5kIG9wdGlvbmFsbHkgbmFtZXNwYWNlKSB0byB3b3JrIGFnYWluc3RcIiB9ID0ge30pIHtcbiAgICBzdXBlcih7XG4gICAgICBoZWxwLFxuICAgICAgcmVxdWlyZWQ6IGZhbHNlLFxuICAgICAgYWxpYXM6IFwiZVwiLFxuICAgIH0pXG4gIH1cbn1cblxuZXhwb3J0IHR5cGUgUGFyYW1ldGVycyA9IHsgW2tleTogc3RyaW5nXTogUGFyYW1ldGVyPGFueT4gfVxuZXhwb3J0IHR5cGUgUGFyYW1ldGVyVmFsdWVzPFQgZXh0ZW5kcyBQYXJhbWV0ZXJzPiA9IHsgW1AgaW4ga2V5b2YgVF06IFRbUF1bXCJfdmFsdWVUeXBlXCJdIH1cblxuZXhwb3J0IGludGVyZmFjZSBDb21tYW5kQ29uc3RydWN0b3Ige1xuICBuZXcocGFyZW50PzogQ29tbWFuZCk6IENvbW1hbmRcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb21tYW5kUmVzdWx0PFQgPSBhbnk+IHtcbiAgcmVzdWx0PzogVFxuICByZXN0YXJ0UmVxdWlyZWQ/OiBib29sZWFuXG4gIGVycm9ycz86IEdhcmRlbkVycm9yW11cbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb21tYW5kUGFyYW1zPFQgZXh0ZW5kcyBQYXJhbWV0ZXJzID0ge30sIFUgZXh0ZW5kcyBQYXJhbWV0ZXJzID0ge30+IHtcbiAgYXJnczogUGFyYW1ldGVyVmFsdWVzPFQ+XG4gIG9wdHM6IFBhcmFtZXRlclZhbHVlczxVPlxuICBnYXJkZW46IEdhcmRlblxufVxuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgQ29tbWFuZDxUIGV4dGVuZHMgUGFyYW1ldGVycyA9IHt9LCBVIGV4dGVuZHMgUGFyYW1ldGVycyA9IHt9PiB7XG4gIGFic3RyYWN0IG5hbWU6IHN0cmluZ1xuICBhYnN0cmFjdCBoZWxwOiBzdHJpbmdcblxuICBkZXNjcmlwdGlvbj86IHN0cmluZ1xuXG4gIGFsaWFzPzogc3RyaW5nXG4gIGxvZ2dlclR5cGU/OiBMb2dnZXJUeXBlXG5cbiAgYXJndW1lbnRzPzogVFxuICBvcHRpb25zPzogVVxuXG4gIG5vUHJvamVjdDogYm9vbGVhbiA9IGZhbHNlXG4gIHN1YkNvbW1hbmRzOiBDb21tYW5kQ29uc3RydWN0b3JbXSA9IFtdXG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBwYXJlbnQ/OiBDb21tYW5kKSB7IH1cblxuICBnZXRGdWxsTmFtZSgpIHtcbiAgICByZXR1cm4gISF0aGlzLnBhcmVudCA/IGAke3RoaXMucGFyZW50LmdldEZ1bGxOYW1lKCl9ICR7dGhpcy5uYW1lfWAgOiB0aGlzLm5hbWVcbiAgfVxuXG4gIGRlc2NyaWJlKCkge1xuICAgIGNvbnN0IHsgbmFtZSwgaGVscCwgZGVzY3JpcHRpb24gfSA9IHRoaXNcblxuICAgIHJldHVybiB7XG4gICAgICBuYW1lLFxuICAgICAgZnVsbE5hbWU6IHRoaXMuZ2V0RnVsbE5hbWUoKSxcbiAgICAgIGhlbHAsXG4gICAgICBkZXNjcmlwdGlvbixcbiAgICAgIGFyZ3VtZW50czogZGVzY3JpYmVQYXJhbWV0ZXJzKHRoaXMuYXJndW1lbnRzKSxcbiAgICAgIG9wdGlvbnM6IGRlc2NyaWJlUGFyYW1ldGVycyh0aGlzLm9wdGlvbnMpLFxuICAgIH1cbiAgfVxuXG4gIC8vIE5vdGU6IER1ZSB0byBhIGN1cnJlbnQgVFMgbGltaXRhdGlvbiAoYXBwYXJlbnRseSBjb3ZlcmVkIGJ5IGh0dHBzOi8vZ2l0aHViLmNvbS9NaWNyb3NvZnQvVHlwZVNjcmlwdC9pc3N1ZXMvNzAxMSksXG4gIC8vIHN1YmNsYXNzIGltcGxlbWVudGF0aW9ucyBuZWVkIHRvIGV4cGxpY2l0bHkgc2V0IHRoZSB0eXBlcyBpbiB0aGUgaW1wbGVtZW50ZWQgZnVuY3Rpb24gc2lnbmF0dXJlLiBTbyBmb3Igbm93IHdlXG4gIC8vIGNhbid0IGVuZm9yY2UgdGhlIHR5cGVzIG9mIGBhcmdzYCBhbmQgYG9wdHNgIGF1dG9tYXRpY2FsbHkgYXQgdGhlIGFic3RyYWN0IGNsYXNzIGxldmVsIGFuZCBoYXZlIHRvIHNwZWNpZnlcbiAgLy8gdGhlIHR5cGVzIGV4cGxpY2l0bHkgb24gdGhlIHN1YmNsYXNzZWQgbWV0aG9kcy5cbiAgYWJzdHJhY3QgYXN5bmMgYWN0aW9uKHBhcmFtczogQ29tbWFuZFBhcmFtczxULCBVPik6IFByb21pc2U8Q29tbWFuZFJlc3VsdD5cbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVRhc2tSZXN1bHRzKFxuICBnYXJkZW46IEdhcmRlbiwgdGFza1R5cGU6IHN0cmluZywgcmVzdWx0czogUHJvY2Vzc1Jlc3VsdHMsXG4pOiBQcm9taXNlPENvbW1hbmRSZXN1bHQ8VGFza1Jlc3VsdHM+PiB7XG4gIGNvbnN0IGZhaWxlZCA9IE9iamVjdC52YWx1ZXMocmVzdWx0cy50YXNrUmVzdWx0cykuZmlsdGVyKHIgPT4gISFyLmVycm9yKS5sZW5ndGhcblxuICBpZiAoZmFpbGVkKSB7XG4gICAgY29uc3QgZXJyb3IgPSBuZXcgUnVudGltZUVycm9yKGAke2ZhaWxlZH0gJHt0YXNrVHlwZX0gdGFzayhzKSBmYWlsZWQhYCwge1xuICAgICAgcmVzdWx0cyxcbiAgICB9KVxuICAgIHJldHVybiB7IGVycm9yczogW2Vycm9yXSB9XG4gIH1cblxuICBnYXJkZW4ubG9nLmluZm8oXCJcIilcbiAgaWYgKCFyZXN1bHRzLnJlc3RhcnRSZXF1aXJlZCkge1xuICAgIGdhcmRlbi5sb2cuaGVhZGVyKHsgZW1vamk6IFwiaGVhdnlfY2hlY2tfbWFya1wiLCBjb21tYW5kOiBgRG9uZSFgIH0pXG4gIH1cbiAgcmV0dXJuIHtcbiAgICByZXN1bHQ6IHJlc3VsdHMudGFza1Jlc3VsdHMsXG4gICAgcmVzdGFydFJlcXVpcmVkOiByZXN1bHRzLnJlc3RhcnRSZXF1aXJlZCxcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZGVzY3JpYmVQYXJhbWV0ZXJzKGFyZ3M/OiBQYXJhbWV0ZXJzKSB7XG4gIGlmICghYXJncykgeyByZXR1cm4gfVxuICByZXR1cm4gT2JqZWN0LmVudHJpZXMoYXJncykubWFwKChbYXJnTmFtZSwgYXJnXSkgPT4gKHtcbiAgICBuYW1lOiBhcmdOYW1lLFxuICAgIHVzYWdlTmFtZTogYXJnLnJlcXVpcmVkID8gYDwke2FyZ05hbWV9PmAgOiBgWyR7YXJnTmFtZX1dYCxcbiAgICAuLi5hcmcsXG4gIH0pKVxufVxuIl19
diff --git a/garden-service/build/commands/build.d.ts b/garden-service/build/commands/build.d.ts
new file mode 100644
index 00000000000..812edf1cb5a
--- /dev/null
+++ b/garden-service/build/commands/build.d.ts
@@ -0,0 +1,26 @@
+import { BooleanParameter, Command, CommandResult, CommandParams, StringsParameter } from "./base";
+import { TaskResults } from "../task-graph";
+declare const buildArguments: {
+ module: StringsParameter;
+};
+declare const buildOptions: {
+ force: BooleanParameter;
+ watch: BooleanParameter;
+};
+declare type BuildArguments = typeof buildArguments;
+declare type BuildOptions = typeof buildOptions;
+export declare class BuildCommand extends Command {
+ name: string;
+ help: string;
+ description: string;
+ arguments: {
+ module: StringsParameter;
+ };
+ options: {
+ force: BooleanParameter;
+ watch: BooleanParameter;
+ };
+ action({ args, opts, garden }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=build.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/build.js b/garden-service/build/commands/build.js
new file mode 100644
index 00000000000..f541c92c617
--- /dev/null
+++ b/garden-service/build/commands/build.js
@@ -0,0 +1,75 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const base_1 = require("./base");
+const build_1 = require("../tasks/build");
+const dedent = require("dedent");
+const process_1 = require("../process");
+const watch_1 = require("../watch");
+const buildArguments = {
+ module: new base_1.StringsParameter({
+ help: "Specify module(s) to build. Use comma separator to specify multiple modules.",
+ }),
+};
+const buildOptions = {
+ force: new base_1.BooleanParameter({ help: "Force rebuild of module(s)." }),
+ watch: new base_1.BooleanParameter({ help: "Watch for changes in module(s) and auto-build.", alias: "w" }),
+};
+class BuildCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "build";
+ this.help = "Build your modules.";
+ this.description = dedent `
+ Builds all or specified modules, taking into account build dependency order.
+ Optionally stays running and automatically builds modules if their source (or their dependencies' sources) change.
+
+ Examples:
+
+ garden build # build all modules in the project
+ garden build my-module # only build my-module
+ garden build --force # force rebuild of modules
+ garden build --watch # watch for changes to code
+ `;
+ this.arguments = buildArguments;
+ this.options = buildOptions;
+ }
+ action({ args, opts, garden }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ yield garden.clearBuilds();
+ const autoReloadDependants = yield watch_1.computeAutoReloadDependants(garden);
+ const modules = yield garden.getModules(args.module);
+ const moduleNames = modules.map(m => m.name);
+ garden.log.header({ emoji: "hammer", command: "Build" });
+ const results = yield process_1.processModules({
+ garden,
+ modules,
+ watch: opts.watch,
+ handler: (module) => __awaiter(this, void 0, void 0, function* () { return [new build_1.BuildTask({ garden, module, force: opts.force })]; }),
+ changeHandler: (module) => __awaiter(this, void 0, void 0, function* () {
+ return (yield watch_1.withDependants(garden, [module], autoReloadDependants))
+ .filter(m => moduleNames.includes(m.name))
+ .map(m => new build_1.BuildTask({ garden, module: m, force: true }));
+ }),
+ });
+ return base_1.handleTaskResults(garden, "build", results);
+ });
+ }
+}
+exports.BuildCommand = BuildCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2J1aWxkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7QUFFSCxpQ0FPZTtBQUNmLDBDQUEwQztBQUUxQyxpQ0FBaUM7QUFDakMsd0NBQTJDO0FBQzNDLG9DQUFzRTtBQUd0RSxNQUFNLGNBQWMsR0FBRztJQUNyQixNQUFNLEVBQUUsSUFBSSx1QkFBZ0IsQ0FBQztRQUMzQixJQUFJLEVBQUUsOEVBQThFO0tBQ3JGLENBQUM7Q0FDSCxDQUFBO0FBRUQsTUFBTSxZQUFZLEdBQUc7SUFDbkIsS0FBSyxFQUFFLElBQUksdUJBQWdCLENBQUMsRUFBRSxJQUFJLEVBQUUsNkJBQTZCLEVBQUUsQ0FBQztJQUNwRSxLQUFLLEVBQUUsSUFBSSx1QkFBZ0IsQ0FBQyxFQUFFLElBQUksRUFBRSxnREFBZ0QsRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLENBQUM7Q0FDcEcsQ0FBQTtBQUtELE1BQWEsWUFBYSxTQUFRLGNBQXFDO0lBQXZFOztRQUNFLFNBQUksR0FBRyxPQUFPLENBQUE7UUFDZCxTQUFJLEdBQUcscUJBQXFCLENBQUE7UUFFNUIsZ0JBQVcsR0FBRyxNQUFNLENBQUE7Ozs7Ozs7Ozs7R0FVbkIsQ0FBQTtRQUVELGNBQVMsR0FBRyxjQUFjLENBQUE7UUFDMUIsWUFBTyxHQUFHLFlBQVksQ0FBQTtJQTRCeEIsQ0FBQztJQTFCTyxNQUFNLENBQ1YsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBK0M7O1lBR25FLE1BQU0sTUFBTSxDQUFDLFdBQVcsRUFBRSxDQUFBO1lBRTFCLE1BQU0sb0JBQW9CLEdBQUcsTUFBTSxtQ0FBMkIsQ0FBQyxNQUFNLENBQUMsQ0FBQTtZQUN0RSxNQUFNLE9BQU8sR0FBRyxNQUFNLE1BQU0sQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFBO1lBQ3BELE1BQU0sV0FBVyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUE7WUFFNUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFBO1lBRXhELE1BQU0sT0FBTyxHQUFHLE1BQU0sd0JBQWMsQ0FBQztnQkFDbkMsTUFBTTtnQkFDTixPQUFPO2dCQUNQLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSztnQkFDakIsT0FBTyxFQUFFLENBQU8sTUFBTSxFQUFFLEVBQUUsZ0RBQUMsT0FBQSxDQUFDLElBQUksaUJBQVMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUEsR0FBQTtnQkFDakYsYUFBYSxFQUFFLENBQU8sTUFBYyxFQUFFLEVBQUU7b0JBQ3RDLE9BQU8sQ0FBQyxNQUFNLHNCQUFjLENBQUMsTUFBTSxFQUFFLENBQUMsTUFBTSxDQUFDLEVBQUUsb0JBQW9CLENBQUMsQ0FBQzt5QkFDbEUsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7eUJBQ3pDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLElBQUksaUJBQVMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsQ0FBQyxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUE7Z0JBQ2hFLENBQUMsQ0FBQTthQUNGLENBQUMsQ0FBQTtZQUVGLE9BQU8sd0JBQWlCLENBQUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQTtRQUNwRCxDQUFDO0tBQUE7Q0FDRjtBQTdDRCxvQ0E2Q0MiLCJmaWxlIjoiY29tbWFuZHMvYnVpbGQuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChDKSAyMDE4IEdhcmRlbiBUZWNobm9sb2dpZXMsIEluYy4gPGluZm9AZ2FyZGVuLmlvPlxuICpcbiAqIFRoaXMgU291cmNlIENvZGUgRm9ybSBpcyBzdWJqZWN0IHRvIHRoZSB0ZXJtcyBvZiB0aGUgTW96aWxsYSBQdWJsaWNcbiAqIExpY2Vuc2UsIHYuIDIuMC4gSWYgYSBjb3B5IG9mIHRoZSBNUEwgd2FzIG5vdCBkaXN0cmlidXRlZCB3aXRoIHRoaXNcbiAqIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uXG4gKi9cblxuaW1wb3J0IHtcbiAgQm9vbGVhblBhcmFtZXRlcixcbiAgQ29tbWFuZCxcbiAgQ29tbWFuZFJlc3VsdCxcbiAgQ29tbWFuZFBhcmFtcyxcbiAgaGFuZGxlVGFza1Jlc3VsdHMsXG4gIFN0cmluZ3NQYXJhbWV0ZXIsXG59IGZyb20gXCIuL2Jhc2VcIlxuaW1wb3J0IHsgQnVpbGRUYXNrIH0gZnJvbSBcIi4uL3Rhc2tzL2J1aWxkXCJcbmltcG9ydCB7IFRhc2tSZXN1bHRzIH0gZnJvbSBcIi4uL3Rhc2stZ3JhcGhcIlxuaW1wb3J0IGRlZGVudCA9IHJlcXVpcmUoXCJkZWRlbnRcIilcbmltcG9ydCB7IHByb2Nlc3NNb2R1bGVzIH0gZnJvbSBcIi4uL3Byb2Nlc3NcIlxuaW1wb3J0IHsgY29tcHV0ZUF1dG9SZWxvYWREZXBlbmRhbnRzLCB3aXRoRGVwZW5kYW50cyB9IGZyb20gXCIuLi93YXRjaFwiXG5pbXBvcnQgeyBNb2R1bGUgfSBmcm9tIFwiLi4vdHlwZXMvbW9kdWxlXCJcblxuY29uc3QgYnVpbGRBcmd1bWVudHMgPSB7XG4gIG1vZHVsZTogbmV3IFN0cmluZ3NQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiU3BlY2lmeSBtb2R1bGUocykgdG8gYnVpbGQuIFVzZSBjb21tYSBzZXBhcmF0b3IgdG8gc3BlY2lmeSBtdWx0aXBsZSBtb2R1bGVzLlwiLFxuICB9KSxcbn1cblxuY29uc3QgYnVpbGRPcHRpb25zID0ge1xuICBmb3JjZTogbmV3IEJvb2xlYW5QYXJhbWV0ZXIoeyBoZWxwOiBcIkZvcmNlIHJlYnVpbGQgb2YgbW9kdWxlKHMpLlwiIH0pLFxuICB3YXRjaDogbmV3IEJvb2xlYW5QYXJhbWV0ZXIoeyBoZWxwOiBcIldhdGNoIGZvciBjaGFuZ2VzIGluIG1vZHVsZShzKSBhbmQgYXV0by1idWlsZC5cIiwgYWxpYXM6IFwid1wiIH0pLFxufVxuXG50eXBlIEJ1aWxkQXJndW1lbnRzID0gdHlwZW9mIGJ1aWxkQXJndW1lbnRzXG50eXBlIEJ1aWxkT3B0aW9ucyA9IHR5cGVvZiBidWlsZE9wdGlvbnNcblxuZXhwb3J0IGNsYXNzIEJ1aWxkQ29tbWFuZCBleHRlbmRzIENvbW1hbmQ8QnVpbGRBcmd1bWVudHMsIEJ1aWxkT3B0aW9ucz4ge1xuICBuYW1lID0gXCJidWlsZFwiXG4gIGhlbHAgPSBcIkJ1aWxkIHlvdXIgbW9kdWxlcy5cIlxuXG4gIGRlc2NyaXB0aW9uID0gZGVkZW50YFxuICAgIEJ1aWxkcyBhbGwgb3Igc3BlY2lmaWVkIG1vZHVsZXMsIHRha2luZyBpbnRvIGFjY291bnQgYnVpbGQgZGVwZW5kZW5jeSBvcmRlci5cbiAgICBPcHRpb25hbGx5IHN0YXlzIHJ1bm5pbmcgYW5kIGF1dG9tYXRpY2FsbHkgYnVpbGRzIG1vZHVsZXMgaWYgdGhlaXIgc291cmNlIChvciB0aGVpciBkZXBlbmRlbmNpZXMnIHNvdXJjZXMpIGNoYW5nZS5cblxuICAgIEV4YW1wbGVzOlxuXG4gICAgICAgIGdhcmRlbiBidWlsZCAgICAgICAgICAgICMgYnVpbGQgYWxsIG1vZHVsZXMgaW4gdGhlIHByb2plY3RcbiAgICAgICAgZ2FyZGVuIGJ1aWxkIG15LW1vZHVsZSAgIyBvbmx5IGJ1aWxkIG15LW1vZHVsZVxuICAgICAgICBnYXJkZW4gYnVpbGQgLS1mb3JjZSAgICAjIGZvcmNlIHJlYnVpbGQgb2YgbW9kdWxlc1xuICAgICAgICBnYXJkZW4gYnVpbGQgLS13YXRjaCAgICAjIHdhdGNoIGZvciBjaGFuZ2VzIHRvIGNvZGVcbiAgYFxuXG4gIGFyZ3VtZW50cyA9IGJ1aWxkQXJndW1lbnRzXG4gIG9wdGlvbnMgPSBidWlsZE9wdGlvbnNcblxuICBhc3luYyBhY3Rpb24oXG4gICAgeyBhcmdzLCBvcHRzLCBnYXJkZW4gfTogQ29tbWFuZFBhcmFtczxCdWlsZEFyZ3VtZW50cywgQnVpbGRPcHRpb25zPixcbiAgKTogUHJvbWlzZTxDb21tYW5kUmVzdWx0PFRhc2tSZXN1bHRzPj4ge1xuXG4gICAgYXdhaXQgZ2FyZGVuLmNsZWFyQnVpbGRzKClcblxuICAgIGNvbnN0IGF1dG9SZWxvYWREZXBlbmRhbnRzID0gYXdhaXQgY29tcHV0ZUF1dG9SZWxvYWREZXBlbmRhbnRzKGdhcmRlbilcbiAgICBjb25zdCBtb2R1bGVzID0gYXdhaXQgZ2FyZGVuLmdldE1vZHVsZXMoYXJncy5tb2R1bGUpXG4gICAgY29uc3QgbW9kdWxlTmFtZXMgPSBtb2R1bGVzLm1hcChtID0+IG0ubmFtZSlcblxuICAgIGdhcmRlbi5sb2cuaGVhZGVyKHsgZW1vamk6IFwiaGFtbWVyXCIsIGNvbW1hbmQ6IFwiQnVpbGRcIiB9KVxuXG4gICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IHByb2Nlc3NNb2R1bGVzKHtcbiAgICAgIGdhcmRlbixcbiAgICAgIG1vZHVsZXMsXG4gICAgICB3YXRjaDogb3B0cy53YXRjaCxcbiAgICAgIGhhbmRsZXI6IGFzeW5jIChtb2R1bGUpID0+IFtuZXcgQnVpbGRUYXNrKHsgZ2FyZGVuLCBtb2R1bGUsIGZvcmNlOiBvcHRzLmZvcmNlIH0pXSxcbiAgICAgIGNoYW5nZUhhbmRsZXI6IGFzeW5jIChtb2R1bGU6IE1vZHVsZSkgPT4ge1xuICAgICAgICByZXR1cm4gKGF3YWl0IHdpdGhEZXBlbmRhbnRzKGdhcmRlbiwgW21vZHVsZV0sIGF1dG9SZWxvYWREZXBlbmRhbnRzKSlcbiAgICAgICAgICAuZmlsdGVyKG0gPT4gbW9kdWxlTmFtZXMuaW5jbHVkZXMobS5uYW1lKSlcbiAgICAgICAgICAubWFwKG0gPT4gbmV3IEJ1aWxkVGFzayh7IGdhcmRlbiwgbW9kdWxlOiBtLCBmb3JjZTogdHJ1ZSB9KSlcbiAgICAgIH0sXG4gICAgfSlcblxuICAgIHJldHVybiBoYW5kbGVUYXNrUmVzdWx0cyhnYXJkZW4sIFwiYnVpbGRcIiwgcmVzdWx0cylcbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/call.d.ts b/garden-service/build/commands/call.d.ts
new file mode 100644
index 00000000000..9f8af097c49
--- /dev/null
+++ b/garden-service/build/commands/call.d.ts
@@ -0,0 +1,16 @@
+import { Command, CommandResult, CommandParams, StringParameter } from "./base";
+declare const callArgs: {
+ serviceAndPath: StringParameter;
+};
+declare type Args = typeof callArgs;
+export declare class CallCommand extends Command {
+ name: string;
+ help: string;
+ description: string;
+ arguments: {
+ serviceAndPath: StringParameter;
+ };
+ action({ garden, args }: CommandParams): Promise;
+}
+export {};
+//# sourceMappingURL=call.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/call.js b/garden-service/build/commands/call.js
new file mode 100644
index 00000000000..dda9ed23f70
--- /dev/null
+++ b/garden-service/build/commands/call.js
@@ -0,0 +1,155 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const url_1 = require("url");
+const axios_1 = require("axios");
+const chalk_1 = require("chalk");
+const util_1 = require("util");
+const base_1 = require("./base");
+const util_2 = require("../util/util");
+const exceptions_1 = require("../exceptions");
+const lodash_1 = require("lodash");
+const service_1 = require("../types/service");
+const dedent = require("dedent");
+const callArgs = {
+ serviceAndPath: new base_1.StringParameter({
+ help: "The name of the service(s) to call followed by the ingress path (e.g. my-container/somepath).",
+ required: true,
+ }),
+};
+class CallCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "call";
+ this.help = "Call a service ingress endpoint.";
+ this.description = dedent `
+ This command resolves the deployed ingress endpoint for the given service and path, calls the given endpoint and
+ outputs the result.
+
+ Examples:
+
+ garden call my-container
+ garden call my-container/some-path
+
+ Note: Currently only supports simple GET requests for HTTP/HTTPS ingresses.
+ `;
+ this.arguments = callArgs;
+ }
+ action({ garden, args }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ let [serviceName, path] = util_2.splitFirst(args.serviceAndPath, "/");
+ // TODO: better error when service doesn't exist
+ const service = yield garden.getService(serviceName);
+ const status = yield garden.actions.getServiceStatus({ service });
+ if (status.state !== "ready") {
+ throw new exceptions_1.RuntimeError(`Service ${service.name} is not running`, {
+ serviceName: service.name,
+ status,
+ });
+ }
+ if (!status.ingresses) {
+ throw new exceptions_1.ParameterError(`Service ${service.name} has no active ingresses`, {
+ serviceName: service.name,
+ serviceStatus: status,
+ });
+ }
+ // find the correct endpoint to call
+ let matchedIngress = null;
+ let matchedPath;
+ // we can't easily support raw TCP or UDP in a command like this
+ const ingresses = status.ingresses.filter(e => e.protocol === "http" || e.protocol === "https");
+ if (!path) {
+ // if no path is specified and there's a root endpoint (path === "/") we use that
+ const rootIngress = lodash_1.find(ingresses, e => e.path === "/");
+ if (rootIngress) {
+ matchedIngress = rootIngress;
+ matchedPath = "/";
+ }
+ else {
+ // if there's no root endpoint, pick the first endpoint
+ matchedIngress = ingresses[0];
+ matchedPath = ingresses[0].path;
+ }
+ path = matchedPath;
+ }
+ else {
+ path = "/" + path;
+ for (const ingress of status.ingresses) {
+ if (ingress.path) {
+ if (path.startsWith(ingress.path) && (!matchedPath || ingress.path.length > matchedPath.length)) {
+ matchedIngress = ingress;
+ matchedPath = ingress.path;
+ }
+ }
+ else if (!matchedPath) {
+ matchedIngress = ingress;
+ }
+ }
+ }
+ if (!matchedIngress) {
+ throw new exceptions_1.ParameterError(`Service ${service.name} does not have an HTTP/HTTPS ingress at ${path}`, {
+ serviceName: service.name,
+ path,
+ availableIngresses: status.ingresses,
+ });
+ }
+ const url = url_1.resolve(service_1.getIngressUrl(matchedIngress), path || matchedPath);
+ // TODO: support POST requests with request body
+ const method = "get";
+ const entry = garden.log.info({
+ msg: chalk_1.default.cyan(`Sending ${matchedIngress.protocol.toUpperCase()} GET request to `) + url + "\n",
+ status: "active",
+ });
+ // this is to accept self-signed certs
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
+ const req = axios_1.default({
+ method,
+ url,
+ headers: {
+ host: matchedIngress.hostname,
+ },
+ });
+ // TODO: add verbose and debug logging (request/response headers etc.)
+ let res;
+ try {
+ res = yield req;
+ entry.setSuccess();
+ garden.log.info(chalk_1.default.green(`${res.status} ${res.statusText}\n`));
+ }
+ catch (err) {
+ res = err.response;
+ entry.setError();
+ const error = res ? `${res.status} ${res.statusText}` : err.message;
+ garden.log.info(chalk_1.default.red(error + "\n"));
+ return {};
+ }
+ const resStr = util_1.isObject(res.data) ? JSON.stringify(res.data, null, 2) : res.data;
+ res.data && garden.log.info(chalk_1.default.white(resStr) + "\n");
+ return {
+ result: {
+ serviceName,
+ path,
+ url,
+ response: lodash_1.pick(res, ["status", "statusText", "headers", "data"]),
+ },
+ };
+ });
+ }
+}
+exports.CallCommand = CallCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,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Q0FBQyxHQUFHLElBQUksQ0FBQyxDQUFBO1lBRXZELE9BQU87Z0JBQ0wsTUFBTSxFQUFFO29CQUNOLFdBQVc7b0JBQ1gsSUFBSTtvQkFDSixHQUFHO29CQUNILFFBQVEsRUFBRSxhQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsUUFBUSxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7aUJBQ2pFO2FBQ0YsQ0FBQTtRQUNILENBQUM7S0FBQTtDQUNGO0FBcElELGtDQW9JQyIsImZpbGUiOiJjb21tYW5kcy9jYWxsLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoQykgMjAxOCBHYXJkZW4gVGVjaG5vbG9naWVzLCBJbmMuIDxpbmZvQGdhcmRlbi5pbz5cbiAqXG4gKiBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljXG4gKiBMaWNlbnNlLCB2LiAyLjAuIElmIGEgY29weSBvZiB0aGUgTVBMIHdhcyBub3QgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzXG4gKiBmaWxlLCBZb3UgY2FuIG9idGFpbiBvbmUgYXQgaHR0cDovL21vemlsbGEub3JnL01QTC8yLjAvLlxuICovXG5cbmltcG9ydCB7IHJlc29sdmUgfSBmcm9tIFwidXJsXCJcbmltcG9ydCBBeGlvcyBmcm9tIFwiYXhpb3NcIlxuaW1wb3J0IGNoYWxrIGZyb20gXCJjaGFsa1wiXG5pbXBvcnQgeyBpc09iamVjdCB9IGZyb20gXCJ1dGlsXCJcbmltcG9ydCB7XG4gIENvbW1hbmQsXG4gIENvbW1hbmRSZXN1bHQsXG4gIENvbW1hbmRQYXJhbXMsXG4gIFN0cmluZ1BhcmFtZXRlcixcbn0gZnJvbSBcIi4vYmFzZVwiXG5pbXBvcnQgeyBzcGxpdEZpcnN0IH0gZnJvbSBcIi4uL3V0aWwvdXRpbFwiXG5pbXBvcnQgeyBQYXJhbWV0ZXJFcnJvciwgUnVudGltZUVycm9yIH0gZnJvbSBcIi4uL2V4Y2VwdGlvbnNcIlxuaW1wb3J0IHsgcGljaywgZmluZCB9IGZyb20gXCJsb2Rhc2hcIlxuaW1wb3J0IHsgU2VydmljZUluZ3Jlc3MsIGdldEluZ3Jlc3NVcmwgfSBmcm9tIFwiLi4vdHlwZXMvc2VydmljZVwiXG5pbXBvcnQgZGVkZW50ID0gcmVxdWlyZShcImRlZGVudFwiKVxuXG5jb25zdCBjYWxsQXJncyA9IHtcbiAgc2VydmljZUFuZFBhdGg6IG5ldyBTdHJpbmdQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiVGhlIG5hbWUgb2YgdGhlIHNlcnZpY2UocykgdG8gY2FsbCBmb2xsb3dlZCBieSB0aGUgaW5ncmVzcyBwYXRoIChlLmcuIG15LWNvbnRhaW5lci9zb21lcGF0aCkuXCIsXG4gICAgcmVxdWlyZWQ6IHRydWUsXG4gIH0pLFxufVxuXG50eXBlIEFyZ3MgPSB0eXBlb2YgY2FsbEFyZ3NcblxuZXhwb3J0IGNsYXNzIENhbGxDb21tYW5kIGV4dGVuZHMgQ29tbWFuZDxBcmdzPiB7XG4gIG5hbWUgPSBcImNhbGxcIlxuICBoZWxwID0gXCJDYWxsIGEgc2VydmljZSBpbmdyZXNzIGVuZHBvaW50LlwiXG5cbiAgZGVzY3JpcHRpb24gPSBkZWRlbnRgXG4gICAgVGhpcyBjb21tYW5kIHJlc29sdmVzIHRoZSBkZXBsb3llZCBpbmdyZXNzIGVuZHBvaW50IGZvciB0aGUgZ2l2ZW4gc2VydmljZSBhbmQgcGF0aCwgY2FsbHMgdGhlIGdpdmVuIGVuZHBvaW50IGFuZFxuICAgIG91dHB1dHMgdGhlIHJlc3VsdC5cblxuICAgIEV4YW1wbGVzOlxuXG4gICAgICAgIGdhcmRlbiBjYWxsIG15LWNvbnRhaW5lclxuICAgICAgICBnYXJkZW4gY2FsbCBteS1jb250YWluZXIvc29tZS1wYXRoXG5cbiAgICBOb3RlOiBDdXJyZW50bHkgb25seSBzdXBwb3J0cyBzaW1wbGUgR0VUIHJlcXVlc3RzIGZvciBIVFRQL0hUVFBTIGluZ3Jlc3Nlcy5cbiAgYFxuXG4gIGFyZ3VtZW50cyA9IGNhbGxBcmdzXG5cbiAgYXN5bmMgYWN0aW9uKHsgZ2FyZGVuLCBhcmdzIH06IENvbW1hbmRQYXJhbXM8QXJncz4pOiBQcm9taXNlPENvbW1hbmRSZXN1bHQ+IHtcbiAgICBsZXQgW3NlcnZpY2VOYW1lLCBwYXRoXSA9IHNwbGl0Rmlyc3QoYXJncy5zZXJ2aWNlQW5kUGF0aCwgXCIvXCIpXG5cbiAgICAvLyBUT0RPOiBiZXR0ZXIgZXJyb3Igd2hlbiBzZXJ2aWNlIGRvZXNuJ3QgZXhpc3RcbiAgICBjb25zdCBzZXJ2aWNlID0gYXdhaXQgZ2FyZGVuLmdldFNlcnZpY2Uoc2VydmljZU5hbWUpXG4gICAgY29uc3Qgc3RhdHVzID0gYXdhaXQgZ2FyZGVuLmFjdGlvbnMuZ2V0U2VydmljZVN0YXR1cyh7IHNlcnZpY2UgfSlcblxuICAgIGlmIChzdGF0dXMuc3RhdGUgIT09IFwicmVhZHlcIikge1xuICAgICAgdGhyb3cgbmV3IFJ1bnRpbWVFcnJvcihgU2VydmljZSAke3NlcnZpY2UubmFtZX0gaXMgbm90IHJ1bm5pbmdgLCB7XG4gICAgICAgIHNlcnZpY2VOYW1lOiBzZXJ2aWNlLm5hbWUsXG4gICAgICAgIHN0YXR1cyxcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgaWYgKCFzdGF0dXMuaW5ncmVzc2VzKSB7XG4gICAgICB0aHJvdyBuZXcgUGFyYW1ldGVyRXJyb3IoYFNlcnZpY2UgJHtzZXJ2aWNlLm5hbWV9IGhhcyBubyBhY3RpdmUgaW5ncmVzc2VzYCwge1xuICAgICAgICBzZXJ2aWNlTmFtZTogc2VydmljZS5uYW1lLFxuICAgICAgICBzZXJ2aWNlU3RhdHVzOiBzdGF0dXMsXG4gICAgICB9KVxuICAgIH1cblxuICAgIC8vIGZpbmQgdGhlIGNvcnJlY3QgZW5kcG9pbnQgdG8gY2FsbFxuICAgIGxldCBtYXRjaGVkSW5ncmVzczogU2VydmljZUluZ3Jlc3MgfCBudWxsID0gbnVsbFxuICAgIGxldCBtYXRjaGVkUGF0aFxuXG4gICAgLy8gd2UgY2FuJ3QgZWFzaWx5IHN1cHBvcnQgcmF3IFRDUCBvciBVRFAgaW4gYSBjb21tYW5kIGxpa2UgdGhpc1xuICAgIGNvbnN0IGluZ3Jlc3NlcyA9IHN0YXR1cy5pbmdyZXNzZXMuZmlsdGVyKGUgPT4gZS5wcm90b2NvbCA9PT0gXCJodHRwXCIgfHwgZS5wcm90b2NvbCA9PT0gXCJodHRwc1wiKVxuXG4gICAgaWYgKCFwYXRoKSB7XG4gICAgICAvLyBpZiBubyBwYXRoIGlzIHNwZWNpZmllZCBhbmQgdGhlcmUncyBhIHJvb3QgZW5kcG9pbnQgKHBhdGggPT09IFwiL1wiKSB3ZSB1c2UgdGhhdFxuICAgICAgY29uc3Qgcm9vdEluZ3Jlc3MgPSA8U2VydmljZUluZ3Jlc3M+ZmluZChpbmdyZXNzZXMsIGUgPT4gZS5wYXRoID09PSBcIi9cIilcblxuICAgICAgaWYgKHJvb3RJbmdyZXNzKSB7XG4gICAgICAgIG1hdGNoZWRJbmdyZXNzID0gcm9vdEluZ3Jlc3NcbiAgICAgICAgbWF0Y2hlZFBhdGggPSBcIi9cIlxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gaWYgdGhlcmUncyBubyByb290IGVuZHBvaW50LCBwaWNrIHRoZSBmaXJzdCBlbmRwb2ludFxuICAgICAgICBtYXRjaGVkSW5ncmVzcyA9IGluZ3Jlc3Nlc1swXVxuICAgICAgICBtYXRjaGVkUGF0aCA9IGluZ3Jlc3Nlc1swXS5wYXRoXG4gICAgICB9XG5cbiAgICAgIHBhdGggPSBtYXRjaGVkUGF0aFxuXG4gICAgfSBlbHNlIHtcbiAgICAgIHBhdGggPSBcIi9cIiArIHBhdGhcblxuICAgICAgZm9yIChjb25zdCBpbmdyZXNzIG9mIHN0YXR1cy5pbmdyZXNzZXMpIHtcbiAgICAgICAgaWYgKGluZ3Jlc3MucGF0aCkge1xuICAgICAgICAgIGlmIChwYXRoLnN0YXJ0c1dpdGgoaW5ncmVzcy5wYXRoKSAmJiAoIW1hdGNoZWRQYXRoIHx8IGluZ3Jlc3MucGF0aC5sZW5ndGggPiBtYXRjaGVkUGF0aC5sZW5ndGgpKSB7XG4gICAgICAgICAgICBtYXRjaGVkSW5ncmVzcyA9IGluZ3Jlc3NcbiAgICAgICAgICAgIG1hdGNoZWRQYXRoID0gaW5ncmVzcy5wYXRoXG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKCFtYXRjaGVkUGF0aCkge1xuICAgICAgICAgIG1hdGNoZWRJbmdyZXNzID0gaW5ncmVzc1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFtYXRjaGVkSW5ncmVzcykge1xuICAgICAgdGhyb3cgbmV3IFBhcmFtZXRlckVycm9yKGBTZXJ2aWNlICR7c2VydmljZS5uYW1lfSBkb2VzIG5vdCBoYXZlIGFuIEhUVFAvSFRUUFMgaW5ncmVzcyBhdCAke3BhdGh9YCwge1xuICAgICAgICBzZXJ2aWNlTmFtZTogc2VydmljZS5uYW1lLFxuICAgICAgICBwYXRoLFxuICAgICAgICBhdmFpbGFibGVJbmdyZXNzZXM6IHN0YXR1cy5pbmdyZXNzZXMsXG4gICAgICB9KVxuICAgIH1cblxuICAgIGNvbnN0IHVybCA9IHJlc29sdmUoZ2V0SW5ncmVzc1VybChtYXRjaGVkSW5ncmVzcyksIHBhdGggfHwgbWF0Y2hlZFBhdGgpXG4gICAgLy8gVE9ETzogc3VwcG9ydCBQT1NUIHJlcXVlc3RzIHdpdGggcmVxdWVzdCBib2R5XG4gICAgY29uc3QgbWV0aG9kID0gXCJnZXRcIlxuXG4gICAgY29uc3QgZW50cnkgPSBnYXJkZW4ubG9nLmluZm8oe1xuICAgICAgbXNnOiBjaGFsay5jeWFuKGBTZW5kaW5nICR7bWF0Y2hlZEluZ3Jlc3MucHJvdG9jb2wudG9VcHBlckNhc2UoKX0gR0VUIHJlcXVlc3QgdG8gYCkgKyB1cmwgKyBcIlxcblwiLFxuICAgICAgc3RhdHVzOiBcImFjdGl2ZVwiLFxuICAgIH0pXG5cbiAgICAvLyB0aGlzIGlzIHRvIGFjY2VwdCBzZWxmLXNpZ25lZCBjZXJ0c1xuICAgIHByb2Nlc3MuZW52Lk5PREVfVExTX1JFSkVDVF9VTkFVVEhPUklaRUQgPSBcIjBcIlxuXG4gICAgY29uc3QgcmVxID0gQXhpb3Moe1xuICAgICAgbWV0aG9kLFxuICAgICAgdXJsLFxuICAgICAgaGVhZGVyczoge1xuICAgICAgICBob3N0OiBtYXRjaGVkSW5ncmVzcy5ob3N0bmFtZSxcbiAgICAgIH0sXG4gICAgfSlcblxuICAgIC8vIFRPRE86IGFkZCB2ZXJib3NlIGFuZCBkZWJ1ZyBsb2dnaW5nIChyZXF1ZXN0L3Jlc3BvbnNlIGhlYWRlcnMgZXRjLilcbiAgICBsZXQgcmVzXG5cbiAgICB0cnkge1xuICAgICAgcmVzID0gYXdhaXQgcmVxXG4gICAgICBlbnRyeS5zZXRTdWNjZXNzKClcbiAgICAgIGdhcmRlbi5sb2cuaW5mbyhjaGFsay5ncmVlbihgJHtyZXMuc3RhdHVzfSAke3Jlcy5zdGF0dXNUZXh0fVxcbmApKVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgcmVzID0gZXJyLnJlc3BvbnNlXG4gICAgICBlbnRyeS5zZXRFcnJvcigpXG4gICAgICBjb25zdCBlcnJvciA9IHJlcyA/IGAke3Jlcy5zdGF0dXN9ICR7cmVzLnN0YXR1c1RleHR9YCA6IGVyci5tZXNzYWdlXG4gICAgICBnYXJkZW4ubG9nLmluZm8oY2hhbGsucmVkKGVycm9yICsgXCJcXG5cIikpXG4gICAgICByZXR1cm4ge31cbiAgICB9XG5cbiAgICBjb25zdCByZXNTdHIgPSBpc09iamVjdChyZXMuZGF0YSkgPyBKU09OLnN0cmluZ2lmeShyZXMuZGF0YSwgbnVsbCwgMikgOiByZXMuZGF0YVxuXG4gICAgcmVzLmRhdGEgJiYgZ2FyZGVuLmxvZy5pbmZvKGNoYWxrLndoaXRlKHJlc1N0cikgKyBcIlxcblwiKVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIHJlc3VsdDoge1xuICAgICAgICBzZXJ2aWNlTmFtZSxcbiAgICAgICAgcGF0aCxcbiAgICAgICAgdXJsLFxuICAgICAgICByZXNwb25zZTogcGljayhyZXMsIFtcInN0YXR1c1wiLCBcInN0YXR1c1RleHRcIiwgXCJoZWFkZXJzXCIsIFwiZGF0YVwiXSksXG4gICAgICB9LFxuICAgIH1cbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/commands.d.ts b/garden-service/build/commands/commands.d.ts
new file mode 100644
index 00000000000..2af1247a565
--- /dev/null
+++ b/garden-service/build/commands/commands.d.ts
@@ -0,0 +1,3 @@
+import { Command } from "./base";
+export declare const coreCommands: Command[];
+//# sourceMappingURL=commands.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/commands.js b/garden-service/build/commands/commands.js
new file mode 100644
index 00000000000..7f2af8082ab
--- /dev/null
+++ b/garden-service/build/commands/commands.js
@@ -0,0 +1,51 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+Object.defineProperty(exports, "__esModule", { value: true });
+const build_1 = require("./build");
+const create_1 = require("./create/create");
+const call_1 = require("./call");
+const init_1 = require("./init");
+const delete_1 = require("./delete");
+const deploy_1 = require("./deploy");
+const dev_1 = require("./dev");
+const get_1 = require("./get");
+const link_1 = require("./link/link");
+const logs_1 = require("./logs");
+const publish_1 = require("./publish");
+const run_1 = require("./run/run");
+const scan_1 = require("./scan");
+const set_1 = require("./set");
+const test_1 = require("./test");
+const unlink_1 = require("./unlink/unlink");
+const update_remote_1 = require("./update-remote/update-remote");
+const validate_1 = require("./validate");
+const exec_1 = require("./exec");
+exports.coreCommands = [
+ new build_1.BuildCommand(),
+ new call_1.CallCommand(),
+ new create_1.CreateCommand(),
+ new delete_1.DeleteCommand(),
+ new deploy_1.DeployCommand(),
+ new dev_1.DevCommand(),
+ new exec_1.ExecCommand(),
+ new get_1.GetCommand(),
+ new init_1.InitCommand(),
+ new link_1.LinkCommand(),
+ new logs_1.LogsCommand(),
+ new publish_1.PublishCommand(),
+ new run_1.RunCommand(),
+ new scan_1.ScanCommand(),
+ new set_1.SetCommand(),
+ new test_1.TestCommand(),
+ new unlink_1.UnlinkCommand(),
+ new update_remote_1.UpdateRemoteCommand(),
+ new validate_1.ValidateCommand(),
+];
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2NvbW1hbmRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7O0FBR0gsbUNBQXNDO0FBQ3RDLDRDQUErQztBQUMvQyxpQ0FBb0M7QUFDcEMsaUNBQW9DO0FBQ3BDLHFDQUF3QztBQUN4QyxxQ0FBd0M7QUFDeEMsK0JBQWtDO0FBQ2xDLCtCQUFrQztBQUNsQyxzQ0FBeUM7QUFDekMsaUNBQW9DO0FBQ3BDLHVDQUEwQztBQUMxQyxtQ0FBc0M7QUFDdEMsaUNBQW9DO0FBQ3BDLCtCQUFrQztBQUNsQyxpQ0FBb0M7QUFDcEMsNENBQStDO0FBQy9DLGlFQUFtRTtBQUNuRSx5Q0FBNEM7QUFDNUMsaUNBQW9DO0FBRXZCLFFBQUEsWUFBWSxHQUFjO0lBQ3JDLElBQUksb0JBQVksRUFBRTtJQUNsQixJQUFJLGtCQUFXLEVBQUU7SUFDakIsSUFBSSxzQkFBYSxFQUFFO0lBQ25CLElBQUksc0JBQWEsRUFBRTtJQUNuQixJQUFJLHNCQUFhLEVBQUU7SUFDbkIsSUFBSSxnQkFBVSxFQUFFO0lBQ2hCLElBQUksa0JBQVcsRUFBRTtJQUNqQixJQUFJLGdCQUFVLEVBQUU7SUFDaEIsSUFBSSxrQkFBVyxFQUFFO0lBQ2pCLElBQUksa0JBQVcsRUFBRTtJQUNqQixJQUFJLGtCQUFXLEVBQUU7SUFDakIsSUFBSSx3QkFBYyxFQUFFO0lBQ3BCLElBQUksZ0JBQVUsRUFBRTtJQUNoQixJQUFJLGtCQUFXLEVBQUU7SUFDakIsSUFBSSxnQkFBVSxFQUFFO0lBQ2hCLElBQUksa0JBQVcsRUFBRTtJQUNqQixJQUFJLHNCQUFhLEVBQUU7SUFDbkIsSUFBSSxtQ0FBbUIsRUFBRTtJQUN6QixJQUFJLDBCQUFlLEVBQUU7Q0FDdEIsQ0FBQSIsImZpbGUiOiJjb21tYW5kcy9jb21tYW5kcy5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgeyBDb21tYW5kIH0gZnJvbSBcIi4vYmFzZVwiXG5pbXBvcnQgeyBCdWlsZENvbW1hbmQgfSBmcm9tIFwiLi9idWlsZFwiXG5pbXBvcnQgeyBDcmVhdGVDb21tYW5kIH0gZnJvbSBcIi4vY3JlYXRlL2NyZWF0ZVwiXG5pbXBvcnQgeyBDYWxsQ29tbWFuZCB9IGZyb20gXCIuL2NhbGxcIlxuaW1wb3J0IHsgSW5pdENvbW1hbmQgfSBmcm9tIFwiLi9pbml0XCJcbmltcG9ydCB7IERlbGV0ZUNvbW1hbmQgfSBmcm9tIFwiLi9kZWxldGVcIlxuaW1wb3J0IHsgRGVwbG95Q29tbWFuZCB9IGZyb20gXCIuL2RlcGxveVwiXG5pbXBvcnQgeyBEZXZDb21tYW5kIH0gZnJvbSBcIi4vZGV2XCJcbmltcG9ydCB7IEdldENvbW1hbmQgfSBmcm9tIFwiLi9nZXRcIlxuaW1wb3J0IHsgTGlua0NvbW1hbmQgfSBmcm9tIFwiLi9saW5rL2xpbmtcIlxuaW1wb3J0IHsgTG9nc0NvbW1hbmQgfSBmcm9tIFwiLi9sb2dzXCJcbmltcG9ydCB7IFB1Ymxpc2hDb21tYW5kIH0gZnJvbSBcIi4vcHVibGlzaFwiXG5pbXBvcnQgeyBSdW5Db21tYW5kIH0gZnJvbSBcIi4vcnVuL3J1blwiXG5pbXBvcnQgeyBTY2FuQ29tbWFuZCB9IGZyb20gXCIuL3NjYW5cIlxuaW1wb3J0IHsgU2V0Q29tbWFuZCB9IGZyb20gXCIuL3NldFwiXG5pbXBvcnQgeyBUZXN0Q29tbWFuZCB9IGZyb20gXCIuL3Rlc3RcIlxuaW1wb3J0IHsgVW5saW5rQ29tbWFuZCB9IGZyb20gXCIuL3VubGluay91bmxpbmtcIlxuaW1wb3J0IHsgVXBkYXRlUmVtb3RlQ29tbWFuZCB9IGZyb20gXCIuL3VwZGF0ZS1yZW1vdGUvdXBkYXRlLXJlbW90ZVwiXG5pbXBvcnQgeyBWYWxpZGF0ZUNvbW1hbmQgfSBmcm9tIFwiLi92YWxpZGF0ZVwiXG5pbXBvcnQgeyBFeGVjQ29tbWFuZCB9IGZyb20gXCIuL2V4ZWNcIlxuXG5leHBvcnQgY29uc3QgY29yZUNvbW1hbmRzOiBDb21tYW5kW10gPSBbXG4gIG5ldyBCdWlsZENvbW1hbmQoKSxcbiAgbmV3IENhbGxDb21tYW5kKCksXG4gIG5ldyBDcmVhdGVDb21tYW5kKCksXG4gIG5ldyBEZWxldGVDb21tYW5kKCksXG4gIG5ldyBEZXBsb3lDb21tYW5kKCksXG4gIG5ldyBEZXZDb21tYW5kKCksXG4gIG5ldyBFeGVjQ29tbWFuZCgpLFxuICBuZXcgR2V0Q29tbWFuZCgpLFxuICBuZXcgSW5pdENvbW1hbmQoKSxcbiAgbmV3IExpbmtDb21tYW5kKCksXG4gIG5ldyBMb2dzQ29tbWFuZCgpLFxuICBuZXcgUHVibGlzaENvbW1hbmQoKSxcbiAgbmV3IFJ1bkNvbW1hbmQoKSxcbiAgbmV3IFNjYW5Db21tYW5kKCksXG4gIG5ldyBTZXRDb21tYW5kKCksXG4gIG5ldyBUZXN0Q29tbWFuZCgpLFxuICBuZXcgVW5saW5rQ29tbWFuZCgpLFxuICBuZXcgVXBkYXRlUmVtb3RlQ29tbWFuZCgpLFxuICBuZXcgVmFsaWRhdGVDb21tYW5kKCksXG5dXG4iXX0=
diff --git a/garden-service/build/commands/create/config-templates.d.ts b/garden-service/build/commands/create/config-templates.d.ts
new file mode 100644
index 00000000000..c402cce39df
--- /dev/null
+++ b/garden-service/build/commands/create/config-templates.d.ts
@@ -0,0 +1,43 @@
+import * as Joi from "joi";
+import { DeepPartial } from "../../util/util";
+import { ContainerModuleSpec } from "../../plugins/container";
+import { GcfModuleSpec } from "../../plugins/google/google-cloud-functions";
+import { ProjectConfig } from "../../config/project";
+import { BaseModuleSpec, ModuleConfig } from "../../config/module";
+/**
+ * Ideally there would be some mechanism to discover available module types,
+ * and for plugins to expose a minimal config for the given type along with
+ * a list of providers per environment, rather than hard coding these values.
+ *
+ * Alternatively, consider co-locating the templates with the plugins.
+ */
+export declare const MODULE_PROVIDER_MAP: {
+ container: string;
+ "google-cloud-function": string;
+ "npm-package": string;
+};
+export declare const availableModuleTypes: ("container" | "npm-package" | "google-cloud-function")[];
+export declare type ModuleType = keyof typeof MODULE_PROVIDER_MAP;
+export declare const moduleSchema: Joi.ObjectSchema;
+export interface ConfigOpts {
+ name: string;
+ path: string;
+ config: {
+ module: Partial;
+ } | Partial;
+}
+export interface ModuleConfigOpts extends ConfigOpts {
+ type: ModuleType;
+ config: {
+ module: Partial;
+ };
+}
+export interface ProjectConfigOpts extends ConfigOpts {
+ config: Partial;
+}
+export declare function containerTemplate(moduleName: string): DeepPartial;
+export declare function googleCloudFunctionTemplate(moduleName: string): DeepPartial;
+export declare function npmPackageTemplate(_moduleName: string): any;
+export declare const projectTemplate: (name: string, moduleTypes: ("container" | "npm-package" | "google-cloud-function")[]) => Partial;
+export declare const moduleTemplate: (name: string, type: "container" | "npm-package" | "google-cloud-function") => Partial;
+//# sourceMappingURL=config-templates.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/create/config-templates.js b/garden-service/build/commands/create/config-templates.js
new file mode 100644
index 00000000000..50dc11bd291
--- /dev/null
+++ b/garden-service/build/commands/create/config-templates.js
@@ -0,0 +1,81 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+Object.defineProperty(exports, "__esModule", { value: true });
+const lodash_1 = require("lodash");
+const Joi = require("joi");
+const module_1 = require("../../config/module");
+/**
+ * Ideally there would be some mechanism to discover available module types,
+ * and for plugins to expose a minimal config for the given type along with
+ * a list of providers per environment, rather than hard coding these values.
+ *
+ * Alternatively, consider co-locating the templates with the plugins.
+ */
+exports.MODULE_PROVIDER_MAP = {
+ container: "local-kubernetes",
+ "google-cloud-function": "local-google-cloud-functions",
+ "npm-package": "npm-package",
+};
+exports.availableModuleTypes = Object.keys(exports.MODULE_PROVIDER_MAP);
+exports.moduleSchema = Joi.object().keys({
+ module: module_1.baseModuleSpecSchema,
+});
+const noCase = (str) => str.replace(/-|_/g, " ");
+const titleize = (str) => lodash_1.capitalize(noCase(str));
+function containerTemplate(moduleName) {
+ return {
+ services: [
+ {
+ name: `${moduleName}-service`,
+ ports: [{
+ name: "http",
+ containerPort: 8080,
+ }],
+ ingresses: [{
+ path: "/",
+ port: "http",
+ }],
+ },
+ ],
+ };
+}
+exports.containerTemplate = containerTemplate;
+function googleCloudFunctionTemplate(moduleName) {
+ return {
+ functions: [{
+ name: `${moduleName}-google-cloud-function`,
+ entrypoint: lodash_1.camelCase(`${moduleName}-google-cloud-function`),
+ }],
+ };
+}
+exports.googleCloudFunctionTemplate = googleCloudFunctionTemplate;
+function npmPackageTemplate(_moduleName) {
+ return {};
+}
+exports.npmPackageTemplate = npmPackageTemplate;
+exports.projectTemplate = (name, moduleTypes) => {
+ const providers = lodash_1.uniq(moduleTypes).map(type => ({ name: exports.MODULE_PROVIDER_MAP[type] }));
+ return {
+ name,
+ environments: [
+ {
+ name: "local",
+ providers,
+ variables: {},
+ },
+ ],
+ };
+};
+exports.moduleTemplate = (name, type) => ({
+ name,
+ type,
+ description: `${titleize(name)} ${noCase(type)}`,
+});
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2NyZWF0ZS9jb25maWctdGVtcGxhdGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7O0FBRUgsbUNBQW9EO0FBQ3BELDJCQUEwQjtBQU0xQixnREFBd0Y7QUFFeEY7Ozs7OztHQU1HO0FBQ1UsUUFBQSxtQkFBbUIsR0FBRztJQUNqQyxTQUFTLEVBQUUsa0JBQWtCO0lBQzdCLHVCQUF1QixFQUFFLDhCQUE4QjtJQUN2RCxhQUFhLEVBQUUsYUFBYTtDQUM3QixDQUFBO0FBRVksUUFBQSxvQkFBb0IsR0FBaUIsTUFBTSxDQUFDLElBQUksQ0FBQywyQkFBbUIsQ0FBQyxDQUFBO0FBSXJFLFFBQUEsWUFBWSxHQUFHLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLENBQUM7SUFDNUMsTUFBTSxFQUFFLDZCQUFvQjtDQUM3QixDQUFDLENBQUE7QUFpQkYsTUFBTSxNQUFNLEdBQUcsQ0FBQyxHQUFXLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFBO0FBQ3hELE1BQU0sUUFBUSxHQUFHLENBQUMsR0FBVyxFQUFFLEVBQUUsQ0FBQyxtQkFBVSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFBO0FBRXpELFNBQWdCLGlCQUFpQixDQUFDLFVBQWtCO0lBQ2xELE9BQU87UUFDTCxRQUFRLEVBQUU7WUFDUjtnQkFDRSxJQUFJLEVBQUUsR0FBRyxVQUFVLFVBQVU7Z0JBQzdCLEtBQUssRUFBRSxDQUFDO3dCQUNOLElBQUksRUFBRSxNQUFNO3dCQUNaLGFBQWEsRUFBRSxJQUFJO3FCQUNwQixDQUFDO2dCQUNGLFNBQVMsRUFBRSxDQUFDO3dCQUNWLElBQUksRUFBRSxHQUFHO3dCQUNULElBQUksRUFBRSxNQUFNO3FCQUNiLENBQUM7YUFDSDtTQUNGO0tBQ0YsQ0FBQTtBQUNILENBQUM7QUFoQkQsOENBZ0JDO0FBRUQsU0FBZ0IsMkJBQTJCLENBQUMsVUFBa0I7SUFDNUQsT0FBTztRQUNMLFNBQVMsRUFBRSxDQUFDO2dCQUNWLElBQUksRUFBRSxHQUFHLFVBQVUsd0JBQXdCO2dCQUMzQyxVQUFVLEVBQUUsa0JBQVMsQ0FBQyxHQUFHLFVBQVUsd0JBQXdCLENBQUM7YUFDN0QsQ0FBQztLQUNILENBQUE7QUFDSCxDQUFDO0FBUEQsa0VBT0M7QUFFRCxTQUFnQixrQkFBa0IsQ0FBQyxXQUFtQjtJQUNwRCxPQUFPLEVBQUUsQ0FBQTtBQUNYLENBQUM7QUFGRCxnREFFQztBQUVZLFFBQUEsZUFBZSxHQUFHLENBQUMsSUFBWSxFQUFFLFdBQXlCLEVBQTBCLEVBQUU7SUFDakcsTUFBTSxTQUFTLEdBQUcsYUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsMkJBQW1CLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUE7SUFDdEYsT0FBTztRQUNMLElBQUk7UUFDSixZQUFZLEVBQUU7WUFDWjtnQkFDRSxJQUFJLEVBQUUsT0FBTztnQkFDYixTQUFTO2dCQUNULFNBQVMsRUFBRSxFQUFFO2FBQ2Q7U0FDRjtLQUNGLENBQUE7QUFDSCxDQUFDLENBQUE7QUFFWSxRQUFBLGNBQWMsR0FBRyxDQUFDLElBQVksRUFBRSxJQUFnQixFQUEyQixFQUFFLENBQUMsQ0FBQztJQUMxRixJQUFJO0lBQ0osSUFBSTtJQUNKLFdBQVcsRUFBRSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUU7Q0FDakQsQ0FBQyxDQUFBIiwiZmlsZSI6ImNvbW1hbmRzL2NyZWF0ZS9jb25maWctdGVtcGxhdGVzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoQykgMjAxOCBHYXJkZW4gVGVjaG5vbG9naWVzLCBJbmMuIDxpbmZvQGdhcmRlbi5pbz5cbiAqXG4gKiBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljXG4gKiBMaWNlbnNlLCB2LiAyLjAuIElmIGEgY29weSBvZiB0aGUgTVBMIHdhcyBub3QgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzXG4gKiBmaWxlLCBZb3UgY2FuIG9idGFpbiBvbmUgYXQgaHR0cDovL21vemlsbGEub3JnL01QTC8yLjAvLlxuICovXG5cbmltcG9ydCB7IGNhcGl0YWxpemUsIGNhbWVsQ2FzZSwgdW5pcSB9IGZyb20gXCJsb2Rhc2hcIlxuaW1wb3J0ICogYXMgSm9pIGZyb20gXCJqb2lcIlxuXG5pbXBvcnQgeyBEZWVwUGFydGlhbCB9IGZyb20gXCIuLi8uLi91dGlsL3V0aWxcIlxuaW1wb3J0IHsgQ29udGFpbmVyTW9kdWxlU3BlYyB9IGZyb20gXCIuLi8uLi9wbHVnaW5zL2NvbnRhaW5lclwiXG5pbXBvcnQgeyBHY2ZNb2R1bGVTcGVjIH0gZnJvbSBcIi4uLy4uL3BsdWdpbnMvZ29vZ2xlL2dvb2dsZS1jbG91ZC1mdW5jdGlvbnNcIlxuaW1wb3J0IHsgUHJvamVjdENvbmZpZyB9IGZyb20gXCIuLi8uLi9jb25maWcvcHJvamVjdFwiXG5pbXBvcnQgeyBCYXNlTW9kdWxlU3BlYywgTW9kdWxlQ29uZmlnLCBiYXNlTW9kdWxlU3BlY1NjaGVtYSB9IGZyb20gXCIuLi8uLi9jb25maWcvbW9kdWxlXCJcblxuLyoqXG4gKiBJZGVhbGx5IHRoZXJlIHdvdWxkIGJlIHNvbWUgbWVjaGFuaXNtIHRvIGRpc2NvdmVyIGF2YWlsYWJsZSBtb2R1bGUgdHlwZXMsXG4gKiBhbmQgZm9yIHBsdWdpbnMgdG8gZXhwb3NlIGEgbWluaW1hbCBjb25maWcgZm9yIHRoZSBnaXZlbiB0eXBlIGFsb25nIHdpdGhcbiAqIGEgbGlzdCBvZiBwcm92aWRlcnMgcGVyIGVudmlyb25tZW50LCByYXRoZXIgdGhhbiBoYXJkIGNvZGluZyB0aGVzZSB2YWx1ZXMuXG4gKlxuICogQWx0ZXJuYXRpdmVseSwgY29uc2lkZXIgY28tbG9jYXRpbmcgdGhlIHRlbXBsYXRlcyB3aXRoIHRoZSBwbHVnaW5zLlxuICovXG5leHBvcnQgY29uc3QgTU9EVUxFX1BST1ZJREVSX01BUCA9IHtcbiAgY29udGFpbmVyOiBcImxvY2FsLWt1YmVybmV0ZXNcIixcbiAgXCJnb29nbGUtY2xvdWQtZnVuY3Rpb25cIjogXCJsb2NhbC1nb29nbGUtY2xvdWQtZnVuY3Rpb25zXCIsXG4gIFwibnBtLXBhY2thZ2VcIjogXCJucG0tcGFja2FnZVwiLFxufVxuXG5leHBvcnQgY29uc3QgYXZhaWxhYmxlTW9kdWxlVHlwZXMgPSA8TW9kdWxlVHlwZVtdPk9iamVjdC5rZXlzKE1PRFVMRV9QUk9WSURFUl9NQVApXG5cbmV4cG9ydCB0eXBlIE1vZHVsZVR5cGUgPSBrZXlvZiB0eXBlb2YgTU9EVUxFX1BST1ZJREVSX01BUFxuXG5leHBvcnQgY29uc3QgbW9kdWxlU2NoZW1hID0gSm9pLm9iamVjdCgpLmtleXMoe1xuICBtb2R1bGU6IGJhc2VNb2R1bGVTcGVjU2NoZW1hLFxufSlcblxuZXhwb3J0IGludGVyZmFjZSBDb25maWdPcHRzIHtcbiAgbmFtZTogc3RyaW5nXG4gIHBhdGg6IHN0cmluZ1xuICBjb25maWc6IHsgbW9kdWxlOiBQYXJ0aWFsPE1vZHVsZUNvbmZpZz4gfSB8IFBhcnRpYWw8UHJvamVjdENvbmZpZz5cbn1cblxuZXhwb3J0IGludGVyZmFjZSBNb2R1bGVDb25maWdPcHRzIGV4dGVuZHMgQ29uZmlnT3B0cyB7XG4gIHR5cGU6IE1vZHVsZVR5cGVcbiAgY29uZmlnOiB7IG1vZHVsZTogUGFydGlhbDxNb2R1bGVDb25maWc+IH1cbn1cblxuZXhwb3J0IGludGVyZmFjZSBQcm9qZWN0Q29uZmlnT3B0cyBleHRlbmRzIENvbmZpZ09wdHMge1xuICBjb25maWc6IFBhcnRpYWw8UHJvamVjdENvbmZpZz5cbn1cblxuY29uc3Qgbm9DYXNlID0gKHN0cjogc3RyaW5nKSA9PiBzdHIucmVwbGFjZSgvLXxfL2csIFwiIFwiKVxuY29uc3QgdGl0bGVpemUgPSAoc3RyOiBzdHJpbmcpID0+IGNhcGl0YWxpemUobm9DYXNlKHN0cikpXG5cbmV4cG9ydCBmdW5jdGlvbiBjb250YWluZXJUZW1wbGF0ZShtb2R1bGVOYW1lOiBzdHJpbmcpOiBEZWVwUGFydGlhbDxDb250YWluZXJNb2R1bGVTcGVjPiB7XG4gIHJldHVybiB7XG4gICAgc2VydmljZXM6IFtcbiAgICAgIHtcbiAgICAgICAgbmFtZTogYCR7bW9kdWxlTmFtZX0tc2VydmljZWAsXG4gICAgICAgIHBvcnRzOiBbe1xuICAgICAgICAgIG5hbWU6IFwiaHR0cFwiLFxuICAgICAgICAgIGNvbnRhaW5lclBvcnQ6IDgwODAsXG4gICAgICAgIH1dLFxuICAgICAgICBpbmdyZXNzZXM6IFt7XG4gICAgICAgICAgcGF0aDogXCIvXCIsXG4gICAgICAgICAgcG9ydDogXCJodHRwXCIsXG4gICAgICAgIH1dLFxuICAgICAgfSxcbiAgICBdLFxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnb29nbGVDbG91ZEZ1bmN0aW9uVGVtcGxhdGUobW9kdWxlTmFtZTogc3RyaW5nKTogRGVlcFBhcnRpYWw8R2NmTW9kdWxlU3BlYz4ge1xuICByZXR1cm4ge1xuICAgIGZ1bmN0aW9uczogW3tcbiAgICAgIG5hbWU6IGAke21vZHVsZU5hbWV9LWdvb2dsZS1jbG91ZC1mdW5jdGlvbmAsXG4gICAgICBlbnRyeXBvaW50OiBjYW1lbENhc2UoYCR7bW9kdWxlTmFtZX0tZ29vZ2xlLWNsb3VkLWZ1bmN0aW9uYCksXG4gICAgfV0sXG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG5wbVBhY2thZ2VUZW1wbGF0ZShfbW9kdWxlTmFtZTogc3RyaW5nKTogYW55IHtcbiAgcmV0dXJuIHt9XG59XG5cbmV4cG9ydCBjb25zdCBwcm9qZWN0VGVtcGxhdGUgPSAobmFtZTogc3RyaW5nLCBtb2R1bGVUeXBlczogTW9kdWxlVHlwZVtdKTogUGFydGlhbDxQcm9qZWN0Q29uZmlnPiA9PiB7XG4gIGNvbnN0IHByb3ZpZGVycyA9IHVuaXEobW9kdWxlVHlwZXMpLm1hcCh0eXBlID0+ICh7IG5hbWU6IE1PRFVMRV9QUk9WSURFUl9NQVBbdHlwZV0gfSkpXG4gIHJldHVybiB7XG4gICAgbmFtZSxcbiAgICBlbnZpcm9ubWVudHM6IFtcbiAgICAgIHtcbiAgICAgICAgbmFtZTogXCJsb2NhbFwiLFxuICAgICAgICBwcm92aWRlcnMsXG4gICAgICAgIHZhcmlhYmxlczoge30sXG4gICAgICB9LFxuICAgIF0sXG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IG1vZHVsZVRlbXBsYXRlID0gKG5hbWU6IHN0cmluZywgdHlwZTogTW9kdWxlVHlwZSk6IFBhcnRpYWw8QmFzZU1vZHVsZVNwZWM+ID0+ICh7XG4gIG5hbWUsXG4gIHR5cGUsXG4gIGRlc2NyaXB0aW9uOiBgJHt0aXRsZWl6ZShuYW1lKX0gJHtub0Nhc2UodHlwZSl9YCxcbn0pXG4iXX0=
diff --git a/garden-service/build/commands/create/create.d.ts b/garden-service/build/commands/create/create.d.ts
new file mode 100644
index 00000000000..3664a416f24
--- /dev/null
+++ b/garden-service/build/commands/create/create.d.ts
@@ -0,0 +1,11 @@
+import { Command } from "../base";
+import { CreateProjectCommand } from "./project";
+import { CreateModuleCommand } from "./module";
+export declare class CreateCommand extends Command {
+ name: string;
+ alias: string;
+ help: string;
+ subCommands: (typeof CreateProjectCommand | typeof CreateModuleCommand)[];
+ action(): Promise<{}>;
+}
+//# sourceMappingURL=create.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/create/create.js b/garden-service/build/commands/create/create.js
new file mode 100644
index 00000000000..a34f8f9461b
--- /dev/null
+++ b/garden-service/build/commands/create/create.js
@@ -0,0 +1,38 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const base_1 = require("../base");
+const project_1 = require("./project");
+const module_1 = require("./module");
+class CreateCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "create";
+ this.alias = "r";
+ this.help = "Create a new project or add a new module";
+ this.subCommands = [
+ project_1.CreateProjectCommand,
+ module_1.CreateModuleCommand,
+ ];
+ }
+ action() {
+ return __awaiter(this, void 0, void 0, function* () { return {}; });
+ }
+}
+exports.CreateCommand = CreateCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2NyZWF0ZS9jcmVhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7R0FNRzs7Ozs7Ozs7OztBQUVILGtDQUFpQztBQUNqQyx1Q0FBZ0Q7QUFDaEQscUNBQThDO0FBRTlDLE1BQWEsYUFBYyxTQUFRLGNBQU87SUFBMUM7O1FBQ0UsU0FBSSxHQUFHLFFBQVEsQ0FBQTtRQUNmLFVBQUssR0FBRyxHQUFHLENBQUE7UUFDWCxTQUFJLEdBQUcsMENBQTBDLENBQUE7UUFFakQsZ0JBQVcsR0FBRztZQUNaLDhCQUFvQjtZQUNwQiw0QkFBbUI7U0FDcEIsQ0FBQTtJQUdILENBQUM7SUFETyxNQUFNOzhEQUFLLE9BQU8sRUFBRSxDQUFBLENBQUMsQ0FBQztLQUFBO0NBQzdCO0FBWEQsc0NBV0MiLCJmaWxlIjoiY29tbWFuZHMvY3JlYXRlL2NyZWF0ZS5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgeyBDb21tYW5kIH0gZnJvbSBcIi4uL2Jhc2VcIlxuaW1wb3J0IHsgQ3JlYXRlUHJvamVjdENvbW1hbmQgfSBmcm9tIFwiLi9wcm9qZWN0XCJcbmltcG9ydCB7IENyZWF0ZU1vZHVsZUNvbW1hbmQgfSBmcm9tIFwiLi9tb2R1bGVcIlxuXG5leHBvcnQgY2xhc3MgQ3JlYXRlQ29tbWFuZCBleHRlbmRzIENvbW1hbmQge1xuICBuYW1lID0gXCJjcmVhdGVcIlxuICBhbGlhcyA9IFwiclwiXG4gIGhlbHAgPSBcIkNyZWF0ZSBhIG5ldyBwcm9qZWN0IG9yIGFkZCBhIG5ldyBtb2R1bGVcIlxuXG4gIHN1YkNvbW1hbmRzID0gW1xuICAgIENyZWF0ZVByb2plY3RDb21tYW5kLFxuICAgIENyZWF0ZU1vZHVsZUNvbW1hbmQsXG4gIF1cblxuICBhc3luYyBhY3Rpb24oKSB7IHJldHVybiB7fSB9XG59XG4iXX0=
diff --git a/garden-service/build/commands/create/helpers.d.ts b/garden-service/build/commands/create/helpers.d.ts
new file mode 100644
index 00000000000..e1a40b51d20
--- /dev/null
+++ b/garden-service/build/commands/create/helpers.d.ts
@@ -0,0 +1,6 @@
+import * as Joi from "joi";
+import { ModuleConfigOpts, ModuleType, ConfigOpts } from "./config-templates";
+import { LogNode } from "../../logger/log-node";
+export declare function prepareNewModuleConfig(name: string, type: ModuleType, path: string): ModuleConfigOpts;
+export declare function dumpConfig(configOpts: ConfigOpts, schema: Joi.Schema, logger: LogNode): Promise;
+//# sourceMappingURL=helpers.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/create/helpers.js b/garden-service/build/commands/create/helpers.js
new file mode 100644
index 00000000000..db429dba13b
--- /dev/null
+++ b/garden-service/build/commands/create/helpers.js
@@ -0,0 +1,65 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const config_templates_1 = require("./config-templates");
+const path_1 = require("path");
+const fs_extra_1 = require("fs-extra");
+const common_1 = require("../../config/common");
+const util_1 = require("../../util/util");
+const constants_1 = require("../../constants");
+function prepareNewModuleConfig(name, type, path) {
+ const moduleTypeTemplate = {
+ container: config_templates_1.containerTemplate,
+ "google-cloud-function": config_templates_1.googleCloudFunctionTemplate,
+ "npm-package": config_templates_1.npmPackageTemplate,
+ }[type];
+ return {
+ name,
+ type,
+ path,
+ config: {
+ module: Object.assign({}, config_templates_1.moduleTemplate(name, type), moduleTypeTemplate(name)),
+ },
+ };
+}
+exports.prepareNewModuleConfig = prepareNewModuleConfig;
+function dumpConfig(configOpts, schema, logger) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const { config, name, path } = configOpts;
+ const yamlPath = path_1.join(path, constants_1.MODULE_CONFIG_FILENAME);
+ const task = logger.info({
+ msg: `Writing config for ${name}`,
+ status: "active",
+ });
+ if (yield fs_extra_1.pathExists(yamlPath)) {
+ task.setWarn({ msg: `Garden config file already exists at path, skipping`, append: true });
+ return;
+ }
+ try {
+ common_1.validate(config, schema);
+ yield util_1.dumpYaml(yamlPath, config);
+ task.setSuccess();
+ }
+ catch (err) {
+ task.setError({ msg: `Generated config is invalid, skipping`, append: true });
+ throw err;
+ }
+ });
+}
+exports.dumpConfig = dumpConfig;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2NyZWF0ZS9oZWxwZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7QUFHSCx5REFRMkI7QUFDM0IsK0JBQTJCO0FBQzNCLHVDQUFxQztBQUNyQyxnREFBOEM7QUFDOUMsMENBQTBDO0FBQzFDLCtDQUF3RDtBQUd4RCxTQUFnQixzQkFBc0IsQ0FBQyxJQUFZLEVBQUUsSUFBZ0IsRUFBRSxJQUFZO0lBQ2pGLE1BQU0sa0JBQWtCLEdBQUc7UUFDekIsU0FBUyxFQUFFLG9DQUFpQjtRQUM1Qix1QkFBdUIsRUFBRSw4Q0FBMkI7UUFDcEQsYUFBYSxFQUFFLHFDQUFrQjtLQUNsQyxDQUFDLElBQUksQ0FBQyxDQUFBO0lBQ1AsT0FBTztRQUNMLElBQUk7UUFDSixJQUFJO1FBQ0osSUFBSTtRQUNKLE1BQU0sRUFBRTtZQUNOLE1BQU0sb0JBQ0QsaUNBQWMsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLEVBQzFCLGtCQUFrQixDQUFDLElBQUksQ0FBQyxDQUM1QjtTQUNGO0tBQ0YsQ0FBQTtBQUNILENBQUM7QUFqQkQsd0RBaUJDO0FBRUQsU0FBc0IsVUFBVSxDQUFDLFVBQXNCLEVBQUUsTUFBa0IsRUFBRSxNQUFlOztRQUMxRixNQUFNLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsR0FBRyxVQUFVLENBQUE7UUFDekMsTUFBTSxRQUFRLEdBQUcsV0FBSSxDQUFDLElBQUksRUFBRSxrQ0FBc0IsQ0FBQyxDQUFBO1FBQ25ELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUM7WUFDdkIsR0FBRyxFQUFFLHNCQUFzQixJQUFJLEVBQUU7WUFDakMsTUFBTSxFQUFFLFFBQVE7U0FDakIsQ0FBQyxDQUFBO1FBRUYsSUFBSSxNQUFNLHFCQUFVLENBQUMsUUFBUSxDQUFDLEVBQUU7WUFDOUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEdBQUcsRUFBRSxxREFBcUQsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtZQUMxRixPQUFNO1NBQ1A7UUFFRCxJQUFJO1lBQ0YsaUJBQVEsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUE7WUFDeEIsTUFBTSxlQUFRLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxDQUFBO1lBQ2hDLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQTtTQUNsQjtRQUFDLE9BQU8sR0FBRyxFQUFFO1lBQ1osSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLEdBQUcsRUFBRSx1Q0FBdUMsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtZQUM3RSxNQUFNLEdBQUcsQ0FBQTtTQUNWO0lBQ0gsQ0FBQztDQUFBO0FBckJELGdDQXFCQyIsImZpbGUiOiJjb21tYW5kcy9jcmVhdGUvaGVscGVycy5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgKiBhcyBKb2kgZnJvbSBcImpvaVwiXG5pbXBvcnQge1xuICBjb250YWluZXJUZW1wbGF0ZSxcbiAgZ29vZ2xlQ2xvdWRGdW5jdGlvblRlbXBsYXRlLFxuICBucG1QYWNrYWdlVGVtcGxhdGUsXG4gIE1vZHVsZUNvbmZpZ09wdHMsXG4gIE1vZHVsZVR5cGUsXG4gIG1vZHVsZVRlbXBsYXRlLFxuICBDb25maWdPcHRzLFxufSBmcm9tIFwiLi9jb25maWctdGVtcGxhdGVzXCJcbmltcG9ydCB7IGpvaW4gfSBmcm9tIFwicGF0aFwiXG5pbXBvcnQgeyBwYXRoRXhpc3RzIH0gZnJvbSBcImZzLWV4dHJhXCJcbmltcG9ydCB7IHZhbGlkYXRlIH0gZnJvbSBcIi4uLy4uL2NvbmZpZy9jb21tb25cIlxuaW1wb3J0IHsgZHVtcFlhbWwgfSBmcm9tIFwiLi4vLi4vdXRpbC91dGlsXCJcbmltcG9ydCB7IE1PRFVMRV9DT05GSUdfRklMRU5BTUUgfSBmcm9tIFwiLi4vLi4vY29uc3RhbnRzXCJcbmltcG9ydCB7IExvZ05vZGUgfSBmcm9tIFwiLi4vLi4vbG9nZ2VyL2xvZy1ub2RlXCJcblxuZXhwb3J0IGZ1bmN0aW9uIHByZXBhcmVOZXdNb2R1bGVDb25maWcobmFtZTogc3RyaW5nLCB0eXBlOiBNb2R1bGVUeXBlLCBwYXRoOiBzdHJpbmcpOiBNb2R1bGVDb25maWdPcHRzIHtcbiAgY29uc3QgbW9kdWxlVHlwZVRlbXBsYXRlID0ge1xuICAgIGNvbnRhaW5lcjogY29udGFpbmVyVGVtcGxhdGUsXG4gICAgXCJnb29nbGUtY2xvdWQtZnVuY3Rpb25cIjogZ29vZ2xlQ2xvdWRGdW5jdGlvblRlbXBsYXRlLFxuICAgIFwibnBtLXBhY2thZ2VcIjogbnBtUGFja2FnZVRlbXBsYXRlLFxuICB9W3R5cGVdXG4gIHJldHVybiB7XG4gICAgbmFtZSxcbiAgICB0eXBlLFxuICAgIHBhdGgsXG4gICAgY29uZmlnOiB7XG4gICAgICBtb2R1bGU6IHtcbiAgICAgICAgLi4ubW9kdWxlVGVtcGxhdGUobmFtZSwgdHlwZSksXG4gICAgICAgIC4uLm1vZHVsZVR5cGVUZW1wbGF0ZShuYW1lKSxcbiAgICAgIH0sXG4gICAgfSxcbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZHVtcENvbmZpZyhjb25maWdPcHRzOiBDb25maWdPcHRzLCBzY2hlbWE6IEpvaS5TY2hlbWEsIGxvZ2dlcjogTG9nTm9kZSkge1xuICBjb25zdCB7IGNvbmZpZywgbmFtZSwgcGF0aCB9ID0gY29uZmlnT3B0c1xuICBjb25zdCB5YW1sUGF0aCA9IGpvaW4ocGF0aCwgTU9EVUxFX0NPTkZJR19GSUxFTkFNRSlcbiAgY29uc3QgdGFzayA9IGxvZ2dlci5pbmZvKHtcbiAgICBtc2c6IGBXcml0aW5nIGNvbmZpZyBmb3IgJHtuYW1lfWAsXG4gICAgc3RhdHVzOiBcImFjdGl2ZVwiLFxuICB9KVxuXG4gIGlmIChhd2FpdCBwYXRoRXhpc3RzKHlhbWxQYXRoKSkge1xuICAgIHRhc2suc2V0V2Fybih7IG1zZzogYEdhcmRlbiBjb25maWcgZmlsZSBhbHJlYWR5IGV4aXN0cyBhdCBwYXRoLCBza2lwcGluZ2AsIGFwcGVuZDogdHJ1ZSB9KVxuICAgIHJldHVyblxuICB9XG5cbiAgdHJ5IHtcbiAgICB2YWxpZGF0ZShjb25maWcsIHNjaGVtYSlcbiAgICBhd2FpdCBkdW1wWWFtbCh5YW1sUGF0aCwgY29uZmlnKVxuICAgIHRhc2suc2V0U3VjY2VzcygpXG4gIH0gY2F0Y2ggKGVycikge1xuICAgIHRhc2suc2V0RXJyb3IoeyBtc2c6IGBHZW5lcmF0ZWQgY29uZmlnIGlzIGludmFsaWQsIHNraXBwaW5nYCwgYXBwZW5kOiB0cnVlIH0pXG4gICAgdGhyb3cgZXJyXG4gIH1cbn1cbiJdfQ==
diff --git a/garden-service/build/commands/create/module.d.ts b/garden-service/build/commands/create/module.d.ts
new file mode 100644
index 00000000000..0c254266e9b
--- /dev/null
+++ b/garden-service/build/commands/create/module.d.ts
@@ -0,0 +1,33 @@
+import { Command, CommandResult, StringParameter, ChoicesParameter, CommandParams } from "../base";
+import { ModuleConfigOpts } from "./config-templates";
+declare const createModuleOptions: {
+ name: StringParameter;
+ type: ChoicesParameter;
+};
+declare const createModuleArguments: {
+ "module-dir": StringParameter;
+};
+declare type Args = typeof createModuleArguments;
+declare type Opts = typeof createModuleOptions;
+interface CreateModuleResult extends CommandResult {
+ result: {
+ module?: ModuleConfigOpts;
+ };
+}
+export declare class CreateModuleCommand extends Command {
+ name: string;
+ alias: string;
+ help: string;
+ description: string;
+ noProject: boolean;
+ arguments: {
+ "module-dir": StringParameter;
+ };
+ options: {
+ name: StringParameter;
+ type: ChoicesParameter;
+ };
+ action({ garden, args, opts }: CommandParams): Promise;
+}
+export {};
+//# sourceMappingURL=module.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/create/module.js b/garden-service/build/commands/create/module.js
new file mode 100644
index 00000000000..7732a805cc2
--- /dev/null
+++ b/garden-service/build/commands/create/module.js
@@ -0,0 +1,103 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const path_1 = require("path");
+const dedent = require("dedent");
+const base_1 = require("../base");
+const exceptions_1 = require("../../exceptions");
+const config_templates_1 = require("./config-templates");
+const helpers_1 = require("./helpers");
+const prompts_1 = require("./prompts");
+const common_1 = require("../../config/common");
+const fs_extra_1 = require("fs-extra");
+const createModuleOptions = {
+ name: new base_1.StringParameter({
+ help: "Assigns a custom name to the module. (Defaults to name of the current directory.)",
+ }),
+ type: new base_1.ChoicesParameter({
+ help: "Type of module.",
+ choices: config_templates_1.availableModuleTypes,
+ }),
+};
+const createModuleArguments = {
+ "module-dir": new base_1.StringParameter({
+ help: "Directory of the module. (Defaults to current directory.)",
+ }),
+};
+class CreateModuleCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "module";
+ this.alias = "m";
+ this.help = "Creates a new Garden module.";
+ this.description = dedent `
+ Creates a new Garden module of the given type
+
+ Examples:
+
+ garden create module # creates a new module in the current directory (module name defaults to directory name)
+ garden create module my-module # creates a new module in my-module directory
+ garden create module --type=container # creates a new container module
+ garden create module --name=my-module # creates a new module in current directory and names it my-module
+ `;
+ this.noProject = true;
+ this.arguments = createModuleArguments;
+ this.options = createModuleOptions;
+ }
+ action({ garden, args, opts }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ let errors = [];
+ const moduleRoot = path_1.join(garden.projectRoot, (args["module-dir"] || "").trim());
+ const moduleName = common_1.validate(opts.name || path_1.basename(moduleRoot), common_1.joiIdentifier(), { context: "module name" });
+ yield fs_extra_1.ensureDir(moduleRoot);
+ garden.log.header({ emoji: "house_with_garden", command: "create" });
+ garden.log.info(`Initializing new module ${moduleName}`);
+ let type;
+ if (opts.type) {
+ // Type passed as parameter
+ type = opts.type;
+ if (!config_templates_1.availableModuleTypes.includes(type)) {
+ throw new exceptions_1.ParameterError("Module type not available", {});
+ }
+ }
+ else {
+ // Prompt for type
+ garden.log.info("---------");
+ garden.log.stop();
+ type = (yield prompts_1.prompts.addConfigForModule(moduleName)).type;
+ garden.log.info("---------");
+ if (!type) {
+ return { result: {} };
+ }
+ }
+ const module = helpers_1.prepareNewModuleConfig(moduleName, type, moduleRoot);
+ try {
+ yield helpers_1.dumpConfig(module, config_templates_1.moduleSchema, garden.log);
+ }
+ catch (err) {
+ errors.push(err);
+ }
+ return {
+ result: { module },
+ errors,
+ };
+ });
+ }
+}
+exports.CreateModuleCommand = CreateModuleCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2NyZWF0ZS9tb2R1bGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7R0FNRzs7Ozs7Ozs7OztBQUVILCtCQUFxQztBQUNyQyxpQ0FBaUM7QUFFakMsa0NBTWdCO0FBQ2hCLGlEQUFrRTtBQUNsRSx5REFBcUc7QUFDckcsdUNBR2tCO0FBQ2xCLHVDQUFtQztBQUNuQyxnREFBNkQ7QUFDN0QsdUNBQW9DO0FBRXBDLE1BQU0sbUJBQW1CLEdBQUc7SUFDMUIsSUFBSSxFQUFFLElBQUksc0JBQWUsQ0FBQztRQUN4QixJQUFJLEVBQUUsbUZBQW1GO0tBQzFGLENBQUM7SUFDRixJQUFJLEVBQUUsSUFBSSx1QkFBZ0IsQ0FBQztRQUN6QixJQUFJLEVBQUUsaUJBQWlCO1FBQ3ZCLE9BQU8sRUFBRSx1Q0FBb0I7S0FDOUIsQ0FBQztDQUNILENBQUE7QUFFRCxNQUFNLHFCQUFxQixHQUFHO0lBQzVCLFlBQVksRUFBRSxJQUFJLHNCQUFlLENBQUM7UUFDaEMsSUFBSSxFQUFFLDJEQUEyRDtLQUNsRSxDQUFDO0NBQ0gsQ0FBQTtBQVdELE1BQWEsbUJBQW9CLFNBQVEsY0FBbUI7SUFBNUQ7O1FBQ0UsU0FBSSxHQUFHLFFBQVEsQ0FBQTtRQUNmLFVBQUssR0FBRyxHQUFHLENBQUE7UUFDWCxTQUFJLEdBQUcsOEJBQThCLENBQUE7UUFFckMsZ0JBQVcsR0FBRyxNQUFNLENBQUE7Ozs7Ozs7OztHQVNuQixDQUFBO1FBRUQsY0FBUyxHQUFHLElBQUksQ0FBQTtRQUNoQixjQUFTLEdBQUcscUJBQXFCLENBQUE7UUFDakMsWUFBTyxHQUFHLG1CQUFtQixDQUFBO0lBK0MvQixDQUFDO0lBN0NPLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUE2Qjs7WUFDNUQsSUFBSSxNQUFNLEdBQXNCLEVBQUUsQ0FBQTtZQUVsQyxNQUFNLFVBQVUsR0FBRyxXQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsRUFBRSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFBO1lBQzlFLE1BQU0sVUFBVSxHQUFHLGlCQUFRLENBQ3pCLElBQUksQ0FBQyxJQUFJLElBQUksZUFBUSxDQUFDLFVBQVUsQ0FBQyxFQUNqQyxzQkFBYSxFQUFFLEVBQ2YsRUFBRSxPQUFPLEVBQUUsYUFBYSxFQUFFLENBQzNCLENBQUE7WUFFRCxNQUFNLG9CQUFTLENBQUMsVUFBVSxDQUFDLENBQUE7WUFFM0IsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsRUFBRSxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUE7WUFDcEUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsMkJBQTJCLFVBQVUsRUFBRSxDQUFDLENBQUE7WUFFeEQsSUFBSSxJQUFnQixDQUFBO1lBRXBCLElBQUksSUFBSSxDQUFDLElBQUksRUFBRTtnQkFDYiwyQkFBMkI7Z0JBQzNCLElBQUksR0FBZSxJQUFJLENBQUMsSUFBSSxDQUFBO2dCQUM1QixJQUFJLENBQUMsdUNBQW9CLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFO29CQUN4QyxNQUFNLElBQUksMkJBQWMsQ0FBQywyQkFBMkIsRUFBRSxFQUFFLENBQUMsQ0FBQTtpQkFDMUQ7YUFDRjtpQkFBTTtnQkFDTCxrQkFBa0I7Z0JBQ2xCLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFBO2dCQUM1QixNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFBO2dCQUNqQixJQUFJLEdBQUcsQ0FBQyxNQUFNLGlCQUFPLENBQUMsa0JBQWtCLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUE7Z0JBQzFELE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFBO2dCQUM1QixJQUFJLENBQUMsSUFBSSxFQUFFO29CQUNULE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLENBQUE7aUJBQ3RCO2FBQ0Y7WUFFRCxNQUFNLE1BQU0sR0FBRyxnQ0FBc0IsQ0FBQyxVQUFVLEVBQUUsSUFBSSxFQUFFLFVBQVUsQ0FBQyxDQUFBO1lBQ25FLElBQUk7Z0JBQ0YsTUFBTSxvQkFBVSxDQUFDLE1BQU0sRUFBRSwrQkFBWSxFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQTthQUNuRDtZQUFDLE9BQU8sR0FBRyxFQUFFO2dCQUNaLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUE7YUFDakI7WUFDRCxPQUFPO2dCQUNMLE1BQU0sRUFBRSxFQUFFLE1BQU0sRUFBRTtnQkFDbEIsTUFBTTthQUNQLENBQUE7UUFDSCxDQUFDO0tBQUE7Q0FDRjtBQWpFRCxrREFpRUMiLCJmaWxlIjoiY29tbWFuZHMvY3JlYXRlL21vZHVsZS5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgeyBiYXNlbmFtZSwgam9pbiB9IGZyb20gXCJwYXRoXCJcbmltcG9ydCBkZWRlbnQgPSByZXF1aXJlKFwiZGVkZW50XCIpXG5cbmltcG9ydCB7XG4gIENvbW1hbmQsXG4gIENvbW1hbmRSZXN1bHQsXG4gIFN0cmluZ1BhcmFtZXRlcixcbiAgQ2hvaWNlc1BhcmFtZXRlcixcbiAgQ29tbWFuZFBhcmFtcyxcbn0gZnJvbSBcIi4uL2Jhc2VcIlxuaW1wb3J0IHsgUGFyYW1ldGVyRXJyb3IsIEdhcmRlbkJhc2VFcnJvciB9IGZyb20gXCIuLi8uLi9leGNlcHRpb25zXCJcbmltcG9ydCB7IGF2YWlsYWJsZU1vZHVsZVR5cGVzLCBNb2R1bGVUeXBlLCBtb2R1bGVTY2hlbWEsIE1vZHVsZUNvbmZpZ09wdHMgfSBmcm9tIFwiLi9jb25maWctdGVtcGxhdGVzXCJcbmltcG9ydCB7XG4gIHByZXBhcmVOZXdNb2R1bGVDb25maWcsXG4gIGR1bXBDb25maWcsXG59IGZyb20gXCIuL2hlbHBlcnNcIlxuaW1wb3J0IHsgcHJvbXB0cyB9IGZyb20gXCIuL3Byb21wdHNcIlxuaW1wb3J0IHsgdmFsaWRhdGUsIGpvaUlkZW50aWZpZXIgfSBmcm9tIFwiLi4vLi4vY29uZmlnL2NvbW1vblwiXG5pbXBvcnQgeyBlbnN1cmVEaXIgfSBmcm9tIFwiZnMtZXh0cmFcIlxuXG5jb25zdCBjcmVhdGVNb2R1bGVPcHRpb25zID0ge1xuICBuYW1lOiBuZXcgU3RyaW5nUGFyYW1ldGVyKHtcbiAgICBoZWxwOiBcIkFzc2lnbnMgYSBjdXN0b20gbmFtZSB0byB0aGUgbW9kdWxlLiAoRGVmYXVsdHMgdG8gbmFtZSBvZiB0aGUgY3VycmVudCBkaXJlY3RvcnkuKVwiLFxuICB9KSxcbiAgdHlwZTogbmV3IENob2ljZXNQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiVHlwZSBvZiBtb2R1bGUuXCIsXG4gICAgY2hvaWNlczogYXZhaWxhYmxlTW9kdWxlVHlwZXMsXG4gIH0pLFxufVxuXG5jb25zdCBjcmVhdGVNb2R1bGVBcmd1bWVudHMgPSB7XG4gIFwibW9kdWxlLWRpclwiOiBuZXcgU3RyaW5nUGFyYW1ldGVyKHtcbiAgICBoZWxwOiBcIkRpcmVjdG9yeSBvZiB0aGUgbW9kdWxlLiAoRGVmYXVsdHMgdG8gY3VycmVudCBkaXJlY3RvcnkuKVwiLFxuICB9KSxcbn1cblxudHlwZSBBcmdzID0gdHlwZW9mIGNyZWF0ZU1vZHVsZUFyZ3VtZW50c1xudHlwZSBPcHRzID0gdHlwZW9mIGNyZWF0ZU1vZHVsZU9wdGlvbnNcblxuaW50ZXJmYWNlIENyZWF0ZU1vZHVsZVJlc3VsdCBleHRlbmRzIENvbW1hbmRSZXN1bHQge1xuICByZXN1bHQ6IHtcbiAgICBtb2R1bGU/OiBNb2R1bGVDb25maWdPcHRzLFxuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBDcmVhdGVNb2R1bGVDb21tYW5kIGV4dGVuZHMgQ29tbWFuZDxBcmdzLCBPcHRzPiB7XG4gIG5hbWUgPSBcIm1vZHVsZVwiXG4gIGFsaWFzID0gXCJtXCJcbiAgaGVscCA9IFwiQ3JlYXRlcyBhIG5ldyBHYXJkZW4gbW9kdWxlLlwiXG5cbiAgZGVzY3JpcHRpb24gPSBkZWRlbnRgXG4gICAgQ3JlYXRlcyBhIG5ldyBHYXJkZW4gbW9kdWxlIG9mIHRoZSBnaXZlbiB0eXBlXG5cbiAgICBFeGFtcGxlczpcblxuICAgICAgICBnYXJkZW4gY3JlYXRlIG1vZHVsZSAjIGNyZWF0ZXMgYSBuZXcgbW9kdWxlIGluIHRoZSBjdXJyZW50IGRpcmVjdG9yeSAobW9kdWxlIG5hbWUgZGVmYXVsdHMgdG8gZGlyZWN0b3J5IG5hbWUpXG4gICAgICAgIGdhcmRlbiBjcmVhdGUgbW9kdWxlIG15LW1vZHVsZSAjIGNyZWF0ZXMgYSBuZXcgbW9kdWxlIGluIG15LW1vZHVsZSBkaXJlY3RvcnlcbiAgICAgICAgZ2FyZGVuIGNyZWF0ZSBtb2R1bGUgLS10eXBlPWNvbnRhaW5lciAjIGNyZWF0ZXMgYSBuZXcgY29udGFpbmVyIG1vZHVsZVxuICAgICAgICBnYXJkZW4gY3JlYXRlIG1vZHVsZSAtLW5hbWU9bXktbW9kdWxlICMgY3JlYXRlcyBhIG5ldyBtb2R1bGUgaW4gY3VycmVudCBkaXJlY3RvcnkgYW5kIG5hbWVzIGl0IG15LW1vZHVsZVxuICBgXG5cbiAgbm9Qcm9qZWN0ID0gdHJ1ZVxuICBhcmd1bWVudHMgPSBjcmVhdGVNb2R1bGVBcmd1bWVudHNcbiAgb3B0aW9ucyA9IGNyZWF0ZU1vZHVsZU9wdGlvbnNcblxuICBhc3luYyBhY3Rpb24oeyBnYXJkZW4sIGFyZ3MsIG9wdHMgfTogQ29tbWFuZFBhcmFtczxBcmdzLCBPcHRzPik6IFByb21pc2U8Q3JlYXRlTW9kdWxlUmVzdWx0PiB7XG4gICAgbGV0IGVycm9yczogR2FyZGVuQmFzZUVycm9yW10gPSBbXVxuXG4gICAgY29uc3QgbW9kdWxlUm9vdCA9IGpvaW4oZ2FyZGVuLnByb2plY3RSb290LCAoYXJnc1tcIm1vZHVsZS1kaXJcIl0gfHwgXCJcIikudHJpbSgpKVxuICAgIGNvbnN0IG1vZHVsZU5hbWUgPSB2YWxpZGF0ZShcbiAgICAgIG9wdHMubmFtZSB8fCBiYXNlbmFtZShtb2R1bGVSb290KSxcbiAgICAgIGpvaUlkZW50aWZpZXIoKSxcbiAgICAgIHsgY29udGV4dDogXCJtb2R1bGUgbmFtZVwiIH0sXG4gICAgKVxuXG4gICAgYXdhaXQgZW5zdXJlRGlyKG1vZHVsZVJvb3QpXG5cbiAgICBnYXJkZW4ubG9nLmhlYWRlcih7IGVtb2ppOiBcImhvdXNlX3dpdGhfZ2FyZGVuXCIsIGNvbW1hbmQ6IFwiY3JlYXRlXCIgfSlcbiAgICBnYXJkZW4ubG9nLmluZm8oYEluaXRpYWxpemluZyBuZXcgbW9kdWxlICR7bW9kdWxlTmFtZX1gKVxuXG4gICAgbGV0IHR5cGU6IE1vZHVsZVR5cGVcblxuICAgIGlmIChvcHRzLnR5cGUpIHtcbiAgICAgIC8vIFR5cGUgcGFzc2VkIGFzIHBhcmFtZXRlclxuICAgICAgdHlwZSA9IDxNb2R1bGVUeXBlPm9wdHMudHlwZVxuICAgICAgaWYgKCFhdmFpbGFibGVNb2R1bGVUeXBlcy5pbmNsdWRlcyh0eXBlKSkge1xuICAgICAgICB0aHJvdyBuZXcgUGFyYW1ldGVyRXJyb3IoXCJNb2R1bGUgdHlwZSBub3QgYXZhaWxhYmxlXCIsIHt9KVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBQcm9tcHQgZm9yIHR5cGVcbiAgICAgIGdhcmRlbi5sb2cuaW5mbyhcIi0tLS0tLS0tLVwiKVxuICAgICAgZ2FyZGVuLmxvZy5zdG9wKClcbiAgICAgIHR5cGUgPSAoYXdhaXQgcHJvbXB0cy5hZGRDb25maWdGb3JNb2R1bGUobW9kdWxlTmFtZSkpLnR5cGVcbiAgICAgIGdhcmRlbi5sb2cuaW5mbyhcIi0tLS0tLS0tLVwiKVxuICAgICAgaWYgKCF0eXBlKSB7XG4gICAgICAgIHJldHVybiB7IHJlc3VsdDoge30gfVxuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IG1vZHVsZSA9IHByZXBhcmVOZXdNb2R1bGVDb25maWcobW9kdWxlTmFtZSwgdHlwZSwgbW9kdWxlUm9vdClcbiAgICB0cnkge1xuICAgICAgYXdhaXQgZHVtcENvbmZpZyhtb2R1bGUsIG1vZHVsZVNjaGVtYSwgZ2FyZGVuLmxvZylcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGVycm9ycy5wdXNoKGVycilcbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgIHJlc3VsdDogeyBtb2R1bGUgfSxcbiAgICAgIGVycm9ycyxcbiAgICB9XG4gIH1cbn1cbiJdfQ==
diff --git a/garden-service/build/commands/create/project.d.ts b/garden-service/build/commands/create/project.d.ts
new file mode 100644
index 00000000000..5d049665c62
--- /dev/null
+++ b/garden-service/build/commands/create/project.d.ts
@@ -0,0 +1,34 @@
+import { Command, CommandParams, CommandResult, StringParameter, PathsParameter } from "../base";
+import { ModuleConfigOpts, ProjectConfigOpts } from "./config-templates";
+declare const createProjectOptions: {
+ "module-dirs": PathsParameter;
+ name: StringParameter;
+};
+declare const createProjectArguments: {
+ "project-dir": StringParameter;
+};
+declare type Args = typeof createProjectArguments;
+declare type Opts = typeof createProjectOptions;
+interface CreateProjectResult extends CommandResult {
+ result: {
+ projectConfig: ProjectConfigOpts;
+ moduleConfigs: ModuleConfigOpts[];
+ };
+}
+export declare class CreateProjectCommand extends Command {
+ name: string;
+ alias: string;
+ help: string;
+ description: string;
+ noProject: boolean;
+ arguments: {
+ "project-dir": StringParameter;
+ };
+ options: {
+ "module-dirs": PathsParameter;
+ name: StringParameter;
+ };
+ action({ garden, args, opts }: CommandParams): Promise;
+}
+export {};
+//# sourceMappingURL=project.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/create/project.js b/garden-service/build/commands/create/project.js
new file mode 100644
index 00000000000..80a8107d11a
--- /dev/null
+++ b/garden-service/build/commands/create/project.js
@@ -0,0 +1,143 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const path_1 = require("path");
+const fs_extra_1 = require("fs-extra");
+const Bluebird = require("bluebird");
+const dedent = require("dedent");
+const terminalLink = require("terminal-link");
+const base_1 = require("../base");
+const helpers_1 = require("./helpers");
+const prompts_1 = require("./prompts");
+const config_templates_1 = require("./config-templates");
+const util_1 = require("../../util/util");
+const common_1 = require("../../config/common");
+const project_1 = require("../../config/project");
+const createProjectOptions = {
+ "module-dirs": new base_1.PathsParameter({
+ help: "Relative path to modules directory. Use comma as a separator to specify multiple directories",
+ }),
+ name: new base_1.StringParameter({
+ help: "Assigns a custom name to the project. (Defaults to name of the current directory.)",
+ }),
+};
+const createProjectArguments = {
+ "project-dir": new base_1.StringParameter({
+ help: "Directory of the project. (Defaults to current directory.)",
+ }),
+};
+const flatten = (acc, val) => acc.concat(val);
+class CreateProjectCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "project";
+ this.alias = "p";
+ this.help = "Creates a new Garden project.";
+ this.description = dedent `
+ The 'create project' command walks the user through setting up a new Garden project and
+ generates scaffolding based on user input.
+
+ Examples:
+
+ garden create project # creates a new Garden project in the current directory (project name defaults to
+ directory name)
+ garden create project my-project # creates a new Garden project in my-project directory
+ garden create project --module-dirs=path/to/modules1,path/to/modules2
+ # creates a new Garden project and looks for pre-existing modules in the modules1 and modules2 directories
+ garden create project --name my-project
+ # creates a new Garden project in the current directory and names it my-project
+ `;
+ this.noProject = true;
+ this.arguments = createProjectArguments;
+ this.options = createProjectOptions;
+ }
+ action({ garden, args, opts }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ let moduleConfigs = [];
+ let errors = [];
+ const projectRoot = args["project-dir"] ? path_1.join(garden.projectRoot, args["project-dir"].trim()) : garden.projectRoot;
+ const moduleParentDirs = yield Bluebird.map(opts["module-dirs"] || [], (dir) => path_1.resolve(projectRoot, dir));
+ const projectName = common_1.validate(opts.name || path_1.basename(projectRoot), common_1.joiIdentifier(), { context: "project name" });
+ yield fs_extra_1.ensureDir(projectRoot);
+ garden.log.header({ emoji: "house_with_garden", command: "create" });
+ garden.log.info(`Initializing new Garden project ${projectName}`);
+ garden.log.info("---------");
+ // Stop logger while prompting
+ garden.log.stop();
+ if (moduleParentDirs.length > 0) {
+ // If module-dirs option provided we scan for modules in the parent dir(s) and add them one by one
+ moduleConfigs = (yield Bluebird.mapSeries(moduleParentDirs, (parentDir) => __awaiter(this, void 0, void 0, function* () {
+ const moduleNames = yield util_1.getChildDirNames(parentDir);
+ return Bluebird.reduce(moduleNames, (acc, moduleName) => __awaiter(this, void 0, void 0, function* () {
+ const { type } = yield prompts_1.prompts.addConfigForModule(moduleName);
+ if (type) {
+ acc.push(helpers_1.prepareNewModuleConfig(moduleName, type, path_1.join(parentDir, moduleName)));
+ }
+ return acc;
+ }), []);
+ })))
+ .reduce(flatten, [])
+ .filter(m => m);
+ }
+ else {
+ // Otherwise we prompt the user for modules to add
+ moduleConfigs = (yield prompts_1.prompts.repeatAddModule())
+ .map(({ name, type }) => helpers_1.prepareNewModuleConfig(name, type, path_1.join(projectRoot, name)));
+ }
+ garden.log.info("---------");
+ const taskLog = garden.log.info({ msg: "Setting up project", status: "active" });
+ for (const module of moduleConfigs) {
+ yield fs_extra_1.ensureDir(module.path);
+ try {
+ yield helpers_1.dumpConfig(module, config_templates_1.moduleSchema, garden.log);
+ }
+ catch (err) {
+ errors.push(err);
+ }
+ }
+ const projectConfig = {
+ path: projectRoot,
+ name: projectName,
+ config: config_templates_1.projectTemplate(projectName, moduleConfigs.map(module => module.type)),
+ };
+ try {
+ yield helpers_1.dumpConfig(projectConfig, project_1.projectSchema, garden.log);
+ }
+ catch (err) {
+ errors.push(err);
+ }
+ if (errors.length === 0) {
+ taskLog.setSuccess();
+ }
+ else {
+ taskLog.setWarn({ msg: "Finished with errors", append: true });
+ }
+ const docs = terminalLink("docs", "https://docs.garden.io");
+ garden.log.info(`Project created! Be sure to check out our ${docs} for how to get sarted!`);
+ return {
+ result: {
+ moduleConfigs,
+ projectConfig,
+ },
+ errors,
+ };
+ });
+ }
+}
+exports.CreateProjectCommand = CreateProjectCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2NyZWF0ZS9wcm9qZWN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7QUFFSCwrQkFBOEM7QUFDOUMsdUNBQW9DO0FBQ3BDLHFDQUFxQztBQUNyQyxpQ0FBaUM7QUFDakMsOENBQThDO0FBRTlDLGtDQU1nQjtBQUVoQix1Q0FHa0I7QUFDbEIsdUNBQW1DO0FBQ25DLHlEQUsyQjtBQUMzQiwwQ0FBa0Q7QUFDbEQsZ0RBQTZEO0FBQzdELGtEQUFvRDtBQUVwRCxNQUFNLG9CQUFvQixHQUFHO0lBQzNCLGFBQWEsRUFBRSxJQUFJLHFCQUFjLENBQUM7UUFDaEMsSUFBSSxFQUFFLDhGQUE4RjtLQUNyRyxDQUFDO0lBQ0YsSUFBSSxFQUFFLElBQUksc0JBQWUsQ0FBQztRQUN4QixJQUFJLEVBQUUsb0ZBQW9GO0tBQzNGLENBQUM7Q0FDSCxDQUFBO0FBRUQsTUFBTSxzQkFBc0IsR0FBRztJQUM3QixhQUFhLEVBQUUsSUFBSSxzQkFBZSxDQUFDO1FBQ2pDLElBQUksRUFBRSw0REFBNEQ7S0FDbkUsQ0FBQztDQUNILENBQUE7QUFLRCxNQUFNLE9BQU8sR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUE7QUFTN0MsTUFBYSxvQkFBcUIsU0FBUSxjQUFtQjtJQUE3RDs7UUFDRSxTQUFJLEdBQUcsU0FBUyxDQUFBO1FBQ2hCLFVBQUssR0FBRyxHQUFHLENBQUE7UUFDWCxTQUFJLEdBQUcsK0JBQStCLENBQUE7UUFFdEMsZ0JBQVcsR0FBRyxNQUFNLENBQUE7Ozs7Ozs7Ozs7Ozs7R0FhbkIsQ0FBQTtRQUVELGNBQVMsR0FBRyxJQUFJLENBQUE7UUFDaEIsY0FBUyxHQUFHLHNCQUFzQixDQUFBO1FBQ2xDLFlBQU8sR0FBRyxvQkFBb0IsQ0FBQTtJQW9GaEMsQ0FBQztJQWxGTyxNQUFNLENBQUMsRUFBRSxNQUFNLEVBQUUsSUFB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eUIsQ0FBQyxDQUFBO1lBRTNGLE9BQU87Z0JBQ0wsTUFBTSxFQUFFO29CQUNOLGFBQWE7b0JBQ2IsYUFBYTtpQkFDZDtnQkFDRCxNQUFNO2FBQ1AsQ0FBQTtRQUNILENBQUM7S0FBQTtDQUNGO0FBMUdELG9EQTBHQyIsImZpbGUiOiJjb21tYW5kcy9jcmVhdGUvcHJvamVjdC5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgeyByZXNvbHZlLCBqb2luLCBiYXNlbmFtZSB9IGZyb20gXCJwYXRoXCJcbmltcG9ydCB7IGVuc3VyZURpciB9IGZyb20gXCJmcy1leHRyYVwiXG5pbXBvcnQgQmx1ZWJpcmQgPSByZXF1aXJlKFwiYmx1ZWJpcmRcIilcbmltcG9ydCBkZWRlbnQgPSByZXF1aXJlKFwiZGVkZW50XCIpXG5pbXBvcnQgdGVybWluYWxMaW5rID0gcmVxdWlyZShcInRlcm1pbmFsLWxpbmtcIilcblxuaW1wb3J0IHtcbiAgQ29tbWFuZCxcbiAgQ29tbWFuZFBhcmFtcyxcbiAgQ29tbWFuZFJlc3VsdCxcbiAgU3RyaW5nUGFyYW1ldGVyLFxuICBQYXRoc1BhcmFtZXRlcixcbn0gZnJvbSBcIi4uL2Jhc2VcIlxuaW1wb3J0IHsgR2FyZGVuQmFzZUVycm9yIH0gZnJvbSBcIi4uLy4uL2V4Y2VwdGlvbnNcIlxuaW1wb3J0IHtcbiAgcHJlcGFyZU5ld01vZHVsZUNvbmZpZyxcbiAgZHVtcENvbmZpZyxcbn0gZnJvbSBcIi4vaGVscGVyc1wiXG5pbXBvcnQgeyBwcm9tcHRzIH0gZnJvbSBcIi4vcHJvbXB0c1wiXG5pbXBvcnQge1xuICBwcm9qZWN0VGVtcGxhdGUsXG4gIE1vZHVsZUNvbmZpZ09wdHMsXG4gIFByb2plY3RDb25maWdPcHRzLFxuICBtb2R1bGVTY2hlbWEsXG59IGZyb20gXCIuL2NvbmZpZy10ZW1wbGF0ZXNcIlxuaW1wb3J0IHsgZ2V0Q2hpbGREaXJOYW1lcyB9IGZyb20gXCIuLi8uLi91dGlsL3V0aWxcIlxuaW1wb3J0IHsgdmFsaWRhdGUsIGpvaUlkZW50aWZpZXIgfSBmcm9tIFwiLi4vLi4vY29uZmlnL2NvbW1vblwiXG5pbXBvcnQgeyBwcm9qZWN0U2NoZW1hIH0gZnJvbSBcIi4uLy4uL2NvbmZpZy9wcm9qZWN0XCJcblxuY29uc3QgY3JlYXRlUHJvamVjdE9wdGlvbnMgPSB7XG4gIFwibW9kdWxlLWRpcnNcIjogbmV3IFBhdGhzUGFyYW1ldGVyKHtcbiAgICBoZWxwOiBcIlJlbGF0aXZlIHBhdGggdG8gbW9kdWxlcyBkaXJlY3RvcnkuIFVzZSBjb21tYSBhcyBhIHNlcGFyYXRvciB0byBzcGVjaWZ5IG11bHRpcGxlIGRpcmVjdG9yaWVzXCIsXG4gIH0pLFxuICBuYW1lOiBuZXcgU3RyaW5nUGFyYW1ldGVyKHtcbiAgICBoZWxwOiBcIkFzc2lnbnMgYSBjdXN0b20gbmFtZSB0byB0aGUgcHJvamVjdC4gKERlZmF1bHRzIHRvIG5hbWUgb2YgdGhlIGN1cnJlbnQgZGlyZWN0b3J5LilcIixcbiAgfSksXG59XG5cbmNvbnN0IGNyZWF0ZVByb2plY3RBcmd1bWVudHMgPSB7XG4gIFwicHJvamVjdC1kaXJcIjogbmV3IFN0cmluZ1BhcmFtZXRlcih7XG4gICAgaGVscDogXCJEaXJlY3Rvcnkgb2YgdGhlIHByb2plY3QuIChEZWZhdWx0cyB0byBjdXJyZW50IGRpcmVjdG9yeS4pXCIsXG4gIH0pLFxufVxuXG50eXBlIEFyZ3MgPSB0eXBlb2YgY3JlYXRlUHJvamVjdEFyZ3VtZW50c1xudHlwZSBPcHRzID0gdHlwZW9mIGNyZWF0ZVByb2plY3RPcHRpb25zXG5cbmNvbnN0IGZsYXR0ZW4gPSAoYWNjLCB2YWwpID0+IGFjYy5jb25jYXQodmFsKVxuXG5pbnRlcmZhY2UgQ3JlYXRlUHJvamVjdFJlc3VsdCBleHRlbmRzIENvbW1hbmRSZXN1bHQge1xuICByZXN1bHQ6IHtcbiAgICBwcm9qZWN0Q29uZmlnOiBQcm9qZWN0Q29uZmlnT3B0cyxcbiAgICBtb2R1bGVDb25maWdzOiBNb2R1bGVDb25maWdPcHRzW10sXG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIENyZWF0ZVByb2plY3RDb21tYW5kIGV4dGVuZHMgQ29tbWFuZDxBcmdzLCBPcHRzPiB7XG4gIG5hbWUgPSBcInByb2plY3RcIlxuICBhbGlhcyA9IFwicFwiXG4gIGhlbHAgPSBcIkNyZWF0ZXMgYSBuZXcgR2FyZGVuIHByb2plY3QuXCJcblxuICBkZXNjcmlwdGlvbiA9IGRlZGVudGBcbiAgICBUaGUgJ2NyZWF0ZSBwcm9qZWN0JyBjb21tYW5kIHdhbGtzIHRoZSB1c2VyIHRocm91Z2ggc2V0dGluZyB1cCBhIG5ldyBHYXJkZW4gcHJvamVjdCBhbmRcbiAgICBnZW5lcmF0ZXMgc2NhZmZvbGRpbmcgYmFzZWQgb24gdXNlciBpbnB1dC5cblxuICAgIEV4YW1wbGVzOlxuXG4gICAgICAgIGdhcmRlbiBjcmVhdGUgcHJvamVjdCAjIGNyZWF0ZXMgYSBuZXcgR2FyZGVuIHByb2plY3QgaW4gdGhlIGN1cnJlbnQgZGlyZWN0b3J5IChwcm9qZWN0IG5hbWUgZGVmYXVsdHMgdG9cbiAgICAgICAgZGlyZWN0b3J5IG5hbWUpXG4gICAgICAgIGdhcmRlbiBjcmVhdGUgcHJvamVjdCBteS1wcm9qZWN0ICMgY3JlYXRlcyBhIG5ldyBHYXJkZW4gcHJvamVjdCBpbiBteS1wcm9qZWN0IGRpcmVjdG9yeVxuICAgICAgICBnYXJkZW4gY3JlYXRlIHByb2plY3QgLS1tb2R1bGUtZGlycz1wYXRoL3RvL21vZHVsZXMxLHBhdGgvdG8vbW9kdWxlczJcbiAgICAgICAgIyBjcmVhdGVzIGEgbmV3IEdhcmRlbiBwcm9qZWN0IGFuZCBsb29rcyBmb3IgcHJlLWV4aXN0aW5nIG1vZHVsZXMgaW4gdGhlIG1vZHVsZXMxIGFuZCBtb2R1bGVzMiBkaXJlY3Rvcmllc1xuICAgICAgICBnYXJkZW4gY3JlYXRlIHByb2plY3QgLS1uYW1lIG15LXByb2plY3RcbiAgICAgICAgIyBjcmVhdGVzIGEgbmV3IEdhcmRlbiBwcm9qZWN0IGluIHRoZSBjdXJyZW50IGRpcmVjdG9yeSBhbmQgbmFtZXMgaXQgbXktcHJvamVjdFxuICBgXG5cbiAgbm9Qcm9qZWN0ID0gdHJ1ZVxuICBhcmd1bWVudHMgPSBjcmVhdGVQcm9qZWN0QXJndW1lbnRzXG4gIG9wdGlvbnMgPSBjcmVhdGVQcm9qZWN0T3B0aW9uc1xuXG4gIGFzeW5jIGFjdGlvbih7IGdhcmRlbiwgYXJncywgb3B0cyB9OiBDb21tYW5kUGFyYW1zPEFyZ3MsIE9wdHM+KTogUHJvbWlzZTxDcmVhdGVQcm9qZWN0UmVzdWx0PiB7XG4gICAgbGV0IG1vZHVsZUNvbmZpZ3M6IE1vZHVsZUNvbmZpZ09wdHNbXSA9IFtdXG4gICAgbGV0IGVycm9yczogR2FyZGVuQmFzZUVycm9yW10gPSBbXVxuXG4gICAgY29uc3QgcHJvamVjdFJvb3QgPSBhcmdzW1wicHJvamVjdC1kaXJcIl0gPyBqb2luKGdhcmRlbi5wcm9qZWN0Um9vdCwgYXJnc1tcInByb2plY3QtZGlyXCJdLnRyaW0oKSkgOiBnYXJkZW4ucHJvamVjdFJvb3RcbiAgICBjb25zdCBtb2R1bGVQYXJlbnREaXJzID0gYXdhaXQgQmx1ZWJpcmQubWFwKG9wdHNbXCJtb2R1bGUtZGlyc1wiXSB8fCBbXSwgKGRpcjogc3RyaW5nKSA9PiByZXNvbHZlKHByb2plY3RSb290LCBkaXIpKVxuICAgIGNvbnN0IHByb2plY3ROYW1lID0gdmFsaWRhdGUoXG4gICAgICBvcHRzLm5hbWUgfHwgYmFzZW5hbWUocHJvamVjdFJvb3QpLFxuICAgICAgam9pSWRlbnRpZmllcigpLFxuICAgICAgeyBjb250ZXh0OiBcInByb2plY3QgbmFtZVwiIH0sXG4gICAgKVxuXG4gICAgYXdhaXQgZW5zdXJlRGlyKHByb2plY3RSb290KVxuXG4gICAgZ2FyZGVuLmxvZy5oZWFkZXIoeyBlbW9qaTogXCJob3VzZV93aXRoX2dhcmRlblwiLCBjb21tYW5kOiBcImNyZWF0ZVwiIH0pXG4gICAgZ2FyZGVuLmxvZy5pbmZvKGBJbml0aWFsaXppbmcgbmV3IEdhcmRlbiBwcm9qZWN0ICR7cHJvamVjdE5hbWV9YClcbiAgICBnYXJkZW4ubG9nLmluZm8oXCItLS0tLS0tLS1cIilcbiAgICAvLyBTdG9wIGxvZ2dlciB3aGlsZSBwcm9tcHRpbmdcbiAgICBnYXJkZW4ubG9nLnN0b3AoKVxuXG4gICAgaWYgKG1vZHVsZVBhcmVudERpcnMubGVuZ3RoID4gMCkge1xuICAgICAgLy8gSWYgbW9kdWxlLWRpcnMgb3B0aW9uIHByb3ZpZGVkIHdlIHNjYW4gZm9yIG1vZHVsZXMgaW4gdGhlIHBhcmVudCBkaXIocykgYW5kIGFkZCB0aGVtIG9uZSBieSBvbmVcbiAgICAgIG1vZHVsZUNvbmZpZ3MgPSAoYXdhaXQgQmx1ZWJpcmQubWFwU2VyaWVzKG1vZHVsZVBhcmVudERpcnMsIGFzeW5jIHBhcmVudERpciA9PiB7XG4gICAgICAgIGNvbnN0IG1vZHVsZU5hbWVzID0gYXdhaXQgZ2V0Q2hpbGREaXJOYW1lcyhwYXJlbnREaXIpXG5cbiAgICAgICAgcmV0dXJuIEJsdWViaXJkLnJlZHVjZShtb2R1bGVOYW1lcywgYXN5bmMgKGFjYzogTW9kdWxlQ29uZmlnT3B0c1tdLCBtb2R1bGVOYW1lOiBzdHJpbmcpID0+IHtcbiAgICAgICAgICBjb25zdCB7IHR5cGUgfSA9IGF3YWl0IHByb21wdHMuYWRkQ29uZmlnRm9yTW9kdWxlKG1vZHVsZU5hbWUpXG4gICAgICAgICAgaWYgKHR5cGUpIHtcbiAgICAgICAgICAgIGFjYy5wdXNoKHByZXBhcmVOZXdNb2R1bGVDb25maWcobW9kdWxlTmFtZSwgdHlwZSwgam9pbihwYXJlbnREaXIsIG1vZHVsZU5hbWUpKSlcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIGFjY1xuICAgICAgICB9LCBbXSlcbiAgICAgIH0pKVxuICAgICAgICAucmVkdWNlKGZsYXR0ZW4sIFtdKVxuICAgICAgICAuZmlsdGVyKG0gPT4gbSlcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gT3RoZXJ3aXNlIHdlIHByb21wdCB0aGUgdXNlciBmb3IgbW9kdWxlcyB0byBhZGRcbiAgICAgIG1vZHVsZUNvbmZpZ3MgPSAoYXdhaXQgcHJvbXB0cy5yZXBlYXRBZGRNb2R1bGUoKSlcbiAgICAgICAgLm1hcCgoeyBuYW1lLCB0eXBlIH0pID0+IHByZXBhcmVOZXdNb2R1bGVDb25maWcobmFtZSwgdHlwZSwgam9pbihwcm9qZWN0Um9vdCwgbmFtZSkpKVxuICAgIH1cblxuICAgIGdhcmRlbi5sb2cuaW5mbyhcIi0tLS0tLS0tLVwiKVxuICAgIGNvbnN0IHRhc2tMb2cgPSBnYXJkZW4ubG9nLmluZm8oeyBtc2c6IFwiU2V0dGluZyB1cCBwcm9qZWN0XCIsIHN0YXR1czogXCJhY3RpdmVcIiB9KVxuXG4gICAgZm9yIChjb25zdCBtb2R1bGUgb2YgbW9kdWxlQ29uZmlncykge1xuICAgICAgYXdhaXQgZW5zdXJlRGlyKG1vZHVsZS5wYXRoKVxuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgZHVtcENvbmZpZyhtb2R1bGUsIG1vZHVsZVNjaGVtYSwgZ2FyZGVuLmxvZylcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBlcnJvcnMucHVzaChlcnIpXG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgcHJvamVjdENvbmZpZzogUHJvamVjdENvbmZpZ09wdHMgPSB7XG4gICAgICBwYXRoOiBwcm9qZWN0Um9vdCxcbiAgICAgIG5hbWU6IHByb2plY3ROYW1lLFxuICAgICAgY29uZmlnOiBwcm9qZWN0VGVtcGxhdGUocHJvamVjdE5hbWUsIG1vZHVsZUNvbmZpZ3MubWFwKG1vZHVsZSA9PiBtb2R1bGUudHlwZSkpLFxuICAgIH1cblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCBkdW1wQ29uZmlnKHByb2plY3RDb25maWcsIHByb2plY3RTY2hlbWEsIGdhcmRlbi5sb2cpXG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBlcnJvcnMucHVzaChlcnIpXG4gICAgfVxuXG4gICAgaWYgKGVycm9ycy5sZW5ndGggPT09IDApIHtcbiAgICAgIHRhc2tMb2cuc2V0U3VjY2VzcygpXG4gICAgfSBlbHNlIHtcbiAgICAgIHRhc2tMb2cuc2V0V2Fybih7IG1zZzogXCJGaW5pc2hlZCB3aXRoIGVycm9yc1wiLCBhcHBlbmQ6IHRydWUgfSlcbiAgICB9XG5cbiAgICBjb25zdCBkb2NzID0gdGVybWluYWxMaW5rKFwiZG9jc1wiLCBcImh0dHBzOi8vZG9jcy5nYXJkZW4uaW9cIilcbiAgICBnYXJkZW4ubG9nLmluZm8oYFByb2plY3QgY3JlYXRlZCEgQmUgc3VyZSB0byBjaGVjayBvdXQgb3VyICR7ZG9jc30gZm9yIGhvdyB0byBnZXQgc2FydGVkIWApXG5cbiAgICByZXR1cm4ge1xuICAgICAgcmVzdWx0OiB7XG4gICAgICAgIG1vZHVsZUNvbmZpZ3MsXG4gICAgICAgIHByb2plY3RDb25maWcsXG4gICAgICB9LFxuICAgICAgZXJyb3JzLFxuICAgIH1cbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/create/prompts.d.ts b/garden-service/build/commands/create/prompts.d.ts
new file mode 100644
index 00000000000..c31c7685612
--- /dev/null
+++ b/garden-service/build/commands/create/prompts.d.ts
@@ -0,0 +1,19 @@
+import * as inquirer from "inquirer";
+import { ModuleType } from "./config-templates";
+export interface ModuleTypeChoice extends inquirer.objects.ChoiceOption {
+ value: ModuleType;
+}
+export interface ModuleTypeMap {
+ type: ModuleType;
+}
+export interface ModuleTypeAndName extends ModuleTypeMap {
+ name: string;
+}
+export interface Prompts {
+ addConfigForModule: (...args: any[]) => Promise;
+ addModule: (...args: any[]) => Promise;
+ repeatAddModule: (...args: any[]) => Promise;
+}
+export declare function repeatAddModule(): Promise;
+export declare const prompts: Prompts;
+//# sourceMappingURL=prompts.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/create/prompts.js b/garden-service/build/commands/create/prompts.js
new file mode 100644
index 00000000000..13de190d867
--- /dev/null
+++ b/garden-service/build/commands/create/prompts.js
@@ -0,0 +1,121 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const inquirer = require("inquirer");
+const Joi = require("joi");
+const chalk_1 = require("chalk");
+const common_1 = require("../../config/common");
+const moduleTypeChoices = [
+ {
+ name: "container",
+ value: "container",
+ },
+ {
+ name: `google-cloud-function (${chalk_1.default.red.italic("experimental")})`,
+ value: "google-cloud-function",
+ },
+ {
+ name: `npm package (${chalk_1.default.red.italic("experimental")})`,
+ value: "npm-package",
+ },
+];
+// Create config for an existing module
+function addConfigForModule(dir) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const qNames = {
+ ADD_MODULE: "addModule",
+ TYPE: "type",
+ };
+ const questions = [
+ {
+ name: qNames.ADD_MODULE,
+ message: `Add module config for ${chalk_1.default.italic(dir)}?`,
+ type: "confirm",
+ },
+ {
+ name: qNames.TYPE,
+ message: "Module type",
+ choices: moduleTypeChoices,
+ when: ans => ans[qNames.ADD_MODULE],
+ type: "list",
+ },
+ ];
+ return yield inquirer.prompt(questions);
+ });
+}
+// Create a new module with config
+function addModule(addModuleMessage) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const qNames = {
+ ADD_MODULE: "addModule",
+ NAME: "name",
+ TYPE: "type",
+ };
+ const questions = [
+ {
+ name: qNames.ADD_MODULE,
+ message: addModuleMessage,
+ type: "confirm",
+ },
+ {
+ name: qNames.NAME,
+ message: "Enter module name",
+ type: "input",
+ validate: input => {
+ try {
+ Joi.attempt(input.trim(), common_1.joiIdentifier());
+ }
+ catch (err) {
+ return `Invalid module name, please try again\nError: ${err.message}`;
+ }
+ return true;
+ },
+ filter: input => input.trim(),
+ when: ans => ans[qNames.ADD_MODULE],
+ },
+ {
+ name: qNames.TYPE,
+ message: "Module type",
+ choices: moduleTypeChoices,
+ when: ans => ans[qNames.NAME],
+ type: "list",
+ },
+ ];
+ return yield inquirer.prompt(questions);
+ });
+}
+function repeatAddModule() {
+ return __awaiter(this, void 0, void 0, function* () {
+ let modules = [];
+ let addModuleMessage = "Would you like to add a module to your project?";
+ let ans = yield addModule(addModuleMessage);
+ while (ans.type) {
+ modules.push({ name: ans.name, type: ans.type });
+ addModuleMessage = `Add another module? (current modules: ${modules.map(m => m.name).join(", ")})`;
+ ans = yield addModule(addModuleMessage);
+ }
+ return modules;
+ });
+}
+exports.repeatAddModule = repeatAddModule;
+exports.prompts = {
+ addConfigForModule,
+ addModule,
+ repeatAddModule,
+};
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2NyZWF0ZS9wcm9tcHRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7QUFFSCxxQ0FBb0M7QUFDcEMsMkJBQTBCO0FBQzFCLGlDQUF5QjtBQUV6QixnREFBbUQ7QUFxQm5ELE1BQU0saUJBQWlCLEdBQXVCO0lBQzVDO1FBQ0UsSUFBSSxFQUFFLFdBQVc7UUFDakIsS0FBSyxFQUFFLFdBQVc7S0FDbkI7SUFDRDtRQUNFLElBQUksRUFBRSwwQkFBMEIsZUFBSyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLEdBQUc7UUFDbkUsS0FBSyxFQUFFLHVCQUF1QjtLQUMvQjtJQUNEO1FBQ0UsSUFBSSxFQUFFLGdCQUFnQixlQUFLLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRztRQUN6RCxLQUFLLEVBQUUsYUFBYTtLQUNyQjtDQUNGLENBQUE7QUFFRCx1Q0FBdUM7QUFDdkMsU0FBZSxrQkFBa0IsQ0FBQyxHQUFXOztRQUMzQyxNQUFNLE1BQU0sR0FBRztZQUNiLFVBQVUsRUFBRSxXQUFXO1lBQ3ZCLElBQUksRUFBRSxNQUFNO1NBQ2IsQ0FBQTtRQUNELE1BQU0sU0FBUyxHQUF1QjtZQUNwQztnQkFDRSxJQUFJLEVBQUUsTUFBTSxDQUFDLFVBQVU7Z0JBQ3ZCLE9BQU8sRUFBRSx5QkFBeUIsZUFBSyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsR0FBRztnQkFDdEQsSUFBSSxFQUFFLFNBQVM7YUFDaEI7WUFDRDtnQkFDRSxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUk7Z0JBQ2pCLE9BQU8sRUFBRSxhQUFhO2dCQUN0QixPQUFPLEVBQUUsaUJBQWlCO2dCQUMxQixJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQztnQkFDbkMsSUFBSSxFQUFFLE1BQU07YUFDYjtTQUNGLENBQUE7UUFDRCxPQUFPLE1BQU0sUUFBUSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQWtCLENBQUE7SUFDMUQsQ0FBQztDQUFBO0FBRUQsa0NBQWtDO0FBQ2xDLFNBQWUsU0FBUyxDQUFDLGdCQUF3Qjs7UUFDL0MsTUFBTSxNQUFNLEdBQUc7WUFDYixVQUFVLEVBQUUsV0FBVztZQUN2QixJQUFJLEVBQUUsTUFBTTtZQUNaLElBQUksRUFBRSxNQUFNO1NBQ2IsQ0FBQTtRQUNELE1BQU0sU0FBUyxHQUF1QjtZQUNwQztnQkFDRSxJQUFJLEVBQUUsTUFBTSxDQUFDLFVBQVU7Z0JBQ3ZCLE9BQU8sRUFBRSxnQkFBZ0I7Z0JBQ3pCLElBQUksRUFBRSxTQUFTO2FBQ2hCO1lBQ0Q7Z0JBQ0UsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJO2dCQUNqQixPQUFPLEVBQUUsbUJBQW1CO2dCQUM1QixJQUFJLEVBQUUsT0FBTztnQkFDYixRQUFRLEVBQUUsS0FBSyxDQUFDLEVBQUU7b0JBQ2hCLElBQUk7d0JBQ0YsR0FBRyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLEVBQUUsc0JBQWEsRUFBRSxDQUFDLENBQUE7cUJBQzNDO29CQUFDLE9BQU8sR0FBRyxFQUFFO3dCQUNaLE9BQU8saURBQWlELEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQTtxQkFDdEU7b0JBQ0QsT0FBTyxJQUFJLENBQUE7Z0JBQ2IsQ0FBQztnQkFDRCxNQUFNLEVBQUUsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFO2dCQUM3QixJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQzthQUNwQztZQUNEO2dCQUNFLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSTtnQkFDakIsT0FBTyxFQUFFLGFBQWE7Z0JBQ3RCLE9BQU8sRUFBRSxpQkFBaUI7Z0JBQzFCLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDO2dCQUM3QixJQUFJLEVBQUUsTUFBTTthQUNiO1NBQ0YsQ0FBQTtRQUNELE9BQU8sTUFBTSxRQUFRLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBc0IsQ0FBQTtJQUM5RCxDQUFDO0NBQUE7QUFFRCxTQUFzQixlQUFlOztRQUNuQyxJQUFJLE9BQU8sR0FBd0IsRUFBRSxDQUFBO1FBQ3JDLElBQUksZ0JBQWdCLEdBQUcsaURBQWlELENBQUE7UUFDeEUsSUFBSSxHQUFHLEdBQUcsTUFBTSxTQUFTLENBQUMsZ0JBQWdCLENBQUMsQ0FBQTtRQUUzQyxPQUFPLEdBQUcsQ0FBQyxJQUFJLEVBQUU7WUFDZixPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFBO1lBQ2hELGdCQUFnQixHQUFHLHlDQUF5QyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFBO1lBQ2xHLEdBQUcsR0FBRyxNQUFNLFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFBO1NBQ3hDO1FBQ0QsT0FBTyxPQUFPLENBQUE7SUFDaEIsQ0FBQztDQUFBO0FBWEQsMENBV0M7QUFFWSxRQUFBLE9BQU8sR0FBWTtJQUM5QixrQkFBa0I7SUFDbEIsU0FBUztJQUNULGVBQWU7Q0FDaEIsQ0FBQSIsImZpbGUiOiJjb21tYW5kcy9jcmVhdGUvcHJvbXB0cy5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgKiBhcyBpbnF1aXJlciBmcm9tIFwiaW5xdWlyZXJcIlxuaW1wb3J0ICogYXMgSm9pIGZyb20gXCJqb2lcIlxuaW1wb3J0IGNoYWxrIGZyb20gXCJjaGFsa1wiXG5cbmltcG9ydCB7IGpvaUlkZW50aWZpZXIgfSBmcm9tIFwiLi4vLi4vY29uZmlnL2NvbW1vblwiXG5pbXBvcnQgeyBNb2R1bGVUeXBlIH0gZnJvbSBcIi4vY29uZmlnLXRlbXBsYXRlc1wiXG5cbmV4cG9ydCBpbnRlcmZhY2UgTW9kdWxlVHlwZUNob2ljZSBleHRlbmRzIGlucXVpcmVyLm9iamVjdHMuQ2hvaWNlT3B0aW9uIHtcbiAgdmFsdWU6IE1vZHVsZVR5cGVcbn1cblxuZXhwb3J0IGludGVyZmFjZSBNb2R1bGVUeXBlTWFwIHtcbiAgdHlwZTogTW9kdWxlVHlwZVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIE1vZHVsZVR5cGVBbmROYW1lIGV4dGVuZHMgTW9kdWxlVHlwZU1hcCB7XG4gIG5hbWU6IHN0cmluZ1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFByb21wdHMge1xuICBhZGRDb25maWdGb3JNb2R1bGU6ICguLi5hcmdzOiBhbnlbXSkgPT4gUHJvbWlzZTxNb2R1bGVUeXBlTWFwPlxuICBhZGRNb2R1bGU6ICguLi5hcmdzOiBhbnlbXSkgPT4gUHJvbWlzZTxNb2R1bGVUeXBlQW5kTmFtZT5cbiAgcmVwZWF0QWRkTW9kdWxlOiAoLi4uYXJnczogYW55W10pID0+IFByb21pc2U8TW9kdWxlVHlwZUFuZE5hbWVbXT5cbn1cblxuY29uc3QgbW9kdWxlVHlwZUNob2ljZXM6IE1vZHVsZVR5cGVDaG9pY2VbXSA9IFtcbiAge1xuICAgIG5hbWU6IFwiY29udGFpbmVyXCIsXG4gICAgdmFsdWU6IFwiY29udGFpbmVyXCIsXG4gIH0sXG4gIHtcbiAgICBuYW1lOiBgZ29vZ2xlLWNsb3VkLWZ1bmN0aW9uICgke2NoYWxrLnJlZC5pdGFsaWMoXCJleHBlcmltZW50YWxcIil9KWAsXG4gICAgdmFsdWU6IFwiZ29vZ2xlLWNsb3VkLWZ1bmN0aW9uXCIsXG4gIH0sXG4gIHtcbiAgICBuYW1lOiBgbnBtIHBhY2thZ2UgKCR7Y2hhbGsucmVkLml0YWxpYyhcImV4cGVyaW1lbnRhbFwiKX0pYCxcbiAgICB2YWx1ZTogXCJucG0tcGFja2FnZVwiLFxuICB9LFxuXVxuXG4vLyBDcmVhdGUgY29uZmlnIGZvciBhbiBleGlzdGluZyBtb2R1bGVcbmFzeW5jIGZ1bmN0aW9uIGFkZENvbmZpZ0Zvck1vZHVsZShkaXI6IHN0cmluZyk6IFByb21pc2U8TW9kdWxlVHlwZU1hcD4ge1xuICBjb25zdCBxTmFtZXMgPSB7XG4gICAgQUREX01PRFVMRTogXCJhZGRNb2R1bGVcIixcbiAgICBUWVBFOiBcInR5cGVcIixcbiAgfVxuICBjb25zdCBxdWVzdGlvbnM6IGlucXVpcmVyLlF1ZXN0aW9ucyA9IFtcbiAgICB7XG4gICAgICBuYW1lOiBxTmFtZXMuQUREX01PRFVMRSxcbiAgICAgIG1lc3NhZ2U6IGBBZGQgbW9kdWxlIGNvbmZpZyBmb3IgJHtjaGFsay5pdGFsaWMoZGlyKX0/YCxcbiAgICAgIHR5cGU6IFwiY29uZmlybVwiLFxuICAgIH0sXG4gICAge1xuICAgICAgbmFtZTogcU5hbWVzLlRZUEUsXG4gICAgICBtZXNzYWdlOiBcIk1vZHVsZSB0eXBlXCIsXG4gICAgICBjaG9pY2VzOiBtb2R1bGVUeXBlQ2hvaWNlcyxcbiAgICAgIHdoZW46IGFucyA9PiBhbnNbcU5hbWVzLkFERF9NT0RVTEVdLFxuICAgICAgdHlwZTogXCJsaXN0XCIsXG4gICAgfSxcbiAgXVxuICByZXR1cm4gYXdhaXQgaW5xdWlyZXIucHJvbXB0KHF1ZXN0aW9ucykgYXMgTW9kdWxlVHlwZU1hcFxufVxuXG4vLyBDcmVhdGUgYSBuZXcgbW9kdWxlIHdpdGggY29uZmlnXG5hc3luYyBmdW5jdGlvbiBhZGRNb2R1bGUoYWRkTW9kdWxlTWVzc2FnZTogc3RyaW5nKTogUHJvbWlzZTxNb2R1bGVUeXBlQW5kTmFtZT4ge1xuICBjb25zdCBxTmFtZXMgPSB7XG4gICAgQUREX01PRFVMRTogXCJhZGRNb2R1bGVcIixcbiAgICBOQU1FOiBcIm5hbWVcIixcbiAgICBUWVBFOiBcInR5cGVcIixcbiAgfVxuICBjb25zdCBxdWVzdGlvbnM6IGlucXVpcmVyLlF1ZXN0aW9ucyA9IFtcbiAgICB7XG4gICAgICBuYW1lOiBxTmFtZXMuQUREX01PRFVMRSxcbiAgICAgIG1lc3NhZ2U6IGFkZE1vZHVsZU1lc3NhZ2UsXG4gICAgICB0eXBlOiBcImNvbmZpcm1cIixcbiAgICB9LFxuICAgIHtcbiAgICAgIG5hbWU6IHFOYW1lcy5OQU1FLFxuICAgICAgbWVzc2FnZTogXCJFbnRlciBtb2R1bGUgbmFtZVwiLFxuICAgICAgdHlwZTogXCJpbnB1dFwiLFxuICAgICAgdmFsaWRhdGU6IGlucHV0ID0+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBKb2kuYXR0ZW1wdChpbnB1dC50cmltKCksIGpvaUlkZW50aWZpZXIoKSlcbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgcmV0dXJuIGBJbnZhbGlkIG1vZHVsZSBuYW1lLCBwbGVhc2UgdHJ5IGFnYWluXFxuRXJyb3I6ICR7ZXJyLm1lc3NhZ2V9YFxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0cnVlXG4gICAgICB9LFxuICAgICAgZmlsdGVyOiBpbnB1dCA9PiBpbnB1dC50cmltKCksXG4gICAgICB3aGVuOiBhbnMgPT4gYW5zW3FOYW1lcy5BRERfTU9EVUxFXSxcbiAgICB9LFxuICAgIHtcbiAgICAgIG5hbWU6IHFOYW1lcy5UWVBFLFxuICAgICAgbWVzc2FnZTogXCJNb2R1bGUgdHlwZVwiLFxuICAgICAgY2hvaWNlczogbW9kdWxlVHlwZUNob2ljZXMsXG4gICAgICB3aGVuOiBhbnMgPT4gYW5zW3FOYW1lcy5OQU1FXSxcbiAgICAgIHR5cGU6IFwibGlzdFwiLFxuICAgIH0sXG4gIF1cbiAgcmV0dXJuIGF3YWl0IGlucXVpcmVyLnByb21wdChxdWVzdGlvbnMpIGFzIE1vZHVsZVR5cGVBbmROYW1lXG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZXBlYXRBZGRNb2R1bGUoKTogUHJvbWlzZTxNb2R1bGVUeXBlQW5kTmFtZVtdPiB7XG4gIGxldCBtb2R1bGVzOiBNb2R1bGVUeXBlQW5kTmFtZVtdID0gW11cbiAgbGV0IGFkZE1vZHVsZU1lc3NhZ2UgPSBcIldvdWxkIHlvdSBsaWtlIHRvIGFkZCBhIG1vZHVsZSB0byB5b3VyIHByb2plY3Q/XCJcbiAgbGV0IGFucyA9IGF3YWl0IGFkZE1vZHVsZShhZGRNb2R1bGVNZXNzYWdlKVxuXG4gIHdoaWxlIChhbnMudHlwZSkge1xuICAgIG1vZHVsZXMucHVzaCh7IG5hbWU6IGFucy5uYW1lLCB0eXBlOiBhbnMudHlwZSB9KVxuICAgIGFkZE1vZHVsZU1lc3NhZ2UgPSBgQWRkIGFub3RoZXIgbW9kdWxlPyAoY3VycmVudCBtb2R1bGVzOiAke21vZHVsZXMubWFwKG0gPT4gbS5uYW1lKS5qb2luKFwiLCBcIil9KWBcbiAgICBhbnMgPSBhd2FpdCBhZGRNb2R1bGUoYWRkTW9kdWxlTWVzc2FnZSlcbiAgfVxuICByZXR1cm4gbW9kdWxlc1xufVxuXG5leHBvcnQgY29uc3QgcHJvbXB0czogUHJvbXB0cyA9IHtcbiAgYWRkQ29uZmlnRm9yTW9kdWxlLFxuICBhZGRNb2R1bGUsXG4gIHJlcGVhdEFkZE1vZHVsZSxcbn1cbiJdfQ==
diff --git a/garden-service/build/commands/delete.d.ts b/garden-service/build/commands/delete.d.ts
new file mode 100644
index 00000000000..e00120fa5cb
--- /dev/null
+++ b/garden-service/build/commands/delete.d.ts
@@ -0,0 +1,46 @@
+import { DeleteSecretResult, EnvironmentStatusMap } from "../types/plugin/outputs";
+import { Command, CommandResult, CommandParams, StringParameter, StringsParameter } from "./base";
+export declare class DeleteCommand extends Command {
+ name: string;
+ alias: string;
+ help: string;
+ subCommands: (typeof DeleteSecretCommand | typeof DeleteEnvironmentCommand | typeof DeleteServiceCommand)[];
+ action(): Promise<{}>;
+}
+declare const deleteSecretArgs: {
+ provider: StringParameter;
+ key: StringParameter;
+};
+declare type DeleteSecretArgs = typeof deleteSecretArgs;
+export declare class DeleteSecretCommand extends Command {
+ name: string;
+ help: string;
+ description: string;
+ arguments: {
+ provider: StringParameter;
+ key: StringParameter;
+ };
+ action({ garden, args }: CommandParams): Promise>;
+}
+export declare class DeleteEnvironmentCommand extends Command {
+ name: string;
+ alias: string;
+ help: string;
+ description: string;
+ action({ garden }: CommandParams): Promise>;
+}
+declare const deleteServiceArgs: {
+ service: StringsParameter;
+};
+declare type DeleteServiceArgs = typeof deleteServiceArgs;
+export declare class DeleteServiceCommand extends Command {
+ name: string;
+ help: string;
+ arguments: {
+ service: StringsParameter;
+ };
+ description: string;
+ action({ garden, args }: CommandParams): Promise;
+}
+export {};
+//# sourceMappingURL=delete.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/delete.js b/garden-service/build/commands/delete.js
new file mode 100644
index 00000000000..2007378b1af
--- /dev/null
+++ b/garden-service/build/commands/delete.js
@@ -0,0 +1,145 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const Bluebird = require("bluebird");
+const base_1 = require("./base");
+const exceptions_1 = require("../exceptions");
+const dedent = require("dedent");
+class DeleteCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "delete";
+ this.alias = "del";
+ this.help = "Delete configuration or objects.";
+ this.subCommands = [
+ DeleteSecretCommand,
+ DeleteEnvironmentCommand,
+ DeleteServiceCommand,
+ ];
+ }
+ action() {
+ return __awaiter(this, void 0, void 0, function* () { return {}; });
+ }
+}
+exports.DeleteCommand = DeleteCommand;
+const deleteSecretArgs = {
+ provider: new base_1.StringParameter({
+ help: "The name of the provider to remove the secret from.",
+ required: true,
+ }),
+ key: new base_1.StringParameter({
+ help: "The key of the configuration variable. Separate with dots to get a nested key (e.g. key.nested).",
+ required: true,
+ }),
+};
+class DeleteSecretCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "secret";
+ this.help = "Delete a secret from the environment.";
+ this.description = dedent `
+ Returns with an error if the provided key could not be found by the provider.
+
+ Examples:
+
+ garden delete secret kubernetes somekey
+ garden del secret local-kubernetes some-other-key
+ `;
+ this.arguments = deleteSecretArgs;
+ }
+ action({ garden, args }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const key = args.key;
+ const result = yield garden.actions.deleteSecret({ pluginName: args.provider, key });
+ if (result.found) {
+ garden.log.info(`Deleted config key ${args.key}`);
+ }
+ else {
+ throw new exceptions_1.NotFoundError(`Could not find config key ${args.key}`, { key });
+ }
+ return { result };
+ });
+ }
+}
+exports.DeleteSecretCommand = DeleteSecretCommand;
+class DeleteEnvironmentCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "environment";
+ this.alias = "env";
+ this.help = "Deletes a running environment.";
+ this.description = dedent `
+ This will trigger providers to clear up any deployments in a Garden environment and reset it.
+ When you then run \`garden init\`, the environment will be reconfigured.
+
+ This can be useful if you find the environment to be in an inconsistent state, or need/want to free up
+ resources.
+ `;
+ }
+ action({ garden }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const { name } = garden.environment;
+ garden.log.header({ emoji: "skull_and_crossbones", command: `Deleting ${name} environment` });
+ const result = yield garden.actions.cleanupEnvironment({});
+ garden.log.finish();
+ return { result };
+ });
+ }
+}
+exports.DeleteEnvironmentCommand = DeleteEnvironmentCommand;
+const deleteServiceArgs = {
+ service: new base_1.StringsParameter({
+ help: "The name of the service(s) to delete. Use comma as separator to specify multiple services.",
+ required: true,
+ }),
+};
+class DeleteServiceCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "service";
+ this.help = "Deletes a running service.";
+ this.arguments = deleteServiceArgs;
+ this.description = dedent `
+ Deletes (i.e. un-deploys) the specified services. Note that this command does not take into account any
+ services depending on the deleted service, and might therefore leave the project in an unstable state.
+ Running \`garden deploy\` will re-deploy any missing services.
+
+ Examples:
+
+ garden delete service my-service # deletes my-service
+ `;
+ }
+ action({ garden, args }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const services = yield garden.getServices(args.service);
+ if (services.length === 0) {
+ garden.log.warn({ msg: "No services found. Aborting." });
+ return { result: {} };
+ }
+ garden.log.header({ emoji: "skull_and_crossbones", command: `Delete service` });
+ const result = {};
+ yield Bluebird.map(services, (service) => __awaiter(this, void 0, void 0, function* () {
+ result[service.name] = yield garden.actions.deleteService({ service });
+ }));
+ garden.log.finish();
+ return { result };
+ });
+ }
+}
+exports.DeleteServiceCommand = DeleteServiceCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,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TSxFQUFFLENBQUE7UUFDbkIsQ0FBQztLQUFBO0NBQ0Y7QUFsQ0Qsb0RBa0NDIiwiZmlsZSI6ImNvbW1hbmRzL2RlbGV0ZS5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgKiBhcyBCbHVlYmlyZCBmcm9tIFwiYmx1ZWJpcmRcIlxuaW1wb3J0IHtcbiAgRGVsZXRlU2VjcmV0UmVzdWx0LFxuICBFbnZpcm9ubWVudFN0YXR1c01hcCxcbn0gZnJvbSBcIi4uL3R5cGVzL3BsdWdpbi9vdXRwdXRzXCJcbmltcG9ydCB7XG4gIENvbW1hbmQsXG4gIENvbW1hbmRSZXN1bHQsXG4gIENvbW1hbmRQYXJhbXMsXG4gIFN0cmluZ1BhcmFtZXRlcixcbiAgU3RyaW5nc1BhcmFtZXRlcixcbn0gZnJvbSBcIi4vYmFzZVwiXG5pbXBvcnQgeyBOb3RGb3VuZEVycm9yIH0gZnJvbSBcIi4uL2V4Y2VwdGlvbnNcIlxuaW1wb3J0IGRlZGVudCA9IHJlcXVpcmUoXCJkZWRlbnRcIilcbmltcG9ydCB7IFNlcnZpY2VTdGF0dXMgfSBmcm9tIFwiLi4vdHlwZXMvc2VydmljZVwiXG5cbmV4cG9ydCBjbGFzcyBEZWxldGVDb21tYW5kIGV4dGVuZHMgQ29tbWFuZCB7XG4gIG5hbWUgPSBcImRlbGV0ZVwiXG4gIGFsaWFzID0gXCJkZWxcIlxuICBoZWxwID0gXCJEZWxldGUgY29uZmlndXJhdGlvbiBvciBvYmplY3RzLlwiXG5cbiAgc3ViQ29tbWFuZHMgPSBbXG4gICAgRGVsZXRlU2VjcmV0Q29tbWFuZCxcbiAgICBEZWxldGVFbnZpcm9ubWVudENvbW1hbmQsXG4gICAgRGVsZXRlU2VydmljZUNvbW1hbmQsXG4gIF1cblxuICBhc3luYyBhY3Rpb24oKSB7IHJldHVybiB7fSB9XG59XG5cbmNvbnN0IGRlbGV0ZVNlY3JldEFyZ3MgPSB7XG4gIHByb3ZpZGVyOiBuZXcgU3RyaW5nUGFyYW1ldGVyKHtcbiAgICBoZWxwOiBcIlRoZSBuYW1lIG9mIHRoZSBwcm92aWRlciB0byByZW1vdmUgdGhlIHNlY3JldCBmcm9tLlwiLFxuICAgIHJlcXVpcmVkOiB0cnVlLFxuICB9KSxcbiAga2V5OiBuZXcgU3RyaW5nUGFyYW1ldGVyKHtcbiAgICBoZWxwOiBcIlRoZSBrZXkgb2YgdGhlIGNvbmZpZ3VyYXRpb24gdmFyaWFibGUuIFNlcGFyYXRlIHdpdGggZG90cyB0byBnZXQgYSBuZXN0ZWQga2V5IChlLmcuIGtleS5uZXN0ZWQpLlwiLFxuICAgIHJlcXVpcmVkOiB0cnVlLFxuICB9KSxcbn1cblxudHlwZSBEZWxldGVTZWNyZXRBcmdzID0gdHlwZW9mIGRlbGV0ZVNlY3JldEFyZ3NcblxuZXhwb3J0IGNsYXNzIERlbGV0ZVNlY3JldENvbW1hbmQgZXh0ZW5kcyBDb21tYW5kPHR5cGVvZiBkZWxldGVTZWNyZXRBcmdzPiB7XG4gIG5hbWUgPSBcInNlY3JldFwiXG4gIGhlbHAgPSBcIkRlbGV0ZSBhIHNlY3JldCBmcm9tIHRoZSBlbnZpcm9ubWVudC5cIlxuXG4gIGRlc2NyaXB0aW9uID0gZGVkZW50YFxuICAgIFJldHVybnMgd2l0aCBhbiBlcnJvciBpZiB0aGUgcHJvdmlkZWQga2V5IGNvdWxkIG5vdCBiZSBmb3VuZCBieSB0aGUgcHJvdmlkZXIuXG5cbiAgICBFeGFtcGxlczpcblxuICAgICAgICBnYXJkZW4gZGVsZXRlIHNlY3JldCBrdWJlcm5ldGVzIHNvbWVrZXlcbiAgICAgICAgZ2FyZGVuIGRlbCBzZWNyZXQgbG9jYWwta3ViZXJuZXRlcyBzb21lLW90aGVyLWtleVxuICBgXG5cbiAgYXJndW1lbnRzID0gZGVsZXRlU2VjcmV0QXJnc1xuXG4gIGFzeW5jIGFjdGlvbih7IGdhcmRlbiwgYXJncyB9OiBDb21tYW5kUGFyYW1zPERlbGV0ZVNlY3JldEFyZ3M+KTogUHJvbWlzZTxDb21tYW5kUmVzdWx0PERlbGV0ZVNlY3JldFJlc3VsdD4+IHtcbiAgICBjb25zdCBrZXkgPSBhcmdzLmtleSFcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBnYXJkZW4uYWN0aW9ucy5kZWxldGVTZWNyZXQoeyBwbHVnaW5OYW1lOiBhcmdzLnByb3ZpZGVyISwga2V5IH0pXG5cbiAgICBpZiAocmVzdWx0LmZvdW5kKSB7XG4gICAgICBnYXJkZW4ubG9nLmluZm8oYERlbGV0ZWQgY29uZmlnIGtleSAke2FyZ3Mua2V5fWApXG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBOb3RGb3VuZEVycm9yKGBDb3VsZCBub3QgZmluZCBjb25maWcga2V5ICR7YXJncy5rZXl9YCwgeyBrZXkgfSlcbiAgICB9XG5cbiAgICByZXR1cm4geyByZXN1bHQgfVxuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBEZWxldGVFbnZpcm9ubWVudENvbW1hbmQgZXh0ZW5kcyBDb21tYW5kIHtcbiAgbmFtZSA9IFwiZW52aXJvbm1lbnRcIlxuICBhbGlhcyA9IFwiZW52XCJcbiAgaGVscCA9IFwiRGVsZXRlcyBhIHJ1bm5pbmcgZW52aXJvbm1lbnQuXCJcblxuICBkZXNjcmlwdGlvbiA9IGRlZGVudGBcbiAgICBUaGlzIHdpbGwgdHJpZ2dlciBwcm92aWRlcnMgdG8gY2xlYXIgdXAgYW55IGRlcGxveW1lbnRzIGluIGEgR2FyZGVuIGVudmlyb25tZW50IGFuZCByZXNldCBpdC5cbiAgICBXaGVuIHlvdSB0aGVuIHJ1biBcXGBnYXJkZW4gaW5pdFxcYCwgdGhlIGVudmlyb25tZW50IHdpbGwgYmUgcmVjb25maWd1cmVkLlxuXG4gICAgVGhpcyBjYW4gYmUgdXNlZnVsIGlmIHlvdSBmaW5kIHRoZSBlbnZpcm9ubWVudCB0byBiZSBpbiBhbiBpbmNvbnNpc3RlbnQgc3RhdGUsIG9yIG5lZWQvd2FudCB0byBmcmVlIHVwXG4gICAgcmVzb3VyY2VzLlxuICBgXG5cbiAgYXN5bmMgYWN0aW9uKHsgZ2FyZGVuIH06IENvbW1hbmRQYXJhbXMpOiBQcm9taXNlPENvbW1hbmRSZXN1bHQ8RW52aXJvbm1lbnRTdGF0dXNNYXA+PiB7XG4gICAgY29uc3QgeyBuYW1lIH0gPSBnYXJkZW4uZW52aXJvbm1lbnRcbiAgICBnYXJkZW4ubG9nLmhlYWRlcih7IGVtb2ppOiBcInNrdWxsX2FuZF9jcm9zc2JvbmVzXCIsIGNvbW1hbmQ6IGBEZWxldGluZyAke25hbWV9IGVudmlyb25tZW50YCB9KVxuXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZ2FyZGVuLmFjdGlvbnMuY2xlYW51cEVudmlyb25tZW50KHt9KVxuXG4gICAgZ2FyZGVuLmxvZy5maW5pc2goKVxuXG4gICAgcmV0dXJuIHsgcmVzdWx0IH1cbiAgfVxufVxuXG5jb25zdCBkZWxldGVTZXJ2aWNlQXJncyA9IHtcbiAgc2VydmljZTogbmV3IFN0cmluZ3NQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiVGhlIG5hbWUgb2YgdGhlIHNlcnZpY2UocykgdG8gZGVsZXRlLiBVc2UgY29tbWEgYXMgc2VwYXJhdG9yIHRvIHNwZWNpZnkgbXVsdGlwbGUgc2VydmljZXMuXCIsXG4gICAgcmVxdWlyZWQ6IHRydWUsXG4gIH0pLFxufVxudHlwZSBEZWxldGVTZXJ2aWNlQXJncyA9IHR5cGVvZiBkZWxldGVTZXJ2aWNlQXJnc1xuXG5leHBvcnQgY2xhc3MgRGVsZXRlU2VydmljZUNvbW1hbmQgZXh0ZW5kcyBDb21tYW5kIHtcbiAgbmFtZSA9IFwic2VydmljZVwiXG4gIGhlbHAgPSBcIkRlbGV0ZXMgYSBydW5uaW5nIHNlcnZpY2UuXCJcbiAgYXJndW1lbnRzID0gZGVsZXRlU2VydmljZUFyZ3NcblxuICBkZXNjcmlwdGlvbiA9IGRlZGVudGBcbiAgICBEZWxldGVzIChpLmUuIHVuLWRlcGxveXMpIHRoZSBzcGVjaWZpZWQgc2VydmljZXMuIE5vdGUgdGhhdCB0aGlzIGNvbW1hbmQgZG9lcyBub3QgdGFrZSBpbnRvIGFjY291bnQgYW55XG4gICAgc2VydmljZXMgZGVwZW5kaW5nIG9uIHRoZSBkZWxldGVkIHNlcnZpY2UsIGFuZCBtaWdodCB0aGVyZWZvcmUgbGVhdmUgdGhlIHByb2plY3QgaW4gYW4gdW5zdGFibGUgc3RhdGUuXG4gICAgUnVubmluZyBcXGBnYXJkZW4gZGVwbG95XFxgIHdpbGwgcmUtZGVwbG95IGFueSBtaXNzaW5nIHNlcnZpY2VzLlxuXG4gICAgRXhhbXBsZXM6XG5cbiAgICAgICAgZ2FyZGVuIGRlbGV0ZSBzZXJ2aWNlIG15LXNlcnZpY2UgIyBkZWxldGVzIG15LXNlcnZpY2VcbiAgYFxuXG4gIGFzeW5jIGFjdGlvbih7IGdhcmRlbiwgYXJncyB9OiBDb21tYW5kUGFyYW1zPERlbGV0ZVNlcnZpY2VBcmdzPik6IFByb21pc2U8Q29tbWFuZFJlc3VsdD4ge1xuICAgIGNvbnN0IHNlcnZpY2VzID0gYXdhaXQgZ2FyZGVuLmdldFNlcnZpY2VzKGFyZ3Muc2VydmljZSlcblxuICAgIGlmIChzZXJ2aWNlcy5sZW5ndGggPT09IDApIHtcbiAgICAgIGdhcmRlbi5sb2cud2Fybih7IG1zZzogXCJObyBzZXJ2aWNlcyBmb3VuZC4gQWJvcnRpbmcuXCIgfSlcbiAgICAgIHJldHVybiB7IHJlc3VsdDoge30gfVxuICAgIH1cblxuICAgIGdhcmRlbi5sb2cuaGVhZGVyKHsgZW1vamk6IFwic2t1bGxfYW5kX2Nyb3NzYm9uZXNcIiwgY29tbWFuZDogYERlbGV0ZSBzZXJ2aWNlYCB9KVxuXG4gICAgY29uc3QgcmVzdWx0OiB7IFtrZXk6IHN0cmluZ106IFNlcnZpY2VTdGF0dXMgfSA9IHt9XG5cbiAgICBhd2FpdCBCbHVlYmlyZC5tYXAoc2VydmljZXMsIGFzeW5jIHNlcnZpY2UgPT4ge1xuICAgICAgcmVzdWx0W3NlcnZpY2UubmFtZV0gPSBhd2FpdCBnYXJkZW4uYWN0aW9ucy5kZWxldGVTZXJ2aWNlKHsgc2VydmljZSB9KVxuICAgIH0pXG5cbiAgICBnYXJkZW4ubG9nLmZpbmlzaCgpXG4gICAgcmV0dXJuIHsgcmVzdWx0IH1cbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/deploy.d.ts b/garden-service/build/commands/deploy.d.ts
new file mode 100644
index 00000000000..0a41ba26a08
--- /dev/null
+++ b/garden-service/build/commands/deploy.d.ts
@@ -0,0 +1,28 @@
+import { BooleanParameter, Command, CommandParams, CommandResult, StringsParameter } from "./base";
+import { TaskResults } from "../task-graph";
+declare const deployArgs: {
+ service: StringsParameter;
+};
+declare const deployOpts: {
+ force: BooleanParameter;
+ "force-build": BooleanParameter;
+ watch: BooleanParameter;
+};
+declare type Args = typeof deployArgs;
+declare type Opts = typeof deployOpts;
+export declare class DeployCommand extends Command {
+ name: string;
+ help: string;
+ description: string;
+ arguments: {
+ service: StringsParameter;
+ };
+ options: {
+ force: BooleanParameter;
+ "force-build": BooleanParameter;
+ watch: BooleanParameter;
+ };
+ action({ garden, args, opts }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=deploy.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/deploy.js b/garden-service/build/commands/deploy.js
new file mode 100644
index 00000000000..1596e95e3e7
--- /dev/null
+++ b/garden-service/build/commands/deploy.js
@@ -0,0 +1,98 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const base_1 = require("./base");
+const deploy_1 = require("../tasks/deploy");
+const process_1 = require("../process");
+const util_1 = require("../util/util");
+const deployArgs = {
+ service: new base_1.StringsParameter({
+ help: "The name of the service(s) to deploy (skip to deploy all services). " +
+ "Use comma as separator to specify multiple services.",
+ }),
+};
+const deployOpts = {
+ force: new base_1.BooleanParameter({ help: "Force redeploy of service(s)." }),
+ "force-build": new base_1.BooleanParameter({ help: "Force rebuild of module(s)." }),
+ watch: new base_1.BooleanParameter({ help: "Watch for changes in module(s) and auto-deploy.", alias: "w" }),
+};
+class DeployCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "deploy";
+ this.help = "Deploy service(s) to your environment.";
+ this.description = `
+ Deploys all or specified services, taking into account service dependency order.
+ Also builds modules and dependencies if needed.
+
+ Optionally stays running and automatically re-builds and re-deploys services if their module source
+ (or their dependencies' sources) change.
+
+ Examples:
+
+ garden deploy # deploy all modules in the project
+ garden deploy my-service # only deploy my-service
+ garden deploy --force # force re-deploy of modules, even if they're already deployed
+ garden deploy --watch # watch for changes to code
+ garden deploy --env stage # deploy your services to an environment called stage
+ `;
+ this.arguments = deployArgs;
+ this.options = deployOpts;
+ }
+ action({ garden, args, opts }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const services = yield garden.getServices(args.service);
+ const serviceNames = util_1.getNames(services);
+ if (services.length === 0) {
+ garden.log.warn({ msg: "No services found. Aborting." });
+ return { result: {} };
+ }
+ garden.log.header({ emoji: "rocket", command: "Deploy" });
+ // TODO: make this a task
+ yield garden.actions.prepareEnvironment({});
+ const results = yield process_1.processServices({
+ garden,
+ services,
+ watch: opts.watch,
+ handler: (module) => __awaiter(this, void 0, void 0, function* () {
+ return deploy_1.getDeployTasks({
+ garden,
+ module,
+ serviceNames,
+ force: opts.force,
+ forceBuild: opts["force-build"],
+ includeDependants: false,
+ });
+ }),
+ changeHandler: (module) => __awaiter(this, void 0, void 0, function* () {
+ return deploy_1.getDeployTasks({
+ garden,
+ module,
+ serviceNames,
+ force: true,
+ forceBuild: true,
+ includeDependants: true,
+ });
+ }),
+ });
+ return base_1.handleTaskResults(garden, "deploy", results);
+ });
+ }
+}
+exports.DeployCommand = DeployCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,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dXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQge1xuICBCb29sZWFuUGFyYW1ldGVyLFxuICBDb21tYW5kLFxuICBDb21tYW5kUGFyYW1zLFxuICBDb21tYW5kUmVzdWx0LFxuICBoYW5kbGVUYXNrUmVzdWx0cyxcbiAgU3RyaW5nc1BhcmFtZXRlcixcbn0gZnJvbSBcIi4vYmFzZVwiXG5pbXBvcnQgeyBnZXREZXBsb3lUYXNrcyB9IGZyb20gXCIuLi90YXNrcy9kZXBsb3lcIlxuaW1wb3J0IHsgVGFza1Jlc3VsdHMgfSBmcm9tIFwiLi4vdGFzay1ncmFwaFwiXG5pbXBvcnQgeyBwcm9jZXNzU2VydmljZXMgfSBmcm9tIFwiLi4vcHJvY2Vzc1wiXG5pbXBvcnQgeyBnZXROYW1lcyB9IGZyb20gXCIuLi91dGlsL3V0aWxcIlxuXG5jb25zdCBkZXBsb3lBcmdzID0ge1xuICBzZXJ2aWNlOiBuZXcgU3RyaW5nc1BhcmFtZXRlcih7XG4gICAgaGVscDogXCJUaGUgbmFtZSBvZiB0aGUgc2VydmljZShzKSB0byBkZXBsb3kgKHNraXAgdG8gZGVwbG95IGFsbCBzZXJ2aWNlcykuIFwiICtcbiAgICAgIFwiVXNlIGNvbW1hIGFzIHNlcGFyYXRvciB0byBzcGVjaWZ5IG11bHRpcGxlIHNlcnZpY2VzLlwiLFxuICB9KSxcbn1cblxuY29uc3QgZGVwbG95T3B0cyA9IHtcbiAgZm9yY2U6IG5ldyBCb29sZWFuUGFyYW1ldGVyKHsgaGVscDogXCJGb3JjZSByZWRlcGxveSBvZiBzZXJ2aWNlKHMpLlwiIH0pLFxuICBcImZvcmNlLWJ1aWxkXCI6IG5ldyBCb29sZWFuUGFyYW1ldGVyKHsgaGVscDogXCJGb3JjZSByZWJ1aWxkIG9mIG1vZHVsZShzKS5cIiB9KSxcbiAgd2F0Y2g6IG5ldyBCb29sZWFuUGFyYW1ldGVyKHsgaGVscDogXCJXYXRjaCBmb3IgY2hhbmdlcyBpbiBtb2R1bGUocykgYW5kIGF1dG8tZGVwbG95LlwiLCBhbGlhczogXCJ3XCIgfSksXG59XG5cbnR5cGUgQXJncyA9IHR5cGVvZiBkZXBsb3lBcmdzXG50eXBlIE9wdHMgPSB0eXBlb2YgZGVwbG95T3B0c1xuXG5leHBvcnQgY2xhc3MgRGVwbG95Q29tbWFuZCBleHRlbmRzIENvbW1hbmQ8QXJncywgT3B0cz4ge1xuICBuYW1lID0gXCJkZXBsb3lcIlxuICBoZWxwID0gXCJEZXBsb3kgc2VydmljZShzKSB0byB5b3VyIGVudmlyb25tZW50LlwiXG5cbiAgZGVzY3JpcHRpb24gPSBgXG4gICAgRGVwbG95cyBhbGwgb3Igc3BlY2lmaWVkIHNlcnZpY2VzLCB0YWtpbmcgaW50byBhY2NvdW50IHNlcnZpY2UgZGVwZW5kZW5jeSBvcmRlci5cbiAgICBBbHNvIGJ1aWxkcyBtb2R1bGVzIGFuZCBkZXBlbmRlbmNpZXMgaWYgbmVlZGVkLlxuXG4gICAgT3B0aW9uYWxseSBzdGF5cyBydW5uaW5nIGFuZCBhdXRvbWF0aWNhbGx5IHJlLWJ1aWxkcyBhbmQgcmUtZGVwbG95cyBzZXJ2aWNlcyBpZiB0aGVpciBtb2R1bGUgc291cmNlXG4gICAgKG9yIHRoZWlyIGRlcGVuZGVuY2llcycgc291cmNlcykgY2hhbmdlLlxuXG4gICAgRXhhbXBsZXM6XG5cbiAgICAgICAgZ2FyZGVuIGRlcGxveSAgICAgICAgICAgICAgIyBkZXBsb3kgYWxsIG1vZHVsZXMgaW4gdGhlIHByb2plY3RcbiAgICAgICAgZ2FyZGVuIGRlcGxveSBteS1zZXJ2aWNlICAgIyBvbmx5IGRlcGxveSBteS1zZXJ2aWNlXG4gICAgICAgIGdhcmRlbiBkZXBsb3kgLS1mb3JjZSAgICAgICMgZm9yY2UgcmUtZGVwbG95IG9mIG1vZHVsZXMsIGV2ZW4gaWYgdGhleSdyZSBhbHJlYWR5IGRlcGxveWVkXG4gICAgICAgIGdhcmRlbiBkZXBsb3kgLS13YXRjaCAgICAgICMgd2F0Y2ggZm9yIGNoYW5nZXMgdG8gY29kZVxuICAgICAgICBnYXJkZW4gZGVwbG95IC0tZW52IHN0YWdlICAjIGRlcGxveSB5b3VyIHNlcnZpY2VzIHRvIGFuIGVudmlyb25tZW50IGNhbGxlZCBzdGFnZVxuICBgXG5cbiAgYXJndW1lbnRzID0gZGVwbG95QXJnc1xuICBvcHRpb25zID0gZGVwbG95T3B0c1xuXG4gIGFzeW5jIGFjdGlvbih7IGdhcmRlbiwgYXJncywgb3B0cyB9OiBDb21tYW5kUGFyYW1zPEFyZ3MsIE9wdHM+KTogUHJvbWlzZTxDb21tYW5kUmVzdWx0PFRhc2tSZXN1bHRzPj4ge1xuICAgIGNvbnN0IHNlcnZpY2VzID0gYXdhaXQgZ2FyZGVuLmdldFNlcnZpY2VzKGFyZ3Muc2VydmljZSlcbiAgICBjb25zdCBzZXJ2aWNlTmFtZXMgPSBnZXROYW1lcyhzZXJ2aWNlcylcblxuICAgIGlmIChzZXJ2aWNlcy5sZW5ndGggPT09IDApIHtcbiAgICAgIGdhcmRlbi5sb2cud2Fybih7IG1zZzogXCJObyBzZXJ2aWNlcyBmb3VuZC4gQWJvcnRpbmcuXCIgfSlcbiAgICAgIHJldHVybiB7IHJlc3VsdDoge30gfVxuICAgIH1cblxuICAgIGdhcmRlbi5sb2cuaGVhZGVyKHsgZW1vamk6IFwicm9ja2V0XCIsIGNvbW1hbmQ6IFwiRGVwbG95XCIgfSlcblxuICAgIC8vIFRPRE86IG1ha2UgdGhpcyBhIHRhc2tcbiAgICBhd2FpdCBnYXJkZW4uYWN0aW9ucy5wcmVwYXJlRW52aXJvbm1lbnQoe30pXG5cbiAgICBjb25zdCByZXN1bHRzID0gYXdhaXQgcHJvY2Vzc1NlcnZpY2VzKHtcbiAgICAgIGdhcmRlbixcbiAgICAgIHNlcnZpY2VzLFxuICAgICAgd2F0Y2g6IG9wdHMud2F0Y2gsXG4gICAgICBoYW5kbGVyOiBhc3luYyAobW9kdWxlKSA9PiBnZXREZXBsb3lUYXNrcyh7XG4gICAgICAgIGdhcmRlbixcbiAgICAgICAgbW9kdWxlLFxuICAgICAgICBzZXJ2aWNlTmFtZXMsXG4gICAgICAgIGZvcmNlOiBvcHRzLmZvcmNlLFxuICAgICAgICBmb3JjZUJ1aWxkOiBvcHRzW1wiZm9yY2UtYnVpbGRcIl0sXG4gICAgICAgIGluY2x1ZGVEZXBlbmRhbnRzOiBmYWxzZSxcbiAgICAgIH0pLFxuICAgICAgY2hhbmdlSGFuZGxlcjogYXN5bmMgKG1vZHVsZSkgPT4gZ2V0RGVwbG95VGFza3Moe1xuICAgICAgICBnYXJkZW4sXG4gICAgICAgIG1vZHVsZSxcbiAgICAgICAgc2VydmljZU5hbWVzLFxuICAgICAgICBmb3JjZTogdHJ1ZSxcbiAgICAgICAgZm9yY2VCdWlsZDogdHJ1ZSxcbiAgICAgICAgaW5jbHVkZURlcGVuZGFudHM6IHRydWUsXG4gICAgICB9KSxcbiAgICB9KVxuXG4gICAgcmV0dXJuIGhhbmRsZVRhc2tSZXN1bHRzKGdhcmRlbiwgXCJkZXBsb3lcIiwgcmVzdWx0cylcbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/dev.d.ts b/garden-service/build/commands/dev.d.ts
new file mode 100644
index 00000000000..4d1dbc941cc
--- /dev/null
+++ b/garden-service/build/commands/dev.d.ts
@@ -0,0 +1,8 @@
+import { Command, CommandResult, CommandParams } from "./base";
+export declare class DevCommand extends Command {
+ name: string;
+ help: string;
+ description: string;
+ action({ garden }: CommandParams): Promise;
+}
+//# sourceMappingURL=dev.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/dev.js b/garden-service/build/commands/dev.js
new file mode 100644
index 00000000000..a9adf3743a3
--- /dev/null
+++ b/garden-service/build/commands/dev.js
@@ -0,0 +1,108 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const Bluebird = require("bluebird");
+const chalk_1 = require("chalk");
+const lodash_1 = require("lodash");
+const moment = require("moment");
+const path_1 = require("path");
+const build_1 = require("../tasks/build");
+const base_1 = require("./base");
+const constants_1 = require("../constants");
+const process_1 = require("../process");
+const fs_extra_1 = require("fs-extra");
+const test_1 = require("./test");
+const watch_1 = require("../watch");
+const deploy_1 = require("../tasks/deploy");
+const ansiBannerPath = path_1.join(constants_1.STATIC_DIR, "garden-banner-2.txt");
+// TODO: allow limiting to certain modules and/or services
+class DevCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "dev";
+ this.help = "Starts the garden development console.";
+ this.description = `
+ The Garden dev console is a combination of the \`build\`, \`deploy\` and \`test\` commands.
+ It builds, deploys and tests all your modules and services, and re-builds, re-deploys and re-tests
+ as you modify the code.
+
+ Examples:
+
+ garden dev
+ `;
+ }
+ action({ garden }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ // print ANSI banner image
+ const data = yield fs_extra_1.readFile(ansiBannerPath);
+ console.log(data.toString());
+ garden.log.info(chalk_1.default.gray.italic(`\nGood ${getGreetingTime()}! Let's get your environment wired up...\n`));
+ yield garden.actions.prepareEnvironment({});
+ const autoReloadDependants = yield watch_1.computeAutoReloadDependants(garden);
+ const modules = yield garden.getModules();
+ if (modules.length === 0) {
+ if (modules.length === 0) {
+ garden.log.info({ msg: "No modules found in project." });
+ }
+ garden.log.info({ msg: "Aborting..." });
+ return {};
+ }
+ const tasksForModule = (watch) => {
+ return (module) => __awaiter(this, void 0, void 0, function* () {
+ const testModules = watch
+ ? (yield watch_1.withDependants(garden, [module], autoReloadDependants))
+ : [module];
+ const testTasks = lodash_1.flatten(yield Bluebird.map(testModules, m => test_1.getTestTasks({ garden, module: m })));
+ const deployTasks = yield deploy_1.getDeployTasks({
+ garden, module, force: watch, forceBuild: watch, includeDependants: watch,
+ });
+ const tasks = testTasks.concat(deployTasks);
+ if (tasks.length === 0) {
+ return [new build_1.BuildTask({ garden, module, force: watch })];
+ }
+ else {
+ return tasks;
+ }
+ });
+ };
+ yield process_1.processModules({
+ garden,
+ modules,
+ watch: true,
+ handler: tasksForModule(false),
+ changeHandler: tasksForModule(true),
+ });
+ return {};
+ });
+ }
+}
+exports.DevCommand = DevCommand;
+function getGreetingTime() {
+ const m = moment();
+ const currentHour = parseFloat(m.format("HH"));
+ if (currentHour >= 17) {
+ return "evening";
+ }
+ else if (currentHour >= 12) {
+ return "afternoon";
+ }
+ else {
+ return "morning";
+ }
+}
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,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RSxNQUFNLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQTtxQkFDekQ7eUJBQU07d0JBQ0wsT0FBTyxLQUFLLENBQUE7cUJBQ2I7Z0JBQ0gsQ0FBQyxDQUFBLENBQUE7WUFFSCxDQUFDLENBQUE7WUFFRCxNQUFNLHdCQUFjLENBQUM7Z0JBQ25CLE1BQU07Z0JBQ04sT0FBTztnQkFDUCxLQUFLLEVBQUUsSUFBSTtnQkFDWCxPQUFPLEVBQUUsY0FBYyxDQUFDLEtBQUssQ0FBQztnQkFDOUIsYUFBYSxFQUFFLGNBQWMsQ0FBQyxJQUFJLENBQUM7YUFDcEMsQ0FBQyxDQUFBO1lBRUYsT0FBTyxFQUFFLENBQUE7UUFDWCxDQUFDO0tBQUE7Q0FDRjtBQXBFRCxnQ0FvRUM7QUFFRCxTQUFTLGVBQWU7SUFDdEIsTUFBTSxDQUFDLEdBQUcsTUFBTSxFQUFFLENBQUE7SUFFbEIsTUFBTSxXQUFXLEdBQUcsVUFBVSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQTtJQUU5QyxJQUFJLFdBQVcsSUFBSSxFQUFFLEVBQUU7UUFDckIsT0FBTyxTQUFTLENBQUE7S0FDakI7U0FBTSxJQUFJLFdBQVcsSUFBSSxFQUFFLEVBQUU7UUFDNUIsT0FBTyxXQUFXLENBQUE7S0FDbkI7U0FBTTtRQUNMLE9BQU8sU0FBUyxDQUFBO0tBQ2pCO0FBQ0gsQ0FBQyIsImZpbGUiOiJjb21tYW5kcy9kZXYuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChDKSAyMDE4IEdhcmRlbiBUZWNobm9sb2dpZXMsIEluYy4gPGluZm9AZ2FyZGVuLmlvPlxuICpcbiAqIFRoaXMgU291cmNlIENvZGUgRm9ybSBpcyBzdWJqZWN0IHRvIHRoZSB0ZXJtcyBvZiB0aGUgTW96aWxsYSBQdWJsaWNcbiAqIExpY2Vuc2UsIHYuIDIuMC4gSWYgYSBjb3B5IG9mIHRoZSBNUEwgd2FzIG5vdCBkaXN0cmlidXRlZCB3aXRoIHRoaXNcbiAqIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uXG4gKi9cblxuaW1wb3J0ICogYXMgQmx1ZWJpcmQgZnJvbSBcImJsdWViaXJkXCJcbmltcG9ydCBjaGFsayBmcm9tIFwiY2hhbGtcIlxuaW1wb3J0IHsgZmxhdHRlbiB9IGZyb20gXCJsb2Rhc2hcIlxuaW1wb3J0IG1vbWVudCA9IHJlcXVpcmUoXCJtb21lbnRcIilcbmltcG9ydCB7IGpvaW4gfSBmcm9tIFwicGF0aFwiXG5cbmltcG9ydCB7IEJ1aWxkVGFzayB9IGZyb20gXCIuLi90YXNrcy9idWlsZFwiXG5pbXBvcnQgeyBUYXNrIH0gZnJvbSBcIi4uL3Rhc2tzL2Jhc2VcIlxuaW1wb3J0IHtcbiAgQ29tbWFuZCxcbiAgQ29tbWFuZFJlc3VsdCxcbiAgQ29tbWFuZFBhcmFtcyxcbn0gZnJvbSBcIi4vYmFzZVwiXG5pbXBvcnQgeyBTVEFUSUNfRElSIH0gZnJvbSBcIi4uL2NvbnN0YW50c1wiXG5pbXBvcnQgeyBwcm9jZXNzTW9kdWxlcyB9IGZyb20gXCIuLi9wcm9jZXNzXCJcbmltcG9ydCB7IHJlYWRGaWxlIH0gZnJvbSBcImZzLWV4dHJhXCJcbmltcG9ydCB7IE1vZHVsZSB9IGZyb20gXCIuLi90eXBlcy9tb2R1bGVcIlxuaW1wb3J0IHsgZ2V0VGVzdFRhc2tzIH0gZnJvbSBcIi4vdGVzdFwiXG5pbXBvcnQgeyBjb21wdXRlQXV0b1JlbG9hZERlcGVuZGFudHMsIHdpdGhEZXBlbmRhbnRzIH0gZnJvbSBcIi4uL3dhdGNoXCJcbmltcG9ydCB7IGdldERlcGxveVRhc2tzIH0gZnJvbSBcIi4uL3Rhc2tzL2RlcGxveVwiXG5cbmNvbnN0IGFuc2lCYW5uZXJQYXRoID0gam9pbihTVEFUSUNfRElSLCBcImdhcmRlbi1iYW5uZXItMi50eHRcIilcblxuLy8gVE9ETzogYWxsb3cgbGltaXRpbmcgdG8gY2VydGFpbiBtb2R1bGVzIGFuZC9vciBzZXJ2aWNlc1xuZXhwb3J0IGNsYXNzIERldkNvbW1hbmQgZXh0ZW5kcyBDb21tYW5kIHtcbiAgbmFtZSA9IFwiZGV2XCJcbiAgaGVscCA9IFwiU3RhcnRzIHRoZSBnYXJkZW4gZGV2ZWxvcG1lbnQgY29uc29sZS5cIlxuXG4gIGRlc2NyaXB0aW9uID0gYFxuICAgIFRoZSBHYXJkZW4gZGV2IGNvbnNvbGUgaXMgYSBjb21iaW5hdGlvbiBvZiB0aGUgXFxgYnVpbGRcXGAsIFxcYGRlcGxveVxcYCBhbmQgXFxgdGVzdFxcYCBjb21tYW5kcy5cbiAgICBJdCBidWlsZHMsIGRlcGxveXMgYW5kIHRlc3RzIGFsbCB5b3VyIG1vZHVsZXMgYW5kIHNlcnZpY2VzLCBhbmQgcmUtYnVpbGRzLCByZS1kZXBsb3lzIGFuZCByZS10ZXN0c1xuICAgIGFzIHlvdSBtb2RpZnkgdGhlIGNvZGUuXG5cbiAgICBFeGFtcGxlczpcblxuICAgICAgICBnYXJkZW4gZGV2XG4gIGBcblxuICBhc3luYyBhY3Rpb24oeyBnYXJkZW4gfTogQ29tbWFuZFBhcmFtcyk6IFByb21pc2U8Q29tbWFuZFJlc3VsdD4ge1xuICAgIC8vIHByaW50IEFOU0kgYmFubmVyIGltYWdlXG4gICAgY29uc3QgZGF0YSA9IGF3YWl0IHJlYWRGaWxlKGFuc2lCYW5uZXJQYXRoKVxuICAgIGNvbnNvbGUubG9nKGRhdGEudG9TdHJpbmcoKSlcblxuICAgIGdhcmRlbi5sb2cuaW5mbyhjaGFsay5ncmF5Lml0YWxpYyhgXFxuR29vZCAke2dldEdyZWV0aW5nVGltZSgpfSEgTGV0J3MgZ2V0IHlvdXIgZW52aXJvbm1lbnQgd2lyZWQgdXAuLi5cXG5gKSlcblxuICAgIGF3YWl0IGdhcmRlbi5hY3Rpb25zLnByZXBhcmVFbnZpcm9ubWVudCh7fSlcblxuICAgIGNvbnN0IGF1dG9SZWxvYWREZXBlbmRhbnRzID0gYXdhaXQgY29tcHV0ZUF1dG9SZWxvYWREZXBlbmRhbnRzKGdhcmRlbilcbiAgICBjb25zdCBtb2R1bGVzID0gYXdhaXQgZ2FyZGVuLmdldE1vZHVsZXMoKVxuXG4gICAgaWYgKG1vZHVsZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBpZiAobW9kdWxlcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgZ2FyZGVuLmxvZy5pbmZvKHsgbXNnOiBcIk5vIG1vZHVsZXMgZm91bmQgaW4gcHJvamVjdC5cIiB9KVxuICAgICAgfVxuICAgICAgZ2FyZGVuLmxvZy5pbmZvKHsgbXNnOiBcIkFib3J0aW5nLi4uXCIgfSlcbiAgICAgIHJldHVybiB7fVxuICAgIH1cblxuICAgIGNvbnN0IHRhc2tzRm9yTW9kdWxlID0gKHdhdGNoOiBib29sZWFuKSA9PiB7XG4gICAgICByZXR1cm4gYXN5bmMgKG1vZHVsZTogTW9kdWxlKSA9PiB7XG5cbiAgICAgICAgY29uc3QgdGVzdE1vZHVsZXM6IE1vZHVsZVtdID0gd2F0Y2hcbiAgICAgICAgICA/IChhd2FpdCB3aXRoRGVwZW5kYW50cyhnYXJkZW4sIFttb2R1bGVdLCBhdXRvUmVsb2FkRGVwZW5kYW50cykpXG4gICAgICAgICAgOiBbbW9kdWxlXVxuXG4gICAgICAgIGNvbnN0IHRlc3RUYXNrczogVGFza1tdID0gZmxhdHRlbihhd2FpdCBCbHVlYmlyZC5tYXAoXG4gICAgICAgICAgdGVzdE1vZHVsZXMsIG0gPT4gZ2V0VGVzdFRhc2tzKHsgZ2FyZGVuLCBtb2R1bGU6IG0gfSkpKVxuXG4gICAgICAgIGNvbnN0IGRlcGxveVRhc2tzID0gYXdhaXQgZ2V0RGVwbG95VGFza3Moe1xuICAgICAgICAgIGdhcmRlbiwgbW9kdWxlLCBmb3JjZTogd2F0Y2gsIGZvcmNlQnVpbGQ6IHdhdGNoLCBpbmNsdWRlRGVwZW5kYW50czogd2F0Y2gsXG4gICAgICAgIH0pXG4gICAgICAgIGNvbnN0IHRhc2tzID0gdGVzdFRhc2tzLmNvbmNhdChkZXBsb3lUYXNrcylcblxuICAgICAgICBpZiAodGFza3MubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgcmV0dXJuIFtuZXcgQnVpbGRUYXNrKHsgZ2FyZGVuLCBtb2R1bGUsIGZvcmNlOiB3YXRjaCB9KV1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gdGFza3NcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgfVxuXG4gICAgYXdhaXQgcHJvY2Vzc01vZHVsZXMoe1xuICAgICAgZ2FyZGVuLFxuICAgICAgbW9kdWxlcyxcbiAgICAgIHdhdGNoOiB0cnVlLFxuICAgICAgaGFuZGxlcjogdGFza3NGb3JNb2R1bGUoZmFsc2UpLFxuICAgICAgY2hhbmdlSGFuZGxlcjogdGFza3NGb3JNb2R1bGUodHJ1ZSksXG4gICAgfSlcblxuICAgIHJldHVybiB7fVxuICB9XG59XG5cbmZ1bmN0aW9uIGdldEdyZWV0aW5nVGltZSgpIHtcbiAgY29uc3QgbSA9IG1vbWVudCgpXG5cbiAgY29uc3QgY3VycmVudEhvdXIgPSBwYXJzZUZsb2F0KG0uZm9ybWF0KFwiSEhcIikpXG5cbiAgaWYgKGN1cnJlbnRIb3VyID49IDE3KSB7XG4gICAgcmV0dXJuIFwiZXZlbmluZ1wiXG4gIH0gZWxzZSBpZiAoY3VycmVudEhvdXIgPj0gMTIpIHtcbiAgICByZXR1cm4gXCJhZnRlcm5vb25cIlxuICB9IGVsc2Uge1xuICAgIHJldHVybiBcIm1vcm5pbmdcIlxuICB9XG59XG4iXX0=
diff --git a/garden-service/build/commands/exec.d.ts b/garden-service/build/commands/exec.d.ts
new file mode 100644
index 00000000000..73afacb3cdc
--- /dev/null
+++ b/garden-service/build/commands/exec.d.ts
@@ -0,0 +1,23 @@
+import { LoggerType } from "../logger/logger";
+import { ExecInServiceResult } from "../types/plugin/outputs";
+import { Command, CommandResult, CommandParams, StringParameter, StringsParameter } from "./base";
+declare const runArgs: {
+ service: StringParameter;
+ command: StringsParameter;
+};
+declare type Args = typeof runArgs;
+export declare class ExecCommand extends Command {
+ name: string;
+ alias: string;
+ help: string;
+ description: string;
+ arguments: {
+ service: StringParameter;
+ command: StringsParameter;
+ };
+ options: {};
+ loggerType: LoggerType;
+ action({ garden, args }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=exec.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/exec.js b/garden-service/build/commands/exec.js
new file mode 100644
index 00000000000..7f4ae693c34
--- /dev/null
+++ b/garden-service/build/commands/exec.js
@@ -0,0 +1,74 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const chalk_1 = require("chalk");
+const logger_1 = require("../logger/logger");
+const base_1 = require("./base");
+const dedent = require("dedent");
+const runArgs = {
+ service: new base_1.StringParameter({
+ help: "The service to exec the command in.",
+ required: true,
+ }),
+ command: new base_1.StringsParameter({
+ help: "The command to run.",
+ required: true,
+ }),
+};
+const runOpts = {
+// interactive: new BooleanParameter({
+// help: "Set to false to skip interactive mode and just output the command result",
+// defaultValue: true,
+// }),
+};
+class ExecCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "exec";
+ this.alias = "e";
+ this.help = "Executes a command (such as an interactive shell) in a running service.";
+ this.description = dedent `
+ Finds an active container for a deployed service and executes the given command within the container.
+ Supports interactive shells.
+
+ _NOTE: This command may not be supported for all module types._
+
+ Examples:
+
+ garden exec my-service /bin/sh # runs a shell in the my-service container
+ `;
+ this.arguments = runArgs;
+ this.options = runOpts;
+ this.loggerType = logger_1.LoggerType.basic;
+ }
+ action({ garden, args }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const serviceName = args.service;
+ const command = args.command || [];
+ garden.log.header({
+ emoji: "runner",
+ command: `Running command ${chalk_1.default.cyan(command.join(" "))} in service ${chalk_1.default.cyan(serviceName)}`,
+ });
+ const service = yield garden.getService(serviceName);
+ const result = yield garden.actions.execInService({ service, command });
+ return { result };
+ });
+ }
+}
+exports.ExecCommand = ExecCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2V4ZWMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7R0FNRzs7Ozs7Ozs7OztBQUVILGlDQUF5QjtBQUN6Qiw2Q0FBNkM7QUFFN0MsaUNBTWU7QUFDZixpQ0FBaUM7QUFFakMsTUFBTSxPQUFPLEdBQUc7SUFDZCxPQUFPLEVBQUUsSUFBSSxzQkFBZSxDQUFDO1FBQzNCLElBQUksRUFBRSxxQ0FBcUM7UUFDM0MsUUFBUSxFQUFFLElBQUk7S0FDZixDQUFDO0lBQ0YsT0FBTyxFQUFFLElBQUksdUJBQWdCLENBQUM7UUFDNUIsSUFBSSxFQUFFLHFCQUFxQjtRQUMzQixRQUFRLEVBQUUsSUFBSTtLQUNmLENBQUM7Q0FDSCxDQUFBO0FBRUQsTUFBTSxPQUFPLEdBQUc7QUFDZCxzQ0FBc0M7QUFDdEMsc0ZBQXNGO0FBQ3RGLHdCQUF3QjtBQUN4QixNQUFNO0NBQ1AsQ0FBQTtBQUlELE1BQWEsV0FBWSxTQUFRLGNBQWE7SUFBOUM7O1FBQ0UsU0FBSSxHQUFHLE1BQU0sQ0FBQTtRQUNiLFVBQUssR0FBRyxHQUFHLENBQUE7UUFDWCxTQUFJLEdBQUcseUVBQXlFLENBQUE7UUFFaEYsZ0JBQVcsR0FBRyxNQUFNLENBQUE7Ozs7Ozs7OztHQVNuQixDQUFBO1FBRUQsY0FBUyxHQUFHLE9BQU8sQ0FBQTtRQUNuQixZQUFPLEdBQUcsT0FBTyxDQUFBO1FBQ2pCLGVBQVUsR0FBRyxtQkFBVSxDQUFDLEtBQUssQ0FBQTtJQWdCL0IsQ0FBQztJQWRPLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQXVCOztZQUNoRCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFBO1lBQ2hDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLElBQUksRUFBRSxDQUFBO1lBRWxDLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDO2dCQUNoQixLQUFLLEVBQUUsUUFBUTtnQkFDZixPQUFPLEVBQUUsbUJBQW1CLGVBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxlQUFlLGVBQUssQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUU7YUFDbEcsQ0FBQyxDQUFBO1lBRUYsTUFBTSxPQUFPLEdBQUcsTUFBTSxNQUFNLENBQUMsVUFBVSxDQUFDLFdBQVcsQ0FBQyxDQUFBO1lBQ3BELE1BQU0sTUFBTSxHQUFHLE1BQU0sTUFBTSxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQTtZQUV2RSxPQUFPLEVBQUUsTUFBTSxFQUFFLENBQUE7UUFDbkIsQ0FBQztLQUFBO0NBQ0Y7QUFsQ0Qsa0NBa0NDIiwiZmlsZSI6ImNvbW1hbmRzL2V4ZWMuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChDKSAyMDE4IEdhcmRlbiBUZWNobm9sb2dpZXMsIEluYy4gPGluZm9AZ2FyZGVuLmlvPlxuICpcbiAqIFRoaXMgU291cmNlIENvZGUgRm9ybSBpcyBzdWJqZWN0IHRvIHRoZSB0ZXJtcyBvZiB0aGUgTW96aWxsYSBQdWJsaWNcbiAqIExpY2Vuc2UsIHYuIDIuMC4gSWYgYSBjb3B5IG9mIHRoZSBNUEwgd2FzIG5vdCBkaXN0cmlidXRlZCB3aXRoIHRoaXNcbiAqIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uXG4gKi9cblxuaW1wb3J0IGNoYWxrIGZyb20gXCJjaGFsa1wiXG5pbXBvcnQgeyBMb2dnZXJUeXBlIH0gZnJvbSBcIi4uL2xvZ2dlci9sb2dnZXJcIlxuaW1wb3J0IHsgRXhlY0luU2VydmljZVJlc3VsdCB9IGZyb20gXCIuLi90eXBlcy9wbHVnaW4vb3V0cHV0c1wiXG5pbXBvcnQge1xuICBDb21tYW5kLFxuICBDb21tYW5kUmVzdWx0LFxuICBDb21tYW5kUGFyYW1zLFxuICBTdHJpbmdQYXJhbWV0ZXIsXG4gIFN0cmluZ3NQYXJhbWV0ZXIsXG59IGZyb20gXCIuL2Jhc2VcIlxuaW1wb3J0IGRlZGVudCA9IHJlcXVpcmUoXCJkZWRlbnRcIilcblxuY29uc3QgcnVuQXJncyA9IHtcbiAgc2VydmljZTogbmV3IFN0cmluZ1BhcmFtZXRlcih7XG4gICAgaGVscDogXCJUaGUgc2VydmljZSB0byBleGVjIHRoZSBjb21tYW5kIGluLlwiLFxuICAgIHJlcXVpcmVkOiB0cnVlLFxuICB9KSxcbiAgY29tbWFuZDogbmV3IFN0cmluZ3NQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiVGhlIGNvbW1hbmQgdG8gcnVuLlwiLFxuICAgIHJlcXVpcmVkOiB0cnVlLFxuICB9KSxcbn1cblxuY29uc3QgcnVuT3B0cyA9IHtcbiAgLy8gaW50ZXJhY3RpdmU6IG5ldyBCb29sZWFuUGFyYW1ldGVyKHtcbiAgLy8gICBoZWxwOiBcIlNldCB0byBmYWxzZSB0byBza2lwIGludGVyYWN0aXZlIG1vZGUgYW5kIGp1c3Qgb3V0cHV0IHRoZSBjb21tYW5kIHJlc3VsdFwiLFxuICAvLyAgIGRlZmF1bHRWYWx1ZTogdHJ1ZSxcbiAgLy8gfSksXG59XG5cbnR5cGUgQXJncyA9IHR5cGVvZiBydW5BcmdzXG5cbmV4cG9ydCBjbGFzcyBFeGVjQ29tbWFuZCBleHRlbmRzIENvbW1hbmQ8QXJncz4ge1xuICBuYW1lID0gXCJleGVjXCJcbiAgYWxpYXMgPSBcImVcIlxuICBoZWxwID0gXCJFeGVjdXRlcyBhIGNvbW1hbmQgKHN1Y2ggYXMgYW4gaW50ZXJhY3RpdmUgc2hlbGwpIGluIGEgcnVubmluZyBzZXJ2aWNlLlwiXG5cbiAgZGVzY3JpcHRpb24gPSBkZWRlbnRgXG4gICAgRmluZHMgYW4gYWN0aXZlIGNvbnRhaW5lciBmb3IgYSBkZXBsb3llZCBzZXJ2aWNlIGFuZCBleGVjdXRlcyB0aGUgZ2l2ZW4gY29tbWFuZCB3aXRoaW4gdGhlIGNvbnRhaW5lci5cbiAgICBTdXBwb3J0cyBpbnRlcmFjdGl2ZSBzaGVsbHMuXG5cbiAgICBfTk9URTogVGhpcyBjb21tYW5kIG1heSBub3QgYmUgc3VwcG9ydGVkIGZvciBhbGwgbW9kdWxlIHR5cGVzLl9cblxuICAgIEV4YW1wbGVzOlxuXG4gICAgICAgICBnYXJkZW4gZXhlYyBteS1zZXJ2aWNlIC9iaW4vc2ggICAjIHJ1bnMgYSBzaGVsbCBpbiB0aGUgbXktc2VydmljZSBjb250YWluZXJcbiAgYFxuXG4gIGFyZ3VtZW50cyA9IHJ1bkFyZ3NcbiAgb3B0aW9ucyA9IHJ1bk9wdHNcbiAgbG9nZ2VyVHlwZSA9IExvZ2dlclR5cGUuYmFzaWNcblxuICBhc3luYyBhY3Rpb24oeyBnYXJkZW4sIGFyZ3MgfTogQ29tbWFuZFBhcmFtczxBcmdzPik6IFByb21pc2U8Q29tbWFuZFJlc3VsdDxFeGVjSW5TZXJ2aWNlUmVzdWx0Pj4ge1xuICAgIGNvbnN0IHNlcnZpY2VOYW1lID0gYXJncy5zZXJ2aWNlXG4gICAgY29uc3QgY29tbWFuZCA9IGFyZ3MuY29tbWFuZCB8fCBbXVxuXG4gICAgZ2FyZGVuLmxvZy5oZWFkZXIoe1xuICAgICAgZW1vamk6IFwicnVubmVyXCIsXG4gICAgICBjb21tYW5kOiBgUnVubmluZyBjb21tYW5kICR7Y2hhbGsuY3lhbihjb21tYW5kLmpvaW4oXCIgXCIpKX0gaW4gc2VydmljZSAke2NoYWxrLmN5YW4oc2VydmljZU5hbWUpfWAsXG4gICAgfSlcblxuICAgIGNvbnN0IHNlcnZpY2UgPSBhd2FpdCBnYXJkZW4uZ2V0U2VydmljZShzZXJ2aWNlTmFtZSlcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBnYXJkZW4uYWN0aW9ucy5leGVjSW5TZXJ2aWNlKHsgc2VydmljZSwgY29tbWFuZCB9KVxuXG4gICAgcmV0dXJuIHsgcmVzdWx0IH1cbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/get.d.ts b/garden-service/build/commands/get.d.ts
new file mode 100644
index 00000000000..99e845a6389
--- /dev/null
+++ b/garden-service/build/commands/get.d.ts
@@ -0,0 +1,30 @@
+import { Command, CommandResult, CommandParams, StringParameter } from "./base";
+import { ContextStatus } from "../actions";
+export declare class GetCommand extends Command {
+ name: string;
+ help: string;
+ subCommands: (typeof GetSecretCommand | typeof GetStatusCommand)[];
+ action(): Promise<{}>;
+}
+declare const getSecretArgs: {
+ provider: StringParameter;
+ key: StringParameter;
+};
+declare type GetArgs = typeof getSecretArgs;
+export declare class GetSecretCommand extends Command {
+ name: string;
+ help: string;
+ description: string;
+ arguments: {
+ provider: StringParameter;
+ key: StringParameter;
+ };
+ action({ garden, args }: CommandParams): Promise;
+}
+export declare class GetStatusCommand extends Command {
+ name: string;
+ help: string;
+ action({ garden }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=get.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/get.js b/garden-service/build/commands/get.js
new file mode 100644
index 00000000000..755ce1befb6
--- /dev/null
+++ b/garden-service/build/commands/get.js
@@ -0,0 +1,95 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const yaml = require("js-yaml");
+const exceptions_1 = require("../exceptions");
+const util_1 = require("../util/util");
+const base_1 = require("./base");
+const dedent = require("dedent");
+class GetCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "get";
+ this.help = "Retrieve and output data and objects, e.g. secrets, status info etc.";
+ this.subCommands = [
+ GetSecretCommand,
+ GetStatusCommand,
+ ];
+ }
+ action() {
+ return __awaiter(this, void 0, void 0, function* () { return {}; });
+ }
+}
+exports.GetCommand = GetCommand;
+const getSecretArgs = {
+ provider: new base_1.StringParameter({
+ help: "The name of the provider to read the secret from.",
+ required: true,
+ }),
+ key: new base_1.StringParameter({
+ help: "The key of the configuration variable.",
+ required: true,
+ }),
+};
+// TODO: allow omitting key to return all configs
+class GetSecretCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "secret";
+ this.help = "Get a secret from the environment.";
+ this.description = dedent `
+ Returns with an error if the provided key could not be found.
+
+ Examples:
+
+ garden get secret kubernetes somekey
+ garden get secret local-kubernetes some-other-key
+ `;
+ this.arguments = getSecretArgs;
+ }
+ action({ garden, args }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const key = args.key;
+ const { value } = yield garden.actions.getSecret({ pluginName: args.provider, key });
+ if (value === null || value === undefined) {
+ throw new exceptions_1.NotFoundError(`Could not find config key ${key}`, { key });
+ }
+ garden.log.info(value);
+ return { [key]: value };
+ });
+ }
+}
+exports.GetSecretCommand = GetSecretCommand;
+class GetStatusCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "status";
+ this.help = "Outputs the status of your environment.";
+ }
+ action({ garden }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const status = yield garden.actions.getStatus();
+ const yamlStatus = yaml.safeDump(status, { noRefs: true, skipInvalid: true });
+ // TODO: do a nicer print of this by default and add --yaml/--json options (maybe globally) for exporting
+ garden.log.info(util_1.highlightYaml(yamlStatus));
+ return { result: status };
+ });
+ }
+}
+exports.GetStatusCommand = GetStatusCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2dldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7OztHQU1HOzs7Ozs7Ozs7O0FBRUgsZ0NBQStCO0FBQy9CLDhDQUE2QztBQUM3Qyx1Q0FBNEM7QUFDNUMsaUNBS2U7QUFDZixpQ0FBaUM7QUFHakMsTUFBYSxVQUFXLFNBQVEsY0FBTztJQUF2Qzs7UUFDRSxTQUFJLEdBQUcsS0FBSyxDQUFBO1FBQ1osU0FBSSxHQUFHLHNFQUFzRSxDQUFBO1FBRTdFLGdCQUFXLEdBQUc7WUFDWixnQkFBZ0I7WUFDaEIsZ0JBQWdCO1NBQ2pCLENBQUE7SUFHSCxDQUFDO0lBRE8sTUFBTTs4REFBSyxPQUFPLEVBQUUsQ0FBQSxDQUFDLENBQUM7S0FBQTtDQUM3QjtBQVZELGdDQVVDO0FBRUQsTUFBTSxhQUFhLEdBQUc7SUFDcEIsUUFBUSxFQUFFLElBQUksc0JBQWUsQ0FBQztRQUM1QixJQUFJLEVBQUUsbURBQW1EO1FBQ3pELFFBQVEsRUFBRSxJQUFJO0tBQ2YsQ0FBQztJQUNGLEdBQUcsRUFBRSxJQUFJLHNCQUFlLENBQUM7UUFDdkIsSUFBSSxFQUFFLHdDQUF3QztRQUM5QyxRQUFRLEVBQUUsSUFBSTtLQUNmLENBQUM7Q0FDSCxDQUFBO0FBSUQsaURBQWlEO0FBRWpELE1BQWEsZ0JBQWlCLFNBQVEsY0FBNkI7SUFBbkU7O1FBQ0UsU0FBSSxHQUFHLFFBQVEsQ0FBQTtRQUNmLFNBQUksR0FBRyxvQ0FBb0MsQ0FBQTtRQUUzQyxnQkFBVyxHQUFHLE1BQU0sQ0FBQTs7Ozs7OztHQU9uQixDQUFBO1FBRUQsY0FBUyxHQUFHLGFBQWEsQ0FBQTtJQWMzQixDQUFDO0lBWk8sTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBMEI7O1lBQ25ELE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUE7WUFDcEIsTUFBTSxFQUFFLEtBQUssRUFBRSxHQUFHLE1BQU0sTUFBTSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxVQUFVLEVBQUUsSUFBSSxDQUFDLFFBQVEsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFBO1lBRXBGLElBQUksS0FBSyxLQUFLLElBQUksSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFO2dCQUN6QyxNQUFNLElBQUksMEJBQWEsQ0FBQyw2QkFBNkIsR0FBRyxFQUFFLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFBO2FBQ3JFO1lBRUQsTUFBTSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUE7WUFFdEIsT0FBTyxFQUFFLENBQUMsR0FBRyxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUE7UUFDekIsQ0FBQztLQUFBO0NBQ0Y7QUEzQkQsNENBMkJDO0FBRUQsTUFBYSxnQkFBaUIsU0FBUSxjQUFPO0lBQTdDOztRQUNFLFNBQUksR0FBRyxRQUFRLENBQUE7UUFDZixTQUFJLEdBQUcseUNBQXlDLENBQUE7SUFXbEQsQ0FBQztJQVRPLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFBaUI7O1lBQ3BDLE1BQU0sTUFBTSxHQUFHLE1BQU0sTUFBTSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQTtZQUMvQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUE7WUFFN0UseUdBQXlHO1lBQ3pHLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLG9CQUFhLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQTtZQUUxQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxDQUFBO1FBQzNCLENBQUM7S0FBQTtDQUNGO0FBYkQsNENBYUMiLCJmaWxlIjoiY29tbWFuZHMvZ2V0LmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoQykgMjAxOCBHYXJkZW4gVGVjaG5vbG9naWVzLCBJbmMuIDxpbmZvQGdhcmRlbi5pbz5cbiAqXG4gKiBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljXG4gKiBMaWNlbnNlLCB2LiAyLjAuIElmIGEgY29weSBvZiB0aGUgTVBMIHdhcyBub3QgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzXG4gKiBmaWxlLCBZb3UgY2FuIG9idGFpbiBvbmUgYXQgaHR0cDovL21vemlsbGEub3JnL01QTC8yLjAvLlxuICovXG5cbmltcG9ydCAqIGFzIHlhbWwgZnJvbSBcImpzLXlhbWxcIlxuaW1wb3J0IHsgTm90Rm91bmRFcnJvciB9IGZyb20gXCIuLi9leGNlcHRpb25zXCJcbmltcG9ydCB7IGhpZ2hsaWdodFlhbWwgfSBmcm9tIFwiLi4vdXRpbC91dGlsXCJcbmltcG9ydCB7XG4gIENvbW1hbmQsXG4gIENvbW1hbmRSZXN1bHQsXG4gIENvbW1hbmRQYXJhbXMsXG4gIFN0cmluZ1BhcmFtZXRlcixcbn0gZnJvbSBcIi4vYmFzZVwiXG5pbXBvcnQgZGVkZW50ID0gcmVxdWlyZShcImRlZGVudFwiKVxuaW1wb3J0IHsgQ29udGV4dFN0YXR1cyB9IGZyb20gXCIuLi9hY3Rpb25zXCJcblxuZXhwb3J0IGNsYXNzIEdldENvbW1hbmQgZXh0ZW5kcyBDb21tYW5kIHtcbiAgbmFtZSA9IFwiZ2V0XCJcbiAgaGVscCA9IFwiUmV0cmlldmUgYW5kIG91dHB1dCBkYXRhIGFuZCBvYmplY3RzLCBlLmcuIHNlY3JldHMsIHN0YXR1cyBpbmZvIGV0Yy5cIlxuXG4gIHN1YkNvbW1hbmRzID0gW1xuICAgIEdldFNlY3JldENvbW1hbmQsXG4gICAgR2V0U3RhdHVzQ29tbWFuZCxcbiAgXVxuXG4gIGFzeW5jIGFjdGlvbigpIHsgcmV0dXJuIHt9IH1cbn1cblxuY29uc3QgZ2V0U2VjcmV0QXJncyA9IHtcbiAgcHJvdmlkZXI6IG5ldyBTdHJpbmdQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiVGhlIG5hbWUgb2YgdGhlIHByb3ZpZGVyIHRvIHJlYWQgdGhlIHNlY3JldCBmcm9tLlwiLFxuICAgIHJlcXVpcmVkOiB0cnVlLFxuICB9KSxcbiAga2V5OiBuZXcgU3RyaW5nUGFyYW1ldGVyKHtcbiAgICBoZWxwOiBcIlRoZSBrZXkgb2YgdGhlIGNvbmZpZ3VyYXRpb24gdmFyaWFibGUuXCIsXG4gICAgcmVxdWlyZWQ6IHRydWUsXG4gIH0pLFxufVxuXG50eXBlIEdldEFyZ3MgPSB0eXBlb2YgZ2V0U2VjcmV0QXJnc1xuXG4vLyBUT0RPOiBhbGxvdyBvbWl0dGluZyBrZXkgdG8gcmV0dXJuIGFsbCBjb25maWdzXG5cbmV4cG9ydCBjbGFzcyBHZXRTZWNyZXRDb21tYW5kIGV4dGVuZHMgQ29tbWFuZDx0eXBlb2YgZ2V0U2VjcmV0QXJncz4ge1xuICBuYW1lID0gXCJzZWNyZXRcIlxuICBoZWxwID0gXCJHZXQgYSBzZWNyZXQgZnJvbSB0aGUgZW52aXJvbm1lbnQuXCJcblxuICBkZXNjcmlwdGlvbiA9IGRlZGVudGBcbiAgICBSZXR1cm5zIHdpdGggYW4gZXJyb3IgaWYgdGhlIHByb3ZpZGVkIGtleSBjb3VsZCBub3QgYmUgZm91bmQuXG5cbiAgICBFeGFtcGxlczpcblxuICAgICAgICBnYXJkZW4gZ2V0IHNlY3JldCBrdWJlcm5ldGVzIHNvbWVrZXlcbiAgICAgICAgZ2FyZGVuIGdldCBzZWNyZXQgbG9jYWwta3ViZXJuZXRlcyBzb21lLW90aGVyLWtleVxuICBgXG5cbiAgYXJndW1lbnRzID0gZ2V0U2VjcmV0QXJnc1xuXG4gIGFzeW5jIGFjdGlvbih7IGdhcmRlbiwgYXJncyB9OiBDb21tYW5kUGFyYW1zPEdldEFyZ3M+KTogUHJvbWlzZTxDb21tYW5kUmVzdWx0PiB7XG4gICAgY29uc3Qga2V5ID0gYXJncy5rZXlcbiAgICBjb25zdCB7IHZhbHVlIH0gPSBhd2FpdCBnYXJkZW4uYWN0aW9ucy5nZXRTZWNyZXQoeyBwbHVnaW5OYW1lOiBhcmdzLnByb3ZpZGVyLCBrZXkgfSlcblxuICAgIGlmICh2YWx1ZSA9PT0gbnVsbCB8fCB2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgTm90Rm91bmRFcnJvcihgQ291bGQgbm90IGZpbmQgY29uZmlnIGtleSAke2tleX1gLCB7IGtleSB9KVxuICAgIH1cblxuICAgIGdhcmRlbi5sb2cuaW5mbyh2YWx1ZSlcblxuICAgIHJldHVybiB7IFtrZXldOiB2YWx1ZSB9XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIEdldFN0YXR1c0NvbW1hbmQgZXh0ZW5kcyBDb21tYW5kIHtcbiAgbmFtZSA9IFwic3RhdHVzXCJcbiAgaGVscCA9IFwiT3V0cHV0cyB0aGUgc3RhdHVzIG9mIHlvdXIgZW52aXJvbm1lbnQuXCJcblxuICBhc3luYyBhY3Rpb24oeyBnYXJkZW4gfTogQ29tbWFuZFBhcmFtcyk6IFByb21pc2U8Q29tbWFuZFJlc3VsdDxDb250ZXh0U3RhdHVzPj4ge1xuICAgIGNvbnN0IHN0YXR1cyA9IGF3YWl0IGdhcmRlbi5hY3Rpb25zLmdldFN0YXR1cygpXG4gICAgY29uc3QgeWFtbFN0YXR1cyA9IHlhbWwuc2FmZUR1bXAoc3RhdHVzLCB7IG5vUmVmczogdHJ1ZSwgc2tpcEludmFsaWQ6IHRydWUgfSlcblxuICAgIC8vIFRPRE86IGRvIGEgbmljZXIgcHJpbnQgb2YgdGhpcyBieSBkZWZhdWx0IGFuZCBhZGQgLS15YW1sLy0tanNvbiBvcHRpb25zIChtYXliZSBnbG9iYWxseSkgZm9yIGV4cG9ydGluZ1xuICAgIGdhcmRlbi5sb2cuaW5mbyhoaWdobGlnaHRZYW1sKHlhbWxTdGF0dXMpKVxuXG4gICAgcmV0dXJuIHsgcmVzdWx0OiBzdGF0dXMgfVxuICB9XG59XG4iXX0=
diff --git a/garden-service/build/commands/init.d.ts b/garden-service/build/commands/init.d.ts
new file mode 100644
index 00000000000..b4072fb01c0
--- /dev/null
+++ b/garden-service/build/commands/init.d.ts
@@ -0,0 +1,16 @@
+import { BooleanParameter, Command, CommandResult, CommandParams } from "./base";
+declare const initOpts: {
+ force: BooleanParameter;
+};
+declare type Opts = typeof initOpts;
+export declare class InitCommand extends Command {
+ name: string;
+ help: string;
+ description: string;
+ options: {
+ force: BooleanParameter;
+ };
+ action({ garden, opts }: CommandParams<{}, Opts>): Promise>;
+}
+export {};
+//# sourceMappingURL=init.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/init.js b/garden-service/build/commands/init.js
new file mode 100644
index 00000000000..9dc68dd3f59
--- /dev/null
+++ b/garden-service/build/commands/init.js
@@ -0,0 +1,52 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const base_1 = require("./base");
+const dedent = require("dedent");
+const initOpts = {
+ force: new base_1.BooleanParameter({ help: "Force initalization of environment, ignoring the environment status check." }),
+};
+class InitCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "init";
+ this.help = "Initialize system, environment or other runtime components.";
+ this.description = dedent `
+ This command needs to be run before first deploying a Garden project, and occasionally after updating Garden,
+ plugins or project configuration.
+
+ Examples:
+
+ garden init
+ garden init --force # runs the init flows even if status checks report that the environment is ready
+ `;
+ this.options = initOpts;
+ }
+ action({ garden, opts }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const { name } = garden.environment;
+ garden.log.header({ emoji: "gear", command: `Initializing ${name} environment` });
+ yield garden.actions.prepareEnvironment({ force: opts.force, allowUserInput: true });
+ garden.log.info("");
+ garden.log.header({ emoji: "heavy_check_mark", command: `Done!` });
+ return { result: {} };
+ });
+ }
+}
+exports.InitCommand = InitCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2luaXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7R0FNRzs7Ozs7Ozs7OztBQUVILGlDQUtlO0FBQ2YsaUNBQWlDO0FBRWpDLE1BQU0sUUFBUSxHQUFHO0lBQ2YsS0FBSyxFQUFFLElBQUksdUJBQWdCLENBQUMsRUFBRSxJQUFJLEVBQUUsNEVBQTRFLEVBQUUsQ0FBQztDQUNwSCxDQUFBO0FBSUQsTUFBYSxXQUFZLFNBQVEsY0FBTztJQUF4Qzs7UUFDRSxTQUFJLEdBQUcsTUFBTSxDQUFBO1FBQ2IsU0FBSSxHQUFHLDZEQUE2RCxDQUFBO1FBRXBFLGdCQUFXLEdBQUcsTUFBTSxDQUFBOzs7Ozs7OztHQVFuQixDQUFBO1FBRUQsWUFBTyxHQUFHLFFBQVEsQ0FBQTtJQWFwQixDQUFDO0lBWE8sTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBMkI7O1lBQ3BELE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxNQUFNLENBQUMsV0FBVyxDQUFBO1lBQ25DLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsZ0JBQWdCLElBQUksY0FBYyxFQUFFLENBQUMsQ0FBQTtZQUVqRixNQUFNLE1BQU0sQ0FBQyxPQUFPLENBQUMsa0JBQWtCLENBQUMsRUFBRSxLQUFLLEVBQUUsSUFBSSxDQUFDLEtBQUssRUFBRSxjQUFjLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtZQUVwRixNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQTtZQUNuQixNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQTtZQUVsRSxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBRSxDQUFBO1FBQ3ZCLENBQUM7S0FBQTtDQUNGO0FBM0JELGtDQTJCQyIsImZpbGUiOiJjb21tYW5kcy9pbml0LmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoQykgMjAxOCBHYXJkZW4gVGVjaG5vbG9naWVzLCBJbmMuIDxpbmZvQGdhcmRlbi5pbz5cbiAqXG4gKiBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljXG4gKiBMaWNlbnNlLCB2LiAyLjAuIElmIGEgY29weSBvZiB0aGUgTVBMIHdhcyBub3QgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzXG4gKiBmaWxlLCBZb3UgY2FuIG9idGFpbiBvbmUgYXQgaHR0cDovL21vemlsbGEub3JnL01QTC8yLjAvLlxuICovXG5cbmltcG9ydCB7XG4gIEJvb2xlYW5QYXJhbWV0ZXIsXG4gIENvbW1hbmQsXG4gIENvbW1hbmRSZXN1bHQsXG4gIENvbW1hbmRQYXJhbXMsXG59IGZyb20gXCIuL2Jhc2VcIlxuaW1wb3J0IGRlZGVudCA9IHJlcXVpcmUoXCJkZWRlbnRcIilcblxuY29uc3QgaW5pdE9wdHMgPSB7XG4gIGZvcmNlOiBuZXcgQm9vbGVhblBhcmFtZXRlcih7IGhlbHA6IFwiRm9yY2UgaW5pdGFsaXphdGlvbiBvZiBlbnZpcm9ubWVudCwgaWdub3JpbmcgdGhlIGVudmlyb25tZW50IHN0YXR1cyBjaGVjay5cIiB9KSxcbn1cblxudHlwZSBPcHRzID0gdHlwZW9mIGluaXRPcHRzXG5cbmV4cG9ydCBjbGFzcyBJbml0Q29tbWFuZCBleHRlbmRzIENvbW1hbmQge1xuICBuYW1lID0gXCJpbml0XCJcbiAgaGVscCA9IFwiSW5pdGlhbGl6ZSBzeXN0ZW0sIGVudmlyb25tZW50IG9yIG90aGVyIHJ1bnRpbWUgY29tcG9uZW50cy5cIlxuXG4gIGRlc2NyaXB0aW9uID0gZGVkZW50YFxuICAgIFRoaXMgY29tbWFuZCBuZWVkcyB0byBiZSBydW4gYmVmb3JlIGZpcnN0IGRlcGxveWluZyBhIEdhcmRlbiBwcm9qZWN0LCBhbmQgb2NjYXNpb25hbGx5IGFmdGVyIHVwZGF0aW5nIEdhcmRlbixcbiAgICBwbHVnaW5zIG9yIHByb2plY3QgY29uZmlndXJhdGlvbi5cblxuICAgIEV4YW1wbGVzOlxuXG4gICAgICAgIGdhcmRlbiBpbml0XG4gICAgICAgIGdhcmRlbiBpbml0IC0tZm9yY2UgICAjIHJ1bnMgdGhlIGluaXQgZmxvd3MgZXZlbiBpZiBzdGF0dXMgY2hlY2tzIHJlcG9ydCB0aGF0IHRoZSBlbnZpcm9ubWVudCBpcyByZWFkeVxuICBgXG5cbiAgb3B0aW9ucyA9IGluaXRPcHRzXG5cbiAgYXN5bmMgYWN0aW9uKHsgZ2FyZGVuLCBvcHRzIH06IENvbW1hbmRQYXJhbXM8e30sIE9wdHM+KTogUHJvbWlzZTxDb21tYW5kUmVzdWx0PHt9Pj4ge1xuICAgIGNvbnN0IHsgbmFtZSB9ID0gZ2FyZGVuLmVudmlyb25tZW50XG4gICAgZ2FyZGVuLmxvZy5oZWFkZXIoeyBlbW9qaTogXCJnZWFyXCIsIGNvbW1hbmQ6IGBJbml0aWFsaXppbmcgJHtuYW1lfSBlbnZpcm9ubWVudGAgfSlcblxuICAgIGF3YWl0IGdhcmRlbi5hY3Rpb25zLnByZXBhcmVFbnZpcm9ubWVudCh7IGZvcmNlOiBvcHRzLmZvcmNlLCBhbGxvd1VzZXJJbnB1dDogdHJ1ZSB9KVxuXG4gICAgZ2FyZGVuLmxvZy5pbmZvKFwiXCIpXG4gICAgZ2FyZGVuLmxvZy5oZWFkZXIoeyBlbW9qaTogXCJoZWF2eV9jaGVja19tYXJrXCIsIGNvbW1hbmQ6IGBEb25lIWAgfSlcblxuICAgIHJldHVybiB7IHJlc3VsdDoge30gfVxuICB9XG59XG4iXX0=
diff --git a/garden-service/build/commands/link/link.d.ts b/garden-service/build/commands/link/link.d.ts
new file mode 100644
index 00000000000..566ee5ec305
--- /dev/null
+++ b/garden-service/build/commands/link/link.d.ts
@@ -0,0 +1,10 @@
+import { Command } from "../base";
+import { LinkSourceCommand } from "./source";
+import { LinkModuleCommand } from "./module";
+export declare class LinkCommand extends Command {
+ name: string;
+ help: string;
+ subCommands: (typeof LinkSourceCommand | typeof LinkModuleCommand)[];
+ action(): Promise<{}>;
+}
+//# sourceMappingURL=link.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/link/link.js b/garden-service/build/commands/link/link.js
new file mode 100644
index 00000000000..6e7cbffd171
--- /dev/null
+++ b/garden-service/build/commands/link/link.js
@@ -0,0 +1,37 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const base_1 = require("../base");
+const source_1 = require("./source");
+const module_1 = require("./module");
+class LinkCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "link";
+ this.help = "Link a remote source or module to a local path";
+ this.subCommands = [
+ source_1.LinkSourceCommand,
+ module_1.LinkModuleCommand,
+ ];
+ }
+ action() {
+ return __awaiter(this, void 0, void 0, function* () { return {}; });
+ }
+}
+exports.LinkCommand = LinkCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2xpbmsvbGluay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7OztHQU1HOzs7Ozs7Ozs7O0FBRUgsa0NBQWlDO0FBQ2pDLHFDQUE0QztBQUM1QyxxQ0FBNEM7QUFFNUMsTUFBYSxXQUFZLFNBQVEsY0FBTztJQUF4Qzs7UUFDRSxTQUFJLEdBQUcsTUFBTSxDQUFBO1FBQ2IsU0FBSSxHQUFHLGdEQUFnRCxDQUFBO1FBRXZELGdCQUFXLEdBQUc7WUFDWiwwQkFBaUI7WUFDakIsMEJBQWlCO1NBQ2xCLENBQUE7SUFHSCxDQUFDO0lBRE8sTUFBTTs4REFBSyxPQUFPLEVBQUUsQ0FBQSxDQUFDLENBQUM7S0FBQTtDQUM3QjtBQVZELGtDQVVDIiwiZmlsZSI6ImNvbW1hbmRzL2xpbmsvbGluay5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgeyBDb21tYW5kIH0gZnJvbSBcIi4uL2Jhc2VcIlxuaW1wb3J0IHsgTGlua1NvdXJjZUNvbW1hbmQgfSBmcm9tIFwiLi9zb3VyY2VcIlxuaW1wb3J0IHsgTGlua01vZHVsZUNvbW1hbmQgfSBmcm9tIFwiLi9tb2R1bGVcIlxuXG5leHBvcnQgY2xhc3MgTGlua0NvbW1hbmQgZXh0ZW5kcyBDb21tYW5kIHtcbiAgbmFtZSA9IFwibGlua1wiXG4gIGhlbHAgPSBcIkxpbmsgYSByZW1vdGUgc291cmNlIG9yIG1vZHVsZSB0byBhIGxvY2FsIHBhdGhcIlxuXG4gIHN1YkNvbW1hbmRzID0gW1xuICAgIExpbmtTb3VyY2VDb21tYW5kLFxuICAgIExpbmtNb2R1bGVDb21tYW5kLFxuICBdXG5cbiAgYXN5bmMgYWN0aW9uKCkgeyByZXR1cm4ge30gfVxufVxuIl19
diff --git a/garden-service/build/commands/link/module.d.ts b/garden-service/build/commands/link/module.d.ts
new file mode 100644
index 00000000000..bcf442b9f16
--- /dev/null
+++ b/garden-service/build/commands/link/module.d.ts
@@ -0,0 +1,19 @@
+import { Command, CommandResult, StringParameter, PathParameter, CommandParams } from "../base";
+import { LinkedSource } from "../../config-store";
+declare const linkModuleArguments: {
+ module: StringParameter;
+ path: PathParameter;
+};
+declare type Args = typeof linkModuleArguments;
+export declare class LinkModuleCommand extends Command {
+ name: string;
+ help: string;
+ arguments: {
+ module: StringParameter;
+ path: PathParameter;
+ };
+ description: string;
+ action({ garden, args }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=module.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/link/module.js b/garden-service/build/commands/link/module.js
new file mode 100644
index 00000000000..5b04f010d6f
--- /dev/null
+++ b/garden-service/build/commands/link/module.js
@@ -0,0 +1,78 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const path_1 = require("path");
+const dedent = require("dedent");
+const chalk_1 = require("chalk");
+const exceptions_1 = require("../../exceptions");
+const base_1 = require("../base");
+const ext_source_util_1 = require("../../util/ext-source-util");
+const linkModuleArguments = {
+ module: new base_1.StringParameter({
+ help: "Name of the module to link.",
+ required: true,
+ }),
+ path: new base_1.PathParameter({
+ help: "Path to the local directory that containes the module.",
+ required: true,
+ }),
+};
+class LinkModuleCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "module";
+ this.help = "Link a module to a local directory.";
+ this.arguments = linkModuleArguments;
+ this.description = dedent `
+ After linking a remote module, Garden will read the source from the module's local directory instead of from
+ the remote URL. Garden can only link modules that have a remote source,
+ i.e. modules that specifiy a repositoryUrl in their garden.yml config file.
+
+ Examples:
+
+ garden link module my-module path/to/my-module # links my-module to its local version at the given path
+ `;
+ }
+ action({ garden, args }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ garden.log.header({ emoji: "link", command: "link module" });
+ const sourceType = "module";
+ const { module: moduleName, path } = args;
+ const moduleToLink = yield garden.getModule(moduleName);
+ const isRemote = [moduleToLink].filter(ext_source_util_1.hasRemoteSource)[0];
+ if (!isRemote) {
+ const modulesWithRemoteSource = (yield garden.getModules()).filter(ext_source_util_1.hasRemoteSource).sort();
+ throw new exceptions_1.ParameterError(`Expected module(s) ${chalk_1.default.underline(moduleName)} to have a remote source.` +
+ ` Did you mean to use the "link source" command?`, {
+ modulesWithRemoteSource,
+ input: module,
+ });
+ }
+ const absPath = path_1.resolve(garden.projectRoot, path);
+ const linkedModuleSources = yield ext_source_util_1.addLinkedSources({
+ garden,
+ sourceType,
+ sources: [{ name: moduleName, path: absPath }],
+ });
+ garden.log.info(`Linked module ${moduleName}`);
+ return { result: linkedModuleSources };
+ });
+ }
+}
+exports.LinkModuleCommand = LinkModuleCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2xpbmsvbW9kdWxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7QUFFSCwrQkFBOEI7QUFDOUIsaUNBQWlDO0FBQ2pDLGlDQUF5QjtBQUV6QixpREFBaUQ7QUFDakQsa0NBTWdCO0FBSWhCLGdFQUdtQztBQUVuQyxNQUFNLG1CQUFtQixHQUFHO0lBQzFCLE1BQU0sRUFBRSxJQUFJLHNCQUFlLENBQUM7UUFDMUIsSUFBSSxFQUFFLDZCQUE2QjtRQUNuQyxRQUFRLEVBQUUsSUFBSTtLQUNmLENBQUM7SUFDRixJQUFJLEVBQUUsSUFBSSxvQkFBYSxDQUFDO1FBQ3RCLElBQUksRUFBRSx3REFBd0Q7UUFDOUQsUUFBUSxFQUFFLElBQUk7S0FDZixDQUFDO0NBQ0gsQ0FBQTtBQUlELE1BQWEsaUJBQWtCLFNBQVEsY0FBYTtJQUFwRDs7UUFDRSxTQUFJLEdBQUcsUUFBUSxDQUFBO1FBQ2YsU0FBSSxHQUFHLHFDQUFxQyxDQUFBO1FBQzVDLGNBQVMsR0FBRyxtQkFBbUIsQ0FBQTtRQUUvQixnQkFBVyxHQUFHLE1BQU0sQ0FBQTs7Ozs7Ozs7R0FRbkIsQ0FBQTtJQW9DSCxDQUFDO0lBbENPLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQXVCOztZQUNoRCxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUE7WUFFNUQsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFBO1lBRTNCLE1BQU0sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxHQUFHLElBQUksQ0FBQTtZQUN6QyxNQUFNLFlBQVksR0FBRyxNQUFNLE1BQU0sQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLENBQUE7WUFFdkQsTUFBTSxRQUFRLEdBQUcsQ0FBQyxZQUFZLENBQUMsQ0FBQyxNQUFNLENBQUMsaUNBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBO1lBQzFELElBQUksQ0FBQyxRQUFRLEVBQUU7Z0JBQ2IsTUFBTSx1QkFBdUIsR0FBRyxDQUFDLE1BQU0sTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLGlDQUFlLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQTtnQkFFMUYsTUFBTSxJQUFJLDJCQUFjLENBQ3RCLHNCQUFzQixlQUFLLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQywyQkFBMkI7b0JBQzVFLGlEQUFpRCxFQUNqRDtvQkFDRSx1QkFBdUI7b0JBQ3ZCLEtBQUssRUFBRSxNQUFNO2lCQUNkLENBQ0YsQ0FBQTthQUNGO1lBRUQsTUFBTSxPQUFPLEdBQUcsY0FBTyxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUUsSUFBSSxDQUFDLENBQUE7WUFDakQsTUFBTSxtQkFBbUIsR0FBRyxNQUFNLGtDQUFnQixDQUFDO2dCQUNqRCxNQUFNO2dCQUNOLFVBQVU7Z0JBQ1YsT0FBTyxFQUFFLENBQUMsRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsQ0FBQzthQUMvQyxDQUFDLENBQUE7WUFFRixNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsVUFBVSxFQUFFLENBQUMsQ0FBQTtZQUU5QyxPQUFPLEVBQUUsTUFBTSxFQUFFLG1CQUFtQixFQUFFLENBQUE7UUFFeEMsQ0FBQztLQUFBO0NBQ0Y7QUFqREQsOENBaURDIiwiZmlsZSI6ImNvbW1hbmRzL2xpbmsvbW9kdWxlLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoQykgMjAxOCBHYXJkZW4gVGVjaG5vbG9naWVzLCBJbmMuIDxpbmZvQGdhcmRlbi5pbz5cbiAqXG4gKiBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljXG4gKiBMaWNlbnNlLCB2LiAyLjAuIElmIGEgY29weSBvZiB0aGUgTVBMIHdhcyBub3QgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzXG4gKiBmaWxlLCBZb3UgY2FuIG9idGFpbiBvbmUgYXQgaHR0cDovL21vemlsbGEub3JnL01QTC8yLjAvLlxuICovXG5cbmltcG9ydCB7IHJlc29sdmUgfSBmcm9tIFwicGF0aFwiXG5pbXBvcnQgZGVkZW50ID0gcmVxdWlyZShcImRlZGVudFwiKVxuaW1wb3J0IGNoYWxrIGZyb20gXCJjaGFsa1wiXG5cbmltcG9ydCB7IFBhcmFtZXRlckVycm9yIH0gZnJvbSBcIi4uLy4uL2V4Y2VwdGlvbnNcIlxuaW1wb3J0IHtcbiAgQ29tbWFuZCxcbiAgQ29tbWFuZFJlc3VsdCxcbiAgU3RyaW5nUGFyYW1ldGVyLFxuICBQYXRoUGFyYW1ldGVyLFxuICBDb21tYW5kUGFyYW1zLFxufSBmcm9tIFwiLi4vYmFzZVwiXG5pbXBvcnQge1xuICBMaW5rZWRTb3VyY2UsXG59IGZyb20gXCIuLi8uLi9jb25maWctc3RvcmVcIlxuaW1wb3J0IHtcbiAgYWRkTGlua2VkU291cmNlcyxcbiAgaGFzUmVtb3RlU291cmNlLFxufSBmcm9tIFwiLi4vLi4vdXRpbC9leHQtc291cmNlLXV0aWxcIlxuXG5jb25zdCBsaW5rTW9kdWxlQXJndW1lbnRzID0ge1xuICBtb2R1bGU6IG5ldyBTdHJpbmdQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiTmFtZSBvZiB0aGUgbW9kdWxlIHRvIGxpbmsuXCIsXG4gICAgcmVxdWlyZWQ6IHRydWUsXG4gIH0pLFxuICBwYXRoOiBuZXcgUGF0aFBhcmFtZXRlcih7XG4gICAgaGVscDogXCJQYXRoIHRvIHRoZSBsb2NhbCBkaXJlY3RvcnkgdGhhdCBjb250YWluZXMgdGhlIG1vZHVsZS5cIixcbiAgICByZXF1aXJlZDogdHJ1ZSxcbiAgfSksXG59XG5cbnR5cGUgQXJncyA9IHR5cGVvZiBsaW5rTW9kdWxlQXJndW1lbnRzXG5cbmV4cG9ydCBjbGFzcyBMaW5rTW9kdWxlQ29tbWFuZCBleHRlbmRzIENvbW1hbmQ8QXJncz4ge1xuICBuYW1lID0gXCJtb2R1bGVcIlxuICBoZWxwID0gXCJMaW5rIGEgbW9kdWxlIHRvIGEgbG9jYWwgZGlyZWN0b3J5LlwiXG4gIGFyZ3VtZW50cyA9IGxpbmtNb2R1bGVBcmd1bWVudHNcblxuICBkZXNjcmlwdGlvbiA9IGRlZGVudGBcbiAgICBBZnRlciBsaW5raW5nIGEgcmVtb3RlIG1vZHVsZSwgR2FyZGVuIHdpbGwgcmVhZCB0aGUgc291cmNlIGZyb20gdGhlIG1vZHVsZSdzIGxvY2FsIGRpcmVjdG9yeSBpbnN0ZWFkIG9mIGZyb21cbiAgICB0aGUgcmVtb3RlIFVSTC4gR2FyZGVuIGNhbiBvbmx5IGxpbmsgbW9kdWxlcyB0aGF0IGhhdmUgYSByZW1vdGUgc291cmNlLFxuICAgIGkuZS4gbW9kdWxlcyB0aGF0IHNwZWNpZml5IGEgcmVwb3NpdG9yeVVybCBpbiB0aGVpciBnYXJkZW4ueW1sIGNvbmZpZyBmaWxlLlxuXG4gICAgRXhhbXBsZXM6XG5cbiAgICAgICAgZ2FyZGVuIGxpbmsgbW9kdWxlIG15LW1vZHVsZSBwYXRoL3RvL215LW1vZHVsZSAjIGxpbmtzIG15LW1vZHVsZSB0byBpdHMgbG9jYWwgdmVyc2lvbiBhdCB0aGUgZ2l2ZW4gcGF0aFxuICBgXG5cbiAgYXN5bmMgYWN0aW9uKHsgZ2FyZGVuLCBhcmdzIH06IENvbW1hbmRQYXJhbXM8QXJncz4pOiBQcm9taXNlPENvbW1hbmRSZXN1bHQ8TGlua2VkU291cmNlW10+PiB7XG4gICAgZ2FyZGVuLmxvZy5oZWFkZXIoeyBlbW9qaTogXCJsaW5rXCIsIGNvbW1hbmQ6IFwibGluayBtb2R1bGVcIiB9KVxuXG4gICAgY29uc3Qgc291cmNlVHlwZSA9IFwibW9kdWxlXCJcblxuICAgIGNvbnN0IHsgbW9kdWxlOiBtb2R1bGVOYW1lLCBwYXRoIH0gPSBhcmdzXG4gICAgY29uc3QgbW9kdWxlVG9MaW5rID0gYXdhaXQgZ2FyZGVuLmdldE1vZHVsZShtb2R1bGVOYW1lKVxuXG4gICAgY29uc3QgaXNSZW1vdGUgPSBbbW9kdWxlVG9MaW5rXS5maWx0ZXIoaGFzUmVtb3RlU291cmNlKVswXVxuICAgIGlmICghaXNSZW1vdGUpIHtcbiAgICAgIGNvbnN0IG1vZHVsZXNXaXRoUmVtb3RlU291cmNlID0gKGF3YWl0IGdhcmRlbi5nZXRNb2R1bGVzKCkpLmZpbHRlcihoYXNSZW1vdGVTb3VyY2UpLnNvcnQoKVxuXG4gICAgICB0aHJvdyBuZXcgUGFyYW1ldGVyRXJyb3IoXG4gICAgICAgIGBFeHBlY3RlZCBtb2R1bGUocykgJHtjaGFsay51bmRlcmxpbmUobW9kdWxlTmFtZSl9IHRvIGhhdmUgYSByZW1vdGUgc291cmNlLmAgK1xuICAgICAgICBgIERpZCB5b3UgbWVhbiB0byB1c2UgdGhlIFwibGluayBzb3VyY2VcIiBjb21tYW5kP2AsXG4gICAgICAgIHtcbiAgICAgICAgICBtb2R1bGVzV2l0aFJlbW90ZVNvdXJjZSxcbiAgICAgICAgICBpbnB1dDogbW9kdWxlLFxuICAgICAgICB9LFxuICAgICAgKVxuICAgIH1cblxuICAgIGNvbnN0IGFic1BhdGggPSByZXNvbHZlKGdhcmRlbi5wcm9qZWN0Um9vdCwgcGF0aClcbiAgICBjb25zdCBsaW5rZWRNb2R1bGVTb3VyY2VzID0gYXdhaXQgYWRkTGlua2VkU291cmNlcyh7XG4gICAgICBnYXJkZW4sXG4gICAgICBzb3VyY2VUeXBlLFxuICAgICAgc291cmNlczogW3sgbmFtZTogbW9kdWxlTmFtZSwgcGF0aDogYWJzUGF0aCB9XSxcbiAgICB9KVxuXG4gICAgZ2FyZGVuLmxvZy5pbmZvKGBMaW5rZWQgbW9kdWxlICR7bW9kdWxlTmFtZX1gKVxuXG4gICAgcmV0dXJuIHsgcmVzdWx0OiBsaW5rZWRNb2R1bGVTb3VyY2VzIH1cblxuICB9XG59XG4iXX0=
diff --git a/garden-service/build/commands/link/source.d.ts b/garden-service/build/commands/link/source.d.ts
new file mode 100644
index 00000000000..d236c1a7cd6
--- /dev/null
+++ b/garden-service/build/commands/link/source.d.ts
@@ -0,0 +1,20 @@
+import { Command, CommandResult, StringParameter, PathParameter } from "../base";
+import { LinkedSource } from "../../config-store";
+import { CommandParams } from "../base";
+declare const linkSourceArguments: {
+ source: StringParameter;
+ path: PathParameter;
+};
+declare type Args = typeof linkSourceArguments;
+export declare class LinkSourceCommand extends Command {
+ name: string;
+ help: string;
+ arguments: {
+ source: StringParameter;
+ path: PathParameter;
+ };
+ description: string;
+ action({ garden, args }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=source.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/link/source.js b/garden-service/build/commands/link/source.js
new file mode 100644
index 00000000000..3f977c768b9
--- /dev/null
+++ b/garden-service/build/commands/link/source.js
@@ -0,0 +1,77 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const path_1 = require("path");
+const dedent = require("dedent");
+const chalk_1 = require("chalk");
+const exceptions_1 = require("../../exceptions");
+const base_1 = require("../base");
+const ext_source_util_1 = require("../../util/ext-source-util");
+const linkSourceArguments = {
+ source: new base_1.StringParameter({
+ help: "Name of the source to link as declared in the project config.",
+ required: true,
+ }),
+ path: new base_1.PathParameter({
+ help: "Path to the local directory that containes the source.",
+ required: true,
+ }),
+};
+class LinkSourceCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "source";
+ this.help = "Link a remote source to a local directory.";
+ this.arguments = linkSourceArguments;
+ this.description = dedent `
+ After linking a remote source, Garden will read it from its local directory instead of
+ from the remote URL. Garden can only link remote sources that have been declared in the project
+ level garden.yml config.
+
+ Examples:
+
+ garden link source my-source path/to/my-source # links my-source to its local version at the given path
+ `;
+ }
+ action({ garden, args }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ garden.log.header({ emoji: "link", command: "link source" });
+ const sourceType = "project";
+ const { source: sourceName, path } = args;
+ const projectSourceToLink = garden.projectSources.find(src => src.name === sourceName);
+ if (!projectSourceToLink) {
+ const availableRemoteSources = garden.projectSources.map(s => s.name).sort();
+ throw new exceptions_1.ParameterError(`Remote source ${chalk_1.default.underline(sourceName)} not found in project config.` +
+ ` Did you mean to use the "link module" command?`, {
+ availableRemoteSources,
+ input: sourceName,
+ });
+ }
+ const absPath = path_1.resolve(garden.projectRoot, path);
+ const linkedProjectSources = yield ext_source_util_1.addLinkedSources({
+ garden,
+ sourceType,
+ sources: [{ name: sourceName, path: absPath }],
+ });
+ garden.log.info(`Linked source ${sourceName}`);
+ return { result: linkedProjectSources };
+ });
+ }
+}
+exports.LinkSourceCommand = LinkSourceCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2xpbmsvc291cmNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7QUFFSCwrQkFBOEI7QUFDOUIsaUNBQWlDO0FBQ2pDLGlDQUF5QjtBQUV6QixpREFBaUQ7QUFDakQsa0NBS2dCO0FBQ2hCLGdFQUE2RDtBQUk3RCxNQUFNLG1CQUFtQixHQUFHO0lBQzFCLE1BQU0sRUFBRSxJQUFJLHNCQUFlLENBQUM7UUFDMUIsSUFBSSxFQUFFLCtEQUErRDtRQUNyRSxRQUFRLEVBQUUsSUFBSTtLQUNmLENBQUM7SUFDRixJQUFJLEVBQUUsSUFBSSxvQkFBYSxDQUFDO1FBQ3RCLElBQUksRUFBRSx3REFBd0Q7UUFDOUQsUUFBUSxFQUFFLElBQUk7S0FDZixDQUFDO0NBQ0gsQ0FBQTtBQUlELE1BQWEsaUJBQWtCLFNBQVEsY0FBYTtJQUFwRDs7UUFDRSxTQUFJLEdBQUcsUUFBUSxDQUFBO1FBQ2YsU0FBSSxHQUFHLDRDQUE0QyxDQUFBO1FBQ25ELGNBQVMsR0FBRyxtQkFBbUIsQ0FBQTtRQUUvQixnQkFBVyxHQUFHLE1BQU0sQ0FBQTs7Ozs7Ozs7R0FRbkIsQ0FBQTtJQW1DSCxDQUFDO0lBakNPLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQXVCOztZQUNoRCxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUE7WUFFNUQsTUFBTSxVQUFVLEdBQUcsU0FBUyxDQUFBO1lBRTVCLE1BQU0sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxHQUFHLElBQUksQ0FBQTtZQUN6QyxNQUFNLG1CQUFtQixHQUFHLE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksS0FBSyxVQUFVLENBQUMsQ0FBQTtZQUV0RixJQUFJLENBQUMsbUJBQW1CLEVBQUU7Z0JBQ3hCLE1BQU0sc0JBQXNCLEdBQUcsTUFBTSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUE7Z0JBRTVFLE1BQU0sSUFBSSwyQkFBYyxDQUN0QixpQkFBaUIsZUFBSyxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUMsK0JBQStCO29CQUMzRSxpREFBaUQsRUFDakQ7b0JBQ0Usc0JBQXNCO29CQUN0QixLQUFLLEVBQUUsVUFBVTtpQkFDbEIsQ0FDRixDQUFBO2FBQ0Y7WUFFRCxNQUFNLE9BQU8sR0FBRyxjQUFPLENBQUMsTUFBTSxDQUFDLFdBQVcsRUFBRSxJQUFJLENBQUMsQ0FBQTtZQUVqRCxNQUFNLG9CQUFvQixHQUFHLE1BQU0sa0NBQWdCLENBQUM7Z0JBQ2xELE1BQU07Z0JBQ04sVUFBVTtnQkFDVixPQUFPLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxDQUFDO2FBQy9DLENBQUMsQ0FBQTtZQUVGLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLGlCQUFpQixVQUFVLEVBQUUsQ0FBQyxDQUFBO1lBRTlDLE9BQU8sRUFBRSxNQUFNLEVBQUUsb0JBQW9CLEVBQUUsQ0FBQTtRQUN6QyxDQUFDO0tBQUE7Q0FDRjtBQWhERCw4Q0FnREMiLCJmaWxlIjoiY29tbWFuZHMvbGluay9zb3VyY2UuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChDKSAyMDE4IEdhcmRlbiBUZWNobm9sb2dpZXMsIEluYy4gPGluZm9AZ2FyZGVuLmlvPlxuICpcbiAqIFRoaXMgU291cmNlIENvZGUgRm9ybSBpcyBzdWJqZWN0IHRvIHRoZSB0ZXJtcyBvZiB0aGUgTW96aWxsYSBQdWJsaWNcbiAqIExpY2Vuc2UsIHYuIDIuMC4gSWYgYSBjb3B5IG9mIHRoZSBNUEwgd2FzIG5vdCBkaXN0cmlidXRlZCB3aXRoIHRoaXNcbiAqIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uXG4gKi9cblxuaW1wb3J0IHsgcmVzb2x2ZSB9IGZyb20gXCJwYXRoXCJcbmltcG9ydCBkZWRlbnQgPSByZXF1aXJlKFwiZGVkZW50XCIpXG5pbXBvcnQgY2hhbGsgZnJvbSBcImNoYWxrXCJcblxuaW1wb3J0IHsgUGFyYW1ldGVyRXJyb3IgfSBmcm9tIFwiLi4vLi4vZXhjZXB0aW9uc1wiXG5pbXBvcnQge1xuICBDb21tYW5kLFxuICBDb21tYW5kUmVzdWx0LFxuICBTdHJpbmdQYXJhbWV0ZXIsXG4gIFBhdGhQYXJhbWV0ZXIsXG59IGZyb20gXCIuLi9iYXNlXCJcbmltcG9ydCB7IGFkZExpbmtlZFNvdXJjZXMgfSBmcm9tIFwiLi4vLi4vdXRpbC9leHQtc291cmNlLXV0aWxcIlxuaW1wb3J0IHsgTGlua2VkU291cmNlIH0gZnJvbSBcIi4uLy4uL2NvbmZpZy1zdG9yZVwiXG5pbXBvcnQgeyBDb21tYW5kUGFyYW1zIH0gZnJvbSBcIi4uL2Jhc2VcIlxuXG5jb25zdCBsaW5rU291cmNlQXJndW1lbnRzID0ge1xuICBzb3VyY2U6IG5ldyBTdHJpbmdQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiTmFtZSBvZiB0aGUgc291cmNlIHRvIGxpbmsgYXMgZGVjbGFyZWQgaW4gdGhlIHByb2plY3QgY29uZmlnLlwiLFxuICAgIHJlcXVpcmVkOiB0cnVlLFxuICB9KSxcbiAgcGF0aDogbmV3IFBhdGhQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiUGF0aCB0byB0aGUgbG9jYWwgZGlyZWN0b3J5IHRoYXQgY29udGFpbmVzIHRoZSBzb3VyY2UuXCIsXG4gICAgcmVxdWlyZWQ6IHRydWUsXG4gIH0pLFxufVxuXG50eXBlIEFyZ3MgPSB0eXBlb2YgbGlua1NvdXJjZUFyZ3VtZW50c1xuXG5leHBvcnQgY2xhc3MgTGlua1NvdXJjZUNvbW1hbmQgZXh0ZW5kcyBDb21tYW5kPEFyZ3M+IHtcbiAgbmFtZSA9IFwic291cmNlXCJcbiAgaGVscCA9IFwiTGluayBhIHJlbW90ZSBzb3VyY2UgdG8gYSBsb2NhbCBkaXJlY3RvcnkuXCJcbiAgYXJndW1lbnRzID0gbGlua1NvdXJjZUFyZ3VtZW50c1xuXG4gIGRlc2NyaXB0aW9uID0gZGVkZW50YFxuICAgIEFmdGVyIGxpbmtpbmcgYSByZW1vdGUgc291cmNlLCBHYXJkZW4gd2lsbCByZWFkIGl0IGZyb20gaXRzIGxvY2FsIGRpcmVjdG9yeSBpbnN0ZWFkIG9mXG4gICAgZnJvbSB0aGUgcmVtb3RlIFVSTC4gR2FyZGVuIGNhbiBvbmx5IGxpbmsgcmVtb3RlIHNvdXJjZXMgdGhhdCBoYXZlIGJlZW4gZGVjbGFyZWQgaW4gdGhlIHByb2plY3RcbiAgICBsZXZlbCBnYXJkZW4ueW1sIGNvbmZpZy5cblxuICAgIEV4YW1wbGVzOlxuXG4gICAgICAgIGdhcmRlbiBsaW5rIHNvdXJjZSBteS1zb3VyY2UgcGF0aC90by9teS1zb3VyY2UgIyBsaW5rcyBteS1zb3VyY2UgdG8gaXRzIGxvY2FsIHZlcnNpb24gYXQgdGhlIGdpdmVuIHBhdGhcbiAgYFxuXG4gIGFzeW5jIGFjdGlvbih7IGdhcmRlbiwgYXJncyB9OiBDb21tYW5kUGFyYW1zPEFyZ3M+KTogUHJvbWlzZTxDb21tYW5kUmVzdWx0PExpbmtlZFNvdXJjZVtdPj4ge1xuICAgIGdhcmRlbi5sb2cuaGVhZGVyKHsgZW1vamk6IFwibGlua1wiLCBjb21tYW5kOiBcImxpbmsgc291cmNlXCIgfSlcblxuICAgIGNvbnN0IHNvdXJjZVR5cGUgPSBcInByb2plY3RcIlxuXG4gICAgY29uc3QgeyBzb3VyY2U6IHNvdXJjZU5hbWUsIHBhdGggfSA9IGFyZ3NcbiAgICBjb25zdCBwcm9qZWN0U291cmNlVG9MaW5rID0gZ2FyZGVuLnByb2plY3RTb3VyY2VzLmZpbmQoc3JjID0+IHNyYy5uYW1lID09PSBzb3VyY2VOYW1lKVxuXG4gICAgaWYgKCFwcm9qZWN0U291cmNlVG9MaW5rKSB7XG4gICAgICBjb25zdCBhdmFpbGFibGVSZW1vdGVTb3VyY2VzID0gZ2FyZGVuLnByb2plY3RTb3VyY2VzLm1hcChzID0+IHMubmFtZSkuc29ydCgpXG5cbiAgICAgIHRocm93IG5ldyBQYXJhbWV0ZXJFcnJvcihcbiAgICAgICAgYFJlbW90ZSBzb3VyY2UgJHtjaGFsay51bmRlcmxpbmUoc291cmNlTmFtZSl9IG5vdCBmb3VuZCBpbiBwcm9qZWN0IGNvbmZpZy5gICtcbiAgICAgICAgYCBEaWQgeW91IG1lYW4gdG8gdXNlIHRoZSBcImxpbmsgbW9kdWxlXCIgY29tbWFuZD9gLFxuICAgICAgICB7XG4gICAgICAgICAgYXZhaWxhYmxlUmVtb3RlU291cmNlcyxcbiAgICAgICAgICBpbnB1dDogc291cmNlTmFtZSxcbiAgICAgICAgfSxcbiAgICAgIClcbiAgICB9XG5cbiAgICBjb25zdCBhYnNQYXRoID0gcmVzb2x2ZShnYXJkZW4ucHJvamVjdFJvb3QsIHBhdGgpXG5cbiAgICBjb25zdCBsaW5rZWRQcm9qZWN0U291cmNlcyA9IGF3YWl0IGFkZExpbmtlZFNvdXJjZXMoe1xuICAgICAgZ2FyZGVuLFxuICAgICAgc291cmNlVHlwZSxcbiAgICAgIHNvdXJjZXM6IFt7IG5hbWU6IHNvdXJjZU5hbWUsIHBhdGg6IGFic1BhdGggfV0sXG4gICAgfSlcblxuICAgIGdhcmRlbi5sb2cuaW5mbyhgTGlua2VkIHNvdXJjZSAke3NvdXJjZU5hbWV9YClcblxuICAgIHJldHVybiB7IHJlc3VsdDogbGlua2VkUHJvamVjdFNvdXJjZXMgfVxuICB9XG59XG4iXX0=
diff --git a/garden-service/build/commands/logs.d.ts b/garden-service/build/commands/logs.d.ts
new file mode 100644
index 00000000000..25751a86fee
--- /dev/null
+++ b/garden-service/build/commands/logs.d.ts
@@ -0,0 +1,26 @@
+import { BooleanParameter, Command, CommandResult, CommandParams, StringsParameter } from "./base";
+import { ServiceLogEntry } from "../types/plugin/outputs";
+import { LoggerType } from "../logger/logger";
+declare const logsArgs: {
+ service: StringsParameter;
+};
+declare const logsOpts: {
+ tail: BooleanParameter;
+};
+declare type Args = typeof logsArgs;
+declare type Opts = typeof logsOpts;
+export declare class LogsCommand extends Command {
+ name: string;
+ help: string;
+ description: string;
+ arguments: {
+ service: StringsParameter;
+ };
+ options: {
+ tail: BooleanParameter;
+ };
+ loggerType: LoggerType;
+ action({ garden, args, opts }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=logs.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/logs.js b/garden-service/build/commands/logs.js
new file mode 100644
index 00000000000..bbd42ece155
--- /dev/null
+++ b/garden-service/build/commands/logs.js
@@ -0,0 +1,87 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const base_1 = require("./base");
+const chalk_1 = require("chalk");
+const Bluebird = require("bluebird");
+const ts_stream_1 = require("ts-stream");
+const logger_1 = require("../logger/logger");
+const dedent = require("dedent");
+const logsArgs = {
+ service: new base_1.StringsParameter({
+ help: "The name of the service(s) to logs (skip to logs all services). " +
+ "Use comma as separator to specify multiple services.",
+ }),
+};
+const logsOpts = {
+ tail: new base_1.BooleanParameter({ help: "Continuously stream new logs from the service(s).", alias: "t" }),
+};
+class LogsCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "logs";
+ this.help = "Retrieves the most recent logs for the specified service(s).";
+ this.description = dedent `
+ Outputs logs for all or specified services, and optionally waits for news logs to come in.
+
+ Examples:
+
+ garden logs # prints latest logs from all services
+ garden logs my-service # prints latest logs for my-service
+ garden logs -t # keeps running and streams all incoming logs to the console
+ `;
+ this.arguments = logsArgs;
+ this.options = logsOpts;
+ this.loggerType = logger_1.LoggerType.basic;
+ }
+ action({ garden, args, opts }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const tail = opts.tail;
+ const services = yield garden.getServices(args.service);
+ const result = [];
+ const stream = new ts_stream_1.default();
+ // TODO: use basic logger (no need for fancy stuff here, just causes flickering)
+ void stream.forEach((entry) => {
+ // TODO: color each service differently for easier visual parsing
+ let timestamp = " ";
+ // bad timestamp values can cause crash if not caught
+ if (entry.timestamp) {
+ try {
+ timestamp = entry.timestamp.toISOString();
+ }
+ catch (_a) { }
+ }
+ garden.log.info({
+ section: entry.serviceName,
+ msg: [timestamp, chalk_1.default.white(entry.msg)],
+ });
+ if (!tail) {
+ result.push(entry);
+ }
+ });
+ // NOTE: This will work differently when we have Elasticsearch set up for logging, but is
+ // quite servicable for now.
+ yield Bluebird.map(services, (service) => __awaiter(this, void 0, void 0, function* () {
+ yield garden.actions.getServiceLogs({ service, stream, tail });
+ }));
+ return { result };
+ });
+ }
+}
+exports.LogsCommand = LogsCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL2xvZ3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7R0FNRzs7Ozs7Ozs7OztBQUVILGlDQU1lO0FBQ2YsaUNBQXlCO0FBRXpCLHFDQUFxQztBQUVyQyx5Q0FBOEI7QUFDOUIsNkNBQTZDO0FBQzdDLGlDQUFpQztBQUVqQyxNQUFNLFFBQVEsR0FBRztJQUNmLE9BQU8sRUFBRSxJQUFJLHVCQUFnQixDQUFDO1FBQzVCLElBQUksRUFBRSxrRUFBa0U7WUFDdEUsc0RBQXNEO0tBQ3pELENBQUM7Q0FDSCxDQUFBO0FBRUQsTUFBTSxRQUFRLEdBQUc7SUFDZixJQUFJLEVBQUUsSUFBSSx1QkFBZ0IsQ0FBQyxFQUFFLElBQUksRUFBRSxtREFBbUQsRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLENBQUM7Q0FHdEcsQ0FBQTtBQUtELE1BQWEsV0FBWSxTQUFRLGNBQW1CO0lBQXBEOztRQUNFLFNBQUksR0FBRyxNQUFNLENBQUE7UUFDYixTQUFJLEdBQUcsOERBQThELENBQUE7UUFFckUsZ0JBQVcsR0FBRyxNQUFNLENBQUE7Ozs7Ozs7O0dBUW5CLENBQUE7UUFFRCxjQUFTLEdBQUcsUUFBUSxDQUFBO1FBQ3BCLFlBQU8sR0FBRyxRQUFRLENBQUE7UUFDbEIsZUFBVSxHQUFHLG1CQUFVLENBQUMsS0FBSyxDQUFBO0lBdUMvQixDQUFDO0lBckNPLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUE2Qjs7WUFDNUQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQTtZQUN0QixNQUFNLFFBQVEsR0FBRyxNQUFNLE1BQU0sQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFBO1lBRXZELE1BQU0sTUFBTSxHQUFzQixFQUFFLENBQUE7WUFDcEMsTUFBTSxNQUFNLEdBQUcsSUFBSSxtQkFBTSxFQUFtQixDQUFBO1lBRTVDLGdGQUFnRjtZQUNoRixLQUFLLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRTtnQkFDNUIsaUVBQWlFO2dCQUNqRSxJQUFJLFNBQVMsR0FBRywwQkFBMEIsQ0FBQTtnQkFFMUMscURBQXFEO2dCQUNyRCxJQUFJLEtBQUssQ0FBQyxTQUFTLEVBQUU7b0JBQ25CLElBQUk7d0JBQ0YsU0FBUyxHQUFHLEtBQUssQ0FBQyxTQUFTLENBQUMsV0FBVyxFQUFFLENBQUE7cUJBQzFDO29CQUFDLFdBQU0sR0FBRztpQkFDWjtnQkFFRCxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQztvQkFDZCxPQUFPLEVBQUUsS0FBSyxDQUFDLFdBQVc7b0JBQzFCLEdBQUcsRUFBRSxDQUFDLFNBQVMsRUFBRSxlQUFLLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztpQkFDekMsQ0FBQyxDQUFBO2dCQUVGLElBQUksQ0FBQyxJQUFJLEVBQUU7b0JBQ1QsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQTtpQkFDbkI7WUFDSCxDQUFDLENBQUMsQ0FBQTtZQUVGLHlGQUF5RjtZQUN6RixrQ0FBa0M7WUFDbEMsTUFBTSxRQUFRLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFPLE9BQXFCLEVBQUUsRUFBRTtnQkFDM0QsTUFBTSxNQUFNLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtZQUNoRSxDQUFDLENBQUEsQ0FBQyxDQUFBO1lBRUYsT0FBTyxFQUFFLE1BQU0sRUFBRSxDQUFBO1FBQ25CLENBQUM7S0FBQTtDQUNGO0FBdkRELGtDQXVEQyIsImZpbGUiOiJjb21tYW5kcy9sb2dzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoQykgMjAxOCBHYXJkZW4gVGVjaG5vbG9naWVzLCBJbmMuIDxpbmZvQGdhcmRlbi5pbz5cbiAqXG4gKiBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljXG4gKiBMaWNlbnNlLCB2LiAyLjAuIElmIGEgY29weSBvZiB0aGUgTVBMIHdhcyBub3QgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzXG4gKiBmaWxlLCBZb3UgY2FuIG9idGFpbiBvbmUgYXQgaHR0cDovL21vemlsbGEub3JnL01QTC8yLjAvLlxuICovXG5cbmltcG9ydCB7XG4gIEJvb2xlYW5QYXJhbWV0ZXIsXG4gIENvbW1hbmQsXG4gIENvbW1hbmRSZXN1bHQsXG4gIENvbW1hbmRQYXJhbXMsXG4gIFN0cmluZ3NQYXJhbWV0ZXIsXG59IGZyb20gXCIuL2Jhc2VcIlxuaW1wb3J0IGNoYWxrIGZyb20gXCJjaGFsa1wiXG5pbXBvcnQgeyBTZXJ2aWNlTG9nRW50cnkgfSBmcm9tIFwiLi4vdHlwZXMvcGx1Z2luL291dHB1dHNcIlxuaW1wb3J0IEJsdWViaXJkID0gcmVxdWlyZShcImJsdWViaXJkXCIpXG5pbXBvcnQgeyBTZXJ2aWNlIH0gZnJvbSBcIi4uL3R5cGVzL3NlcnZpY2VcIlxuaW1wb3J0IFN0cmVhbSBmcm9tIFwidHMtc3RyZWFtXCJcbmltcG9ydCB7IExvZ2dlclR5cGUgfSBmcm9tIFwiLi4vbG9nZ2VyL2xvZ2dlclwiXG5pbXBvcnQgZGVkZW50ID0gcmVxdWlyZShcImRlZGVudFwiKVxuXG5jb25zdCBsb2dzQXJncyA9IHtcbiAgc2VydmljZTogbmV3IFN0cmluZ3NQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiVGhlIG5hbWUgb2YgdGhlIHNlcnZpY2UocykgdG8gbG9ncyAoc2tpcCB0byBsb2dzIGFsbCBzZXJ2aWNlcykuIFwiICtcbiAgICAgIFwiVXNlIGNvbW1hIGFzIHNlcGFyYXRvciB0byBzcGVjaWZ5IG11bHRpcGxlIHNlcnZpY2VzLlwiLFxuICB9KSxcbn1cblxuY29uc3QgbG9nc09wdHMgPSB7XG4gIHRhaWw6IG5ldyBCb29sZWFuUGFyYW1ldGVyKHsgaGVscDogXCJDb250aW51b3VzbHkgc3RyZWFtIG5ldyBsb2dzIGZyb20gdGhlIHNlcnZpY2UocykuXCIsIGFsaWFzOiBcInRcIiB9KSxcbiAgLy8gVE9ET1xuICAvLyBzaW5jZTogbmV3IE1vbWVudFBhcmFtZXRlcih7IGhlbHA6IFwiUmV0cmlldmUgbG9ncyBmcm9tIHRoZSBzcGVjaWZpZWQgcG9pbnQgb253YXJkc1wiIH0pLFxufVxuXG50eXBlIEFyZ3MgPSB0eXBlb2YgbG9nc0FyZ3NcbnR5cGUgT3B0cyA9IHR5cGVvZiBsb2dzT3B0c1xuXG5leHBvcnQgY2xhc3MgTG9nc0NvbW1hbmQgZXh0ZW5kcyBDb21tYW5kPEFyZ3MsIE9wdHM+IHtcbiAgbmFtZSA9IFwibG9nc1wiXG4gIGhlbHAgPSBcIlJldHJpZXZlcyB0aGUgbW9zdCByZWNlbnQgbG9ncyBmb3IgdGhlIHNwZWNpZmllZCBzZXJ2aWNlKHMpLlwiXG5cbiAgZGVzY3JpcHRpb24gPSBkZWRlbnRgXG4gICAgT3V0cHV0cyBsb2dzIGZvciBhbGwgb3Igc3BlY2lmaWVkIHNlcnZpY2VzLCBhbmQgb3B0aW9uYWxseSB3YWl0cyBmb3IgbmV3cyBsb2dzIHRvIGNvbWUgaW4uXG5cbiAgICBFeGFtcGxlczpcblxuICAgICAgICBnYXJkZW4gbG9ncyAgICAgICAgICAgICAgICMgcHJpbnRzIGxhdGVzdCBsb2dzIGZyb20gYWxsIHNlcnZpY2VzXG4gICAgICAgIGdhcmRlbiBsb2dzIG15LXNlcnZpY2UgICAgIyBwcmludHMgbGF0ZXN0IGxvZ3MgZm9yIG15LXNlcnZpY2VcbiAgICAgICAgZ2FyZGVuIGxvZ3MgLXQgICAgICAgICAgICAjIGtlZXBzIHJ1bm5pbmcgYW5kIHN0cmVhbXMgYWxsIGluY29taW5nIGxvZ3MgdG8gdGhlIGNvbnNvbGVcbiAgYFxuXG4gIGFyZ3VtZW50cyA9IGxvZ3NBcmdzXG4gIG9wdGlvbnMgPSBsb2dzT3B0c1xuICBsb2dnZXJUeXBlID0gTG9nZ2VyVHlwZS5iYXNpY1xuXG4gIGFzeW5jIGFjdGlvbih7IGdhcmRlbiwgYXJncywgb3B0cyB9OiBDb21tYW5kUGFyYW1zPEFyZ3MsIE9wdHM+KTogUHJvbWlzZTxDb21tYW5kUmVzdWx0PFNlcnZpY2VMb2dFbnRyeVtdPj4ge1xuICAgIGNvbnN0IHRhaWwgPSBvcHRzLnRhaWxcbiAgICBjb25zdCBzZXJ2aWNlcyA9IGF3YWl0IGdhcmRlbi5nZXRTZXJ2aWNlcyhhcmdzLnNlcnZpY2UpXG5cbiAgICBjb25zdCByZXN1bHQ6IFNlcnZpY2VMb2dFbnRyeVtdID0gW11cbiAgICBjb25zdCBzdHJlYW0gPSBuZXcgU3RyZWFtPFNlcnZpY2VMb2dFbnRyeT4oKVxuXG4gICAgLy8gVE9ETzogdXNlIGJhc2ljIGxvZ2dlciAobm8gbmVlZCBmb3IgZmFuY3kgc3R1ZmYgaGVyZSwganVzdCBjYXVzZXMgZmxpY2tlcmluZylcbiAgICB2b2lkIHN0cmVhbS5mb3JFYWNoKChlbnRyeSkgPT4ge1xuICAgICAgLy8gVE9ETzogY29sb3IgZWFjaCBzZXJ2aWNlIGRpZmZlcmVudGx5IGZvciBlYXNpZXIgdmlzdWFsIHBhcnNpbmdcbiAgICAgIGxldCB0aW1lc3RhbXAgPSBcIiAgICAgICAgICAgICAgICAgICAgICAgIFwiXG5cbiAgICAgIC8vIGJhZCB0aW1lc3RhbXAgdmFsdWVzIGNhbiBjYXVzZSBjcmFzaCBpZiBub3QgY2F1Z2h0XG4gICAgICBpZiAoZW50cnkudGltZXN0YW1wKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgdGltZXN0YW1wID0gZW50cnkudGltZXN0YW1wLnRvSVNPU3RyaW5nKClcbiAgICAgICAgfSBjYXRjaCB7IH1cbiAgICAgIH1cblxuICAgICAgZ2FyZGVuLmxvZy5pbmZvKHtcbiAgICAgICAgc2VjdGlvbjogZW50cnkuc2VydmljZU5hbWUsXG4gICAgICAgIG1zZzogW3RpbWVzdGFtcCwgY2hhbGsud2hpdGUoZW50cnkubXNnKV0sXG4gICAgICB9KVxuXG4gICAgICBpZiAoIXRhaWwpIHtcbiAgICAgICAgcmVzdWx0LnB1c2goZW50cnkpXG4gICAgICB9XG4gICAgfSlcblxuICAgIC8vIE5PVEU6IFRoaXMgd2lsbCB3b3JrIGRpZmZlcmVudGx5IHdoZW4gd2UgaGF2ZSBFbGFzdGljc2VhcmNoIHNldCB1cCBmb3IgbG9nZ2luZywgYnV0IGlzXG4gICAgLy8gICAgICAgcXVpdGUgc2VydmljYWJsZSBmb3Igbm93LlxuICAgIGF3YWl0IEJsdWViaXJkLm1hcChzZXJ2aWNlcywgYXN5bmMgKHNlcnZpY2U6IFNlcnZpY2U8YW55PikgPT4ge1xuICAgICAgYXdhaXQgZ2FyZGVuLmFjdGlvbnMuZ2V0U2VydmljZUxvZ3MoeyBzZXJ2aWNlLCBzdHJlYW0sIHRhaWwgfSlcbiAgICB9KVxuXG4gICAgcmV0dXJuIHsgcmVzdWx0IH1cbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/publish.d.ts b/garden-service/build/commands/publish.d.ts
new file mode 100644
index 00000000000..1e360cc3fb5
--- /dev/null
+++ b/garden-service/build/commands/publish.d.ts
@@ -0,0 +1,29 @@
+import { BooleanParameter, Command, CommandParams, CommandResult, StringsParameter } from "./base";
+import { Module } from "../types/module";
+import { TaskResults } from "../task-graph";
+import { Garden } from "../garden";
+declare const publishArgs: {
+ module: StringsParameter;
+};
+declare const publishOpts: {
+ "force-build": BooleanParameter;
+ "allow-dirty": BooleanParameter;
+};
+declare type Args = typeof publishArgs;
+declare type Opts = typeof publishOpts;
+export declare class PublishCommand extends Command {
+ name: string;
+ help: string;
+ description: string;
+ arguments: {
+ module: StringsParameter;
+ };
+ options: {
+ "force-build": BooleanParameter;
+ "allow-dirty": BooleanParameter;
+ };
+ action({ garden, args, opts }: CommandParams): Promise>;
+}
+export declare function publishModules(garden: Garden, modules: Module[], forceBuild: boolean, allowDirty: boolean): Promise;
+export {};
+//# sourceMappingURL=publish.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/publish.js b/garden-service/build/commands/publish.js
new file mode 100644
index 00000000000..f0793a7d5ab
--- /dev/null
+++ b/garden-service/build/commands/publish.js
@@ -0,0 +1,81 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const base_1 = require("./base");
+const publish_1 = require("../tasks/publish");
+const exceptions_1 = require("../exceptions");
+const dedent = require("dedent");
+const publishArgs = {
+ module: new base_1.StringsParameter({
+ help: "The name of the module(s) to publish (skip to publish all modules). " +
+ "Use comma as separator to specify multiple modules.",
+ }),
+};
+const publishOpts = {
+ "force-build": new base_1.BooleanParameter({
+ help: "Force rebuild of module(s) before publishing.",
+ }),
+ "allow-dirty": new base_1.BooleanParameter({
+ help: "Allow publishing dirty builds (with untracked/uncommitted changes).",
+ }),
+};
+class PublishCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "publish";
+ this.help = "Build and publish module(s) to a remote registry.";
+ this.description = dedent `
+ Publishes built module artifacts for all or specified modules.
+ Also builds modules and dependencies if needed.
+
+ Examples:
+
+ garden publish # publish artifacts for all modules in the project
+ garden publish my-container # only publish my-container
+ garden publish --force-build # force re-build of modules before publishing artifacts
+ garden publish --allow-dirty # allow publishing dirty builds (which by default triggers error)
+ `;
+ this.arguments = publishArgs;
+ this.options = publishOpts;
+ }
+ action({ garden, args, opts }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ garden.log.header({ emoji: "rocket", command: "Publish modules" });
+ const modules = yield garden.getModules(args.module);
+ const results = yield publishModules(garden, modules, !!opts["force-build"], !!opts["allow-dirty"]);
+ return base_1.handleTaskResults(garden, "publish", { taskResults: results });
+ });
+ }
+}
+exports.PublishCommand = PublishCommand;
+function publishModules(garden, modules, forceBuild, allowDirty) {
+ return __awaiter(this, void 0, void 0, function* () {
+ for (const module of modules) {
+ const version = module.version;
+ if (version.dirtyTimestamp && !allowDirty) {
+ throw new exceptions_1.RuntimeError(`Module ${module.name} has uncommitted changes. ` +
+ `Please commit them, clean the module's source tree or set the --allow-dirty flag to override.`, { moduleName: module.name, version });
+ }
+ const task = new publish_1.PublishTask({ garden, module, forceBuild });
+ yield garden.addTask(task);
+ }
+ return yield garden.processTasks();
+ });
+}
+exports.publishModules = publishModules;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL3B1Ymxpc2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7R0FNRzs7Ozs7Ozs7OztBQUVILGlDQU9lO0FBRWYsOENBQThDO0FBQzlDLDhDQUE0QztBQUc1QyxpQ0FBaUM7QUFFakMsTUFBTSxXQUFXLEdBQUc7SUFDbEIsTUFBTSxFQUFFLElBQUksdUJBQWdCLENBQUM7UUFDM0IsSUFBSSxFQUFFLHNFQUFzRTtZQUMxRSxxREFBcUQ7S0FDeEQsQ0FBQztDQUNILENBQUE7QUFFRCxNQUFNLFdBQVcsR0FBRztJQUNsQixhQUFhLEVBQUUsSUFBSSx1QkFBZ0IsQ0FBQztRQUNsQyxJQUFJLEVBQUUsK0NBQStDO0tBQ3RELENBQUM7SUFDRixhQUFhLEVBQUUsSUFBSSx1QkFBZ0IsQ0FBQztRQUNsQyxJQUFJLEVBQUUscUVBQXFFO0tBQzVFLENBQUM7Q0FDSCxDQUFBO0FBS0QsTUFBYSxjQUFlLFNBQVEsY0FBbUI7SUFBdkQ7O1FBQ0UsU0FBSSxHQUFHLFNBQVMsQ0FBQTtRQUNoQixTQUFJLEdBQUcsbURBQW1ELENBQUE7UUFFMUQsZ0JBQVcsR0FBRyxNQUFNLENBQUE7Ozs7Ozs7Ozs7R0FVbkIsQ0FBQTtRQUVELGNBQVMsR0FBRyxXQUFXLENBQUE7UUFDdkIsWUFBTyxHQUFHLFdBQVcsQ0FBQTtJQVd2QixDQUFDO0lBVE8sTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQTZCOztZQUM1RCxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLENBQUMsQ0FBQTtZQUVsRSxNQUFNLE9BQU8sR0FBRyxNQUFNLE1BQU0sQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFBO1lBRXBELE1BQU0sT0FBTyxHQUFHLE1BQU0sY0FBYyxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUE7WUFFbkcsT0FBTyx3QkFBaUIsQ0FBQyxNQUFNLEVBQUUsU0FBUyxFQUFFLEVBQUUsV0FBVyxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUE7UUFDdkUsQ0FBQztLQUFBO0NBQ0Y7QUE1QkQsd0NBNEJDO0FBRUQsU0FBc0IsY0FBYyxDQUNsQyxNQUFjLEVBQ2QsT0FBc0IsRUFDdEIsVUFBbUIsRUFDbkIsVUFBbUI7O1FBRW5CLEtBQUssTUFBTSxNQUFNLElBQUksT0FBTyxFQUFFO1lBQzVCLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUE7WUFFOUIsSUFBSSxPQUFPLENBQUMsY0FBYyxJQUFJLENBQUMsVUFBVSxFQUFFO2dCQUN6QyxNQUFNLElBQUkseUJBQVksQ0FDcEIsVUFBVSxNQUFNLENBQUMsSUFBSSw0QkFBNEI7b0JBQ2pELCtGQUErRixFQUMvRixFQUFFLFVBQVUsRUFBRSxNQUFNLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxDQUNyQyxDQUFBO2FBQ0Y7WUFFRCxNQUFNLElBQUksR0FBRyxJQUFJLHFCQUFXLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUE7WUFDNUQsTUFBTSxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFBO1NBQzNCO1FBRUQsT0FBTyxNQUFNLE1BQU0sQ0FBQyxZQUFZLEVBQUUsQ0FBQTtJQUNwQyxDQUFDO0NBQUE7QUF0QkQsd0NBc0JDIiwiZmlsZSI6ImNvbW1hbmRzL3B1Ymxpc2guanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChDKSAyMDE4IEdhcmRlbiBUZWNobm9sb2dpZXMsIEluYy4gPGluZm9AZ2FyZGVuLmlvPlxuICpcbiAqIFRoaXMgU291cmNlIENvZGUgRm9ybSBpcyBzdWJqZWN0IHRvIHRoZSB0ZXJtcyBvZiB0aGUgTW96aWxsYSBQdWJsaWNcbiAqIExpY2Vuc2UsIHYuIDIuMC4gSWYgYSBjb3B5IG9mIHRoZSBNUEwgd2FzIG5vdCBkaXN0cmlidXRlZCB3aXRoIHRoaXNcbiAqIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uXG4gKi9cblxuaW1wb3J0IHtcbiAgQm9vbGVhblBhcmFtZXRlcixcbiAgQ29tbWFuZCxcbiAgQ29tbWFuZFBhcmFtcyxcbiAgQ29tbWFuZFJlc3VsdCxcbiAgaGFuZGxlVGFza1Jlc3VsdHMsXG4gIFN0cmluZ3NQYXJhbWV0ZXIsXG59IGZyb20gXCIuL2Jhc2VcIlxuaW1wb3J0IHsgTW9kdWxlIH0gZnJvbSBcIi4uL3R5cGVzL21vZHVsZVwiXG5pbXBvcnQgeyBQdWJsaXNoVGFzayB9IGZyb20gXCIuLi90YXNrcy9wdWJsaXNoXCJcbmltcG9ydCB7IFJ1bnRpbWVFcnJvciB9IGZyb20gXCIuLi9leGNlcHRpb25zXCJcbmltcG9ydCB7IFRhc2tSZXN1bHRzIH0gZnJvbSBcIi4uL3Rhc2stZ3JhcGhcIlxuaW1wb3J0IHsgR2FyZGVuIH0gZnJvbSBcIi4uL2dhcmRlblwiXG5pbXBvcnQgZGVkZW50ID0gcmVxdWlyZShcImRlZGVudFwiKVxuXG5jb25zdCBwdWJsaXNoQXJncyA9IHtcbiAgbW9kdWxlOiBuZXcgU3RyaW5nc1BhcmFtZXRlcih7XG4gICAgaGVscDogXCJUaGUgbmFtZSBvZiB0aGUgbW9kdWxlKHMpIHRvIHB1Ymxpc2ggKHNraXAgdG8gcHVibGlzaCBhbGwgbW9kdWxlcykuIFwiICtcbiAgICAgIFwiVXNlIGNvbW1hIGFzIHNlcGFyYXRvciB0byBzcGVjaWZ5IG11bHRpcGxlIG1vZHVsZXMuXCIsXG4gIH0pLFxufVxuXG5jb25zdCBwdWJsaXNoT3B0cyA9IHtcbiAgXCJmb3JjZS1idWlsZFwiOiBuZXcgQm9vbGVhblBhcmFtZXRlcih7XG4gICAgaGVscDogXCJGb3JjZSByZWJ1aWxkIG9mIG1vZHVsZShzKSBiZWZvcmUgcHVibGlzaGluZy5cIixcbiAgfSksXG4gIFwiYWxsb3ctZGlydHlcIjogbmV3IEJvb2xlYW5QYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiQWxsb3cgcHVibGlzaGluZyBkaXJ0eSBidWlsZHMgKHdpdGggdW50cmFja2VkL3VuY29tbWl0dGVkIGNoYW5nZXMpLlwiLFxuICB9KSxcbn1cblxudHlwZSBBcmdzID0gdHlwZW9mIHB1Ymxpc2hBcmdzXG50eXBlIE9wdHMgPSB0eXBlb2YgcHVibGlzaE9wdHNcblxuZXhwb3J0IGNsYXNzIFB1Ymxpc2hDb21tYW5kIGV4dGVuZHMgQ29tbWFuZDxBcmdzLCBPcHRzPiB7XG4gIG5hbWUgPSBcInB1Ymxpc2hcIlxuICBoZWxwID0gXCJCdWlsZCBhbmQgcHVibGlzaCBtb2R1bGUocykgdG8gYSByZW1vdGUgcmVnaXN0cnkuXCJcblxuICBkZXNjcmlwdGlvbiA9IGRlZGVudGBcbiAgICBQdWJsaXNoZXMgYnVpbHQgbW9kdWxlIGFydGlmYWN0cyBmb3IgYWxsIG9yIHNwZWNpZmllZCBtb2R1bGVzLlxuICAgIEFsc28gYnVpbGRzIG1vZHVsZXMgYW5kIGRlcGVuZGVuY2llcyBpZiBuZWVkZWQuXG5cbiAgICBFeGFtcGxlczpcblxuICAgICAgICBnYXJkZW4gcHVibGlzaCAgICAgICAgICAgICAgICAjIHB1Ymxpc2ggYXJ0aWZhY3RzIGZvciBhbGwgbW9kdWxlcyBpbiB0aGUgcHJvamVjdFxuICAgICAgICBnYXJkZW4gcHVibGlzaCBteS1jb250YWluZXIgICAjIG9ubHkgcHVibGlzaCBteS1jb250YWluZXJcbiAgICAgICAgZ2FyZGVuIHB1Ymxpc2ggLS1mb3JjZS1idWlsZCAgIyBmb3JjZSByZS1idWlsZCBvZiBtb2R1bGVzIGJlZm9yZSBwdWJsaXNoaW5nIGFydGlmYWN0c1xuICAgICAgICBnYXJkZW4gcHVibGlzaCAtLWFsbG93LWRpcnR5ICAjIGFsbG93IHB1Ymxpc2hpbmcgZGlydHkgYnVpbGRzICh3aGljaCBieSBkZWZhdWx0IHRyaWdnZXJzIGVycm9yKVxuICBgXG5cbiAgYXJndW1lbnRzID0gcHVibGlzaEFyZ3NcbiAgb3B0aW9ucyA9IHB1Ymxpc2hPcHRzXG5cbiAgYXN5bmMgYWN0aW9uKHsgZ2FyZGVuLCBhcmdzLCBvcHRzIH06IENvbW1hbmRQYXJhbXM8QXJncywgT3B0cz4pOiBQcm9taXNlPENvbW1hbmRSZXN1bHQ8VGFza1Jlc3VsdHM+PiB7XG4gICAgZ2FyZGVuLmxvZy5oZWFkZXIoeyBlbW9qaTogXCJyb2NrZXRcIiwgY29tbWFuZDogXCJQdWJsaXNoIG1vZHVsZXNcIiB9KVxuXG4gICAgY29uc3QgbW9kdWxlcyA9IGF3YWl0IGdhcmRlbi5nZXRNb2R1bGVzKGFyZ3MubW9kdWxlKVxuXG4gICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IHB1Ymxpc2hNb2R1bGVzKGdhcmRlbiwgbW9kdWxlcywgISFvcHRzW1wiZm9yY2UtYnVpbGRcIl0sICEhb3B0c1tcImFsbG93LWRpcnR5XCJdKVxuXG4gICAgcmV0dXJuIGhhbmRsZVRhc2tSZXN1bHRzKGdhcmRlbiwgXCJwdWJsaXNoXCIsIHsgdGFza1Jlc3VsdHM6IHJlc3VsdHMgfSlcbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcHVibGlzaE1vZHVsZXMoXG4gIGdhcmRlbjogR2FyZGVuLFxuICBtb2R1bGVzOiBNb2R1bGU8YW55PltdLFxuICBmb3JjZUJ1aWxkOiBib29sZWFuLFxuICBhbGxvd0RpcnR5OiBib29sZWFuLFxuKTogUHJvbWlzZTxUYXNrUmVzdWx0cz4ge1xuICBmb3IgKGNvbnN0IG1vZHVsZSBvZiBtb2R1bGVzKSB7XG4gICAgY29uc3QgdmVyc2lvbiA9IG1vZHVsZS52ZXJzaW9uXG5cbiAgICBpZiAodmVyc2lvbi5kaXJ0eVRpbWVzdGFtcCAmJiAhYWxsb3dEaXJ0eSkge1xuICAgICAgdGhyb3cgbmV3IFJ1bnRpbWVFcnJvcihcbiAgICAgICAgYE1vZHVsZSAke21vZHVsZS5uYW1lfSBoYXMgdW5jb21taXR0ZWQgY2hhbmdlcy4gYCArXG4gICAgICAgIGBQbGVhc2UgY29tbWl0IHRoZW0sIGNsZWFuIHRoZSBtb2R1bGUncyBzb3VyY2UgdHJlZSBvciBzZXQgdGhlIC0tYWxsb3ctZGlydHkgZmxhZyB0byBvdmVycmlkZS5gLFxuICAgICAgICB7IG1vZHVsZU5hbWU6IG1vZHVsZS5uYW1lLCB2ZXJzaW9uIH0sXG4gICAgICApXG4gICAgfVxuXG4gICAgY29uc3QgdGFzayA9IG5ldyBQdWJsaXNoVGFzayh7IGdhcmRlbiwgbW9kdWxlLCBmb3JjZUJ1aWxkIH0pXG4gICAgYXdhaXQgZ2FyZGVuLmFkZFRhc2sodGFzaylcbiAgfVxuXG4gIHJldHVybiBhd2FpdCBnYXJkZW4ucHJvY2Vzc1Rhc2tzKClcbn1cbiJdfQ==
diff --git a/garden-service/build/commands/run/module.d.ts b/garden-service/build/commands/run/module.d.ts
new file mode 100644
index 00000000000..427e2d0f3cc
--- /dev/null
+++ b/garden-service/build/commands/run/module.d.ts
@@ -0,0 +1,29 @@
+import { RunResult } from "../../types/plugin/outputs";
+import { BooleanParameter, Command, CommandParams, StringParameter, CommandResult, StringsParameter } from "../base";
+declare const runArgs: {
+ module: StringParameter;
+ command: StringsParameter;
+};
+declare const runOpts: {
+ interactive: BooleanParameter;
+ "force-build": BooleanParameter;
+};
+declare type Args = typeof runArgs;
+declare type Opts = typeof runOpts;
+export declare class RunModuleCommand extends Command {
+ name: string;
+ alias: string;
+ help: string;
+ description: string;
+ arguments: {
+ module: StringParameter;
+ command: StringsParameter;
+ };
+ options: {
+ interactive: BooleanParameter;
+ "force-build": BooleanParameter;
+ };
+ action({ garden, args, opts }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=module.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/run/module.js b/garden-service/build/commands/run/module.js
new file mode 100644
index 00000000000..ca0cae47cbe
--- /dev/null
+++ b/garden-service/build/commands/run/module.js
@@ -0,0 +1,97 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const chalk_1 = require("chalk");
+const build_1 = require("../../tasks/build");
+const base_1 = require("../base");
+const lodash_1 = require("lodash");
+const run_1 = require("./run");
+const dedent = require("dedent");
+const service_1 = require("../../types/service");
+const runArgs = {
+ module: new base_1.StringParameter({
+ help: "The name of the module to run.",
+ required: true,
+ }),
+ // TODO: make this a variadic arg
+ command: new base_1.StringsParameter({
+ help: "The command to run in the module.",
+ }),
+};
+const runOpts = {
+ // TODO: we could provide specific parameters like this by adding commands for specific modules, via plugins
+ //entrypoint: new StringParameter({ help: "Override default entrypoint in module" }),
+ interactive: new base_1.BooleanParameter({
+ help: "Set to false to skip interactive mode and just output the command result.",
+ defaultValue: true,
+ }),
+ "force-build": new base_1.BooleanParameter({ help: "Force rebuild of module before running." }),
+};
+class RunModuleCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "module";
+ this.alias = "m";
+ this.help = "Run an ad-hoc instance of a module.";
+ this.description = dedent `
+ This is useful for debugging or ad-hoc experimentation with modules.
+
+ Examples:
+
+ garden run module my-container # run an ad-hoc instance of a my-container container and attach to it
+ garden run module my-container /bin/sh # run an interactive shell in a new my-container container
+ garden run module my-container --i=false /some/script # execute a script in my-container and return the output
+ `;
+ this.arguments = runArgs;
+ this.options = runOpts;
+ }
+ action({ garden, args, opts }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const moduleName = args.module;
+ const module = yield garden.getModule(moduleName);
+ const msg = args.command
+ ? `Running command ${chalk_1.default.white(args.command.join(" "))} in module ${chalk_1.default.white(moduleName)}`
+ : `Running module ${chalk_1.default.white(moduleName)}`;
+ garden.log.header({
+ emoji: "runner",
+ command: msg,
+ });
+ yield garden.actions.prepareEnvironment({});
+ const buildTask = new build_1.BuildTask({ garden, module, force: opts["force-build"] });
+ yield garden.addTask(buildTask);
+ yield garden.processTasks();
+ const command = args.command || [];
+ // combine all dependencies for all services in the module, to be sure we have all the context we need
+ const depNames = lodash_1.uniq(lodash_1.flatten(module.serviceConfigs.map(s => s.dependencies)));
+ const deps = yield garden.getServices(depNames);
+ const runtimeContext = yield service_1.prepareRuntimeContext(garden, module, deps);
+ run_1.printRuntimeContext(garden, runtimeContext);
+ garden.log.info("");
+ const result = yield garden.actions.runModule({
+ module,
+ command,
+ runtimeContext,
+ silent: false,
+ interactive: opts.interactive,
+ });
+ return { result };
+ });
+ }
+}
+exports.RunModuleCommand = RunModuleCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,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LEdBQUcsTUFBTSwrQkFBcUIsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFBO1lBRXhFLHlCQUFtQixDQUFDLE1BQU0sRUFBRSxjQUFjLENBQUMsQ0FBQTtZQUUzQyxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQTtZQUVuQixNQUFNLE1BQU0sR0FBRyxNQUFNLE1BQU0sQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDO2dCQUM1QyxNQUFNO2dCQUNOLE9BQU87Z0JBQ1AsY0FBYztnQkFDZCxNQUFNLEVBQUUsS0FBSztnQkFDYixXQUFXLEVBQUUsSUFBSSxDQUFDLFdBQVc7YUFDOUIsQ0FBQyxDQUFBO1lBRUYsT0FBTyxFQUFFLE1BQU0sRUFBRSxDQUFBO1FBQ25CLENBQUM7S0FBQTtDQUNGO0FBM0RELDRDQTJEQyIsImZpbGUiOiJjb21tYW5kcy9ydW4vbW9kdWxlLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoQykgMjAxOCBHYXJkZW4gVGVjaG5vbG9naWVzLCBJbmMuIDxpbmZvQGdhcmRlbi5pbz5cbiAqXG4gKiBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljXG4gKiBMaWNlbnNlLCB2LiAyLjAuIElmIGEgY29weSBvZiB0aGUgTVBMIHdhcyBub3QgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzXG4gKiBmaWxlLCBZb3UgY2FuIG9idGFpbiBvbmUgYXQgaHR0cDovL21vemlsbGEub3JnL01QTC8yLjAvLlxuICovXG5cbmltcG9ydCBjaGFsayBmcm9tIFwiY2hhbGtcIlxuaW1wb3J0IHsgQnVpbGRUYXNrIH0gZnJvbSBcIi4uLy4uL3Rhc2tzL2J1aWxkXCJcbmltcG9ydCB7IFJ1blJlc3VsdCB9IGZyb20gXCIuLi8uLi90eXBlcy9wbHVnaW4vb3V0cHV0c1wiXG5pbXBvcnQge1xuICBCb29sZWFuUGFyYW1ldGVyLFxuICBDb21tYW5kLFxuICBDb21tYW5kUGFyYW1zLFxuICBTdHJpbmdQYXJhbWV0ZXIsXG4gIENvbW1hbmRSZXN1bHQsXG4gIFN0cmluZ3NQYXJhbWV0ZXIsXG59IGZyb20gXCIuLi9iYXNlXCJcbmltcG9ydCB7XG4gIHVuaXEsXG4gIGZsYXR0ZW4sXG59IGZyb20gXCJsb2Rhc2hcIlxuaW1wb3J0IHsgcHJpbnRSdW50aW1lQ29udGV4dCB9IGZyb20gXCIuL3J1blwiXG5pbXBvcnQgZGVkZW50ID0gcmVxdWlyZShcImRlZGVudFwiKVxuaW1wb3J0IHsgcHJlcGFyZVJ1bnRpbWVDb250ZXh0IH0gZnJvbSBcIi4uLy4uL3R5cGVzL3NlcnZpY2VcIlxuXG5jb25zdCBydW5BcmdzID0ge1xuICBtb2R1bGU6IG5ldyBTdHJpbmdQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiVGhlIG5hbWUgb2YgdGhlIG1vZHVsZSB0byBydW4uXCIsXG4gICAgcmVxdWlyZWQ6IHRydWUsXG4gIH0pLFxuICAvLyBUT0RPOiBtYWtlIHRoaXMgYSB2YXJpYWRpYyBhcmdcbiAgY29tbWFuZDogbmV3IFN0cmluZ3NQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiVGhlIGNvbW1hbmQgdG8gcnVuIGluIHRoZSBtb2R1bGUuXCIsXG4gIH0pLFxufVxuXG5jb25zdCBydW5PcHRzID0ge1xuICAvLyBUT0RPOiB3ZSBjb3VsZCBwcm92aWRlIHNwZWNpZmljIHBhcmFtZXRlcnMgbGlrZSB0aGlzIGJ5IGFkZGluZyBjb21tYW5kcyBmb3Igc3BlY2lmaWMgbW9kdWxlcywgdmlhIHBsdWdpbnNcbiAgLy9lbnRyeXBvaW50OiBuZXcgU3RyaW5nUGFyYW1ldGVyKHsgaGVscDogXCJPdmVycmlkZSBkZWZhdWx0IGVudHJ5cG9pbnQgaW4gbW9kdWxlXCIgfSksXG4gIGludGVyYWN0aXZlOiBuZXcgQm9vbGVhblBhcmFtZXRlcih7XG4gICAgaGVscDogXCJTZXQgdG8gZmFsc2UgdG8gc2tpcCBpbnRlcmFjdGl2ZSBtb2RlIGFuZCBqdXN0IG91dHB1dCB0aGUgY29tbWFuZCByZXN1bHQuXCIsXG4gICAgZGVmYXVsdFZhbHVlOiB0cnVlLFxuICB9KSxcbiAgXCJmb3JjZS1idWlsZFwiOiBuZXcgQm9vbGVhblBhcmFtZXRlcih7IGhlbHA6IFwiRm9yY2UgcmVidWlsZCBvZiBtb2R1bGUgYmVmb3JlIHJ1bm5pbmcuXCIgfSksXG59XG5cbnR5cGUgQXJncyA9IHR5cGVvZiBydW5BcmdzXG50eXBlIE9wdHMgPSB0eXBlb2YgcnVuT3B0c1xuXG5leHBvcnQgY2xhc3MgUnVuTW9kdWxlQ29tbWFuZCBleHRlbmRzIENvbW1hbmQ8QXJncywgT3B0cz4ge1xuICBuYW1lID0gXCJtb2R1bGVcIlxuICBhbGlhcyA9IFwibVwiXG4gIGhlbHAgPSBcIlJ1biBhbiBhZC1ob2MgaW5zdGFuY2Ugb2YgYSBtb2R1bGUuXCJcblxuICBkZXNjcmlwdGlvbiA9IGRlZGVudGBcbiAgICBUaGlzIGlzIHVzZWZ1bCBmb3IgZGVidWdnaW5nIG9yIGFkLWhvYyBleHBlcmltZW50YXRpb24gd2l0aCBtb2R1bGVzLlxuXG4gICAgRXhhbXBsZXM6XG5cbiAgICAgICAgZ2FyZGVuIHJ1biBtb2R1bGUgbXktY29udGFpbmVyICAgICAgICAgICAjIHJ1biBhbiBhZC1ob2MgaW5zdGFuY2Ugb2YgYSBteS1jb250YWluZXIgY29udGFpbmVyIGFuZCBhdHRhY2ggdG8gaXRcbiAgICAgICAgZ2FyZGVuIHJ1biBtb2R1bGUgbXktY29udGFpbmVyIC9iaW4vc2ggICAjIHJ1biBhbiBpbnRlcmFjdGl2ZSBzaGVsbCBpbiBhIG5ldyBteS1jb250YWluZXIgY29udGFpbmVyXG4gICAgICAgIGdhcmRlbiBydW4gbW9kdWxlIG15LWNvbnRhaW5lciAtLWk9ZmFsc2UgL3NvbWUvc2NyaXB0ICAjIGV4ZWN1dGUgYSBzY3JpcHQgaW4gbXktY29udGFpbmVyIGFuZCByZXR1cm4gdGhlIG91dHB1dFxuICBgXG5cbiAgYXJndW1lbnRzID0gcnVuQXJnc1xuICBvcHRpb25zID0gcnVuT3B0c1xuXG4gIGFzeW5jIGFjdGlvbih7IGdhcmRlbiwgYXJncywgb3B0cyB9OiBDb21tYW5kUGFyYW1zPEFyZ3MsIE9wdHM+KTogUHJvbWlzZTxDb21tYW5kUmVzdWx0PFJ1blJlc3VsdD4+IHtcbiAgICBjb25zdCBtb2R1bGVOYW1lID0gYXJncy5tb2R1bGVcbiAgICBjb25zdCBtb2R1bGUgPSBhd2FpdCBnYXJkZW4uZ2V0TW9kdWxlKG1vZHVsZU5hbWUpXG5cbiAgICBjb25zdCBtc2cgPSBhcmdzLmNvbW1hbmRcbiAgICAgID8gYFJ1bm5pbmcgY29tbWFuZCAke2NoYWxrLndoaXRlKGFyZ3MuY29tbWFuZC5qb2luKFwiIFwiKSl9IGluIG1vZHVsZSAke2NoYWxrLndoaXRlKG1vZHVsZU5hbWUpfWBcbiAgICAgIDogYFJ1bm5pbmcgbW9kdWxlICR7Y2hhbGsud2hpdGUobW9kdWxlTmFtZSl9YFxuXG4gICAgZ2FyZGVuLmxvZy5oZWFkZXIoe1xuICAgICAgZW1vamk6IFwicnVubmVyXCIsXG4gICAgICBjb21tYW5kOiBtc2csXG4gICAgfSlcblxuICAgIGF3YWl0IGdhcmRlbi5hY3Rpb25zLnByZXBhcmVFbnZpcm9ubWVudCh7fSlcblxuICAgIGNvbnN0IGJ1aWxkVGFzayA9IG5ldyBCdWlsZFRhc2soeyBnYXJkZW4sIG1vZHVsZSwgZm9yY2U6IG9wdHNbXCJmb3JjZS1idWlsZFwiXSB9KVxuICAgIGF3YWl0IGdhcmRlbi5hZGRUYXNrKGJ1aWxkVGFzaylcbiAgICBhd2FpdCBnYXJkZW4ucHJvY2Vzc1Rhc2tzKClcblxuICAgIGNvbnN0IGNvbW1hbmQgPSBhcmdzLmNvbW1hbmQgfHwgW11cblxuICAgIC8vIGNvbWJpbmUgYWxsIGRlcGVuZGVuY2llcyBmb3IgYWxsIHNlcnZpY2VzIGluIHRoZSBtb2R1bGUsIHRvIGJlIHN1cmUgd2UgaGF2ZSBhbGwgdGhlIGNvbnRleHQgd2UgbmVlZFxuICAgIGNvbnN0IGRlcE5hbWVzID0gdW5pcShmbGF0dGVuKG1vZHVsZS5zZXJ2aWNlQ29uZmlncy5tYXAocyA9PiBzLmRlcGVuZGVuY2llcykpKVxuICAgIGNvbnN0IGRlcHMgPSBhd2FpdCBnYXJkZW4uZ2V0U2VydmljZXMoZGVwTmFtZXMpXG5cbiAgICBjb25zdCBydW50aW1lQ29udGV4dCA9IGF3YWl0IHByZXBhcmVSdW50aW1lQ29udGV4dChnYXJkZW4sIG1vZHVsZSwgZGVwcylcblxuICAgIHByaW50UnVudGltZUNvbnRleHQoZ2FyZGVuLCBydW50aW1lQ29udGV4dClcblxuICAgIGdhcmRlbi5sb2cuaW5mbyhcIlwiKVxuXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZ2FyZGVuLmFjdGlvbnMucnVuTW9kdWxlKHtcbiAgICAgIG1vZHVsZSxcbiAgICAgIGNvbW1hbmQsXG4gICAgICBydW50aW1lQ29udGV4dCxcbiAgICAgIHNpbGVudDogZmFsc2UsXG4gICAgICBpbnRlcmFjdGl2ZTogb3B0cy5pbnRlcmFjdGl2ZSxcbiAgICB9KVxuXG4gICAgcmV0dXJuIHsgcmVzdWx0IH1cbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/run/run.d.ts b/garden-service/build/commands/run/run.d.ts
new file mode 100644
index 00000000000..2b6b62bdd21
--- /dev/null
+++ b/garden-service/build/commands/run/run.d.ts
@@ -0,0 +1,15 @@
+import { RuntimeContext } from "../../types/service";
+import { Command } from "../base";
+import { RunModuleCommand } from "./module";
+import { RunServiceCommand } from "./service";
+import { RunTestCommand } from "./test";
+import { Garden } from "../../garden";
+export declare class RunCommand extends Command {
+ name: string;
+ alias: string;
+ help: string;
+ subCommands: (typeof RunModuleCommand | typeof RunServiceCommand | typeof RunTestCommand)[];
+ action(): Promise<{}>;
+}
+export declare function printRuntimeContext(garden: Garden, runtimeContext: RuntimeContext): void;
+//# sourceMappingURL=run.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/run/run.js b/garden-service/build/commands/run/run.js
new file mode 100644
index 00000000000..6e0307f6c8b
--- /dev/null
+++ b/garden-service/build/commands/run/run.js
@@ -0,0 +1,51 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const js_yaml_1 = require("js-yaml");
+const util_1 = require("../../util/util");
+const base_1 = require("../base");
+const module_1 = require("./module");
+const service_1 = require("./service");
+const test_1 = require("./test");
+class RunCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "run";
+ this.alias = "r";
+ this.help = "Run ad-hoc instances of your modules, services and tests";
+ this.subCommands = [
+ module_1.RunModuleCommand,
+ service_1.RunServiceCommand,
+ test_1.RunTestCommand,
+ ];
+ }
+ action() {
+ return __awaiter(this, void 0, void 0, function* () { return {}; });
+ }
+}
+exports.RunCommand = RunCommand;
+function printRuntimeContext(garden, runtimeContext) {
+ garden.log.verbose("-----------------------------------\n");
+ garden.log.verbose("Environment variables:");
+ garden.log.verbose(util_1.highlightYaml(js_yaml_1.safeDump(runtimeContext.envVars)));
+ garden.log.verbose("Dependencies:");
+ garden.log.verbose(util_1.highlightYaml(js_yaml_1.safeDump(runtimeContext.dependencies)));
+ garden.log.verbose("-----------------------------------\n");
+}
+exports.printRuntimeContext = printRuntimeContext;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL3J1bi9ydW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7R0FNRzs7Ozs7Ozs7OztBQUVILHFDQUFrQztBQUVsQywwQ0FBK0M7QUFDL0Msa0NBQWlDO0FBQ2pDLHFDQUEyQztBQUMzQyx1Q0FBNkM7QUFDN0MsaUNBQXVDO0FBR3ZDLE1BQWEsVUFBVyxTQUFRLGNBQU87SUFBdkM7O1FBQ0UsU0FBSSxHQUFHLEtBQUssQ0FBQTtRQUNaLFVBQUssR0FBRyxHQUFHLENBQUE7UUFDWCxTQUFJLEdBQUcsMERBQTBELENBQUE7UUFFakUsZ0JBQVcsR0FBRztZQUNaLHlCQUFnQjtZQUNoQiwyQkFBaUI7WUFDakIscUJBQWM7U0FDZixDQUFBO0lBR0gsQ0FBQztJQURPLE1BQU07OERBQUssT0FBTyxFQUFFLENBQUEsQ0FBQyxDQUFDO0tBQUE7Q0FDN0I7QUFaRCxnQ0FZQztBQUVELFNBQWdCLG1CQUFtQixDQUFDLE1BQWMsRUFBRSxjQUE4QjtJQUNoRixNQUFNLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyx1Q0FBdUMsQ0FBQyxDQUFBO0lBQzNELE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLHdCQUF3QixDQUFDLENBQUE7SUFDNUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsb0JBQWEsQ0FBQyxrQkFBUSxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDbkUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLENBQUE7SUFDbkMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsb0JBQWEsQ0FBQyxrQkFBUSxDQUFDLGNBQWMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDeEUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsdUNBQXVDLENBQUMsQ0FBQTtBQUM3RCxDQUFDO0FBUEQsa0RBT0MiLCJmaWxlIjoiY29tbWFuZHMvcnVuL3J1bi5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgeyBzYWZlRHVtcCB9IGZyb20gXCJqcy15YW1sXCJcbmltcG9ydCB7IFJ1bnRpbWVDb250ZXh0IH0gZnJvbSBcIi4uLy4uL3R5cGVzL3NlcnZpY2VcIlxuaW1wb3J0IHsgaGlnaGxpZ2h0WWFtbCB9IGZyb20gXCIuLi8uLi91dGlsL3V0aWxcIlxuaW1wb3J0IHsgQ29tbWFuZCB9IGZyb20gXCIuLi9iYXNlXCJcbmltcG9ydCB7IFJ1bk1vZHVsZUNvbW1hbmQgfSBmcm9tIFwiLi9tb2R1bGVcIlxuaW1wb3J0IHsgUnVuU2VydmljZUNvbW1hbmQgfSBmcm9tIFwiLi9zZXJ2aWNlXCJcbmltcG9ydCB7IFJ1blRlc3RDb21tYW5kIH0gZnJvbSBcIi4vdGVzdFwiXG5pbXBvcnQgeyBHYXJkZW4gfSBmcm9tIFwiLi4vLi4vZ2FyZGVuXCJcblxuZXhwb3J0IGNsYXNzIFJ1bkNvbW1hbmQgZXh0ZW5kcyBDb21tYW5kIHtcbiAgbmFtZSA9IFwicnVuXCJcbiAgYWxpYXMgPSBcInJcIlxuICBoZWxwID0gXCJSdW4gYWQtaG9jIGluc3RhbmNlcyBvZiB5b3VyIG1vZHVsZXMsIHNlcnZpY2VzIGFuZCB0ZXN0c1wiXG5cbiAgc3ViQ29tbWFuZHMgPSBbXG4gICAgUnVuTW9kdWxlQ29tbWFuZCxcbiAgICBSdW5TZXJ2aWNlQ29tbWFuZCxcbiAgICBSdW5UZXN0Q29tbWFuZCxcbiAgXVxuXG4gIGFzeW5jIGFjdGlvbigpIHsgcmV0dXJuIHt9IH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHByaW50UnVudGltZUNvbnRleHQoZ2FyZGVuOiBHYXJkZW4sIHJ1bnRpbWVDb250ZXh0OiBSdW50aW1lQ29udGV4dCkge1xuICBnYXJkZW4ubG9nLnZlcmJvc2UoXCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcblwiKVxuICBnYXJkZW4ubG9nLnZlcmJvc2UoXCJFbnZpcm9ubWVudCB2YXJpYWJsZXM6XCIpXG4gIGdhcmRlbi5sb2cudmVyYm9zZShoaWdobGlnaHRZYW1sKHNhZmVEdW1wKHJ1bnRpbWVDb250ZXh0LmVudlZhcnMpKSlcbiAgZ2FyZGVuLmxvZy52ZXJib3NlKFwiRGVwZW5kZW5jaWVzOlwiKVxuICBnYXJkZW4ubG9nLnZlcmJvc2UoaGlnaGxpZ2h0WWFtbChzYWZlRHVtcChydW50aW1lQ29udGV4dC5kZXBlbmRlbmNpZXMpKSlcbiAgZ2FyZGVuLmxvZy52ZXJib3NlKFwiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG5cIilcbn1cbiJdfQ==
diff --git a/garden-service/build/commands/run/service.d.ts b/garden-service/build/commands/run/service.d.ts
new file mode 100644
index 00000000000..6858913a3db
--- /dev/null
+++ b/garden-service/build/commands/run/service.d.ts
@@ -0,0 +1,25 @@
+import { RunResult } from "../../types/plugin/outputs";
+import { BooleanParameter, Command, CommandParams, CommandResult, StringParameter } from "../base";
+declare const runArgs: {
+ service: StringParameter;
+};
+declare const runOpts: {
+ "force-build": BooleanParameter;
+};
+declare type Args = typeof runArgs;
+declare type Opts = typeof runOpts;
+export declare class RunServiceCommand extends Command {
+ name: string;
+ alias: string;
+ help: string;
+ description: string;
+ arguments: {
+ service: StringParameter;
+ };
+ options: {
+ "force-build": BooleanParameter;
+ };
+ action({ garden, args, opts }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=service.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/run/service.js b/garden-service/build/commands/run/service.js
new file mode 100644
index 00000000000..85c51a9ab45
--- /dev/null
+++ b/garden-service/build/commands/run/service.js
@@ -0,0 +1,72 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const chalk_1 = require("chalk");
+const build_1 = require("../../tasks/build");
+const base_1 = require("../base");
+const run_1 = require("./run");
+const dedent = require("dedent");
+const service_1 = require("../../types/service");
+const runArgs = {
+ service: new base_1.StringParameter({
+ help: "The service to run",
+ required: true,
+ }),
+};
+const runOpts = {
+ "force-build": new base_1.BooleanParameter({ help: "Force rebuild of module" }),
+};
+class RunServiceCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "service";
+ this.alias = "s";
+ this.help = "Run an ad-hoc instance of the specified service";
+ this.description = dedent `
+ This can be useful for debugging or ad-hoc experimentation with services.
+
+ Examples:
+
+ garden run service my-service # run an ad-hoc instance of a my-service and attach to it
+ `;
+ this.arguments = runArgs;
+ this.options = runOpts;
+ }
+ action({ garden, args, opts }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const serviceName = args.service;
+ const service = yield garden.getService(serviceName);
+ const module = service.module;
+ garden.log.header({
+ emoji: "runner",
+ command: `Running service ${chalk_1.default.cyan(serviceName)} in module ${chalk_1.default.cyan(module.name)}`,
+ });
+ yield garden.actions.prepareEnvironment({});
+ const buildTask = new build_1.BuildTask({ garden, module, force: opts["force-build"] });
+ yield garden.addTask(buildTask);
+ yield garden.processTasks();
+ const dependencies = yield garden.getServices(module.serviceDependencyNames);
+ const runtimeContext = yield service_1.prepareRuntimeContext(garden, module, dependencies);
+ run_1.printRuntimeContext(garden, runtimeContext);
+ const result = yield garden.actions.runService({ service, runtimeContext, silent: false, interactive: true });
+ return { result };
+ });
+ }
+}
+exports.RunServiceCommand = RunServiceCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL3J1bi9zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7QUFFSCxpQ0FBeUI7QUFDekIsNkNBQTZDO0FBRTdDLGtDQU1nQjtBQUNoQiwrQkFBMkM7QUFDM0MsaUNBQWlDO0FBQ2pDLGlEQUEyRDtBQUUzRCxNQUFNLE9BQU8sR0FBRztJQUNkLE9BQU8sRUFBRSxJQUFJLHNCQUFlLENBQUM7UUFDM0IsSUFBSSxFQUFFLG9CQUFvQjtRQUMxQixRQUFRLEVBQUUsSUFBSTtLQUNmLENBQUM7Q0FDSCxDQUFBO0FBRUQsTUFBTSxPQUFPLEdBQUc7SUFDZCxhQUFhLEVBQUUsSUFBSSx1QkFBZ0IsQ0FBQyxFQUFFLElBQUksRUFBRSx5QkFBeUIsRUFBRSxDQUFDO0NBQ3pFLENBQUE7QUFLRCxNQUFhLGlCQUFrQixTQUFRLGNBQW1CO0lBQTFEOztRQUNFLFNBQUksR0FBRyxTQUFTLENBQUE7UUFDaEIsVUFBSyxHQUFHLEdBQUcsQ0FBQTtRQUNYLFNBQUksR0FBRyxpREFBaUQsQ0FBQTtRQUV4RCxnQkFBVyxHQUFHLE1BQU0sQ0FBQTs7Ozs7O0dBTW5CLENBQUE7UUFFRCxjQUFTLEdBQUcsT0FBTyxDQUFBO1FBQ25CLFlBQU8sR0FBRyxPQUFPLENBQUE7SUEyQm5CLENBQUM7SUF6Qk8sTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQTZCOztZQUM1RCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFBO1lBQ2hDLE1BQU0sT0FBTyxHQUFHLE1BQU0sTUFBTSxDQUFDLFVBQVUsQ0FBQyxXQUFXLENBQUMsQ0FBQTtZQUNwRCxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFBO1lBRTdCLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDO2dCQUNoQixLQUFLLEVBQUUsUUFBUTtnQkFDZixPQUFPLEVBQUUsbUJBQW1CLGVBQUssQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLGNBQWMsZUFBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUU7YUFDM0YsQ0FBQyxDQUFBO1lBRUYsTUFBTSxNQUFNLENBQUMsT0FBTyxDQUFDLGtCQUFrQixDQUFDLEVBQUUsQ0FBQyxDQUFBO1lBRTNDLE1BQU0sU0FBUyxHQUFHLElBQUksaUJBQVMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDLENBQUE7WUFDL0UsTUFBTSxNQUFNLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFBO1lBQy9CLE1BQU0sTUFBTSxDQUFDLFlBQVksRUFBRSxDQUFBO1lBRTNCLE1BQU0sWUFBWSxHQUFHLE1BQU0sTUFBTSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsc0JBQXNCLENBQUMsQ0FBQTtZQUM1RSxNQUFNLGNBQWMsR0FBRyxNQUFNLCtCQUFxQixDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsWUFBWSxDQUFDLENBQUE7WUFFaEYseUJBQW1CLENBQUMsTUFBTSxFQUFFLGNBQWMsQ0FBQyxDQUFBO1lBRTNDLE1BQU0sTUFBTSxHQUFHLE1BQU0sTUFBTSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsRUFBRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUE7WUFFN0csT0FBTyxFQUFFLE1BQU0sRUFBRSxDQUFBO1FBQ25CLENBQUM7S0FBQTtDQUNGO0FBekNELDhDQXlDQyIsImZpbGUiOiJjb21tYW5kcy9ydW4vc2VydmljZS5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgY2hhbGsgZnJvbSBcImNoYWxrXCJcbmltcG9ydCB7IEJ1aWxkVGFzayB9IGZyb20gXCIuLi8uLi90YXNrcy9idWlsZFwiXG5pbXBvcnQgeyBSdW5SZXN1bHQgfSBmcm9tIFwiLi4vLi4vdHlwZXMvcGx1Z2luL291dHB1dHNcIlxuaW1wb3J0IHtcbiAgQm9vbGVhblBhcmFtZXRlcixcbiAgQ29tbWFuZCxcbiAgQ29tbWFuZFBhcmFtcyxcbiAgQ29tbWFuZFJlc3VsdCxcbiAgU3RyaW5nUGFyYW1ldGVyLFxufSBmcm9tIFwiLi4vYmFzZVwiXG5pbXBvcnQgeyBwcmludFJ1bnRpbWVDb250ZXh0IH0gZnJvbSBcIi4vcnVuXCJcbmltcG9ydCBkZWRlbnQgPSByZXF1aXJlKFwiZGVkZW50XCIpXG5pbXBvcnQgeyBwcmVwYXJlUnVudGltZUNvbnRleHQgfSBmcm9tIFwiLi4vLi4vdHlwZXMvc2VydmljZVwiXG5cbmNvbnN0IHJ1bkFyZ3MgPSB7XG4gIHNlcnZpY2U6IG5ldyBTdHJpbmdQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiVGhlIHNlcnZpY2UgdG8gcnVuXCIsXG4gICAgcmVxdWlyZWQ6IHRydWUsXG4gIH0pLFxufVxuXG5jb25zdCBydW5PcHRzID0ge1xuICBcImZvcmNlLWJ1aWxkXCI6IG5ldyBCb29sZWFuUGFyYW1ldGVyKHsgaGVscDogXCJGb3JjZSByZWJ1aWxkIG9mIG1vZHVsZVwiIH0pLFxufVxuXG50eXBlIEFyZ3MgPSB0eXBlb2YgcnVuQXJnc1xudHlwZSBPcHRzID0gdHlwZW9mIHJ1bk9wdHNcblxuZXhwb3J0IGNsYXNzIFJ1blNlcnZpY2VDb21tYW5kIGV4dGVuZHMgQ29tbWFuZDxBcmdzLCBPcHRzPiB7XG4gIG5hbWUgPSBcInNlcnZpY2VcIlxuICBhbGlhcyA9IFwic1wiXG4gIGhlbHAgPSBcIlJ1biBhbiBhZC1ob2MgaW5zdGFuY2Ugb2YgdGhlIHNwZWNpZmllZCBzZXJ2aWNlXCJcblxuICBkZXNjcmlwdGlvbiA9IGRlZGVudGBcbiAgICBUaGlzIGNhbiBiZSB1c2VmdWwgZm9yIGRlYnVnZ2luZyBvciBhZC1ob2MgZXhwZXJpbWVudGF0aW9uIHdpdGggc2VydmljZXMuXG5cbiAgICBFeGFtcGxlczpcblxuICAgICAgICBnYXJkZW4gcnVuIHNlcnZpY2UgbXktc2VydmljZSAgICMgcnVuIGFuIGFkLWhvYyBpbnN0YW5jZSBvZiBhIG15LXNlcnZpY2UgYW5kIGF0dGFjaCB0byBpdFxuICBgXG5cbiAgYXJndW1lbnRzID0gcnVuQXJnc1xuICBvcHRpb25zID0gcnVuT3B0c1xuXG4gIGFzeW5jIGFjdGlvbih7IGdhcmRlbiwgYXJncywgb3B0cyB9OiBDb21tYW5kUGFyYW1zPEFyZ3MsIE9wdHM+KTogUHJvbWlzZTxDb21tYW5kUmVzdWx0PFJ1blJlc3VsdD4+IHtcbiAgICBjb25zdCBzZXJ2aWNlTmFtZSA9IGFyZ3Muc2VydmljZVxuICAgIGNvbnN0IHNlcnZpY2UgPSBhd2FpdCBnYXJkZW4uZ2V0U2VydmljZShzZXJ2aWNlTmFtZSlcbiAgICBjb25zdCBtb2R1bGUgPSBzZXJ2aWNlLm1vZHVsZVxuXG4gICAgZ2FyZGVuLmxvZy5oZWFkZXIoe1xuICAgICAgZW1vamk6IFwicnVubmVyXCIsXG4gICAgICBjb21tYW5kOiBgUnVubmluZyBzZXJ2aWNlICR7Y2hhbGsuY3lhbihzZXJ2aWNlTmFtZSl9IGluIG1vZHVsZSAke2NoYWxrLmN5YW4obW9kdWxlLm5hbWUpfWAsXG4gICAgfSlcblxuICAgIGF3YWl0IGdhcmRlbi5hY3Rpb25zLnByZXBhcmVFbnZpcm9ubWVudCh7fSlcblxuICAgIGNvbnN0IGJ1aWxkVGFzayA9IG5ldyBCdWlsZFRhc2soeyBnYXJkZW4sIG1vZHVsZSwgZm9yY2U6IG9wdHNbXCJmb3JjZS1idWlsZFwiXSB9KVxuICAgIGF3YWl0IGdhcmRlbi5hZGRUYXNrKGJ1aWxkVGFzaylcbiAgICBhd2FpdCBnYXJkZW4ucHJvY2Vzc1Rhc2tzKClcblxuICAgIGNvbnN0IGRlcGVuZGVuY2llcyA9IGF3YWl0IGdhcmRlbi5nZXRTZXJ2aWNlcyhtb2R1bGUuc2VydmljZURlcGVuZGVuY3lOYW1lcylcbiAgICBjb25zdCBydW50aW1lQ29udGV4dCA9IGF3YWl0IHByZXBhcmVSdW50aW1lQ29udGV4dChnYXJkZW4sIG1vZHVsZSwgZGVwZW5kZW5jaWVzKVxuXG4gICAgcHJpbnRSdW50aW1lQ29udGV4dChnYXJkZW4sIHJ1bnRpbWVDb250ZXh0KVxuXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZ2FyZGVuLmFjdGlvbnMucnVuU2VydmljZSh7IHNlcnZpY2UsIHJ1bnRpbWVDb250ZXh0LCBzaWxlbnQ6IGZhbHNlLCBpbnRlcmFjdGl2ZTogdHJ1ZSB9KVxuXG4gICAgcmV0dXJuIHsgcmVzdWx0IH1cbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/run/test.d.ts b/garden-service/build/commands/run/test.d.ts
new file mode 100644
index 00000000000..50d309f237b
--- /dev/null
+++ b/garden-service/build/commands/run/test.d.ts
@@ -0,0 +1,29 @@
+import { RunResult } from "../../types/plugin/outputs";
+import { BooleanParameter, Command, CommandParams, CommandResult, StringParameter } from "../base";
+declare const runArgs: {
+ module: StringParameter;
+ test: StringParameter;
+};
+declare const runOpts: {
+ interactive: BooleanParameter;
+ "force-build": BooleanParameter;
+};
+declare type Args = typeof runArgs;
+declare type Opts = typeof runOpts;
+export declare class RunTestCommand extends Command {
+ name: string;
+ alias: string;
+ help: string;
+ description: string;
+ arguments: {
+ module: StringParameter;
+ test: StringParameter;
+ };
+ options: {
+ interactive: BooleanParameter;
+ "force-build": BooleanParameter;
+ };
+ action({ garden, args, opts }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=test.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/run/test.js b/garden-service/build/commands/run/test.js
new file mode 100644
index 00000000000..5c6e8dfe74d
--- /dev/null
+++ b/garden-service/build/commands/run/test.js
@@ -0,0 +1,98 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const chalk_1 = require("chalk");
+const exceptions_1 = require("../../exceptions");
+const build_1 = require("../../tasks/build");
+const util_1 = require("../../util/util");
+const base_1 = require("../base");
+const run_1 = require("./run");
+const dedent = require("dedent");
+const service_1 = require("../../types/service");
+const runArgs = {
+ module: new base_1.StringParameter({
+ help: "The name of the module to run.",
+ required: true,
+ }),
+ test: new base_1.StringParameter({
+ help: "The name of the test to run in the module.",
+ required: true,
+ }),
+};
+const runOpts = {
+ interactive: new base_1.BooleanParameter({
+ help: "Set to false to skip interactive mode and just output the command result.",
+ defaultValue: true,
+ }),
+ "force-build": new base_1.BooleanParameter({ help: "Force rebuild of module before running." }),
+};
+class RunTestCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "test";
+ this.alias = "t";
+ this.help = "Run the specified module test.";
+ this.description = dedent `
+ This can be useful for debugging tests, particularly integration/end-to-end tests.
+
+ Examples:
+
+ garden run test my-module integ # run the test named 'integ' in my-module
+ garden run test my-module integ --i=false # do not attach to the test run, just output results when completed
+ `;
+ this.arguments = runArgs;
+ this.options = runOpts;
+ }
+ action({ garden, args, opts }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const moduleName = args.module;
+ const testName = args.test;
+ const module = yield garden.getModule(moduleName);
+ const testConfig = util_1.findByName(module.testConfigs, testName);
+ if (!testConfig) {
+ throw new exceptions_1.ParameterError(`Could not find test "${testName}" in module ${moduleName}`, {
+ moduleName,
+ testName,
+ availableTests: util_1.getNames(module.testConfigs),
+ });
+ }
+ garden.log.header({
+ emoji: "runner",
+ command: `Running test ${chalk_1.default.cyan(testName)} in module ${chalk_1.default.cyan(moduleName)}`,
+ });
+ yield garden.actions.prepareEnvironment({});
+ const buildTask = new build_1.BuildTask({ garden, module, force: opts["force-build"] });
+ yield garden.addTask(buildTask);
+ yield garden.processTasks();
+ const interactive = opts.interactive;
+ const deps = yield garden.getServices(testConfig.dependencies);
+ const runtimeContext = yield service_1.prepareRuntimeContext(garden, module, deps);
+ run_1.printRuntimeContext(garden, runtimeContext);
+ const result = yield garden.actions.testModule({
+ module,
+ interactive,
+ runtimeContext,
+ silent: false,
+ testConfig,
+ });
+ return { result };
+ });
+ }
+}
+exports.RunTestCommand = RunTestCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,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TUFBTSxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQTtZQUV4RSx5QkFBbUIsQ0FBQyxNQUFNLEVBQUUsY0FBYyxDQUFDLENBQUE7WUFFM0MsTUFBTSxNQUFNLEdBQUcsTUFBTSxNQUFNLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQztnQkFDN0MsTUFBTTtnQkFDTixXQUFXO2dCQUNYLGNBQWM7Z0JBQ2QsTUFBTSxFQUFFLEtBQUs7Z0JBQ2IsVUFBVTthQUNYLENBQUMsQ0FBQTtZQUVGLE9BQU8sRUFBRSxNQUFNLEVBQUUsQ0FBQTtRQUNuQixDQUFDO0tBQUE7Q0FDRjtBQTNERCx3Q0EyREMiLCJmaWxlIjoiY29tbWFuZHMvcnVuL3Rlc3QuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChDKSAyMDE4IEdhcmRlbiBUZWNobm9sb2dpZXMsIEluYy4gPGluZm9AZ2FyZGVuLmlvPlxuICpcbiAqIFRoaXMgU291cmNlIENvZGUgRm9ybSBpcyBzdWJqZWN0IHRvIHRoZSB0ZXJtcyBvZiB0aGUgTW96aWxsYSBQdWJsaWNcbiAqIExpY2Vuc2UsIHYuIDIuMC4gSWYgYSBjb3B5IG9mIHRoZSBNUEwgd2FzIG5vdCBkaXN0cmlidXRlZCB3aXRoIHRoaXNcbiAqIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uXG4gKi9cblxuaW1wb3J0IGNoYWxrIGZyb20gXCJjaGFsa1wiXG5pbXBvcnQgeyBQYXJhbWV0ZXJFcnJvciB9IGZyb20gXCIuLi8uLi9leGNlcHRpb25zXCJcbmltcG9ydCB7IEJ1aWxkVGFzayB9IGZyb20gXCIuLi8uLi90YXNrcy9idWlsZFwiXG5pbXBvcnQgeyBSdW5SZXN1bHQgfSBmcm9tIFwiLi4vLi4vdHlwZXMvcGx1Z2luL291dHB1dHNcIlxuaW1wb3J0IHtcbiAgZmluZEJ5TmFtZSxcbiAgZ2V0TmFtZXMsXG59IGZyb20gXCIuLi8uLi91dGlsL3V0aWxcIlxuaW1wb3J0IHtcbiAgQm9vbGVhblBhcmFtZXRlcixcbiAgQ29tbWFuZCxcbiAgQ29tbWFuZFBhcmFtcyxcbiAgQ29tbWFuZFJlc3VsdCxcbiAgU3RyaW5nUGFyYW1ldGVyLFxufSBmcm9tIFwiLi4vYmFzZVwiXG5pbXBvcnQgeyBwcmludFJ1bnRpbWVDb250ZXh0IH0gZnJvbSBcIi4vcnVuXCJcbmltcG9ydCBkZWRlbnQgPSByZXF1aXJlKFwiZGVkZW50XCIpXG5pbXBvcnQgeyBwcmVwYXJlUnVudGltZUNvbnRleHQgfSBmcm9tIFwiLi4vLi4vdHlwZXMvc2VydmljZVwiXG5cbmNvbnN0IHJ1bkFyZ3MgPSB7XG4gIG1vZHVsZTogbmV3IFN0cmluZ1BhcmFtZXRlcih7XG4gICAgaGVscDogXCJUaGUgbmFtZSBvZiB0aGUgbW9kdWxlIHRvIHJ1bi5cIixcbiAgICByZXF1aXJlZDogdHJ1ZSxcbiAgfSksXG4gIHRlc3Q6IG5ldyBTdHJpbmdQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiVGhlIG5hbWUgb2YgdGhlIHRlc3QgdG8gcnVuIGluIHRoZSBtb2R1bGUuXCIsXG4gICAgcmVxdWlyZWQ6IHRydWUsXG4gIH0pLFxufVxuXG5jb25zdCBydW5PcHRzID0ge1xuICBpbnRlcmFjdGl2ZTogbmV3IEJvb2xlYW5QYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiU2V0IHRvIGZhbHNlIHRvIHNraXAgaW50ZXJhY3RpdmUgbW9kZSBhbmQganVzdCBvdXRwdXQgdGhlIGNvbW1hbmQgcmVzdWx0LlwiLFxuICAgIGRlZmF1bHRWYWx1ZTogdHJ1ZSxcbiAgfSksXG4gIFwiZm9yY2UtYnVpbGRcIjogbmV3IEJvb2xlYW5QYXJhbWV0ZXIoeyBoZWxwOiBcIkZvcmNlIHJlYnVpbGQgb2YgbW9kdWxlIGJlZm9yZSBydW5uaW5nLlwiIH0pLFxufVxuXG50eXBlIEFyZ3MgPSB0eXBlb2YgcnVuQXJnc1xudHlwZSBPcHRzID0gdHlwZW9mIHJ1bk9wdHNcblxuZXhwb3J0IGNsYXNzIFJ1blRlc3RDb21tYW5kIGV4dGVuZHMgQ29tbWFuZDxBcmdzLCBPcHRzPiB7XG4gIG5hbWUgPSBcInRlc3RcIlxuICBhbGlhcyA9IFwidFwiXG4gIGhlbHAgPSBcIlJ1biB0aGUgc3BlY2lmaWVkIG1vZHVsZSB0ZXN0LlwiXG5cbiAgZGVzY3JpcHRpb24gPSBkZWRlbnRgXG4gICAgVGhpcyBjYW4gYmUgdXNlZnVsIGZvciBkZWJ1Z2dpbmcgdGVzdHMsIHBhcnRpY3VsYXJseSBpbnRlZ3JhdGlvbi9lbmQtdG8tZW5kIHRlc3RzLlxuXG4gICAgRXhhbXBsZXM6XG5cbiAgICAgICAgZ2FyZGVuIHJ1biB0ZXN0IG15LW1vZHVsZSBpbnRlZyAgICAgICAgICAgICMgcnVuIHRoZSB0ZXN0IG5hbWVkICdpbnRlZycgaW4gbXktbW9kdWxlXG4gICAgICAgIGdhcmRlbiBydW4gdGVzdCBteS1tb2R1bGUgaW50ZWcgLS1pPWZhbHNlICAjIGRvIG5vdCBhdHRhY2ggdG8gdGhlIHRlc3QgcnVuLCBqdXN0IG91dHB1dCByZXN1bHRzIHdoZW4gY29tcGxldGVkXG4gIGBcblxuICBhcmd1bWVudHMgPSBydW5BcmdzXG4gIG9wdGlvbnMgPSBydW5PcHRzXG5cbiAgYXN5bmMgYWN0aW9uKHsgZ2FyZGVuLCBhcmdzLCBvcHRzIH06IENvbW1hbmRQYXJhbXM8QXJncywgT3B0cz4pOiBQcm9taXNlPENvbW1hbmRSZXN1bHQ8UnVuUmVzdWx0Pj4ge1xuICAgIGNvbnN0IG1vZHVsZU5hbWUgPSBhcmdzLm1vZHVsZVxuICAgIGNvbnN0IHRlc3ROYW1lID0gYXJncy50ZXN0XG4gICAgY29uc3QgbW9kdWxlID0gYXdhaXQgZ2FyZGVuLmdldE1vZHVsZShtb2R1bGVOYW1lKVxuXG4gICAgY29uc3QgdGVzdENvbmZpZyA9IGZpbmRCeU5hbWUobW9kdWxlLnRlc3RDb25maWdzLCB0ZXN0TmFtZSlcblxuICAgIGlmICghdGVzdENvbmZpZykge1xuICAgICAgdGhyb3cgbmV3IFBhcmFtZXRlckVycm9yKGBDb3VsZCBub3QgZmluZCB0ZXN0IFwiJHt0ZXN0TmFtZX1cIiBpbiBtb2R1bGUgJHttb2R1bGVOYW1lfWAsIHtcbiAgICAgICAgbW9kdWxlTmFtZSxcbiAgICAgICAgdGVzdE5hbWUsXG4gICAgICAgIGF2YWlsYWJsZVRlc3RzOiBnZXROYW1lcyhtb2R1bGUudGVzdENvbmZpZ3MpLFxuICAgICAgfSlcbiAgICB9XG5cbiAgICBnYXJkZW4ubG9nLmhlYWRlcih7XG4gICAgICBlbW9qaTogXCJydW5uZXJcIixcbiAgICAgIGNvbW1hbmQ6IGBSdW5uaW5nIHRlc3QgJHtjaGFsay5jeWFuKHRlc3ROYW1lKX0gaW4gbW9kdWxlICR7Y2hhbGsuY3lhbihtb2R1bGVOYW1lKX1gLFxuICAgIH0pXG5cbiAgICBhd2FpdCBnYXJkZW4uYWN0aW9ucy5wcmVwYXJlRW52aXJvbm1lbnQoe30pXG5cbiAgICBjb25zdCBidWlsZFRhc2sgPSBuZXcgQnVpbGRUYXNrKHsgZ2FyZGVuLCBtb2R1bGUsIGZvcmNlOiBvcHRzW1wiZm9yY2UtYnVpbGRcIl0gfSlcbiAgICBhd2FpdCBnYXJkZW4uYWRkVGFzayhidWlsZFRhc2spXG4gICAgYXdhaXQgZ2FyZGVuLnByb2Nlc3NUYXNrcygpXG5cbiAgICBjb25zdCBpbnRlcmFjdGl2ZSA9IG9wdHMuaW50ZXJhY3RpdmVcbiAgICBjb25zdCBkZXBzID0gYXdhaXQgZ2FyZGVuLmdldFNlcnZpY2VzKHRlc3RDb25maWcuZGVwZW5kZW5jaWVzKVxuICAgIGNvbnN0IHJ1bnRpbWVDb250ZXh0ID0gYXdhaXQgcHJlcGFyZVJ1bnRpbWVDb250ZXh0KGdhcmRlbiwgbW9kdWxlLCBkZXBzKVxuXG4gICAgcHJpbnRSdW50aW1lQ29udGV4dChnYXJkZW4sIHJ1bnRpbWVDb250ZXh0KVxuXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZ2FyZGVuLmFjdGlvbnMudGVzdE1vZHVsZSh7XG4gICAgICBtb2R1bGUsXG4gICAgICBpbnRlcmFjdGl2ZSxcbiAgICAgIHJ1bnRpbWVDb250ZXh0LFxuICAgICAgc2lsZW50OiBmYWxzZSxcbiAgICAgIHRlc3RDb25maWcsXG4gICAgfSlcblxuICAgIHJldHVybiB7IHJlc3VsdCB9XG4gIH1cbn1cbiJdfQ==
diff --git a/garden-service/build/commands/scan.d.ts b/garden-service/build/commands/scan.d.ts
new file mode 100644
index 00000000000..ab19af30eaa
--- /dev/null
+++ b/garden-service/build/commands/scan.d.ts
@@ -0,0 +1,8 @@
+import { DeepPrimitiveMap } from "../config/common";
+import { Command, CommandParams, CommandResult } from "./base";
+export declare class ScanCommand extends Command {
+ name: string;
+ help: string;
+ action({ garden }: CommandParams): Promise>;
+}
+//# sourceMappingURL=scan.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/scan.js b/garden-service/build/commands/scan.js
new file mode 100644
index 00000000000..0745a5ecbb6
--- /dev/null
+++ b/garden-service/build/commands/scan.js
@@ -0,0 +1,49 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const js_yaml_1 = require("js-yaml");
+const util_1 = require("../util/util");
+const base_1 = require("./base");
+const lodash_1 = require("lodash");
+class ScanCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "scan";
+ this.help = "Scans your project and outputs an overview of all modules.";
+ }
+ action({ garden }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const modules = (yield garden.getModules())
+ .map(m => {
+ m.services.forEach(s => delete s.module);
+ return lodash_1.omit(m, ["_ConfigType", "cacheContext", "serviceConfigs", "serviceNames"]);
+ });
+ const output = { modules };
+ const shortOutput = {
+ modules: modules.map(m => {
+ m.services.map(s => delete s.spec);
+ return lodash_1.omit(m, ["spec"]);
+ }),
+ };
+ garden.log.info(util_1.highlightYaml(js_yaml_1.safeDump(shortOutput, { noRefs: true, skipInvalid: true, sortKeys: true })));
+ return { result: output };
+ });
+ }
+}
+exports.ScanCommand = ScanCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL3NjYW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7R0FNRzs7Ozs7Ozs7OztBQUVILHFDQUFrQztBQUVsQyx1Q0FBNEM7QUFDNUMsaUNBSWU7QUFDZixtQ0FBNkI7QUFFN0IsTUFBYSxXQUFZLFNBQVEsY0FBTztJQUF4Qzs7UUFDRSxTQUFJLEdBQUcsTUFBTSxDQUFBO1FBQ2IsU0FBSSxHQUFHLDREQUE0RCxDQUFBO0lBc0JyRSxDQUFDO0lBcEJPLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFBaUI7O1lBQ3BDLE1BQU0sT0FBTyxHQUFHLENBQUMsTUFBTSxNQUFNLENBQUMsVUFBVSxFQUFFLENBQUM7aUJBQ3hDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRTtnQkFDUCxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFBO2dCQUN4QyxPQUFPLGFBQUksQ0FBQyxDQUFDLEVBQUUsQ0FBQyxhQUFhLEVBQUUsY0FBYyxFQUFFLGdCQUFnQixFQUFFLGNBQWMsQ0FBQyxDQUFDLENBQUE7WUFDbkYsQ0FBQyxDQUFDLENBQUE7WUFFSixNQUFNLE1BQU0sR0FBRyxFQUFFLE9BQU8sRUFBRSxDQUFBO1lBRTFCLE1BQU0sV0FBVyxHQUFHO2dCQUNsQixPQUFPLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRTtvQkFDdkIsQ0FBQyxDQUFDLFFBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQTtvQkFDbkMsT0FBTyxhQUFJLENBQUMsQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQTtnQkFDMUIsQ0FBQyxDQUFDO2FBQ0gsQ0FBQTtZQUVELE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLG9CQUFhLENBQUMsa0JBQVEsQ0FBQyxXQUFXLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFBO1lBRTFHLE9BQU8sRUFBRSxNQUFNLEVBQXlCLE1BQU0sRUFBRSxDQUFBO1FBQ2xELENBQUM7S0FBQTtDQUNGO0FBeEJELGtDQXdCQyIsImZpbGUiOiJjb21tYW5kcy9zY2FuLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoQykgMjAxOCBHYXJkZW4gVGVjaG5vbG9naWVzLCBJbmMuIDxpbmZvQGdhcmRlbi5pbz5cbiAqXG4gKiBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljXG4gKiBMaWNlbnNlLCB2LiAyLjAuIElmIGEgY29weSBvZiB0aGUgTVBMIHdhcyBub3QgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzXG4gKiBmaWxlLCBZb3UgY2FuIG9idGFpbiBvbmUgYXQgaHR0cDovL21vemlsbGEub3JnL01QTC8yLjAvLlxuICovXG5cbmltcG9ydCB7IHNhZmVEdW1wIH0gZnJvbSBcImpzLXlhbWxcIlxuaW1wb3J0IHsgRGVlcFByaW1pdGl2ZU1hcCB9IGZyb20gXCIuLi9jb25maWcvY29tbW9uXCJcbmltcG9ydCB7IGhpZ2hsaWdodFlhbWwgfSBmcm9tIFwiLi4vdXRpbC91dGlsXCJcbmltcG9ydCB7XG4gIENvbW1hbmQsXG4gIENvbW1hbmRQYXJhbXMsXG4gIENvbW1hbmRSZXN1bHQsXG59IGZyb20gXCIuL2Jhc2VcIlxuaW1wb3J0IHsgb21pdCB9IGZyb20gXCJsb2Rhc2hcIlxuXG5leHBvcnQgY2xhc3MgU2NhbkNvbW1hbmQgZXh0ZW5kcyBDb21tYW5kIHtcbiAgbmFtZSA9IFwic2NhblwiXG4gIGhlbHAgPSBcIlNjYW5zIHlvdXIgcHJvamVjdCBhbmQgb3V0cHV0cyBhbiBvdmVydmlldyBvZiBhbGwgbW9kdWxlcy5cIlxuXG4gIGFzeW5jIGFjdGlvbih7IGdhcmRlbiB9OiBDb21tYW5kUGFyYW1zKTogUHJvbWlzZTxDb21tYW5kUmVzdWx0PERlZXBQcmltaXRpdmVNYXA+PiB7XG4gICAgY29uc3QgbW9kdWxlcyA9IChhd2FpdCBnYXJkZW4uZ2V0TW9kdWxlcygpKVxuICAgICAgLm1hcChtID0+IHtcbiAgICAgICAgbS5zZXJ2aWNlcy5mb3JFYWNoKHMgPT4gZGVsZXRlIHMubW9kdWxlKVxuICAgICAgICByZXR1cm4gb21pdChtLCBbXCJfQ29uZmlnVHlwZVwiLCBcImNhY2hlQ29udGV4dFwiLCBcInNlcnZpY2VDb25maWdzXCIsIFwic2VydmljZU5hbWVzXCJdKVxuICAgICAgfSlcblxuICAgIGNvbnN0IG91dHB1dCA9IHsgbW9kdWxlcyB9XG5cbiAgICBjb25zdCBzaG9ydE91dHB1dCA9IHtcbiAgICAgIG1vZHVsZXM6IG1vZHVsZXMubWFwKG0gPT4ge1xuICAgICAgICBtLnNlcnZpY2VzIS5tYXAocyA9PiBkZWxldGUgcy5zcGVjKVxuICAgICAgICByZXR1cm4gb21pdChtLCBbXCJzcGVjXCJdKVxuICAgICAgfSksXG4gICAgfVxuXG4gICAgZ2FyZGVuLmxvZy5pbmZvKGhpZ2hsaWdodFlhbWwoc2FmZUR1bXAoc2hvcnRPdXRwdXQsIHsgbm9SZWZzOiB0cnVlLCBza2lwSW52YWxpZDogdHJ1ZSwgc29ydEtleXM6IHRydWUgfSkpKVxuXG4gICAgcmV0dXJuIHsgcmVzdWx0OiA8RGVlcFByaW1pdGl2ZU1hcD48YW55Pm91dHB1dCB9XG4gIH1cbn1cbiJdfQ==
diff --git a/garden-service/build/commands/set.d.ts b/garden-service/build/commands/set.d.ts
new file mode 100644
index 00000000000..5869ad745b6
--- /dev/null
+++ b/garden-service/build/commands/set.d.ts
@@ -0,0 +1,27 @@
+import { SetSecretResult } from "../types/plugin/outputs";
+import { Command, CommandResult, CommandParams, StringParameter } from "./base";
+export declare class SetCommand extends Command {
+ name: string;
+ help: string;
+ subCommands: (typeof SetSecretCommand)[];
+ action(): Promise<{}>;
+}
+declare const setSecretArgs: {
+ provider: StringParameter;
+ key: StringParameter;
+ value: StringParameter;
+};
+declare type SetArgs = typeof setSecretArgs;
+export declare class SetSecretCommand extends Command {
+ name: string;
+ help: string;
+ description: string;
+ arguments: {
+ provider: StringParameter;
+ key: StringParameter;
+ value: StringParameter;
+ };
+ action({ garden, args }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=set.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/set.js b/garden-service/build/commands/set.js
new file mode 100644
index 00000000000..8ac4de3fd9b
--- /dev/null
+++ b/garden-service/build/commands/set.js
@@ -0,0 +1,79 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const base_1 = require("./base");
+const dedent = require("dedent");
+class SetCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "set";
+ this.help = "Set or modify data, e.g. secrets.";
+ this.subCommands = [
+ SetSecretCommand,
+ ];
+ }
+ action() {
+ return __awaiter(this, void 0, void 0, function* () { return {}; });
+ }
+}
+exports.SetCommand = SetCommand;
+const setSecretArgs = {
+ provider: new base_1.StringParameter({
+ help: "The name of the provider to store the secret with.",
+ required: true,
+ }),
+ key: new base_1.StringParameter({
+ help: "A unique identifier for the secret.",
+ required: true,
+ }),
+ value: new base_1.StringParameter({
+ help: "The value of the secret.",
+ required: true,
+ }),
+};
+// TODO: allow storing data from files
+class SetSecretCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "secret";
+ this.help = "Set a secret value for a provider in an environment.";
+ this.description = dedent `
+ These secrets are handled by each provider, and may for example be exposed as environment
+ variables for services or mounted as files, depending on how the provider is implemented
+ and configured.
+
+ _Note: The value is currently always stored as a string._
+
+ Examples:
+
+ garden set secret kubernetes somekey myvalue
+ garden set secret local-kubernets somekey myvalue
+ `;
+ this.arguments = setSecretArgs;
+ }
+ action({ garden, args }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const key = args.key;
+ const result = yield garden.actions.setSecret({ pluginName: args.provider, key, value: args.value });
+ garden.log.info(`Set config key ${args.key}`);
+ return { result };
+ });
+ }
+}
+exports.SetSecretCommand = SetSecretCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL3NldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7OztHQU1HOzs7Ozs7Ozs7O0FBR0gsaUNBS2U7QUFDZixpQ0FBaUM7QUFFakMsTUFBYSxVQUFXLFNBQVEsY0FBTztJQUF2Qzs7UUFDRSxTQUFJLEdBQUcsS0FBSyxDQUFBO1FBQ1osU0FBSSxHQUFHLG1DQUFtQyxDQUFBO1FBRTFDLGdCQUFXLEdBQUc7WUFDWixnQkFBZ0I7U0FDakIsQ0FBQTtJQUdILENBQUM7SUFETyxNQUFNOzhEQUFLLE9BQU8sRUFBRSxDQUFBLENBQUMsQ0FBQztLQUFBO0NBQzdCO0FBVEQsZ0NBU0M7QUFFRCxNQUFNLGFBQWEsR0FBRztJQUNwQixRQUFRLEVBQUUsSUFBSSxzQkFBZSxDQUFDO1FBQzVCLElBQUksRUFBRSxvREFBb0Q7UUFDMUQsUUFBUSxFQUFFLElBQUk7S0FDZixDQUFDO0lBQ0YsR0FBRyxFQUFFLElBQUksc0JBQWUsQ0FBQztRQUN2QixJQUFJLEVBQUUscUNBQXFDO1FBQzNDLFFBQVEsRUFBRSxJQUFJO0tBQ2YsQ0FBQztJQUNGLEtBQUssRUFBRSxJQUFJLHNCQUFlLENBQUM7UUFDekIsSUFBSSxFQUFFLDBCQUEwQjtRQUNoQyxRQUFRLEVBQUUsSUFBSTtLQUNmLENBQUM7Q0FDSCxDQUFBO0FBSUQsc0NBQXNDO0FBRXRDLE1BQWEsZ0JBQWlCLFNBQVEsY0FBNkI7SUFBbkU7O1FBQ0UsU0FBSSxHQUFHLFFBQVEsQ0FBQTtRQUNmLFNBQUksR0FBRyxzREFBc0QsQ0FBQTtRQUU3RCxnQkFBVyxHQUFHLE1BQU0sQ0FBQTs7Ozs7Ozs7Ozs7R0FXbkIsQ0FBQTtRQUVELGNBQVMsR0FBRyxhQUFhLENBQUE7SUFRM0IsQ0FBQztJQU5PLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQTBCOztZQUNuRCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFBO1lBQ3BCLE1BQU0sTUFBTSxHQUFHLE1BQU0sTUFBTSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxVQUFVLEVBQUUsSUFBSSxDQUFDLFFBQVEsRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFBO1lBQ3BHLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLGtCQUFrQixJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQTtZQUM3QyxPQUFPLEVBQUUsTUFBTSxFQUFFLENBQUE7UUFDbkIsQ0FBQztLQUFBO0NBQ0Y7QUF6QkQsNENBeUJDIiwiZmlsZSI6ImNvbW1hbmRzL3NldC5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgeyBTZXRTZWNyZXRSZXN1bHQgfSBmcm9tIFwiLi4vdHlwZXMvcGx1Z2luL291dHB1dHNcIlxuaW1wb3J0IHtcbiAgQ29tbWFuZCxcbiAgQ29tbWFuZFJlc3VsdCxcbiAgQ29tbWFuZFBhcmFtcyxcbiAgU3RyaW5nUGFyYW1ldGVyLFxufSBmcm9tIFwiLi9iYXNlXCJcbmltcG9ydCBkZWRlbnQgPSByZXF1aXJlKFwiZGVkZW50XCIpXG5cbmV4cG9ydCBjbGFzcyBTZXRDb21tYW5kIGV4dGVuZHMgQ29tbWFuZCB7XG4gIG5hbWUgPSBcInNldFwiXG4gIGhlbHAgPSBcIlNldCBvciBtb2RpZnkgZGF0YSwgZS5nLiBzZWNyZXRzLlwiXG5cbiAgc3ViQ29tbWFuZHMgPSBbXG4gICAgU2V0U2VjcmV0Q29tbWFuZCxcbiAgXVxuXG4gIGFzeW5jIGFjdGlvbigpIHsgcmV0dXJuIHt9IH1cbn1cblxuY29uc3Qgc2V0U2VjcmV0QXJncyA9IHtcbiAgcHJvdmlkZXI6IG5ldyBTdHJpbmdQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiVGhlIG5hbWUgb2YgdGhlIHByb3ZpZGVyIHRvIHN0b3JlIHRoZSBzZWNyZXQgd2l0aC5cIixcbiAgICByZXF1aXJlZDogdHJ1ZSxcbiAgfSksXG4gIGtleTogbmV3IFN0cmluZ1BhcmFtZXRlcih7XG4gICAgaGVscDogXCJBIHVuaXF1ZSBpZGVudGlmaWVyIGZvciB0aGUgc2VjcmV0LlwiLFxuICAgIHJlcXVpcmVkOiB0cnVlLFxuICB9KSxcbiAgdmFsdWU6IG5ldyBTdHJpbmdQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiVGhlIHZhbHVlIG9mIHRoZSBzZWNyZXQuXCIsXG4gICAgcmVxdWlyZWQ6IHRydWUsXG4gIH0pLFxufVxuXG50eXBlIFNldEFyZ3MgPSB0eXBlb2Ygc2V0U2VjcmV0QXJnc1xuXG4vLyBUT0RPOiBhbGxvdyBzdG9yaW5nIGRhdGEgZnJvbSBmaWxlc1xuXG5leHBvcnQgY2xhc3MgU2V0U2VjcmV0Q29tbWFuZCBleHRlbmRzIENvbW1hbmQ8dHlwZW9mIHNldFNlY3JldEFyZ3M+IHtcbiAgbmFtZSA9IFwic2VjcmV0XCJcbiAgaGVscCA9IFwiU2V0IGEgc2VjcmV0IHZhbHVlIGZvciBhIHByb3ZpZGVyIGluIGFuIGVudmlyb25tZW50LlwiXG5cbiAgZGVzY3JpcHRpb24gPSBkZWRlbnRgXG4gICAgVGhlc2Ugc2VjcmV0cyBhcmUgaGFuZGxlZCBieSBlYWNoIHByb3ZpZGVyLCBhbmQgbWF5IGZvciBleGFtcGxlIGJlIGV4cG9zZWQgYXMgZW52aXJvbm1lbnRcbiAgICB2YXJpYWJsZXMgZm9yIHNlcnZpY2VzIG9yIG1vdW50ZWQgYXMgZmlsZXMsIGRlcGVuZGluZyBvbiBob3cgdGhlIHByb3ZpZGVyIGlzIGltcGxlbWVudGVkXG4gICAgYW5kIGNvbmZpZ3VyZWQuXG5cbiAgICBfTm90ZTogVGhlIHZhbHVlIGlzIGN1cnJlbnRseSBhbHdheXMgc3RvcmVkIGFzIGEgc3RyaW5nLl9cblxuICAgIEV4YW1wbGVzOlxuXG4gICAgICAgIGdhcmRlbiBzZXQgc2VjcmV0IGt1YmVybmV0ZXMgc29tZWtleSBteXZhbHVlXG4gICAgICAgIGdhcmRlbiBzZXQgc2VjcmV0IGxvY2FsLWt1YmVybmV0cyBzb21la2V5IG15dmFsdWVcbiAgYFxuXG4gIGFyZ3VtZW50cyA9IHNldFNlY3JldEFyZ3NcblxuICBhc3luYyBhY3Rpb24oeyBnYXJkZW4sIGFyZ3MgfTogQ29tbWFuZFBhcmFtczxTZXRBcmdzPik6IFByb21pc2U8Q29tbWFuZFJlc3VsdDxTZXRTZWNyZXRSZXN1bHQ+PiB7XG4gICAgY29uc3Qga2V5ID0gYXJncy5rZXlcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBnYXJkZW4uYWN0aW9ucy5zZXRTZWNyZXQoeyBwbHVnaW5OYW1lOiBhcmdzLnByb3ZpZGVyLCBrZXksIHZhbHVlOiBhcmdzLnZhbHVlIH0pXG4gICAgZ2FyZGVuLmxvZy5pbmZvKGBTZXQgY29uZmlnIGtleSAke2FyZ3Mua2V5fWApXG4gICAgcmV0dXJuIHsgcmVzdWx0IH1cbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/test.d.ts b/garden-service/build/commands/test.d.ts
new file mode 100644
index 00000000000..a69727ac4e4
--- /dev/null
+++ b/garden-service/build/commands/test.d.ts
@@ -0,0 +1,40 @@
+import { BooleanParameter, Command, CommandParams, CommandResult, StringOption, StringsParameter } from "./base";
+import { TaskResults } from "../task-graph";
+import { Module } from "../types/module";
+import { TestTask } from "../tasks/test";
+import { Garden } from "../garden";
+declare const testArgs: {
+ module: StringsParameter;
+};
+declare const testOpts: {
+ name: StringOption;
+ force: BooleanParameter;
+ "force-build": BooleanParameter;
+ watch: BooleanParameter;
+};
+declare type Args = typeof testArgs;
+declare type Opts = typeof testOpts;
+export declare class TestCommand extends Command {
+ name: string;
+ help: string;
+ description: string;
+ arguments: {
+ module: StringsParameter;
+ };
+ options: {
+ name: StringOption;
+ force: BooleanParameter;
+ "force-build": BooleanParameter;
+ watch: BooleanParameter;
+ };
+ action({ garden, args, opts }: CommandParams): Promise>;
+}
+export declare function getTestTasks({ garden, module, name, force, forceBuild }: {
+ garden: Garden;
+ module: Module;
+ name?: string;
+ force?: boolean;
+ forceBuild?: boolean;
+}): Promise;
+export {};
+//# sourceMappingURL=test.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/test.js b/garden-service/build/commands/test.js
new file mode 100644
index 00000000000..97a65cbe7b7
--- /dev/null
+++ b/garden-service/build/commands/test.js
@@ -0,0 +1,116 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const Bluebird = require("bluebird");
+const lodash_1 = require("lodash");
+const base_1 = require("./base");
+const process_1 = require("../process");
+const test_1 = require("../tasks/test");
+const watch_1 = require("../watch");
+const testArgs = {
+ module: new base_1.StringsParameter({
+ help: "The name of the module(s) to deploy (skip to test all modules). " +
+ "Use comma as separator to specify multiple modules.",
+ }),
+};
+const testOpts = {
+ name: new base_1.StringOption({
+ help: "Only run tests with the specfied name (e.g. unit or integ).",
+ alias: "n",
+ }),
+ force: new base_1.BooleanParameter({ help: "Force re-test of module(s).", alias: "f" }),
+ "force-build": new base_1.BooleanParameter({ help: "Force rebuild of module(s)." }),
+ watch: new base_1.BooleanParameter({ help: "Watch for changes in module(s) and auto-test.", alias: "w" }),
+};
+class TestCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "test";
+ this.help = "Test all or specified modules.";
+ this.description = `
+ Runs all or specified tests defined in the project. Also builds modules and dependencies,
+ and deploy service dependencies if needed.
+
+ Optionally stays running and automatically re-runs tests if their module source
+ (or their dependencies' sources) change.
+
+ Examples:
+
+ garden test # run all tests in the project
+ garden test my-module # run all tests in the my-module module
+ garden test -n integ # run all tests with the name 'integ' in the project
+ garden test --force # force tests to be re-run, even if they're already run successfully
+ garden test --watch # watch for changes to code
+ `;
+ this.arguments = testArgs;
+ this.options = testOpts;
+ }
+ action({ garden, args, opts }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const autoReloadDependants = yield watch_1.computeAutoReloadDependants(garden);
+ let modules;
+ if (args.module) {
+ modules = yield watch_1.withDependants(garden, yield garden.getModules(args.module), autoReloadDependants);
+ }
+ else {
+ // All modules are included in this case, so there's no need to compute dependants.
+ modules = yield garden.getModules();
+ }
+ garden.log.header({
+ emoji: "thermometer",
+ command: `Running tests`,
+ });
+ yield garden.actions.prepareEnvironment({});
+ const name = opts.name;
+ const force = opts.force;
+ const forceBuild = opts["force-build"];
+ const results = yield process_1.processModules({
+ garden,
+ modules,
+ watch: opts.watch,
+ handler: (module) => __awaiter(this, void 0, void 0, function* () { return getTestTasks({ garden, module, name, force, forceBuild }); }),
+ changeHandler: (module) => __awaiter(this, void 0, void 0, function* () {
+ const modulesToProcess = yield watch_1.withDependants(garden, [module], autoReloadDependants);
+ return lodash_1.flatten(yield Bluebird.map(modulesToProcess, m => getTestTasks({ garden, module: m, name, force, forceBuild })));
+ }),
+ });
+ return base_1.handleTaskResults(garden, "test", results);
+ });
+ }
+}
+exports.TestCommand = TestCommand;
+function getTestTasks({ garden, module, name, force = false, forceBuild = false }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const tasks = [];
+ for (const test of module.testConfigs) {
+ if (name && test.name !== name) {
+ continue;
+ }
+ tasks.push(test_1.TestTask.factory({
+ garden,
+ force,
+ forceBuild,
+ testConfig: test,
+ module,
+ }));
+ }
+ return Bluebird.all(tasks);
+ });
+}
+exports.getTestTasks = getTestTasks;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,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QUFPLENBQUMsTUFBTSxRQUFRLENBQUMsR0FBRyxDQUMvQixnQkFBZ0IsRUFDaEIsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFBO2dCQUN2RSxDQUFDLENBQUE7YUFDRixDQUFDLENBQUE7WUFFRixPQUFPLHdCQUFpQixDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUE7UUFDbkQsQ0FBQztLQUFBO0NBQ0Y7QUEzREQsa0NBMkRDO0FBRUQsU0FBc0IsWUFBWSxDQUNoQyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLEtBQUssR0FBRyxLQUFLLEVBQUUsVUFBVSxHQUFHLEtBQUssRUFDaUM7O1FBRTFGLE1BQU0sS0FBSyxHQUF3QixFQUFFLENBQUE7UUFFckMsS0FBSyxNQUFNLElBQUksSUFBSSxNQUFNLENBQUMsV0FBVyxFQUFFO1lBQ3JDLElBQUksSUFBSSxJQUFJLElBQUksQ0FBQyxJQUFJLEtBQUssSUFBSSxFQUFFO2dCQUM5QixTQUFRO2FBQ1Q7WUFDRCxLQUFLLENBQUMsSUFBSSxDQUFDLGVBQVEsQ0FBQyxPQUFPLENBQUM7Z0JBQzFCLE1BQU07Z0JBQ04sS0FBSztnQkFDTCxVQUFVO2dCQUNWLFVBQVUsRUFBRSxJQUFJO2dCQUNoQixNQUFNO2FBQ1AsQ0FBQyxDQUFDLENBQUE7U0FDSjtRQUVELE9BQU8sUUFBUSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUM1QixDQUFDO0NBQUE7QUFwQkQsb0NBb0JDIiwiZmlsZSI6ImNvbW1hbmRzL3Rlc3QuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChDKSAyMDE4IEdhcmRlbiBUZWNobm9sb2dpZXMsIEluYy4gPGluZm9AZ2FyZGVuLmlvPlxuICpcbiAqIFRoaXMgU291cmNlIENvZGUgRm9ybSBpcyBzdWJqZWN0IHRvIHRoZSB0ZXJtcyBvZiB0aGUgTW96aWxsYSBQdWJsaWNcbiAqIExpY2Vuc2UsIHYuIDIuMC4gSWYgYSBjb3B5IG9mIHRoZSBNUEwgd2FzIG5vdCBkaXN0cmlidXRlZCB3aXRoIHRoaXNcbiAqIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uXG4gKi9cblxuaW1wb3J0ICogYXMgQmx1ZWJpcmQgZnJvbSBcImJsdWViaXJkXCJcbmltcG9ydCB7IGZsYXR0ZW4gfSBmcm9tIFwibG9kYXNoXCJcbmltcG9ydCB7XG4gIEJvb2xlYW5QYXJhbWV0ZXIsXG4gIENvbW1hbmQsXG4gIENvbW1hbmRQYXJhbXMsXG4gIENvbW1hbmRSZXN1bHQsXG4gIGhhbmRsZVRhc2tSZXN1bHRzLFxuICBTdHJpbmdPcHRpb24sXG4gIFN0cmluZ3NQYXJhbWV0ZXIsXG59IGZyb20gXCIuL2Jhc2VcIlxuaW1wb3J0IHsgVGFza1Jlc3VsdHMgfSBmcm9tIFwiLi4vdGFzay1ncmFwaFwiXG5pbXBvcnQgeyBwcm9jZXNzTW9kdWxlcyB9IGZyb20gXCIuLi9wcm9jZXNzXCJcbmltcG9ydCB7IE1vZHVsZSB9IGZyb20gXCIuLi90eXBlcy9tb2R1bGVcIlxuaW1wb3J0IHsgVGVzdFRhc2sgfSBmcm9tIFwiLi4vdGFza3MvdGVzdFwiXG5pbXBvcnQgeyBjb21wdXRlQXV0b1JlbG9hZERlcGVuZGFudHMsIHdpdGhEZXBlbmRhbnRzIH0gZnJvbSBcIi4uL3dhdGNoXCJcbmltcG9ydCB7IEdhcmRlbiB9IGZyb20gXCIuLi9nYXJkZW5cIlxuXG5jb25zdCB0ZXN0QXJncyA9IHtcbiAgbW9kdWxlOiBuZXcgU3RyaW5nc1BhcmFtZXRlcih7XG4gICAgaGVscDogXCJUaGUgbmFtZSBvZiB0aGUgbW9kdWxlKHMpIHRvIGRlcGxveSAoc2tpcCB0byB0ZXN0IGFsbCBtb2R1bGVzKS4gXCIgK1xuICAgICAgXCJVc2UgY29tbWEgYXMgc2VwYXJhdG9yIHRvIHNwZWNpZnkgbXVsdGlwbGUgbW9kdWxlcy5cIixcbiAgfSksXG59XG5cbmNvbnN0IHRlc3RPcHRzID0ge1xuICBuYW1lOiBuZXcgU3RyaW5nT3B0aW9uKHtcbiAgICBoZWxwOiBcIk9ubHkgcnVuIHRlc3RzIHdpdGggdGhlIHNwZWNmaWVkIG5hbWUgKGUuZy4gdW5pdCBvciBpbnRlZykuXCIsXG4gICAgYWxpYXM6IFwiblwiLFxuICB9KSxcbiAgZm9yY2U6IG5ldyBCb29sZWFuUGFyYW1ldGVyKHsgaGVscDogXCJGb3JjZSByZS10ZXN0IG9mIG1vZHVsZShzKS5cIiwgYWxpYXM6IFwiZlwiIH0pLFxuICBcImZvcmNlLWJ1aWxkXCI6IG5ldyBCb29sZWFuUGFyYW1ldGVyKHsgaGVscDogXCJGb3JjZSByZWJ1aWxkIG9mIG1vZHVsZShzKS5cIiB9KSxcbiAgd2F0Y2g6IG5ldyBCb29sZWFuUGFyYW1ldGVyKHsgaGVscDogXCJXYXRjaCBmb3IgY2hhbmdlcyBpbiBtb2R1bGUocykgYW5kIGF1dG8tdGVzdC5cIiwgYWxpYXM6IFwid1wiIH0pLFxufVxuXG50eXBlIEFyZ3MgPSB0eXBlb2YgdGVzdEFyZ3NcbnR5cGUgT3B0cyA9IHR5cGVvZiB0ZXN0T3B0c1xuXG5leHBvcnQgY2xhc3MgVGVzdENvbW1hbmQgZXh0ZW5kcyBDb21tYW5kPEFyZ3MsIE9wdHM+IHtcbiAgbmFtZSA9IFwidGVzdFwiXG4gIGhlbHAgPSBcIlRlc3QgYWxsIG9yIHNwZWNpZmllZCBtb2R1bGVzLlwiXG5cbiAgZGVzY3JpcHRpb24gPSBgXG4gICAgUnVucyBhbGwgb3Igc3BlY2lmaWVkIHRlc3RzIGRlZmluZWQgaW4gdGhlIHByb2plY3QuIEFsc28gYnVpbGRzIG1vZHVsZXMgYW5kIGRlcGVuZGVuY2llcyxcbiAgICBhbmQgZGVwbG95IHNlcnZpY2UgZGVwZW5kZW5jaWVzIGlmIG5lZWRlZC5cblxuICAgIE9wdGlvbmFsbHkgc3RheXMgcnVubmluZyBhbmQgYXV0b21hdGljYWxseSByZS1ydW5zIHRlc3RzIGlmIHRoZWlyIG1vZHVsZSBzb3VyY2VcbiAgICAob3IgdGhlaXIgZGVwZW5kZW5jaWVzJyBzb3VyY2VzKSBjaGFuZ2UuXG5cbiAgICBFeGFtcGxlczpcblxuICAgICAgICBnYXJkZW4gdGVzdCAgICAgICAgICAgICAgIyBydW4gYWxsIHRlc3RzIGluIHRoZSBwcm9qZWN0XG4gICAgICAgIGdhcmRlbiB0ZXN0IG15LW1vZHVsZSAgICAjIHJ1biBhbGwgdGVzdHMgaW4gdGhlIG15LW1vZHVsZSBtb2R1bGVcbiAgICAgICAgZ2FyZGVuIHRlc3QgLW4gaW50ZWcgICAgICMgcnVuIGFsbCB0ZXN0cyB3aXRoIHRoZSBuYW1lICdpbnRlZycgaW4gdGhlIHByb2plY3RcbiAgICAgICAgZ2FyZGVuIHRlc3QgLS1mb3JjZSAgICAgICMgZm9yY2UgdGVzdHMgdG8gYmUgcmUtcnVuLCBldmVuIGlmIHRoZXkncmUgYWxyZWFkeSBydW4gc3VjY2Vzc2Z1bGx5XG4gICAgICAgIGdhcmRlbiB0ZXN0IC0td2F0Y2ggICAgICAjIHdhdGNoIGZvciBjaGFuZ2VzIHRvIGNvZGVcbiAgYFxuXG4gIGFyZ3VtZW50cyA9IHRlc3RBcmdzXG4gIG9wdGlvbnMgPSB0ZXN0T3B0c1xuXG4gIGFzeW5jIGFjdGlvbih7IGdhcmRlbiwgYXJncywgb3B0cyB9OiBDb21tYW5kUGFyYW1zPEFyZ3MsIE9wdHM+KTogUHJvbWlzZTxDb21tYW5kUmVzdWx0PFRhc2tSZXN1bHRzPj4ge1xuICAgIGNvbnN0IGF1dG9SZWxvYWREZXBlbmRhbnRzID0gYXdhaXQgY29tcHV0ZUF1dG9SZWxvYWREZXBlbmRhbnRzKGdhcmRlbilcbiAgICBsZXQgbW9kdWxlczogTW9kdWxlW11cbiAgICBpZiAoYXJncy5tb2R1bGUpIHtcbiAgICAgIG1vZHVsZXMgPSBhd2FpdCB3aXRoRGVwZW5kYW50cyhnYXJkZW4sIGF3YWl0IGdhcmRlbi5nZXRNb2R1bGVzKGFyZ3MubW9kdWxlKSwgYXV0b1JlbG9hZERlcGVuZGFudHMpXG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEFsbCBtb2R1bGVzIGFyZSBpbmNsdWRlZCBpbiB0aGlzIGNhc2UsIHNvIHRoZXJlJ3Mgbm8gbmVlZCB0byBjb21wdXRlIGRlcGVuZGFudHMuXG4gICAgICBtb2R1bGVzID0gYXdhaXQgZ2FyZGVuLmdldE1vZHVsZXMoKVxuICAgIH1cblxuICAgIGdhcmRlbi5sb2cuaGVhZGVyKHtcbiAgICAgIGVtb2ppOiBcInRoZXJtb21ldGVyXCIsXG4gICAgICBjb21tYW5kOiBgUnVubmluZyB0ZXN0c2AsXG4gICAgfSlcblxuICAgIGF3YWl0IGdhcmRlbi5hY3Rpb25zLnByZXBhcmVFbnZpcm9ubWVudCh7fSlcblxuICAgIGNvbnN0IG5hbWUgPSBvcHRzLm5hbWVcbiAgICBjb25zdCBmb3JjZSA9IG9wdHMuZm9yY2VcbiAgICBjb25zdCBmb3JjZUJ1aWxkID0gb3B0c1tcImZvcmNlLWJ1aWxkXCJdXG5cbiAgICBjb25zdCByZXN1bHRzID0gYXdhaXQgcHJvY2Vzc01vZHVsZXMoe1xuICAgICAgZ2FyZGVuLFxuICAgICAgbW9kdWxlcyxcbiAgICAgIHdhdGNoOiBvcHRzLndhdGNoLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKG1vZHVsZSkgPT4gZ2V0VGVzdFRhc2tzKHsgZ2FyZGVuLCBtb2R1bGUsIG5hbWUsIGZvcmNlLCBmb3JjZUJ1aWxkIH0pLFxuICAgICAgY2hhbmdlSGFuZGxlcjogYXN5bmMgKG1vZHVsZSkgPT4ge1xuICAgICAgICBjb25zdCBtb2R1bGVzVG9Qcm9jZXNzID0gYXdhaXQgd2l0aERlcGVuZGFudHMoZ2FyZGVuLCBbbW9kdWxlXSwgYXV0b1JlbG9hZERlcGVuZGFudHMpXG4gICAgICAgIHJldHVybiBmbGF0dGVuKGF3YWl0IEJsdWViaXJkLm1hcChcbiAgICAgICAgICBtb2R1bGVzVG9Qcm9jZXNzLFxuICAgICAgICAgIG0gPT4gZ2V0VGVzdFRhc2tzKHsgZ2FyZGVuLCBtb2R1bGU6IG0sIG5hbWUsIGZvcmNlLCBmb3JjZUJ1aWxkIH0pKSlcbiAgICAgIH0sXG4gICAgfSlcblxuICAgIHJldHVybiBoYW5kbGVUYXNrUmVzdWx0cyhnYXJkZW4sIFwidGVzdFwiLCByZXN1bHRzKVxuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRUZXN0VGFza3MoXG4gIHsgZ2FyZGVuLCBtb2R1bGUsIG5hbWUsIGZvcmNlID0gZmFsc2UsIGZvcmNlQnVpbGQgPSBmYWxzZSB9OlxuICAgIHsgZ2FyZGVuOiBHYXJkZW4sIG1vZHVsZTogTW9kdWxlLCBuYW1lPzogc3RyaW5nLCBmb3JjZT86IGJvb2xlYW4sIGZvcmNlQnVpbGQ/OiBib29sZWFuIH0sXG4pIHtcbiAgY29uc3QgdGFza3M6IFByb21pc2U8VGVzdFRhc2s+W10gPSBbXVxuXG4gIGZvciAoY29uc3QgdGVzdCBvZiBtb2R1bGUudGVzdENvbmZpZ3MpIHtcbiAgICBpZiAobmFtZSAmJiB0ZXN0Lm5hbWUgIT09IG5hbWUpIHtcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuICAgIHRhc2tzLnB1c2goVGVzdFRhc2suZmFjdG9yeSh7XG4gICAgICBnYXJkZW4sXG4gICAgICBmb3JjZSxcbiAgICAgIGZvcmNlQnVpbGQsXG4gICAgICB0ZXN0Q29uZmlnOiB0ZXN0LFxuICAgICAgbW9kdWxlLFxuICAgIH0pKVxuICB9XG5cbiAgcmV0dXJuIEJsdWViaXJkLmFsbCh0YXNrcylcbn1cbiJdfQ==
diff --git a/garden-service/build/commands/unlink/module.d.ts b/garden-service/build/commands/unlink/module.d.ts
new file mode 100644
index 00000000000..1db71e2e0c4
--- /dev/null
+++ b/garden-service/build/commands/unlink/module.d.ts
@@ -0,0 +1,24 @@
+import { Command, CommandResult, StringsParameter, BooleanParameter, CommandParams } from "../base";
+import { LinkedSource } from "../../config-store";
+declare const unlinkModuleArguments: {
+ module: StringsParameter;
+};
+declare const unlinkModuleOptions: {
+ all: BooleanParameter;
+};
+declare type Args = typeof unlinkModuleArguments;
+declare type Opts = typeof unlinkModuleOptions;
+export declare class UnlinkModuleCommand extends Command {
+ name: string;
+ help: string;
+ arguments: {
+ module: StringsParameter;
+ };
+ options: {
+ all: BooleanParameter;
+ };
+ description: string;
+ action({ garden, args, opts }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=module.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/unlink/module.js b/garden-service/build/commands/unlink/module.js
new file mode 100644
index 00000000000..f8ed03cf747
--- /dev/null
+++ b/garden-service/build/commands/unlink/module.js
@@ -0,0 +1,68 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const dedent = require("dedent");
+const base_1 = require("../base");
+const ext_source_util_1 = require("../../util/ext-source-util");
+const config_store_1 = require("../../config-store");
+const unlinkModuleArguments = {
+ module: new base_1.StringsParameter({
+ help: "Name of the module(s) to unlink. Use comma separator to specify multiple modules.",
+ }),
+};
+const unlinkModuleOptions = {
+ all: new base_1.BooleanParameter({
+ help: "Unlink all modules.",
+ alias: "a",
+ }),
+};
+class UnlinkModuleCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "module";
+ this.help = "Unlink a previously linked remote module from its local directory.";
+ this.arguments = unlinkModuleArguments;
+ this.options = unlinkModuleOptions;
+ this.description = dedent `
+ After unlinking a remote module, Garden will go back to reading the module's source from
+ its remote URL instead of its local directory.
+
+ Examples:
+
+ garden unlink module my-module # unlinks my-module
+ garden unlink module --all # unlink all modules
+ `;
+ }
+ action({ garden, args, opts }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ garden.log.header({ emoji: "chains", command: "unlink module" });
+ const sourceType = "module";
+ const { module = [] } = args;
+ if (opts.all) {
+ yield garden.localConfigStore.set([config_store_1.localConfigKeys.linkedModuleSources], []);
+ garden.log.info("Unlinked all modules");
+ return { result: [] };
+ }
+ const linkedModuleSources = yield ext_source_util_1.removeLinkedSources({ garden, sourceType, names: module });
+ garden.log.info(`Unlinked module(s) ${module}`);
+ return { result: linkedModuleSources };
+ });
+ }
+}
+exports.UnlinkModuleCommand = UnlinkModuleCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL3VubGluay9tb2R1bGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7R0FNRzs7Ozs7Ozs7OztBQUVILGlDQUFpQztBQUVqQyxrQ0FNZ0I7QUFDaEIsZ0VBQWdFO0FBQ2hFLHFEQUcyQjtBQUUzQixNQUFNLHFCQUFxQixHQUFHO0lBQzVCLE1BQU0sRUFBRSxJQUFJLHVCQUFnQixDQUFDO1FBQzNCLElBQUksRUFBRSxtRkFBbUY7S0FDMUYsQ0FBQztDQUNILENBQUE7QUFFRCxNQUFNLG1CQUFtQixHQUFHO0lBQzFCLEdBQUcsRUFBRSxJQUFJLHVCQUFnQixDQUFDO1FBQ3hCLElBQUksRUFBRSxxQkFBcUI7UUFDM0IsS0FBSyxFQUFFLEdBQUc7S0FDWCxDQUFDO0NBQ0gsQ0FBQTtBQUtELE1BQWEsbUJBQW9CLFNBQVEsY0FBbUI7SUFBNUQ7O1FBQ0UsU0FBSSxHQUFHLFFBQVEsQ0FBQTtRQUNmLFNBQUksR0FBRyxvRUFBb0UsQ0FBQTtRQUMzRSxjQUFTLEdBQUcscUJBQXFCLENBQUE7UUFDakMsWUFBTyxHQUFHLG1CQUFtQixDQUFBO1FBRTdCLGdCQUFXLEdBQUcsTUFBTSxDQUFBOzs7Ozs7OztHQVFuQixDQUFBO0lBcUJILENBQUM7SUFuQk8sTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQTZCOztZQUM1RCxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxDQUFDLENBQUE7WUFFaEUsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFBO1lBRTNCLE1BQU0sRUFBRSxNQUFNLEdBQUcsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFBO1lBRTVCLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRTtnQkFDWixNQUFNLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsQ0FBQyw4QkFBZSxDQUFDLG1CQUFtQixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUE7Z0JBQzVFLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLHNCQUFzQixDQUFDLENBQUE7Z0JBQ3ZDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLENBQUE7YUFDdEI7WUFFRCxNQUFNLG1CQUFtQixHQUFHLE1BQU0scUNBQW1CLENBQUMsRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFBO1lBRTVGLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLHNCQUFzQixNQUFNLEVBQUUsQ0FBQyxDQUFBO1lBRS9DLE9BQU8sRUFBRSxNQUFNLEVBQUUsbUJBQW1CLEVBQUUsQ0FBQTtRQUN4QyxDQUFDO0tBQUE7Q0FDRjtBQW5DRCxrREFtQ0MiLCJmaWxlIjoiY29tbWFuZHMvdW5saW5rL21vZHVsZS5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgZGVkZW50ID0gcmVxdWlyZShcImRlZGVudFwiKVxuXG5pbXBvcnQge1xuICBDb21tYW5kLFxuICBDb21tYW5kUmVzdWx0LFxuICBTdHJpbmdzUGFyYW1ldGVyLFxuICBCb29sZWFuUGFyYW1ldGVyLFxuICBDb21tYW5kUGFyYW1zLFxufSBmcm9tIFwiLi4vYmFzZVwiXG5pbXBvcnQgeyByZW1vdmVMaW5rZWRTb3VyY2VzIH0gZnJvbSBcIi4uLy4uL3V0aWwvZXh0LXNvdXJjZS11dGlsXCJcbmltcG9ydCB7XG4gIGxvY2FsQ29uZmlnS2V5cyxcbiAgTGlua2VkU291cmNlLFxufSBmcm9tIFwiLi4vLi4vY29uZmlnLXN0b3JlXCJcblxuY29uc3QgdW5saW5rTW9kdWxlQXJndW1lbnRzID0ge1xuICBtb2R1bGU6IG5ldyBTdHJpbmdzUGFyYW1ldGVyKHtcbiAgICBoZWxwOiBcIk5hbWUgb2YgdGhlIG1vZHVsZShzKSB0byB1bmxpbmsuIFVzZSBjb21tYSBzZXBhcmF0b3IgdG8gc3BlY2lmeSBtdWx0aXBsZSBtb2R1bGVzLlwiLFxuICB9KSxcbn1cblxuY29uc3QgdW5saW5rTW9kdWxlT3B0aW9ucyA9IHtcbiAgYWxsOiBuZXcgQm9vbGVhblBhcmFtZXRlcih7XG4gICAgaGVscDogXCJVbmxpbmsgYWxsIG1vZHVsZXMuXCIsXG4gICAgYWxpYXM6IFwiYVwiLFxuICB9KSxcbn1cblxudHlwZSBBcmdzID0gdHlwZW9mIHVubGlua01vZHVsZUFyZ3VtZW50c1xudHlwZSBPcHRzID0gdHlwZW9mIHVubGlua01vZHVsZU9wdGlvbnNcblxuZXhwb3J0IGNsYXNzIFVubGlua01vZHVsZUNvbW1hbmQgZXh0ZW5kcyBDb21tYW5kPEFyZ3MsIE9wdHM+IHtcbiAgbmFtZSA9IFwibW9kdWxlXCJcbiAgaGVscCA9IFwiVW5saW5rIGEgcHJldmlvdXNseSBsaW5rZWQgcmVtb3RlIG1vZHVsZSBmcm9tIGl0cyBsb2NhbCBkaXJlY3RvcnkuXCJcbiAgYXJndW1lbnRzID0gdW5saW5rTW9kdWxlQXJndW1lbnRzXG4gIG9wdGlvbnMgPSB1bmxpbmtNb2R1bGVPcHRpb25zXG5cbiAgZGVzY3JpcHRpb24gPSBkZWRlbnRgXG4gICAgQWZ0ZXIgdW5saW5raW5nIGEgcmVtb3RlIG1vZHVsZSwgR2FyZGVuIHdpbGwgZ28gYmFjayB0byByZWFkaW5nIHRoZSBtb2R1bGUncyBzb3VyY2UgZnJvbVxuICAgIGl0cyByZW1vdGUgVVJMIGluc3RlYWQgb2YgaXRzIGxvY2FsIGRpcmVjdG9yeS5cblxuICAgIEV4YW1wbGVzOlxuXG4gICAgICAgIGdhcmRlbiB1bmxpbmsgbW9kdWxlIG15LW1vZHVsZSAjIHVubGlua3MgbXktbW9kdWxlXG4gICAgICAgIGdhcmRlbiB1bmxpbmsgbW9kdWxlIC0tYWxsICMgdW5saW5rIGFsbCBtb2R1bGVzXG4gIGBcblxuICBhc3luYyBhY3Rpb24oeyBnYXJkZW4sIGFyZ3MsIG9wdHMgfTogQ29tbWFuZFBhcmFtczxBcmdzLCBPcHRzPik6IFByb21pc2U8Q29tbWFuZFJlc3VsdDxMaW5rZWRTb3VyY2VbXT4+IHtcbiAgICBnYXJkZW4ubG9nLmhlYWRlcih7IGVtb2ppOiBcImNoYWluc1wiLCBjb21tYW5kOiBcInVubGluayBtb2R1bGVcIiB9KVxuXG4gICAgY29uc3Qgc291cmNlVHlwZSA9IFwibW9kdWxlXCJcblxuICAgIGNvbnN0IHsgbW9kdWxlID0gW10gfSA9IGFyZ3NcblxuICAgIGlmIChvcHRzLmFsbCkge1xuICAgICAgYXdhaXQgZ2FyZGVuLmxvY2FsQ29uZmlnU3RvcmUuc2V0KFtsb2NhbENvbmZpZ0tleXMubGlua2VkTW9kdWxlU291cmNlc10sIFtdKVxuICAgICAgZ2FyZGVuLmxvZy5pbmZvKFwiVW5saW5rZWQgYWxsIG1vZHVsZXNcIilcbiAgICAgIHJldHVybiB7IHJlc3VsdDogW10gfVxuICAgIH1cblxuICAgIGNvbnN0IGxpbmtlZE1vZHVsZVNvdXJjZXMgPSBhd2FpdCByZW1vdmVMaW5rZWRTb3VyY2VzKHsgZ2FyZGVuLCBzb3VyY2VUeXBlLCBuYW1lczogbW9kdWxlIH0pXG5cbiAgICBnYXJkZW4ubG9nLmluZm8oYFVubGlua2VkIG1vZHVsZShzKSAke21vZHVsZX1gKVxuXG4gICAgcmV0dXJuIHsgcmVzdWx0OiBsaW5rZWRNb2R1bGVTb3VyY2VzIH1cbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/unlink/source.d.ts b/garden-service/build/commands/unlink/source.d.ts
new file mode 100644
index 00000000000..5754eb7ef11
--- /dev/null
+++ b/garden-service/build/commands/unlink/source.d.ts
@@ -0,0 +1,24 @@
+import { Command, CommandResult, StringsParameter, BooleanParameter, CommandParams } from "../base";
+import { LinkedSource } from "../../config-store";
+declare const unlinkSourceArguments: {
+ source: StringsParameter;
+};
+declare const unlinkSourceOptions: {
+ all: BooleanParameter;
+};
+declare type Args = typeof unlinkSourceArguments;
+declare type Opts = typeof unlinkSourceOptions;
+export declare class UnlinkSourceCommand extends Command {
+ name: string;
+ help: string;
+ arguments: {
+ source: StringsParameter;
+ };
+ options: {
+ all: BooleanParameter;
+ };
+ description: string;
+ action({ garden, args, opts }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=source.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/unlink/source.js b/garden-service/build/commands/unlink/source.js
new file mode 100644
index 00000000000..e28d7b0823e
--- /dev/null
+++ b/garden-service/build/commands/unlink/source.js
@@ -0,0 +1,68 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const dedent = require("dedent");
+const base_1 = require("../base");
+const ext_source_util_1 = require("../../util/ext-source-util");
+const config_store_1 = require("../../config-store");
+const unlinkSourceArguments = {
+ source: new base_1.StringsParameter({
+ help: "Name of the source(s) to unlink. Use comma separator to specify multiple sources.",
+ }),
+};
+const unlinkSourceOptions = {
+ all: new base_1.BooleanParameter({
+ help: "Unlink all sources.",
+ alias: "a",
+ }),
+};
+class UnlinkSourceCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "source";
+ this.help = "Unlink a previously linked remote source from its local directory.";
+ this.arguments = unlinkSourceArguments;
+ this.options = unlinkSourceOptions;
+ this.description = dedent `
+ After unlinking a remote source, Garden will go back to reading it from its remote URL instead
+ of its local directory.
+
+ Examples:
+
+ garden unlink source my-source # unlinks my-source
+ garden unlink source --all # unlinks all sources
+ `;
+ }
+ action({ garden, args, opts }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ garden.log.header({ emoji: "chains", command: "unlink source" });
+ const sourceType = "project";
+ const { source = [] } = args;
+ if (opts.all) {
+ yield garden.localConfigStore.set([config_store_1.localConfigKeys.linkedProjectSources], []);
+ garden.log.info("Unlinked all sources");
+ return { result: [] };
+ }
+ const linkedProjectSources = yield ext_source_util_1.removeLinkedSources({ garden, sourceType, names: source });
+ garden.log.info(`Unlinked source(s) ${source}`);
+ return { result: linkedProjectSources };
+ });
+ }
+}
+exports.UnlinkSourceCommand = UnlinkSourceCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL3VubGluay9zb3VyY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7R0FNRzs7Ozs7Ozs7OztBQUVILGlDQUFpQztBQUVqQyxrQ0FNZ0I7QUFDaEIsZ0VBQWdFO0FBQ2hFLHFEQUcyQjtBQUUzQixNQUFNLHFCQUFxQixHQUFHO0lBQzVCLE1BQU0sRUFBRSxJQUFJLHVCQUFnQixDQUFDO1FBQzNCLElBQUksRUFBRSxtRkFBbUY7S0FDMUYsQ0FBQztDQUNILENBQUE7QUFFRCxNQUFNLG1CQUFtQixHQUFHO0lBQzFCLEdBQUcsRUFBRSxJQUFJLHVCQUFnQixDQUFDO1FBQ3hCLElBQUksRUFBRSxxQkFBcUI7UUFDM0IsS0FBSyxFQUFFLEdBQUc7S0FDWCxDQUFDO0NBQ0gsQ0FBQTtBQUtELE1BQWEsbUJBQW9CLFNBQVEsY0FBbUI7SUFBNUQ7O1FBQ0UsU0FBSSxHQUFHLFFBQVEsQ0FBQTtRQUNmLFNBQUksR0FBRyxvRUFBb0UsQ0FBQTtRQUMzRSxjQUFTLEdBQUcscUJBQXFCLENBQUE7UUFDakMsWUFBTyxHQUFHLG1CQUFtQixDQUFBO1FBRTdCLGdCQUFXLEdBQUcsTUFBTSxDQUFBOzs7Ozs7OztHQVFuQixDQUFBO0lBcUJILENBQUM7SUFuQk8sTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQTZCOztZQUM1RCxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxDQUFDLENBQUE7WUFFaEUsTUFBTSxVQUFVLEdBQUcsU0FBUyxDQUFBO1lBRTVCLE1BQU0sRUFBRSxNQUFNLEdBQUcsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFBO1lBRTVCLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRTtnQkFDWixNQUFNLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsQ0FBQyw4QkFBZSxDQUFDLG9CQUFvQixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUE7Z0JBQzdFLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLHNCQUFzQixDQUFDLENBQUE7Z0JBQ3ZDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLENBQUE7YUFDdEI7WUFFRCxNQUFNLG9CQUFvQixHQUFHLE1BQU0scUNBQW1CLENBQUMsRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFBO1lBRTdGLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLHNCQUFzQixNQUFNLEVBQUUsQ0FBQyxDQUFBO1lBRS9DLE9BQU8sRUFBRSxNQUFNLEVBQUUsb0JBQW9CLEVBQUUsQ0FBQTtRQUN6QyxDQUFDO0tBQUE7Q0FDRjtBQW5DRCxrREFtQ0MiLCJmaWxlIjoiY29tbWFuZHMvdW5saW5rL3NvdXJjZS5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTggR2FyZGVuIFRlY2hub2xvZ2llcywgSW5jLiA8aW5mb0BnYXJkZW4uaW8+XG4gKlxuICogVGhpcyBTb3VyY2UgQ29kZSBGb3JtIGlzIHN1YmplY3QgdG8gdGhlIHRlcm1zIG9mIHRoZSBNb3ppbGxhIFB1YmxpY1xuICogTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpc1xuICogZmlsZSwgWW91IGNhbiBvYnRhaW4gb25lIGF0IGh0dHA6Ly9tb3ppbGxhLm9yZy9NUEwvMi4wLy5cbiAqL1xuXG5pbXBvcnQgZGVkZW50ID0gcmVxdWlyZShcImRlZGVudFwiKVxuXG5pbXBvcnQge1xuICBDb21tYW5kLFxuICBDb21tYW5kUmVzdWx0LFxuICBTdHJpbmdzUGFyYW1ldGVyLFxuICBCb29sZWFuUGFyYW1ldGVyLFxuICBDb21tYW5kUGFyYW1zLFxufSBmcm9tIFwiLi4vYmFzZVwiXG5pbXBvcnQgeyByZW1vdmVMaW5rZWRTb3VyY2VzIH0gZnJvbSBcIi4uLy4uL3V0aWwvZXh0LXNvdXJjZS11dGlsXCJcbmltcG9ydCB7XG4gIGxvY2FsQ29uZmlnS2V5cyxcbiAgTGlua2VkU291cmNlLFxufSBmcm9tIFwiLi4vLi4vY29uZmlnLXN0b3JlXCJcblxuY29uc3QgdW5saW5rU291cmNlQXJndW1lbnRzID0ge1xuICBzb3VyY2U6IG5ldyBTdHJpbmdzUGFyYW1ldGVyKHtcbiAgICBoZWxwOiBcIk5hbWUgb2YgdGhlIHNvdXJjZShzKSB0byB1bmxpbmsuIFVzZSBjb21tYSBzZXBhcmF0b3IgdG8gc3BlY2lmeSBtdWx0aXBsZSBzb3VyY2VzLlwiLFxuICB9KSxcbn1cblxuY29uc3QgdW5saW5rU291cmNlT3B0aW9ucyA9IHtcbiAgYWxsOiBuZXcgQm9vbGVhblBhcmFtZXRlcih7XG4gICAgaGVscDogXCJVbmxpbmsgYWxsIHNvdXJjZXMuXCIsXG4gICAgYWxpYXM6IFwiYVwiLFxuICB9KSxcbn1cblxudHlwZSBBcmdzID0gdHlwZW9mIHVubGlua1NvdXJjZUFyZ3VtZW50c1xudHlwZSBPcHRzID0gdHlwZW9mIHVubGlua1NvdXJjZU9wdGlvbnNcblxuZXhwb3J0IGNsYXNzIFVubGlua1NvdXJjZUNvbW1hbmQgZXh0ZW5kcyBDb21tYW5kPEFyZ3MsIE9wdHM+IHtcbiAgbmFtZSA9IFwic291cmNlXCJcbiAgaGVscCA9IFwiVW5saW5rIGEgcHJldmlvdXNseSBsaW5rZWQgcmVtb3RlIHNvdXJjZSBmcm9tIGl0cyBsb2NhbCBkaXJlY3RvcnkuXCJcbiAgYXJndW1lbnRzID0gdW5saW5rU291cmNlQXJndW1lbnRzXG4gIG9wdGlvbnMgPSB1bmxpbmtTb3VyY2VPcHRpb25zXG5cbiAgZGVzY3JpcHRpb24gPSBkZWRlbnRgXG4gICAgQWZ0ZXIgdW5saW5raW5nIGEgcmVtb3RlIHNvdXJjZSwgR2FyZGVuIHdpbGwgZ28gYmFjayB0byByZWFkaW5nIGl0IGZyb20gaXRzIHJlbW90ZSBVUkwgaW5zdGVhZFxuICAgIG9mIGl0cyBsb2NhbCBkaXJlY3RvcnkuXG5cbiAgICBFeGFtcGxlczpcblxuICAgICAgICBnYXJkZW4gdW5saW5rIHNvdXJjZSBteS1zb3VyY2UgIyB1bmxpbmtzIG15LXNvdXJjZVxuICAgICAgICBnYXJkZW4gdW5saW5rIHNvdXJjZSAtLWFsbCAjIHVubGlua3MgYWxsIHNvdXJjZXNcbiAgYFxuXG4gIGFzeW5jIGFjdGlvbih7IGdhcmRlbiwgYXJncywgb3B0cyB9OiBDb21tYW5kUGFyYW1zPEFyZ3MsIE9wdHM+KTogUHJvbWlzZTxDb21tYW5kUmVzdWx0PExpbmtlZFNvdXJjZVtdPj4ge1xuICAgIGdhcmRlbi5sb2cuaGVhZGVyKHsgZW1vamk6IFwiY2hhaW5zXCIsIGNvbW1hbmQ6IFwidW5saW5rIHNvdXJjZVwiIH0pXG5cbiAgICBjb25zdCBzb3VyY2VUeXBlID0gXCJwcm9qZWN0XCJcblxuICAgIGNvbnN0IHsgc291cmNlID0gW10gfSA9IGFyZ3NcblxuICAgIGlmIChvcHRzLmFsbCkge1xuICAgICAgYXdhaXQgZ2FyZGVuLmxvY2FsQ29uZmlnU3RvcmUuc2V0KFtsb2NhbENvbmZpZ0tleXMubGlua2VkUHJvamVjdFNvdXJjZXNdLCBbXSlcbiAgICAgIGdhcmRlbi5sb2cuaW5mbyhcIlVubGlua2VkIGFsbCBzb3VyY2VzXCIpXG4gICAgICByZXR1cm4geyByZXN1bHQ6IFtdIH1cbiAgICB9XG5cbiAgICBjb25zdCBsaW5rZWRQcm9qZWN0U291cmNlcyA9IGF3YWl0IHJlbW92ZUxpbmtlZFNvdXJjZXMoeyBnYXJkZW4sIHNvdXJjZVR5cGUsIG5hbWVzOiBzb3VyY2UgfSlcblxuICAgIGdhcmRlbi5sb2cuaW5mbyhgVW5saW5rZWQgc291cmNlKHMpICR7c291cmNlfWApXG5cbiAgICByZXR1cm4geyByZXN1bHQ6IGxpbmtlZFByb2plY3RTb3VyY2VzIH1cbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/unlink/unlink.d.ts b/garden-service/build/commands/unlink/unlink.d.ts
new file mode 100644
index 00000000000..08d3d05b22a
--- /dev/null
+++ b/garden-service/build/commands/unlink/unlink.d.ts
@@ -0,0 +1,10 @@
+import { Command } from "../base";
+import { UnlinkSourceCommand } from "./source";
+import { UnlinkModuleCommand } from "./module";
+export declare class UnlinkCommand extends Command {
+ name: string;
+ help: string;
+ subCommands: (typeof UnlinkSourceCommand | typeof UnlinkModuleCommand)[];
+ action(): Promise<{}>;
+}
+//# sourceMappingURL=unlink.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/unlink/unlink.js b/garden-service/build/commands/unlink/unlink.js
new file mode 100644
index 00000000000..600e4156b56
--- /dev/null
+++ b/garden-service/build/commands/unlink/unlink.js
@@ -0,0 +1,37 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const base_1 = require("../base");
+const source_1 = require("./source");
+const module_1 = require("./module");
+class UnlinkCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "unlink";
+ this.help = "Unlink a remote source or module from its local path";
+ this.subCommands = [
+ source_1.UnlinkSourceCommand,
+ module_1.UnlinkModuleCommand,
+ ];
+ }
+ action() {
+ return __awaiter(this, void 0, void 0, function* () { return {}; });
+ }
+}
+exports.UnlinkCommand = UnlinkCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL3VubGluay91bmxpbmsudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7R0FNRzs7Ozs7Ozs7OztBQUVILGtDQUFpQztBQUNqQyxxQ0FBOEM7QUFDOUMscUNBQThDO0FBRTlDLE1BQWEsYUFBYyxTQUFRLGNBQU87SUFBMUM7O1FBQ0UsU0FBSSxHQUFHLFFBQVEsQ0FBQTtRQUNmLFNBQUksR0FBRyxzREFBc0QsQ0FBQTtRQUU3RCxnQkFBVyxHQUFHO1lBQ1osNEJBQW1CO1lBQ25CLDRCQUFtQjtTQUNwQixDQUFBO0lBR0gsQ0FBQztJQURPLE1BQU07OERBQUssT0FBTyxFQUFFLENBQUEsQ0FBQyxDQUFDO0tBQUE7Q0FDN0I7QUFWRCxzQ0FVQyIsImZpbGUiOiJjb21tYW5kcy91bmxpbmsvdW5saW5rLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoQykgMjAxOCBHYXJkZW4gVGVjaG5vbG9naWVzLCBJbmMuIDxpbmZvQGdhcmRlbi5pbz5cbiAqXG4gKiBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljXG4gKiBMaWNlbnNlLCB2LiAyLjAuIElmIGEgY29weSBvZiB0aGUgTVBMIHdhcyBub3QgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzXG4gKiBmaWxlLCBZb3UgY2FuIG9idGFpbiBvbmUgYXQgaHR0cDovL21vemlsbGEub3JnL01QTC8yLjAvLlxuICovXG5cbmltcG9ydCB7IENvbW1hbmQgfSBmcm9tIFwiLi4vYmFzZVwiXG5pbXBvcnQgeyBVbmxpbmtTb3VyY2VDb21tYW5kIH0gZnJvbSBcIi4vc291cmNlXCJcbmltcG9ydCB7IFVubGlua01vZHVsZUNvbW1hbmQgfSBmcm9tIFwiLi9tb2R1bGVcIlxuXG5leHBvcnQgY2xhc3MgVW5saW5rQ29tbWFuZCBleHRlbmRzIENvbW1hbmQge1xuICBuYW1lID0gXCJ1bmxpbmtcIlxuICBoZWxwID0gXCJVbmxpbmsgYSByZW1vdGUgc291cmNlIG9yIG1vZHVsZSBmcm9tIGl0cyBsb2NhbCBwYXRoXCJcblxuICBzdWJDb21tYW5kcyA9IFtcbiAgICBVbmxpbmtTb3VyY2VDb21tYW5kLFxuICAgIFVubGlua01vZHVsZUNvbW1hbmQsXG4gIF1cblxuICBhc3luYyBhY3Rpb24oKSB7IHJldHVybiB7fSB9XG59XG4iXX0=
diff --git a/garden-service/build/commands/update-remote/all.d.ts b/garden-service/build/commands/update-remote/all.d.ts
new file mode 100644
index 00000000000..22c849e8630
--- /dev/null
+++ b/garden-service/build/commands/update-remote/all.d.ts
@@ -0,0 +1,13 @@
+import { Command, CommandResult, CommandParams } from "../base";
+import { SourceConfig } from "../../config/project";
+export interface UpdateRemoteAllResult {
+ projectSources: SourceConfig[];
+ moduleSources: SourceConfig[];
+}
+export declare class UpdateRemoteAllCommand extends Command {
+ name: string;
+ help: string;
+ description: string;
+ action({ garden }: CommandParams): Promise>;
+}
+//# sourceMappingURL=all.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/update-remote/all.js b/garden-service/build/commands/update-remote/all.js
new file mode 100644
index 00000000000..992ec3c069c
--- /dev/null
+++ b/garden-service/build/commands/update-remote/all.js
@@ -0,0 +1,46 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const dedent = require("dedent");
+const base_1 = require("../base");
+const sources_1 = require("./sources");
+const modules_1 = require("./modules");
+class UpdateRemoteAllCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "all";
+ this.help = "Update all remote sources and modules.";
+ this.description = dedent `
+ Examples:
+
+ garden update-remote all # update all remote sources and modules in the project
+ `;
+ }
+ action({ garden }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ garden.log.header({ emoji: "hammer_and_wrench", command: "update-remote all" });
+ const sourcesCmd = new sources_1.UpdateRemoteSourcesCommand();
+ const modulesCmd = new modules_1.UpdateRemoteModulesCommand();
+ const { result: projectSources } = yield sourcesCmd.action({ garden, args: { source: undefined }, opts: {} });
+ const { result: moduleSources } = yield modulesCmd.action({ garden, args: { module: undefined }, opts: {} });
+ return { result: { projectSources: projectSources, moduleSources: moduleSources } };
+ });
+ }
+}
+exports.UpdateRemoteAllCommand = UpdateRemoteAllCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL3VwZGF0ZS1yZW1vdGUvYWxsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7QUFFSCxpQ0FBaUM7QUFFakMsa0NBSWdCO0FBQ2hCLHVDQUFzRDtBQUN0RCx1Q0FBc0Q7QUFRdEQsTUFBYSxzQkFBdUIsU0FBUSxjQUFPO0lBQW5EOztRQUNFLFNBQUksR0FBRyxLQUFLLENBQUE7UUFDWixTQUFJLEdBQUcsd0NBQXdDLENBQUE7UUFFL0MsZ0JBQVcsR0FBRyxNQUFNLENBQUE7Ozs7R0FJbkIsQ0FBQTtJQWNILENBQUM7SUFaTyxNQUFNLENBQUMsRUFBRSxNQUFNLEVBQWlCOztZQUVwQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsQ0FBQyxDQUFBO1lBRS9FLE1BQU0sVUFBVSxHQUFHLElBQUksb0NBQTBCLEVBQUUsQ0FBQTtZQUNuRCxNQUFNLFVBQVUsR0FBRyxJQUFJLG9DQUEwQixFQUFFLENBQUE7WUFFbkQsTUFBTSxFQUFFLE1BQU0sRUFBRSxjQUFjLEVBQUUsR0FBRyxNQUFNLFVBQVUsQ0FBQyxNQUFNLENBQUMsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFBO1lBQzdHLE1BQU0sRUFBRSxNQUFNLEVBQUUsYUFBYSxFQUFFLEdBQUcsTUFBTSxVQUFVLENBQUMsTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQTtZQUU1RyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUUsY0FBYyxFQUFFLGNBQWUsRUFBRSxhQUFhLEVBQUUsYUFBYyxFQUFFLEVBQUUsQ0FBQTtRQUN2RixDQUFDO0tBQUE7Q0FDRjtBQXRCRCx3REFzQkMiLCJmaWxlIjoiY29tbWFuZHMvdXBkYXRlLXJlbW90ZS9hbGwuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChDKSAyMDE4IEdhcmRlbiBUZWNobm9sb2dpZXMsIEluYy4gPGluZm9AZ2FyZGVuLmlvPlxuICpcbiAqIFRoaXMgU291cmNlIENvZGUgRm9ybSBpcyBzdWJqZWN0IHRvIHRoZSB0ZXJtcyBvZiB0aGUgTW96aWxsYSBQdWJsaWNcbiAqIExpY2Vuc2UsIHYuIDIuMC4gSWYgYSBjb3B5IG9mIHRoZSBNUEwgd2FzIG5vdCBkaXN0cmlidXRlZCB3aXRoIHRoaXNcbiAqIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uXG4gKi9cblxuaW1wb3J0IGRlZGVudCA9IHJlcXVpcmUoXCJkZWRlbnRcIilcblxuaW1wb3J0IHtcbiAgQ29tbWFuZCxcbiAgQ29tbWFuZFJlc3VsdCxcbiAgQ29tbWFuZFBhcmFtcyxcbn0gZnJvbSBcIi4uL2Jhc2VcIlxuaW1wb3J0IHsgVXBkYXRlUmVtb3RlU291cmNlc0NvbW1hbmQgfSBmcm9tIFwiLi9zb3VyY2VzXCJcbmltcG9ydCB7IFVwZGF0ZVJlbW90ZU1vZHVsZXNDb21tYW5kIH0gZnJvbSBcIi4vbW9kdWxlc1wiXG5pbXBvcnQgeyBTb3VyY2VDb25maWcgfSBmcm9tIFwiLi4vLi4vY29uZmlnL3Byb2plY3RcIlxuXG5leHBvcnQgaW50ZXJmYWNlIFVwZGF0ZVJlbW90ZUFsbFJlc3VsdCB7XG4gIHByb2plY3RTb3VyY2VzOiBTb3VyY2VDb25maWdbXSxcbiAgbW9kdWxlU291cmNlczogU291cmNlQ29uZmlnW10sXG59XG5cbmV4cG9ydCBjbGFzcyBVcGRhdGVSZW1vdGVBbGxDb21tYW5kIGV4dGVuZHMgQ29tbWFuZCB7XG4gIG5hbWUgPSBcImFsbFwiXG4gIGhlbHAgPSBcIlVwZGF0ZSBhbGwgcmVtb3RlIHNvdXJjZXMgYW5kIG1vZHVsZXMuXCJcblxuICBkZXNjcmlwdGlvbiA9IGRlZGVudGBcbiAgICBFeGFtcGxlczpcblxuICAgICAgICBnYXJkZW4gdXBkYXRlLXJlbW90ZSBhbGwgIyB1cGRhdGUgYWxsIHJlbW90ZSBzb3VyY2VzIGFuZCBtb2R1bGVzIGluIHRoZSBwcm9qZWN0XG4gIGBcblxuICBhc3luYyBhY3Rpb24oeyBnYXJkZW4gfTogQ29tbWFuZFBhcmFtcyk6IFByb21pc2U8Q29tbWFuZFJlc3VsdDxVcGRhdGVSZW1vdGVBbGxSZXN1bHQ+PiB7XG5cbiAgICBnYXJkZW4ubG9nLmhlYWRlcih7IGVtb2ppOiBcImhhbW1lcl9hbmRfd3JlbmNoXCIsIGNvbW1hbmQ6IFwidXBkYXRlLXJlbW90ZSBhbGxcIiB9KVxuXG4gICAgY29uc3Qgc291cmNlc0NtZCA9IG5ldyBVcGRhdGVSZW1vdGVTb3VyY2VzQ29tbWFuZCgpXG4gICAgY29uc3QgbW9kdWxlc0NtZCA9IG5ldyBVcGRhdGVSZW1vdGVNb2R1bGVzQ29tbWFuZCgpXG5cbiAgICBjb25zdCB7IHJlc3VsdDogcHJvamVjdFNvdXJjZXMgfSA9IGF3YWl0IHNvdXJjZXNDbWQuYWN0aW9uKHsgZ2FyZGVuLCBhcmdzOiB7IHNvdXJjZTogdW5kZWZpbmVkIH0sIG9wdHM6IHt9IH0pXG4gICAgY29uc3QgeyByZXN1bHQ6IG1vZHVsZVNvdXJjZXMgfSA9IGF3YWl0IG1vZHVsZXNDbWQuYWN0aW9uKHsgZ2FyZGVuLCBhcmdzOiB7IG1vZHVsZTogdW5kZWZpbmVkIH0sIG9wdHM6IHt9IH0pXG5cbiAgICByZXR1cm4geyByZXN1bHQ6IHsgcHJvamVjdFNvdXJjZXM6IHByb2plY3RTb3VyY2VzISwgbW9kdWxlU291cmNlczogbW9kdWxlU291cmNlcyEgfSB9XG4gIH1cbn1cbiJdfQ==
diff --git a/garden-service/build/commands/update-remote/helpers.d.ts b/garden-service/build/commands/update-remote/helpers.d.ts
new file mode 100644
index 00000000000..5804896d96a
--- /dev/null
+++ b/garden-service/build/commands/update-remote/helpers.d.ts
@@ -0,0 +1,8 @@
+import { ExternalSourceType } from "../../util/ext-source-util";
+import { SourceConfig } from "../../config/project";
+export declare function pruneRemoteSources({ projectRoot, sources, type }: {
+ projectRoot: string;
+ sources: SourceConfig[];
+ type: ExternalSourceType;
+}): Promise;
+//# sourceMappingURL=helpers.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/update-remote/helpers.js b/garden-service/build/commands/update-remote/helpers.js
new file mode 100644
index 00000000000..80709ad8767
--- /dev/null
+++ b/garden-service/build/commands/update-remote/helpers.js
@@ -0,0 +1,41 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const lodash_1 = require("lodash");
+const path_1 = require("path");
+const fs_extra_1 = require("fs-extra");
+const util_1 = require("../../util/util");
+const ext_source_util_1 = require("../../util/ext-source-util");
+function pruneRemoteSources({ projectRoot, sources, type }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const remoteSourcesPath = path_1.join(projectRoot, ext_source_util_1.getRemoteSourcesDirname(type));
+ if (!(yield fs_extra_1.pathExists(remoteSourcesPath))) {
+ return;
+ }
+ const sourceNames = sources
+ .map(({ name, repositoryUrl: url }) => ext_source_util_1.getRemoteSourcePath({ name, url, sourceType: type }))
+ .map(srcPath => path_1.basename(srcPath));
+ const currentRemoteSources = yield util_1.getChildDirNames(remoteSourcesPath);
+ const staleRemoteSources = lodash_1.difference(currentRemoteSources, sourceNames);
+ for (const dirName of staleRemoteSources) {
+ yield fs_extra_1.remove(path_1.join(remoteSourcesPath, dirName));
+ }
+ });
+}
+exports.pruneRemoteSources = pruneRemoteSources;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL3VwZGF0ZS1yZW1vdGUvaGVscGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7OztHQU1HOzs7Ozs7Ozs7O0FBRUgsbUNBQW1DO0FBQ25DLCtCQUFxQztBQUNyQyx1Q0FBNkM7QUFFN0MsMENBQWtEO0FBQ2xELGdFQUltQztBQUduQyxTQUFzQixrQkFBa0IsQ0FBQyxFQUFFLFdBQVcsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUlwRTs7UUFDQyxNQUFNLGlCQUFpQixHQUFHLFdBQUksQ0FBQyxXQUFXLEVBQUUseUNBQXVCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQTtRQUUxRSxJQUFJLENBQUMsQ0FBQyxNQUFNLHFCQUFVLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxFQUFFO1lBQzFDLE9BQU07U0FDUDtRQUVELE1BQU0sV0FBVyxHQUFHLE9BQU87YUFDeEIsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsYUFBYSxFQUFFLEdBQUcsRUFBRSxFQUFFLEVBQUUsQ0FBQyxxQ0FBbUIsQ0FBQyxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7YUFDM0YsR0FBRyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsZUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUE7UUFFcEMsTUFBTSxvQkFBb0IsR0FBRyxNQUFNLHVCQUFnQixDQUFDLGlCQUFpQixDQUFDLENBQUE7UUFDdEUsTUFBTSxrQkFBa0IsR0FBRyxtQkFBVSxDQUFDLG9CQUFvQixFQUFFLFdBQVcsQ0FBQyxDQUFBO1FBRXhFLEtBQUssTUFBTSxPQUFPLElBQUksa0JBQWtCLEVBQUU7WUFDeEMsTUFBTSxpQkFBTSxDQUFDLFdBQUksQ0FBQyxpQkFBaUIsRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFBO1NBQy9DO0lBQ0gsQ0FBQztDQUFBO0FBckJELGdEQXFCQyIsImZpbGUiOiJjb21tYW5kcy91cGRhdGUtcmVtb3RlL2hlbHBlcnMuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChDKSAyMDE4IEdhcmRlbiBUZWNobm9sb2dpZXMsIEluYy4gPGluZm9AZ2FyZGVuLmlvPlxuICpcbiAqIFRoaXMgU291cmNlIENvZGUgRm9ybSBpcyBzdWJqZWN0IHRvIHRoZSB0ZXJtcyBvZiB0aGUgTW96aWxsYSBQdWJsaWNcbiAqIExpY2Vuc2UsIHYuIDIuMC4gSWYgYSBjb3B5IG9mIHRoZSBNUEwgd2FzIG5vdCBkaXN0cmlidXRlZCB3aXRoIHRoaXNcbiAqIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uXG4gKi9cblxuaW1wb3J0IHsgZGlmZmVyZW5jZSB9IGZyb20gXCJsb2Rhc2hcIlxuaW1wb3J0IHsgam9pbiwgYmFzZW5hbWUgfSBmcm9tIFwicGF0aFwiXG5pbXBvcnQgeyByZW1vdmUsIHBhdGhFeGlzdHMgfSBmcm9tIFwiZnMtZXh0cmFcIlxuXG5pbXBvcnQgeyBnZXRDaGlsZERpck5hbWVzIH0gZnJvbSBcIi4uLy4uL3V0aWwvdXRpbFwiXG5pbXBvcnQge1xuICBFeHRlcm5hbFNvdXJjZVR5cGUsXG4gIGdldFJlbW90ZVNvdXJjZXNEaXJuYW1lLFxuICBnZXRSZW1vdGVTb3VyY2VQYXRoLFxufSBmcm9tIFwiLi4vLi4vdXRpbC9leHQtc291cmNlLXV0aWxcIlxuaW1wb3J0IHsgU291cmNlQ29uZmlnIH0gZnJvbSBcIi4uLy4uL2NvbmZpZy9wcm9qZWN0XCJcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBydW5lUmVtb3RlU291cmNlcyh7IHByb2plY3RSb290LCBzb3VyY2VzLCB0eXBlIH06IHtcbiAgcHJvamVjdFJvb3Q6IHN0cmluZyxcbiAgc291cmNlczogU291cmNlQ29uZmlnW10sXG4gIHR5cGU6IEV4dGVybmFsU291cmNlVHlwZSxcbn0pIHtcbiAgY29uc3QgcmVtb3RlU291cmNlc1BhdGggPSBqb2luKHByb2plY3RSb290LCBnZXRSZW1vdGVTb3VyY2VzRGlybmFtZSh0eXBlKSlcblxuICBpZiAoIShhd2FpdCBwYXRoRXhpc3RzKHJlbW90ZVNvdXJjZXNQYXRoKSkpIHtcbiAgICByZXR1cm5cbiAgfVxuXG4gIGNvbnN0IHNvdXJjZU5hbWVzID0gc291cmNlc1xuICAgIC5tYXAoKHsgbmFtZSwgcmVwb3NpdG9yeVVybDogdXJsIH0pID0+IGdldFJlbW90ZVNvdXJjZVBhdGgoeyBuYW1lLCB1cmwsIHNvdXJjZVR5cGU6IHR5cGUgfSkpXG4gICAgLm1hcChzcmNQYXRoID0+IGJhc2VuYW1lKHNyY1BhdGgpKVxuXG4gIGNvbnN0IGN1cnJlbnRSZW1vdGVTb3VyY2VzID0gYXdhaXQgZ2V0Q2hpbGREaXJOYW1lcyhyZW1vdGVTb3VyY2VzUGF0aClcbiAgY29uc3Qgc3RhbGVSZW1vdGVTb3VyY2VzID0gZGlmZmVyZW5jZShjdXJyZW50UmVtb3RlU291cmNlcywgc291cmNlTmFtZXMpXG5cbiAgZm9yIChjb25zdCBkaXJOYW1lIG9mIHN0YWxlUmVtb3RlU291cmNlcykge1xuICAgIGF3YWl0IHJlbW92ZShqb2luKHJlbW90ZVNvdXJjZXNQYXRoLCBkaXJOYW1lKSlcbiAgfVxufVxuIl19
diff --git a/garden-service/build/commands/update-remote/modules.d.ts b/garden-service/build/commands/update-remote/modules.d.ts
new file mode 100644
index 00000000000..17ad7e6e4e1
--- /dev/null
+++ b/garden-service/build/commands/update-remote/modules.d.ts
@@ -0,0 +1,17 @@
+import { Command, StringsParameter, CommandResult, CommandParams } from "../base";
+import { SourceConfig } from "../../config/project";
+declare const updateRemoteModulesArguments: {
+ module: StringsParameter;
+};
+declare type Args = typeof updateRemoteModulesArguments;
+export declare class UpdateRemoteModulesCommand extends Command {
+ name: string;
+ help: string;
+ arguments: {
+ module: StringsParameter;
+ };
+ description: string;
+ action({ garden, args }: CommandParams): Promise>;
+}
+export {};
+//# sourceMappingURL=modules.d.ts.map
\ No newline at end of file
diff --git a/garden-service/build/commands/update-remote/modules.js b/garden-service/build/commands/update-remote/modules.js
new file mode 100644
index 00000000000..53bd6298153
--- /dev/null
+++ b/garden-service/build/commands/update-remote/modules.js
@@ -0,0 +1,75 @@
+"use strict";
+/*
+ * Copyright (C) 2018 Garden Technologies, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const lodash_1 = require("lodash");
+const dedent = require("dedent");
+const chalk_1 = require("chalk");
+const base_1 = require("../base");
+const exceptions_1 = require("../../exceptions");
+const helpers_1 = require("./helpers");
+const ext_source_util_1 = require("../../util/ext-source-util");
+const updateRemoteModulesArguments = {
+ module: new base_1.StringsParameter({
+ help: "Name of the remote module(s) to update. Use comma separator to specify multiple modules.",
+ }),
+};
+class UpdateRemoteModulesCommand extends base_1.Command {
+ constructor() {
+ super(...arguments);
+ this.name = "modules";
+ this.help = "Update remote modules.";
+ this.arguments = updateRemoteModulesArguments;
+ this.description = dedent `
+ Remote modules are modules that have a repositoryUrl field
+ in their garden.yml config that points to a remote repository.
+
+ Examples:
+
+ garden update-remote modules # update all remote modules in the project
+ garden update-remote modules my-module # update remote module my-module
+ `;
+ }
+ action({ garden, args }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ garden.log.header({ emoji: "hammer_and_wrench", command: "update-remote modules" });
+ const { module } = args;
+ const modules = yield garden.getModules(module);
+ const moduleSources = modules
+ .filter(ext_source_util_1.hasRemoteSource)
+ .filter(src => module ? module.includes(src.name) : true);
+ const names = moduleSources.map(src => src.name);
+ const diff = lodash_1.difference(module, names);
+ if (diff.length > 0) {
+ const modulesWithRemoteSource = (yield garden.getModules()).filter(ext_source_util_1.hasRemoteSource).sort();
+ throw new exceptions_1.ParameterError(`Expected module(s) ${chalk_1.default.underline(diff.join(","))} to have a remote source.`, {
+ modulesWithRemoteSource,
+ input: module ? module.sort() : undefined,
+ });
+ }
+ // TODO Update remotes in parallel. Currently not possible since updating might
+ // trigger a username and password prompt from git.
+ for (const { name, repositoryUrl } of moduleSources) {
+ yield garden.vcs.updateRemoteSource({ name, url: repositoryUrl, sourceType: "module", logEntry: garden.log });
+ }
+ yield helpers_1.pruneRemoteSources({ projectRoot: garden.projectRoot, type: "module", sources: moduleSources });
+ return { result: moduleSources };
+ });
+ }
+}
+exports.UpdateRemoteModulesCommand = UpdateRemoteModulesCommand;
+
+//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1hbmRzL3VwZGF0ZS1yZW1vdGUvbW9kdWxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7OztHQU1HOzs7Ozs7Ozs7O0FBRUgsbUNBQW1DO0FBQ25DLGlDQUFpQztBQUNqQyxpQ0FBeUI7QUFFekIsa0NBS2dCO0FBRWhCLGlEQUFpRDtBQUNqRCx1Q0FBOEM7QUFDOUMsZ0VBQTREO0FBRTVELE1BQU0sNEJBQTRCLEdBQUc7SUFDbkMsTUFBTSxFQUFFLElBQUksdUJBQWdCLENBQUM7UUFDM0IsSUFBSSxFQUFFLDBGQUEwRjtLQUNqRyxDQUFDO0NBQ0gsQ0FBQTtBQUlELE1BQWEsMEJBQTJCLFNBQVEsY0FBYTtJQUE3RDs7UUFDRSxTQUFJLEdBQUcsU0FBUyxDQUFBO1FBQ2hCLFNBQUksR0FBRyx3QkFBd0IsQ0FBQTtRQUMvQixjQUFTLEdBQUcsNEJBQTRCLENBQUE7UUFFeEMsZ0JBQVcsR0FBRyxNQUFNLENBQUE7Ozs7Ozs7O0dBUW5CLENBQUE7SUF1Q0gsQ0FBQztJQXJDTyxNQUFNLENBQ1YsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUF1Qjs7WUFFckMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsRUFBRSxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsT0FBTyxFQUFFLHVCQUF1QixFQUFFLENBQUMsQ0FBQTtZQUVuRixNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFBO1lBQ3ZCLE1BQU0sT0FBTyxHQUFHLE1BQU0sTUFBTSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQTtZQUUvQyxNQUFNLGFBQWEsR0FBbUIsT0FBTztpQkFDMUMsTUFBTSxDQUFDLGlDQUFlLENBQUM7aUJBQ3ZCLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFBO1lBRTNELE1BQU0sS0FBSyxHQUFHLGFBQWEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUE7WUFFaEQsTUFBTSxJQUFJLEdBQUcsbUJBQVUsQ0FBQyxNQUFNLEVBQUUsS0FBSyxDQUFDLENBQUE7WUFDdEMsSUFBSSxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtnQkFDbkIsTUFBTSx1QkFBdUIsR0FBRyxDQUFDLE1BQU0sTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLGlDQUFlLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQTtnQkFFMUYsTUFBTSxJQUFJLDJCQUFjLENBQ3RCLHNCQUFzQixlQUFLLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsMkJBQTJCLEVBQ2hGO29CQUNFLHVCQUF1QjtvQkFDdkIsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTO2lCQUMxQyxDQUNGLENBQUE7YUFDRjtZQUVELCtFQUErRTtZQUMvRSxtREFBbUQ7WUFDbkQsS0FBSyxNQUFNLEVBQUUsSUFBSSxFQUFFLGFBQWEsRUFBRSxJQUFJLGFBQWEsRUFBRTtnQkFDbkQsTUFBTSxNQUFNLENBQUMsR0FBRyxDQUFDLGtCQUFrQixDQUFDLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxhQUFhLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUE7YUFDOUc7WUFFRCxNQUFNLDRCQUFrQixDQUFDLEVBQUUsV0FBVyxFQUFFLE1BQU0sQ0FBQyxXQUFXLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQTtZQUVyRyxPQUFPLEVBQUUsTUFBTSxFQUFFLGFBQWEsRUFBRSxDQUFBO1FBQ2xDLENBQUM7S0FBQTtDQUNGO0FBcERELGdFQW9EQyIsImZpbGUiOiJjb21tYW5kcy91cGRhdGUtcmVtb3RlL21vZHVsZXMuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChDKSAyMDE4IEdhcmRlbiBUZWNobm9sb2dpZXMsIEluYy4gPGluZm9AZ2FyZGVuLmlvPlxuICpcbiAqIFRoaXMgU291cmNlIENvZGUgRm9ybSBpcyBzdWJqZWN0IHRvIHRoZSB0ZXJtcyBvZiB0aGUgTW96aWxsYSBQdWJsaWNcbiAqIExpY2Vuc2UsIHYuIDIuMC4gSWYgYSBjb3B5IG9mIHRoZSBNUEwgd2FzIG5vdCBkaXN0cmlidXRlZCB3aXRoIHRoaXNcbiAqIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uXG4gKi9cblxuaW1wb3J0IHsgZGlmZmVyZW5jZSB9IGZyb20gXCJsb2Rhc2hcIlxuaW1wb3J0IGRlZGVudCA9IHJlcXVpcmUoXCJkZWRlbnRcIilcbmltcG9ydCBjaGFsayBmcm9tIFwiY2hhbGtcIlxuXG5pbXBvcnQge1xuICBDb21tYW5kLFxuICBTdHJpbmdzUGFyYW1ldGVyLFxuICBDb21tYW5kUmVzdWx0LFxuICBDb21tYW5kUGFyYW1zLFxufSBmcm9tIFwiLi4vYmFzZVwiXG5pbXBvcnQgeyBTb3VyY2VDb25maWcgfSBmcm9tIFwiLi4vLi4vY29uZmlnL3Byb2plY3RcIlxuaW1wb3J0IHsgUGFyYW1ldGVyRXJyb3IgfSBmcm9tIFwiLi4vLi4vZXhjZXB0aW9uc1wiXG5pbXBvcnQgeyBwcnVuZVJlbW90ZVNvdXJjZXMgfSBmcm9tIFwiLi9oZWxwZXJzXCJcbmltcG9ydCB7IGhhc1JlbW90ZVNvdXJjZSB9IGZyb20gXCIuLi8uLi91dGlsL2V4dC1zb3VyY2UtdXRpbFwiXG5cbmNvbnN0IHVwZGF0ZVJlbW90ZU1vZHVsZXNBcmd1bWVudHMgPSB7XG4gIG1vZHVsZTogbmV3IFN0cmluZ3NQYXJhbWV0ZXIoe1xuICAgIGhlbHA6IFwiTmFtZSBvZiB0aGUgcmVtb3RlIG1vZHVsZShzKSB0byB1cGRhdGUuIFVzZSBjb21tYSBzZXBhcmF0b3IgdG8gc3BlY2lmeSBtdWx0aXBsZSBtb2R1bGVzLlwiLFxuICB9KSxcbn1cblxudHlwZSBBcmdzID0gdHlwZW9mIHVwZGF0ZVJlbW90ZU1vZHVsZXNBcmd1bWVudHNcblxuZXhwb3J0IGNsYXNzIFVwZGF0ZVJlbW90ZU1vZHVsZXNDb21tYW5kIGV4dGVuZHMgQ29tbWFuZDxBcmdzPiB7XG4gIG5hbWUgPSBcIm1vZHVsZXNcIlxuICBoZWxwID0gXCJVcGRhdGUgcmVtb3RlIG1vZHVsZXMuXCJcbiAgYXJndW1lbnRzID0gdXBkYXRlUmVtb3RlTW9kdWxlc0FyZ3VtZW50c1xuXG4gIGRlc2NyaXB0aW9uID0gZGVkZW50YFxuICAgIFJlbW90ZSBtb2R1bGVzIGFyZSBtb2R1bGVzIHRoYXQgaGF2ZSBhIHJlcG9zaXRvcnlVcmwgZmllbGRcbiAgICBpbiB0aGVpciBnYXJkZW4ueW1sIGNvbmZpZyB0aGF0IHBvaW50cyB0byBhIHJlbW90ZSByZXBvc2l0b3J5LlxuXG4gICAgRXhhbXBsZXM6XG5cbiAgICAgICAgZ2FyZGVuIHVwZGF0ZS1yZW1vdGUgbW9kdWxlcyAgICAgICAgICAgICMgdXBkYXRlIGFsbCByZW1vdGUgbW9kdWxlcyBpbiB0aGUgcHJvamVjdFxuICAgICAgICBnYXJkZW4gdXBkYXRlLXJlbW90ZSBtb2R1bGVzIG15LW1vZHVsZSAgIyB1cGRhdGUgcmVtb3RlIG1vZHVsZSBteS1tb2R1bGVcbiAgYFxuXG4gIGFzeW5jIGFjdGlvbihcbiAgICB7IGdhcmRlbiwgYXJncyB9OiBDb21tYW5kUGFyYW1zPEFyZ3M+LFxuICApOiBQcm9taXNlPENvbW1hbmRSZXN1bHQ8U291cmNlQ29uZmlnW10+PiB7XG4gICAgZ2FyZGVuLmxvZy5oZWFkZXIoeyBlbW9qaTogXCJoYW1tZXJfYW5kX3dyZW5jaFwiLCBjb21tYW5kOiBcInVwZGF0ZS1yZW1vdGUgbW9kdWxlc1wiIH0pXG5cbiAgICBjb25zdCB7IG1vZHVsZSB9ID0gYXJnc1xuICAgIGNvbnN0IG1vZHVsZXMgPSBhd2FpdCBnYXJkZW4uZ2V0TW9kdWxlcyhtb2R1bGUpXG5cbiAgICBjb25zdCBtb2R1bGVTb3VyY2VzID0gPFNvdXJjZUNvbmZpZ1tdPm1vZHVsZXNcbiAgICAgIC5maWx0ZXIoaGFzUmVtb3RlU291cmNlKVxuICAgICAgLmZpbHRlcihzcmMgPT4gbW9kdWxlID8gbW9kdWxlLmluY2x1ZGVzKHNyYy5uYW1lKSA6IHRydWUpXG5cbiAgICBjb25zdCBuYW1lcyA9IG1vZHVsZVNvdXJjZXMubWFwKHNyYyA9PiBzcmMubmFtZSlcblxuICAgIGNvbnN0IGRpZmYgPSBkaWZmZXJlbmNlKG1vZHVsZSwgbmFtZXMpXG4gICAgaWYgKGRpZmYubGVuZ3RoID4gMCkge1xuICAgICAgY29uc3QgbW9kdWxlc1dpdGhSZW1vdGVTb3VyY2UgPSAoYXdhaXQgZ2FyZGVuLmdldE1vZHVsZXMoKSkuZmlsdGVyKGhhc1JlbW90ZVNvdXJjZSkuc29ydCgpXG5cbiAgICAgIHRocm93IG5ldyBQYXJhbWV0ZXJFcnJvcihcbiAgICAgICAgYEV4cGVjdGVkIG1vZHVsZShzKSAke2NoYWxrLnVuZGVybGluZShkaWZmLmpvaW4oXCIsXCIpKX0gdG8gaGF2ZSBhIHJlbW90ZSBzb3VyY2UuYCxcbiAgICAgICAge1xuICAgICAgICAgIG1vZHVsZXNXaXRoUmVtb3RlU291cmNlLFxuICAgICAgICAgIGlucHV0OiBtb2R1bGUgPyBtb2R1bGUuc29ydCgpIDogdW5kZWZpbmVkLFxuICAgICAgICB9LFxuICAgICAgKVxuICAgIH1cblxuICAgIC8vIFRPRE8gVXBkYXRlIHJlbW90ZXMgaW4gcGFyYWxsZWwuIEN1cnJlbnRseSBub3QgcG9zc2libGUgc2luY2UgdXBkYXRpbmcgbWlnaHRcbiAgICAvLyB0cmlnZ2VyIGEgdXNlcm5hbWUgYW5kIHBhc3N3b3JkIHByb21wdCBmcm9tIGdpdC5cbiAgICBmb3IgKGNvbnN0IHsgbmFtZSwgcmVwb3NpdG9yeVVybCB9IG9mIG1vZHVsZVNvdXJjZXMpIHtcbiAgICAgIGF3YWl0IGdhcmRlbi52Y3MudXBkYXRlUmVtb3RlU291cmNlKHsgbmFtZSwgdXJsOiByZXBvc2l0b3J5VXJsLCBzb3VyY2VUeXBlOiBcIm1vZHVsZVwiLCBsb2dFbnRyeTogZ2FyZGVuLmxvZyB9KVxuICAgIH1cblxuICAgIGF3YWl0IHBydW5lUmVtb3RlU291cmNlcyh7IHByb2plY3RSb290OiBnYXJkZW4ucHJvamVjdFJvb3QsIHR5cGU6IFwibW9kdWxlXCIsIHNvdXJjZXM6IG1vZHVsZVNvdXJjZXMgfSlcblxuICAgIHJldHVybiB7IHJlc3VsdDogbW9kdWxlU291cmNlcyB9XG4gIH1cbn1cbiJdfQ==
diff --git a/garden-service/build/commands/update-remote/sources.d.ts b/garden-service/build/commands/update-remote/sources.d.ts
new file mode 100644
index 00000000000..01f37f74312
--- /dev/null
+++ b/garden-service/build/commands/update-remote/sources.d.ts
@@ -0,0 +1,17 @@
+import { Command, StringsParameter, CommandResult, CommandParams } from "../base";
+import { SourceConfig } from "../../config/project";
+declare const updateRemoteSourcesArguments: {
+ source: StringsParameter;
+};
+declare type Args = typeof updateRemoteSourcesArguments;
+export declare class UpdateRemoteSourcesCommand extends Command