Skip to content

Commit

Permalink
Merge pull request #451 from garden-io/watcher-refactor
Browse files Browse the repository at this point in the history
refactor: use events for file watching instead of callbacks
  • Loading branch information
edvald authored Jan 10, 2019
2 parents 2bf433b + f6a99c2 commit a933b17
Show file tree
Hide file tree
Showing 16 changed files with 414 additions and 121 deletions.
2 changes: 2 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ jobs:
name: test
command: |
npm run ci-test
environment:
CHOKIDAR_USEPOLLING: "1"
release-service-npm:
<<: *node-config
steps:
Expand Down
53 changes: 53 additions & 0 deletions garden-service/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions garden-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,14 @@
"@types/node": "^10.12.15",
"@types/node-emoji": "^1.8.0",
"@types/normalize-url": "^3.3.0",
"@types/p-event": "^1.3.0",
"@types/p-queue": "^3.0.0",
"@types/path-is-inside": "^1.0.0",
"@types/prettyjson": "0.0.28",
"@types/string-width": "^2.0.0",
"@types/supertest": "^2.0.7",
"@types/tar": "^4.0.0",
"@types/touch": "^3.1.1",
"@types/uniqid": "^4.1.2",
"@types/unzip": "^0.1.1",
"@types/unzipper": "^0.9.1",
Expand All @@ -140,13 +142,15 @@
"nock": "^10.0.4",
"nodetree": "0.0.3",
"nyc": "^13.1.0",
"p-event": "^2.1.0",
"pegjs": "^0.10.0",
"shx": "^0.3.2",
"snyk": "^1.117.2",
"testdouble": "^3.9.1",
"testdouble-chai": "^0.5.0",
"timekeeper": "^2.1.2",
"tmp-promise": "^1.0.5",
"touch": "^3.1.0",
"ts-node": "^7.0.1",
"tslint": "^5.12.0",
"tslint-microsoft-contrib": "^6.0.0",
Expand Down
3 changes: 3 additions & 0 deletions garden-service/src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,9 @@ export class GardenCli {
args: parsedArgs,
opts: parsedOpts,
})

await garden.close()

} while (result.restartRequired)

// We attach the action result to cli context so that we can process it in the parse method
Expand Down
20 changes: 20 additions & 0 deletions garden-service/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,27 @@ export class EventBus extends EventEmitter2 {
* The supported events and their interfaces.
*/
export interface Events {
// Internal test/control events
_restart: string,
_test: string,

// Watcher events
configAdded: {
path: string,
},
projectConfigChanged: {},
moduleConfigChanged: {
name: string,
},
moduleSourcesChanged: {
name: string,
pathChanged: string,
},
moduleRemoved: {
name: string,
},

// TaskGraph events
taskPending: {
addedAt: Date,
key: string,
Expand Down
23 changes: 23 additions & 0 deletions garden-service/src/garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
pickKeys,
throwOnMissingNames,
uniqByName,
Ignorer,
} from "./util/util"
import {
DEFAULT_NAMESPACE,
Expand Down Expand Up @@ -108,6 +109,7 @@ import { SUPPORTED_PLATFORMS, SupportedPlatform } from "./constants"
import { platform, arch } from "os"
import { LogEntry } from "./logger/log-entry"
import { EventBus } from "./events"
import { Watcher } from "./watch"

export interface ActionHandlerMap<T extends keyof PluginActions> {
[actionName: string]: PluginActions[T]
Expand Down Expand Up @@ -154,6 +156,7 @@ export class Garden {
private readonly taskNameIndex: { [key: string]: string } // task name -> module name
private readonly hotReloadScheduler: HotReloadScheduler
private readonly taskGraph: TaskGraph
private readonly watcher: Watcher

public readonly environment: Environment
public readonly localConfigStore: LocalConfigStore
Expand All @@ -169,6 +172,7 @@ export class Garden {
variables: PrimitiveMap,
public readonly projectSources: SourceConfig[] = [],
public readonly buildDir: BuildDir,
public readonly ignorer: Ignorer,
public readonly opts: GardenOpts,
) {
// make sure we're on a supported platform
Expand Down Expand Up @@ -209,6 +213,7 @@ export class Garden {
this.actions = new ActionHelper(this)
this.hotReloadScheduler = new HotReloadScheduler()
this.events = new EventBus()
this.watcher = new Watcher(this, this.log)
}

static async factory<T extends typeof Garden>(
Expand Down Expand Up @@ -293,6 +298,7 @@ export class Garden {
const variables = merge({}, environmentDefaults.variables, environmentConfig.variables)

const buildDir = await BuildDir.factory(projectRoot)
const ignorer = await getIgnorer(projectRoot)

const garden = new this(
projectRoot,
Expand All @@ -301,6 +307,7 @@ export class Garden {
variables,
projectSources,
buildDir,
ignorer,
opts,
) as InstanceType<T>

Expand All @@ -319,6 +326,13 @@ export class Garden {
return garden
}

/**
* Clean up before shutting down.
*/
async close() {
this.watcher.stop()
}

getPluginContext(providerName: string) {
return createPluginContext(this, providerName)
}
Expand All @@ -339,6 +353,15 @@ export class Garden {
return this.hotReloadScheduler.requestHotReload(moduleName, hotReloadHandler)
}

/**
* Enables the file watcher for the project.
* Make sure to stop it using `.close()` when cleaning up or when watching is no longer needed.
*/
async startWatcher() {
const modules = await this.getModules()
this.watcher.start(modules)
}

private registerPlugin(name: string, moduleOrFactory: RegisterPluginParam) {
let factory: PluginFactory

Expand Down
65 changes: 35 additions & 30 deletions garden-service/src/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@

import Bluebird = require("bluebird")
import chalk from "chalk"
import { padEnd } from "lodash"
import { padEnd, keyBy } from "lodash"

import { Module } from "./types/module"
import { Service } from "./types/service"
import { BaseTask } from "./tasks/base"
import { TaskResults } from "./task-graph"
import { FSWatcher } from "./watch"
import { registerCleanupFunction } from "./util/util"
import { isModuleLinked } from "./util/ext-source-util"
import { Garden } from "./garden"
import { LogEntry } from "./logger/log-entry"
Expand Down Expand Up @@ -110,32 +108,40 @@ export async function processModules(
changeHandler = handler
}

const watcher = new FSWatcher(garden, log)

const restartPromise = new Promise(async (resolve) => {
await watcher.watchModules(modules,
async (changedModule: Module | null, configChanged: boolean) => {
if (configChanged) {
if (changedModule) {
log.info({ section: changedModule.name, msg: `Module configuration changed, reloading...`, symbol: "info" })
} else {
log.info({ symbol: "info", msg: `Project configuration changed, reloading...` })
}
resolve()
return
}

if (changedModule) {
log.silly({ msg: `Files changed for module ${changedModule.name}` })
changedModule = await garden.getModule(changedModule.name)
await Bluebird.map(changeHandler!(changedModule), (task) => garden.addTask(task))
}

await garden.processTasks()
})

registerCleanupFunction("clearAutoReloadWatches", () => {
watcher.close()
const modulesByName = keyBy(modules, "name")

await garden.startWatcher()

const restartPromise = new Promise((resolve) => {
garden.events.on("_restart", () => {
log.debug({ symbol: "info", msg: `Manual restart triggered` })
resolve()
})

garden.events.on("projectConfigChanged", () => {
log.info({ symbol: "info", msg: `Project configuration changed, reloading...` })
resolve()
})

garden.events.on("configAdded", (event) => {
log.info({ symbol: "info", msg: `Garden config added at ${event.path}, reloading...` })
resolve()
})

garden.events.on("moduleConfigChanged", (event) => {
log.info({ symbol: "info", section: event.name, msg: `Module configuration changed, reloading...` })
resolve()
})

garden.events.on("moduleSourcesChanged", async (event) => {
const changedModule = modulesByName[event.name]

if (!changedModule) {
return
}

await Bluebird.map(changeHandler!(changedModule), (task) => garden.addTask(task))
await garden.processTasks()
})
})

Expand All @@ -147,7 +153,6 @@ export async function processModules(
}

await restartPromise
watcher.close()

return {
taskResults: {}, // TODO: Return latest results for each task baseKey processed between restarts?
Expand Down
6 changes: 5 additions & 1 deletion garden-service/src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ export async function getChildDirNames(parentDir: string): Promise<string[]> {
return dirNames
}

export async function getIgnorer(rootPath: string) {
export interface Ignorer {
ignores: (path: string) => boolean
}

export async function getIgnorer(rootPath: string): Promise<Ignorer> {
// TODO: this doesn't handle nested .gitignore files, we should revisit
const gitignorePath = join(rootPath, ".gitignore")
const gardenignorePath = join(rootPath, ".gardenignore")
Expand Down
Loading

0 comments on commit a933b17

Please sign in to comment.