Skip to content

Commit 30ea043

Browse files
authored
Merge pull request #16 from aether-framework/feature/cli-module
Add comprehensive CLI documentation and Aether Datafixers module
2 parents e74efd1 + 8368273 commit 30ea043

45 files changed

Lines changed: 7894 additions & 143 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,49 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
66

77
---
88

9+
## [0.3.0] - 2026-01-07
10+
11+
### Added
12+
13+
#### CLI Module (`aether-datafixers-cli`)
14+
15+
New command-line interface for data migration without writing Java code.
16+
17+
**Commands:**
18+
- `migrate` — Migrate data files from one schema version to another
19+
- `validate` — Check if files need migration without modifying them
20+
- `info` — Display version info, available formats, and bootstrap details
21+
22+
**Core Features:**
23+
- Batch processing of multiple files with shell glob expansion
24+
- Auto-detection of source version from configurable data field path
25+
- In-place file modification with automatic `.bak` backup
26+
- Output to stdout, file, or directory
27+
- Pretty-printed or compact JSON output
28+
- Migration reports in text or JSON format
29+
- Verbose mode with detailed progress and stack traces
30+
- Fail-fast or continue-on-error modes
31+
- CI/CD friendly exit codes (0=success, 1=error, 2=migration needed)
32+
33+
**Format Handler System:**
34+
- `FormatHandler<T>` — SPI for pluggable serialization formats
35+
- `FormatRegistry` — ServiceLoader-based handler discovery
36+
- `json-gson` — JSON format using Google Gson (default)
37+
- `json-jackson` — JSON format using Jackson Databind
38+
39+
**Utilities:**
40+
- `BootstrapLoader` — Reflective loading of DataFixerBootstrap implementations
41+
- `VersionExtractor` — Extract version from nested JSON paths (dot notation)
42+
- `ReportFormatter` — Text and JSON migration report formatting
43+
- `TextReportFormatter` — Human-readable single-line reports
44+
- `JsonReportFormatter` — Machine-readable JSON reports
45+
46+
**Exceptions:**
47+
- `BootstrapLoadException` — Bootstrap class loading failures
48+
- `FormatParseException` — Input parsing failures
49+
50+
---
51+
952
## [0.2.0] - 2026-01-07
1053

1154
### Added

aether-datafixers-bom/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
<artifactId>aether-datafixers-testkit</artifactId>
3838
<version>${project.version}</version>
3939
</dependency>
40+
<dependency>
41+
<groupId>de.splatgames.aether.datafixers</groupId>
42+
<artifactId>aether-datafixers-cli</artifactId>
43+
<version>${project.version}</version>
44+
</dependency>
4045
</dependencies>
4146
</dependencyManagement>
4247
</project>

aether-datafixers-cli/pom.xml

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>de.splatgames.aether.datafixers</groupId>
9+
<artifactId>aether-datafixers</artifactId>
10+
<version>0.3.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>aether-datafixers-cli</artifactId>
14+
<packaging>jar</packaging>
15+
16+
<name>Aether Datafixers :: CLI</name>
17+
<description>Command-line interface for migrating data files using Aether Datafixers.</description>
18+
19+
<properties>
20+
<main.class>de.splatgames.aether.datafixers.cli.AetherCli</main.class>
21+
</properties>
22+
23+
<dependencies>
24+
<!-- Aether Datafixers modules -->
25+
<dependency>
26+
<groupId>de.splatgames.aether.datafixers</groupId>
27+
<artifactId>aether-datafixers-api</artifactId>
28+
</dependency>
29+
<dependency>
30+
<groupId>de.splatgames.aether.datafixers</groupId>
31+
<artifactId>aether-datafixers-core</artifactId>
32+
</dependency>
33+
<dependency>
34+
<groupId>de.splatgames.aether.datafixers</groupId>
35+
<artifactId>aether-datafixers-codec</artifactId>
36+
</dependency>
37+
38+
<!-- Picocli for CLI parsing -->
39+
<dependency>
40+
<groupId>info.picocli</groupId>
41+
<artifactId>picocli</artifactId>
42+
</dependency>
43+
44+
<!-- JSON libraries (for built-in format handlers) -->
45+
<dependency>
46+
<groupId>com.google.code.gson</groupId>
47+
<artifactId>gson</artifactId>
48+
</dependency>
49+
<dependency>
50+
<groupId>com.fasterxml.jackson.core</groupId>
51+
<artifactId>jackson-databind</artifactId>
52+
</dependency>
53+
54+
<!-- Guava for utilities -->
55+
<dependency>
56+
<groupId>com.google.guava</groupId>
57+
<artifactId>guava</artifactId>
58+
</dependency>
59+
60+
<!-- JetBrains annotations -->
61+
<dependency>
62+
<groupId>org.jetbrains</groupId>
63+
<artifactId>annotations</artifactId>
64+
</dependency>
65+
66+
<!-- SLF4J for logging -->
67+
<dependency>
68+
<groupId>org.slf4j</groupId>
69+
<artifactId>slf4j-api</artifactId>
70+
</dependency>
71+
<dependency>
72+
<groupId>org.slf4j</groupId>
73+
<artifactId>slf4j-simple</artifactId>
74+
<version>${slf4j.version}</version>
75+
<scope>runtime</scope>
76+
</dependency>
77+
78+
<!-- Testing -->
79+
<dependency>
80+
<groupId>org.junit.jupiter</groupId>
81+
<artifactId>junit-jupiter</artifactId>
82+
<scope>test</scope>
83+
</dependency>
84+
<dependency>
85+
<groupId>org.assertj</groupId>
86+
<artifactId>assertj-core</artifactId>
87+
<scope>test</scope>
88+
</dependency>
89+
<dependency>
90+
<groupId>de.splatgames.aether.datafixers</groupId>
91+
<artifactId>aether-datafixers-testkit</artifactId>
92+
<scope>test</scope>
93+
</dependency>
94+
</dependencies>
95+
96+
<build>
97+
<plugins>
98+
<plugin>
99+
<groupId>org.apache.maven.plugins</groupId>
100+
<artifactId>maven-compiler-plugin</artifactId>
101+
<configuration>
102+
<annotationProcessorPaths>
103+
<path>
104+
<groupId>info.picocli</groupId>
105+
<artifactId>picocli-codegen</artifactId>
106+
<version>${picocli.version}</version>
107+
</path>
108+
</annotationProcessorPaths>
109+
</configuration>
110+
</plugin>
111+
<plugin>
112+
<groupId>org.apache.maven.plugins</groupId>
113+
<artifactId>maven-enforcer-plugin</artifactId>
114+
</plugin>
115+
<plugin>
116+
<groupId>org.apache.maven.plugins</groupId>
117+
<artifactId>maven-surefire-plugin</artifactId>
118+
</plugin>
119+
120+
<!-- Fat JAR with all dependencies -->
121+
<plugin>
122+
<groupId>org.apache.maven.plugins</groupId>
123+
<artifactId>maven-shade-plugin</artifactId>
124+
<version>${plugin.shade.version}</version>
125+
<executions>
126+
<execution>
127+
<phase>package</phase>
128+
<goals>
129+
<goal>shade</goal>
130+
</goals>
131+
<configuration>
132+
<transformers>
133+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
134+
<mainClass>${main.class}</mainClass>
135+
</transformer>
136+
<!-- Merge META-INF/services files -->
137+
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
138+
</transformers>
139+
<filters>
140+
<filter>
141+
<artifact>*:*</artifact>
142+
<excludes>
143+
<exclude>META-INF/*.SF</exclude>
144+
<exclude>META-INF/*.DSA</exclude>
145+
<exclude>META-INF/*.RSA</exclude>
146+
</excludes>
147+
</filter>
148+
</filters>
149+
<createDependencyReducedPom>false</createDependencyReducedPom>
150+
<shadedArtifactAttached>true</shadedArtifactAttached>
151+
<shadedClassifierName>fat</shadedClassifierName>
152+
</configuration>
153+
</execution>
154+
</executions>
155+
</plugin>
156+
157+
<!-- Allow running via exec:java -->
158+
<plugin>
159+
<groupId>org.codehaus.mojo</groupId>
160+
<artifactId>exec-maven-plugin</artifactId>
161+
<version>3.1.0</version>
162+
<configuration>
163+
<mainClass>${main.class}</mainClass>
164+
</configuration>
165+
</plugin>
166+
</plugins>
167+
</build>
168+
</project>
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright (c) 2025 Splatgames.de Software and Contributors
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
package de.splatgames.aether.datafixers.cli;
24+
25+
import de.splatgames.aether.datafixers.cli.command.InfoCommand;
26+
import de.splatgames.aether.datafixers.cli.command.MigrateCommand;
27+
import de.splatgames.aether.datafixers.cli.command.ValidateCommand;
28+
import picocli.CommandLine;
29+
import picocli.CommandLine.Command;
30+
import picocli.CommandLine.HelpCommand;
31+
32+
import java.util.concurrent.Callable;
33+
34+
/**
35+
* Main entry point for the Aether Datafixers CLI.
36+
*
37+
* <p>The CLI provides commands for migrating, validating, and inspecting
38+
* data files using Aether Datafixers.</p>
39+
*
40+
* <h2>Usage Examples</h2>
41+
* <pre>{@code
42+
* # Migrate a single file
43+
* aether-cli migrate --from 100 --to 200 --type player --bootstrap com.example.MyBootstrap input.json
44+
*
45+
* # Migrate with auto-detected version
46+
* aether-cli migrate --to 200 --type player --version-field dataVersion --bootstrap com.example.MyBootstrap input.json
47+
*
48+
* # Validate files
49+
* aether-cli validate --to 200 --type player --bootstrap com.example.MyBootstrap *.json
50+
*
51+
* # Show info
52+
* aether-cli info --formats
53+
* aether-cli info --bootstrap com.example.MyBootstrap
54+
* }</pre>
55+
*
56+
* @author Erik Pfoertner
57+
* @since 0.3.0
58+
*/
59+
@Command(
60+
name = "aether-cli",
61+
mixinStandardHelpOptions = true,
62+
version = "Aether Datafixers CLI 0.3.0",
63+
description = "Command-line data migration tool using Aether Datafixers.",
64+
subcommands = {
65+
MigrateCommand.class,
66+
ValidateCommand.class,
67+
InfoCommand.class,
68+
HelpCommand.class
69+
},
70+
synopsisHeading = "%nUsage:%n%n",
71+
descriptionHeading = "%nDescription:%n%n",
72+
parameterListHeading = "%nParameters:%n",
73+
optionListHeading = "%nOptions:%n",
74+
commandListHeading = "%nCommands:%n"
75+
)
76+
public class AetherCli implements Callable<Integer> {
77+
78+
/**
79+
* Main entry point for the Aether Datafixers CLI application.
80+
*
81+
* <p>This method initializes the picocli {@link CommandLine} parser with the
82+
* root {@link AetherCli} command and executes it with the provided arguments.
83+
* The process exit code is set based on the command execution result.</p>
84+
*
85+
* <h3>Exit Codes</h3>
86+
* <ul>
87+
* <li>{@code 0} - Success (or help displayed)</li>
88+
* <li>{@code 1} - Error occurred during command execution</li>
89+
* <li>{@code 2} - Validation found files needing migration (validate command only)</li>
90+
* </ul>
91+
*
92+
* <h3>Configuration</h3>
93+
* <p>The CommandLine instance is configured with:</p>
94+
* <ul>
95+
* <li>Case-insensitive enum value parsing enabled</li>
96+
* </ul>
97+
*
98+
* @param args command-line arguments passed from the shell; may be empty
99+
* but should not be {@code null}
100+
* @see CommandLine#execute(String...)
101+
*/
102+
public static void main(final String[] args) {
103+
final int exitCode = new CommandLine(new AetherCli())
104+
.setCaseInsensitiveEnumValuesAllowed(true)
105+
.execute(args);
106+
System.exit(exitCode);
107+
}
108+
109+
/**
110+
* Executes the root command when invoked without a subcommand.
111+
*
112+
* <p>When the CLI is invoked without specifying a subcommand (e.g., just
113+
* {@code aether-cli}), this method is called. It displays the help message
114+
* showing available commands and options.</p>
115+
*
116+
* <p>This behavior provides a user-friendly experience where running the
117+
* CLI without arguments shows how to use it, rather than doing nothing
118+
* or showing an error.</p>
119+
*
120+
* @return {@code 0} indicating successful execution (help was displayed)
121+
* @see CommandLine#usage(Object, java.io.PrintStream)
122+
*/
123+
@Override
124+
public Integer call() {
125+
// Print help when no subcommand is specified
126+
CommandLine.usage(this, System.out);
127+
return 0;
128+
}
129+
}

0 commit comments

Comments
 (0)