98 lines
3.8 KiB
Groovy
98 lines
3.8 KiB
Groovy
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||
|
||
import java.text.SimpleDateFormat
|
||
|
||
/**
|
||
* Generates a "unique" versionCode for release builds.
|
||
*
|
||
* The resulting versionCode depends on the local timezone of the machine running this script.
|
||
* This is OK because we only use this for release builds on CI, where the timezone is fixed.
|
||
*
|
||
* Format: byDDDHHmm
|
||
* - b = base / epoch digit
|
||
* Historically hardcoded to "3". This digit is incremented when the year-derived
|
||
* component overflows its single digit (e.g., in 2026).
|
||
* - y = 1 digit derived from (two-digit year - 16), modulo 10
|
||
* - DDD = day of year (001–366), zero-padded to 3 digits
|
||
* - HHmm = 24h time (00–23)(00–59)
|
||
*
|
||
* Example:
|
||
* Sept 6, 2017 @ 09:41
|
||
* year = 17 - 16 = 1
|
||
* base = 3
|
||
* -> 3-1-249-09-41 -> 312490941
|
||
*
|
||
* Historical note:
|
||
* Focus first shipped in 2017. The original scheme unconditionally used (yy - 16) which
|
||
* only fit in a single digit from 2017–2025.
|
||
*
|
||
* 2026 rollover:
|
||
* In 2026, (yy - 16) became 10. Allowing this to grow to two digits breaks the intended
|
||
* byDDDHHmm layout and can exceed Play / int limits.
|
||
*
|
||
* To preserve:
|
||
* - a single-digit `y`
|
||
* - monotonic versionCodes across year boundaries
|
||
*
|
||
* we keep `y` as (yearOffset % 10) and carry overflow into the base digit:
|
||
* 2025 -> base=3, y=9 -> 39DDDHHmm
|
||
* 2026 -> base=4, y=0 -> 40DDDHHmm
|
||
*/
|
||
ext {
|
||
// "Epoch" digit(s). Historically this was "3".
|
||
// We bump it by +1 each time (yy - 16) crosses another multiple of 10 (i.e., 2026, 2036, ...).
|
||
def epochDigit = 3
|
||
|
||
def today = new Date()
|
||
|
||
def yy = (new SimpleDateFormat("yy").format(today) as int)
|
||
def yearOffset = yy - 16 // 2017 -> 1, 2025 -> 9, 2026 -> 10, etc.
|
||
if (yearOffset < 0) {
|
||
throw new GradleException(
|
||
"versionCode yearOffset underflow: yearOffset=$yearOffset (yy=$yy)."
|
||
)
|
||
}
|
||
|
||
// Keep the "y" component as one digit, and carry overflow into the epoch digit.
|
||
def carry = (int) (yearOffset / 10)
|
||
def yearDigit = (int) (yearOffset % 10)
|
||
|
||
def epoch = epochDigit + carry
|
||
if (epoch >= 10) {
|
||
throw new GradleException(
|
||
"versionCode epoch overflow: epoch=$epoch (yy=$yy). Update versionCode scheme."
|
||
)
|
||
}
|
||
|
||
// We use the day in the Year (e.g. 248) as opposed to month + day (0510) because it's one digit shorter.
|
||
// If needed we pad with zeros (e.g. 25 -> 025)
|
||
def day = String.format("%03d", (new SimpleDateFormat("D").format(today) as int))
|
||
|
||
// We append the hour in day (24h) and minute in hour (7:26 pm -> 1926). We do not append
|
||
// seconds. This assumes that we do not need to build multiple release(!) builds the same
|
||
// minute.
|
||
def time = new SimpleDateFormat("HHmm").format(today)
|
||
|
||
// Build the final versionCode using the previously-calculated inputs.
|
||
def versionCode = ("${epoch}${yearDigit}${day}${time}" as long)
|
||
|
||
// The Play Console has historically enforced a 2,100,000,000 cap. Keep a defensive ceiling here.
|
||
// Even without this, Android requires versionCode to fit in a signed 32-bit int.
|
||
def MAX_VERSION_CODE = 2_100_000_000
|
||
if (versionCode > MAX_VERSION_CODE) {
|
||
throw new GradleException(
|
||
"Generated versionCode exceeds MAX_VERSION_CODE ($MAX_VERSION_CODE): $versionCode (from $versionCodeStr)"
|
||
)
|
||
}
|
||
if (versionCode > Integer.MAX_VALUE) {
|
||
throw new GradleException(
|
||
"Generated versionCode exceeds Integer.MAX_VALUE: $versionCode (from $versionCodeStr)"
|
||
)
|
||
}
|
||
|
||
generatedVersionCode = (versionCode as int)
|
||
println("Generated versionCode: $generatedVersionCode")
|
||
println()
|
||
}
|