Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removing managed dependency problem: Upgrade parent version or remove redundant version recipe (GTE) #4949

Open
crankydillo opened this issue Jan 25, 2025 · 5 comments
Labels
bug Something isn't working test provided Already replicated with a unit test, using JUnit pioneer's ExpectedToFail

Comments

@crankydillo
Copy link
Contributor

My ultimate goal is to upgrade the parent pom of a multi-module Maven project and to remove any project dependency versions that are lower that what the new parent manages. We initially started with UpgradeParentVersion, but doesn't seem to work well with modules. I'm suspicious that UpgradeMavenModel isn't propagating all changes to the 'POM tree', but I may be misunderstanding how that works. I have a few other issues/comments about this (apologies for not linking yet). Regardless, in later recipes we are messing with dependencies , so added a RemoveRedundantDependencyVersions recipe at the end. This covers multiple cases that you'd think would be covered by UpgradeParentVersion, but it doesn't seem to handle the case where if a.group:a.artifact:1.0 did not exist in the old parent, but does in the new. In that case, we expect all project usage of a.group:a.artifact:1.0 to be replaced with a.group:a.artifact (no version).

The following test, which I added to UpgradeParentVersionTest, is the shortest form of what we're experiencing. jakarta.annotation-api was added in spring-boot-dependencies:3. When you run, you will see that dependency version does not get removed from the child module pom. If you only run UpgradeParentVersion in the test (remove RemoveRedundant..), you'll see neither gets removed. If I bring that back and set onlyIfManagedVersionIs to ANY, it does remove it. However, we don't want to override project-level dependencies that are higher than what we manage.

Originally posted in this discussion

What version of OpenRewrite are you using?

Test below was run on that main branch in the rewrite-maven module

What is the smallest, simplest way to reproduce the problem?

    @Test
    void sams() {
        rewriteRun(
          spec -> spec.recipe(
            new CompositeRecipe(
              Arrays.asList(
                new UpgradeParentVersion("org.springframework.boot", "spring-boot-dependencies", "3.4.1", null, null)
               , new RemoveRedundantDependencyVersions(null, null, RemoveRedundantDependencyVersions.Comparator.GTE, null)
              )
            )
          ),
            pomXml("""
              <project>
                <parent>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-dependencies</artifactId>
                  <version>2.4.0</version>
                </parent>
                <groupId>cool.stuff</groupId>
                <artifactId>parent</artifactId>
                <version>1.0-SNAPSHOT</version>
                <packaging>pom</packaging>
                <modules>
                  <module>child</module>
                </modules>
                <dependencies>
                  <dependency>
                    <groupId>jakarta.annotation</groupId>
                    <artifactId>jakarta.annotation-api</artifactId>
                    <version>2.1.1</version>
                  </dependency>
                  <dependency>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                    <version>1.7.30</version>
                  </dependency>
                </dependencies>
              </project>
              """, """
              <project>
                <parent>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-dependencies</artifactId>
                  <version>3.4.1</version>
                </parent>
                <groupId>cool.stuff</groupId>
                <artifactId>parent</artifactId>
                <version>1.0-SNAPSHOT</version>
                <packaging>pom</packaging>
                <modules>
                  <module>child</module>
                </modules>
                <dependencies>
                  <dependency>
                    <groupId>jakarta.annotation</groupId>
                    <artifactId>jakarta.annotation-api</artifactId>
                  </dependency>
                  <dependency>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                  </dependency>
                </dependencies>
              </project>
              """),
              mavenProject("child", pomXml("""
                  <project>
                    <parent>
                      <groupId>cool.stuff</groupId>
                      <artifactId>parent</artifactId>
                      <version>1.0-SNAPSHOT</version>
                    </parent>
                    <artifactId>child</artifactId>
                    <version>1.0-SNAPSHOT</version>
                    <dependencies>
                      <dependency>
                        <groupId>jakarta.annotation</groupId>
                        <artifactId>jakarta.annotation-api</artifactId>
                        <version>2.1.1</version>
                      </dependency>
                      <dependency>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-api</artifactId>
                        <version>1.7.30</version>
                      </dependency>
                    </dependencies>
                  </project>
                """,
                """
                  <project>
                    <parent>
                      <groupId>cool.stuff</groupId>
                      <artifactId>parent</artifactId>
                      <version>1.0-SNAPSHOT</version>
                    </parent>
                    <artifactId>child</artifactId>
                    <version>1.0-SNAPSHOT</version>
                    <dependencies>
                      <dependency>
                        <groupId>jakarta.annotation</groupId>
                        <artifactId>jakarta.annotation-api</artifactId>
                      </dependency>
                      <dependency>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-api</artifactId>
                      </dependency>
                    </dependencies>
                  </project>
                """
              ))
        );
    }
@crankydillo crankydillo added the bug Something isn't working label Jan 25, 2025
@MBoegers MBoegers added the test provided Already replicated with a unit test, using JUnit pioneer's ExpectedToFail label Jan 27, 2025
@MBoegers
Copy link
Contributor

Hi @crankydillo 👋 , thanks for the reproducer and the report! Out of curiosity: Do you test it with a parent pom inside the same project? If that worked, it would indicate a problem with resolved dependencies.

@crankydillo
Copy link
Contributor Author

crankydillo commented Jan 27, 2025

The parent version we are upgrading to is not inside the project. However, the project is multi-module and the modules parent from a pom that is inside the project. The POM setup from the test case I give is pretty much what our projects look like. Let me know if that doesn't explain it well.

I didn't really debug things this time, but I think it might be the same issue I was experiencing here. Near the end, I do talk about some suspicions around resolved dependencies not getting updated.

@mryan43
Copy link

mryan43 commented Feb 13, 2025

I'm also having issues with this, we are using AddManagedDependency, then RemoveRedundantDependencyVersions

I'm using the following test to reproduce the issue:

class AddBomToParentThenRemoveRedundantDependencyTest implements RewriteTest {

    @Override
    public void defaults(RecipeSpec spec) {
        spec.recipes(
          new AddManagedDependency("org.springframework.boot", "spring-boot-dependencies", "3.4.2", "import", "pom", null, null, null, null, true),
          new RemoveRedundantDependencyVersions("*", "*", RemoveRedundantDependencyVersions.Comparator.ANY, null));
    }

    @Test
    void bomShouldBeAddedToParentPomAndVersionTagShouldBeRemoved() {
        rewriteRun(
          mavenProject(
            "my-app-parent",
            //language=xml
            pomXml(
              """
                <project>
                  <modelVersion>4.0.0</modelVersion>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app-parent</artifactId>
                  <version>1</version>
                  <packaging>pom</packaging>
                  <modules>
                    <module>my-app</module>
                  </modules>
                </project>
                """,
              """
                <project>
                  <modelVersion>4.0.0</modelVersion>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app-parent</artifactId>
                  <version>1</version>
                  <packaging>pom</packaging>
                  <modules>
                    <module>my-app</module>
                  </modules>
                  <dependencyManagement>
                    <dependencies>
                      <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-dependencies</artifactId>
                        <version>3.4.2</version>
                        <type>pom</type>
                        <scope>import</scope>
                      </dependency>
                    </dependencies>
                  </dependencyManagement>
                </project>
                """
            )
          ),
          mavenProject(
            "my-app",
            //language=xml
            pomXml("""
              <project>
                <parent>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app-parent</artifactId>
                  <version>1</version>
                </parent>
              	<artifactId>my-app</artifactId>
              	<dependencies>
              		<dependency>
              		   <groupId>org.hibernate.orm</groupId>
              		   <artifactId>hibernate-core</artifactId>
              		   <version>6.6.6.Final</version>
              	   </dependency>
              	</dependencies>
              </project>
              """, """
              <project>
                <parent>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app-parent</artifactId>
                  <version>1</version>
                </parent>
              	<artifactId>my-app</artifactId>
              	<dependencies>
              		<dependency>
              		   <groupId>org.hibernate.orm</groupId>
              		   <artifactId>hibernate-core</artifactId>
              	   </dependency>
              	</dependencies>
              </project>
              """)
          )
        );
    }

}

I'm going to try to debug this test to see if I can submit a fix suggestion

@mryan43
Copy link

mryan43 commented Feb 13, 2025

Update on the debugging session

in RemoveRedundantDependencyVersions:

private boolean matchesVersion(ResolvedDependency d) {
                if (d.getRequested().getVersion() == null) {
                    return false;
                }
                String managedVersion = getResolutionResult().getPom().getManagedVersion(d.getGroupId(),
                        d.getArtifactId(), d.getRequested().getType(), d.getRequested().getClassifier());
                return matchesComparator(managedVersion, d.getRequested().getVersion());
            }

managedVersion is null but should not be

@mryan43
Copy link

mryan43 commented Feb 13, 2025

I looks like the UpdateMavenModel visitor updates the model for the parent pom, but the child model is never updated. Anyone has an idea of how we could run UpdateMavenModel on the childen when a parent is updated ?

After a bit more debugging, it seem that the model of the child is updated, but only in the MavenResolutionResult that is attached to the document of the parent (visitor of AddManagedDependency or UpgradeParentVersion). The MavenResolutionResult for the MavenVisitor of RemoveRedundantDependencyVersions is never updated.

Could the ExecutionContext be a better place than MavenVisitor to store MavenResolutionResults ? I'll give it a try

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working test provided Already replicated with a unit test, using JUnit pioneer's ExpectedToFail
Projects
Status: No status
Development

No branches or pull requests

3 participants