Skip to content

feat(release-tests): prepare setup script for automated app builds - #4474

Open
Loloekk wants to merge 72 commits into
mainfrom
@Loloekk/setup-tests-builds-on-fresh-applications
Open

feat(release-tests): prepare setup script for automated app builds#4474
Loloekk wants to merge 72 commits into
mainfrom
@Loloekk/setup-tests-builds-on-fresh-applications

Conversation

@Loloekk

@Loloekk Loloekk commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Description

Adds a release_tests setup script that creates a fresh React Native app and builds it against a chosen React Native version and react-native-screens version. This makes release verification faster and more repeatable without manually wiring each playground app.

Usage

node release_tests/create_playground.js
node release_tests/create_playground.js -h   # full help

Setup options

  • React Native version (-r / --rn-version)
  • react-native-screens version (-s / --screens-version) — use the current worktree (current), or a branch, tag, or commit from the local/remote react-native-screens repository. By default the script take a local ref.
  • From-origin (-o / --from-origin) — take RNS from the remote repository
  • Example app (-e / --example-app) — one of the examples:
    • tabsAndStack — Stack v5 (RNS 5.x)
    • tabsAndStack4.x — legacy Stack v4 + Tabs
    • empty — keep the default RN app
    • user could add his own example, and use it to create a new app
  • app name (-a / --app-name) sets the name of the created app
  • Help (-h / --help) — explains all flags

Running the app

After setup, the script can build and launch the app with --run. Run-related flags are only allowed when --run is set.

  • Variant (-v) — debug or release
  • Platform (-p) — ios, android, or both
  • Device flags — same idea as yarn/RN CLI (--ios-simulator, --ios-device, --ios-udid, --android-device). At most one device can be selected per platform.

Notes

  • RNS depends on the kconv library. Older RN versions (e.g. around 0.84) don’t pull it in automatically. Ruby < 3.4 provides it via the stdlib; on Ruby 3.4+ with those RN versions, setup fails without it. The script detects this and adds the nkf gem (which includes kconv).
  • Before running, the script kills any Metro process on port 8081.
  • For Android, start the emulator first (or connect a device). --android-device expects an adb id from adb devices (e.g. emulator-5554), not the AVD name. If the emulator is not running, the build may still succeed but the app will not launch.
  • All screens versions other than current are cloned into a temporary folder and prepared there.
  • From the example app, only App.tsx and src/ (if present) are copied.
  • Project init uses:
    npx @react-native-community/cli@latest init ${appName} --version ${rnVersion} --skip-install
  • Logs from running programs are written to setup.log.

Test plan

1. Conflicting / invalid flags

  • node release_tests/create_playground.js -s current -o
  • node release_tests/create_playground.js -v release
  • node release_tests/create_playground.js -p ios
  • node release_tests/create_playground.js --ios-simulator "iPhone 17"
  • node release_tests/create_playground.js --run --ios-simulator "iPhone 17" --ios-device "Karol"
  • node release_tests/create_playground.js --run -p ios --android-device "emulator-5554"
  • node release_tests/create_playground.js --run -p android --ios-simulator "iPhone 17"

2. Happy paths

  • Setup only (current worktree, default example):

    node release_tests/create_playground.js -a SmokeCurrent

    Check setup.log: init + copy tabsAndStack + pack/install current screens; no native build.

  • Setup + run both platforms:

    node release_tests/create_playground.js -r 0.84.0 -s current -e tabsAndStack -a SmokeBoth --run -p both --android-device "emulator-5554" --ios-simulator "iPhone 17"

    Check log: Metro 8081 freed, Android/iOS launch, Ruby 3.4 nkf handling if needed, navigation works.

  • Legacy 4.x + force-fetch:

    node release_tests/create_playground.js -r 0.84.0 -s branch:4.26-stable -f -e tabsAndStack4.x -a SmokeLegacy --run -p ios --ios-simulator "iPhone 17"

    Check log: remote clone (not current pack), app runs.

  • Commit + release:

    node release_tests/create_playground.js -r 0.84.0 -s commit:886754c6e0387c9a486c8c6f601abf7957557763 -f -a SmokeGamma --run -v release -p ios --ios-simulator "iPhone 17" -e tabsAndStack4.x

    Check log: --mode Release, app runs.

  • Empty example, Android only:

    node release_tests/create_playground.js -e empty -s tag:4.27.0 -f -a SmokeEmpty --run -p android --android-device "emulator-5554"

    Check log: no example copy, no iOS steps, Android launches.

Checklist

  • Included code example that can be used to test this change.
  • For visual changes, included screenshots / GIFs / recordings documenting the change.
  • For API changes, updated relevant public types.
  • Ensured that CI passes

@Loloekk Loloekk self-assigned this Aug 6, 2026
@Loloekk Loloekk added type:feature New feature or request type:test Actions related to test maintenance and development, especially manual. labels Aug 6, 2026
@Loloekk
Loloekk requested a review from t0maboro August 13, 2026 07:04
}

// remove this helper when we drop support for 0.84.
function canRequireKconv(appPath, { runCommand, logPath }) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function canRequireKconv(appPath, { runCommand, logPath }) {
function isKconvAvailable(appPath, { runCommand, logPath }) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ad0ddf1

const gemfile = fs.readFileSync(gemfilePath, 'utf8');

if (gemfileHasGem(gemfile, 'nkf')) {
// if the gemfile has the gem name, return

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment doesn't add any value, should be removed

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ad0ddf1

}

if (canRequireKconv(appPath, { runCommand, logPath })) {
// if the gem can require kconv, return

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment doesn't add any value, should be removed

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ad0ddf1

`\n⚠️ Gemfile is missing 'nkf' (required to load kconv). Adding it...`,
);
runCommand('bundle', ['add', 'nkf'], appPath, logPath);
// `bundle add nkf` deafult make bundle install, so we don't need to run it again

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd put it above the line it touches; in this form, it can cause some confusion about whether there was some content below that comment at some point

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ad0ddf1

['exec', 'ruby', '-e', "require 'kconv'"],
appPath,
logPath,
true,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about that, because it will produce an error in the output for which we make recovery internally. putting some log before that's indicating that this command might fail is good enough for me.

@Loloekk Loloekk Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 544ab1a

Comment thread release_tests/create_playground/tasks/setupScreens.js
`\n⚠️ Gemfile is missing 'nkf' (required to load kconv). Adding it...`,
);
runCommand('bundle', ['add', 'nkf'], appPath, logPath);
// `bundle add nkf` deafult make bundle install, so we don't need to run it again

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo in 'default'

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ad0ddf1

const METRO_PORT = 8081;

function formatCommand(file, args = []) {
return [file, ...args].join(' ');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formatting should include quoting

function formatCommand(file, args = []) {
  return [file, ...args].join(' ');
}

console.log(formatCommand("file", ["--flag", "some value with spaces"]))

when you'll have some value with spaces and just copy and paste the output to the console, some value with spaces will be treated by the shell as a list of subsequent arguments

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2f467d3


function isMetroRespondingOnPort(port) {
try {
const response = execSync(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

execFileSync?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b55c338.

}

function sleepSync(ms) {
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It may throw if we use it incorrectly, or if the current thread cannot be blocked. In our case we use it correctly, and the thread our script runs on can always be blocked because it is run by Node.

@Loloekk
Loloekk force-pushed the @Loloekk/setup-tests-builds-on-fresh-applications branch from cff6821 to 6b004a5 Compare August 18, 2026 08:16
@Loloekk
Loloekk force-pushed the @Loloekk/setup-tests-builds-on-fresh-applications branch from 6b004a5 to 544ab1a Compare August 18, 2026 08:18
@Loloekk

Loloekk commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

I decided to force the user to use a reference type every time, instead of the current option. The script also does not try to fetch from origin if the target is absent locally. To fetch the target from origin, --force-fetch is required.

Fetch only if -f is used: e78b08e
Force using the target type: be5e38c
I also refactored the errors: 638691b

@Loloekk
Loloekk requested a review from t0maboro August 18, 2026 14:38
@Loloekk Loloekk removed their assignment Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:feature New feature or request type:test Actions related to test maintenance and development, especially manual.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants