Skip to content
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/build-release-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:

- name: Perform gradle build
run: |
./gradlew firebasePublish
./gradlew firebasePublish -PpublishConfigFilePath=release.cfg -PpublishMode=RELEASE

- name: Generate release notes
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/check-head-dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ jobs:

- name: Perform gradle build
run: |
./gradlew checkHeadDependencies
./gradlew checkHeadDependencies -PpublishConfigFilePath=release.cfg -PpublishMode=RELEASE
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.cfg,release_report.md
title: '${{ inputs.name}} release'
body: 'Auto-generated PR for release ${{ inputs.name}}'
commit-message: 'Create release config for ${{ inputs.name }}'
2 changes: 1 addition & 1 deletion .github/workflows/semver-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ jobs:

- name: Perform gradle build
run: |
./gradlew semverCheckForRelease
./gradlew semverCheckForRelease -PpublishConfigFilePath=release.cfg -PpublishMode=RELEASE
2 changes: 1 addition & 1 deletion .github/workflows/validate-dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ jobs:

- name: Perform gradle build
run: |
./gradlew validatePomForRelease
./gradlew validatePomForRelease -PpublishConfigFilePath=release.cfg -PpublishMode=RELEASE
6 changes: 5 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import com.google.firebase.gradle.plugins.license.LicenseResolverPlugin
import com.google.firebase.gradle.MultiProjectReleasePlugin

buildscript {
// TODO: remove once all sdks have migrated to version catalog
ext.kotlinVersion = libs.versions.kotlin.get()
Expand Down Expand Up @@ -55,10 +58,11 @@ ext {
protobufJavaUtilVersion = libs.versions.protobufjavautil.get()
}

apply plugin: com.google.firebase.gradle.plugins.publish.PublishingPlugin
apply plugin: com.google.firebase.gradle.plugins.ci.ContinuousIntegrationPlugin
apply plugin: com.google.firebase.gradle.plugins.ci.SmokeTestsPlugin

apply plugin: com.google.firebase.gradle.plugins.PublishingPlugin
apply plugin: MultiProjectReleasePlugin

firebaseContinuousIntegration {
ignorePaths = [
Expand Down
4 changes: 1 addition & 3 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
plugins {
id("com.ncorti.ktfmt.gradle") version "0.11.0"
id("com.github.sherter.google-java-format") version "0.9"
kotlin("plugin.serialization") version "1.7.10"
`kotlin-dsl`
}

Expand Down Expand Up @@ -68,7 +67,6 @@ dependencies {

implementation("org.eclipse.jgit:org.eclipse.jgit:6.3.0.202209071007-r")

implementation(libs.kotlinx.serialization.json)
implementation("com.google.code.gson:gson:2.8.9")
implementation("com.android.tools.build:gradle:7.2.2")
implementation("com.android.tools.build:builder-test-api:7.2.2")
Expand All @@ -92,7 +90,7 @@ gradlePlugin {
}
register("publishingPlugin") {
id = "PublishingPlugin"
implementationClass = "com.google.firebase.gradle.plugins.PublishingPlugin"
implementationClass = "com.google.firebase.gradle.plugins.publish.PublishingPlugin"
}
register("firebaseLibraryPlugin") {
id = "firebase-library"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.firebase.gradle;

import com.google.common.collect.ImmutableMap;
import com.google.firebase.gradle.bomgenerator.BomGeneratorTask;
import com.google.firebase.gradle.plugins.FirebaseLibraryExtension;
import com.google.firebase.gradle.plugins.publish.PublishingPlugin;
import java.util.Set;
import java.util.stream.Collectors;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.tasks.Copy;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.api.tasks.bundling.Zip;

/**
* Orchestrates the release process by automating validations, documentation and prebuilts
* generation.
*
* <ul>
* <li>Pre-release validations:
* <ul>
* <li>Build maven artifacts.
* </ul>
* <li>Documentation:
* <ul>
* <li>Generates javadoc for all SDKs being released.
* </ul>
* <li>Artifact generation:
* <ul>
* <li>Releases artifacts into a maven repo in build/m2repository.
* <li>Bundles all artifacts into a distributable .zip file.
* </ul>
* </ul>
*/
public class MultiProjectReleasePlugin implements Plugin<Project> {

// TODO() - Will be removed once migrated to Kotlin
private static String findStringProperty(Project p, String property) {
Object value = p.findProperty(property);
return value != null ? value.toString() : null;
}

@Override
public void apply(Project project) {
project.apply(ImmutableMap.of("plugin", PublishingPlugin.class));

project
.getTasks()
.create(
"buildBomZip",
Zip.class,
task -> {
task.dependsOn(project.getTasks().create("generateBom", BomGeneratorTask.class));
task.from("bom");
task.getArchiveFileName().set("bom.zip");
task.getDestinationDirectory().set(project.getRootDir());
});

TaskProvider<ReleaseGenerator> generatorTask =
project
.getTasks()
.register(
"makeReleaseConfigFiles",
ReleaseGenerator.class,
task -> {
task.getCurrentRelease()
.convention(findStringProperty(project, "currentRelease"));
task.getPastRelease().convention(findStringProperty(project, "pastRelease"));
task.getPrintReleaseConfig()
.convention(findStringProperty(project, "printOutput"));
task.getReleaseConfigFile()
.convention(project.getLayout().getBuildDirectory().file("release.cfg"));
task.getReleaseReportFile()
.convention(
project.getLayout().getBuildDirectory().file("release_report.md"));
});

project
.getTasks()
.register(
"generateReleaseConfig",
Copy.class,
task -> {
task.from(generatorTask);
task.into(project.getRootDir());
});

project
.getGradle()
.projectsEvaluated(
gradle -> {
Set<FirebaseLibraryExtension> librariesToPublish =
(Set<FirebaseLibraryExtension>)
project.getExtensions().getExtraProperties().get("projectsToPublish");

Set<Project> projectsToPublish =
librariesToPublish.stream().map(lib -> lib.project).collect(Collectors.toSet());

Task validateProjectsToPublish =
project.task(
"validateProjectsToPublish",
task ->
task.doLast(
t -> {
if (projectsToPublish.isEmpty()) {
throw new GradleException(
"Required projectsToPublish parameter missing.");
}
}));
project.getTasks().findByName("firebasePublish").dependsOn(validateProjectsToPublish);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.firebase.gradle.plugins
package com.google.firebase.gradle

import com.google.common.collect.ImmutableList
import com.google.firebase.gradle.plugins.FirebaseLibraryExtension
import java.io.File
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.ListBranchCommand
Expand All @@ -26,11 +27,12 @@ import org.gradle.api.Project
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.findByType

data class FirebaseLibrary(val moduleNames: List<String>, val directories: List<String>)

data class CommitDiff(
val commitId: String,
val author: String,
Expand All @@ -55,7 +57,7 @@ abstract class ReleaseGenerator : DefaultTask() {

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

@get:Optional @get:Input abstract val printReleaseConfig: Property<String>
@get:Input abstract val printReleaseConfig: Property<String>

@get:OutputFile abstract val releaseConfigFile: RegularFileProperty

Expand All @@ -77,12 +79,12 @@ abstract class ReleaseGenerator : DefaultTask() {
libsToRelease.map { it.path }.toSet()

val changes = getChangesForLibraries(repo, branchRef, headRef, libsToRelease)

val releaseConfig = ReleaseConfig(currentRelease.get(), libsToRelease.map { it.path })
releaseConfig.toFile(releaseConfigFile.get().asFile)

writeReleaseConfig(
releaseConfigFile.get().asFile,
ReleaseConfig(currentRelease.get(), libsToRelease.map { it.path }.toSet())
)
val releaseReport = generateReleaseReport(changes, changedLibsWithNoChangelog)
if (printReleaseConfig.orNull.toBoolean()) {
if (printReleaseConfig.get().toBoolean()) {
project.logger.info(releaseReport)
}
writeReleaseReport(releaseReportFile.get().asFile, releaseReport)
Expand Down Expand Up @@ -200,5 +202,30 @@ abstract class ReleaseGenerator : DefaultTask() {

private fun writeReleaseReport(file: File, report: String) = file.writeText(report)

private fun writeReleaseConfig(file: File, config: ReleaseConfig) =
file.writeText(config.toFile())

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

data class ReleaseConfig(val releaseName: String, val libs: Set<String>) {
companion object {
fun fromFile(file: File): ReleaseConfig {
val contents = file.readLines()
val libs = contents.filter { it.startsWith(":") }.toSet()
val releaseName = contents.first { it.startsWith("name") }.substringAfter("=").trim()
return ReleaseConfig(releaseName, libs)
}
}

fun toFile() =
"""
|[release]
|name = $releaseName
|mode = RELEASE

|[modules]
|${libs.sorted().joinToString("\n")}
"""
.trimMargin()
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,45 +20,43 @@ import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction

/**
* Validates that all project level dependencies are in the release.
*
* Any releasing library that has a project level dependency on another library invokes the release
* of said dependent libary. This is checked via the [artifactId]
* [FirebaseLibraryExtension.artifactId], so that the check is version agnostic.
*
* @throws GradleException if any project level dependencies are found that are not included in the
* release
*/
abstract class CheckHeadDependencies : DefaultTask() {
@get:Input abstract val projectsToPublish: ListProperty<FirebaseLibraryExtension>

@get:Input abstract val allFirebaseProjects: ListProperty<String>

@TaskAction
fun run() {
val projectsReleasing = computeProjectsReleasing()

val projectsReleasing: Set<String> = projectsToPublish.get().map { it.artifactId.get() }.toSet()
val errors =
projectsToPublish
.get()
.associate { it.artifactId.get() to it.projectLevelDepsAsArtifactIds() - projectsReleasing }
.associate {
it.artifactId.get() to
it
.projectDependenciesByName()
.intersect(allFirebaseProjects.get())
.subtract(projectsReleasing)
.subtract(DEPENDENCIES_TO_IGNORE)
}
.filterValues { it.isNotEmpty() }
.map { "${it.key} requires: ${it.value.joinToString(", ") }" }

if (errors.isNotEmpty()) {
throw GradleException(
"Project-level dependency errors found. Please update the release config.\n" +
"${errors.joinToString("\n")}"
"Project-level dependency errors found. Please update the release config.\n${
errors.joinToString(
"\n"
)
}"
)
}
}

private fun FirebaseLibraryExtension.projectLevelDepsAsArtifactIds() =
private fun FirebaseLibraryExtension.projectDependenciesByName() =
resolveProjectLevelDependencies().map { it.artifactId.get() }

private fun computeProjectsReleasing() =
projectsToPublish.get().map { it.artifactId.get() } + DEPENDENCIES_TO_IGNORE

companion object {
val DEPENDENCIES_TO_IGNORE = listOf("protolite-well-known-types")
val DEPENDENCIES_TO_IGNORE: List<String> = listOf("protolite-well-known-types")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,6 @@ abstract class DackkaPlugin : Plugin<Project> {

kotlinDoc.configure {
dependsOn(generateDocumentation, firesiteTransform, copyDocsToCommonDirectory)

outputs.dir(copyDocsToCommonDirectory.map { it.destinationDir })
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
import org.w3c.dom.Document

/** TODO(b/279466888) - Make GmavenHelper testable */
class GmavenHelper(val groupId: String, val artifactId: String) {
val GMAVEN_ROOT = "https://dl.google.com/dl/android/maven2"

Expand Down
Loading