diff --git a/docs/reference/commands.md b/docs/reference/commands.md index 653db9e6ab..cdf1fb1721 100644 --- a/docs/reference/commands.md +++ b/docs/reference/commands.md @@ -210,11 +210,12 @@ Deploy service(s) to your environment. 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 + 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 --hot-reload=my-service # deploys all services, with hot reloading enabled for my-service + garden deploy --env stage # deploy your services to an environment called stage ##### Usage @@ -225,7 +226,7 @@ Deploy service(s) to your environment. | 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. + | `service` | No | The name(s) of the service(s) to deploy (skip to deploy all services). Use comma as separator to specify multiple services. ##### Options @@ -234,6 +235,7 @@ Deploy service(s) to your environment. | `--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. + | `--hot-reload` | | array:string | The name(s) of the service(s) to deploy with hot reloading enabled. Use comma as separator to specify multiple services. When this option is used, the command is run in watch mode (i.e. implicitly assumes the --watch/-w flag). ### garden dev @@ -247,11 +249,18 @@ Starts the garden development console. Examples: garden dev + garden dev --hot-reload=foo-service,bar-service # enable hot reloading for foo-service and bar-service ##### Usage - garden dev + garden dev [options] + +##### Options + +| Argument | Alias | Type | Description | +| -------- | ----- | ---- | ----------- | + | `--hot-reload` | | array:string | The name(s) of the service(s) to deploy with hot reloading enabled. Use comma as separator to specify multiple services. ### garden exec diff --git a/docs/reference/config.md b/docs/reference/config.md index 0b2636fa51..e91211c758 100644 --- a/docs/reference/config.md +++ b/docs/reference/config.md @@ -176,7 +176,7 @@ project: {} # 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 + # start with a letter,and cannot end with a dash) and additionally cannot contain # consecutive dashes or be longer than 63 characters. # # Required. @@ -452,7 +452,7 @@ module: # # 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 + # start with a letter,and cannot end with a dash) and additionally cannot contain # consecutive dashes or be longer than 63 characters. # # Required. @@ -463,7 +463,7 @@ module: # 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 + # must start with a letter,and cannot end with a dash) and additionally cannot contain # consecutive dashes or be longer than 63 characters. # # Optional. diff --git a/examples/hello-world/.garden/local-config.yml b/examples/hello-world/.garden/local-config.yml index 6372a8fbd8..bddd183713 100644 --- a/examples/hello-world/.garden/local-config.yml +++ b/examples/hello-world/.garden/local-config.yml @@ -1,4 +1,5 @@ kubernetes: - username: hello previous-usernames: - hello +linkedModuleSources: [] +linkedProjectSources: [] diff --git a/examples/hot-reload/garden.yml b/examples/hot-reload/garden.yml index 36a0a3ae04..ae0c441560 100644 --- a/examples/hot-reload/garden.yml +++ b/examples/hot-reload/garden.yml @@ -3,7 +3,6 @@ project: environmentDefaults: providers: - name: container - - name: npm-package environments: - name: local providers: diff --git a/examples/hot-reload/build-dependant/.dockerignore b/examples/hot-reload/node-service/.dockerignore similarity index 100% rename from examples/hot-reload/build-dependant/.dockerignore rename to examples/hot-reload/node-service/.dockerignore diff --git a/examples/hot-reload/build-dependant/Dockerfile b/examples/hot-reload/node-service/Dockerfile similarity index 100% rename from examples/hot-reload/build-dependant/Dockerfile rename to examples/hot-reload/node-service/Dockerfile diff --git a/examples/hot-reload/node-service/app.js b/examples/hot-reload/node-service/app.js new file mode 100644 index 0000000000..2d958edd04 --- /dev/null +++ b/examples/hot-reload/node-service/app.js @@ -0,0 +1,14 @@ +const express = require("express") + +const app = express() + +app.get("/hello", (req, res) => { + res.json({message: "Hello from Node!"}) +}) + +// This is the path GAE uses for health checks +app.get("/_ah/health", (req, res) => { + res.sendStatus(200) +}) + +module.exports = { app } diff --git a/examples/hot-reload/node-service/garden.yml b/examples/hot-reload/node-service/garden.yml new file mode 100644 index 0000000000..e3678f70ea --- /dev/null +++ b/examples/hot-reload/node-service/garden.yml @@ -0,0 +1,21 @@ +module: + description: Node greeting service + name: node-service + type: container + hotReload: + sync: + - target: /app/ + services: + - name: node-service + command: [npm, start] + hotReloadCommand: [npm, run, dev] + ports: + - name: http + containerPort: 8080 + ingresses: + - path: /hello + port: http + healthCheck: + httpGet: + path: /_ah/health + port: http \ No newline at end of file diff --git a/examples/hot-reload/build-dependant/main.js b/examples/hot-reload/node-service/main.js similarity index 100% rename from examples/hot-reload/build-dependant/main.js rename to examples/hot-reload/node-service/main.js diff --git a/examples/hot-reload/build-dependant/package-lock.json b/examples/hot-reload/node-service/package-lock.json similarity index 100% rename from examples/hot-reload/build-dependant/package-lock.json rename to examples/hot-reload/node-service/package-lock.json diff --git a/examples/hot-reload/good-morning/package.json b/examples/hot-reload/node-service/package.json similarity index 100% rename from examples/hot-reload/good-morning/package.json rename to examples/hot-reload/node-service/package.json diff --git a/garden-service/.gitignore b/garden-service/.gitignore index 8a2769eb3c..7e9ff56e97 100644 --- a/garden-service/.gitignore +++ b/garden-service/.gitignore @@ -8,5 +8,6 @@ static/**/.garden-version support/**/*.js support/**/*.map support/**/*.d.ts -test/**/*.js -test/**/*.map +test/*.js +test/src/**/*.js +test/integ/**/*.js diff --git a/garden-service/package-lock.json b/garden-service/package-lock.json index 2cf8aa6848..decbcd4af8 100644 --- a/garden-service/package-lock.json +++ b/garden-service/package-lock.json @@ -2584,6 +2584,11 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, + "deline": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/deline/-/deline-1.0.4.tgz", + "integrity": "sha1-bAXIeDaSbhocY+R4gvPS6yxvFMk=" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", diff --git a/garden-service/src/actions.ts b/garden-service/src/actions.ts index 16886e9664..d1d37951a2 100644 --- a/garden-service/src/actions.ts +++ b/garden-service/src/actions.ts @@ -67,7 +67,7 @@ import { mapValues, values, keyBy, omit } from "lodash" import { Omit } from "./util/util" import { RuntimeContext } from "./types/service" import { processServices, ProcessResults } from "./process" -import { getDeployTasks } from "./tasks/deploy" +import { getDeployTasks } from "./tasks/helpers" import { LogEntry } from "./logger/log-entry" import { createPluginContext } from "./plugin-context" import { CleanupEnvironmentParams } from "./types/plugin/params" @@ -229,7 +229,9 @@ export class ActionHelper implements TypeGuard { async hotReload(params: ModuleActionHelperParams>) : Promise { - return this.callModuleHandler(({ params, actionType: "hotReload" })) + return this.garden.hotReload(params.module.name, async () => { + return this.callModuleHandler(({ params, actionType: "hotReload" })) + }) } async testModule(params: ModuleActionHelperParams>): Promise { @@ -333,9 +335,9 @@ export class ActionHelper implements TypeGuard { garden: this.garden, module, serviceNames, + hotReloadServiceNames: [], force, forceBuild, - includeDependants: false, }), }) } diff --git a/garden-service/src/commands/build.ts b/garden-service/src/commands/build.ts index ac445a987f..2fa53e9789 100644 --- a/garden-service/src/commands/build.ts +++ b/garden-service/src/commands/build.ts @@ -20,7 +20,7 @@ import dedent = require("dedent") import { processModules } from "../process" import { computeAutoReloadDependants, withDependants } from "../watch" import { Module } from "../types/module" -import { hotReloadAndLog } from "./deploy" +import { hotReloadAndLog } from "./helpers" const buildArguments = { module: new StringsParameter({ @@ -73,8 +73,9 @@ export class BuildCommand extends Command { watch: opts.watch, handler: async (module) => [new BuildTask({ garden, module, force: opts.force })], changeHandler: async (module: Module) => { + if (module.spec.hotReload) { - hotReloadAndLog(module, garden) + await hotReloadAndLog(garden, module) } return (await withDependants(garden, [module], autoReloadDependants)) diff --git a/garden-service/src/commands/deploy.ts b/garden-service/src/commands/deploy.ts index 688af1800f..68a065bbfc 100644 --- a/garden-service/src/commands/deploy.ts +++ b/garden-service/src/commands/deploy.ts @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import chalk from "chalk" +import deline = require("deline") import { BooleanParameter, Command, @@ -15,20 +15,16 @@ import { handleTaskResults, StringsParameter, } from "./base" -import { Garden } from "../garden" -import { Module } from "../types/module" -import { getDeployTasks } from "../tasks/deploy" +import { hotReloadAndLog, validateHotReloadOpt } from "./helpers" +import { getDeployTasks, getTasksForHotReload, getHotReloadModuleNames } from "../tasks/helpers" import { TaskResults } from "../task-graph" import { processServices } from "../process" import { getNames } from "../util/util" -import { prepareRuntimeContext } from "../types/service" -import { uniq } from "lodash" -import { flatten } from "underscore" const deployArgs = { service: new StringsParameter({ - help: "The name of the service(s) to deploy (skip to deploy all services). " + - "Use comma as separator to specify multiple services.", + help: deline`The name(s) of the service(s) to deploy (skip to deploy all services). + Use comma as separator to specify multiple services.`, }), } @@ -36,6 +32,12 @@ const deployOpts = { force: new BooleanParameter({ help: "Force redeploy of service(s)." }), "force-build": new BooleanParameter({ help: "Force rebuild of module(s)." }), watch: new BooleanParameter({ help: "Watch for changes in module(s) and auto-deploy.", alias: "w" }), + "hot-reload": new StringsParameter({ + help: deline`The name(s) of the service(s) to deploy with hot reloading enabled. + Use comma as separator to specify multiple services. When this option is used, + the command is run in watch mode (i.e. implicitly assumes the --watch/-w flag). + `, + }), } type Args = typeof deployArgs @@ -54,11 +56,12 @@ export class DeployCommand extends Command { 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 + 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 --hot-reload=my-service # deploys all services, with hot reloading enabled for my-service + garden deploy --env stage # deploy your services to an environment called stage ` arguments = deployArgs @@ -69,10 +72,23 @@ export class DeployCommand extends Command { const serviceNames = getNames(services) if (services.length === 0) { - garden.log.warn({ msg: "No services found. Aborting." }) + garden.log.error({ msg: "No services found. Aborting." }) return { result: {} } } + let watch + const hotReloadServiceNames = opts["hot-reload"] || [] + const hotReloadModuleNames = await getHotReloadModuleNames(garden, hotReloadServiceNames) + + if (opts["hot-reload"]) { + if (!validateHotReloadOpt(garden, hotReloadServiceNames)) { + return { result: {} } + } + watch = true + } else { + watch = opts.watch + } + garden.log.header({ emoji: "rocket", command: "Deploy" }) // TODO: make this a task @@ -81,64 +97,28 @@ export class DeployCommand extends Command { const results = await processServices({ garden, services, - watch: opts.watch, + watch, handler: async (module) => getDeployTasks({ garden, module, serviceNames, + watch, + hotReloadServiceNames, force: opts.force, forceBuild: opts["force-build"], - watch: opts.watch, - includeDependants: false, }), changeHandler: async (module) => { - - const hotReload = !!module.spec.hotReload - - if (hotReload) { - hotReloadAndLog(module, garden) + if (hotReloadModuleNames.has(module.name)) { + await hotReloadAndLog(garden, module) + return getTasksForHotReload({ garden, module, hotReloadServiceNames, serviceNames }) + } else { + return getDeployTasks({ + garden, module, serviceNames, hotReloadServiceNames, force: true, forceBuild: true, watch: true, + }) } - - return getDeployTasks({ - garden, - module, - serviceNames, - force: true, - forceBuild: true, - watch: true, - includeDependants: true, - skipDeployTaskForModule: hotReload, - }) - }, }) return handleTaskResults(garden, "deploy", results) } } - -export async function hotReloadAndLog(module: Module, garden: Garden) { - - const logEntry = garden.log.info({ - section: module.name, - msg: "Hot reloading", - status: "active", - }) - - const serviceDependencyNames = uniq(flatten(module.services.map(s => s.config.dependencies))) - const runtimeContext = await prepareRuntimeContext(garden, module, await garden.getServices(serviceDependencyNames)) - - try { - await garden.actions.hotReload({ module, runtimeContext }) - } catch (err) { - logEntry.setError() - throw err - } - - const msec = logEntry.getDuration(5) * 1000 - logEntry.setSuccess({ - msg: chalk.green(`Done (took ${msec} ms)`), - append: true, - }) - -} diff --git a/garden-service/src/commands/dev.ts b/garden-service/src/commands/dev.ts index 8443cbb6eb..43b5b140dc 100644 --- a/garden-service/src/commands/dev.ts +++ b/garden-service/src/commands/dev.ts @@ -7,31 +7,46 @@ */ import * as Bluebird from "bluebird" +import deline = require("deline") import chalk from "chalk" +import { readFile } from "fs-extra" import { flatten } from "lodash" import moment = require("moment") import { join } from "path" import { BuildTask } from "../tasks/build" import { Task } from "../tasks/base" -import { hotReloadAndLog } from "./deploy" +import { hotReloadAndLog, validateHotReloadOpt } from "./helpers" +import { getDeployTasks, getTasksForHotReload, getHotReloadModuleNames } from "../tasks/helpers" import { Command, CommandResult, CommandParams, + StringsParameter, } from "./base" import { STATIC_DIR } from "../constants" import { processModules } from "../process" -import { readFile } from "fs-extra" import { Module } from "../types/module" import { computeAutoReloadDependants, withDependants } from "../watch" -import { getDeployTasks } from "../tasks/deploy" import { getTestTasks } from "../tasks/test" +import { getNames } from "../util/util" const ansiBannerPath = join(STATIC_DIR, "garden-banner-2.txt") +const devArgs = {} + +const devOpts = { + "hot-reload": new StringsParameter({ + help: deline`The name(s) of the service(s) to deploy with hot reloading enabled. + Use comma as separator to specify multiple services. + `}), +} + +type Args = typeof devArgs +type Opts = typeof devOpts + // TODO: allow limiting to certain modules and/or services -export class DevCommand extends Command { +export class DevCommand extends Command { name = "dev" help = "Starts the garden development console." @@ -43,9 +58,12 @@ export class DevCommand extends Command { Examples: garden dev + garden dev --hot-reload=foo-service,bar-service # enable hot reloading for foo-service and bar-service ` - async action({ garden }: CommandParams): Promise { + options = devOpts + + async action({ garden, opts }: CommandParams): Promise { // print ANSI banner image const data = await readFile(ansiBannerPath) console.log(data.toString()) @@ -58,20 +76,28 @@ export class DevCommand extends Command { const modules = await garden.getModules() if (modules.length === 0) { - if (modules.length === 0) { - garden.log.info({ msg: "No modules found in project." }) - } + garden.log.info({ msg: "No modules found in project." }) garden.log.info({ msg: "Aborting..." }) return {} } + const hotReloadServiceNames = opts["hot-reload"] || [] + const hotReloadModuleNames = await getHotReloadModuleNames(garden, hotReloadServiceNames) + + if (opts["hot-reload"] && !validateHotReloadOpt(garden, hotReloadServiceNames)) { + return {} + } + + const services = await garden.getServices() + const serviceNames = getNames(services) + const tasksForModule = (watch: boolean) => { return async (module: Module) => { - const hotReload = !!module.spec.hotReload + const hotReload = hotReloadModuleNames.has(module.name) if (watch && hotReload) { - hotReloadAndLog(module, garden) + await hotReloadAndLog(garden, module) } const testModules: Module[] = watch @@ -81,16 +107,21 @@ export class DevCommand extends Command { const testTasks: Task[] = flatten(await Bluebird.map( testModules, m => getTestTasks({ garden, module: m }))) - const deployTasks = await getDeployTasks({ - garden, - module, - watch, - force: watch, - forceBuild: watch, - includeDependants: watch, - skipDeployTaskForModule: watch && hotReload, - }) - const tasks = testTasks.concat(deployTasks) + let tasks + if (watch && hotReload) { + tasks = testTasks.concat(await getTasksForHotReload({ garden, module, hotReloadServiceNames, serviceNames })) + } else { + tasks = testTasks.concat(await getDeployTasks({ + garden, + module, + watch, + serviceNames, + hotReloadServiceNames, + force: watch, + forceBuild: watch, + includeDependants: watch, + })) + } if (tasks.length === 0) { return [new BuildTask({ garden, module, force: watch })] diff --git a/garden-service/src/commands/helpers.ts b/garden-service/src/commands/helpers.ts new file mode 100644 index 0000000000..2ca3b8e7e7 --- /dev/null +++ b/garden-service/src/commands/helpers.ts @@ -0,0 +1,72 @@ +/* + * 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/. + */ + +import chalk from "chalk" +import dedent = require("dedent") +import { uniq, flatten } from "lodash" +import { Garden } from "../garden" +import { Module } from "../types/module" +import { prepareRuntimeContext, Service } from "../types/service" + +// Returns true if validation succeeded, false otherwise. +export async function validateHotReloadOpt(garden: Garden, hotReloadServiceNames: string[]): Promise { + const incompatibleServices: Service[] = [] + + for (const hotReloadService of await garden.getServices(hotReloadServiceNames)) { + if (!hotReloadService.module.spec.hotReload) { + incompatibleServices.push(hotReloadService) + } + } + + if (incompatibleServices.length === 0) { + return true + } else { + const singular = incompatibleServices.length === 1 + const incompatibleServicesDescription = incompatibleServices + .map(s => `${s.name} (from module ${s.module.name})`) + .join("\n") + const errMsg = dedent` + Error: Hot reloading was requested for the following ${singular ? "service" : "services"}, \ + but ${singular ? "its parent module is" : "their parent modules are"} not configured \ + for hot reloading: + + ${incompatibleServicesDescription} + + Aborting. + ` + garden.log.error({ msg: errMsg }) + return false + } + +} + +export async function hotReloadAndLog(garden: Garden, module: Module) { + + const logEntry = garden.log.info({ + section: module.name, + msg: "Hot reloading", + status: "active", + }) + + const serviceDependencyNames = uniq(flatten(module.services.map(s => s.config.dependencies))) + const runtimeContext = await prepareRuntimeContext(garden, module, await garden.getServices(serviceDependencyNames)) + + try { + await garden.actions.hotReload({ module, runtimeContext }) + } catch (err) { + logEntry.setError() + throw err + } + + const msec = logEntry.getDuration(5) * 1000 + logEntry.setSuccess({ + msg: chalk.green(`Done (took ${msec} ms)`), + append: true, + }) + +} diff --git a/garden-service/src/config/common.ts b/garden-service/src/config/common.ts index d08c1a386c..6f98d71da9 100644 --- a/garden-service/src/config/common.ts +++ b/garden-service/src/config/common.ts @@ -38,7 +38,6 @@ export const joiIdentifier = () => Joi.string() .max(63) .description( "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.", ) diff --git a/garden-service/src/hotReloadScheduler.ts b/garden-service/src/hotReloadScheduler.ts index 6272402441..52693c0b68 100644 --- a/garden-service/src/hotReloadScheduler.ts +++ b/garden-service/src/hotReloadScheduler.ts @@ -29,14 +29,20 @@ export class HotReloadScheduler { const pendingRequest = this.pending[moduleName] - const prom = new Promise((resolver) => { + const prom = new Promise((resolve, reject) => { if (pendingRequest) { - pendingRequest.addResolver(resolver) + pendingRequest.addPromiseCallbacks(resolve, reject) } else { - this.pending[moduleName] = new HotReloadRequest(hotReloadHandler) + this.pending[moduleName] = new HotReloadRequest(hotReloadHandler, resolve, reject) } }) + /** + * We disable the no-floating-promises tslint rule when calling this.process, since we are, in fact, + * properly handling these promises, though the control flow here is somewhat unusual (by design). + */ + + // tslint:disable-next-line:no-floating-promises this.process(moduleName) return prom @@ -53,30 +59,43 @@ export class HotReloadScheduler { delete this.pending[moduleName] await request.process() + // tslint:disable-next-line:no-floating-promises this.process(moduleName) } } +type PromiseCallback = (any) => any + class HotReloadRequest { private handler: HotReloadHandler - private resolvers: ((any) => any)[] + private promiseCallbacks: { + resolve: PromiseCallback + reject: PromiseCallback, + }[] - constructor(handler: HotReloadHandler) { + constructor(handler: HotReloadHandler, resolve: PromiseCallback, reject: PromiseCallback) { this.handler = handler - this.resolvers = [] + this.promiseCallbacks = [{ resolve, reject }] } - addResolver(resolver: () => any) { - this.resolvers.push(resolver) + addPromiseCallbacks(resolve: () => any, reject: () => any) { + this.promiseCallbacks.push({ resolve, reject }) } async process() { - const result = await this.handler() - for (const resolver of this.resolvers) { - resolver(result) + try { + const result = await this.handler() + for (const { resolve } of this.promiseCallbacks) { + resolve(result) + } + } catch (error) { + for (const { reject } of this.promiseCallbacks) { + reject(error) + } + } finally { + this.promiseCallbacks = [] } - this.resolvers = [] } } diff --git a/garden-service/src/plugins/container.ts b/garden-service/src/plugins/container.ts index 28a08ee199..c83378de8b 100644 --- a/garden-service/src/plugins/container.ts +++ b/garden-service/src/plugins/container.ts @@ -240,7 +240,7 @@ export const containerRegistryConfigSchema = Joi.object() .example("my-project"), }) .required() - .description(dedent` + .description(deline` The registry where built containers should be pushed to, and then pulled to the cluster when deploying services. `) @@ -495,20 +495,22 @@ export async function validateContainerModule({ moduleConfig }: ValidateModulePa // Verify that sync targets are mutually disjoint - i.e. that no target is a subdirectory of // another target. const targets = hotReloadConfig.sync.map(syncSpec => syncSpec.target) - const invalidTargetPairs: string[] = [] + const invalidTargetDescriptions: string[] = [] for (const t of targets) { for (const t2 of targets) { if (t2.startsWith(t) && t !== t2) { - invalidTargetPairs.push(`${t} is a subdirectory of ${t2}.`) + invalidTargetDescriptions.push(`${t} is a subdirectory of ${t2}.`) } } } - if (invalidTargetPairs.length > 0) { + if (invalidTargetDescriptions.length > 0) { throw new ConfigurationError( - dedent`Hot reload configuration invalid - a target may not be a subdirectory of another target.\n - ${invalidTargetPairs.join("\n")}`, - { invalidTargetPairs }, + dedent`Invalid hot reload configuration - a target may not be a subdirectory of another target \ + in the same module. + + ${invalidTargetDescriptions.join("\n")}`, + { invalidTargetDescriptions, hotReloadConfig }, ) } } diff --git a/garden-service/src/plugins/generic.ts b/garden-service/src/plugins/generic.ts index 519d0d4c2b..d2261332f2 100644 --- a/garden-service/src/plugins/generic.ts +++ b/garden-service/src/plugins/generic.ts @@ -22,14 +22,12 @@ import { BuildResult, BuildStatus, ValidateModuleResult, - HotReloadResult, TestResult, } from "../types/plugin/outputs" import { BuildModuleParams, GetBuildStatusParams, ValidateModuleParams, - HotReloadParams, TestModuleParams, } from "../types/plugin/params" import { BaseServiceSpec } from "../config/service" @@ -127,10 +125,6 @@ export async function buildGenericModule({ module }: BuildModuleParams): Promise { - return {} -} - export async function testGenericModule({ module, testConfig }: TestModuleParams): Promise { const startedAt = new Date() const command = testConfig.spec.command @@ -167,7 +161,6 @@ export const genericPlugin: GardenPlugin = { validate: parseGenericModule, getBuildStatus: getGenericModuleBuildStatus, build: buildGenericModule, - hotReload: reloadGenericModule, testModule: testGenericModule, }, }, diff --git a/garden-service/src/plugins/kubernetes/actions.ts b/garden-service/src/plugins/kubernetes/actions.ts index 8307a3eec8..ed28a3cd81 100644 --- a/garden-service/src/plugins/kubernetes/actions.ts +++ b/garden-service/src/plugins/kubernetes/actions.ts @@ -8,8 +8,6 @@ import * as Bluebird from "bluebird" import * as execa from "execa" -import * as inquirer from "inquirer" -import * as Joi from "joi" import * as split from "split" import { includes } from "lodash" import moment = require("moment") @@ -137,17 +135,13 @@ export async function execInService(params: ExecInServiceParams } export async function hotReload( - { ctx, runtimeContext, module }: HotReloadParams, + { ctx, runtimeContext, module, buildDependencies }: HotReloadParams, ): Promise { - const hotReloadConfig = module.spec.hotReload - - if (!hotReloadConfig) { - return {} - } + const hotReloadConfig = module.spec.hotReload! const services = module.services - if (!await waitForServices(ctx, runtimeContext, services)) { + if (!await waitForServices(ctx, runtimeContext, services, buildDependencies)) { // Service deployment timed out, skip hot reload return {} } diff --git a/garden-service/src/plugins/kubernetes/deployment.ts b/garden-service/src/plugins/kubernetes/deployment.ts index 08532e0a06..62ad1c13a9 100644 --- a/garden-service/src/plugins/kubernetes/deployment.ts +++ b/garden-service/src/plugins/kubernetes/deployment.ts @@ -42,7 +42,7 @@ interface KubeEnvVar { } export async function getContainerServiceStatus( - { ctx, module, service, runtimeContext }: GetServiceStatusParams, + { ctx, module, service, runtimeContext, verifyHotReloadStatus }: GetServiceStatusParams, ): Promise { // TODO: hash and compare all the configuration files (otherwise internal changes don't get deployed) @@ -52,20 +52,25 @@ export async function getContainerServiceStatus( const namespace = await getAppNamespace(ctx, ctx.provider) let enableHotReload - try { - const currentDeployment = (await api.extensions - .readNamespacedDeployment(service.name, namespace)) - - const hr = get( - currentDeployment, - ["body", "metadata", "annotations", "garden.io/hotReload"]) - enableHotReload = hr === "true" - } catch (err) { - if (err.code === 404) { - // Deployment was not found - enableHotReload = false - } else { - throw err + if (verifyHotReloadStatus) { + enableHotReload = verifyHotReloadStatus === "enabled" ? true : false + } else { + // Create deployment objects with the current deployment's hot reload state, if any + try { + const currentDeployment = (await api.extensions + .readNamespacedDeployment(service.name, namespace)) + + const hr = get( + currentDeployment, + ["body", "metadata", "annotations", "garden.io/hotReload"]) + enableHotReload = hr === "true" + } catch (err) { + if (err.code === 404) { + // Deployment was not found + enableHotReload = false + } else { + throw err + } } } @@ -82,11 +87,10 @@ export async function getContainerServiceStatus( } export async function deployContainerService(params: DeployServiceParams): Promise { - const { ctx, service, runtimeContext, force, logEntry, watch } = params + const { ctx, service, runtimeContext, force, logEntry, hotReload } = params const namespace = await getAppNamespace(ctx, ctx.provider) - const module = service.module - const enableHotReload = !!watch && !!module.spec.hotReload && ctx.provider.name === "local-kubernetes" + const enableHotReload = !!hotReload && ctx.provider.name === "local-kubernetes" const objects = await createContainerObjects(ctx, service, runtimeContext, enableHotReload) @@ -108,7 +112,7 @@ export async function createContainerObjects( const namespace = await getAppNamespace(ctx, ctx.provider) const api = new KubeApi(ctx.provider) const ingresses = await createIngresses(api, namespace, service) - const deployment = await createDeployment(service, runtimeContext, namespace, enableHotReload) + const deployment = await createDeployment(ctx.provider, service, runtimeContext, namespace, enableHotReload) const kubeservices = await createServices(service, namespace, enableHotReload) const objects = [deployment, ...kubeservices, ...ingresses] @@ -124,7 +128,7 @@ export async function createContainerObjects( export async function createDeployment( provider: KubernetesProvider, service: ContainerService, - runtimeContext: RuntimeContext, namespace: string, enableHotReload: boolean + runtimeContext: RuntimeContext, namespace: string, enableHotReload: boolean, ): Promise { const module = service.module diff --git a/garden-service/src/plugins/kubernetes/status.ts b/garden-service/src/plugins/kubernetes/status.ts index c0c558cd11..9ee17ded5f 100644 --- a/garden-service/src/plugins/kubernetes/status.ts +++ b/garden-service/src/plugins/kubernetes/status.ts @@ -367,7 +367,7 @@ export async function waitForObjects({ ctx, provider, service, objects, logEntry * TODO: This function is repetitive of waitForObjects above. */ export async function waitForServices( - ctx: PluginContext, runtimeContext: RuntimeContext, services: Service[], + ctx: PluginContext, runtimeContext: RuntimeContext, services: Service[], buildDependencies, ): Promise { let ready const startTime = new Date().getTime() @@ -375,8 +375,9 @@ export async function waitForServices( while (true) { ready = (await Bluebird.map(services, async (service) => { - const state = (await getContainerServiceStatus({ ctx, service, runtimeContext, module: service.module })) - .state + const state = (await getContainerServiceStatus({ + ctx, buildDependencies, service, runtimeContext, module: service.module, + })).state return state === "ready" || state === "outdated" })).every(serviceReady => serviceReady) diff --git a/garden-service/src/tasks/deploy.ts b/garden-service/src/tasks/deploy.ts index 1fe891225b..9f5126e9f4 100644 --- a/garden-service/src/tasks/deploy.ts +++ b/garden-service/src/tasks/deploy.ts @@ -6,9 +6,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { flatten } from "lodash" import * as Bluebird from "bluebird" import chalk from "chalk" +import { includes } from "lodash" import { LogEntry } from "../logger/log-entry" import { Task } from "./base" import { @@ -16,9 +16,6 @@ import { ServiceStatus, prepareRuntimeContext, } from "../types/service" -import { Module } from "../types/module" -import { withDependants, computeAutoReloadDependants } from "../watch" -import { getNames } from "../util/util" import { Garden } from "../garden" import { PushTask } from "./push" @@ -28,7 +25,9 @@ export interface DeployTaskParams { force: boolean forceBuild: boolean logEntry?: LogEntry + //TODO: Move watch and hotReloadServiceNames to a new commandContext object? watch?: boolean + hotReloadServiceNames?: string[] } export class DeployTask extends Task { @@ -38,28 +37,29 @@ export class DeployTask extends Task { private forceBuild: boolean private logEntry?: LogEntry private watch: boolean + private hotReloadServiceNames?: string[] - constructor({ garden, service, force, forceBuild, logEntry, watch }: DeployTaskParams) { + constructor({ garden, service, force, forceBuild, logEntry, watch, hotReloadServiceNames }: DeployTaskParams) { super({ garden, force, version: service.module.version }) this.service = service this.forceBuild = forceBuild this.logEntry = logEntry this.watch = !!watch + this.hotReloadServiceNames = hotReloadServiceNames } async getDependencies() { - const serviceDeps = this.service.config.dependencies + const servicesToDeploy = (await this.garden.getServices(this.service.config.dependencies)) + .filter(s => !includes(this.hotReloadServiceNames, s.name)) - const services = (await this.garden.getServices(serviceDeps)) - .filter(s => !s.module.spec.hotReload) - - const deps: Task[] = await Bluebird.map(services, async (service) => { + const deps: Task[] = await Bluebird.map(servicesToDeploy, async (service) => { return new DeployTask({ garden: this.garden, service, force: false, forceBuild: this.forceBuild, watch: this.watch, + hotReloadServiceNames: this.hotReloadServiceNames, }) }) @@ -89,7 +89,12 @@ export class DeployTask extends Task { // TODO: get version from build task results const { versionString } = await this.service.module.version - const status = await this.garden.actions.getServiceStatus({ service: this.service, logEntry }) + const hotReloadEnabled = includes(this.hotReloadServiceNames, this.service.name) + const status = await this.garden.actions.getServiceStatus({ + service: this.service, + verifyHotReloadStatus: hotReloadEnabled ? "enabled" : "disabled", + logEntry, + }) if ( !this.force && @@ -115,7 +120,7 @@ export class DeployTask extends Task { runtimeContext: await prepareRuntimeContext(this.garden, this.service.module, dependencies), logEntry, force: this.force, - watch: this.watch, + hotReload: hotReloadEnabled, }) } catch (err) { logEntry.setError() @@ -126,47 +131,3 @@ export class DeployTask extends Task { return result } } - -export async function getDeployTasks( - { garden, module, serviceNames, force = false, forceBuild = false, watch = false, - includeDependants = false, skipDeployTaskForModule = false }: - { - garden: Garden, module: Module, serviceNames?: string[] | null, - force?: boolean, forceBuild?: boolean, watch?: boolean, - includeDependants?: boolean, skipDeployTaskForModule?: boolean, - }, -) { - - let modulesToProcess = includeDependants - ? (await withDependants(garden, [module], await computeAutoReloadDependants(garden))) - : [module] - - if (skipDeployTaskForModule) { - // We don't add deploy tasks for module... - const moduleName = module.name - modulesToProcess = modulesToProcess.filter(m => m.name !== moduleName) - } - - const moduleServices = flatten(await Bluebird.map( - modulesToProcess, - m => garden.getServices(getNames(m.serviceConfigs)))) - - const servicesToProcess = serviceNames - ? moduleServices.filter(s => serviceNames.includes(s.name)) - : moduleServices - - const deployTasks: Task[] = servicesToProcess.map(service => { - return new DeployTask({ garden, service, force, forceBuild, watch }) - }) - - if (skipDeployTaskForModule) { - /** - * ... and add a build task for module instead, since there are no deploy tasks for module's - * services to trigger this build task as one of their dependenceis when added to the task graph. - */ - const buildTask = new BuildTask({ garden, module, force: true }) - return [buildTask, ...deployTasks] - } else { - return deployTasks - } -} diff --git a/garden-service/src/tasks/helpers.ts b/garden-service/src/tasks/helpers.ts new file mode 100644 index 0000000000..4b7861569d --- /dev/null +++ b/garden-service/src/tasks/helpers.ts @@ -0,0 +1,74 @@ +/* + * 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/. + */ + +import * as Bluebird from "bluebird" +import { flatten } from "lodash" +import { computeAutoReloadDependants, withDependants } from "../watch" +import { DeployTask } from "./deploy" +import { BuildTask } from "./build" +import { getNames } from "../util/util" +import { Garden } from "../garden" +import { Module } from "../types/module" + +/** + * @param hotReloadServiceNames - names of services with hot reloading enabled (should not be redeployed) + */ +export async function getTasksForHotReload( + { garden, module, hotReloadServiceNames, serviceNames }: + { garden: Garden, module: Module, hotReloadServiceNames: string[], serviceNames: string[] }, +) { + + const hotReloadModuleNames = await getHotReloadModuleNames(garden, hotReloadServiceNames) + + const modulesForDeployment = (await withDependants(garden, [module], + await computeAutoReloadDependants(garden))) + .filter(m => !hotReloadModuleNames.has(m.name)) + + const buildTask = new BuildTask({ garden, module, force: true }) + + const deployTasks = (await servicesForModules(garden, modulesForDeployment, serviceNames)) + .map(service => new DeployTask({ + garden, service, force: true, forceBuild: true, watch: true, hotReloadServiceNames, + })) + + return [buildTask, ...deployTasks] + +} + +export async function getHotReloadModuleNames(garden: Garden, hotReloadServiceNames: string[]): Promise> { + return new Set(flatten((await garden.getServices(hotReloadServiceNames || [])) + .map(s => s.module.name))) +} + +export async function getDeployTasks( + { garden, module, serviceNames, hotReloadServiceNames, force = false, forceBuild = false, + watch = false, includeDependants = false }: + { + garden: Garden, module: Module, serviceNames?: string[], hotReloadServiceNames: string[], + force?: boolean, forceBuild?: boolean, watch?: boolean, includeDependants?: boolean, + }, +) { + + const modulesForDeployment = includeDependants + ? (await withDependants(garden, [module], await computeAutoReloadDependants(garden))) + : [module] + + return (await servicesForModules(garden, modulesForDeployment, serviceNames)) + .map(service => new DeployTask({ garden, service, force, forceBuild, watch, hotReloadServiceNames })) + +} + +async function servicesForModules(garden: Garden, modules: Module[], serviceNames?: string[]) { + const moduleServices = flatten(await Bluebird.map( + modules, + m => garden.getServices(getNames(m.serviceConfigs)))) + + return serviceNames + ? moduleServices.filter(s => serviceNames.includes(s.name)) + : moduleServices +} diff --git a/garden-service/src/types/plugin/params.ts b/garden-service/src/types/plugin/params.ts index 113f17ba1b..d5711c30e6 100644 --- a/garden-service/src/types/plugin/params.ts +++ b/garden-service/src/types/plugin/params.ts @@ -203,10 +203,14 @@ export const getTestResultParamsSchema = moduleActionParamsSchema /** * Service actions */ + +export type hotReloadStatus = "enabled" | "disabled" + export interface GetServiceStatusParams extends PluginServiceActionParamsBase { - watch?: boolean runtimeContext: RuntimeContext + verifyHotReloadStatus?: hotReloadStatus } + export const getServiceStatusParamsSchema = serviceActionParamsSchema .keys({ runtimeContext: runtimeContextSchema, @@ -214,7 +218,7 @@ export const getServiceStatusParamsSchema = serviceActionParamsSchema export interface DeployServiceParams extends PluginServiceActionParamsBase { force: boolean, - watch?: boolean, + hotReload?: boolean, runtimeContext: RuntimeContext } export const deployServiceParamsSchema = serviceActionParamsSchema diff --git a/examples/hot-reload/build-dependency/.dockerignore b/garden-service/test/data/test-project-hot-reload/build-dependant/.dockerignore similarity index 100% rename from examples/hot-reload/build-dependency/.dockerignore rename to garden-service/test/data/test-project-hot-reload/build-dependant/.dockerignore diff --git a/examples/hot-reload/build-dependency/Dockerfile b/garden-service/test/data/test-project-hot-reload/build-dependant/Dockerfile similarity index 100% rename from examples/hot-reload/build-dependency/Dockerfile rename to garden-service/test/data/test-project-hot-reload/build-dependant/Dockerfile diff --git a/examples/hot-reload/build-dependant/app.js b/garden-service/test/data/test-project-hot-reload/build-dependant/app.js similarity index 100% rename from examples/hot-reload/build-dependant/app.js rename to garden-service/test/data/test-project-hot-reload/build-dependant/app.js diff --git a/examples/hot-reload/build-dependant/garden.yml b/garden-service/test/data/test-project-hot-reload/build-dependant/garden.yml similarity index 100% rename from examples/hot-reload/build-dependant/garden.yml rename to garden-service/test/data/test-project-hot-reload/build-dependant/garden.yml diff --git a/examples/hot-reload/build-dependency/main.js b/garden-service/test/data/test-project-hot-reload/build-dependant/main.js similarity index 100% rename from examples/hot-reload/build-dependency/main.js rename to garden-service/test/data/test-project-hot-reload/build-dependant/main.js diff --git a/examples/hot-reload/build-dependency/package-lock.json b/garden-service/test/data/test-project-hot-reload/build-dependant/package-lock.json similarity index 100% rename from examples/hot-reload/build-dependency/package-lock.json rename to garden-service/test/data/test-project-hot-reload/build-dependant/package-lock.json diff --git a/examples/hot-reload/build-dependant/package.json b/garden-service/test/data/test-project-hot-reload/build-dependant/package.json similarity index 100% rename from examples/hot-reload/build-dependant/package.json rename to garden-service/test/data/test-project-hot-reload/build-dependant/package.json diff --git a/examples/hot-reload/good-evening/.dockerignore b/garden-service/test/data/test-project-hot-reload/build-dependency/.dockerignore similarity index 100% rename from examples/hot-reload/good-evening/.dockerignore rename to garden-service/test/data/test-project-hot-reload/build-dependency/.dockerignore diff --git a/examples/hot-reload/good-evening/Dockerfile b/garden-service/test/data/test-project-hot-reload/build-dependency/Dockerfile similarity index 100% rename from examples/hot-reload/good-evening/Dockerfile rename to garden-service/test/data/test-project-hot-reload/build-dependency/Dockerfile diff --git a/examples/hot-reload/build-dependency/app.js b/garden-service/test/data/test-project-hot-reload/build-dependency/app.js similarity index 100% rename from examples/hot-reload/build-dependency/app.js rename to garden-service/test/data/test-project-hot-reload/build-dependency/app.js diff --git a/examples/hot-reload/build-dependency/garden.yml b/garden-service/test/data/test-project-hot-reload/build-dependency/garden.yml similarity index 100% rename from examples/hot-reload/build-dependency/garden.yml rename to garden-service/test/data/test-project-hot-reload/build-dependency/garden.yml diff --git a/examples/hot-reload/good-evening/main.js b/garden-service/test/data/test-project-hot-reload/build-dependency/main.js similarity index 100% rename from examples/hot-reload/good-evening/main.js rename to garden-service/test/data/test-project-hot-reload/build-dependency/main.js diff --git a/examples/hot-reload/good-evening/package-lock.json b/garden-service/test/data/test-project-hot-reload/build-dependency/package-lock.json similarity index 100% rename from examples/hot-reload/good-evening/package-lock.json rename to garden-service/test/data/test-project-hot-reload/build-dependency/package-lock.json diff --git a/examples/hot-reload/build-dependency/package.json b/garden-service/test/data/test-project-hot-reload/build-dependency/package.json similarity index 100% rename from examples/hot-reload/build-dependency/package.json rename to garden-service/test/data/test-project-hot-reload/build-dependency/package.json diff --git a/garden-service/test/data/test-project-hot-reload/garden.yml b/garden-service/test/data/test-project-hot-reload/garden.yml new file mode 100644 index 0000000000..36a0a3ae04 --- /dev/null +++ b/garden-service/test/data/test-project-hot-reload/garden.yml @@ -0,0 +1,10 @@ +project: + name: hot-reload + environmentDefaults: + providers: + - name: container + - name: npm-package + environments: + - name: local + providers: + - name: local-kubernetes diff --git a/examples/hot-reload/good-morning/.dockerignore b/garden-service/test/data/test-project-hot-reload/good-evening/.dockerignore similarity index 100% rename from examples/hot-reload/good-morning/.dockerignore rename to garden-service/test/data/test-project-hot-reload/good-evening/.dockerignore diff --git a/examples/hot-reload/good-morning/Dockerfile b/garden-service/test/data/test-project-hot-reload/good-evening/Dockerfile similarity index 100% rename from examples/hot-reload/good-morning/Dockerfile rename to garden-service/test/data/test-project-hot-reload/good-evening/Dockerfile diff --git a/examples/hot-reload/good-evening/app.js b/garden-service/test/data/test-project-hot-reload/good-evening/app.js similarity index 100% rename from examples/hot-reload/good-evening/app.js rename to garden-service/test/data/test-project-hot-reload/good-evening/app.js diff --git a/examples/hot-reload/good-evening/garden.yml b/garden-service/test/data/test-project-hot-reload/good-evening/garden.yml similarity index 100% rename from examples/hot-reload/good-evening/garden.yml rename to garden-service/test/data/test-project-hot-reload/good-evening/garden.yml diff --git a/examples/hot-reload/good-morning/main.js b/garden-service/test/data/test-project-hot-reload/good-evening/main.js similarity index 100% rename from examples/hot-reload/good-morning/main.js rename to garden-service/test/data/test-project-hot-reload/good-evening/main.js diff --git a/examples/hot-reload/good-morning/package-lock.json b/garden-service/test/data/test-project-hot-reload/good-evening/package-lock.json similarity index 100% rename from examples/hot-reload/good-morning/package-lock.json rename to garden-service/test/data/test-project-hot-reload/good-evening/package-lock.json diff --git a/examples/hot-reload/good-evening/package.json b/garden-service/test/data/test-project-hot-reload/good-evening/package.json similarity index 100% rename from examples/hot-reload/good-evening/package.json rename to garden-service/test/data/test-project-hot-reload/good-evening/package.json diff --git a/examples/hot-reload/service-dependant/.dockerignore b/garden-service/test/data/test-project-hot-reload/good-morning/.dockerignore similarity index 100% rename from examples/hot-reload/service-dependant/.dockerignore rename to garden-service/test/data/test-project-hot-reload/good-morning/.dockerignore diff --git a/examples/hot-reload/service-dependant/Dockerfile b/garden-service/test/data/test-project-hot-reload/good-morning/Dockerfile similarity index 100% rename from examples/hot-reload/service-dependant/Dockerfile rename to garden-service/test/data/test-project-hot-reload/good-morning/Dockerfile diff --git a/examples/hot-reload/good-morning/app.js b/garden-service/test/data/test-project-hot-reload/good-morning/app.js similarity index 100% rename from examples/hot-reload/good-morning/app.js rename to garden-service/test/data/test-project-hot-reload/good-morning/app.js diff --git a/examples/hot-reload/good-morning/garden.yml b/garden-service/test/data/test-project-hot-reload/good-morning/garden.yml similarity index 100% rename from examples/hot-reload/good-morning/garden.yml rename to garden-service/test/data/test-project-hot-reload/good-morning/garden.yml diff --git a/examples/hot-reload/service-dependant/main.js b/garden-service/test/data/test-project-hot-reload/good-morning/main.js similarity index 100% rename from examples/hot-reload/service-dependant/main.js rename to garden-service/test/data/test-project-hot-reload/good-morning/main.js diff --git a/examples/hot-reload/service-dependant/package-lock.json b/garden-service/test/data/test-project-hot-reload/good-morning/package-lock.json similarity index 100% rename from examples/hot-reload/service-dependant/package-lock.json rename to garden-service/test/data/test-project-hot-reload/good-morning/package-lock.json diff --git a/garden-service/test/data/test-project-hot-reload/good-morning/package.json b/garden-service/test/data/test-project-hot-reload/good-morning/package.json new file mode 100644 index 0000000000..f250e448fa --- /dev/null +++ b/garden-service/test/data/test-project-hot-reload/good-morning/package.json @@ -0,0 +1,16 @@ +{ + "name": "good-morning", + "version": "1.0.0", + "description": "Good morning greeting service", + "main": "index.js", + "scripts": { + "start": "node main.js", + "dev": "nodemon main.js", + "test": "echo OK" + }, + "author": "garden.io ", + "license": "ISC", + "dependencies": { + "express": "^4.16.2" + } +} diff --git a/garden-service/test/data/test-project-hot-reload/service-dependant/.dockerignore b/garden-service/test/data/test-project-hot-reload/service-dependant/.dockerignore new file mode 100644 index 0000000000..1cd4736667 --- /dev/null +++ b/garden-service/test/data/test-project-hot-reload/service-dependant/.dockerignore @@ -0,0 +1,4 @@ +node_modules +Dockerfile +garden.yml +app.yaml diff --git a/garden-service/test/data/test-project-hot-reload/service-dependant/Dockerfile b/garden-service/test/data/test-project-hot-reload/service-dependant/Dockerfile new file mode 100644 index 0000000000..a4d1dacb72 --- /dev/null +++ b/garden-service/test/data/test-project-hot-reload/service-dependant/Dockerfile @@ -0,0 +1,12 @@ +FROM node:9-alpine + +ENV PORT=8080 +EXPOSE ${PORT} + +ADD . /app +WORKDIR /app + +RUN npm install -g nodemon +RUN npm install + +CMD ["npm", "start"] diff --git a/examples/hot-reload/service-dependant/app.js b/garden-service/test/data/test-project-hot-reload/service-dependant/app.js similarity index 100% rename from examples/hot-reload/service-dependant/app.js rename to garden-service/test/data/test-project-hot-reload/service-dependant/app.js diff --git a/examples/hot-reload/service-dependant/garden.yml b/garden-service/test/data/test-project-hot-reload/service-dependant/garden.yml similarity index 82% rename from examples/hot-reload/service-dependant/garden.yml rename to garden-service/test/data/test-project-hot-reload/service-dependant/garden.yml index 3db181567b..219e5a809a 100644 --- a/examples/hot-reload/service-dependant/garden.yml +++ b/garden-service/test/data/test-project-hot-reload/service-dependant/garden.yml @@ -2,9 +2,13 @@ module: description: Service dependant name: service-dependant type: container + hotReload: + sync: + - target: /app/ services: - name: service-dependant command: [npm, start] + hotReloadCommand: [npm, run, dev] ports: - name: http containerPort: 8080 diff --git a/garden-service/test/data/test-project-hot-reload/service-dependant/main.js b/garden-service/test/data/test-project-hot-reload/service-dependant/main.js new file mode 100644 index 0000000000..c20d705f46 --- /dev/null +++ b/garden-service/test/data/test-project-hot-reload/service-dependant/main.js @@ -0,0 +1,3 @@ +const { app } = require("./app") + +app.listen(process.env.PORT, "0.0.0.0", () => console.log("App started")) diff --git a/garden-service/test/data/test-project-hot-reload/service-dependant/package-lock.json b/garden-service/test/data/test-project-hot-reload/service-dependant/package-lock.json new file mode 100644 index 0000000000..f1a7d946df --- /dev/null +++ b/garden-service/test/data/test-project-hot-reload/service-dependant/package-lock.json @@ -0,0 +1,1023 @@ +{ + "name": "hello-container", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", + "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", + "requires": { + "mime-types": "2.1.17", + "negotiator": "0.6.1" + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.9", + "depd": "1.1.2", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "1.6.15" + } + }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "requires": { + "hoek": "4.2.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cookiejar": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.1.tgz", + "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "requires": { + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "requires": { + "hoek": "4.2.0" + } + } + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", + "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", + "requires": { + "accepts": "1.3.4", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "1.1.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", + "finalhandler": "1.1.0", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "2.0.2", + "qs": "6.5.1", + "range-parser": "1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.1", + "serve-static": "1.13.1", + "setprototypeof": "1.1.0", + "statuses": "1.3.1", + "type-is": "1.6.15", + "utils-merge": "1.0.1", + "vary": "1.1.2" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "requires": { + "debug": "2.6.9", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", + "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "formidable": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", + "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", + "dev": true + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "5.5.2", + "har-schema": "2.0.0" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "requires": { + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.0", + "sntp": "2.1.0" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "hoek": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", + "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "1.30.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.1.1.tgz", + "integrity": "sha512-kKKs/H1KrMMQIEsWNxGmb4/BGsmj0dkeyotEvbrAuQ01FcWRLssUNXCEUZk6SZtyJBi6EE7SL0zDDtItw1rGhw==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", + "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", + "requires": { + "forwarded": "0.1.2", + "ipaddr.js": "1.5.2" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.1", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" + } + }, + "request-promise": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz", + "integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=", + "requires": { + "bluebird": "3.5.1", + "request-promise-core": "1.1.1", + "stealthy-require": "1.1.1", + "tough-cookie": "2.3.3" + } + }, + "request-promise-core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", + "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", + "requires": { + "lodash": "4.17.4" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "send": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", + "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", + "requires": { + "debug": "2.6.9", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", + "fresh": "0.5.2", + "http-errors": "1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.1" + } + }, + "serve-static": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", + "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", + "requires": { + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.2", + "send": "0.16.1" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "sntp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "requires": { + "hoek": "4.2.0" + } + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "superagent": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz", + "integrity": "sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "cookiejar": "2.1.1", + "debug": "3.1.0", + "extend": "3.0.1", + "form-data": "2.3.1", + "formidable": "1.2.1", + "methods": "1.1.2", + "mime": "1.4.1", + "qs": "6.5.1", + "readable-stream": "2.3.6" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "supertest": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.0.0.tgz", + "integrity": "sha1-jUu2j9GDDuBwM7HFpamkAhyWUpY=", + "dev": true, + "requires": { + "methods": "1.1.2", + "superagent": "3.8.2" + } + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.17" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/examples/hot-reload/service-dependant/package.json b/garden-service/test/data/test-project-hot-reload/service-dependant/package.json similarity index 100% rename from examples/hot-reload/service-dependant/package.json rename to garden-service/test/data/test-project-hot-reload/service-dependant/package.json diff --git a/garden-service/test/src/actions.ts b/garden-service/test/src/actions.ts index 75e5486a36..02c854939c 100644 --- a/garden-service/test/src/actions.ts +++ b/garden-service/test/src/actions.ts @@ -153,7 +153,7 @@ describe("ActionHelper", () => { }) describe("hotReload", () => { - it.only("should correctly call the corresponding plugin handler", async () => { + it("should correctly call the corresponding plugin handler", async () => { const result = await actions.hotReload({ module, runtimeContext: { diff --git a/garden-service/test/src/commands/deploy.ts b/garden-service/test/src/commands/deploy.ts index fd3375afb8..30e955fe6c 100644 --- a/garden-service/test/src/commands/deploy.ts +++ b/garden-service/test/src/commands/deploy.ts @@ -74,6 +74,7 @@ describe("DeployCommand", () => { service: undefined, }, opts: { + "hot-reload": undefined, watch: false, force: false, "force-build": true, @@ -104,6 +105,7 @@ describe("DeployCommand", () => { service: ["service-b"], }, opts: { + "hot-reload": undefined, watch: false, force: false, "force-build": true, diff --git a/package-lock.json b/package-lock.json index b52deceda3..ae26fbaeb3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1294,12 +1294,6 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, - "add-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", - "integrity": "sha1-anmQQ3ynNtXhKI25K9MmbV9csqo=", - "dev": true - }, "agent-base": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", @@ -2452,215 +2446,6 @@ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, - "conventional-changelog": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-2.0.1.tgz", - "integrity": "sha512-WeWcEcR7uBtRZ/uG6DRIlVqsm7UTnxrixaAPoPvfQP7FRPf1qIXL76nGKy4wXq+wO3zOpqYubWUqrYLIL3+xww==", - "dev": true, - "requires": { - "conventional-changelog-angular": "^1.6.6", - "conventional-changelog-atom": "^2.0.0", - "conventional-changelog-codemirror": "^2.0.0", - "conventional-changelog-core": "^3.0.0", - "conventional-changelog-ember": "^2.0.0", - "conventional-changelog-eslint": "^3.0.0", - "conventional-changelog-express": "^2.0.0", - "conventional-changelog-jquery": "^0.1.0", - "conventional-changelog-jscs": "^0.1.0", - "conventional-changelog-jshint": "^2.0.0", - "conventional-changelog-preset-loader": "^2.0.0" - }, - "dependencies": { - "conventional-changelog-core": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.0.0.tgz", - "integrity": "sha512-D2hApWWsdh4tkNgDjn1KtRapxUJ70Sd+V84btTVJJJ96S3cVRES8Ty3ih0TRkOZmDkw/uS0mxrHSskQ/P/Gvsg==", - "dev": true, - "requires": { - "conventional-changelog-writer": "^4.0.0", - "conventional-commits-parser": "^3.0.0", - "dateformat": "^3.0.0", - "get-pkg-repo": "^1.0.0", - "git-raw-commits": "^2.0.0", - "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^2.0.0", - "lodash": "^4.2.1", - "normalize-package-data": "^2.3.5", - "q": "^1.5.1", - "read-pkg": "^1.1.0", - "read-pkg-up": "^1.0.1", - "through2": "^2.0.0" - } - }, - "conventional-changelog-preset-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.0.0.tgz", - "integrity": "sha512-hEWm9o6TxjS9aO1AKaHpl8avSXaUHiUXBT25vJ4ToaDi/gPDqt3OnZkwhIgubADUF+lPqcXpjFTOYcOL4AwyvA==", - "dev": true - }, - "conventional-changelog-writer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.0.tgz", - "integrity": "sha512-hMZPe0AQ6Bi05epeK/7hz80xxk59nPA5z/b63TOHq2wigM0/akreOc8N4Jam5b9nFgKWX1e9PdPv2ewgW6bcfg==", - "dev": true, - "requires": { - "compare-func": "^1.3.1", - "conventional-commits-filter": "^2.0.0", - "dateformat": "^3.0.0", - "handlebars": "^4.0.2", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.2.1", - "meow": "^4.0.0", - "semver": "^5.5.0", - "split": "^1.0.0", - "through2": "^2.0.0" - } - }, - "conventional-commits-filter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.0.tgz", - "integrity": "sha512-Cfl0j1/NquB/TMVx7Wrmyq7uRM+/rPQbtVVGwzfkhZ6/yH6fcMmP0Q/9044TBZPTNdGzm46vXFXL14wbET0/Mg==", - "dev": true, - "requires": { - "is-subset": "^0.1.1", - "modify-values": "^1.0.0" - } - }, - "conventional-commits-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.0.tgz", - "integrity": "sha512-GWh71U26BLWgMykCp+VghZ4s64wVbtseECcKQ/PvcPZR2cUnz+FUc2J9KjxNl7/ZbCxST8R03c9fc+Vi0umS9Q==", - "dev": true, - "requires": { - "JSONStream": "^1.0.4", - "is-text-path": "^1.0.0", - "lodash": "^4.2.1", - "meow": "^4.0.0", - "split2": "^2.0.0", - "through2": "^2.0.0", - "trim-off-newlines": "^1.0.0" - } - }, - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true - }, - "git-raw-commits": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.0.tgz", - "integrity": "sha512-w4jFEJFgKXMQJ0H0ikBk2S+4KP2VEjhCvLCNqbNRQC8BgGWgLKNCO7a9K9LI+TVT7Gfoloje502sEnctibffgg==", - "dev": true, - "requires": { - "dargs": "^4.0.1", - "lodash.template": "^4.0.2", - "meow": "^4.0.0", - "split2": "^2.0.0", - "through2": "^2.0.0" - } - }, - "git-semver-tags": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.0.tgz", - "integrity": "sha512-lSgFc3zQTul31nFje2Q8XdNcTOI6B4I3mJRPCgFzHQQLfxfqdWTYzdtCaynkK5Xmb2wQlSJoKolhXJ1VhKROnQ==", - "dev": true, - "requires": { - "meow": "^4.0.0", - "semver": "^5.5.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "meow": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", - "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", - "dev": true, - "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist": "^1.1.3", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0" - }, - "dependencies": { - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - } - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, "conventional-changelog-angular": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-1.6.6.tgz", @@ -2671,120 +2456,6 @@ "q": "^1.5.1" } }, - "conventional-changelog-atom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.0.tgz", - "integrity": "sha512-ygwkwyTQYAm4S0tsDt+1yg8tHhRrv7qu9SOWPhNQlVrInFLsfKc0FActCA3de2ChknxpVPY2B53yhKvCAtkBCg==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-cli": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-2.0.1.tgz", - "integrity": "sha512-gQzMbLyPNYymbzJncJNBapLZTXEtXrq6qmQOJH0w/jVX9fxIli4sLalQgzEPjD7M1noLJd1cIdQAP1R++TkGxg==", - "dev": true, - "requires": { - "add-stream": "^1.0.0", - "conventional-changelog": "^2.0.1", - "lodash": "^4.2.1", - "meow": "^4.0.0", - "tempfile": "^1.1.1" - }, - "dependencies": { - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "meow": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", - "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", - "dev": true, - "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist": "^1.1.3", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "conventional-changelog-codemirror": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.0.tgz", - "integrity": "sha512-pZt/YynJ5m8C9MGV5wkBuhM1eX+8a84fmNrdOylxg/lJV+lgtAiNhnpskNuixtf71iKVWSlEqMQ6z6CH7/Uo5A==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, "conventional-changelog-core": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-2.0.11.tgz", @@ -2814,61 +2485,6 @@ } } }, - "conventional-changelog-ember": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.0.tgz", - "integrity": "sha512-s9ZYf3VMdYe8ca8bw1X+he050HZNy9Pm3dBpYA+BunDGFE4Fy7whOvYhWah2U9+j9l6y/whfa0+eHANvZytE9A==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-eslint": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.0.tgz", - "integrity": "sha512-Acn20v+13c+o1OAWKvc9sCCl73Nj2vOMyn+G82euiMZwgYNE9CcBkTnw/GKdBi9KiZMK9uy+SCQ/QyAEE+8vZA==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-express": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.0.tgz", - "integrity": "sha512-2svPjeXCrjwwqnzu/f3qU5LWoLO0jmUIEbtbbSRXAAP9Ag+137b484eJsiRt9DPYXSVzog0Eoek3rvCzfHcphQ==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-jquery": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-0.1.0.tgz", - "integrity": "sha1-Agg5cWLjhGmG5xJztsecW1+A9RA=", - "dev": true, - "requires": { - "q": "^1.4.1" - } - }, - "conventional-changelog-jscs": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-jscs/-/conventional-changelog-jscs-0.1.0.tgz", - "integrity": "sha1-BHnrRDzH1yxYvwvPDvHURKkvDlw=", - "dev": true, - "requires": { - "q": "^1.4.1" - } - }, - "conventional-changelog-jshint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.0.tgz", - "integrity": "sha512-+4fCln755N0ZzRUEdcDWR5Due71Dsqkbov6K/UmVCnljZvhVh0/wpT4YROoSsAnhfZO8shyWDPFKm6EP20pLQg==", - "dev": true, - "requires": { - "compare-func": "^1.3.1", - "q": "^1.5.1" - } - }, "conventional-changelog-preset-loader": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-1.1.8.tgz", @@ -9940,24 +9556,6 @@ "uuid": "^3.0.1" } }, - "tempfile": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-1.1.1.tgz", - "integrity": "sha1-W8xOrsxKsscH2LwR2ZzMmiyyh/I=", - "dev": true, - "requires": { - "os-tmpdir": "^1.0.0", - "uuid": "^2.0.1" - }, - "dependencies": { - "uuid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", - "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", - "dev": true - } - } - }, "text-extensions": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.7.0.tgz",