Custom OpenRewrite recipes by Anacoders.
Creates YAML files in multiple directories matching a wildcard pattern. Files are only created if they don't already exist.
Create a config.yaml file in every subdirectory under projects/:
---
type: specs.openrewrite.org/v1beta/recipe
name: com.yourorg.CreateProjectConfigs
displayName: Create config files in all projects
recipeList:
- com.anacoders.cookbook.yaml.CreateYamlFilesByPattern:
filePattern: projects/*/config.yaml
fileContents: |
apiVersion: v1
kind: Config
metadata:
name: example
overwriteExisting: false # set to true to replace existing files| Pattern | Description | Creates Files In |
|---|---|---|
projects/*/config.yaml |
Single wildcard * |
Each direct subdirectory of projects/ |
apps/*/deployment.yaml |
Works at any depth | Each subdirectory under apps/ |
src/main/*/application.yaml |
Nested paths | Each subdirectory under src/main/ |
apps/*/config/*/settings.yaml |
Multiple wildcards | Matching nested paths |
src/**/config.yaml |
Recursive ** |
Any depth under src/ |
| Option | Description | Example |
|---|---|---|
filePattern |
Pattern with wildcards for where to create files | projects/*/config.yaml |
fileContents |
The YAML content to write to each created file | apiVersion: v1 |
overwriteExisting |
If true, overwrite existing files with new contents (default: false) |
true |
- ✅ Creates files only in directories that match the pattern
- ✅ Skips creation if the file already exists (use
overwriteExisting: trueto override) - ✅ Works with any directory depth and nesting
- ✅ Supports multiple
*wildcards in a single pattern
Change a YAML property, optionally only when specified conditions are met. Expects dot notation for nested YAML mappings. Based on org.openrewrite.yaml.ChangePropertyValue with added support for conditional execution.
Update spec.replicas only in Deployments in the production environment:
---
type: specs.openrewrite.org/v1beta/recipe
name: com.yourorg.ScaleProductionDeployments
displayName: Scale production deployments
recipeList:
- com.anacoders.cookbook.yaml.ChangeYamlPropertyConditionally:
propertyKey: spec.replicas
newValue: "5"
conditions:
- key: kind
value: Deployment
- key: metadata.labels.environment
value: productionUpdate the version in a path while preserving prefix and suffix using capture groups, only for Deployments:
---
type: specs.openrewrite.org/v1beta/recipe
name: com.yourorg.UpdateVersion
displayName: Update version in path
recipeList:
- com.anacoders.cookbook.yaml.ChangeYamlPropertyConditionally:
propertyKey: spec.path
oldValue: "(.*/)v[0-9]+\\.[0-9]+\\.[0-9]+(/.*)"
newValue: "$1v2.0.0$2"
regex: true
conditions:
- key: kind
value: DeploymentThis transforms /apps/myapp/v1.2.3/config/settings.yaml into /apps/myapp/v2.0.0/config/settings.yaml.
| Option | Description | Example |
|---|---|---|
propertyKey |
The key to look for (dot notation). Supports glob patterns. | spec.replicas |
newValue |
The new value to set | 3 |
oldValue |
Only change if current value matches (exact match, or regex if regex: true) |
1 |
regex |
If true, interpret oldValue as a regex pattern (default: false) |
true |
relaxedBinding |
Use relaxed binding rules for matching (default: true) |
false |
conditions |
List of conditions that must ALL match (AND logic) for change to occur | See examples |
conditions[].key |
Property key to check (dot notation) | kind |
conditions[].value |
Value that the property must have | Deployment |
filePattern |
Optional glob to filter files | **/k8s/**/*.yaml |
- ✅ Uses dot notation for nested properties (e.g.,
spec.replicas) - ✅ Supports glob patterns in property key (e.g.,
spec.*.replicas) - ✅ Regex-based replacement with capture groups (
$1,$2, etc.) whenregex: true - ✅ Multiple conditions with AND logic (all must match)
- ✅ Conditions evaluated per YAML document (supports multi-document files)
- ✅ No change if target value already matches
- ✅ No change if
oldValuedoesn't match current value - ✅ Optional file pattern filtering
- ✅ Supports relaxed binding (kebab-case, camelCase matching)
Copy a property out of a YAML document that is embedded in a scalar, such as a Flux Kustomization patch block scalar, into a property of the enclosing document. Stock YAML recipes see the text inside a patch: block as one string; this recipe parses that text as YAML and reads a value out of it.
Move the ingress host from a leaf patch into a postBuild.substitute variable:
---
type: specs.openrewrite.org/v1beta/recipe
name: com.yourorg.ExtractIngressHost
displayName: Extract ingress host
recipeList:
- com.anacoders.cookbook.yaml.CopyEmbeddedYamlValue:
sourceKeyPath: $.spec.patches[*].patch
sourceProperty: spec.values.ingress.host
propertyKey: spec.postBuild.substitute.ingressHostThis transforms:
spec:
postBuild:
substitute:
environment: "production"
patches:
- patch: |
spec:
values:
ingress:
host: app.example.com
target:
kind: HelmReleaseInto:
spec:
postBuild:
substitute:
environment: "production"
ingressHost: "app.example.com"
patches:
- patch: |
spec:
values:
ingress:
host: app.example.com
target:
kind: HelmRelease| Option | Description | Example |
|---|---|---|
sourceKeyPath |
JsonPath matching the mapping value(s) whose text is a YAML document. A filter on the value's own text works, e.g. $.spec.patches[?(@.patch =~ '(?s).*ingress:.*')].patch; filters on sibling keys such as @.target.kind are not supported by OpenRewrite's JsonPath matcher |
$.spec.patches[*].patch |
sourceProperty |
Property to read inside the embedded document (dot notation, exact case-sensitive match; list items are transparent, so a.b.c also matches c inside items of list b; keys containing dots cannot be addressed) |
spec.values.ingress.host |
propertyKey |
Property of the enclosing document to write (dot notation) | spec.postBuild.substitute.ingressHost |
filePattern |
Optional glob to filter files | **/*.yaml |
- ✅ Uses the first matching value whose embedded document holds
sourcePropertyas a plain, single-quoted or double-quoted scalar; block scalars are skipped - ✅ Creates missing parent keys; replaces an existing scalar value. Leaves the document unchanged if the target or one of its parents is a mapping, list or flow mapping
- ✅ Writes the value as a double-quoted string, so
trueor3stay strings (as Fluxsubstituterequires) - ✅ Evaluated per YAML document (supports multi-document files)
- ✅ No change when the target already holds the value as a double-quoted string (a plain or single-quoted equal value is re-quoted)
- ✅ No change when no value holds the property, or the embedded text is not a YAML mapping; a value that is not valid YAML is skipped silently
- ✅ Optional file pattern filtering
Change an HCL attribute value based on comment-based conditions. Useful for updating Terraform configurations conditionally.
Update version only in modules marked with a # release-channel: stable comment:
---
type: specs.openrewrite.org/v1beta/recipe
name: com.yourorg.UpdateStableModules
displayName: Update stable module versions
recipeList:
- com.anacoders.cookbook.hcl.ChangeHclAttributeConditionally:
attributeName: version
newValue: "2.0.0"
commentConditions:
- pattern: "# release-channel:"
value: stableThis transforms:
module "app" {
# release-channel: stable
source = "registry.example.com/modules/app"
version = "1.0.0"
}Into:
module "app" {
# release-channel: stable
source = "registry.example.com/modules/app"
version = "2.0.0"
}Update the minor version while preserving the patch version:
---
type: specs.openrewrite.org/v1beta/recipe
name: com.yourorg.BumpMinorVersion
displayName: Bump minor version
recipeList:
- com.anacoders.cookbook.hcl.ChangeHclAttributeConditionally:
attributeName: version
oldValue: "1\\.0\\.(\\d+)"
newValue: "2.0.$1"
regex: true
commentConditions:
- pattern: "# release-channel:"
value: stable| Option | Description | Example |
|---|---|---|
attributeName |
The HCL attribute name to update | version |
newValue |
The new value to set | 2.0.0 |
oldValue |
Only change if current value matches (exact match, or regex if regex: true) |
1.0.0 |
regex |
If true, interpret oldValue as a regex pattern (default: false) |
true |
commentConditions |
List of comment conditions that must ALL match (AND logic) | See examples |
commentConditions[].pattern |
Comment text pattern to match (include # prefix) |
# release-channel: |
commentConditions[].value |
Value that must appear after the pattern | stable |
filePattern |
Optional glob to filter files | **/*.tf |
- ✅ Updates HCL attribute values in Terraform files
- ✅ Comment-based conditions check comments within the containing block
- ✅ Multiple conditions with AND logic (all must match)
- ✅ Regex-based replacement with capture groups (
$1,$2, etc.) whenregex: true - ✅ No change if target value already matches
- ✅ No change if
oldValuedoesn't match current value - ✅ Optional file pattern filtering
- ✅ Preserves quoting style (quoted strings stay quoted)
Add to your build.gradle.kts or build.gradle:
plugins {
id("org.openrewrite.rewrite") version("latest.release")
}
repositories {
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
dependencies {
// Use a specific release version (recommended)
rewrite("com.github.rhart:openrewrite-cookbook:v0.6.0")
// Or use the latest commit from main branch (snapshot)
// rewrite("com.github.rhart:openrewrite-cookbook:main-SNAPSHOT")
// Or use a specific commit hash
// rewrite("com.github.rhart:openrewrite-cookbook:5811e78")
}Then run:
./gradlew rewriteRunYou can also use the recipes directly in code:
import com.anacoders.cookbook.yaml.CreateYamlFilesByPattern;
import com.anacoders.cookbook.yaml.ChangeYamlPropertyConditionally;
import com.anacoders.cookbook.yaml.CopyEmbeddedYamlValue;
import com.anacoders.cookbook.hcl.ChangeHclAttributeConditionally;
import java.util.List;
var createRecipe = new CreateYamlFilesByPattern(
"projects/*/config.yaml",
"apiVersion: v1\nkind: Config",
null // overwriteExisting — set to true to replace existing files
);
// Simple replacement
var changeRecipe = new ChangeYamlPropertyConditionally(
"spec.replicas", // propertyKey
"3", // newValue
null, // oldValue
null, // regex
null, // relaxedBinding
null, // conditions
null // filePattern
);
// Conditional replacement
var conditionalRecipe = new ChangeYamlPropertyConditionally(
"spec.replicas",
"5",
null,
null,
null,
List.of(
new ChangeYamlPropertyConditionally.Condition("kind", "Deployment"),
new ChangeYamlPropertyConditionally.Condition("metadata.labels.environment", "production")
),
null
);
// Regex replacement with capture groups
var regexRecipe = new ChangeYamlPropertyConditionally(
"spec.path", // propertyKey
"$1v2.0.0$2", // newValue
"(.*/)v[0-9]+\\.[0-9]+\\.[0-9]+(/.*)", // oldValue (regex)
true, // regex
null, // relaxedBinding
null, // conditions
"**/k8s/**/*.yaml" // filePattern
);
// Copy a value out of an embedded YAML document
var copyRecipe = new CopyEmbeddedYamlValue(
"$.spec.patches[*].patch", // sourceKeyPath
"spec.values.ingress.host", // sourceProperty
"spec.postBuild.substitute.ingressHost", // propertyKey
"**/*.yaml" // filePattern
);
// HCL attribute change with comment conditions
var hclRecipe = new ChangeHclAttributeConditionally(
"version", // attributeName
"2.0.0", // newValue
null, // oldValue
null, // regex
List.of(
new ChangeHclAttributeConditionally.CommentCondition(
"# release-channel:",
"stable"
)
),
"**/*.tf" // filePattern
);Build and test:
./gradlew build testTest locally in another project:
./gradlew publishToMavenLocalThen in your test project, use mavenLocal() in repositories and reference:
rewrite("com.anacoders:openrewrite-cookbook:latest.integration")Apache License 2.0
This project uses JitPack for distribution - no manual publishing needed!
-
Tag a version:
git tag v0.1.0 git push origin v0.1.0
-
Create a GitHub Release (optional but recommended):
- Go to your GitHub repository
- Click "Releases" → "Create a new release"
- Select your tag (e.g.,
v0.1.0) - Add release notes describing changes
- Publish the release
-
JitPack builds automatically!
- Visit https://jitpack.io/#rhart/openrewrite-cookbook
- Your release will be available within a few minutes
- Users can then add it as a dependency
For testing unreleased features, users can depend on a specific commit:
- Get the commit hash from GitHub (short form, first 7-10 characters)
- Use it as the version:
rewrite("com.github.rhart:openrewrite-cookbook:abc1234")
Note: JitPack will build the commit on first request, which may take a few minutes. Check the build status at https://jitpack.io/#rhart/openrewrite-cookbook