mirror of https://github.com/micromata/borgbackup-butler.git

Kai Reinhard
15.39.2021 d80a4b3ab35af575fd06d4e2fb9a3726418c6727
Package typo, java -> kotlin
2 files deleted
2 files added
4 files renamed
15 files modified
734 ■■■■ changed files
README.adoc 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/BorgJob.java 154 ●●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java 4 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/cache/JCSCache.java 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/demo/DemoRepos.java 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractCommandLineJob.java 202 ●●●●● patch | view | raw | blame | history
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/BorgJob.kt 135 ●●●●● patch | view | raw | blame | history
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Configuration.kt 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/ConfigurationHandler.kt 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Legacy.kt 5 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/YamlUtils.kt 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/jobs/AbstractCommandLineJob.kt 194 ●●●●● patch | view | raw | blame | history
borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java 4 ●●●● patch | view | raw | blame | history
borgbutler-core/src/test/java/de/micromata/borgbutler/config/ConfigHandlerTest.java 2 ●●● patch | view | raw | blame | history
borgbutler-docker/app/Dockerfile 2 ●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java 4 ●●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/Main.java 2 ●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/ServerConfiguration.java 4 ●●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java 2 ●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/BorgRepoConfigsRest.java 2 ●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ConfigurationRest.java 2 ●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java 2 ●●● patch | view | raw | blame | history
borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java 2 ●●● patch | view | raw | blame | history
README.adoc
@@ -60,7 +60,7 @@
BorgButler working directory `$HOME/BorgButler` is assumed, but you may define any other.
1. Create your local BorgButler directory: `mkdir $HOME/BorgButler` (for config, caches, backups and restoring of backuped files and directories)
2. `docker run -v $HOME/BorgButler:/Borgbutler -p 127.0.0.1:9042:9042 --name borgbuttler kreinhard/borgbutler` (exporting of `.ssh` is useful for ssh remotes, otherwise skip this setting.)
2. `docker run -v $HOME/BorgButler:/BorgButler -v  $HOME/.ssh:/home/borgbutler/.ssh -p 127.0.0.1:9042:9042 --name borgbuttler kreinhard/borgbutler` (exporting of `.ssh` is useful for ssh remotes, otherwise skip this setting.)
3. Stopping: simly click `CTRL-C`.
4. Restart: `docker start`
5. Stop: `docker stop`
borgbutler-core/src/main/java/de/micromata/borgbutler/BorgJob.java
File was deleted
borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java
@@ -3,8 +3,8 @@
import de.micromata.borgbutler.BorgCommandResult;
import de.micromata.borgbutler.BorgCommands;
import de.micromata.borgbutler.config.BorgRepoConfig;
import org.micromata.borgbutler.config.Configuration;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.Configuration;
import de.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.data.Archive;
import de.micromata.borgbutler.data.ArchiveShortInfo;
import de.micromata.borgbutler.data.FileSystemFilter;
borgbutler-core/src/main/java/de/micromata/borgbutler/cache/JCSCache.java
@@ -1,6 +1,6 @@
package de.micromata.borgbutler.cache;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.ConfigurationHandler;
import org.apache.commons.jcs.JCS;
import org.apache.commons.jcs.access.CacheAccess;
import org.slf4j.Logger;
borgbutler-core/src/main/java/de/micromata/borgbutler/demo/DemoRepos.java
@@ -3,7 +3,7 @@
import de.micromata.borgbutler.BorgCommand;
import de.micromata.borgbutler.BorgJob;
import de.micromata.borgbutler.config.BorgRepoConfig;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.Definitions;
import de.micromata.borgbutler.data.Repository;
import de.micromata.borgbutler.jobs.JobResult;
borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractCommandLineJob.java
File was deleted
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/BorgJob.kt
New file
@@ -0,0 +1,135 @@
package de.micromata.borgbutler
import de.micromata.borgbutler.config.ConfigurationHandler.Companion.getConfiguration
import de.micromata.borgbutler.demo.DemoRepos
import de.micromata.borgbutler.jobs.AbstractCommandLineJob
import de.micromata.borgbutler.jobs.JobResult
import de.micromata.borgbutler.json.JsonUtils
import de.micromata.borgbutler.json.borg.ProgressInfo
import org.apache.commons.exec.CommandLine
import org.apache.commons.exec.environment.EnvironmentUtils
import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory
import java.io.IOException
/**
 * A queue is important because Borg doesn't support parallel calls for one repository.
 * For each repository one single queue is allocated.
 */
open class BorgJob<T> : AbstractCommandLineJob, Cloneable {
    private val log = LoggerFactory.getLogger(BorgJob::class.java)
    var command: BorgCommand? = null
        private set
    /**
     * Some jobs may store here the result of the command (e. g. [BorgCommands.listArchiveContent]).
     */
    @JvmField
    var payload: T? = null
    var progressInfo: ProgressInfo? = null
        private set
    constructor(command: BorgCommand) {
        this.command = command
        setWorkingDirectory(command.workingDir)
        setDescription(command.description)
    }
    private constructor() {}
    override fun buildCommandLine(): CommandLine? {
        if (command == null) {
            return null
        }
        val borgCommand = getConfiguration()!!.borgCommand
        if (StringUtils.isBlank(borgCommand)) {
            log.error("Can't run empty borg command.")
            return null
        }
        val commandLine = CommandLine(borgCommand)
        commandLine.addArgument(command!!.command)
        if (command!!.params != null) {
            for (param in command!!.params) {
                if (param != null) commandLine.addArgument(param)
            }
        }
        if (command!!.repoArchive != null) {
            commandLine.addArgument(command!!.repoArchive)
        }
        if (command!!.args != null) {
            for (arg in command!!.args) {
                if (arg != null) commandLine.addArgument(arg)
            }
        }
        return commandLine
    }
    override fun processStdErrLine(line: String, level: Int) {
        if (StringUtils.startsWith(line, "{\"message")) {
            val message = JsonUtils.fromJson(ProgressInfo::class.java, line)
            if (message != null) {
                progressInfo = message
                return
            }
        }
        super.processStdErrLine(line, level)
    }
    // For MacOS BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
    @get:Throws(IOException::class)
    override val environment: Map<String?, String?>?
        protected get() {
            val repoConfig = command!!.repoConfig ?: return null
            val env = EnvironmentUtils.getProcEnvironment()
            val variables = repoConfig.getEnvironmentVariables(true)
            for (variable in variables) {
                // For MacOS BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
                val environmentVariable = variable.replace("\$USER", System.getProperty("user.name"))
                addEnvironmentVariable(env, environmentVariable)
            }
            return env
        }
    override fun execute(): JobResult<String>? {
        return if (command!!.repoConfig != null && DemoRepos.isDemo(command!!.repoConfig.repo)) {
            DemoRepos.execute(this)
        } else super.execute()
    }
    public override fun clone(): BorgJob<*> {
        val clone: BorgJob<*> = BorgJob<Any>()
        if (command != null) {
            // Needed for getting environment variables: JsonJob of borgbutler-server.
            clone.command = BorgCommand().setRepoConfig(command!!.repoConfig)
        }
        clone.uniqueJobNumber = uniqueJobNumber
        clone.title = title
        clone.setExecuteStarted(isExecuteStarted)
        clone.setCommandLineAsString(getCommandLineAsString())
        clone.isCancellationRequested = isCancellationRequested
        clone.status = status
        clone.setWorkingDirectory(workingDirectory)
        clone.setDescription(description)
        if (progressInfo != null) {
            clone.setProgressInfo(progressInfo!!.clone())
        }
        clone.createTime = createTime
        clone.startTime = startTime
        clone.stopTime = stopTime
        return clone
    }
    override fun cleanUp() {
        super.cleanUp()
        payload = null
    }
    fun getPayload(): T? {
        return payload
    }
    protected fun setProgressInfo(progressInfo: ProgressInfo?): BorgJob<T> {
        this.progressInfo = progressInfo
        return this
    }
}
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Configuration.kt
File was renamed from borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/Configuration.kt
@@ -1,4 +1,4 @@
package org.micromata.borgbutler.config
package de.micromata.borgbutler.config
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonProperty
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/ConfigurationHandler.kt
File was renamed from borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/ConfigurationHandler.kt
@@ -1,4 +1,4 @@
package org.micromata.borgbutler.config
package de.micromata.borgbutler.config
import de.micromata.borgbutler.config.Definitions
import de.micromata.borgbutler.json.JsonUtils
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Legacy.kt
File was renamed from borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/Legacy.kt
@@ -1,12 +1,9 @@
package org.micromata.borgbutler.config
package de.micromata.borgbutler.config
import de.micromata.borgbutler.config.Definitions
import de.micromata.borgbutler.json.JsonUtils
import mu.KotlinLogging
import org.apache.commons.io.FileUtils
import org.slf4j.LoggerFactory
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/YamlUtils.kt
File was renamed from borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/YamlUtils.kt
@@ -1,4 +1,4 @@
package org.micromata.borgbutler.config
package de.micromata.borgbutler.config
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.core.type.TypeReference
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/jobs/AbstractCommandLineJob.kt
New file
@@ -0,0 +1,194 @@
package de.micromata.borgbutler.jobs
import de.micromata.borgbutler.config.Definitions
import mu.KotlinLogging
import org.apache.commons.exec.*
import org.apache.commons.exec.environment.EnvironmentUtils
import org.apache.commons.io.output.ByteArrayOutputStream
import org.apache.commons.lang3.StringUtils
import java.io.File
import java.io.IOException
private val log = KotlinLogging.logger {}
/**
 * A queue is important because Borg doesn't support parallel calls for one repository.
 * For each repository one single queue is allocated.
 */
abstract class AbstractCommandLineJob : AbstractJob<String>() {
    private var watchdog: ExecuteWatchdog? = null
    var isExecuteStarted = false
        private set
    private var commandLine: CommandLine? = null
    /**
     * The command line as string. This property is also used as ID for detecting multiple borg calls.
     */
    private var commandLineAsString: String? = null
    var workingDirectory: File? = null
        private set
    var description: String? = null
        private set
    protected var outputStream = ByteArrayOutputStream()
    protected var errorOutputStream = ByteArrayOutputStream()
    @JvmField
    protected var logError = true
    protected abstract fun buildCommandLine(): CommandLine?
    override fun getId(): Any {
        return getCommandLineAsString()!!
    }
    fun getCommandLineAsString(): String? {
        if (commandLine == null) {
            commandLine = buildCommandLine()
        }
        if (commandLine == null) {
            return null
        }
        if (commandLineAsString == null) {
            commandLineAsString = commandLine!!.executable + " " + StringUtils.join(
                commandLine!!.arguments, " "
            )
        }
        return commandLineAsString
    }
    override fun execute(): JobResult<String>? {
        getCommandLineAsString()
        if (commandLine == null) {
            return null
        }
        val executor = DefaultExecutor()
        if (workingDirectory != null) {
            executor.workingDirectory = workingDirectory
        }
        //executor.setExitValue(2);
        watchdog = ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT)
        executor.watchdog = watchdog
        //  ExecuteResultHandler handler = new DefaultExecuteResultHandler();
        val streamHandler = PumpStreamHandler(object : LogOutputStream() {
            override fun processLine(line: String, level: Int) {
                processStdOutLine(line, level)
            }
        }, object : LogOutputStream() {
            override fun processLine(line: String, level: Int) {
                processStdErrLine(line, level)
            }
        })
        executor.streamHandler = streamHandler
        val msg =
            if (StringUtils.isNotBlank(description)) "$description ('$commandLineAsString')..." else "Executing '$commandLineAsString'..."
        log.info(msg)
        //log.info("Environment: ${environment?.entries?.joinToString { "${it.key}='${it.value}'" }}")
        isExecuteStarted = true
        val result = JobResult<String>()
        try {
            executor.execute(commandLine, environment)
            result.status = JobResult.Status.OK
            log.info("$msg Done.")
        } catch (ex: Exception) {
            result.status = JobResult.Status.ERROR
            if (logError && !isCancellationRequested && status != Status.CANCELLED) {
                log.error("Execution failed for job: '" + commandLineAsString + "': " + ex.message)
                log.error(
                    "Error output of job '" + commandLineAsString + "': "
                            + getErrorString(2000)
                )
            }
            failed()
        }
        result.resultObject = outputStream.toString(Definitions.STD_CHARSET)
        return result
    }
    /**
     * @param maxlength The result string will be abbreviated (in the middle).
     * @return
     * @see StringUtils.abbreviateMiddle
     */
    fun getErrorString(maxlength: Int): String {
        return StringUtils.abbreviateMiddle(
            errorOutputStream.toString(Definitions.STD_CHARSET),
            "\n    [... ***** error log abbreviated ***** ...]\n", maxlength
        )
    }
    open fun processStdOutLine(line: String, level: Int) {
        //log.info(line);
        try {
            outputStream.write(line.toByteArray())
            outputStream.write("\n".toByteArray())
        } catch (ex: IOException) {
            log.error(ex.message, ex)
        }
    }
    open fun processStdErrLine(line: String, level: Int) {
        //log.info(line);
        try {
            errorOutputStream.write(line.toByteArray())
            errorOutputStream.write("\n".toByteArray())
        } catch (ex: IOException) {
            log.error(ex.message, ex)
        }
    }
    override fun cancelRunningProcess() {
        if (watchdog != null) {
            log.info("Cancelling job #$uniqueJobNumber: $id")
            watchdog!!.destroyProcess()
            watchdog = null
        }
    }
    @get:Throws(IOException::class)
    protected open val environment: Map<String?, String?>?
        get() = null
    protected fun addEnvironmentVariable(env: Map<String?, String?>?, name: String, value: String) {
        if (StringUtils.isNotBlank(value)) {
            EnvironmentUtils.addVariableToEnvironment(env, "$name=$value")
        }
    }
    /**
     * @param env
     * @param variable Variable in format "variable=value".
     */
    protected fun addEnvironmentVariable(env: Map<String?, String?>?, variable: String?) {
        if (StringUtils.isNotBlank(variable)) {
            EnvironmentUtils.addVariableToEnvironment(env, variable)
        }
    }
    /**
     * Frees the output streams.
     * Should be called after a job was done, failed or cancelled while running.
     */
    open fun cleanUp() {
        log.debug("Freeing resources of job: $commandLineAsString")
        outputStream = ByteArrayOutputStream()
        errorOutputStream = ByteArrayOutputStream()
    }
    protected fun setExecuteStarted(executeStarted: Boolean): AbstractCommandLineJob {
        isExecuteStarted = executeStarted
        return this
    }
    protected fun setCommandLineAsString(commandLineAsString: String?): AbstractCommandLineJob {
        this.commandLineAsString = commandLineAsString
        return this
    }
    fun setWorkingDirectory(workingDirectory: File?): AbstractCommandLineJob {
        this.workingDirectory = workingDirectory
        return this
    }
    fun setDescription(description: String?): AbstractCommandLineJob {
        this.description = description
        return this
    }
}
borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java
@@ -1,8 +1,8 @@
package de.micromata.borgbutler.cache;
import de.micromata.borgbutler.config.BorgRepoConfig;
import org.micromata.borgbutler.config.Configuration;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.Configuration;
import de.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.data.Archive;
import de.micromata.borgbutler.data.Repository;
import de.micromata.borgbutler.json.borg.BorgFilesystemItem;
borgbutler-core/src/test/java/de/micromata/borgbutler/config/ConfigHandlerTest.java
@@ -2,7 +2,7 @@
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.Test;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.ConfigurationHandler;
import java.io.File;
import java.io.IOException;
borgbutler-docker/app/Dockerfile
@@ -30,6 +30,6 @@
# Variable expansion doesn't work for ENTRYPOINT definition as array, but array is required, because graceful shutdown of
# container isn't given if java is started via 'sh -c' as it will be done by ENTRYPOINT java .....
# Java options are modifiable by user through own ENTRYPOINT definition on docker run or in docker-compose.yml.
ENTRYPOINT ["java", "-Xms1g", "-Xmx1g", "-cp", "app/web/*:app/lib/*", "-DborgbutlerHome=/BorgButler/", "-DapplicationHome=/app", "-DbindAddress=0.0.0.0", "-DallowedClientIps=172.17.", "de.micromata.borgbutler.server.Main", "-q"]
ENTRYPOINT ["java", "-Xms4g", "-Xmx4g", "-cp", "app/web/*:app/lib/*", "-DborgbutlerHome=/BorgButler/", "-DapplicationHome=/app", "-DbindAddress=0.0.0.0", "-DallowedClientIps=172.17.", "de.micromata.borgbutler.server.Main", "-q"]
MAINTAINER Micromata
borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java
@@ -1,8 +1,8 @@
package de.micromata.borgbutler.server;
import de.micromata.borgbutler.BorgCommands;
import org.micromata.borgbutler.config.Configuration;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.Configuration;
import de.micromata.borgbutler.config.ConfigurationHandler;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
borgbutler-server/src/main/java/de/micromata/borgbutler/server/Main.java
@@ -1,7 +1,7 @@
package de.micromata.borgbutler.server;
import de.micromata.borgbutler.cache.ButlerCache;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.json.borg.BorgFilesystemItem;
import de.micromata.borgbutler.server.jetty.JettyServer;
import de.micromata.borgbutler.server.user.SingleUserManager;
borgbutler-server/src/main/java/de/micromata/borgbutler/server/ServerConfiguration.java
@@ -1,8 +1,8 @@
package de.micromata.borgbutler.server;
import org.apache.commons.lang3.StringUtils;
import org.micromata.borgbutler.config.Configuration;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.Configuration;
import de.micromata.borgbutler.config.ConfigurationHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java
@@ -3,7 +3,7 @@
import de.micromata.borgbutler.BorgCommands;
import de.micromata.borgbutler.cache.ButlerCache;
import de.micromata.borgbutler.config.BorgRepoConfig;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.data.Archive;
import de.micromata.borgbutler.data.DiffFileSystemFilter;
import de.micromata.borgbutler.data.FileSystemFilter;
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/BorgRepoConfigsRest.java
@@ -4,7 +4,7 @@
import de.micromata.borgbutler.BorgCommands;
import de.micromata.borgbutler.cache.ButlerCache;
import de.micromata.borgbutler.config.BorgRepoConfig;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.data.Repository;
import de.micromata.borgbutler.jobs.JobResult;
import de.micromata.borgbutler.json.JsonUtils;
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ConfigurationRest.java
@@ -1,7 +1,7 @@
package de.micromata.borgbutler.server.rest;
import de.micromata.borgbutler.cache.ButlerCache;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.json.JsonUtils;
import de.micromata.borgbutler.server.BorgInstallation;
import de.micromata.borgbutler.server.ServerConfiguration;
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java
@@ -3,7 +3,7 @@
import de.micromata.borgbutler.BorgJob;
import de.micromata.borgbutler.BorgQueueExecutor;
import de.micromata.borgbutler.config.BorgRepoConfig;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.jobs.AbstractJob;
import de.micromata.borgbutler.json.JsonUtils;
import de.micromata.borgbutler.json.borg.ProgressInfo;
borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java
@@ -1,6 +1,6 @@
package de.micromata.borgbutler.server;
import org.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.ConfigurationHandler;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;