From 76e8adf0533c3ad4b81acf439bc0d1f63a880c99 Mon Sep 17 00:00:00 2001 From: Jake Wharton Date: Fri, 5 Sep 2025 09:32:31 -0400 Subject: [PATCH] Add timeout option, default to 10m --- CHANGELOG.md | 1 + src/main/kotlin/com/jakewharton/gitout/Engine.kt | 5 +++-- src/main/kotlin/com/jakewharton/gitout/main.kt | 8 ++++++++ src/main/kotlin/com/jakewharton/gitout/util.kt | 14 ++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/com/jakewharton/gitout/util.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index e66fe51..d5682a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ New: - Rewrite the app in Kotlin (from Rust). Sorry (not sorry). - The binary distribution (which now requires a JVM) supports scheduled sync with the `--cron` option. +- Add `--timeout` option / `GITOUT_TIMEOUT` env var to control limit on git operations. Default is 10 minutes. Changed: - The `CRON`, `HEALTHCHECK_ID`, and `HEALTHCHECK_HOST` Docker container environment variables are now named `GITOUT_CRON`, `GITOUT_HC_ID`, and `GITOUT_HC_HOST`, respectively. These will also be honored by the standalone binary. diff --git a/src/main/kotlin/com/jakewharton/gitout/Engine.kt b/src/main/kotlin/com/jakewharton/gitout/Engine.kt index ffcf9f4..09ac063 100644 --- a/src/main/kotlin/com/jakewharton/gitout/Engine.kt +++ b/src/main/kotlin/com/jakewharton/gitout/Engine.kt @@ -3,7 +3,6 @@ package com.jakewharton.gitout import java.lang.ProcessBuilder.Redirect.INHERIT import java.nio.file.Files import java.nio.file.Path -import java.util.concurrent.TimeUnit.MINUTES import kotlin.io.path.absolutePathString import kotlin.io.path.exists import kotlin.io.path.isDirectory @@ -11,12 +10,14 @@ import kotlin.io.path.name import kotlin.io.path.notExists import kotlin.io.path.readText import kotlin.io.path.writeText +import kotlin.time.Duration import okhttp3.HttpUrl import okhttp3.OkHttpClient internal class Engine( private val config: Path, private val destination: Path, + private val timeout: Duration, private val logger: Logger, private val client: OkHttpClient, private val healthCheck: HealthCheck?, @@ -181,7 +182,7 @@ internal class Engine( .directory(directory.toFile()) .redirectError(INHERIT) .start() - check(process.waitFor(5, MINUTES)) { "Unable to sync $url into $repo: 5m timeout" } + check(process.waitFor(timeout)) { "Unable to sync $url into $repo: timeout $timeout" } check(process.exitValue() == 0) { "Unable to sync $url into $repo: exit ${process.exitValue()}" } } } diff --git a/src/main/kotlin/com/jakewharton/gitout/main.kt b/src/main/kotlin/com/jakewharton/gitout/main.kt index fbe74e8..c5b6ffd 100644 --- a/src/main/kotlin/com/jakewharton/gitout/main.kt +++ b/src/main/kotlin/com/jakewharton/gitout/main.kt @@ -20,6 +20,8 @@ import io.github.kevincianfarini.cardiologist.schedulePulse import java.nio.file.FileSystem import java.nio.file.FileSystems import kotlin.time.Clock +import kotlin.time.Duration +import kotlin.time.Duration.Companion.minutes import kotlinx.datetime.TimeZone import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient @@ -50,6 +52,11 @@ private class GitOutCommand( .path(mustExist = true, canBeFile = false, fileSystem = fs) .help("Backup directory") + private val timeout by option(envvar = "GITOUT_TIMEOUT") + .convert { Duration.parse(it) } + .default(10.minutes) + .help("Timeout for git clone/update operations (default: 10m)") + private val verbosity by option("--verbose", "-v") .counted(limit = 3) .help("Increase logging verbosity. -v = informational, -vv = debug, -vvv = trace") @@ -97,6 +104,7 @@ private class GitOutCommand( val engine = Engine( config = config, destination = destination, + timeout = timeout, logger = logger, client = client, healthCheck = healthCheck, diff --git a/src/main/kotlin/com/jakewharton/gitout/util.kt b/src/main/kotlin/com/jakewharton/gitout/util.kt new file mode 100644 index 0000000..6cfec03 --- /dev/null +++ b/src/main/kotlin/com/jakewharton/gitout/util.kt @@ -0,0 +1,14 @@ +package com.jakewharton.gitout + +import java.util.concurrent.TimeUnit.SECONDS +import kotlin.time.Duration + +internal fun Process.waitFor(timeout: Duration): Boolean { + return timeout.toComponents { seconds, nanoseconds -> + var seconds = seconds + if (nanoseconds != 0 && seconds < Long.MAX_VALUE) { + seconds += 1 + } + waitFor(seconds, SECONDS) + } +}