From 193fb87d34ac56313e7ae48d6021ab27baee2048 Mon Sep 17 00:00:00 2001 From: Srikanta Date: Thu, 27 Jan 2022 11:13:29 -0800 Subject: [PATCH 1/8] Azure SDK build tool Maven plugin --- sdk/tools/azure-sdk-build-tool/CHANGELOG.md | 5 + sdk/tools/azure-sdk-build-tool/README.md | 40 ++++ sdk/tools/azure-sdk-build-tool/pom.xml | 158 +++++++++++++++ .../build/tool/AnnotationProcessingTool.java | 86 ++++++++ .../build/tool/AzureDependencyMapping.java | 81 ++++++++ .../sdk/build/tool/DependencyCheckerTool.java | 129 ++++++++++++ .../java/com/azure/sdk/build/tool/Tools.java | 23 +++ .../sdk/build/tool/models/BuildReport.java | 188 ++++++++++++++++++ .../build/tool/models/OutdatedDependency.java | 42 ++++ .../sdk/build/tool/mojo/AzureSdkMojo.java | 129 ++++++++++++ .../util/AnnotatedMethodCallerResult.java | 44 ++++ .../sdk/build/tool/util/AnnotationUtils.java | 133 +++++++++++++ .../azure/sdk/build/tool/util/MavenUtils.java | 75 +++++++ .../azure/sdk/build/tool/util/MojoUtils.java | 45 +++++ .../tool/util/logging/ConsoleLogger.java | 40 ++++ .../sdk/build/tool/util/logging/Logger.java | 47 +++++ .../build/tool/util/logging/MojoLogger.java | 48 +++++ .../src/main/resources/strings.properties | 6 + .../test/azure-sdk-build-tool-test/pom.xml | 76 +++++++ .../com/test/appconfig/AppConfigTestApp.java | 20 ++ .../build/tool/util/AnnotationUtilsTests.java | 51 +++++ .../java/com/test/models/AnnotationA.java | 8 + .../src/test/java/com/test/models/ClassA.java | 9 + .../src/test/java/com/test/models/ClassB.java | 9 + sdk/tools/ci.yml | 5 + sdk/tools/pom.xml | 1 + 26 files changed, 1498 insertions(+) create mode 100644 sdk/tools/azure-sdk-build-tool/CHANGELOG.md create mode 100644 sdk/tools/azure-sdk-build-tool/README.md create mode 100644 sdk/tools/azure-sdk-build-tool/pom.xml create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AnnotationProcessingTool.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AzureDependencyMapping.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/Tools.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/BuildReport.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/OutdatedDependency.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotatedMethodCallerResult.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MavenUtils.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MojoUtils.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/ConsoleLogger.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/Logger.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/MojoLogger.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties create mode 100644 sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml create mode 100644 sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/src/main/java/com/test/appconfig/AppConfigTestApp.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/test/java/com/azure/sdk/build/tool/util/AnnotationUtilsTests.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/test/java/com/test/models/AnnotationA.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/test/java/com/test/models/ClassA.java create mode 100644 sdk/tools/azure-sdk-build-tool/src/test/java/com/test/models/ClassB.java diff --git a/sdk/tools/azure-sdk-build-tool/CHANGELOG.md b/sdk/tools/azure-sdk-build-tool/CHANGELOG.md new file mode 100644 index 0000000000000..48eeef7b02f00 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/CHANGELOG.md @@ -0,0 +1,5 @@ +# Release History + +## 1.0.0-beta.1 (Unreleased) + +### Features Added diff --git a/sdk/tools/azure-sdk-build-tool/README.md b/sdk/tools/azure-sdk-build-tool/README.md new file mode 100644 index 0000000000000..db07ef588eda7 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/README.md @@ -0,0 +1,40 @@ +# Azure SDK Maven Build Tool + +The Azure SDK for Java project ships a Maven build tool that developers can choose to include in their projects. This tool runs locally and does not transmit any data to Microsoft. It can be configured to generate a report or fail the build when certain conditions are met, which is useful to ensure compliance with numerous best practices. These include: + +- Validating the correct use of the azure-sdk-for-java BOM, including using the latest version and relying on it to +define dependency versions on Azure SDK for Java client libraries. +- Validating that historical Azure client libraries are not being used when newer and improved versions exist. +- Providing insight into usage of beta APIs. + +The build tool can be configured in a project Maven POM file as such: + +```xml + + + + com.azure.tools + azure-sdk-build-tool + {latest_version} + + ... + + + + +``` +Within the configuration section, it is possible to configure the settings in the table below if desired, but by default they are configured with the recommended settings. Because of this, it is ok to not have any configuration specified at all. + + +| Property | Default Value | Description | +|------------------------------------------|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| validateAzureSdkBomUsed | true | Ensures that the build has the azure-sdk-for-java BOM referenced appropriately, so that Azure SDK for Java client library dependencies may take their versions from the BOM. | +| validateBomVersionsAreUsed | true | Ensures that where a dependency is available from the azure-sdk-for-java BOM the version is not being manually overridden. | +| validateNoDeprecatedMicrosoftLibraryUsed | true | Ensures that the project does not make use of previous-generation Azure libraries. Using the new and previous-generation libraries in a single project is unlikely to cause any issue, but is will result in a sub-optimal developer experience. | +| validateNoBetaLibraryUsed | false | Some Azure SDK for Java client libraries have beta releases, with version strings in the form x.y.z-beta.n. Enabling this feature will ensure that no beta libraries are being used. | +| validateNoBetaAPIUsed | true | Azure SDK for Java client libraries sometimes do GA releases with methods annotated with @Beta. This check looks to see if any such methods are being used. | +| validateLatestBomVersionUsed | true | Ensures that dependencies are kept up to date by reporting back (or failing the build) if a newer azure-sdk-for-java BOM exists. | +| reportFile | "" | (Optional) Specifies the location to write the build report out to, in JSON format. If not specified, no report will be written (and a summary of the build, or the appropriate build failures), will be shown in the terminal. | +After adding the build tool into a Maven project, the tool can be run by calling mvn azure:run. Depending on the configuration provided, you can expect to see build failures or report files generated that can inform you about potential issues before they become more serious. + +As the build tool evolves, new releases will be published, and it is recommended that developers frequently check for new releases and update as appropriate. diff --git a/sdk/tools/azure-sdk-build-tool/pom.xml b/sdk/tools/azure-sdk-build-tool/pom.xml new file mode 100644 index 0000000000000..0e2c8ede7e14b --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/pom.xml @@ -0,0 +1,158 @@ + + + 4.0.0 + + com.azure.tools + azure-sdk-build-tool + maven-plugin + 1.0.0-beta.1 + + Azure Maven Build Tool + A tool that makes working with the Azure SDK for Java more productive. + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + https://github.com/azure/azure-sdk-for-java + + Microsoft Corporation + http://microsoft.com + + + + The MIT License (MIT) + http://opensource.org/licenses/MIT + repo + + + + + + microsoft + Microsoft Corporation + + + + + GitHub + https://github.com/Azure/azure-sdk-for-java/issues + + + + https://github.com/Azure/azure-sdk-for-java + scm:git:https://github.com/Azure/azure-sdk-for-java.git + + HEAD + + + + 8 + 8 + + + + + + org.apache.maven + maven-plugin-api + 3.8.1 + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.6.0 + + + org.apache.maven + maven-project + 2.2.1 + + + junit + junit + + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.8.2 + + + com.github.javaparser + javaparser-core + 3.20.2 + + + + + net.oneandone.reflections8 + reflections8 + 0.11.7 + + + + org.javassist + javassist + 3.28.0-GA + + + + + org.junit.jupiter + junit-jupiter-api + 5.8.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.8.2 + test + + + org.junit.jupiter + junit-jupiter-params + 5.8.2 + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + + org.apache.maven.plugins + maven-plugin-plugin + 3.6.0 + + + mojo-descriptor + + descriptor + + + + + + azure + + + + + + diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AnnotationProcessingTool.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AnnotationProcessingTool.java new file mode 100644 index 0000000000000..88f7fb2ef6eb2 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AnnotationProcessingTool.java @@ -0,0 +1,86 @@ +package com.azure.sdk.build.tool; + +import com.azure.sdk.build.tool.mojo.AzureSdkMojo; +import com.azure.sdk.build.tool.util.logging.Logger; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Stream; + +import static com.azure.sdk.build.tool.util.AnnotationUtils.findCallsToAnnotatedMethod; +import static com.azure.sdk.build.tool.util.AnnotationUtils.getAnnotation; +import static com.azure.sdk.build.tool.util.AnnotationUtils.getCompleteClassLoader; +import static com.azure.sdk.build.tool.util.MojoUtils.getAllDependencies; +import static com.azure.sdk.build.tool.util.MojoUtils.getCompileSourceRoots; + +/** + * Performs the following tasks: + * + * + */ +public class AnnotationProcessingTool implements Runnable { + private static Logger LOGGER = Logger.getInstance(); + + /** + * Runs the annotation processing task to look for @ServiceMethod and @Beta usage. + */ + public void run() { + LOGGER.info("Running Annotation Processing Tool"); + + // We build up a list of packages in the source of the user maven project, so that we only report on the + // usage of annotation methods from code within these packages + final Set interestedPackages = new TreeSet<>(Comparator.comparingInt(String::length)); + getCompileSourceRoots().forEach(root -> buildPackageList(root, root, interestedPackages)); + + final ClassLoader classLoader = getCompleteClassLoader(getAllPaths()); + + // Collect all calls to methods annotated with the Azure SDK @ServiceMethod annotation + getAnnotation("com.azure.core.annotation.ServiceMethod", classLoader) + .map(a -> findCallsToAnnotatedMethod(a, getAllPaths(), interestedPackages, true)) + .ifPresent(AzureSdkMojo.MOJO.getReport()::setServiceMethodCalls); + + // Collect all calls to methods annotated with the Azure SDK @Beta annotation + getAnnotation("com.azure.cosmos.util.Beta", classLoader) + .map(a -> findCallsToAnnotatedMethod(a, getAllPaths(), interestedPackages, true)) + .ifPresent(AzureSdkMojo.MOJO.getReport()::setBetaMethodCalls); + + } + + private static Stream getAllPaths() { + // This is the user maven build target directory - we look in here for the compiled source code + final File targetDir = new File(AzureSdkMojo.MOJO.getProject().getBuild().getDirectory() + "/classes/"); + + // this stream of paths is a stream containing the users maven project compiled class files, as well as all + // jar file dependencies. We use this to analyse the use of annotations and report back to the user. + return Stream.concat( + Stream.of(targetDir.getAbsolutePath()), + getAllDependencies().stream().map(a -> a.getFile().getAbsolutePath())) + .map(Paths::get); + } + + private static void buildPackageList(String rootDir, String currentDir, Set packages) { + final File directory = new File(currentDir); + + final File[] files = directory.listFiles(); + if (files == null) { + return; + } + + for (final File file : files) { + if (file.isFile()) { + final String path = file.getPath(); + final String packageName = path.substring(rootDir.length() + 1, path.lastIndexOf(File.separator)); + packages.add(packageName.replace(File.separatorChar, '.')); + } else if (file.isDirectory()) { + buildPackageList(rootDir, file.getAbsolutePath(), packages); + } + } + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AzureDependencyMapping.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AzureDependencyMapping.java new file mode 100644 index 0000000000000..46c0419061b17 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AzureDependencyMapping.java @@ -0,0 +1,81 @@ +package com.azure.sdk.build.tool; + +import com.azure.sdk.build.tool.models.OutdatedDependency; +import com.azure.sdk.build.tool.util.MavenUtils; +import com.azure.sdk.build.tool.util.logging.Logger; +import edu.emory.mathcs.backport.java.util.Collections; +import org.apache.maven.artifact.Artifact; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Contains the mapping for outdated dependencies and it's replacement. + */ +public class AzureDependencyMapping { + private static Logger LOGGER = Logger.getInstance(); + + private static final String TRACK_ONE_GROUP_ID = "com.microsoft.azure"; + private static final String TRACK_TWO_GROUP_ID = "com.azure"; + + // This map is for all com.microsoft.* group IDs, mapping them into their com.azure equivalents + private static final Map> TRACK_ONE_REDIRECTS = new HashMap<>(); + static { + // Cosmos + TRACK_ONE_REDIRECTS.put("azure-cosmosdb", Collections.singletonList("azure-cosmos")); + + // Key Vault - Track 1 KeyVault library is split into three Track 2 libraries + TRACK_ONE_REDIRECTS.put("azure-keyvault", + Arrays.asList("azure-security-keyvault-keys", + "azure-security-keyvault-certificates", + "azure-security-keyvault-secrets")); + + // Blob Storage + TRACK_ONE_REDIRECTS.put("azure-storage-blob", Collections.singletonList("azure-storage-blob")); + + // Event Hubs + TRACK_ONE_REDIRECTS.put("azure-eventhubs", Collections.singletonList("azure-messaging-eventhubs")); + TRACK_ONE_REDIRECTS.put("azure-eventhubs-eph", Collections.singletonList("azure-messaging-eventhubs-checkpointstore-blob")); + + // Service Bus + TRACK_ONE_REDIRECTS.put("azure-servicebus", Collections.singletonList("azure-messaging-servicebus")); + + // Event Grid + TRACK_ONE_REDIRECTS.put("azure-eventgrid", Collections.singletonList("azure-messaging-eventgrid")); + + // Log Analytics + TRACK_ONE_REDIRECTS.put("azure-loganalytics", Collections.singletonList("azure-monitor-query")); + } + + /** + * This method will look to see if we have any recorded guidance on how to replace the given artifact with + * something else. + * + * @param artifact The artifact for which we want to find a replacement + * @return An {@link OutdatedDependency} if a replacement for the given {@code artifact} exists. + */ + public static Optional lookupReplacement(Artifact artifact) { + String groupId = artifact.getGroupId(); + String artifactId = artifact.getArtifactId(); + + if (TRACK_ONE_GROUP_ID.equals(groupId)) { + if (TRACK_ONE_REDIRECTS.containsKey(artifactId)) { + final List newArtifactIds = TRACK_ONE_REDIRECTS.get(artifactId); + + List newGavs = newArtifactIds.stream() + .map(newArtifactId -> TRACK_TWO_GROUP_ID + ":" + newArtifactId + ":" + MavenUtils.getLatestArtifactVersion(TRACK_TWO_GROUP_ID, newArtifactId)).collect(Collectors.toList()); + return Optional.of(new OutdatedDependency(MavenUtils.toGAV(artifact), newGavs)); + } else { + // we've hit artifact location where we don't know know if the com.microsoft.azure artifact has artifact newer + // replacement...For now we will not give artifact failure + return Optional.empty(); + } + } + + return Optional.empty(); + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java new file mode 100644 index 0000000000000..9c952c1c26300 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java @@ -0,0 +1,129 @@ +package com.azure.sdk.build.tool; + +import com.azure.sdk.build.tool.models.OutdatedDependency; +import com.azure.sdk.build.tool.mojo.AzureSdkMojo; +import com.azure.sdk.build.tool.util.MavenUtils; +import com.azure.sdk.build.tool.util.logging.Logger; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; + +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static com.azure.sdk.build.tool.util.MojoUtils.getAllDependencies; +import static com.azure.sdk.build.tool.util.MojoUtils.getDirectDependencies; +import static com.azure.sdk.build.tool.util.MojoUtils.getString; + +/** + * Performs the following tasks: + * + *
    + *
  • Warnings about missing BOM.
  • + *
  • Warnings about not using the latest available version of BOM.
  • + *
  • Warnings about explicit dependency versions.
  • + *
  • Warnings about dependency clashes between Azure libraries and other dependencies.
  • + *
  • Warnings about using track one libraries.
  • + *
  • Warnings about out of date track two dependencies (BOM and individual libraries).
  • + *
+ */ +public class DependencyCheckerTool implements Runnable { + private static Logger LOGGER = Logger.getInstance(); + + private static final String AZURE_SDK_BOM_ARTIFACT_ID = "azure-sdk-bom"; + private static final String COM_MICROSOFT_AZURE_GROUP_ID = "com.microsoft.azure"; + + public void run() { + LOGGER.info("Running Dependency Checker Tool"); + + checkForBom(); + checkForAzureSdkDependencyVersions(); + checkForAzureSdkTransitiveDependencyConflicts(); + checkForAzureSdkTrackOneDependencies(); + checkForOutdatedAzureSdkDependencies(); + } + + private void checkForBom() { + // we are looking for the azure-sdk-bom artifact ID listed as a dependency in the dependency management section + DependencyManagement depMgmt = AzureSdkMojo.MOJO.getProject().getDependencyManagement(); + Optional bomDependency = Optional.empty(); + if (depMgmt != null) { + bomDependency = depMgmt.getDependencies().stream() + .filter(d -> d.getArtifactId().equals(AZURE_SDK_BOM_ARTIFACT_ID)) + .findAny(); + } + + if (bomDependency.isPresent()) { + String latestAvailableBomVersion = MavenUtils.getLatestArtifactVersion("com.azure", "azure-sdk-bom"); + boolean isLatestBomVersion = bomDependency.get().getVersion().equals(latestAvailableBomVersion); + if (!isLatestBomVersion) { + failOrError(AzureSdkMojo.MOJO::isValidateAzureSdkBomUsed, getString("outdatedBomDependency")); + } + } else { + failOrError(AzureSdkMojo.MOJO::isValidateAzureSdkBomUsed, getString("missingBomDependency")); + } + } + + private void checkForAzureSdkDependencyVersions() { + // TODO + } + + private void checkForAzureSdkTransitiveDependencyConflicts() { + // TODO + } + + private void checkForAzureSdkTrackOneDependencies() { + // Check direct dependencies first for any 'com.microsoft.azure' group IDs. These are under the users direct + // control, so they could try to upgrade to a newer 'com.azure' version instead. + Set outdatedDirectDependencies = getDirectDependencies().stream() + .filter(a -> COM_MICROSOFT_AZURE_GROUP_ID.equals(a.getGroupId())) + .map(AzureDependencyMapping::lookupReplacement) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toSet()); + + // check indirect dependencies too, but filter out any dependencies we've already discovered above + Set outdatedTransitiveDependencies = getAllDependencies().stream() + .filter(d -> COM_MICROSOFT_AZURE_GROUP_ID.equals(d.getGroupId())) + .map(AzureDependencyMapping::lookupReplacement) + .filter(Optional::isPresent) + .map(Optional::get) + .filter(d -> !outdatedDirectDependencies.contains(d)) + .collect(Collectors.toSet()); + + // The report is only concerned with GAV, so we simplify it here + AzureSdkMojo.MOJO.getReport().setOutdatedDirectDependencies(outdatedDirectDependencies); + AzureSdkMojo.MOJO.getReport().setOutdatedTransitiveDependencies(outdatedTransitiveDependencies); + + if (!outdatedDirectDependencies.isEmpty()) { + // convert each track one dependency into actionable guidance + String message = getString("deprecatedDirectDependency"); + for (OutdatedDependency outdatedDependency : outdatedDirectDependencies) { + message += "\n - " + outdatedDependency.getGav() + " --> " + outdatedDependency.getSuggestedReplacementGav(); + } + failOrError(AzureSdkMojo.MOJO::isValidateNoDeprecatedMicrosoftLibraryUsed, message); + } + if (!outdatedTransitiveDependencies.isEmpty()) { + // convert each track one dependency into actionable guidance + String message = getString("deprecatedIndirectDependency"); + for (OutdatedDependency outdatedDependency : outdatedDirectDependencies) { + message += "\n - " + outdatedDependency.getGav(); + } + failOrError(AzureSdkMojo.MOJO::isValidateNoDeprecatedMicrosoftLibraryUsed, message); + } + } + + private void checkForOutdatedAzureSdkDependencies() { + // TODO + } + + private void failOrError(Supplier condition, String message) { + // warn about lack of BOM dependency + if (condition.get()) { + AzureSdkMojo.MOJO.getReport().addFailureMessage(message); + } else { + AzureSdkMojo.MOJO.getReport().addErrorMessage(message); + } + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/Tools.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/Tools.java new file mode 100644 index 0000000000000..fae94f888ef2a --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/Tools.java @@ -0,0 +1,23 @@ +package com.azure.sdk.build.tool; + +import java.util.ArrayList; +import java.util.List; + +/** + * A class containing list of build tools that can be executed when the plugin is run. + */ +public class Tools { + private static final List TOOLS = new ArrayList<>(); + static { + TOOLS.add(new DependencyCheckerTool()); + TOOLS.add(new AnnotationProcessingTool()); + } + + /** + * Returns the list of tools available to run. + * @return The list of tools. + */ + public static List getTools() { + return TOOLS; + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/BuildReport.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/BuildReport.java new file mode 100644 index 0000000000000..ba103f87e4eb4 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/BuildReport.java @@ -0,0 +1,188 @@ +package com.azure.sdk.build.tool.models; + +import com.azure.sdk.build.tool.mojo.AzureSdkMojo; +import com.azure.sdk.build.tool.util.AnnotatedMethodCallerResult; +import com.azure.sdk.build.tool.util.MavenUtils; +import com.azure.sdk.build.tool.util.logging.Logger; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.azure.sdk.build.tool.util.MojoUtils.getAllDependencies; + +/** + * The build report that contains detailed information about the build including failure messages, recommended + * changes and Azure SDK usage. + */ +public class BuildReport { + private static final Logger LOGGER = Logger.getInstance(); + + public static final String AZURE_DEPENDENCY_GROUP = "com.azure"; + private static final String AZURE_SDK_BOM_ARTIFACT_ID = "azure-sdk-bom"; + private final List warningMessages; + private final List errorMessages; + private final List failureMessages; + + private List azureDependencies; + private Set serviceMethodCalls; + private Set betaMethodCalls; + private Set outdatedDirectDependencies; + private Set outdatedTransitiveDependencies; + private String bomVersion; + private String jsonReport; + + public BuildReport() { + this.warningMessages = new ArrayList<>(); + this.errorMessages = new ArrayList<>(); + this.failureMessages = new ArrayList<>(); + } + + /** + * The report is concluded once all the tools are run. The tools are designed to withhold all result until + * the conclusion of this report, and then it is the duty of this report to provide the result to the user. + */ + public void conclude() { + if (!warningMessages.isEmpty() && LOGGER.isWarnEnabled()) { + warningMessages.forEach(LOGGER::warn); + } + if (!errorMessages.isEmpty() && LOGGER.isErrorEnabled()) { + errorMessages.forEach(LOGGER::error); + } + this.bomVersion = getBomVersion(); + this.azureDependencies = getAzureDependencies(); + + createJsonReport(); + // we throw a single runtime exception encapsulating all failure messages into one + if (!failureMessages.isEmpty()) { + StringBuilder sb = new StringBuilder("Build failure for the following reasons:\n"); + failureMessages.forEach(s -> sb.append(" - " + s + "\n")); + throw new RuntimeException(sb.toString()); + } + } + + private String getBomVersion() { + DependencyManagement depMgmt = AzureSdkMojo.MOJO.getProject().getDependencyManagement(); + Optional bomDependency = Optional.empty(); + if (depMgmt != null) { + bomDependency = depMgmt.getDependencies().stream() + .filter(d -> d.getArtifactId().equals(AZURE_SDK_BOM_ARTIFACT_ID)) + .findAny(); + } + + if (bomDependency.isPresent()) { + return bomDependency.get().getVersion(); + } + return null; + } + + private void createJsonReport() { + + try { + StringWriter writer = new StringWriter(); + JsonGenerator generator = new JsonFactory().createGenerator(writer).useDefaultPrettyPrinter(); + + generator.writeStartObject(); + generator.writeStringField("group", AzureSdkMojo.MOJO.getProject().getGroupId()); + generator.writeStringField("artifact", AzureSdkMojo.MOJO.getProject().getArtifactId()); + generator.writeStringField("version", AzureSdkMojo.MOJO.getProject().getVersion()); + generator.writeStringField("name", AzureSdkMojo.MOJO.getProject().getName()); + if (this.bomVersion != null && !this.bomVersion.isEmpty()) { + generator.writeStringField("bomVersion", this.bomVersion); + } + if (this.azureDependencies != null && !this.azureDependencies.isEmpty()) { + writeArray("azureDependencies", azureDependencies, generator); + } + + if (this.serviceMethodCalls != null && !this.serviceMethodCalls.isEmpty()) { + writeArray("serviceMethodCalls", serviceMethodCalls + .stream() + .map(AnnotatedMethodCallerResult::toString) + .collect(Collectors.toList()), generator); + } + + if (!this.errorMessages.isEmpty()) { + writeArray("errorMessages", errorMessages, generator); + } + + if (!this.warningMessages.isEmpty()) { + writeArray("warningMessages", warningMessages, generator); + } + + if (!this.failureMessages.isEmpty()) { + writeArray("failureMessages", failureMessages, generator); + } + + generator.writeEndObject(); + generator.close(); + writer.close(); + + this.jsonReport = writer.toString(); + final String reportFileString = AzureSdkMojo.MOJO.getReportFile(); + if (reportFileString != null && !reportFileString.isEmpty()) { + final File reportFile = new File(reportFileString); + try (FileWriter fileWriter = new FileWriter(reportFile)) { + fileWriter.write(this.jsonReport); + } + } + } catch (IOException exception) { + + } + } + + private void writeArray(String fieldName, Collection values, JsonGenerator generator) throws IOException { + generator.writeFieldName(fieldName); + generator.writeStartArray(); + for (String value : values) { + generator.writeString(value); + } + generator.writeEndArray(); + } + + private List getAzureDependencies() { + return getAllDependencies().stream() + // this includes Track 2 mgmt libraries, spring libraries and data plane libraries + .filter(artifact -> artifact.getGroupId().startsWith(AZURE_DEPENDENCY_GROUP)) + .map(MavenUtils::toGAV) + .collect(Collectors.toList()); + } + + public void addWarningMessage(String message) { + warningMessages.add(message); + } + + public void addErrorMessage(String message) { + errorMessages.add(message); + } + + public void addFailureMessage(String message) { + failureMessages.add(message); + } + + public void setServiceMethodCalls(Set serviceMethodCalls) { + this.serviceMethodCalls = serviceMethodCalls; + } + + public void setBetaMethodCalls(Set betaMethodCalls) { + this.betaMethodCalls = betaMethodCalls; + } + + public void setOutdatedDirectDependencies(Set outdatedDirectDependencies) { + this.outdatedDirectDependencies = outdatedDirectDependencies; + } + + public void setOutdatedTransitiveDependencies(Set outdatedTransitiveDependencies) { + this.outdatedTransitiveDependencies = outdatedTransitiveDependencies; + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/OutdatedDependency.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/OutdatedDependency.java new file mode 100644 index 0000000000000..04adb38a9b9b2 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/OutdatedDependency.java @@ -0,0 +1,42 @@ +package com.azure.sdk.build.tool.models; + +import java.util.List; +import java.util.Objects; + +/** + * A model class to represent an outdated dependency and a list of suggested replacements. + */ +public class OutdatedDependency { + private final String gav; + private final List suggestedReplacementGav; + + public OutdatedDependency(final String gav, final List suggestedReplacementGav) { + this.gav = gav; + this.suggestedReplacementGav = suggestedReplacementGav; + } + + public String getGav() { + return gav; + } + + public List getSuggestedReplacementGav() { + return suggestedReplacementGav; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final OutdatedDependency that = (OutdatedDependency) o; + return gav.equals(that.gav); + } + + @Override + public int hashCode() { + return Objects.hash(gav); + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java new file mode 100644 index 0000000000000..d3dc764c85c3b --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java @@ -0,0 +1,129 @@ +package com.azure.sdk.build.tool.mojo; + +import com.azure.sdk.build.tool.Tools; +import com.azure.sdk.build.tool.models.BuildReport; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; + +/** + * Azure SDK build tools Maven plugin Mojo for analyzing Maven configuration of an application to provide Azure + * SDK-specific recommendations. + */ +@Mojo(name = "run", + defaultPhase = LifecyclePhase.PREPARE_PACKAGE, + requiresDependencyCollection = ResolutionScope.RUNTIME, + requiresDependencyResolution = ResolutionScope.RUNTIME) +public class AzureSdkMojo extends AbstractMojo { + public static AzureSdkMojo MOJO; + + @Parameter(defaultValue = "${project}", readonly = true, required = true) + private MavenProject project; + + @Parameter(property = "validateAzureSdkBomUsed", defaultValue = "true") + private boolean validateAzureSdkBomUsed; + + @Parameter(property = "validateNoDeprecatedMicrosoftLibraryUsed", defaultValue = "true") + private boolean validateNoDeprecatedMicrosoftLibraryUsed; + + @Parameter(property = "validateBomVersionsAreUsed", defaultValue = "true") + private boolean validateBomVersionsAreUsed; + + @Parameter(property = "validateNoBetaLibraryUsed", defaultValue = "true") + private boolean validateNoBetaLibraryUsed; + + @Parameter(property = "validateNoBetaAPIUsed", defaultValue = "true") + private boolean validateNoBetaApiUsed; + + @Parameter(property = "reportFile", defaultValue = "") + private String reportFile; + + private final BuildReport buildReport; + + /** + * Creates an instance of Azure SDK build tool Mojo. + */ + public AzureSdkMojo() { + MOJO = this; + this.buildReport = new BuildReport(); + } + + /** + * Returns the build report. + * @return The build report. + */ + public BuildReport getReport() { + return buildReport; + } + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + getLog().info("========================================================================"); + getLog().info("= Running the Azure SDK Maven Build Tool ="); + getLog().info("========================================================================"); + + // Run all of the tools. They will collect their results in the report. + Tools.getTools().forEach(Runnable::run); + + buildReport.conclude(); + } + + /** + * Returns the Maven project. + * @return The Maven project. + */ + public MavenProject getProject() { + return project; + } + + /** + * If this validation is enabled, build is configured to fail if Azure SDK BOM is not used. By default, this is + * set to {@code true}. + * + * @return {@code true} if this validation is enabled. + */ + public boolean isValidateAzureSdkBomUsed() { + return validateAzureSdkBomUsed; + } + + /** + * If this validation is enabled, build will fail if the application uses deprecated Microsoft libraries. By + * default, this is set to {@code true}. + * @return {@code true} if validation is enabled. + */ + public boolean isValidateNoDeprecatedMicrosoftLibraryUsed() { + return validateNoDeprecatedMicrosoftLibraryUsed; + } + + /** + * If this validation is enabled, build will fail if the any dependency overrides the version used in Azure SDK + * BOM. By default, this is set to {@code true}. + * + * @return {@code true} if this validation is enabled. + */ + public boolean isValidateBomVersionsAreUsed() { + return validateBomVersionsAreUsed; + } + + /** + * If this validation is enabled, build will fail if a beta (preview) version of Azure library is used. By + * default, this is set to {@code true}. + * @return {@code true} if this validation is enabled. + */ + public boolean isValidateNoBetaLibraryUsed() { + return validateNoBetaLibraryUsed; + } + + /** + * The report file to which the build report is written to. + * @return The report file. + */ + public String getReportFile() { + return reportFile; + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotatedMethodCallerResult.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotatedMethodCallerResult.java new file mode 100644 index 0000000000000..34426389984d3 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotatedMethodCallerResult.java @@ -0,0 +1,44 @@ +package com.azure.sdk.build.tool.util; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.Objects; + +public class AnnotatedMethodCallerResult { + private final Class annotation; + private final Method annotatedMethod; + private final Member callingMember; + + public AnnotatedMethodCallerResult(final Class annotation, + final Method annotatedMethod, + final Member callingMember) { + this.annotation = Objects.requireNonNull(annotation); + this.annotatedMethod = Objects.requireNonNull(annotatedMethod); + this.callingMember = Objects.requireNonNull(callingMember); + } + + @Override + public String toString() { + return "Method " + annotatedMethod + " is annotated with @" + annotation.getSimpleName() + " and called by " + callingMember; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final AnnotatedMethodCallerResult that = (AnnotatedMethodCallerResult) o; + return annotation.getSimpleName().equals(that.annotation.getSimpleName()) + && annotatedMethod.getName().equals(that.annotatedMethod.getName()) + && callingMember.getName().equals(that.callingMember.getName()); + } + + @Override + public int hashCode() { + return Objects.hash(annotation.getSimpleName(), annotatedMethod.getName(), callingMember.getName()); + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java new file mode 100644 index 0000000000000..6253a0085d60b --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java @@ -0,0 +1,133 @@ +package com.azure.sdk.build.tool.util; + +import com.azure.sdk.build.tool.util.logging.Logger; +import org.reflections8.Reflections; +import org.reflections8.ReflectionsException; +import org.reflections8.scanners.MemberUsageScanner; +import org.reflections8.scanners.MethodAnnotationsScanner; +import org.reflections8.util.ClasspathHelper; +import org.reflections8.util.ConfigurationBuilder; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Utility class to check for annotations. + */ +public final class AnnotationUtils { + private static Logger LOGGER = Logger.getInstance(); + + private AnnotationUtils() { + // no-op + } + + public static ClassLoader getCompleteClassLoader(final Stream paths) { + final List urls = paths.map(AnnotationUtils::pathToUrl).collect(Collectors.toList()); + return URLClassLoader.newInstance(urls.toArray(new URL[0])); + } + + public static Optional> getAnnotation(String name, ClassLoader classLoader) { + try { + return Optional.of(Class.forName(name, false, classLoader).asSubclass(Annotation.class)); + } catch (ClassNotFoundException e) { + LOGGER.info("Unable to find annotation " + name + " in classpath"); + } + return Optional.empty(); + } + + public static Set findCallsToAnnotatedMethod(final Class annotation, + final Stream paths, + final Set interestedPackages, + final boolean recursive) { + + final ConfigurationBuilder config = new ConfigurationBuilder() + .setScanners(new MethodAnnotationsScanner(), new MemberUsageScanner()); + + final List urls = paths.map(AnnotationUtils::pathToUrl).collect(Collectors.toList()); + config.addUrls(urls); + config.addClassLoader(URLClassLoader.newInstance(urls.toArray(new URL[0]))); + + // This is extremely ugly code, but it is necessary as the reflections library throws away the classloader + // I have built above, and so when it goes looking for classes it cannot always find them. What I am doing here + // is augmenting the actual context class loader with the additional urls, so that when the reflections library + // falls back to using the context class loader (which it does by default, because it throws away the proper + // class loader I built above), it can still find the classes I want it to find. + final URLClassLoader contextClassLoader = (URLClassLoader) ClasspathHelper.contextClassLoader(); + try { + final Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + method.setAccessible(true); + for (final URL url : urls) { + method.invoke(contextClassLoader, url); + } + } catch (Exception e) { + e.printStackTrace(); + } + + final Reflections reflections = new Reflections(config); + final Set annotatedMethods = reflections.getMethodsAnnotatedWith(annotation); + final Set results = new HashSet<>(); + + annotatedMethods.forEach(method -> { + checkMethod(reflections, annotation, method, interestedPackages, recursive, results); + }); + + return results; + } + + private static void checkMethod(final Reflections reflections, + final Class annotation, + final Method method, + final Set interestedPackages, + final boolean recursive, + final Set results) { + final Set callingMethods; + try { + callingMethods = reflections.getMethodUsage(method); + } catch (ReflectionsException e) { + LOGGER.info("Unable to get method usage for method " + method.getName() + ". " + e.getMessage()); + return; + } + + callingMethods.forEach(member -> { + // we only add a result if the calling method is in the list of packages we are interested in + if (member instanceof Method) { + final Method methodMember = (Method) member; + final String packageName = methodMember.getDeclaringClass().getPackage().getName(); + + if (interestedPackages.contains(packageName)) { + // we have reached a point where we have found a method call from code in a package + // we are interested in, so we will record it as a valid result. We do not recurse + // further from this method. + results.add(new AnnotatedMethodCallerResult(annotation, method, member)); + } else { + if (recursive) { + // we are looking at code that we know calls an annotated service method, but it is not + // within one of the packages we are interested in. We recurse here, finding all methods + // that call this method, until such time that we run out of methods to check. + checkMethod(reflections, annotation, methodMember, interestedPackages, recursive, results); + } + } + } + }); + } + + private static URL pathToUrl(Path path) { + try { + return path.toUri().toURL(); + } catch (MalformedURLException e) { + LOGGER.info("Path " + path + " cannot be converted to URL. " + e.getMessage()); + return null; + } + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MavenUtils.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MavenUtils.java new file mode 100644 index 0000000000000..67604a9b527a4 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MavenUtils.java @@ -0,0 +1,75 @@ +package com.azure.sdk.build.tool.util; + +import com.azure.sdk.build.tool.util.logging.Logger; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.Dependency; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +public class MavenUtils { + private static final Logger LOGGER = Logger.getInstance(); + + public static String toGAV(Artifact a) { + return a.getGroupId() + ":" + a.getArtifactId() + ":" + a.getVersion(); + } + + /** + * Gets the latest released Azure SDK BOM version from Maven repository. + * @return The latest Azure SDK BOM version or {@code null} if an error occurred while retrieving the latest + * version. + */ + public static String getLatestArtifactVersion(String groupId, String artifactId) { + HttpURLConnection connection = null; + try { + groupId = groupId.replace(".", "/"); + URL url = new URL("https://repo1.maven.org/maven2/" + groupId + "/" + artifactId + "/maven-metadata.xml"); + System.out.println(url); + connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setRequestProperty("accept", "application/xml"); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + int responseCode = connection.getResponseCode(); + if (HttpURLConnection.HTTP_OK == responseCode) { + InputStream responseStream = connection.getInputStream(); + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse(responseStream); + + // Maven-metadata.xml lists versions from oldest to newest. Therefore, we want the bottom-most version + // that is not a beta, preview, etc release. + NodeList versionsList = doc.getElementsByTagName("version"); + String latestVersion = null; + for (int i = versionsList.getLength() - 1; i >=0; i--) { + Node versionNode = versionsList.item(i); + if (!versionNode.getTextContent().contains("beta")) { + latestVersion = versionNode.getTextContent(); + break; + } + } + + LOGGER.info("The latest version for SDK BOM is " + latestVersion); + return latestVersion; + } else { + LOGGER.info("Got a non-successful response for " + artifactId + ": " + responseCode); + } + } catch (Exception exception) { + LOGGER.error("Got error getting latest maven dependency version "); + exception.printStackTrace(); + } finally { + if (connection != null) { + // closes the input streams and discards the socket + connection.disconnect(); + } + } + return null; + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MojoUtils.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MojoUtils.java new file mode 100644 index 0000000000000..805bd44030518 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MojoUtils.java @@ -0,0 +1,45 @@ +package com.azure.sdk.build.tool.util; + +import com.azure.sdk.build.tool.mojo.AzureSdkMojo; +import org.apache.maven.artifact.Artifact; + +import java.text.MessageFormat; +import java.util.List; +import java.util.ResourceBundle; +import java.util.Set; + +/** + * Utility class to perform an miscellaneous Mojo-related operations. + */ +public final class MojoUtils { + + private static final ResourceBundle strings = ResourceBundle.getBundle("strings"); + + private MojoUtils() { + // no-op + } + + @SuppressWarnings("unchecked") + public static Set getDirectDependencies() { + return AzureSdkMojo.MOJO.getProject().getDependencyArtifacts(); + } + + @SuppressWarnings("unchecked") + public static Set getAllDependencies() { + return AzureSdkMojo.MOJO.getProject().getArtifacts(); + } + + @SuppressWarnings("unchecked") + public static List getCompileSourceRoots() { + return ((List)AzureSdkMojo.MOJO.getProject() + .getCompileSourceRoots()); + } + + public static String getString(String key) { + return strings.getString(key); + } + + public static String getString(String key, String... parameters) { + return MessageFormat.format(getString(key), parameters); + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/ConsoleLogger.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/ConsoleLogger.java new file mode 100644 index 0000000000000..9973c47ae85df --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/ConsoleLogger.java @@ -0,0 +1,40 @@ +package com.azure.sdk.build.tool.util.logging; + +/** + * An implementation of {@link Logger} that logs messages to console. + */ +public class ConsoleLogger implements Logger { + private static ConsoleLogger INSTANCE; + + public static Logger getInstance() { + if (INSTANCE == null) { + INSTANCE = new ConsoleLogger(); + } + return INSTANCE; + } + + @Override + public void info(String msg) { + System.out.println(msg); + } + + @Override + public boolean isWarnEnabled() { + return true; + } + + @Override + public void warn(String msg) { + System.err.println(msg); + } + + @Override + public boolean isErrorEnabled() { + return true; + } + + @Override + public void error(String msg) { + System.err.println(msg); + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/Logger.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/Logger.java new file mode 100644 index 0000000000000..0a112491bd47b --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/Logger.java @@ -0,0 +1,47 @@ +package com.azure.sdk.build.tool.util.logging; + +import com.azure.sdk.build.tool.mojo.AzureSdkMojo; + +/** + * A simple logger interface to support enable logging for the Azure SDK build tool. + */ +public interface Logger { + + static Logger getInstance() { + if (AzureSdkMojo.MOJO == null) { + return ConsoleLogger.getInstance(); + } else { + return MojoLogger.getInstance(); + } + } + + /** + * Logs message at info level. + * @param msg The message to log. + */ + void info(String msg); + + /** + * Returns true if logging at warning level is enabled. + * @return {@code true} if logging at warning level is enabled. + */ + boolean isWarnEnabled(); + + /** + * Logs the message at warning level. + * @param msg The message to log. + */ + void warn(String msg); + + /** + * Returns true if logging at error level is enabled. + * @return {@code true} if logging at error level is enabled. + */ + boolean isErrorEnabled(); + + /** + * Logs the message at error level. + * @param msg The message to log. + */ + void error(String msg); +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/MojoLogger.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/MojoLogger.java new file mode 100644 index 0000000000000..39121005f4bd3 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/MojoLogger.java @@ -0,0 +1,48 @@ +package com.azure.sdk.build.tool.util.logging; + +import com.azure.sdk.build.tool.mojo.AzureSdkMojo; +import org.apache.maven.plugin.logging.Log; + +/** + * An implementation of {@link Logger} that uses the Maven plugin logger. + */ +public class MojoLogger implements Logger { + private static MojoLogger INSTANCE; + private Log mojoLog; + + public static Logger getInstance() { + if (INSTANCE == null) { + INSTANCE = new MojoLogger(AzureSdkMojo.MOJO.getLog()); + } + return INSTANCE; + } + + private MojoLogger(Log mojoLog) { + this.mojoLog = mojoLog; + } + + @Override + public void info(String msg) { + mojoLog.info(msg); + } + + @Override + public boolean isWarnEnabled() { + return mojoLog.isWarnEnabled(); + } + + @Override + public void warn(String msg) { + mojoLog.warn(msg); + } + + @Override + public boolean isErrorEnabled() { + return mojoLog.isErrorEnabled(); + } + + @Override + public void error(String msg) { + mojoLog.error(msg); + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties b/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties new file mode 100644 index 0000000000000..4c12bea91b16e --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties @@ -0,0 +1,6 @@ +# Error messages +missingBomDependency = The azure-sdk-bom is not being used! +outdatedBomDependency = The azure-sdk-bom is outdated - consider updating to the latest release! + +deprecatedDirectDependency = A direct dependency of this project relies on a `com.microsoft.*` library. Consider upgrading to the `com.azure.*` library listed below: +deprecatedIndirectDependency = A transitive dependency of this project relies on a `com.microsoft.*` library. Consider checking for a newer release of the following libraries: \ No newline at end of file diff --git a/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml b/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml new file mode 100644 index 0000000000000..527878f1e3354 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml @@ -0,0 +1,76 @@ + + + 4.0.0 + + com.azure.tools + azure-maven-build-tool-test + 1.0.0-SNAPSHOT + + + 8 + 8 + + + + + + + + + + + + + + + com.azure + azure-data-appconfiguration + 1.2.1 + + + + + com.microsoft.azure + azure-cosmosdb + 2.6.13 + + + + com.microsoft.azure + azure-keyvault + 1.2.6 + + + + + io.quarkus + quarkus-jdbc-mssql + 1.13.1.Final + + + + + + + com.azure.tools + azure-sdk-build-tool + 1.0.0-beta.1 + + true + true + true + true + ./azure-sdk-usage-report.txt + + + + + diff --git a/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/src/main/java/com/test/appconfig/AppConfigTestApp.java b/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/src/main/java/com/test/appconfig/AppConfigTestApp.java new file mode 100644 index 0000000000000..993c1db036cb0 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/src/main/java/com/test/appconfig/AppConfigTestApp.java @@ -0,0 +1,20 @@ +package com.test.appconfig; + +import com.azure.data.appconfiguration.ConfigurationClient; +import com.azure.data.appconfiguration.ConfigurationClientBuilder; +import com.azure.data.appconfiguration.models.ConfigurationSetting; + +public class AppConfigTestApp { + public static void main(String[] args) { + final ConfigurationClient configurationClient = new ConfigurationClientBuilder() + .connectionString("foo") + .buildClient(); + + System.out.println("Setting configuration"); + ConfigurationSetting setting = configurationClient.setConfigurationSetting("key", "label", "value"); + System.out.println("Done: " + setting.getLastModified()); + + setting = configurationClient.getConfigurationSetting("key", "label"); + System.out.println("Retrieved setting again, value is " + setting.getValue()); + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/test/java/com/azure/sdk/build/tool/util/AnnotationUtilsTests.java b/sdk/tools/azure-sdk-build-tool/src/test/java/com/azure/sdk/build/tool/util/AnnotationUtilsTests.java new file mode 100644 index 0000000000000..9ace7c25ff70a --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/test/java/com/azure/sdk/build/tool/util/AnnotationUtilsTests.java @@ -0,0 +1,51 @@ +package com.azure.sdk.build.tool.util; + +import com.test.models.AnnotationA; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Stream; + +public class AnnotationUtilsTests { + + @Disabled("Due to classloader issue noted below") + @Test + public void findAnnotationTest() { + final Set interestedPackages = new TreeSet<>(Comparator.comparingInt(String::length)); + Path path = Paths.get("target", "test-classes"); + Stream pathStream = Stream.of(path); + + buildPackageList(path.toFile().getAbsolutePath(), path.toFile().getAbsolutePath(), interestedPackages); + + // This throws ClassCastException because AnnotationUtils has some custom logic to use the URLClassLoader + // java.lang.ClassCastException: class jdk.internal.loader.ClassLoaders$AppClassLoader + // cannot be cast to class java.net.URLClassLoader (jdk.internal.loader.ClassLoaders$AppClassLoader + // and java.net.URLClassLoader are in module java.base of loader 'bootstrap') + Set callsToAnnotatedMethod = AnnotationUtils.findCallsToAnnotatedMethod(AnnotationA.class, pathStream, interestedPackages, true); + } + + static void buildPackageList(String rootDir, String currentDir, Set packages) { + final File directory = new File(currentDir); + + final File[] files = directory.listFiles(); + if (files == null) { + return; + } + + for (final File file : files) { + if (file.isFile()) { + final String path = file.getPath(); + final String packageName = path.substring(rootDir.length() + 1, path.lastIndexOf(File.separator)); + packages.add(packageName.replace(File.separatorChar, '.')); + } else if (file.isDirectory()) { + buildPackageList(rootDir, file.getAbsolutePath(), packages); + } + } + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/test/java/com/test/models/AnnotationA.java b/sdk/tools/azure-sdk-build-tool/src/test/java/com/test/models/AnnotationA.java new file mode 100644 index 0000000000000..3a7ce26170a07 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/test/java/com/test/models/AnnotationA.java @@ -0,0 +1,8 @@ +package com.test.models; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface AnnotationA { +} diff --git a/sdk/tools/azure-sdk-build-tool/src/test/java/com/test/models/ClassA.java b/sdk/tools/azure-sdk-build-tool/src/test/java/com/test/models/ClassA.java new file mode 100644 index 0000000000000..83482381006a4 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/test/java/com/test/models/ClassA.java @@ -0,0 +1,9 @@ +package com.test.models; + +public class ClassA { + + @AnnotationA + public void methodA() { + // no-op + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/test/java/com/test/models/ClassB.java b/sdk/tools/azure-sdk-build-tool/src/test/java/com/test/models/ClassB.java new file mode 100644 index 0000000000000..f370fb8afd84e --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/test/java/com/test/models/ClassB.java @@ -0,0 +1,9 @@ +package com.test.models; + +public class ClassB { + + public void methodB() { + // calls through to ClassA.methodA(), which is annotated + new ClassA().methodA(); + } +} diff --git a/sdk/tools/ci.yml b/sdk/tools/ci.yml index c8e26fc06e803..6d65e147ea428 100644 --- a/sdk/tools/ci.yml +++ b/sdk/tools/ci.yml @@ -5,6 +5,7 @@ trigger: paths: include: - /sdk/tools/azure-sdk-archetype/ + - /sdk/tools/azure-sdk-build-tool/ pr: branches: @@ -16,6 +17,7 @@ pr: paths: include: - /sdk/tools/azure-sdk-archetype/ + - /sdk/tools/azure-sdk-build-tool/ extends: template: /eng/pipelines/templates/stages/archetype-sdk-client.yml @@ -26,3 +28,6 @@ extends: - name: azure-sdk-archetype groupId: com.azure.tools safeName: azuresdkarchetype + - name: azure-sdk-build-tool + groupId: com.azure.tools + safeName: azuresdkbuildtool diff --git a/sdk/tools/pom.xml b/sdk/tools/pom.xml index 4e99e5b20b1a7..b253bde795899 100644 --- a/sdk/tools/pom.xml +++ b/sdk/tools/pom.xml @@ -10,5 +10,6 @@ 1.0.0 azure-sdk-archetype + azure-sdk-build-tool From 687cdeef06df783fac76d16d5238e5189ceab663 Mon Sep 17 00:00:00 2001 From: Srikanta Date: Thu, 27 Jan 2022 11:45:27 -0800 Subject: [PATCH 2/8] update javadocs and log exceptions --- sdk/tools/azure-sdk-build-tool/pom.xml | 18 +++++---------- .../sdk/build/tool/util/AnnotationUtils.java | 10 ++++++++- .../azure/sdk/build/tool/util/MavenUtils.java | 22 ++++++++++++------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/sdk/tools/azure-sdk-build-tool/pom.xml b/sdk/tools/azure-sdk-build-tool/pom.xml index 0e2c8ede7e14b..752043be55d64 100644 --- a/sdk/tools/azure-sdk-build-tool/pom.xml +++ b/sdk/tools/azure-sdk-build-tool/pom.xml @@ -65,17 +65,17 @@ org.apache.maven maven-plugin-api - 3.8.1 + 3.8.1 org.apache.maven.plugin-tools maven-plugin-annotations - 3.6.0 + 3.6.0 org.apache.maven maven-project - 2.2.1 + 2.2.1 junit @@ -86,25 +86,19 @@ com.fasterxml.jackson.dataformat jackson-dataformat-xml - 2.8.2 + 2.8.2 com.github.javaparser javaparser-core - 3.20.2 + 3.20.2 net.oneandone.reflections8 reflections8 - 0.11.7 - - - - org.javassist - javassist - 3.28.0-GA + 0.11.7 diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java index 6253a0085d60b..2237480d00e85 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java @@ -46,6 +46,14 @@ public static Optional> getAnnotation(String name, C return Optional.empty(); } + /** + * Returns a list of methods that call methods that are annotated with the given annotation. + * @param annotation The annotation on the method to look for. + * @param paths The paths to scan. + * @param interestedPackages The packages that this scan should be limited to. + * @param recursive If true, look for packages in the sub-directories of the given paths. + * @return A set of methods that call methods with the annotation. + */ public static Set findCallsToAnnotatedMethod(final Class annotation, final Stream paths, final Set interestedPackages, @@ -71,7 +79,7 @@ public static Set findCallsToAnnotatedMethod(final method.invoke(contextClassLoader, url); } } catch (Exception e) { - e.printStackTrace(); + LOGGER.error("Unable to reflectively call addURL method on URL class. " + e.getMessage()); } final Reflections reflections = new Reflections(config); diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MavenUtils.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MavenUtils.java index 67604a9b527a4..a3460c75bd87f 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MavenUtils.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MavenUtils.java @@ -2,7 +2,6 @@ import com.azure.sdk.build.tool.util.logging.Logger; import org.apache.maven.artifact.Artifact; -import org.apache.maven.model.Dependency; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -13,16 +12,24 @@ import java.net.HttpURLConnection; import java.net.URL; +/** + * Utility class to perform Maven related operations. + */ public class MavenUtils { private static final Logger LOGGER = Logger.getInstance(); - public static String toGAV(Artifact a) { - return a.getGroupId() + ":" + a.getArtifactId() + ":" + a.getVersion(); + /** + * Creates artifact string representation of an artifact. + * @param artifact The artifact. + * @return The string representation of the artifact. + */ + public static String toGAV(Artifact artifact) { + return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion(); } /** - * Gets the latest released Azure SDK BOM version from Maven repository. - * @return The latest Azure SDK BOM version or {@code null} if an error occurred while retrieving the latest + * Gets the latest released version of the given artifact from Maven repository. + * @return The latest version or {@code null} if an error occurred while retrieving the latest * version. */ public static String getLatestArtifactVersion(String groupId, String artifactId) { @@ -56,14 +63,13 @@ public static String getLatestArtifactVersion(String groupId, String artifactId) } } - LOGGER.info("The latest version for SDK BOM is " + latestVersion); + LOGGER.info("The latest version of " + artifactId + " is " + latestVersion); return latestVersion; } else { LOGGER.info("Got a non-successful response for " + artifactId + ": " + responseCode); } } catch (Exception exception) { - LOGGER.error("Got error getting latest maven dependency version "); - exception.printStackTrace(); + LOGGER.error("Got error getting latest maven dependency version. " + exception.getMessage()); } finally { if (connection != null) { // closes the input streams and discards the socket From 45d6e474a87fdedbca848ea8f1cfe608d5b0bf00 Mon Sep 17 00:00:00 2001 From: Srikanta Date: Thu, 27 Jan 2022 12:59:46 -0800 Subject: [PATCH 3/8] check for bom version usage --- .../sdk/build/tool/DependencyCheckerTool.java | 22 +++++++++---------- .../src/main/resources/strings.properties | 3 ++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java index 9c952c1c26300..9b351014acb20 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java @@ -7,6 +7,7 @@ import org.apache.maven.model.Dependency; import org.apache.maven.model.DependencyManagement; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Supplier; @@ -39,9 +40,7 @@ public void run() { checkForBom(); checkForAzureSdkDependencyVersions(); - checkForAzureSdkTransitiveDependencyConflicts(); checkForAzureSdkTrackOneDependencies(); - checkForOutdatedAzureSdkDependencies(); } private void checkForBom() { @@ -66,11 +65,16 @@ private void checkForBom() { } private void checkForAzureSdkDependencyVersions() { - // TODO - } - - private void checkForAzureSdkTransitiveDependencyConflicts() { - // TODO + List dependencies = AzureSdkMojo.MOJO.getProject().getDependencies(); + List dependenciesWithOverriddenVersions = dependencies.stream() + .filter(dependency -> dependency.getGroupId().equals("com.azure")) + .filter(dependency -> { + String version = dependency.getVersion(); + return version != null && !version.isEmpty(); + }).collect(Collectors.toList()); + + dependenciesWithOverriddenVersions.forEach(dependency -> failOrError(AzureSdkMojo.MOJO::isValidateBomVersionsAreUsed, + dependency.getArtifactId() + " " + getString("overrideBomVersion")); } private void checkForAzureSdkTrackOneDependencies() { @@ -114,10 +118,6 @@ private void checkForAzureSdkTrackOneDependencies() { } } - private void checkForOutdatedAzureSdkDependencies() { - // TODO - } - private void failOrError(Supplier condition, String message) { // warn about lack of BOM dependency if (condition.get()) { diff --git a/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties b/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties index 4c12bea91b16e..c0c4174001f6c 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties +++ b/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties @@ -1,6 +1,7 @@ # Error messages missingBomDependency = The azure-sdk-bom is not being used! outdatedBomDependency = The azure-sdk-bom is outdated - consider updating to the latest release! +overrideBomVersion = The azure-sdk-bom version is ignored and a dependency version is explicitly specified . deprecatedDirectDependency = A direct dependency of this project relies on a `com.microsoft.*` library. Consider upgrading to the `com.azure.*` library listed below: -deprecatedIndirectDependency = A transitive dependency of this project relies on a `com.microsoft.*` library. Consider checking for a newer release of the following libraries: \ No newline at end of file +deprecatedIndirectDependency = A transitive dependency of this project relies on a `com.microsoft.*` library. Consider checking for a newer release of the following libraries: From ceac3e3ede37690a250b4a1b78fc5980cadf29ac Mon Sep 17 00:00:00 2001 From: Srikanta Date: Thu, 27 Jan 2022 15:48:40 -0800 Subject: [PATCH 4/8] fix compilation error --- .../java/com/azure/sdk/build/tool/DependencyCheckerTool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java index 9b351014acb20..b548b3769b087 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java @@ -74,7 +74,7 @@ private void checkForAzureSdkDependencyVersions() { }).collect(Collectors.toList()); dependenciesWithOverriddenVersions.forEach(dependency -> failOrError(AzureSdkMojo.MOJO::isValidateBomVersionsAreUsed, - dependency.getArtifactId() + " " + getString("overrideBomVersion")); + dependency.getArtifactId() + " " + getString("overrideBomVersion"))); } private void checkForAzureSdkTrackOneDependencies() { From d3d38e65862b3efc7b54a405650488016d16d8c7 Mon Sep 17 00:00:00 2001 From: Srikanta Date: Wed, 2 Feb 2022 10:17:02 -0800 Subject: [PATCH 5/8] Beta APIs and exception handling --- sdk/tools/azure-sdk-build-tool/README.md | 3 +- sdk/tools/azure-sdk-build-tool/pom.xml | 9 +++++- .../build/tool/AnnotationProcessingTool.java | 27 +++++++++++++---- .../sdk/build/tool/DependencyCheckerTool.java | 30 +++++++++++-------- .../sdk/build/tool/models/BuildReport.java | 14 +++++++++ .../sdk/build/tool/mojo/AzureSdkMojo.java | 9 ++++++ .../sdk/build/tool/util/AnnotationUtils.java | 5 ++-- .../azure/sdk/build/tool/util/MojoUtils.java | 10 +++++++ .../src/main/resources/strings.properties | 2 ++ .../test/azure-sdk-build-tool-test/pom.xml | 26 +++++++++------- .../AppConfigTestApp.java | 5 +++- .../com/test/annotation/BetaApiTestApp.java | 23 ++++++++++++++ 12 files changed, 130 insertions(+), 33 deletions(-) rename sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/src/main/java/com/test/{appconfig => annotation}/AppConfigTestApp.java (91%) create mode 100644 sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/src/main/java/com/test/annotation/BetaApiTestApp.java diff --git a/sdk/tools/azure-sdk-build-tool/README.md b/sdk/tools/azure-sdk-build-tool/README.md index db07ef588eda7..3d950a95374e3 100644 --- a/sdk/tools/azure-sdk-build-tool/README.md +++ b/sdk/tools/azure-sdk-build-tool/README.md @@ -35,6 +35,7 @@ Within the configuration section, it is possible to configure the settings in th | validateNoBetaAPIUsed | true | Azure SDK for Java client libraries sometimes do GA releases with methods annotated with @Beta. This check looks to see if any such methods are being used. | | validateLatestBomVersionUsed | true | Ensures that dependencies are kept up to date by reporting back (or failing the build) if a newer azure-sdk-for-java BOM exists. | | reportFile | "" | (Optional) Specifies the location to write the build report out to, in JSON format. If not specified, no report will be written (and a summary of the build, or the appropriate build failures), will be shown in the terminal. | -After adding the build tool into a Maven project, the tool can be run by calling mvn azure:run. Depending on the configuration provided, you can expect to see build failures or report files generated that can inform you about potential issues before they become more serious. +After adding the build tool into a Maven project, the tool can be run by calling `mvn compile azure:run`. Depending on +the configuration provided, you can expect to see build failures or report files generated that can inform you about potential issues before they become more serious. As the build tool evolves, new releases will be published, and it is recommended that developers frequently check for new releases and update as appropriate. diff --git a/sdk/tools/azure-sdk-build-tool/pom.xml b/sdk/tools/azure-sdk-build-tool/pom.xml index 752043be55d64..6bf769ea030c9 100644 --- a/sdk/tools/azure-sdk-build-tool/pom.xml +++ b/sdk/tools/azure-sdk-build-tool/pom.xml @@ -101,6 +101,13 @@ 0.11.7 + + + com.azure + azure-opentelemetry-exporter-azuremonitor + 1.0.0-beta.3 + + org.junit.jupiter @@ -134,12 +141,12 @@ maven-plugin-plugin 3.6.0 + mojo-descriptor descriptor - diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AnnotationProcessingTool.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AnnotationProcessingTool.java index 88f7fb2ef6eb2..cc847a9729709 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AnnotationProcessingTool.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AnnotationProcessingTool.java @@ -1,12 +1,17 @@ package com.azure.sdk.build.tool; import com.azure.sdk.build.tool.mojo.AzureSdkMojo; +import com.azure.sdk.build.tool.util.AnnotatedMethodCallerResult; import com.azure.sdk.build.tool.util.logging.Logger; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; import java.util.Comparator; +import java.util.Optional; import java.util.Set; import java.util.TreeSet; import java.util.stream.Stream; @@ -14,8 +19,10 @@ import static com.azure.sdk.build.tool.util.AnnotationUtils.findCallsToAnnotatedMethod; import static com.azure.sdk.build.tool.util.AnnotationUtils.getAnnotation; import static com.azure.sdk.build.tool.util.AnnotationUtils.getCompleteClassLoader; +import static com.azure.sdk.build.tool.util.MojoUtils.failOrError; import static com.azure.sdk.build.tool.util.MojoUtils.getAllDependencies; import static com.azure.sdk.build.tool.util.MojoUtils.getCompileSourceRoots; +import static com.azure.sdk.build.tool.util.MojoUtils.getString; /** * Performs the following tasks: @@ -43,14 +50,22 @@ public void run() { // Collect all calls to methods annotated with the Azure SDK @ServiceMethod annotation getAnnotation("com.azure.core.annotation.ServiceMethod", classLoader) - .map(a -> findCallsToAnnotatedMethod(a, getAllPaths(), interestedPackages, true)) - .ifPresent(AzureSdkMojo.MOJO.getReport()::setServiceMethodCalls); - - // Collect all calls to methods annotated with the Azure SDK @Beta annotation - getAnnotation("com.azure.cosmos.util.Beta", classLoader) .map(a -> findCallsToAnnotatedMethod(a, getAllPaths(), interestedPackages, true)) - .ifPresent(AzureSdkMojo.MOJO.getReport()::setBetaMethodCalls); + .ifPresent(AzureSdkMojo.MOJO.getReport()::setServiceMethodCalls); + // Collect all calls to methods annotated with the Azure SDK @Beta annotation + Optional> annotatedMethodCallerResults = getAnnotation("com.azure.cosmos.util.Beta", classLoader) + .map(a -> findCallsToAnnotatedMethod(a, getAllPaths(), interestedPackages, true)); + if (annotatedMethodCallerResults.isPresent()) { + Set betaMethodCallers = annotatedMethodCallerResults.get(); + AzureSdkMojo.MOJO.getReport().setBetaMethodCalls(betaMethodCallers); + if (!betaMethodCallers.isEmpty()) { + StringBuilder message = new StringBuilder(); + message.append(getString("betaApiUsed")).append(System.lineSeparator()); + betaMethodCallers.forEach(method -> message.append(" - ").append(method.toString()).append(System.lineSeparator())); + failOrError(() -> AzureSdkMojo.MOJO.isValidateNoBetaApiUsed(), message.toString()); + } + } } private static Stream getAllPaths() { diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java index b548b3769b087..eb4312f57eaba 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java @@ -6,6 +6,7 @@ import com.azure.sdk.build.tool.util.logging.Logger; import org.apache.maven.model.Dependency; import org.apache.maven.model.DependencyManagement; +import org.apache.maven.model.InputLocation; import java.util.List; import java.util.Optional; @@ -13,6 +14,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; +import static com.azure.sdk.build.tool.util.MojoUtils.failOrError; import static com.azure.sdk.build.tool.util.MojoUtils.getAllDependencies; import static com.azure.sdk.build.tool.util.MojoUtils.getDirectDependencies; import static com.azure.sdk.build.tool.util.MojoUtils.getString; @@ -39,26 +41,38 @@ public void run() { LOGGER.info("Running Dependency Checker Tool"); checkForBom(); - checkForAzureSdkDependencyVersions(); checkForAzureSdkTrackOneDependencies(); } private void checkForBom() { // we are looking for the azure-sdk-bom artifact ID listed as a dependency in the dependency management section DependencyManagement depMgmt = AzureSdkMojo.MOJO.getProject().getDependencyManagement(); + DependencyManagement originalDepMgmt = + AzureSdkMojo.MOJO.getProject().getOriginalModel().getDependencyManagement(); + Optional bomDependency = Optional.empty(); + Optional originalBomDependency = Optional.empty(); if (depMgmt != null) { bomDependency = depMgmt.getDependencies().stream() .filter(d -> d.getArtifactId().equals(AZURE_SDK_BOM_ARTIFACT_ID)) .findAny(); } + if (originalDepMgmt != null) { + originalBomDependency = originalDepMgmt.getDependencies().stream() + .filter(d -> d.getArtifactId().equals(AZURE_SDK_BOM_ARTIFACT_ID)) + .findAny(); + } + + bomDependency = bomDependency.isPresent() ? bomDependency : originalBomDependency; + if (bomDependency.isPresent()) { String latestAvailableBomVersion = MavenUtils.getLatestArtifactVersion("com.azure", "azure-sdk-bom"); boolean isLatestBomVersion = bomDependency.get().getVersion().equals(latestAvailableBomVersion); if (!isLatestBomVersion) { failOrError(AzureSdkMojo.MOJO::isValidateAzureSdkBomUsed, getString("outdatedBomDependency")); } + checkForAzureSdkDependencyVersions(); } else { failOrError(AzureSdkMojo.MOJO::isValidateAzureSdkBomUsed, getString("missingBomDependency")); } @@ -69,8 +83,9 @@ private void checkForAzureSdkDependencyVersions() { List dependenciesWithOverriddenVersions = dependencies.stream() .filter(dependency -> dependency.getGroupId().equals("com.azure")) .filter(dependency -> { - String version = dependency.getVersion(); - return version != null && !version.isEmpty(); + InputLocation location = dependency.getLocation("version"); + // if the version is not coming from Azure SDK BOM, filter those dependencies + return !location.getSource().getModelId().startsWith("com.azure:azure-sdk-bom"); }).collect(Collectors.toList()); dependenciesWithOverriddenVersions.forEach(dependency -> failOrError(AzureSdkMojo.MOJO::isValidateBomVersionsAreUsed, @@ -117,13 +132,4 @@ private void checkForAzureSdkTrackOneDependencies() { failOrError(AzureSdkMojo.MOJO::isValidateNoDeprecatedMicrosoftLibraryUsed, message); } } - - private void failOrError(Supplier condition, String message) { - // warn about lack of BOM dependency - if (condition.get()) { - AzureSdkMojo.MOJO.getReport().addFailureMessage(message); - } else { - AzureSdkMojo.MOJO.getReport().addErrorMessage(message); - } - } } diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/BuildReport.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/BuildReport.java index ba103f87e4eb4..450b90077d914 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/BuildReport.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/BuildReport.java @@ -1,11 +1,16 @@ package com.azure.sdk.build.tool.models; +import com.azure.opentelemetry.exporter.azuremonitor.AzureMonitorExporter; +import com.azure.opentelemetry.exporter.azuremonitor.AzureMonitorExporterBuilder; import com.azure.sdk.build.tool.mojo.AzureSdkMojo; import com.azure.sdk.build.tool.util.AnnotatedMethodCallerResult; import com.azure.sdk.build.tool.util.MavenUtils; import com.azure.sdk.build.tool.util.logging.Logger; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; +import edu.emory.mathcs.backport.java.util.Collections; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; import org.apache.maven.model.Dependency; import org.apache.maven.model.DependencyManagement; @@ -18,6 +23,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static com.azure.sdk.build.tool.util.MojoUtils.getAllDependencies; @@ -112,6 +118,14 @@ private void createJsonReport() { .collect(Collectors.toList()), generator); } + if (this.betaMethodCalls != null && !this.betaMethodCalls.isEmpty()) { + writeArray("betaMethodCalls", betaMethodCalls + .stream() + .map(AnnotatedMethodCallerResult::toString) + .collect(Collectors.toList()), generator); + } + + if (!this.errorMessages.isEmpty()) { writeArray("errorMessages", errorMessages, generator); } diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java index d3dc764c85c3b..06e58ec879791 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java @@ -119,6 +119,15 @@ public boolean isValidateNoBetaLibraryUsed() { return validateNoBetaLibraryUsed; } + /** + * If this validation is enabled, build will fail if any method annotated with @Beta is called. By + * default, this is set to {@code true}. + * @return {@code true} if this validation is enabled. + */ + public boolean isValidateNoBetaApiUsed() { + return validateNoBetaApiUsed; + } + /** * The report file to which the build report is written to. * @return The report file. diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java index 2237480d00e85..a9a9d14f8ac6b 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java @@ -119,7 +119,7 @@ private static void checkMethod(final Reflections reflections, // further from this method. results.add(new AnnotatedMethodCallerResult(annotation, method, member)); } else { - if (recursive) { + if (recursive && !methodMember.equals(method)) { // we are looking at code that we know calls an annotated service method, but it is not // within one of the packages we are interested in. We recurse here, finding all methods // that call this method, until such time that we run out of methods to check. @@ -132,7 +132,8 @@ private static void checkMethod(final Reflections reflections, private static URL pathToUrl(Path path) { try { - return path.toUri().toURL(); + URL url = path.toUri().toURL(); + return url; } catch (MalformedURLException e) { LOGGER.info("Path " + path + " cannot be converted to URL. " + e.getMessage()); return null; diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MojoUtils.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MojoUtils.java index 805bd44030518..2e630771fc6bd 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MojoUtils.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MojoUtils.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.ResourceBundle; import java.util.Set; +import java.util.function.Supplier; /** * Utility class to perform an miscellaneous Mojo-related operations. @@ -42,4 +43,13 @@ public static String getString(String key) { public static String getString(String key, String... parameters) { return MessageFormat.format(getString(key), parameters); } + + public static void failOrError(Supplier condition, String message) { + // warn about lack of BOM dependency + if (condition.get()) { + AzureSdkMojo.MOJO.getReport().addFailureMessage(message); + } else { + AzureSdkMojo.MOJO.getReport().addErrorMessage(message); + } + } } diff --git a/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties b/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties index c0c4174001f6c..48187a6832f87 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties +++ b/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties @@ -5,3 +5,5 @@ overrideBomVersion = The azure-sdk-bom version is ignored and a dependency versi deprecatedDirectDependency = A direct dependency of this project relies on a `com.microsoft.*` library. Consider upgrading to the `com.azure.*` library listed below: deprecatedIndirectDependency = A transitive dependency of this project relies on a `com.microsoft.*` library. Consider checking for a newer release of the following libraries: + +betaApiUsed = A method annotated with Beta is called! diff --git a/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml b/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml index 527878f1e3354..64f26bbc5cc87 100644 --- a/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml +++ b/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml @@ -13,21 +13,27 @@ 8 - - - - - - - - - + + + + com.azure + azure-sdk-bom + 1.0.5 + pom + import + + + com.azure azure-data-appconfiguration - 1.2.1 + + + + com.azure + azure-cosmos - com.github.javaparser javaparser-core @@ -101,11 +96,10 @@ 0.11.7 - com.azure - azure-opentelemetry-exporter-azuremonitor - 1.0.0-beta.3 + azure-monitor-opentelemetry-exporter + 1.0.0-beta.5 @@ -134,12 +128,12 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 2.22.2 org.apache.maven.plugins maven-plugin-plugin - 3.6.0 + 3.6.0 diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java index eb4312f57eaba..9ed8989ef74a7 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java @@ -11,7 +11,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import static com.azure.sdk.build.tool.util.MojoUtils.failOrError; @@ -90,6 +89,14 @@ private void checkForAzureSdkDependencyVersions() { dependenciesWithOverriddenVersions.forEach(dependency -> failOrError(AzureSdkMojo.MOJO::isValidateBomVersionsAreUsed, dependency.getArtifactId() + " " + getString("overrideBomVersion"))); + + List betaDependencies = dependencies.stream() + .filter(dependency -> dependency.getGroupId().equals("com.azure")) + .filter(dependency -> dependency.getVersion().contains("-beta")) + .collect(Collectors.toList()); + + betaDependencies.forEach(dependency -> failOrError(AzureSdkMojo.MOJO::isValidateNoBetaLibraryUsed, + dependency.getArtifactId() + " " + getString("betaDependencyUsed"))); } private void checkForAzureSdkTrackOneDependencies() { diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/ReportGenerator.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/ReportGenerator.java new file mode 100644 index 0000000000000..4d1c23510c64c --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/ReportGenerator.java @@ -0,0 +1,150 @@ +package com.azure.sdk.build.tool; + +import com.azure.sdk.build.tool.models.BuildReport; +import com.azure.sdk.build.tool.mojo.AzureSdkMojo; +import com.azure.sdk.build.tool.util.AnnotatedMethodCallerResult; +import com.azure.sdk.build.tool.util.MavenUtils; +import com.azure.sdk.build.tool.util.logging.Logger; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static com.azure.sdk.build.tool.util.MojoUtils.getAllDependencies; + +/** + * The tool to generate the final report of the build. + */ +public class ReportGenerator { + private static final Logger LOGGER = Logger.getInstance(); + private static final String AZURE_DEPENDENCY_GROUP = "com.azure"; + private static final String AZURE_SDK_BOM_ARTIFACT_ID = "azure-sdk-bom"; + private final BuildReport report; + + public ReportGenerator(BuildReport report) { + this.report = report; + } + + public void generateReport() { + if (!report.getWarningMessages().isEmpty() && LOGGER.isWarnEnabled()) { + report.getWarningMessages().forEach(LOGGER::warn); + } + if (!report.getErrorMessages().isEmpty() && LOGGER.isErrorEnabled()) { + report.getErrorMessages().forEach(LOGGER::error); + } + report.setBomVersion(computeBomVersion()); + report.setAzureDependencies(computeAzureDependencies()); + + createJsonReport(); + // we throw a single runtime exception encapsulating all failure messages into one + if (!report.getFailureMessages().isEmpty()) { + StringBuilder sb = new StringBuilder("Build failure for the following reasons:\n"); + report.getFailureMessages().forEach(s -> sb.append(" - " + s + "\n")); + throw new RuntimeException(sb.toString()); + } + } + + private String computeBomVersion() { + DependencyManagement depMgmt = AzureSdkMojo.MOJO.getProject().getDependencyManagement(); + Optional bomDependency = Optional.empty(); + if (depMgmt != null) { + bomDependency = depMgmt.getDependencies().stream() + .filter(d -> d.getArtifactId().equals(AZURE_SDK_BOM_ARTIFACT_ID)) + .findAny(); + } + + if (bomDependency.isPresent()) { + return bomDependency.get().getVersion(); + } + return null; + } + + private void createJsonReport() { + + try { + StringWriter writer = new StringWriter(); + JsonGenerator generator = new JsonFactory().createGenerator(writer).useDefaultPrettyPrinter(); + + generator.writeStartObject(); + generator.writeStringField("group", AzureSdkMojo.MOJO.getProject().getGroupId()); + generator.writeStringField("artifact", AzureSdkMojo.MOJO.getProject().getArtifactId()); + generator.writeStringField("version", AzureSdkMojo.MOJO.getProject().getVersion()); + generator.writeStringField("name", AzureSdkMojo.MOJO.getProject().getName()); + if (report.getBomVersion() != null && !report.getBomVersion().isEmpty()) { + generator.writeStringField("bomVersion", report.getBomVersion()); + } + if (report.getAzureDependencies() != null && !report.getAzureDependencies().isEmpty()) { + writeArray("azureDependencies", report.getAzureDependencies(), generator); + } + + if (report.getServiceMethodCalls() != null && !report.getServiceMethodCalls().isEmpty()) { + writeArray("serviceMethodCalls", report.getServiceMethodCalls() + .stream() + .map(AnnotatedMethodCallerResult::toString) + .collect(Collectors.toList()), generator); + } + + if (report.getBetaMethodCalls() != null && !report.getBetaMethodCalls().isEmpty()) { + writeArray("betaMethodCalls", report.getBetaMethodCalls() + .stream() + .map(AnnotatedMethodCallerResult::toString) + .collect(Collectors.toList()), generator); + } + + + if (!report.getErrorMessages().isEmpty()) { + writeArray("errorMessages", report.getErrorMessages(), generator); + } + + if (!report.getWarningMessages().isEmpty()) { + writeArray("warningMessages", report.getWarningMessages(), generator); + } + + if (!report.getFailureMessages().isEmpty()) { + writeArray("failureMessages", report.getFailureMessages(), generator); + } + + generator.writeEndObject(); + generator.close(); + writer.close(); + + report.setJsonReport(writer.toString()); + final String reportFileString = AzureSdkMojo.MOJO.getReportFile(); + if (reportFileString != null && !reportFileString.isEmpty()) { + final File reportFile = new File(reportFileString); + try (FileWriter fileWriter = new FileWriter(reportFile)) { + fileWriter.write(report.getJsonReport()); + } + } + } catch (IOException exception) { + + } + } + + private void writeArray(String fieldName, Collection values, JsonGenerator generator) throws IOException { + generator.writeFieldName(fieldName); + generator.writeStartArray(); + for (String value : values) { + generator.writeString(value); + } + generator.writeEndArray(); + } + + private List computeAzureDependencies() { + return getAllDependencies().stream() + // this includes Track 2 mgmt libraries, spring libraries and data plane libraries + .filter(artifact -> artifact.getGroupId().startsWith(AZURE_DEPENDENCY_GROUP)) + .map(MavenUtils::toGAV) + .collect(Collectors.toList()); + } +} + diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/BuildReport.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/BuildReport.java index 450b90077d914..725813775c525 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/BuildReport.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/BuildReport.java @@ -1,42 +1,17 @@ package com.azure.sdk.build.tool.models; -import com.azure.opentelemetry.exporter.azuremonitor.AzureMonitorExporter; -import com.azure.opentelemetry.exporter.azuremonitor.AzureMonitorExporterBuilder; -import com.azure.sdk.build.tool.mojo.AzureSdkMojo; import com.azure.sdk.build.tool.util.AnnotatedMethodCallerResult; -import com.azure.sdk.build.tool.util.MavenUtils; import com.azure.sdk.build.tool.util.logging.Logger; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; -import edu.emory.mathcs.backport.java.util.Collections; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.trace.data.SpanData; -import org.apache.maven.model.Dependency; -import org.apache.maven.model.DependencyManagement; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.StringWriter; + import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.Optional; import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import static com.azure.sdk.build.tool.util.MojoUtils.getAllDependencies; /** * The build report that contains detailed information about the build including failure messages, recommended * changes and Azure SDK usage. */ public class BuildReport { - private static final Logger LOGGER = Logger.getInstance(); - - public static final String AZURE_DEPENDENCY_GROUP = "com.azure"; - private static final String AZURE_SDK_BOM_ARTIFACT_ID = "azure-sdk-bom"; private final List warningMessages; private final List errorMessages; private final List failureMessages; @@ -49,127 +24,30 @@ public class BuildReport { private String bomVersion; private String jsonReport; + public List getWarningMessages() { + return warningMessages; + } + + public List getErrorMessages() { + return errorMessages; + } + + public List getFailureMessages() { + return failureMessages; + } + public BuildReport() { this.warningMessages = new ArrayList<>(); this.errorMessages = new ArrayList<>(); this.failureMessages = new ArrayList<>(); } - /** - * The report is concluded once all the tools are run. The tools are designed to withhold all result until - * the conclusion of this report, and then it is the duty of this report to provide the result to the user. - */ - public void conclude() { - if (!warningMessages.isEmpty() && LOGGER.isWarnEnabled()) { - warningMessages.forEach(LOGGER::warn); - } - if (!errorMessages.isEmpty() && LOGGER.isErrorEnabled()) { - errorMessages.forEach(LOGGER::error); - } - this.bomVersion = getBomVersion(); - this.azureDependencies = getAzureDependencies(); - - createJsonReport(); - // we throw a single runtime exception encapsulating all failure messages into one - if (!failureMessages.isEmpty()) { - StringBuilder sb = new StringBuilder("Build failure for the following reasons:\n"); - failureMessages.forEach(s -> sb.append(" - " + s + "\n")); - throw new RuntimeException(sb.toString()); - } - } - - private String getBomVersion() { - DependencyManagement depMgmt = AzureSdkMojo.MOJO.getProject().getDependencyManagement(); - Optional bomDependency = Optional.empty(); - if (depMgmt != null) { - bomDependency = depMgmt.getDependencies().stream() - .filter(d -> d.getArtifactId().equals(AZURE_SDK_BOM_ARTIFACT_ID)) - .findAny(); - } - - if (bomDependency.isPresent()) { - return bomDependency.get().getVersion(); - } - return null; - } - - private void createJsonReport() { - - try { - StringWriter writer = new StringWriter(); - JsonGenerator generator = new JsonFactory().createGenerator(writer).useDefaultPrettyPrinter(); - - generator.writeStartObject(); - generator.writeStringField("group", AzureSdkMojo.MOJO.getProject().getGroupId()); - generator.writeStringField("artifact", AzureSdkMojo.MOJO.getProject().getArtifactId()); - generator.writeStringField("version", AzureSdkMojo.MOJO.getProject().getVersion()); - generator.writeStringField("name", AzureSdkMojo.MOJO.getProject().getName()); - if (this.bomVersion != null && !this.bomVersion.isEmpty()) { - generator.writeStringField("bomVersion", this.bomVersion); - } - if (this.azureDependencies != null && !this.azureDependencies.isEmpty()) { - writeArray("azureDependencies", azureDependencies, generator); - } - - if (this.serviceMethodCalls != null && !this.serviceMethodCalls.isEmpty()) { - writeArray("serviceMethodCalls", serviceMethodCalls - .stream() - .map(AnnotatedMethodCallerResult::toString) - .collect(Collectors.toList()), generator); - } - - if (this.betaMethodCalls != null && !this.betaMethodCalls.isEmpty()) { - writeArray("betaMethodCalls", betaMethodCalls - .stream() - .map(AnnotatedMethodCallerResult::toString) - .collect(Collectors.toList()), generator); - } - - - if (!this.errorMessages.isEmpty()) { - writeArray("errorMessages", errorMessages, generator); - } - - if (!this.warningMessages.isEmpty()) { - writeArray("warningMessages", warningMessages, generator); - } - - if (!this.failureMessages.isEmpty()) { - writeArray("failureMessages", failureMessages, generator); - } - - generator.writeEndObject(); - generator.close(); - writer.close(); - - this.jsonReport = writer.toString(); - final String reportFileString = AzureSdkMojo.MOJO.getReportFile(); - if (reportFileString != null && !reportFileString.isEmpty()) { - final File reportFile = new File(reportFileString); - try (FileWriter fileWriter = new FileWriter(reportFile)) { - fileWriter.write(this.jsonReport); - } - } - } catch (IOException exception) { - - } - } - - private void writeArray(String fieldName, Collection values, JsonGenerator generator) throws IOException { - generator.writeFieldName(fieldName); - generator.writeStartArray(); - for (String value : values) { - generator.writeString(value); - } - generator.writeEndArray(); - } - - private List getAzureDependencies() { - return getAllDependencies().stream() - // this includes Track 2 mgmt libraries, spring libraries and data plane libraries - .filter(artifact -> artifact.getGroupId().startsWith(AZURE_DEPENDENCY_GROUP)) - .map(MavenUtils::toGAV) - .collect(Collectors.toList()); + public String getBomVersion() { + return this.bomVersion; + } + + public List getAzureDependencies() { + return this.azureDependencies; } public void addWarningMessage(String message) { @@ -199,4 +77,28 @@ public void setOutdatedDirectDependencies(Set outdatedDirect public void setOutdatedTransitiveDependencies(Set outdatedTransitiveDependencies) { this.outdatedTransitiveDependencies = outdatedTransitiveDependencies; } + + public void setBomVersion(String bomVersion) { + this.bomVersion = bomVersion; + } + + public void setAzureDependencies(List azureDependencies) { + this.azureDependencies = azureDependencies; + } + + public void setJsonReport(String jsonReport) { + this.jsonReport = jsonReport; + } + + public String getJsonReport() { + return jsonReport; + } + + public Set getServiceMethodCalls() { + return this.serviceMethodCalls; + } + + public Set getBetaMethodCalls() { + return this.betaMethodCalls; + } } diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/OutdatedDependency.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/OutdatedDependency.java index 04adb38a9b9b2..88f604b77719d 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/OutdatedDependency.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/OutdatedDependency.java @@ -10,15 +10,28 @@ public class OutdatedDependency { private final String gav; private final List suggestedReplacementGav; + /** + * Creates an instance of {@link OutdatedDependency}. + * @param gav The group, artifact and version string. + * @param suggestedReplacementGav The suggested replacement for the outdated dependency. + */ public OutdatedDependency(final String gav, final List suggestedReplacementGav) { this.gav = gav; this.suggestedReplacementGav = suggestedReplacementGav; } + /** + * Returns the group, artifact and version string for the outdated dependency. + * @return The group, artifact and version string. + */ public String getGav() { return gav; } + /** + * Returns the list of suggested replacements for the outdated dependency. + * @return The list of suggested replacements for the outdated dependency. + */ public List getSuggestedReplacementGav() { return suggestedReplacementGav; } diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/PingSpanData.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/PingSpanData.java new file mode 100644 index 0000000000000..6530e8291b7e2 --- /dev/null +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/models/PingSpanData.java @@ -0,0 +1,136 @@ +package com.azure.sdk.build.tool.models; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.sdk.common.InstrumentationLibraryInfo; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.data.EventData; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.data.StatusData; + +import java.time.Instant; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +/** + * Sends a ping to Application Insights when the build tool is run. + */ +public class PingSpanData implements SpanData { + + private final String traceId; + private final String spanId; + private final long startEpochNanos; + private final long endEpochNanos; + + /** + * Creates an instance of ping span to export to Application Insights. + */ + public PingSpanData() { + this.traceId = UUID.randomUUID().toString(); + this.spanId = UUID.randomUUID().toString(); + this.startEpochNanos = MILLISECONDS.toNanos(Instant.now().toEpochMilli()); + this.endEpochNanos = MILLISECONDS.toNanos(Instant.now().toEpochMilli()); + } + + @Override + public SpanContext getSpanContext() { + return SpanContext.create(traceId, spanId, TraceFlags.getDefault(), TraceState.getDefault()); + } + + @Override + public String getTraceId() { + return this.traceId; + } + + @Override + public String getSpanId() { + return this.spanId; + } + + @Override + public SpanContext getParentSpanContext() { + return SpanContext.create(traceId, spanId, TraceFlags.getDefault(), TraceState.getDefault()); + } + + @Override + public String getParentSpanId() { + return SpanData.super.getParentSpanId(); + } + + @Override + public Resource getResource() { + return null; + } + + @Override + public InstrumentationLibraryInfo getInstrumentationLibraryInfo() { + return InstrumentationLibraryInfo.create("AzureSDKMavenBuildTool", "1"); + } + + @Override + public String getName() { + return "azsdk-maven-build-tool"; + } + + @Override + public SpanKind getKind() { + return SpanKind.INTERNAL; + } + + @Override + public long getStartEpochNanos() { + return this.startEpochNanos; + } + + @Override + public Attributes getAttributes() { + return Attributes.builder().build(); + } + + @Override + public List getEvents() { + return Collections.emptyList(); + } + + @Override + public List getLinks() { + return Collections.emptyList(); + } + + @Override + public StatusData getStatus() { + return StatusData.ok(); + } + + @Override + public long getEndEpochNanos() { + return this.endEpochNanos; + } + + @Override + public boolean hasEnded() { + return false; + } + + @Override + public int getTotalRecordedEvents() { + return 0; + } + + @Override + public int getTotalRecordedLinks() { + return 0; + } + + @Override + public int getTotalAttributeCount() { + return 0; + } +} diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java index 06e58ec879791..9d6f42e82e057 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java @@ -1,7 +1,14 @@ package com.azure.sdk.build.tool.mojo; +import com.azure.monitor.opentelemetry.exporter.AzureMonitorExporterBuilder; +import com.azure.monitor.opentelemetry.exporter.AzureMonitorTraceExporter; +import com.azure.sdk.build.tool.ReportGenerator; import com.azure.sdk.build.tool.Tools; import com.azure.sdk.build.tool.models.BuildReport; +import com.azure.sdk.build.tool.models.PingSpanData; +import com.azure.sdk.build.tool.util.logging.Logger; +import edu.emory.mathcs.backport.java.util.Collections; +import io.opentelemetry.sdk.common.CompletableResultCode; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -11,6 +18,8 @@ import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; +import java.util.concurrent.TimeUnit; + /** * Azure SDK build tools Maven plugin Mojo for analyzing Maven configuration of an application to provide Azure * SDK-specific recommendations. @@ -20,8 +29,14 @@ requiresDependencyCollection = ResolutionScope.RUNTIME, requiresDependencyResolution = ResolutionScope.RUNTIME) public class AzureSdkMojo extends AbstractMojo { + public static AzureSdkMojo MOJO; + private static final Logger LOGGER = Logger.getInstance(); + + // TODO: Create an App Insights resource that can be used for collecting ping data + private static final String APP_INSIGHTS_CONNECTION_STRING = ""; + @Parameter(defaultValue = "${project}", readonly = true, required = true) private MavenProject project; @@ -37,12 +52,15 @@ public class AzureSdkMojo extends AbstractMojo { @Parameter(property = "validateNoBetaLibraryUsed", defaultValue = "true") private boolean validateNoBetaLibraryUsed; - @Parameter(property = "validateNoBetaAPIUsed", defaultValue = "true") + @Parameter(property = "validateNoBetaApiUsed", defaultValue = "true") private boolean validateNoBetaApiUsed; @Parameter(property = "reportFile", defaultValue = "") private String reportFile; + @Parameter(property = "sendToMicrosoft", defaultValue = "true") + private boolean sendToMicrosoft; + private final BuildReport buildReport; /** @@ -68,9 +86,37 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("========================================================================"); // Run all of the tools. They will collect their results in the report. + if (sendToMicrosoft) { + // front-load pinging App Insights asynchronously to avoid any blocking at the end of the plugin execution + pingAppInsights(); + } Tools.getTools().forEach(Runnable::run); + ReportGenerator reportGenerator = new ReportGenerator(buildReport); + reportGenerator.generateReport(); + } - buildReport.conclude(); + private void pingAppInsights() { + try { + LOGGER.info("Sending ping message to Application Insights"); + AzureMonitorTraceExporter azureMonitorExporter = new AzureMonitorExporterBuilder() + .connectionString(APP_INSIGHTS_CONNECTION_STRING) + .buildTraceExporter(); + + PingSpanData pingSpanData = new PingSpanData(); + CompletableResultCode completionCode = + azureMonitorExporter.export(Collections.singletonList(pingSpanData)).join(30, TimeUnit.SECONDS); + if (completionCode.isSuccess()) { + LOGGER.info("Successfully sent ping message to Application Insights"); + } else { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Failed to send ping message to Application Insights"); + } + } + } catch (Exception ex) { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Unable to send ping message to Application Insights. " + ex.getMessage()); + } + } } /** diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java index a9a9d14f8ac6b..c169ef2318ffc 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/AnnotationUtils.java @@ -79,7 +79,9 @@ public static Set findCallsToAnnotatedMethod(final method.invoke(contextClassLoader, url); } } catch (Exception e) { - LOGGER.error("Unable to reflectively call addURL method on URL class. " + e.getMessage()); + if (LOGGER.isErrorEnabled()) { + LOGGER.error("Unable to reflectively call addURL method on URL class. " + e.getMessage()); + } } final Reflections reflections = new Reflections(config); diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MavenUtils.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MavenUtils.java index a3460c75bd87f..92eeb5ff8e86b 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MavenUtils.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MavenUtils.java @@ -37,7 +37,9 @@ public static String getLatestArtifactVersion(String groupId, String artifactId) try { groupId = groupId.replace(".", "/"); URL url = new URL("https://repo1.maven.org/maven2/" + groupId + "/" + artifactId + "/maven-metadata.xml"); - System.out.println(url); + if (LOGGER.isVerboseEnabled()) { + LOGGER.verbose(url.toString()); + } connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setRequestProperty("accept", "application/xml"); @@ -63,13 +65,19 @@ public static String getLatestArtifactVersion(String groupId, String artifactId) } } - LOGGER.info("The latest version of " + artifactId + " is " + latestVersion); + if (LOGGER.isVerboseEnabled()) { + LOGGER.verbose("The latest version of " + artifactId + " is " + latestVersion); + } return latestVersion; } else { - LOGGER.info("Got a non-successful response for " + artifactId + ": " + responseCode); + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Got a non-successful response for " + artifactId + ": " + responseCode); + } } } catch (Exception exception) { - LOGGER.error("Got error getting latest maven dependency version. " + exception.getMessage()); + if (LOGGER.isErrorEnabled()) { + LOGGER.error("Got error getting latest maven dependency version. " + exception.getMessage()); + } } finally { if (connection != null) { // closes the input streams and discards the socket diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/ConsoleLogger.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/ConsoleLogger.java index 9973c47ae85df..c62cf17a5dada 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/ConsoleLogger.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/ConsoleLogger.java @@ -37,4 +37,14 @@ public boolean isErrorEnabled() { public void error(String msg) { System.err.println(msg); } + + @Override + public boolean isVerboseEnabled() { + return false; + } + + @Override + public void verbose(String msg) { + System.out.println(msg); + } } diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/Logger.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/Logger.java index 0a112491bd47b..90c7a1e6128be 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/Logger.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/Logger.java @@ -44,4 +44,17 @@ static Logger getInstance() { * @param msg The message to log. */ void error(String msg); + + + /** + * Returns true if logging at verbose level is enabled. + * @return {@code true} if logging at verbose level is enabled. + */ + boolean isVerboseEnabled(); + + /** + * Logs the message at verbose level. + * @param msg The message to log. + */ + void verbose(String msg); } diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/MojoLogger.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/MojoLogger.java index 39121005f4bd3..06ab50e867637 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/MojoLogger.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/logging/MojoLogger.java @@ -45,4 +45,14 @@ public boolean isErrorEnabled() { public void error(String msg) { mojoLog.error(msg); } + + @Override + public boolean isVerboseEnabled() { + return mojoLog.isDebugEnabled(); + } + + @Override + public void verbose(String msg) { + mojoLog.debug(msg); + } } diff --git a/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties b/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties index 48187a6832f87..f6a514055d913 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties +++ b/sdk/tools/azure-sdk-build-tool/src/main/resources/strings.properties @@ -1,7 +1,8 @@ # Error messages missingBomDependency = The azure-sdk-bom is not being used! outdatedBomDependency = The azure-sdk-bom is outdated - consider updating to the latest release! -overrideBomVersion = The azure-sdk-bom version is ignored and a dependency version is explicitly specified . +overrideBomVersion = The azure-sdk-bom version is ignored and a dependency version is explicitly specified. +betaDependencyUsed = A beta dependency is used. deprecatedDirectDependency = A direct dependency of this project relies on a `com.microsoft.*` library. Consider upgrading to the `com.azure.*` library listed below: deprecatedIndirectDependency = A transitive dependency of this project relies on a `com.microsoft.*` library. Consider checking for a newer release of the following libraries: diff --git a/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml b/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml index 64f26bbc5cc87..7f026d9f0e2c1 100644 --- a/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml +++ b/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml @@ -36,6 +36,13 @@ azure-cosmos + + + com.azure + azure-security-keyvault-keys + 4.3.6 + + + + com.azure + azure-monitor-opentelemetry-exporter + 1.0.0-beta.5 + @@ -70,10 +84,11 @@ azure-sdk-build-tool 1.0.0-beta.1 - true - true - true - true + false + false + false + false + false ./azure-sdk-usage-report.txt From be68077fcfc746516d591cb725f25fc364b3cbff Mon Sep 17 00:00:00 2001 From: Srikanta Date: Fri, 11 Feb 2022 15:44:40 -0800 Subject: [PATCH 7/8] Update tests and log warnings --- eng/.docsettings.yml | 1 + sdk/tools/azure-sdk-build-tool/pom.xml | 2 +- .../sdk/build/tool/AnnotationProcessingTool.java | 7 ++----- .../sdk/build/tool/DependencyCheckerTool.java | 14 +++++++------- .../azure/sdk/build/tool/mojo/AzureSdkMojo.java | 3 --- .../com/azure/sdk/build/tool/util/MojoUtils.java | 5 ++--- .../src/test/azure-sdk-build-tool-test/pom.xml | 10 +++++----- 7 files changed, 18 insertions(+), 24 deletions(-) diff --git a/eng/.docsettings.yml b/eng/.docsettings.yml index ad4fb4901e3b7..eaf4d645baa89 100644 --- a/eng/.docsettings.yml +++ b/eng/.docsettings.yml @@ -137,6 +137,7 @@ known_content_issues: - ['sdk/storage/azure-storage-internal-avro/README.md', '#3113'] - ['sdk/storage/README.md', '#3113'] - ['sdk/tools/azure-sdk-archetype/README.md', '#3113'] + - ['sdk/tools/azure-sdk-build-tool/README.md', '#3113'] package_indexing_exclusion_list: - azure-loganalytics-sample diff --git a/sdk/tools/azure-sdk-build-tool/pom.xml b/sdk/tools/azure-sdk-build-tool/pom.xml index 13b640323e410..545cab10a7292 100644 --- a/sdk/tools/azure-sdk-build-tool/pom.xml +++ b/sdk/tools/azure-sdk-build-tool/pom.xml @@ -9,7 +9,7 @@ maven-plugin 1.0.0-beta.1 - Azure Maven Build Tool + Azure SDK for Java Maven Build Tool A tool that makes working with the Azure SDK for Java more productive. diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AnnotationProcessingTool.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AnnotationProcessingTool.java index cc847a9729709..ae4e9a7c6c42e 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AnnotationProcessingTool.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AnnotationProcessingTool.java @@ -5,11 +5,8 @@ import com.azure.sdk.build.tool.util.logging.Logger; import java.io.File; -import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; import java.util.Comparator; import java.util.Optional; import java.util.Set; @@ -19,7 +16,7 @@ import static com.azure.sdk.build.tool.util.AnnotationUtils.findCallsToAnnotatedMethod; import static com.azure.sdk.build.tool.util.AnnotationUtils.getAnnotation; import static com.azure.sdk.build.tool.util.AnnotationUtils.getCompleteClassLoader; -import static com.azure.sdk.build.tool.util.MojoUtils.failOrError; +import static com.azure.sdk.build.tool.util.MojoUtils.failOrWarn; import static com.azure.sdk.build.tool.util.MojoUtils.getAllDependencies; import static com.azure.sdk.build.tool.util.MojoUtils.getCompileSourceRoots; import static com.azure.sdk.build.tool.util.MojoUtils.getString; @@ -63,7 +60,7 @@ public void run() { StringBuilder message = new StringBuilder(); message.append(getString("betaApiUsed")).append(System.lineSeparator()); betaMethodCallers.forEach(method -> message.append(" - ").append(method.toString()).append(System.lineSeparator())); - failOrError(() -> AzureSdkMojo.MOJO.isValidateNoBetaApiUsed(), message.toString()); + failOrWarn(() -> AzureSdkMojo.MOJO.isValidateNoBetaApiUsed(), message.toString()); } } } diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java index 9ed8989ef74a7..4022201f98761 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/DependencyCheckerTool.java @@ -13,7 +13,7 @@ import java.util.Set; import java.util.stream.Collectors; -import static com.azure.sdk.build.tool.util.MojoUtils.failOrError; +import static com.azure.sdk.build.tool.util.MojoUtils.failOrWarn; import static com.azure.sdk.build.tool.util.MojoUtils.getAllDependencies; import static com.azure.sdk.build.tool.util.MojoUtils.getDirectDependencies; import static com.azure.sdk.build.tool.util.MojoUtils.getString; @@ -69,11 +69,11 @@ private void checkForBom() { String latestAvailableBomVersion = MavenUtils.getLatestArtifactVersion("com.azure", "azure-sdk-bom"); boolean isLatestBomVersion = bomDependency.get().getVersion().equals(latestAvailableBomVersion); if (!isLatestBomVersion) { - failOrError(AzureSdkMojo.MOJO::isValidateAzureSdkBomUsed, getString("outdatedBomDependency")); + failOrWarn(AzureSdkMojo.MOJO::isValidateAzureSdkBomUsed, getString("outdatedBomDependency")); } checkForAzureSdkDependencyVersions(); } else { - failOrError(AzureSdkMojo.MOJO::isValidateAzureSdkBomUsed, getString("missingBomDependency")); + failOrWarn(AzureSdkMojo.MOJO::isValidateAzureSdkBomUsed, getString("missingBomDependency")); } } @@ -87,7 +87,7 @@ private void checkForAzureSdkDependencyVersions() { return !location.getSource().getModelId().startsWith("com.azure:azure-sdk-bom"); }).collect(Collectors.toList()); - dependenciesWithOverriddenVersions.forEach(dependency -> failOrError(AzureSdkMojo.MOJO::isValidateBomVersionsAreUsed, + dependenciesWithOverriddenVersions.forEach(dependency -> failOrWarn(AzureSdkMojo.MOJO::isValidateBomVersionsAreUsed, dependency.getArtifactId() + " " + getString("overrideBomVersion"))); List betaDependencies = dependencies.stream() @@ -95,7 +95,7 @@ private void checkForAzureSdkDependencyVersions() { .filter(dependency -> dependency.getVersion().contains("-beta")) .collect(Collectors.toList()); - betaDependencies.forEach(dependency -> failOrError(AzureSdkMojo.MOJO::isValidateNoBetaLibraryUsed, + betaDependencies.forEach(dependency -> failOrWarn(AzureSdkMojo.MOJO::isValidateNoBetaLibraryUsed, dependency.getArtifactId() + " " + getString("betaDependencyUsed"))); } @@ -128,7 +128,7 @@ private void checkForAzureSdkTrackOneDependencies() { for (OutdatedDependency outdatedDependency : outdatedDirectDependencies) { message += "\n - " + outdatedDependency.getGav() + " --> " + outdatedDependency.getSuggestedReplacementGav(); } - failOrError(AzureSdkMojo.MOJO::isValidateNoDeprecatedMicrosoftLibraryUsed, message); + failOrWarn(AzureSdkMojo.MOJO::isValidateNoDeprecatedMicrosoftLibraryUsed, message); } if (!outdatedTransitiveDependencies.isEmpty()) { // convert each track one dependency into actionable guidance @@ -136,7 +136,7 @@ private void checkForAzureSdkTrackOneDependencies() { for (OutdatedDependency outdatedDependency : outdatedDirectDependencies) { message += "\n - " + outdatedDependency.getGav(); } - failOrError(AzureSdkMojo.MOJO::isValidateNoDeprecatedMicrosoftLibraryUsed, message); + failOrWarn(AzureSdkMojo.MOJO::isValidateNoDeprecatedMicrosoftLibraryUsed, message); } } } diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java index 9d6f42e82e057..bedd8ed2c05eb 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/mojo/AzureSdkMojo.java @@ -31,10 +31,7 @@ public class AzureSdkMojo extends AbstractMojo { public static AzureSdkMojo MOJO; - private static final Logger LOGGER = Logger.getInstance(); - - // TODO: Create an App Insights resource that can be used for collecting ping data private static final String APP_INSIGHTS_CONNECTION_STRING = ""; @Parameter(defaultValue = "${project}", readonly = true, required = true) diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MojoUtils.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MojoUtils.java index 2e630771fc6bd..bbd68261a6e5a 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MojoUtils.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/util/MojoUtils.java @@ -44,12 +44,11 @@ public static String getString(String key, String... parameters) { return MessageFormat.format(getString(key), parameters); } - public static void failOrError(Supplier condition, String message) { - // warn about lack of BOM dependency + public static void failOrWarn(Supplier condition, String message) { if (condition.get()) { AzureSdkMojo.MOJO.getReport().addFailureMessage(message); } else { - AzureSdkMojo.MOJO.getReport().addErrorMessage(message); + AzureSdkMojo.MOJO.getReport().addWarningMessage(message); } } } diff --git a/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml b/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml index 7f026d9f0e2c1..1186015308fe6 100644 --- a/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml +++ b/sdk/tools/azure-sdk-build-tool/src/test/azure-sdk-build-tool-test/pom.xml @@ -84,11 +84,11 @@ azure-sdk-build-tool 1.0.0-beta.1 - false - false - false - false - false + true + true + true + true + true ./azure-sdk-usage-report.txt From 6d87e96b9aaa53cd95f3af3582fbe5bf8b363542 Mon Sep 17 00:00:00 2001 From: Srikanta Date: Fri, 11 Feb 2022 15:49:44 -0800 Subject: [PATCH 8/8] fix import --- .../java/com/azure/sdk/build/tool/AzureDependencyMapping.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AzureDependencyMapping.java b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AzureDependencyMapping.java index 46c0419061b17..edcdf2a9b6202 100644 --- a/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AzureDependencyMapping.java +++ b/sdk/tools/azure-sdk-build-tool/src/main/java/com/azure/sdk/build/tool/AzureDependencyMapping.java @@ -3,10 +3,10 @@ import com.azure.sdk.build.tool.models.OutdatedDependency; import com.azure.sdk.build.tool.util.MavenUtils; import com.azure.sdk.build.tool.util.logging.Logger; -import edu.emory.mathcs.backport.java.util.Collections; import org.apache.maven.artifact.Artifact; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map;