Skip to content

WIP: Move release tasks to separate scripts for better reusability #154

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/gradle-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ jobs:
- run: git config user.name 'Nya Ξlimu'
- run: git config user.email '[email protected]'

- run: ./gradlew releaseClean
- run: ./gradlew releasePrepare -PbumpType=patch
- run: ./gradlew releasePerform
- run: |
./gradlew releaseClean
./gradlew app:releasePrepare -PbumpType=patch
./gradlew app:releasePerform

./gradlew utils:releasePrepare -PbumpType=patch
./gradlew utils:releasePerform
env:
GITHUB_TOKEN: ${{ github.token }}
114 changes: 9 additions & 105 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
apply plugin: 'com.android.application'
apply plugin: 'org.ajoberstar.grgit'
apply from: "${rootProject.projectDir}/gradle/prepare_release.gradle"
apply from: "${rootProject.projectDir}/gradle/clean_release.gradle"
apply from: "${rootProject.projectDir}/gradle/perform_release.gradle"
apply from: "${rootProject.projectDir}/gradle/ensure_clean.gradle"

android {
compileSdk 34
Expand Down Expand Up @@ -72,128 +76,28 @@ dependencies {

task ensureCleanRepo {
doLast {
if (!grgit.repository.jgit.status().call().clean) {
throw new GradleException('Git status is not clean, please stash your changes!')
}
ensureClean()
}
}

task releaseClean(dependsOn: ensureCleanRepo) {
doLast {
def clean = true
def applicationId = android.defaultConfig.applicationId

String headCommitMessage = grgit.head().shortMessage
while (headCommitMessage.contains("[gradle-release-task]")) {
clean = false
println "Found git commit: $headCommitMessage"
if (headCommitMessage.indexOf("$applicationId-") > -1) {
def tagName = headCommitMessage.split("$applicationId-")[1]
println "Removing the git tag: $tagName"
try {
grgit.tag.remove {
names = [tagName]
}
} catch (Exception e) {
println "Error while removing git tag:\n $e"
}
}
println "Resetting the git commit permanently!"
grgit.reset(commit: "HEAD~1", mode: "hard")
headCommitMessage = grgit.head().shortMessage

}
if (clean){
println "Repository is already clean"
}
println "Done!"
cleanRelease()
}
}

// Task parameters:
// bumpVersion -> if available will specify new versionName directly and ignores the `bumpType` parameter.
// bumpType[major|minor|patch] -> will specify how the version bumping occurs.

task releasePrepare(dependsOn: ensureCleanRepo) {
doLast {
def applicationId = android.defaultConfig.applicationId
def versionName = android.defaultConfig.versionName

if (versionName.indexOf("-") > -1) {
versionName = versionName.split("-")[0]
}

// Prepare the release commit with the specific tag.
String buildText = buildFile.getText()
buildText = buildText.replaceFirst(/versionName(\s+.*)/, "versionName '$versionName'")
buildFile.setText(buildText) //replace the build file's text
grgit.add(patterns: ['app/build.gradle'])
try {
grgit.commit(message: "[gradle-release-task] prepare release $applicationId-$versionName")
} catch (Exception e) {
throw new GradleException("Failed to commit, error:\n $e")
}
try {
grgit.tag.add {
name = versionName
message = "Release of $versionName"
}
} catch (Exception e) {
throw new GradleException("Failed to tag the repo, error:\n $e")
}

// Set new version name from input parameters.
def newVersionName
if (project.properties.containsKey("bumpVersion")) {
newVersionName = project.properties["bumpVersion"]
println "Bumping the version directly (bumpVersion=$newVersionName)"
} else if (project.properties.containsKey("bumpType")) {
def (major, minor, patch) = versionName.tokenize('.')
switch (bumpType) {
case "major":
major = major.toInteger() + 1
minor = 0
patch = 0
break
case "minor":
minor = minor.toInteger() + 1
break
case "patch":
patch = patch.toInteger() + 1
break
}
newVersionName = "$major.$minor.$patch"
} else {
throw new GradleException('Either bumpType or bumpVersion parameters should be provided')
}

// Prepare for next development iteration.
def versionCode = android.defaultConfig.versionCode
def newVersionCode = versionCode + 1
println "Bumping versionName from $versionName to $newVersionName"
println "Bumping versionCode from $versionCode to $newVersionCode"
buildText = buildFile.getText()
buildText = buildText.replaceFirst(/versionName(\s+.*)/, "versionName '$newVersionName-SNAPSHOT'")
buildText = buildText.replaceFirst(/versionCode(\s+.*)/, "versionCode $newVersionCode")
buildFile.setText(buildText) //replace the build file's text
grgit.add(patterns: ['app/build.gradle'])
grgit.commit(message: "[gradle-release-task] prepare for next development iteration")
println "Done!"
prepareRelease()
}
}

task releasePerform(dependsOn: ensureCleanRepo) {
doLast {
boolean force = false
if (project.properties.containsKey("force")) {
force = project.properties["force"]
}
println "Pushing the newest commits to the remote repository (force: $force)"
try {
grgit.push(force: force, tags: true)
} catch (Exception e) {
throw new GradleException("Failed to push to the repo,\n" +
" you can try using -Pforce=true parameter to force the push, error: \n$e")
}
println "Done!"
performRelease()
}
}
29 changes: 29 additions & 0 deletions gradle/clean_release.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
ext.cleanRelease = {
def clean = true
def applicationId = android.defaultConfig.applicationId

String headCommitMessage = grgit.head().shortMessage
while (headCommitMessage.contains("[gradle-release-task]")) {
clean = false
println "Found git commit: $headCommitMessage"
if (headCommitMessage.indexOf("$applicationId-") > -1) {
def tagName = headCommitMessage.split("$applicationId-")[1]
println "Removing the git tag: $tagName"
try {
grgit.tag.remove {
names = [tagName]
}
} catch (Exception e) {
println "Error while removing git tag:\n $e"
}
}
println "Resetting the git commit permanently!"
grgit.reset(commit: "HEAD~1", mode: "hard")
headCommitMessage = grgit.head().shortMessage

}
if (clean){
println "Repository is already clean"
}
println "Done!"
}
5 changes: 5 additions & 0 deletions gradle/ensure_clean.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ext.ensureClean = {
if (!grgit.repository.jgit.status().call().clean) {
throw new GradleException('Git status is not clean, please stash your changes!')
}
}
14 changes: 14 additions & 0 deletions gradle/perform_release.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ext.performRelease = {
boolean force = false
if (project.properties.containsKey("force")) {
force = project.properties["force"]
}
println "Pushing the newest commits to the remote repository (force: $force)"
try {
grgit.push(force: force, tags: true)
} catch (Exception e) {
throw new GradleException("Failed to push to the repo,\n" +
" you can try using -Pforce=true parameter to force the push, error: \n$e")
}
println "Done!"
}
65 changes: 65 additions & 0 deletions gradle/prepare_release.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
ext.prepareRelease = {
def applicationId = android.defaultConfig.applicationId
def versionName = android.defaultConfig.versionName

if (versionName.indexOf("-") > -1) {
versionName = versionName.split("-")[0]
}

// Prepare the release commit with the specific tag.
String buildText = buildFile.getText()
buildText = buildText.replaceFirst(/versionName(\s+.*)/, "versionName '$versionName'")
buildFile.setText(buildText) //replace the build file's text
grgit.add(patterns: [project.name + '/build.gradle'])
try {
grgit.commit(message: "[gradle-release-task] prepare release $applicationId-$versionName")
} catch (Exception e) {
throw new GradleException("Failed to commit, error:\n $e")
}
try {
grgit.tag.add {
name = versionName
message = "Release of $versionName"
}
} catch (Exception e) {
throw new GradleException("Failed to tag the repo, error:\n $e")
}

// Set new version name from input parameters.
def newVersionName
if (project.properties.containsKey("bumpVersion")) {
newVersionName = project.properties["bumpVersion"]
println "Bumping the version directly (bumpVersion=$newVersionName)"
} else if (project.properties.containsKey("bumpType")) {
def (major, minor, patch) = versionName.tokenize('.')
switch (bumpType) {
case "major":
major = major.toInteger() + 1
minor = 0
patch = 0
break
case "minor":
minor = minor.toInteger() + 1
break
case "patch":
patch = patch.toInteger() + 1
break
}
newVersionName = "$major.$minor.$patch"
} else {
throw new GradleException('Either bumpType or bumpVersion parameters should be provided')
}
Comment on lines +28 to +51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Parameter Handling for Version Bumping

There is a potential issue in the switch-case block: the variable bumpType is referenced without being explicitly defined. This could lead to runtime errors if bumpType is not already available in the scope. To improve clarity and reliability, extract the property into a local variable before using it. For instance:

-    } else if (project.properties.containsKey("bumpType")) {
-        def (major, minor, patch) = versionName.tokenize('.')
-        switch (bumpType) {
+    } else if (project.properties.containsKey("bumpType")) {
+        def bumpType = project.properties["bumpType"]
+        def (major, minor, patch) = versionName.tokenize('.')
+        switch (bumpType) {

This ensures that bumpType is defined and clarifies its provenance.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Set new version name from input parameters.
def newVersionName
if (project.properties.containsKey("bumpVersion")) {
newVersionName = project.properties["bumpVersion"]
println "Bumping the version directly (bumpVersion=$newVersionName)"
} else if (project.properties.containsKey("bumpType")) {
def (major, minor, patch) = versionName.tokenize('.')
switch (bumpType) {
case "major":
major = major.toInteger() + 1
minor = 0
patch = 0
break
case "minor":
minor = minor.toInteger() + 1
break
case "patch":
patch = patch.toInteger() + 1
break
}
newVersionName = "$major.$minor.$patch"
} else {
throw new GradleException('Either bumpType or bumpVersion parameters should be provided')
}
// Set new version name from input parameters.
def newVersionName
if (project.properties.containsKey("bumpVersion")) {
newVersionName = project.properties["bumpVersion"]
println "Bumping the version directly (bumpVersion=$newVersionName)"
} else if (project.properties.containsKey("bumpType")) {
def bumpType = project.properties["bumpType"]
def (major, minor, patch) = versionName.tokenize('.')
switch (bumpType) {
case "major":
major = major.toInteger() + 1
minor = 0
patch = 0
break
case "minor":
minor = minor.toInteger() + 1
break
case "patch":
patch = patch.toInteger() + 1
break
}
newVersionName = "$major.$minor.$patch"
} else {
throw new GradleException('Either bumpType or bumpVersion parameters should be provided')
}


// Prepare for next development iteration.
def versionCode = android.defaultConfig.versionCode
def newVersionCode = versionCode + 1
println "Bumping versionName from $versionName to $newVersionName"
println "Bumping versionCode from $versionCode to $newVersionCode"
buildText = buildFile.getText()
buildText = buildText.replaceFirst(/versionName(\s+.*)/, "versionName '$newVersionName-SNAPSHOT'")
buildText = buildText.replaceFirst(/versionCode(\s+.*)/, "versionCode $newVersionCode")
buildFile.setText(buildText) //replace the build file's text
grgit.add(patterns: [project.name + '/build.gradle'])
grgit.commit(message: "[gradle-release-task] prepare for next development iteration")
println "Done!"
}
36 changes: 36 additions & 0 deletions utils/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ plugins {
id 'maven-publish'
}

apply from: "${rootProject.projectDir}/gradle/prepare_release.gradle"
apply from: "${rootProject.projectDir}/gradle/clean_release.gradle"
apply from: "${rootProject.projectDir}/gradle/perform_release.gradle"
apply from: "${rootProject.projectDir}/gradle/ensure_clean.gradle"

android {
compileSdk 34
namespace 'ai.elimu.content_provider.utils'
Expand Down Expand Up @@ -53,3 +58,34 @@ publishing {
tasks.named("publishReleasePublicationToMavenLocal") {
mustRunAfter(":utils:bundleReleaseAar")
}

tasks.register('ensureCleanRepo') {
doLast {
ensureClean()
}
}

tasks.register('releaseClean') {
dependsOn ensureCleanRepo
doLast {
cleanRelease()
}
}

// Task parameters:
// bumpVersion -> if available will specify new versionName directly and ignores the `bumpType` parameter.
// bumpType[major|minor|patch] -> will specify how the version bumping occurs.

tasks.register('releasePrepare') {
dependsOn ensureCleanRepo
doLast {
prepareRelease()
}
}

tasks.register('releasePerform') {
dependsOn ensureCleanRepo
doLast {
performRelease()
}
}