From 3ef3d2eeda571179d8da15ac88b7bc5269e11247 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:36:13 +0000 Subject: [PATCH 1/4] Add Spring Boot Migration workshop lesson This commit adds a comprehensive tutorial lesson for migrating Spring Boot applications using OpenRewrite and the Moderne CLI. - Created `examples/moderne-spring-boot-migration/lessons/spring-boot-migration/lesson.yml` with lesson metadata. - Implemented 6 lesson modules covering Assessment, Wave Planning, Baseline, Smoke Test, and Wave Migration. - Updated `examples/moderne-spring-boot-migration/Dockerfile` to install Java 17 (required for the migration path) and set it as the default JDK for the user, while ensuring the platform runs on Java 25. - Included detailed instructions and quiz questions in each module. - Improved UX by using `cat` commands for file creation steps. Co-authored-by: mtthwcmpbll <226487+mtthwcmpbll@users.noreply.github.com> --- .../moderne-spring-boot-migration/Dockerfile | 15 +- .../spring-boot-migration/01-introduction.mdx | 76 ++++++++++ .../spring-boot-migration/01-setup.mdx | 49 ------- .../spring-boot-migration/02-assessment.mdx | 97 ++++++++++++ .../02-dry-run-full-migration.mdx | 56 ------- .../03-wave-planning.mdx | 74 ++++++++++ .../04-establish-baseline.mdx | 103 +++++++++++++ .../spring-boot-migration/05-smoke-test.mdx | 101 +++++++++++++ .../06-finish-migration.mdx | 138 ++++++++++++++++++ .../lessons/spring-boot-migration/lesson.yml | 2 + 10 files changed, 605 insertions(+), 106 deletions(-) create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-introduction.mdx delete mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-setup.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-assessment.mdx delete mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-dry-run-full-migration.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-wave-planning.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-establish-baseline.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-smoke-test.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-finish-migration.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/lesson.yml diff --git a/examples/moderne-spring-boot-migration/Dockerfile b/examples/moderne-spring-boot-migration/Dockerfile index 759f16b..7552a1c 100644 --- a/examples/moderne-spring-boot-migration/Dockerfile +++ b/examples/moderne-spring-boot-migration/Dockerfile @@ -12,6 +12,16 @@ FROM turtorial:latest AS base ################################################################################ USER root + +# Install Temurin JDK 17 (needed for Spring Boot 2.7 migration workshop) +# Note: turtorial:latest installs Java 25 to /opt/java/openjdk +RUN mkdir -p /opt/java/openjdk17 \ + && curl -L "https://api.adoptium.net/v3/binary/latest/17/ga/linux/aarch64/jdk/hotspot/normal/eclipse" | tar -xz -C /opt/java/openjdk17 --strip-components=1 + +# Set JAVA_HOME to Java 17 for the workshop exercises +ENV JAVA_HOME=/opt/java/openjdk17 +ENV PATH=$JAVA_HOME/bin:$PATH + ARG MODERNE_CLI_STAGE=stable ARG MODERNE_CLI_VERSION # Set the environment variable MODERNE_CLI_VERSION @@ -67,4 +77,7 @@ RUN mkdir -p /home/turtorial/workspaces/step1 \ # Copy over our lessons COPY --chown=turtorial:turtorial lessons /app/lessons -WORKDIR /home/turtorial \ No newline at end of file +WORKDIR /home/turtorial + +# Run the application (using Java 25 from the base image location) +ENTRYPOINT ["/opt/java/openjdk/bin/java", "-jar", "/app/turtorial.jar"] \ No newline at end of file diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-introduction.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-introduction.mdx new file mode 100644 index 0000000..c2b24ad --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-introduction.mdx @@ -0,0 +1,76 @@ +--- +title: Introduction +--- +# Preparing for Spring Boot Migration + +Spring Boot migrations are rarely just a version bump. You need to understand your repositories, identify hidden dependencies, align build tooling, and plan release waves so downstream services can upgrade safely. This workshop walks you through a realistic, end-to-end prep flow using the Moderne CLI so you can move into a Spring Boot 4 migration with fewer surprises. + +## Workshop Overview + +In this workshop, you will: + +1. **Assess**: Run an initial migration and gather code insight data. +2. **Plan**: Analyze dependencies and organize upgrade waves. +3. **Baseline**: Normalize Maven, Java, and Spring Boot versions. +4. **Smoke Test**: Upgrade to Java 17 and run a Spring Boot 4 smoke test. +5. **Migrate**: Finish the migration in waves using custom recipes. + +## Environment Setup + +This tutorial runs in a pre-configured environment with the Moderne CLI (`mod`) and necessary build tools (Maven, Java). + +The example repositories have been cloned into your workspace at `/home/turtorial/moderne-migration-practice` (referred to as `$WORKSHOP`). + +Let's start by setting up some environment variables to make the commands easier to run. + +```bash +export WORKSHOP=/home/turtorial/moderne-migration-practice +export WORKSPACE=/home/turtorial/workspaces/migration-practice-workspace +export PROJECTS=/home/turtorial/projects +``` + +Now, let's create our workspace directory and sync the repositories. + +```bash +mkdir -p $WORKSPACE +cd $WORKSPACE + +# Sync the repositories from the workshop configuration +mod git sync csv $WORKSPACE $WORKSHOP/repos.csv --with-sources +``` + +Verify that the repositories are synced: + +```bash +ls -F $WORKSPACE +``` + +You should see a directory structure corresponding to the repositories. + +## Build LSTs + +To run recipes, we need to build Lossless Semantic Trees (LSTs) for the projects. + +```bash +mod build $WORKSPACE +``` + +This might take a few minutes as it compiles the projects and generates the LSTs. + +## Install Recipes + +We'll install the necessary recipes for this workshop. + +```bash +mod config recipes jar install \ + io.moderne.recipe:rewrite-spring:0.19.0 \ + org.openrewrite.recipe:rewrite-migrate-java:3.24.0 \ + org.openrewrite.recipe:rewrite-java-dependencies:1.48.0 \ + org.openrewrite:rewrite-java:8.69.0 \ + org.openrewrite:rewrite-maven:8.69.0 \ + io.moderne.recipe:rewrite-devcenter:1.13.1 \ + org.openrewrite.recipe:rewrite-spring:6.21.0 \ + org.openrewrite.recipe:rewrite-testing-frameworks:3.24.0 +``` + +Now you are ready to start the assessment! diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-setup.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-setup.mdx deleted file mode 100644 index d72b6db..0000000 --- a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-setup.mdx +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: Setup ---- -# Setup - -> Note: We'll use a couple of conventions in this document to refer to some common directories. `$WORKSHOP` will refer to the root of this repository. `$WORKSPACE` will refer to the directory you're cloning repos into and running `mod` commands against. `$PROJECTS` refers to a directory outside of `$WORKSPACE` where you can clone other repositories to work on separately. If you want to make this simpler and run commands copied directly from this workshop guide, you can use `export WORKSPACE=~/workspaces/migration_workshop`, `export WORKSHOP=~/projects/moderne-migration-practice`, and `export PROJECTS=~/projects` in your shell session. Replace the values of these variables with their real locations on your hard drive. - -Let's start by using the Moderne CLI to clone all of our example repositories into an empty directory: - -```bash -# Navigate to your empty workspace (make sure you've set `$WORKSPACE` in your shell previously, or just replace it with the actual place you want to go) -cd $WORKSPACE - -# Clone all of our example repositories into the workspace with their source code so that we can apply suggested changes from recipes -# We will use this environment variable explicitly going forward so you can run `mod` commands from anywhere, but if you navigate to the `$WORKSPACE` path with the previous command and run `mod` from there, you can replace `$WORKSPACE` with `.` for future `mod` commands -mod git sync csv $WORKSPACE $WORKSHOP/repos.csv --with-sources -``` - -This `repos.csv` lists all of our example repositories, and you can view the final workspace structure to see that you have a directory for the GitHub org (or user) with the repositories inside: - -```bash -tree -d $WORKSPACE. -L 3 -``` - -This repository includes some helper scripts to run commands like testing, releasing, and doing common git actions across your repositories. The software development lifecycle of building and releasing new versions of these repositories is usually handled by your existing process outside of OpenRewrite and Moderne, but in this workshop you can simulate a release with the included `release.sh` script. Since all of our projects currently depend on a release version 1.0.0 of each other and those don't exist yet, go ahead and run a first release to get everything building: - -```bash -# Release -$WORKSHOP/release.sh -``` - -This will automatically install the current non-SNAPSHOT version of each repository into your local Maven cache. It will then bump the project's version to the next available minor SNAPSHOT version, ready for you to make more changes. - -Now we can build our first Lossless Semantic Trees (LSTs) so we can run OpenRewrite recipes on the synced repositories. This command may take a few minutes to run as it compiles the projects and builds the LSTs for each project: - -```bash -# Build an LST for each project -mod build $WORKSPACE -``` - -To make sure we have all the OpenRewrite recipes we will need for this workshop, we can install the following recipe artifacts: - -```bash -# For a deterministic workshop, this clears any installed recipes first -mod config recipes delete - -# Install the necessary recipes for this workshop from the public Maven repository -mod config recipes jar install io.moderne.recipe:rewrite-spring:0.19.0 org.openrewrite.recipe:rewrite-migrate-java:3.24.0 org.openrewrite.recipe:rewrite-java-dependencies:1.48.0 org.openrewrite:rewrite-java:8.69.0 org.openrewrite:rewrite-maven:8.69.0 io.moderne.recipe:rewrite-devcenter:1.13.1 org.openrewrite.recipe:rewrite-spring:6.21.0 org.openrewrite.recipe:rewrite-testing-frameworks:3.24.0 -``` \ No newline at end of file diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-assessment.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-assessment.mdx new file mode 100644 index 0000000..f646b97 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-assessment.mdx @@ -0,0 +1,97 @@ +--- +title: Assessment +--- +# Module 1: Migration Assessment + +In this module, you will run the Spring Boot 4 migration recipe to find the biggest risks and blockers. The idea is to see what breaks before investing time in fixes. + +## Exercise 1: Run the Full Migration Recipe (Dry Run) + +First, let's run a "dry run" of the full Spring Boot 4 migration recipe. This will not modify the code but will identify what changes would be made and what errors might occur. + +```bash +mod run $WORKSPACE --recipe io.moderne.java.spring.boot4.UpgradeSpringBoot_4_0 --dry-run +``` + +Review the output. You might see warnings or errors related to missing types or compilation failures. This is expected! + +Specifically, look for errors related to `QueryDSL` (generated code issues) or missing dependencies. + +## Exercise 2: Identify Java Version Usage + +Let's see what Java versions are currently in use across the repositories. + +```bash +mod run $WORKSPACE --recipe org.openrewrite.java.migrate.search.PlanJavaMigration --dry-run +``` + +To see the data table produced by this recipe: + +```bash +mod study $WORKSPACE --last-recipe-run --data-table org.openrewrite.java.migrate.table.JavaVersionMigrationPlan +``` + +You should see that most projects are on Java 8. + +## Exercise 3: Inspect Spring Boot Versions + +Now, let's find out which Spring Boot versions are currently used. + +```bash +mod run $WORKSPACE --recipe org.openrewrite.java.dependencies.DependencyInsight --dry-run \ + --recipe-option groupPattern=org.springframework.boot \ + --recipe-option artifactPattern=* \ + --recipe-option scope=runtime +``` + +Check the results: + +```bash +mod study $WORKSPACE --last-recipe-run --data-table org.openrewrite.maven.table.DependenciesInUse +``` + +## Exercise 4: Find `javax.*` Usage + +Spring Boot 3+ (and thus 4) moves from `javax.*` to `jakarta.*`. High usage of `javax.*` means more work. + +```bash +mod run $WORKSPACE --recipe org.openrewrite.java.search.FindTypes --dry-run \ + --recipe-option fullyQualifiedTypeName=javax..* +``` + +Check the results: + +```bash +mod study $WORKSPACE --last-recipe-run --data-table org.openrewrite.java.search.TypeUses +``` + +## Exercise 5: Locate Code Generators (QueryDSL) + +Code generators like QueryDSL can be major blockers. Let's find where they are used. + +```bash +mod run $WORKSPACE --recipe org.openrewrite.maven.search.FindPlugin --dry-run \ + --recipe-option groupId=com.mysema.maven \ + --recipe-option artifactId=apt-maven-plugin +``` + +Check the results: + +```bash +mod study $WORKSPACE --last-recipe-run --data-table org.openrewrite.maven.table.SearchResults +``` + +You should see usage in several services. + +--- + +**Quiz** + +Why is it important to run a dry run and code insight recipes before starting the actual migration? + +1. To generate more code. +2. To identify blockers, dependencies, and plan the migration strategy. +3. To fix bugs automatically without checking. +4. To update the Java version immediately. + +**Correct Answer**: 2 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-dry-run-full-migration.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-dry-run-full-migration.mdx deleted file mode 100644 index d77aeb2..0000000 --- a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-dry-run-full-migration.mdx +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: Dry Run Full Migration ---- - -# Step 1: Run the full migration - -First up, you can always start by running the full migration recipe that we ultimately want to finish with. This often gives us some information on some of the obvious pitfalls that we might run into, including incompatible libraries or additional customizations that we need to make to the recipe. Go ahead and run the upgrade Spring Boot 4.0 recipe now: - -```bash -# Run the recipe -mod run $WORKSPACE --recipe io.moderne.java.spring.boot4.UpgradeSpringBoot_4_0 - -# Apply the suggested changes to all projects -mod git apply $WORKSPACE --last-recipe-run - -# Build the projects to see if they compile -$WORKSHOP/build.sh -``` - -> Note: To ensure reproducible builds, projects refer to each other with specific release versions - not SNAPSHOTs or other dynamic references. This simulates an environment in a large organization where each of these repositories is owned by different teams and released independently. This probably isn't the case for _every_ repository in your portfolio - there's likely collections of related repositories that are all built and released together as a constellation of services. In our workshop, we'll simplify this to "every repository is independent from the others" to make sure we practice this particular speed bump. - -Take a second to look at the output for the failed Maven builds. Why doesn't this work? - -While every repository presents its own unique challenges, there are common speed bumps along the way that we can look for in any set of depositors. These include: - -- Inconsistent or brittle build tool configuration -- Incompatible build tool versions (both the tools themselves and their plugins) -- Third-party dependencies that are incompatible with new versions of Java or upgraded frameworks -- Dependencies between repositories in the whole set that need to be built in order - -In the example applications, we can see that there's actually a number of issues: - -- We're failing to build some classes like `QOrder` and `QInventory`. These "Q" classes are coming from a code generator called QueryDSL, and code generators are a class of tools that are generally problematic during migrations. They often generate code that's specifically tuned for a particular version of Java or frameworks like Spring. Upgrading those runtimes often require an update to the code generator. -- We're seeing failures compiling test classes with errors like `package org.springframework.boot.test.autoconfigure.web.servlet does not exist`. Upgrading Spring also includes upgrading to a newer version of JUnit, so this might require us to update our tests, or we might be pulling in outdated testing libraries as dependencies. -- Errors like `'dependencies.dependency.version' for org.springframework.cloud:spring-cloud-starter-zipkin:jar is missing.` sounds like these used to be managed dependencies in our older Spring Boot versions but now there is no managed version specified in Spring Boot 4.0. This can happen when the dependency has move or been replaced by a different Spring Boot starter, or if the particular functionality was deprecated and removed. - -## What makes a third-party library incompatible with newer versions of Java? - -While you can run libraries compiled with an older version of Java in newer versions of JVM, the Java runtime has deprecated specific APIs and ultimately removed or refactored those as it has evolved. The major change that many people run into includes many `javax.*` internal APIs, including the Java EE APIs that were ultimately moved out of the JVM entirely and into the Jakarta namespace. - -## Why do we care about dependencies between repositories as we're going through a migration? - -Large organizations often have repositories that depend on each other, using internal shared libraries to share code and standardize specific functionality. These shared libraries can be owned, built, released, and versioned independently from the repositories that use them, so we need to upgrade these projects _and_ run them through their whole software developer lifecycle to release a new updated version that downstream consumers can move to. This can sometimes be as simple as upgrading a version number in a build manifest, or it can include complex code changes for those consumers to upgrade their code depending on how the libraries are built. - -Regardless, in order to upgrade all of our repositories, we often need to find the sets of repositories that are depended on and upgrade them first, then upgrade the rest in the proper sequence. For this workshop, we'll refer to each of these sets of repositories as "waves" of our migration. - -We can use the Moderne CLI to apply any command across all of our repositories, including git commands. Go ahead and reset our repositories to a clean state now that we've learned from this experiment: - -```bash -# Restore -mod exec $WORKSPACE git restore MODERNE_BUILD_TOOL_DIR - -# Rebuild the LST -$WORKSHOP/build.sh -mod build $WORKSPACE -``` \ No newline at end of file diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-wave-planning.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-wave-planning.mdx new file mode 100644 index 0000000..1d36446 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-wave-planning.mdx @@ -0,0 +1,74 @@ +--- +title: Wave Planning +--- +# Module 2: Wave Planning + +In this module, you'll move from assessment to planning. You will group repositories into “waves” based on dependencies and upgrade them in order. + +## Exercise 1: Generate Dependency Data + +First, we need to install a recipe that analyzes dependencies to create a wave plan. + +Clone the `Release-Train-Metro-Plan` repository: + +```bash +mkdir -p $PROJECTS +cd $PROJECTS +git clone https://github.com/MBoegers/Release-Train-Metro-Plan.git +``` + +Install the recipe artifact locally: + +```bash +mod config recipes jar install dev.mboegie.rewrite:release-train-metro-plan:RELEASE +``` + +Now, run the recipe on your workspace: + +```bash +mod run $WORKSPACE --recipe dev.mboegie.rewrite.releasemetro.ReleaseMetroPlan --parallel +``` + +This generates several data tables (e.g., `ParentRelationships`, `ProjectCoordinates`) that can be used to visualize the dependency graph. + +In a real-world scenario, you would export these tables and use a tool (like a Jupyter notebook) to generate a "metro map" of your upgrade waves. For this workshop, we have pre-calculated the waves for you. + +## Exercise 2: Organize Workspace by Wave + +Based on the dependency analysis, we have identified the following waves: + +* **Wave 0**: Independent repositories (libraries). +* **Wave 1**: Services depending only on Wave 0. +* **Wave 2**: Services depending on Wave 1. +* **Wave 3**: Services depending on Wave 2. + +We will use a special `repos-waves.csv` file that groups the repositories into these waves. This allows us to target specific waves with our recipes. + +Sync the repositories using the wave-aware CSV: + +```bash +mod git sync csv $WORKSPACE $WORKSHOP/repos-waves.csv --with-sources +``` + +If prompted to replace the existing organization, confirm with `Y`. + +Now, verify the new structure: + +```bash +ls -F $WORKSPACE +``` + +You should see directories like `Wave0`, `Wave1`, etc. This structure is critical for running wave-based migrations. + +--- + +**Quiz** + +Why do we upgrade in waves? + +1. To upgrade everything at once. +2. To upgrade downstream services before their dependencies. +3. To ensure that dependencies are upgraded and released before dependent services consume them. +4. Because it looks cool on a map. + +**Correct Answer**: 3 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-establish-baseline.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-establish-baseline.mdx new file mode 100644 index 0000000..50ffac2 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-establish-baseline.mdx @@ -0,0 +1,103 @@ +--- +title: Establish Baseline +--- +# Module 3: Establish a Baseline + +Before we can upgrade to Spring Boot 4, we need to normalize our repositories to a consistent baseline: Java 8 and Spring Boot 2.7. This makes the upgrade path predictable and safer. + +## Exercise 1: Create a Baseline Recipe + +We will create a composite recipe that applies the following: +* Maven best practices (`org.openrewrite.maven.BestPractices`). +* Migrate to Java 8 (`org.openrewrite.java.migrate.UpgradeToJava8`). +* Migrate to Spring Boot 2.7 (`org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7`). +* Replace `spring-cloud-starter-zipkin` (deprecated) with `spring-cloud-sleuth-zipkin`. + +Create a file named `WorkshopBaseline.yml` in your `$WORKSHOP` directory: + +```bash +cat <<'EOF' > $WORKSHOP/WorkshopBaseline.yml +--- +type: specs.openrewrite.org/v1beta/recipe +name: com.example.ecom.recipe.SpringBootMigrationWorkshopBaseline +displayName: Spring Boot Migration Workshop Baseline +description: Upgrade Java 8, Maven, and Spring Boot 2.7. +recipeList: + - org.openrewrite.maven.BestPractices + - org.openrewrite.java.migrate.UpgradeToJava8 + - org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7 + - org.openrewrite.maven.ChangeDependencyGroupIdAndArtifactId: + oldGroupId: org.springframework.cloud + oldArtifactId: spring-cloud-starter-zipkin + newGroupId: org.springframework.cloud + newArtifactId: spring-cloud-sleuth-zipkin +EOF +``` + +Install the recipe locally: + +```bash +mod config recipes yaml install $WORKSHOP/WorkshopBaseline.yml +``` + +## Exercise 2: Apply the Baseline Recipe + +First, let's build the LSTs for the new wave structure: + +```bash +mod build $WORKSPACE +``` + +Now, run the baseline recipe across the entire workspace (all waves): + +```bash +mod run $WORKSPACE --recipe com.example.ecom.recipe.SpringBootMigrationWorkshopBaseline +``` + +Apply the changes: + +```bash +mod git apply $WORKSPACE --last-recipe-run +``` + +## Exercise 3: Build and Release + +Before proceeding, we must ensure everything builds and release the new versions. We will use the provided helper scripts. + +Verify the changes with a build: + +```bash +$WORKSHOP/build.sh +``` + +If successful, commit the changes: + +```bash +mod git add $WORKSPACE --last-recipe-run +mod git commit $WORKSPACE -m "Workshop baseline: Maven, Java 8, Spring Boot 2.7" --last-recipe-run +``` + +Now, perform a release to update dependencies and bump versions for the next step: + +```bash +$WORKSHOP/release.sh +``` + +Finally, rebuild the LSTs so the CLI sees the updated code state: + +```bash +mod build $WORKSPACE +``` + +--- + +**Quiz** + +Why do we establish a baseline before upgrading to Spring Boot 4? + +1. To make the migration harder. +2. To ensure all repositories are on a known, stable configuration (e.g., Spring Boot 2.7) before attempting major upgrades. +3. To confuse the developers. +4. To test the CI pipeline. + +**Correct Answer**: 2 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-smoke-test.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-smoke-test.mdx new file mode 100644 index 0000000..0427136 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-smoke-test.mdx @@ -0,0 +1,101 @@ +--- +title: Smoke Test +--- +# Module 4: Raise Baseline and Smoke Test + +In this module, you will raise the Java baseline to 17 and run a controlled Spring Boot 4 upgrade to identify any remaining blockers. + +## Exercise 1: Upgrade to Java 17 + +Spring Boot 3+ (and 4) requires Java 17 or higher. So, let's upgrade all repositories to Java 17 first. + +Run the `UpgradeToJava17` recipe: + +```bash +mod run $WORKSPACE --recipe org.openrewrite.java.migrate.UpgradeToJava17 +``` + +Apply the changes: + +```bash +mod git apply $WORKSPACE --last-recipe-run +``` + +Now, build and commit the changes: + +```bash +$WORKSHOP/build.sh +mod git add $WORKSPACE --last-recipe-run +mod git commit $WORKSPACE -m "Upgrade to Java 17" --last-recipe-run +``` + +And rebuild the LSTs: + +```bash +mod build $WORKSPACE +``` + +## Exercise 2: Run a Spring Boot 4 Smoke Test + +Now that we are on Java 17, let's see how far the Spring Boot 4 upgrade gets. This is a "smoke test" to identify remaining blockers. + +Run the upgrade recipe: + +```bash +mod run $WORKSPACE --recipe io.moderne.java.spring.boot4.UpgradeSpringBoot_4_0 +``` + +Apply the changes temporarily: + +```bash +mod git apply $WORKSPACE --last-recipe-run +``` + +## Exercise 3: Build Waves (Expect Failure) + +Let's try to build Wave 0 and Wave 1. + +```bash +$WORKSHOP/build.sh 0 +``` + +Wave 0 (common libraries) should build successfully. + +```bash +$WORKSHOP/build.sh 1 +``` + +Wave 1 is expected to fail. Why? Because of QueryDSL issues! We'll fix this in the next module. + +## Exercise 4: Restore Workspace + +Since the build is broken, let's revert the changes. + +```bash +mod exec $WORKSPACE git restore . +``` + +Verify the workspace is clean: + +```bash +mod git status $WORKSPACE +``` + +Rebuild LSTs to ensure we are back to the Java 17 baseline: + +```bash +mod build $WORKSPACE +``` + +--- + +**Quiz** + +Why do we run a smoke test before the full migration? + +1. To see if the computer is smoking. +2. To commit broken code. +3. To isolate remaining obstacles (like QueryDSL) without committing to a full upgrade. +4. To verify that Java 17 is installed correctly. + +**Correct Answer**: 3 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-finish-migration.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-finish-migration.mdx new file mode 100644 index 0000000..cef9523 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-finish-migration.mdx @@ -0,0 +1,138 @@ +--- +title: Finish Migration +--- +# Module 5: Finish Migration in Waves + +In this module, you will install a custom recipe to handle the QueryDSL issues, build a composite upgrade recipe, and upgrade each wave of repositories to Spring Boot 4. + +## Exercise 1: Install the QueryDSL Recipe + +We need a custom recipe to rename packages and update dependencies for QueryDSL. + +Clone the custom recipe repository: + +```bash +cd $PROJECTS +git clone https://github.com/modernetraining/rewrite-querydsl.git +``` + +Build and install it locally: + +```bash +cd rewrite-querydsl +mvn clean install +mod config recipes jar install org.openrewrite.recipe:rewrite-querydsl:0.1.0-SNAPSHOT +``` + +## Exercise 2: Create a Composite Upgrade Recipe + +Create a composite recipe that combines: +* `UpgradeDependencyVersion` (to bump internal dependencies). +* `UpgradeSpringBoot_4_0` (Spring Boot 4). +* `UpgradeToQueryDsl5` (QueryDSL 5). + +Create a file named `CustomUpgradeSpringBoot_4_0.yml` in your `$WORKSHOP` directory: + +```bash +cat <<'EOF' > $WORKSHOP/CustomUpgradeSpringBoot_4_0.yml +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.recipe.querydsl.CustomUpgradeSpringBoot_4_0 +displayName: CustomUpgradeSpringBoot_4_0 +description: Upgrade internal deps, Spring Boot 4.0, and QueryDSL 5. +recipeList: + - org.openrewrite.java.dependencies.UpgradeDependencyVersion: + groupId: com.example.ecom + artifactId: "*" + newVersion: 1.x + - io.moderne.java.spring.boot4.UpgradeSpringBoot_4_0 + - org.openrewrite.recipe.querydsl.UpgradeToQueryDsl5 +EOF +``` + +Install the composite recipe locally: + +```bash +mod config recipes yaml install $WORKSHOP/CustomUpgradeSpringBoot_4_0.yml +``` + +## Exercise 3: Upgrade Wave 0 + +Run the custom recipe on Wave 0: + +```bash +mod run $WORKSPACE/Wave0 --recipe org.openrewrite.recipe.querydsl.CustomUpgradeSpringBoot_4_0 +``` + +Apply the changes: + +```bash +mod git apply $WORKSPACE/Wave0 --last-recipe-run +``` + +Build and release Wave 0: + +```bash +$WORKSHOP/build.sh 0 +$WORKSHOP/release.sh 0 +``` + +## Exercise 4: Upgrade Wave 1 + +Now repeat for Wave 1: + +```bash +mod run $WORKSPACE/Wave1 --recipe org.openrewrite.recipe.querydsl.CustomUpgradeSpringBoot_4_0 +``` + +Apply changes: + +```bash +mod git apply $WORKSPACE/Wave1 --last-recipe-run +``` + +Build and release Wave 1: + +```bash +$WORKSHOP/build.sh 1 +$WORKSHOP/release.sh 1 +``` + +You can continue this process for Wave 2 and Wave 3 to complete the migration across all repositories. + +## Exercise 5: Refresh DevCenter + +After upgrading waves, you can generate a DevCenter dashboard to visualize the progress. + +First, rebuild LSTs: + +```bash +mod build $WORKSPACE +``` + +Run the `DevCenterStarter` recipe: + +```bash +mod run $WORKSPACE --recipe io.moderne.devcenter.DevCenterStarter +``` + +Generate the DevCenter dashboard: + +```bash +mod devcenter $WORKSPACE --last-recipe-run +``` + +This will produce an HTML file you can open to see the updated status of your repositories. + +--- + +**Quiz** + +Why is it beneficial to upgrade in waves using a composite recipe? + +1. To ensure that internal dependencies are updated in the correct order and custom issues (like QueryDSL) are handled alongside framework upgrades. +2. To make the migration take longer. +3. To confuse the build system. +4. To avoid using the CLI. + +**Correct Answer**: 1 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/lesson.yml b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/lesson.yml new file mode 100644 index 0000000..df78e17 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/lesson.yml @@ -0,0 +1,2 @@ +title: Spring Boot Migration +description: A hands-on workshop to migrate Spring Boot applications using OpenRewrite and Moderne. From dd02c5df5a7e4b4c9aca58cffd4c55704bfc239b Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:00:09 +0000 Subject: [PATCH 2/4] Refactor Spring Boot Migration lesson into smaller steps This commit breaks down the 6 lesson modules into 28 smaller, individual steps. Each step now focuses on a single concept or action, making the tutorial easier to follow and digest. - Replaced `01-introduction.mdx` with `01-01` to `01-04`. - Replaced `02-assessment.mdx` with `02-01` to `02-06`. - Replaced `03-wave-planning.mdx` with `03-01` to `03-03`. - Replaced `04-establish-baseline.mdx` with `04-01` to `04-04`. - Replaced `05-smoke-test.mdx` with `05-01` to `05-05`. - Replaced `06-finish-migration.mdx` with `06-01` to `06-06`. - Updated frontmatter to include `section` and `order` for proper organization. Co-authored-by: mtthwcmpbll <226487+mtthwcmpbll@users.noreply.github.com> --- .../spring-boot-migration/01-01-overview.mdx | 18 +++ .../spring-boot-migration/01-02-env-setup.mdx | 36 +++++ .../01-03-build-lsts.mdx | 14 ++ .../01-04-install-recipes.mdx | 22 +++ .../spring-boot-migration/01-introduction.mdx | 76 ---------- .../spring-boot-migration/02-01-dry-run.mdx | 18 +++ .../02-02-java-version.mdx | 20 +++ .../02-03-spring-boot-version.mdx | 21 +++ .../02-04-javax-usage.mdx | 19 +++ .../02-05-code-generators.mdx | 22 +++ .../02-06-assessment-quiz.mdx | 15 ++ .../spring-boot-migration/02-assessment.mdx | 97 ------------ .../03-01-generate-dependency-data.mdx | 34 +++++ .../03-02-organize-workspace.mdx | 31 ++++ .../03-03-wave-planning-quiz.mdx | 15 ++ .../03-wave-planning.mdx | 74 ---------- .../04-01-create-baseline-recipe.mdx | 41 ++++++ .../04-02-apply-baseline.mdx | 24 +++ .../04-03-build-and-release.mdx | 33 +++++ .../04-04-baseline-quiz.mdx | 15 ++ .../04-establish-baseline.mdx | 103 ------------- .../05-01-upgrade-java-17.mdx | 36 +++++ .../05-02-smoke-test.mdx | 20 +++ .../05-03-verify-waves.mdx | 20 +++ .../05-04-restore-workspace.mdx | 24 +++ .../05-05-smoke-test-quiz.mdx | 15 ++ .../spring-boot-migration/05-smoke-test.mdx | 101 ------------- .../06-01-install-querydsl-recipe.mdx | 25 ++++ .../06-02-create-composite-recipe.mdx | 36 +++++ .../06-03-upgrade-wave-0.mdx | 25 ++++ .../06-04-upgrade-wave-1.mdx | 27 ++++ .../06-05-refresh-devcenter.mdx | 28 ++++ .../06-06-finish-migration-quiz.mdx | 15 ++ .../06-finish-migration.mdx | 138 ------------------ 34 files changed, 669 insertions(+), 589 deletions(-) create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-01-overview.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-02-env-setup.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-03-build-lsts.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-04-install-recipes.mdx delete mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-introduction.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-01-dry-run.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-02-java-version.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-03-spring-boot-version.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-04-javax-usage.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-05-code-generators.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-06-assessment-quiz.mdx delete mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-assessment.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-01-generate-dependency-data.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-02-organize-workspace.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-03-wave-planning-quiz.mdx delete mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-wave-planning.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-01-create-baseline-recipe.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-02-apply-baseline.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-03-build-and-release.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-04-baseline-quiz.mdx delete mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-establish-baseline.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-01-upgrade-java-17.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-02-smoke-test.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-03-verify-waves.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-04-restore-workspace.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-05-smoke-test-quiz.mdx delete mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-smoke-test.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-01-install-querydsl-recipe.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-02-create-composite-recipe.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-03-upgrade-wave-0.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-04-upgrade-wave-1.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-05-refresh-devcenter.mdx create mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-06-finish-migration-quiz.mdx delete mode 100644 examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-finish-migration.mdx diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-01-overview.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-01-overview.mdx new file mode 100644 index 0000000..cdead0a --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-01-overview.mdx @@ -0,0 +1,18 @@ +--- +title: Workshop Overview +section: Introduction +order: 1 +--- +# Preparing for Spring Boot Migration + +Spring Boot migrations are rarely just a version bump. You need to understand your repositories, identify hidden dependencies, align build tooling, and plan release waves so downstream services can upgrade safely. This workshop walks you through a realistic, end-to-end prep flow using the Moderne CLI so you can move into a Spring Boot 4 migration with fewer surprises. + +## Workshop Overview + +In this workshop, you will: + +1. **Assess**: Run an initial migration and gather code insight data. +2. **Plan**: Analyze dependencies and organize upgrade waves. +3. **Baseline**: Normalize Maven, Java, and Spring Boot versions. +4. **Smoke Test**: Upgrade to Java 17 and run a Spring Boot 4 smoke test. +5. **Migrate**: Finish the migration in waves using custom recipes. diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-02-env-setup.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-02-env-setup.mdx new file mode 100644 index 0000000..28644cd --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-02-env-setup.mdx @@ -0,0 +1,36 @@ +--- +title: Environment Setup +section: Introduction +order: 2 +--- +# Environment Setup + +This tutorial runs in a pre-configured environment with the Moderne CLI (`mod`) and necessary build tools (Maven, Java). + +The example repositories have been cloned into your workspace at `/home/turtorial/moderne-migration-practice` (referred to as `$WORKSHOP`). + +Let's start by setting up some environment variables to make the commands easier to run. + +```bash +export WORKSHOP=/home/turtorial/moderne-migration-practice +export WORKSPACE=/home/turtorial/workspaces/migration-practice-workspace +export PROJECTS=/home/turtorial/projects +``` + +Now, let's create our workspace directory and sync the repositories. + +```bash +mkdir -p $WORKSPACE +cd $WORKSPACE + +# Sync the repositories from the workshop configuration +mod git sync csv $WORKSPACE $WORKSHOP/repos.csv --with-sources +``` + +Verify that the repositories are synced: + +```bash +ls -F $WORKSPACE +``` + +You should see a directory structure corresponding to the repositories. diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-03-build-lsts.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-03-build-lsts.mdx new file mode 100644 index 0000000..f0aa401 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-03-build-lsts.mdx @@ -0,0 +1,14 @@ +--- +title: Build LSTs +section: Introduction +order: 3 +--- +# Build LSTs + +To run recipes, we need to build Lossless Semantic Trees (LSTs) for the projects. + +```bash +mod build $WORKSPACE +``` + +This might take a few minutes as it compiles the projects and generates the LSTs. diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-04-install-recipes.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-04-install-recipes.mdx new file mode 100644 index 0000000..2b96de1 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-04-install-recipes.mdx @@ -0,0 +1,22 @@ +--- +title: Install Recipes +section: Introduction +order: 4 +--- +# Install Recipes + +We'll install the necessary recipes for this workshop. + +```bash +mod config recipes jar install \ + io.moderne.recipe:rewrite-spring:0.19.0 \ + org.openrewrite.recipe:rewrite-migrate-java:3.24.0 \ + org.openrewrite.recipe:rewrite-java-dependencies:1.48.0 \ + org.openrewrite:rewrite-java:8.69.0 \ + org.openrewrite:rewrite-maven:8.69.0 \ + io.moderne.recipe:rewrite-devcenter:1.13.1 \ + org.openrewrite.recipe:rewrite-spring:6.21.0 \ + org.openrewrite.recipe:rewrite-testing-frameworks:3.24.0 +``` + +Now you are ready to start the assessment! diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-introduction.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-introduction.mdx deleted file mode 100644 index c2b24ad..0000000 --- a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/01-introduction.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: Introduction ---- -# Preparing for Spring Boot Migration - -Spring Boot migrations are rarely just a version bump. You need to understand your repositories, identify hidden dependencies, align build tooling, and plan release waves so downstream services can upgrade safely. This workshop walks you through a realistic, end-to-end prep flow using the Moderne CLI so you can move into a Spring Boot 4 migration with fewer surprises. - -## Workshop Overview - -In this workshop, you will: - -1. **Assess**: Run an initial migration and gather code insight data. -2. **Plan**: Analyze dependencies and organize upgrade waves. -3. **Baseline**: Normalize Maven, Java, and Spring Boot versions. -4. **Smoke Test**: Upgrade to Java 17 and run a Spring Boot 4 smoke test. -5. **Migrate**: Finish the migration in waves using custom recipes. - -## Environment Setup - -This tutorial runs in a pre-configured environment with the Moderne CLI (`mod`) and necessary build tools (Maven, Java). - -The example repositories have been cloned into your workspace at `/home/turtorial/moderne-migration-practice` (referred to as `$WORKSHOP`). - -Let's start by setting up some environment variables to make the commands easier to run. - -```bash -export WORKSHOP=/home/turtorial/moderne-migration-practice -export WORKSPACE=/home/turtorial/workspaces/migration-practice-workspace -export PROJECTS=/home/turtorial/projects -``` - -Now, let's create our workspace directory and sync the repositories. - -```bash -mkdir -p $WORKSPACE -cd $WORKSPACE - -# Sync the repositories from the workshop configuration -mod git sync csv $WORKSPACE $WORKSHOP/repos.csv --with-sources -``` - -Verify that the repositories are synced: - -```bash -ls -F $WORKSPACE -``` - -You should see a directory structure corresponding to the repositories. - -## Build LSTs - -To run recipes, we need to build Lossless Semantic Trees (LSTs) for the projects. - -```bash -mod build $WORKSPACE -``` - -This might take a few minutes as it compiles the projects and generates the LSTs. - -## Install Recipes - -We'll install the necessary recipes for this workshop. - -```bash -mod config recipes jar install \ - io.moderne.recipe:rewrite-spring:0.19.0 \ - org.openrewrite.recipe:rewrite-migrate-java:3.24.0 \ - org.openrewrite.recipe:rewrite-java-dependencies:1.48.0 \ - org.openrewrite:rewrite-java:8.69.0 \ - org.openrewrite:rewrite-maven:8.69.0 \ - io.moderne.recipe:rewrite-devcenter:1.13.1 \ - org.openrewrite.recipe:rewrite-spring:6.21.0 \ - org.openrewrite.recipe:rewrite-testing-frameworks:3.24.0 -``` - -Now you are ready to start the assessment! diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-01-dry-run.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-01-dry-run.mdx new file mode 100644 index 0000000..6af937f --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-01-dry-run.mdx @@ -0,0 +1,18 @@ +--- +title: Full Migration Dry Run +section: Assessment +order: 5 +--- +# Exercise 1: Run the Full Migration Recipe (Dry Run) + +In this module, you will run the Spring Boot 4 migration recipe to find the biggest risks and blockers. The idea is to see what breaks before investing time in fixes. + +First, let's run a "dry run" of the full Spring Boot 4 migration recipe. This will not modify the code but will identify what changes would be made and what errors might occur. + +```bash +mod run $WORKSPACE --recipe io.moderne.java.spring.boot4.UpgradeSpringBoot_4_0 --dry-run +``` + +Review the output. You might see warnings or errors related to missing types or compilation failures. This is expected! + +Specifically, look for errors related to `QueryDSL` (generated code issues) or missing dependencies. diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-02-java-version.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-02-java-version.mdx new file mode 100644 index 0000000..8899fbc --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-02-java-version.mdx @@ -0,0 +1,20 @@ +--- +title: Identify Java Version +section: Assessment +order: 6 +--- +# Exercise 2: Identify Java Version Usage + +Let's see what Java versions are currently in use across the repositories. + +```bash +mod run $WORKSPACE --recipe org.openrewrite.java.migrate.search.PlanJavaMigration --dry-run +``` + +To see the data table produced by this recipe: + +```bash +mod study $WORKSPACE --last-recipe-run --data-table org.openrewrite.java.migrate.table.JavaVersionMigrationPlan +``` + +You should see that most projects are on Java 8. diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-03-spring-boot-version.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-03-spring-boot-version.mdx new file mode 100644 index 0000000..ab15d30 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-03-spring-boot-version.mdx @@ -0,0 +1,21 @@ +--- +title: Inspect Spring Boot Version +section: Assessment +order: 7 +--- +# Exercise 3: Inspect Spring Boot Versions + +Now, let's find out which Spring Boot versions are currently used. + +```bash +mod run $WORKSPACE --recipe org.openrewrite.java.dependencies.DependencyInsight --dry-run \ + --recipe-option groupPattern=org.springframework.boot \ + --recipe-option artifactPattern=* \ + --recipe-option scope=runtime +``` + +Check the results: + +```bash +mod study $WORKSPACE --last-recipe-run --data-table org.openrewrite.maven.table.DependenciesInUse +``` diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-04-javax-usage.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-04-javax-usage.mdx new file mode 100644 index 0000000..b0a6c44 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-04-javax-usage.mdx @@ -0,0 +1,19 @@ +--- +title: Find javax.* Usage +section: Assessment +order: 8 +--- +# Exercise 4: Find `javax.*` Usage + +Spring Boot 3+ (and thus 4) moves from `javax.*` to `jakarta.*`. High usage of `javax.*` means more work. + +```bash +mod run $WORKSPACE --recipe org.openrewrite.java.search.FindTypes --dry-run \ + --recipe-option fullyQualifiedTypeName=javax..* +``` + +Check the results: + +```bash +mod study $WORKSPACE --last-recipe-run --data-table org.openrewrite.java.search.TypeUses +``` diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-05-code-generators.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-05-code-generators.mdx new file mode 100644 index 0000000..130cc7a --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-05-code-generators.mdx @@ -0,0 +1,22 @@ +--- +title: Locate Code Generators +section: Assessment +order: 9 +--- +# Exercise 5: Locate Code Generators (QueryDSL) + +Code generators like QueryDSL can be major blockers. Let's find where they are used. + +```bash +mod run $WORKSPACE --recipe org.openrewrite.maven.search.FindPlugin --dry-run \ + --recipe-option groupId=com.mysema.maven \ + --recipe-option artifactId=apt-maven-plugin +``` + +Check the results: + +```bash +mod study $WORKSPACE --last-recipe-run --data-table org.openrewrite.maven.table.SearchResults +``` + +You should see usage in several services. diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-06-assessment-quiz.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-06-assessment-quiz.mdx new file mode 100644 index 0000000..f0e53fa --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-06-assessment-quiz.mdx @@ -0,0 +1,15 @@ +--- +title: Assessment Quiz +section: Assessment +order: 10 +--- +# Quiz + +Why is it important to run a dry run and code insight recipes before starting the actual migration? + +1. To generate more code. +2. To identify blockers, dependencies, and plan the migration strategy. +3. To fix bugs automatically without checking. +4. To update the Java version immediately. + +**Correct Answer**: 2 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-assessment.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-assessment.mdx deleted file mode 100644 index f646b97..0000000 --- a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/02-assessment.mdx +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: Assessment ---- -# Module 1: Migration Assessment - -In this module, you will run the Spring Boot 4 migration recipe to find the biggest risks and blockers. The idea is to see what breaks before investing time in fixes. - -## Exercise 1: Run the Full Migration Recipe (Dry Run) - -First, let's run a "dry run" of the full Spring Boot 4 migration recipe. This will not modify the code but will identify what changes would be made and what errors might occur. - -```bash -mod run $WORKSPACE --recipe io.moderne.java.spring.boot4.UpgradeSpringBoot_4_0 --dry-run -``` - -Review the output. You might see warnings or errors related to missing types or compilation failures. This is expected! - -Specifically, look for errors related to `QueryDSL` (generated code issues) or missing dependencies. - -## Exercise 2: Identify Java Version Usage - -Let's see what Java versions are currently in use across the repositories. - -```bash -mod run $WORKSPACE --recipe org.openrewrite.java.migrate.search.PlanJavaMigration --dry-run -``` - -To see the data table produced by this recipe: - -```bash -mod study $WORKSPACE --last-recipe-run --data-table org.openrewrite.java.migrate.table.JavaVersionMigrationPlan -``` - -You should see that most projects are on Java 8. - -## Exercise 3: Inspect Spring Boot Versions - -Now, let's find out which Spring Boot versions are currently used. - -```bash -mod run $WORKSPACE --recipe org.openrewrite.java.dependencies.DependencyInsight --dry-run \ - --recipe-option groupPattern=org.springframework.boot \ - --recipe-option artifactPattern=* \ - --recipe-option scope=runtime -``` - -Check the results: - -```bash -mod study $WORKSPACE --last-recipe-run --data-table org.openrewrite.maven.table.DependenciesInUse -``` - -## Exercise 4: Find `javax.*` Usage - -Spring Boot 3+ (and thus 4) moves from `javax.*` to `jakarta.*`. High usage of `javax.*` means more work. - -```bash -mod run $WORKSPACE --recipe org.openrewrite.java.search.FindTypes --dry-run \ - --recipe-option fullyQualifiedTypeName=javax..* -``` - -Check the results: - -```bash -mod study $WORKSPACE --last-recipe-run --data-table org.openrewrite.java.search.TypeUses -``` - -## Exercise 5: Locate Code Generators (QueryDSL) - -Code generators like QueryDSL can be major blockers. Let's find where they are used. - -```bash -mod run $WORKSPACE --recipe org.openrewrite.maven.search.FindPlugin --dry-run \ - --recipe-option groupId=com.mysema.maven \ - --recipe-option artifactId=apt-maven-plugin -``` - -Check the results: - -```bash -mod study $WORKSPACE --last-recipe-run --data-table org.openrewrite.maven.table.SearchResults -``` - -You should see usage in several services. - ---- - -**Quiz** - -Why is it important to run a dry run and code insight recipes before starting the actual migration? - -1. To generate more code. -2. To identify blockers, dependencies, and plan the migration strategy. -3. To fix bugs automatically without checking. -4. To update the Java version immediately. - -**Correct Answer**: 2 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-01-generate-dependency-data.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-01-generate-dependency-data.mdx new file mode 100644 index 0000000..1eab178 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-01-generate-dependency-data.mdx @@ -0,0 +1,34 @@ +--- +title: Generate Dependency Data +section: Wave Planning +order: 11 +--- +# Exercise 1: Generate Dependency Data + +In this module, you'll move from assessment to planning. You will group repositories into “waves” based on dependencies and upgrade them in order. + +First, we need to install a recipe that analyzes dependencies to create a wave plan. + +Clone the `Release-Train-Metro-Plan` repository: + +```bash +mkdir -p $PROJECTS +cd $PROJECTS +git clone https://github.com/MBoegers/Release-Train-Metro-Plan.git +``` + +Install the recipe artifact locally: + +```bash +mod config recipes jar install dev.mboegie.rewrite:release-train-metro-plan:RELEASE +``` + +Now, run the recipe on your workspace: + +```bash +mod run $WORKSPACE --recipe dev.mboegie.rewrite.releasemetro.ReleaseMetroPlan --parallel +``` + +This generates several data tables (e.g., `ParentRelationships`, `ProjectCoordinates`) that can be used to visualize the dependency graph. + +In a real-world scenario, you would export these tables and use a tool (like a Jupyter notebook) to generate a "metro map" of your upgrade waves. For this workshop, we have pre-calculated the waves for you. diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-02-organize-workspace.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-02-organize-workspace.mdx new file mode 100644 index 0000000..1ecf09d --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-02-organize-workspace.mdx @@ -0,0 +1,31 @@ +--- +title: Organize Workspace +section: Wave Planning +order: 12 +--- +# Exercise 2: Organize Workspace by Wave + +Based on the dependency analysis, we have identified the following waves: + +* **Wave 0**: Independent repositories (libraries). +* **Wave 1**: Services depending only on Wave 0. +* **Wave 2**: Services depending on Wave 1. +* **Wave 3**: Services depending on Wave 2. + +We will use a special `repos-waves.csv` file that groups the repositories into these waves. This allows us to target specific waves with our recipes. + +Sync the repositories using the wave-aware CSV: + +```bash +mod git sync csv $WORKSPACE $WORKSHOP/repos-waves.csv --with-sources +``` + +If prompted to replace the existing organization, confirm with `Y`. + +Now, verify the new structure: + +```bash +ls -F $WORKSPACE +``` + +You should see directories like `Wave0`, `Wave1`, etc. This structure is critical for running wave-based migrations. diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-03-wave-planning-quiz.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-03-wave-planning-quiz.mdx new file mode 100644 index 0000000..d503d9f --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-03-wave-planning-quiz.mdx @@ -0,0 +1,15 @@ +--- +title: Wave Planning Quiz +section: Wave Planning +order: 13 +--- +# Quiz + +Why do we upgrade in waves? + +1. To upgrade everything at once. +2. To upgrade downstream services before their dependencies. +3. To ensure that dependencies are upgraded and released before dependent services consume them. +4. Because it looks cool on a map. + +**Correct Answer**: 3 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-wave-planning.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-wave-planning.mdx deleted file mode 100644 index 1d36446..0000000 --- a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/03-wave-planning.mdx +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: Wave Planning ---- -# Module 2: Wave Planning - -In this module, you'll move from assessment to planning. You will group repositories into “waves” based on dependencies and upgrade them in order. - -## Exercise 1: Generate Dependency Data - -First, we need to install a recipe that analyzes dependencies to create a wave plan. - -Clone the `Release-Train-Metro-Plan` repository: - -```bash -mkdir -p $PROJECTS -cd $PROJECTS -git clone https://github.com/MBoegers/Release-Train-Metro-Plan.git -``` - -Install the recipe artifact locally: - -```bash -mod config recipes jar install dev.mboegie.rewrite:release-train-metro-plan:RELEASE -``` - -Now, run the recipe on your workspace: - -```bash -mod run $WORKSPACE --recipe dev.mboegie.rewrite.releasemetro.ReleaseMetroPlan --parallel -``` - -This generates several data tables (e.g., `ParentRelationships`, `ProjectCoordinates`) that can be used to visualize the dependency graph. - -In a real-world scenario, you would export these tables and use a tool (like a Jupyter notebook) to generate a "metro map" of your upgrade waves. For this workshop, we have pre-calculated the waves for you. - -## Exercise 2: Organize Workspace by Wave - -Based on the dependency analysis, we have identified the following waves: - -* **Wave 0**: Independent repositories (libraries). -* **Wave 1**: Services depending only on Wave 0. -* **Wave 2**: Services depending on Wave 1. -* **Wave 3**: Services depending on Wave 2. - -We will use a special `repos-waves.csv` file that groups the repositories into these waves. This allows us to target specific waves with our recipes. - -Sync the repositories using the wave-aware CSV: - -```bash -mod git sync csv $WORKSPACE $WORKSHOP/repos-waves.csv --with-sources -``` - -If prompted to replace the existing organization, confirm with `Y`. - -Now, verify the new structure: - -```bash -ls -F $WORKSPACE -``` - -You should see directories like `Wave0`, `Wave1`, etc. This structure is critical for running wave-based migrations. - ---- - -**Quiz** - -Why do we upgrade in waves? - -1. To upgrade everything at once. -2. To upgrade downstream services before their dependencies. -3. To ensure that dependencies are upgraded and released before dependent services consume them. -4. Because it looks cool on a map. - -**Correct Answer**: 3 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-01-create-baseline-recipe.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-01-create-baseline-recipe.mdx new file mode 100644 index 0000000..f83de00 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-01-create-baseline-recipe.mdx @@ -0,0 +1,41 @@ +--- +title: Create Baseline Recipe +section: Establish Baseline +order: 14 +--- +# Exercise 1: Create a Baseline Recipe + +In this module, we will normalize our repositories to a consistent baseline: Java 8 and Spring Boot 2.7. + +We will create a composite recipe that applies the following: +* Maven best practices (`org.openrewrite.maven.BestPractices`). +* Migrate to Java 8 (`org.openrewrite.java.migrate.UpgradeToJava8`). +* Migrate to Spring Boot 2.7 (`org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7`). +* Replace `spring-cloud-starter-zipkin` (deprecated) with `spring-cloud-sleuth-zipkin`. + +Create a file named `WorkshopBaseline.yml` in your `$WORKSHOP` directory: + +```bash +cat <<'EOF' > $WORKSHOP/WorkshopBaseline.yml +--- +type: specs.openrewrite.org/v1beta/recipe +name: com.example.ecom.recipe.SpringBootMigrationWorkshopBaseline +displayName: Spring Boot Migration Workshop Baseline +description: Upgrade Java 8, Maven, and Spring Boot 2.7. +recipeList: + - org.openrewrite.maven.BestPractices + - org.openrewrite.java.migrate.UpgradeToJava8 + - org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7 + - org.openrewrite.maven.ChangeDependencyGroupIdAndArtifactId: + oldGroupId: org.springframework.cloud + oldArtifactId: spring-cloud-starter-zipkin + newGroupId: org.springframework.cloud + newArtifactId: spring-cloud-sleuth-zipkin +EOF +``` + +Install the recipe locally: + +```bash +mod config recipes yaml install $WORKSHOP/WorkshopBaseline.yml +``` diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-02-apply-baseline.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-02-apply-baseline.mdx new file mode 100644 index 0000000..6956fa0 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-02-apply-baseline.mdx @@ -0,0 +1,24 @@ +--- +title: Apply Baseline +section: Establish Baseline +order: 15 +--- +# Exercise 2: Apply the Baseline Recipe + +First, let's build the LSTs for the new wave structure: + +```bash +mod build $WORKSPACE +``` + +Now, run the baseline recipe across the entire workspace (all waves): + +```bash +mod run $WORKSPACE --recipe com.example.ecom.recipe.SpringBootMigrationWorkshopBaseline +``` + +Apply the changes: + +```bash +mod git apply $WORKSPACE --last-recipe-run +``` diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-03-build-and-release.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-03-build-and-release.mdx new file mode 100644 index 0000000..8794cfd --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-03-build-and-release.mdx @@ -0,0 +1,33 @@ +--- +title: Build and Release +section: Establish Baseline +order: 16 +--- +# Exercise 3: Build and Release + +Before proceeding, we must ensure everything builds and release the new versions. We will use the provided helper scripts. + +Verify the changes with a build: + +```bash +$WORKSHOP/build.sh +``` + +If successful, commit the changes: + +```bash +mod git add $WORKSPACE --last-recipe-run +mod git commit $WORKSPACE -m "Workshop baseline: Maven, Java 8, Spring Boot 2.7" --last-recipe-run +``` + +Now, perform a release to update dependencies and bump versions for the next step: + +```bash +$WORKSHOP/release.sh +``` + +Finally, rebuild the LSTs so the CLI sees the updated code state: + +```bash +mod build $WORKSPACE +``` diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-04-baseline-quiz.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-04-baseline-quiz.mdx new file mode 100644 index 0000000..5821ad1 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-04-baseline-quiz.mdx @@ -0,0 +1,15 @@ +--- +title: Baseline Quiz +section: Establish Baseline +order: 17 +--- +# Quiz + +Why do we establish a baseline before upgrading to Spring Boot 4? + +1. To make the migration harder. +2. To ensure all repositories are on a known, stable configuration (e.g., Spring Boot 2.7) before attempting major upgrades. +3. To confuse the developers. +4. To test the CI pipeline. + +**Correct Answer**: 2 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-establish-baseline.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-establish-baseline.mdx deleted file mode 100644 index 50ffac2..0000000 --- a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/04-establish-baseline.mdx +++ /dev/null @@ -1,103 +0,0 @@ ---- -title: Establish Baseline ---- -# Module 3: Establish a Baseline - -Before we can upgrade to Spring Boot 4, we need to normalize our repositories to a consistent baseline: Java 8 and Spring Boot 2.7. This makes the upgrade path predictable and safer. - -## Exercise 1: Create a Baseline Recipe - -We will create a composite recipe that applies the following: -* Maven best practices (`org.openrewrite.maven.BestPractices`). -* Migrate to Java 8 (`org.openrewrite.java.migrate.UpgradeToJava8`). -* Migrate to Spring Boot 2.7 (`org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7`). -* Replace `spring-cloud-starter-zipkin` (deprecated) with `spring-cloud-sleuth-zipkin`. - -Create a file named `WorkshopBaseline.yml` in your `$WORKSHOP` directory: - -```bash -cat <<'EOF' > $WORKSHOP/WorkshopBaseline.yml ---- -type: specs.openrewrite.org/v1beta/recipe -name: com.example.ecom.recipe.SpringBootMigrationWorkshopBaseline -displayName: Spring Boot Migration Workshop Baseline -description: Upgrade Java 8, Maven, and Spring Boot 2.7. -recipeList: - - org.openrewrite.maven.BestPractices - - org.openrewrite.java.migrate.UpgradeToJava8 - - org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7 - - org.openrewrite.maven.ChangeDependencyGroupIdAndArtifactId: - oldGroupId: org.springframework.cloud - oldArtifactId: spring-cloud-starter-zipkin - newGroupId: org.springframework.cloud - newArtifactId: spring-cloud-sleuth-zipkin -EOF -``` - -Install the recipe locally: - -```bash -mod config recipes yaml install $WORKSHOP/WorkshopBaseline.yml -``` - -## Exercise 2: Apply the Baseline Recipe - -First, let's build the LSTs for the new wave structure: - -```bash -mod build $WORKSPACE -``` - -Now, run the baseline recipe across the entire workspace (all waves): - -```bash -mod run $WORKSPACE --recipe com.example.ecom.recipe.SpringBootMigrationWorkshopBaseline -``` - -Apply the changes: - -```bash -mod git apply $WORKSPACE --last-recipe-run -``` - -## Exercise 3: Build and Release - -Before proceeding, we must ensure everything builds and release the new versions. We will use the provided helper scripts. - -Verify the changes with a build: - -```bash -$WORKSHOP/build.sh -``` - -If successful, commit the changes: - -```bash -mod git add $WORKSPACE --last-recipe-run -mod git commit $WORKSPACE -m "Workshop baseline: Maven, Java 8, Spring Boot 2.7" --last-recipe-run -``` - -Now, perform a release to update dependencies and bump versions for the next step: - -```bash -$WORKSHOP/release.sh -``` - -Finally, rebuild the LSTs so the CLI sees the updated code state: - -```bash -mod build $WORKSPACE -``` - ---- - -**Quiz** - -Why do we establish a baseline before upgrading to Spring Boot 4? - -1. To make the migration harder. -2. To ensure all repositories are on a known, stable configuration (e.g., Spring Boot 2.7) before attempting major upgrades. -3. To confuse the developers. -4. To test the CI pipeline. - -**Correct Answer**: 2 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-01-upgrade-java-17.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-01-upgrade-java-17.mdx new file mode 100644 index 0000000..c22859b --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-01-upgrade-java-17.mdx @@ -0,0 +1,36 @@ +--- +title: Upgrade to Java 17 +section: Smoke Test +order: 18 +--- +# Exercise 1: Upgrade to Java 17 + +In this module, you will raise the Java baseline to 17 and run a controlled Spring Boot 4 upgrade to identify any remaining blockers. + +Spring Boot 3+ (and 4) requires Java 17 or higher. So, let's upgrade all repositories to Java 17 first. + +Run the `UpgradeToJava17` recipe: + +```bash +mod run $WORKSPACE --recipe org.openrewrite.java.migrate.UpgradeToJava17 +``` + +Apply the changes: + +```bash +mod git apply $WORKSPACE --last-recipe-run +``` + +Now, build and commit the changes: + +```bash +$WORKSHOP/build.sh +mod git add $WORKSPACE --last-recipe-run +mod git commit $WORKSPACE -m "Upgrade to Java 17" --last-recipe-run +``` + +And rebuild the LSTs: + +```bash +mod build $WORKSPACE +``` diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-02-smoke-test.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-02-smoke-test.mdx new file mode 100644 index 0000000..bc00b3c --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-02-smoke-test.mdx @@ -0,0 +1,20 @@ +--- +title: Spring Boot 4 Smoke Test +section: Smoke Test +order: 19 +--- +# Exercise 2: Run a Spring Boot 4 Smoke Test + +Now that we are on Java 17, let's see how far the Spring Boot 4 upgrade gets. This is a "smoke test" to identify remaining blockers. + +Run the upgrade recipe: + +```bash +mod run $WORKSPACE --recipe io.moderne.java.spring.boot4.UpgradeSpringBoot_4_0 +``` + +Apply the changes temporarily: + +```bash +mod git apply $WORKSPACE --last-recipe-run +``` diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-03-verify-waves.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-03-verify-waves.mdx new file mode 100644 index 0000000..3214ac4 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-03-verify-waves.mdx @@ -0,0 +1,20 @@ +--- +title: Verify Waves +section: Smoke Test +order: 20 +--- +# Exercise 3: Build Waves (Expect Failure) + +Let's try to build Wave 0 and Wave 1. + +```bash +$WORKSHOP/build.sh 0 +``` + +Wave 0 (common libraries) should build successfully. + +```bash +$WORKSHOP/build.sh 1 +``` + +Wave 1 is expected to fail. Why? Because of QueryDSL issues! We'll fix this in the next module. diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-04-restore-workspace.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-04-restore-workspace.mdx new file mode 100644 index 0000000..4d5a344 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-04-restore-workspace.mdx @@ -0,0 +1,24 @@ +--- +title: Restore Workspace +section: Smoke Test +order: 21 +--- +# Exercise 4: Restore Workspace + +Since the build is broken, let's revert the changes. + +```bash +mod exec $WORKSPACE git restore . +``` + +Verify the workspace is clean: + +```bash +mod git status $WORKSPACE +``` + +Rebuild LSTs to ensure we are back to the Java 17 baseline: + +```bash +mod build $WORKSPACE +``` diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-05-smoke-test-quiz.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-05-smoke-test-quiz.mdx new file mode 100644 index 0000000..fe65e26 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-05-smoke-test-quiz.mdx @@ -0,0 +1,15 @@ +--- +title: Smoke Test Quiz +section: Smoke Test +order: 22 +--- +# Quiz + +Why do we run a smoke test before the full migration? + +1. To see if the computer is smoking. +2. To commit broken code. +3. To isolate remaining obstacles (like QueryDSL) without committing to a full upgrade. +4. To verify that Java 17 is installed correctly. + +**Correct Answer**: 3 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-smoke-test.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-smoke-test.mdx deleted file mode 100644 index 0427136..0000000 --- a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/05-smoke-test.mdx +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: Smoke Test ---- -# Module 4: Raise Baseline and Smoke Test - -In this module, you will raise the Java baseline to 17 and run a controlled Spring Boot 4 upgrade to identify any remaining blockers. - -## Exercise 1: Upgrade to Java 17 - -Spring Boot 3+ (and 4) requires Java 17 or higher. So, let's upgrade all repositories to Java 17 first. - -Run the `UpgradeToJava17` recipe: - -```bash -mod run $WORKSPACE --recipe org.openrewrite.java.migrate.UpgradeToJava17 -``` - -Apply the changes: - -```bash -mod git apply $WORKSPACE --last-recipe-run -``` - -Now, build and commit the changes: - -```bash -$WORKSHOP/build.sh -mod git add $WORKSPACE --last-recipe-run -mod git commit $WORKSPACE -m "Upgrade to Java 17" --last-recipe-run -``` - -And rebuild the LSTs: - -```bash -mod build $WORKSPACE -``` - -## Exercise 2: Run a Spring Boot 4 Smoke Test - -Now that we are on Java 17, let's see how far the Spring Boot 4 upgrade gets. This is a "smoke test" to identify remaining blockers. - -Run the upgrade recipe: - -```bash -mod run $WORKSPACE --recipe io.moderne.java.spring.boot4.UpgradeSpringBoot_4_0 -``` - -Apply the changes temporarily: - -```bash -mod git apply $WORKSPACE --last-recipe-run -``` - -## Exercise 3: Build Waves (Expect Failure) - -Let's try to build Wave 0 and Wave 1. - -```bash -$WORKSHOP/build.sh 0 -``` - -Wave 0 (common libraries) should build successfully. - -```bash -$WORKSHOP/build.sh 1 -``` - -Wave 1 is expected to fail. Why? Because of QueryDSL issues! We'll fix this in the next module. - -## Exercise 4: Restore Workspace - -Since the build is broken, let's revert the changes. - -```bash -mod exec $WORKSPACE git restore . -``` - -Verify the workspace is clean: - -```bash -mod git status $WORKSPACE -``` - -Rebuild LSTs to ensure we are back to the Java 17 baseline: - -```bash -mod build $WORKSPACE -``` - ---- - -**Quiz** - -Why do we run a smoke test before the full migration? - -1. To see if the computer is smoking. -2. To commit broken code. -3. To isolate remaining obstacles (like QueryDSL) without committing to a full upgrade. -4. To verify that Java 17 is installed correctly. - -**Correct Answer**: 3 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-01-install-querydsl-recipe.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-01-install-querydsl-recipe.mdx new file mode 100644 index 0000000..24fade7 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-01-install-querydsl-recipe.mdx @@ -0,0 +1,25 @@ +--- +title: Install QueryDSL Recipe +section: Finish Migration +order: 23 +--- +# Exercise 1: Install the QueryDSL Recipe + +In this module, you will install a custom recipe to handle the QueryDSL issues, build a composite upgrade recipe, and upgrade each wave of repositories to Spring Boot 4. + +We need a custom recipe to rename packages and update dependencies for QueryDSL. + +Clone the custom recipe repository: + +```bash +cd $PROJECTS +git clone https://github.com/modernetraining/rewrite-querydsl.git +``` + +Build and install it locally: + +```bash +cd rewrite-querydsl +mvn clean install +mod config recipes jar install org.openrewrite.recipe:rewrite-querydsl:0.1.0-SNAPSHOT +``` diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-02-create-composite-recipe.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-02-create-composite-recipe.mdx new file mode 100644 index 0000000..b486af7 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-02-create-composite-recipe.mdx @@ -0,0 +1,36 @@ +--- +title: Create Composite Recipe +section: Finish Migration +order: 24 +--- +# Exercise 2: Create a Composite Upgrade Recipe + +Create a composite recipe that combines: +* `UpgradeDependencyVersion` (to bump internal dependencies). +* `UpgradeSpringBoot_4_0` (Spring Boot 4). +* `UpgradeToQueryDsl5` (QueryDSL 5). + +Create a file named `CustomUpgradeSpringBoot_4_0.yml` in your `$WORKSHOP` directory: + +```bash +cat <<'EOF' > $WORKSHOP/CustomUpgradeSpringBoot_4_0.yml +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.recipe.querydsl.CustomUpgradeSpringBoot_4_0 +displayName: CustomUpgradeSpringBoot_4_0 +description: Upgrade internal deps, Spring Boot 4.0, and QueryDSL 5. +recipeList: + - org.openrewrite.java.dependencies.UpgradeDependencyVersion: + groupId: com.example.ecom + artifactId: "*" + newVersion: 1.x + - io.moderne.java.spring.boot4.UpgradeSpringBoot_4_0 + - org.openrewrite.recipe.querydsl.UpgradeToQueryDsl5 +EOF +``` + +Install the composite recipe locally: + +```bash +mod config recipes yaml install $WORKSHOP/CustomUpgradeSpringBoot_4_0.yml +``` diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-03-upgrade-wave-0.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-03-upgrade-wave-0.mdx new file mode 100644 index 0000000..a985711 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-03-upgrade-wave-0.mdx @@ -0,0 +1,25 @@ +--- +title: Upgrade Wave 0 +section: Finish Migration +order: 25 +--- +# Exercise 3: Upgrade Wave 0 + +Run the custom recipe on Wave 0: + +```bash +mod run $WORKSPACE/Wave0 --recipe org.openrewrite.recipe.querydsl.CustomUpgradeSpringBoot_4_0 +``` + +Apply the changes: + +```bash +mod git apply $WORKSPACE/Wave0 --last-recipe-run +``` + +Build and release Wave 0: + +```bash +$WORKSHOP/build.sh 0 +$WORKSHOP/release.sh 0 +``` diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-04-upgrade-wave-1.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-04-upgrade-wave-1.mdx new file mode 100644 index 0000000..3a0cf41 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-04-upgrade-wave-1.mdx @@ -0,0 +1,27 @@ +--- +title: Upgrade Wave 1 +section: Finish Migration +order: 26 +--- +# Exercise 4: Upgrade Wave 1 + +Now repeat for Wave 1: + +```bash +mod run $WORKSPACE/Wave1 --recipe org.openrewrite.recipe.querydsl.CustomUpgradeSpringBoot_4_0 +``` + +Apply changes: + +```bash +mod git apply $WORKSPACE/Wave1 --last-recipe-run +``` + +Build and release Wave 1: + +```bash +$WORKSHOP/build.sh 1 +$WORKSHOP/release.sh 1 +``` + +You can continue this process for Wave 2 and Wave 3 to complete the migration across all repositories. diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-05-refresh-devcenter.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-05-refresh-devcenter.mdx new file mode 100644 index 0000000..5c172e8 --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-05-refresh-devcenter.mdx @@ -0,0 +1,28 @@ +--- +title: Refresh DevCenter +section: Finish Migration +order: 27 +--- +# Exercise 5: Refresh DevCenter + +After upgrading waves, you can generate a DevCenter dashboard to visualize the progress. + +First, rebuild LSTs: + +```bash +mod build $WORKSPACE +``` + +Run the `DevCenterStarter` recipe: + +```bash +mod run $WORKSPACE --recipe io.moderne.devcenter.DevCenterStarter +``` + +Generate the DevCenter dashboard: + +```bash +mod devcenter $WORKSPACE --last-recipe-run +``` + +This will produce an HTML file you can open to see the updated status of your repositories. diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-06-finish-migration-quiz.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-06-finish-migration-quiz.mdx new file mode 100644 index 0000000..0557a0f --- /dev/null +++ b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-06-finish-migration-quiz.mdx @@ -0,0 +1,15 @@ +--- +title: Finish Migration Quiz +section: Finish Migration +order: 28 +--- +# Quiz + +Why is it beneficial to upgrade in waves using a composite recipe? + +1. To ensure that internal dependencies are updated in the correct order and custom issues (like QueryDSL) are handled alongside framework upgrades. +2. To make the migration take longer. +3. To confuse the build system. +4. To avoid using the CLI. + +**Correct Answer**: 1 diff --git a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-finish-migration.mdx b/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-finish-migration.mdx deleted file mode 100644 index cef9523..0000000 --- a/examples/moderne-spring-boot-migration/lessons/spring-boot-migration/06-finish-migration.mdx +++ /dev/null @@ -1,138 +0,0 @@ ---- -title: Finish Migration ---- -# Module 5: Finish Migration in Waves - -In this module, you will install a custom recipe to handle the QueryDSL issues, build a composite upgrade recipe, and upgrade each wave of repositories to Spring Boot 4. - -## Exercise 1: Install the QueryDSL Recipe - -We need a custom recipe to rename packages and update dependencies for QueryDSL. - -Clone the custom recipe repository: - -```bash -cd $PROJECTS -git clone https://github.com/modernetraining/rewrite-querydsl.git -``` - -Build and install it locally: - -```bash -cd rewrite-querydsl -mvn clean install -mod config recipes jar install org.openrewrite.recipe:rewrite-querydsl:0.1.0-SNAPSHOT -``` - -## Exercise 2: Create a Composite Upgrade Recipe - -Create a composite recipe that combines: -* `UpgradeDependencyVersion` (to bump internal dependencies). -* `UpgradeSpringBoot_4_0` (Spring Boot 4). -* `UpgradeToQueryDsl5` (QueryDSL 5). - -Create a file named `CustomUpgradeSpringBoot_4_0.yml` in your `$WORKSHOP` directory: - -```bash -cat <<'EOF' > $WORKSHOP/CustomUpgradeSpringBoot_4_0.yml ---- -type: specs.openrewrite.org/v1beta/recipe -name: org.openrewrite.recipe.querydsl.CustomUpgradeSpringBoot_4_0 -displayName: CustomUpgradeSpringBoot_4_0 -description: Upgrade internal deps, Spring Boot 4.0, and QueryDSL 5. -recipeList: - - org.openrewrite.java.dependencies.UpgradeDependencyVersion: - groupId: com.example.ecom - artifactId: "*" - newVersion: 1.x - - io.moderne.java.spring.boot4.UpgradeSpringBoot_4_0 - - org.openrewrite.recipe.querydsl.UpgradeToQueryDsl5 -EOF -``` - -Install the composite recipe locally: - -```bash -mod config recipes yaml install $WORKSHOP/CustomUpgradeSpringBoot_4_0.yml -``` - -## Exercise 3: Upgrade Wave 0 - -Run the custom recipe on Wave 0: - -```bash -mod run $WORKSPACE/Wave0 --recipe org.openrewrite.recipe.querydsl.CustomUpgradeSpringBoot_4_0 -``` - -Apply the changes: - -```bash -mod git apply $WORKSPACE/Wave0 --last-recipe-run -``` - -Build and release Wave 0: - -```bash -$WORKSHOP/build.sh 0 -$WORKSHOP/release.sh 0 -``` - -## Exercise 4: Upgrade Wave 1 - -Now repeat for Wave 1: - -```bash -mod run $WORKSPACE/Wave1 --recipe org.openrewrite.recipe.querydsl.CustomUpgradeSpringBoot_4_0 -``` - -Apply changes: - -```bash -mod git apply $WORKSPACE/Wave1 --last-recipe-run -``` - -Build and release Wave 1: - -```bash -$WORKSHOP/build.sh 1 -$WORKSHOP/release.sh 1 -``` - -You can continue this process for Wave 2 and Wave 3 to complete the migration across all repositories. - -## Exercise 5: Refresh DevCenter - -After upgrading waves, you can generate a DevCenter dashboard to visualize the progress. - -First, rebuild LSTs: - -```bash -mod build $WORKSPACE -``` - -Run the `DevCenterStarter` recipe: - -```bash -mod run $WORKSPACE --recipe io.moderne.devcenter.DevCenterStarter -``` - -Generate the DevCenter dashboard: - -```bash -mod devcenter $WORKSPACE --last-recipe-run -``` - -This will produce an HTML file you can open to see the updated status of your repositories. - ---- - -**Quiz** - -Why is it beneficial to upgrade in waves using a composite recipe? - -1. To ensure that internal dependencies are updated in the correct order and custom issues (like QueryDSL) are handled alongside framework upgrades. -2. To make the migration take longer. -3. To confuse the build system. -4. To avoid using the CLI. - -**Correct Answer**: 1 From c4cc43573411cdd0ec51d804630d74328dbd475b Mon Sep 17 00:00:00 2001 From: Matthew Campbell Date: Sun, 8 Feb 2026 22:21:20 -0500 Subject: [PATCH 3/4] get example lesson working on top of GraalVM native image --- .../moderne-spring-boot-migration/Dockerfile | 4 +- pom.xml | 21 +++- .../com.snowfort/turtorial/jni-config.json | 50 ++++++++ .../com.snowfort/turtorial/proxy-config.json | 11 ++ .../turtorial/reflect-config.json | 119 ++++++++++++++++++ .../turtorial/resource-config.json | 13 ++ 6 files changed, 212 insertions(+), 6 deletions(-) create mode 100644 src/main/resources/META-INF/native-image/com.snowfort/turtorial/jni-config.json create mode 100644 src/main/resources/META-INF/native-image/com.snowfort/turtorial/proxy-config.json create mode 100644 src/main/resources/META-INF/native-image/com.snowfort/turtorial/reflect-config.json create mode 100644 src/main/resources/META-INF/native-image/com.snowfort/turtorial/resource-config.json diff --git a/examples/moderne-spring-boot-migration/Dockerfile b/examples/moderne-spring-boot-migration/Dockerfile index 7552a1c..3756478 100644 --- a/examples/moderne-spring-boot-migration/Dockerfile +++ b/examples/moderne-spring-boot-migration/Dockerfile @@ -79,5 +79,5 @@ COPY --chown=turtorial:turtorial lessons /app/lessons WORKDIR /home/turtorial -# Run the application (using Java 25 from the base image location) -ENTRYPOINT ["/opt/java/openjdk/bin/java", "-jar", "/app/turtorial.jar"] \ No newline at end of file +# Run the application +ENTRYPOINT ["/app/turtorial"] \ No newline at end of file diff --git a/pom.xml b/pom.xml index 469f49e..825934b 100644 --- a/pom.xml +++ b/pom.xml @@ -161,10 +161,6 @@ - - org.graalvm.buildtools - native-maven-plugin - org.springframework.boot spring-boot-maven-plugin @@ -270,6 +266,23 @@ + + native + + + + org.graalvm.buildtools + native-maven-plugin + + turtorial + + --initialize-at-build-time=jtermios.linux.JTermiosImpl$Linux_C_lib_DirectMapping + + + + + + diff --git a/src/main/resources/META-INF/native-image/com.snowfort/turtorial/jni-config.json b/src/main/resources/META-INF/native-image/com.snowfort/turtorial/jni-config.json new file mode 100644 index 0000000..bd6c8de --- /dev/null +++ b/src/main/resources/META-INF/native-image/com.snowfort/turtorial/jni-config.json @@ -0,0 +1,50 @@ +[ + { + "name": "jtermios.linux.JTermiosImpl$Linux_C_lib_DirectMapping", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "jtermios.linux.JTermiosImpl$Linux_C_lib$termios", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "jtermios.linux.JTermiosImpl$Linux_C_lib$serial_struct", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "jtermios.linux.JTermiosImpl$Linux_C_lib$timeval", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "jtermios.linux.JTermiosImpl$Linux_C_lib$pollfd", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.sun.jna.Pointer", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.sun.jna.NativeLong", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.sun.jna.ptr.IntByReference", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + } +] diff --git a/src/main/resources/META-INF/native-image/com.snowfort/turtorial/proxy-config.json b/src/main/resources/META-INF/native-image/com.snowfort/turtorial/proxy-config.json new file mode 100644 index 0000000..faa9c49 --- /dev/null +++ b/src/main/resources/META-INF/native-image/com.snowfort/turtorial/proxy-config.json @@ -0,0 +1,11 @@ +[ + { + "interfaces": ["com.pty4j.unix.linux.OSFacadeImpl$C_lib"] + }, + { + "interfaces": ["com.pty4j.unix.linux.OSFacadeImpl$Linux_Util_lib"] + }, + { + "interfaces": ["jtermios.linux.JTermiosImpl$Linux_C_lib"] + } +] diff --git a/src/main/resources/META-INF/native-image/com.snowfort/turtorial/reflect-config.json b/src/main/resources/META-INF/native-image/com.snowfort/turtorial/reflect-config.json new file mode 100644 index 0000000..684b232 --- /dev/null +++ b/src/main/resources/META-INF/native-image/com.snowfort/turtorial/reflect-config.json @@ -0,0 +1,119 @@ +[ + { + "name": "jtermios.linux.JTermiosImpl$Linux_C_lib_DirectMapping", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "jtermios.linux.JTermiosImpl$Linux_C_lib", + "allDeclaredMethods": true + }, + { + "name": "jtermios.linux.JTermiosImpl$Linux_C_lib$termios", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "jtermios.linux.JTermiosImpl$Linux_C_lib$serial_struct", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "jtermios.linux.JTermiosImpl$Linux_C_lib$timeval", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "jtermios.linux.JTermiosImpl$Linux_C_lib$pollfd", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "jtermios.linux.JTermiosImpl$FDSetImpl", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "jtermios.linux.JTermiosImpl", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "jtermios.JTermios", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.pty4j.unix.linux.OSFacadeImpl", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.pty4j.unix.linux.OSFacadeImpl$C_lib", + "allDeclaredMethods": true + }, + { + "name": "com.pty4j.unix.linux.OSFacadeImpl$Linux_Util_lib", + "allDeclaredMethods": true + }, + { + "name": "com.pty4j.unix.PtyHelpers", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.pty4j.unix.PtyHelpers$OSFacade", + "allDeclaredMethods": true + }, + { + "name": "com.pty4j.unix.Pty", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.pty4j.unix.UnixPtyProcess", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.pty4j.PtyProcessBuilder", + "allDeclaredConstructors": true, + "allDeclaredMethods": true + }, + { + "name": "com.sun.jna.Structure", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.sun.jna.Pointer", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.sun.jna.NativeLong", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.sun.jna.ptr.IntByReference", + "allDeclaredConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + } +] diff --git a/src/main/resources/META-INF/native-image/com.snowfort/turtorial/resource-config.json b/src/main/resources/META-INF/native-image/com.snowfort/turtorial/resource-config.json new file mode 100644 index 0000000..223b077 --- /dev/null +++ b/src/main/resources/META-INF/native-image/com.snowfort/turtorial/resource-config.json @@ -0,0 +1,13 @@ +{ + "resources": { + "includes": [ + {"pattern": "resources/com/pty4j/native/.*"}, + {"pattern": "com/sun/jna/.*"}, + {"pattern": "lessons/.*"}, + {"pattern": "schemas/.*"}, + {"pattern": "static/.*"}, + {"pattern": ".*\\.yml"}, + {"pattern": ".*\\.yaml"} + ] + } +} From 8adb9f0acdfb658836c923479f987e2d8b49eaeb Mon Sep 17 00:00:00 2001 From: Matthew Campbell Date: Sun, 8 Feb 2026 23:05:11 -0500 Subject: [PATCH 4/4] Adding some fixes for routing, and rolling back to non-GraalVM builds since the PTY library doesn't seem to support this --- .github/workflows/manual-publish-image.yml | 6 +-- .github/workflows/release.yml | 6 +-- Dockerfile | 40 +++++++++++++++---- .../moderne-spring-boot-migration/Dockerfile | 19 +++++---- pom.xml | 20 ++-------- .../turtorial/controller/SpaController.java | 13 ++++++ 6 files changed, 65 insertions(+), 39 deletions(-) create mode 100644 src/main/java/com/snowfort/turtorial/controller/SpaController.java diff --git a/.github/workflows/manual-publish-image.yml b/.github/workflows/manual-publish-image.yml index 64f87ff..affd3cf 100644 --- a/.github/workflows/manual-publish-image.yml +++ b/.github/workflows/manual-publish-image.yml @@ -40,7 +40,7 @@ jobs: cache: 'maven' - name: Build Native Binary - run: ./mvnw clean package -Pnative,prod -DskipTests + run: ./mvnw clean package -Pprod -DskipTests - name: Log in to the Container registry uses: docker/login-action@v3 @@ -56,8 +56,8 @@ jobs: uses: docker/build-push-action@v6 with: context: . - file: ./Dockerfile.native - build-args: BINARY=prebuilt + file: ./Dockerfile + build-args: JAR_SOURCE=build platforms: ${{ matrix.platform }} push: true tags: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3b48ec2..c130942 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -133,7 +133,7 @@ jobs: run: mvn -B versions:set -DnewVersion=${{ needs.prepare-release.outputs.release-version }} -DgenerateBackupPoms=false - name: Build Native Binary - run: ./mvnw clean package -Pnative,prod -DskipTests + run: ./mvnw clean package -Pprod -DskipTests - name: Login to GitHub Container Registry uses: docker/login-action@v3 @@ -149,8 +149,8 @@ jobs: uses: docker/build-push-action@v6 with: context: . - file: ./Dockerfile.native - build-args: BINARY=prebuilt + file: ./Dockerfile + build-args: JAR_SOURCE=build platforms: ${{ matrix.platform }} push: true tags: | diff --git a/Dockerfile b/Dockerfile index 4bc7d90..8410fd9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,23 @@ +# Build arg to select JAR source: "build" (compile from source) or "prebuilt" (use pre-built JAR) +ARG JAR_SOURCE=build + +# Builder stage — compiles JAR from source with frontend (default) +FROM eclipse-temurin:25-jdk AS build +WORKDIR /build +COPY .mvn .mvn +COPY mvnw pom.xml ./ +RUN ./mvnw dependency:go-offline -B +COPY src src +RUN ./mvnw clean package -Pprod -DskipTests \ + && cp target/turtorial-*.jar /turtorial.jar + +# Prebuilt stage — copies JAR from build context (CI or local pre-built) +FROM scratch AS prebuilt +COPY target/turtorial-*.jar /turtorial.jar + +# Select JAR source based on ARG (BuildKit skips unused stages) +FROM ${JAR_SOURCE} AS jar + FROM ubuntu:24.04 AS base # Set install locations @@ -22,9 +42,15 @@ RUN apt-get update && apt-get install -y \ nano \ && rm -rf /var/lib/apt/lists/* -# Install Temurin JDK 25 -RUN mkdir -p $JAVA_HOME \ - && curl -L "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.1%2B8/OpenJDK25U-jdk_aarch64_linux_hotspot_25.0.1_8.tar.gz" | tar -xz -C $JAVA_HOME --strip-components=1 +# Install Temurin JDK 25 (detect architecture automatically) +RUN ARCH=$(uname -m) \ + && case "$ARCH" in \ + x86_64) TEMURIN_ARCH="x64" ;; \ + aarch64) TEMURIN_ARCH="aarch64" ;; \ + *) echo "Unsupported architecture: $ARCH" && exit 1 ;; \ + esac \ + && mkdir -p $JAVA_HOME \ + && curl -L "https://api.adoptium.net/v3/binary/latest/25/ga/linux/${TEMURIN_ARCH}/jdk/hotspot/normal/eclipse?project=jdk" | tar -xz -C $JAVA_HOME --strip-components=1 # Install Maven RUN mkdir -p $MAVEN_HOME \ @@ -48,10 +74,8 @@ RUN mkdir -p /home/turtorial/.ssh \ # Create app directory WORKDIR /app -# Copy the built application -# Assumes `mvn clean package` has been run locally -ARG JAR_FILE=target/turtorial-*-SNAPSHOT.jar -COPY ${JAR_FILE} /app/turtorial.jar +# Copy the JAR from the selected source +COPY --from=jar /turtorial.jar /app/turtorial.jar # Change ownership of the app directory RUN chown -R turtorial:turtorial /app @@ -69,4 +93,4 @@ EXPOSE 8080 RUN mkdir -p /app/lessons # Run the application -ENTRYPOINT ["java", "-jar", "/app/turtorial.jar"] \ No newline at end of file +ENTRYPOINT ["java", "-jar", "/app/turtorial.jar"] diff --git a/examples/moderne-spring-boot-migration/Dockerfile b/examples/moderne-spring-boot-migration/Dockerfile index 3756478..89d9e9a 100644 --- a/examples/moderne-spring-boot-migration/Dockerfile +++ b/examples/moderne-spring-boot-migration/Dockerfile @@ -13,14 +13,14 @@ FROM turtorial:latest AS base USER root -# Install Temurin JDK 17 (needed for Spring Boot 2.7 migration workshop) -# Note: turtorial:latest installs Java 25 to /opt/java/openjdk -RUN mkdir -p /opt/java/openjdk17 \ - && curl -L "https://api.adoptium.net/v3/binary/latest/17/ga/linux/aarch64/jdk/hotspot/normal/eclipse" | tar -xz -C /opt/java/openjdk17 --strip-components=1 +# # Install Temurin JDK 17 (needed for Spring Boot 2.7 migration workshop) +# # Note: turtorial:latest installs Java 25 to /opt/java/openjdk +# RUN mkdir -p /opt/java/openjdk17 \ +# && curl -L "https://api.adoptium.net/v3/binary/latest/17/ga/linux/aarch64/jdk/hotspot/normal/eclipse" | tar -xz -C /opt/java/openjdk17 --strip-components=1 -# Set JAVA_HOME to Java 17 for the workshop exercises -ENV JAVA_HOME=/opt/java/openjdk17 -ENV PATH=$JAVA_HOME/bin:$PATH +# # Set JAVA_HOME to Java 17 for the workshop exercises +# ENV JAVA_HOME=/opt/java/openjdk17 +# ENV PATH=$JAVA_HOME/bin:$PATH ARG MODERNE_CLI_STAGE=stable ARG MODERNE_CLI_VERSION @@ -79,5 +79,8 @@ COPY --chown=turtorial:turtorial lessons /app/lessons WORKDIR /home/turtorial +ENV SPRING_PROFILES_ACTIVE=prod + # Run the application -ENTRYPOINT ["/app/turtorial"] \ No newline at end of file +# ENTRYPOINT ["/app/turtorial"] +ENTRYPOINT ["java", "-jar", "/app/turtorial.jar"] \ No newline at end of file diff --git a/pom.xml b/pom.xml index 1dcf4c5..6ce3c23 100644 --- a/pom.xml +++ b/pom.xml @@ -172,6 +172,9 @@ turtorial + + --initialize-at-build-time=jtermios.linux.JTermiosImpl$Linux_C_lib_DirectMapping + @@ -266,23 +269,6 @@ - - native - - - - org.graalvm.buildtools - native-maven-plugin - - turtorial - - --initialize-at-build-time=jtermios.linux.JTermiosImpl$Linux_C_lib_DirectMapping - - - - - - diff --git a/src/main/java/com/snowfort/turtorial/controller/SpaController.java b/src/main/java/com/snowfort/turtorial/controller/SpaController.java new file mode 100644 index 0000000..3b9dfec --- /dev/null +++ b/src/main/java/com/snowfort/turtorial/controller/SpaController.java @@ -0,0 +1,13 @@ +package com.snowfort.turtorial.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class SpaController { + + @GetMapping("/lesson/{id}") + public String forwardLesson() { + return "forward:/index.html"; + } +}