Skip to content

fix(runscript): run a script file as written - #194

Merged
omnarayan merged 2 commits into
devicelab-dev:mainfrom
vyrahealth:upstream-pr/b2-runscript-file-as-written
Oct 3, 2026
Merged

omnarayan merged 2 commits into
devicelab-dev:mainfrom
vyrahealth:upstream-pr/b2-runscript-file-as-written

Conversation

@bulatgaleev

@bulatgaleev bulatgaleev commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Maestro runs a runScript file as plain JavaScript. It reads the file and evaluates ${...} only in the step's env, when: condition and label; the script text goes to the engine as it is (YamlFluentCommand.kt, RunScriptCommand.evaluateScripts in Commands.kt, runScriptCommand in Orchestra.kt, all at cli-2.10.0). maestro-runner expanded ${...} and $VAR across the whole file before running it, so a template literal that used the script's own variables was replaced ahead of the script, against variables that did not exist yet: `/v1/x?email=${encodeURIComponent(who)}&state=${state}` came out as /v1/x?email=undefined&state=. A script file now runs as written. Inline script text, which Maestro has no equivalent of, keeps the expansion.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)

Changes Made

  • ExecuteRunScript runs a .js file without expanding it first. RunScript still expands, so inline script text is unchanged.
  • TestScriptEngine_ExecuteRunScript_FileTemplateLiteral in pkg/executor/scripting_test.go. Without the change it fails with url = "/v1/x?email=undefined&state=".
  • A CHANGELOG.md entry.

Related Issues

No existing issue found.

Testing

  • go test ./pkg/executor/
  • Added tests for new functionality
  • Part of our fork's build, which runs a 47-flow suite every night on a physical iPhone (iOS 27, WebDriverAgent reached through a forwarded port). The tests above are the evidence for this change, not that suite
  • go test -race for pkg/executor, pkg/driver/wda, pkg/jsengine, pkg/flow and pkg/driver/uiautomator2 passes at the top of the stack; go vet ./..., gofmt -l and golangci-lint v2.13.2 (the version CI pins) report nothing on every branch of the stack
  • make test as a whole: not run, because its device tests drive whatever device is attached to the machine
  • make lint: the Makefile has no lint target, and the other linters make check runs (staticcheck, revive, errcheck, nilaway, gosec) are not installed here

Checklist

  • Code follows project style guidelines
  • Self-reviewed the code
  • Added/updated documentation as needed (no documentation change needed)
  • No breaking changes (or documented if breaking)
  • CHANGELOG.md updated (for notable changes)

Additional Notes

A script file that counted on the early expansion no longer gets it: var name = "${NAME}" in a .js file now holds the text ${NAME}, as it does under Maestro. The variable is there as NAME.

Stack 2 of 7: #200 → #194 → #195 → #196 → #197 → #198 → #199. Merge in that order. This branch is built on #200, so it also contains their commits. This PR's own change is the top commit, a289cc7. Once the PRs below it merge, the rest of the diff disappears, and nothing conflicts. All seven sit on main at d3f738f (the merge of #185). #200 only removes a function that makes Lint fail on main (see its description); it is in this stack so that Lint passes here too.

@codecov

codecov Bot commented Oct 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/executor/scripting.go 85.71% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

…lls any more

30257df (Android device queries match the whole text and id) took out the last
caller of this helper in the uiautomator2 driver. The unused linter has flagged
it since, so Lint has failed on every push to main, and Build, which needs
Lint, has been skipped each time. The same helper in the appium and devicelab
drivers is untouched.
Maestro runs a runScript file as plain JavaScript. It reads the file and
evaluates ${...} only in the step's env, when: condition and label; the
script text goes to the engine as it is (YamlFluentCommand.kt:409-424,
Commands.kt:1029-1035, Orchestra.kt:723-737).

The runner expanded ${...} and $VAR across the whole file before running
it. A template literal that used the script's own variables was replaced
ahead of the script, against variables that did not exist yet:
`/v1/x?email=${encodeURIComponent(who)}&state=${state}` came out as
`/v1/x?email=undefined&state=`.

A script file now runs as written. Inline script text, which Maestro has no
equivalent of, keeps the expansion.
@omnarayan

Copy link
Copy Markdown
Contributor

Thanks @bulatgaleev, good catch. Expanding ${...} across the whole script file broke the file's own template literals. Expo's image-comparison script hit the same bug (${SERVER_URL}/process turned into /process). Running script files as written matches Maestro, and keeping expansion for inline script text leaves existing flows alone. Merging.

@omnarayan
omnarayan merged commit 3981495 into devicelab-dev:main Oct 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants