diff --git a/build-logic/src/main/kotlin/publishing/configurePom.kt b/build-logic/src/main/kotlin/publishing/configurePom.kt index b81c51d2a6..c140dd2cd7 100644 --- a/build-logic/src/main/kotlin/publishing/configurePom.kt +++ b/build-logic/src/main/kotlin/publishing/configurePom.kt @@ -24,7 +24,6 @@ import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.artifacts.component.ModuleComponentSelector import org.gradle.api.provider.Provider -import org.gradle.api.publish.maven.MavenPom import org.gradle.api.publish.maven.MavenPublication import org.gradle.internal.extensions.stdlib.capitalized @@ -36,13 +35,14 @@ import org.gradle.internal.extensions.stdlib.capitalized * consumable by Maven. * * The root project generates the parent pom, containing all the necessary elements to pass Sonatype - * validation and some more information like `` and ``. Most of the - * information is taken from publicly consumable Apache project information from - * `https://projects.apache.org/json/projects/>.json`. `` contains all - * (P)PMC members and committers from that project info JSON, ordered by real name. `` - * is taken from GitHub's - * `https://api.github.com/repos/apache//contributors?per_page=1000` endpoint to give - * all contributors credit, ordered by number of contributions (as returned by that endpoint). + * validation. Most of the information is taken from publicly consumable Apache project information + * from `https://projects.apache.org/json/projects/.json`. Changes to the Apache + * project metadata, including podling information, will break the reproducibility of the build. + * + * Developer and contributor elements are intentionally *not* included in the POM. Such information + * is not considered stable (enough) to satisfy reproducible build requirements. The generated POM + * must be exactly the same when built by a release manager and by someone else to verify the built + * artifact(s). */ internal fun configurePom(project: Project, mavenPublication: MavenPublication, task: Task) = mavenPublication.run { @@ -50,7 +50,7 @@ internal fun configurePom(project: Project, mavenPublication: MavenPublication, pom { if (project != project.rootProject) { - // Add the license to every pom to make it easier for downstream project to retrieve the + // Add the license to every pom to make it easier for downstream projects to retrieve the // license. licenses { license { @@ -75,7 +75,7 @@ internal fun configurePom(project: Project, mavenPublication: MavenPublication, task.doFirst { mavenPom.run { val asfName = e.asfProjectId.get() - val projectPeople = fetchProjectPeople(asfName) + val projectInfo = fetchProjectInformation(asfName) organization { name.set("The Apache Software Foundation") @@ -84,7 +84,7 @@ internal fun configurePom(project: Project, mavenPublication: MavenPublication, licenses { license { name.set("Apache-2.0") // SPDX identifier - url.set(projectPeople.licenseUrl) + url.set(projectInfo.licenseUrl) } } mailingLists { @@ -92,7 +92,7 @@ internal fun configurePom(project: Project, mavenPublication: MavenPublication, mailingList { name.set("${ml.capitalized()} Mailing List") subscribe.set("$ml-subscribe@$asfName.apache.org") - unsubscribe.set("$ml-ubsubscribe@$asfName.apache.org") + unsubscribe.set("$ml-unsubscribe@$asfName.apache.org") post.set("$ml@$asfName.apache.org") archive.set("https://lists.apache.org/list.html?$ml@$asfName.apache.org") } @@ -104,7 +104,7 @@ internal fun configurePom(project: Project, mavenPublication: MavenPublication, e.overrideScm.orElse( githubRepoName .map { r -> "https://github.com/apache/$r" } - .orElse(projectPeople.repository) + .orElse(projectInfo.repository) ) scm { @@ -115,59 +115,28 @@ internal fun configurePom(project: Project, mavenPublication: MavenPublication, val version = project.version.toString() if (!version.endsWith("-SNAPSHOT")) { val tagPrefix: String = - e.overrideTagPrefix.orElse("apache-${projectPeople.apacheId}").get() + e.overrideTagPrefix.orElse("apache-${projectInfo.apacheId}").get() tag.set("$tagPrefix-$version") } } issueManagement { val issuesUrl: Provider = - codeRepo.map { r -> "$r/issues" }.orElse(projectPeople.bugDatabase) + codeRepo.map { r -> "$r/issues" }.orElse(projectInfo.bugDatabase) url.set(e.overrideIssueManagement.orElse(issuesUrl)) } - name.set(e.overrideName.orElse("Apache ${projectPeople.name}")) - description.set(e.overrideDescription.orElse(projectPeople.description)) - url.set(e.overrideProjectUrl.orElse(projectPeople.website)) - inceptionYear.set(projectPeople.inceptionYear.toString()) - - developers { - projectPeople.people.forEach { person -> - developer { - this.id.set(person.apacheId) - this.name.set(person.name) - this.organization.set("Apache Software Foundation") - this.email.set("${person.apacheId}@apache.org") - this.roles.addAll(person.roles) - } - } - } + name.set(e.overrideName.orElse("Apache ${projectInfo.name}")) + description.set(e.overrideDescription.orElse(projectInfo.description)) + url.set(e.overrideProjectUrl.orElse(projectInfo.website)) + inceptionYear.set(projectInfo.inceptionYear.toString()) - addContributorsToPom(mavenPom, githubRepoName.get(), "Apache ${projectPeople.name}") + developers { developer { url.set("https://$asfName.apache.org/community/") } } } } } } } -/** Adds contributors as returned by GitHub, in descending `contributions` order. */ -fun addContributorsToPom(mavenPom: MavenPom, asfName: String, asfProjectName: String) = - mavenPom.run { - contributors { - val contributors: List> = - parseJson("https://api.github.com/repos/apache/$asfName/contributors?per_page=1000") - contributors - .filter { contributor -> contributor["type"] == "User" } - .forEach { contributor -> - contributor { - name.set(contributor["login"] as String) - url.set(contributor["url"] as String) - organization.set("$asfProjectName, GitHub contributors") - organizationUrl.set("https://github.com/apache/$asfName") - } - } - } - } - /** * Scans the generated `pom.xml` for `` in `` that do not have a * `` and adds one, if possible. Maven kinda requires `` tags there, even if the diff --git a/build-logic/src/main/kotlin/publishing/util.kt b/build-logic/src/main/kotlin/publishing/util.kt index 864a14c72c..fd1366731d 100644 --- a/build-logic/src/main/kotlin/publishing/util.kt +++ b/build-logic/src/main/kotlin/publishing/util.kt @@ -99,36 +99,26 @@ internal fun parseJson(urlStr: String): T { /** Retrieves the project name, for example `Polaris` using the lower-case project ID. */ internal fun fetchAsfProjectName(apacheId: String): String { - val projectsAll: Map> = - parseJson("https://whimsy.apache.org/public/public_ldap_projects.json") - val projects = unsafeCast>>(projectsAll["projects"]) - val project = - projects[apacheId] - ?: throw IllegalArgumentException( - "No project '$apacheId' found in https://whimsy.apache.org/public/public_ldap_projects.json" - ) + val project = projectMap(apacheId) val isPodlingCurrent = project.containsKey("podling") && project["podling"] == "current" if (isPodlingCurrent) { - val podlingsAll: Map> = - parseJson("https://whimsy.apache.org/public/public_podlings.json") - val podlings = unsafeCast>>(podlingsAll["podling"]) - val podling = - podlings[apacheId] - ?: throw IllegalArgumentException( - "No podling '$apacheId' found in https://whimsy.apache.org/public/public_podlings.json" - ) + val podling = podlingMap(apacheId) return podling["name"] as String } else { // top-level-project - val committeesAll: Map> = - parseJson("https://whimsy.apache.org/public/committee-info.json") - val committees = unsafeCast>>(committeesAll["committees"]) - val committee = unsafeCast>(committees[apacheId]) + val committee = projectCommitteeMap(apacheId) return committee["display_name"] as String } } -internal fun fetchProjectPeople(apacheId: String): ProjectPeople { +internal fun projectCommitteeMap(apacheId: String): Map { + val committeesAll: Map> = + parseJson("https://whimsy.apache.org/public/committee-info.json") + val committees = unsafeCast>>(committeesAll["committees"]) + return unsafeCast(committees[apacheId]) +} + +internal fun projectMap(apacheId: String): Map { val projectsAll: Map> = parseJson("https://whimsy.apache.org/public/public_ldap_projects.json") val projects = unsafeCast>>(projectsAll["projects"]) @@ -137,19 +127,26 @@ internal fun fetchProjectPeople(apacheId: String): ProjectPeople { ?: throw IllegalArgumentException( "No project '$apacheId' found in https://whimsy.apache.org/public/public_ldap_projects.json" ) - val isPodlingCurrent = project.containsKey("podling") && project["podling"] == "current" + return project +} - val inceptionYear = (project["createTimestamp"] as String).subSequence(0, 4).toString().toInt() +internal fun podlingMap(apacheId: String): Map { + val podlingsAll: Map> = + parseJson("https://whimsy.apache.org/public/public_podlings.json") + val podlings = unsafeCast>>(podlingsAll["podling"]) + val podling = + podlings[apacheId] + ?: throw IllegalArgumentException( + "No podling '$apacheId' found in https://whimsy.apache.org/public/public_podlings.json" + ) + return podling +} - // Committers - val peopleProjectRoles: MutableMap> = mutableMapOf() - val members = unsafeCast(project["members"]) as List - members.forEach { member -> peopleProjectRoles.put(member, mutableListOf("Committer")) } +internal fun fetchProjectInformation(apacheId: String): ProjectInformation { + val project = projectMap(apacheId) + val isPodlingCurrent = project.containsKey("podling") && project["podling"] == "current" - // (P)PMC Members - val pmcRoleName = if (isPodlingCurrent) "PPMC member" else "PMC member" - val owners = unsafeCast(project["owners"]) as List - owners.forEach { member -> peopleProjectRoles[member]!!.add(pmcRoleName) } + val inceptionYear = (project["createTimestamp"] as String).subSequence(0, 4).toString().toInt() val projectName: String val description: String @@ -158,14 +155,7 @@ internal fun fetchProjectPeople(apacheId: String): ProjectPeople { val licenseUrl: String val bugDatabase: String if (isPodlingCurrent) { - val podlingsAll: Map> = - parseJson("https://whimsy.apache.org/public/public_podlings.json") - val podlings = unsafeCast>>(podlingsAll["podling"]) - val podling = - podlings[apacheId] - ?: throw IllegalArgumentException( - "No podling '$apacheId' found in https://whimsy.apache.org/public/public_podlings.json" - ) + val podling = podlingMap(apacheId) projectName = podling["name"] as String description = podling["description"] as String val podlingStatus = unsafeCast(podling["podlingStatus"]) as Map @@ -174,12 +164,6 @@ internal fun fetchProjectPeople(apacheId: String): ProjectPeople { repository = "https://github.com/apache/$apacheId.git" bugDatabase = "https://github.com/apache/$apacheId/issues" licenseUrl = "https://www.apache.org/licenses/LICENSE-2.0.txt" - - val champion = podling["champion"] as String - peopleProjectRoles[champion]!!.add("Champion") - - val mentors = unsafeCast(podling["mentors"]) as List - mentors.forEach { member -> peopleProjectRoles[member]!!.add("Mentor") } } else { // top-level-project val tlpPrj: Map = @@ -189,33 +173,12 @@ internal fun fetchProjectPeople(apacheId: String): ProjectPeople { bugDatabase = tlpPrj["bug-database"] as String licenseUrl = tlpPrj["license"] as String - val committeesAll: Map> = - parseJson("https://whimsy.apache.org/public/committee-info.json") - val committees = unsafeCast>>(committeesAll["committees"]) - val committee = unsafeCast>(committees[apacheId]) - val pmcChair = unsafeCast>>(committee["chair"]) + val committee = projectCommitteeMap(apacheId) projectName = committee["display_name"] as String description = committee["description"] as String - pmcChair.keys.forEach { chair -> peopleProjectRoles[chair]!!.add("PMC Chair") } } - val peopleNames: Map> = - parseJson("https://whimsy.apache.org/public/public_ldap_people.json") - val people: Map> = - unsafeCast(peopleNames["people"]) as Map> - val peopleList = - peopleProjectRoles.entries - .map { entry -> - val person = - people[entry.key] - ?: throw IllegalStateException( - "No person '${entry.key}' found in https://whimsy.apache.org/public/public_ldap_people.json" - ) - ProjectMember(entry.key, person["name"]!! as String, entry.value) - } - .sortedBy { it.name } - - return ProjectPeople( + return ProjectInformation( apacheId, projectName, description, @@ -224,11 +187,10 @@ internal fun fetchProjectPeople(apacheId: String): ProjectPeople { licenseUrl, bugDatabase, inceptionYear, - peopleList, ) } -internal class ProjectPeople( +internal class ProjectInformation( val apacheId: String, val name: String, val description: String, @@ -237,7 +199,4 @@ internal class ProjectPeople( val licenseUrl: String, val bugDatabase: String, val inceptionYear: Int, - val people: List, ) - -internal class ProjectMember(val apacheId: String, val name: String, val roles: List)