Skip to content

Commit 354eade

Browse files
committed
feat: use es6 templates for substitutions
1 parent bcf2db8 commit 354eade

File tree

5 files changed

+61
-27
lines changed

5 files changed

+61
-27
lines changed

deno.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/workflow/exec.ts

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,13 @@
11
import { Command, EnumType, ValidationError } from '/deps.ts'
22
import { config } from '/lib/config.ts'
3+
import { getSubstitutions, render } from '/lib/template.ts'
34
import { cmd, GlobalOptions } from '/index.ts'
45
import { CliOptions, RunrealConfig } from '/lib/types.ts'
56
import { exec as execCmd, randomBuildkiteEmoji } from '/lib/utils.ts'
67

78
export type ExecOptions = typeof exec extends Command<any, any, infer Options, any, any> ? Options
89
: never
910

10-
// Object containing the allowed substitutions
11-
const getSubstitutions = (cfg: RunrealConfig): Record<string, string | undefined> => ({
12-
'engine.path': cfg.engine.path,
13-
'project.path': cfg.project.path,
14-
'project.name': cfg.project.name,
15-
'build.id': cfg.build.id,
16-
'build.path': cfg.build.path,
17-
'build.branch': cfg.build.branchSafe,
18-
'build.commit': cfg.build.commitShort,
19-
'buildkite.buildNumber': cfg.buildkite.buildNumber,
20-
})
21-
22-
// This helper function will take a command string with placeholders and a substitutions object
23-
// It will replace all placeholders in the command with their corresponding values
24-
// If the key is not found in substitutions, keep the original placeholder
25-
function interpolateCommand(input: string[], substitutions: Record<string, string | undefined>) {
26-
// This regular expression matches all occurrences of ${placeholder}
27-
const placeholderRegex = /\$\{([^}]+)\}/g
28-
return input.map((arg) =>
29-
arg.replace(placeholderRegex, (_, key: string) => {
30-
return key in substitutions ? substitutions[key] || key : _
31-
})
32-
)
33-
}
34-
3511
enum Mode {
3612
Local = 'local',
3713
Buildkite = 'buildkite',
@@ -84,8 +60,8 @@ export const exec = new Command<GlobalOptions>()
8460

8561
const steps: { command: string; args: string[] }[] = []
8662
for await (const step of run.steps) {
87-
const command = interpolateCommand([step.command], getSubstitutions(cfg))[0]
88-
const args = interpolateCommand(step.args || [], getSubstitutions(cfg))
63+
const command = render(step.command, getSubstitutions(cfg))[0]
64+
const args = render(step.args, getSubstitutions(cfg))
8965
steps.push({ command, args })
9066
}
9167

src/lib/source.ts

Whitespace-only changes.

src/lib/template.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { RunrealConfig } from '/lib/types.ts'
2+
3+
export const render = (template: string, data: any) => {
4+
return new Function(
5+
'vars',
6+
[
7+
'const keys = (' + Object.keys(data).join(', ') + ') =>',
8+
'`' + template + '`',
9+
'return keys(...Object.values(vars))',
10+
].join('\n'),
11+
)(data)
12+
}
13+
14+
// Object containing the allowed substitutions
15+
export const getSubstitutions = (cfg: any): Partial<RunrealConfig> => {
16+
const buildkite: Partial<RunrealConfig['buildkite']> = {
17+
buildNumber: cfg.buildkite?.buildNumber || 'buildkite.buildNumber',
18+
}
19+
return {
20+
engine: {
21+
path: cfg.engine?.path || 'engine.path',
22+
},
23+
project: {
24+
path: cfg.project?.path || 'project.path',
25+
name: cfg.project?.name || 'project.name',
26+
},
27+
build: {
28+
id: cfg.build?.id || 'build.id',
29+
path: cfg.build?.path || 'build.path',
30+
branch: cfg.build?.branchSafe || 'build.branch',
31+
commit: cfg.build?.commitShort || 'build.commit',
32+
},
33+
// @ts-ignore partial
34+
buildkite,
35+
}
36+
}

src/tests/template_test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { render } from '/lib/template.ts'
2+
import { assertEquals } from 'std/assert/mod.ts'
3+
import { getSubstitutions } from '/lib/template.ts'
4+
5+
Deno.test('template test', () => {
6+
const tmpl = '{"name": "${project.name}", "engine": "${engine.path}", "project": "${project.name}"}'
7+
const cfg = { project: { name: 'Deno' }, engine: { path: 'V8' } }
8+
9+
const result = render(tmpl, cfg)
10+
assertEquals(result, '{"name": "Deno", "engine": "V8", "project": "Deno"}')
11+
})
12+
13+
Deno.test('template test with substitutions', () => {
14+
const tmpl = '{"name": "${project.name}", "engine": "${engine.path}", "project": "${project.path}"}'
15+
const cfg = { project: { name: 'Deno' }, engine: { path: 'V8' } }
16+
17+
const res = getSubstitutions(cfg)
18+
19+
const result = render(tmpl, res)
20+
assertEquals(result, '{"name": "Deno", "engine": "V8", "project": "project.path"}')
21+
})

0 commit comments

Comments
 (0)