ci: route Maestro e2e selection through sniffler impact analysis - #7476
Conversation
Regular dev PRs (and local runs) now execute only the Maestro flows sniffler flags as impacted; release-cut PRs still run the full 14-shard suite. Under-selection is impossible — every uncertainty falls back to the full suite. CI (build-pr.yml): - e2e-shards runs first and narrows the shard matrix via select-impacted-shards.sh (release-lane / no-base / merge-base-fail / sniffler-error / bad-JSON / run-all all fall back to full 14). - A confident zero (no impacted flow, e.g. docs-only) sets should_run=false, skipping the whole e2e stage (no approval, no native build, no flows). - New always() e2e-result job is the single required e2e check: green on a confident-zero skip, green iff both run jobs pass, red otherwise — never hangs "Waiting for status". Local: pnpm e2e:changed <android|ios> runs the impacted flows against a booted device, working-tree-aware, mirroring CI's tag excludes. Coverage: .sniffler config maps runtime-decoupled files the import graph can't trace — domain sagas anchored to their flows, global sagas / routers / nav stacks / root container / store / root reducer in runAllWhenChanged. Freshness: validate-test-map.js (wired into eslint.yml) fails a PR on orphan flows, dangling globs, stale globals, or any uncovered saga/stack (decoupled gap); warns on uncovered views. NOTE for whoever merges: branch-protection must be reconfigured to require the e2e-result check and de-require the per-shard + build contexts, or a confident-zero PR hangs on missing contexts.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📜 Recent review details⏰ Context from checks skipped due to timeout. (3)
|
| Layer / File(s) | Summary |
|---|---|
Sniffler configuration and test-map data .sniffler/*, .gitignore, .github/scripts/testlib/fixtures/maps/*, .github/scripts/__tests__/fixtures/flows/* |
Defines source roots, run-all triggers, Maestro-to-source mappings, cache handling, and validation fixtures. |
Local changed-flow E2E runner .github/scripts/e2e-changed.sh, .github/scripts/testlib/runScript.js, .github/scripts/__tests__/changed-runner.test.js, package.json |
Collects changed paths, selects impacted flows, runs platform-specific Maestro tests, and validates command behavior. |
CI impacted-shard selection .github/scripts/select-impacted-shards.sh, .github/scripts/__tests__/select-impacted-shards.test.js, .github/scripts/__tests__/coverage.test.js, .github/scripts/__tests__/fixtures/scenario-catalog.json |
Emits selected shards and should_run, with full-suite fallback, confident-zero skipping, and scenario coverage. |
Test-map freshness validation .github/scripts/validate-test-map.js, .github/scripts/__tests__/validate-test-map.test.js |
Checks orphan flows, dangling globs, uncovered views, stale global paths, and decoupled saga or stack files. |
CI workflow integration .github/workflows/build-pr.yml, .github/workflows/eslint.yml, .github/README.md |
Adds the E2E preflight, gates Android and iOS jobs, aggregates results, runs map validation, and updates CI documentation. |
Estimated code review effort: 4 (Complex) | ~60 minutes
Sequence Diagram(s)
sequenceDiagram
participant build_pr as build-pr.yml
participant select as select-impacted-shards.sh
participant sniffler as sniffler impact
participant hold as e2e-hold
participant e2e as Android/iOS E2E jobs
participant result as e2e-result
build_pr->>select: Run E2E preflight
select->>sniffler: Request impacted tests
sniffler-->>select: Return recommendedTests
alt should_run=true
select-->>build_pr: Emit shards and should_run=true
build_pr->>hold: Request approval
hold->>e2e: Run selected platform jobs
e2e-->>result: Return platform outcomes
else should_run=false
select-->>build_pr: Emit empty shards and should_run=false
build_pr->>result: Skip E2E jobs
end
result-->>build_pr: Report aggregate status
Possibly related PRs
- RocketChat/Rocket.Chat.ReactNative#7320: Both changes use platform-specific Maestro app identifiers and flow selection.
- RocketChat/Rocket.Chat.ReactNative#7445: Both changes update CI workflow documentation and execution gates.
Suggested labels: type: feature
Suggested reviewers: otaviostasiak
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title clearly and concisely describes the main change: routing Maestro E2E selection through Sniffler impact analysis. |
| Linked Issues check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
- Create stacked PR
- Commit on current branch
Warning
Review ran into problems
🔥 Problems
Errors were encountered while retrieving linked issues.
Errors (2)
- NATIVE-1405: Request failed with status code 401
- NATIVE-1408: Request failed with status code 401
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/scripts/select-impacted-shards.sh:
- Around line 46-62: The `select-impacted-shards.sh` logic only checks that
`recommendedTests` exists, so a `null` value can slip through and make the
`.recommendedTests[]` queries in the run-all and paths handling behave like “no
tests,” causing an incorrect confident-zero skip. Update the validation around
the `recommendedTests` check in this script to require that `recommendedTests`
is an array before proceeding, and keep malformed payloads on the
full/emit-false path rather than letting `paths` become empty. Use the existing
`recommendedTests` handling block and the `paths`/`emit` flow to place the type
guard so under-selection cannot happen.
In @.github/scripts/validate-test-map.js:
- Around line 69-70: The uncovered-view check in validate-test-map.js is using a
prefix match that can falsely treat sibling directories as covered. Update the
uncovered calculation around allDependsOn and viewDirs so it only matches an
exact view anchor or a true descendant path, not any string that merely starts
with the directory name. Keep the fix localized to the coverage filter logic and
preserve the intended warning behavior for real uncovered view directories.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9a0aea5b-415f-4839-88ba-cb5624c68c9d
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (9)
.github/scripts/e2e-changed.sh.github/scripts/select-impacted-shards.sh.github/scripts/validate-test-map.js.github/workflows/build-pr.yml.github/workflows/eslint.yml.gitignore.sniffler/config.json.sniffler/test-map.jsonpackage.json
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
- GitHub Check: E2E Shard Preflight
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-02-05T13:55:00.974Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6930
File: package.json:101-101
Timestamp: 2026-02-05T13:55:00.974Z
Learning: In this repository, the dependency on react-native-image-crop-picker should reference the RocketChat fork (RocketChat/react-native-image-crop-picker) with explicit commit pins, not the upstream ivpusic/react-native-image-crop-picker. Update package.json dependencies (and any lockfile) to point to the fork URL and a specific commit, ensuring edge-to-edge Android fixes are included. This pattern should apply to all package.json files in the repo that declare this dependency.
Applied to files:
package.json
📚 Learning: 2026-05-07T17:47:14.516Z
Learnt from: diegolmello
Repo: RocketChat/Rocket.Chat.ReactNative PR: 7303
File: package.json:5-5
Timestamp: 2026-05-07T17:47:14.516Z
Learning: When reviewing pnpm `packageManager` version pins in any `package.json` (e.g., `"packageManager": "pnpm@<version>"`), don’t rely solely on web-search results to determine whether a version exists. For very recently published versions, cross-check the target version against the official pnpm release page (https://github.com/pnpm/pnpm/releases) and the npm registry page for pnpm (https://www.npmjs.com/package/pnpm) before flagging the pinned version as non-existent.
Applied to files:
package.json
🪛 ast-grep (0.44.1)
.github/scripts/validate-test-map.js
[warning] 34-34: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(TEST_MAP_PATH, 'utf8')
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
(detect-non-literal-fs-filename)
[warning] 35-35: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(CONFIG_PATH, 'utf8')
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
(detect-non-literal-fs-filename)
[warning] 40-40: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(f, 'utf8')
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
(detect-non-literal-fs-filename)
🪛 zizmor (1.26.1)
.github/workflows/build-pr.yml
[warning] 74-77: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[warning] 107-107: secrets unconditionally inherited by called workflow (secrets-inherit): this reusable workflow
(secrets-inherit)
[warning] 127-127: secrets unconditionally inherited by called workflow (secrets-inherit): this reusable workflow
(secrets-inherit)
🔇 Additional comments (8)
.github/scripts/e2e-changed.sh (1)
1-54: LGTM!package.json (2)
22-22: LGTM!
216-220: Checksniffler's published version
package.jsonpinssnifflerto^0.4.0, and the lockfile currently resolvessniffler@0.4.0. Keep this only if that release is the intended published version..sniffler/config.json (1)
1-33: LGTM!.sniffler/test-map.json (1)
1-336: LGTM!.gitignore (1)
98-98: LGTM!.github/workflows/build-pr.yml (1)
63-177: LGTM!.github/workflows/eslint.yml (1)
22-24: LGTM!
- Add unit tests for select-impacted-shards.sh, e2e-changed.sh and validate-test-map.js: per-branch fixture maps plus a shell harness that stubs external deps (sniffler/git/maestro) on $PATH. They wire into the existing jest run, so CI's ESLint-and-test job exercises the selection tooling the PR previously only verified by hand. - Fix an uncovered-view false negative in validate-test-map.js: the prefix match let a view dir cover-match a longer dir (SomeView vs SomeViewExtra/**); anchor it on a trailing slash. Add a fixture that proves it. - Add a TESTMAP_ROOT override so the validator can run against fixtures. - Pin sniffler to an exact version: it gates what e2e runs, so range drift should be an explicit reviewed change (matches fast-glob's pin). - Update .github/README.md for the new e2e-shards preflight, the e2e-result required check, and the conditional approve_e2e_testing gate.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
.github/scripts/testlib/runScript.js (1)
45-48: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider escaping the
keybefore interpolating into the RegExp.The
getfunction interpolateskeydirectly into aRegExpconstructor. Current callers only passshardsandshould_run(no metacharacters), so this is safe today. Adding a simple escape would future-proof the helper against accidental misuse.♻️ Optional refactor
const get = key => { - const m = output.match(new RegExp(`^${key}=(.*)$`, 'm')); + const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const m = output.match(new RegExp(`^${escaped}=(.*)$`, 'm')); return m ? m[1] : undefined; };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/scripts/testlib/runScript.js around lines 45 - 48, The get helper in runScript.js interpolates key directly into the RegExp pattern, so update that lookup logic to escape any regex metacharacters in key before building the expression. Keep the change scoped to the get function so callers like the shards and should_run lookups continue to work, but future keys cannot accidentally change the match behavior. Ensure the escaped key is used in the RegExp constructor and the rest of the parsing logic stays the same.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In @.github/scripts/testlib/runScript.js:
- Around line 45-48: The get helper in runScript.js interpolates key directly
into the RegExp pattern, so update that lookup logic to escape any regex
metacharacters in key before building the expression. Keep the change scoped to
the get function so callers like the shards and should_run lookups continue to
work, but future keys cannot accidentally change the match behavior. Ensure the
escaped key is used in the RegExp constructor and the rest of the parsing logic
stays the same.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c0bdd7dc-adbf-47db-b80f-ed91a78bf69f
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (42)
.github/README.md.github/scripts/__tests__/changed-runner.test.js.github/scripts/__tests__/coverage.test.js.github/scripts/__tests__/fixtures/flows/no-tag.yaml.github/scripts/__tests__/fixtures/scenario-catalog.json.github/scripts/__tests__/select-impacted-shards.test.js.github/scripts/__tests__/validate-test-map.test.js.github/scripts/testlib/fixtures/maps/clean-pass/.maestro/tests/foo.yaml.github/scripts/testlib/fixtures/maps/clean-pass/.sniffler/config.json.github/scripts/testlib/fixtures/maps/clean-pass/.sniffler/test-map.json.github/scripts/testlib/fixtures/maps/clean-pass/app/sagas/foo.js.github/scripts/testlib/fixtures/maps/clean-pass/app/stacks/bar.tsx.github/scripts/testlib/fixtures/maps/clean-pass/app/views/Foo/placeholder.txt.github/scripts/testlib/fixtures/maps/dangling-glob/.maestro/tests/.gitkeep.github/scripts/testlib/fixtures/maps/dangling-glob/.sniffler/config.json.github/scripts/testlib/fixtures/maps/dangling-glob/.sniffler/test-map.json.github/scripts/testlib/fixtures/maps/dangling-glob/app/views/.gitkeep.github/scripts/testlib/fixtures/maps/decoupled-gap/.maestro/tests/.gitkeep.github/scripts/testlib/fixtures/maps/decoupled-gap/.sniffler/config.json.github/scripts/testlib/fixtures/maps/decoupled-gap/.sniffler/test-map.json.github/scripts/testlib/fixtures/maps/decoupled-gap/app/sagas/foo.js.github/scripts/testlib/fixtures/maps/decoupled-gap/app/views/.gitkeep.github/scripts/testlib/fixtures/maps/orphan-flow/.maestro/tests/orphan.yaml.github/scripts/testlib/fixtures/maps/orphan-flow/.sniffler/config.json.github/scripts/testlib/fixtures/maps/orphan-flow/.sniffler/test-map.json.github/scripts/testlib/fixtures/maps/orphan-flow/app/views/.gitkeep.github/scripts/testlib/fixtures/maps/prefix-collision/.maestro/tests/some.yaml.github/scripts/testlib/fixtures/maps/prefix-collision/.sniffler/config.json.github/scripts/testlib/fixtures/maps/prefix-collision/.sniffler/test-map.json.github/scripts/testlib/fixtures/maps/prefix-collision/app/views/SomeView/placeholder.txt.github/scripts/testlib/fixtures/maps/prefix-collision/app/views/SomeViewExtra/placeholder.txt.github/scripts/testlib/fixtures/maps/stale-global/.maestro/tests/.gitkeep.github/scripts/testlib/fixtures/maps/stale-global/.sniffler/config.json.github/scripts/testlib/fixtures/maps/stale-global/.sniffler/test-map.json.github/scripts/testlib/fixtures/maps/stale-global/app/views/.gitkeep.github/scripts/testlib/fixtures/maps/uncovered-view/.maestro/tests/.gitkeep.github/scripts/testlib/fixtures/maps/uncovered-view/.sniffler/config.json.github/scripts/testlib/fixtures/maps/uncovered-view/.sniffler/test-map.json.github/scripts/testlib/fixtures/maps/uncovered-view/app/views/SomeView/placeholder.txt.github/scripts/testlib/runScript.js.github/scripts/validate-test-map.jspackage.json
✅ Files skipped from review due to trivial changes (18)
- .github/scripts/testlib/fixtures/maps/decoupled-gap/.sniffler/config.json
- .github/scripts/testlib/fixtures/maps/stale-global/.sniffler/config.json
- .github/scripts/testlib/fixtures/maps/uncovered-view/.sniffler/config.json
- .github/scripts/testlib/fixtures/maps/prefix-collision/.maestro/tests/some.yaml
- .github/scripts/testlib/fixtures/maps/clean-pass/.sniffler/config.json
- .github/scripts/testlib/fixtures/maps/clean-pass/app/stacks/bar.tsx
- .github/scripts/testlib/fixtures/maps/uncovered-view/app/views/SomeView/placeholder.txt
- .github/scripts/testlib/fixtures/maps/clean-pass/app/sagas/foo.js
- .github/scripts/testlib/fixtures/maps/prefix-collision/.sniffler/config.json
- .github/scripts/testlib/fixtures/maps/clean-pass/app/views/Foo/placeholder.txt
- .github/scripts/testlib/fixtures/maps/stale-global/.sniffler/test-map.json
- .github/scripts/testlib/fixtures/maps/decoupled-gap/.sniffler/test-map.json
- .github/scripts/testlib/fixtures/maps/clean-pass/.maestro/tests/foo.yaml
- .github/scripts/testlib/fixtures/maps/dangling-glob/.sniffler/test-map.json
- .github/scripts/testlib/fixtures/maps/orphan-flow/.sniffler/config.json
- .github/scripts/testlib/fixtures/maps/dangling-glob/.sniffler/config.json
- .github/scripts/testlib/fixtures/maps/orphan-flow/.maestro/tests/orphan.yaml
- .github/scripts/testlib/fixtures/maps/decoupled-gap/app/sagas/foo.js
🚧 Files skipped from review as they are similar to previous changes (2)
- package.json
- .github/scripts/validate-test-map.js
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
- GitHub Check: E2E Shard Preflight
- GitHub Check: ESLint and Test / run-eslint-and-test
- GitHub Check: format
🔇 Additional comments (12)
.github/scripts/__tests__/fixtures/flows/no-tag.yaml (1)
1-5: LGTM!.github/scripts/__tests__/fixtures/scenario-catalog.json (1)
1-164: LGTM!.github/scripts/testlib/fixtures/maps/clean-pass/.sniffler/test-map.json (1)
1-7: LGTM!.github/scripts/__tests__/select-impacted-shards.test.js (1)
1-187: LGTM!.github/scripts/__tests__/coverage.test.js (1)
1-93: LGTM!.github/scripts/__tests__/validate-test-map.test.js (1)
1-74: LGTM!.github/scripts/testlib/fixtures/maps/orphan-flow/.sniffler/test-map.json (1)
1-1: LGTM!.github/scripts/testlib/fixtures/maps/prefix-collision/.sniffler/test-map.json (1)
1-6: LGTM!.github/scripts/testlib/fixtures/maps/uncovered-view/.sniffler/test-map.json (1)
1-1: LGTM!.github/scripts/__tests__/changed-runner.test.js (1)
1-198: LGTM!.github/README.md (2)
9-9: 📐 Maintainability & Code QualityDocument the release-cut full-suite bypass. The CI docs should mention the release-cut path (
master/releaselabel) runs the full 14-shard suite instead of the sniffler-selected subset.
48-63: Outdated E2E job names in the diagram. The current workflow usese2e-run-androidande2e-run-ios, note2e-result, so the missing-edge concern no longer applies.> Likely an incorrect or invalid review comment.
|
/layne exception-approve LAYNE-bad4b10f7ead3086 LAYNE-75189201c2aac8e2 reason: hardcoded scripts |
|
✅ Exception recorded for LAYNE-bad4b10f7ead3086, LAYNE-75189201c2aac8e2 by @julio-rocketchat: "hardcoded scripts". Re-running scan... |
* fix: render mentions, emojis, and inline elements inside headings (#6911) * chore: OXC (#7515) * chore: replace ESLint with Oxlint Migrate linting from ESLint 8 to Oxlint. `pnpm lint` drops from ~1min to ~0.6s and 17 eslint packages are removed from devDependencies. Config lives in `.oxlintrc.json`, generated with `@oxlint/migrate` from the old `.eslintrc.js` and then tuned: - `eslint-plugin-react-native` is loaded through `jsPlugins`, since Oxlint has no built-in equivalent. - `import/extensions` is off. Its old options (`js: 'warning'`, ...) were not valid values for the rule, so it never reported anything under ESLint. - `no-unused-vars` sets `caughtErrors: 'none'` to match the ESLint 8 default. - `import/no-cycle` and the React Compiler rules report as warnings. They surface findings ESLint never showed, so they are not gated yet. Rules with no Oxlint equivalent are dropped: `no-restricted-syntax` (the ban on `React.*` member syntax), `import/order`, `import/no-unresolved` and `import/named`. ESLint 8 skipped dot-directories, so files under `.rnstorybook/` and `.maestro/` were never linted. Oxlint does lint them, which surfaced four violations that are fixed here. The CI workflow keeps its filename and job id so branch protection checks stay valid. * chore: replace Prettier with Oxfmt Migrate formatting from Prettier 2.8.8 to Oxfmt via `oxfmt --migrate prettier`. - `.oxfmtrc.json` carries every previous Prettier option unchanged (tabs, single quotes, printWidth 130, no trailing comma, avoid arrow parens, bracketSameLine) plus the `.prettierignore` patterns as `ignorePatterns`. `sortPackageJson` is disabled to match previous behavior. - `.prettierrc.js` and `.prettierignore` removed; `prettier` dropped from devDependencies. - `prettier-lint` script renamed to `format-lint` and now runs `oxfmt`. - 44 files reformatted: Oxfmt follows Prettier 3 style, so nested ternaries and `extends`/type-argument wrapping indent differently than under Prettier 2. No semantic changes. - prettier.yml still ran `eslint --fix`, missed in the Oxlint migration; it now runs `oxfmt` and `oxlint --fix`. `react/exhaustive-deps` is allowed there because its autofix rewrites dependency arrays, which changes behavior and must not land unreviewed from CI. - Workflow filename kept as prettier.yml to avoid disturbing branch protection checks, same as eslint.yml. Verified: `pnpm lint` exit 0 (0 errors), `tsc` clean, 217/217 suites and 2032/2032 tests pass, `oxfmt --check` clean. * chore: update lockfile for oxfmt * chore: migrate typecheck to TypeScript 7.0 (#7516) * chore: bump TypeScript to 6.0 Baseline hop ahead of the TypeScript 7.0 (native compiler) migration, so the 7.0 cut is a version swap against a config that is already 7.0-shaped. TypeScript 6.0 raises both `moduleResolution: node10` and `baseUrl` as errors rather than warnings, and `ignoreDeprecations: "6.0"` stops working in 7.0, so clearing them properly is the only route: - `moduleResolution` -> `bundler` (Metro is a bundler), which requires an esnext-shaped `module`. - `baseUrl` removed. Exactly one import relied on it; it is now relative. - `types` enumerated, since a resolution mode that honours package `exports` no longer auto-includes every `@types` package. `@types/node` becomes an explicit devDependency. Honouring `exports` also stranded the bundled `.d.ts` of three dependencies whose maps expose only JavaScript. Each gets a `types` condition via patch-package; this is visible to the type checker only, as Metro ignores that condition. A `paths` mapping was tried first and rejected, because the jest-expo resolver reads `paths` and then loads those `.d.ts` files at runtime. The inherited block of commented-out option documentation is dropped. * chore: migrate typecheck to TypeScript 7.0 Replaces TypeScript 6.0.3 with the native Go compiler. The version is pinned exactly, since the platform binaries ship as version-matched optional dependencies; the lockfile records the linux-x64 target CI resolves. Typecheck wall time drops from 5.77s on 5.9.3 to ~1.0s. No configuration change was required: the 6.0 hop already left tsconfig in a 7.0-shaped state, and the default parallelism saturates without `--checkers`. `@react-navigation/core` needs a patch to type-check. TypeScript 7.0.2 resolves the mutual recursion between `StaticParamList` and `ParamListForScreens` eagerly where earlier versions defer it, reports the alias as circular, and degrades it to a non-generic symbol -- surfacing as `TS2315: Type 'StaticParamList' is not generic` at our call sites. The patch drops a `FlatType<>` wrapper from the alias, which only flattens intersections for editor display, so the type is unchanged and the misfire stops. * chore: Bump version to 4.76.0 (#7542) * chore(ci): apply least privilege permission to GitHub Actions (#7350) * chore: switch React Compiler to infer mode (#7545) * ci: route Maestro e2e selection through sniffler impact analysis (#7476) * fix(iOS): RoomItem Swipe not working after scroll (#7532) * fix(ci): select shards for changes outside app and honor the release label (#7555) * fix: delete background taller than its row on ServersHistory (#7536) * fix: delete background taller than its row on server items * chore: code improvements --------- Co-authored-by: Diego Mello <diegolmello@gmail.com> * fix: UIKit block messages rendering with smaller font size (#7531) * fix: UIKit block messages rendering with smaller font size * fix: snapshot * fix(db): move deleteMessage finds and prepares inside the writer lock (#7550) * fix: quote has no effect on older thread messages (#7535) * fix: Quote has no effect on older thread messages * fix: Quote has no effect on older thread messages * chore: e2e test * fix: e2e test * chore: format code and fix lint issues * fix(MessageComposer): resolve quoted thread messages and guard stale lookups * fix: test --------- Co-authored-by: OtavioStasiak <OtavioStasiak@users.noreply.github.com> * fix(db): move persistMessage lookups and prepares inside the writer lock (#7551) * fix: test case 11 and 12 flaky tests (#7564) * fix: test * fix: room last messa test * fix: jumptomessage test * chore: remove comments * fix: jump to message e2e test iOS * remove unused comment * fix(db): move sendMessage reads and prepares inside the writer lock (#7546) * fix(db): move sendMessage reads and prepares inside the writer lock * fix: test improvements * chore: remove comments * fix: Admin Panel content hidden behind bottom navigation bar (#7538) * feat: add tabular numbers (#7568) * feat: tabular numbers across the app, upgrade Inter to 4.1 * update snapshot * chore: pin @rocket.chat/sdk to a specific commit hash (#7569) * fix(e2ee): re-fetch subscription inside the write in toggleRoomE2EE (#7554) * fix(e2ee): re-fetch subscription inside the write in toggleRoomE2EE * code improvements * removed unused comment * fix: run handleDelete finds and prepares inside the writer lock (#7552) * fix: run handleDelete finds and prepares inside the writer lock * chore: reuse mockWMDB * fix: crop screen hidden behind navigation bar on iOS 26 (#7529) * fix: re-fetch message inside the write in getThreadName (#7557) * fix: re-fetch message inside the write in getThreadName * fix: re-fetch message inside the write in getThreadName * chore: new test cases * remove unecessary async * fix(db): move decryptPendingMessages prepares inside the writer lock (#7548) * fix(db): move decryptPendingMessages prepares inside the writer lock * code improvements * chore: new test case encryption * chore: format code and fix lint issues --------- Co-authored-by: OtavioStasiak <OtavioStasiak@users.noreply.github.com> * fix(android): VideoConf notification accept and decline button hidden and touch not working (#7533) * fix: in-app notification buttons ignoring taps on Android * fix: Decline and Accept invisible on the incoming call notification * fix: UIKit buttons not responding on some Android devices (#7573) * fix: force Google account chooser on OAuth login (#7572) * fix: ISO format support in markdown component (#6943) * fix: resolve deep links by room id for channels and groups (#7111) * Merge pull request #7570 from RocketChat/deeplink-saml-auth feat: SAML deeplink auth * fix: grant pull-requests write to build call sites in build-develop (#7599) The reusable workflows build-android.yml and build-ios.yml declare pull-requests: write on their upload jobs. GitHub validates these at call time regardless of job conditionals, so build-develop.yml (caller) must grant the permission or the workflow fails validation. build-pr.yml already grants it; this mirrors that. --------- Co-authored-by: Rohit Bansal <40559587+Rohit3523@users.noreply.github.com> Co-authored-by: Yasmim Nagat <117310290+yasnagat@users.noreply.github.com> Co-authored-by: Otávio Stasiak <91474186+OtavioStasiak@users.noreply.github.com> Co-authored-by: OtavioStasiak <OtavioStasiak@users.noreply.github.com> Co-authored-by: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com>
* fix: render mentions, emojis, and inline elements inside headings (#6911) * chore: OXC (#7515) * chore: replace ESLint with Oxlint Migrate linting from ESLint 8 to Oxlint. `pnpm lint` drops from ~1min to ~0.6s and 17 eslint packages are removed from devDependencies. Config lives in `.oxlintrc.json`, generated with `@oxlint/migrate` from the old `.eslintrc.js` and then tuned: - `eslint-plugin-react-native` is loaded through `jsPlugins`, since Oxlint has no built-in equivalent. - `import/extensions` is off. Its old options (`js: 'warning'`, ...) were not valid values for the rule, so it never reported anything under ESLint. - `no-unused-vars` sets `caughtErrors: 'none'` to match the ESLint 8 default. - `import/no-cycle` and the React Compiler rules report as warnings. They surface findings ESLint never showed, so they are not gated yet. Rules with no Oxlint equivalent are dropped: `no-restricted-syntax` (the ban on `React.*` member syntax), `import/order`, `import/no-unresolved` and `import/named`. ESLint 8 skipped dot-directories, so files under `.rnstorybook/` and `.maestro/` were never linted. Oxlint does lint them, which surfaced four violations that are fixed here. The CI workflow keeps its filename and job id so branch protection checks stay valid. * chore: replace Prettier with Oxfmt Migrate formatting from Prettier 2.8.8 to Oxfmt via `oxfmt --migrate prettier`. - `.oxfmtrc.json` carries every previous Prettier option unchanged (tabs, single quotes, printWidth 130, no trailing comma, avoid arrow parens, bracketSameLine) plus the `.prettierignore` patterns as `ignorePatterns`. `sortPackageJson` is disabled to match previous behavior. - `.prettierrc.js` and `.prettierignore` removed; `prettier` dropped from devDependencies. - `prettier-lint` script renamed to `format-lint` and now runs `oxfmt`. - 44 files reformatted: Oxfmt follows Prettier 3 style, so nested ternaries and `extends`/type-argument wrapping indent differently than under Prettier 2. No semantic changes. - prettier.yml still ran `eslint --fix`, missed in the Oxlint migration; it now runs `oxfmt` and `oxlint --fix`. `react/exhaustive-deps` is allowed there because its autofix rewrites dependency arrays, which changes behavior and must not land unreviewed from CI. - Workflow filename kept as prettier.yml to avoid disturbing branch protection checks, same as eslint.yml. Verified: `pnpm lint` exit 0 (0 errors), `tsc` clean, 217/217 suites and 2032/2032 tests pass, `oxfmt --check` clean. * chore: update lockfile for oxfmt * chore: migrate typecheck to TypeScript 7.0 (#7516) * chore: bump TypeScript to 6.0 Baseline hop ahead of the TypeScript 7.0 (native compiler) migration, so the 7.0 cut is a version swap against a config that is already 7.0-shaped. TypeScript 6.0 raises both `moduleResolution: node10` and `baseUrl` as errors rather than warnings, and `ignoreDeprecations: "6.0"` stops working in 7.0, so clearing them properly is the only route: - `moduleResolution` -> `bundler` (Metro is a bundler), which requires an esnext-shaped `module`. - `baseUrl` removed. Exactly one import relied on it; it is now relative. - `types` enumerated, since a resolution mode that honours package `exports` no longer auto-includes every `@types` package. `@types/node` becomes an explicit devDependency. Honouring `exports` also stranded the bundled `.d.ts` of three dependencies whose maps expose only JavaScript. Each gets a `types` condition via patch-package; this is visible to the type checker only, as Metro ignores that condition. A `paths` mapping was tried first and rejected, because the jest-expo resolver reads `paths` and then loads those `.d.ts` files at runtime. The inherited block of commented-out option documentation is dropped. * chore: migrate typecheck to TypeScript 7.0 Replaces TypeScript 6.0.3 with the native Go compiler. The version is pinned exactly, since the platform binaries ship as version-matched optional dependencies; the lockfile records the linux-x64 target CI resolves. Typecheck wall time drops from 5.77s on 5.9.3 to ~1.0s. No configuration change was required: the 6.0 hop already left tsconfig in a 7.0-shaped state, and the default parallelism saturates without `--checkers`. `@react-navigation/core` needs a patch to type-check. TypeScript 7.0.2 resolves the mutual recursion between `StaticParamList` and `ParamListForScreens` eagerly where earlier versions defer it, reports the alias as circular, and degrades it to a non-generic symbol -- surfacing as `TS2315: Type 'StaticParamList' is not generic` at our call sites. The patch drops a `FlatType<>` wrapper from the alias, which only flattens intersections for editor display, so the type is unchanged and the misfire stops. * chore: Bump version to 4.76.0 (#7542) * chore(ci): apply least privilege permission to GitHub Actions (#7350) * chore: switch React Compiler to infer mode (#7545) * ci: route Maestro e2e selection through sniffler impact analysis (#7476) * fix(iOS): RoomItem Swipe not working after scroll (#7532) * fix(ci): select shards for changes outside app and honor the release label (#7555) * fix: delete background taller than its row on ServersHistory (#7536) * fix: delete background taller than its row on server items * chore: code improvements --------- Co-authored-by: Diego Mello <diegolmello@gmail.com> * fix: UIKit block messages rendering with smaller font size (#7531) * fix: UIKit block messages rendering with smaller font size * fix: snapshot * fix(db): move deleteMessage finds and prepares inside the writer lock (#7550) * fix: quote has no effect on older thread messages (#7535) * fix: Quote has no effect on older thread messages * fix: Quote has no effect on older thread messages * chore: e2e test * fix: e2e test * chore: format code and fix lint issues * fix(MessageComposer): resolve quoted thread messages and guard stale lookups * fix: test --------- Co-authored-by: OtavioStasiak <OtavioStasiak@users.noreply.github.com> * fix(db): move persistMessage lookups and prepares inside the writer lock (#7551) * fix: test case 11 and 12 flaky tests (#7564) * fix: test * fix: room last messa test * fix: jumptomessage test * chore: remove comments * fix: jump to message e2e test iOS * remove unused comment * fix(db): move sendMessage reads and prepares inside the writer lock (#7546) * fix(db): move sendMessage reads and prepares inside the writer lock * fix: test improvements * chore: remove comments * fix: Admin Panel content hidden behind bottom navigation bar (#7538) * feat: add tabular numbers (#7568) * feat: tabular numbers across the app, upgrade Inter to 4.1 * update snapshot * chore: pin @rocket.chat/sdk to a specific commit hash (#7569) * fix(e2ee): re-fetch subscription inside the write in toggleRoomE2EE (#7554) * fix(e2ee): re-fetch subscription inside the write in toggleRoomE2EE * code improvements * removed unused comment * fix: run handleDelete finds and prepares inside the writer lock (#7552) * fix: run handleDelete finds and prepares inside the writer lock * chore: reuse mockWMDB * fix: crop screen hidden behind navigation bar on iOS 26 (#7529) * fix: re-fetch message inside the write in getThreadName (#7557) * fix: re-fetch message inside the write in getThreadName * fix: re-fetch message inside the write in getThreadName * chore: new test cases * remove unecessary async * fix(db): move decryptPendingMessages prepares inside the writer lock (#7548) * fix(db): move decryptPendingMessages prepares inside the writer lock * code improvements * chore: new test case encryption * chore: format code and fix lint issues --------- Co-authored-by: OtavioStasiak <OtavioStasiak@users.noreply.github.com> * fix(android): VideoConf notification accept and decline button hidden and touch not working (#7533) * fix: in-app notification buttons ignoring taps on Android * fix: Decline and Accept invisible on the incoming call notification * fix: UIKit buttons not responding on some Android devices (#7573) * fix: force Google account chooser on OAuth login (#7572) * fix: ISO format support in markdown component (#6943) * fix: resolve deep links by room id for channels and groups (#7111) * Merge pull request #7570 from RocketChat/deeplink-saml-auth feat: SAML deeplink auth * fix: grant pull-requests write to build call sites in build-develop (#7599) The reusable workflows build-android.yml and build-ios.yml declare pull-requests: write on their upload jobs. GitHub validates these at call time regardless of job conditionals, so build-develop.yml (caller) must grant the permission or the workflow fails validation. build-pr.yml already grants it; this mirrors that. --------- Co-authored-by: Rohit Bansal <40559587+Rohit3523@users.noreply.github.com> Co-authored-by: Yasmim Nagat <117310290+yasnagat@users.noreply.github.com> Co-authored-by: Otávio Stasiak <91474186+OtavioStasiak@users.noreply.github.com> Co-authored-by: OtavioStasiak <OtavioStasiak@users.noreply.github.com> Co-authored-by: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com>
Proposed changes
E2E test selection now runs through sniffler impact analysis, so a regular dev PR only executes the Maestro flows its diff actually touches instead of the full 14-shard suite every time. Release-cut PRs (base
masteror areleaselabel) still run all 14. The design's hard rule is that under-selection is impossible — every uncertainty (no base ref, shallow clone, sniffler error, unparseable output, arunAllWhenChangedhit, a release lane) falls back to the full suite; the optimization only ever narrows, never risks missing coverage.CI (
build-pr.yml)e2e-shardsnow runs first and narrows the shard matrix viaselect-impacted-shards.sh. All six fail-safe guards fall back to the full 14.should_run=falseand skips the whole e2e stage: no manual approval, no native build, no flows.always()e2e-resultjob is the single required e2e check: green on a confident-zero skip, green when both run jobs pass, red otherwise. It never hangs "Waiting for status".Local
pnpm e2e:changed <android|ios>runs the impacted flows against a booted device with the app installed. It is working-tree-aware (committed + uncommitted + untracked) and mirrors CI'sutil/ wrong-platform tag excludes.Coverage + freshness
.sniffler/config.json+test-map.jsonmap the runtime-decoupled surface the import graph can't trace: domain sagas anchored to their flows; global sagas, notification/stream routers, navigation stacks, the root container, the store, and the root reducer inrunAllWhenChanged.validate-test-map.js(wired intoeslint.yml) fails a PR on orphan flows, dangling globs, stale globals, or any uncovered saga/stack, and warns on uncovered views — so the map can't silently rot.Tests
select-impacted-shards.sh,e2e-changed.sh, andvalidate-test-map.js— so the logic that gates e2e is exercised on everyESLint and Testrun, not just verified by hand. A shell harness stubs the external deps (sniffler/git/maestro) on$PATH; per-branch fixture maps drive each validator check.Other
sniffleris pinned to an exact version — it decides what e2e runs, so range drift should be an explicit reviewed change.validate-test-map.js: the prefix match let a view dir cover-match a longer sibling (e.g.SomeViewvsSomeViewExtra/**); it now anchors on a trailing slash, with a fixture proving it..github/README.mdupdated for the newe2e-shardspreflight, thee2e-resultrequired check, and the now-conditionalapprove_e2e_testinggate.Issue(s)
https://rocketchat.atlassian.net/browse/NATIVE-1405
How to test or reproduce
CI evidence
Real
build-pr.ymlruns, exercised on a throwaway branch stacked on this PR (opened/closed as #7477 — do not look for it, it's gone):e2e-shardsdecisionapp/views/RoomView/index.tsxshould_run=true, matrix narrowed to shards[3,7,9,11,12,13,14](7 of 14) on both Android + iOS;E2E Holdapproved and greenREADME.md)should_run=false;E2E Holdand the whole e2e stage skipped — no approval, no build, no flowspackage.json(this PR's own run)should_run=true, full 14-shard suite queued behind the manualapprove_e2e_testinggateThe narrow run's shard list is visible directly in the
E2E Run Android/iOS (N)matrix job names; the skip run showsE2E Holdand downstream e2e jobs as skipped.Local verification
Against the live
sniffler@0.4.0binary (exact-restore, nothing left in the tree):IS_RELEASE_LANE=true→ all 14, short-circuits before sniffler.pnpm e2e:changed androidon the same RoomView edit → the exact same flows the dev-PR shards cover, no under-selection.sniffler impactonAppContainer.tsx, each nav stack,lib/store/index.ts,reducers/index.js→ all select the full 55 flows.node .github/scripts/validate-test-map.js→ exit 0 (0 errors, 8 uncovered-view warnings); trips to exit 1 on a synthetic orphan / dangling glob / decoupled gap.TZ=UTC pnpm test --testPathPattern='.github/scripts/__tests__'→ all pass; runs as part of the normalESLint and Testjob.Screenshots
N/A — CI + tooling only, no user-facing UI.
Types of changes
Checklist
Further comments
Branch protection: no change required on merge.
develop's required checks arelicense/cla,ESLint and Test, andCodeRabbit— no per-shard or native-build context is required, and e2e gates through a manual approval environment rather than a required status check. So a confident-zero PR can't hang "Waiting for status". Optionally, to make e2e a hard merge gate, addE2E Resultto the required checks — it's safe with confident-zero because the job isalways()and reports green on a skip. Do not add the per-shard (E2E Run Android (N)…) or native-build contexts: they zero out on a narrowed run. That optional follow-up (to be done only after merge) is tracked in https://rocketchat.atlassian.net/browse/NATIVE-1408.Seam choice: shard-level narrowing (not per-flow) in CI keeps
run-maestro.sh, bothmaestro-*.yml, and the retry loop untouched — over-selection at shard granularity is acceptable since selection is a dev-speed optimization, never a correctness gate.Summary by CodeRabbit