From a1e8443c417249dc6ff9a24c7c9d8fb2fce07c88 Mon Sep 17 00:00:00 2001 From: Matthias Andreas Benkard Date: Tue, 5 Dec 2023 21:12:16 +0100 Subject: jgvariant-tool: New module. Adds a command line tool that can read and (in the future) manipulate GVariant-formatted files. Change-Id: Icc92eb409a97e7cf72dfd7535f6a8b3587dd4a48 --- README.md | 26 ++- jgvariant-parent/pom.xml | 38 ++++ jgvariant-tool/pom.xml | 239 +++++++++++++++++++++ .../src/main/java/eu/mulk/jgvariant/tool/Main.java | 28 +++ .../java/eu/mulk/jgvariant/tool/MainCommand.java | 115 ++++++++++ .../jgvariant/tool/jsonb/ByteArraySerializer.java | 24 +++ .../jgvariant/tool/jsonb/ByteStringSerializer.java | 24 +++ .../mulk/jgvariant/tool/jsonb/ChecksumAdapter.java | 27 +++ .../jgvariant/tool/jsonb/SignatureSerializer.java | 24 +++ .../jgvariant/tool/jsonb/VariantSerializer.java | 43 ++++ .../java/eu/mulk/jgvariant/tool/package-info.java | 8 + jgvariant-tool/src/main/java/module-info.java | 20 ++ pom.xml | 2 + 13 files changed, 617 insertions(+), 1 deletion(-) create mode 100644 jgvariant-tool/pom.xml create mode 100644 jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/Main.java create mode 100644 jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/MainCommand.java create mode 100644 jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteArraySerializer.java create mode 100644 jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteStringSerializer.java create mode 100644 jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ChecksumAdapter.java create mode 100644 jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/SignatureSerializer.java create mode 100644 jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/VariantSerializer.java create mode 100644 jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/package-info.java create mode 100644 jgvariant-tool/src/main/java/module-info.java diff --git a/README.md b/README.md index d01d141..caa9938 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,31 @@ pairs of [String][] and `int`, you can use the following code: List example = decoder.decode(ByteBuffer.wrap(bytes)); -## Installation +## Command line tool + +The `jgvariant-tool` module contains a tool called `jgvariant` that can +be used to manipulate [GVariant][]-formatted files from the command line. +Its primary purpose is to enable the scripting of [OSTree][] repository +management tasks. + +Usage example (dumping the contents of an [OSTree][] summary file): + + $ jgvariant ostree summary read ./jgvariant-ostree/src/test/resources/ostree/summary + +You can build the tool either as a shaded JAR or as a native executable. + +To build and run a shaded JAR: + + $ mvn package -pl jgvariant-tool -am -Pshade + $ java -jar /home/mulk/Arbeitskasten/jgvariant/jgvariant-tool/target/jgvariant-tool-*.jar + +To build and run a native executable: + + $ mvn package -pl jgvariant-tool -am -Pnative + $ ./jgvariant-tool/target/jgvariant + + +## Library installation ### Usage with Maven diff --git a/jgvariant-parent/pom.xml b/jgvariant-parent/pom.xml index 91acdfa..4a2c2f2 100644 --- a/jgvariant-parent/pom.xml +++ b/jgvariant-parent/pom.xml @@ -59,10 +59,12 @@ SPDX-License-Identifier: LGPL-3.0-or-later ${surefire-plugin.version} 1.5.0 3.3.0 + 0.1.5 2.0.1 3.1.0 3.6.3 3.3.0 + 0.9.23 1.6.13 2.41.1 3.2.2 @@ -71,10 +73,13 @@ SPDX-License-Identifier: LGPL-3.0-or-later 1.1.2 2.23.0 1.15.0 + 32.1.3-jre 0.3.3 24.1.0 5.10.1 0.10.18 + 4.7.4 + 3.0.2 1.9 @@ -111,6 +116,27 @@ SPDX-License-Identifier: LGPL-3.0-or-later ${xz.version} + + + info.picocli + picocli + ${picocli.version} + + + + + org.eclipse + yasson + ${yasson.version} + + + + + com.google.guava + guava + ${guava.version} + + org.junit.jupiter @@ -240,6 +266,18 @@ SPDX-License-Identifier: LGPL-3.0-or-later + + org.graalvm.buildtools + native-maven-plugin + ${native-plugin.version} + + + + com.github.akman + jpackage-maven-plugin + ${jpackage-plugin.version} + + diff --git a/jgvariant-tool/pom.xml b/jgvariant-tool/pom.xml new file mode 100644 index 0000000..ee2b8a8 --- /dev/null +++ b/jgvariant-tool/pom.xml @@ -0,0 +1,239 @@ + + + + + + + 4.0.0 + + 0.1.8-SNAPSHOT + + jgvariant-tool + jar + + JGVariant Command Line Tool + https://gerrit.benkard.de/plugins/gitiles/jgvariant + + + GVariant command line tool. + + + + eu.mulk.jgvariant + jgvariant-parent + 0.1.8-SNAPSHOT + ../jgvariant-parent/pom.xml + + + + + + eu.mulk.jgvariant + jgvariant-core + 0.1.8-SNAPSHOT + + + eu.mulk.jgvariant + jgvariant-ostree + 0.1.8-SNAPSHOT + + + + + com.google.errorprone + error_prone_annotations + provided + + + org.jetbrains + annotations + provided + + + org.apiguardian + apiguardian-api + provided + + + + + info.picocli + picocli + + + + + org.eclipse + yasson + + + + + com.google.guava + guava + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + info.picocli + picocli-codegen + ${picocli.version} + + + + -Aproject=${project.groupId}/${project.artifactId} + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + eu.mulk.jgvariant.tool.Main + + + + + + + + + + shade + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + + + + + + + + uberjar + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + + eu.mulk.jgvariant.tool.Main + + + + jar-with-dependencies + + + + + + + + + + + native + + + + org.graalvm.buildtools + native-maven-plugin + true + + + build-native + + compile-no-fork + + package + + + test-native + + test + + test + + + + false + false + + -O3 + --strict-image-heap + + jgvariant + + + + + + + + jpackage + + + + com.github.akman + jpackage-maven-plugin + + + package + + jpackage + + + IMAGE + eu.mulk.jgvariant.tool/eu.mulk.jgvariant.tool.Main + + + + true + true + + + + + + + + + + + + + diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/Main.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/Main.java new file mode 100644 index 0000000..64e375a --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/Main.java @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool; + +import static java.util.logging.Level.WARNING; + +import java.util.logging.Logger; +import picocli.CommandLine; + +/** + * A command line tool to read and manipulate GVariant-formatted files. + * + *

Also provides ways to manipulate OSTree repositories. + */ +public final class Main { + static { + Logger.getGlobal().setLevel(WARNING); + } + + public static void main(String[] args) { + int exitCode = new CommandLine(new MainCommand()).execute(args); + System.exit(exitCode); + } + + private Main() {} +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/MainCommand.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/MainCommand.java new file mode 100644 index 0000000..7b25dfc --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/MainCommand.java @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool; + +import static java.util.logging.Level.*; + +import eu.mulk.jgvariant.ostree.Summary; +import eu.mulk.jgvariant.tool.jsonb.*; +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.util.logging.Logger; +import org.jetbrains.annotations.VisibleForTesting; +import picocli.AutoComplete; +import picocli.CommandLine; +import picocli.CommandLine.*; + +@Command( + name = "jgvariant", + mixinStandardHelpOptions = true, + description = "Manipulate files in GVariant format.", + subcommands = {MainCommand.OstreeCommand.class, AutoComplete.GenerateCompletion.class}) +final class MainCommand { + + private static final Logger LOG = Logger.getLogger("eu.mulk.jgvariant.tool"); + + private static final Jsonb jsonb = + JsonbBuilder.newBuilder() + .withConfig( + new JsonbConfig() + .withFormatting(true) + .withAdapters(ChecksumAdapter.INSTANCE) + .withSerializers( + ByteStringSerializer.INSTANCE, + ByteArraySerializer.INSTANCE, + SignatureSerializer.INSTANCE, + VariantSerializer.INSTANCE)) + .build(); + + @Option( + names = {"-v", "--verbose"}, + description = "Enable verbose logging.", + scope = CommandLine.ScopeType.INHERIT) + void setVerbose(boolean[] verbose) { + Logger.getGlobal() + .setLevel( + switch (verbose.length) { + case 0 -> WARNING; + case 1 -> INFO; + case 2 -> FINE; + default -> ALL; + }); + } + + @Command( + name = "ostree", + mixinStandardHelpOptions = true, + description = "Manipulate OSTree files.", + subcommands = {OstreeCommand.SummaryCommand.class}) + static final class OstreeCommand { + + @Command( + name = "summary", + mixinStandardHelpOptions = true, + description = "Manipulate OSTree summary files.") + static final class SummaryCommand extends BaseCommand { + + @Command(mixinStandardHelpOptions = true) + void read(@Parameters(paramLabel = "") File file) throws IOException { + LOG.fine(() -> "Reading file %s".formatted(file)); + var fileBytes = ByteBuffer.wrap(Files.readAllBytes(fs().getPath(file.getPath()))); + var decoder = Summary.decoder(); + var thing = decoder.decode(fileBytes); + out().println(jsonb.toJson(thing)); + } + + SummaryCommand() {} + } + + OstreeCommand() {} + } + + @Command + abstract static class BaseCommand { + + @Spec CommandLine.Model.CommandSpec spec; + + @VisibleForTesting FileSystem fs = FileSystems.getDefault(); + + protected BaseCommand() {} + + protected PrintWriter out() { + return spec.commandLine().getOut(); + } + + protected PrintWriter err() { + return spec.commandLine().getErr(); + } + + protected FileSystem fs() { + return fs; + } + } + + MainCommand() {} +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteArraySerializer.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteArraySerializer.java new file mode 100644 index 0000000..7f80440 --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteArraySerializer.java @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool.jsonb; + +import jakarta.json.bind.serializer.JsonbSerializer; +import jakarta.json.bind.serializer.SerializationContext; +import jakarta.json.stream.JsonGenerator; +import java.util.HexFormat; + +@SuppressWarnings("java:S6548") +public final class ByteArraySerializer implements JsonbSerializer { + + public static final ByteArraySerializer INSTANCE = new ByteArraySerializer(); + + private ByteArraySerializer() {} + + @Override + public void serialize( + byte[] o, JsonGenerator jsonGenerator, SerializationContext serializationContext) { + jsonGenerator.write(HexFormat.of().formatHex(o)); + } +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteStringSerializer.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteStringSerializer.java new file mode 100644 index 0000000..3537c3d --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteStringSerializer.java @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool.jsonb; + +import eu.mulk.jgvariant.ostree.ByteString; +import jakarta.json.bind.serializer.JsonbSerializer; +import jakarta.json.bind.serializer.SerializationContext; +import jakarta.json.stream.JsonGenerator; + +@SuppressWarnings("java:S6548") +public final class ByteStringSerializer implements JsonbSerializer { + + public static final ByteStringSerializer INSTANCE = new ByteStringSerializer(); + + private ByteStringSerializer() {} + + @Override + public void serialize( + ByteString o, JsonGenerator jsonGenerator, SerializationContext serializationContext) { + jsonGenerator.write(o.hex()); + } +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ChecksumAdapter.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ChecksumAdapter.java new file mode 100644 index 0000000..58a8951 --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ChecksumAdapter.java @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool.jsonb; + +import eu.mulk.jgvariant.ostree.ByteString; +import eu.mulk.jgvariant.ostree.Checksum; +import jakarta.json.bind.adapter.JsonbAdapter; + +@SuppressWarnings("java:S6548") +public final class ChecksumAdapter implements JsonbAdapter { + + public static final ChecksumAdapter INSTANCE = new ChecksumAdapter(); + + private ChecksumAdapter() {} + + @Override + public ByteString adaptToJson(Checksum obj) throws Exception { + return obj.byteString(); + } + + @Override + public Checksum adaptFromJson(ByteString obj) throws Exception { + return new Checksum(obj); + } +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/SignatureSerializer.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/SignatureSerializer.java new file mode 100644 index 0000000..dca1044 --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/SignatureSerializer.java @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool.jsonb; + +import eu.mulk.jgvariant.core.Signature; +import jakarta.json.bind.serializer.JsonbSerializer; +import jakarta.json.bind.serializer.SerializationContext; +import jakarta.json.stream.JsonGenerator; + +@SuppressWarnings("java:S6548") +public final class SignatureSerializer implements JsonbSerializer { + + public static final SignatureSerializer INSTANCE = new SignatureSerializer(); + + private SignatureSerializer() {} + + @Override + public void serialize( + Signature o, JsonGenerator jsonGenerator, SerializationContext serializationContext) { + jsonGenerator.write(o.toString()); + } +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/VariantSerializer.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/VariantSerializer.java new file mode 100644 index 0000000..99ff553 --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/VariantSerializer.java @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool.jsonb; + +import com.google.common.primitives.Bytes; +import eu.mulk.jgvariant.core.Signature; +import eu.mulk.jgvariant.core.Variant; +import jakarta.json.bind.serializer.JsonbSerializer; +import jakarta.json.bind.serializer.SerializationContext; +import jakarta.json.stream.JsonGenerator; +import java.text.ParseException; +import java.util.List; + +@SuppressWarnings("java:S6548") +public final class VariantSerializer implements JsonbSerializer { + + public static final VariantSerializer INSTANCE = new VariantSerializer(); + + private final ByteArraySerializer byteArraySerializer = ByteArraySerializer.INSTANCE; + + private final Signature byteArraySignature; + + private VariantSerializer() { + try { + byteArraySignature = Signature.parse("ay"); + } catch (ParseException e) { + // impossible + throw new IllegalArgumentException(e); + } + } + + @Override + @SuppressWarnings("unchecked") + public void serialize(Variant obj, JsonGenerator generator, SerializationContext ctx) { + if (obj.signature().equals(byteArraySignature)) { + byteArraySerializer.serialize(Bytes.toArray((List) obj.value()), generator, ctx); + } else { + ctx.serialize(obj.value(), generator); + } + } +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/package-info.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/package-info.java new file mode 100644 index 0000000..da5c3ba --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/package-info.java @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard +// +// SPDX-License-Identifier: GPL-3.0-or-later + +@API(status = API.Status.INTERNAL) +package eu.mulk.jgvariant.tool; + +import org.apiguardian.api.API; diff --git a/jgvariant-tool/src/main/java/module-info.java b/jgvariant-tool/src/main/java/module-info.java new file mode 100644 index 0000000..e66981c --- /dev/null +++ b/jgvariant-tool/src/main/java/module-info.java @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard +// +// SPDX-License-Identifier: GPL-3.0-or-later + +module eu.mulk.jgvariant.tool { + requires transitive eu.mulk.jgvariant.ostree; + requires com.google.common; + requires info.picocli; + requires jakarta.json; + requires jakarta.json.bind; + requires java.logging; + requires static com.google.errorprone.annotations; + requires static org.apiguardian.api; + requires static org.jetbrains.annotations; + + opens eu.mulk.jgvariant.tool to + info.picocli; + + exports eu.mulk.jgvariant.tool; +} diff --git a/pom.xml b/pom.xml index be9fd26..1ab4b48 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,8 @@ SPDX-License-Identifier: LGPL-3.0-or-later jgvariant-core jgvariant-ostree + jgvariant-tool + jgvariant-bom -- cgit v1.2.3