Skip to content

Contributing guide

How to write a recipe?

All recipe implementations are kept in the allwrite-recipes module.

In general, you should follow the official Authoring Recipes docs from OpenRewrite.

However, allwrite has some custom features that you can use.

Visibility

Every recipe within allwrite-recipes must provide the visibility:[internal/public] tag. Public recipes will be presented to the user via allwrite ls command.

Friendly names

Every public recipe must provide a friendly name in the form of 2 tags:

  • group:<someGroup>
  • action:<someAction>

For example, the following set of tags [group:workflows, action:introduceSetupGradle] will result in a recipe that can be executed like that:

allwrite run workflows/introduceSetupGradle

Convenient base classes

For convenience, you can extend either AllwriteRecipe or AllwriteScanningRecipe (from the allwrite-spi module). They will build all required tags for you:

class SomeRecipe : AllwriteRecipe(
    displayName = "Some recipe", // optional, defaults to class name
    description = "Some description.", // optional, defaults to displayName + '.'
    visibility = PUBLIC, // optional, defaults to INTERNAL
    group = "some-group", // required if the visibility is PUBLIC
    action = "some-action" // required if the visibility is PUBLIC
) {
    // your implementation
}

Dependabot integration

If your recipe should be triggered automatically when Dependabot bumps a specific dependency, declare dependabotArtifacts:

class SomeMigrationRecipe : AllwriteRecipe(
    visibility = PUBLIC,
    group = "some-group",
    action = "upgrade",
    dependabotArtifacts = listOf("com.example:some-library"),
) {
    // your implementation
}

For declarative YAML recipes, add dependabot-artifact:<coordinates> tags:

tags:
  - visibility:public
  - group:some-group
  - action:upgrade
  - dependabot-artifact:com.example:some-library

When allwrite run-dependabot processes a Dependabot PR that bumps com.example:some-library, it will dynamically match and run all recipes that declare this artifact tag (and match the version range).

Limiting which files should be parsed

[!TIP] It may be very useful for improving performance and overcoming OpenRewrite issues with parsing Groovy files

If your recipe is only interested in very specific files (for example it only modifies the tycho.yaml file) you can implement the ParsingAwareRecipe interface:

class SomeRecipe : AllwriteRecipe(visibility = INTERNAL), ParsingAwareRecipe {

    override fun selectFilesToParse(inputFiles: List<Path>): List<Path> {
        // return the files to be parsed
    }
}

Supplying a recipe classpath

Implement ClasspathAwareRecipe when a recipe needs additional artifacts on the parser classpath:

class SomeRecipe : AllwriteRecipe(visibility = INTERNAL), ClasspathAwareRecipe {

    override fun requireOnClasspath(): List<String> =
        listOf("spring-web-6", "spring-core-6")
}

allwrite resolves the requested classpath entries before parsing. Classpath-aware recipes are executed in isolated phases so recipes requiring different classpaths do not interfere with each other.

Running postprocessing

Implement PostprocessingRecipe when work must run after OpenRewrite changes have been applied:

class SomeRecipe : AllwriteRecipe(visibility = INTERNAL), PostprocessingRecipe {

    override fun postprocess(): PostprocessingResult =
        PostprocessingResult.Success
}

Return PostprocessingResult.Success after successful postprocessing. Return PostprocessingResult.Failure(errorMessage) to fail the recipe execution and report the error.

Working on the documentation

The documentation site is built with MkDocs. From the docs/ directory, create a virtual environment and install the documentation dependencies:

python3 -m venv .venv
./.venv/bin/python -m pip install -r requirements.txt

Build the documentation locally with:

./.venv/bin/python -m mkdocs build --config-file ../mkdocs.yml --strict

To preview the documentation while editing, start the local development server:

./.venv/bin/python -m mkdocs serve --config-file ../mkdocs.yml

Then open http://127.0.0.1:8000/ in a browser. MkDocs automatically rebuilds the site when source files change.