Migrate versioning to Gradle plugin and automate version bumps (#1282)

This commit is contained in:
Harsh Shandilya 2021-01-20 20:27:04 +05:30 committed by GitHub
parent 405e1d1772
commit 3a2cfd22e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 180 additions and 12 deletions

View file

@ -9,32 +9,73 @@ jobs:
name: "Draft a new release" name: "Draft a new release"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
with:
ref: 'release'
- name: Extract version from milestone - name: Extract version from milestone
run: | run: |
VERSION="${{ github.event.milestone.title }}" VERSION="${{ github.event.milestone.title }}"
echo "RELEASE_VERSION=${VERSION/v/}" >> $GITHUB_ENV RELEASE_VERSION="${VERSION/v/}"
# Transforms 1.13.2 to 1.13 so that we can re-use the same
# branch for patch releases.
BRANCH_VERSION="${RELEASE_VERSION:$i:-2}"
if [[ "${RELEASE_VERSION: -1}" == "0" ]]; then
CHECKOUT_REF="develop"
else
CHECKOUT_REF="release-${BRANCH_VERSION}"
fi
# Export variables separately so the scripting above is more legible,
# and we can actually use them within this block. Changes to $GITHUB_ENV
# only affect the next step, not the current one.
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> $GITHUB_ENV
echo "CHECKOUT_REF=${CHECKOUT_REF}" >> $GITHUB_ENV
echo "BRANCH_VERSION=${BRANCH_VERSION}" >> $GITHUB_ENV
echo "PR_BASE=release-${BRANCH_VERSION}" >> $GITHUB_ENV
echo "PR_HEAD=release-prep" >> $GITHUB_ENV
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
with:
ref: ${{ env.CHECKOUT_REF }}
- name: Update changelog - name: Update changelog
uses: thomaseizinger/keep-a-changelog-new-release@96ebf19f2bddaf72406c84691b9c2d827da53140 uses: thomaseizinger/keep-a-changelog-new-release@96ebf19f2bddaf72406c84691b9c2d827da53140
with: with:
version: ${{ env.RELEASE_VERSION }} version: ${{ github.event.milestone.title }}
- name: Initialize git config and commit changes - name: Initialize git config and commit changes
run: | run: |
# Configure name and email for Actions user
git config user.name "GitHub Actions" git config user.name "GitHub Actions"
git config user.email noreply@github.com git config user.email noreply@github.com
# It is necessary to create the $PR_BASE branch if it doesn't
# already exist because we want to start a PR against it.
if [[ "${CHECKOUT_REF}" == "develop" ]]; then
git branch -c develop "${PR_BASE}"
git push origin "${PR_BASE}"
fi
# Stage and commit changes to the changelog
git add CHANGELOG.md
git commit -m "CHANGELOG: bump for ${{ github.event.milestone.title }}"
# Increment the version as necessary. If we checked out develop it means
# that the version number is already correct, and we only need to drop the
# -SNAPSHOT suffix.
if [[ "${CHECKOUT_REF}" == "develop" ]]; then
./gradlew clearPreRelease
else
./gradlew bumpPatch
fi
# Commit changes to the versioning
git add **/version.properties
git commit -m "build: bump version"
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@45c510e1f68ba052e3cd911f661a799cfb9ba3a3 uses: peter-evans/create-pull-request@45c510e1f68ba052e3cd911f661a799cfb9ba3a3
with: with:
author: GitHub Actions <noreply@github.com> author: GitHub Actions <noreply@github.com>
base: release body: This is an automated pull request to bump the changelog for the ${{ github.event.milestone.title }} release.
body: This is an automated pull request to bump the changelog for the v${{ env.RELEASE_VERSION }} release. base: ${{ env.PR_BASE }}
branch: release-${{ env.RELEASE_VERSION }} branch: ${{ env.PR_HEAD }}
commit-message: "CHANGELOG: bump for ${{ env.RELEASE_VERSION }}"
draft: true
title: Release v${{ env.RELEASE_VERSION }} title: Release v${{ env.RELEASE_VERSION }}
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -7,6 +7,7 @@ import com.android.build.gradle.internal.api.BaseVariantOutputImpl
plugins { plugins {
id("com.android.application") id("com.android.application")
kotlin("android") kotlin("android")
`versioning-plugin`
`aps-plugin` `aps-plugin`
`crowdin-plugin` `crowdin-plugin`
} }
@ -26,8 +27,6 @@ android {
defaultConfig { defaultConfig {
applicationId = "dev.msfjarvis.aps" applicationId = "dev.msfjarvis.aps"
versionCode = 2_00_00
versionName = "2.0.0-SNAPSHOT"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }

6
app/version.properties Normal file
View file

@ -0,0 +1,6 @@
#
#This file was automatically generated by 'versioning-plugin'. DO NOT EDIT MANUALLY.
#
#Sun Jan 17 12:32:03 IST 2021
versioning-plugin.versionCode=20000
versioning-plugin.versionName=2.0.0-SNAPSHOT

View file

@ -25,6 +25,10 @@ gradlePlugin {
id = "crowdin-plugin" id = "crowdin-plugin"
implementationClass = "CrowdinDownloadPlugin" implementationClass = "CrowdinDownloadPlugin"
} }
register("versioning") {
id = "versioning-plugin"
implementationClass = "VersioningPlugin"
}
} }
} }
@ -33,4 +37,5 @@ dependencies {
implementation(build.getValue("androidGradlePlugin")) implementation(build.getValue("androidGradlePlugin"))
implementation(build.getValue("binaryCompatibilityValidator")) implementation(build.getValue("binaryCompatibilityValidator"))
implementation(build.getValue("downloadTaskPlugin")) implementation(build.getValue("downloadTaskPlugin"))
implementation(build.getValue("jsemver"))
} }

View file

@ -3,6 +3,7 @@ rootProject.ext.versions = [
kotlin : '1.4.21', kotlin : '1.4.21',
binary_compatibility_validator : '0.2.4', binary_compatibility_validator : '0.2.4',
download_plugin : '4.1.1', download_plugin : '4.1.1',
semver : '0.9.0',
] ]
rootProject.ext.build = [ rootProject.ext.build = [
@ -10,4 +11,5 @@ rootProject.ext.build = [
kotlinGradlePlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}", kotlinGradlePlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}",
binaryCompatibilityValidator : "org.jetbrains.kotlinx:binary-compatibility-validator:${versions.binary_compatibility_validator}", binaryCompatibilityValidator : "org.jetbrains.kotlinx:binary-compatibility-validator:${versions.binary_compatibility_validator}",
downloadTaskPlugin : "de.undercouch:gradle-download-task:${versions.download_plugin}", downloadTaskPlugin : "de.undercouch:gradle-download-task:${versions.download_plugin}",
jsemver : "com.github.zafarkhaja:java-semver:${versions.semver}",
] ]

View file

@ -0,0 +1,115 @@
/*
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
*/
import com.android.build.gradle.internal.plugins.AppPlugin
import com.github.zafarkhaja.semver.Version
import java.io.OutputStream
import java.util.Properties
import org.gradle.api.Plugin
import org.gradle.api.Project
private const val VERSIONING_PROP_FILE = "version.properties"
private const val VERSIONING_PROP_VERSION_NAME = "versioning-plugin.versionName"
private const val VERSIONING_PROP_VERSION_CODE = "versioning-plugin.versionCode"
private const val VERSIONING_PROP_COMMENT = """
This file was automatically generated by 'versioning-plugin'. DO NOT EDIT MANUALLY.
"""
/**
* A Gradle [Plugin] that takes a [Project] with the [AppPlugin] applied and dynamically sets the
* versionCode and versionName properties based on values read from a [VERSIONING_PROP_FILE] file in
* the [Project.getBuildDir] directory. It also adds Gradle tasks to bump the major, minor, and patch
* versions along with one to prepare the next snapshot.
*/
@Suppress(
"UnstableApiUsage",
"NAME_SHADOWING"
)
class VersioningPlugin : Plugin<Project> {
/**
* Generate the Android 'versionCode' property
*/
private fun Version.androidCode(): Int {
return majorVersion * 1_00_00 +
minorVersion * 1_00 +
patchVersion
}
/**
* Write an Android-specific variant of [this] to [stream]
*/
private fun Version.writeForAndroid(stream: OutputStream) {
val newVersionCode = androidCode()
val props = Properties()
props.setProperty(VERSIONING_PROP_VERSION_CODE, "$newVersionCode")
props.setProperty(VERSIONING_PROP_VERSION_NAME, toString())
props.store(stream, VERSIONING_PROP_COMMENT)
}
/**
* Returns the same [Version], but with build metadata stripped.
*/
private fun Version.clearPreRelease(): Version {
return Version.forIntegers(majorVersion, minorVersion, patchVersion)
}
override fun apply(project: Project) {
with(project) {
val appPlugin = requireNotNull(plugins.findPlugin(AppPlugin::class.java)) {
"Plugin 'com.android.application' must be applied to use this plugin"
}
val propFile = layout.projectDirectory.file(VERSIONING_PROP_FILE)
require(propFile.asFile.exists()) {
"A 'version.properties' file must exist in the project subdirectory to use this plugin"
}
val contents = providers.fileContents(propFile).asText.forUseAtConfigurationTime()
val versionProps = Properties().also { it.load(contents.get().byteInputStream()) }
val versionName = requireNotNull(versionProps.getProperty(VERSIONING_PROP_VERSION_NAME)) {
"version.properties must contain a '$VERSIONING_PROP_VERSION_NAME' property"
}
val versionCode = requireNotNull(versionProps.getProperty(VERSIONING_PROP_VERSION_CODE).toInt()) {
"version.properties must contain a '$VERSIONING_PROP_VERSION_CODE' property"
}
appPlugin.extension.defaultConfig.versionName = versionName
appPlugin.extension.defaultConfig.versionCode = versionCode
afterEvaluate {
val version = Version.valueOf(versionName)
tasks.register("clearPreRelease") {
doLast {
version.clearPreRelease()
.writeForAndroid(propFile.asFile.outputStream())
}
}
tasks.register("bumpMajor") {
doLast {
version.incrementMajorVersion()
.writeForAndroid(propFile.asFile.outputStream())
}
}
tasks.register("bumpMinor") {
doLast {
version.incrementMinorVersion()
.writeForAndroid(propFile.asFile.outputStream())
}
}
tasks.register("bumpPatch") {
doLast {
version.incrementPatchVersion()
.writeForAndroid(propFile.asFile.outputStream())
}
}
tasks.register("bumpSnapshot") {
doLast {
version.incrementMinorVersion()
.setPreReleaseVersion("SNAPSHOT")
.writeForAndroid(propFile.asFile.outputStream())
}
}
}
}
}
}