[steps][build-tools][eas-cli] Expand legacy command/path local functions to a single build step; rename references in the consumers - #4096
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## szymonswierk/eng-25402-eas-build-job-legacy-function-shape #4096 +/- ##
=============================================================================================
Coverage ? 63.45%
=============================================================================================
Files ? 1028
Lines ? 46952
Branches ? 9914
=============================================================================================
Hits ? 29790
Misses ? 15707
Partials ? 1455 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
b77819d to
b43b302
Compare
22be929 to
a49aa69
Compare
a49aa69 to
0df6118
Compare
0df6118 to
2e32e71
Compare
2e32e71 to
3ba680d
Compare
3ba680d to
096a5fd
Compare
096a5fd to
04ebb71
Compare
🤖 AI code reviewDecision: Ready for human review (with comments) Overall PR risk: Medium. The change renames the legacy "composite local function" path to a general "local function" path across 🟡 Warning (1)
This review is advisory — it never blocks a merge and never auto-approves. |
2e21d7e to
d544e0c
Compare
d4e75da to
5b53032
Compare
|
Re. AI review:
Not something specific to this PR, ignoring |
|
Subscribed to pull request
Generated by CodeMention Warning: The preamble and epilogue options in commentConfiguration are deprecated. Use template instead. |
hSATAC
left a comment
There was a problem hiding this comment.
I took a pass through this and left three inline comments, mostly around compatibility with existing custom build functions. I don't have much context on the legacy behavior, so I'm calling out differences I noticed rather than making a call on which ones we need to preserve.
I'll leave the final approval to Stanley since he has more context on the intended compatibility scope.
| return BuildStepInput.createProvider({ | ||
| id: input.name, | ||
| required: input.required ?? true, | ||
| defaultValue: input.default_value, |
There was a problem hiding this comment.
This looks like a compatibility gap for two kinds of function.yml that are accepted by custom builds today. Both pass eas workflow:validate because it only builds the catalog, then fail when the worker parses the step.
For typed scalars, the Joi schema coerces quoted defaults and allowed values to their declared type. Here the raw values are passed through, so something like default_value: "42" for a number input remains a string and fails parse-time validation, even though getValue() would coerce it correctly at runtime.
For JSON inputs, an object default can never match an object in allowed_values because isRawValueOneOfAllowedValues() uses Array.includes(), while Joi compares them structurally:
inputs:
- name: payload
type: json
default_value: { a: 1 }
allowed_values: [{ a: 1 }]
command: echo hiCould we preserve the Joi behavior here by coercing typed scalars and comparing JSON values structurally? Otherwise, I think these should at least fail during catalog loading with a field-level error, so eas workflow:validate catches them.
|
|
||
| const result = LocalFunctionConfigZ.safeParse(parsed); | ||
| if (!result.success) { | ||
| throw new Error(`Invalid local function "${functionPath}": ${z.prettifyError(result.error)}`); |
There was a problem hiding this comment.
Non-blocking, but parsing the union here seems to make the validation errors less useful for both shapes.
A composite function.yml with a typo like shel: used to report Unrecognized key: "shel". Now it gets the generic three-shapes message. Legacy functions lose their field-level errors too; for example:
command: echo hi
supported_platforms: [windows]reports that it must declare one of runs.steps, command, or path, even though command is already present.
Could we use the key that is present (runs, command, or path) to re-parse the matching branch and report its errors, keeping the generic message for cases where the shape is actually ambiguous? Since this PR is where the union starts being used in production, I think it may be worth handling here.
There was a problem hiding this comment.
Addressed it in PR lower in the stack, by implementing a custom error logic: #4095
| name: call.name ?? config.name ?? functionPath, | ||
| callInputs: call.callWith, | ||
| workingDirectory: call.workingDirectory, | ||
| shell: config.shell, |
There was a problem hiding this comment.
Non-blocking question: is honoring the function-level shell here intentional?
From what I can tell, custom builds don't use BuildFunction.shell; only the call-site shell is passed through. That means a shell: declared by the function is currently ignored and the command runs with the default /bin/bash -eo pipefail. After migration, the same function would use its declared shell and lose the default -eo pipefail behavior.
Honoring the declaration may be the better behavior, but it does mean migration can change how an existing function runs. If that's intentional, could we adjust the “runs it exactly like a custom build does” docstring in utils/legacyFunction.ts?
There was a problem hiding this comment.
Yes, this was intentional even though it's a real behavior divergence. My thinking is: since we can easily match user's intent, let's do that instead of preserving the confusing behavior.
Clarified the module doc that stated that there are "no differences" in behavior.
A local function can now be a composite function or a single-step command/path function, so the "composite function" naming throughout packages/steps (and its build-tools/eas-cli consumers) is renamed to the more general "local function". Pure rename: no behavior, wording, or type changes.
…m workflows Folded from #4098, which added only this test and CHANGELOG entry.
…he legacy function, with respect to shell option
|
✅ Thank you for adding the changelog entry! |
Why
We want to grandfather the legacy reusable functions from custom builds (https://docs.expo.dev/custom-builds/functions/) into the new reusable functions that can be used in workflows. This is to make the migration from custom builds to workflows easier.
This PR adds support for parsing the legacy custom build functions into workflow steps.
How
First, across
steps, and the consumers (build-toolsandeas-cli):The actual feature in
steps:utils/localFunctions.ts).path:have thepathresolved in this step.BuildFunction(utils/legacyFunction.ts).LocalFunctionExpander).BuildStepalready has the machinery to run such function.working_directoryset by the caller is allowed, because there's just one step.This affects consumers:
build-tools: legacy functions referenced in workflows, hooks or composite functions used in workflows are now loaded.eas-cli:eas workflow:validateaccepts legacy functions.Test Plan
Added unit tests.