From 05812856f5f0b3fb20bb298885e0ce89e458fbd8 Mon Sep 17 00:00:00 2001 From: Steven Tompkins Date: Fri, 3 Apr 2026 11:08:31 -0500 Subject: [PATCH 1/2] Detect indentation from existing YAML in MigrateToRules MigrateToRules hardcoded 2-space indent increments when building the replacement rules block, producing mixed indentation on files using 4-space or other indent widths. Detect the actual indent unit by comparing the only/except entry prefix with its first sequence child prefix, falling back to 2-space when detection is not possible. --- .../openrewrite/gitlab/MigrateToRules.java | 21 +++++- .../gitlab/MigrateToRulesTest.java | 74 +++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/gitlab/MigrateToRules.java b/src/main/java/org/openrewrite/gitlab/MigrateToRules.java index 60a0151..671b0ce 100644 --- a/src/main/java/org/openrewrite/gitlab/MigrateToRules.java +++ b/src/main/java/org/openrewrite/gitlab/MigrateToRules.java @@ -99,7 +99,8 @@ private static Yaml.Mapping migrateWithOnly(Yaml.Mapping m, Yaml.Mapping.Entry o String entryPrefix = onlyEntry.getPrefix(); String baseIndent = entryPrefix.contains("\n") ? entryPrefix.substring(entryPrefix.lastIndexOf('\n') + 1) : " "; - String seqIndent = baseIndent + " "; + String indentUnit = detectIndentUnit(baseIndent, (Yaml.Sequence) onlyEntry.getValue()); + String seqIndent = baseIndent + indentUnit; String contentIndent = seqIndent + " "; StringBuilder sb = new StringBuilder("rules:"); @@ -147,7 +148,8 @@ private static Yaml.Mapping migrateExceptOnly(Yaml.Mapping m, Yaml.Mapping.Entry String entryPrefix = exceptEntry.getPrefix(); String baseIndent = entryPrefix.contains("\n") ? entryPrefix.substring(entryPrefix.lastIndexOf('\n') + 1) : " "; - String seqIndent = baseIndent + " "; + String indentUnit = detectIndentUnit(baseIndent, (Yaml.Sequence) exceptEntry.getValue()); + String seqIndent = baseIndent + indentUnit; String contentIndent = seqIndent + " "; StringBuilder sb = new StringBuilder("rules:"); @@ -190,6 +192,21 @@ private static Yaml.Mapping migrateExceptOnly(Yaml.Mapping m, Yaml.Mapping.Entry .orElse(null); } + private static String detectIndentUnit(String baseIndent, Yaml.Sequence seq) { + List entries = seq.getEntries(); + if (entries.isEmpty()) { + return " "; + } + String childPrefix = entries.get(0).getPrefix(); + String childIndent = childPrefix.contains("\n") ? + childPrefix.substring(childPrefix.lastIndexOf('\n') + 1) : ""; + int increment = childIndent.length() - baseIndent.length(); + if (increment <= 0) { + return " "; + } + return childIndent.substring(baseIndent.length()); + } + static String refToCondition(String ref) { switch (ref) { case "branches": diff --git a/src/test/java/org/openrewrite/gitlab/MigrateToRulesTest.java b/src/test/java/org/openrewrite/gitlab/MigrateToRulesTest.java index ffcddae..06d823e 100644 --- a/src/test/java/org/openrewrite/gitlab/MigrateToRulesTest.java +++ b/src/test/java/org/openrewrite/gitlab/MigrateToRulesTest.java @@ -348,6 +348,80 @@ void multipleJobs() { ); } + @Test + void migrateFourSpaceIndentOnly() { + rewriteRun( + //language=yaml + yaml( + """ + build_job: + script: make build + only: + - main + - tags + """, + """ + build_job: + script: make build + rules: + - if: $CI_COMMIT_BRANCH == 'main' + - if: $CI_COMMIT_TAG + """, + source -> source.path(".gitlab-ci.yml") + ) + ); + } + + @Test + void migrateFourSpaceIndentExcept() { + rewriteRun( + //language=yaml + yaml( + """ + build_job: + script: make build + except: + - tags + """, + """ + build_job: + script: make build + rules: + - if: $CI_COMMIT_TAG + when: never + - when: always + """, + source -> source.path(".gitlab-ci.yml") + ) + ); + } + + @Test + void migrateFourSpaceIndentCombined() { + rewriteRun( + //language=yaml + yaml( + """ + build_job: + script: make build + only: + - branches + except: + - main + """, + """ + build_job: + script: make build + rules: + - if: $CI_COMMIT_BRANCH == 'main' + when: never + - if: $CI_COMMIT_BRANCH + """, + source -> source.path(".gitlab-ci.yml") + ) + ); + } + @Test void noopForNonGitlabCiFiles() { rewriteRun( From 9e6bb9560f2b2a5af1d43936c66401e809875389 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 17 Apr 2026 11:09:59 +0200 Subject: [PATCH 2/2] Use AutoFormatVisitor for indentation in MigrateToRules Replace manual indent detection with `AutoFormatVisitor` so the YAML library re-indents the inserted `rules` block to match the document's actual indent style. --- .../openrewrite/gitlab/MigrateToRules.java | 57 +++++-------------- 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/src/main/java/org/openrewrite/gitlab/MigrateToRules.java b/src/main/java/org/openrewrite/gitlab/MigrateToRules.java index 671b0ce..ebd9c0a 100644 --- a/src/main/java/org/openrewrite/gitlab/MigrateToRules.java +++ b/src/main/java/org/openrewrite/gitlab/MigrateToRules.java @@ -22,6 +22,7 @@ import org.openrewrite.internal.ListUtils; import org.openrewrite.yaml.YamlIsoVisitor; import org.openrewrite.yaml.YamlParser; +import org.openrewrite.yaml.format.AutoFormatVisitor; import org.openrewrite.yaml.tree.Yaml; import java.util.ArrayList; @@ -65,10 +66,13 @@ public Yaml.Mapping visitMapping(Yaml.Mapping mapping, ExecutionContext ctx) { return m; } - if (onlyEntry != null) { - return migrateWithOnly(m, onlyEntry, exceptEntry); + Yaml.Mapping result = onlyEntry != null ? + migrateWithOnly(m, onlyEntry, exceptEntry) : + migrateExceptOnly(m, exceptEntry); + if (result != m) { + doAfterVisit(new AutoFormatVisitor<>(null)); } - return migrateExceptOnly(m, exceptEntry); + return result; } } ); @@ -96,26 +100,17 @@ private static Yaml.Mapping migrateWithOnly(Yaml.Mapping m, Yaml.Mapping.Entry o } } - String entryPrefix = onlyEntry.getPrefix(); - String baseIndent = entryPrefix.contains("\n") ? - entryPrefix.substring(entryPrefix.lastIndexOf('\n') + 1) : " "; - String indentUnit = detectIndentUnit(baseIndent, (Yaml.Sequence) onlyEntry.getValue()); - String seqIndent = baseIndent + indentUnit; - String contentIndent = seqIndent + " "; - + // Build with deeply-indented sequence entries so Autodetect picks indented-sequence + // style; AutoFormatVisitor re-indents to the document's actual indent width. StringBuilder sb = new StringBuilder("rules:"); - - // Except refs come first as 'when: never' if (exceptRefs != null && !exceptRefs.isEmpty()) { for (String ref : exceptRefs) { - sb.append("\n").append(seqIndent).append("- if: ").append(refToCondition(ref)); - sb.append("\n").append(contentIndent).append("when: never"); + sb.append("\n - if: ").append(refToCondition(ref)); + sb.append("\n when: never"); } } - - // Only refs as positive rules for (String ref : onlyRefs) { - sb.append("\n").append(seqIndent).append("- if: ").append(refToCondition(ref)); + sb.append("\n - if: ").append(refToCondition(ref)); } Yaml.Mapping.Entry rulesEntry = parseRulesEntry(sb.toString(), onlyEntry.getPrefix()); @@ -145,19 +140,12 @@ private static Yaml.Mapping migrateExceptOnly(Yaml.Mapping m, Yaml.Mapping.Entry return m; } - String entryPrefix = exceptEntry.getPrefix(); - String baseIndent = entryPrefix.contains("\n") ? - entryPrefix.substring(entryPrefix.lastIndexOf('\n') + 1) : " "; - String indentUnit = detectIndentUnit(baseIndent, (Yaml.Sequence) exceptEntry.getValue()); - String seqIndent = baseIndent + indentUnit; - String contentIndent = seqIndent + " "; - StringBuilder sb = new StringBuilder("rules:"); for (String ref : refs) { - sb.append("\n").append(seqIndent).append("- if: ").append(refToCondition(ref)); - sb.append("\n").append(contentIndent).append("when: never"); + sb.append("\n - if: ").append(refToCondition(ref)); + sb.append("\n when: never"); } - sb.append("\n").append(seqIndent).append("- when: always"); + sb.append("\n - when: always"); Yaml.Mapping.Entry rulesEntry = parseRulesEntry(sb.toString(), exceptEntry.getPrefix()); if (rulesEntry == null) { @@ -192,21 +180,6 @@ private static Yaml.Mapping migrateExceptOnly(Yaml.Mapping m, Yaml.Mapping.Entry .orElse(null); } - private static String detectIndentUnit(String baseIndent, Yaml.Sequence seq) { - List entries = seq.getEntries(); - if (entries.isEmpty()) { - return " "; - } - String childPrefix = entries.get(0).getPrefix(); - String childIndent = childPrefix.contains("\n") ? - childPrefix.substring(childPrefix.lastIndexOf('\n') + 1) : ""; - int increment = childIndent.length() - baseIndent.length(); - if (increment <= 0) { - return " "; - } - return childIndent.substring(baseIndent.length()); - } - static String refToCondition(String ref) { switch (ref) { case "branches":