rewrite-yaml

Writing OpenRewrite YAML Recipes

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "rewrite-yaml" with this command: npx skills add sjungling/claude-plugins/sjungling-claude-plugins-rewrite-yaml

Writing OpenRewrite YAML Recipes

Overview

Create production-quality OpenRewrite recipes for YAML manipulation using test-first development.

Core principle: Write tests first (RED), implement minimally (GREEN), apply OpenRewrite idioms (REFACTOR).

When to Use

  • User asks to create/modify OpenRewrite recipes for YAML

  • Need to manipulate YAML files (GitHub Actions, K8s manifests, CI configs, generic YAML)

  • Building search or transformation recipes for YAML structures

When NOT to Use This Skill

Do not use this skill for:

  • General YAML editing (use standard Edit tools)

  • Non-OpenRewrite transformation tools

  • Java versions above 8 (skill specializes in Java 8 compatibility)

  • Generic code refactoring outside OpenRewrite ecosystem

Critical Constraints

JAVA 8 COMPATIBILITY ONLY: Use traditional if-else, switch statements, explicit casting. NO switch expressions, pattern matching, var , text blocks, or Java 9+ features.

LICENSE HEADERS: Always check for {repository_root}/gradle/licenseHeader.txt when creating new recipe files. If this file exists, include its contents as the license header at the top of the generated Java recipe file. Remember to substitute ${year} with the current year (2025 or later as appropriate).

The Workflow

Phase 1: RED - Write Failing Tests

Write test cases with before/after YAML examples using OpenRewrite's testing framework.

Phase 2: Architecture Decision

Choose declarative (YAML composition) vs imperative (Java Visitor) approach.

Phase 3: GREEN - Minimal Implementation

Implement just enough to make tests pass.

Phase 4: REFACTOR - Apply OpenRewrite Idioms

Improve recipe using traits, composition, and conventions.

Phase 5: Documentation

Add displayName, description (with markdown), and usage examples.

Phase 1: RED - Write Failing Tests

Test Structure

Use OpenRewrite's testing framework with before/after YAML. For detailed test patterns and examples, see ./references/testing-patterns.md .

Checklist

  • Write happy path test (simplest transformation)

  • Include edge cases (empty files, missing keys, null values)

  • Test no-op scenarios (recipe shouldn't change unrelated YAML)

  • Consider multi-document YAML if relevant

  • Include real-world examples if domain is known

Verification

Run tests to confirm RED state - tests must fail initially.

Key principle: Start with simplest possible before/after. Add complexity incrementally.

Phase 2: Architecture Decision - Declarative vs Imperative

Decision Framework

Start with: Can this be done by composing existing recipes?

  • YES → Use declarative YAML recipe

  • NO → Need imperative Java recipe with Visitor

Declarative Recipe (YAML Composition)

Create YAML file in src/main/resources/META-INF/rewrite/ :


type: specs.openrewrite.org/v1beta/recipe name: com.example.MyComposedRecipe displayName: My composed recipe description: Composes existing recipes recipeList:

  • org.openrewrite.yaml.search.FindKey: keyPath: $.some.path
  • org.openrewrite.yaml.ChangeValue: keyPath: $.some.path value: newValue

When to use declarative:

  • Simple transformations using existing recipes

  • Searching for patterns

  • Standard modifications (change values, delete keys)

  • Composing multiple existing recipes

Common declarative recipes:

  • org.openrewrite.yaml.search.FindKey , FindValue

  • searching

  • org.openrewrite.yaml.ChangeKey , ChangeValue , DeleteKey

  • modifications

  • org.openrewrite.yaml.MergeYaml , CopyValue

  • additions

Imperative Recipe (Java Visitor)

When to use imperative:

  • Complex logic or conditional transformations

  • Need to traverse YAML LST structure

  • Creating new YAML structures dynamically

  • Custom matching beyond JsonPath capabilities

Key principle: Always try declarative first. Only go imperative when you hit limitations.

Phase 3: GREEN - Minimal Implementation

For Declarative Recipes

  • Create YAML file in src/main/resources/META-INF/rewrite/

  • Compose existing org.openrewrite.yaml.* recipes

  • Run tests to verify GREEN state

For common recipe patterns, see ./references/recipe-patterns.md .

For Imperative Recipes (Java Visitor)

Extend YamlIsoVisitor<ExecutionContext> (when not changing tree structure) or YamlVisitor<ExecutionContext> (when structure may change).

For complete recipe templates and examples, see ./references/recipe-template.java .

Automation: Use ./scripts/init_recipe.py <RecipeName> to generate recipe boilerplate (class, test file, YAML declarative option).

Verification

Run tests to achieve GREEN state - all tests must pass with formatting preserved.

Phase 4: REFACTOR - Apply OpenRewrite Idioms

Checklist for Idiomatic Recipes

  1. Trait Usage
  • Can this recipe implement an existing trait?

  • Should a new trait be created for reusable matching logic?

  • Separate "what to find" (trait) from "what to do" (recipe)

  1. Recipe Composition
  • Can parts be extracted into smaller, composable recipes?

  • Are there opportunities for configurability (parameters)?

  • Could this be split into search recipe + modification recipe?

  1. OpenRewrite Conventions
  • Recipe has clear displayName and description (both support markdown)

  • Parameters use @Option annotations with descriptions

  • Recipe class is Java 8 compatible (run ./scripts/validate_java8.py src/ to check)

  • Properly handles null values and missing elements

  • Preserves formatting and comments where possible

  1. Performance Considerations
  • Minimize LST traversals (don't visit more than necessary)

  • Use preconditions to skip files that won't match

  • Return original object if no changes made (identity check)

Verification

  • All tests still pass after refactoring

  • Recipe follows OpenRewrite naming conventions

  • Code is cleaner and more maintainable

Phase 5: Documentation

Documentation Requirements

Recipe metadata (supports markdown):

  • displayName : User-friendly name with markdown formatting

  • description : Detailed explanation with code examples, lists, links

  • @Option descriptions: Clear explanations with inline code examples

Example with markdown:

@Override public String getDisplayName() { return "Update GitHub Actions to actions/checkout@v4"; }

@Override public String getDescription() { return "Updates all uses of actions/checkout@v2 and actions/checkout@v3 to actions/checkout@v4.\n\n" + "Before:\nyaml\n- uses: actions/checkout@v2\n\n\n" + "After:\nyaml\n- uses: actions/checkout@v4\n"; }

Usage examples:

  • Add Javadoc with common use cases

  • Show before/after transformations

  • Document parameter effects

Technical Reference

YAML LST Structure

Understanding the YAML LST hierarchy is essential. For detailed structure documentation, see ./references/yaml-lst-reference.md .

Key concepts:

  • Yaml.Documents → Yaml.Document → Yaml.Mapping → Yaml.Mapping.Entry

  • Yaml.Sequence → Yaml.Sequence.Entry

  • Yaml.Scalar (primitive values)

Essential Patterns

For complete recipe templates and patterns, see:

  • ./references/recipe-template.java

  • Complete recipe structure with annotations

  • ./references/recipe-patterns.md

  • Search, replacement, and modification patterns

  • ./references/jsonpath-patterns.md

  • Common JsonPath patterns for GitHub Actions, K8s, and generic YAML

Quick Reference

Key matching:

if ("targetKey".equals(entry.getKey().getValue())) { /* match */ }

Safe value access:

String value = entry.getValue() instanceof Yaml.Scalar ? ((Yaml.Scalar) entry.getValue()).getValue() : null;

JsonPath matching:

JsonPathMatcher matcher = new JsonPathMatcher("$.jobs..steps[].uses"); if (matcher.matches(getCursor())) { /* process */ }

For more JsonPath patterns, search ./references/jsonpath-patterns.md for your use case.

Recipe Development Rules

  • Always call super methods: return super.visitMappingEntry(entry, ctx);

  • Return modified copies - never mutate LST elements directly

  • Use withX() methods for all modifications

  • Handle null cases with conditional expressions

  • Preserve formatting - LST methods maintain it automatically

  • Java 8 only - no modern Java features

OpenRewrite Traits (Advanced)

For complex recipes that benefit from higher-level semantic abstractions, OpenRewrite Traits provide domain-specific logic by wrapping LST elements.

For detailed information about Traits, refer to the companion guide: ./references/openrewrite-traits-guide.md .

Quick Traits Overview:

  • Traits implement Trait<T extends Tree> interface

  • Include a nested Matcher class extending SimpleTraitMatcher<T>

  • Use matcher.asVisitor() to convert to TreeVisitor in recipes

  • Provide semantic methods like getActionRef() instead of raw LST navigation

Example using Traits:

@Override public TreeVisitor<?, ExecutionContext> getVisitor() { return new ActionStep.Matcher().asVisitor((step, ctx) -> { String ref = step.getActionRef(); if (ref != null && ref.contains("@v2")) { return step.withActionRef(ref.replace("@v2", "@v3")).getTree(); } return step.getTree(); }); }

For complete trait implementation patterns, matcher API details, and advanced examples, consult ./references/openrewrite-traits-guide.md .

Final Validation

Production-Ready Checklist

  • All tests pass (GREEN state maintained)

  • Recipe follows OpenRewrite naming conventions (com.yourorg.RecipeName )

  • No hardcoded values that should be parameters

  • Recipe handles edge cases gracefully (no NPEs)

  • Code is Java 8 compatible (verify with ./scripts/validate_java8.py )

  • Formatting/comments preserved in YAML output

  • Documentation is clear and includes examples

  • Recipe could be contributed to OpenRewrite or org recipe library

  • License header included if gradle/licenseHeader.txt exists (use ./scripts/add_license_header.sh )

Success Criteria

Recipe is production-ready when:

  • Tests comprehensively cover happy path and edge cases

  • Implementation follows OpenRewrite idioms (traits, composition)

  • Documentation enables users to understand and use the recipe

  • Code quality meets standards for upstream contribution

Recommended Approach

Follow this workflow when creating OpenRewrite recipes:

  • Phase 1 (RED) - Write failing tests with before/after YAML

  • Phase 2 (DECIDE) - Choose declarative vs imperative approach

  • Phase 3 (GREEN) - Implement minimal working solution

  • Phase 4 (REFACTOR) - Apply OpenRewrite idioms (traits, composition)

  • Phase 5 (DOCUMENT) - Add markdown documentation and examples

  • Validate - Confirm production readiness with checklist

Always provide complete, working recipes with proper annotations, error handling, and clear comments explaining the logic.

Remember

  • Always start with tests (RED)

  • Try declarative before imperative

  • Apply idioms during REFACTOR, not GREEN

  • Document with markdown for clarity

  • Validate production-readiness before completion

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

ios-swift-expert

No summary provided by upstream source.

Repository SourceNeeds Review
General

git-bisect-debugging

No summary provided by upstream source.

Repository SourceNeeds Review
General

obsidian-vault-manager

No summary provided by upstream source.

Repository SourceNeeds Review
General

technical-writer

No summary provided by upstream source.

Repository SourceNeeds Review