Skip to content

Commit

Permalink
fix(vcs): fixed path handling for modified files
Browse files Browse the repository at this point in the history
This fixes an issue with the new version hash calculation logic where
tests would not be re-run even if their underlying module sources
changed.

Because the relative paths being used were relativized from different
starting directories (git root VS module root), the modified file paths
weren't matching with the module file paths.
  • Loading branch information
thsig committed Apr 11, 2019
1 parent 99b21c9 commit ec82c22
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 55 deletions.
52 changes: 14 additions & 38 deletions garden-service/package-lock.json

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

3 changes: 2 additions & 1 deletion garden-service/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { EventEmitter2 } from "eventemitter2"
import { TaskResult } from "./task-graph"
import { ModuleVersion } from "./vcs/vcs"
import { LogEntry } from "./logger/log-entry"
import { isObject } from "util"

/**
* This simple class serves as the central event bus for a Garden instance. Its function
Expand All @@ -27,7 +28,7 @@ export class EventBus extends EventEmitter2 {
}

emit<T extends EventName>(name: T, payload: Events[T]) {
this.log.silly(`Emit event '${name}' with payload: ${payload}`)
this.log.silly(`Emit event '${name}' with payload: ${isObject(payload) ? JSON.stringify(payload) : payload}`)
return super.emit(name, payload)
}

Expand Down
29 changes: 21 additions & 8 deletions garden-service/src/vcs/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import execa = require("execa")
import * as execa from "execa"
import { join, resolve } from "path"
import { ensureDir, pathExists } from "fs-extra"

import { VcsHandler, RemoteSourceParams } from "./vcs"
import { VcsHandler, RemoteSourceParams, VcsFile } from "./vcs"
import { ConfigurationError, RuntimeError } from "../exceptions"
import * as Bluebird from "bluebird"
import { matchGlobs } from "../util/fs"
import { deline } from "../util/string"

export function getCommitIdFromRefList(refList: string[]): string {
try {
Expand All @@ -28,8 +29,9 @@ export function parseGitUrl(url: string) {
const parsed = { repositoryUrl: parts[0], hash: parts[1] }
if (!parsed.hash) {
throw new ConfigurationError(
"Repository URLs must contain a hash part pointing to a specific branch or tag" +
" (e.g. https://github.com/org/repo.git#master)",
deline`
Repository URLs must contain a hash part pointing to a specific branch or tag
(e.g. https://github.com/org/repo.git#master)`,
{ repositoryUrl: url },
)
}
Expand Down Expand Up @@ -64,13 +66,23 @@ export class GitHandler extends VcsHandler {
}
}

async getFiles(path: string, include?: string[]) {
async getFiles(path: string, include?: string[]): Promise<VcsFile[]> {
const git = this.gitCli(path)

let lines: string[] = []
let ignored: string[] = []

/**
* TODO: Replace the relative path handling in this function with a generic convertible path object
* once that's been implemented.
*/
const gitRoot = (await git("rev-parse", "--show-toplevel"))[0]

try {
/**
* We need to exclude .garden to avoid errors when path is the project root. This happens e.g. for modules
* whose config is colocated with the project config, and that don't specify include paths/patterns.
*/
lines = await git("ls-files", "-s", "--other", "--exclude=.garden", path)
ignored = await git("ls-files", "--ignored", "--exclude-per-directory=.gardenignore", path)
} catch (err) {
Expand All @@ -90,15 +102,16 @@ export class GitHandler extends VcsHandler {
}
})

const modified = new Set(await this.getModifiedFiles(git, path))
const modifiedArr = ((await this.getModifiedFiles(git, path)) || [])
.map(modifiedRelPath => resolve(gitRoot, modifiedRelPath))
const modified = new Set(modifiedArr)
const filtered = files
.filter(f => !include || matchGlobs(f.path, include))
.filter(f => !ignored.includes(f.path))

return Bluebird.map(filtered, async (f) => {
const resolvedPath = resolve(path, f.path)

if (!f.hash || modified.has(f.path)) {
if (!f.hash || modified.has(resolvedPath)) {
// If we can't compute the hash, i.e. the file is gone, we filter it out below
let hash = ""
try {
Expand Down
44 changes: 36 additions & 8 deletions garden-service/test/unit/src/vcs/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,42 @@ describe("GitHandler", () => {
])
})

it("should return untracked files as absolute paths with hash", async () => {
await createFile(join(tmpPath, "foo.txt"))
const hash = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"

expect(await handler.getFiles(tmpPath)).to.eql([
{ path: resolve(tmpPath, "foo.txt"), hash },
])
})
const dirContexts = [
{ ctx: "when called from repo root", pathFn: (tp) => tp },
{ ctx: "when called from project root", pathFn: (tp) => resolve(tp, "somedir") },
]

for (const { ctx, pathFn } of dirContexts) {
context(ctx, () => {
it("should return different hashes before and after a file is modified", async () => {
const dirPath = pathFn(tmpPath)
const filePath = resolve(tmpPath, "somedir", "foo.txt")

await createFile(filePath)
await writeFile(filePath, "original content")
await git("add", ".")
await git("commit", "-m", "foo")

await writeFile(filePath, "my change")
const beforeHash = (await handler.getFiles(dirPath))[0].hash

await writeFile(filePath, "ch-ch-ch-ch-changes")
const afterHash = (await handler.getFiles(dirPath))[0].hash

expect(beforeHash).to.not.eql(afterHash)
})

it("should return untracked files as absolute paths with hash", async () => {
const dirPath = pathFn(tmpPath)
await createFile(join(dirPath, "foo.txt"))
const hash = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"

expect(await handler.getFiles(dirPath)).to.eql([
{ path: resolve(dirPath, "foo.txt"), hash },
])
})
})
}

it("should return untracked files in untracked directory", async () => {
const dirPath = join(tmpPath, "dir")
Expand Down

0 comments on commit ec82c22

Please sign in to comment.