The two day air-gapped Moderne CLI workshop allows participants to learn how to leverage OSS OpenRewrite recipes for OSS framework migrations and vulnerability management, as well as conduct impact analysis studies and build custom migrations via custom OpenRewrite recipes. The workshop is conducted by developers leveraging Moderne CLI and OpenRewrite recipes on their workstations. The Moderne team assists your developers during the workshop, but the code doesn’t leave developer workstations during the workshop. Existing access control policies govern developer access to SCM and Artifact stores.
After developers familiarize themselves with the CLI, we run code cleanup, framework migrations, and OWASP Top 10 vulnerability management recipes and collect ROI with our data tables. This would feed into the value deck we create after the workshop to describe results to management.
The second part of the workshop is focused on teaching developers how to plan and execute custom migration. Some example migrations we implemented in the past - switching identity providers, moving from one database to another, integrating with an internal third-party vulnerability management tool, and integrating with Launch Darkly to remove unused feature flags. We show how to quickly write a search recipe that allows us to output a data table of technology in use across all repositories. Then we implement custom recipes for the migration. The migration is not guaranteed to be complete by the end of the workshop, however, your developers come out with the necessary skills to complete the migration after.
Prerequisites
The Moderne CLI and open source OpenRewrite recipe JARs need to be available in your internal mirror of Maven Central.
You will need to be proficient with Java.
Someone at your company needs to perform all of the steps in the environment preparation section before the workshop begins.
(Optionally) Create an alias for the Moderne CLI JAR
While not required, you are strongly encouraged to set up an alias for running the CLI JAR. Below are some ways you might configure this depending on your OS and terminal:
Please run the following command to configure your license:
modconfiglicenseedit<license-you-were-provided>
If you want more details about the license and its checks, please see our Moderne CLI license doc.
Configure the CLI to point to your internal Artifact repository
In order for the CLI to have access to read and publish LSTs, it will need to be provided with the path to your Maven settings file. This likely already exists on developer machines for the sake of redirecting requests from Maven Central to an internal Artifact instance. Recipe installation in the next step will use this Maven settings file.
For each of the GAV coordinates below, please ensure that the artifact has been added to your internal Artifactory instance (assuming that your Artifactory instance is not a pure remote proxy to Maven Central already or that there isn't some automatic procurement step at dependency resolution time):
(Optionally) Create a CSV of the repositories you want to use
If you are going to be performing the workshop on a developer machine that already has a bunch of locally cloned repositories you want to run recipes against, feel free to skip to the building LSTs step.
If you need to clone repositories locally, you'll need to create a repos.csv file. This file will take in a list of repositories, the branch to use, and (optionally) various attributes about them that the CLI will use when building the repository.
This only applies if you created a CSV in the previous step
With the CSV created, please run the mod git clone command similar to the following (replace spring-data with the location where you want the repositories cloned to):
Building LSTs is the most time-consuming step, as it is proportional to compilation time. In contrast to open-source OpenRewrite, these LSTs are saved to disk in a proprietary format.
In customer environments, we set up the build and publish of LSTs to an artifact store on a scheduled basis. mod build can take advantage of this and will download an LST from that artifact store if its changeset matches the changeset of the cloned repository.
Notice that you didn't need to configure Maven or Gradle, select Java versions, etc. The Moderne CLI detects all of that, matching up build tools and JDKs on the machine to each repository based on each repository's unique requirements. The detection can be overridden where needed – but it generally isn't.
➜modbuild./spring-dataModerneCLI3.2.0> Selecting repositories> spring-projects/spring-data-cassandra@main> spring-projects/spring-data-commons@main> spring-projects/spring-data-couchbase@main> spring-projects/spring-data-elasticsearch@main> spring-projects/spring-data-jpa@main> spring-projects/spring-data-keyvalue@main> spring-projects/spring-data-ldap@main> spring-projects/spring-data-mongodb@main> spring-projects/spring-data-neo4j@main> spring-projects/spring-data-redis@main> spring-projects/spring-data-relational@main> spring-projects/spring-data-rest@mainSelected12repositories (0.63s)> Building LST(s)> spring-projects/spring-data-cassandra@main Build output will be written to file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-cassandra/.moderne/build/20240313103801-vgPGE/build.log
>Step1-buildwithMavenSelectedthe21.0.1-oracleJDK>Step2-buildwithmod-javaSelectedthe21.0.1-oracleJDK>Step3-buildresourcesusingthenativeCLI ✓ Built LST file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-cassandra/.moderne/build/20240313103801-vgPGE/spring-data-cassandra-20240313103852-ast.jar
+ReportedbuildmetricstoModerne+Cleaned1olderbuilds.> spring-projects/spring-data-commons@main Build output will be written to file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-commons/.moderne/build/20240313103801-vgPGE/build.log
...
With an empty M2_LOCAL, this can take approximately 30 minutes to build.
Run a refactoring recipe
To make sure everything works, it's a good idea to run a refactoring recipe. Please run the following:
mod run ./spring-data --recipe CommonStaticAnalysis
mod run ./spring-data --recipe UpgradeToJava17
To view the diff, you can command + click on the file (or ctrl + click if on Windows).
Example CommonStaticAnalysis run:
➜modrun./spring-data--recipeCommonStaticAnalysisModerneCLI3.2.0> Selecting repositories> spring-projects/spring-data-cassandra@main> spring-projects/spring-data-commons@main> spring-projects/spring-data-couchbase@main> spring-projects/spring-data-elasticsearch@main> spring-projects/spring-data-jpa@main> spring-projects/spring-data-keyvalue@main> spring-projects/spring-data-ldap@main> spring-projects/spring-data-mongodb@main> spring-projects/spring-data-neo4j@main> spring-projects/spring-data-redis@main> spring-projects/spring-data-relational@main> spring-projects/spring-data-rest@mainSelected12repositories (0.74s)[1] Common static analysis issues (org.openrewrite.python.cleanup.CommonStaticAnalysis)[2] Common static analysis issues (org.openrewrite.staticanalysis.CommonStaticAnalysis)Selectarecipe [1-2]: 2> Running recipe org.openrewrite.staticanalysis.CommonStaticAnalysis> spring-projects/spring-data-cassandra@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-cassandra/.moderne/run/20240318101307-SOK8L/fix.patch
> spring-projects/spring-data-commons@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-commons/.moderne/run/20240318101307-SOK8L/fix.patch
> spring-projects/spring-data-couchbase@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-couchbase/.moderne/run/20240318101307-SOK8L/fix.patch
> spring-projects/spring-data-elasticsearch@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-elasticsearch/.moderne/run/20240318101307-SOK8L/fix.patch
> spring-projects/spring-data-jpa@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-jpa/.moderne/run/20240318101307-SOK8L/fix.patch
> spring-projects/spring-data-keyvalue@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-keyvalue/.moderne/run/20240318101307-SOK8L/fix.patch
> spring-projects/spring-data-ldap@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-ldap/.moderne/run/20240318101307-SOK8L/fix.patch
> spring-projects/spring-data-mongodb@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-mongodb/.moderne/run/20240318101307-SOK8L/fix.patch
> spring-projects/spring-data-neo4j@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-neo4j/.moderne/run/20240318101307-SOK8L/fix.patch
> spring-projects/spring-data-redis@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-redis/.moderne/run/20240318101307-SOK8L/fix.patch
> spring-projects/spring-data-relational@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-relational/.moderne/run/20240318101307-SOK8L/fix.patch
> spring-projects/spring-data-rest@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-rest/.moderne/run/20240318101307-SOK8L/fix.patch
Foundresultson12repositories (2m 5s)6m24ssavedbyusingpreviouslybuiltLSTs* What to donext>Clickononeofthepatchlinksabovetoviewthechangesonaparticularrepository > Run mod study ./spring-data --last-recipe-run --data-table <DATA-TABLE> to examine the following data tables produced by this recipe:
org.openrewrite.table.RecipeRunStatsorg.openrewrite.table.SourcesFileResults>Runnpminstall-gdiff2html-clitoproducepatchfilesonsubsequentrunsthatareeasiertoview>Runmodgitapply./spring-data--last-recipe-runtoapplythechanges>Runmodgitapply./spring-data--recipe-run20240318101307-SOK8LtoapplythechangesMODSUCCEEDEDin (2m 15s)
Example UpgradeToJava17 run:
➜modrun./spring-data--recipeUpgradeToJava17ModerneCLI3.2.0> Selecting repositories> spring-projects/spring-data-cassandra@main> spring-projects/spring-data-commons@main> spring-projects/spring-data-couchbase@main> spring-projects/spring-data-elasticsearch@main> spring-projects/spring-data-jpa@main> spring-projects/spring-data-keyvalue@main> spring-projects/spring-data-ldap@main> spring-projects/spring-data-mongodb@main> spring-projects/spring-data-neo4j@main> spring-projects/spring-data-redis@main> spring-projects/spring-data-relational@main> spring-projects/spring-data-rest@mainSelected12repositories (0.64s)[1] Migrate to Java 17 (org.openrewrite.java.migrate.UpgradeToJava17)[2] Migrate to Java 17 (io.quarkus.updates.core.quarkus37.UpgradeToJava17)Selectarecipe [1-2]: 1> Running recipe org.openrewrite.java.migrate.UpgradeToJava17> spring-projects/spring-data-cassandra@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-cassandra/.moderne/run/20240318101630-1v5cN/fix.patch
> spring-projects/spring-data-commons@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-commons/.moderne/run/20240318101630-1v5cN/fix.patch
> spring-projects/spring-data-couchbase@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-couchbase/.moderne/run/20240318101630-1v5cN/fix.patch
> spring-projects/spring-data-elasticsearch@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-elasticsearch/.moderne/run/20240318101630-1v5cN/fix.patch
> spring-projects/spring-data-jpa@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-jpa/.moderne/run/20240318101630-1v5cN/fix.patch
> spring-projects/spring-data-keyvalue@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-keyvalue/.moderne/run/20240318101630-1v5cN/fix.patch
> spring-projects/spring-data-ldap@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-ldap/.moderne/run/20240318101630-1v5cN/fix.patch
> spring-projects/spring-data-mongodb@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-mongodb/.moderne/run/20240318101630-1v5cN/fix.patch
> spring-projects/spring-data-neo4j@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-neo4j/.moderne/run/20240318101630-1v5cN/fix.patch
> spring-projects/spring-data-redis@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-redis/.moderne/run/20240318101630-1v5cN/fix.patch
> spring-projects/spring-data-relational@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-relational/.moderne/run/20240318101630-1v5cN/fix.patch
> spring-projects/spring-data-rest@main ✓ Fix results at file:///Users/mikesol/Desktop/code/tmp/spring-data/spring-data-rest/.moderne/run/20240318101630-1v5cN/fix.patch
Foundresultson12repositories (2m 30s)6m24ssavedbyusingpreviouslybuiltLSTs* What to donext>Clickononeofthepatchlinksabovetoviewthechangesonaparticularrepository > Run mod study ./spring-data --last-recipe-run --data-table <DATA-TABLE> to examine the following data tables produced by this recipe:
org.openrewrite.table.RecipeRunStatsorg.openrewrite.table.SourcesFileResultsorg.openrewrite.table.SourcesFiles>Runnpminstall-gdiff2html-clitoproducepatchfilesonsubsequentrunsthatareeasiertoview>Runmodgitapply./spring-data--last-recipe-runtoapplythechanges>Runmodgitapply./spring-data--recipe-run20240318101630-1v5cNtoapplythechangesMODSUCCEEDEDin (2m 43s)
These recipes will prepare patch files in .moderne folders in each repository, which can then be studied further or applied to the repository and committed:
modgitapply./spring-data--last-recipe-runmodgitcheckout./spring-datajava-17-upgrade-bmodgitcommit./spring-data-m"Upgrade to Java 17"
You likely have noticed the pattern by now. Moderne CLI commands are meant to operate recursively on the ./spring-data directory. For getting changes committed, mod acts as a poly-repository git.
Study the results of a recipe run
Recipes can produce data tables as a recipe run proceeds. Data tables are columnar data in a schema defined by the recipe.
Then, you can customize the output by providing a --template flag:
mod study ./spring-data --last-recipe-run --data-table MethodCalls --json sourceFile,method --template '{{"# Search results\n\n"}}{{range .}}{{"* "}}{{.sourceFile}}{{"\n```\n"}}{{.method}}{{"\n```\n"}}{{end}}' > methods.md
In the above example, we are filtering the data table to only a couple columns we are interested in and then using a GoTemplate to produce a markdown file containing code samples for all of the matching methods we found in these 12 repositories.