diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 915cd3f..30f1fe5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,5 +47,28 @@ jobs: - name: Check build-logic run: ./gradlew -p build-logic ktfmtCheck checkSortDependencies detekt + # `build` overlaps the Gradle and Kotlin daemons, Kotlin/Native, R8, webpack and karma's + # Chrome, and peaks above the runner's 16 GB. When it does, the runner itself is OOM-killed + # and the job ends as "The operation was canceled" with no Gradle error. The image ships a + # 4 GB swapfile; a larger one lets the peak page out instead. + - name: Enlarge swap + run: | + sudo swapoff -a + sudo rm -f /mnt/swapfile + sudo fallocate -l 12G /mnt/swapfile + sudo chmod 600 /mnt/swapfile + sudo mkswap /mnt/swapfile + sudo swapon /mnt/swapfile + free -h + + # The sampler prints into this step's own log so the numbers survive the runner being + # killed; a file or a later step would not. - name: Build - run: ./gradlew build + run: | + ( while sleep 30; do + echo "::group::resources $(date -u +%T)" + free -m; df -h / /mnt + ps -eo rss:10,args --sort=-rss | head -8 | cut -c1-160 + echo "::endgroup::" + done ) & + ./gradlew build diff --git a/build.gradle.kts b/build.gradle.kts index 7fb9912..cd5cfc6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,7 @@ import com.ncorti.ktfmt.gradle.KtfmtExtension import org.gradle.buildconfiguration.tasks.UpdateDaemonJvm +import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest +import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack // AGP 9.x has built-in Kotlin support, pinned to an older Kotlin Gradle Plugin (KGP) version. // Metro 1.3.2 needs Kotlin >= 2.3.20 and Compose needs a matching compiler plugin, so we override @@ -29,3 +31,21 @@ tasks.named("updateDaemonJvm") { languageVersion = JavaLanguageVersion.of(25) vendor.set(JvmVendorSpec.AMAZON) } + +// On CI, run the webpack and karma tasks one at a time. Each starts a Node process sized for a +// whole Compose bundle, and karma adds a Chrome on top. `build` has seven modules with web +// executables, so under org.gradle.parallel they overlap alongside the Gradle and Kotlin daemons, +// exhaust ubuntu-latest's 16 GB, and the runner is killed: the job ends as "The operation was +// canceled" or "The runner has received a shutdown signal", with no Gradle error at all. +// Locally there is enough memory, so they stay parallel. +abstract class WebBundleTaskLimiter : BuildService + +val webBundleTaskLimiter = + gradle.sharedServices.registerIfAbsent("webBundleTaskLimiter", WebBundleTaskLimiter::class) { + if (providers.environmentVariable("CI").isPresent) maxParallelUsages = 1 + } + +subprojects { + tasks.withType().configureEach { usesService(webBundleTaskLimiter) } + tasks.withType().configureEach { usesService(webBundleTaskLimiter) } +}