Skip to content

Commit

Permalink
fix(template): parse template strings in varfiles (#6844)
Browse files Browse the repository at this point in the history
* test(template): add test case

* fix(template): parse template strings in varfiles

* fix(test): fix some test assertions
  • Loading branch information
vvagaytsev authored Feb 12, 2025
1 parent c7da2f2 commit f855d91
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 27 deletions.
28 changes: 22 additions & 6 deletions core/src/config/template-contexts/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { ActionSpecContext } from "./actions.js"
import type { CustomCommandContext } from "./custom-command.js"
import type { CommandResource } from "../command.js"
import type { GroupConfig } from "../group.js"
import { parseTemplateCollection } from "../../template/templated-collections.js"

export class VariablesContext extends LayeredContext {
/**
Expand Down Expand Up @@ -124,12 +125,15 @@ export class VariablesContext extends LayeredContext {
variableOverrides: DeepPrimitiveMap,
context: ProjectConfigContext
) {
// TODO: parse template strings in varfiles?
const projectVarfileVars = await loadVarfile({
const rawProjectVarfileVars = await loadVarfile({
configRoot: projectConfig.path,
path: projectConfig.varfile,
defaultPath: defaultProjectVarfilePath,
})
const projectVarfileVars = parseTemplateCollection({
value: rawProjectVarfileVars,
source: { path: ["varfile"], yamlDoc: projectConfig.internal.yamlDoc },
})

return new this(`project ${projectConfig.name}`, {
context,
Expand All @@ -145,11 +149,15 @@ export class VariablesContext extends LayeredContext {
variableOverrides: DeepPrimitiveMap,
context: ProjectConfigContext
) {
const envVarfileVars = await loadVarfile({
const rawEnvVarfileVars = await loadVarfile({
configRoot: projectConfig.path,
path: environmentConfig.varfile,
defaultPath: defaultEnvVarfilePath(environment),
})
const envVarfileVars = parseTemplateCollection({
value: rawEnvVarfileVars,
source: { path: ["varfile"], yamlDoc: projectConfig.internal.yamlDoc },
})

return new this(`environment ${environmentConfig.name}`, {
variablePrecedence: [environmentConfig.variables, envVarfileVars],
Expand Down Expand Up @@ -197,17 +205,25 @@ export class VariablesContext extends LayeredContext {
group?: GroupConfig
) {
const effectiveConfigFileLocation = getEffectiveConfigFileLocation(config)
const actionVarfileVars = await loadVarfiles(garden, effectiveConfigFileLocation, config.varfiles || [])
const rawActionVarfileVars = await loadVarfiles(garden, effectiveConfigFileLocation, config.varfiles || [])
const actionVarfileVars = parseTemplateCollection({
value: rawActionVarfileVars,
source: { path: ["varfiles"], yamlDoc: config.internal.yamlDoc },
})
const actionVariables = [config.variables, ...actionVarfileVars]

let groupVarfileVars: ParsedTemplate[] = []
let groupVariables: ParsedTemplate[] = []
if (group) {
groupVarfileVars = await loadVarfiles(garden, group.path, group.varfiles || [])
const rawGroupVarfileVars = await loadVarfiles(garden, group.path, group.varfiles || [])
groupVarfileVars = parseTemplateCollection({
value: rawGroupVarfileVars,
source: { path: [] },
})
groupVariables = [group.variables, ...groupVarfileVars]
}

return new this(describeConfig(config) + (!group ? " (without group variables" : ""), {
return new this(describeConfig(config) + (!group ? " (without group variables)" : ""), {
variablePrecedence: [...groupVariables, ...actionVariables],
context,
variableOverrides: garden.variableOverrides,
Expand Down
20 changes: 20 additions & 0 deletions core/test/data/test-projects/varfiles-with-templates/garden.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: garden.io/v1
kind: Project
name: varfiles-with-templates

varfile: "vars-project.yml"

environments:
- name: default
varfile: "vars-env.yml"

---

kind: Run
type: exec
name: echo

varfiles: [ "vars-action.yml" ]

spec:
command: [ echo, 'PROJECT_VAR=${var.PROJECT_VAR}', 'ENV_VAR=${var.ENV_VAR}', 'ACTION_VAR=${var.ACTION_VAR}' ]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ACTION_VAR: "${project.name}"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ENV_VAR: "${project.name}"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROJECT_VAR: "${project.name}"
36 changes: 22 additions & 14 deletions core/test/unit/src/actions/action-configs-to-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,15 +542,15 @@ describe("actionConfigsToGraph", () => {
})

const action = graph.getBuild("foo")
const vars = action["variables"]
const resolved = deepResolveContext("action variables", vars, garden.getProjectConfigContext())
const varContext = action.getVariablesContext()
const resolved = deepResolveContext("action variables", varContext, garden.getProjectConfigContext())

expect(resolved).to.eql({
projectName: garden.projectName,
})
})

it("loads varfiles for the action", async () => {
it("loads varfiles for the action and resolve template strings in varfile", async () => {
const varfilePath = join(tmpDir.path, "varfile.yml")
await dumpYaml(varfilePath, {
projectName: "${project.name}",
Expand Down Expand Up @@ -579,17 +579,19 @@ describe("actionConfigsToGraph", () => {
})

const action = graph.getBuild("foo")
const vars = action["variables"]
const varContext = action.getVariablesContext()

expect(vars.resolve({ nodePath: [], key: [], opts: {}, rootContext: garden.getProjectConfigContext() })).to.eql({
expect(
varContext.resolve({ nodePath: [], key: [], opts: {}, rootContext: garden.getProjectConfigContext() })
).to.eql({
found: true,
resolved: {
projectName: "${project.name}",
projectName: "test",
},
})
})

it("loads optional varfiles for the action", async () => {
it("loads optional varfiles for the action and resolve template strings in varfile", async () => {
const varfilePath = join(tmpDir.path, "varfile.yml")
await dumpYaml(varfilePath, {
projectName: "${project.name}",
Expand Down Expand Up @@ -618,11 +620,13 @@ describe("actionConfigsToGraph", () => {
})

const action = graph.getBuild("foo")
const vars = action["variables"]
const varContext = action.getVariablesContext()

expect(vars.resolve({ nodePath: [], key: [], opts: {}, rootContext: garden.getProjectConfigContext() })).to.eql({
expect(
varContext.resolve({ nodePath: [], key: [], opts: {}, rootContext: garden.getProjectConfigContext() })
).to.eql({
found: true,
resolved: { projectName: "${project.name}" },
resolved: { projectName: "test" },
})
})

Expand Down Expand Up @@ -660,9 +664,11 @@ describe("actionConfigsToGraph", () => {
})

const action = graph.getBuild("foo")
const vars = action["variables"]
const varContext = action.getVariablesContext()

expect(vars.resolve({ nodePath: [], key: [], opts: {}, rootContext: garden.getProjectConfigContext() })).to.eql({
expect(
varContext.resolve({ nodePath: [], key: [], opts: {}, rootContext: garden.getProjectConfigContext() })
).to.eql({
found: true,
resolved: { foo: "FOO", bar: "BAR", baz: "baz" },
})
Expand Down Expand Up @@ -718,9 +724,11 @@ describe("actionConfigsToGraph", () => {
})

const action = graph.getBuild("foo")
const vars = action["variables"]
const varContext = action.getVariablesContext()

expect(vars.resolve({ nodePath: [], key: [], opts: {}, rootContext: garden.getProjectConfigContext() })).to.eql({
expect(
varContext.resolve({ nodePath: [], key: [], opts: {}, rootContext: garden.getProjectConfigContext() })
).to.eql({
found: true,
resolved: {
foo: "NEW_FOO",
Expand Down
41 changes: 34 additions & 7 deletions core/test/unit/src/config/template-contexts/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,40 @@ import { TestContext } from "./base.js"
import { deepResolveContext } from "../../../../../src/config/template-contexts/base.js"
import { resolveAction } from "../../../../../src/graph/actions.js"

/*
* Copyright (C) 2018-2024 Garden Technologies, Inc. <info@garden.io>
*
* 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/.
*/
describe("varfiles", () => {
let garden: TestGarden

beforeEach(async () => {
garden = await makeTestGarden(getDataDir("test-projects", "varfiles-with-templates"))
})

afterEach(() => {
garden.close()
})

it("should parse template strings in varfiles", async () => {
const graph = await garden.getConfigGraph({ log: garden.log, emit: false })
const runAction = graph.getRun("echo")

const varContext = runAction.getVariablesContext()
const output = varContext.resolve({
nodePath: [],
key: [],
opts: {},
rootContext: garden.getProjectConfigContext(),
})

expect(output).to.eql({
found: true,
resolved: {
ACTION_VAR: "varfiles-with-templates",
ENV_VAR: "varfiles-with-templates",
PROJECT_VAR: "varfiles-with-templates",
},
})
})
})

describe("VariablesContext", () => {
let garden: TestGarden

Expand Down

0 comments on commit f855d91

Please sign in to comment.