diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b860a4c4..b222508b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,12 +19,16 @@ jobs: distribution: "temurin" java-version: "18" cache: "gradle" + - # Required for the package command tests to work + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - name: Validate Gradle wrapper uses: gradle/wrapper-validation-action@63d15e7a1e697b1de6f3ba0507106f89100c8518 - name: Build package run: ./gradlew build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_ACTOR: ${{ github.actor }} - name: Publish Test Report uses: mikepenz/action-junit-report@v4 if: success() || failure() # always run even if the previous step fails diff --git a/.gitignore b/.gitignore index 449ced55..1f85c677 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ build # Intellij .idea .cq + +dist \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..c5cfc2de --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +FROM --platform=$BUILDPLATFORM gradle:8.3-jdk20 as build +ARG GITHUB_ACTOR +ARG GITHUB_TOKEN + +WORKDIR /code + +COPY . . + +RUN gradle jar --no-daemon + +FROM eclipse-temurin:20-jre + +COPY --from=build /code/lib/build/libs/*.jar /app/app.jar + +EXPOSE 7777 + +ENV _JAVA_OPTIONS="--add-opens=java.base/java.nio=ALL-UNNAMED" + +ENTRYPOINT ["java", "-jar", "/app/app.jar"] + +CMD [ "serve", "--address", "localhost:7777", "--log-format", "json", "--log-level", "info" ] diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 00000000..027ec107 --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,3 @@ +# MemDB Plugin + +Test docs for the MemDB plugin. diff --git a/lib/build.gradle b/lib/build.gradle index 4916e1ac..7287c96c 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -40,6 +40,7 @@ dependencies { implementation 'io.cloudquery:plugin-pb-java:0.0.15' implementation 'org.apache.arrow:arrow-memory-core:12.0.1' implementation 'org.apache.arrow:arrow-vector:12.0.1' + implementation 'commons-io:commons-io:2.15.1' implementation "com.fasterxml.jackson.core:jackson-core:2.16.1" implementation "com.fasterxml.jackson.core:jackson-annotations:2.16.1" @@ -77,6 +78,17 @@ java { } } +jar { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + manifest { + attributes "Main-Class": "io.cloudquery.MainClass" + } + + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} + publishing { repositories { maven { diff --git a/lib/src/main/java/io/cloudquery/memdb/MemDB.java b/lib/src/main/java/io/cloudquery/memdb/MemDB.java index 07f50e48..7bd87df5 100644 --- a/lib/src/main/java/io/cloudquery/memdb/MemDB.java +++ b/lib/src/main/java/io/cloudquery/memdb/MemDB.java @@ -5,6 +5,7 @@ import io.cloudquery.plugin.ClientNotInitializedException; import io.cloudquery.plugin.NewClientOptions; import io.cloudquery.plugin.Plugin; +import io.cloudquery.plugin.PluginKind; import io.cloudquery.plugin.TableOutputStream; import io.cloudquery.scheduler.Scheduler; import io.cloudquery.schema.ClientMeta; @@ -88,6 +89,8 @@ public void resolve( public MemDB() { super("memdb", "0.0.1"); + setTeam("cloudquery"); + setKind(PluginKind.Source); } @Override @@ -144,12 +147,14 @@ public void close() { @Override public ClientMeta newClient(String spec, NewClientOptions options) throws Exception { - this.spec = Spec.fromJSON(spec); this.allTables = getTables(); Tables.transformTables(this.allTables); for (Table table : this.allTables) { table.addCQIDs(); } + if (!options.isNoConnection()) { + this.spec = Spec.fromJSON(spec); + } return new MemDBClient(); } } diff --git a/lib/src/main/java/io/cloudquery/plugin/BuildArch.java b/lib/src/main/java/io/cloudquery/plugin/BuildArch.java new file mode 100644 index 00000000..ee4a3501 --- /dev/null +++ b/lib/src/main/java/io/cloudquery/plugin/BuildArch.java @@ -0,0 +1,16 @@ +package io.cloudquery.plugin; + +public enum BuildArch { + AMD64("amd64"), + ARM64("arm64"); + + public final String arch; + + private BuildArch(String arch) { + this.arch = arch; + } + + public String toString() { + return this.arch; + } +} diff --git a/lib/src/main/java/io/cloudquery/plugin/BuildOS.java b/lib/src/main/java/io/cloudquery/plugin/BuildOS.java new file mode 100644 index 00000000..41b05e0b --- /dev/null +++ b/lib/src/main/java/io/cloudquery/plugin/BuildOS.java @@ -0,0 +1,17 @@ +package io.cloudquery.plugin; + +public enum BuildOS { + Windows("windows"), + Linux("linux"), + Darwin("darwin"); + + public final String os; + + private BuildOS(String os) { + this.os = os; + } + + public String toString() { + return this.os; + } +} diff --git a/lib/src/main/java/io/cloudquery/plugin/BuildTarget.java b/lib/src/main/java/io/cloudquery/plugin/BuildTarget.java new file mode 100644 index 00000000..f9780a19 --- /dev/null +++ b/lib/src/main/java/io/cloudquery/plugin/BuildTarget.java @@ -0,0 +1,12 @@ +package io.cloudquery.plugin; + +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class BuildTarget { + @NonNull protected final BuildOS os; + @NonNull protected final BuildArch arch; +} diff --git a/lib/src/main/java/io/cloudquery/plugin/Plugin.java b/lib/src/main/java/io/cloudquery/plugin/Plugin.java index bb46a0bb..9f2ef6f7 100644 --- a/lib/src/main/java/io/cloudquery/plugin/Plugin.java +++ b/lib/src/main/java/io/cloudquery/plugin/Plugin.java @@ -17,8 +17,21 @@ public abstract class Plugin { @NonNull protected final String name; @NonNull protected final String version; + @Setter protected Logger logger; @Setter protected String jsonSchema; + + @Setter protected String team; + @Setter protected PluginKind kind; + @Setter protected String dockerfile = "Dockerfile"; + + @Setter + protected BuildTarget[] buildTargets = + new BuildTarget[] { + new BuildTarget(BuildOS.Linux, BuildArch.ARM64), + new BuildTarget(BuildOS.Linux, BuildArch.AMD64), + }; + protected ClientMeta client; public void init(String spec, NewClientOptions options) throws Exception { diff --git a/lib/src/main/java/io/cloudquery/plugin/PluginKind.java b/lib/src/main/java/io/cloudquery/plugin/PluginKind.java new file mode 100644 index 00000000..e4e94334 --- /dev/null +++ b/lib/src/main/java/io/cloudquery/plugin/PluginKind.java @@ -0,0 +1,16 @@ +package io.cloudquery.plugin; + +public enum PluginKind { + Source("source"), + Destination("destination"); + + public final String kind; + + private PluginKind(String kind) { + this.kind = kind; + } + + public String toString() { + return this.kind; + } +} diff --git a/lib/src/main/java/io/cloudquery/schema/Table.java b/lib/src/main/java/io/cloudquery/schema/Table.java index 7ee91859..858b16d6 100644 --- a/lib/src/main/java/io/cloudquery/schema/Table.java +++ b/lib/src/main/java/io/cloudquery/schema/Table.java @@ -9,7 +9,6 @@ import io.cloudquery.schema.Column.ColumnBuilder; import io.cloudquery.transformers.TransformerException; import java.util.ArrayList; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -32,7 +31,7 @@ public interface Transform { public static List flattenTables(List
tables) { Map flattenMap = new LinkedHashMap<>(); for (Table table : tables) { - Table newTable = table.toBuilder().relations(Collections.emptyList()).build(); + Table newTable = table.toBuilder().build(); flattenMap.put(newTable.name, newTable); for (Table child : flattenTables(table.getRelations())) { flattenMap.put(child.name, child); diff --git a/lib/src/main/java/io/cloudquery/server/PackageCommand.java b/lib/src/main/java/io/cloudquery/server/PackageCommand.java new file mode 100644 index 00000000..289072fd --- /dev/null +++ b/lib/src/main/java/io/cloudquery/server/PackageCommand.java @@ -0,0 +1,299 @@ +package io.cloudquery.server; + +import com.google.common.base.Strings; +import io.cloudquery.plugin.BuildTarget; +import io.cloudquery.plugin.NewClientOptions; +import io.cloudquery.plugin.Plugin; +import io.cloudquery.plugin.PluginKind; +import io.cloudquery.schema.Table; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; +import lombok.ToString; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.appender.ConsoleAppender; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.core.config.LoggerConfig; +import org.apache.logging.log4j.core.layout.JsonLayout; +import org.apache.logging.log4j.core.layout.PatternLayout; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; + +@Command(name = "package", description = "package the plugin as a Docker image") +@ToString +public class PackageCommand implements Callable { + private static Logger logger; + + @Parameters(index = "0", description = "plugin version to package") + private String pluginVersion; + + @Parameters(index = "1", description = "plugin directory to package") + private String pluginDirectory; + + @Option( + required = true, + names = {"-m", "--message"}, + description = + "message that summarizes what is new or changed in this version. Supports markdown.") + private String message = ""; + + @Option( + names = "--log-format", + description = "log format. one of: text,json (default \"${DEFAULT-VALUE}\")") + private String logFormat = "text"; + + @Option( + names = "--log-level", + description = "log level. one of: trace,debug,info,warn,error (default \"${DEFAULT-VALUE}\")") + private String logLevel = "info"; + + @Option(names = "--no-sentry", description = "disable sentry") + private Boolean disableSentry = false; + + @Option(names = "--otel-endpoint", description = "Open Telemetry HTTP collector endpoint") + private String otelEndpoint = ""; + + @Option( + names = "--otel-endpoint-insecure", + description = "use Open Telemetry HTTP endpoint (for development only)") + private Boolean otelEndpointInsecure = false; + + @Option( + names = "--dist-dir", + description = "dist directory to output the built plugin. (default: /dist)") + private String distDir = ""; + + @Option( + names = "--docs-dir", + description = + "docs directory containing markdown files to copy to the dist directory. (default: /docs)") + private String docsDir = ""; + + private final Plugin plugin; + + public PackageCommand(Plugin plugin) { + this.plugin = plugin; + } + + private LoggerContext initLogger() { + ConsoleAppender appender = + ConsoleAppender.createDefaultAppenderForLayout( + this.logFormat == "text" + ? PatternLayout.createDefaultLayout() + : JsonLayout.createDefaultLayout()); + + Configuration configuration = ConfigurationFactory.newConfigurationBuilder().build(); + configuration.addAppender(appender); + LoggerConfig loggerConfig = new LoggerConfig("io.cloudquery", Level.getLevel(logLevel), false); + loggerConfig.addAppender(appender, null, null); + configuration.addLogger("io.cloudquery", loggerConfig); + LoggerContext context = new LoggerContext(ServeCommand.class.getName() + "Context"); + context.start(configuration); + + logger = context.getLogger(ServeCommand.class.getName()); + this.plugin.setLogger(logger); + return context; + } + + @SuppressWarnings("null") + @Override + public Integer call() { + try (LoggerContext context = this.initLogger()) { + if (Strings.isNullOrEmpty(plugin.getName())) { + logger.error("name is required"); + return 1; + } + if (Strings.isNullOrEmpty(plugin.getVersion())) { + logger.error("version is required"); + return 1; + } + if (Strings.isNullOrEmpty(plugin.getTeam())) { + logger.error("team is required"); + return 1; + } + if (Strings.isNullOrEmpty(plugin.getDockerfile())) { + logger.error("Dockerfile is required"); + return 1; + } + if (plugin.getBuildTargets() == null || plugin.getBuildTargets().length == 0) { + logger.error("At least one build target is required"); + return 1; + } + if (Strings.isNullOrEmpty(distDir)) { + distDir = Paths.get(pluginDirectory, "dist").toString(); + } + if (Strings.isNullOrEmpty(docsDir)) { + docsDir = Paths.get(pluginDirectory, "docs").toString(); + } + + initDist(); + copyDocs(); + writeTablesJson(); + List supportedTargets = buildDocker(); + writePackageJson(supportedTargets); + + return 0; + } catch (Exception e) { + logger.error("Failed to package plugin", e); + return 1; + } + } + + private void initDist() throws IOException { + logger.info("Packaging plugin to {}", distDir); + File dist = new File(distDir); + if (!dist.exists()) { + boolean created = dist.mkdirs(); + if (!created) { + logger.error("Failed to create dist directory {}", distDir); + throw new IOException("Failed to create dist directory"); + } + } + } + + private void copyDocs() throws IllegalArgumentException, IOException { + File docs = new File(docsDir); + if (!docs.exists()) { + logger.error("Docs directory path{} does not exist", docsDir); + throw new IllegalArgumentException("Docs directory does not exist"); + } + if (!docs.isDirectory()) { + logger.error("Docs path {} is not a directory", docsDir); + throw new IllegalArgumentException("Docs path is not a directory"); + } + + String outputPath = Paths.get(distDir, "docs").toString(); + logger.info("Copying docs from {} to {}", docsDir, outputPath); + File output = new File(outputPath); + FileUtils.copyDirectory(docs, output); + } + + private void writeTablesJson() throws Exception { + if (plugin.getKind() != PluginKind.Source) { + return; + } + + String outputPath = Paths.get(distDir, "tables.json").toString(); + logger.info("Writing tables.json to {}", outputPath); + plugin.init("", NewClientOptions.builder().noConnection(true).build()); + List
tables = plugin.tables(Arrays.asList("*"), Arrays.asList(), false); + List
flattenTables = Table.flattenTables(tables); + TablesJson tablesJson = new TablesJson(flattenTables); + String json = tablesJson.toJson(); + FileUtils.writeStringToFile(new File(outputPath), json, "UTF-8"); + } + + @SuppressWarnings("null") + private List buildDocker() + throws IllegalArgumentException, IOException, InterruptedException { + String dockerFilePath = Paths.get(pluginDirectory, plugin.getDockerfile()).toString(); + File dockerFile = new File(dockerFilePath); + if (!dockerFile.exists()) { + logger.error("Dockerfile {} does not exist", dockerFilePath); + throw new IllegalArgumentException("Dockerfile does not exist"); + } + if (!dockerFile.isFile()) { + logger.error("Dockerfile {} is not a file", dockerFilePath); + throw new IllegalArgumentException("Dockerfile is not a file"); + } + + List supportedTargets = new ArrayList<>(); + for (BuildTarget target : plugin.getBuildTargets()) { + String imageRepository = + String.format( + "docker.cloudquery.io/%s/%s-%s", + plugin.getTeam(), plugin.getKind(), plugin.getName()); + String os = target.getOs().toString(); + String arch = target.getArch().toString(); + String imageTag = String.format("%s:%s-%s-%s", imageRepository, pluginVersion, os, arch); + String imageTar = + String.format("plugin-%s-%s-%s-%s.tar", plugin.getName(), pluginVersion, os, arch); + String imagePath = Paths.get(distDir, imageTar).toString(); + logger.info("Building docker image {}", imageTag); + // GITHUB_ACTOR and GITHUB_TOKEN are required for the Dockerfile to pull the CloudQuery Java + // libs from GitHub Packages + String githubActor = System.getenv("GITHUB_ACTOR"); + if (Strings.isNullOrEmpty(githubActor)) { + logger.error("GITHUB_ACTOR env variable is required"); + throw new IllegalArgumentException("GITHUB_ACTOR env variable is required"); + } + String githubToken = System.getenv("GITHUB_TOKEN"); + if (Strings.isNullOrEmpty(githubToken)) { + logger.error("GITHUB_TOKEN env variable is required"); + throw new IllegalArgumentException("GITHUB_TOKEN env variable is required"); + } + String[] dockerBuildArguments = { + "buildx", + "build", + "-t", + imageTag, + "--platform", + String.format("%s/%s", os, arch), + "-f", + dockerFilePath, + ".", + "--progress", + "plain", + "--load", + "--build-arg", + String.format("GITHUB_ACTOR=%s", githubActor), + "--build-arg", + String.format("GITHUB_TOKEN=%s", githubToken), + }; + logger.debug("Running docker command: '{}'", String.join(" ", dockerBuildArguments)); + runCommand(dockerBuildArguments); + logger.debug("Saving docker image {} to {}", imageTag, imagePath); + String[] dockerSaveArguments = {"save", "-o", imagePath, imageTag}; + logger.debug("Running docker command: '{}'", String.join(" ", dockerSaveArguments)); + runCommand(dockerSaveArguments); + try (InputStream is = Files.newInputStream(Paths.get(imagePath))) { + String checksum = DigestUtils.sha256Hex(is); + SupportedTargetJson supportedTarget = new SupportedTargetJson(os, arch, imageTar, checksum); + supportedTargets.add(supportedTarget); + } + } + + return supportedTargets; + } + + private void runCommand(String[] command) throws IOException, InterruptedException { + ArrayList allArgs = new ArrayList<>(Arrays.asList(command)); + allArgs.add(0, "docker"); + ProcessBuilder processBuilder = + new ProcessBuilder(allArgs.toArray(new String[allArgs.size()])).inheritIO(); + processBuilder.directory(new File(pluginDirectory)); + Process process = processBuilder.start(); + int exitCode = process.waitFor(); + if (exitCode != 0) { + logger.error("Failed to run command: '{}'", String.join(" ", command)); + throw new IOException("Failed to run command"); + } + } + + private void writePackageJson(List supportedTargets) throws IOException { + String outputPath = Paths.get(distDir, "package.json").toString(); + logger.info("Writing package.json to {}", outputPath); + PackageJson packageJson = + new PackageJson( + plugin.getName(), + plugin.getTeam(), + plugin.getKind().toString(), + pluginVersion, + message, + supportedTargets); + String json = packageJson.toJson(); + FileUtils.writeStringToFile(new File(outputPath), json, "UTF-8"); + } +} diff --git a/lib/src/main/java/io/cloudquery/server/PackageJson.java b/lib/src/main/java/io/cloudquery/server/PackageJson.java new file mode 100644 index 00000000..1092cbf6 --- /dev/null +++ b/lib/src/main/java/io/cloudquery/server/PackageJson.java @@ -0,0 +1,30 @@ +package io.cloudquery.server; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import java.util.List; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class PackageJson { + @NonNull private final String name; + @NonNull private final String team; + @NonNull private final String kind; + @NonNull private final String version; + @NonNull private final String message; + @NonNull private final List supported_targets; + + private final int schema_version = 1; + private int[] protocols = {3}; + private String package_type = "docker"; + + public String toJson() throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + return objectMapper.writeValueAsString(this); + } +} diff --git a/lib/src/main/java/io/cloudquery/server/PluginServe.java b/lib/src/main/java/io/cloudquery/server/PluginServe.java index f308d92e..b1153421 100644 --- a/lib/src/main/java/io/cloudquery/server/PluginServe.java +++ b/lib/src/main/java/io/cloudquery/server/PluginServe.java @@ -14,6 +14,9 @@ public class PluginServe { public int Serve() { Extensions.registerExtensions(); - return new CommandLine(new RootCommand()).addSubcommand(new ServeCommand(plugin)).execute(args); + CommandLine cli = new CommandLine(new RootCommand()); + cli.addSubcommand(new ServeCommand(plugin)); + cli.addSubcommand(new PackageCommand(plugin)); + return cli.execute(args); } } diff --git a/lib/src/main/java/io/cloudquery/server/SupportedTargetJson.java b/lib/src/main/java/io/cloudquery/server/SupportedTargetJson.java new file mode 100644 index 00000000..ac228330 --- /dev/null +++ b/lib/src/main/java/io/cloudquery/server/SupportedTargetJson.java @@ -0,0 +1,14 @@ +package io.cloudquery.server; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; + +@AllArgsConstructor +@Getter +public class SupportedTargetJson { + @NonNull private final String os; + @NonNull private final String arch; + @NonNull private final String path; + @NonNull private final String checksum; +} diff --git a/lib/src/main/java/io/cloudquery/server/TablesJson.java b/lib/src/main/java/io/cloudquery/server/TablesJson.java new file mode 100644 index 00000000..e57b857b --- /dev/null +++ b/lib/src/main/java/io/cloudquery/server/TablesJson.java @@ -0,0 +1,68 @@ +package io.cloudquery.server; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import io.cloudquery.schema.Column; +import io.cloudquery.schema.Table; +import java.util.ArrayList; +import java.util.List; + +public class TablesJson { + public class ColumnJson { + public String name; + public String description; + public String type; + public boolean incremental_key; + public boolean not_null; + public boolean primary_key; + public boolean unique; + } + + public class TableJson { + public String name; + public String description; + public boolean is_incremental; + public String parent; + public List relations; + public List columns; + } + + private List
tables; + + public TablesJson(List
tables) { + this.tables = tables; + } + + public String toJson() throws JsonProcessingException { + List json = new ArrayList<>(); + for (Table table : tables) { + TableJson tableJson = new TableJson(); + tableJson.name = table.getName(); + tableJson.description = table.getDescription(); + tableJson.is_incremental = false; + tableJson.parent = table.getParent() == null ? null : table.getParent().getName(); + tableJson.relations = new ArrayList<>(); + for (Table relation : table.getRelations()) { + tableJson.relations.add(relation.getName()); + } + tableJson.columns = new ArrayList<>(); + for (Column column : table.getColumns()) { + ColumnJson columnJson = new ColumnJson(); + columnJson.name = column.getName(); + columnJson.description = column.getDescription(); + columnJson.type = column.getType().toString(); + columnJson.incremental_key = column.isIncrementalKey(); + columnJson.not_null = column.isNotNull(); + columnJson.primary_key = column.isPrimaryKey(); + columnJson.unique = column.isUnique(); + tableJson.columns.add(columnJson); + } + json.add(tableJson); + } + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + return objectMapper.writeValueAsString(json); + } +} diff --git a/lib/src/test/java/io/cloudquery/server/PluginPackageTest.java b/lib/src/test/java/io/cloudquery/server/PluginPackageTest.java new file mode 100644 index 00000000..fe5f34ed --- /dev/null +++ b/lib/src/test/java/io/cloudquery/server/PluginPackageTest.java @@ -0,0 +1,26 @@ +package io.cloudquery.server; + +import io.cloudquery.memdb.MemDB; +import io.cloudquery.server.PluginServe.PluginServeBuilder; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.jupiter.api.Test; + +public class PluginPackageTest { + @Test + public void packageTest() { + Path file = Paths.get("..").toAbsolutePath().normalize(); + String absolutePath = file.toString(); + + PluginServe pluginServe = + new PluginServeBuilder() + .plugin(new MemDB()) + .args( + new String[] { + "package", "--log-level", "debug", "-m", "initial version", "v1.0.0", absolutePath + }) + .build(); + int exitCode = pluginServe.Serve(); + assert exitCode == 0; + } +}