Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/steps/src/BuildConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { BuildRuntimePlatform } from './BuildRuntimePlatform';
import { BuildStepEnv } from './BuildStepEnv';
import { BuildStepInputValueType, BuildStepInputValueTypeName } from './BuildStepInput';
import { BuildConfigError, BuildWorkflowError } from './errors';
import { isLocalCompositeFunctionPath } from './utils/localCompositeFunctions';
import { BUILD_STEP_OR_BUILD_GLOBAL_CONTEXT_REFERENCE_REGEX } from './utils/template';

export type BuildFunctions = Record<string, BuildFunctionConfig>;
Expand Down Expand Up @@ -438,6 +439,18 @@ export function validateAllFunctionsExist(
}
}
const calledFunctionsOrFunctionGroup = Array.from(calledFunctionsOrFunctionGroupsSet);
const compositeFunctionPaths = calledFunctionsOrFunctionGroup.filter(
isLocalCompositeFunctionPath
);
if (compositeFunctionPaths.length > 0) {
throw new BuildConfigError(
`Local composite functions (${compositeFunctionPaths
.map(compositeFunctionPath => `"${compositeFunctionPath}"`)
.join(
', '
)}) are not supported in ".eas/build/*.yml" custom builds. Local composite functions can only be used in EAS workflows (".eas/workflows/*.yml").`
);
}
const externalFunctionIdsSet = new Set(externalFunctionIds);
const externalFunctionGroupsIdsSet = new Set(externalFunctionGroupsIds);
const nonExistentFunctionsOrFunctionGroups = calledFunctionsOrFunctionGroup.filter(
Expand Down
34 changes: 34 additions & 0 deletions packages/steps/src/__tests__/BuildConfig-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,40 @@ describe(validateAllFunctionsExist, () => {
validateAllFunctionsExist(buildConfig, { externalFunctionIds: [] });
}).toThrowError(/Calling non-existent functions: "eas\/build"/);
});
test('rejects local composite function references with a dedicated error', () => {
const buildConfig: BuildConfig = {
build: {
steps: ['./.eas/functions/my-function'],
},
};

expect(() => {
validateAllFunctionsExist(buildConfig, { externalFunctionIds: [] });
}).toThrowError(
/Local composite functions \("\.\/\.eas\/functions\/my-function"\) are not supported in "\.eas\/build\/\*\.yml" custom builds\. Local composite functions can only be used in EAS workflows \("\.eas\/workflows\/\*\.yml"\)\./
);
});
test('rejects object-form local composite function references with a dedicated error', () => {
const buildConfig: BuildConfig = {
build: {
steps: [
{
'./.eas/functions/my-function': {
inputs: {
foo: 'bar',
},
},
},
],
},
};

expect(() => {
validateAllFunctionsExist(buildConfig, { externalFunctionIds: [] });
}).toThrow(
/Local composite functions \("\.\/\.eas\/functions\/my-function"\) are not supported in "\.eas\/build\/\*\.yml" custom builds\. Local composite functions can only be used in EAS workflows \("\.eas\/workflows\/\*\.yml"\)\./
);
});
test('non-existent namespaced functions with skipNamespacedFunctionsOrFunctionGroupsCheck = false', () => {
const buildConfig: BuildConfig = {
build: {
Expand Down
15 changes: 15 additions & 0 deletions packages/steps/src/utils/__tests__/localCompositeFunctions-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { isLocalCompositeFunctionPath } from '../localCompositeFunctions';

describe(isLocalCompositeFunctionPath, () => {
it('recognizes relative paths as local composite function paths', () => {
expect(isLocalCompositeFunctionPath('./.eas/functions/setup')).toBe(true);
expect(isLocalCompositeFunctionPath('../../shared/functions/setup')).toBe(true);
expect(isLocalCompositeFunctionPath(' ./.eas/functions/setup/ ')).toBe(true);
});

it('rejects function ids and absolute or backslash-prefixed paths', () => {
expect(isLocalCompositeFunctionPath('eas/build')).toBe(false);
expect(isLocalCompositeFunctionPath('/functions/setup')).toBe(false);
expect(isLocalCompositeFunctionPath('..\\functions\\setup')).toBe(false);
});
});
7 changes: 7 additions & 0 deletions packages/steps/src/utils/localCompositeFunctions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Local composite functions referenced via `uses: ./path` or `uses: ../path` in EAS workflows.
// Not supported in `.eas/build/*.yml` custom build configs.

export function isLocalCompositeFunctionPath(uses: string): boolean {
const trimmed = uses.trim();
return trimmed.startsWith('./') || trimmed.startsWith('../');
}
Loading