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.