diff --git a/README.md b/README.md index 4e18b55387..a4c78f000d 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,6 @@ This project explores common infrastructure and best practices for open source Java projects for the Google Cloud Platform (GCP). -# Google Cloud Platform Java Dependency Dashboard - -[Google Cloud Platform Java Dependency Dashboard]( -https://storage.googleapis.com/cloud-opensource-java-dashboard/com.google.cloud/libraries-bom/snapshot/index.html) -(runs daily; work in progress) shows multiple checks on the consistency among -Google Cloud Java libraries. For manually generating the dashboard, see -[its README](./dashboard/README.md). - # Google Best Practices for Java Libraries [Google Best Practices for Java Libraries](https://googlecloudplatform.github.io/cloud-opensource-java/) @@ -43,6 +35,8 @@ The [GCP Libraries BOM](https://cloud.google.com/java/docs/bom) is a Bill-of-Mat provides consistent versions of Google Cloud Java libraries that work together without linkage errors. +This has moved to https://github.com/googleapis/java-cloud-bom/tree/main/libraries-bom. + # Development This project is built using _Maven_. @@ -62,5 +56,3 @@ This project is built using _Maven_. This is not an officially supported Google product. - - diff --git a/boms/cloud-oss-bom/pom.xml b/boms/cloud-oss-bom/pom.xml deleted file mode 100644 index 0fbd96533f..0000000000 --- a/boms/cloud-oss-bom/pom.xml +++ /dev/null @@ -1,288 +0,0 @@ - - - 4.0.0 - - com.google.cloud - libraries-bom - 25.3.1-SNAPSHOT - pom - - Google Cloud Platform Supported Libraries - - A compatible set of Google Cloud open source libraries. - Document: https://cloud.google.com/java/docs/bom - - https://cloud.google.com/java/docs/bom - - Google LLC - https://cloud.google.com - - 2019 - - - Elliotte Rusty Harold - - - - - https://github.com/GoogleCloudPlatform/cloud-opensource-java/issues - - - scm:git:git@github.com:GoogleCloudPlatform/cloud-opensource-java.git - scm:git:git@github.com:GoogleCloudPlatform/cloud-opensource-java.git - - https://github.com/GoogleCloudPlatform/cloud-opensource-java/boms/cloud-oss-bom - HEAD - - - - - The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - - - - UTF-8 - 31.1-jre - 2.9.0 - 0.174.0 - 2.6.0 - 1.45.1 - 1.41.7 - 3.19.4 - - 2.16.0 - 0.101.0 - 1.6.0 - 2.1.5 - 2.8.3 - 1.3.1 - - - - - sonatype-nexus-snapshots - Sonatype Nexus Snapshots - https://oss.sonatype.org/content/repositories/snapshots/ - - - - - - - - com.google.guava - guava - ${guava.version} - - - com.google.guava - guava-testlib - ${guava.version} - - - com.google.code.gson - gson - ${gson.version} - - - - com.google.protobuf - protobuf-bom - ${protobuf.version} - pom - import - - - - - com.google.http-client - google-http-client - ${http.version} - - - com.google.http-client - google-http-client-android - ${http.version} - - - com.google.http-client - google-http-client-apache-v2 - ${http.version} - - - com.google.http-client - google-http-client-appengine - ${http.version} - - - com.google.http-client - google-http-client-gson - ${http.version} - - - com.google.http-client - google-http-client-jackson2 - ${http.version} - - - com.google.http-client - google-http-client-protobuf - ${http.version} - - - com.google.http-client - google-http-client-test - ${http.version} - - - com.google.http-client - google-http-client-xml - ${http.version} - - - - - io.grpc - grpc-alts - ${io.grpc.version} - - - io.grpc - grpc-api - ${io.grpc.version} - - - io.grpc - grpc-auth - ${io.grpc.version} - - - io.grpc - grpc-context - ${io.grpc.version} - - - io.grpc - grpc-core - ${io.grpc.version} - - - io.grpc - grpc-grpclb - ${io.grpc.version} - - - io.grpc - grpc-netty - ${io.grpc.version} - - - io.grpc - grpc-netty-shaded - ${io.grpc.version} - - - io.grpc - grpc-okhttp - ${io.grpc.version} - - - io.grpc - grpc-protobuf - ${io.grpc.version} - - - io.grpc - grpc-protobuf-lite - ${io.grpc.version} - - - io.grpc - grpc-services - ${io.grpc.version} - - - io.grpc - grpc-stub - ${io.grpc.version} - - - io.grpc - grpc-testing - ${io.grpc.version} - - - - - com.google.cloud - google-cloud-bom - ${google.cloud.bom.version} - pom - import - - - - com.google.api - api-common - ${api-common.version} - - - com.google.api - gax - ${gax.version} - - - com.google.api - gax-grpc - ${gax.version} - - - com.google.api - gax-httpjson - ${gax.httpjson.version} - - - com.google.auth - google-auth-library-bom - ${auth.version} - pom - import - - - com.google.cloud - google-cloud-core-bom - ${google.cloud.core.version} - pom - import - - - com.google.api.grpc - proto-google-common-protos - ${common.protos.version} - - - com.google.api.grpc - grpc-google-common-protos - ${common.protos.version} - - - com.google.api.grpc - proto-google-iam-v1 - ${iam.protos.version} - - - com.google.api.grpc - grpc-google-iam-v1 - ${iam.protos.version} - - - - - - diff --git a/boms/pom.xml b/boms/pom.xml index 507915a380..5aa20037ba 100644 --- a/boms/pom.xml +++ b/boms/pom.xml @@ -29,7 +29,6 @@ - cloud-oss-bom cloud-lts-bom upper-bounds-check integration-tests diff --git a/dashboard/README.md b/dashboard/README.md deleted file mode 100644 index 9e83166283..0000000000 --- a/dashboard/README.md +++ /dev/null @@ -1,7 +0,0 @@ -To generate the dashboard from the root directory run: - -``` -$ mvn clean install -$ cd dashboard -$ mvn exec:java -Dexec.arguments="-f ../boms/cloud-oss-bom/pom.xml" -``` diff --git a/dashboard/pom.xml b/dashboard/pom.xml deleted file mode 100644 index 932ec0e4f3..0000000000 --- a/dashboard/pom.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - 4.0.0 - - com.google.cloud.tools - dependencies-parent - 1.5.16-SNAPSHOT - - dashboard - - Cloud Tools Open Source Code Hygiene Dashboard - https://github.com/GoogleCloudPlatform/cloud-opensource-java/dashboard - - Google LLC. - https://www.google.com - - - - - The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - - - - UTF-8 - 1.8 - 1.8 - - - - - org.freemarker - freemarker - 2.3.31 - - - com.google.guava - guava - - - ${project.groupId} - dependencies - ${project.version} - - - - xom - xom - 1.3.7 - test - - - junit - junit - test - - - com.google.truth - truth - test - - - - - - - org.codehaus.mojo - exec-maven-plugin - - false - com.google.cloud.tools.opensource.dashboard.DashboardMain - - - - - - diff --git a/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/ArtifactCache.java b/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/ArtifactCache.java deleted file mode 100644 index ec12246c52..0000000000 --- a/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/ArtifactCache.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2018 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.cloud.tools.opensource.dashboard; - -import java.util.List; -import java.util.Map; - -import org.eclipse.aether.artifact.Artifact; - -import com.google.cloud.tools.opensource.dependencies.DependencyGraph; - -/** - * Unified return type to bundle a lot of information about multiple artifacts together. - */ -class ArtifactCache { - - private Map infoMap; - private List globalDependencies; - - void setInfoMap(Map infoMap) { - this.infoMap = infoMap; - } - - void setGlobalDependencies(List globalDependencies) { - this.globalDependencies = globalDependencies; - } - - Map getInfoMap() { - return infoMap; - } - - List getGlobalDependencies() { - return globalDependencies; - } - -} diff --git a/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/ArtifactInfo.java b/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/ArtifactInfo.java deleted file mode 100644 index 1bf711c88e..0000000000 --- a/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/ArtifactInfo.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2018 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.cloud.tools.opensource.dashboard; - -import org.eclipse.aether.RepositoryException; - -import com.google.cloud.tools.opensource.dependencies.DependencyGraph; - -/** - * Cache of info looked up for an artifact. - */ -class ArtifactInfo { - - private DependencyGraph completeDependencies; - private DependencyGraph transitiveDependencies; - private RepositoryException exception; - - ArtifactInfo(DependencyGraph completeDependencies, - DependencyGraph transitiveDependencies) { - this.completeDependencies = completeDependencies; - this.transitiveDependencies = transitiveDependencies; - } - - ArtifactInfo(RepositoryException ex) { - this.exception = ex; - } - - DependencyGraph getCompleteDependencies() { - return completeDependencies; - } - - DependencyGraph getTransitiveDependencies() { - return transitiveDependencies; - } - - RepositoryException getException() { - return exception; - } - -} diff --git a/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/ArtifactResults.java b/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/ArtifactResults.java deleted file mode 100644 index da528a2f84..0000000000 --- a/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/ArtifactResults.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2018 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.cloud.tools.opensource.dashboard; - -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.Nullable; - -import org.eclipse.aether.artifact.Artifact; - -import com.google.cloud.tools.opensource.dependencies.Artifacts; - -/** - * Collection of test results for a single artifact. - */ -public final class ArtifactResults { - - private final Map results = new HashMap<>(); - private final Artifact artifact; - private String exceptionMessage; - - public ArtifactResults(Artifact artifact) { - this.artifact = artifact; - } - - public void setExceptionMessage(String exceptionMessage) { - this.exceptionMessage = exceptionMessage; - } - - void addResult(String testName, int failures) { - results.put(testName, failures); - } - - /** - * @return true for pass, false for fail, null for unknown test - */ - @Nullable - public Boolean getResult(String testName) { - Integer failures = results.get(testName); - if (failures != null) { - return failures == 0; - } - return null; - } - - public String getCoordinates() { - return Artifacts.toCoordinates(artifact); - } - - /** - * @return message of exception occurred when running test, null for no exception - */ - @Nullable - public String getExceptionMessage() { - return exceptionMessage; - } - - /** - * - * @return number of times the specified test failed. Returns 0 - * if the test was not run. - */ - public int getFailureCount(String testName) { - Integer failures = results.get(testName); - if (failures == null) { - return 0; - } - return failures; - } -} diff --git a/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/DashboardArguments.java b/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/DashboardArguments.java deleted file mode 100644 index 6dda1bb91a..0000000000 --- a/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/DashboardArguments.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2019 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.cloud.tools.opensource.dashboard; - -import com.google.common.collect.ImmutableList; -import java.nio.file.Path; -import java.nio.file.Paths; -import javax.annotation.Nullable; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.OptionGroup; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; - -/** - * Command-line option for {@link DashboardMain}. The tool takes either a pom.xml file path or Maven - * coordinates for a BOM. - */ -final class DashboardArguments { - private static final Options options = configureOptions(); - private static final HelpFormatter helpFormatter = new HelpFormatter(); - - private static final ImmutableList validDependencyMediationValues = - ImmutableList.of("maven", "gradle"); - - private final CommandLine commandLine; - - private DashboardArguments(CommandLine commandLine) { - this.commandLine = commandLine; - } - - /** - * Returns true if the argument for a file is specified. False if the argument for coordinates is - * specified. - * - *

It is guaranteed that either a file path or Maven coordinates for a BOM are available. - */ - boolean hasFile() { - return commandLine.hasOption('f'); - } - - /** - * Returns true if the argument for a versionless coordinates is specified; otherwise false. - * - *

It is guaranteed that either a file path or Maven coordinates for a BOM are available. - */ - boolean hasVersionlessCoordinates() { - return commandLine.hasOption('a'); - } - - /** Returns an absolute path to pom.xml file of a BOM. Null if file is not specified. */ - @Nullable - Path getBomFile() { - if (!commandLine.hasOption('f')) { - return null; - } - // Trim the value so that maven exec plugin can pass arguments with exec.arguments="-f pom.xml" - return Paths.get(commandLine.getOptionValue('f').trim()).toAbsolutePath(); - } - - /** Returns the Maven coordinates of a BOM. Null if coordinates are not specified. */ - @Nullable - String getBomCoordinates() { - if (!commandLine.hasOption('c')) { - return null; - } - return commandLine.getOptionValue('c').trim(); - } - - /** - * Returns the versionless Maven coordinates of a BOM. Null if versionless coordinates are not - * specified. - */ - @Nullable - String getVersionlessCoordinates() { - if (!commandLine.hasOption('a')) { - return null; - } - return commandLine.getOptionValue('a').trim(); - } - - static DashboardArguments readCommandLine(String... arguments) throws ParseException { - CommandLineParser parser = new DefaultParser(); - - try { - // Throws ParseException if required option group ('-f' or '-c') is not specified - CommandLine commandLine = parser.parse(options, arguments); - String dependencyMediationValue = commandLine.getOptionValue('m'); - if (dependencyMediationValue != null - && !validDependencyMediationValues.contains(dependencyMediationValue)) { - throw new ParseException("Valid values for '-m' are " + validDependencyMediationValues); - } - - return new DashboardArguments(commandLine); - } catch (ParseException ex) { - helpFormatter.printHelp("DashboardMain", options); - throw ex; - } - } - - enum DependencyMediationAlgorithm { - MAVEN, - GRADLE, - } - - /** - * Returns dependency mediation algorithm. By default it's {@link - * DependencyMediationAlgorithm#MAVEN}. - */ - DependencyMediationAlgorithm getDependencyMediation() { - if (!commandLine.hasOption('m')) { - return DependencyMediationAlgorithm.MAVEN; - } - String optionValue = commandLine.getOptionValue('m').trim(); - return "maven".equals(optionValue) - ? DependencyMediationAlgorithm.MAVEN - : DependencyMediationAlgorithm.GRADLE; - } - - private static Options configureOptions() { - Options options = new Options(); - OptionGroup inputGroup = new OptionGroup(); - inputGroup.setRequired(true); - - Option inputFileOption = - Option.builder("f").longOpt("bom-file").hasArg().desc("File to a BOM (pom.xml)").build(); - inputGroup.addOption(inputFileOption); - - Option inputCoordinatesOption = - Option.builder("c") - .longOpt("bom-coordinates") - .hasArg() - .desc( - "Maven coordinates of a BOM. For example, com.google.cloud:libraries-bom:1.0.0") - .build(); - inputGroup.addOption(inputCoordinatesOption); - - Option versionlessCoordinatesOption = - Option.builder("a") - .longOpt("all-versions") - .hasArg() - .desc( - "Maven coordinates of a BOM without version. " - + "For example, com.google.cloud:libraries-bom") - .build(); - inputGroup.addOption(versionlessCoordinatesOption); - - Option dependencyMediationOption = - Option.builder("m") - .longOpt("dependency-mediation") - .hasArg() - .desc( - "The dependency mediation algorithm to choose versions. The valid values are:\n" - + "- 'maven' for nearest-win strategy (default)\n" - + "- 'gradle' for highest-win strategy.") - .build(); - options.addOption(dependencyMediationOption); - - options.addOptionGroup(inputGroup); - return options; - } -} diff --git a/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/DashboardMain.java b/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/DashboardMain.java deleted file mode 100644 index 94427516e2..0000000000 --- a/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/DashboardMain.java +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Copyright 2018 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.cloud.tools.opensource.dashboard; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.cloud.tools.opensource.classpath.ClassFile; -import com.google.cloud.tools.opensource.classpath.ClassPathBuilder; -import com.google.cloud.tools.opensource.classpath.ClassPathEntry; -import com.google.cloud.tools.opensource.classpath.ClassPathResult; -import com.google.cloud.tools.opensource.classpath.DependencyMediation; -import com.google.cloud.tools.opensource.classpath.GradleDependencyMediation; -import com.google.cloud.tools.opensource.classpath.LinkageChecker; -import com.google.cloud.tools.opensource.classpath.LinkageProblem; -import com.google.cloud.tools.opensource.dashboard.DashboardArguments.DependencyMediationAlgorithm; -import com.google.cloud.tools.opensource.dependencies.Artifacts; -import com.google.cloud.tools.opensource.dependencies.Bom; -import com.google.cloud.tools.opensource.dependencies.DependencyGraph; -import com.google.cloud.tools.opensource.dependencies.DependencyGraphBuilder; -import com.google.cloud.tools.opensource.dependencies.DependencyPath; -import com.google.cloud.tools.opensource.dependencies.MavenRepositoryException; -import com.google.cloud.tools.opensource.dependencies.RepositoryUtility; -import com.google.cloud.tools.opensource.dependencies.Update; -import com.google.cloud.tools.opensource.dependencies.VersionComparator; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Joiner; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimaps; -import com.google.common.collect.Sets; -import freemarker.template.Configuration; -import freemarker.template.DefaultObjectWrapper; -import freemarker.template.DefaultObjectWrapperBuilder; -import freemarker.template.Template; -import freemarker.template.TemplateException; -import freemarker.template.TemplateHashModel; -import freemarker.template.Version; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; -import org.apache.commons.cli.ParseException; -import org.eclipse.aether.RepositoryException; -import org.eclipse.aether.RepositorySystem; -import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.graph.Dependency; -import org.eclipse.aether.version.InvalidVersionSpecificationException; - -public class DashboardMain { - - public static final String TEST_NAME_LINKAGE_CHECK = "Linkage Errors"; - public static final String TEST_NAME_UPPER_BOUND = "Upper Bounds"; - public static final String TEST_NAME_GLOBAL_UPPER_BOUND = "Global Upper Bounds"; - public static final String TEST_NAME_DEPENDENCY_CONVERGENCE = "Dependency Convergence"; - public static final int LATEST_VERSIONS_COUNT = 10; - - private static final Configuration freemarkerConfiguration = configureFreemarker(); - - private static final DependencyGraphBuilder dependencyGraphBuilder = new DependencyGraphBuilder(); - private static final ClassPathBuilder classPathBuilder = - new ClassPathBuilder(dependencyGraphBuilder); - - /** - * Generates a code hygiene dashboard for a BOM. This tool takes a path to pom.xml of the BOM as - * an argument or Maven coordinates to a BOM. - * - *

Generated dashboard is at {@code target/$groupId/$artifactId/$version/index.html}, where - * each value is from BOM coordinates except {@code $version} is "snapshot" if the BOM has - * snapshot version. - */ - public static void main(String[] arguments) - throws IOException, TemplateException, RepositoryException, URISyntaxException, - ParseException, MavenRepositoryException { - DashboardArguments dashboardArguments = DashboardArguments.readCommandLine(arguments); - - if (dashboardArguments.hasVersionlessCoordinates()) { - generateLatestVersions( - dashboardArguments.getVersionlessCoordinates(), - dashboardArguments.getDependencyMediation()); - } else if (dashboardArguments.hasFile()) { - generate(dashboardArguments.getBomFile(), dashboardArguments.getDependencyMediation()); - } else { - generate(dashboardArguments.getBomCoordinates(), dashboardArguments.getDependencyMediation()); - } - } - - private static void generateLatestVersions( - String versionlessCoordinates, DependencyMediationAlgorithm dependencyMediationAlgorithm) - throws IOException, TemplateException, RepositoryException, URISyntaxException, - MavenRepositoryException { - List elements = Splitter.on(':').splitToList(versionlessCoordinates); - if (elements.size() != 2) { - System.err.println( - "Versionless coordinates should have one colon: " + versionlessCoordinates); - return; - } - String groupId = elements.get(0); - String artifactId = elements.get(1); - - RepositorySystem repositorySystem = RepositoryUtility.newRepositorySystem(); - // The highest version comes last. - ImmutableList versions = - RepositoryUtility.findVersions(repositorySystem, groupId, artifactId); - ImmutableList latestVersions = - versions.size() > LATEST_VERSIONS_COUNT ? versions.subList( - versions.size() - LATEST_VERSIONS_COUNT, - versions.size()) : versions; - for (String version : latestVersions) { - generate( - String.format("%s:%s:%s", groupId, artifactId, version), dependencyMediationAlgorithm); - } - generateVersionIndex(groupId, artifactId, versions); - } - - @VisibleForTesting - static Path generateVersionIndex(String groupId, String artifactId, List versions) - throws IOException, TemplateException, URISyntaxException { - Path directory = outputDirectory(groupId, artifactId, "snapshot").getParent(); - directory.toFile().mkdirs(); - Path page = directory.resolve("index.html"); - - Map templateData = new HashMap<>(); - templateData.put("versions", versions); - templateData.put("groupId", groupId); - templateData.put("artifactId", artifactId); - - File dashboardFile = page.toFile(); - try (Writer out = - new OutputStreamWriter(new FileOutputStream(dashboardFile), StandardCharsets.UTF_8)) { - Template dashboard = freemarkerConfiguration.getTemplate("/templates/version_index.ftl"); - dashboard.process(templateData, out); - } - - copyResource(directory, "css/dashboard.css"); - - return page; - } - - @VisibleForTesting - static Path generate( - String bomCoordinates, DependencyMediationAlgorithm dependencyMediationAlgorithm) - throws IOException, TemplateException, RepositoryException, URISyntaxException { - Path output = generate(Bom.readBom(bomCoordinates), dependencyMediationAlgorithm); - System.out.println("Wrote dashboard for " + bomCoordinates + " to " + output); - return output; - } - - @VisibleForTesting - static Path generate(Path bomFile, DependencyMediationAlgorithm dependencyMediationAlgorithm) - throws IOException, TemplateException, URISyntaxException, MavenRepositoryException, - InvalidVersionSpecificationException { - checkArgument(Files.isRegularFile(bomFile), "The input BOM %s is not a regular file", bomFile); - checkArgument(Files.isReadable(bomFile), "The input BOM %s is not readable", bomFile); - Path output = generate(Bom.readBom(bomFile), dependencyMediationAlgorithm); - System.out.println("Wrote dashboard for " + bomFile + " to " + output); - return output; - } - - private static Path generate(Bom bom, DependencyMediationAlgorithm dependencyMediationAlgorithm) - throws IOException, TemplateException, URISyntaxException, - InvalidVersionSpecificationException { - - ImmutableList managedDependencies = bom.getManagedDependencies(); - - DependencyMediation dependencyMediation = - dependencyMediationAlgorithm == DependencyMediationAlgorithm.MAVEN - ? DependencyMediation.MAVEN - : GradleDependencyMediation.withEnforcedPlatform(bom); - - ClassPathResult classPathResult = - classPathBuilder.resolve(managedDependencies, false, dependencyMediation); - ImmutableList classpath = classPathResult.getClassPath(); - - LinkageChecker linkageChecker = LinkageChecker.create(classpath); - - ImmutableSet linkageProblems = linkageChecker.findLinkageProblems(); - - ArtifactCache cache = loadArtifactInfo(managedDependencies); - Path output = generateHtml(bom, cache, classPathResult, linkageProblems); - - return output; - } - - private static Path outputDirectory(String groupId, String artifactId, String version) { - String versionPathElement = version.contains("-SNAPSHOT") ? "snapshot" : version; - return Paths.get("target", groupId, artifactId, versionPathElement); - } - - private static Path generateHtml( - Bom bom, - ArtifactCache cache, - ClassPathResult classPathResult, - ImmutableSet linkageProblems) - throws IOException, TemplateException, URISyntaxException { - - Artifact bomArtifact = new DefaultArtifact(bom.getCoordinates()); - - Path relativePath = - outputDirectory( - bomArtifact.getGroupId(), bomArtifact.getArtifactId(), bomArtifact.getVersion()); - Path output = Files.createDirectories(relativePath); - - copyResource(output, "css/dashboard.css"); - copyResource(output, "js/dashboard.js"); - - ImmutableMap> linkageProblemTable = - indexByJar(linkageProblems); - - List table = - generateReports( - freemarkerConfiguration, output, cache, linkageProblemTable, classPathResult, bom); - - generateDashboard( - freemarkerConfiguration, - output, - table, - cache.getGlobalDependencies(), - linkageProblemTable, - classPathResult, - bom); - - return output; - } - - private static void copyResource(Path output, String resourceName) - throws IOException, URISyntaxException { - ClassLoader classLoader = DashboardMain.class.getClassLoader(); - Path input = Paths.get(classLoader.getResource(resourceName).toURI()).toAbsolutePath(); - Path copy = output.resolve(input.getFileName()); - if (!Files.exists(copy)) { - Files.copy(input, copy); - } - } - - @VisibleForTesting - static Configuration configureFreemarker() { - Configuration configuration = new Configuration(new Version("2.3.28")); - configuration.setDefaultEncoding("UTF-8"); - configuration.setClassForTemplateLoading(DashboardMain.class, "/"); - return configuration; - } - - @VisibleForTesting - static List generateReports( - Configuration configuration, - Path output, - ArtifactCache cache, - ImmutableMap> linkageProblemTable, - ClassPathResult classPathResult, - Bom bom) - throws TemplateException { - - Map artifacts = cache.getInfoMap(); - List table = new ArrayList<>(); - for (Entry entry : artifacts.entrySet()) { - ArtifactInfo info = entry.getValue(); - try { - if (info.getException() != null) { - ArtifactResults unavailable = new ArtifactResults(entry.getKey()); - unavailable.setExceptionMessage(info.getException().getMessage()); - table.add(unavailable); - } else { - Artifact artifact = entry.getKey(); - ImmutableSet jarsInDependencyTree = - classPathResult.getClassPathEntries(Artifacts.toCoordinates(artifact)); - Map> relevantLinkageProblemTable = - Maps.filterKeys(linkageProblemTable, jarsInDependencyTree::contains); - - ArtifactResults results = - generateArtifactReport( - configuration, - output, - artifact, - entry.getValue(), - cache.getGlobalDependencies(), - ImmutableMap.copyOf(relevantLinkageProblemTable), - classPathResult, - bom); - table.add(results); - } - } catch (IOException ex) { - ArtifactResults unavailableTestResult = new ArtifactResults(entry.getKey()); - unavailableTestResult.setExceptionMessage(ex.getMessage()); - // Even when there's a problem generating test result, show the error in the dashboard - table.add(unavailableTestResult); - } - } - - return table; - } - - /** - * This is the only method that queries the Maven repository. - */ - private static ArtifactCache loadArtifactInfo(List artifacts) { - Map infoMap = new LinkedHashMap<>(); - List globalDependencies = new ArrayList<>(); - - for (Artifact artifact : artifacts) { - DependencyGraph completeDependencies = - dependencyGraphBuilder.buildVerboseDependencyGraph(artifact); - globalDependencies.add(completeDependencies); - - // picks versions according to Maven rules - DependencyGraph transitiveDependencies = - dependencyGraphBuilder.buildMavenDependencyGraph(new Dependency(artifact, "compile")); - - ArtifactInfo info = new ArtifactInfo(completeDependencies, transitiveDependencies); - infoMap.put(artifact, info); - } - - ArtifactCache cache = new ArtifactCache(); - cache.setInfoMap(infoMap); - cache.setGlobalDependencies(globalDependencies); - - return cache; - } - - private static ArtifactResults generateArtifactReport( - Configuration configuration, - Path output, - Artifact artifact, - ArtifactInfo artifactInfo, - List globalDependencies, - ImmutableMap> linkageProblemTable, - ClassPathResult classPathResult, - Bom bom) - throws IOException, TemplateException { - - String coordinates = Artifacts.toCoordinates(artifact); - File outputFile = output.resolve(coordinates.replace(':', '_') + ".html").toFile(); - - try (Writer out = new OutputStreamWriter( - new FileOutputStream(outputFile), StandardCharsets.UTF_8)) { - - // includes all versions - DependencyGraph graph = artifactInfo.getCompleteDependencies(); - List convergenceIssues = graph.findUpdates(); - - // picks versions according to Maven rules - DependencyGraph transitiveDependencies = artifactInfo.getTransitiveDependencies(); - - Map upperBoundFailures = - findUpperBoundsFailures(graph.getHighestVersionMap(), transitiveDependencies); - - Map globalUpperBoundFailures = findUpperBoundsFailures( - collectLatestVersions(globalDependencies), transitiveDependencies); - - long totalLinkageErrorCount = - linkageProblemTable.values().stream() - .flatMap(problemToClasses -> problemToClasses.stream().map(LinkageProblem::getSymbol)) - .distinct() // The dashboard counts linkage errors by the symbols - .count(); - - Template report = configuration.getTemplate("/templates/component.ftl"); - - Map templateData = new HashMap<>(); - - DefaultObjectWrapper wrapper = - new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_28).build(); - TemplateHashModel staticModels = wrapper.getStaticModels(); - templateData.put("linkageProblem", staticModels.get(LinkageProblem.class.getName())); - - templateData.put("artifact", artifact); - templateData.put("updates", convergenceIssues); - templateData.put("upperBoundFailures", upperBoundFailures); - templateData.put("globalUpperBoundFailures", globalUpperBoundFailures); - templateData.put("lastUpdated", LocalDateTime.now()); - templateData.put("dependencyGraph", graph); - templateData.put("linkageProblems", linkageProblemTable); - templateData.put("classPathResult", classPathResult); - templateData.put("totalLinkageErrorCount", totalLinkageErrorCount); - templateData.put("coordinates", bom.getCoordinates()); - - report.process(templateData, out); - - ArtifactResults results = new ArtifactResults(artifact); - results.addResult(TEST_NAME_UPPER_BOUND, upperBoundFailures.size()); - results.addResult(TEST_NAME_GLOBAL_UPPER_BOUND, globalUpperBoundFailures.size()); - results.addResult(TEST_NAME_DEPENDENCY_CONVERGENCE, convergenceIssues.size()); - results.addResult(TEST_NAME_LINKAGE_CHECK, (int) totalLinkageErrorCount); - - return results; - } - } - - private static Map findUpperBoundsFailures( - Map expectedVersionMap, - DependencyGraph transitiveDependencies) { - - Map actualVersionMap = transitiveDependencies.getHighestVersionMap(); - - VersionComparator comparator = new VersionComparator(); - - Map upperBoundFailures = new LinkedHashMap<>(); - - for (String id : expectedVersionMap.keySet()) { - String expectedVersion = expectedVersionMap.get(id); - String actualVersion = actualVersionMap.get(id); - // Check that the actual version is not null because it is - // possible for dependencies to appear or disappear from the tree - // depending on which version of another dependency is loaded. - // In both cases, no action is needed. - if (actualVersion != null && comparator.compare(actualVersion, expectedVersion) < 0) { - // Maven did not choose highest version - DefaultArtifact lower = new DefaultArtifact(id + ":" + actualVersion); - DefaultArtifact upper = new DefaultArtifact(id + ":" + expectedVersion); - upperBoundFailures.put(lower, upper); - } - } - return upperBoundFailures; - } - - /** - * Partitions {@code linkageProblems} by the JAR file that contains the {@link ClassFile}. - * - *

For example, {@code classes = result.get(JarX).get(linkageProblemY)} where {@code classes} - * are not null means that {@code JarX} has {@code linkageProblemY} and that {@code JarX} contains - * {@code classes} which reference {@code linkageProblemY.getSymbol()}. - */ - private static ImmutableMap> indexByJar( - ImmutableSet linkageProblems) { - - ImmutableMap> jarMap = - Multimaps.index(linkageProblems, problem -> problem.getSourceClass().getClassPathEntry()) - .asMap(); - - return ImmutableMap.copyOf(Maps.transformValues(jarMap, ImmutableSet::copyOf)); - } - - @VisibleForTesting - static void generateDashboard( - Configuration configuration, - Path output, - List table, - List globalDependencies, - ImmutableMap> linkageProblemTable, - ClassPathResult classPathResult, - Bom bom) - throws IOException, TemplateException { - - Map latestArtifacts = collectLatestVersions(globalDependencies); - - Map templateData = new HashMap<>(); - templateData.put("table", table); - templateData.put("lastUpdated", LocalDateTime.now()); - templateData.put("latestArtifacts", latestArtifacts); - templateData.put("linkageProblems", linkageProblemTable); - templateData.put("classPathResult", classPathResult); - templateData.put("dependencyPathRootCauses", findRootCauses(classPathResult)); - templateData.put("coordinates", bom.getCoordinates()); - templateData.put("dependencyGraphs", globalDependencies); - - // Accessing static methods from Freemarker template - // https://freemarker.apache.org/docs/pgui_misc_beanwrapper.html#autoid_60 - DefaultObjectWrapper wrapper = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_28) - .build(); - TemplateHashModel staticModels = wrapper.getStaticModels(); - templateData.put("dashboardMain", staticModels.get(DashboardMain.class.getName())); - templateData.put("pieChart", staticModels.get(PieChart.class.getName())); - templateData.put("linkageProblem", staticModels.get(LinkageProblem.class.getName())); - - File dashboardFile = output.resolve("index.html").toFile(); - try (Writer out = new OutputStreamWriter( - new FileOutputStream(dashboardFile), StandardCharsets.UTF_8)) { - Template dashboard = configuration.getTemplate("/templates/index.ftl"); - dashboard.process(templateData, out); - } - - File detailsFile = output.resolve("artifact_details.html").toFile(); - try (Writer out = new OutputStreamWriter( - new FileOutputStream(detailsFile), StandardCharsets.UTF_8)) { - Template details = configuration.getTemplate("/templates/artifact_details.ftl"); - details.process(templateData, out); - } - - File unstable = output.resolve("unstable_artifacts.html").toFile(); - try (Writer out = new OutputStreamWriter( - new FileOutputStream(unstable), StandardCharsets.UTF_8)) { - Template details = configuration.getTemplate("/templates/unstable_artifacts.ftl"); - details.process(templateData, out); - } - - File dependencyTrees = output.resolve("dependency_trees.html").toFile(); - try (Writer out = - new OutputStreamWriter(new FileOutputStream(dependencyTrees), StandardCharsets.UTF_8)) { - Template details = configuration.getTemplate("/templates/dependency_trees.ftl"); - details.process(templateData, out); - } - } - - private static Map collectLatestVersions( - List globalDependencies) { - Map latestArtifacts = new TreeMap<>(); - VersionComparator comparator = new VersionComparator(); - - if (globalDependencies != null) { - for (DependencyGraph graph : globalDependencies) { - Map map = graph.getHighestVersionMap(); - for (String key : map.keySet()) { - String newVersion = map.get(key); - String oldVersion = latestArtifacts.get(key); - if (oldVersion == null || comparator.compare(newVersion, oldVersion) > 0) { - latestArtifacts.put(key, map.get(key)); - } - } - } - } - return latestArtifacts; - } - - /** - * Returns the number of rows in {@code table} that show unavailable ({@code null} result) or some - * failures for {@code columnName}. - */ - public static long countFailures(List table, String columnName) { - return table.stream() - .filter(row -> row.getResult(columnName) == null || row.getFailureCount(columnName) > 0) - .count(); - } - - private static final int MINIMUM_NUMBER_DEPENDENCY_PATHS = 5; - - /** - * Returns mapping from jar files to summaries of the root problem in their {@link - * DependencyPath}s. The summary explains common patterns ({@code groupId:artifactId}) in the path - * elements. The returned map does not have a key for a jar file when it has fewer than {@link - * #MINIMUM_NUMBER_DEPENDENCY_PATHS} dependency paths or a common pattern is not found among the - * elements in the paths. - * - *

Example summary: "Artifacts 'com.google.http-client:google-http-client > - * commons-logging:commons-logging > log4j:log4j' exist in all 994 dependency paths. Example - * path: com.google.cloud:google-cloud-core:1.59.0 ..." - * - *

Using this summary in the BOM dashboard avoids repetitive items in the {@link - * DependencyPath} list that share the same root problem caused by widely-used libraries, for - * example, {@code commons-logging:commons-logging}, {@code com.google.http-client:google-http-client} - * and {@code log4j:log4j}. - */ - private static ImmutableMap findRootCauses(ClassPathResult classPathResult) { - // Freemarker is not good at handling non-string keys. Path object in .ftl is automatically - // converted to String. https://freemarker.apache.org/docs/app_faq.html#faq_nonstring_keys - ImmutableMap.Builder builder = ImmutableMap.builder(); - - for (ClassPathEntry entry : classPathResult.getClassPath()) { - List dependencyPaths = classPathResult.getDependencyPaths(entry); - - ImmutableList commonVersionlessArtifacts = - commonVersionlessArtifacts(dependencyPaths); - - if (dependencyPaths.size() > MINIMUM_NUMBER_DEPENDENCY_PATHS - && commonVersionlessArtifacts.size() > 1) { // The last paths elements are always same - builder.put( - entry.toString(), - summaryMessage( - dependencyPaths.size(), commonVersionlessArtifacts, dependencyPaths.get(0))); - } - } - return builder.build(); - } - - private static ImmutableList commonVersionlessArtifacts( - List dependencyPaths) { - ImmutableList initialVersionlessCoordinates = - dependencyPaths.get(0).getArtifactKeys(); - // LinkedHashSet remembers insertion order - LinkedHashSet versionlessCoordinatesIntersection = - Sets.newLinkedHashSet(initialVersionlessCoordinates); - for (DependencyPath dependencyPath : dependencyPaths) { - // List of versionless coordinates ("groupId:artifactId") - ImmutableList versionlessCoordinatesInPath = dependencyPath.getArtifactKeys(); - // intersection of elements in DependencyPaths - versionlessCoordinatesIntersection.retainAll(versionlessCoordinatesInPath); - } - - return ImmutableList.copyOf(versionlessCoordinatesIntersection); - } - - private static String summaryMessage( - int dependencyPathCount, List coordinates, DependencyPath examplePath) { - StringBuilder messageBuilder = new StringBuilder(); - messageBuilder.append("Dependency path '"); - messageBuilder.append(Joiner.on(" > ").join(coordinates)); - messageBuilder.append("' exists in all " + dependencyPathCount + " dependency paths. "); - messageBuilder.append("Example path: "); - messageBuilder.append(examplePath); - return messageBuilder.toString(); - } -} diff --git a/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/PieChart.java b/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/PieChart.java deleted file mode 100644 index 8b4ecd75f7..0000000000 --- a/dashboard/src/main/java/com/google/cloud/tools/opensource/dashboard/PieChart.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2019 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.cloud.tools.opensource.dashboard; - -import java.awt.geom.Point2D; - -public class PieChart { - - /** - * Calculate SVG arc end for a pie piece. Assumes the piece starts at the top of the circle. - * - * Do not forget: - * - * 1. SVG origin starts at top left. - * 2. x increases to the right and y increases **down**. - */ - static Point2D calculateEndPoint(double radius, double centerX, double centerY, double ratio) { - if (ratio > 1) { - ratio = 1.0; - } - - double radians = ratio * 2 * Math.PI; - - // Since we're starting at the top of the circle this is rotated 90 degrees - // from the normal coordinates. This is why we use sine for x and cosine for y. - double x = radius * (1 + Math.sin(radians)); - double y = radius * (1 - Math.cos(radians)); - return new Point2D.Double(x + centerX - radius, y + centerY - radius); - } - - // so I can avoid teaching FreeMarker how to wrap a java.awt.Point - public static double calculateEndPointX( - double radius, double centerX, double centerY, double ratio) { - return calculateEndPoint(radius, centerX, centerY, ratio).getX(); - } - - public static double calculateEndPointY( - double radius, double centerX, double centerY, double ratio) { - return calculateEndPoint(radius, centerX, centerY, ratio).getY(); - } -} diff --git a/dashboard/src/main/resources/css/dashboard.css b/dashboard/src/main/resources/css/dashboard.css deleted file mode 100644 index 9997f125cc..0000000000 --- a/dashboard/src/main/resources/css/dashboard.css +++ /dev/null @@ -1,147 +0,0 @@ -body { - font-family: "Poppins", sans-serif; - font-weight: 400; - line-height: 1.625; - margin-left: 2em; -} - -h1, -h2, -h3 { - color: #333333; - font-weight: 700; - margin: 0; - line-height: 1.2; - margin-top: 1ex; - margin-bottom: 1ex; -} - -h1 { - font-size: 3em; -} - -h2 { - font-size: 2.5em; -} - -h3 { - font-size: 2em; -} - -th, td { - padding: 5pt; -} - -pre { - line-height: normal; -} - -.pass { - background-color: lightgreen; - font-weight: bold; -} - -.fail { - background-color: pink; - font-weight: bold; -} - -.unavailable { - background-color: gray; - font-weight: bold; -} - -p.dependency-tree-node { - margin-top: 0; - margin-bottom: 0; -} - -.linkage-check-dependency-paths, .jar-linkage-report { - margin-left: 1em; -} - -p.jar-linkage-report-cause { - margin-bottom: 0; - margin-left: 2em; -} - -ul.jar-linkage-report-cause { - margin-top: 0; - margin-left: 3em; -} - -ul.jar-linkage-report-cause > li { - font: 1em 'Droid Sans Mono', monospace; -} - - /* ----- Statistic ----- */ -.statistics { - padding-top: 50px; -} - -.container { - min-height: 20ex; - display: flex; - flex-wrap: wrap; - align-items: flex-start; -} - -.statistic-item { - flex: 0 0 16em; - padding: 1.6em 2.5em; - position: relative; - min-height: 180px; - overflow: hidden; - margin-bottom: 40px; - border: none; - border-radius: 3px; - box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.03); - margin: 1em; - margin-left: unset; - margin-right: 2em; -} - -.statistic-item .desc { - font-size: 1.15em; - text-transform: uppercase; - font-weight: 300; - color: rgba(255, 255, 255, 0.6); -} - -.statistic-item h2 { - font-size: 2.3em; - font-weight: 300; - color: #fff; -} - -.statistic-item-green { - background: #00b26f; -} - -.statistic-item-orange { - background: #ff8300; -} - -.statistic-item-blue { - background: #00b5e9; -} - -.statistic-item-red { - background: #fa4251; -} - -.statistic-item-yellow { - background: #f1c40f; -} - -#piecharts th { - vertical-align: top; -} - -#piecharts td { - vertical-align: top; -} - -.pie { - "text-align: center" -} diff --git a/dashboard/src/main/resources/js/dashboard.js b/dashboard/src/main/resources/js/dashboard.js deleted file mode 100644 index 2a2128c45a..0000000000 --- a/dashboard/src/main/resources/js/dashboard.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2019 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. - */ - -/** - * Toggles the visibility of an HTML element below the button. - * @param button clicked button element - */ -function toggleNextSiblingVisibility(button) { - const nextSibling = button.parentElement.nextElementSibling; - const currentVisibility = nextSibling.style.display !== "none"; - const nextVisibility = !currentVisibility; - nextSibling.style.display = nextVisibility ? "" : "none"; - button.innerText = nextVisibility ? "▼" : "▶"; -} diff --git a/dashboard/src/main/resources/poms/demo.xml b/dashboard/src/main/resources/poms/demo.xml deleted file mode 100644 index 7167c0415a..0000000000 --- a/dashboard/src/main/resources/poms/demo.xml +++ /dev/null @@ -1,49 +0,0 @@ - - 4.0.0 - - com.google.cloud - demo - 1.0-SNAPSHOT - jar - - demo-pom - - - UTF-8 - 1.8 - - - - - ${dependencyGroupId} - ${dependencyArtifactId} - ${dependencyVersion} - test - - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.0.0-M1 - - - enforce - - enforce - - - - - - - - - - - - \ No newline at end of file diff --git a/dashboard/src/main/resources/templates/artifact_details.ftl b/dashboard/src/main/resources/templates/artifact_details.ftl deleted file mode 100644 index 75acdcd51b..0000000000 --- a/dashboard/src/main/resources/templates/artifact_details.ftl +++ /dev/null @@ -1,55 +0,0 @@ - - - <#include "macros.ftl"> - - - Google Cloud Platform Java Open Source Dependency Dashboard Artifact Details Table - - - - -

Google Cloud Platform Java Dependency Dashboard Artifact Details

-

BOM: ${coordinates?html}

-

Dependency Details

- - - - - - - - - - <#list table as row> - <#assign report_url = row.getCoordinates()?replace(":", "_") + '.html' /> - - - <#-- The name key should match TEST_NAME_XXXX variables --> - <@testResult row=row name="Linkage Errors"/> - <@testResult row=row name="Upper Bounds"/> - <@testResult row=row name="Global Upper Bounds"/> - <@testResult row=row name="Dependency Convergence"/> - - -
Artifact - Linkage Check - Upper Bounds - Global Upper Bounds - Dependency Convergence
${row.getCoordinates()}
- -
- -

Linkage Errors

- - <#list linkageProblems as jar, problems> - <@formatJarLinkageReport jar problems classPathResult dependencyPathRootCauses/> - - -
-

Last generated at ${lastUpdated}

- - \ No newline at end of file diff --git a/dashboard/src/main/resources/templates/component.ftl b/dashboard/src/main/resources/templates/component.ftl deleted file mode 100644 index afdc26cc09..0000000000 --- a/dashboard/src/main/resources/templates/component.ftl +++ /dev/null @@ -1,156 +0,0 @@ - - - <#include "macros.ftl"> - <#assign groupId = artifact.getGroupId()> - <#assign artifactId = artifact.getArtifactId()> - <#assign version = artifact.getVersion()> - - - Google Cloud Platform Dependency Analysis Report for ${groupId}:${artifactId}:${version} - - - - - -

Dependency Analysis of ${groupId}:${artifactId}:${version}

-

BOM: ${coordinates?html}

-

Global Upper Bounds Check

- -

For each transitive dependency the library pulls in, the highest version - found anywhere in the union of the BOM's dependency trees is picked.

- - <#if globalUpperBoundFailures?size gt 0> -

Global Upper Bounds Fixes

- -

Suggested updates to bring this artifact into sync with the highest versions - of its dependencies used by any artifact in the BOM:

- -
    - <#list globalUpperBoundFailures as lower, upper> - <#if lower.getGroupId() == groupId && lower.getArtifactId() == artifactId - && lower.getVersion() == version ><#-- Not checking 'file' attribute of Artifact --> - <#-- When this is upgrading a BOM member --> -
  • - Upgrade ${lower} in the BOM to version "${upper.getVersion()}": - -

    Update the version of managed dependency element - ${groupId}:${artifactId} in the BOM:

    - -
    <dependencyManagement>
    -  <dependencies>
    -    ...
    -    <dependency>
    -      <groupId>${upper.getGroupId()}</groupId>
    -      <artifactId>${upper.getArtifactId()}</artifactId>
    -      <version>${upper.getVersion()}</version>
    -    </dependency>
    - -
  • - <#else > -
  • - Upgrade ${lower} to version "${upper.getVersion()}": - -

    Add this dependency element to the pom.xml for ${groupId}:${artifactId}:${version}: -

    - -
    <dependency>
    -<groupId>${upper.getGroupId()}</groupId>
    -<artifactId>${upper.getArtifactId()}</artifactId>
    -<version>${upper.getVersion()}</version>
    -</dependency>
    - -

    If the pom.xml for ${groupId}:${artifactId}:${version} already includes this - dependency, update the version of the existing dependency element. - Otherwise add a new dependency element to the - dependencyManagement section.

    -
  • - - -
- - <#else> -

- ${groupId}:${artifactId}:${version} selects the highest version of all dependencies. -

- - - -

Local Upper Bounds Check

- - -

For each transitive dependency the library pulls in, the highest version - found anywhere in the dependency tree is picked.

- - <#if upperBoundFailures?size gt 0> -

Upper Bounds Fixes

- -

Suggested updates to bring this artifact into sync with the highest versions - of each dependency found in its own dependency tree:

- -
    - <#list upperBoundFailures as lower, upper> -
  • Upgrade ${lower} to ${upper}: - -

    Add this dependency element to the pom.xml for ${groupId}:${artifactId}:${version}:

    - -
    <dependency>
    -  <groupId>${upper.getGroupId()}</groupId>
    -  <artifactId>${upper.getArtifactId()}</artifactId>
    -  <version>${upper.getVersion()}</version>
    -</dependency>
    - -
  • - -
- - <#else> -

- ${groupId}:${artifactId}:${version} selects the highest version of all dependencies. -

- - -

Dependency Convergence

- -

There is exactly one version of each dependency in the library's transitive dependency tree. - That is, two artifacts with the same group ID and artifact ID but different versions - do not appear in the tree. No dependency mediation is necessary.

- - <#if updates?size gt 0> -

Suggested Dependency Updates

- -

Caution: The algorithm for suggesting updates is imperfect. - They are not ordered by importance, and one change - may render another moot.

- -

Suggested updates to bring this artifact and its dependencies - into sync with the highest versions - of each dependency found in its own dependency tree:

- -
    - <#list updates as update> -
  • ${update}
  • - -
- <#else> -

${groupId}:${artifactId}:${version} Converges

- - - -

Linkage Check

- -

${totalLinkageErrorCount} linkage error(s)

- <#list linkageProblems as jar, problems> - <@formatJarLinkageReport jar problems classPathResult {} /> - - -

Dependencies

- - <#if dependencyGraph?? > - <@formatDependencyGraph dependencyGraph dependencyGraph.getRootPath() "" /> - <#else> -

Dependency information is unavailable

- -
-

Last generated at ${lastUpdated}

- - \ No newline at end of file diff --git a/dashboard/src/main/resources/templates/dependency_trees.ftl b/dashboard/src/main/resources/templates/dependency_trees.ftl deleted file mode 100644 index 6641141c2c..0000000000 --- a/dashboard/src/main/resources/templates/dependency_trees.ftl +++ /dev/null @@ -1,22 +0,0 @@ - - - <#include "macros.ftl"> - - - Google Cloud Platform Java Open Source Dependency Dashboard: Dependency Trees - - - - -

Dependency Tree of the Artifacts in ${coordinates}

-

BOM: ${coordinates?html}

- <#list dependencyGraphs as graph> - <#assign rootPath = graph.getRootPath() /> -

Dependency Tree of ${rootPath.getLeaf()?html}

- <@formatDependencyGraph graph rootPath "" /> - - -
-

Last generated at ${lastUpdated}

- - \ No newline at end of file diff --git a/dashboard/src/main/resources/templates/index.ftl b/dashboard/src/main/resources/templates/index.ftl deleted file mode 100644 index 36d5d1dade..0000000000 --- a/dashboard/src/main/resources/templates/index.ftl +++ /dev/null @@ -1,142 +0,0 @@ - - - <#include "macros.ftl"> - - - Google Cloud Platform Java Open Source Dependency Dashboard - - - - -

${coordinates} Dependency Status

-
- <#assign totalArtifacts = table?size> - -
-
-
-

${table?size}

- Total Artifacts Checked -
-
- <#assign linkageErrorCount = dashboardMain.countFailures(table, "Linkage Errors")> -

${linkageErrorCount}

- ${(linkageErrorCount == 1)?then("Has", "Have")} Linkage Errors -
-
- <#assign localUpperBoundsErrorCount = dashboardMain.countFailures(table, "Upper Bounds")> -

${dashboardMain.countFailures(table, "Upper Bounds")}

- ${(localUpperBoundsErrorCount == 1)?then("Has", "Have")} Upper Bounds Errors -
-
- <#assign globalUpperBoundsErrorCount = dashboardMain.countFailures(table, "Global Upper Bounds")> -

${dashboardMain.countFailures(table, "Global Upper Bounds")}

- ${(globalUpperBoundsErrorCount == 1)?then("Has", "Have")} Global Upper Bounds Errors -
-
- <#assign convergenceErrorCount = dashboardMain.countFailures(table, "Dependency Convergence")> -

${dashboardMain.countFailures(table, "Dependency Convergence")}

- ${(convergenceErrorCount == 1)?then("Fails", "Fail")} to Converge -
-
-
- - <#assign pieSize = 300 > -
- - - - - - - - - - - - - - - - - - - - - - - -
- Linkage Errors - Local Upper Bounds - Global Upper Bounds - Dependency Convergence
${linkageErrorCount} out of ${totalArtifacts} artifacts - ${plural(linkageErrorCount, "has", "have")} linkage errors. - ${localUpperBoundsErrorCount} out of ${totalArtifacts} artifacts - ${plural(localUpperBoundsErrorCount, "does not", "do not")} pick the - latest versions of all artifacts in their own dependency tree. - ${globalUpperBoundsErrorCount} out of ${totalArtifacts} artifacts - ${plural(globalUpperBoundsErrorCount, "does not", "do not")} select the - most recent version of all artifacts in the BOM.${convergenceErrorCount} out of ${totalArtifacts} artifacts - ${plural(convergenceErrorCount, "fails", "fail")} to converge. -
- <@pieChartSvg - description="${linkageErrorCount} out of ${totalArtifacts} artifacts have linkage - errors." - ratio=linkageErrorCount / totalArtifacts /> - - <#assign doesNot=plural(localUpperBoundsErrorCount, "does not", "do not")> - <@pieChartSvg - description="${localUpperBoundsErrorCount} out of ${totalArtifacts} artifacts - $doesNot pick the - latest versions of all artifacts in their own dependency tree." - ratio=localUpperBoundsErrorCount / totalArtifacts /> - - <@pieChartSvg - description="${globalUpperBoundsErrorCount} out of ${totalArtifacts} artifacts have - global upper bounds errors." - ratio=globalUpperBoundsErrorCount / totalArtifacts /> - - <#assign fails=plural(convergenceErrorCount, "fails", "fail")/> - <@pieChartSvg - description="${convergenceErrorCount} out of ${totalArtifacts} artifacts - ${fails} to converge." - ratio=convergenceErrorCount / totalArtifacts /> -
-
- -

- BOM source code -

- -

- Detailed Artifact Reports -

- -

- Pre 1.0 Artifacts -

- -

Recommended Versions

- -

These are the most recent versions of dependencies used by any of the covered artifacts.

- - - -

- Dependency Trees -

- -
- -

Last generated at ${lastUpdated}

- - \ No newline at end of file diff --git a/dashboard/src/main/resources/templates/macros.ftl b/dashboard/src/main/resources/templates/macros.ftl deleted file mode 100644 index 3d160cdcee..0000000000 --- a/dashboard/src/main/resources/templates/macros.ftl +++ /dev/null @@ -1,142 +0,0 @@ -<#function pluralize number singularNoun pluralNoun> - <#local plural = number gt 1 /> - <#return number + " " + plural?string(pluralNoun, singularNoun)> - -<#-- same as above but without the number --> -<#function plural number singularNoun pluralNoun> - <#local plural = number gt 1 /> - <#return plural?string(pluralNoun, singularNoun)> - - -<#macro formatJarLinkageReport classPathEntry linkageProblems classPathResult - dependencyPathRootCauses> - <#-- problemsToClasses: ImmutableMap> to get key and set of - values in Freemarker --> - <#assign problemsToClasses = linkageProblem.groupBySymbolProblem(linkageProblems) /> - <#assign symbolProblemCount = problemsToClasses?size /> - <#assign referenceCount = 0 /> - <#list problemsToClasses?values as classes> - <#assign referenceCount += classes?size /> - - -

${classPathEntry?html}

-

- ${pluralize(symbolProblemCount, "target class", "target classes")} - causing linkage errors referenced from - ${pluralize(referenceCount, "source class", "source classes")}. -

- <#list problemsToClasses as problem, sourceClasses> - <#if sourceClasses?size == 1> - <#assign sourceClass = sourceClasses[0] /> -

${problem?html}, referenced from ${sourceClass?html}

- <#else> -

${problem?html}, referenced from ${ - pluralize(sourceClasses?size, "class", "classes")?html} - -

- - - - - <#assign jarsInProblem = {} > - <#list linkageProblems as problem> - <#if (problem.getTargetClass())?? > - <#assign targetClassPathEntry = problem.getTargetClass().getClassPathEntry() /> - <#-- Freemarker's hash requires its keys to be strings. - https://freemarker.apache.org/docs/app_faq.html#faq_nonstring_keys --> - <#assign jarsInProblem = jarsInProblem + { targetClassPathEntry.toString() : targetClassPathEntry } > - - - <#list jarsInProblem?values as jarInProblem> - <@showDependencyPath dependencyPathRootCauses classPathResult jarInProblem /> - - <#if !jarsInProblem?values?seq_contains(classPathEntry) > - <@showDependencyPath dependencyPathRootCauses classPathResult classPathEntry /> - - - - -<#macro showDependencyPath dependencyPathRootCauses classPathResult classPathEntry> - <#assign dependencyPaths = classPathResult.getDependencyPaths(classPathEntry) /> - <#assign hasRootCause = dependencyPathRootCauses[classPathEntry]?? /> - <#assign hideDependencyPathsByDefault = (!hasRootCause) && (dependencyPaths?size > 5) /> -

- The following ${plural(dependencyPaths?size, "path contains", "paths contain")} ${classPathEntry?html}: - <#if hideDependencyPathsByDefault> - <#-- The dependency paths are not summarized --> - - -

- - <#if hasRootCause> -

${dependencyPathRootCauses[classPathEntry]?html} -

- <#else> - -
    - <#list dependencyPaths as dependencyPath > -
  • ${dependencyPath}
  • - -
- - - -<#macro formatDependencyGraph graph node parent> - <#if node == graph.getRootPath() > - <#assign label = 'root' /> - <#else> - <#assign label = 'parent: ' + parent.getLeaf() /> - -

${node.getLeaf()}

-
    - <#list graph.getChildren(node) as childNode> - <#if node != childNode> -
  • - <@formatDependencyGraph graph childNode node /> -
  • - - -
- - -<#macro testResult row name> - <#if row.getResult(name)?? ><#-- checking isNotNull() --> - <#-- When it's not null, the test ran. It's either PASS or FAIL --> - <#assign test_label = row.getResult(name)?then('PASS', 'FAIL')> - <#assign failure_count = row.getFailureCount(name)> - <#else> - <#-- Null means there's an exception and test couldn't run --> - <#assign test_label = "UNAVAILABLE"> - - - <#if row.getResult(name)?? > - <#assign page_anchor = name?replace(" ", "-")?lower_case /> - - <#if failure_count gt 0>${pluralize(failure_count, "FAILURE", "FAILURES")} - <#else>PASS - - - <#else>UNAVAILABLE - - - - -<#macro pieChartSvg description ratio> - <#assign largeArcFlag = (ratio gt 0.5)?string("1", "0")> - <#assign endPointX = pieChart.calculateEndPointX(100, 100, 100, ratio)> - <#assign endPointY = pieChart.calculateEndPointY(100, 100, 100, ratio)> - - ${description} - - - - \ No newline at end of file diff --git a/dashboard/src/main/resources/templates/unstable_artifacts.ftl b/dashboard/src/main/resources/templates/unstable_artifacts.ftl deleted file mode 100644 index e02e56204b..0000000000 --- a/dashboard/src/main/resources/templates/unstable_artifacts.ftl +++ /dev/null @@ -1,40 +0,0 @@ - - - <#include "macros.ftl"> - - - Google Cloud Platform Java Open Source Dependency Dashboard: Unstable Artifacts - - - - -

Dependency Status of ${coordinates}

-

BOM: ${coordinates?html}

-

Pre 1.0 Versions

- -

- These are dependencies found in the GCP orbit that have not yet reached 1.0. - No 1.0 or later library should depend on them. - If the libraries are stable, advance them to 1.0. - Otherwise replace the dependency with something else. -

- - <#assign unstableCount = 0> -
    - <#list latestArtifacts as artifact, version> - <#if version[0] == '0'> - <#assign unstableCount++> -
  • ${artifact}:${version}
  • - - -
- - <#if unstableCount == 0> -

All versions are 1.0 or later.

- - - -
-

Last generated at ${lastUpdated}

- - \ No newline at end of file diff --git a/dashboard/src/main/resources/templates/version_index.ftl b/dashboard/src/main/resources/templates/version_index.ftl deleted file mode 100644 index 602bdf633a..0000000000 --- a/dashboard/src/main/resources/templates/version_index.ftl +++ /dev/null @@ -1,20 +0,0 @@ - - - - - ${groupId}:${artifactId} - - - -

${groupId}:${artifactId}

- -
    - <#list versions as version> -
  • - ${version} -
  • - -
- - - diff --git a/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/ArtifactResultsTest.java b/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/ArtifactResultsTest.java deleted file mode 100644 index bcfab9e49c..0000000000 --- a/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/ArtifactResultsTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2018 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.cloud.tools.opensource.dashboard; - -import org.eclipse.aether.artifact.DefaultArtifact; -import org.junit.Assert; -import org.junit.Test; - -public class ArtifactResultsTest { - - private ArtifactResults results = - new ArtifactResults(new DefaultArtifact("com.google.guava:guava:23.0")); - - @Test - public void testAddResult() { - results.addResult("foo", 0); - results.addResult("bar", 10); - Assert.assertTrue(results.getResult("foo")); - Assert.assertFalse(results.getResult("bar")); - Assert.assertNull(results.getResult("baz")); - } - - @Test - public void testGetFailureCount() { - results.addResult("foo", 0); - results.addResult("bar", 10); - Assert.assertEquals(0, results.getFailureCount("foo")); - Assert.assertEquals(10, results.getFailureCount("bar")); - Assert.assertEquals(0, results.getFailureCount("baz")); - } - - @Test - public void testGetCoordinates() { - Assert.assertEquals("com.google.guava:guava:23.0", results.getCoordinates()); - } - -} diff --git a/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/DashboardArgumentsTest.java b/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/DashboardArgumentsTest.java deleted file mode 100644 index 62204552b6..0000000000 --- a/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/DashboardArgumentsTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2019 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.cloud.tools.opensource.dashboard; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import com.google.cloud.tools.opensource.dashboard.DashboardArguments.DependencyMediationAlgorithm; -import com.google.common.truth.Truth; -import java.nio.file.Paths; -import org.apache.commons.cli.AlreadySelectedException; -import org.apache.commons.cli.MissingOptionException; -import org.apache.commons.cli.ParseException; -import org.junit.Assert; -import org.junit.Test; - -public class DashboardArgumentsTest { - - @Test - public void testParseArgument_file() throws ParseException { - DashboardArguments dashboardArguments = DashboardArguments.readCommandLine("-f", "../pom.xml"); - - assertTrue(dashboardArguments.hasFile()); - assertEquals(dashboardArguments.getDependencyMediation(), DependencyMediationAlgorithm.MAVEN); - assertEquals(Paths.get("../pom.xml").toAbsolutePath(), dashboardArguments.getBomFile()); - assertNull(dashboardArguments.getBomCoordinates()); - } - - @Test - public void testParseArgument_coordinates() throws ParseException { - DashboardArguments dashboardArguments = - DashboardArguments.readCommandLine("-c", "com.google.cloud:libraries-bom:1.0.0"); - assertFalse(dashboardArguments.hasFile()); - assertEquals( - "com.google.cloud:libraries-bom:1.0.0", dashboardArguments.getBomCoordinates()); - } - - @Test - public void testParseArgument_coordinatesWithLeadingSpace() throws ParseException { - // Maven exec plugin adds a leading space when arguments are passed as - // -Dexec.arguments="-c com.google.cloud:libraries-bom:$VERSION" - DashboardArguments dashboardArguments = - DashboardArguments.readCommandLine("-c", " com.google.cloud:libraries-bom:1.0.0"); - assertEquals( - "com.google.cloud:libraries-bom:1.0.0", dashboardArguments.getBomCoordinates()); - assertNull(dashboardArguments.getBomFile()); - } - - @Test - public void testParseArgument_missingInput() throws ParseException { - try { - DashboardArguments.readCommandLine(); - Assert.fail("The argument should validate missing input"); - } catch (MissingOptionException ex) { - // pass - } - } - - @Test - public void testParseArgument_duplicateOptions_coordinates_file() throws ParseException { - try { - DashboardArguments.readCommandLine( - "-c", "com.google.cloud:libraries-bom:1.0.0", "-f", "../pom.xml"); - Assert.fail("The argument should validate duplicate input"); - } catch (AlreadySelectedException ex) { - // pass - } - } - - @Test - public void testParseArgument_duplicateOptions_coordinates_allVersions() throws ParseException { - try { - DashboardArguments.readCommandLine( - "-c", "com.google.cloud:libraries-bom:1.0.0", "-a", "com.google.cloud:libraries-bom"); - Assert.fail("The argument should validate duplicate input"); - } catch (AlreadySelectedException ex) { - // pass - } - } - - @Test - public void testParseArgument_dependencyMediation_valid() throws ParseException { - DashboardArguments dashboardArguments = - DashboardArguments.readCommandLine("-f", "../pom.xml", "-m", "gradle"); - - assertEquals(dashboardArguments.getDependencyMediation(), DependencyMediationAlgorithm.GRADLE); - } - - @Test - public void testParseArgument_dependencyMediation_invalid() { - try { - DashboardArguments dashboardArguments = - DashboardArguments.readCommandLine("-f", "../pom.xml", "-m", "ant"); - Assert.fail("The argument should validate invalid dependency mediation value"); - } catch (ParseException ex) { - // pass - Truth.assertThat(ex.getMessage()).isEqualTo("Valid values for '-m' are [maven, gradle]"); - } - } -} diff --git a/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/DashboardTest.java b/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/DashboardTest.java deleted file mode 100644 index ffeba49324..0000000000 --- a/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/DashboardTest.java +++ /dev/null @@ -1,491 +0,0 @@ -/* - * Copyright 2018 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.cloud.tools.opensource.dashboard; - -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; - -import com.google.cloud.tools.opensource.dashboard.DashboardArguments.DependencyMediationAlgorithm; -import com.google.cloud.tools.opensource.dependencies.Artifacts; -import com.google.cloud.tools.opensource.dependencies.Bom; -import com.google.common.base.CharMatcher; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Streams; -import com.google.common.io.MoreFiles; -import com.google.common.io.RecursiveDeleteOption; -import com.google.common.truth.Correspondence; -import java.io.IOException; -import java.io.InputStream; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import nu.xom.Builder; -import nu.xom.Document; -import nu.xom.Element; -import nu.xom.Node; -import nu.xom.Nodes; -import nu.xom.ParsingException; -import nu.xom.XPathContext; -import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.resolution.ArtifactDescriptorException; -import org.hamcrest.core.StringStartsWith; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Assume; -import org.junit.BeforeClass; -import org.junit.Test; - -public class DashboardTest { - - private static final Correspondence NODE_VALUES = - Correspondence.transforming(node -> trimAndCollapseWhiteSpace(node.getValue()), "has value"); - - private static String trimAndCollapseWhiteSpace(String value) { - return CharMatcher.whitespace().trimAndCollapseFrom(value, ' '); - } - - private static Path outputDirectory; - private static Builder builder = new Builder(); - private static Document dashboard; - private static Document details; - private static Document unstable; - - @BeforeClass - public static void setUp() throws IOException, ParsingException { - // Creates "index.html" and artifact reports in outputDirectory - try { - outputDirectory = - DashboardMain.generate( - "com.google.cloud:libraries-bom:1.0.0", DependencyMediationAlgorithm.MAVEN); - } catch (Throwable t) { - t.printStackTrace(); - Assert.fail("Could not generate dashboard"); - } - - dashboard = parseOutputFile("index.html"); - details = parseOutputFile("artifact_details.html"); - unstable = parseOutputFile("unstable_artifacts.html"); - } - - @AfterClass - public static void cleanUp() { - try { - // Mac's APFS fails with InsecureRecursiveDeleteException without ALLOW_INSECURE. - // Still safe as this test does not use symbolic links - if (outputDirectory != null) { - MoreFiles.deleteRecursively(outputDirectory, RecursiveDeleteOption.ALLOW_INSECURE); - } - } catch (IOException ex) { - // no big deal - } - } - - @Test // https://github.com/GoogleCloudPlatform/cloud-opensource-java/issues/617 - public void testPlural() { - Nodes statisticItems = dashboard.query("//section[@class='statistics']/div/div"); - for (Node node : statisticItems) { - Node h2 = node.query("h2").get(0); - Node span = node.query("span").get(0); - int errorCount = Integer.parseInt(h2.getValue()); - if (errorCount == 1) { - String message = span.getValue(); - Assert.assertEquals("Has Linkage Errors", message); - } - } - - Assert.assertFalse(dashboard.toXML().contains("1 HAVE")); - } - - @Test - public void testHeader() { - Nodes h1 = dashboard.query("//h1"); - assertThat(h1).hasSize(1); - Assert.assertEquals("com.google.cloud:libraries-bom:1.0.0 Dependency Status", - h1.get(0).getValue()); - } - - @Test - public void testSvg() { - XPathContext context = new XPathContext("svg", "http://www.w3.org/2000/svg"); - Nodes svg = dashboard.query("//svg:svg", context); - assertThat(svg).hasSize(4); - } - - @Test - public void testCss() { - Path dashboardCss = outputDirectory.resolve("dashboard.css"); - Assert.assertTrue(Files.exists(dashboardCss)); - Assert.assertTrue(Files.isRegularFile(dashboardCss)); - } - - private static Document parseOutputFile(String fileName) - throws IOException, ParsingException { - Path html = outputDirectory.resolve(fileName); - Assert.assertTrue("Could not find a regular file for " + fileName, - Files.isRegularFile(html)); - Assert.assertTrue("The file is not readable: " + fileName, Files.isReadable(html)); - - try (InputStream source = Files.newInputStream(html)) { - return builder.build(source); - } - } - - @Test - public void testArtifactDetails() throws IOException, ArtifactDescriptorException { - List artifacts = Bom.readBom("com.google.cloud:libraries-bom:1.0.0") - .getManagedDependencies(); - Assert.assertTrue("Not enough artifacts found", artifacts.size() > 1); - - Assert.assertEquals("en-US", dashboard.getRootElement().getAttribute("lang").getValue()); - - Nodes tr = details.query("//tr"); - Assert.assertEquals(artifacts.size() + 1, tr.size()); // header row adds 1 - for (int i = 1; i < tr.size(); i++) { // start at 1 to skip header row - Nodes td = tr.get(i).query("td"); - Assert.assertEquals(Artifacts.toCoordinates(artifacts.get(i - 1)), td.get(0).getValue()); - for (int j = 1; j < 5; ++j) { // start at 1 to skip the leftmost artifact coordinates column - assertValidCellValue((Element) td.get(j)); - } - } - Nodes href = details.query("//tr/td[@class='artifact-name']/a/@href"); - for (int i = 0; i < href.size(); i++) { - String fileName = href.get(i).getValue(); - Artifact artifact = artifacts.get(i); - Assert.assertEquals( - Artifacts.toCoordinates(artifact).replace(':', '_') + ".html", - URLDecoder.decode(fileName, "UTF-8")); - Path componentReport = outputDirectory.resolve(fileName); - Assert.assertTrue(fileName + " is missing", Files.isRegularFile(componentReport)); - try { - Document report = builder.build(componentReport.toFile()); - Assert.assertEquals("en-US", report.getRootElement().getAttribute("lang").getValue()); - } catch (ParsingException ex) { - byte[] data = Files.readAllBytes(componentReport); - String message = "Could not parse " + componentReport + " at line " + - ex.getLineNumber() + ", column " + ex.getColumnNumber() + "\r\n"; - message += ex.getMessage() + "\r\n"; - message += new String(data, StandardCharsets.UTF_8); - Assert.fail(message); - } - } - } - - private static void assertValidCellValue(Element cellElement) { - String cellValue = cellElement.getValue().replaceAll("\\s", ""); - assertThat(cellValue).containsMatch("PASS|\\d+FAILURES?"); - assertWithMessage("It should not use plural for 1 item") - .that(cellValue) - .doesNotContainMatch("1 FAILURES"); - assertThat(cellElement.getAttributeValue("class")).isAnyOf("pass", "fail"); - } - - @Test - public void testDashboard_statisticBox() { - Nodes artifactCount = - dashboard.query("//div[@class='statistic-item statistic-item-green']/h2"); - Assert.assertTrue(artifactCount.size() > 0); - for (Node artifactCountElement : artifactCount) { - String value = artifactCountElement.getValue().trim(); - Assert.assertTrue(value, Integer.parseInt(value) > 0); - } - } - - @Test - public void testLinkageReports() { - Nodes dependencyPaths = details.query("//p[@class='linkage-check-dependency-paths']"); - Node dependencyPathMessageOnProblem = dependencyPaths.get(dependencyPaths.size() - 1); - Assert.assertEquals( - "The following paths contain com.google.guava:guava-jdk5:13.0: ▶", - trimAndCollapseWhiteSpace(dependencyPathMessageOnProblem.getValue())); - - Node dependencyPathMessageOnSource = dependencyPaths.get(dependencyPaths.size() - 2); - Assert.assertEquals( - "The following paths contain com.google.guava:guava:27.1-android:", - trimAndCollapseWhiteSpace(dependencyPathMessageOnSource.getValue())); - } - - @Test - public void testDashboard_recommendedCoordinates() { - Nodes recommendedListItem = dashboard.query("//ul[@id='recommended']/li"); - Assert.assertTrue(recommendedListItem.size() > 100); - - // fails if these are not valid Maven coordinates - for (Node node : recommendedListItem) { - new DefaultArtifact(node.getValue()); - } - - ImmutableList coordinateList = - Streams.stream(recommendedListItem).map(Node::getValue).collect(toImmutableList()); - - ArrayList sorted = new ArrayList<>(coordinateList); - Comparator comparator = new SortWithoutVersion(); - Collections.sort(sorted, comparator); - - for (int i = 0; i < sorted.size(); i++) { - Assert.assertEquals( - "Coordinates are not sorted: ", sorted.get(i), coordinateList.get(i)); - } - } - - private static class SortWithoutVersion implements Comparator { - @Override - public int compare(String s1, String s2) { - s1 = s1.substring(0, s1.lastIndexOf(':')); - s2 = s2.substring(0, s2.lastIndexOf(':')); - return s1.compareTo(s2); - } - } - - @Test - public void testDashboard_unstableDependencies() { - // Pre 1.0 version section - Nodes li = unstable.query("//ul[@id='unstable']/li"); - Assert.assertTrue(li.size() > 1); - for (int i = 0; i < li.size(); i++) { - String value = li.get(i).getValue(); - Assert.assertTrue(value, value.contains(":0")); - } - - // This element appears only when every dependency becomes stable - Nodes stable = dashboard.query("//p[@id='stable-notice']"); - assertThat(stable).isEmpty(); - } - - @Test - public void testDashboard_lastUpdatedField() { - Nodes updated = dashboard.query("//p[@id='updated']"); - Assert.assertEquals( - "Could not find updated field: " + dashboard.toXML(), 1, updated.size()); - } - - @Test - public void testComponent_linkageCheckResult_java8() throws IOException, ParsingException { - Assume.assumeThat(System.getProperty("java.version"), StringStartsWith.startsWith("1.8.")); - // The version used in libraries-bom 1.0.0 - Document document = parseOutputFile( - "com.google.http-client_google-http-client-appengine_1.29.1.html"); - Nodes reports = document.query("//p[@class='jar-linkage-report']"); - assertThat(reports).hasSize(1); - assertThat(trimAndCollapseWhiteSpace(reports.get(0).getValue())) - .isEqualTo("100 target classes causing linkage errors referenced from 540 source classes."); - - - Nodes artifactDetailsReports = details.query("//p[@class='jar-linkage-report']"); - // appengine-api-sdk, shown as first item in linkage errors, has these errors - assertThat(trimAndCollapseWhiteSpace(artifactDetailsReports.get(0).getValue())) - .isEqualTo("4 target classes causing linkage errors referenced from 4 source classes."); - - Nodes causes = document.query("//p[@class='jar-linkage-report-cause']"); - assertWithMessage( - "google-http-client-appengine should show linkage errors for RpcStubDescriptor") - .that(causes) - .comparingElementsUsing(NODE_VALUES) - .contains( - "Class com.google.net.rpc3.client.RpcStubDescriptor is not found," - + " referenced from 21 classes ▶"); // '▶' is the toggle button - } - - @Test - public void testComponent_linkageCheckResult_java11() throws IOException, ParsingException { - String javaVersion = System.getProperty("java.version"); - // javaMajorVersion is 1 when we use Java 8. Still good indicator to ensure Java 11 or higher. - int javaMajorVersion = Integer.parseInt(javaVersion.split("\\.")[0]); - Assume.assumeTrue(javaMajorVersion >= 11); - - Nodes artifactDetailsReports = details.query("//p[@class='jar-linkage-report']"); - // appengine-api-sdk, shown as first item in linkage errors, has these errors - assertThat(trimAndCollapseWhiteSpace(artifactDetailsReports.get(0).getValue())) - .isEqualTo("5 target classes causing linkage errors referenced from 5 source classes."); - - // The version used in libraries-bom 1.0.0. The google-http-client-appengine has been known to - // have linkage errors in its dependency appengine-api-1.0-sdk:1.9.71. - Document document = - parseOutputFile("com.google.http-client_google-http-client-appengine_1.29.1.html"); - Nodes reports = document.query("//p[@class='jar-linkage-report']"); - assertThat(reports).hasSize(1); - - // This number of linkage errors differs between Java 8 and Java 11 for the javax.activation - // package removal (JEP 320: Remove the Java EE and CORBA Modules). For the detail, see - // https://github.com/GoogleCloudPlatform/cloud-opensource-java/issues/1849. - assertThat(trimAndCollapseWhiteSpace(reports.get(0).getValue())) - .isEqualTo("105 target classes causing linkage errors referenced from 562 source classes."); - - Nodes causes = document.query("//p[@class='jar-linkage-report-cause']"); - assertWithMessage( - "google-http-client-appengine should show linkage errors for RpcStubDescriptor") - .that(causes) - .comparingElementsUsing(NODE_VALUES) - .contains( - "Class com.google.net.rpc3.client.RpcStubDescriptor is not found," - + " referenced from 21 classes ▶"); // '▶' is the toggle button - } - - @Test - public void testComponent_success() throws IOException, ParsingException { - Document document = parseOutputFile( - "com.google.api.grpc_proto-google-common-protos_1.14.0.html"); - Nodes greens = document.query("//h3[@style='color: green']"); - Assert.assertTrue(greens.size() >= 2); - Nodes presDependencyMediation = - document.query("//pre[@class='suggested-dependency-mediation']"); - // There's a pre tag for dependency - assertThat(presDependencyMediation).hasSize(1); - - Nodes presDependencyTree = document.query("//p[@class='dependency-tree-node']"); - Assert.assertTrue( - "Dependency Tree should be shown in dashboard", presDependencyTree.size() > 0); - } - - @Test - public void testComponent_failure() throws IOException, ParsingException { - Document document = parseOutputFile( - "com.google.api.grpc_grpc-google-common-protos_1.14.0.html"); - - // com.google.api.grpc:grpc-google-common-protos:1.14.0 has no green section - Nodes greens = document.query("//h3[@style='color: green']"); - assertThat(greens).isEmpty(); - - // "Global Upper Bounds Fixes", "Upper Bounds Fixes", and "Suggested Dependency Updates" are red - Nodes reds = document.query("//h3[@style='color: red']"); - assertThat(reds).hasSize(3); - Nodes presDependencyMediation = - document.query("//pre[@class='suggested-dependency-mediation']"); - Assert.assertTrue( - "For failed component, suggested dependency should be shown", - presDependencyMediation.size() >= 1); - Nodes dependencyTree = document.query("//p[@class='dependency-tree-node']"); - Assert.assertTrue( - "Dependency Tree should be shown in dashboard even when FAILED", - dependencyTree.size() > 0); - } - - @Test - public void testLinkageErrorsInProvidedDependency() throws IOException, ParsingException { - // google-http-client-appengine has provided dependency to (problematic) appengine-api-1.0-sdk - Document document = parseOutputFile( - "com.google.http-client_google-http-client-appengine_1.29.1.html"); - Nodes linkageCheckMessages = document.query("//ul[@class='jar-linkage-report-cause']/li"); - assertThat(linkageCheckMessages.size()).isGreaterThan(0); - assertThat(linkageCheckMessages.get(0).getValue()) - .contains("com.google.appengine.api.appidentity.AppIdentityServicePb"); - } - - @Test - public void testLinkageErrors_ensureNoDuplicateSymbols() throws IOException, ParsingException { - Document document = - parseOutputFile("com.google.http-client_google-http-client-appengine_1.29.1.html"); - Nodes linkageCheckMessages = document.query("//p[@class='jar-linkage-report-cause']"); - assertThat(linkageCheckMessages.size()).isGreaterThan(0); - - List messages = new ArrayList<>(); - for (int i = 0; i < linkageCheckMessages.size(); ++i) { - messages.add(linkageCheckMessages.get(i).getValue()); - } - - // When uniqueness of SymbolProblem and Symbol classes are incorrect, dashboard has duplicates. - assertThat(messages).containsNoDuplicates(); - } - - @Test - public void testZeroLinkageErrorShowsZero() throws IOException, ParsingException { - // grpc-auth does not have a linkage error, and it should show zero in the section - Document document = parseOutputFile("io.grpc_grpc-auth_1.20.0.html"); - Nodes linkageErrorsTotal = document.query("//p[@id='linkage-errors-total']"); - assertThat(linkageErrorsTotal).hasSize(1); - assertThat(linkageErrorsTotal.get(0).getValue()).contains("0 linkage error(s)"); - } - - @Test - public void testGlobalUpperBoundUpgradeMessage() throws IOException, ParsingException { - // Case 1: BOM needs to be updated - Document document = parseOutputFile("com.google.protobuf_protobuf-java-util_3.6.1.html"); - Nodes globalUpperBoundBomUpgradeNodes = - document.query("//li[@class='global-upper-bound-bom-upgrade']"); - assertThat(globalUpperBoundBomUpgradeNodes).hasSize(1); - String bomUpgradeMessage = globalUpperBoundBomUpgradeNodes.get(0).getValue(); - assertThat(bomUpgradeMessage) - .contains( - "Upgrade com.google.protobuf:protobuf-java-util:jar:3.6.1 in the BOM to version" - + " \"3.7.1\""); - - // Case 2: Dependency needs to be updated - Nodes globalUpperBoundDependencyUpgradeNodes = - document.query("//li[@class='global-upper-bound-dependency-upgrade']"); - - // The artifact report should contain the following 6 global upper bound dependency upgrades: - // Upgrade com.google.guava:guava:jar:19.0 to version "27.1-android" - // Upgrade com.google.protobuf:protobuf-java:jar:3.6.1 to version "3.7.1" - assertThat(globalUpperBoundDependencyUpgradeNodes.size()).isEqualTo(2); - String dependencyUpgradeMessage = globalUpperBoundDependencyUpgradeNodes.get(0).getValue(); - assertThat(dependencyUpgradeMessage) - .contains("Upgrade com.google.guava:guava:jar:19.0 to version \"27.1-android\""); - } - - @Test - public void testBomCoordinatesInComponent() throws IOException, ParsingException { - Document document = parseOutputFile("com.google.protobuf_protobuf-java-util_3.6.1.html"); - Nodes bomCoordinatesNodes = document.query("//p[@class='bom-coordinates']"); - assertThat(bomCoordinatesNodes).hasSize(1); - Assert.assertEquals( - "BOM: com.google.cloud:libraries-bom:1.0.0", bomCoordinatesNodes.get(0).getValue()); - } - - @Test - public void testBomCoordinatesInArtifactDetails() throws IOException, ParsingException { - Document document = parseOutputFile("artifact_details.html"); - Nodes bomCoordinatesNodes = document.query("//p[@class='bom-coordinates']"); - assertThat(bomCoordinatesNodes).hasSize(1); - Assert.assertEquals( - "BOM: com.google.cloud:libraries-bom:1.0.0", bomCoordinatesNodes.get(0).getValue()); - } - - @Test - public void testBomCoordinatesInUnstableArtifacts() throws IOException, ParsingException { - Document document = parseOutputFile("unstable_artifacts.html"); - Nodes bomCoordinatesNodes = document.query("//p[@class='bom-coordinates']"); - assertThat(bomCoordinatesNodes).hasSize(1); - Assert.assertEquals( - "BOM: com.google.cloud:libraries-bom:1.0.0", bomCoordinatesNodes.get(0).getValue()); - } - - @Test - public void testDependencyTrees() throws IOException, ParsingException { - Document document = parseOutputFile("dependency_trees.html"); - Nodes dependencyTreeParagraph = document.query("//p[@class='dependency-tree-node']"); - - // characterization test - assertThat(dependencyTreeParagraph).hasSize(39649); - Assert.assertEquals( - "com.google.protobuf:protobuf-java:jar:3.6.1", dependencyTreeParagraph.get(0).getValue()); - } - - @Test - public void testOutputDirectory() { - Assert.assertTrue( - "The dashboard should be created at target/com.google.cloud/libraries-bom/1.0.0", - outputDirectory.endsWith( - Paths.get("target", "com.google.cloud", "libraries-bom", "1.0.0"))); - } -} diff --git a/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/DashboardUnavailableArtifactTest.java b/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/DashboardUnavailableArtifactTest.java deleted file mode 100644 index 1d8f8998bf..0000000000 --- a/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/DashboardUnavailableArtifactTest.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2018 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.cloud.tools.opensource.dashboard; - -import com.google.cloud.tools.opensource.classpath.AnnotatedClassPath; -import com.google.cloud.tools.opensource.classpath.ClassPathResult; -import com.google.cloud.tools.opensource.dependencies.Artifacts; -import com.google.cloud.tools.opensource.dependencies.Bom; -import com.google.cloud.tools.opensource.dependencies.DependencyGraph; -import com.google.cloud.tools.opensource.dependencies.DependencyGraphBuilder; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.io.MoreFiles; -import com.google.common.io.RecursiveDeleteOption; -import com.google.common.truth.Truth; -import freemarker.template.Configuration; -import freemarker.template.TemplateException; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import nu.xom.Builder; -import nu.xom.Document; -import nu.xom.Element; -import nu.xom.Nodes; -import nu.xom.ParsingException; -import org.eclipse.aether.RepositoryException; -import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.graph.Dependency; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -public class DashboardUnavailableArtifactTest { - - private static Path outputDirectory; - private Bom bom = new Bom("test:test:1.2.4", null); - private Builder builder = new Builder(); - - @BeforeClass - public static void setUp() throws IOException { - outputDirectory = Files.createDirectories(Paths.get("target", "dashboard")); - } - - @AfterClass - public static void cleanUp() throws IOException { - // Mac's APFS fails with InsecureRecursiveDeleteException without ALLOW_INSECURE. - // Still safe as this test does not use symbolic links - MoreFiles.deleteRecursively(outputDirectory, RecursiveDeleteOption.ALLOW_INSECURE); - } - - @Test - public void testDashboardForRepositoryException() throws TemplateException { - Configuration configuration = DashboardMain.configureFreemarker(); - Artifact validArtifact = new DefaultArtifact("io.grpc:grpc-context:1.15.0"); - Artifact nonExistentArtifact = new DefaultArtifact("io.grpc:nonexistent:jar:1.15.0"); - DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(); - Map map = new LinkedHashMap<>(); - DependencyGraph graph1 = - graphBuilder.buildMavenDependencyGraph(new Dependency(validArtifact, "compile")); - DependencyGraph graph2 = - graphBuilder.buildMavenDependencyGraph(new Dependency(nonExistentArtifact, "compile")); - map.put(validArtifact, new ArtifactInfo(graph1, graph2)); - map.put(nonExistentArtifact, new ArtifactInfo(new RepositoryException("foo"))); - - ArtifactCache cache = new ArtifactCache(); - cache.setInfoMap(map); - List artifactResults = - DashboardMain.generateReports( - configuration, - outputDirectory, - cache, - ImmutableMap.of(), - new ClassPathResult(new AnnotatedClassPath(), ImmutableList.of()), - bom); - - Assert.assertEquals( - "The length of the ArtifactResults should match the length of artifacts", - 2, - artifactResults.size()); - Assert.assertEquals( - "The first artifact result should be valid", - true, - artifactResults.get(0).getResult(DashboardMain.TEST_NAME_UPPER_BOUND)); - ArtifactResults errorArtifactResult = artifactResults.get(1); - Assert.assertNull( - "The second artifact result should be null", - errorArtifactResult.getResult(DashboardMain.TEST_NAME_UPPER_BOUND)); - Assert.assertEquals( - "The error artifact result should contain error message", - "foo", - errorArtifactResult.getExceptionMessage()); - } - - @Test - public void testDashboardWithRepositoryException() - throws IOException, TemplateException, ParsingException { - Configuration configuration = DashboardMain.configureFreemarker(); - - Artifact validArtifact = new DefaultArtifact("io.grpc:grpc-context:1.15.0"); - ArtifactResults validArtifactResult = new ArtifactResults(validArtifact); - validArtifactResult.addResult(DashboardMain.TEST_NAME_UPPER_BOUND, 0); - validArtifactResult.addResult(DashboardMain.TEST_NAME_DEPENDENCY_CONVERGENCE, 0); - validArtifactResult.addResult(DashboardMain.TEST_NAME_GLOBAL_UPPER_BOUND, 0); - - Artifact invalidArtifact = new DefaultArtifact("io.grpc:nonexistent:jar:1.15.0"); - ArtifactResults errorArtifactResult = new ArtifactResults(invalidArtifact); - errorArtifactResult.setExceptionMessage( - "Could not find artifact io.grpc:nonexistent:jar:1.15.0 in central" - + " (https://repo1.maven.org/maven2/)"); - - List table = new ArrayList<>(); - table.add(validArtifactResult); - table.add(errorArtifactResult); - - DashboardMain.generateDashboard( - configuration, - outputDirectory, - table, - ImmutableList.of(), - ImmutableMap.of(), - new ClassPathResult(new AnnotatedClassPath(), ImmutableList.of()), - bom); - - Path generatedHtml = outputDirectory.resolve("artifact_details.html"); - Assert.assertTrue(Files.isRegularFile(generatedHtml)); - Document document = builder.build(generatedHtml.toFile()); - Assert.assertEquals("en-US", document.getRootElement().getAttribute("lang").getValue()); - Nodes tr = document.query("//tr"); - - Assert.assertEquals( - "The size of rows in table should match the number of artifacts + 1 (header)", - tr.size(),table.size() + 1); - - Nodes tdForValidArtifact = tr.get(1).query("td"); - Assert.assertEquals( - Artifacts.toCoordinates(validArtifact), tdForValidArtifact.get(0).getValue()); - Element firstResult = (Element) (tdForValidArtifact.get(2)); - Truth.assertThat(firstResult.getValue().trim()).isEqualTo("PASS"); - Truth.assertThat(firstResult.getAttributeValue("class")).isEqualTo("pass"); - - Nodes tdForErrorArtifact = tr.get(2).query("td"); - Assert.assertEquals( - Artifacts.toCoordinates(invalidArtifact), tdForErrorArtifact.get(0).getValue()); - Element secondResult = (Element) (tdForErrorArtifact.get(2)); - Truth.assertThat(secondResult.getValue().trim()).isEqualTo("UNAVAILABLE"); - Truth.assertThat(secondResult.getAttributeValue("class")).isEqualTo("unavailable"); - } -} diff --git a/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/FreemarkerTest.java b/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/FreemarkerTest.java deleted file mode 100644 index a499fa0552..0000000000 --- a/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/FreemarkerTest.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2019 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.cloud.tools.opensource.dashboard; - -import com.google.cloud.tools.opensource.classpath.AnnotatedClassPath; -import com.google.cloud.tools.opensource.classpath.ClassFile; -import com.google.cloud.tools.opensource.classpath.ClassNotFoundProblem; -import com.google.cloud.tools.opensource.classpath.ClassPathEntry; -import com.google.cloud.tools.opensource.classpath.ClassPathResult; -import com.google.cloud.tools.opensource.classpath.ClassSymbol; -import com.google.cloud.tools.opensource.classpath.LinkageProblem; -import com.google.cloud.tools.opensource.dependencies.Bom; -import com.google.cloud.tools.opensource.dependencies.DependencyGraph; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.io.MoreFiles; -import com.google.common.io.RecursiveDeleteOption; -import com.google.common.truth.Truth; -import freemarker.template.Configuration; -import freemarker.template.TemplateException; -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import nu.xom.Builder; -import nu.xom.Document; -import nu.xom.Node; -import nu.xom.Nodes; -import nu.xom.ParsingException; -import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.artifact.DefaultArtifact; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -/** - * Unit tests for FreeMarker logic without reading any JAR files. - */ -public class FreemarkerTest { - - private static Path outputDirectory; - private static ImmutableMap> symbolProblemTable; - - private Builder builder = new Builder(); - - @BeforeClass - public static void setUpDirectory() throws IOException { - outputDirectory = Files.createDirectories(Paths.get("target", "dashboard")); - } - - @Before - public void setUp() { - Artifact artifact = new DefaultArtifact("com.google:foo:1.0.0") - .setFile(new File("foo/bar-1.2.3.jar")); - ClassPathEntry entry = new ClassPathEntry(artifact); - ImmutableSet dummyProblems = - ImmutableSet.of( - new ClassNotFoundProblem( - new ClassFile(entry, "abc.def.G"), new ClassSymbol("com.foo.Bar"))); - symbolProblemTable = ImmutableMap.of(entry, dummyProblems); - } - - @AfterClass - public static void cleanUp() throws IOException { - // Mac's APFS fails with InsecureRecursiveDeleteException without ALLOW_INSECURE. - // Still safe as this test does not use symbolic links - MoreFiles.deleteRecursively(outputDirectory, RecursiveDeleteOption.ALLOW_INSECURE); - } - - @Test - public void testCountFailures() throws IOException, TemplateException, ParsingException { - Configuration configuration = DashboardMain.configureFreemarker(); - - Artifact artifact1 = new DefaultArtifact("io.grpc:grpc-context:1.15.0"); - ArtifactResults results1 = new ArtifactResults(artifact1); - results1.addResult("Linkage Errors", 56); - - Artifact artifact2 = new DefaultArtifact("grpc:grpc:1.15.0"); - ArtifactResults results2 = new ArtifactResults(artifact2); - results2.addResult("Linkage Errors", 0); - - List table = ImmutableList.of(results1, results2); - List globalDependencies = ImmutableList.of(); - DashboardMain.generateDashboard( - configuration, - outputDirectory, - table, - globalDependencies, - symbolProblemTable, - new ClassPathResult(new AnnotatedClassPath(), ImmutableList.of()), - new Bom("mock:artifact:1.6.7", null)); - - Path dashboardHtml = outputDirectory.resolve("index.html"); - Assert.assertTrue(Files.isRegularFile(dashboardHtml)); - Document document = builder.build(dashboardHtml.toFile()); - - // xom's query cannot specify partial class field, e.g., 'statistic-item' - Nodes counts = document.query("//div[@class='container']/div/h2"); - Assert.assertTrue(counts.size() > 0); - for (int i = 0; i < counts.size(); i++) { - Integer.parseInt(counts.get(i).getValue().trim()); - } - // Linkage Errors - Truth.assertThat(counts.get(1).getValue().trim()).isEqualTo("1"); - } - - @Test - public void testVersionIndex() - throws IOException, TemplateException, URISyntaxException, ParsingException { - Path output = - DashboardMain.generateVersionIndex( - "com.google.cloud", - "libraries-bom", - ImmutableList.of("1.0.0", "2.0.0", "2.1.0-SNAPSHOT")); - Assert.assertTrue( - output.endsWith(Paths.get("target", "com.google.cloud", "libraries-bom", "index.html"))); - Assert.assertTrue(Files.isRegularFile(output)); - - Document document = builder.build(output.toFile()); - Nodes links = document.query("//a/@href"); - Assert.assertEquals(3, links.size()); - Node snapshotLink = links.get(2); - // 2.1.0-SNAPSHOT has directory 'snapshot' - Assert.assertEquals("snapshot/index.html", snapshotLink.getValue()); - } -} diff --git a/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/PieChartTest.java b/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/PieChartTest.java deleted file mode 100644 index 177f4243c9..0000000000 --- a/dashboard/src/test/java/com/google/cloud/tools/opensource/dashboard/PieChartTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2019 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.cloud.tools.opensource.dashboard; - -import java.awt.geom.Point2D; -import org.junit.Assert; -import org.junit.Test; - -public class PieChartTest { - - private static final double TOLERANCE = 0.0001; - - @Test - public void testRightAngle() { - Point2D actual = PieChart.calculateEndPoint(100, 100, 100, .25); - Point2D expected = new Point2D.Double(200, 100); - Assert.assertEquals(expected.getX(), actual.getX(), TOLERANCE); - Assert.assertEquals(expected.getY(), actual.getY(), TOLERANCE); - } - - @Test - public void testZero() { - Point2D actual = PieChart.calculateEndPoint(100, 100, 100, 0); - Point2D expected = new Point2D.Double(100, 0); - Assert.assertEquals(expected.getX(), actual.getX(), TOLERANCE); - Assert.assertEquals(expected.getY(), actual.getY(), TOLERANCE); - } - - @Test - public void testOneEighth() { - int radius = 100; - double ratio = 1.0/8.0; - Point2D actual = PieChart.calculateEndPoint(radius, 100, 100, ratio); - Point2D expected = new Point2D.Double(100 + 100 / Math.sqrt(2), 100 - 100 / Math.sqrt(2)); - Assert.assertEquals(expected.getX(), actual.getX(), TOLERANCE); - Assert.assertEquals(expected.getY(), actual.getY(), TOLERANCE); - } - - @Test - public void testHalf() { - Point2D actual = PieChart.calculateEndPoint(100, 100, 100, 0.5); - Point2D expected = new Point2D.Double(100, 200); - Assert.assertEquals(expected.getX(), actual.getX(), TOLERANCE); - Assert.assertEquals(expected.getY(), actual.getY(), TOLERANCE); - } - - @Test - public void testOffCenter() { - Point2D actual = PieChart.calculateEndPoint(150, 150, 100, 0.5); - Point2D expected = new Point2D.Double(150, 250); - Assert.assertEquals(expected.getX(), actual.getX(), TOLERANCE); - Assert.assertEquals(expected.getY(), actual.getY(), TOLERANCE); - } - - @Test - public void testFull() { - Point2D actual = PieChart.calculateEndPoint(100, 100, 100, 1.0); - Point2D expected = new Point2D.Double(100, 0); - Assert.assertEquals(expected.getX(), actual.getX(), TOLERANCE); - Assert.assertEquals(expected.getY(), actual.getY(), TOLERANCE); - } - - @Test - public void testMoreThanFull() { - Point2D actual = PieChart.calculateEndPoint(100, 100, 100, 5.5); - Point2D expected = new Point2D.Double(100, 0); - Assert.assertEquals(expected.getX(), actual.getX(), TOLERANCE); - Assert.assertEquals(expected.getY(), actual.getY(), TOLERANCE); - } - -} - - - diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 2296ec7d84..69bd85b9af 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -150,23 +150,5 @@ - - java8-incompatible-reference-check - - - - org.codehaus.mojo - exec-maven-plugin - - false - com.google.cloud.tools.opensource.dependencies.Java8IncompatibleReferenceCheck - - ../boms/cloud-oss-bom/pom.xml - - - - - - diff --git a/enforcer-rules/pom.xml b/enforcer-rules/pom.xml index af2abf7020..6f4147ac24 100644 --- a/enforcer-rules/pom.xml +++ b/enforcer-rules/pom.xml @@ -145,24 +145,38 @@ - - maven-invoker-plugin - 3.2.2 - - ${project.build.directory}/it - ${project.build.directory}/local-repo - verify - - - - integration-test - - install - run - - - - + + + invoker-integration-test + + + !performRelease + + + + + + maven-invoker-plugin + 3.2.2 + + ${project.build.directory}/it + ${project.build.directory}/local-repo + verify + + + + integration-test + + install + run + + + + + + + + diff --git a/gradle-plugin/build.gradle b/gradle-plugin/build.gradle index 2e53631137..d921ff18da 100644 --- a/gradle-plugin/build.gradle +++ b/gradle-plugin/build.gradle @@ -27,7 +27,7 @@ group = 'com.google.cloud.tools' sourceCompatibility = 1.8 -version = '1.5.15' // {x-version-update:dependencies:current} +version = '1.5.16-SNAPSHOT' // {x-version-update:dependencies:current} dependencies { implementation "com.google.cloud.tools:dependencies:$version" diff --git a/kokoro/ubuntu/java8-incompatible-reference-check.sh b/kokoro/ubuntu/java8-incompatible-reference-check.sh deleted file mode 100644 index f2bd70480c..0000000000 --- a/kokoro/ubuntu/java8-incompatible-reference-check.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# Fail on any error. -set -e -# Display commands being run. -set -x - -cd github/cloud-opensource-java/dependencies -mvn -V -B clean install - -mvn exec:java -e -Pjava8-incompatible-reference-check \ No newline at end of file diff --git a/kokoro/ubuntu/periodic.cfg b/kokoro/ubuntu/periodic.cfg deleted file mode 100644 index cf6d39fd94..0000000000 --- a/kokoro/ubuntu/periodic.cfg +++ /dev/null @@ -1,13 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Location of the periodic build bash script in git. -build_file: "cloud-opensource-java/kokoro/ubuntu/periodic.sh" - -timeout_mins: 240 - -action { - define_artifacts { - regex: "**/target/com.google.cloud/**" - strip_prefix: "github/cloud-opensource-java/dashboard/target" - } -} diff --git a/kokoro/ubuntu/periodic.sh b/kokoro/ubuntu/periodic.sh deleted file mode 100755 index c7f5d71901..0000000000 --- a/kokoro/ubuntu/periodic.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# Fail on any error. -set -e -# Display commands being run. -set -x - -cd github/cloud-opensource-java -# No need to run tests -./mvnw -V -B -ntp clean install -DskipTests -Denforcer.skip -Dinvoker.skip - -# Running target of dashboard submodule -# https://stackoverflow.com/questions/3459928/running-a-specific-maven-plugin-goal-from-the-command-line-in-a-sub-module-of-a/26448447#26448447 -# https://stackoverflow.com/questions/11091311/maven-execjava-goal-on-a-multi-module-project -cd dashboard - -# For all versions available in Maven Central and local repository -../mvnw -V -B -ntp exec:java -Dexec.mainClass="com.google.cloud.tools.opensource.dashboard.DashboardMain" \ - -Dexec.arguments="-a com.google.cloud:libraries-bom" - -../mvnw -V -B -ntp exec:java -Dexec.mainClass="com.google.cloud.tools.opensource.dashboard.DashboardMain" \ - -Dexec.arguments="-a com.google.cloud:gcp-lts-bom" diff --git a/pom.xml b/pom.xml index 25a32f6222..215bbe8486 100644 --- a/pom.xml +++ b/pom.xml @@ -54,11 +54,8 @@ - boms dependencies - dashboard enforcer-rules - linkage-monitor