Support buildIds in reportModifications task
Now the task could print all modified projects and their dependents
between two given build ids.
bug: 67555870
Test: ./gradlew reportModifications -PbuildIds=4385749...4387453
Change-Id: I73a11754e4b992440f297b165c655870d2a1264a
diff --git a/buildSrc/modifications_reporter.gradle b/buildSrc/modifications_reporter.gradle
index b8fcbc5..cfa0f2b 100644
--- a/buildSrc/modifications_reporter.gradle
+++ b/buildSrc/modifications_reporter.gradle
@@ -16,20 +16,29 @@
import android.support.ModifiedProjectsReporter
+def supportRoot = ext.supportRootFolder
task(reportModifications) {
- doLast{
- if (!rootProject.hasProperty("SHAs")) {
- throw new GradleException("You must specify SHAs range: -PSHAs=<rev1>...<rev2>")
+ doLast {
+ if (!rootProject.hasProperty("SHAs") && !rootProject.hasProperty("buildIds")) {
+ throw new GradleException("You must specify SHAs range: -PSHAs=<rev1>...<rev2>"
+ + "or builds range: -PbuildIds=<buildId1>..<buildId2>")
}
- def range = SHAs.tokenize('...')
- if (range.size() != 2) {
- throw new GradleException("You must specify SHAs in format: <rev1>...<rev2>")
+ def modificationsReporter = new ModifiedProjectsReporter()
+ if (rootProject.hasProperty("SHAs")) {
+ def range = SHAs.tokenize('...')
+ if (range.size() != 2) {
+ throw new GradleException("You must specify SHAs in format: <rev1>...<rev2>")
+ }
+ modificationsReporter.printModifiedProjectsSHAs(supportRoot, range[0], range[1], rootProject)
}
- printModifications(range[0], range[1])
- }
-}
-def printModifications(sha1, sha2) {
- def modificationsReporter = new ModifiedProjectsReporter()
- modificationsReporter.printModifiedProjects(ext.supportRootFolder, sha1, sha2, rootProject)
-}
+ if (rootProject.hasProperty("buildIds")) {
+ def range = buildIds.tokenize('...')
+ if (range.size() != 2) {
+ throw new GradleException("You must specify builds in format: <buildId1>..<buildId2")
+ }
+ modificationsReporter.printModifiedProjectsbyBuildIds(supportRoot, range[0].toInteger(),
+ range[1].toInteger(), rootProject)
+ }
+ }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/android/support/FetchArtifact.kt b/buildSrc/src/main/kotlin/android/support/FetchArtifact.kt
new file mode 100644
index 0000000..769a1eb
--- /dev/null
+++ b/buildSrc/src/main/kotlin/android/support/FetchArtifact.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 android.support
+
+import org.gradle.api.GradleException
+import java.io.File
+
+const val FETCH_ARTIFACT = "/google/data/ro/projects/android/fetch_artifact"
+
+/**
+ * fetch artifact from the build server it is similar to run in command line:
+ * /google/data/ro/projects/android/fetch_artifact --bid <buildId> --target <target> path
+ */
+fun fetch(workingDir: File, buildId: Int, target: String, path: String) = ioThread {
+ if (!File(FETCH_ARTIFACT).exists()) {
+ throw GradleException("$FETCH_ARTIFACT doesn't exist")
+ }
+
+ val process = ProcessBuilder(FETCH_ARTIFACT, "--bid", "$buildId", "--target", target, path)
+ .directory(workingDir)
+ .start()
+
+ process.awaitSuccess("Fetch artifact")
+ File(workingDir, path)
+}
diff --git a/buildSrc/src/main/kotlin/android/support/IoUtils.kt b/buildSrc/src/main/kotlin/android/support/IoUtils.kt
new file mode 100644
index 0000000..71b3bc9
--- /dev/null
+++ b/buildSrc/src/main/kotlin/android/support/IoUtils.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 android.support
+
+import org.gradle.api.GradleException
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import java.util.concurrent.Future
+import java.util.concurrent.TimeUnit
+
+const val TIMEOUT_IN_SECS = 20L //secs
+
+val IO_EXECUTOR: ExecutorService = Executors.newFixedThreadPool(5)
+fun <T> ioThread(f: () -> T): Future<T> = IO_EXECUTOR.submit(f)
+
+fun Process.awaitSuccess(processName: String) {
+ val awaitResult = waitFor(TIMEOUT_IN_SECS, TimeUnit.SECONDS)
+ if (!awaitResult) {
+ throw GradleException("Failed to await for $processName")
+ }
+
+ if (exitValue() != 0) {
+ val errorMessage = errorStream.bufferedReader().use { it.readText() }
+ throw GradleException("$processName failed: $errorMessage")
+ }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/android/support/ModifiedProjectsReporter.kt b/buildSrc/src/main/kotlin/android/support/ModifiedProjectsReporter.kt
index 1adbd0b..2c1560e 100644
--- a/buildSrc/src/main/kotlin/android/support/ModifiedProjectsReporter.kt
+++ b/buildSrc/src/main/kotlin/android/support/ModifiedProjectsReporter.kt
@@ -16,21 +16,32 @@
package android.support
-import org.gradle.api.GradleException
-import java.io.File
-import java.util.concurrent.Executors
-import java.util.concurrent.Future
-import java.util.concurrent.TimeUnit
import org.gradle.api.Project
+import java.io.File
+import java.util.concurrent.Future
+private const val REPO_PROP = "repo.prop"
@Suppress("unused")
class ModifiedProjectsReporter {
- private val ioExecutor = Executors.newFixedThreadPool(5)
- private fun <T> ioThread(f: () -> T): Future<T> = ioExecutor.submit(f)
+ private fun fetchRepoProps(rootProject: Project, buildId: Int): Future<File> {
+ val workingDir = File(rootProject.buildDir, "$buildId")
+ if (workingDir.exists()) {
+ workingDir.deleteRecursively()
+ }
+ workingDir.mkdir()
+ return fetch(rootProject.buildDir, buildId, "support_library_app_toolkit", REPO_PROP)
+ }
- fun printModifiedProjects(gitRoot: File,
+ fun printModifiedProjectsbyBuildIds(gitRoot: File, buildIdFirst: Int, buildIdSecond: Int,
+ rootProject: Project) {
+ val sha1 = frameworksSupportSHA(fetchRepoProps(rootProject, buildIdFirst).get())
+ val sha2 = frameworksSupportSHA(fetchRepoProps(rootProject, buildIdSecond).get())
+ printModifiedProjectsSHAs(gitRoot, sha1, sha2, rootProject)
+ }
+
+ fun printModifiedProjectsSHAs(gitRoot: File,
shaFirst: String, shaLast: String, rootProject: Project) {
val modifiedRootProjects = rootProject.subprojects
@@ -78,14 +89,7 @@
val process = ProcessBuilder("git", "log", "$shaFirst..$shaLast", "--",
projectDir.absolutePath).start()
- val awaitResult = process.waitFor(1, TimeUnit.SECONDS)
- if (!awaitResult) {
- throw GradleException("Failed to await for git")
- }
- if (process.exitValue() != 0) {
- val errorMessage = process.errorStream.bufferedReader().use { it.readText() }
- throw GradleException("Git command failed: $errorMessage")
- }
+ process.awaitSuccess("Git log")
process.inputStream.bufferedReader().use { !it.readLine().isNullOrEmpty() }
}
}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/android/support/RepoPropParser.kt b/buildSrc/src/main/kotlin/android/support/RepoPropParser.kt
new file mode 100644
index 0000000..db45c5e
--- /dev/null
+++ b/buildSrc/src/main/kotlin/android/support/RepoPropParser.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 android.support
+
+import org.gradle.api.GradleException
+import java.io.File
+
+private const val FRAMEWORKS_SUPPORT = "platform/frameworks/support"
+
+/**
+ * parses repo.prop
+ * expected format is:
+ * <project1 path> <revision sha1>
+ * ....
+ * <projectN path> <revision shaN>
+ */
+private fun parseRepoPropFile(file: File): Map<String, String> {
+ val result = mutableMapOf<String, String>()
+ file.forEachLine { line ->
+ val split = line.split(' ')
+ if (split.size != 2) {
+ throw GradleException("Invalid format for repo.prop, line: $line")
+ }
+ val project = split[0]
+ val sha = split[1]
+ result[project] = sha
+ }
+ return result
+}
+
+fun frameworksSupportSHA(file: File): String {
+ return parseRepoPropFile(file).getOrElse(FRAMEWORKS_SUPPORT, {
+ throw GradleException("$FRAMEWORKS_SUPPORT wasn't found in $file")
+ })
+}
\ No newline at end of file