Skip to content

Commit

Permalink
improvement(core): detect rsync and ensure the version is recent enough
Browse files Browse the repository at this point in the history
Closes #1493
  • Loading branch information
edvald authored and eysi09 committed Jan 20, 2020
1 parent 043afec commit f3df17d
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ jobs:
<<: *node-config
steps:
- checkout
- run: sudo apt-get update && sudo apt-get -y install parallel
- run: sudo apt-get update && sudo apt-get -y install parallel rsync
- npm_install
- *attach-workspace
- run:
Expand Down
55 changes: 54 additions & 1 deletion garden-service/src/build-dir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,87 @@
*/

import { map as bluebirdMap } from "bluebird"
import semver from "semver"
import normalize = require("normalize-path")
import { isAbsolute, join, parse, resolve, sep, relative } from "path"
import { emptyDir, ensureDir } from "fs-extra"
import { ConfigurationError } from "./exceptions"
import { ConfigurationError, RuntimeError } from "./exceptions"
import { FileCopySpec, Module, getModuleKey } from "./types/module"
import { normalizeLocalRsyncPath } from "./util/fs"
import { LogEntry } from "./logger/log-entry"
import { ModuleConfig } from "./config/module"
import { ConfigGraph } from "./config-graph"
import { exec } from "./util/util"
import { LogLevel } from "./logger/log-node"
import { deline } from "./util/string"

const minRsyncVersion = "3.1.0"
const versionRegex = /rsync version ([\d\.]+) /

// FIXME: We don't want to keep special casing this module type so we need to think
// of a better way around this.
function isLocalExecModule(moduleConfig: ModuleConfig) {
return moduleConfig.type === "exec" && moduleConfig.spec.local
}

const versionDetectFailure = new RuntimeError(
deline`
Could not detect rsync version.
Please make sure rsync version ${minRsyncVersion} or later is installed and on your PATH.
`,
{}
)

// Lazily construct a directory of modules inside which all build steps are performed.

export class BuildDir {
constructor(private projectRoot: string, public buildDirPath: string, public buildMetadataDirPath: string) {}

static async factory(projectRoot: string, gardenDirPath: string) {
// Make sure rsync is installed and is recent enough
let version: string | undefined = undefined

try {
const versionOutput = (await exec("rsync", ["--version"])).stdout
version = versionOutput.split("\n")[0].match(versionRegex)?.[1]
} catch (error) {
throw new RuntimeError(
deline`
Could not find rsync binary.
Please make sure rsync (version ${minRsyncVersion} or later) is installed and on your PATH.
`,
{ error }
)
}

if (!version) {
throw versionDetectFailure
}

let versionGte = true

try {
versionGte = semver.gte(version, minRsyncVersion)
} catch (_) {
throw versionDetectFailure
}

if (!versionGte) {
throw new RuntimeError(
deline`
Found rsync binary but the version is too old (${version}).
Please install version ${minRsyncVersion} or later.
`,
{ version }
)
}

// Make sure build directories exist
const buildDirPath = join(gardenDirPath, "build")
const buildMetadataDirPath = join(gardenDirPath, "build-metadata")
await ensureDir(buildDirPath)
await ensureDir(buildMetadataDirPath)

return new BuildDir(projectRoot, buildDirPath, buildMetadataDirPath)
}

Expand Down
14 changes: 14 additions & 0 deletions garden-service/test/data/dummy-rsync/invalid/rsync
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh

echo "rsync version ABCDEF protocol version 31
Copyright (C) 1996-2018 by Andrew Tridgell, Wayne Davison, and others.
Web site: http://rsync.samba.org/
Capabilities:
64-bit files, 64-bit inums, 64-bit timestamps, 64-bit long ints,
socketpairs, hardlinks, symlinks, IPv6, batchfiles, inplace,
append, ACLs, xattrs, iconv, symtimes, no prealloc, file-flags
rsync comes with ABSOLUTELY NO WARRANTY. This is free software, and you
are welcome to redistribute it under certain conditions. See the GNU
General Public Licence for details.
"
14 changes: 14 additions & 0 deletions garden-service/test/data/dummy-rsync/new-version/rsync
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh

echo "rsync version 3.1.3 protocol version 31
Copyright (C) 1996-2018 by Andrew Tridgell, Wayne Davison, and others.
Web site: http://rsync.samba.org/
Capabilities:
64-bit files, 64-bit inums, 64-bit timestamps, 64-bit long ints,
socketpairs, hardlinks, symlinks, IPv6, batchfiles, inplace,
append, ACLs, xattrs, iconv, symtimes, no prealloc, file-flags
rsync comes with ABSOLUTELY NO WARRANTY. This is free software, and you
are welcome to redistribute it under certain conditions. See the GNU
General Public Licence for details.
"
14 changes: 14 additions & 0 deletions garden-service/test/data/dummy-rsync/old-version/rsync
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh

echo "rsync version 2.1.2 protocol version 31
Copyright (C) 1996-2018 by Andrew Tridgell, Wayne Davison, and others.
Web site: http://rsync.samba.org/
Capabilities:
64-bit files, 64-bit inums, 64-bit timestamps, 64-bit long ints,
socketpairs, hardlinks, symlinks, IPv6, batchfiles, inplace,
append, ACLs, xattrs, iconv, symtimes, no prealloc, file-flags
rsync comes with ABSOLUTELY NO WARRANTY. This is free software, and you
are welcome to redistribute it under certain conditions. See the GNU
General Public Licence for details.
"
56 changes: 55 additions & 1 deletion garden-service/test/unit/src/build-dir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { join } from "path"
import { pathExists, readdir, createFile } from "fs-extra"
import { expect } from "chai"
import { BuildTask } from "../../../src/tasks/build"
import { makeTestGarden, dataDir } from "../../helpers"
import { makeTestGarden, dataDir, expectError, getDataDir } from "../../helpers"
import { getConfigFilePath } from "../../../src/util/fs"
import { Garden } from "../../../src/garden"
import { BuildDir } from "../../../src/build-dir"

/*
Module dependency diagram for build-dir test project
Expand Down Expand Up @@ -50,6 +51,59 @@ describe("BuildDir", () => {
expect(await pathExists(buildPath)).to.eql(true)
})

it("should throw if rsync is not on PATH", async () => {
const orgPath = process.env.PATH

try {
process.env.PATH = ""
await expectError(
() => BuildDir.factory(garden.projectRoot, garden.gardenDirPath),
(err) =>
expect(err.message).to.equal(
"Could not find rsync binary. Please make sure rsync (version 3.1.0 or later) is installed " +
"and on your PATH."
)
)
} finally {
process.env.PATH = orgPath
}
})

it("should throw if rsync is too old", async () => {
const orgPath = process.env.PATH

try {
process.env.PATH = getDataDir("dummy-rsync", "old-version")
await expectError(
() => BuildDir.factory(garden.projectRoot, garden.gardenDirPath),
(err) =>
expect(err.message).to.equal(
"Found rsync binary but the version is too old (2.1.2). Please install version 3.1.0 or later."
)
)
} finally {
process.env.PATH = orgPath
}
})

it("should throw if rsync returns invalid version", async () => {
const orgPath = process.env.PATH

try {
process.env.PATH = getDataDir("dummy-rsync", "invalid")
await expectError(
() => BuildDir.factory(garden.projectRoot, garden.gardenDirPath),
(err) =>
expect(err.message).to.equal(
"Could not detect rsync version. Please make sure rsync version 3.1.0 or later is installed " +
"and on your PATH."
)
)
} finally {
process.env.PATH = orgPath
}
})

describe("syncFromSrc", () => {
it("should sync sources to the build dir", async () => {
const graph = await garden.getConfigGraph(garden.log)
Expand Down

0 comments on commit f3df17d

Please sign in to comment.