diff --git a/cli/src/add-version-files.ts b/cli/src/add-version-files.ts index 0d09904cbd..4de7cb94ee 100644 --- a/cli/src/add-version-files.ts +++ b/cli/src/add-version-files.ts @@ -34,7 +34,7 @@ async function addVersionFiles() { const path = config.path const versionFilePath = resolve(path, GARDEN_VERSIONFILE_NAME) - const vcsHandler = new GitHandler(garden.gardenDirPath, garden.dotIgnoreFiles) + const vcsHandler = new GitHandler(STATIC_DIR, garden.gardenDirPath, garden.dotIgnoreFiles) const treeVersion = await vcsHandler.getTreeVersion(garden.log, garden.projectName, config) // tslint:disable-next-line: no-console diff --git a/core/src/commands/get/get-debug-info.ts b/core/src/commands/get/get-debug-info.ts index 964cd0b515..4c9ed96df0 100644 --- a/core/src/commands/get/get-debug-info.ts +++ b/core/src/commands/get/get-debug-info.ts @@ -64,7 +64,7 @@ export async function collectBasicDebugInfo(root: string, gardenDirPath: string, } // Find all services paths - const vcs = new GitHandler(root, projectConfig.dotIgnoreFiles || defaultDotIgnoreFiles) + const vcs = new GitHandler(root, gardenDirPath, projectConfig.dotIgnoreFiles || defaultDotIgnoreFiles) const include = projectConfig.modules && projectConfig.modules.include const exclude = projectConfig.modules && projectConfig.modules.exclude const paths = await findConfigPathsInPath({ vcs, dir: root, include, exclude, log }) diff --git a/core/src/commands/migrate.ts b/core/src/commands/migrate.ts index 0409131dfc..3f63dd8469 100644 --- a/core/src/commands/migrate.ts +++ b/core/src/commands/migrate.ts @@ -88,7 +88,7 @@ export class MigrateCommand extends Command { if (args.configPaths && args.configPaths.length > 0) { configPaths = args.configPaths.map((path) => resolve(root, path)) } else { - const vcs = new GitHandler(resolve(root, DEFAULT_GARDEN_DIR_NAME), []) + const vcs = new GitHandler(root, resolve(root, DEFAULT_GARDEN_DIR_NAME), []) configPaths = await findConfigPathsInPath({ dir: root, vcs, diff --git a/core/src/garden.ts b/core/src/garden.ts index 4c1f087ccc..a80f9d5735 100644 --- a/core/src/garden.ts +++ b/core/src/garden.ts @@ -56,7 +56,7 @@ import { Watcher } from "./watch" import { findConfigPathsInPath, getWorkingCopyId, - fixedExcludes, + fixedProjectExcludes, detectModuleOverlap, ModuleOverlap, defaultConfigFilename, @@ -358,10 +358,14 @@ export class Garden { // We always exclude the garden dir const gardenDirExcludePattern = `${relative(projectRoot, gardenDirPath)}/**/*` - const moduleExcludePatterns = [...((config.modules || {}).exclude || []), gardenDirExcludePattern, ...fixedExcludes] + const moduleExcludePatterns = [ + ...((config.modules || {}).exclude || []), + gardenDirExcludePattern, + ...fixedProjectExcludes, + ] // Ensure the project root is in a git repo - const vcs = new GitHandler(gardenDirPath, config.dotIgnoreFiles) + const vcs = new GitHandler(projectRoot, gardenDirPath, config.dotIgnoreFiles) await vcs.getRepoRoot(log, projectRoot) const garden = new this({ diff --git a/core/src/util/fs.ts b/core/src/util/fs.ts index 9dd197b8fe..79f9d7f771 100644 --- a/core/src/util/fs.ts +++ b/core/src/util/fs.ts @@ -27,7 +27,7 @@ import { uuidv4, exec } from "./util" export const defaultConfigFilename = "garden.yml" const metadataFilename = "metadata.json" export const defaultDotIgnoreFiles = [".gardenignore"] -export const fixedExcludes = [".git", ".gitmodules", ".garden/**/*", "debug-info*/**"] +export const fixedProjectExcludes = [".git", ".gitmodules", ".garden/**/*", "debug-info*/**"] /* Warning: Don't make any async calls in the loop body when using this function, since this may cause diff --git a/core/src/vcs/vcs.ts b/core/src/vcs/vcs.ts index 3b0a05ddae..08fcf35aac 100644 --- a/core/src/vcs/vcs.ts +++ b/core/src/vcs/vcs.ts @@ -22,6 +22,7 @@ import { LogEntry } from "../logger/log-entry" import { treeVersionSchema, moduleVersionSchema } from "../config/common" import { Warning } from "../db/entities/warning" import { dedent } from "../util/string" +import { fixedProjectExcludes } from "../util/fs" export const NEW_MODULE_VERSION = "0000000000" const fileCountWarningThreshold = 10000 @@ -66,7 +67,7 @@ export interface VcsFile { } export abstract class VcsHandler { - constructor(protected gardenDirPath: string, protected ignoreFiles: string[]) {} + constructor(protected projectRoot: string, protected gardenDirPath: string, protected ignoreFiles: string[]) {} abstract name: string abstract async getRepoRoot(log: LogEntry, path: string): Promise @@ -78,12 +79,18 @@ export abstract class VcsHandler { async getTreeVersion(log: LogEntry, projectName: string, moduleConfig: ModuleConfig): Promise { const configPath = moduleConfig.configPath + // Apply project root excludes if the module config is in the project root and `include` isn't set + const exclude = + moduleConfig.path === this.projectRoot && !moduleConfig.include + ? [...(moduleConfig.exclude || []), ...fixedProjectExcludes] + : moduleConfig.exclude + let files = await this.getFiles({ log, path: moduleConfig.path, pathDescription: "module root", include: moduleConfig.include, - exclude: moduleConfig.exclude, + exclude, }) if (files.length > fileCountWarningThreshold) { diff --git a/core/test/unit/src/vcs/git.ts b/core/test/unit/src/vcs/git.ts index 4d7c3437ed..bf62f10914 100644 --- a/core/test/unit/src/vcs/git.ts +++ b/core/test/unit/src/vcs/git.ts @@ -64,7 +64,7 @@ describe("GitHandler", () => { log = garden.log tmpDir = await makeTempGitRepo() tmpPath = await realpath(tmpDir.path) - handler = new GitHandler(tmpPath, [defaultIgnoreFilename]) + handler = new GitHandler(tmpPath, join(tmpPath, ".garden"), [defaultIgnoreFilename]) git = (handler).gitCli(log, tmpPath) }) @@ -305,7 +305,7 @@ describe("GitHandler", () => { const hash = "6e1ab2d7d26c1c66f27fea8c136e13c914e3f137" - const _handler = new GitHandler(tmpPath, []) + const _handler = new GitHandler(tmpPath, join(tmpPath, ".garden"), []) expect(await _handler.getFiles({ path: tmpPath, log })).to.eql([{ path, hash }]) }) @@ -325,7 +325,7 @@ describe("GitHandler", () => { await git("add", pathA) await git("commit", "-m", "foo") - const _handler = new GitHandler(tmpPath, [defaultIgnoreFilename, ".testignore2"]) + const _handler = new GitHandler(tmpPath, join(tmpPath, ".garden"), [defaultIgnoreFilename, ".testignore2"]) const files = (await _handler.getFiles({ path: tmpPath, exclude: [], log })).filter( (f) => !f.path.includes(defaultIgnoreFilename) @@ -545,7 +545,7 @@ describe("GitHandler", () => { await commit("test commit B", tmpRepoPathB) const hash = hashRepoUrl(repositoryUrlA) - clonePath = join(tmpPath, "sources", "module", `foo--${hash}`) + clonePath = join(tmpPath, ".garden", "sources", "module", `foo--${hash}`) }) afterEach(async () => { @@ -583,7 +583,7 @@ describe("GitHandler", () => { }) const hash = hashRepoUrl(repositoryUrlA) - expect(res).to.eql(join(tmpPath, "sources", "project", `foo--${hash}`)) + expect(res).to.eql(join(tmpPath, ".garden", "sources", "project", `foo--${hash}`)) }) it("should not error if source already cloned", async () => { await handler.ensureRemoteSource({ @@ -642,7 +642,7 @@ describe("GitHandler", () => { }) const hash = hashRepoUrl(repositoryUrlA) - clonePath = join(tmpPath, "sources", "project", `foo--${hash}`) + clonePath = join(tmpPath, ".garden", "sources", "project", `foo--${hash}`) expect(await getCommitMsg(clonePath)).to.eql("test commit A") }) diff --git a/core/test/unit/src/vcs/vcs.ts b/core/test/unit/src/vcs/vcs.ts index e606b8b905..1bcdc69736 100644 --- a/core/test/unit/src/vcs/vcs.ts +++ b/core/test/unit/src/vcs/vcs.ts @@ -13,6 +13,8 @@ import { getVersionString, writeTreeVersionFile, readTreeVersionFile, + GetFilesParams, + VcsFile, } from "../../../../src/vcs/vcs" import { projectRootA, makeTestGardenA, makeTestGarden, getDataDir, TestGarden } from "../../../helpers" import { expect } from "chai" @@ -24,7 +26,7 @@ import td from "testdouble" import tmp from "tmp-promise" import { realpath, readFile, writeFile } from "fs-extra" import { GARDEN_VERSIONFILE_NAME } from "../../../../src/constants" -import { defaultDotIgnoreFiles } from "../../../../src/util/fs" +import { defaultDotIgnoreFiles, fixedProjectExcludes } from "../../../../src/util/fs" import { LogEntry } from "../../../../src/logger/log-entry" import { findByName } from "../../../../src/util/util" @@ -36,7 +38,7 @@ class TestVcsHandler extends VcsHandler { return "/foo" } - async getFiles() { + async getFiles(_: GetFilesParams): Promise { return [] } @@ -72,7 +74,7 @@ describe("VcsHandler", () => { } beforeEach(async () => { - handlerA = new TestVcsHandler(projectRootA, defaultDotIgnoreFiles) + handlerA = new TestVcsHandler(projectRootA, join(projectRootA, ".garden"), defaultDotIgnoreFiles) gardenA = await makeTestGardenA() }) @@ -121,7 +123,7 @@ describe("VcsHandler", () => { const projectRoot = getDataDir("test-projects", "include-exclude") const garden = await makeTestGarden(projectRoot) const moduleConfig = await garden.resolveModule("module-a") - const handler = new GitHandler(garden.gardenDirPath, garden.dotIgnoreFiles) + const handler = new GitHandler(garden.projectRoot, garden.gardenDirPath, garden.dotIgnoreFiles) const version = await handler.getTreeVersion(gardenA.log, gardenA.projectName, moduleConfig) @@ -135,7 +137,7 @@ describe("VcsHandler", () => { const projectRoot = getDataDir("test-projects", "include-exclude") const garden = await makeTestGarden(projectRoot) const moduleConfig = await garden.resolveModule("module-b") - const handler = new GitHandler(garden.gardenDirPath, garden.dotIgnoreFiles) + const handler = new GitHandler(garden.projectRoot, garden.gardenDirPath, garden.dotIgnoreFiles) const version = await handler.getTreeVersion(garden.log, garden.projectName, moduleConfig) @@ -146,7 +148,7 @@ describe("VcsHandler", () => { const projectRoot = getDataDir("test-projects", "include-exclude") const garden = await makeTestGarden(projectRoot) const moduleConfig = await garden.resolveModule("module-c") - const handler = new GitHandler(garden.gardenDirPath, garden.dotIgnoreFiles) + const handler = new GitHandler(garden.projectRoot, garden.gardenDirPath, garden.dotIgnoreFiles) const version = await handler.getTreeVersion(garden.log, garden.projectName, moduleConfig) @@ -169,6 +171,27 @@ describe("VcsHandler", () => { await writeFile(configPath, orgConfig) } }) + + it("should apply project-level excludes if module's path is same as root and no include is set", async () => { + td.replace(handlerA, "getFiles", async ({ exclude }: GetFilesParams) => { + expect(exclude).to.eql(fixedProjectExcludes) + return [] + }) + const moduleConfig = await gardenA.resolveModule("module-a") + moduleConfig.path = gardenA.projectRoot + await handlerA.getTreeVersion(gardenA.log, gardenA.projectName, moduleConfig) + }) + + it("should not apply project-level excludes if module's path is same as root but include is set", async () => { + td.replace(handlerA, "getFiles", async ({ exclude }: GetFilesParams) => { + expect(exclude).to.be.undefined + return [] + }) + const moduleConfig = await gardenA.resolveModule("module-a") + moduleConfig.path = gardenA.projectRoot + moduleConfig.include = [] + await handlerA.getTreeVersion(gardenA.log, gardenA.projectName, moduleConfig) + }) }) describe("resolveTreeVersion", () => {