Configuring the DevCenter

The Moderne DevCenter is the mission-control dashboard of the Moderne Platform. It provides you with high-level details about the state of all of your repositories. Using it, you can track the progress of upgrades, migrations, and security vulnerabilities. You can also use it to view key visualizations you care about – such as a dependency graph or a SQL operation usage chart.

In this doc, we'll walk you through everything you need to know to configure your own DevCenter.

Prerequisites

In order to configure any DevCenters, there are two things you need to do (which we'll walk through in more detail below):

  1. You must ensure that the Moderne agent Maven configuration only has one entry where the recipe source is set to true. (Note: this does not apply to one Maven repository configured identically in multiple agents. Only that you cannot have two distinct Maven repositories configured where recipe source is set to true.)

Organizations service

If you are configuring an Organizations service for the first time, we strongly recommend that you use our Organizations service template and modify it to meet your needs. By doing so, you will only need to update some JSON files rather than writing your own code.

If you've chosen to create your own Organizations service without using our template, please ensure your service fulfills the latest GraphQL schema. After doing so, please ensure you've set up the Moderne agent with Maven configuration correctly and then jump to the section of this doc about card types and what is necessary for each.

Moderne agent Maven configuration

In order for the DevCenter to function correctly, the agent needs to be configured with details about a Maven repository it can use to store and access recipe JARs from.

If you have not configured the Moderne agent with Maven repository access before, please follow the instructions in our Moderne agent Maven configuration documentation to add one entry with recipe source set to true.

If you have already configured the Moderne agent with Maven repository access, then you need to ensure that only one has the configuration of MODERNE_AGENT_MAVEN_{index}_RECIPESOURCE / moderne.agent.maven[{index}].recipeSource set to true.

If you have multiple locations where recipes are stored, you will need to create a virtual repository that wraps all of the locations where recipes can be stored. You will also need to ensure that the virtual repository points to the following four repositories (alongside the other repositories where recipe artifacts are stored):

  1. https://oss.sonatype.org/content/repositories/snapshots/

  2. https://s01.oss.sonatype.org/content/repositories/snapshots/

  3. https://repo.maven.apache.org/maven2

  4. https://repo1.maven.org/maven2/

Step 1: Ensure you have a DevCenterDataFetcher class

This step only applies if you used the Moderne Organizations service template. If you made your own, please jump to step 3.

If you've created an Organizations service prior to March 2024, you will need to copy the new DevCenterDataFetcher file to your Organizations service repository. It will go in the same location as the other source classes such as Application.java.

If you've created an Organizations service after March 2024, please ensure that you have a DevCenterDataFetcher.java file in your Organizations service repository before moving on to step 2.

Step 2: Ensure you have an up-to-date moderne-organizations.graphqls schema

Similar to the previous step, please double-check your moderne-organizations.graphqls file and ensure that there is a devCenter field in the Organization object:

type Organization {
    id: ID!
    name: String!

    """
    Ordered list of commit options as they should appear in the UI.
    """
    commitOptions: [CommitOption!]!

    parent: Organization

    devCenter: DevCenter
}

The DevCenter object is the schema you need to follow in the below step to configure your DevCenter.

Step 3: Create and configure the DevCenter

Your service must fulfill the GraphQL contract mentioned in the previous step. If you chose to use our template repository for your Organizations service, you will need to run ./gradlew generateGraphqlJava copyGeneratedGraphql to get the latest types, and then you will need to configure your own devcenter.json file.

The devcenter.json file is where all of the configuration lies for DevCenters. In this file, you can configure things like which organizations should have a DevCenter, what cards should appear on said DevCenter, and what the keys should be on the cards. This file must follow the GraphQL schema mentioned above.

When creating a DevCenter for the first time, we strongly recommend that you only create a DevCenter for a few key organizations. This will allow you to get data into the platform faster and ensure that you've configured everything correctly. Once everything is working as expected, you can then add more DevCenters as desired.

Frameworks and migration cards

Framework and migration cards allow you to see things like what Java version your repositories use, what version of Spring Boot they're on, or what JUnit version they use:

To create these cards, you will need three things:

  1. A relevant title for the card (e.g., Spring Boot 3.2)

  2. A recipe that can be run to fix the core issue the card is highlighting (e.g., org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_2)

Measures

Each measure consists of a name and a recipe that can be used to determine whether or not a repository falls into said measure. We do not recommend that this recipe be the same recipe as the one used to "fix" the card. For instance, in a Spring Boot 3.2 card, one measure might have a name of Major – which represents all repositories that are one or more major versions behind. The corresponding recipe would be org.openrewrite.java.dependencies.DependencyInsight and it would include the options of 1-2. This would mark all repositories that use Spring Boot version 1.x or 2.x as being a major version or more behind.

You must ensure that the measure recipes return disjointed results (i.e., the same repository can not be returned by multiple recipes).

For example, if you were tracking Java versions, you may have a repository that contains some code that uses Java 8, 11, and 17. However, you should ensure that your measure recipes only return this repository once.

Each card can have up to three measures. These measures should be returned in a specific order; with the most urgent being returned first and the least urgent being returned last. In the Spring Boot 3.2 example, you might have: Major, Minor, and Patch returned in that specific order.

If you change any part of any measure on a card (such as the name of the measure or what recipe it should run), you will lose all results for the card until the next rebuild of the DevCenter.

Example

Below is an example of an upgradesAndMigrations section for a Spring Boot 3.2 card:
[
  {
    "devCenter": {
      "upgradesAndMigrations": [
        {
          "title": "Spring boot 3.2",
          "measures": [
            {
              "name": "Major",
              "recipe": {
                "recipeId": "org.openrewrite.java.dependencies.DependencyInsight",
                "options": [
                  {
                    "name": "groupIdPattern",
                    "value": "org.springframework.boot"
                  },
                  {
                    "name": "artifactIdPattern",
                    "value": "spring-boot-starter"
                  },
                  {
                    "name": "version",
                    "value": "1-2"
                  }
                ]
              }
            },
            {
              "name": "Minor",
              "recipe": {
                "recipeId": "org.openrewrite.java.dependencies.DependencyInsight",
                "options": [
                  {
                    "name": "groupIdPattern",
                    "value": "org.springframework.boot"
                  },
                  {
                    "name": "artifactIdPattern",
                    "value": "spring-boot-starter"
                  },
                  {
                    "name": "version",
                    "value": "3-3.1"
                  }
                ]
              }
            },
            {
              "name": "Patch",
              "recipe": {
                "recipeId": "org.openrewrite.java.dependencies.DependencyInsight",
                "options": [
                  {
                    "name": "groupIdPattern",
                    "value": "org.springframework.boot"
                  },
                  {
                    "name": "artifactIdPattern",
                    "value": "spring-boot-starter"
                  },
                  {
                    "name": "version",
                    "value": "3.2-3.2.2"
                  }
                ]
              }
            }
          ],
          "fix": {
            "recipeId": "org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_2"
          }
        },
        {
          "title": "Java 21",
          "measures": [
            {
              "name": "Java 8+",
              "recipe": {
                "recipeId": "org.openrewrite.java.search.HasJavaVersion",
                "options": [
                  {
                    "name": "version",
                    "value": "8-10"
                  }
                ]
              }
            },
            {
              "name": "Java 11+",
              "recipe": {
                "recipeId": "org.openrewrite.java.search.HasJavaVersion",
                "options": [
                  {
                    "name": "version",
                    "value": "11-16"
                  }
                ]
              }
            },
            {
              "name": "Java 17+",
              "recipe": {
                "recipeId": "org.openrewrite.java.search.HasJavaVersion",
                "options": [
                  {
                    "name": "version",
                    "value": "17-20"
                  }
                ]
              }
            }
          ],
          "fix": {
            "recipeId": "org.openrewrite.java.migrate.UpgradeToJava21"
          }
        },
        {
          "title": "JUnit 5",
          "measures": [
            {
              "name": "JUnit 4",
              "recipe": {
                "recipeId": "org.openrewrite.java.search.FindAnnotations",
                "options": [
                  {
                    "name": "annotationPattern",
                    "value": "@org.junit.Test"
                  }
                ]
              }
            }
          ],
          "fix": {
            "recipeId": "org.openrewrite.java.testing.junit5.JUnit4to5Migration"
          }
        }
      ],

      // Other things
  }
]

Visualization cards

If there are some visualizations you find particularly important for your organization, you can add them to your DevCenter so that you can quickly view and share them with others.

To create these cards you need two things:

  1. A visualizationId that identifies which visualization you want to include (e.g., io.moderne.DependencyUsageViolin).

  2. Options for the visualization (if any exist).

The visualizationId can be found on any visualization card you run in the Moderne platform:

Example

Below is an example of a visualizations section you might have in your DevCenter:
"visualizations": [
    {
        "visualizationId": "io.moderne.DependencyUsageViolin",
    },
    {
        "visualizationId": "io.moderne.LanguageComposition"
    },
    {
        "visualizationId": "io.moderne-private.SqlHistogram"
    }
],

Security cards

Security cards give your team a high-level overview of what security issues your repositories have or have not resolved.

Security cards are composed of a list of recipes that fix security issues you care about. You should include no more than 10 security recipes on one DevCenter.

Example

Below is an example of a security section you might have in your DevCenter:
"security": [
    {"recipeId": "org.openrewrite.java.security.OwaspA01"},
    {"recipeId": "org.openrewrite.java.security.OwaspA02"},
    {"recipeId": "org.openrewrite.java.security.OwaspA03"},
    {"recipeId": "org.openrewrite.java.security.OwaspA05"},
    {"recipeId": "org.openrewrite.java.security.OwaspA06"},
    {"recipeId": "org.openrewrite.java.security.OwaspA08"},
    {"recipeId": "org.openrewrite.java.security.RegularExpressionDenialOfService"},
    {"recipeId": "org.openrewrite.java.security.SecureRandom"},
    {"recipeId": "org.openrewrite.java.security.ZipSlip"},
    {"recipeId": "org.openrewrite.java.security.XmlParserXXEVulnerability"}
]

Next steps

Once you've configured a DevCenter, you will need to wait for data to propagate to it. This can take a considerable amount of time depending on how many repositories and recipes you've configured. Please wait at least 24 hours for data to flow in.

Last updated