[build-tools] Load local composite function catalog at build runtime - #3930
Conversation
72189ae to
4764420
Compare
10a8039 to
32e1305
Compare
4764420 to
56ee52f
Compare
32e1305 to
d95f9f7
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3930 +/- ##
==========================================
+ Coverage 62.15% 62.20% +0.05%
==========================================
Files 993 994 +1
Lines 44658 44716 +58
Branches 9403 9409 +6
==========================================
+ Hits 27754 27812 +58
- Misses 15455 15457 +2
+ Partials 1449 1447 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
d95f9f7 to
e5d34d9
Compare
56ee52f to
e436769
Compare
e5d34d9 to
4f20b26
Compare
e436769 to
92fe812
Compare
4f20b26 to
d6b61d3
Compare
3e774f9 to
f74b6da
Compare
d6b61d3 to
00ab20d
Compare
f74b6da to
b5be21a
Compare
506eea1 to
0eb9999
Compare
b5be21a to
1e89296
Compare
0eb9999 to
b8bb5e0
Compare
1e89296 to
4f79fdc
Compare
4f79fdc to
2190527
Compare
b8bb5e0 to
6d1c11d
Compare
2190527 to
8d70398
Compare
6d1c11d to
899157c
Compare
ea64846 to
31f7032
Compare
31e714c to
78b4981
Compare
b1da672 to
f8463eb
Compare
a4a93c7 to
6dc8837
Compare
f8463eb to
28fbfd1
Compare
6dc8837 to
c64ad5c
Compare
28fbfd1 to
78a6801
Compare
c64ad5c to
63d2c7b
Compare
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
| @@ -0,0 +1,85 @@ | |||
| /** | |||
| * Loads local function.yml files from the project and builds the catalog consumed by | |||
| * {@link StepsConfigParser}. Keeps filesystem I/O in build-tools; expansion logic lives in @expo/steps. | |||
There was a problem hiding this comment.
Is keeping filesystem io in build tools a goal for us?
There was a problem hiding this comment.
Not a hard goal, just me being careful about not breaking any boundaries here. We could move this to steps, which would also help with the duplication between build-tools and eas-cli #3931 (comment) if you think that is okay
sjchmiela
left a comment
There was a problem hiding this comment.
supposedly these two tests are going to fail
| CompositeFunctionConfigZ.parse({ runs: { steps: [{ run: 'echo setup' }] } }), | ||
| }); | ||
| expect(Object.keys(catalog)).toEqual([]); | ||
| }); |
There was a problem hiding this comment.
| }); | |
| }); | |
| it('does not load composite functions beyond the maximum nesting depth', async () => { | |
| const paths = Array.from({ length: 11 }, (_, index) => `./.eas/functions/a${index}`); | |
| const loadCompositeFunction = jest.fn(async (compositeFunctionPath: string) => { | |
| const index = paths.indexOf(compositeFunctionPath); | |
| if (index === 10) { | |
| throw new Error('function beyond maximum depth was loaded'); | |
| } | |
| return CompositeFunctionConfigZ.parse({ | |
| runs: { | |
| steps: [{ uses: paths[index + 1] }], | |
| }, | |
| }); | |
| }); | |
| const catalog = await buildCompositeFunctionCatalogFromStepsAsync({ | |
| rootSteps: [{ uses: paths[0] }], | |
| loadCompositeFunction, | |
| }); | |
| expect(loadCompositeFunction).toHaveBeenCalledTimes(10); | |
| expect(Object.keys(catalog)).toEqual(paths.slice(0, 10)); | |
| }); | |
| it('does not let an error beyond the maximum depth mask the nesting-depth error', async () => { | |
| const paths = Array.from({ length: 11 }, (_, index) => `./.eas/functions/a${index}`); | |
| const catalog = await buildCompositeFunctionCatalogFromStepsAsync({ | |
| rootSteps: [{ uses: paths[0] }], | |
| loadCompositeFunction: async compositeFunctionPath => { | |
| const index = paths.indexOf(compositeFunctionPath); | |
| if (index === 10) { | |
| throw new Error('missing composite function beyond maximum depth'); | |
| } | |
| return CompositeFunctionConfigZ.parse({ | |
| runs: { | |
| steps: [{ uses: paths[index + 1] }], | |
| }, | |
| }); | |
| }, | |
| }); | |
| expect(Object.keys(catalog)).toEqual(paths.slice(0, 10)); | |
| }); |
There was a problem hiding this comment.
I think it's not that bad if we let build-tools load those files that user intends to load. The depth limit is an expansion time concern and it's going to error there, so the logic stays isolated.
…romSteps function
|
✅ Thank you for adding the changelog entry! |
|
Merged with 2 non-blocking open discussions to no longer block this feature. There are similar/related PRs coming with hooks support, so we can follow up there. |

Why
This PR stack adds local composite functions for EAS workflows (
uses: ./.eas/functions/...). Earlier PRs define the schema, expand calls into steps, and wire inputs/outputs. The build runtime still needs to load thosefunction.ymlfiles from the project and pass them intoStepsConfigParser.This PR is the I/O layer: discover referenced local composite functions on disk, validate them, and hand the catalog to the parser for custom builds and generic jobs.
How
@expo/steps(buildCompositeFunctionCatalogFromStepsAsync,resolveLocalCompositeFunctionPath)packages/build-tools/src/steps/compositeFunctions.tsto resolve paths relative to the EAS project root, readfunction.yml/function.yaml, and validate withCompositeFunctionConfigZrunCustomBuildAsyncandrunGenericJobAsyncTest Plan
Added unit tests for catalog loading and custom-build parse integration.