Skip to content

Release generator improvements #5077

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

Merged
merged 8 commits into from
Jun 22, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/create_releases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
with:
base: 'releases/${{ inputs.name }}'
branch: 'releases/${{ inputs.name }}.release'
add-paths: release.json,release_report.md
add-paths: release.json,release_report.md,release_report.json
title: '${{ inputs.name}} release'
body: 'Auto-generated PR for release ${{ inputs.name}}'
commit-message: 'Create release config for ${{ inputs.name }}'
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,10 @@ abstract class PublishingPlugin : Plugin<Project> {
printReleaseConfig.convention(project.provideProperty("printOutput"))

releaseConfigFile.convention(project.layout.projectDirectory.file(RELEASE_CONFIG_FILE))
releaseReportFile.convention(project.layout.projectDirectory.file(RELEASE_REPORT_FILE))
releaseReportMdFile.convention(project.layout.projectDirectory.file(RELEASE_REPORT_MD_FILE))
releaseReportJsonFile.convention(
project.layout.projectDirectory.file(RELEASE_REPORT_JSON_FILE)
)
}

/**
Expand Down Expand Up @@ -441,7 +444,8 @@ abstract class PublishingPlugin : Plugin<Project> {

companion object {
const val RELEASE_CONFIG_FILE = "release.json"
const val RELEASE_REPORT_FILE = "release_report.md"
const val RELEASE_REPORT_MD_FILE = "release_report.md"
const val RELEASE_REPORT_JSON_FILE = "release_report.json"

const val GENERATE_BOM_TASK = "generateBom"
const val VALIDATE_PROJECTS_TO_PUBLISH_TASK = "validateProjectsToPublish"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ package com.google.firebase.gradle.plugins

import com.google.common.collect.ImmutableList
import java.io.File
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.ListBranchCommand
import org.eclipse.jgit.api.errors.GitAPIException
Expand All @@ -31,25 +34,85 @@ import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.findByType

/**
* Contains output data from the Release Generator, published as release_report.json
*
* @property changesByLibraryName contains libs which have opted into the release, and their changes
* @property changedLibrariesWithNoChangelog contains libs not opted into the release, despite
* having changes
*/
@Serializable
data class ReleaseReport(
val changesByLibraryName: Map<String, List<CommitDiff>>,
val changedLibrariesWithNoChangelog: Set<String>
) {
companion object {
val formatter = Json { prettyPrint = true }
}

fun toFile(file: File) = file.also { it.writeText(formatter.encodeToString(this)) }

override fun toString() =
"""
|# Release Report
|${
changesByLibraryName.entries.joinToString("\n") {
"""
|## ${it.key}

|${it.value.joinToString("\n") { it.toString() }}
""".trimMargin()
}
}
|
|## SDKs with changes, but no changelogs
|${changedLibrariesWithNoChangelog.joinToString(" \n")}
"""
.trimMargin()
}

@Serializable
data class CommitDiff(
val commitId: String,
val prId: String,
val author: String,
val message: String,
val commitLink: String,
val prLink: String,
) {
constructor(
revCommit: RevCommit
) : this(revCommit.id.name, revCommit.authorIdent.name, revCommit.fullMessage) {}
companion object {
// This is a meant to capture the PR number from PR Titles
// ex: "Fix a problem (#1234)" -> "1234"
private val PR_ID_EXTRACTOR = Regex(".*\\(#(\\d+)\\).*")

public fun fromRevCommit(commit: RevCommit): CommitDiff {
val commitId = commit.id.name
val prId =
PR_ID_EXTRACTOR.find(commit.fullMessage.split("\n").first())?.groupValues?.get(1) ?: ""
return CommitDiff(
commitId,
prId,
commit.authorIdent.name,
commit.fullMessage,
"https://github.com/firebase/firebase-android-sdk/commit/$commitId",
"https://github.com/firebase/firebase-android-sdk/pull/$prId"
)
}
}

override fun toString(): String =
"""
|* ${message.split("\n").first()}
| https://github.com/firebase/firebase-android-sdk/commit/${commitId} [${author}]
| [pr]($prLink) [commit]($commitLink) [$author]

"""
.trimMargin()
}

abstract class ReleaseGenerator : DefaultTask() {
companion object {
private val RELEASE_CHANGE_FILTER = "NO_RELEASE_CHANGE"
}

@get:Input abstract val currentRelease: Property<String>

Expand All @@ -59,7 +122,9 @@ abstract class ReleaseGenerator : DefaultTask() {

@get:OutputFile abstract val releaseConfigFile: RegularFileProperty

@get:OutputFile abstract val releaseReportFile: RegularFileProperty
@get:OutputFile abstract val releaseReportMdFile: RegularFileProperty

@get:OutputFile abstract val releaseReportJsonFile: RegularFileProperty

@TaskAction
@Throws(Exception::class)
Expand All @@ -81,34 +146,14 @@ abstract class ReleaseGenerator : DefaultTask() {
val releaseConfig = ReleaseConfig(currentRelease.get(), libsToRelease.map { it.path })
releaseConfig.toFile(releaseConfigFile.get().asFile)

val releaseReport = generateReleaseReport(changes, changedLibsWithNoChangelog)
val releaseReport = ReleaseReport(changes, changedLibsWithNoChangelog)
if (printReleaseConfig.orNull.toBoolean()) {
project.logger.info(releaseReport)
project.logger.info(releaseReport.toString())
}
writeReleaseReport(releaseReportFile.get().asFile, releaseReport)
releaseReportMdFile.get().asFile.writeText(releaseReport.toString())
releaseReportJsonFile.get().asFile.let { releaseReport.toFile(it) }
}

private fun generateReleaseReport(
changes: Map<String, List<CommitDiff>>,
changedLibrariesWithNoChangelog: Set<String>
) =
"""
|# Release Report
|${
changes.entries.joinToString("\n") {
"""
|## ${it.key}

|${it.value.joinToString("\n") { it.toString() }}
""".trimMargin()
}
}
|
|## SDKs with changes, but no changelogs
|${changedLibrariesWithNoChangelog.joinToString(" \n")}
"""
.trimMargin()

private fun getChangesForLibraries(
repo: Git,
branchRef: ObjectId,
Expand Down Expand Up @@ -179,7 +224,7 @@ abstract class ReleaseGenerator : DefaultTask() {
.addRange(previousReleaseRef, currentReleaseRef)
.setMaxCount(10)
.call()
.filter { !it.fullMessage.contains("NO_RELEASE_CHANGE") }
.filter { !it.fullMessage.contains(RELEASE_CHANGE_FILTER) }
.isNotEmpty()

private fun getDirChanges(
Expand All @@ -188,11 +233,13 @@ abstract class ReleaseGenerator : DefaultTask() {
currentReleaseRef: ObjectId,
directory: String
) =
repo.log().addPath(directory).addRange(previousReleaseRef, currentReleaseRef).call().map {
CommitDiff(it)
}

private fun writeReleaseReport(file: File, report: String) = file.writeText(report)
repo
.log()
.addPath(directory)
.addRange(previousReleaseRef, currentReleaseRef)
.call()
.filter { !it.fullMessage.contains(RELEASE_CHANGE_FILTER) }
.map { CommitDiff.fromRevCommit(it) }

private fun getRelativeDir(project: Project) = project.path.substring(1).replace(':', '/')
}