Skip to content

Commit

Permalink
Add history section in the release note config and add the ability to…
Browse files Browse the repository at this point in the history
… set a history start point (#25)
  • Loading branch information
mirland authored Apr 16, 2019
1 parent 8625afb commit f7ef0b2
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 52 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
[Version 1.0.1 _(Pending release)_]()
---

### New Features
- Added new `history` variable to use in `releaseNotesFormat` given by `historyFormat`.
It's shown only if the `{commitHistory}` is not empty.
- Added `includeHistorySinceLastTag` release notes setup variable.
It enables to generate the history only for the commits after the latest git's tag.
It's useful to show only the changes that changed since the last build. (#25)

### Fixes
- Fix version name issue in generated release notes. (24)

Expand Down
65 changes: 51 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@ Currently the available services are:
- [Google Play](https://play.google.com/apps/publish/)
- [Fabric Beta](https://docs.fabric.io/apple/beta/overview.html)

## Table of content

- [Installation](#installation)
- [Setup](#setup)
- [Version customization](#version-customization)
- [Release notes](#release-notes)
- [Version](#version)
- [Header](#header)
- [History](#history)
- [Other](#other)
- [Fabric Beta](#fabric-beta)
- [Google Play](#google-play)
- [How to use it?](#how-to-use-it)
- [Getting involved](#getting-involved)
- [About](#about)

## Installation

The plugin is hosted in the Gradle Plugin Portal.
Expand Down Expand Up @@ -121,55 +137,76 @@ All fields in that block are optional and their default values are:
snapshotPublisher {
releaseNotes {
releaseNotesFormat = """{version}: {header}
Last Changes:
{commitHistory}
{history}
"""
versionFormat = '{versionName}'
headerFormat = '%s%n%nAuthor: %an <%ae>'
historyFormat = '\nLast Changes:\n{commitHistory}'
commitHistoryFormat = '• %s (%an - %ci)'
maxCommitHistoryLines = 10
outputFile = null
includeLastCommitInHistory = false
includeMergeCommitsInHistory = true
includeHistorySinceLastTag = false
outputFile = null
}
// ...
}
```

> Note: you can test the generated release notes executing the `generateSnapshotReleaseNotes` gradle task.
> Note: you can test the generated release notes executing the `generateSnapshotReleaseNotes` gradle task and if you also add the `--info` flag you will see them in the command's output.
- `releaseNotesFormat`: Defines the format of the release notes:
The possible variables to play with in this case are:
- `{version}` given by `versionFormat`.
- `{header}` given by `headerFormat`.
- [`{version}`](#version) given by `versionFormat`.
- [`{header}`](#header) given by `headerFormat`.
By default it contains information about the most recent commit and their author.
- `{commitHistory}` given by the result of apply `commitHistoryFormat` to a range of commits.
By default the range includes all commits from the last -not current- commit to `maxCommitHistoryLines` commits before that.
If you want to include the last commit in that range, you can set `includeLastCommitInHistory` as `true`.
- [`{history}`](#history) given by `historyFormat`.
It contains information about the most recent history of the build.

#### Version
It contains information about the build version.
- `versionFormat`: Specifies the version's variable format.

`{versionName}` (Android app's Version Name) and `{versionCode}` (Android app's Version Code) can be used to create it.

#### Header
It contains information about the most recent commit.

- `headerFormat`: Specifies the header's variable format.
The plugin uses [Git's pretty format] to retrieve the information about the current commit.
If you want to modify this, you may want to use it.

#### History
It contains information about the most recent commits, starting from the last -not current- commit to `maxCommitHistoryLines` commits before that.
If you want to include the last commit in that range, you can set `includeLastCommitInHistory` as `true`.
The format of these commits is given by `commitHistoryFormat`.
This section is shown only if there is at least one commit history.

- `historyFormat`: Specifies the history's variable format.
It contains the `{commitHistory}`, which is given by the result of applying `commitHistoryFormat` to a certain range of commits of commits.

- `commitHistoryFormat`: Specifies the `{commitHistory}` variable format.
As `headerFormat` does, it uses [Git's pretty format] to create the `commitHistory` for the previous commits.

- `maxCommitHistoryLines`: Indicates the number of commits included in `{commitHistory}`.

- `includeLastCommitInHistory`: Flag to include the most recent commit in `{commitHistory}`.
By default this value is `false` because it's used in `{header}`

- `includeMergeCommitsInHistory`: Flag to include merge commits in `{commitHistory}`.

- `includeHistorySinceLastTag`: Indicates the history start point.
By default it's `false`, which means that all commits are used to get the history.
However, if its value is `true`, only the commits after the most recent tag will be included in the build history.
This is useful if you want to include in the history only the changes after the previous release.

#### Other

- `outputFile`: The file where the release notes will be saved.
By default this value is `null` and that means the release notes will be generated and delivered with the snapshot build but it will not be saved in the file system.
If you want to save the release notes in the file system, you can set `outputFile = file("release-notes.txt")`.
You can define it using a relative path (where the start point is the Android application module folder) or an absolute path.

- `includeLastCommitInHistory`: Flag to include the most recent commit in `{commitHistory}`.
By default this value is `false` because it's used in `{header}`

- `includeMergeCommitsInHistory`: Flag to include merge commits in `{commitHistory}`.

### Fabric Beta
This block defines the configuration needed to deploy the artifacts in Fabric's Beta.
Expand Down
6 changes: 4 additions & 2 deletions src/main/kotlin/com/xmartlabs/snapshotpublisher/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ internal object Constants {
const val VERSION_FORMAT = "$VERSION_FORMAT_CURRENT_VERSION_NAME_KEY-$VERSION_FORMAT_COMMIT_HASH_KEY"

const val RELEASE_NOTES_COMMIT_HISTORY_KEY = "{commitHistory}"
const val RELEASE_NOTES_HISTORY_KEY = "{history}"
const val RELEASE_NOTES_HEADER_KEY = "{header}"
const val RELEASE_NOTES_VERSION_CODE_KEY = "{versionCode}"
const val RELEASE_NOTES_VERSION_KEY = "{version}"
Expand All @@ -36,15 +37,16 @@ internal object Constants {
const val RELEASE_NOTES_VERSION_FORMAT_DEFAULT_VALUE = RELEASE_NOTES_VERSION_NAME_KEY
const val RELEASE_NOTES_CONFIG_COMMIT_HISTORY_FORMAT_DEFAULT_VALUE = "• %s (%an - %ci)"
const val RELEASE_NOTES_CONFIG_HEADER_FORMAT_DEFAULT_VALUE = "%s%n%nAuthor: %an <%ae>"
const val RELEASE_NOTES_CONFIG_HISTORY_FORMAT_DEFAULT_VALUE = "Last Changes:\n$RELEASE_NOTES_COMMIT_HISTORY_KEY"
const val RELEASE_NOTES_CONFIG_MAX_COMMIT_HISTORY_LINES_DEFAULT_VALUE = 10
const val RELEASE_NOTES_INCLUDE_MERGE_COMMITS_DEFAULT_VALUE = true
const val RELEASE_NOTES_INCLUDE_LAST_COMMIT_DEFAULT_VALUE = false
const val RELEASE_NOTES_INCLUDE_HISTORY_ONLY_FROM_PREVIOUS_TAG_DEFAULT_VALUE = false
val RELEASE_NOTES_OUTPUT_FILE_DEFAULT_VALUE: File? = null
const val RELEASE_NOTES_CONFIG_FORMAT_DEFAULT_VALUE =
"""$RELEASE_NOTES_VERSION_KEY: $RELEASE_NOTES_HEADER_KEY
Last Changes:
$RELEASE_NOTES_COMMIT_HISTORY_KEY
$RELEASE_NOTES_HISTORY_KEY
"""

const val FABRIC_DEPLOY_DISTRIBUTION_EMAILS_DEFAULT_VALUE = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,27 @@ open class ReleaseNotesConfig {
var versionFormat: String = Constants.RELEASE_NOTES_VERSION_FORMAT_DEFAULT_VALUE
var headerFormat: String = Constants.RELEASE_NOTES_CONFIG_HEADER_FORMAT_DEFAULT_VALUE
var commitHistoryFormat: String = Constants.RELEASE_NOTES_CONFIG_COMMIT_HISTORY_FORMAT_DEFAULT_VALUE
var historyFormat: String = Constants.RELEASE_NOTES_CONFIG_HISTORY_FORMAT_DEFAULT_VALUE
var maxCommitHistoryLines: Int = Constants.RELEASE_NOTES_CONFIG_MAX_COMMIT_HISTORY_LINES_DEFAULT_VALUE
@Suppress("MemberVisibilityCanBePrivate")
var releaseNotesFormat: String = Constants.RELEASE_NOTES_CONFIG_FORMAT_DEFAULT_VALUE
var outputFile: File? = Constants.RELEASE_NOTES_OUTPUT_FILE_DEFAULT_VALUE
var includeMergeCommitsInHistory: Boolean = Constants.RELEASE_NOTES_INCLUDE_MERGE_COMMITS_DEFAULT_VALUE
var includeLastCommitInHistory: Boolean = Constants.RELEASE_NOTES_INCLUDE_LAST_COMMIT_DEFAULT_VALUE
var includeHistorySinceLastTag: Boolean =
Constants.RELEASE_NOTES_INCLUDE_HISTORY_ONLY_FROM_PREVIOUS_TAG_DEFAULT_VALUE

internal fun getHistory(commitHistory: String): String = historyFormat
.replace(Constants.RELEASE_NOTES_COMMIT_HISTORY_KEY, commitHistory, true)

internal fun getReleaseNotes(version: String, header: String, history: String, changelog: String): String =
releaseNotesFormat
.replace(Constants.RELEASE_NOTES_VERSION_KEY, version, true)
.replace(Constants.RELEASE_NOTES_HEADER_KEY, header, true)
.replace(Constants.RELEASE_NOTES_HISTORY_KEY, history, true)
.replace(Constants.RELEASE_NOTES_COMMIT_HISTORY_KEY, changelog, true)

internal fun getVersion(versionName: String, versionCode: Int) = versionFormat
.replace(Constants.RELEASE_NOTES_VERSION_NAME_KEY, versionName, true)
.replace(Constants.RELEASE_NOTES_VERSION_CODE_KEY, versionCode.toString(), true)

internal fun getReleaseNotes(version: String, header: String, changelog: String): String = releaseNotesFormat
.replace(Constants.RELEASE_NOTES_VERSION_KEY, version, true)
.replace(Constants.RELEASE_NOTES_HEADER_KEY, header, true)
.replace(Constants.RELEASE_NOTES_COMMIT_HISTORY_KEY, changelog, true)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.xmartlabs.snapshotpublisher.task

import com.android.build.gradle.api.ApplicationVariant
import com.xmartlabs.snapshotpublisher.Constants
import com.xmartlabs.snapshotpublisher.model.ReleaseNotesConfig
import com.xmartlabs.snapshotpublisher.plugin.AndroidPluginHelper
import com.xmartlabs.snapshotpublisher.utils.ReleaseNotesGenerator
import com.xmartlabs.snapshotpublisher.utils.snapshotReleaseExtension
Expand All @@ -18,6 +19,12 @@ open class GenerateReleaseNotesTask : DefaultTask() {
@TaskAction
fun action() {
val releaseNotesConfig = project.snapshotReleaseExtension.releaseNotes

if (releaseNotesConfig.releaseNotesFormat.contains(Constants.RELEASE_NOTES_COMMIT_HISTORY_KEY)) {
project.logger.warn("${Constants.RELEASE_NOTES_COMMIT_HISTORY_KEY} in `releaseNotesFormat` is deprecated and " +
"it will remove in the next major update")
}

val generatedReleaseNotes = ReleaseNotesGenerator.generate(
releaseNotesConfig = releaseNotesConfig,
versionName = AndroidPluginHelper.getVersionName(project, variant),
Expand All @@ -26,16 +33,21 @@ open class GenerateReleaseNotesTask : DefaultTask() {

project.logger.info("Generated Release Notes: \n$generatedReleaseNotes")

val releaseNotesFile = File(project.buildDir, Constants.OUTPUT_RELEASE_NOTES_FILE_PATH)
.apply {
parentFile.mkdirs()
writeText(generatedReleaseNotes)
}

releaseNotesConfig.outputFile
?.apply {
parentFile.mkdirs()
releaseNotesFile.copyTo(target = this, overwrite = true)
}
val releaseNotesFile = createGeneratedReleaseNotesFile(generatedReleaseNotes)
copyGeneratedReleaseNoteFileToOutputFile(releaseNotesConfig, releaseNotesFile)
}

private fun copyGeneratedReleaseNoteFileToOutputFile(releaseNotesConfig: ReleaseNotesConfig, releaseNotesFile: File) =
releaseNotesConfig.outputFile
?.apply {
parentFile.mkdirs()
releaseNotesFile.copyTo(target = this, overwrite = true)
}

private fun createGeneratedReleaseNotesFile(generatedReleaseNotes: String) =
File(project.buildDir, Constants.OUTPUT_RELEASE_NOTES_FILE_PATH)
.apply {
parentFile.mkdirs()
writeText(generatedReleaseNotes)
}
}
19 changes: 14 additions & 5 deletions src/main/kotlin/com/xmartlabs/snapshotpublisher/utils/GitHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,32 @@ internal object GitHelper {
return proc.inputStream.bufferedReader().readText().trim()
}

private fun getLatestTag() = "git rev-list --tags --max-count=1".execute()

fun getCommitHash() = "git rev-parse --short HEAD".execute()

fun getBranchName() = "git rev-parse --abbrev-ref HEAD".execute()

fun getLog(format: String, numberOfCommits: Int = Int.MAX_VALUE) =
"git log --pretty=format:'$format' -n $numberOfCommits".execute()

private fun getTotalNumberOfCommits(from: String = "HEAD", commandArg: String?) =
"git rev-list --count $from ${commandArg ?: ""}".execute().toInt()
private fun getTotalNumberOfCommits(logRange: String, commandArg: String?) =
"git rev-list --count $logRange ${commandArg ?: ""}".execute().toInt()

private fun getHistoryRange(releaseNotesConfig: ReleaseNotesConfig): String {
val startRange = if (releaseNotesConfig.includeHistorySinceLastTag) getLatestTag() else null
val endRange = "HEAD" + if (releaseNotesConfig.includeLastCommitInHistory) "" else "^"
return if (startRange.isNullOrBlank()) endRange else "$startRange..$endRange"
}

fun getHistoryFromPreviousCommit(releaseNotesConfig: ReleaseNotesConfig): String {
with(releaseNotesConfig) {
val allowMergeCommitCommandArg = if (includeMergeCommitsInHistory) "" else "--no-merges"
val logStartCommand = "HEAD" + if (includeLastCommitInHistory) "" else "^"
val numberOfCommits = GitHelper.getTotalNumberOfCommits(logStartCommand, allowMergeCommitCommandArg)

val logRange = getHistoryRange(this)
val numberOfCommits = GitHelper.getTotalNumberOfCommits(logRange, allowMergeCommitCommandArg)
val requireCommitsCommandArg = if (numberOfCommits <= maxCommitHistoryLines) "" else " -n $maxCommitHistoryLines"
val gitLogCommand = "git log --pretty=format:'$commitHistoryFormat'$requireCommitsCommandArg $logStartCommand"
val gitLogCommand = "git log --pretty=format:'$commitHistoryFormat'$requireCommitsCommandArg $logRange"

return "$gitLogCommand $allowMergeCommitCommandArg".execute()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ internal object ReleaseNotesGenerator {
fun generate(releaseNotesConfig: ReleaseNotesConfig, versionName: String, versionCode: Int): String {
val version = getVersionSection(releaseNotesConfig, versionName, versionCode)
val header = getHeaderSection(releaseNotesConfig)
val changelog = getHistorySection(releaseNotesConfig)
return releaseNotesConfig.getReleaseNotes(version = version, header = header, changelog = changelog)
val history = getHistorySection(releaseNotesConfig)
val changelog = getChangelogSection(releaseNotesConfig)
return releaseNotesConfig.getReleaseNotes(
version = version,
header = header,
history = history,
changelog = changelog
)
}

fun truncateReleaseNotesIfNeeded(releaseNotes: String, maxCharacters: Int): String {
Expand Down Expand Up @@ -41,5 +47,10 @@ internal object ReleaseNotesGenerator {

@VisibleForTesting
internal fun getHistorySection(releaseNotesConfig: ReleaseNotesConfig) =
getChangelogSection(releaseNotesConfig).let { changelog ->
if (changelog.isBlank()) "" else releaseNotesConfig.getHistory(changelog)
}

private fun getChangelogSection(releaseNotesConfig: ReleaseNotesConfig) =
GitHelper.getHistoryFromPreviousCommit(releaseNotesConfig)
}
Loading

0 comments on commit f7ef0b2

Please sign in to comment.