I have been working on setting up an integration pipeline, such that I can quickly build new APK files for android phones when I push a new version to the main branch. For this, I have created the following CI build file:
name: React Native build android
on:
pull_request:
branches: [ "main", "test-branch" ]
jobs:
build-android:
runs-on: ubuntu-latest
env:
NODE_ENV: production
steps:
- name: install Linux dependencies
run: sudo apt-get update || true
- name: Checkout
uses: actions/checkout@v2
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install npm dependencies
run: npm install
- name: make gradlew executable
run: chmod +x ./android/gradlew
- name: Build android APK
run: cd ./android && ./gradlew assembleRelease --stacktrace --no-daemon
- name: Upload Artifact
uses: actions/upload-artifact@v1
with:
name: PFM-release.apk
path: android/app/build/outputs/apk/release/
Quite straightforwardly, it does some updating, sets up node and builds the android .APK file. Then, it should upload the generated artifact.
Now, this fails on building the android APK. It goes as follows,
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:bundleReleaseJsAndAssets'.
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:142)
at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:282)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:140)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:128)
at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:69)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:327)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:314)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:307)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:293)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:420)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:342)
at org.gradle.execution.plan.DefaultPlanExecutor.process(DefaultPlanExecutor.java:96)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph.executeWithServices(DefaultTaskExecutionGraph.java:140)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph.execute(DefaultTaskExecutionGraph.java:125)
at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:39)
at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:51)
at org.gradle.execution.BuildOperationFiringBuildWorkerExecutor$ExecuteTasks.call(BuildOperationFiringBuildWorkerExecutor.java:54)
at org.gradle.execution.BuildOperationFiringBuildWorkerExecutor$ExecuteTasks.call(BuildOperationFiringBuildWorkerExecutor.java:43)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
at org.gradle.execution.BuildOperationFiringBuildWorkerExecutor.execute(BuildOperationFiringBuildWorkerExecutor.java:40)
at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$executeTasks$7(DefaultBuildLifecycleController.java:161)
at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:247)
at org.gradle.internal.model.StateTransitionController.lambda$tryTransition$7(StateTransitionController.java:174)
at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:44)
at org.gradle.internal.model.StateTransitionController.tryTransition(StateTransitionController.java:174)
at org.gradle.internal.build.DefaultBuildLifecycleController.executeTasks(DefaultBuildLifecycleController.java:161)
at org.gradle.internal.build.DefaultBuildWorkGraphController$DefaultBuildWorkGraph.runWork(DefaultBuildWorkGraphController.java:156)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:249)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:109)
at org.gradle.composite.internal.DefaultBuildController.doRun(DefaultBuildController.java:164)
at org.gradle.composite.internal.DefaultBuildController.access$000(DefaultBuildController.java:45)
at org.gradle.composite.internal.DefaultBuildController$BuildOpRunnable.run(DefaultBuildController.java:183)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
Caused by: org.gradle.process.internal.ExecException: Process 'command 'node'' finished with non-zero exit value 1
at org.gradle.process.internal.DefaultExecHandle$ExecResultImpl.assertNormalExitValue(DefaultExecHandle.java:415)
at org.gradle.process.internal.DefaultExecAction.execute(DefaultExecAction.java:38)
at org.gradle.api.tasks.AbstractExecTask.exec(AbstractExecTask.java:73)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:125)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:236)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:221)
at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:204)
at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:187)
at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:165)
at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:89)
at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:40)
at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:53)
at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:50)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:50)
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:40)
at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:68)
at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:38)
at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:41)
at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:74)
at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:51)
at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:29)
at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.executeDelegateBroadcastingChanges(CaptureStateAfterExecutionStep.java:124)
at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:80)
at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:58)
at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:48)
at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:36)
at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:181)
at org.gradle.internal.execution.steps.BuildCacheStep.lambda$execute$1(BuildCacheStep.java:71)
at org.gradle.internal.Either$Right.fold(Either.java:175)
at org.gradle.internal.execution.caching.CachingState.fold(CachingState.java:59)
at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:69)
at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:47)
at ...
and then continues with
java.lang.StackOverflowError
at org.gradle.execution.plan.FinalizerGroup.getFinalizedNodes(FinalizerGroup.java:77)
at org.gradle.execution.plan.FinalizerGroup.isCanCancel(FinalizerGroup.java:153)
at org.gradle.execution.plan.CompositeNodeGroup.isCanCancel(CompositeNodeGroup.java:101)
at org.gradle.execution.plan.Node.isCanCancel(Node.java:232)
at org.gradle.execution.plan.FinalizerGroup.isCanCancel(FinalizerGroup.java:155)
at org.gradle.execution.plan.CompositeNodeGroup.isCanCancel(CompositeNodeGroup.java:101)
at org.gradle.execution.plan.Node.isCanCancel(Node.java:232)
at org.gradle.execution.plan.FinalizerGroup.isCanCancel(FinalizerGroup.java:155)
at org.gradle.execution.plan.CompositeNodeGroup.isCanCancel(CompositeNodeGroup.java:101)
at org.gradle.execution.plan.Node.isCanCancel(Node.java:232)
at org.gradle.execution.plan.FinalizerGroup.isCanCancel(FinalizerGroup.java:155)
at org.gradle.execution.plan.CompositeNodeGroup.isCanCancel(CompositeNodeGroup.java:101)
at org.gradle.execution.plan.Node.isCanCancel(Node.java:232)
at org.gradle.execution.plan.FinalizerGroup.isCanCancel(FinalizerGroup.java:155)
at org.gradle.execution.plan.CompositeNodeGroup.isCanCancel(CompositeNodeGroup.java:101)
at org.gradle.execution.plan.Node.isCanCancel(Node.java:232)
at org.gradle.execution.plan.FinalizerGroup.isCanCancel(FinalizerGroup.java:155)
at org.gradle.execution.plan.CompositeNodeGroup.isCanCancel(CompositeNodeGroup.java:101)
at org.gradle.execution.plan.Node.isCanCancel(Node.java:232)
at org.gradle.execution.plan.FinalizerGroup.isCanCancel(FinalizerGroup.java:155)
at org.gradle.execution.plan.CompositeNodeGroup.isCanCancel(CompositeNodeGroup.java:101)
at org.gradle.execution.plan.Node.isCanCancel(Node.java:232)
at org.gradle.execution.plan.FinalizerGroup.isCanCancel(FinalizerGroup.java:155)
at org.gradle.execution.plan.CompositeNodeGroup.isCanCancel(CompositeNodeGroup.java:101)
at org.gradle.execution.plan.Node.isCanCancel(Node.java:232)
at org.gradle.execution.plan.FinalizerGroup.isCanCancel(FinalizerGroup.java:155)
at org.gradle.execution.plan.CompositeNodeGroup.isCanCancel(CompositeNodeGroup.java:101)
at org.gradle.execution.plan.Node.isCanCancel(Node.java:232)
at org.gradle.execution.plan.FinalizerGroup.isCanCancel(FinalizerGroup.java:155)
at org.gradle.execution.plan.CompositeNodeGroup.isCanCancel(CompositeNodeGroup.java:101)
at org.gradle.execution.plan.Node.isCanCancel(Node.java:232)
...
It is the same when I tried cleaning it beforehand, with cd ./android && ./gradlew --clean or when I stopped gradlew before running it again with cd ./android && ./gradlew --clean.
I have found out that this will not occur when doing cd ./android && ./gradlew assembleRelease -x bundleReleaseJsAndAssets which works because we are simply not bundling the assets. This leads me to believe that bundling is where it fails. Now, then I thought about bundling beforehand, which shouldn't be necessary, but nevertheless I was hoping for it to work. This did not help me in my issue at all.
Now, locally all is fine. It builds and creates an .APK file with the assets and all, it also automatically bundles these assets. I expected this to happen on the CI pipeline as well. I have also tried making use of already existing templates for creating a .APK via GitHub Actions, but this didn't work out.
My package.json might be of interest, so here it is. But, do note that everything works for me locally.
{
"name": "MYAPP",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"server:mock": "ts-node scripts/mock/run-mocked-db.ts",
"test": "cross-env NODE_ENV=test jest",
"test:snap": "cross-env NODE_ENV=test jest --config ./tests/snapshot/config.js",
"e2e:build": "cross-env NODE_ENV=test detox build --configuration",
"e2e": "cross-env NODE_ENV=test detox test --configuration",
"e2e:android:nobuild": "cross-env NODE_ENV=test npm run e2e android",
"e2e:android": "cross-env NODE_ENV=test npm run e2e:build android && npm run e2e android",
"e2e:ios": "cross-env NODE_ENV=test yarn e2e:build ios && yarn e2e ios",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
"lint:nofix": "eslint \"{src,test}/**/*.ts\"",
"prettier": "prettier --check {src,test}/**/*.ts",
"precommit-msg": "echo 'Running pre-commit checks... [package.json]' && exit 0"
},
"pre-commit": [
"precommit-msg",
"prettier",
"lint:nofix",
"test:snap",
"e2e:android"
],
"dependencies": {
"@react-native-async-storage/async-storage": "^1.17.11",
"@react-native-community/cli": "^10.1.3",
"@react-native-cookies/cookies": "^6.2.1",
"@react-navigation/native": "^6.1.2",
"@react-navigation/native-stack": "^6.9.8",
"@tanstack/react-query": "^4.20.4",
"appcenter": "4.4.5",
"appcenter-analytics": "4.4.5",
"appcenter-crashes": "4.4.5",
"axios": "^1.2.2",
"babel-jest": "^29.3.1",
"detox": "^20.1.0",
"dotenv-flow": "^3.2.0",
"metro-config": "^0.74.0",
"react": "18.1.0",
"react-native": "0.70.6",
"react-native-dotenv": "^3.4.7",
"react-native-linear-gradient": "^2.6.2",
"react-native-paper": "^5.1.2",
"react-native-safe-area-context": "^4.5.0",
"react-native-screens": "^3.19.0",
"react-native-svg": "^13.7.0",
"react-native-vector-icons": "^9.2.0",
"rn-android-keyboard-adjust": "^2.1.1",
"rn-secure-storage": "^2.0.8",
"ts-jest": "^29.0.3"
},
"devDependencies": {
"@babel/core": "^7.12.9",
"@babel/runtime": "^7.12.5",
"@faker-js/faker": "^7.6.0",
"@react-native-community/eslint-config": "^2.0.0",
"@tsconfig/react-native": "^2.0.2",
"@types/dotenv-flow": "^3.2.0",
"@types/express": "^4.17.15",
"@types/jest": "^26.0.23",
"@types/react": "^18.0.21",
"@types/react-native": "^0.70.6",
"@types/react-native-android-keyboard-adjust": "^1.0.0",
"@types/react-native-vector-icons": "^6.4.13",
"@types/react-test-renderer": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.37.0",
"@typescript-eslint/parser": "^5.37.0",
"axios-mock-adapter": "^1.21.2",
"body-parser": "^1.20.1",
"cross-env": "^7.0.3",
"eslint": "^7.32.0",
"express": "^4.18.2",
"jest": "^29.3.1",
"metro-react-native-babel-preset": "0.72.3",
"pre-commit": "^1.2.2",
"react-native-svg-transformer": "^1.0.0",
"react-test-renderer": "18.1.0",
"ts-node": "^10.9.1",
"typescript": "^4.8.3"
},
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
}
}
Doing things such as replicating my local environment via the build file did not work for me either. I tried applying windows-latest since I have a Windows machine, but I ended up on the same issue. I also tried checking whether it was because I forgot some configuration by trying to build it on a clean machine, which I could do (but, this is local, I want to automate the build process.)
It turns out that the issue occurred due to two separate factors. First, the recursion issue is due to an issue within gradle itself -- specifically for 7.4.2 till 7.5.2. Updating to gradle 7.6 in the
gradle-wrapper.propertiesfile, e.g.https\://services.gradle.org/distributions/gradle-7.6-all.zipof react native will solve this.As for the bundling part, I was unable to figure it out. I ended up bundling 'manually' beforehand in the build file. My build file ended up looking as follows:
Basically, I ended up bundling beforehand, and opting out of bundling if it fails in the assembleRelease itself.