- Never write any comments in code
- Exception: tests must use
// given,// when,// then,// expect, and// andstructural comments to separate test phases (does NOT apply to recipe tests usingbefore/beforeAndAfter/afterRewriteTest patterns) - Update AGENTS.md every time project structure is changed
- ktlint must pass (
./gradlew ktlintCheck); run the full./gradlew checkbefore pushing anything
allwrite is a CLI tool and collection of OpenRewrite recipes for automated code transformations across YAML, Gradle, Java/Kotlin, and Spring projects. It wraps OpenRewrite's runtime into a CLI that can run recipes by name, integrate with GitHub Actions, and work with Dependabot PRs.
| Category | Technology |
|---|---|
| Primary language | Kotlin |
| Secondary language | Java (minimal, in allwrite-recipes) |
| Core framework | OpenRewrite |
| Dependency injection | Koin (with KSP annotation processing) |
| CLI framework | Clikt |
| Logging | SLF4J + Logback + kotlin-logging |
| Serialization | kotlinx-serialization |
| HTTP client | Ktor |
| Markdown rendering | Markout, clikt-markdown, Mordant |
| Build system | Gradle (Kotlin DSL) with composite build |
| Release management | JReleaser + Axion Release Plugin |
| Testing | Kotest (FunSpec), JUnit 5, MockK, OpenRewrite RewriteTest |
The documentation site is built with MkDocs from mkdocs.yml, docs/requirements.txt, and the docs/ directory.
docs/index.mdβ project overviewdocs/cli.mdβ CLI referencedocs/contributing.mdβ recipe authoring guidedocs/recipes/index.mdβ built-in recipes overview and authoring helpersdocs/recipes/{yaml,gradle,java,spring}/β individual built-in recipe pages grouped by category- Every new recipe gets a dedicated page under
docs/recipes/and a matching entry inmkdocs.ymlnavigation. docs/extra.cssβ documentation layout overrides
Multi-module Gradle project using Hexagonal Architecture (Ports & Adapters).
allwrite-cli --> allwrite-runtime --> allwrite-api
| | ^
| v |
| allwrite-recipes -----------+
| |
| v
| allwrite-spi
| ^
+-----> allwrite-completions
(annotation processor)
|
+-----> allwrite-recipes (direct, for recipe classpath)
| Module | Role |
|---|---|
allwrite-api |
Public API layer. Incoming port interfaces (RecipeExecutor, RecipeSource, RecipeCoordinates). Published as a Maven artifact. |
allwrite-spi |
Published SPI for recipe authors. Base classes (AllwriteRecipe, CliAllwriteRecipe, AllwriteScanningRecipe, CliAllwriteScanningRecipe), RecipeMetadata, tag generation (including dependabot-artifact). |
allwrite-recipes |
Pure OpenRewrite recipe implementations. Published as a Maven artifact. Depends on allwrite-api and allwrite-spi. |
allwrite-runtime |
Domain layer. Outgoing port interfaces and OpenRewrite-backed implementations. Depends on allwrite-api. |
allwrite-cli |
Application + Infrastructure layer. CLI commands, OS/GitHub integration, DI wiring. |
allwrite-completions |
Build-time annotation processor for shell completion generation. |
build-logic |
Gradle composite build with convention plugins and custom tasks. |
allwrite/
βββ allwrite-api/
β βββ src/main/kotlin/ Incoming port interfaces (RecipeExecutor, RecipeSource, RecipeCoordinates)
β
βββ allwrite-cli/
β βββ src/main/kotlin/ CLI application (commands, infrastructure adapters)
β β βββ runner/Main.kt Entry point
β β βββ runner/RunnerModule.kt
β β βββ runner/application/ CLI commands (run, ls, external add/update/rm/ls), application logic
β β βββ runner/infrastructure/ OS, GitHub, PR manager adapters (incl. external recipe store)
β β βββ runner/util/ Utility classes
β βββ src/main/resources/ logback.xml
β βββ src/test/kotlin/ Unit tests (Kotest FunSpec)
β βββ src/e2e/kotlin/ End-to-end tests
β
βββ allwrite-runtime/
β βββ src/main/kotlin/ Outgoing ports, recipe executor, source file parser
β βββ src/test/kotlin/ Unit tests (Kotest FunSpec)
β βββ src/testFixtures/kotlin/ Shared test fakes/fixtures
β
βββ allwrite-recipes/
β βββ src/main/kotlin/
β β βββ recipes/yaml/ YAML transformation recipes
β β βββ recipes/spring/ Spring property/annotation and migration recipes, including conditional dependency and custom Spring Boot 4 migration helpers
β β βββ recipes/java/ Java refactoring recipes
β β βββ recipes/gradle/ Gradle dependency recipes
β β β βββ *DependencyRewriter.kt Dedicated helpers for Gradle dependency transforms
β β β βββ AddTomlVersionCatalogPlugin.kt Add and apply version catalog plugins
β β β βββ AddSettingsPluginRepository.kt Add gated repositories to Gradle settings files
β β β βββ AppliedPluginDetector.kt Detect applied Gradle plugins
β β β βββ RemoveLibraryVersionRefs.kt Remove version references from matching libraries
β β β βββ RemoveTomlVersionCatalogLibrary.kt Remove a library from a TOML version catalog
β β βββ recipes/properties/ Properties file recipes
β β βββ recipes/toml/ TOML utilities
β β βββ recipes/util/ Shared recipe utilities
β βββ src/main/resources/META-INF/rewrite/ Declarative YAML recipes
β βββ src/test/kotlin/ Unit tests (JUnit 5 + RewriteTest)
β βββ src/testFixtures/kotlin/ Test fixture classes
β
βββ allwrite-spi/
β βββ src/main/kotlin/ Recipe base classes (AllwriteRecipe, CliAllwriteRecipe, AllwriteScanningRecipe, CliAllwriteScanningRecipe, RecipeMetadata)
β
βββ allwrite-completions/
β βββ src/main/kotlin/ kapt processors + generators
β
βββ build-logic/
β βββ src/main/kotlin/
β βββ conventions/ Convention plugins (kotlin, koin, openrewrite-recipe-library, jreleaser, etc.)
β βββ *.kt Custom Gradle tasks (FetchJdkTask, etc.)
β
βββ gradle/
β βββ libs.versions.toml Central dependency version catalog
β βββ wrapper/
β
βββ .github/workflows/ CI, documentation build/publish, and releases
βββ docs/ MkDocs documentation source, recipe catalog, and dependencies
βββ mkdocs.yml MkDocs site configuration
allwrite-cli/src/main/kotlin/pl/allegro/tech/allwrite/runner/Main.kt
The main() function bootstraps Koin DI, conditionally loads GithubModule when running in GitHub Actions, then delegates to AppEntrypoint.execute(args). AppEntrypoint is implemented by MainCommand (a Clikt command) dispatching to sub-commands.
- Kotlin explicit API mode is enabled β all public declarations require explicit visibility modifiers
.editorconfigenforces: 4-space indentation, 160 char line length, LF line endings, UTF-8- Kotlin style:
ktlint_code_style = intellij_idea, trailing commas required, no star imports - YAML files: 2-space indentation
- Shell scripts: 2-space indentation
- Kotlin files: PascalCase class names matching file names
- Test files:
*Spec.kt(Kotest) or*Test.kt(JUnit) - Packages:
pl.allegro.tech.allwrite.* - Koin modules:
*Module.kt - Incoming port interfaces: in
pl.allegro.tech.allwrite.apipackage (allwrite-apimodule) - Outgoing port interfaces: under
port/outgoing/inallwrite-runtime - Fake test implementations:
Fake*prefix
- Hexagonal Architecture: Incoming ports (
RecipeExecutor,RecipeSource,AppEntrypoint) and outgoing ports (UserProblemReporter,InputFilesProvider,GitMetadata,PullRequestContext,TelemetryPublisher,SystemInfo,ExternalRecipeJarsProvider) - Koin + KSP DI: Modules annotated with
@Moduleand@ComponentScan, services with@Single - Convention Plugins: Shared build logic in
build-logic/(conventions.kotlin,conventions.koin,conventions.recipe-classpaths, etc.) - Template Method:
SubCommandabstract class definesrun()lifecycle; subclasses implementrunSubCommand().ExternalSubCommandextendsSubCommandas a marker for commands nested under theexternalgroup.ExternalCommandis a Clikt group command that collectsExternalSubCommandinstances as subcommands. - Observer/Listener:
CommandListenerinstances notified after each command execution (telemetry) - Recipe Strategy: Recipes can implement
ParsingAwareRecipeorPostprocessingRecipe. Base classes:AllwriteRecipe,CliAllwriteRecipe,AllwriteScanningRecipe,CliAllwriteScanningRecipe - Test Fakes over Mocks: Heavy use of
Fake*implementations for test isolation - Tag-based Recipe Metadata: Custom tag system (
group:*,action:*,from:*,to:*,dependabot-artifact:*) for CLI recipe discovery, version matching, and Dependabot integration - Declarative Recipes: Recipes defined programmatically (Kotlin) or declaratively (YAML under
META-INF/rewrite/)
| Module | Location | Framework | Naming |
|---|---|---|---|
allwrite-cli |
src/test/kotlin/ |
Kotest FunSpec | *Spec.kt |
allwrite-cli |
src/e2e/kotlin/ |
Kotest FunSpec | *IntegrationSpec.kt |
allwrite-runtime |
src/test/kotlin/ |
Kotest FunSpec | *Spec.kt |
allwrite-recipes |
src/test/kotlin/ |
JUnit 5 + RewriteTest | *Test.kt |
BaseRunnerSpecβ abstract base for runner tests, sets up Koin DI with faked modulesBaseRuntimeSpecβ abstract base for runtime tests- Test fixtures shared via
testFixturessource set - Recipe tests use OpenRewrite's
RewriteTestinterface for before/after source assertions
| Command | Description |
|---|---|
./gradlew test |
Run unit tests across all modules |
./gradlew build |
Full build including JReleaser assembly |
./gradlew :allwrite-cli:run --args "<args>" |
Run the CLI locally |
./gradlew :allwrite-cli:e2e |
Run end-to-end tests |
./gradlew check |
Run tests + e2e |
Core: openrewrite (rewrite-core, rewrite-java, rewrite-java-11, rewrite-java-17, rewrite-java-21, rewrite-kotlin, rewrite-yaml, rewrite-toml, rewrite-properties, rewrite-gradle, rewrite-groovy), openrewrite-recipe (rewrite-spring, rewrite-static-analysis), koin-core, koin-annotations, koin-ksp-compiler, clikt, clikt-markdown, markout, markout-markdown, kotlin-reflect, kotlinx-serialization-json, kotlinx-datetime
Infrastructure: ktor-client-core, ktor-client-cio, ktor-client-content-negotiation, ktor-serialization-kotlinx-json, github-client (Spotify), java-semver, snakeyaml, logback-classic, kotlin-logging, slf4j-api
Build-only: kotlinpoet-jvm, mustache-java, auto-service, commons-compress, pgpainless-core, jackson-module-kotlin
Testing: kotest-runner-junit5-jvm, kotest-assertions-core-jvm, kotest-framework-datatest, kotest-extensions-koin, mockk, rewrite-test, junit-jupiter, assertj-core