diff --git a/.circleci/config.yml b/.circleci/config.yml index 44471d6510..e8afc52180 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,17 +4,60 @@ # version: 2 jobs: + build-go: + docker: + - image: circleci/golang:1.10 + working_directory: /go/src/github.com/garden-io/garden + steps: + - checkout + - restore_cache: + keys: + - pkg-cache-{{ checksum "Gopkg.lock" }} + - run: + name: Install dep + command: | + if [ ! -d /go/src/github.com/garden-io/garden/vendor ]; then + curl -L -s https://github.com/golang/dep/releases/download/v0.4.1/dep-linux-amd64 -o /go/bin/dep + chmod +x /go/bin/dep + /go/bin/dep ensure + fi + - save_cache: + key: pkg-cache-{{ checksum "Gopkg.lock" }} + paths: + - "/go/src/github.com/garden-io/garden/vendor" + - run: + name: Build All + command: | + cd garden-cli + package_name="garden" + platforms=("windows/amd64" "windows/386" "darwin/amd64" "linux/amd64") + for platform in "${platforms[@]}" + do + platform_split=(${platform//\// }) + GOOS=${platform_split[0]} + GOARCH=${platform_split[1]} + output_name=$package_name'-'$GOOS'-'$GOARCH + if [ $GOOS = "windows" ]; then + output_name+='.exe' + fi + + env GOOS=$GOOS GOARCH=$GOARCH go build -o build/$output_name + if [ $? -ne 0 ]; then + echo 'An error has occurred! Aborting the script execution...' + exit 1 + fi + done + - store_artifacts: + path: garden-cli/build + destination: /downloads test-node: docker: - # specify the version you desire here - image: circleci/node:10 steps: - checkout + - run: sudo apt-get update && sudo apt-get install rsync - setup_remote_docker: docker_layer_caching: true - - - run: sudo apt-get update && sudo apt-get install rsync - # Download and cache dependencies - restore_cache: keys: @@ -40,17 +83,18 @@ jobs: # build, lint and run tests - run: - name: npm build - command: npm run build + name: ci-build + command: npm run ci-build - run: - name: npm lint + name: npm run lint command: npm run lint - run: name: npm test - command: npm test + command: npm run ci-test workflows: version: 2 all-tests: jobs: + - build-go - test-node diff --git a/.gitignore b/.gitignore index aa46cb822c..b0bed9ad4d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,9 @@ *.log npm-debug.log* -# Node dependencies directory +# vendor dependencies node_modules +vendor/ # IDEs .idea @@ -24,3 +25,4 @@ coverage/ gulpfile.js *.map *.tgz +build/ diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000000..451f7b7aba --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,28 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:78bbb1ba5b7c3f2ed0ea1eab57bdd3859aec7e177811563edc41198a760b06af" + name = "github.com/mitchellh/go-homedir" + packages = ["."] + pruneopts = "UT" + revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4" + version = "v1.0.0" + +[[projects]] + digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" + name = "gopkg.in/yaml.v2" + packages = ["."] + pruneopts = "UT" + revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" + version = "v2.2.1" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/mitchellh/go-homedir", + "gopkg.in/yaml.v2", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000000..a2f7f97901 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,50 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + name = "github.com/mitchellh/go-homedir" + version = "1.0.0" + +[[constraint]] + name = "gopkg.in/yaml.v2" + version = "2.2.1" + +# [[constraint]] +# branch = "master" +# name = "k8s.io/api" + +# [[constraint]] +# name = "k8s.io/client-go" +# branch = "release-8.0" + +# [[constraint]] +# name = "k8s.io/apimachinery" +# revision = "488889b0007f63ffee90b66a34a2deca9ec58774" + +[prune] + go-tests = true + unused-packages = true diff --git a/bin/bootstrap-osx b/bin/bootstrap-osx index fd36e7d825..cd29004349 100755 --- a/bin/bootstrap-osx +++ b/bin/bootstrap-osx @@ -1,7 +1,7 @@ #!/usr/bin/env bash # install/update homebrew dependencies -BREW_DEPS="cmake git kubectl kubernetes-helm stern rsync icu4c pkg-config faas-cli git-chglog" +BREW_DEPS="cmake git kubectl kubernetes-helm stern rsync icu4c pkg-config faas-cli dep git-chglog" brew update brew install ${BREW_DEPS} diff --git a/bin/publish b/bin/publish index b93e46bac2..ef26ea8fe4 100755 --- a/bin/publish +++ b/bin/publish @@ -27,4 +27,5 @@ git push origin HEAD --no-verify # TODO: set this up to work with multiple packages cd garden-service npm publish +cd .. gulp update-brew diff --git a/garden-cli/config.go b/garden-cli/config.go new file mode 100644 index 0000000000..3d3477e15f --- /dev/null +++ b/garden-cli/config.go @@ -0,0 +1,83 @@ +package main + +import ( + "io/ioutil" + "log" + "os" + "path" + "strings" + + "gopkg.in/yaml.v2" +) + +// Config type must be public for the yaml parser (for some reason). We only need the project key and the name. +type Config struct { + Project struct { + Name *string + } +} + +func findProject(cwd string) (string, string) { + projectDir := cwd + + for { + configPath := path.Join(projectDir, "garden.yml") + + if _, err := os.Stat(configPath); !os.IsNotExist(err) { + configYaml, err := ioutil.ReadFile(configPath) + check(err) + + config := Config{} + + err = yaml.Unmarshal(configYaml, &config) + if err != nil { + log.Fatalf("Unable to parse %s as a valid garden configuration file", configPath) + } + + if config.Project.Name != nil { + // found project config + return projectDir, *config.Project.Name + } + } + + // move up one level + projectDir = path.Dir(projectDir) + + if projectDir == "/" { + log.Fatalf("Not a project directory (or any of the parent directories): %s", cwd) + } + } +} + +// Get or set the ID of this project (stored in PROJECT_ROOT/.garden/id). +// TODO: might wanna use a lockfile for concurrency here +func getProjectID(projectDir string) string { + gardenDir := path.Join(projectDir, ".garden") + ensureDir(gardenDir) + + idPath := path.Join(gardenDir, "id") + + var projectID string + + if _, err := os.Stat(idPath); !os.IsNotExist(err) { + idData, err := ioutil.ReadFile(idPath) + check(err) + projectID = strings.TrimSpace(string(idData)) + } else { + projectID = randSeq(8) + err := ioutil.WriteFile(idPath, []byte(projectID), 0644) + check(err) + } + + return projectID +} + +func getGardenHomeDir() string { + // TODO: allow override via env var + homeDir := getHomeDir() + gardenHome := path.Join(homeDir, ".garden") + + ensureDir(gardenHome) + + return gardenHome +} diff --git a/garden-cli/dockerutil/docker.go b/garden-cli/dockerutil/docker.go new file mode 100644 index 0000000000..d4de2ac6e3 --- /dev/null +++ b/garden-cli/dockerutil/docker.go @@ -0,0 +1,56 @@ +package dockerutil + +import ( + "log" + "os" + "os/exec" +) + +// TODO: Use the Docker Golang SDK instead of shelling out here + +func Exec(args []string, silent bool) error { + binary, err := exec.LookPath("docker") + if err != nil { + log.Fatal("Could not find docker - Garden requires docker to be installed in order to run.") + } + + cmd := exec.Command(binary, args...) + + cmd.Env = os.Environ() + if !silent { + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + } + + return cmd.Run() +} + +// TODO Use Docker SDK +func HasVolume(volumeName string) bool { + args := []string{"volume", "inspect", volumeName} + err := Exec(args, true) + if err != nil { + return false + } + return true +} + +func MakeVolume(volumeName string) error { + args := []string{"volume", "create", "--name", volumeName} + return Exec(args, true) +} + +func GetVolumeName(projectName string, projectID string) string { + return "garden-volume--" + projectName + "-" + projectID +} + +// TODO Use Docker SDK +func HasContainer(containerName string) bool { + args := []string{"inspect", containerName} + err := Exec(args, true) + if err != nil { + return false + } + return true +} diff --git a/garden-cli/gulpfile.ts b/garden-cli/gulpfile.ts new file mode 100644 index 0000000000..a5264078ce --- /dev/null +++ b/garden-cli/gulpfile.ts @@ -0,0 +1,21 @@ +/* + * 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 { join } from "path" +import { spawn } from "../support/support-util" + +const sources = join(__dirname, "**", "*.go") + +module.exports = (gulp) => { + gulp.task("build", () => spawn("go", ["build", "-o", join("build", "garden")], __dirname)) + gulp.task("watch", () => gulp.watch([sources, join(__dirname, "Dockerfile")], gulp.parallel("build"))) +} + +if (process.cwd() === __dirname) { + module.exports(require("gulp")) +} diff --git a/garden-cli/main.go b/garden-cli/main.go new file mode 100644 index 0000000000..32395a0e85 --- /dev/null +++ b/garden-cli/main.go @@ -0,0 +1,69 @@ +package main + +import ( + "log" + "os" + "os/exec" + "path/filepath" + "strings" +) + +func main() { + // find the project garden.yml + cwd, err := os.Getwd() + check(err) + + _, projectName := findProject(cwd) + + // get the git root and relative path to it (we mount the git root, so that git version checks work) + git, err := exec.LookPath("git") + if err != nil { + log.Fatal("Could not find git (Garden requires git to be installed)") + } + + _, err = exec.LookPath("kubectl") + if err != nil { + log.Fatal( + "Could not find kubectl " + + "(Garden requires a configured local Kubernetes cluster and for kubectl to be configured to access it)", + ) + } + + cmd := exec.Command(git, "rev-parse", "--show-toplevel") + cmd.Env = os.Environ() + gitRootBytes, err := cmd.Output() + if err != nil { + log.Fatal( + "Current directory is not in a git repository (Garden projects currently need to be inside a git repository)", + ) + } + gitRoot := strings.TrimSpace(string(gitRootBytes)) + + // run the command in the service container + relPath, err := filepath.Rel(strings.TrimSpace(gitRoot), cwd) + check(err) + + args := os.Args[1:] + watch := fsWatchEnabled(args) + + if watch { + err := runSyncContainer(projectName, gitRoot, relPath) + check(err) + } + + err = runGardenService(projectName, gitRoot, relPath, watch, args) + check(err) +} + +// FIXME: We need a proper way to check if the command requires file system watch. This is not it. +func fsWatchEnabled(args []string) bool { + if len(args) > 0 && args[0] == "dev" { + return true + } + for _, el := range args { + if el == "--watch" || el == "-w" || el == "hot-reload" { + return true + } + } + return false +} diff --git a/garden-cli/run.go b/garden-cli/run.go new file mode 100644 index 0000000000..2fbc1e26e5 --- /dev/null +++ b/garden-cli/run.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "log" + "path" + + "github.com/garden-io/garden/garden-cli/dockerutil" +) + +func runGardenService(projectName string, gitRoot string, relPath string, watch bool, args []string) error { + homeDir := getHomeDir() + gardenHomeDir := getGardenHomeDir() + projectID := getProjectID(gitRoot) + containerName := "garden-service--" + projectName + "-" + projectID + workingDir := path.Join("/project", relPath) + volumeName := dockerutil.GetVolumeName(projectName, projectID) + + // If the command requires a file system watch we mount the dedicated project volume, + // if not we just bind mount the project dir directly + var projectVolume string + if watch { + if !dockerutil.HasVolume(volumeName) { + log.Fatal("No volume found for project") + } + projectVolume = volumeName + } else { + projectVolume = gitRoot + } + + dockerArgs := append( + []string{ + "run", "-i", "--tty", "--rm", + // Give the container direct access to the host network. + "--net", "host", + // Mount docker socket and configuration directories. + "--volume", "/var/run/docker.sock:/var/run/docker.sock", + "--volume", fmt.Sprintf("%s/.docker:/root/.docker", homeDir), + "--volume", fmt.Sprintf("%s:/root/.garden", gardenHomeDir), + "--volume", fmt.Sprintf("%s/.kube:/root/.kube", homeDir), + // Mount the project directory, either as a bind mount or as a named volume + "--volume", fmt.Sprintf("%s:/project:delegated", projectVolume), + "--workdir", workingDir, + "--name", containerName, + // TODO: use particular version of garden-service container + "garden-service", + }, + args..., + ) + + return dockerutil.Exec(dockerArgs, false) +} diff --git a/garden-cli/sync.go b/garden-cli/sync.go new file mode 100644 index 0000000000..8f8106ecc6 --- /dev/null +++ b/garden-cli/sync.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + + "github.com/garden-io/garden/garden-cli/dockerutil" +) + +// TODO: Sync container life cycle management. At the moment, the sync runs in watch mode indefinitely. +func runSyncContainer(projectName string, gitRoot string, relPath string) error { + homeDir := getHomeDir() + projectID := getProjectID(gitRoot) + containerName := "garden-sync--" + projectName + "-" + projectID + volumeName := dockerutil.GetVolumeName(projectName, projectID) + + if !dockerutil.HasVolume(volumeName) { + dockerutil.MakeVolume(volumeName) + } + + if dockerutil.HasContainer(containerName) { + return nil + } + + dockerArgs := []string{ + "run", "-d", "--rm", + // Mount docker socket and configuration directories. + "--volume", "/var/run/docker.sock:/var/run/docker.sock", + "--volume", fmt.Sprintf("%s/.docker:/root/.docker", homeDir), + // Mount the project directory. + "--volume", fmt.Sprintf("%s:/host-mount:delegated", gitRoot), + // TODO Use delegated? + "--volume", fmt.Sprintf("%s:/project", volumeName), + "--name", containerName, + "garden-sync", + } + + return dockerutil.Exec(dockerArgs, false) +} diff --git a/garden-cli/util.go b/garden-cli/util.go new file mode 100644 index 0000000000..49b8363342 --- /dev/null +++ b/garden-cli/util.go @@ -0,0 +1,46 @@ +package main + +import ( + "math/rand" + "os" + "time" + + "github.com/mitchellh/go-homedir" +) + +// Use this for unexpected errors, like system errors that we have no sensible way of dealing with. +func check(err error) { + if err != nil { + panic(err) + } +} + +var letters = []rune("abcdefghijklmnopqrstuvwxyz1234567890") + +// Generate a random string of length n. +func randSeq(n int) string { + rand.Seed(time.Now().UnixNano()) + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} + +func int32Ptr(value int32) *int32 { + return &value +} + +// Returns the current user's home directory, as an absolute path. +func getHomeDir() string { + homeDir, err := homedir.Dir() + check(err) + homeDir, err = homedir.Expand(homeDir) + check(err) + return homeDir +} + +// Makes sure the given directory path exists. +func ensureDir(path string) { + os.MkdirAll(path, os.ModePerm) +} diff --git a/garden-service/Dockerfile b/garden-service/Dockerfile index 94ea7b3491..63650a7f1b 100644 --- a/garden-service/Dockerfile +++ b/garden-service/Dockerfile @@ -32,6 +32,7 @@ RUN apk add --no-cache --virtual .deps \ && apk del .deps # garden code +ADD bin /garden/bin ADD build /garden/build ADD static /garden/static diff --git a/garden-service/bin/add-version-files.ts b/garden-service/bin/add-version-files.ts new file mode 100755 index 0000000000..852c5c7cee --- /dev/null +++ b/garden-service/bin/add-version-files.ts @@ -0,0 +1,34 @@ +#!/usr/bin/env ts-node + +import { GitHandler } from "../src/vcs/git" +import { Garden } from "../src/garden" +import { Logger } from "../src/logger/logger" +import { LogLevel } from "../src/logger/log-node" +import { resolve } from "path" +import * as Bluebird from "bluebird" +import { writeFile } from "fs-extra" + +// make sure logger is initialized +try { + Logger.initialize({ level: LogLevel.info }) +} catch (_) { } + +async function addVersionFiles() { + const staticPath = resolve(__dirname, "..", "static") + const garden = await Garden.factory(staticPath) + + const modules = await garden.getModules() + + return Bluebird.map(modules, async (module) => { + const path = module.path + const versionFilePath = resolve(path, ".garden-version") + + const vcsHandler = new GitHandler(path) + const treeVersion = await vcsHandler.getTreeVersion(path) + + return writeFile(versionFilePath, JSON.stringify(treeVersion, null, 4) + "\n") + }) +} + +addVersionFiles() + .catch(err => { throw err }) diff --git a/garden-service/bin/exec b/garden-service/bin/exec new file mode 100755 index 0000000000..7040497c11 --- /dev/null +++ b/garden-service/bin/exec @@ -0,0 +1,8 @@ +#!/bin/bash + +args=( $@ ) +working_dir=${args[0]} +garden_args=${args[@]:1} + +cd ${working_dir} +garden "${garden_args}" diff --git a/garden-service/bin/generate-docs.ts b/garden-service/bin/generate-docs.ts new file mode 100755 index 0000000000..25205540bf --- /dev/null +++ b/garden-service/bin/generate-docs.ts @@ -0,0 +1,6 @@ +#!/usr/bin/env ts-node + +import { generateDocs } from "../src/docs/generate" +import { resolve } from "path" + +generateDocs(resolve(__dirname, "..", "..", "docs")) diff --git a/garden-service/gulpfile.ts b/garden-service/gulpfile.ts index d0f082e21a..e50f6db139 100644 --- a/garden-service/gulpfile.ts +++ b/garden-service/gulpfile.ts @@ -8,29 +8,11 @@ import { spawn as _spawn, - ChildProcess, } from "child_process" -import { - ensureDir, - pathExists, - readFile, - remove, - writeFile, -} from "fs-extra" -import * as handlebars from "handlebars" -import { resolve } from "path" -import { generateDocs } from "./src/docs/generate" -import { getUrlChecksum } from "../support/support-util" -import * as Bluebird from "bluebird" -import { GitHandler } from "./src/vcs/git" -import { Garden } from "./src/garden" -import { Logger } from "./src/logger/logger" -import { LogLevel } from "./src/logger/log-node" -import execa = require("execa") - -const gulp = require("gulp") +import { resolve, join } from "path" +import { spawn } from "../support/support-util" + const cached = require("gulp-cached") -const packageJson = require("./package.json") const pegjs = require("gulp-pegjs") const sourcemaps = require("gulp-sourcemaps") const gulpTslint = require("gulp-tslint") @@ -39,7 +21,7 @@ const ts = require("gulp-typescript") const tsConfigFilename = "tsconfig.build.json" const tsConfigPath = resolve(__dirname, tsConfigFilename) -const tsProject = ts.createProject(tsConfigFilename, { +const tsProject = ts.createProject(tsConfigPath, { declaration: true, }) @@ -47,199 +29,84 @@ const tsSources = resolve(__dirname, "src", "**", "*.ts") const testTsSources = resolve(__dirname, "test", "**", "*.ts") const pegjsSources = resolve(__dirname, "src", "*.pegjs") -const tmpDir = resolve(__dirname, "..", "tmp") -const binPath = (name: string) => resolve(__dirname, "node_modules", ".bin", name) +const npmBinPath = (name: string) => resolve(__dirname, "node_modules", ".bin", name) const destDir = resolve(__dirname, "build") +const binDir = resolve(__dirname, "bin") -const children: ChildProcess[] = [] - -process.env.FORCE_COLOR = "true" -process.env.TS_NODE_CACHE = "0" - -function spawn(cmd, args, cb) { - const child = _spawn(cmd, args, { stdio: "pipe", shell: true, env: process.env }) - children.push(child) - - const output: string[] = [] - child.stdout.on("data", (data) => output.push(data.toString())) - child.stderr.on("data", (data) => output.push(data.toString())) - - child.on("exit", (code) => { - if (code !== 0) { - console.log(output.join("")) - die() - } - cb() - }) - - return child -} +module.exports = (gulp) => { + gulp.task("add-version-files", () => spawn(join(binDir, "add-version-files.ts"), [])) -function die() { - for (const child of children) { - !child.killed && child.kill() - } - process.exit(1) -} + gulp.task("build-container", () => spawn("docker", ["build", "-t", "garden-service", __dirname])) -process.on("SIGINT", die) -process.on("SIGTERM", die) + gulp.task("generate-docs", () => spawn(join(binDir, "generate-docs.ts"), [])) -// make sure logger is initialized -try { - Logger.initialize({ level: LogLevel.info }) -} catch (_) { } + gulp.task("mocha", () => spawn(npmBinPath("nyc"), [npmBinPath("mocha")], __dirname)) -gulp.task("add-version-files", async () => { - const staticPath = resolve(__dirname, "static") - const garden = await Garden.factory(staticPath) + gulp.task("pegjs", () => + gulp.src(pegjsSources) + .pipe(pegjs({ format: "commonjs" })) + .pipe(gulp.dest(destDir)), + ) - const modules = await garden.getModules() + gulp.task("pegjs-watch", () => + gulp.watch(pegjsSources, gulp.parallel("pegjs")), + ) - return Bluebird.map(modules, async (module) => { - const path = module.path - const versionFilePath = resolve(path, ".garden-version") + gulp.task("tsc", () => + tsProject.src() + .pipe(sourcemaps.init()) + .pipe(tsProject(ts.reporter.fullReporter(true))) + .pipe(sourcemaps.write()) + .pipe(gulp.dest(destDir)), + ) - const vcsHandler = new GitHandler(path) - const treeVersion = await vcsHandler.getTreeVersion(path) + gulp.task("tsfmt", () => spawn(npmBinPath("tsfmt"), ["--verify"])) - await writeFile(versionFilePath, JSON.stringify(treeVersion, null, 4) + "\n") - }) -}) + gulp.task("tslint", () => + gulp.src(tsSources) + .pipe(cached("tslint")) + .pipe(gulpTslint({ + program: tslint.Linter.createProgram(tsConfigPath), + formatter: "verbose", + })) + .pipe(gulpTslint.report()), + ) -gulp.task("build-container", (cb) => - spawn("docker", ["build", "-t", "garden-service", __dirname], cb), -) + gulp.task("tslint-tests", () => + gulp.src(testTsSources) + .pipe(cached("tslint-tests")) + .pipe(gulpTslint({ + formatter: "verbose", + })) + .pipe(gulpTslint.report()), + ) -gulp.task("generate-docs", (cb) => { - generateDocs(resolve(__dirname, "..", "docs")) - cb() -}) + gulp.task("watch-code", () => { + const verify = (path) => { + try { + _spawn(npmBinPath("tsfmt"), ["--verify", path], { stdio: "inherit" }) + } catch (_) { } + } -gulp.task("mocha", (cb) => - spawn(binPath("nyc"), [binPath("mocha")], cb), -) - -gulp.task("pegjs", () => - gulp.src(pegjsSources) - .pipe(pegjs({ format: "commonjs" })) - .pipe(gulp.dest(destDir)), -) - -gulp.task("pegjs-watch", () => - gulp.watch(pegjsSources, gulp.parallel("pegjs")), -) - -gulp.task("tsc", () => - tsProject.src() - .pipe(sourcemaps.init()) - .pipe(tsProject(ts.reporter.fullReporter(true))) - .pipe(sourcemaps.write()) - .pipe(gulp.dest(destDir)), -) - -gulp.task("tsfmt", (cb) => { - spawn(binPath("tsfmt"), ["--verify"], cb) -}) + const task = gulp.series( + gulp.parallel("generate-docs", "tsc", "tslint", "tslint-tests"), + "build-container", + ) -gulp.task("tslint", () => - gulp.src(tsSources) - .pipe(cached("tslint")) - .pipe(gulpTslint({ - program: tslint.Linter.createProgram(tsConfigPath), - formatter: "verbose", - })) - .pipe(gulpTslint.report()), -) - -gulp.task("tslint-tests", () => - gulp.src(testTsSources) - .pipe(cached("tslint-tests")) - .pipe(gulpTslint({ - formatter: "verbose", - })) - .pipe(gulpTslint.report()), -) - -/** - * Updates our Homebrew tap with the current released package version. Should be run after relasing to NPM. - */ -gulp.task("update-brew", async () => { - // clone the homebrew-garden tap repo - await ensureDir(tmpDir) - const brewRepoDir = resolve(tmpDir, "homebrew-garden") - if (await pathExists(brewRepoDir)) { - await remove(brewRepoDir) - } - await execa("git", ["clone", "git@github.com:garden-io/homebrew-garden.git"], { cwd: tmpDir }) - - // read the existing formula - const formulaDir = resolve(brewRepoDir, "Formula") - await ensureDir(formulaDir) - const formulaPath = resolve(formulaDir, "garden-cli.rb") - const existingFormula = await pathExists(formulaPath) ? (await readFile(formulaPath)).toString() : "" - - // compile the formula handlebars template - const templatePath = resolve(__dirname, "support", "homebrew-formula.rb") - const templateString = (await readFile(templatePath)).toString() - const template = handlebars.compile(templateString) - - // get the metadata from npm - const metadataJson = await execa.stdout("npm", ["view", "garden-cli", "--json"]) - const metadata = JSON.parse(metadataJson) - const version = metadata["dist-tags"].latest - const tarballUrl = metadata.dist.tarball - const sha256 = metadata.dist.shasum.length === 64 ? metadata.dist.shasum : await getUrlChecksum(tarballUrl, "sha256") - - const formula = template({ - version, - homepage: metadata.homepage || packageJson.homepage, - description: metadata.description, - tarballUrl, - sha256, + return gulp.watch([tsSources, testTsSources], task) + .on("add", verify) + .on("change", verify) }) - if (formula === existingFormula) { - console.log("No changes to formula") - } else { - await writeFile(formulaPath, formula) - - // check if the formula is OK - await execa("brew", ["audit", formulaPath]) - - for (const args of [ - ["add", formulaPath], - ["commit", "-m", `update to ${version}`], - ["tag", version], - ["push"], - ["push", "--tags"], - ]) { - await execa("git", args, { cwd: brewRepoDir }) - } - } -}) - -gulp.task("watch-code", () => { - const verify = (path) => { - try { - _spawn(binPath("tsfmt"), ["--verify", path], { stdio: "inherit" }) - } catch (_) { } - } - - const task = gulp.series( - gulp.parallel("generate-docs", "tsc", "tslint", "tslint-tests"), + gulp.task("build", gulp.series( + gulp.parallel("add-version-files", "generate-docs", "pegjs", "tsc"), "build-container", - ) - - return gulp.watch([tsSources, testTsSources], task) - .on("add", verify) - .on("change", verify) -}) + )) + gulp.task("test", gulp.parallel("build", "mocha")) + gulp.task("watch", gulp.parallel("pegjs-watch", "watch-code")) + gulp.task("default", gulp.series("watch")) +} -gulp.task("build", gulp.series( - gulp.parallel("add-version-files", "generate-docs", "pegjs", "tsc"), - "build-container", -)) -gulp.task("test", gulp.parallel("build", "mocha")) -gulp.task("watch", gulp.parallel("pegjs-watch", "watch-code")) -gulp.task("default", gulp.series("watch")) +if (process.cwd() === __dirname) { + module.exports(require("gulp")) +} diff --git a/garden-service/src/config/config-context.ts b/garden-service/src/config/config-context.ts index 0782e734df..b4bef78de2 100644 --- a/garden-service/src/config/config-context.ts +++ b/garden-service/src/config/config-context.ts @@ -126,7 +126,6 @@ export abstract class ConfigContext { value, path, fullPath, - context, }, ) } diff --git a/garden-service/test/mocha.opts b/garden-service/test/mocha.opts index 247b746a0b..c5789bb362 100644 --- a/garden-service/test/mocha.opts +++ b/garden-service/test/mocha.opts @@ -2,6 +2,6 @@ --watch-dirs test/,src/ --watch-extensions ts,json,pegjs,yml --reporter spec ---timeout 5000 +--timeout 10000 test/setup.ts test/**/*.ts diff --git a/garden-sync/Dockerfile b/garden-sync/Dockerfile new file mode 100644 index 0000000000..4e4fb94187 --- /dev/null +++ b/garden-sync/Dockerfile @@ -0,0 +1,6 @@ +FROM alpine:3.8 + +# system dependencies +RUN apk add --no-cache unison + +CMD unison -repeat watch -force host-mount host-mount project diff --git a/garden-sync/gulpfile.ts b/garden-sync/gulpfile.ts new file mode 100644 index 0000000000..ad65ba075b --- /dev/null +++ b/garden-sync/gulpfile.ts @@ -0,0 +1,18 @@ +/* + * 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 { join } from "path" +import { spawn } from "../support/support-util" + +module.exports = (gulp) => { + gulp.task("build-container", () => spawn("docker", ["build", "-t", "garden-sync", join(__dirname)])) +} + +if (process.cwd() === __dirname) { + module.exports(require("gulp")) +} diff --git a/gulpfile.ts b/gulpfile.ts index ab17d880a4..3052c0cde1 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -6,16 +6,49 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { join, resolve } from "path" +import * as execa from "execa" +import { writeFile, readFile, ensureDir, pathExists, remove } from "fs-extra" +import { getUrlChecksum } from "./support/support-util" +import * as handlebars from "handlebars" +import { isString, clone, extend } from "lodash" + const gulp = require("gulp") const checkLicense = require("gulp-license-check") const tsSources = ["garden-service/src/**/*.ts"] const pegjsSources = "src/*.pegjs" - const licenseHeaderPath = "support/license-header.txt" +const modulePaths = ["garden-cli", "garden-service", "garden-sync"] +const tmpDir = resolve(__dirname, "tmp") process.env.FORCE_COLOR = "true" +// import all tasks from nested modules and put a prefix on their name (e.g. "build" -> "garden-service:build") +modulePaths.forEach(m => { + // override gulp methods to automatically prefix task names + const prefix = (name) => `${m}:${name}` + const wrapTask = (nameOrFunction) => isString(nameOrFunction) ? prefix(nameOrFunction) : nameOrFunction + + const gulpfilePath = join(__dirname, m, "gulpfile.ts") + const tasks = require(gulpfilePath) + + const _gulp = clone(gulp) + extend(_gulp, { + ...gulp, + task: (name, ...args) => gulp.task.bind(gulp)(prefix(name), ...args), + series: (...args) => gulp.series.bind(gulp)(...args.map(wrapTask)), + parallel: (...args) => gulp.parallel.bind(gulp)(...args.map(wrapTask)), + watch: (sources, t) => gulp.watch.bind(gulp)(sources, wrapTask(t)), + }) + tasks(_gulp) +}) + +gulp.task("build", gulp.parallel("garden-cli:build", "garden-service:build", "garden-sync:build-container")) +gulp.task("generate-docs", gulp.parallel("garden-service:generate-docs")) +gulp.task("test", gulp.parallel("garden-service:test")) +gulp.task("watch", gulp.parallel("garden-cli:watch", "garden-service:watch")) + gulp.task("check-licenses", () => gulp.src([...tsSources, pegjsSources]) .pipe(checkLicense({ @@ -25,3 +58,65 @@ gulp.task("check-licenses", () => logError: true, })), ) + +/** + * Updates our Homebrew tap with the current released package version. Should be run after relasing to NPM. + */ +gulp.task("update-brew", async () => { + // clone the homebrew-garden tap repo + const packageJson = require(join(__dirname, "garden-service", "/package.json")) + + await ensureDir(tmpDir) + const brewRepoDir = resolve(tmpDir, "homebrew-garden") + if (await pathExists(brewRepoDir)) { + await remove(brewRepoDir) + } + await execa("git", ["clone", "git@github.com:garden-io/homebrew-garden.git"], { cwd: tmpDir }) + + // read the existing formula + const formulaDir = resolve(brewRepoDir, "Formula") + await ensureDir(formulaDir) + const formulaPath = resolve(formulaDir, "garden-cli.rb") + const existingFormula = await pathExists(formulaPath) ? (await readFile(formulaPath)).toString() : "" + + // compile the formula handlebars template + const templatePath = resolve(__dirname, "support", "homebrew-formula.rb") + const templateString = (await readFile(templatePath)).toString() + const template = handlebars.compile(templateString) + + // get the metadata from npm + const metadataJson = await execa.stdout("npm", ["view", "garden-cli", "--json"]) + const metadata = JSON.parse(metadataJson) + const version = metadata["dist-tags"].latest + const tarballUrl = metadata.dist.tarball + const sha256 = metadata.dist.shasum.length === 64 + ? metadata.dist.shasum + : await getUrlChecksum(tarballUrl, "sha256") + + const formula = template({ + version, + homepage: metadata.homepage || packageJson.homepage, + description: metadata.description, + tarballUrl, + sha256, + }) + + if (formula === existingFormula) { + console.log("No changes to formula") + } else { + await writeFile(formulaPath, formula) + + // check if the formula is OK + await execa("brew", ["audit", formulaPath]) + + for (const args of [ + ["add", formulaPath], + ["commit", "-m", `update to ${version}`], + ["tag", version], + ["push"], + ["push", "--tags"], + ]) { + await execa("git", args, { cwd: brewRepoDir }) + } + } +}) diff --git a/package-lock.json b/package-lock.json index ae26fbaeb3..f29403ea78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -401,6 +401,17 @@ "validate-npm-package-name": "^3.0.0" }, "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", @@ -454,6 +465,19 @@ "cmd-shim": "^2.0.2", "fs-extra": "^6.0.1", "npmlog": "^4.1.2" + }, + "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } } }, "@lerna/diff": { @@ -548,6 +572,19 @@ "dedent": "^0.7.0", "fs-extra": "^6.0.1", "p-map-series": "^1.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } } }, "@lerna/init": { @@ -561,6 +598,19 @@ "fs-extra": "^6.0.1", "p-map": "^1.2.0", "write-json-file": "^2.3.0" + }, + "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } } }, "@lerna/link": { @@ -644,6 +694,19 @@ "npmlog": "^4.1.2", "signal-exit": "^3.0.2", "write-pkg": "^3.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } } }, "@lerna/npm-publish": { @@ -659,6 +722,19 @@ "fs-extra": "^6.0.1", "npmlog": "^4.1.2", "p-map": "^1.2.0" + }, + "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } } }, "@lerna/npm-run-script": { @@ -807,6 +883,17 @@ "semver": "^5.5.0" }, "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", @@ -824,6 +911,19 @@ "fs-extra": "^6.0.1", "npmlog": "^4.1.2", "read-cmd-shim": "^1.0.1" + }, + "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } } }, "@lerna/rimraf-dir": { @@ -888,6 +988,17 @@ "read-pkg": "^3.0.0" }, "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -951,6 +1062,19 @@ "p-finally": "^1.0.0", "p-map": "^1.2.0", "p-map-series": "^1.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } } }, "@lerna/validation-error": { @@ -1046,6 +1170,19 @@ "npm-package-arg": "^6.0.0", "npmlog": "^4.1.2", "semver": "^5.5.0" + }, + "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } } }, "@lerna/filter-packages": { @@ -1266,6 +1403,27 @@ "@types/node": "*" } }, + "@types/fs-extra": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.4.tgz", + "integrity": "sha512-DsknoBvD8s+RFfSGjmERJ7ZOP1HI0UZRA3FSI+Zakhrc/Gy26YQsLI+m5V5DHxroHRJqCDLKJp7Hixn8zyaF7g==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/handlebars": { + "version": "4.0.39", + "resolved": "https://registry.npmjs.org/@types/handlebars/-/handlebars-4.0.39.tgz", + "integrity": "sha512-vjaS7Q0dVqFp85QhyPSZqDKnTTCemcSHNHFvDdalO1s0Ifz5KuE64jQD5xoUkfdWwF4WpqdJEl7LsWH8rzhKJA==", + "dev": true + }, + "@types/lodash": { + "version": "4.14.117", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.117.tgz", + "integrity": "sha512-xyf2m6tRbz8qQKcxYZa7PA4SllYcay+eh25DN3jmNYY6gSTL7Htc/bttVdkqj2wfJGbeWlQiX8pIyJpKU+tubw==", + "dev": true + }, "@types/node": { "version": "10.7.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.7.1.tgz", @@ -1315,23 +1473,6 @@ "json-schema-traverse": "^0.3.0" } }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, "ansi-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", @@ -2048,26 +2189,6 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "optional": true, - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - }, - "dependencies": { - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true, - "optional": true - } - } - }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -3885,9 +4006,9 @@ "dev": true }, "fs-extra": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", - "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz", + "integrity": "sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -5368,30 +5489,24 @@ } }, "handlebars": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", - "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz", + "integrity": "sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==", "dev": true, "requires": { - "async": "^1.4.0", + "async": "^2.5.0", "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" }, "dependencies": { "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "lodash": "^4.17.10" } } } @@ -6310,9 +6425,9 @@ } }, "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, "lodash._basecopy": { @@ -6528,12 +6643,6 @@ "integrity": "sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=", "dev": true }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, "loud-rejection": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", @@ -8403,16 +8512,6 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "optional": true, - "requires": { - "align-text": "^0.1.1" - } - }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", @@ -8994,6 +9093,14 @@ "lodash": "4.17.10", "path": "0.12.7", "source-map-support": "^0.5.7" + }, + "dependencies": { + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + } } }, "snyk-nuget-plugin": { @@ -9854,79 +9961,25 @@ } }, "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", + "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", "dev": true, "optional": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "commander": "~2.17.1", + "source-map": "~0.6.1" }, "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", "dev": true, "optional": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "optional": true, - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "optional": true - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true, - "optional": true - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true, - "optional": true - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } } } }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, "uid-number": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", diff --git a/package.json b/package.json index 59b032fd9b..b9d806515a 100644 --- a/package.json +++ b/package.json @@ -17,13 +17,19 @@ "@lerna/version": "^3.0.2", "@types/bluebird": "^3.5.23", "@types/execa": "^0.9.0", + "@types/fs-extra": "^5.0.4", + "@types/handlebars": "^4.0.39", + "@types/lodash": "^4.14.117", "@types/node": "^10.7.0", "axios": "^0.18.0", + "fs-extra": "^7.0.0", "gulp": "^4.0.0", "gulp-cached": "^1.1.1", "gulp-license-check": "^1.2.1", + "handlebars": "^4.0.12", "husky": "^0.15.0-rc.13", "lerna": "^3.0.4", + "lodash": "^4.17.11", "shx": "^0.3.2", "snyk": "^1.90.2", "ts-node": "^7.0.1", @@ -34,26 +40,28 @@ "typescript-formatter": "^7.2.2" }, "scripts": { - "build": "npm run clean && lerna run build", + "build": "npm run clean && gulp build", "check-docs": "git diff --quiet HEAD -- docs/ || (echo 'generated docs are not up-to-date!' && exit 1)", "check-licenses": "gulp check-licenses", "check-package-lock": "git diff --quiet HEAD -- package-lock.json || (echo 'package-lock.json is dirty!' && exit 1)", "clean": "lerna run clean && git clean -X -f", - "dist": "lerna run dist", + "dist": "gulp dist", "fix-format": "node_modules/.bin/tslint -p . --fix && node_modules/.bin/tsfmt -r", - "generate-docs": "lerna run generate-docs", + "generate-docs": "gulp generate-docs", "integ": "lerna run integ", "lint": "tslint -p . && tsfmt --verify && gulp check-licenses", "prepublishOnly": "npm run dist", "preversion": "npm test", "release": "./bin/publish", "release-canary": "./bin/publish-canary", - "test": "npm run clean && lerna run test" + "test": "npm run lint && npm run clean && gulp test", + "ci-build": "gulp garden-service:build", + "ci-test": "gulp test" }, "husky": { "hooks": { "commit-msg": "commitlint -E GIT_PARAMS", - "pre-push": "npm run check-package-lock && npm run lint && npm test && npm run integ" + "pre-push": "npm run check-package-lock && npm test && npm run integ" } }, "snyk": true, diff --git a/support/support-util.ts b/support/support-util.ts index 183321c91a..bcc007ec66 100644 --- a/support/support-util.ts +++ b/support/support-util.ts @@ -15,23 +15,23 @@ import { createHash } from "crypto" const children: ChildProcess[] = [] -export function spawn(cmd, args, cb) { - const child = _spawn(cmd, args, { stdio: "pipe", shell: true, env: process.env }) +export async function spawn(cmd: string, args: string[], cwd?: string) { + const child = _spawn(cmd, args, { stdio: "pipe", shell: true, env: process.env, cwd }) children.push(child) const output: string[] = [] child.stdout.on("data", (data) => output.push(data.toString())) child.stderr.on("data", (data) => output.push(data.toString())) - child.on("exit", (code) => { - if (code !== 0) { - console.log(output.join("")) - die() - } - cb() + return new Promise((resolve, reject) => { + child.on("exit", (code) => { + if (code !== 0) { + console.log(output.join("")) + reject(output) + } + resolve() + }) }) - - return child } function die() {