diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index b71cf6b9e9..e47c90adce 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -299,3 +299,25 @@ jobs:
with:
bom-path: gapic-generator-java-bom/pom.xml
+ unmanaged_dependency_check:
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+ - uses: actions/setup-java@v3
+ with:
+ java-version: 11
+ distribution: temurin
+ - run: mvn -version
+ - name: Unit Tests
+ run: |
+ mvn test --batch-mode --no-transfer-progress
+ working-directory: java-shared-dependencies/unmanaged-dependency-check
+ - name: Install Maven modules
+ run: |
+ mvn install -B -ntp -DskipTests -Dclirr.skip -Dcheckstyle.skip
+ - name: Unmanaged dependency check
+ uses: ./java-shared-dependencies/unmanaged-dependency-check
+ with:
+ bom-path: gapic-generator-java-bom/pom.xml
diff --git a/.github/workflows/create_additional_release_tag.yaml b/.github/workflows/create_additional_release_tag.yaml
index 419cab0ac6..0ecaf63863 100644
--- a/.github/workflows/create_additional_release_tag.yaml
+++ b/.github/workflows/create_additional_release_tag.yaml
@@ -14,16 +14,14 @@ jobs:
uses: actions/checkout@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
-
- name: Set up Git
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
-
- name: Fetch all tags
run: git fetch --tags
-
- name: Create additional tags
+ bash: shell
run: |
ARTIFACT_IDS=('google-cloud-shared-dependencies' 'api-common' 'gax')
for ARTIFACT_ID in "${ARTIFACT_IDS[@]}"; do
@@ -36,3 +34,10 @@ jobs:
git tag $TAG_NAME
git push origin $TAG_NAME
done
+ # Generate a tag for unmanaged dependencies check.
+ # Use fixed tag so that checks in handwritten libraries do not need to
+ # update the version.
+ CHECK_LATEST_TAG="unmanaged-dependencies-check-latest"
+ git tag ${CHECK_LATEST_TAG}
+ git push origin -f ${CHECK_LATEST_TAG}
+
diff --git a/java-shared-dependencies/unmanaged-dependency-check/action.yaml b/java-shared-dependencies/unmanaged-dependency-check/action.yaml
new file mode 100644
index 0000000000..e8988d3be1
--- /dev/null
+++ b/java-shared-dependencies/unmanaged-dependency-check/action.yaml
@@ -0,0 +1,43 @@
+ name: "Unmanaged dependency check"
+ description: "Checks whether there's a dependency that is not managed by java shared dependencies."
+ inputs:
+ bom-path:
+ description: "The relative path from the repository root to the pom.xml file"
+ required: true
+ runs:
+ using: "composite"
+ steps:
+ - uses: actions/setup-java@v3
+ with:
+ distribution: temurin
+ java-version: 11
+ cache: maven
+ - name: Set up Maven
+ uses: stCarolas/setup-maven@v4.5
+ with:
+ maven-version: 3.8.2
+ - name: Install latest Java shared dependencies
+ shell: bash
+ run: |
+ cd ${{ github.action_path }}/..
+ echo "Install Java shared dependencies"
+ mvn clean install -V --batch-mode --no-transfer-progress -DskipTests
+ - name: Install check
+ shell: bash
+ run: |
+ cd ${{ github.action_path }}
+ echo "Install Unmanaged Dependency Check in $(pwd)"
+ mvn clean install -V --batch-mode --no-transfer-progress -DskipTests
+ - name: Run unmanaged dependency check
+ shell: bash
+ run: |
+ bom_absolute_path=$(realpath "${{ inputs.bom-path }}")
+ cd ${{ github.action_path }}
+ echo "Running Unmanaged Dependency Check against ${bom_absolute_path}"
+ unmanaged_dependencies=$(mvn exec:java -Dexec.args="../pom.xml ${bom_absolute_path}" -q)
+ if [[ "${unmanaged_dependencies}" != "[]" ]]; then
+ echo "This pull request seems to add new third-party dependency, ${unmanaged_dependencies}, among the artifacts listed in ${{ inputs.bom-path }}."
+ echo "Please see go/cloud-sdk-java-dependency-governance."
+ exit 1
+ fi
+
diff --git a/java-shared-dependencies/unmanaged-dependency-check/pom.xml b/java-shared-dependencies/unmanaged-dependency-check/pom.xml
new file mode 100644
index 0000000000..c632e59920
--- /dev/null
+++ b/java-shared-dependencies/unmanaged-dependency-check/pom.xml
@@ -0,0 +1,85 @@
+
+
+ 4.0.0
+ com.google.cloud
+ unmanaged-dependency-check
+ 0.0.1-SNAPSHOT
+ Unmanaged dependency check
+
+
+
+
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.1.1
+
+
+
+ java
+
+
+
+
+ install-test-poms
+ test-compile
+
+ exec
+
+
+ bash
+
+ local-install.sh
+
+ src/test/resources
+
+
+
+
+ com.google.cloud.UnmanagedDependencyCheck
+
+
+
+
+
+
+
+ com.google.cloud.tools
+ dependencies
+ 1.5.13
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ com.google.truth
+ truth
+ 1.1.5
+ test
+
+
+ org.slf4j
+ slf4j-api
+ 2.0.9
+
+
+ org.slf4j
+ slf4j-simple
+ 2.0.9
+
+
+
+
\ No newline at end of file
diff --git a/java-shared-dependencies/unmanaged-dependency-check/src/main/java/com/google/cloud/UnmanagedDependencyCheck.java b/java-shared-dependencies/unmanaged-dependency-check/src/main/java/com/google/cloud/UnmanagedDependencyCheck.java
new file mode 100644
index 0000000000..410c5f3966
--- /dev/null
+++ b/java-shared-dependencies/unmanaged-dependency-check/src/main/java/com/google/cloud/UnmanagedDependencyCheck.java
@@ -0,0 +1,82 @@
+package com.google.cloud;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.cloud.tools.opensource.classpath.ClassPathBuilder;
+import com.google.cloud.tools.opensource.classpath.DependencyMediation;
+import com.google.cloud.tools.opensource.dependencies.Bom;
+import com.google.cloud.tools.opensource.dependencies.MavenRepositoryException;
+import java.nio.file.Paths;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.version.InvalidVersionSpecificationException;
+
+/**
+ * A utility class to check unmanaged dependencies in BOM.
+ */
+public class UnmanagedDependencyCheck {
+ // regex of handwritten artifacts
+ private final static String downstreamArtifact = "(com.google.cloud:google-cloud-.*)|(com.google.api.grpc:(grpc|proto)-google-cloud-.*)";
+
+
+ /**
+ * @param args An array with two elements.
The first string is the path of Java shared
+ * dependencies BOM.
The second string is the path of a pom.xml contains BOM.
+ */
+ public static void main(String[] args)
+ throws MavenRepositoryException, InvalidVersionSpecificationException {
+ checkArgument(args.length == 2, "The length of the inputs should be 2");
+ System.out.println(getUnmanagedDependencies(args[0], args[1]));
+ }
+
+ /**
+ * Returns dependency coordinates that are not managed by shared dependency BOM.
+ *
+ * @param sharedDependenciesBomPath the path of shared dependency BOM
+ * @param projectBomPath the path of current project BOM
+ * @return a list of unmanaged dependencies by the given version of shared dependency BOM
+ * @throws MavenRepositoryException thrown if the artifacts in Bom can't be reached in remote or
+ * local Maven repository
+ * @throws InvalidVersionSpecificationException thrown if the shared dependency version can't be
+ * parsed
+ */
+ public static List getUnmanagedDependencies(
+ String sharedDependenciesBomPath, String projectBomPath)
+ throws MavenRepositoryException, InvalidVersionSpecificationException {
+ Set sharedDependencies = getManagedDependencies(sharedDependenciesBomPath);
+ Set managedDependencies = getManagedDependencies(projectBomPath);
+
+ return managedDependencies.stream()
+ .filter(dependency -> !sharedDependencies.contains(dependency))
+ // handwritten artifacts, e.g., com.google.cloud:google-cloud-bigtable, should be excluded.
+ .filter(dependency -> !dependency.matches(downstreamArtifact))
+ .collect(Collectors.toList());
+ }
+
+ private static Set getManagedDependencies(String projectBomPath)
+ throws MavenRepositoryException, InvalidVersionSpecificationException {
+ return getManagedDependenciesFromBom(Bom.readBom(Paths.get(projectBomPath)));
+ }
+
+ private static Set getManagedDependenciesFromBom(Bom bom)
+ throws InvalidVersionSpecificationException {
+ Set res = new HashSet<>();
+ new ClassPathBuilder()
+ .resolve(bom.getManagedDependencies(), true, DependencyMediation.MAVEN)
+ .getClassPath()
+ .forEach(
+ classPath -> {
+ Artifact artifact = classPath.getArtifact();
+ res.add(String.format("%s:%s", artifact.getGroupId(), artifact.getArtifactId()));
+ });
+
+ return res;
+ }
+
+ private UnmanagedDependencyCheck() {
+ throw new IllegalStateException("Utility class");
+ }
+}
diff --git a/java-shared-dependencies/unmanaged-dependency-check/src/test/java/com/google/cloud/UnmanagedDependencyCheckTest.java b/java-shared-dependencies/unmanaged-dependency-check/src/test/java/com/google/cloud/UnmanagedDependencyCheckTest.java
new file mode 100644
index 0000000000..0f1e0e85da
--- /dev/null
+++ b/java-shared-dependencies/unmanaged-dependency-check/src/test/java/com/google/cloud/UnmanagedDependencyCheckTest.java
@@ -0,0 +1,43 @@
+package com.google.cloud;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.google.cloud.tools.opensource.dependencies.MavenRepositoryException;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.eclipse.aether.version.InvalidVersionSpecificationException;
+import org.junit.Test;
+
+public class UnmanagedDependencyCheckTest {
+ @Test
+ public void getUnmanagedDependencyFromSamePomTest()
+ throws MavenRepositoryException, InvalidVersionSpecificationException {
+ String sharedDependenciesBom = "src/test/resources/shared-dependency-3.18.0-pom.xml";
+ List unManagedDependencies =
+ UnmanagedDependencyCheck.getUnmanagedDependencies(sharedDependenciesBom, sharedDependenciesBom);
+ assertTrue(unManagedDependencies.isEmpty());
+ }
+
+ @Test
+ public void getUnmanagedDependencyFromHWBomTest()
+ throws MavenRepositoryException, InvalidVersionSpecificationException {
+ List unManagedDependencies =
+ UnmanagedDependencyCheck.getUnmanagedDependencies(
+ "src/test/resources/shared-dependency-3.18.0-pom.xml", "src/test/resources/bigtable-pom.xml");
+ assertTrue(unManagedDependencies.isEmpty());
+ }
+
+ @Test
+ public void getUnmanagedDependencyFromNestedPomTest()
+ throws MavenRepositoryException, InvalidVersionSpecificationException {
+ List unManagedDependencies =
+ UnmanagedDependencyCheck.getUnmanagedDependencies(
+ "src/test/resources/shared-dependency-3.18.0-pom.xml", "src/test/resources/transitive-dependency-pom.xml");
+ assertThat(unManagedDependencies)
+ .containsAtLeastElementsIn(ImmutableList.of("com.h2database:h2"));
+ // test dependency should be ignored.
+ assertThat(unManagedDependencies)
+ .doesNotContain(ImmutableList.of("com.mysql:mysql-connector-j"));
+ }
+}
diff --git a/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/bigtable-pom.xml b/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/bigtable-pom.xml
new file mode 100644
index 0000000000..b7a896241e
--- /dev/null
+++ b/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/bigtable-pom.xml
@@ -0,0 +1,127 @@
+
+
+ 4.0.0
+ com.google.cloud
+ google-cloud-bigtable-bom
+ 2.29.0
+ pom
+
+ com.google.cloud
+ google-cloud-shared-config
+ 1.6.0
+
+
+
+ Google Cloud Bigtable BOM
+ https://github.com/googleapis/java-bigtable
+
+ BOM for Google Cloud Bigtable
+
+
+
+ Google LLC
+
+
+
+
+ chingor13
+ Jeff Ching
+ chingor@google.com
+ Google LLC
+
+ Developer
+
+
+
+ igorberstein
+ Igor Bernstein
+ igorbernstein@google.com
+ Google LLC
+
+ Developer
+
+
+
+
+
+ scm:git:https://github.com/googleapis/java-bigtable.git
+ scm:git:git@github.com:googleapis/java-bigtable.git
+ https://github.com/googleapis/java-bigtable
+
+
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+
+
+ com.google.cloud
+ google-cloud-bigtable
+ 2.29.0
+
+
+ com.google.cloud
+ google-cloud-bigtable-emulator
+ 0.166.0
+
+
+ com.google.cloud
+ google-cloud-bigtable-emulator-core
+ 0.166.0
+
+
+ com.google.api.grpc
+ grpc-google-cloud-bigtable-admin-v2
+ 2.29.0
+
+
+ com.google.api.grpc
+ grpc-google-cloud-bigtable-v2
+ 2.29.0
+
+
+ com.google.api.grpc
+ proto-google-cloud-bigtable-admin-v2
+ 2.29.0
+
+
+ com.google.api.grpc
+ proto-google-cloud-bigtable-v2
+ 2.29.0
+
+
+ com.google.cloud
+ google-cloud-bigtable-stats
+ 2.29.0
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+
+
+ false
+
+
+
+
+
diff --git a/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/gax-example-pom.xml b/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/gax-example-pom.xml
new file mode 100644
index 0000000000..5bc943c2de
--- /dev/null
+++ b/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/gax-example-pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+ com.google.api
+ gax-grpc
+ 0.0.1-SNAPSHOT
+ jar
+
+
+
+ com.h2database
+ h2
+ 2.2.224
+
+
+ com.mysql
+ mysql-connector-j
+ 8.2.0
+ test
+
+
+
\ No newline at end of file
diff --git a/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/local-install.sh b/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/local-install.sh
new file mode 100755
index 0000000000..f7162e9f7b
--- /dev/null
+++ b/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/local-install.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+mvn install -f shared-dependency-3.18.0-pom.xml
+mvn install -f gax-example-pom.xml
+mvn install -f nested-dependency-pom.xml
+mvn install -f transitive-dependency-pom.xml
\ No newline at end of file
diff --git a/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/nested-dependency-pom.xml b/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/nested-dependency-pom.xml
new file mode 100644
index 0000000000..d841f4daf2
--- /dev/null
+++ b/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/nested-dependency-pom.xml
@@ -0,0 +1,21 @@
+
+
+ 4.0.0
+ com.example
+ dependencies-bom
+ pom
+ 0.0.1-SNAPSHOT
+ Self Dependencies
+
+ Self dependency pom used in test.
+
+
+
+
+ com.google.api
+ gax-grpc
+ 0.0.1-SNAPSHOT
+
+
+
+
diff --git a/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/shared-dependency-3.18.0-pom.xml b/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/shared-dependency-3.18.0-pom.xml
new file mode 100644
index 0000000000..d716ab7738
--- /dev/null
+++ b/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/shared-dependency-3.18.0-pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+ com.google.cloud
+ google-cloud-shared-dependencies
+ pom
+ 3.18.0
+
+ first-party-dependencies
+ third-party-dependencies
+
+ Google Cloud Shared Dependencies
+
+ Shared build configuration for Google Cloud Java libraries.
+
+
+
+ com.google.api
+ gapic-generator-java-pom-parent
+ 2.28.0
+ ../gapic-generator-java-pom-parent
+
+
+
+ UTF-8
+ ${project.artifactId}
+
+
+
+
+
+ com.google.cloud
+ first-party-dependencies
+ 3.18.0
+ pom
+ import
+
+
+ com.google.cloud
+ third-party-dependencies
+ 3.18.0
+ pom
+ import
+
+
+
+
diff --git a/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/transitive-dependency-pom.xml b/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/transitive-dependency-pom.xml
new file mode 100644
index 0000000000..899652963b
--- /dev/null
+++ b/java-shared-dependencies/unmanaged-dependency-check/src/test/resources/transitive-dependency-pom.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+ com.example
+ transitive-dependencies
+ pom
+ 0.0.1
+ Self Dependencies
+
+ Self dependency pom used in test.
+
+
+
+
+ com.example
+ dependencies-bom
+ 0.0.1-SNAPSHOT
+ pom
+ import
+
+
+
+