diff --git a/.github/actions/cache-gradle-build/action.yml b/.github/actions/cache-gradle-build/action.yml index 880c828..9f30b77 100644 --- a/.github/actions/cache-gradle-build/action.yml +++ b/.github/actions/cache-gradle-build/action.yml @@ -17,6 +17,15 @@ outputs: runs: using: composite steps: + # Keep the path lists here and in save-gradle-build-cache identical: actions/cache refuses to + # save when the restore and save paths differ. + # + # Scope: caches are only visible to runs on the same ref (plus the default branch), so a run + # for a new release tag never sees the previous tag's cache — see "Restrictions for accessing + # a cache". This cache therefore pays off for re-runs of a tag and for warm branch runs, not + # across tags. That is why the key must stay stable across releases: everything cached here + # is a pure function of these files, and the release version is injected through Gradle + # properties (see set-version) rather than by editing a hashed source file. - uses: actions/cache/restore@v5 if: inputs.scope == 'android' id: cache-android @@ -28,6 +37,7 @@ runs: app/android/build utils/shared/build utils/android/build + !app/android/build/outputs key: gradle-build-${{ runner.os }}-${{ runner.arch }}-v2-${{ inputs.scope }}-${{ hashFiles('gradle/libs.versions.toml', 'settings.gradle.kts', 'gradle.properties', 'build.gradle.kts', 'app/desktop/build.gradle.kts', 'app/desktop/proguard-rules.pro', 'app/shared/build.gradle.kts', 'app/android/build.gradle.kts', 'utils/shared/build.gradle.kts', 'utils/android/build.gradle.kts') }} restore-keys: | gradle-build-${{ runner.os }}-${{ runner.arch }}-v2-${{ inputs.scope }}- @@ -37,14 +47,20 @@ runs: if: inputs.scope != 'android' id: cache with: + # Desktop scopes never build Android, so the Android module dirs are not archived here. + # The negated paths are final artifacts and regenerated intermediates: they are rebuilt for + # every version anyway, and archiving them is what pushed this repo past the 10 GB cache + # limit and into eviction (see the size of the per-scope entries). path: | .gradle build - app/desktop/build app/shared/build - app/android/build utils/shared/build - utils/android/build + app/desktop/build + !app/desktop/build/distributions + !app/desktop/build/ci + !app/desktop/build/compose/binaries + !app/desktop/build/compose/tmp key: gradle-build-${{ runner.os }}-${{ runner.arch }}-v2-${{ inputs.scope }}-${{ hashFiles('gradle/libs.versions.toml', 'settings.gradle.kts', 'gradle.properties', 'build.gradle.kts', 'app/desktop/build.gradle.kts', 'app/desktop/proguard-rules.pro', 'app/shared/build.gradle.kts', 'app/android/build.gradle.kts', 'utils/shared/build.gradle.kts', 'utils/android/build.gradle.kts') }} restore-keys: | gradle-build-${{ runner.os }}-${{ runner.arch }}-v2-${{ inputs.scope }}- diff --git a/.github/actions/save-gradle-build-cache/action.yml b/.github/actions/save-gradle-build-cache/action.yml index 6eae838..e260beb 100644 --- a/.github/actions/save-gradle-build-cache/action.yml +++ b/.github/actions/save-gradle-build-cache/action.yml @@ -9,6 +9,8 @@ inputs: runs: using: composite steps: + # Path lists must stay identical to cache-gradle-build: actions/cache skips the save when the + # restore and save paths do not match. - name: Save Android Gradle build cache if: inputs.scope == 'android' uses: actions/cache/save@v5 @@ -22,6 +24,7 @@ runs: app/android/build utils/shared/build utils/android/build + !app/android/build/outputs - name: Save Gradle build cache if: inputs.scope != 'android' uses: actions/cache/save@v5 @@ -31,8 +34,10 @@ runs: path: | .gradle build - app/desktop/build app/shared/build - app/android/build utils/shared/build - utils/android/build + app/desktop/build + !app/desktop/build/distributions + !app/desktop/build/ci + !app/desktop/build/compose/binaries + !app/desktop/build/compose/tmp diff --git a/.github/actions/set-version/action.yml b/.github/actions/set-version/action.yml index 7500b45..0dc3088 100644 --- a/.github/actions/set-version/action.yml +++ b/.github/actions/set-version/action.yml @@ -1,5 +1,5 @@ name: Set release version -description: Writes versionName/versionCode into build.gradle.kts for CI +description: Makes versionName/versionCode available to Gradle for CI inputs: version: @@ -12,19 +12,17 @@ runs: - name: Set version shell: bash run: | + set -euo pipefail VERSION="${{ inputs.version }}" VERSION_CODE="$(printf '%s' "$VERSION" | sed 's/[^0-9]//g')" if [[ -z "$VERSION_CODE" ]]; then VERSION_CODE=1 fi - GRADLE_FILE="build.gradle.kts" - CURRENT_NAME="$(sed -n 's/.*extra\["versionName"\] = "\([^"]*\)".*/\1/p' "$GRADLE_FILE" | head -n 1)" - CURRENT_CODE="$(sed -n 's/.*extra\["versionCode"\] = \([0-9]*\).*/\1/p' "$GRADLE_FILE" | head -n 1)" - if [[ "$CURRENT_NAME" == "$VERSION" && "$CURRENT_CODE" == "$VERSION_CODE" ]]; then - echo "versionName=$VERSION versionCode=$VERSION_CODE (unchanged)" - exit 0 - fi - sed -i.bak "s/extra\\[\"versionName\"\\] = \"[^\"]*\"/extra[\"versionName\"] = \"$VERSION\"/" "$GRADLE_FILE" - sed -i.bak "s/extra\\[\"versionCode\"\\] = [0-9]*/extra[\"versionCode\"] = $VERSION_CODE/" "$GRADLE_FILE" - rm -f "$GRADLE_FILE.bak" + # Injected as Gradle project properties rather than rewriting build.gradle.kts: that file + # is hashed into the Gradle build-cache key, so editing it per release meant no release + # could ever reuse the previous one's cache. + { + echo "ORG_GRADLE_PROJECT_versionName=$VERSION" + echo "ORG_GRADLE_PROJECT_versionCode=$VERSION_CODE" + } >> "$GITHUB_ENV" echo "versionName=$VERSION versionCode=$VERSION_CODE" diff --git a/build.gradle.kts b/build.gradle.kts index c24ea30..8e5f80e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,9 +8,16 @@ plugins { alias(libs.plugins.google.services) apply false } -/** Single source of truth for app version (APK + generated [AppBuildInfo]). */ -extra["versionName"] = "1.2.0" -extra["versionCode"] = 120 +/** + * Single source of truth for app version (APK + generated [AppBuildInfo]). + * + * CI injects `-PversionName` / `-PversionCode` (exported as `ORG_GRADLE_PROJECT_*` by + * .github/actions/set-version) instead of rewriting this file. This file is hashed into the + * Gradle build-cache key, so a per-release `sed` here gave every release a brand-new cache key + * and orphaned the previous one — roughly a gigabyte of dead cache per release tag. + */ +extra["versionName"] = providers.gradleProperty("versionName").getOrElse("1.2.0") +extra["versionCode"] = providers.gradleProperty("versionCode").getOrElse("120").toInt() buildscript { repositories {