Implement semi-automated releases

This still requires manually running the gradle task but the next step will be getting a GitHub workflow to perform the release whenever a new tag is pushed
This commit is contained in:
William Brawner 2023-09-28 20:15:38 -06:00
parent f51d669da3
commit f69bf81630
Signed by: wbrawner
GPG key ID: 8FF12381C6C90D35
10 changed files with 168 additions and 12 deletions

View file

@ -8,6 +8,8 @@ plugins {
id("kotlin-android") id("kotlin-android")
id("kotlin-kapt") id("kotlin-kapt")
id("com.osacky.fladle") id("com.osacky.fladle")
id("com.github.triplet.play") version "3.8.4"
id("com.wbrawner.releasehelper")
} }
val keystoreProperties = Properties() val keystoreProperties = Properties()
@ -46,8 +48,8 @@ android {
applicationId = "com.wbrawner.simplemarkdown" applicationId = "com.wbrawner.simplemarkdown"
minSdk = 23 minSdk = 23
targetSdk = 33 targetSdk = 33
versionCode = 39 versionCode = 41
versionName = "0.8.15" versionName = "0.8.16"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments["clearPackageData"] = "true" testInstrumentationRunnerArguments["clearPackageData"] = "true"
buildConfigField("boolean", "ENABLE_CUSTOM_CSS", "true") buildConfigField("boolean", "ENABLE_CUSTOM_CSS", "true")
@ -87,6 +89,19 @@ android {
execution = "ANDROIDX_TEST_ORCHESTRATOR" execution = "ANDROIDX_TEST_ORCHESTRATOR"
} }
namespace = "com.wbrawner.simplemarkdown" namespace = "com.wbrawner.simplemarkdown"
playConfigs {
register("play") {
enabled.set(true)
commit.set(true)
}
}
}
play {
commit.set(false)
enabled.set(false)
track.set("production")
defaultToAppBundles.set(true)
} }
dependencies { dependencies {
@ -124,8 +139,7 @@ dependencies {
} }
android.productFlavors.forEach { flavor -> android.productFlavors.forEach { flavor ->
if (getGradle().getStartParameter().getTaskRequests().toString().toLowerCase() if (gradle.startParameter.taskRequests.toString().lowercase(Locale.getDefault()).contains(flavor.name)
.contains(flavor.name)
&& flavor.name == "play" && flavor.name == "play"
) { ) {
apply(plugin = "com.google.gms.google-services") apply(plugin = "com.google.gms.google-services")

View file

@ -29,3 +29,13 @@
-keepattributes SourceFile,LineNumberTable -keepattributes SourceFile,LineNumberTable
-keep public class * extends java.lang.Exception -keep public class * extends java.lang.Exception
### Crashlytics ### ### Crashlytics ###
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.conscrypt.Conscrypt$Version
-dontwarn org.conscrypt.Conscrypt
-dontwarn org.conscrypt.ConscryptHostnameVerifier
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE

View file

@ -46,8 +46,8 @@ class SettingsFragment
} }
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (!isAdded) return if (!isAdded || sharedPreferences == null || key == null) return
val preference = findPreference(key) as? ListPreference ?: return val preference = findPreference(key) as? ListPreference ?: return
setListPreferenceSummary(sharedPreferences, preference) setListPreferenceSummary(sharedPreferences, preference)
if (preference.key != getString(R.string.pref_key_dark_mode)) { if (preference.key != getString(R.string.pref_key_dark_mode)) {
@ -59,7 +59,7 @@ class SettingsFragment
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
} }
val darkModeValue = sharedPreferences.getString(preference.key, null) val darkModeValue = sharedPreferences.getString(preference.key, null)
if (darkModeValue != null && darkModeValue.isNotEmpty()) { if (!darkModeValue.isNullOrEmpty()) {
if (darkModeValue.equals(getString(R.string.pref_value_light), ignoreCase = true)) { if (darkModeValue.equals(getString(R.string.pref_value_light), ignoreCase = true)) {
darkMode = AppCompatDelegate.MODE_NIGHT_NO darkMode = AppCompatDelegate.MODE_NIGHT_NO
} else if (darkModeValue.equals(getString(R.string.pref_value_dark), ignoreCase = true)) { } else if (darkModeValue.equals(getString(R.string.pref_value_dark), ignoreCase = true)) {

View file

@ -0,0 +1,2 @@
- Add support for themed icon
- Fix crashes

View file

@ -1,5 +1,3 @@
import java.net.URI
buildscript { buildscript {
repositories { repositories {
gradlePluginPortal() gradlePluginPortal()
@ -7,7 +5,7 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath("com.android.tools.build:gradle:7.4.2") classpath("com.android.tools.build:gradle:8.1.1")
classpath("com.google.gms:google-services:4.3.15") classpath("com.google.gms:google-services:4.3.15")
classpath("com.google.firebase:firebase-crashlytics-gradle:2.9.4") classpath("com.google.firebase:firebase-crashlytics-gradle:2.9.4")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20")

1
buildSrc/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build/

18
buildSrc/build.gradle.kts Normal file
View file

@ -0,0 +1,18 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
gradlePlugin {
plugins {
register("release-helper") {
id = "com.wbrawner.releasehelper"
implementationClass = "com.wbrawner.releasehelper.ReleaseHelperPlugin"
}
}
}

View file

@ -0,0 +1,110 @@
package com.wbrawner.releasehelper
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Exec
import org.gradle.kotlin.dsl.extra
import org.gradle.kotlin.dsl.provideDelegate
import java.io.ByteArrayOutputStream
import java.io.File
class ReleaseHelperPlugin : Plugin<Project> {
override fun apply(target: Project) {
target.tasks.register("getLatestTag", Exec::class.java) {
val latestTag = ByteArrayOutputStream()
standardOutput = latestTag
commandLine("git describe --tags --abbrev=0".split(" "))
doLast {
target.project.extra["latestTag"] = latestTag.toString().trim()
logger.info("Latest tag: ${target.project.extra["latestTag"]}")
}
}
target.tasks.register("changelog") {
val changelogFile = File(target.projectDir, "src/main/play/release-notes/en-US/default.txt")
inputs.property("tag", target.provider {
target.project.extra["latestTag"]
})
outputs.file(changelogFile)
dependsOn("getLatestTag")
doLast {
val latestTag: String by target.project.extra
val changelog = ByteArrayOutputStream()
target.exec {
standardOutput = changelog
commandLine = "git log --format=\"%B\" ${latestTag.trim()}..".split(" ")
}
changelogFile.writeText(
changelog.toString()
.split("\n")
.mapNotNull { it.trim('"').ifBlank { null } }
.joinToString("\n") { "- $it" }
)
}
}
target.tasks.register("majorRelease") {
dependsOn("changelog", "getLatestTag")
doLast {
val latestTag: String by target.project.extra
val newVersion = latestTag.incrementVersion(VersionPart.MAJOR)
target.updateVersionName(latestTag, newVersion)
target.exec {
commandLine = "git tag $newVersion".split(" ")
}
}
}
target.tasks.register("minorRelease") {
dependsOn("changelog", "getLatestTag")
doLast {
val latestTag: String by target.project.extra
val newVersion = latestTag.incrementVersion(VersionPart.MAJOR)
target.updateVersionName(latestTag, newVersion)
target.exec {
commandLine = "git tag $newVersion".split(" ")
}
}
}
target.tasks.register("patchRelease") {
dependsOn("changelog", "getLatestTag")
doLast {
val latestTag: String by target.project.extra
val newVersion = latestTag.incrementVersion(VersionPart.MAJOR)
target.updateVersionName(latestTag, newVersion)
target.exec {
commandLine = "git tag $newVersion".split(" ")
}
}
}
}
}
private enum class VersionPart {
MAJOR,
MINOR,
PATCH
}
private fun String.incrementVersion(part: VersionPart) = split(".")
.mapIndexed { index, numberString ->
val number = numberString.toInt()
return@mapIndexed if (index == part.ordinal) {
number + 1
} else if (index > part.ordinal) {
0
} else {
number
}
}
.joinToString(".")
private fun Project.updateVersionName(oldVersionName: String, newVersionName: String) {
File(projectDir, "app/build.gradle.kts").apply {
writeText(
readText().replace("versionName = \"$oldVersionName\"", "versionName = \"$newVersionName\"")
)
}
}

View file

@ -9,7 +9,10 @@
# Specifies the JVM arguments used for the daemon process. # Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings. # The setting is particularly useful for tweaking memory settings.
android.defaults.buildfeatures.buildconfig=true
android.enableJetifier=true android.enableJetifier=true
android.nonFinalResIds=false
android.nonTransitiveRClass=false
android.useAndroidX=true android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536m org.gradle.jvmargs=-Xmx1536m

View file

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip