Skip to content

Commit

Permalink
feat(config): add this context when resolving modules
Browse files Browse the repository at this point in the history
This adds `${this.name}`, `${this.buildPath}` and `${this.path}` to the
template context when resolving modules.

Because someone might ask, `${this.version}` is not included because the
module version is determined after resolving the configuration.

I'm adding this both as a convenience and as a part of a different
project, where I'll add further keys to the `this.*` context.
  • Loading branch information
edvald authored and thsig committed Mar 29, 2021
1 parent 4d497c7 commit 4b24240
Show file tree
Hide file tree
Showing 19 changed files with 328 additions and 194 deletions.
14 changes: 6 additions & 8 deletions core/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -861,11 +861,10 @@ export class ActionRouter implements TypeGuard {
const configContext = new ModuleConfigContext({
garden: this.garden,
resolvedProviders: providers,
dependencies: modules,
modules,
runtimeContext,
parentName: module.parentName,
templateName: module.templateName,
inputs: module.inputs,
moduleConfig: module,
buildPath: module.buildPath,
partialRuntimeResolution: false,
})

Expand Down Expand Up @@ -924,11 +923,10 @@ export class ActionRouter implements TypeGuard {
const configContext = new ModuleConfigContext({
garden: this.garden,
resolvedProviders: providers,
dependencies: modules,
modules,
runtimeContext,
parentName: module.parentName,
templateName: module.templateName,
inputs: module.inputs,
moduleConfig: module,
buildPath: module.buildPath,
partialRuntimeResolution: false,
})

Expand Down
153 changes: 84 additions & 69 deletions core/src/config/template-contexts/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ import { templateKind } from "../module-template"
import { ConfigContext, schema, ErrorContext } from "./base"
import { ProjectConfigContext, ProjectConfigContextParams } from "./project"
import { ProviderConfigContext } from "./provider"
import { ModuleConfig } from "../module"

const exampleVersion = "v-17ad4cb3fd"

export class ModuleContext extends ConfigContext {
class ModuleThisContext extends ConfigContext {
@schema(
joi
.string()
Expand All @@ -32,6 +33,23 @@ export class ModuleContext extends ConfigContext {
)
public buildPath: string

@schema(joiIdentifier().description(`The name of the module.`))
public name: string

@schema(
joi.string().required().description("The local path of the module.").example("/home/me/code/my-project/my-module")
)
public path: string

constructor(root: ConfigContext, buildPath: string, name: string, path: string) {
super(root)
this.buildPath = buildPath
this.name = name
this.path = path
}
}

export class ModuleContext extends ModuleThisContext {
@schema(
joiIdentifierMap(
joiPrimitive().description(
Expand All @@ -49,19 +67,12 @@ export class ModuleContext extends ConfigContext {
)
public outputs: PrimitiveMap

@schema(
joi.string().required().description("The local path of the module.").example("/home/me/code/my-project/my-module")
)
public path: string

@schema(joi.string().required().description("The current version of the module.").example(exampleVersion))
public version: string

constructor(root: ConfigContext, module: GardenModule) {
super(root)
this.buildPath = module.buildPath
super(root, module.buildPath, module.name, module.path)
this.outputs = module.outputs
this.path = module.path
this.version = module.version.versionString
}
}
Expand Down Expand Up @@ -194,25 +205,20 @@ export class ModuleTemplateConfigContext extends ProjectConfigContext {
}
}

export interface ModuleConfigContextParams {
export interface OutputConfigContextParams {
garden: Garden
resolvedProviders: ProviderMap
moduleName?: string
dependencies: GardenModule[]
modules: GardenModule[]
// We only supply this when resolving configuration in dependency order.
// Otherwise we pass `${runtime.*} template strings through for later resolution.
runtimeContext?: RuntimeContext
parentName: string | undefined
templateName: string | undefined
inputs: DeepPrimitiveMap | undefined
partialRuntimeResolution: boolean
}

/**
* This context is available for template strings under the `module` key in configuration files.
* It is a superset of the context available under the `project` key.
* This context is available for template strings under the `outputs` key in project configuration files.
*/
export class ModuleConfigContext extends ProviderConfigContext {
export class OutputConfigContext extends ProviderConfigContext {
@schema(
joiIdentifierMap(ModuleContext.getSchema())
.description("Retrieve information about modules that are defined in the project.")
Expand All @@ -228,82 +234,91 @@ export class ModuleConfigContext extends ProviderConfigContext {
)
public runtime: RuntimeConfigContext

@schema(
ParentContext.getSchema().description(
`Information about the parent module (if the module is a submodule, e.g. generated in a templated module).`
)
)
public parent?: ParentContext

@schema(
ModuleTemplateContext.getSchema().description(
`Information about the ${templateKind} used when generating the module.`
)
)
public template?: ModuleTemplateContext

@schema(
joiVariables().description(`The inputs provided to the module through a ${templateKind}, if applicable.`).meta({
keyPlaceholder: "<input-key>",
})
)
public inputs: DeepPrimitiveMap

constructor({
garden,
resolvedProviders,
moduleName,
dependencies,
modules,
runtimeContext,
parentName,
templateName,
inputs,
partialRuntimeResolution,
}: ModuleConfigContextParams) {
}: OutputConfigContextParams) {
super(garden, resolvedProviders)

this.modules = new Map(
dependencies.map((config) => <[string, ModuleContext]>[config.name, new ModuleContext(this, config)])
modules.map((config) => <[string, ModuleContext]>[config.name, new ModuleContext(this, config)])
)

if (moduleName) {
// Throw specific error when attempting to resolve self
this.modules.set(moduleName, new ErrorContext(`Module ${chalk.white.bold(moduleName)} cannot reference itself.`))
}

this.runtime = new RuntimeConfigContext(this, partialRuntimeResolution, runtimeContext)
if (parentName && templateName) {
this.parent = new ParentContext(this, parentName)
this.template = new ModuleTemplateContext(this, templateName)
}
this.inputs = inputs || {}
}
}

export interface ModuleConfigContextParams extends OutputConfigContextParams {
garden: Garden
resolvedProviders: ProviderMap
moduleConfig: ModuleConfig
buildPath: string
// We only supply this when resolving configuration in dependency order.
// Otherwise we pass `${runtime.*} template strings through for later resolution.
runtimeContext?: RuntimeContext
}

/**
* This context is available for template strings under the `outputs` key in project configuration files.
* This context is available for template strings under the `module` key in configuration files.
* It is a superset of the context available under the `project` key.
*/
export class OutputConfigContext extends ModuleConfigContext {
export class ModuleConfigContext extends OutputConfigContext {
@schema(
joiVariables().description(`The inputs provided to the module through a ${templateKind}, if applicable.`).meta({
keyPlaceholder: "<input-key>",
})
)
public inputs: DeepPrimitiveMap

@schema(
ParentContext.getSchema().description(
`Information about the parent module (if the module is a submodule, e.g. generated in a templated module).`
)
)
public parent?: ParentContext

@schema(
ModuleTemplateContext.getSchema().description(
`Information about the ${templateKind} used when generating the module.`
)
)
public template?: ModuleTemplateContext

@schema(ModuleThisContext.getSchema().description("Information about the module currently being resolved."))
public this: ModuleThisContext

constructor({
garden,
resolvedProviders,
moduleConfig,
buildPath,
modules,
runtimeContext,
}: {
garden: Garden
resolvedProviders: ProviderMap
modules: GardenModule[]
runtimeContext: RuntimeContext
}) {
partialRuntimeResolution,
}: ModuleConfigContextParams) {
super({
garden,
resolvedProviders,
dependencies: modules,
modules,
runtimeContext,
parentName: undefined,
templateName: undefined,
inputs: {},
partialRuntimeResolution: false,
partialRuntimeResolution,
})

// Throw specific error when attempting to resolve self
this.modules.set(
moduleConfig.name,
new ErrorContext(`Module ${chalk.white.bold(moduleConfig.name)} cannot reference itself.`)
)

if (moduleConfig.parentName && moduleConfig.templateName) {
this.parent = new ParentContext(this, moduleConfig.parentName)
this.template = new ModuleTemplateContext(this, moduleConfig.templateName)
}
this.inputs = moduleConfig.inputs || {}

this.this = new ModuleThisContext(this, buildPath, moduleConfig.name, moduleConfig.path)
}
}
1 change: 1 addition & 0 deletions core/src/garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,7 @@ export class Garden {
resolvedProviders: providers,
modules,
runtimeContext,
partialRuntimeResolution: false,
})
}

Expand Down
1 change: 1 addition & 0 deletions core/src/outputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export async function resolveProjectOutputs(garden: Garden, log: LogEntry): Prom
resolvedProviders: {},
modules: [],
runtimeContext: emptyRuntimeContext,
partialRuntimeResolution: false,
})
)
}
Expand Down
44 changes: 22 additions & 22 deletions core/src/resolve-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { DependencyValidationGraph } from "./util/validate-dependencies"
import Bluebird from "bluebird"
import { readFile, mkdirp, writeFile } from "fs-extra"
import { LogEntry } from "./logger/log-entry"
import { ModuleConfigContext } from "./config/template-contexts/module"
import { ModuleConfigContext, ModuleConfigContextParams } from "./config/template-contexts/module"
import { pathToCacheContext } from "./cache"

// This limit is fairly arbitrary, but we need to have some cap on concurrent processing.
Expand Down Expand Up @@ -82,7 +82,8 @@ export class ModuleResolver {
}
}
for (const rawConfig of this.rawConfigs) {
const deps = this.getModuleDependenciesFromTemplateStrings(rawConfig)
const buildPath = await this.garden.buildStaging.buildPath(rawConfig)
const deps = this.getModuleDependenciesFromTemplateStrings(rawConfig, buildPath)
for (const graph of [fullGraph, processingGraph]) {
for (const dep of deps) {
graph.addNode(dep.name)
Expand Down Expand Up @@ -156,7 +157,8 @@ export class ModuleResolver {
// in the graph and move on to make sure we fully resolve the dependencies and don't run into circular
// dependencies.
if (!foundNewDependency) {
resolvedModules[moduleName] = await this.resolveModule(resolvedConfig, resolvedDependencies)
const buildPath = await this.garden.buildStaging.buildPath(resolvedConfig)
resolvedModules[moduleName] = await this.resolveModule(resolvedConfig, buildPath, resolvedDependencies)
processingGraph.removeNode(moduleName)
}
} catch (err) {
Expand All @@ -183,16 +185,14 @@ export class ModuleResolver {
/**
* Returns module configs for each module that is referenced in a ${modules.*} template string in the raw config.
*/
private getModuleDependenciesFromTemplateStrings(rawConfig: ModuleConfig) {
private getModuleDependenciesFromTemplateStrings(rawConfig: ModuleConfig, buildPath: string) {
const configContext = new ModuleConfigContext({
garden: this.garden,
resolvedProviders: this.resolvedProviders,
moduleName: rawConfig.name,
dependencies: [],
moduleConfig: rawConfig,
buildPath,
modules: [],
runtimeContext: this.runtimeContext,
parentName: rawConfig.parentName,
templateName: rawConfig.templateName,
inputs: rawConfig.inputs,
partialRuntimeResolution: true,
})

Expand Down Expand Up @@ -228,14 +228,15 @@ export class ModuleResolver {
const garden = this.garden
let inputs = {}

const templateContextParams = {
const buildPath = await this.garden.buildStaging.buildPath(config)

const templateContextParams: ModuleConfigContextParams = {
garden,
resolvedProviders: this.resolvedProviders,
dependencies,
modules: dependencies,
moduleConfig: config,
buildPath,
runtimeContext: this.runtimeContext,
parentName: config.parentName,
templateName: config.templateName,
inputs,
partialRuntimeResolution: true,
}

Expand All @@ -262,6 +263,8 @@ export class ModuleResolver {
schema: template.inputsSchema,
projectRoot: garden.projectRoot,
})

config.inputs = inputs
}

// Now resolve just references to inputs on the config
Expand All @@ -272,8 +275,7 @@ export class ModuleResolver {
// And finally fully resolve the config
const configContext = new ModuleConfigContext({
...templateContextParams,
inputs,
moduleName: config.name,
moduleConfig: config,
})

config = resolveTemplateStrings({ ...config, inputs: {} }, configContext, {
Expand Down Expand Up @@ -386,17 +388,15 @@ export class ModuleResolver {
return config
}

private async resolveModule(resolvedConfig: ModuleConfig, dependencies: GardenModule[]) {
private async resolveModule(resolvedConfig: ModuleConfig, buildPath: string, dependencies: GardenModule[]) {
// Write module files
const configContext = new ModuleConfigContext({
garden: this.garden,
resolvedProviders: this.resolvedProviders,
moduleName: resolvedConfig.name,
dependencies,
moduleConfig: resolvedConfig,
buildPath,
modules: dependencies,
runtimeContext: this.runtimeContext,
parentName: resolvedConfig.parentName,
templateName: resolvedConfig.templateName,
inputs: resolvedConfig.inputs,
partialRuntimeResolution: true,
})

Expand Down

0 comments on commit 4b24240

Please sign in to comment.