From 6e19e425fe43b697d98d78fdfaf404829c77d524 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 18 Jun 2026 17:19:54 +0200 Subject: [PATCH 1/7] fix: add turbo configuration to brownfield-navigation to fix CI errors - navigation build artifacts --- packages/brownfield-navigation/turbo.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 packages/brownfield-navigation/turbo.json diff --git a/packages/brownfield-navigation/turbo.json b/packages/brownfield-navigation/turbo.json new file mode 100644 index 00000000..2c795536 --- /dev/null +++ b/packages/brownfield-navigation/turbo.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "build": { + "inputs": ["src/**/*"], + "outputs": ["lib/**"] + } + } +} From 32c1c8216718a475088439103c1a2521b1bf9a06 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 18 Jun 2026 18:05:37 +0200 Subject: [PATCH 2/7] fix: turbo to take into account config files as inputs --- packages/brownfield-navigation/turbo.json | 8 +++++++- packages/brownfield/turbo.json | 8 +++++++- packages/brownie/turbo.json | 8 +++++++- packages/cli/turbo.json | 8 +++++++- packages/react-native-brownfield/turbo.json | 8 +++++++- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/packages/brownfield-navigation/turbo.json b/packages/brownfield-navigation/turbo.json index 2c795536..d03b8931 100644 --- a/packages/brownfield-navigation/turbo.json +++ b/packages/brownfield-navigation/turbo.json @@ -3,7 +3,13 @@ "extends": ["//"], "tasks": { "build": { - "inputs": ["src/**/*"], + "inputs": [ + "src/**/*", + "bob.config.js", + "babel.config.js", + "tsconfig*.json", + "package.json" + ], "outputs": ["lib/**"] } } diff --git a/packages/brownfield/turbo.json b/packages/brownfield/turbo.json index 3bafb127..6bf07282 100644 --- a/packages/brownfield/turbo.json +++ b/packages/brownfield/turbo.json @@ -3,7 +3,13 @@ "extends": ["//"], "tasks": { "build": { - "inputs": ["src/**/*"], + "inputs": [ + "src/**/*", + "bob.config.js", + "babel.config.js", + "tsconfig*.json", + "package.json" + ], "outputs": ["dist/**"] } } diff --git a/packages/brownie/turbo.json b/packages/brownie/turbo.json index 2c795536..d03b8931 100644 --- a/packages/brownie/turbo.json +++ b/packages/brownie/turbo.json @@ -3,7 +3,13 @@ "extends": ["//"], "tasks": { "build": { - "inputs": ["src/**/*"], + "inputs": [ + "src/**/*", + "bob.config.js", + "babel.config.js", + "tsconfig*.json", + "package.json" + ], "outputs": ["lib/**"] } } diff --git a/packages/cli/turbo.json b/packages/cli/turbo.json index a11e03c5..029b0d9f 100644 --- a/packages/cli/turbo.json +++ b/packages/cli/turbo.json @@ -3,7 +3,13 @@ "extends": ["//"], "tasks": { "build": { - "inputs": ["src/**/*"], + "inputs": [ + "src/**/*", + "bob.config.js", + "babel.config.js", + "tsconfig*.json", + "package.json" + ], "outputs": ["dist/**"] }, "build:brownfield": { diff --git a/packages/react-native-brownfield/turbo.json b/packages/react-native-brownfield/turbo.json index 2c795536..d03b8931 100644 --- a/packages/react-native-brownfield/turbo.json +++ b/packages/react-native-brownfield/turbo.json @@ -3,7 +3,13 @@ "extends": ["//"], "tasks": { "build": { - "inputs": ["src/**/*"], + "inputs": [ + "src/**/*", + "bob.config.js", + "babel.config.js", + "tsconfig*.json", + "package.json" + ], "outputs": ["lib/**"] } } From 301c749be24c807c53aa6825773bae5113b14d0c Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 18 Jun 2026 18:33:14 +0200 Subject: [PATCH 3/7] fix(ci): remove build artifacts after android app build to prevent ENOSPC --- .github/actions/androidapp-road-test/action.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/actions/androidapp-road-test/action.yml b/.github/actions/androidapp-road-test/action.yml index 47e1a846..9b22ff7e 100644 --- a/.github/actions/androidapp-road-test/action.yml +++ b/.github/actions/androidapp-road-test/action.yml @@ -82,6 +82,18 @@ runs: run: stat ~/.m2/repository/${{ inputs.rn-project-maven-path }}/0.0.1-SNAPSHOT/brownfieldlib-0.0.1-SNAPSHOT-release.aar shell: bash + # clean up build artifacts to ensure no ENOSPC + - name: Clean up local build artifacts + run: | + rm -rf ${{ inputs.rn-project-path }}/android/build + rm -rf ${{ inputs.rn-project-path }}/android/.cxx + rm -rf ${{ inputs.rn-project-path }}/android/.gradle + rm -rf ${{ inputs.rn-project-path }}/android/app/build + rm -rf ${{ inputs.rn-project-path }}/android/app/.cxx + rm -rf ${{ inputs.rn-project-path }}/android/app/.gradle + rm -rf ${{ inputs.rn-project-path }}/android/app/build + shell: bash + # == AndroidApp == - name: Build native Android Brownfield app From 894f1ba3a8afe8fd3bfd663589d2542fc4ccb289 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Fri, 19 Jun 2026 01:52:58 +0200 Subject: [PATCH 4/7] ci: per-app-variant Gradle cache --- .github/actions/androidapp-road-test/action.yml | 11 +---------- .github/actions/prepare-android/action.yml | 16 ++++++++++++++-- .github/workflows/gradle-plugin-lint.yml | 10 ---------- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/.github/actions/androidapp-road-test/action.yml b/.github/actions/androidapp-road-test/action.yml index 9b22ff7e..3acb2ea0 100644 --- a/.github/actions/androidapp-road-test/action.yml +++ b/.github/actions/androidapp-road-test/action.yml @@ -29,17 +29,8 @@ runs: - name: Prepare Android environment uses: ./.github/actions/prepare-android - - - name: Restore Gradle cache - uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5 with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-android-androidapp-${{ inputs.flavor }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-android-androidapp-${{ inputs.flavor }}-gradle- - ${{ runner.os }}-android-androidapp-gradle- + gradle-workflow-job-context: ${{ inputs.flavor }} # == Brownfield Gradle Plugin == - name: Publish Brownfield Gradle Plugin to Maven Local diff --git a/.github/actions/prepare-android/action.yml b/.github/actions/prepare-android/action.yml index d3694013..41f44b28 100644 --- a/.github/actions/prepare-android/action.yml +++ b/.github/actions/prepare-android/action.yml @@ -8,9 +8,14 @@ inputs: default: 'true' run-yarn-build: - description: 'Whether to run yarn build' + description: 'Whether to run yarn build (usually false when ./.github/actions/setup already ran)' required: false - default: 'true' + default: 'false' + + gradle-workflow-job-context: + description: 'Segment Gradle cache per app/project (e.g. vanilla, expo54). Falls back to github.job when empty.' + required: false + default: '' runs: using: composite @@ -24,6 +29,13 @@ runs: distribution: 'zulu' java-version: '17' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@6f229686ee4375cc4a86b2514c89bac4930e82c4 # v5 + with: + # Validates all gradle-wrapper.jar files, caches Gradle User Home efficiently, + # and captures Build Scan links. Do not combine with actions/cache on ~/.gradle. + workflow-job-context: ${{ inputs.gradle-workflow-job-context != '' && inputs.gradle-workflow-job-context || github.job }} + - name: Free Disk Space (Ubuntu) if: inputs.free-disk-space == 'true' uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 diff --git a/.github/workflows/gradle-plugin-lint.yml b/.github/workflows/gradle-plugin-lint.yml index 39264e96..2c70cc81 100644 --- a/.github/workflows/gradle-plugin-lint.yml +++ b/.github/workflows/gradle-plugin-lint.yml @@ -21,16 +21,6 @@ jobs: free-disk-space: 'false' run-yarn-build: 'false' - - name: Restore Gradle cache - uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Run Detekt working-directory: gradle-plugins/react/brownfield run: ./gradlew detekt --no-daemon --stacktrace From 5789ff7ef716affcad59c9ca21cb4cd4b9448293 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Fri, 19 Jun 2026 01:53:27 +0200 Subject: [PATCH 5/7] ci: more aggresive disk cleanup in Android workflow to prevent ENOSPC --- .github/actions/prepare-android/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/prepare-android/action.yml b/.github/actions/prepare-android/action.yml index 41f44b28..44424131 100644 --- a/.github/actions/prepare-android/action.yml +++ b/.github/actions/prepare-android/action.yml @@ -45,7 +45,7 @@ runs: android: false dotnet: true haskell: true - large-packages: false + large-packages: true docker-images: true swap-storage: false From 6841b8309890af72f90bb5b7846a778f7387cb58 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Fri, 19 Jun 2026 10:31:05 +0200 Subject: [PATCH 6/7] fix(ci): increase e2e timeout --- .../e2e/createDetoxJestConfig.cjs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/brownfield-example-shared-tests/e2e/createDetoxJestConfig.cjs b/apps/brownfield-example-shared-tests/e2e/createDetoxJestConfig.cjs index a8e847be..f7e99e2b 100644 --- a/apps/brownfield-example-shared-tests/e2e/createDetoxJestConfig.cjs +++ b/apps/brownfield-example-shared-tests/e2e/createDetoxJestConfig.cjs @@ -9,7 +9,10 @@ const path = require('node:path'); */ function createDetoxJestConfig({ e2eDir, testMatch }) { const appRoot = path.join(e2eDir, '..'); - const sharedTestsRoot = path.join(e2eDir, '../../brownfield-example-shared-tests'); + const sharedTestsRoot = path.join( + e2eDir, + '../../brownfield-example-shared-tests' + ); return { maxWorkers: 1, @@ -18,7 +21,7 @@ function createDetoxJestConfig({ e2eDir, testMatch }) { // Shared E2E files live under brownfield-example-shared-tests; resolve host-app deps (detox) from here. modulePaths: [path.join(appRoot, 'node_modules')], // beforeEach relaunches the app and waits for the embedded RN surface (up to ~80s on slow CI). - testTimeout: 180000, + testTimeout: 300000, verbose: true, reporters: ['detox/runners/jest/reporter'], globalSetup: 'detox/runners/jest/globalSetup', From 6a8bd83293c9d302218179c6b2a2ff6856519d42 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Fri, 19 Jun 2026 10:54:10 +0200 Subject: [PATCH 7/7] ci: upload recordings from e2e tests --- .github/actions/appleapp-road-test/action.yml | 13 ++++--- apps/AppleApp/.gitignore | 1 + apps/RNApp/.gitignore | 2 +- .../detox-artifacts-config.cjs | 37 +++++++++++++++++++ .../detox-rc-appleapp-ios-sim-debug.cjs | 2 + .../detox-rc-ios-sim-debug.cjs | 2 + 6 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 apps/brownfield-example-shared-tests/detox-artifacts-config.cjs diff --git a/.github/actions/appleapp-road-test/action.yml b/.github/actions/appleapp-road-test/action.yml index 977d2697..031b3854 100644 --- a/.github/actions/appleapp-road-test/action.yml +++ b/.github/actions/appleapp-road-test/action.yml @@ -201,17 +201,20 @@ runs: - name: Detox test (AppleApp ${{ inputs.variant }}) if: inputs.run-e2e == 'true' - run: yarn "$APPLEAPP_E2E_TEST_SCRIPT" + run: | + rm -rf e2e-artifacts + yarn "$APPLEAPP_E2E_TEST_SCRIPT" working-directory: apps/AppleApp shell: bash - - name: Upload Detox artifacts on failure + - name: Upload Detox recordings on failure if: failure() && inputs.run-e2e == 'true' uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: ${{ inputs.e2e-artifact-name }}-${{ inputs.variant }}-ios - path: apps/AppleApp/artifacts - if-no-files-found: ignore + name: ${{ inputs.e2e-artifact-name }}-${{ inputs.variant }}-ios-recordings + path: apps/AppleApp/e2e-artifacts + if-no-files-found: warn + retention-days: 5 # ============== diff --git a/apps/AppleApp/.gitignore b/apps/AppleApp/.gitignore index 384a21b3..821ac41a 100644 --- a/apps/AppleApp/.gitignore +++ b/apps/AppleApp/.gitignore @@ -1,2 +1,3 @@ build package +e2e-artifacts/ diff --git a/apps/RNApp/.gitignore b/apps/RNApp/.gitignore index 2ab2a6b5..b4a457ea 100644 --- a/apps/RNApp/.gitignore +++ b/apps/RNApp/.gitignore @@ -64,7 +64,7 @@ yarn-error.log # testing /coverage -/artifacts +/e2e-artifacts # Yarn .yarn/* diff --git a/apps/brownfield-example-shared-tests/detox-artifacts-config.cjs b/apps/brownfield-example-shared-tests/detox-artifacts-config.cjs new file mode 100644 index 00000000..9eead0d0 --- /dev/null +++ b/apps/brownfield-example-shared-tests/detox-artifacts-config.cjs @@ -0,0 +1,37 @@ +'use strict'; + +/** + * Detox artifacts for failed E2E tests (video, screenshots, logs, UI hierarchy). + * Written under `/e2e-artifacts/` — upload that folder in CI on failure. + * + * @param {string} [rootDir='e2e-artifacts'] + * @returns {import('detox').DetoxArtifactsConfig} + */ +function getDetoxArtifactsConfig(rootDir = 'e2e-artifacts') { + return { + rootDir, + plugins: { + log: 'failing', + screenshot: { + shouldTakeAutomaticSnapshots: true, + keepOnlyFailedTestsArtifacts: true, + takeWhen: { + testStart: false, + testFailure: true, + testDone: true, + }, + }, + video: { + enabled: true, + keepOnlyFailedTestsArtifacts: true, + simulator: { + // h264 is more reliable on GitHub Actions macOS runners than hevc. + codec: 'h264', + }, + }, + uiHierarchy: 'enabled', + }, + }; +} + +module.exports = { getDetoxArtifactsConfig }; diff --git a/apps/brownfield-example-shared-tests/detox-rc-appleapp-ios-sim-debug.cjs b/apps/brownfield-example-shared-tests/detox-rc-appleapp-ios-sim-debug.cjs index c6534803..b3363b26 100644 --- a/apps/brownfield-example-shared-tests/detox-rc-appleapp-ios-sim-debug.cjs +++ b/apps/brownfield-example-shared-tests/detox-rc-appleapp-ios-sim-debug.cjs @@ -1,6 +1,7 @@ 'use strict'; const { getIosSimulatorDeviceType } = require('./detox-ios-simulator-device.cjs'); +const { getDetoxArtifactsConfig } = require('./detox-artifacts-config.cjs'); /** * Detox iOS Simulator debug config for AppleApp (native Xcode project consumer). @@ -30,6 +31,7 @@ function createAppleAppIosSimDebugDetoxConfig({ ` -derivedDataPath build ARCHS=arm64 ONLY_ACTIVE_ARCH=YES CODE_SIGNING_ALLOWED=NO`; return { + artifacts: getDetoxArtifactsConfig(), testRunner: { $0: 'jest', args: { diff --git a/apps/brownfield-example-shared-tests/detox-rc-ios-sim-debug.cjs b/apps/brownfield-example-shared-tests/detox-rc-ios-sim-debug.cjs index ebd06a2a..945cbdbc 100644 --- a/apps/brownfield-example-shared-tests/detox-rc-ios-sim-debug.cjs +++ b/apps/brownfield-example-shared-tests/detox-rc-ios-sim-debug.cjs @@ -1,6 +1,7 @@ 'use strict'; const { getIosSimulatorDeviceType } = require('./detox-ios-simulator-device.cjs'); +const { getDetoxArtifactsConfig } = require('./detox-artifacts-config.cjs'); /** * Shared Detox iOS Simulator debug config for brownfield example apps. @@ -18,6 +19,7 @@ function createIosSimDebugDetoxConfig({ workspace, scheme, appBinaryName }) { ` -derivedDataPath ios/build ARCHS=arm64 ONLY_ACTIVE_ARCH=YES`; return { + artifacts: getDetoxArtifactsConfig(), testRunner: { $0: 'jest', args: {