Skip to content

Commit ce5e01b

Browse files
davidmotsonDavid Motsonashvilirlazo
authored
First commit, removed root files and moved configuration to BomGenera… (#2454)
* First commit, removed root files and moved configuration to BomGeneratorTask Co-authored-by: David Motsonashvili <[email protected]> Co-authored-by: Rodrigo Lazo Paz <[email protected]>
1 parent 34bf6dc commit ce5e01b

File tree

8 files changed

+1059
-0
lines changed

8 files changed

+1059
-0
lines changed

buildSrc/build.gradle

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,21 @@ dependencies {
6060
implementation "com.google.firebase:perf-plugin:$perfPluginVersion"
6161

6262
implementation 'org.jsoup:jsoup:1.11.2'
63+
implementation "com.google.auto.value:auto-value-annotations:1.6.6"
64+
annotationProcessor "com.google.auto.value:auto-value:1.6.5"
6365
implementation 'digital.wup:android-maven-publish:3.6.2'
6466
implementation 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72'
6567
implementation 'org.json:json:20180813'
6668
implementation 'io.opencensus:opencensus-api:0.18.0'
6769
implementation 'io.opencensus:opencensus-exporter-stats-stackdriver:0.18.0'
70+
implementation "org.eclipse.aether:aether-api:1.0.0.v20140518"
71+
implementation "org.eclipse.aether:aether-util:1.0.0.v20140518"
72+
implementation "org.eclipse.aether:aether-impl:1.0.0.v20140518"
73+
implementation "org.eclipse.aether:aether-connector-basic:1.0.0.v20140518"
74+
implementation "org.eclipse.aether:aether-transport-file:1.0.0.v20140518"
75+
implementation "org.eclipse.aether:aether-transport-http:1.0.0.v20140518"
76+
implementation "org.eclipse.aether:aether-transport-wagon:1.0.0.v20140518"
77+
implementation "org.apache.maven:maven-aether-provider:3.1.0"
6878
runtime 'io.opencensus:opencensus-impl:0.18.0'
6979
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.6'
7080
implementation 'org.jetbrains.dokka:dokka-android-gradle-plugin:0.9.17-g013-9b8280a'
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.gradle.bomgenerator;
16+
17+
import static java.util.stream.Collectors.toList;
18+
19+
import com.google.common.collect.ImmutableList;
20+
import com.google.common.collect.ImmutableMap;
21+
import com.google.common.collect.Sets;
22+
import com.google.firebase.gradle.bomgenerator.model.Dependency;
23+
import com.google.firebase.gradle.bomgenerator.model.VersionBump;
24+
import java.io.IOException;
25+
import java.io.InputStream;
26+
import java.net.URL;
27+
import java.nio.file.Path;
28+
import java.util.HashMap;
29+
import java.util.HashSet;
30+
import java.util.List;
31+
import java.util.Map;
32+
import java.util.Optional;
33+
import java.util.Set;
34+
import java.util.stream.Collectors;
35+
import javax.xml.parsers.DocumentBuilder;
36+
import javax.xml.parsers.DocumentBuilderFactory;
37+
import javax.xml.parsers.ParserConfigurationException;
38+
import org.eclipse.aether.resolution.VersionRangeResolutionException;
39+
import org.gradle.api.DefaultTask;
40+
import org.gradle.api.tasks.TaskAction;
41+
import org.w3c.dom.Document;
42+
import org.w3c.dom.Element;
43+
import org.w3c.dom.NodeList;
44+
import org.xml.sax.SAXException;
45+
46+
public class BomGeneratorTask extends DefaultTask {
47+
private static final List<String> BOM_ARTIFACTS =
48+
ImmutableList.of(
49+
"com.crashlytics.sdk.android:crashlytics",
50+
"com.google.firebase:firebase-ads",
51+
"com.google.firebase:firebase-ads-lite",
52+
"com.google.firebase:firebase-analytics",
53+
"com.google.firebase:firebase-analytics-ktx",
54+
"com.google.firebase:firebase-appindexing",
55+
"com.google.firebase:firebase-auth",
56+
"com.google.firebase:firebase-auth-ktx",
57+
"com.google.firebase:firebase-common",
58+
"com.google.firebase:firebase-common-ktx",
59+
"com.google.firebase:firebase-config",
60+
"com.google.firebase:firebase-config-ktx",
61+
"com.google.firebase:firebase-core",
62+
"com.google.firebase:firebase-crash",
63+
"com.google.firebase:firebase-crashlytics",
64+
"com.google.firebase:firebase-crashlytics-ktx",
65+
"com.google.firebase:firebase-crashlytics-ndk",
66+
"com.google.firebase:firebase-database",
67+
"com.google.firebase:firebase-database-ktx",
68+
"com.google.firebase:firebase-dynamic-links",
69+
"com.google.firebase:firebase-dynamic-links-ktx",
70+
"com.google.firebase:firebase-encoders",
71+
"com.google.firebase:firebase-firestore",
72+
"com.google.firebase:firebase-firestore-ktx",
73+
"com.google.firebase:firebase-functions",
74+
"com.google.firebase:firebase-functions-ktx",
75+
"com.google.firebase:firebase-iid",
76+
"com.google.firebase:firebase-inappmessaging",
77+
"com.google.firebase:firebase-inappmessaging-display",
78+
"com.google.firebase:firebase-inappmessaging-display-ktx",
79+
"com.google.firebase:firebase-inappmessaging-ktx",
80+
"com.google.firebase:firebase-installations",
81+
"com.google.firebase:firebase-installations-ktx",
82+
"com.google.firebase:firebase-messaging",
83+
"com.google.firebase:firebase-messaging-directboot",
84+
"com.google.firebase:firebase-messaging-ktx",
85+
"com.google.firebase:firebase-ml-model-interpreter",
86+
"com.google.firebase:firebase-ml-vision",
87+
"com.google.firebase:firebase-perf",
88+
"com.google.firebase:firebase-perf-ktx",
89+
"com.google.firebase:firebase-storage",
90+
"com.google.firebase:firebase-storage-ktx");
91+
private static final List<String> IGNORED_ARTIFACTS =
92+
ImmutableList.of(
93+
"crash-plugin",
94+
"firebase-abt",
95+
"firebase-analytics-impl",
96+
"firebase-analytics-impl-license",
97+
"firebase-analytics-license",
98+
"firebase-annotations",
99+
"firebase-appdistribution-gradle",
100+
"firebase-appindexing-license",
101+
"firebase-auth-common",
102+
"firebase-auth-impl",
103+
"firebase-auth-interop",
104+
"firebase-auth-license",
105+
"firebase-encoders-json",
106+
"firebase-auth-module",
107+
"firebase-bom",
108+
"firebase-common-license",
109+
"firebase-components",
110+
"firebase-config-license",
111+
"firebase-crash-license",
112+
"firebase-crashlytics-buildtools",
113+
"firebase-crashlytics-gradle",
114+
"firebase-database-collection",
115+
"firebase-database-connection",
116+
"firebase-database-connection-license",
117+
"firebase-database-license",
118+
"firebase-datatransport",
119+
"firebase-dynamic-links-license",
120+
"firebase-functions-license",
121+
"firebase-iid-interop",
122+
"firebase-iid-license",
123+
"firebase-invites",
124+
"firebase-measurement-connector",
125+
"firebase-measurement-connector-impl",
126+
"firebase-messaging-license",
127+
"firebase-ml-common",
128+
"firebase-ml-vision-internal-vkp",
129+
"firebase-perf-license",
130+
"firebase-plugins",
131+
"firebase-storage-common",
132+
"firebase-storage-common-license",
133+
"firebase-storage-license",
134+
"perf-plugin",
135+
"protolite-well-known-types",
136+
"testlab-instr-lib",
137+
"firebase-installations-interop",
138+
"google-services",
139+
"gradle",
140+
"firebase-ml-vision-automl",
141+
"firebase-ml-vision-barcode-model",
142+
"firebase-ml-vision-face-model",
143+
"firebase-ml-vision-image-label-model",
144+
"firebase-ml-vision-object-detection-model",
145+
"firebase-ml-natural-language",
146+
"firebase-ml-natural-language-language-id-model",
147+
"firebase-ml-natural-language-smart-reply",
148+
"firebase-ml-natural-language-smart-reply-model",
149+
"firebase-ml-natural-language-translate",
150+
"firebase-ml-natural-language-translate-model");
151+
private static final List<String> IMPORTANT_NON_FIREBASE_LIBRARIES =
152+
ImmutableList.of(
153+
"com.google.gms:google-services",
154+
"com.android.tools.build:gradle",
155+
"com.google.firebase:perf-plugin",
156+
"com.google.firebase:firebase-crashlytics-gradle",
157+
"com.google.firebase:firebase-appdistribution-gradle");
158+
159+
private Set<String> ignoredFirebaseArtifacts;
160+
private Set<String> bomArtifacts;
161+
private Set<String> allFirebaseArtifacts;
162+
163+
public Map<String, String> versionOverrides = new HashMap<>();
164+
165+
/**
166+
* This task generates a current Bill of Materials (BoM) based on the latest versions of
167+
* everything in gMaven. This is meant to be a post-release task so that the BoM contains the most
168+
* recent versions of all artifacts.
169+
*
170+
* <p>Version overrides may be given to this task in a map like so: versionOverrides =
171+
* ["com.google.firebase:firebase-firestore": "17.0.1"]
172+
*/
173+
@TaskAction
174+
public void generateBom() throws Exception {
175+
// Repo Access Setup
176+
RepositoryClient depPopulator = new RepositoryClient();
177+
178+
// Prepare script by pulling the state of the world (checking configuration files and gMaven
179+
// artifacts)
180+
bomArtifacts = new HashSet(BOM_ARTIFACTS);
181+
ignoredFirebaseArtifacts = new HashSet(IGNORED_ARTIFACTS);
182+
allFirebaseArtifacts = depPopulator.getAllFirebaseArtifacts();
183+
allFirebaseArtifacts.addAll(IMPORTANT_NON_FIREBASE_LIBRARIES);
184+
185+
// Find version for BoM artifact. First version released should be 15.0.0
186+
String currentVersion =
187+
depPopulator
188+
.getLastPublishedVersion(Dependency.create("com.google.firebase", "firebase-bom"))
189+
.orElse("15.0.0");
190+
191+
// We need to get the content of the current BoM to compute version bumps.
192+
Map<String, String> previousBomVersions = getBomMap(currentVersion);
193+
194+
// Generate list of firebase libraries, ping gmaven for current versions, and override as needed
195+
// from local settings
196+
List<Dependency> allFirebaseDependencies =
197+
buildVersionedDependencyList(depPopulator, previousBomVersions);
198+
199+
List<Dependency> bomDependencies =
200+
allFirebaseDependencies.stream()
201+
.filter(dep -> bomArtifacts.contains(dep.fullArtifactId()))
202+
.collect(toList());
203+
204+
// Sanity check that there are no unaccounted for artifacts that we might want in the BoM
205+
Set<String> bomArtifactIds =
206+
bomArtifacts.stream().map(x -> x.split(":")[1]).collect(Collectors.toSet());
207+
Set<String> allFirebaseArtifactIds =
208+
allFirebaseArtifacts.stream().map(x -> x.split(":")[1]).collect(Collectors.toSet());
209+
Set<String> invalidArtifacts =
210+
Sets.difference(
211+
Sets.difference(allFirebaseArtifactIds, bomArtifactIds), ignoredFirebaseArtifacts);
212+
213+
if (!invalidArtifacts.isEmpty()) {
214+
throw new RuntimeException(
215+
"Some dependencies are unaccounted for, add to BomGeneratorTask#IGNORED_ARTIFACTS or "
216+
+ "BomGeneratorTask#BOM_ARTIFACTS. Unaccounted for dependencies: "
217+
+ invalidArtifacts.toString());
218+
}
219+
String version = findArtifactVersion(bomDependencies, currentVersion, previousBomVersions);
220+
221+
// Surface generated pom for sanity checking and testing, and then write it.
222+
Path projectRootDir = this.getProject().getRootDir().toPath();
223+
PomXmlWriter xmlWriter = new PomXmlWriter(bomDependencies, version, projectRootDir);
224+
MarkdownDocumentationWriter documentationWriter =
225+
new MarkdownDocumentationWriter(
226+
bomDependencies, version, previousBomVersions, currentVersion);
227+
RecipeVersionWriter recipeWriter = new RecipeVersionWriter(allFirebaseDependencies);
228+
Document outputXmlDoc = xmlWriter.generatePomXml();
229+
String outputDocumentation = documentationWriter.generateDocumentation();
230+
String outputRecipe = recipeWriter.generateVersionUpdate();
231+
xmlWriter.writeXmlDocument(outputXmlDoc);
232+
documentationWriter.writeDocumentation(outputDocumentation);
233+
recipeWriter.writeVersionUpdate(outputRecipe);
234+
}
235+
236+
// Finds the version for the BoM artifact.
237+
private String findArtifactVersion(
238+
List<Dependency> firebaseDependencies,
239+
String currentVersion,
240+
Map<String, String> previousBomVersions)
241+
throws VersionRangeResolutionException {
242+
Optional<VersionBump> bump =
243+
firebaseDependencies.stream().map(Dependency::versionBump).distinct().sorted().findFirst();
244+
245+
if (firebaseDependencies.size() < previousBomVersions.size()) {
246+
bump = Optional.of(VersionBump.MAJOR);
247+
}
248+
249+
return bump.map(x -> VersionBump.bumpVersionBy(currentVersion, x))
250+
.orElseThrow(() -> new RuntimeException("Could not figure out how to bump version"));
251+
}
252+
253+
private Dependency overrideVersion(Dependency dep) {
254+
if (versionOverrides.containsKey(dep.fullArtifactId())) {
255+
return Dependency.create(
256+
dep.groupId(),
257+
dep.artifactId(),
258+
versionOverrides.get(dep.fullArtifactId()),
259+
VersionBump.PATCH);
260+
} else {
261+
return dep;
262+
}
263+
}
264+
265+
private List<Dependency> buildVersionedDependencyList(
266+
RepositoryClient depPopulator, Map<String, String> previousBomVersions) {
267+
return allFirebaseArtifacts.stream()
268+
.map(
269+
dep -> {
270+
String[] splitDep = dep.split(":");
271+
return Dependency.create(splitDep[0], splitDep[1]);
272+
})
273+
.map(dep -> depPopulator.populateDependencyVersion(dep, previousBomVersions))
274+
.map(this::overrideVersion)
275+
.collect(toList());
276+
}
277+
278+
private Map<String, String> getBomMap(String bomVersion) {
279+
String bomUrl =
280+
"https://dl.google.com/dl/android/maven2/com/google/firebase/firebase-bom/"
281+
+ bomVersion
282+
+ "/firebase-bom-"
283+
+ bomVersion
284+
+ ".pom";
285+
try (InputStream index = new URL(bomUrl).openStream()) {
286+
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
287+
factory.setValidating(true);
288+
factory.setIgnoringElementContentWhitespace(true);
289+
DocumentBuilder builder = factory.newDocumentBuilder();
290+
Document doc = builder.parse(index);
291+
NodeList dependencyList = doc.getElementsByTagName("dependency");
292+
ImmutableMap.Builder<String, String> outputBuilder = ImmutableMap.builder();
293+
for (int i = 0; i < dependencyList.getLength(); i++) {
294+
Element artifact = (Element) dependencyList.item(i);
295+
String groupId = artifact.getElementsByTagName("groupId").item(0).getTextContent();
296+
String artifactId = artifact.getElementsByTagName("artifactId").item(0).getTextContent();
297+
String version = artifact.getElementsByTagName("version").item(0).getTextContent();
298+
outputBuilder.put(groupId + ":" + artifactId, version);
299+
}
300+
return outputBuilder.build();
301+
} catch (SAXException | IOException | ParserConfigurationException e) {
302+
throw new RuntimeException("Failed to get contents of BoM version " + bomVersion, e);
303+
}
304+
}
305+
}

0 commit comments

Comments
 (0)