summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesper Jensen <jesper@jnsn.dev>2026-01-28 08:22:11 +0100
committerJesper Jensen <jesper@jnsn.dev>2026-01-28 08:22:11 +0100
commitf4459a5c46e21b69e9878d47e403c92f399eb745 (patch)
tree24d54c6aa8f22a4ebfe3aa70dda2f6b9c83e1354
parent8c8f8035a2dcea22057c967ffc1ef774a6999f77 (diff)
Make it a server
-rw-r--r--Cargo.lock444
-rw-r--r--Cargo.toml3
-rw-r--r--src/main.rs468
3 files changed, 717 insertions, 198 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6079651..43ab354 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -18,18 +18,45 @@ dependencies = [
]
[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "arraydeque"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
+name = "bitflags"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
+
+[[package]]
+name = "bumpalo"
+version = "3.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
+
+[[package]]
name = "bytes"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -52,6 +79,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
+name = "chrono"
+version = "0.4.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-link",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -70,6 +116,18 @@ dependencies = [
]
[[package]]
+name = "fallible-iterator"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
name = "find-msvc-tools"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -86,12 +144,24 @@ dependencies = [
]
[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
+name = "foldhash"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+
+[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -120,7 +190,16 @@ version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
- "foldhash",
+ "foldhash 0.1.5",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+dependencies = [
+ "foldhash 0.2.0",
]
[[package]]
@@ -129,7 +208,16 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
dependencies = [
- "hashbrown",
+ "hashbrown 0.15.5",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230"
+dependencies = [
+ "hashbrown 0.16.1",
]
[[package]]
@@ -149,24 +237,83 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
+name = "iana-time-zone"
+version = "0.1.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
+name = "js-sys"
+version = "0.3.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
name = "json-event-parser"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73267b6bffa5356bd46cfa89386673e9a7f62f4eb3adcb45b1bd031892357853"
[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
name = "libc"
version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]]
+name = "libsqlite3-sys"
+version = "0.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95b4103cffefa72eb8428cb6b47d6627161e51c2739fc5e3b734584157bc642a"
+dependencies = [
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -189,18 +336,56 @@ dependencies = [
]
[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
+name = "parking_lot"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-link",
+]
+
+[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -219,6 +404,41 @@ dependencies = [
]
[[package]]
+name = "prometheus"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ca5326d8d0b950a9acd87e6a3f94745394f62e4dae1b1ee22b2bc0c394af43a"
+dependencies = [
+ "cfg-if",
+ "fnv",
+ "lazy_static",
+ "memchr",
+ "parking_lot",
+ "protobuf",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "protobuf"
+version = "3.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4"
+dependencies = [
+ "once_cell",
+ "protobuf-support",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "protobuf-support"
+version = "3.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6"
+dependencies = [
+ "thiserror 1.0.69",
+]
+
+[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -263,6 +483,15 @@ dependencies = [
]
[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
name = "regex"
version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -306,6 +535,32 @@ dependencies = [
]
[[package]]
+name = "rsqlite-vfs"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8a1f2315036ef6b1fbacd1972e8ee7688030b0a2121edfc2a6550febd41574d"
+dependencies = [
+ "hashbrown 0.16.1",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "rusqlite"
+version = "0.38.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1c93dd1c9683b438c392c492109cb702b8090b2bfc8fed6f6e4eb4523f17af3"
+dependencies = [
+ "bitflags",
+ "chrono",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink 0.11.0",
+ "libsqlite3-sys",
+ "smallvec",
+ "sqlite-wasm-rs",
+]
+
+[[package]]
name = "rustls"
version = "0.23.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -341,6 +596,18 @@ dependencies = [
]
[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -353,6 +620,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "sqlite-wasm-rs"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f4206ed3a67690b9c29b77d728f6acc3ce78f16bf846d83c94f76400320181b"
+dependencies = [
+ "cc",
+ "js-sys",
+ "rsqlite-vfs",
+ "wasm-bindgen",
+]
+
+[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -370,6 +655,46 @@ dependencies = [
]
[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+dependencies = [
+ "thiserror-impl 2.0.18",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "tinyjson"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -423,13 +748,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
name = "version-bumper"
version = "0.1.0"
dependencies = [
"base64",
+ "chrono",
"json-event-parser",
+ "prometheus",
"rand",
"regex",
+ "rusqlite",
"tinyjson",
"ureq",
"yaml-rust2",
@@ -451,6 +785,51 @@ dependencies = [
]
[[package]]
+name = "wasm-bindgen"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
name = "webpki-roots"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -460,6 +839,65 @@ dependencies = [
]
[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -546,7 +984,7 @@ checksum = "2462ea039c445496d8793d052e13787f2b90e750b833afee748e601c17621ed9"
dependencies = [
"arraydeque",
"encoding_rs",
- "hashlink",
+ "hashlink 0.10.0",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 05c8983..4a54256 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,9 +5,12 @@ edition = "2024"
[dependencies]
base64 = "0.22.1"
+chrono = "0.4.43"
json-event-parser = "0.2.2"
+prometheus = "0.14.0"
rand = "0.9.2"
regex = "1.12.2"
+rusqlite = { version = "0.38.0", features = ["chrono"] }
tinyjson = "2.5.1"
ureq = "3.1.4"
yaml-rust2 = "0.10.4"
diff --git a/src/main.rs b/src/main.rs
index a73421c..3fed758 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,6 +3,8 @@ use crate::parser::*;
use base64::prelude::*;
use rand::distr::{Alphanumeric, SampleString};
+use rusqlite::Connection;
+use rusqlite::OptionalExtension;
use yaml_rust2::parser::Parser;
use yaml_rust2::Event;
use std::collections::HashMap;
@@ -147,7 +149,9 @@ fn perform_registry_request(registry: &str, url: &str, accept: &'static str, aut
let url = format!("https://{}{}", registry, &url);
- for _ in 0..2 {
+ let mut authentication_retry = false;
+
+ loop {
let mut request = ureq::get(&url)
.header("Accept", accept)
.config().http_status_as_error(false).build();
@@ -158,14 +162,17 @@ fn perform_registry_request(registry: &str, url: &str, accept: &'static str, aut
let response = request.call().unwrap();
- if response.status() == 401 {
+ if response.status() == 401 && !authentication_retry {
if let Some(ref mut state) = state {
state.authenticate(&response)?;
+ authentication_retry = true;
continue;
} else {
return Err(format!("Server {} returned 401 but we have no credentials", registry));
}
- } else if response.status() != 200 {
+ }
+
+ if response.status() != 200 {
return Err(format!("Unexpected status code: {}", response.status()))
}
@@ -373,7 +380,7 @@ impl VersionPattern {
}
}
-fn update_images(file: &str, auth: &mut Auth, img: DockerRef, edits: &mut Vec<FilePatch>) -> Result<(), String> {
+fn update_images(db: &Connection, file: &str, auth: &mut Auth, img: DockerRef, edits: &mut Vec<FilePatch>) -> Result<(), String> {
let registry = img.registry.map(|x| &file[x]).unwrap_or("registry.hub.docker.com");
let mut tag = img.tag.as_ref().map(|x| file[x.clone()].to_string());
@@ -396,6 +403,11 @@ fn update_images(file: &str, auth: &mut Auth, img: DockerRef, edits: &mut Vec<Fi
for it in body["tags"].get::<Vec<tinyjson::JsonValue>>().unwrap().iter() {
let candidate_str = it.get::<String>().unwrap();
+
+ db.execute("
+ INSERT INTO known_images(registry, image, tag, discovered) VALUES (?1, ?2, ?3, ?4)
+ ", (registry, &file[img.image.clone()], candidate_str, chrono::offset::Utc::now())).unwrap();
+
let candidate = VersionPattern::parse(candidate_str);
match current.compare(&candidate) {
CompareOutcome::Higher => {
@@ -516,6 +528,200 @@ impl std::io::Write for Output {
}
}
+fn run_tool(db: &Connection, auth: &mut Auth, repos: &Vec<Repository>, mut infile_paths: Vec<std::path::PathBuf>, overwrite: bool) {
+ for repo in repos {
+ if repo.dest.exists() {
+ let mut gitcmd = std::process::Command::new("git");
+
+ gitcmd
+ .arg("-C")
+ .arg(&repo.dest)
+ .arg("fetch")
+ .arg("origin");
+
+ if let Some(key) = &repo.key {
+ gitcmd.env("GIT_SSH_COMMAND", format!("ssh -F none -o IdentitiesOnly=yes -i \"{}\"", key.to_str().unwrap()));
+ }
+ let exit = gitcmd.status()
+ .expect("Git command failed");
+ if !exit.success() {
+ panic!("Git exited with failure");
+ }
+
+ let mut gitcmd = std::process::Command::new("git");
+
+ gitcmd
+ .arg("-C")
+ .arg(&repo.dest)
+ .arg("reset")
+ .arg("--hard")
+ .arg("@{u}");
+
+ let exit = gitcmd.status()
+ .expect("Git command failed");
+ if !exit.success() {
+ panic!("Git exited with failure");
+ }
+
+ let mut gitcmd = std::process::Command::new("git");
+
+ gitcmd
+ .arg("-C")
+ .arg(&repo.dest)
+ .arg("clean")
+ .arg("--force")
+ .arg("-d")
+ .arg("-x");
+
+ let exit = gitcmd.status()
+ .expect("Git command failed");
+ if !exit.success() {
+ panic!("Git exited with failure");
+ }
+ } else {
+ let mut gitcmd = std::process::Command::new("git");
+
+ gitcmd
+ .arg("clone")
+ .arg(&repo.url)
+ .arg(&repo.dest);
+
+ if let Some(key) = &repo.key {
+ gitcmd.env("GIT_SSH_COMMAND", format!("ssh -F none -o IdentitiesOnly=yes -i \"{}\"", key.to_str().unwrap()));
+ }
+
+ let exit = gitcmd.status()
+ .expect("Git command failed");
+ if !exit.success() {
+ panic!("Git exited with failure");
+ }
+ }
+
+ infile_paths.push(repo.dest.clone());
+ }
+
+ let mut inpaths = vec!();
+ for infile_path in infile_paths {
+ if infile_path.is_dir() {
+ let mut unsearched = vec![infile_path.clone()];
+
+ while let Some(next) = unsearched.pop() {
+ for child in next.read_dir().unwrap() {
+ let child = child.unwrap();
+ let path = child.path();
+
+ let ft = child.file_type().unwrap();
+ if ft.is_dir() {
+ unsearched.push(path);
+ continue;
+ }
+
+ if let Some(ext) = path.extension() {
+ if ext == "yaml" {
+ inpaths.push(path);
+ }
+ }
+ }
+ }
+ } else {
+ inpaths.push(infile_path);
+ };
+ }
+
+ for infile_path in inpaths {
+ let mut file = std::fs::File::open(&infile_path).unwrap();
+ let mut file_content = String::new();
+ file.read_to_string(&mut file_content).unwrap();
+
+ let images = ManifestFile::parse(&file_content);
+
+ let mut failed = None;
+
+ let mut edits = vec![];
+ for ref image in images.image_tags {
+ println!("Checking image {}", &file_content[image.clone()]);
+ let image_ref = DockerRef::parse(&file_content, image);
+ if let Err(msg) = update_images(db, &file_content, auth, image_ref, &mut edits) {
+ failed = Some(msg);
+ break;
+ }
+ }
+
+ if let Some(msg) = failed {
+ println!("{}: {} ", infile_path.display(), msg);
+ continue;
+ }
+
+ let mut out = if overwrite {
+ Output::overlay_file(&infile_path)
+ } else {
+ Output::Stdout(std::io::stdout())
+ };
+
+ let mut current_position = 0;
+ file.seek(std::io::SeekFrom::Start(0)).unwrap();
+ let mut file_block = file.take(0);
+ for edit in edits {
+ if edit.position.start > current_position {
+ file_block.set_limit((edit.position.start - current_position) as u64);
+ std::io::copy(&mut file_block, &mut out).unwrap();
+ }
+
+ out.write_all(edit.content.as_bytes()).unwrap();
+
+ // We should have been able to just seek to the position.end here, but that doesn't work
+ // for whatever reason. What we can do is calculate the amount to skip ahead, and then do
+ // that.
+ let skip = (edit.position.end - edit.position.start) as i64;
+ file_block.set_limit(skip as u64);
+ file_block.seek(std::io::SeekFrom::Current(skip)).unwrap();
+ current_position = edit.position.end;
+ }
+
+ file_block.set_limit(u64::MAX);
+ std::io::copy(&mut file_block, &mut out).unwrap();
+
+ out.commit();
+ }
+
+ for repo in repos {
+ let mut gitcmd = std::process::Command::new("git");
+
+ gitcmd
+ .arg("-C")
+ .arg(&repo.dest)
+ .arg("commit")
+ .arg("--all")
+ .arg("--message")
+ .arg("Update versions");
+
+ let exit = gitcmd.status()
+ .expect("Git command failed");
+ if !exit.success() {
+ println!("Commit failed, presumably there were no changes");
+ continue;
+ }
+
+ let mut gitcmd = std::process::Command::new("git");
+ gitcmd
+ .arg("-C")
+ .arg(&repo.dest)
+ .arg("push")
+ .arg("origin")
+ .arg("+HEAD:version-bump");
+
+ if let Some(key) = &repo.key {
+ gitcmd.env("GIT_SSH_COMMAND", format!("ssh -F none -o IdentitiesOnly=yes -i \"{}\"", key.to_str().unwrap()));
+ }
+
+ let exit = gitcmd.status()
+ .expect("Git command failed");
+ if !exit.success() {
+ panic!("Git exited with failure");
+ }
+ }
+}
+
fn main() {
let argv: Vec<String> = std::env::args().collect();
let mut it = argv.iter();
@@ -526,6 +732,7 @@ fn main() {
let mut overwrite = false;
let mut config_path = None;
let mut scratch_path = None;
+ let mut continuous = false;
loop {
match it.next().map(|x| x.as_str()) {
@@ -560,6 +767,9 @@ fn main() {
std::process::exit(1);
}
},
+ Some("--continuous") => {
+ continuous = true;
+ },
Some("-i") | Some("--inplace") => {
overwrite = true;
},
@@ -572,8 +782,60 @@ fn main() {
}
let mut infile_paths = vec![];
- if positional.len() == 1 {
- infile_paths.push(std::path::PathBuf::from(positional[0]));
+ if positional.len() < 1 {
+ panic!("BAD ARGUMENTS");
+ }
+
+ let db = Connection::open(positional[0]).unwrap();
+ if positional.len() > 1 {
+ infile_paths.push(std::path::PathBuf::from(positional[1]));
+ }
+
+ {
+ db.execute("
+ CREATE TABLE IF NOT EXISTS migrations (
+ id INTEGER PRIMARY KEY NOT NULL
+ )
+ ", ()).unwrap();
+
+ let newest_migration: u32 = db.query_row("
+ SELECT MAX(id) FROM migrations
+ ", [], |row| row.get::<_, Option<u32>>(0)).unwrap().unwrap_or(0);
+
+ if newest_migration < 1 {
+ db.execute("INSERT INTO migrations(id) VALUES (?1)", (1, )).unwrap();
+ }
+
+ if newest_migration < 2 {
+ db.execute("
+ CREATE TABLE known_images (
+ id INTEGER PRIMARY KEY NOT NULL,
+ registry TEXT NOT NULL,
+ image TEXT NOT NULL,
+ tag TEXT NOT NULL
+ )
+ ", ()).unwrap();
+
+ db.execute("INSERT INTO migrations(id) VALUES (?1)", (2, )).unwrap();
+ }
+
+ if newest_migration < 3 {
+ db.execute("
+ ALTER TABLE known_images ADD COLUMN
+ discovered DATETIME NOT NULL
+ ", ()).unwrap();
+
+ db.execute("INSERT INTO migrations(id) VALUES (?1)", (3, )).unwrap();
+ }
+
+ if newest_migration < 4 {
+ db.execute("
+ CREATE UNIQUE INDEX known_images__registry_image_tag
+ ON known_images(registry, image, tag)
+ ", ()).unwrap();
+
+ db.execute("INSERT INTO migrations(id) VALUES (?1)", (4, )).unwrap();
+ }
}
let mut repos = vec![];
@@ -742,197 +1004,13 @@ fn main() {
}
}
- for repo in &repos {
- if repo.dest.exists() {
- let mut gitcmd = std::process::Command::new("git");
-
- gitcmd
- .arg("-C")
- .arg(&repo.dest)
- .arg("fetch")
- .arg("origin");
-
- if let Some(key) = &repo.key {
- gitcmd.env("GIT_SSH_COMMAND", format!("ssh -F none -o IdentitiesOnly=yes -i \"{}\"", key.to_str().unwrap()));
- }
- let exit = gitcmd.status()
- .expect("Git command failed");
- if !exit.success() {
- panic!("Git exited with failure");
- }
-
- let mut gitcmd = std::process::Command::new("git");
-
- gitcmd
- .arg("-C")
- .arg(&repo.dest)
- .arg("reset")
- .arg("--hard")
- .arg("@{u}");
-
- let exit = gitcmd.status()
- .expect("Git command failed");
- if !exit.success() {
- panic!("Git exited with failure");
- }
-
- let mut gitcmd = std::process::Command::new("git");
-
- gitcmd
- .arg("-C")
- .arg(&repo.dest)
- .arg("clean")
- .arg("--force")
- .arg("-d")
- .arg("-x");
-
- let exit = gitcmd.status()
- .expect("Git command failed");
- if !exit.success() {
- panic!("Git exited with failure");
- }
- } else {
- let mut gitcmd = std::process::Command::new("git");
-
- gitcmd
- .arg("clone")
- .arg(&repo.url)
- .arg(&repo.dest);
-
- if let Some(key) = &repo.key {
- gitcmd.env("GIT_SSH_COMMAND", format!("ssh -F none -o IdentitiesOnly=yes -i \"{}\"", key.to_str().unwrap()));
- }
-
- let exit = gitcmd.status()
- .expect("Git command failed");
- if !exit.success() {
- panic!("Git exited with failure");
- }
- }
-
- infile_paths.push(repo.dest.clone());
- }
-
- let mut inpaths = vec!();
- for infile_path in infile_paths {
- if infile_path.is_dir() {
- let mut unsearched = vec![infile_path.clone()];
-
- while let Some(next) = unsearched.pop() {
- for child in next.read_dir().unwrap() {
- let child = child.unwrap();
- let path = child.path();
-
- let ft = child.file_type().unwrap();
- if ft.is_dir() {
- unsearched.push(path);
- continue;
- }
-
- if let Some(ext) = path.extension() {
- if ext == "yaml" {
- inpaths.push(path);
- }
- }
- }
- }
- } else {
- inpaths.push(infile_path);
- };
- }
-
let mut auth = Auth::new(auths);
- for infile_path in inpaths {
- let mut file = std::fs::File::open(&infile_path).unwrap();
- let mut file_content = String::new();
- file.read_to_string(&mut file_content).unwrap();
-
- let images = ManifestFile::parse(&file_content);
-
- let mut failed = None;
-
- let mut edits = vec![];
- for ref image in images.image_tags {
- println!("Checking image {}", &file_content[image.clone()]);
- let image_ref = DockerRef::parse(&file_content, image);
- if let Err(msg) = update_images(&file_content, &mut auth, image_ref, &mut edits) {
- failed = Some(msg);
- break;
- }
- }
-
- if let Some(msg) = failed {
- println!("{}: {} ", infile_path.display(), msg);
- continue;
- }
-
- let mut out = if overwrite {
- Output::overlay_file(&infile_path)
- } else {
- Output::Stdout(std::io::stdout())
- };
-
- let mut current_position = 0;
- file.seek(std::io::SeekFrom::Start(0)).unwrap();
- let mut file_block = file.take(0);
- for edit in edits {
- if edit.position.start > current_position {
- file_block.set_limit((edit.position.start - current_position) as u64);
- std::io::copy(&mut file_block, &mut out).unwrap();
- }
-
- out.write_all(edit.content.as_bytes()).unwrap();
-
- // We should have been able to just seek to the position.end here, but that doesn't work
- // for whatever reason. What we can do is calculate the amount to skip ahead, and then do
- // that.
- let skip = (edit.position.end - edit.position.start) as i64;
- file_block.set_limit(skip as u64);
- file_block.seek(std::io::SeekFrom::Current(skip)).unwrap();
- current_position = edit.position.end;
- }
-
- file_block.set_limit(u64::MAX);
- std::io::copy(&mut file_block, &mut out).unwrap();
-
- out.commit();
- }
-
- for repo in &repos {
- let mut gitcmd = std::process::Command::new("git");
-
- gitcmd
- .arg("-C")
- .arg(&repo.dest)
- .arg("commit")
- .arg("--all")
- .arg("--message")
- .arg("Update versions");
-
- let exit = gitcmd.status()
- .expect("Git command failed");
- if !exit.success() {
- println!("Commit failed, presumably there were no changes");
- continue;
- }
-
- let mut gitcmd = std::process::Command::new("git");
- gitcmd
- .arg("-C")
- .arg(&repo.dest)
- .arg("push")
- .arg("origin")
- .arg("+HEAD:version-bump");
-
- if let Some(key) = &repo.key {
- gitcmd.env("GIT_SSH_COMMAND", format!("ssh -F none -o IdentitiesOnly=yes -i \"{}\"", key.to_str().unwrap()));
- }
+ loop {
+ run_tool(&db, &mut auth, &repos, infile_paths.clone(), overwrite);
+ if !continuous { break; }
- let exit = gitcmd.status()
- .expect("Git command failed");
- if !exit.success() {
- panic!("Git exited with failure");
- }
+ println!("Waiting for next run");
+ std::thread::sleep(std::time::Duration::from_hours(24));
}
}