Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(modules): allow opting out of build staging #5890

Merged
merged 1 commit into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions core/src/build-staging/build-staging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ export class BuildStaging {
}

getBuildPath(config: BuildActionConfig<string, any> | ModuleConfig): string {
// We don't stage the build for local exec modules, so the module path is effectively the build path.
if (config.kind === "Module" && config.type === "exec" && config["local"] === true) {
// We don't stage the build for local modules, so the module path is effectively the build path.
if (config.kind === "Module" && config["local"] === true) {
return config.path
}

Expand Down
1 change: 1 addition & 0 deletions core/src/config/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ export function prepareModuleResource(spec: any, configPath: string, projectRoot
dependencies,
timeout: spec.build?.timeout || DEFAULT_BUILD_TIMEOUT_SEC,
},
local: spec.local,
configPath,
description: spec.description,
disabled: spec.disabled,
Expand Down
19 changes: 19 additions & 0 deletions core/src/config/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ interface ModuleSpecCommon {
apiVersion?: string
allowPublish?: boolean
build?: BaseBuildSpec
local?: boolean
description?: string
disabled?: boolean
exclude?: string[]
Expand Down Expand Up @@ -187,6 +188,24 @@ export const coreModuleSpecSchema = createSchema({
// These fields may be resolved later in the process, and allow for usage of template strings
export const baseModuleSpecKeys = memoize(() => ({
build: baseBuildSpecSchema().unknown(true),
local: joi
.boolean()
.description(
dedent`
If set to true, Garden will run the build command, services, tests, and tasks in the module source directory,
instead of in the Garden build directory (under .garden/build/<module-name>).

Garden will therefore not stage the build for local modules. This means that include/exclude filters
and ignore files are not applied to local modules, except to calculate the module/action versions.

If you use use \`build.dependencies[].copy\` for one or more build dependencies of this module, the copied files
will be copied to the module source directory (instead of the build directory, as is the default case when
\`local = false\`).

Note: This maps to the \`buildAtSource\` option in this module's generated Build action (if any).
`
)
.default(false),
description: joi.string().description("A description of the module."),
disabled: joi
.boolean()
Expand Down
1 change: 0 additions & 1 deletion core/src/plugins/exec/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export function prepareExecBuildAction(params: ConvertModuleParams<ExecModule>):
...params.baseFields,
...dummyBuild,

buildAtSource: module.spec.local,
dependencies: module.build.dependencies.map(convertBuildDependency),

timeout: module.build.timeout,
Expand Down
29 changes: 0 additions & 29 deletions core/src/plugins/exec/moduleConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import { baseTaskSpecSchema } from "../../config/task.js"
import { dedent } from "../../util/string.js"
import type { ExecSyncModeSpec } from "./config.js"
import type { ConfigureModuleParams, ConfigureModuleResult } from "../../plugin/handlers/Module/configure.js"
import { ConfigurationError } from "../../exceptions.js"
import { memoize, omit } from "lodash-es"
import { DEFAULT_RUN_TIMEOUT_SEC } from "../../constants.js"

Expand All @@ -59,21 +58,6 @@ const artifactsSchema = memoize(() => joiSparseArray(artifactSchema()))
export async function configureExecModule({
moduleConfig,
}: ConfigureModuleParams<ExecModule>): Promise<ConfigureModuleResult> {
const buildDeps = moduleConfig.build.dependencies
if (moduleConfig.spec.local && buildDeps.some((d) => d.copy.length > 0)) {
const buildDependenciesWithCopySpec = buildDeps
.filter((d) => !!d.copy)
.map((d) => d.name)
.join(", ")
throw new ConfigurationError({
message: dedent`
Invalid exec module configuration: Module ${moduleConfig.name} copies ${buildDependenciesWithCopySpec}

A local exec module cannot have a build dependency with a copy spec.
`,
})
}

// All the config keys that affect the build version
moduleConfig.buildConfig = omit(moduleConfig.spec, ["tasks", "tests", "services"])

Expand Down Expand Up @@ -257,7 +241,6 @@ export interface ExecModuleSpec extends ModuleSpec {
services: ExecServiceSpec[]
tasks: ExecTaskSpec[]
tests: ExecTestSpec[]
local?: boolean
}

export type ExecModuleConfig = ModuleConfig<ExecModuleSpec>
Expand All @@ -282,18 +265,6 @@ export const execModuleSpecSchema = createSchema({
name: "exec:Module",
description: "The module specification for an exec module.",
keys: () => ({
local: joi
.boolean()
.description(
dedent`
If set to true, Garden will run the build command, services, tests, and tasks in the module source directory,
instead of in the Garden build directory (under .garden/build/<module-name>).

Garden will therefore not stage the build for local exec modules. This means that include/exclude filters
and ignore files are not applied to local exec modules.
`
)
.default(false),
build: execModuleBuildSpecSchema(),
env: joiEnvVars(),
services: joiSparseArray(execServiceSchema()).description("A list of services to deploy from this module."),
Expand Down
4 changes: 4 additions & 0 deletions core/src/resolve-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,7 @@ export function makeDummyBuild({

copyFrom,
source: module.repositoryUrl ? { repository: { url: module.repositoryUrl } } : undefined,
buildAtSource: module.local,

allowPublish: module.allowPublish,
dependencies,
Expand Down Expand Up @@ -941,6 +942,9 @@ function inheritModuleToAction(module: GardenModule, action: ActionConfig) {
action.disabled = true
}
action.internal.basePath = module.path
if (isBuildActionConfig(action)) {
action.buildAtSource = module.local
}
if (module.configPath) {
action.internal.configFilePath = module.configPath
}
Expand Down
5 changes: 2 additions & 3 deletions core/src/types/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,8 @@ export async function moduleFromConfig({
const moduleTypes = await garden.getModuleTypes()
const compatibleTypes = [config.type, ...getModuleTypeBases(moduleTypes[config.type], moduleTypes).map((t) => t.name)]

// Special-casing local exec modules, otherwise setting build path as <build dir>/<module name>
const buildPath =
config.type === "exec" && config.spec.local ? config.path : join(garden.buildStaging.buildDirPath, config.name)
// Special-casing local modules, otherwise setting build path as <build dir>/<module name>
const buildPath = config.local ? config.path : join(garden.buildStaging.buildDirPath, config.name)

await garden.buildStaging.ensureDir(buildPath)

Expand Down
1 change: 1 addition & 0 deletions core/test/integ/src/plugins/kubernetes/helm/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ describe("configureHelmModule", () => {
dependencies: [],
timeout: DEFAULT_BUILD_TIMEOUT_SEC,
},
local: false,
configPath: resolve(ctx.projectRoot, "api", "garden.yml"),
description: "The API backend for the voting UI",
disabled: false,
Expand Down
4 changes: 4 additions & 0 deletions core/test/unit/src/config/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ describe("loadConfigResources", () => {
repositoryUrl: undefined,
allowPublish: undefined,
build: { dependencies: [], timeout: DEFAULT_BUILD_TIMEOUT_SEC },
local: undefined,
path: modulePathA,
variables: { msg: "OK" },
varfile: undefined,
Expand Down Expand Up @@ -414,6 +415,7 @@ describe("loadConfigResources", () => {
repositoryUrl: undefined,
allowPublish: undefined,
build: { dependencies: [], timeout: DEFAULT_BUILD_TIMEOUT_SEC },
local: undefined,
path: projectPathMultipleModules,
serviceConfigs: [],
spec: {
Expand Down Expand Up @@ -454,6 +456,7 @@ describe("loadConfigResources", () => {
dependencies: [{ name: "module-from-project-config", copy: [] }],
timeout: DEFAULT_BUILD_TIMEOUT_SEC,
},
local: undefined,
path: modulePathAMultiple,
serviceConfigs: [],
spec: {
Expand Down Expand Up @@ -484,6 +487,7 @@ describe("loadConfigResources", () => {
exclude: undefined,
repositoryUrl: undefined,
build: { dependencies: [], timeout: DEFAULT_BUILD_TIMEOUT_SEC },
local: undefined,
path: modulePathAMultiple,
serviceConfigs: [],
spec: {
Expand Down
29 changes: 2 additions & 27 deletions core/test/unit/src/plugins/exec/exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { createActionLog } from "../../../../../src/logger/log-entry.js"
import { keyBy, omit } from "lodash-es"
import {
getDataDir,
makeTestModule,
expectError,
createProjectConfig,
TestGarden,
Expand All @@ -26,15 +25,13 @@ import {
} from "../../../../helpers.js"
import { RunTask } from "../../../../../src/tasks/run.js"
import { makeTestGarden } from "../../../../helpers.js"
import type { ModuleConfig } from "../../../../../src/config/module.js"
import type { ConfigGraph } from "../../../../../src/graph/config-graph.js"
import fsExtra from "fs-extra"
const { pathExists, emptyDir, readFile, remove } = fsExtra
import { TestTask } from "../../../../../src/tasks/test.js"
import { dedent } from "../../../../../src/util/string.js"
import { sleep } from "../../../../../src/util/util.js"
import type { ExecModuleConfig } from "../../../../../src/plugins/exec/moduleConfig.js"
import { configureExecModule } from "../../../../../src/plugins/exec/moduleConfig.js"
import { actionFromConfig } from "../../../../../src/graph/actions.js"
import type { TestAction, TestActionConfig } from "../../../../../src/actions/test.js"
import type { PluginContext } from "../../../../../src/plugin-context.js"
Expand Down Expand Up @@ -226,7 +223,7 @@ describe("exec plugin", () => {
},
])

expect(moduleLocal.spec.local).to.eql(true)
expect(moduleLocal.local).to.eql(true)
expect(moduleLocal.build.dependencies).to.eql([])
expect(moduleLocal.spec.build.command).to.eql(["pwd"])

Expand Down Expand Up @@ -356,28 +353,6 @@ describe("exec plugin", () => {
expect(await pathExists(join(_garden.artifactsPath, "test-outputs", "test-a.txt"))).to.be.true
})

describe("configureExecModule", () => {
it("should throw if a local exec module has a build.copy spec", async () => {
const moduleConfig = makeTestModule(<Partial<ModuleConfig>>{
build: {
dependencies: [
{
name: "foo",
copy: [
{
source: ".",
target: ".",
},
],
},
],
},
spec: { local: true },
})
await expectError(async () => await configureExecModule({ ctx, moduleConfig, log }), "configuration")
})
})

describe("build", () => {
it("should run the build command in the action dir if local true", async () => {
const action = graph.getBuild("module-local")
Expand Down Expand Up @@ -1122,8 +1097,8 @@ describe("exec plugin", () => {
makeModuleConfig<ExecModuleConfig>(garden.projectRoot, {
name,
type: "exec",
local, // <---
spec: {
local, // <---
build: {
command: buildCommand,
},
Expand Down
41 changes: 41 additions & 0 deletions docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -1481,6 +1481,21 @@ providers:
# Maximum time in seconds to wait for build to finish.
timeout:

# If set to true, Garden will run the build command, services, tests, and tasks in the module source
# directory,
# instead of in the Garden build directory (under .garden/build/<module-name>).
#
# Garden will therefore not stage the build for local modules. This means that include/exclude filters
# and ignore files are not applied to local modules, except to calculate the module/action versions.
#
# If you use use `build.dependencies[].copy` for one or more build dependencies of this module, the copied
# files
# will be copied to the module source directory (instead of the build directory, as is the default case when
# `local = false`).
#
# Note: This maps to the `buildAtSource` option in this module's generated Build action (if any).
local:

# A description of the module.
description:

Expand Down Expand Up @@ -2381,6 +2396,19 @@ moduleConfigs:
# Maximum time in seconds to wait for build to finish.
timeout:

# If set to true, Garden will run the build command, services, tests, and tasks in the module source directory,
# instead of in the Garden build directory (under .garden/build/<module-name>).
#
# Garden will therefore not stage the build for local modules. This means that include/exclude filters
# and ignore files are not applied to local modules, except to calculate the module/action versions.
#
# If you use use `build.dependencies[].copy` for one or more build dependencies of this module, the copied files
# will be copied to the module source directory (instead of the build directory, as is the default case when
# `local = false`).
#
# Note: This maps to the `buildAtSource` option in this module's generated Build action (if any).
local:

# A description of the module.
description:

Expand Down Expand Up @@ -2939,6 +2967,19 @@ modules:
# Maximum time in seconds to wait for build to finish.
timeout:

# If set to true, Garden will run the build command, services, tests, and tasks in the module source directory,
# instead of in the Garden build directory (under .garden/build/<module-name>).
#
# Garden will therefore not stage the build for local modules. This means that include/exclude filters
# and ignore files are not applied to local modules, except to calculate the module/action versions.
#
# If you use use `build.dependencies[].copy` for one or more build dependencies of this module, the copied files
# will be copied to the module source directory (instead of the build directory, as is the default case when
# `local = false`).
#
# Note: This maps to the `buildAtSource` option in this module's generated Build action (if any).
local:

# A description of the module.
description:

Expand Down
33 changes: 33 additions & 0 deletions docs/reference/config-template-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,19 @@ modules:
# Maximum time in seconds to wait for build to finish.
timeout: 600

# If set to true, Garden will run the build command, services, tests, and tasks in the module source directory,
# instead of in the Garden build directory (under .garden/build/<module-name>).
#
# Garden will therefore not stage the build for local modules. This means that include/exclude filters
# and ignore files are not applied to local modules, except to calculate the module/action versions.
#
# If you use use `build.dependencies[].copy` for one or more build dependencies of this module, the copied files
# will be copied to the module source directory (instead of the build directory, as is the default case when
# `local = false`).
#
# Note: This maps to the `buildAtSource` option in this module's generated Build action (if any).
local: false

# A description of the module.
description:

Expand Down Expand Up @@ -365,6 +378,26 @@ Maximum time in seconds to wait for build to finish.
| -------- | ------- | -------- |
| `number` | `600` | No |

### `modules[].local`

[modules](#modules) > local

If set to true, Garden will run the build command, services, tests, and tasks in the module source directory,
instead of in the Garden build directory (under .garden/build/<module-name>).

Garden will therefore not stage the build for local modules. This means that include/exclude filters
and ignore files are not applied to local modules, except to calculate the module/action versions.

If you use use `build.dependencies[].copy` for one or more build dependencies of this module, the copied files
will be copied to the module source directory (instead of the build directory, as is the default case when
`local = false`).

Note: This maps to the `buildAtSource` option in this module's generated Build action (if any).

| Type | Default | Required |
| --------- | ------- | -------- |
| `boolean` | `false` | No |

### `modules[].description`

[modules](#modules) > description
Expand Down