From c3e68495ab3d79852d23b8294b47f267d810396b Mon Sep 17 00:00:00 2001 From: sswrk Date: Thu, 23 Jul 2026 15:29:01 +0200 Subject: [PATCH] [eas-cli] Validate local composite functions referenced from workflow hooks --- CHANGELOG.md | 1 + .../__tests__/compositeFunctions-test.ts | 66 +++++++++++++++++++ .../workflow/compositeFunctions.ts | 13 +++- 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33e1cd582e..3f51456fa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This is the log of notable changes to EAS CLI and related packages. - [build-tools] Load local composite function catalogs at build time so custom builds and generic jobs can resolve local composite functions referenced via `uses:`. ([#3930](https://github.com/expo/eas-cli/pull/3930) by [@sswrk](https://github.com/sswrk)) - [eas-cli] Validate local composite functions referenced in workflow files during `eas workflow:validate`, checking that the YAML files exist and are well-formed. ([#3931](https://github.com/expo/eas-cli/pull/3931) by [@sswrk](https://github.com/sswrk)) - [build-tools] Load local composite function catalogs for hook steps, in both steps-based and native builds. ([#4063](https://github.com/expo/eas-cli/pull/4063) by [@sswrk](https://github.com/sswrk)) +- [eas-cli] Validate local composite functions referenced from workflow job hooks during `eas workflow:validate`. ([#4064](https://github.com/expo/eas-cli/pull/4064) by [@sswrk](https://github.com/sswrk)) ### 🐛 Bug fixes diff --git a/packages/eas-cli/src/commandUtils/workflow/__tests__/compositeFunctions-test.ts b/packages/eas-cli/src/commandUtils/workflow/__tests__/compositeFunctions-test.ts index 66bbf27797..a2d8626962 100644 --- a/packages/eas-cli/src/commandUtils/workflow/__tests__/compositeFunctions-test.ts +++ b/packages/eas-cli/src/commandUtils/workflow/__tests__/compositeFunctions-test.ts @@ -240,6 +240,72 @@ describe(validateWorkflowLocalCompositeFunctionsAsync, () => { ).resolves.toBeUndefined(); }); + it('validates local composite functions referenced from job hooks', async () => { + const projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-workflow-functions-hooks-')); + await makeProjectWithCompositeFunctionAsync( + projectRoot, + 'setup', + ['runs:', ' steps:', ' - run: echo setup'].join('\n') + ); + const workflow = { + jobs: { + job: { + steps: [{ run: 'echo hi' }], + hooks: { + before_install_node_modules: [{ uses: './.eas/functions/setup' }], + }, + }, + }, + }; + + await expect( + validateWorkflowLocalCompositeFunctionsAsync(workflow, projectRoot) + ).resolves.toBeUndefined(); + }); + + it('throws when a composite function referenced from a job hook does not exist', async () => { + const projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-workflow-functions-hooks-')); + const workflow = { + jobs: { + job: { + steps: [{ run: 'echo hi' }], + hooks: { + before_install_node_modules: [{ uses: './.eas/functions/setup' }], + }, + }, + }, + }; + + await expect( + validateWorkflowLocalCompositeFunctionsAsync(workflow, projectRoot) + ).rejects.toThrow( + /Local composite function "\.\/\.eas\/functions\/setup" was referenced by a step but no such composite function exists/ + ); + }); + + it('ignores non-object hooks and non-array hook values', async () => { + const projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-workflow-functions-hooks-')); + const workflow = { + jobs: { + garbageHooks: { + steps: [{ run: 'echo hi' }], + hooks: 'not an object', + }, + garbageHookValue: { + steps: [{ run: 'echo hi' }], + hooks: { + before_install_node_modules: 'not an array', + after_install_node_modules: { uses: './.eas/functions/missing' }, + }, + }, + }, + }; + + await expect( + validateWorkflowLocalCompositeFunctionsAsync(workflow, projectRoot) + ).resolves.toBeUndefined(); + }); + it('throws when a referenced local composite function is malformed', async () => { const projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-workflow-functions-test-')); await makeProjectWithCompositeFunctionAsync( diff --git a/packages/eas-cli/src/commandUtils/workflow/compositeFunctions.ts b/packages/eas-cli/src/commandUtils/workflow/compositeFunctions.ts index 9db7d818a5..a673239886 100644 --- a/packages/eas-cli/src/commandUtils/workflow/compositeFunctions.ts +++ b/packages/eas-cli/src/commandUtils/workflow/compositeFunctions.ts @@ -91,5 +91,16 @@ function stepsFromWorkflow(parsedYaml: any): any[] { if (!jobs || typeof jobs !== 'object') { return []; } - return Object.values(jobs).flatMap((job: any) => (Array.isArray(job?.steps) ? job.steps : [])); + return Object.values(jobs).flatMap((job: any) => [ + ...(Array.isArray(job?.steps) ? job.steps : []), + ...hookStepsFromJob(job), + ]); +} + +function hookStepsFromJob(job: any): any[] { + const hooks = job?.hooks; + if (!hooks || typeof hooks !== 'object') { + return []; + } + return Object.values(hooks).flatMap((steps: any) => (Array.isArray(steps) ? steps : [])); }