Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
923b0a1
chore: add fesod-cli module and implement core CLI commands
liugddx Dec 28, 2025
d381ede
1
liugddx Dec 29, 2025
095d710
chore: clean up fesod-cli scripts and add .gitattributes for line end…
liugddx Dec 29, 2025
23cb344
chore: remove some useless file
liugddx Dec 29, 2025
c901384
chore: remove some useless file
liugddx Dec 29, 2025
45a9971
chore: remove some useless file
liugddx Dec 29, 2025
7e78164
chore: remove some useless file
liugddx Dec 29, 2025
5d0cb9c
chore: remove some useless file
liugddx Dec 29, 2025
b0bc52c
chore: remove some useless file
liugddx Dec 29, 2025
8bff01e
chore: remove some useless file
liugddx Dec 29, 2025
ec39c96
chore: remove some useless file
liugddx Dec 30, 2025
a15f5de
Update fesod-cli/src/main/java/org/apache/fesod/cli/formatters/Format…
liugddx Dec 30, 2025
1438b7a
Update fesod-cli/src/main/java/org/apache/fesod/cli/core/sheet/SheetR…
liugddx Dec 30, 2025
19c3c6d
Update fesod-cli/src/main/java/org/apache/fesod/cli/commands/WriteCom…
liugddx Dec 30, 2025
f660aff
Update fesod-cli/src/main/java/org/apache/fesod/cli/FesodCli.java
liugddx Dec 30, 2025
e8867d6
Update fesod-cli/src/main/java/org/apache/fesod/cli/FesodCli.java
liugddx Dec 30, 2025
1b94d76
chore: remove some useless file
liugddx Dec 30, 2025
45e89d7
chore: remove some useless file
liugddx Dec 30, 2025
2d1cce8
Merge remote-tracking branch 'upstream/main' into feat-746
liugddx Jan 11, 2026
8cedcf9
chore: add fesod-cli module and implement core CLI commands
liugddx Jan 11, 2026
9826905
chore: remove application.properties from configuration files
liugddx Jan 11, 2026
f09f406
chore: remove application.properties from configuration files
liugddx Jan 11, 2026
2f5b0fc
chore: remove application.properties from configuration files
liugddx Jan 11, 2026
627b70e
Merge branch 'main' into feat-746
liugddx Jan 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Auto detect text files and perform LF normalization
* text=auto

# Shell scripts must use Unix line endings (LF)
*.sh text eol=lf
fesod-cli text eol=lf
**/scripts/fesod-cli text eol=lf

# Windows batch files use Windows line endings (CRLF)
*.bat text eol=crlf
*.cmd text eol=crlf

# Binary files
*.jar binary
*.xlsx binary
*.xls binary
*.png binary
*.jpg binary
*.gif binary
*.ico binary
*.zip binary
*.tar.gz binary
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:
restore-keys: |
${{ runner.os }}-m2
- name: Test with Maven
run: ./mvnw test -B -Dmaven.test.skip=false -pl fesod-common,fesod-sheet
run: ./mvnw test -B -Dmaven.test.skip=false -pl fesod-common,fesod-sheet,fesod-cli
- name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: (!cancelled())
Expand Down
111 changes: 111 additions & 0 deletions fesod-cli/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.apache.fesod</groupId>
<artifactId>fesod-parent</artifactId>
<version>${revision}</version>
</parent>

<artifactId>fesod-cli</artifactId>
<packaging>jar</packaging>
<name>Apache Fesod CLI Tool</name>
<description>Command-line interface for Apache Fesod spreadsheet processing</description>

<properties>
<picocli.version>4.7.5</picocli.version>
<snakeyaml.version>2.2</snakeyaml.version>
<mainClass>org.apache.fesod.cli.FesodCli</mainClass>
</properties>

<dependencies>
<!-- Fesod Core Dependencies -->
<dependency>
<groupId>org.apache.fesod</groupId>
<artifactId>fesod-sheet</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Picocli for CLI -->
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>${picocli.version}</version>
</dependency>

<!-- JSON Processing -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>

<!-- YAML Configuration -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>

<!-- CSV Support -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
</dependency>

<!-- Logging -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<!-- Maven Compiler Plugin - Picocli annotation processor -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>info.picocli</groupId>
<artifactId>picocli-codegen</artifactId>
<version>${picocli.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
96 changes: 96 additions & 0 deletions fesod-cli/src/main/java/org/apache/fesod/cli/FesodCli.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.fesod.cli;

import org.apache.fesod.cli.commands.ConvertCommand;
import org.apache.fesod.cli.commands.InfoCommand;
import org.apache.fesod.cli.commands.ReadCommand;
import org.apache.fesod.cli.commands.VersionCommand;
import org.apache.fesod.cli.commands.WriteCommand;
import org.apache.fesod.cli.exception.CliException;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.Spec;

@Command(
name = "fesod-cli",
mixinStandardHelpOptions = true,
version = {"Apache Fesod CLI", "Java Runtime: ${java.version}", "OS: ${os.name} ${os.arch}"},
description = "Fast and Easy spreadsheet processing from the command line",
subcommands = {
ReadCommand.class,
WriteCommand.class,
ConvertCommand.class,
InfoCommand.class,
VersionCommand.class,
CommandLine.HelpCommand.class
},
usageHelpAutoWidth = true,
footer = {
"",
"Examples:",
" fesod-cli read data.xlsx --format json",
" fesod-cli convert input.xls output.xlsx",
" fesod-cli info data.xlsx",
"",
"Documentation: https://fesod.apache.org/docs/cli",
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

There is an extra space in the footer. It should be "Documentation: https://fesod.apache.org/docs/cli" without the double space before the URL.

Suggested change
"Documentation: https://fesod.apache.org/docs/cli",
"Documentation: https://fesod.apache.org/docs/cli",

Copilot uses AI. Check for mistakes.
"Report bugs: https://github.com/apache/fesod/issues"
})
public class FesodCli implements Runnable {

@Spec
CommandSpec spec;

@Option(
names = {"--verbose", "-v"},
description = "Enable verbose logging")
private boolean verbose;

@Override
public void run() {
// Default: show help when no command specified
spec.commandLine().usage(spec.commandLine().getOut());
}

public static void main(String[] args) {
int exitCode = new CommandLine(new FesodCli())
.setExecutionExceptionHandler((ex, cmd, parseResult) -> {
cmd.getErr().println(cmd.getColorScheme().errorText("Error: " + ex.getMessage()));

if (ex instanceof CliException) {
CliException cliEx = (CliException) ex;
if (cliEx.getCause() != null && parseResult.hasMatchedOption("--verbose")) {
cliEx.printStackTrace(cmd.getErr());
}
return cliEx.getExitCode();
} else {
if (parseResult.hasMatchedOption("--verbose")) {
ex.printStackTrace(cmd.getErr());
}
return 1;
}
})
.execute(args);

System.exit(exitCode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.fesod.cli.commands;

import java.io.PrintWriter;
import java.nio.file.Paths;
import org.apache.fesod.cli.config.CliConfig;
import org.apache.fesod.cli.config.ConfigLoader;
import org.apache.fesod.cli.core.DocumentProcessor;
import org.apache.fesod.cli.core.ModuleRegistry;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.Spec;

/**
* Base class for all CLI commands
*/
public abstract class BaseCommand implements Runnable {

@Spec
protected CommandSpec spec;

@Option(
names = {"--config", "-c"},
description = "Configuration file path (default: ~/.fesod/config.yaml)",
paramLabel = "<file>")
protected String configFile;

@Option(
names = {"--module", "-m"},
description = "Document module: sheet (default: sheet)",
defaultValue = "sheet")
protected String module;

protected CliConfig config;
protected DocumentProcessor processor;

/**
* Get the output print writer for this command
*/
protected PrintWriter getOut() {
return spec.commandLine().getOut();
}

protected void initialize() {
ConfigLoader loader = new ConfigLoader();
if (configFile != null) {
config = loader.loadFromFile(Paths.get(configFile));
} else {
config = loader.loadDefault();
}

processor = ModuleRegistry.getProcessor(module);
}

@Override
public void run() {
initialize();
execute();
}

protected abstract void execute();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.fesod.cli.commands;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import picocli.CommandLine.Command;
import picocli.CommandLine.Parameters;

/**
* Convert command implementation
*/
@Command(
name = "convert",
description = "Convert spreadsheet between different formats",
mixinStandardHelpOptions = true)
public class ConvertCommand extends BaseCommand {

@Parameters(index = "0", description = "Input file path", paramLabel = "<input>")
private String inputFile;

@Parameters(index = "1", description = "Output file path", paramLabel = "<output>")
private String outputFile;

@Override
protected void execute() {
Path input = Paths.get(inputFile);
Path output = Paths.get(outputFile);

Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The ConvertCommand does not validate whether the input and output files have different paths. If a user accidentally specifies the same path for both input and output, the original file could be overwritten before it's fully read, leading to data corruption or loss. Consider adding validation to prevent input and output files from being the same.

Suggested change
if (input.toAbsolutePath().normalize().equals(output.toAbsolutePath().normalize())) {
throw new IllegalArgumentException(
"Input and output file paths must be different: " + inputFile);
}

Copilot uses AI. Check for mistakes.
Map<String, Object> options = new HashMap<String, Object>();

processor.convert(input, output, options);

getOut().println("✓ Conversion completed: " + inputFile + " → " + outputFile);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

There is an extra space in the success message. It should be "Conversion completed:" without the double space before the input file path.

Suggested change
getOut().println("✓ Conversion completed: " + inputFile + " → " + outputFile);
getOut().println("✓ Conversion completed: " + inputFile + " → " + outputFile);

Copilot uses AI. Check for mistakes.
}
}
Loading
Loading