3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | @NullMarked
17 | @NonNullFields
18 | package org.openrewrite.java.dependencies;
19 |
20 | import org.jspecify.annotations.NullMarked;
21 | import org.openrewrite.internal.lang.NonNullFields;
22 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/search/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | @NullMarked
17 | @NonNullFields
18 | package org.openrewrite.java.dependencies.search;
19 |
20 | import org.jspecify.annotations.NullMarked;
21 | import org.openrewrite.internal.lang.NonNullFields;
22 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/internal/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | @NullMarked
17 | @NonNullFields
18 | package org.openrewrite.java.dependencies.internal;
19 |
20 | import org.jspecify.annotations.NullMarked;
21 | import org.openrewrite.internal.lang.NonNullFields;
22 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "rewrite-java-dependencies"
2 |
3 | plugins {
4 | id("com.gradle.develocity") version "latest.release"
5 | id("com.gradle.common-custom-user-data-gradle-plugin") version "latest.release"
6 | }
7 |
8 | develocity {
9 | server = "https://ge.openrewrite.org/"
10 | val isCiServer = System.getenv("CI")?.equals("true") ?: false
11 | val accessKey = System.getenv("GRADLE_ENTERPRISE_ACCESS_KEY")
12 | val authenticated = !accessKey.isNullOrBlank()
13 | buildCache {
14 | remote(develocity.buildCache) {
15 | isEnabled = true
16 | isPush = isCiServer && authenticated
17 | }
18 | }
19 |
20 | buildScan {
21 | capture {
22 | fileFingerprints = true
23 | }
24 |
25 | publishing {
26 | onlyIf {
27 | authenticated
28 | }
29 | }
30 |
31 | uploadInBackground = !isCiServer
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.claude/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "permissions": {
3 | "allow": [
4 | "Bash(./gradlew:*)",
5 | "Bash(cat:*)",
6 | "Bash(echo:*)",
7 | "Bash(find:*)",
8 | "Bash(gh:*)",
9 | "Bash(git:*)",
10 | "Bash(grep:*)",
11 | "Bash(javac:*)",
12 | "Bash(mv:*)",
13 | "Bash(npm test:*)",
14 | "Bash(rg:*)",
15 | "Bash(rm:*)",
16 | "Bash(timeout:*)",
17 | "WebFetch(domain:github.com)",
18 | "mcp__github__get_issue",
19 | "mcp__github__get_issue_comments",
20 | "mcp__github__get_pull_request",
21 | "mcp__github__get_pull_request_comments",
22 | "mcp__idea__find_files_by_name_substring",
23 | "mcp__idea__get_file_text_by_path",
24 | "mcp__idea__get_open_in_editor_file_path",
25 | "mcp__idea__get_open_in_editor_file_text",
26 | "mcp__idea__get_selected_in_editor_text",
27 | "mcp__idea__list_directory_tree_in_folder",
28 | "mcp__idea__list_files_in_folder",
29 | "mcp__idea__open_file_in_editor",
30 | "mcp__idea__replace_selected_text",
31 | "mcp__idea__replace_specific_text",
32 | "mcp__idea__search_in_files_content"
33 | ],
34 | "deny": []
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/internal/Version.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.openrewrite.java.dependencies.internal;
18 |
19 | public interface Version {
20 | /**
21 | * Returns the original {@link String} representation of the version.
22 | */
23 | String getSource();
24 |
25 | /**
26 | * Returns all the parts of this version. e.g. 1.2.3 returns [1,2,3] or 1.2-beta4 returns [1,2,beta,4].
27 | */
28 | String[] getParts();
29 |
30 | /**
31 | * Returns all the numeric parts of this version as {@link Long}, with nulls in non-numeric positions. eg. 1.2.3 returns [1,2,3] or 1.2-beta4 returns [1,2,null,4].
32 | */
33 | Long[] getNumericParts();
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/oldgroupids/Migration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.openrewrite.java.dependencies.oldgroupids;
17 |
18 | import com.fasterxml.jackson.annotation.JsonPropertyOrder;
19 | import lombok.AllArgsConstructor;
20 | import lombok.Data;
21 | import org.jspecify.annotations.Nullable;
22 |
23 | @AllArgsConstructor
24 | @Data
25 | @JsonPropertyOrder({"oldGroupId", "oldArtifactId", "newGroupId", "newArtifactId", "context"})
26 | public class Migration {
27 | String oldGroupId;
28 |
29 | @Nullable
30 | String oldArtifactId;
31 |
32 | String newGroupId;
33 |
34 | @Nullable
35 | String newArtifactId;
36 |
37 | @Nullable
38 | String context;
39 | }
40 |
--------------------------------------------------------------------------------
/src/test/java/org/openrewrite/java/dependencies/internal/StaticVersionComparatorTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.openrewrite.java.dependencies.internal;
17 |
18 | import org.junit.jupiter.api.Test;
19 |
20 | import static org.assertj.core.api.Assertions.assertThat;
21 |
22 |
23 | class StaticVersionComparatorTest {
24 | VersionParser vp = new VersionParser();
25 | StaticVersionComparator svc = new StaticVersionComparator();
26 |
27 | @Test
28 | void milestone() {
29 | assertThat(svc.compare(v("2.0.0"), v("1.0.0"))).isOne();
30 | assertThat(svc.compare(v("1.0.0"), v("1.0.0-M1"))).isOne();
31 | assertThat(svc.compare(v("1.0.0-M2"), v("1.0.0-M1"))).isOne();
32 | assertThat(svc.compare(v("1.0.0-rc-1"), v("1.0.0-M1"))).isOne();
33 | }
34 |
35 | Version v(String version) {
36 | return vp.transform(version);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.github/workflows/migrations.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Update the Old GroupId migrations CSV
3 |
4 | on:
5 | workflow_dispatch: {}
6 | schedule:
7 | - cron: 0 10 * * MON
8 |
9 | jobs:
10 | update-migrations:
11 | if: github.event_name != 'schedule' || github.repository_owner == 'openrewrite'
12 | runs-on: ubuntu-latest
13 | steps:
14 | # Checkout and build parser
15 | - name: Checkout
16 | uses: actions/checkout@v6
17 | with:
18 | fetch-depth: 0
19 | - uses: actions/setup-java@v5
20 | with:
21 | cache: 'gradle'
22 | distribution: 'temurin'
23 | java-version: '21'
24 |
25 | # Update migrations
26 | - name: Checkout oga-maven-plugin
27 | uses: actions/checkout@v6
28 | with:
29 | repository: jonathanlermitage/oga-maven-plugin
30 | path: oga-maven-plugin
31 | - name: Update migrations
32 | run: ./gradlew parseDefinitionMigrations --args="./oga-maven-plugin src/main/resources/migrations.csv"
33 |
34 | # Commit and push
35 | - name: configure-git-user
36 | run: |
37 | git config user.email "team@moderne.io"
38 | git config user.name "team-moderne[bot]"
39 | - name: Create timestamp
40 | run: echo "NOW=$(date +'%Y-%m-%dT%H%M')" >> $GITHUB_ENV
41 | - name: Commit and push
42 | run: |
43 | git add src/main/resources/migrations.csv
44 | git diff --quiet HEAD || (git commit -m "[Auto] Old GroupId migrations as of ${{ env.NOW }}" && git push origin main)
45 |
--------------------------------------------------------------------------------
/src/test/java/org/openrewrite/java/dependencies/oldgroupids/ParseDefinitionMigrationsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
24 |
25 | ### What is this?
26 |
27 | This project implements a [Rewrite module](https://github.com/openrewrite/rewrite) that performs common Java dependency management tasks.
28 |
29 | Browse [a selection of recipes available through this module in the recipe catalog](https://docs.openrewrite.org/recipes/java/dependencies).
30 |
31 | ## Contributing
32 |
33 | We appreciate all types of contributions. See the [contributing guide](https://github.com/openrewrite/.github/blob/main/CONTRIBUTING.md) for detailed instructions on how to get started.
34 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/table/RelocatedDependencyReport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.openrewrite.java.dependencies.table;
17 |
18 | import com.fasterxml.jackson.annotation.JsonIgnoreType;
19 | import lombok.Value;
20 | import org.jspecify.annotations.Nullable;
21 | import org.openrewrite.Column;
22 | import org.openrewrite.DataTable;
23 | import org.openrewrite.Recipe;
24 |
25 | @JsonIgnoreType
26 | public class RelocatedDependencyReport extends DataTable {
27 | public RelocatedDependencyReport(Recipe recipe) {
28 | super(recipe,
29 | "Relocated dependencies",
30 | "A list of dependencies in use that have relocated.");
31 | }
32 |
33 | @Value
34 | public static class Row {
35 | @Column(displayName = "Dependency group id",
36 | description = "The Group ID of the dependency in use.")
37 | String dependencyGroupId;
38 |
39 | @Column(displayName = "Dependency artifact id",
40 | description = "The Artifact ID of the dependency in use.")
41 | @Nullable
42 | String dependencyArtifactId;
43 |
44 | @Column(displayName = "Relocated dependency group id",
45 | description = "The Group ID of the relocated dependency.")
46 | String relocatedGroupId;
47 |
48 | @Column(displayName = "Relocated ependency artifact id",
49 | description = "The Artifact ID of the relocated dependency.")
50 | @Nullable
51 | String relocatedArtifactId;
52 |
53 | @Column(displayName = "Context",
54 | description = "Context for the relocation, if any.")
55 | @Nullable
56 | String context;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/table/GradleDependencyConfigurationErrors.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.openrewrite.java.dependencies.table;
17 |
18 | import com.fasterxml.jackson.annotation.JsonIgnoreType;
19 | import lombok.Value;
20 | import org.openrewrite.Column;
21 | import org.openrewrite.DataTable;
22 | import org.openrewrite.Recipe;
23 |
24 | @JsonIgnoreType
25 | public class GradleDependencyConfigurationErrors extends DataTable {
26 | public GradleDependencyConfigurationErrors(Recipe recipe) {
27 | super(recipe, "Gradle dependency configuration errors",
28 | "Records Gradle dependency configurations which failed to resolve during parsing. " +
29 | "Partial success/failure is common, a failure in this list does not mean that every dependency failed to resolve.");
30 | }
31 |
32 | @Value
33 | public static class Row {
34 | @Column(displayName = "Project path",
35 | description = "The path of the project which contains the dependency configuration.")
36 | String projectPath;
37 |
38 | @Column(displayName = "Configuration name",
39 | description = "The name of the dependency configuration which failed to resolve.")
40 | String configurationName;
41 |
42 | @Column(displayName = "Exception type",
43 | description = "The type of exception encountered when attempting to resolve the dependency configuration.")
44 | String exceptionType;
45 |
46 | @Column(displayName = "Error message",
47 | description = "The error message encountered when attempting to resolve the dependency configuration.")
48 | String exceptionMessage;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/test/java/org/openrewrite/java/dependencies/DependencyInsightTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.openrewrite.java.dependencies;
17 |
18 | import org.junit.jupiter.api.Test;
19 | import org.openrewrite.DocumentExample;
20 | import org.openrewrite.test.RecipeSpec;
21 | import org.openrewrite.test.RewriteTest;
22 |
23 | import static org.openrewrite.maven.Assertions.pomXml;
24 |
25 | class DependencyInsightTest implements RewriteTest {
26 |
27 | @Override
28 | public void defaults(RecipeSpec spec) {
29 | spec.recipe(new DependencyInsight("org.springframework*", "*", null, null));
30 | }
31 |
32 | @DocumentExample
33 | @Test
34 | void maven() {
35 | rewriteRun(
36 | //language=xml
37 | pomXml(
38 | """
39 |
40 | com.example
41 | foo
42 | 1.0.0
43 |
44 |
45 |
46 | org.springframework
47 | spring-core
48 | 5.2.6.RELEASE
49 |
50 |
51 |
52 | """,
53 | """
54 |
55 | com.example
56 | foo
57 | 1.0.0
58 |
59 |
60 |
61 | org.springframework
62 | spring-core
63 | 5.2.6.RELEASE
64 |
65 |
66 |
67 | """
68 | )
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/table/DependencyListReport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.openrewrite.java.dependencies.table;
17 |
18 | import com.fasterxml.jackson.annotation.JsonIgnoreType;
19 | import lombok.Value;
20 | import org.openrewrite.Column;
21 | import org.openrewrite.DataTable;
22 | import org.openrewrite.Recipe;
23 |
24 | @JsonIgnoreType
25 | public class DependencyListReport extends DataTable {
26 | public DependencyListReport(Recipe recipe) {
27 | super(recipe,
28 | "Dependency report",
29 | "Lists all Gradle and Maven dependencies");
30 | }
31 |
32 | @Value
33 | public static class Row {
34 |
35 | @Column(displayName = "Build tool",
36 | description = "The build tool used to manage dependencies (Gradle or Maven).")
37 | String buildTool;
38 |
39 | @Column(displayName = "Group id",
40 | description = "The Group ID of the Gradle project or Maven module requesting the dependency.")
41 | String groupId;
42 |
43 | @Column(displayName = "Artifact id",
44 | description = "The Artifact ID of the Gradle project or Maven module requesting the dependency.")
45 | String artifactId;
46 |
47 | @Column(displayName = "Version",
48 | description = "The version of Gradle project or Maven module requesting the dependency.")
49 | String version;
50 |
51 | @Column(displayName = "Dependency group id",
52 | description = "The Group ID of the dependency.")
53 | String dependencyGroupId;
54 |
55 | @Column(displayName = "Dependency artifact id",
56 | description = "The Artifact ID of the dependency.")
57 | String dependencyArtifactId;
58 |
59 | @Column(displayName = "Dependency version",
60 | description = "The version of the dependency.")
61 | String dependencyVersion;
62 |
63 | @Column(displayName = "Direct Dependency",
64 | description = "When `true` the project directly depends on the dependency. When `false` the project " +
65 | "depends on the dependency transitively through at least one direct dependency.")
66 | boolean direct;
67 |
68 | @Column(displayName = "Resolution failure",
69 | description = "The reason why the dependency could not be resolved. Blank when resolution was not attempted.")
70 | String resolutionFailure;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/table/RepositoryAccessibilityReport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.openrewrite.java.dependencies.table;
17 |
18 | import com.fasterxml.jackson.annotation.JsonIgnoreType;
19 | import lombok.Value;
20 | import org.jspecify.annotations.Nullable;
21 | import org.openrewrite.Column;
22 | import org.openrewrite.DataTable;
23 | import org.openrewrite.Recipe;
24 |
25 | @JsonIgnoreType
26 | public class RepositoryAccessibilityReport extends DataTable {
27 |
28 | public RepositoryAccessibilityReport(Recipe recipe) {
29 | super(recipe,
30 | "Repository accessibility report",
31 | "Listing of all dependency repositories and whether they are accessible.");
32 |
33 | }
34 |
35 | @Value
36 | public static class Row {
37 | @Column(displayName = "Repository URI",
38 | description = "The URI of the repository")
39 | String uri;
40 |
41 | @Column(displayName = "Ping exception type",
42 | description = "Empty if the repository responded to a ping. Otherwise, the type of exception encountered " +
43 | "when attempting to access the repository.")
44 | String pingExceptionType;
45 |
46 | @Column(displayName = "Ping error message",
47 | description = "Empty if the repository was accessible. Otherwise, the error message encountered when " +
48 | "attempting to access the repository.")
49 | String pingExceptionMessage;
50 |
51 | @Column(displayName = "Ping HTTP code",
52 | description = "The HTTP response code returned by the repository. May be empty for non-HTTP repositories.")
53 | @Nullable
54 | Integer pingHttpCode;
55 |
56 | @Column(displayName = "Dependency resolution exception type",
57 | description = "Empty if ping failed, or if the repository successfully downloaded the specified dependency. Otherwise, the type of exception encountered " +
58 | "when attempting to access the repository.")
59 | String dependencyResolveExceptionType;
60 |
61 | @Column(displayName = "Dependency resolution error message",
62 | description = "Empty if ping failed, or if the repository successfully downloaded the specified dependency. Otherwise, the error message encountered when " +
63 | "attempting to access the repository.")
64 | String dependencyResolveExceptionMessage;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 |
74 |
75 | @rem Execute Gradle
76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
77 |
78 | :end
79 | @rem End local scope for the variables with windows NT shell
80 | if %ERRORLEVEL% equ 0 goto mainEnd
81 |
82 | :fail
83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
84 | rem the _cmd.exe /c_ return code!
85 | set EXIT_CODE=%ERRORLEVEL%
86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
88 | exit /b %EXIT_CODE%
89 |
90 | :mainEnd
91 | if "%OS%"=="Windows_NT" endlocal
92 |
93 | :omega
94 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/DependencyInsight.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.openrewrite.java.dependencies;
17 |
18 | import lombok.EqualsAndHashCode;
19 | import lombok.Value;
20 | import org.jspecify.annotations.Nullable;
21 | import org.openrewrite.*;
22 | import org.openrewrite.maven.table.DependenciesInUse;
23 |
24 | @EqualsAndHashCode(callSuper = false)
25 | @Value
26 | public class DependencyInsight extends Recipe {
27 | // Populated by the other DependencyInsight visitors. Listed here so the saas knows to display this when the recipe runs
28 | transient DependenciesInUse dependenciesInUse = new DependenciesInUse(this);
29 |
30 | @Override
31 | public String getDisplayName() {
32 | return "Dependency insight for Gradle and Maven";
33 | }
34 |
35 | @Override
36 | public String getDescription() {
37 | return "Finds dependencies, including transitive dependencies, in both Gradle and Maven projects. " +
38 | "Matches within all Gradle dependency configurations and Maven scopes.";
39 | }
40 |
41 | @Option(displayName = "Group pattern",
42 | description = "Group ID glob pattern used to match dependencies.",
43 | example = "com.fasterxml.jackson*")
44 | String groupIdPattern;
45 |
46 | @Option(displayName = "Artifact pattern",
47 | description = "Artifact ID glob pattern used to match dependencies.",
48 | example = "jackson-*")
49 | String artifactIdPattern;
50 |
51 | @Option(displayName = "Version",
52 | description = "Match only dependencies with the specified version. " +
53 | "Node-style [version selectors](https://docs.openrewrite.org/reference/dependency-version-selectors) may be used. " +
54 | "All versions are searched by default.",
55 | example = "1.x",
56 | required = false)
57 | @Nullable
58 | String version;
59 |
60 | @Option(displayName = "Scope",
61 | description = "Match dependencies with the specified Maven scope. All scopes are searched by default.",
62 | valid = {"compile", "test", "runtime", "provided", "system"},
63 | example = "compile",
64 | required = false)
65 | @Nullable
66 | String scope;
67 |
68 | @Override
69 | public TreeVisitor, ExecutionContext> getVisitor() {
70 | return new TreeVisitor() {
71 | final TreeVisitor, ExecutionContext> gdi = new org.openrewrite.gradle.search.DependencyInsight(groupIdPattern, artifactIdPattern, version, null)
72 | .getVisitor();
73 | final TreeVisitor, ExecutionContext> mdi = new org.openrewrite.maven.search.DependencyInsight(groupIdPattern, artifactIdPattern, scope, version, false)
74 | .getVisitor();
75 |
76 | @Override
77 | public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
78 | if (!(tree instanceof SourceFile)) {
79 | return tree;
80 | }
81 | SourceFile s = (SourceFile) tree;
82 | if (gdi.isAcceptable(s, ctx)) {
83 | s = (SourceFile) gdi.visitNonNull(s, ctx);
84 | } else if (mdi.isAcceptable(s, ctx)) {
85 | s = (SourceFile) mdi.visitNonNull(s, ctx);
86 | }
87 | return s;
88 | }
89 | };
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/internal/StaticVersionComparator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.openrewrite.java.dependencies.internal;
18 |
19 | import java.util.Comparator;
20 | import java.util.HashMap;
21 | import java.util.Locale;
22 | import java.util.Map;
23 |
24 | /**
25 | * Allows for comparison of Version instances.
26 | * Note that this comparator only considers the 'parts' of a version, and does not consider the part 'separators'.
27 | * This means that it considers `1.1.1 == 1-1-1 == 1.1-1`, and should not be used in cases where this is important.
28 | * One example where this comparator is inappropriate is if versions should be retained in a TreeMap/TreeSet.
29 | */
30 | public class StaticVersionComparator implements Comparator {
31 | static final Map SPECIAL_MEANINGS = new HashMap<>();
32 |
33 | static {
34 | SPECIAL_MEANINGS.put("dev", -1);
35 | SPECIAL_MEANINGS.put("m", 1);
36 | SPECIAL_MEANINGS.put("rc", 2);
37 | SPECIAL_MEANINGS.put("snapshot", 3);
38 | SPECIAL_MEANINGS.put("final", 4);
39 | SPECIAL_MEANINGS.put("ga", 5);
40 | SPECIAL_MEANINGS.put("release", 6);
41 | SPECIAL_MEANINGS.put("sp", 7);
42 | }
43 |
44 | /**
45 | * Compares 2 versions. Algorithm is inspired by PHP version_compare one.
46 | */
47 | @Override
48 | public int compare(Version version1, Version version2) {
49 | if (version1.equals(version2)) {
50 | return 0;
51 | }
52 |
53 | String[] parts1 = version1.getParts();
54 | String[] parts2 = version2.getParts();
55 | Long[] numericParts1 = version1.getNumericParts();
56 | Long[] numericParts2 = version2.getNumericParts();
57 |
58 | int i = 0;
59 | for (; i < parts1.length && i < parts2.length; i++) {
60 | String part1 = parts1[i];
61 | String part2 = parts2[i];
62 |
63 | Long numericPart1 = numericParts1[i];
64 | Long numericPart2 = numericParts2[i];
65 |
66 | boolean is1Number = numericPart1 != null;
67 | boolean is2Number = numericPart2 != null;
68 |
69 | if (part1.equals(part2)) {
70 | continue;
71 | }
72 | if (is1Number && !is2Number) {
73 | return 1;
74 | }
75 | if (is2Number && !is1Number) {
76 | return -1;
77 | }
78 | if (is1Number && is2Number) {
79 | int result = numericPart1.compareTo(numericPart2);
80 | if (result == 0) {
81 | continue;
82 | }
83 | return result;
84 | }
85 | // both are strings, we compare them taking into account special meaning
86 | Integer sm1 = SPECIAL_MEANINGS.get(part1.toLowerCase(Locale.US));
87 | Integer sm2 = SPECIAL_MEANINGS.get(part2.toLowerCase(Locale.US));
88 | if (sm1 != null) {
89 | sm2 = sm2 == null ? 0 : sm2;
90 | return sm1 - sm2;
91 | }
92 | if (sm2 != null) {
93 | return -sm2;
94 | }
95 | return part1.compareTo(part2);
96 | }
97 | if (i < parts1.length) {
98 | return numericParts1[i] == null ? -1 : 1;
99 | }
100 | if (i < parts2.length) {
101 | return numericParts2[i] == null ? 1 : -1;
102 | }
103 |
104 | return 0;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/FindDependency.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.openrewrite.java.dependencies;
17 |
18 | import lombok.EqualsAndHashCode;
19 | import lombok.Value;
20 | import org.jspecify.annotations.Nullable;
21 | import org.openrewrite.*;
22 |
23 | @Value
24 | @EqualsAndHashCode(callSuper = false)
25 | public class FindDependency extends Recipe {
26 |
27 | @Option(displayName = "Group ID",
28 | description = "The first part of a dependency coordinate identifying its publisher.",
29 | example = "com.google.guava")
30 | String groupId;
31 |
32 | @Option(displayName = "Artifact ID",
33 | description = "The second part of a dependency coordinate uniquely identifying it among artifacts from the same publisher.",
34 | example = "guava")
35 | String artifactId;
36 |
37 | @Option(displayName = "Version",
38 | description = "An exact version number or node-style semver selector used to select the version number.",
39 | example = "3.0.0",
40 | required = false)
41 | @Nullable
42 | String version;
43 |
44 | @Option(displayName = "Version pattern",
45 | description = "Allows version selection to be extended beyond the original Node Semver semantics. " +
46 | "So for example, setting 'version' to \"25-29\" can be paired with a metadata pattern of \"-jre\" to select Guava 29.0-jre",
47 | example = "-jre",
48 | required = false)
49 | @Nullable
50 | String versionPattern;
51 |
52 | @Option(displayName = "Configuration",
53 | description = "For Gradle only, the dependency configuration to search for dependencies in. If omitted then all configurations will be searched.",
54 | example = "api",
55 | required = false)
56 | @Nullable
57 | String configuration;
58 |
59 | @Override
60 | public String getDisplayName() {
61 | return "Find Maven and Gradle dependencies";
62 | }
63 |
64 | @Override
65 | public String getDescription() {
66 | return "Finds direct dependencies declared in Maven and Gradle build files. " +
67 | "This does *not* search transitive dependencies. " +
68 | "To detect both direct and transitive dependencies use `org.openrewrite.java.dependencies.DependencyInsight` " +
69 | "This recipe works for both Maven and Gradle projects.";
70 | }
71 |
72 | @Override
73 | public TreeVisitor, ExecutionContext> getVisitor() {
74 | return new TreeVisitor() {
75 |
76 | final TreeVisitor, ExecutionContext> mavenFindDependency = new org.openrewrite.maven.search.FindDependency(groupId,artifactId,version,versionPattern)
77 | .getVisitor();
78 | final TreeVisitor, ExecutionContext> gradleFindDependency = new org.openrewrite.gradle.search.FindDependency(groupId, artifactId, configuration, version, versionPattern)
79 | .getVisitor();
80 |
81 | @Override
82 | public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
83 | if (!(tree instanceof SourceFile)) {
84 | return tree;
85 | }
86 | SourceFile s = (SourceFile) tree;
87 | if (mavenFindDependency.isAcceptable(s, ctx)) {
88 | return mavenFindDependency.visitNonNull(tree, ctx);
89 | }
90 | if (gradleFindDependency.isAcceptable(s, ctx)) {
91 | // Handle Gradle projects
92 | return gradleFindDependency.visitNonNull(tree, ctx);
93 | }
94 | return s;
95 | }
96 | };
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/internal/VersionParser.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.openrewrite.java.dependencies.internal;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 | import java.util.Map;
22 | import java.util.concurrent.ConcurrentHashMap;
23 |
24 | public class VersionParser {
25 | private final Map cache = new ConcurrentHashMap<>();
26 |
27 | public Version transform(String original) {
28 | return cache.computeIfAbsent(original, this::parse);
29 | }
30 |
31 | private Version parse(String original) {
32 | List parts = new ArrayList<>();
33 | boolean digit = false;
34 | int startPart = 0;
35 | int pos = 0;
36 | int endBaseStr = 0;
37 | for (; pos < original.length(); pos++) {
38 | char ch = original.charAt(pos);
39 | if (ch == '.' || ch == '_' || ch == '-' || ch == '+') {
40 | parts.add(original.substring(startPart, pos));
41 | startPart = pos + 1;
42 | digit = false;
43 | if (ch != '.' && endBaseStr == 0) {
44 | endBaseStr = pos;
45 | }
46 | } else if (ch >= '0' && ch <= '9') {
47 | if (!digit && pos > startPart) {
48 | if (endBaseStr == 0) {
49 | endBaseStr = pos;
50 | }
51 | parts.add(original.substring(startPart, pos));
52 | startPart = pos;
53 | }
54 | digit = true;
55 | } else {
56 | if (digit) {
57 | if (endBaseStr == 0) {
58 | endBaseStr = pos;
59 | }
60 | parts.add(original.substring(startPart, pos));
61 | startPart = pos;
62 | }
63 | digit = false;
64 | }
65 | }
66 | if (pos > startPart) {
67 | parts.add(original.substring(startPart, pos));
68 | }
69 | return new DefaultVersion(original, parts);
70 | }
71 |
72 | private static class DefaultVersion implements Version {
73 | private final String source;
74 | private final String[] parts;
75 | private final Long[] numericParts;
76 |
77 | public DefaultVersion(String source, List parts) {
78 | this.source = source;
79 | this.parts = parts.toArray(new String[0]);
80 | this.numericParts = new Long[this.parts.length];
81 | for (int i = 0; i < parts.size(); i++) {
82 | try {
83 | this.numericParts[i] = Long.parseLong(this.parts[i]);
84 | } catch (NumberFormatException ignored) {
85 | }
86 | }
87 | }
88 |
89 | @Override
90 | public String toString() {
91 | return source;
92 | }
93 |
94 | @Override
95 | public boolean equals(Object obj) {
96 | if (obj == this) {
97 | return true;
98 | }
99 | if (obj == null || obj.getClass() != getClass()) {
100 | return false;
101 | }
102 | DefaultVersion other = (DefaultVersion) obj;
103 | return source.equals(other.source);
104 | }
105 |
106 | @Override
107 | public int hashCode() {
108 | return source.hashCode();
109 | }
110 |
111 | @Override
112 | public String[] getParts() {
113 | return parts;
114 | }
115 |
116 | @Override
117 | public Long[] getNumericParts() {
118 | return numericParts;
119 | }
120 |
121 | @Override
122 | public String getSource() {
123 | return source;
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/test/java/org/openrewrite/java/dependencies/FindDependencyTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 the original author or authors.
3 | *
4 | * Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
5 | */
6 | package org.openrewrite.java.dependencies;
7 |
8 | import org.junit.jupiter.api.Test;
9 | import org.openrewrite.DocumentExample;
10 | import org.openrewrite.test.RecipeSpec;
11 | import org.openrewrite.test.RewriteTest;
12 |
13 | import static org.openrewrite.gradle.Assertions.buildGradle;
14 | import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi;
15 | import static org.openrewrite.maven.Assertions.pomXml;
16 |
17 | @SuppressWarnings("GroovyAssignabilityCheck")
18 | class FindDependencyTest implements RewriteTest {
19 |
20 | @Override
21 | public void defaults(RecipeSpec spec) {
22 | spec.recipe(new FindDependency("org.openrewrite", "rewrite-core", "8.0.0", null, null));
23 | }
24 |
25 | @DocumentExample
26 | @Test
27 | void findMavenDependency() {
28 | rewriteRun(
29 | //language=xml
30 | pomXml(
31 | """
32 |
33 | com.mycompany.app
34 | my-app
35 | 1
36 |
37 |
38 | org.openrewrite
39 | rewrite-core
40 | 8.0.0
41 |
42 |
43 |
44 | """,
45 | """
46 |
47 | com.mycompany.app
48 | my-app
49 | 1
50 |
51 |
52 | org.openrewrite
53 | rewrite-core
54 | 8.0.0
55 |
56 |
57 |
58 | """
59 | )
60 | );
61 | }
62 |
63 | @Test
64 | void findMavenDependencyDoesNotFindWrongVersion() {
65 | rewriteRun(
66 | //language=xml
67 | pomXml(
68 | """
69 |
70 | com.mycompany.app
71 | my-app
72 | 1
73 |
74 |
75 | org.openrewrite
76 | rewrite-core
77 | 8.1.0
78 |
79 |
80 |
81 | """
82 | )
83 | );
84 | }
85 |
86 | @Test
87 | void findGradleDependency() {
88 | rewriteRun(
89 | spec -> spec.beforeRecipe(withToolingApi()),
90 | //language=groovy
91 | buildGradle(
92 | """
93 | plugins {
94 | id 'java-library'
95 | }
96 |
97 | repositories {
98 | mavenCentral()
99 | }
100 |
101 | dependencies {
102 | api "org.openrewrite:rewrite-core:8.0.0"
103 | }
104 | """,
105 | """
106 | plugins {
107 | id 'java-library'
108 | }
109 |
110 | repositories {
111 | mavenCentral()
112 | }
113 |
114 | dependencies {
115 | /*~~>*/api "org.openrewrite:rewrite-core:8.0.0"
116 | }
117 | """
118 | )
119 | );
120 | }
121 |
122 | @Test
123 | void findGradleDependencyDoesntFindWrongVersion() {
124 | rewriteRun(
125 | spec -> spec.beforeRecipe(withToolingApi()),
126 | //language=groovy
127 | buildGradle(
128 | """
129 | plugins {
130 | id 'java-library'
131 | }
132 |
133 | repositories {
134 | mavenCentral()
135 | }
136 |
137 | dependencies {
138 | api "org.openrewrite:rewrite-core:8.1.0"
139 | }
140 | """
141 | )
142 | );
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/search/DoesNotIncludeDependency.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.openrewrite.java.dependencies.search;
17 |
18 | import lombok.EqualsAndHashCode;
19 | import lombok.Value;
20 | import org.jspecify.annotations.Nullable;
21 | import org.openrewrite.*;
22 |
23 | @EqualsAndHashCode(callSuper = false)
24 | @Value
25 | public class DoesNotIncludeDependency extends Recipe {
26 | @Override
27 | public String getDisplayName() {
28 | return "Does not include dependency for Gradle and Maven";
29 | }
30 |
31 | @Override
32 | public String getDescription() {
33 | return "A precondition which returns false if visiting a Gradle file / Maven pom which includes the specified dependency in the classpath of some Gradle configuration / Maven scope. " +
34 | "For compatibility with multimodule projects, this should most often be applied as a precondition.";
35 | }
36 |
37 | @Option(displayName = "Group",
38 | description = "The first part of a dependency coordinate `com.google.guava:guava:VERSION`. Supports glob.",
39 | example = "com.google.guava")
40 | String groupId;
41 |
42 | @Option(displayName = "Artifact",
43 | description = "The second part of a dependency coordinate `com.google.guava:guava:VERSION`. Supports glob.",
44 | example = "guava")
45 | String artifactId;
46 |
47 | @Option(displayName = "Version",
48 | description = "Match only dependencies with the specified resolved version. " +
49 | "Node-style [version selectors](https://docs.openrewrite.org/reference/dependency-version-selectors) may be used. " +
50 | "All versions are searched by default.",
51 | example = "1.x",
52 | required = false)
53 | @Nullable
54 | String version;
55 |
56 | @Option(displayName = "Only direct dependencies",
57 | description = "Default false. If enabled, transitive dependencies will not be considered.",
58 | required = false,
59 | example = "true")
60 | @Nullable
61 | Boolean onlyDirect;
62 |
63 | @Option(displayName = "Maven scope",
64 | description = "Default any. If specified, only the requested scope's classpaths will be checked.",
65 | required = false,
66 | valid = {"compile", "test", "runtime", "provided"},
67 | example = "compile")
68 | @Nullable
69 | String scope;
70 |
71 | @Option(displayName = "Gradle configuration",
72 | description = "Match dependencies with the specified configuration. If not specified, all configurations will be searched.",
73 | example = "compileClasspath",
74 | required = false)
75 | @Nullable
76 | String configuration;
77 |
78 | @Override
79 | public TreeVisitor, ExecutionContext> getVisitor() {
80 | return new TreeVisitor() {
81 | final TreeVisitor, ExecutionContext> gdnid = new org.openrewrite.gradle.search.DoesNotIncludeDependency(
82 | groupId, artifactId, version, configuration)
83 | .getVisitor();
84 | final TreeVisitor, ExecutionContext> mdnid = new org.openrewrite.maven.search.DoesNotIncludeDependency(
85 | groupId, artifactId, version, onlyDirect, scope)
86 | .getVisitor();
87 |
88 | @Override
89 | public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) {
90 | return gdnid.isAcceptable(sourceFile, ctx) || mdnid.isAcceptable(sourceFile, ctx);
91 | }
92 |
93 | @Override
94 | public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
95 | if (!(tree instanceof SourceFile)) {
96 | return tree;
97 | }
98 | SourceFile s = (SourceFile) tree;
99 | if (gdnid.isAcceptable(s, ctx)) {
100 | s = (SourceFile) gdnid.visitNonNull(s, ctx);
101 | } else if (mdnid.isAcceptable(s, ctx)) {
102 | s = (SourceFile) mdnid.visitNonNull(s, ctx);
103 | }
104 | return s;
105 | }
106 | };
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/org/openrewrite/java/dependencies/search/RepositoryHasDependency.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.openrewrite.java.dependencies.search;
17 |
18 | import lombok.EqualsAndHashCode;
19 | import lombok.Value;
20 | import org.jspecify.annotations.Nullable;
21 | import org.openrewrite.*;
22 | import org.openrewrite.java.dependencies.DependencyInsight;
23 | import org.openrewrite.java.marker.JavaProject;
24 | import org.openrewrite.marker.SearchResult;
25 |
26 | import java.util.concurrent.atomic.AtomicBoolean;
27 |
28 | @EqualsAndHashCode(callSuper = false)
29 | @Value
30 | public class RepositoryHasDependency extends ScanningRecipe {
31 |
32 | @Override
33 | public String getDisplayName() {
34 | return "Repository has dependency";
35 | }
36 |
37 | @Override
38 | public String getDescription() {
39 | return "Searches for both Gradle and Maven modules that have a dependency matching the specified groupId and artifactId. " +
40 | "Places a `SearchResult` marker on all sources within a repository with a matching dependency. " +
41 | "This recipe is intended to be used as a precondition for other recipes. " +
42 | "For example this could be used to limit the application of a spring boot migration to only projects " +
43 | "that use a springframework dependency, limiting unnecessary upgrading. " +
44 | "If the search result you want is instead just the build.gradle(.kts) or pom.xml file applying the plugin, use the `FindDependency` recipe instead.";
45 | }
46 |
47 | @Option(displayName = "Group pattern",
48 | description = "Group glob pattern used to match dependencies.",
49 | example = "com.fasterxml.jackson.module")
50 | String groupIdPattern;
51 |
52 | @Option(displayName = "Artifact pattern",
53 | description = "Artifact glob pattern used to match dependencies.",
54 | example = "jackson-module-*")
55 | String artifactIdPattern;
56 |
57 | @Option(displayName = "Scope",
58 | description = "Match dependencies with the specified scope. All scopes are searched by default.",
59 | valid = {"compile", "test", "runtime", "provided", "system"},
60 | example = "compile",
61 | required = false)
62 | @Nullable
63 | String scope;
64 |
65 | @Option(displayName = "Version",
66 | description = "Match only dependencies with the specified version. " +
67 | "Node-style [version selectors](https://docs.openrewrite.org/reference/dependency-version-selectors) may be used." +
68 | "All versions are searched by default.",
69 | example = "1.x",
70 | required = false)
71 | @Nullable
72 | String version;
73 |
74 | @Override
75 | public AtomicBoolean getInitialValue(ExecutionContext ctx) {
76 | return new AtomicBoolean();
77 | }
78 |
79 | @Override
80 | public TreeVisitor, ExecutionContext> getScanner(AtomicBoolean acc) {
81 | return new TreeVisitor() {
82 | @Override
83 | public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
84 | if(acc.get()) {
85 | assert tree != null;
86 | return tree;
87 | }
88 | assert tree != null;
89 | tree.getMarkers()
90 | .findFirst(JavaProject.class)
91 | .ifPresent(jp -> {
92 | Tree t = new DependencyInsight(groupIdPattern, artifactIdPattern, scope, version)
93 | .getVisitor()
94 | .visit(tree, ctx);
95 | if (t != tree) {
96 | acc.set(true);
97 | }
98 | });
99 | return tree;
100 | }
101 | };
102 | }
103 |
104 | @Override
105 | public TreeVisitor, ExecutionContext> getVisitor(AtomicBoolean acc) {
106 | if (acc.get()) {
107 | return new TreeVisitor() {
108 | @Override
109 | public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
110 | assert tree != null;
111 | return SearchResult.found(tree, "Repository has dependency: " + groupIdPattern + ":" + artifactIdPattern + (version == null ? "" : ":" + version));
112 | }
113 | };
114 | }
115 | return TreeVisitor.noop();
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/test/java/org/openrewrite/java/dependencies/AddDependencyTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.openrewrite.java.dependencies;
17 |
18 | import lombok.EqualsAndHashCode;
19 | import lombok.Value;
20 | import org.jspecify.annotations.Nullable;
21 | import org.openrewrite.*;
22 | import org.openrewrite.java.marker.JavaProject;
23 | import org.openrewrite.java.search.UsesType;
24 |
25 | import java.util.HashMap;
26 | import java.util.Map;
27 |
28 | @EqualsAndHashCode(callSuper = false)
29 | @Value
30 | public class RemoveDependency extends ScanningRecipe