@@ -30,9 +30,6 @@ container_name=$default_container_name
3030# Is cache enabled (see `$cache_paths` to see what's cached)?
3131cache=
3232
33- # Is gradle cache enabled? Needed only for android integration test and build
34- cache_gradle=
35-
3633# Name of the docker volume to put the cache on
3734cache_volume=" $base_name -cache"
3835
@@ -47,6 +44,10 @@ cache_paths=(
4744 # Cargo per project
4845 /opt/ouisync-app/ouisync/target
4946
47+ # Gradle
48+ /root/.gradle/caches
49+ /root/.gradle/wrapper
50+
5051 # Dart
5152 /root/.pub-cache
5253
@@ -84,7 +85,6 @@ function print_help() {
8485 echo " Options:"
8586 echo " --platform <linux|android> Platform for which to run the tests"
8687 echo " --api <API> Android API level to run in"
87- echo " --prebuild Only build the test binary, don't run the tests. Useful for seeding the cache."
8888 ;;
8989 " analyze" )
9090 echo " Analyze the dart source code"
@@ -136,8 +136,8 @@ function print_help() {
136136 esac
137137}
138138
139- function build_container () {
140- log_group_begin " Building image $image_name "
139+ function build_image () {
140+ log_group_begin " Build image $image_name "
141141
142142 ndk_version=$( cat ndk-version.txt)
143143 dock build -t $image_name --build-arg NDK_VERSION=$ndk_version - < docker/Dockerfile.linux
@@ -155,48 +155,11 @@ function create_cache_volume() {
155155 log_group_end
156156}
157157
158- # Gradle cache doesn't work well when accessed from multiple containers concurrently. This is
159- # because the Gradle daemons need to communicate with each other over localhost TCP sockets in
160- # order to coordinate locking and this doesn't work when each daemon runs in a separate container
161- # (due to network separation). One way around this is to run the containers with `--network host`
162- # but that comes with its own issues and is generally not worth it. To solve this, we copy
163- # (using rsync) the Gradle cache files from the cache volume to the container at the beginning of
164- # the run and the copy them back at the end of it. We use file locks to synchronize the copies.
165- gradle_cache_root=/root/.gradle
166- gradle_cache_dirs=(caches wrapper)
167-
168- function gradle_cache_rsync() {
169- local lock_mode=$1
170- local src=$2
171- local dst=$3
172-
173- exe mkdir -p " ${gradle_cache_dirs[@]/#/ $src / } "
174- exe flock $lock_mode /mnt/cache/gradle.lock \
175- rsync \
176- -a \
177- --exclude=' *.lock' \
178- ${gradle_cache_dirs[@]/*/ --include=/ &/ --include=/ &/*** } \
179- --exclude=' *' \
180- " $src /" " $dst "
181- }
182-
183- function restore_gradle_cache() {
184- log_group_begin " Restore gradle cache"
185- gradle_cache_rsync --shared /mnt/cache$gradle_cache_root $gradle_cache_root
186- log_group_end
187- }
188-
189- function save_gradle_cache() {
190- log_group_begin " Save gradle cache"
191- gradle_cache_rsync --exclusive $gradle_cache_root /mnt/cache$gradle_cache_root
192- log_group_end
193- }
194-
195158function start_container() {
196159 if [ -z " $commit " -a -z " $srcdir " ]; then error " Missing one of --commit or --srcdir" ; fi
197160 if [ -n " $commit " -a -n " $srcdir " ]; then error " --commit and --src are mutually exclusive" ; fi
198161
199- build_container
162+ build_image
200163
201164 if [ " $cache " = 1 ]; then
202165 create_cache_volume
@@ -256,10 +219,6 @@ function start_container() {
256219 fi
257220 log_group_end
258221
259- if [ " $cache " = 1 -a " $cache_gradle " = 1 ]; then
260- restore_gradle_cache
261- fi
262-
263222 # Generate bindings (TODO: This should be done automatically)
264223 log_group_begin " Generate bindings"
265224 exe -w /opt/ouisync-app/ouisync/bindings/dart -t dart pub get
@@ -270,19 +229,15 @@ function start_container() {
270229 exe -w /opt/ouisync-app -t dart pub get
271230 log_group_end
272231
273- # Run cargo sweep to delete all cargo artifacts older than 30 days (prevents unbounded cache grow)
274232 if [ " $cache " = 1 ]; then
233+ # Run cargo sweep to delete all cargo artifacts older than 30 days (prevents unbounded cache grow)
275234 log_group_begin " Prune cached cargo artifacts"
276235 exe -w /opt/ouisync-app/ouisync -t cargo sweep --recursive --time 30
277236 log_group_end
278237 fi
279238}
280239
281240function stop_container() {
282- if [ " $cache " = 1 -a " $cache_gradle " ]; then
283- save_gradle_cache
284- fi
285-
286241 echo " Stop container $container_name "
287242 dock container stop $container_name
288243}
@@ -335,7 +290,7 @@ function manage_container() {
335290 stop_container
336291 ;;
337292 build)
338- build_container
293+ build_image
339294 ;;
340295 esac
341296}
@@ -349,7 +304,6 @@ function init() {
349304# Build the release artifacts for linux and android
350305function build() {
351306 rsync_include_git=1
352- cache_gradle=1
353307
354308 local dst_dir=" ./releases/$container_name "
355309 local flavor=
@@ -511,44 +465,46 @@ function emulator_stop() {
511465}
512466
513467function integration_test_android() {
514- cache_gradle=1
468+ # Share also the build directoty between the integration test jobs, to speed them up
469+ cache_paths+=(" /opt/ouisync-app/build" )
470+
515471 init
516472
517473 local api=
518- local prebuild=
519474
520475 while true ; do
521476 case ${1-} in
522477 --api)
523478 api=" ${2-} "
524479 shift 2
525480 ;;
526- --prebuild)
527- prebuild=1
528- shift
529- ;;
530481 * )
531482 break
532483 ;;
533484 esac
534485 done
535486
536- if [ " $prebuild " = 1 ]; then
537- log_group_begin " Pre-build the test binary"
538- exe -w /opt/ouisync-app -t flutter build apk --debug --flavor itest --target-platform android-x64
539- log_group_end
540- return
541- fi
542-
543487 if [ -z " $api " ]; then
544488 error " Missing --api"
545489 fi
546490
547491 emulator_start --api $api
548492
549493 log_group_begin " Run tests"
550- exe -w /opt/ouisync-app -t flutter \
551- test integration_test --flavor itest --ignore-timeouts $@
494+
495+ # Note: While multiple gradle daemons can safely access the gradle home directory
496+ # (~/.gradle) concurrently when running on the same machine, the same is not true when they run
497+ # on different containers while the gradle home is shared between them. This is because the
498+ # daemons need to coordinate locking and they use localhost TCP sockets to do that which
499+ # doesn't work in containers due to network isolation. To work around that, we put a big fat
500+ # lock around this whole command so that only one container can run it at a time.
501+ #
502+ # TODO: This is not optimal. Try to find a way to split the test building and running into two
503+ # steps and put the lock only around the build step (which needs to happen only once anyway).
504+ exe -w /opt/ouisync-app -t \
505+ flock /mnt/cache/gradle.lock \
506+ flutter test integration_test --flavor itest --ignore-timeouts $@
507+
552508 log_group_end
553509
554510 emulator_stop
0 commit comments