Skip to content

Commit

Permalink
fix: #107 & #108 - incl. deps in auto-reload.
Browse files Browse the repository at this point in the history
When specifying which modules to watch, modules for their build &
service dependencies are now also watched.

Additionally, service dependencies are now included as auto reload
dependants.
  • Loading branch information
thsig committed May 25, 2018
1 parent 8d95c6c commit d1aaf5e
Show file tree
Hide file tree
Showing 19 changed files with 140 additions and 27 deletions.
6 changes: 4 additions & 2 deletions src/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import {
sleep,
} from "./util"
import {
autoReloadModules,
computeAutoReloadDependants,
FSWatcher,
} from "./watch"
Expand Down Expand Up @@ -479,7 +480,8 @@ export function createPluginContext(garden: Garden): PluginContext {
return results
}

const autoReloadDependants = await computeAutoReloadDependants(modules)
const modulesToWatch = await autoReloadModules(modules)
const autoReloadDependants = await computeAutoReloadDependants(ctx)

async function handleChanges(module: Module) {
const tasks = await process(module)
Expand All @@ -498,7 +500,7 @@ export function createPluginContext(garden: Garden): PluginContext {
const watcher = new FSWatcher(ctx.projectRoot)

// TODO: should the prefix here be different or set explicitly per run?
await watcher.watchModules(modules, "addTasksForAutoReload/",
await watcher.watchModules(modulesToWatch, "addTasksForAutoReload/",
async (changedModule) => {
ctx.log.debug({ msg: `Files changed for module ${changedModule.name}` })
await handleChanges(changedModule)
Expand Down
10 changes: 10 additions & 0 deletions src/types/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import {
} from "fs"
import * as Joi from "joi"
import {
flatten,
keyBy,
set,
uniq,
} from "lodash"
import { join } from "path"
import { GARDEN_VERSIONFILE_NAME } from "../constants"
Expand Down Expand Up @@ -257,6 +259,14 @@ export class Module<
return this.ctx.getServices(serviceNames)
}

async getServiceDependencies(): Promise<Service[]> {
const depNames: string[] = uniq(flatten(this.services
.map(serviceConfig => serviceConfig.dependencies)
.filter(deps => deps)))

return this.ctx.getServices(depNames)
}

async getDeployTasks(
{ force = false, forceBuild = false }: { force?: boolean, forceBuild?: boolean },
): Promise<DeployTask[]> {
Expand Down
41 changes: 35 additions & 6 deletions src/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import { map as bluebirdMap } from "bluebird"
import { Client } from "fb-watchman"
import { keyBy } from "lodash"
import { keyBy, uniqBy, values } from "lodash"
import { relative, resolve } from "path"
import { Module } from "./types/module"
import { PluginContext } from "./plugin-context"
Expand All @@ -19,20 +19,49 @@ export interface OnChangeHandler {
(ctx: PluginContext, module: Module): Promise<void>
}

export async function computeAutoReloadDependants(modules: Module[]):
/*
Resolves to modules and their build & service dependency modules (recursively).
Each module is represented at most once in the output.
*/
export async function autoReloadModules(modules: Module[]): Promise<Module[]> {
const modulesByName = {}

const scanner = async (module: Module, byName: object) => {
byName[module.name] = module
for (const dep of await uniqueDependencyModules(module)) {
if (!byName[dep.name]) {
await scanner(dep, byName)
}
}
}

for (const m of modules) {
await scanner(m, modulesByName)
}

return values(modulesByName)
}

export async function computeAutoReloadDependants(ctx: PluginContext):
Promise<AutoReloadDependants> {
let dependants = {}
const dependants = {}

for (const module of modules) {
const deps = await module.getBuildDependencies()
for (const dep of deps) {
for (const module of await ctx.getModules()) {
const depModules: Module[] = await uniqueDependencyModules(module)
for (const dep of depModules) {
dependants[dep.name] = (dependants[dep.name] || new Set()).add(module)
}
}

return dependants
}

async function uniqueDependencyModules(module: Module): Promise<Module[]> {
const buildDepModules = await module.getBuildDependencies()
const serviceDepModules = (await module.getServiceDependencies()).map(s => s.module)
return uniqBy(buildDepModules.concat(serviceDepModules), m => m.name)
}

export type CapabilityOptions = { required?: string[], optional?: string[] }
export type CapabilityResponse = { error: Error, response: { capabilities: { string: boolean } } }

Expand Down
6 changes: 6 additions & 0 deletions test/data/test-project-auto-reload/garden.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
project:
name: test-project-auto-reload
environments:
- name: local
providers:
- name: test-plugin
14 changes: 14 additions & 0 deletions test/data/test-project-auto-reload/module-a/garden.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module:
name: module-a
type: container
image: scratch
services:
- name: service-a
endpoints:
- paths: [/path-b]
port: http
ports:
- name: http
containerPort: 8080
build:
command: echo A
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
module:
name: module-b
type: generic
type: container
image: scratch
services:
- name: service-b
dependencies:
- service-a
endpoints:
- paths: [/path-b]
port: http
ports:
- name: http
containerPort: 8080
build:
command: echo B
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
module:
name: module-c
type: generic
type: container
image: scratch
services:
- name: service-c
endpoints:
- paths: [/path-c]
port: http
ports:
- name: http
containerPort: 8080
build:
command: echo C
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
module:
name: module-d
type: generic
type: container
image: scratch
services:
- name: service-d
dependencies:
- service-b
endpoints:
- paths: [/path-d]
port: http
ports:
- name: http
containerPort: 8080
build:
command: echo D
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
module:
name: module-e
type: generic
type: container
image: scratch
services:
- name: service-e
dependencies:
- service-b
- service-c
endpoints:
- paths: [/path-e]
port: http
ports:
- name: http
containerPort: 8080
build:
dependencies:
- module-b
- module-c
command: echo E
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
module:
name: module-f
type: generic
type: container
image: scratch
services:
- name: service-f
dependencies:
- service-c
endpoints:
- paths: [/path-f]
port: http
ports:
- name: http
containerPort: 8080
build:
dependencies:
Expand Down
7 changes: 0 additions & 7 deletions test/src/commands/data/auto-reload-project/garden.yml

This file was deleted.

This file was deleted.

44 changes: 44 additions & 0 deletions test/src/watch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { expect } from "chai"
import { flatten, keys, mapValues, values, uniq } from "lodash"
import { join } from "path"
import {
AutoReloadDependants,
autoReloadModules,
computeAutoReloadDependants,
} from "../../src/watch"
import { makeTestGarden } from "../helpers"
import { inspect } from "util"

const projectRoot = join(__dirname, "..", "data", "test-project-auto-reload")

export function dependantModuleNames(ard: AutoReloadDependants): { [key: string]: string[] } {
return mapValues(ard, dependants => {
return Array.from(dependants).map(d => d.name)
})
}

describe("watch", () => {
describe("autoReloadModules", () => {
it("should include build and service dependencies of requested modules", async () => {
const ctx = (await makeTestGarden(projectRoot)).pluginContext
const moduleNames = (await autoReloadModules(await ctx.getModules(["module-e", "module-d"])))
.map(m => m.name).sort()

expect(moduleNames).to.eql(["module-a", "module-b", "module-c", "module-d", "module-e"])
})
})

describe("computeAutoReloadDependants", () => {
it("should include build and service dependants of requested modules", async () => {
const ctx = (await makeTestGarden(projectRoot)).pluginContext
const dependants = dependantModuleNames(
await computeAutoReloadDependants(ctx))

expect(dependants).to.eql({
"module-a": [ "module-b" ],
"module-b": [ "module-d", "module-e" ],
"module-c": [ "module-e", "module-f" ],
})
})
})
})

0 comments on commit d1aaf5e

Please sign in to comment.