Skip to content

Commit

Permalink
Interpolate job context in inputs too
Browse files Browse the repository at this point in the history
  • Loading branch information
sjchmiela committed Jan 23, 2025
1 parent c105377 commit 801ab16
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 83 deletions.
42 changes: 18 additions & 24 deletions packages/steps/src/BuildStepInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,30 +109,32 @@ export class BuildStepInput<
);
}

const interpolatedValue = interpolateJobContext({
target: rawValue,
context: interpolationContext,
});

const valueDoesNotRequireInterpolation =
rawValue === undefined ||
rawValue === null ||
typeof rawValue === 'boolean' ||
typeof rawValue === 'number';
interpolatedValue === undefined ||
interpolatedValue === null ||
typeof interpolatedValue === 'boolean' ||
typeof interpolatedValue === 'number';
let returnValue;
if (valueDoesNotRequireInterpolation) {
if (typeof rawValue !== this.allowedValueTypeName && rawValue !== undefined) {
if (
typeof interpolatedValue !== this.allowedValueTypeName &&
interpolatedValue !== undefined
) {
throw new BuildStepRuntimeError(
`Input parameter "${this.id}" for step "${this.stepDisplayName}" must be of type "${this.allowedValueTypeName}".`
);
}
returnValue = rawValue as BuildStepInputValueType<T>;
returnValue = interpolatedValue as BuildStepInputValueType<T>;
} else {
// `valueDoesNotRequireInterpolation` checks that `rawValue` is not undefined
// `valueDoesNotRequireInterpolation` checks that `interpolatedValue` is not undefined
// so this will never be true.
assert(rawValue !== undefined);
const valueInterpolatedWithNewInterpolation = interpolateJobContext({
target: rawValue,
context: interpolationContext,
}) as string | object;
const valueInterpolatedWithGlobalContext = this.ctx.interpolate(
valueInterpolatedWithNewInterpolation
);
assert(interpolatedValue !== undefined);
const valueInterpolatedWithGlobalContext = this.ctx.interpolate(interpolatedValue);
const valueInterpolatedWithOutputsAndGlobalContext = interpolateWithOutputs(
valueInterpolatedWithGlobalContext,
(path) => this.ctx.getStepOutputValue(path) ?? ''
Expand Down Expand Up @@ -218,15 +220,7 @@ export class BuildStepInput<
}

private parseInputValueToString(value: string): string {
let parsedValue = value;
try {
parsedValue = JSON.parse(`"${value}"`);
} catch (err) {
if (!(err instanceof SyntaxError)) {
throw err;
}
}
return parsedValue;
return `${value}`;
}

private parseInputValueToNumber(value: string): number {
Expand Down
5 changes: 3 additions & 2 deletions packages/steps/src/BuildWorkflowValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ export class BuildWorkflowValidator {
typeof currentStepInput.rawValue === 'object'
? BuildStepInputValueTypeName.JSON
: typeof currentStepInput.rawValue;
const rawValueMayRequireInterpolation =
typeof currentStepInput.rawValue === 'string' && currentStepInput.rawValue.includes('${');
if (
currentStepInput.rawValue !== undefined &&
typeof currentStepInput.rawValue === 'string' &&
currentStepInput.rawValue.includes('${') &&
!rawValueMayRequireInterpolation &&
currentType !== currentStepInput.allowedValueTypeName
) {
const error = new BuildConfigError(
Expand Down
43 changes: 33 additions & 10 deletions packages/steps/src/__tests__/BuildConfigParser-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,26 +224,37 @@ describe(BuildConfigParser, () => {
// property3: value4
// command: echo "Hi, ${ inputs.name }, ${ inputs.boolean_value }!"
const step1 = buildSteps[0];
const step1InterpolationContext = step1.getInterpolationContext();
expect(step1.id).toMatch(UUID_REGEX);
expect(step1.name).toBe('Say HI');
expect(step1.command).toBe('echo "Hi, ${ inputs.name }, ${ inputs.boolean_value }!"');
expect(step1.ctx.workingDirectory).toBe(ctx.defaultWorkingDirectory);
expect(step1.shell).toBe(getDefaultShell());
expect(step1.inputs).toBeDefined();
expect(step1.inputs?.[0].id).toBe('name');
expect(step1.inputs?.[0].value).toBe('Dominik Sokal');
expect(step1.inputs?.[0].getValue({ interpolationContext: step1InterpolationContext })).toBe(
'Dominik Sokal'
);
expect(step1.inputs?.[0].allowedValueTypeName).toBe(BuildStepInputValueTypeName.STRING);
expect(step1.inputs?.[1].id).toBe('country');
expect(step1.inputs?.[1].value).toBe('Poland');
expect(step1.inputs?.[1].getValue({ interpolationContext: step1InterpolationContext })).toBe(
'Poland'
);
expect(step1.inputs?.[1].allowedValueTypeName).toBe(BuildStepInputValueTypeName.STRING);
expect(step1.inputs?.[2].id).toBe('boolean_value');
expect(step1.inputs?.[2].value).toBe(true);
expect(step1.inputs?.[2].getValue({ interpolationContext: step1InterpolationContext })).toBe(
true
);
expect(step1.inputs?.[2].allowedValueTypeName).toBe(BuildStepInputValueTypeName.BOOLEAN);
expect(step1.inputs?.[3].id).toBe('number_value');
expect(step1.inputs?.[3].value).toBe(123);
expect(step1.inputs?.[3].getValue({ interpolationContext: step1InterpolationContext })).toBe(
123
);
expect(step1.inputs?.[3].allowedValueTypeName).toBe(BuildStepInputValueTypeName.NUMBER);
expect(step1.inputs?.[4].id).toBe('json_value');
expect(step1.inputs?.[4].value).toMatchObject({
expect(
step1.inputs?.[4].getValue({ interpolationContext: step1InterpolationContext })
).toMatchObject({
property1: 'value1',
property2: ['value2', { value3: { property3: 'value4' } }],
});
Expand Down Expand Up @@ -328,19 +339,24 @@ describe(BuildConfigParser, () => {
// - aaa
// - bbb
const step1 = buildSteps[0];
const step1InterpolationContext = step1.getInterpolationContext();
expect(step1.id).toMatch(UUID_REGEX);
expect(step1.name).toBe('Hi!');
expect(step1.command).toBe('echo "Hi, ${ inputs.name }!"');
expect(step1.ctx.workingDirectory).toBe(ctx.defaultWorkingDirectory);
expect(step1.shell).toBe(getDefaultShell());
expect(step1.inputs?.[0].id).toBe('name');
expect(step1.inputs?.[0].value).toBe('Dominik');
expect(step1.inputs?.[0].getValue({ interpolationContext: step1InterpolationContext })).toBe(
'Dominik'
);
expect(step1.inputs?.[0].allowedValueTypeName).toBe(BuildStepInputValueTypeName.STRING);
expect(step1.inputs?.[1].id).toBe('build_number');
expect(step1.inputs?.[1].rawValue).toBe('${ eas.job.version.buildNumber }');
expect(step1.inputs?.[1].allowedValueTypeName).toBe(BuildStepInputValueTypeName.NUMBER);
expect(step1.inputs?.[2].id).toBe('json_input');
expect(step1.inputs?.[2].value).toMatchObject({
expect(
step1.inputs?.[2].getValue({ interpolationContext: step1InterpolationContext })
).toMatchObject({
property1: 'value1',
property2: ['aaa', 'bbb'],
});
Expand All @@ -356,19 +372,26 @@ describe(BuildConfigParser, () => {
// name: Szymon
// build_number: 122
const step2 = buildSteps[1];
const step2InterpolationContext = step2.getInterpolationContext();
expect(step2.id).toMatch(UUID_REGEX);
expect(step2.name).toBe('Hi, Szymon!');
expect(step2.command).toBe('echo "Hi, ${ inputs.name }!"');
expect(step2.ctx.workingDirectory).toBe(ctx.defaultWorkingDirectory);
expect(step2.shell).toBe(getDefaultShell());
expect(step2.inputs?.[0].id).toBe('name');
expect(step2.inputs?.[0].value).toBe('Szymon');
expect(step2.inputs?.[0].getValue({ interpolationContext: step2InterpolationContext })).toBe(
'Szymon'
);
expect(step2.inputs?.[0].allowedValueTypeName).toBe(BuildStepInputValueTypeName.STRING);
expect(step2.inputs?.[1].id).toBe('build_number');
expect(step2.inputs?.[1].value).toBe(122);
expect(step2.inputs?.[1].getValue({ interpolationContext: step2InterpolationContext })).toBe(
122
);
expect(step2.inputs?.[1].allowedValueTypeName).toBe(BuildStepInputValueTypeName.NUMBER);
expect(step2.inputs?.[2].id).toBe('json_input');
expect(step2.inputs?.[2].value).toMatchObject({
expect(
step2.inputs?.[2].getValue({ interpolationContext: step2InterpolationContext })
).toMatchObject({
property1: 'value1',
property2: ['value2', { value3: { property3: 'value4' } }],
});
Expand Down
9 changes: 5 additions & 4 deletions packages/steps/src/__tests__/BuildFunction-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,11 @@ describe(BuildFunction, () => {
},
workingDirectory: ctx.defaultWorkingDirectory,
});
expect(step.inputs?.[0].value).toBe('abc');
expect(step.inputs?.[1].value).toBe('def');
expect(step.inputs?.[2].value).toBe(false);
expect(step.inputs?.[3].value).toMatchObject({
const interpolationContext = step.getInterpolationContext();
expect(step.inputs?.[0].getValue({ interpolationContext })).toBe('abc');
expect(step.inputs?.[1].getValue({ interpolationContext })).toBe('def');
expect(step.inputs?.[2].getValue({ interpolationContext })).toBe(false);
expect(step.inputs?.[3].getValue({ interpolationContext })).toMatchObject({
b: 2,
});
});
Expand Down
19 changes: 11 additions & 8 deletions packages/steps/src/__tests__/BuildStep-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { jest } from '@jest/globals';
import { instance, mock, verify, when } from 'ts-mockito';
import { v4 as uuidv4 } from 'uuid';

import { BuildStep, BuildStepFunction, BuildStepStatus } from '../BuildStep.js';
import { BuildStep, BuildStepStatus } from '../BuildStep.js';
import {
BuildStepInput,
BuildStepInputById,
Expand Down Expand Up @@ -623,18 +623,21 @@ describe(BuildStep, () => {
}),
];

const fn: BuildStepFunction = (_ctx, { inputs, outputs }) => {
outputs.abc.set(
`${inputs.foo1.value} ${inputs.foo2.value} ${inputs.foo3.value} ${inputs.foo4.value}`
);
};

const step = new BuildStep(baseStepCtx, {
id,
displayName,
inputs,
outputs,
fn,
fn: (_ctx, { inputs, outputs }) => {
const interpolationContext = step.getInterpolationContext();
outputs.abc.set(
`${inputs.foo1.getValue({ interpolationContext })} ${inputs.foo2.getValue({
interpolationContext,
})} ${inputs.foo3.getValue({ interpolationContext })} ${inputs.foo4.getValue({
interpolationContext,
})}`
);
},
});

await step.executeAsync();
Expand Down
Loading

0 comments on commit 801ab16

Please sign in to comment.