Skip to content

chore: replace jline:jansi-core with internal Ansi builder#682

Merged
david-waltermire merged 4 commits intometaschema-framework:developfrom
david-waltermire:chore/remove-jansi-dep
Apr 19, 2026
Merged

chore: replace jline:jansi-core with internal Ansi builder#682
david-waltermire merged 4 commits intometaschema-framework:developfrom
david-waltermire:chore/remove-jansi-dep

Conversation

@david-waltermire
Copy link
Copy Markdown
Contributor

@david-waltermire david-waltermire commented Apr 19, 2026

Summary

Replaces `org.jline:jansi-core` with a small internal ANSI escape-code builder (`dev.metaschema.cli.processor.ansi.Ansi`) and removes the dependency. Motivated by dependabot PR #677 (jline 4.0.0), which is a major breaking upgrade (Maven 4 required, complete JPMS migration, JNA provider removed). The CLI's use of jansi is limited and well-isolated, so replacing it with maintained in-tree code is lower-risk than the 4.0.0 migration and eliminates the dependency chain entirely.

Changes

New code (`cli-processor/.../ansi/`):

  • `Ansi.java` — fluent builder covering the subset used: `a(char)`, `a(CharSequence)`, `format()`, `reset()`, `bold()`, `boldOff()`, `fgRed()`, `fgBrightRed()`, `fgBrightYellow()`, `fgBrightBlue()`, `fgBrightCyan()`, `fgBright(Color)`, plus a global `setEnabled(boolean)` toggle.
  • `AnsiTest.java` — 22 unit tests covering each code, chaining, format, and the disabled-mode path.

Migrated call sites (import-only except as noted):

  • `cli-processor/CLIProcessor` — version banner + `--no-color` toggle.
  • `cli-processor/CallingContext` — sub-command list rendering (jline's `render("@|bold ...|@ ...")` replaced with explicit `.bold().format(...).boldOff()` calls).
  • `metaschema-cli/LoggingValidationHandler` — severity-colored log preambles and findings.

Dependency removal:

  • Parent `pom.xml`: dropped `dependency.jline.version` property and `dependencyManagement` entry.
  • `cli-processor/pom.xml`: dropped `jansi-core` dependency.
  • `cli-processor/module-info.java`: dropped `requires org.jansi.core`; exports new `dev.metaschema.cli.processor.ansi` package.

Follow-up

After merge, close dependabot PR #677 (jline 4.0.0) — no longer applicable.

Test Plan

  • `mvn -pl cli-processor test -Dtest=AnsiTest` — 22/22 pass.
  • `mvn -pl cli-processor,metaschema-cli -am install -DskipTests` — builds clean.
  • Full CI build.

Summary by CodeRabbit

  • Refactor

    • Terminal output formatting and styling now uses an internal ANSI implementation while preserving appearance and behavior.
  • Chores

    • Removed an external ANSI library dependency to simplify the project and reduce external footprint.
  • Tests

    • Added tests covering ANSI styling, chaining, formatted output, and disabled-mode rendering.

Adds a minimal internal ANSI escape-code builder (dev.metaschema.cli.processor.ansi.Ansi)
that covers the subset of jline:jansi functionality used by the CLI — fluent text/color
appending, bold/boldOff, reset, fgRed/fgBrightRed/fgBrightYellow/fgBrightBlue/fgBrightCyan,
fgBright(Color), and a global setEnabled toggle.

The existing Ansi import is renamed to the new package across the three call sites:

- cli-processor/CLIProcessor (version banner formatting and --no-color toggle)
- cli-processor/CallingContext (sub-command list rendering; jline's render("@|bold ...|@ ...")
  is replaced with explicit fluent bold()/boldOff() calls)
- metaschema-cli/LoggingValidationHandler (severity-colored log preambles and findings)

The jline:jansi-core dependency is removed from the parent POM's dependencyManagement,
cli-processor/pom.xml, and cli-processor/module-info.java. The new ansi package is
exported from cli-processor for metaschema-cli to consume.

Motivation: jline 4.0.0 (dependabot PR metaschema-framework#677) is a breaking upgrade requiring Maven 4,
complete JPMS migration, and JNA removal. The CLI's use of jansi is limited and
well-isolated, so replacing it with ~230 lines of maintained code is lower-risk and
eliminates the dependency entirely. Test coverage: 22 unit tests in AnsiTest covering
color codes, bold, reset, chaining, format(), and the setEnabled(false) suppression path.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f2a26fdd-7081-44ca-a24e-44a027bed06e

📥 Commits

Reviewing files that changed from the base of the PR and between 04cb3a1 and 926609f.

📒 Files selected for processing (1)
  • pom.xml
💤 Files with no reviewable changes (1)
  • pom.xml

📝 Walkthrough

Walkthrough

Removed the external org.jline:jansi-core dependency and added an internal dev.metaschema.cli.processor.ansi.Ansi builder; source files, module descriptor, and tests were updated to use and export the new ANSI implementation.

Changes

Cohort / File(s) Summary
Maven config
pom.xml, cli-processor/pom.xml
Removed dependency.jline.version property and removed managed dependency org.jline:jansi-core.
Module descriptor
cli-processor/src/main/java/module-info.java
Removed requires org.jansi.core; and added exports dev.metaschema.cli.processor.ansi;.
New ANSI implementation
cli-processor/src/main/java/dev/metaschema/cli/processor/ansi/Ansi.java
Added chainable ANSI SGR builder with ansi() factory, text append/format, style methods (reset, bold, boldOff), foreground colors (including bright variants), global enable/disable, and Ansi.Color enum.
Import / usage updates
cli-processor/src/main/java/dev/metaschema/cli/processor/CLIProcessor.java, cli-processor/src/main/java/dev/metaschema/cli/processor/CallingContext.java, metaschema-cli/src/main/java/dev/metaschema/cli/util/LoggingValidationHandler.java
Replaced imports and references from org.jline.jansi.Ansi/ansi() to dev.metaschema.cli.processor.ansi.Ansi/ansi(); CallingContext replaces JLine markup rendering with manual builder chaining.
Tests
cli-processor/src/test/java/dev/metaschema/cli/processor/ansi/AnsiTest.java
Added JUnit 5 tests covering appending, formatting, style/color escape sequences, chaining, idempotence, and behavior when ANSI emission is disabled.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I left the jar and built my art,
I stitch the codes, one glowing part.
Bold hops, bright hops, a fluent trail,
I weave the lights where terminals hail.
A rabbit's cheer — new ANSI tale.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.21% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: removing the external jline:jansi-core dependency and replacing it with an internal Ansi builder implementation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
cli-processor/src/main/java/dev/metaschema/cli/processor/ansi/Ansi.java (1)

207-209: Avoid ordinal-coupled SGR mapping for Color.

Line 208 derives bright color code from color.ordinal(). That creates a fragile coupling where enum reordering silently changes emitted ANSI codes.

♻️ Suggested refactor
-  public Ansi fgBright(`@NonNull` Color color) {
-    return emit(ESC + (90 + color.ordinal()) + "m");
-  }
+  public Ansi fgBright(`@NonNull` Color color) {
+    return emit(ESC + color.getBrightCode() + "m");
+  }

   public enum Color {
-    /** Black (code 30, bright 90). */
-    BLACK,
-    /** Red (code 31, bright 91). */
-    RED,
-    /** Green (code 32, bright 92). */
-    GREEN,
-    /** Yellow (code 33, bright 93). */
-    YELLOW,
-    /** Blue (code 34, bright 94). */
-    BLUE,
-    /** Magenta (code 35, bright 95). */
-    MAGENTA,
-    /** Cyan (code 36, bright 96). */
-    CYAN,
-    /** White (code 37, bright 97). */
-    WHITE;
+    /** Black (code 30, bright 90). */
+    BLACK(90),
+    /** Red (code 31, bright 91). */
+    RED(91),
+    /** Green (code 32, bright 92). */
+    GREEN(92),
+    /** Yellow (code 33, bright 93). */
+    YELLOW(93),
+    /** Blue (code 34, bright 94). */
+    BLUE(94),
+    /** Magenta (code 35, bright 95). */
+    MAGENTA(95),
+    /** Cyan (code 36, bright 96). */
+    CYAN(96),
+    /** White (code 37, bright 97). */
+    WHITE(97);
+
+    private final int brightCode;
+
+    Color(int brightCode) {
+      this.brightCode = brightCode;
+    }
+
+    int getBrightCode() {
+      return brightCode;
+    }
   }

Also applies to: 217-237

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli-processor/src/main/java/dev/metaschema/cli/processor/ansi/Ansi.java`
around lines 207 - 209, The fgBright method (and the similar bgBright and other
SGR-emitting methods around it) wrongly computes bright SGR codes using
color.ordinal(), which is brittle if the Color enum order changes; update these
methods to use a stable mapping from Color to its SGR base code instead of
ordinal arithmetic—either add a method on the Color enum (e.g.,
getAnsiBaseCode() or getForegroundBrightCode()/getBackgroundBrightCode()) that
returns explicit integer SGR values for each enum constant, or implement a
switch/map in Ansi (referencing fgBright, bgBright and the other SGR emitters)
that maps each Color to the correct hard-coded bright code and then emit ESC +
code + "m".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli-processor/src/test/java/dev/metaschema/cli/processor/ansi/AnsiTest.java`:
- Around line 19-31: Add a ResourceLock to serialize access to the static
Ansi.enabled state: annotate the AnsiTest class with
`@ResourceLock`("Ansi.enabled") (in the same place as
`@Execution`(ExecutionMode.SAME_THREAD)) so the ensureEnabled() and
restoreEnabled() methods operate under the lock, and apply the same
`@ResourceLock`("Ansi.enabled") to the other test class/block referenced in the
file (the one around lines 139-164) to prevent cross-test interference with the
Ansi.enabled static field.

---

Nitpick comments:
In `@cli-processor/src/main/java/dev/metaschema/cli/processor/ansi/Ansi.java`:
- Around line 207-209: The fgBright method (and the similar bgBright and other
SGR-emitting methods around it) wrongly computes bright SGR codes using
color.ordinal(), which is brittle if the Color enum order changes; update these
methods to use a stable mapping from Color to its SGR base code instead of
ordinal arithmetic—either add a method on the Color enum (e.g.,
getAnsiBaseCode() or getForegroundBrightCode()/getBackgroundBrightCode()) that
returns explicit integer SGR values for each enum constant, or implement a
switch/map in Ansi (referencing fgBright, bgBright and the other SGR emitters)
that maps each Color to the correct hard-coded bright code and then emit ESC +
code + "m".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 98934ce0-936c-401a-aab1-aba0b53f1233

📥 Commits

Reviewing files that changed from the base of the PR and between 2baf561 and 9c2d912.

📒 Files selected for processing (8)
  • cli-processor/pom.xml
  • cli-processor/src/main/java/dev/metaschema/cli/processor/CLIProcessor.java
  • cli-processor/src/main/java/dev/metaschema/cli/processor/CallingContext.java
  • cli-processor/src/main/java/dev/metaschema/cli/processor/ansi/Ansi.java
  • cli-processor/src/main/java/module-info.java
  • cli-processor/src/test/java/dev/metaschema/cli/processor/ansi/AnsiTest.java
  • metaschema-cli/src/main/java/dev/metaschema/cli/util/LoggingValidationHandler.java
  • pom.xml
💤 Files with no reviewable changes (2)
  • cli-processor/pom.xml
  • pom.xml

Comment on lines +19 to +31
@Execution(ExecutionMode.SAME_THREAD)
class AnsiTest {
private static final String ESC = "\u001B";

@BeforeEach
void ensureEnabled() {
Ansi.setEnabled(true);
}

@AfterEach
void restoreEnabled() {
Ansi.setEnabled(true);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking JUnit parallel configuration:"
fd -i 'junit-platform.properties' | xargs -r rg -n -C2 'junit\.jupiter\.execution\.parallel|junit\.jupiter\.testinstance'
fd -i 'pom.xml' | xargs -r rg -n -C2 'junit\.jupiter\.execution\.parallel|surefire|failsafe|parallel'

echo
echo "Checking for global Ansi.setEnabled usage in tests:"
rg -n --type=java -C2 '\bAnsi\.setEnabled\s*\('

Repository: metaschema-framework/metaschema-java

Length of output: 5491


Add @ResourceLock for the shared Ansi.enabled static state.

The @Execution(ExecutionMode.SAME_THREAD) annotation on this class prevents parallel execution within AnsiTest, but the repository has global parallel test execution enabled (see pom.xml: junit.jupiter.execution.parallel.enabled = true). Concurrent tests in other classes can still interfere with the static Ansi.enabled field modified in ensureEnabled() and restoreEnabled(), causing potential test flakiness. Add @ResourceLock("Ansi.enabled") to isolate access to this shared state.

Also applies to: 139-164

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli-processor/src/test/java/dev/metaschema/cli/processor/ansi/AnsiTest.java`
around lines 19 - 31, Add a ResourceLock to serialize access to the static
Ansi.enabled state: annotate the AnsiTest class with
`@ResourceLock`("Ansi.enabled") (in the same place as
`@Execution`(ExecutionMode.SAME_THREAD)) so the ensureEnabled() and
restoreEnabled() methods operate under the lock, and apply the same
`@ResourceLock`("Ansi.enabled") to the other test class/block referenced in the
file (the one around lines 139-164) to prevent cross-test interference with the
Ansi.enabled static field.

PMD's AvoidUsingVolatile rule flags the volatile boolean; AtomicBoolean
is idiomatic for thread-safe flag flipping and passes the same tests.
@david-waltermire david-waltermire merged commit 6a885eb into metaschema-framework:develop Apr 19, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant