Compare commits
No commits in common. "master" and "v0.5.0" have entirely different histories.
16 changed files with 433 additions and 892 deletions
|
@ -1,4 +1,4 @@
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
tab_spaces = 2
|
tab_spaces = 2
|
||||||
match_block_trailing_comma = true
|
match_block_trailing_comma = true
|
||||||
#wrap_comments = true
|
wrap_comments = true
|
274
Cargo.lock
generated
274
Cargo.lock
generated
|
@ -79,9 +79,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.22"
|
version = "1.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1"
|
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -102,9 +102,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.38"
|
version = "4.5.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
|
checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -112,9 +112,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.38"
|
version = "4.5.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
|
checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
@ -176,9 +176,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctrlc"
|
name = "ctrlc"
|
||||||
version = "3.4.7"
|
version = "3.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73"
|
checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nix",
|
"nix",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
|
@ -230,22 +230,11 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.3.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"r-efi",
|
|
||||||
"wasi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "git2"
|
name = "git2"
|
||||||
version = "0.20.1"
|
version = "0.20.0"
|
||||||
source = "git+https://github.com/brysonsteck/git2-rs?branch=certificates#1ca81270e632ac30c6e1367d3032ca3043068b2f"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fda788993cc341f69012feba8bf45c0ba4f3291fcc08e214b4d5a7332d88aff"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -258,9 +247,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.3"
|
version = "0.15.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
|
@ -276,22 +265,21 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_collections"
|
name = "icu_collections"
|
||||||
version = "2.0.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
|
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"potential_utf",
|
|
||||||
"yoke",
|
"yoke",
|
||||||
"zerofrom",
|
"zerofrom",
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_locale_core"
|
name = "icu_locid"
|
||||||
version = "2.0.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
|
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"litemap",
|
"litemap",
|
||||||
|
@ -301,10 +289,30 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_normalizer"
|
name = "icu_locid_transform"
|
||||||
version = "2.0.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
|
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_locid_transform_data",
|
||||||
|
"icu_provider",
|
||||||
|
"tinystr",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_locid_transform_data"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_normalizer"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"icu_collections",
|
"icu_collections",
|
||||||
|
@ -312,54 +320,67 @@ dependencies = [
|
||||||
"icu_properties",
|
"icu_properties",
|
||||||
"icu_provider",
|
"icu_provider",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
"utf16_iter",
|
||||||
|
"utf8_iter",
|
||||||
|
"write16",
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_normalizer_data"
|
name = "icu_normalizer_data"
|
||||||
version = "2.0.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
|
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_properties"
|
name = "icu_properties"
|
||||||
version = "2.0.0"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a"
|
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"icu_collections",
|
"icu_collections",
|
||||||
"icu_locale_core",
|
"icu_locid_transform",
|
||||||
"icu_properties_data",
|
"icu_properties_data",
|
||||||
"icu_provider",
|
"icu_provider",
|
||||||
"potential_utf",
|
"tinystr",
|
||||||
"zerotrie",
|
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_properties_data"
|
name = "icu_properties_data"
|
||||||
version = "2.0.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04"
|
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_provider"
|
name = "icu_provider"
|
||||||
version = "2.0.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
|
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"icu_locale_core",
|
"icu_locid",
|
||||||
|
"icu_provider_macros",
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
"tinystr",
|
"tinystr",
|
||||||
"writeable",
|
"writeable",
|
||||||
"yoke",
|
"yoke",
|
||||||
"zerofrom",
|
"zerofrom",
|
||||||
"zerotrie",
|
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_provider_macros"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
@ -373,9 +394,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna_adapter"
|
name = "idna_adapter"
|
||||||
version = "1.2.1"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
|
checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"icu_normalizer",
|
"icu_normalizer",
|
||||||
"icu_properties",
|
"icu_properties",
|
||||||
|
@ -383,9 +404,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.9.0"
|
version = "2.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
@ -399,24 +420,24 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
version = "0.1.33"
|
version = "0.1.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.172"
|
version = "0.2.171"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libgit2-sys"
|
name = "libgit2-sys"
|
||||||
version = "0.18.1+1.9.0"
|
version = "0.18.0+1.9.0"
|
||||||
source = "git+https://github.com/brysonsteck/git2-rs?branch=certificates#1ca81270e632ac30c6e1367d3032ca3043068b2f"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1a117465e7e1597e8febea8bb0c410f1c7fb93b1e1cddf34363f8390367ffec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -454,15 +475,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
version = "0.8.0"
|
version = "0.7.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.27"
|
version = "0.4.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
|
@ -472,9 +493,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.30.1"
|
version = "0.29.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
@ -484,9 +505,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.3"
|
version = "1.21.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-probe"
|
name = "openssl-probe"
|
||||||
|
@ -496,9 +517,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.108"
|
version = "0.9.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847"
|
checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -518,20 +539,11 @@ version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "potential_utf"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
|
|
||||||
dependencies = [
|
|
||||||
"zerovec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.95"
|
version = "1.0.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -560,15 +572,9 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "r-efi"
|
|
||||||
version = "5.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "refractr"
|
name = "refractr"
|
||||||
version = "0.6.2"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
|
@ -615,9 +621,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.9"
|
version = "0.10.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
|
@ -632,9 +638,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.15.0"
|
version = "1.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
|
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
|
@ -650,9 +656,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.101"
|
version = "2.0.100"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -661,9 +667,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "synstructure"
|
name = "synstructure"
|
||||||
version = "0.13.2"
|
version = "0.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -672,9 +678,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinystr"
|
name = "tinystr"
|
||||||
version = "0.8.1"
|
version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
|
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"zerovec",
|
"zerovec",
|
||||||
|
@ -682,9 +688,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.22"
|
version = "0.8.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
|
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
|
@ -694,33 +700,26 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.6.9"
|
version = "0.6.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
|
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.22.26"
|
version = "0.22.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
|
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"toml_write",
|
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml_write"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.18.0"
|
version = "1.18.0"
|
||||||
|
@ -764,6 +763,12 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf16_iter"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8_iter"
|
name = "utf8_iter"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
|
@ -788,15 +793,6 @@ version = "0.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasi"
|
|
||||||
version = "0.14.2+wasi-0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
|
||||||
dependencies = [
|
|
||||||
"wit-bindgen-rt",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
|
@ -884,33 +880,30 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.10"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
|
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen-rt"
|
name = "write16"
|
||||||
version = "0.39.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "writeable"
|
name = "writeable"
|
||||||
version = "0.6.1"
|
version = "0.5.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.8.0"
|
version = "0.7.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
|
checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
|
@ -920,9 +913,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke-derive"
|
name = "yoke-derive"
|
||||||
version = "0.8.0"
|
version = "0.7.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
|
checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -951,22 +944,11 @@ dependencies = [
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerotrie"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
|
|
||||||
dependencies = [
|
|
||||||
"displaydoc",
|
|
||||||
"yoke",
|
|
||||||
"zerofrom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerovec"
|
name = "zerovec"
|
||||||
version = "0.11.2"
|
version = "0.10.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
|
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"yoke",
|
"yoke",
|
||||||
"zerofrom",
|
"zerofrom",
|
||||||
|
@ -975,9 +957,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerovec-derive"
|
name = "zerovec-derive"
|
||||||
version = "0.11.1"
|
version = "0.10.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
|
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
[package]
|
[package]
|
||||||
name = "refractr"
|
name = "refractr"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
version = "0.6.2"
|
version = "0.5.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.29", features = ["derive"] }
|
clap = { version = "4.5.29", features = ["derive"] }
|
||||||
colored = "3.0.0"
|
colored = "3.0.0"
|
||||||
ctrlc = "3.4.5"
|
ctrlc = "3.4.5"
|
||||||
git2 = { git = "https://github.com/brysonsteck/git2-rs", branch = "certificates" }
|
git2 = "0.20.0"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
quit = "2.0.0"
|
quit = "2.0.0"
|
||||||
serde = "1.0.217"
|
serde = "1.0.217"
|
||||||
|
|
12
Dockerfile
Normal file
12
Dockerfile
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
FROM git.brysonsteck.xyz/brysonsteck/refractr:latest
|
||||||
|
|
||||||
|
# replace "path/" with the path containing your refractr configs
|
||||||
|
COPY path/ /etc/refractr
|
||||||
|
|
||||||
|
# use --secret with docker build to specify your ssh key
|
||||||
|
# make sure your configs use the location below
|
||||||
|
RUN --mount=type=secret,id=key,target=/id.pub \
|
||||||
|
cp /id.pub /etc/refractr && chmod 400 /etc/refractr/id.pub
|
||||||
|
|
||||||
|
# add arguments to specify verbosity and configs as needed
|
||||||
|
CMD ["refractr", "-c", "/etc/refractr/config.json"]
|
30
README.md
30
README.md
|
@ -37,16 +37,7 @@ docker build -t refractr --build-arg UID=$(id -u) --build-arg GID=$(id -g) -f pa
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
You can download pre-built binaries from the [Releases page](https://git.brysonsteck.xyz/brysonsteck/refractr/releases) or from my package mirror:
|
Coming soon...
|
||||||
|
|
||||||
```powershell
|
|
||||||
# Download the exe with PowerShell
|
|
||||||
Invoke-WebRequest -Uri https://pkg.brysonsteck.xyz/dist/refractr/latest/windows/x86_64/refractr.exe -OutFile refractr.exe
|
|
||||||
# Download the SHA256 checksum
|
|
||||||
Invoke-WebRequest -Uri https://pkg.brysonsteck.xyz/dist/refractr/latest/windows/x86_64/refractr.exe.sha256.txt -OutFile refractr.exe.sha256.txt
|
|
||||||
# Verify hashes match (should return "True")
|
|
||||||
(Get-Content .\refractr.exe.sha256.txt) -eq (Get-FileHash .\refractr.exe).hash
|
|
||||||
```
|
|
||||||
|
|
||||||
### macOS
|
### macOS
|
||||||
|
|
||||||
|
@ -54,26 +45,15 @@ Coming soon...
|
||||||
|
|
||||||
### Build from Source
|
### Build from Source
|
||||||
|
|
||||||
To build and run refractr from source, you may need some packages installed on your system. On vanilla installations of FreeBSD, OpenBSD, Windows and macOS, you should be able to compile refractr without issue. On Linux, however, you may need to install additional packages that provide the `pkg-config` binary and OpenSSL headers. Refer to your package manager for the correct package names that you will need to verify are installed.
|
To build and run refractr from source, run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# clone the repository
|
# the apt command only applies to Ubuntu/Debian
|
||||||
|
# you will need to install the correct packages using your package manager otherwise
|
||||||
|
apt install git pkg-config libssl-dev
|
||||||
git clone https://git.brysonsteck.xyz/brysonsteck/refractr && cd refractr
|
git clone https://git.brysonsteck.xyz/brysonsteck/refractr && cd refractr
|
||||||
# build the release binary
|
|
||||||
cargo build --release
|
cargo build --release
|
||||||
# build the debug binary (not recommended for normal use)
|
|
||||||
cargo build
|
|
||||||
# you can run the binary in the target directory
|
# you can run the binary in the target directory
|
||||||
# or move it wherever you wish
|
# or move it wherever you wish
|
||||||
./target/release/refractr
|
./target/release/refractr
|
||||||
```
|
```
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
### Couldn't I do this with a shell script and a cron job or (insert alternative method here)?
|
|
||||||
|
|
||||||
Yes.
|
|
||||||
|
|
||||||
### Then why reinvent the wheel?
|
|
||||||
|
|
||||||
Stop asking such asinine questions.
|
|
||||||
|
|
24
build
24
build
|
@ -2,32 +2,22 @@
|
||||||
# Create all the different builds for refractr
|
# Create all the different builds for refractr
|
||||||
|
|
||||||
version=$(cat Cargo.toml | grep -m1 version | awk -F' ' '{print $3}' | sed 's|"||g')
|
version=$(cat Cargo.toml | grep -m1 version | awk -F' ' '{print $3}' | sed 's|"||g')
|
||||||
major_version=$(echo $version | awk -F'.' '{print $1}')
|
|
||||||
uid=$(id -u)
|
uid=$(id -u)
|
||||||
gid=$(id -g)
|
gid=$(id -g)
|
||||||
date=$(date -u --rfc-3339=seconds)
|
cargo update
|
||||||
cargo=$(which cargo 2> /dev/null)
|
cargo clean
|
||||||
|
|
||||||
if [ -n "$cargo" ]; then
|
|
||||||
cargo update
|
|
||||||
cargo clean
|
|
||||||
fi
|
|
||||||
|
|
||||||
# docker builds
|
# docker builds
|
||||||
docker build -t refractr:$version -t refractr:$major_version -t refractr:latest \
|
docker build -t refractr:$version --build-arg UID=$uid --build-arg GID=$gid --build-arg VERSION=$version -f package.Dockerfile .
|
||||||
--build-arg VERSION=$version --build-arg DATE="$date" -f docker/Dockerfile .
|
docker tag refractr:$version refractr:latest
|
||||||
if [ "$1" = "push" ]; then
|
if test "$1" = "push"; then
|
||||||
docker tag refractr:$version git.brysonsteck.xyz/brysonsteck/refractr:latest
|
docker tag refractr:$version git.brysonsteck.xyz/brysonsteck/refractr:latest
|
||||||
docker tag refractr:$version git.brysonsteck.xyz/brysonsteck/refractr:$version
|
docker tag refractr:$version git.brysonsteck.xyz/brysonsteck/refractr:$version
|
||||||
docker tag refractr:$version git.brysonsteck.xyz/brysonsteck/refractr:$major_version
|
|
||||||
docker push -a git.brysonsteck.xyz/brysonsteck/refractr
|
docker push -a git.brysonsteck.xyz/brysonsteck/refractr
|
||||||
docker image rm git.brysonsteck.xyz/brysonsteck/refractr:latest
|
docker image rm git.brysonsteck.xyz/brysonsteck/refractr:latest
|
||||||
docker image rm git.brysonsteck.xyz/brysonsteck/refractr:$version
|
docker image rm git.brysonsteck.xyz/brysonsteck/refractr:$version
|
||||||
docker image rm git.brysonsteck.xyz/brysonsteck/refractr:$major_version
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# rust build
|
# rust build
|
||||||
if [ -n "$cargo" ]; then
|
cargo build
|
||||||
cargo build
|
cargo build --release
|
||||||
cargo build --release
|
|
||||||
fi
|
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
FROM rust:1-alpine AS build
|
|
||||||
|
|
||||||
ENV REFRACTR_DOCKER="true"
|
|
||||||
|
|
||||||
WORKDIR /usr/src/refractr
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
RUN apk upgrade --no-cache && apk add --no-cache pkgconfig libc-dev openssl-dev openssl openssl-libs-static
|
|
||||||
RUN cargo install --path . && cargo clean
|
|
||||||
|
|
||||||
FROM alpine:3 AS package
|
|
||||||
|
|
||||||
ARG VERSION
|
|
||||||
ARG DATE
|
|
||||||
|
|
||||||
LABEL org.opencontainers.image.title="refractr"
|
|
||||||
LABEL org.opencontainers.image.authors="me@brysonsteck.xyz"
|
|
||||||
LABEL org.opencontainers.image.version="${VERSION}"
|
|
||||||
LABEL org.opencontainers.image.url="https://git.brysonsteck.xyz/brysonsteck/-/packages/container/refractr/${VERSION}"
|
|
||||||
LABEL org.opencontainers.image.source="https://git.brysonsteck.xyz/brysonsteck/refractr"
|
|
||||||
LABEL org.opencontainers.image.licenses="MPL-2.0"
|
|
||||||
LABEL org.opencontainers.image.created="${DATE}"
|
|
||||||
|
|
||||||
RUN apk upgrade --no-cache && apk add --no-cache openssl
|
|
||||||
RUN mkdir /etc/refractr
|
|
||||||
COPY ./docker/entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
||||||
|
|
||||||
COPY --from=build /usr/src/refractr /usr/src
|
|
||||||
COPY --from=build /usr/local/cargo/bin/refractr /usr/local/bin
|
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
|
|
@ -1,17 +0,0 @@
|
||||||
services:
|
|
||||||
refractr:
|
|
||||||
image: git.brysonsteck.xyz/brysonsteck/refractr:latest
|
|
||||||
environment:
|
|
||||||
# change these to your uid/gid
|
|
||||||
# if omitted, the container will guess
|
|
||||||
- UID=1000
|
|
||||||
- GID=1000
|
|
||||||
volumes:
|
|
||||||
- /home/bryson/configs:/etc/refractr:ro
|
|
||||||
secrets:
|
|
||||||
- ssh_key
|
|
||||||
|
|
||||||
secrets:
|
|
||||||
ssh_key:
|
|
||||||
# available in /run/secrets/ssh_key
|
|
||||||
file: /home/bryson/.ssh/id_rsa
|
|
|
@ -1,38 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# Entrypoint file for refractr
|
|
||||||
# Runs refractr with some verbosity and all configs in /etc/refractr
|
|
||||||
#
|
|
||||||
# Copyright 2025 Bryson Steck <me@brysonsteck.xyz>
|
|
||||||
#
|
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
#
|
|
||||||
|
|
||||||
if [ -z "$UID" ]; then
|
|
||||||
set=$(ls -lnd /etc/refractr/ | awk '{print $3}')
|
|
||||||
echo "UID not set! Setting to id $set (owner of /etc/refractr)"
|
|
||||||
export UID=$set
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$GID" ]; then
|
|
||||||
set=$(ls -lnd /etc/refractr/ | awk '{print $4}')
|
|
||||||
echo "GID not set! Setting to id $set (group of /etc/refractr)"
|
|
||||||
export GID=$set
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! ls /etc/refractr/*.toml &> /dev/null; then
|
|
||||||
echo "Failed to find any toml config files for refractr!"
|
|
||||||
echo "Make sure you copied configs or set up volumes correctly."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
args=""
|
|
||||||
for config in /etc/refractr/*.toml; do
|
|
||||||
args="${args}-c '${config}' "
|
|
||||||
done
|
|
||||||
|
|
||||||
addgroup -g $GID dockeruser
|
|
||||||
adduser -u $UID -G $(grep :$GID: /etc/group | awk -F: '{print $1}') -D dockeruser
|
|
||||||
|
|
||||||
su -p - dockeruser -c "refractr -vv ${args}"
|
|
101
man/refractr.1
101
man/refractr.1
|
@ -1,101 +0,0 @@
|
||||||
.TH REFRACTR 1 "Updated 2025-03-25" "Bryson Steck"
|
|
||||||
.SH NAME
|
|
||||||
refractr \- an automated push mirroring utility for Git repositories
|
|
||||||
.SH SYNOPSIS
|
|
||||||
.B refractr
|
|
||||||
[\fBOPTIONS\fR]
|
|
||||||
.SH DESCRIPTION
|
|
||||||
refractr is an automated push mirroring utility for Git repositories.
|
|
||||||
|
|
||||||
With no options, refractr will attempt to load the config located at /etc/refractr/config.toml and run with that configuration.
|
|
||||||
For details for configuration files, see CONFIGURATION OPTIONS.
|
|
||||||
.SH OPTIONS
|
|
||||||
.sp
|
|
||||||
.TP 0.5i
|
|
||||||
\fB\-h\fR, \fB\-\-help\fR
|
|
||||||
Display a brief help message on the command line.
|
|
||||||
.TP 0.5i
|
|
||||||
\fB\-v\fR, \fB\-\-version\fR
|
|
||||||
Display the version of refractr on the command line.
|
|
||||||
.TP 0.5i
|
|
||||||
\fB\-c\fR, \fB\-\-config\fR [FILE]
|
|
||||||
Specify the absolute or relative path to a configuration file. Multiple configuration files can be specified by using multiple flags.
|
|
||||||
.P
|
|
||||||
.TP 0.5i
|
|
||||||
\fB\-v\fR, \fB\-\-verbose...\fR
|
|
||||||
Specify the level of verbose messages for refractr to output.
|
|
||||||
The functionality of this flag is similar to ssh(1); adding multiple flags will increase the amount of output with specific messages to what refractr is doing.
|
|
||||||
The more flags specified, the more low-level messages will appear.
|
|
||||||
.TP 0.5i
|
|
||||||
\fB\-e\fR, \fB\-\-create\fR
|
|
||||||
Print a full, commented config file to the standard output and exit. You can use this to generate config files that you can fill in and pass to refractr with the -c flag.
|
|
||||||
Information on the values for a refractr configuration can be found in the CONFIGURATION section.
|
|
||||||
.TP 0.5i
|
|
||||||
\fB\-s\fR, \fB\-\-strict\fR
|
|
||||||
Enable strict mode.
|
|
||||||
|
|
||||||
By design, refractr will ignore problems that occur when pushing to remotes and verifying host signatures when using SSH remotes.
|
|
||||||
Some of these errors may include networking issues and a host missing from your SSH known_hosts file, but refractr takes your configurations as
|
|
||||||
implicit trust that the remotes you pull/push from are correct and reachable. This also makes it so refractr will continuously try to
|
|
||||||
update the remotes with changes from the upstream repository to avoid synchronization issues.
|
|
||||||
|
|
||||||
If you wish for refractr to exit on these kinds of errors as a way to know of these issues, you can use this flag to enable "strict" mode.
|
|
||||||
Doing so will make refractr exit instead of continue or loop forever when encountering these problems.
|
|
||||||
.TP 0.5i
|
|
||||||
\fB\-p\fR, \fB\-\-persist\fR
|
|
||||||
Do not recursively delete the working directory as defined in your configuration file(s).
|
|
||||||
This can be helpful for troubleshooting issues with your repository, a potential bug with refractr, or general development.
|
|
||||||
.P
|
|
||||||
.SH CONFIGURATION
|
|
||||||
Below are the values for defining a refractr configuration.
|
|
||||||
.TP 0.5i
|
|
||||||
\fBfrom (string, REQUIRED)\fR
|
|
||||||
The original/upstream repository URI. This field MUST START with "https://" or "ssh://".
|
|
||||||
.TP 0.5i
|
|
||||||
\fBto (array, REQUIRED)\fR
|
|
||||||
The list of remotes you wish to push the repo to as cloned from the "from" field.
|
|
||||||
This field must be SSH remotes and the repository must exist on the remotes.
|
|
||||||
.TP 0.5i
|
|
||||||
\fBbranches (array, REQUIRED)\fR
|
|
||||||
The list of branches from the original/upstream repository you wish to push to the remotes.
|
|
||||||
.TP 0.5i
|
|
||||||
\fBwork_dir (string, OPTIONAL)\fR
|
|
||||||
The path on the filesystem refractr will clone this repository to and work from.
|
|
||||||
If omitted, this field defaults to "/tmp/refractr" on UNIX(-like) systems and "$env:TEMP\\refractr" on Windows.
|
|
||||||
.TP 0.5i
|
|
||||||
\fBpush_tags (boolean, REQUIRED)\fR
|
|
||||||
Push tags pulled from the original/upstream repository to the remotes.
|
|
||||||
.TP 0.5i
|
|
||||||
\fBgit.ssh_identity_file (string, REQUIRED)\fR
|
|
||||||
The path to your SSH private key for authenticating to the remotes specified in the "from" (if using SSH) and "to" fields.
|
|
||||||
|
|
||||||
If using Docker, you will need to copy your private key to your container or build it in an image using secrets in your Dockerfile.
|
|
||||||
.TP 0.5i
|
|
||||||
\fBschedule.enabled (bool, REQUIRED)\fR
|
|
||||||
Enable the schedule feature of refractr. This allows you to regularly pull updates and push them out on a regular basis.
|
|
||||||
.TP 0.5i
|
|
||||||
\fBschedule.interval (integer, DEPENDS ON schedule.enabled)\fR
|
|
||||||
The amount of time in seconds to pull updates and push them out to remotes.
|
|
||||||
To avoid creating unneeded stress on servers, this value must be greater than or equal to 60.
|
|
||||||
|
|
||||||
If "schedule.enabled" is false, refractr will not check for this value.
|
|
||||||
If "scheduled.enabled" is true, refractr will check this value. If it does not exist or is less than 60, refractr will return an error.
|
|
||||||
.SH AUTHORS
|
|
||||||
.B Bryson Steck <me@brysonsteck.xyz> (https://brysonsteck.xyz)
|
|
||||||
.P
|
|
||||||
.SH CONTRIBUTE
|
|
||||||
refractr is free and open source! Please report any bugs in, provide feedback for, or contribute to refractr by visiting the Codeberg repository:
|
|
||||||
.IR https://codeberg.org/brysonsteck/refractr
|
|
||||||
|
|
||||||
The Codeberg repository is proof of refractr at work. An upstream, read-only version of the repository is also available at:
|
|
||||||
.IR https://git.brysonsteck.xyz/brysonsteck/refractr
|
|
||||||
.P
|
|
||||||
.SH LICENSE
|
|
||||||
Copyright 2025 Bryson Steck
|
|
||||||
|
|
||||||
The binaries and source code of refractr is distributed under and subject to the terms of the Mozilla Public License v2 (MPL-2.0).
|
|
||||||
A copy of this license should have been distributed with this software. If you did not receive a copy, you can view the license at:
|
|
||||||
.IR https://www.mozilla.org/en-US/MPL/2.0/
|
|
||||||
|
|
||||||
License violations can be reported by creating an issue in the Codeberg repository under the CONTRIBUTE section.
|
|
||||||
.EFRATR
|
|
22
package.Dockerfile
Normal file
22
package.Dockerfile
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
FROM rust:slim
|
||||||
|
ARG UID="1000"
|
||||||
|
ARG GID="1000"
|
||||||
|
ARG VERSION
|
||||||
|
ENV REFRACTR_DOCKER="true"
|
||||||
|
|
||||||
|
LABEL org.opencontainers.image.authors="me@brysonsteck.xyz"
|
||||||
|
LABEL version="${VERSION}"
|
||||||
|
LABEL license="MPL-2.0"
|
||||||
|
|
||||||
|
WORKDIR /usr/src/refractr
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN apt update && apt install pkg-config libssl-dev -y
|
||||||
|
RUN cargo install --path .
|
||||||
|
|
||||||
|
RUN groupadd -g $GID refractr
|
||||||
|
RUN useradd -u $UID -g $GID -mN refractr
|
||||||
|
RUN mkdir /etc/refractr && chown refractr:refractr /etc/refractr
|
||||||
|
USER refractr
|
||||||
|
|
||||||
|
CMD ["refractr"]
|
|
@ -15,8 +15,7 @@ pub enum ExitCode {
|
||||||
RemoteError = 5,
|
RemoteError = 5,
|
||||||
PushError = 6,
|
PushError = 6,
|
||||||
FetchError = 7,
|
FetchError = 7,
|
||||||
ConfigError = 8,
|
ConfigError = 8
|
||||||
HaltError = 130,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ReturnData {
|
pub struct ReturnData {
|
||||||
|
@ -24,23 +23,17 @@ pub struct ReturnData {
|
||||||
pub msg: String,
|
pub msg: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error_quit(msg: String, code: ExitCode) {
|
pub fn error(msg: String, code: ExitCode) {
|
||||||
eprintln!("{} {}", "error:".red().bold(), msg);
|
eprintln!("{} {}", "error:".red().bold(), msg);
|
||||||
quit::with_code(code as u8)
|
quit::with_code(code as u8)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error(msg: String) {
|
|
||||||
eprintln!("{} {}", "error:".red().bold(), msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn warning(msg: String) {
|
pub fn warning(msg: String) {
|
||||||
eprintln!("{} {}", "warning:".yellow().bold(), msg)
|
eprintln!("{} {}", "warning:".yellow().bold(), msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verbose(level: u8, msg_lvl: u8, msg: String) {
|
pub fn verbose(level: u8, msg_lvl: u8, msg: String) {
|
||||||
if level < msg_lvl {
|
if level < msg_lvl { return };
|
||||||
return;
|
|
||||||
};
|
|
||||||
let mut prefix = String::new();
|
let mut prefix = String::new();
|
||||||
for _ in 0..msg_lvl {
|
for _ in 0..msg_lvl {
|
||||||
prefix += "=";
|
prefix += "=";
|
||||||
|
|
159
src/config.rs
159
src/config.rs
|
@ -20,7 +20,7 @@ use toml;
|
||||||
pub struct ConfigFile {
|
pub struct ConfigFile {
|
||||||
pub path: String,
|
pub path: String,
|
||||||
pub file: Metadata,
|
pub file: Metadata,
|
||||||
pub config: Config,
|
pub config: Config
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ConfigFile {
|
impl fmt::Display for ConfigFile {
|
||||||
|
@ -40,24 +40,21 @@ impl fmt::Display for ConfigFile {
|
||||||
if i < self.config.to.len() - 1 {
|
if i < self.config.to.len() - 1 {
|
||||||
to_list.push_str(", ");
|
to_list.push_str(", ");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
to_list.push(']');
|
to_list.push(']');
|
||||||
|
|
||||||
let work_dir_path = match &self.config.work_dir {
|
let work_dir_path = match &self.config.work_dir {
|
||||||
None => {
|
None => {
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
format!(
|
format!("Using default \"{}\\refractr\"", match env::var("TEMP") {
|
||||||
"Using default \"{}\\refractr\"",
|
|
||||||
match env::var("TEMP") {
|
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(_) => format!("This shouldn't happen!"),
|
Err(_) => format!("This shouldn't happen!")
|
||||||
}
|
})
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
format!("Using default: /tmp/refractr")
|
format!("Using default: /tmp/refractr")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(path) => format!("{}", path),
|
Some(path) => format!("{}", path)
|
||||||
};
|
};
|
||||||
|
|
||||||
let schedule_interval = match self.config.schedule.interval {
|
let schedule_interval = match self.config.schedule.interval {
|
||||||
|
@ -68,15 +65,10 @@ impl fmt::Display for ConfigFile {
|
||||||
String::from("This shouldn't happen!\n")
|
String::from("This shouldn't happen!\n")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(int) => format!(
|
Some(int) => format!("\n Scheduled interval in seconds: {}", int.to_string())
|
||||||
"\n Scheduled interval in seconds: {}",
|
|
||||||
int.to_string()
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(
|
write!(f, "Config file: \"{}\"\n \
|
||||||
f,
|
|
||||||
"Config file: \"{}\"\n \
|
|
||||||
Is a file: {}\n \
|
Is a file: {}\n \
|
||||||
Read only: {}\n \
|
Read only: {}\n \
|
||||||
Configuration:\n \
|
Configuration:\n \
|
||||||
|
@ -86,143 +78,79 @@ impl fmt::Display for ConfigFile {
|
||||||
Working directory: {}\n \
|
Working directory: {}\n \
|
||||||
SSH key for pushing clone: {}\n \
|
SSH key for pushing clone: {}\n \
|
||||||
Schedule enabled: {}\
|
Schedule enabled: {}\
|
||||||
{}",
|
{}"
|
||||||
self.path,
|
, self.path, self.file.is_file(), self.file.permissions().readonly(), self.config.from
|
||||||
self.file.is_file(),
|
, to_list, branches_list, work_dir_path, self.config.git.ssh_identity_file, self.config.schedule.enabled
|
||||||
self.file.permissions().readonly(),
|
, schedule_interval)
|
||||||
self.config.from,
|
|
||||||
to_list,
|
|
||||||
branches_list,
|
|
||||||
work_dir_path,
|
|
||||||
self.config.git.ssh_identity_file,
|
|
||||||
self.config.schedule.enabled,
|
|
||||||
schedule_interval
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Deserialize)]
|
#[derive(PartialEq)]
|
||||||
|
#[derive(Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub from: String,
|
pub from: String,
|
||||||
pub to: Vec<String>,
|
pub to: Vec<String>,
|
||||||
pub branches: Vec<String>,
|
pub branches: Vec<String>,
|
||||||
pub work_dir: Option<String>,
|
pub work_dir: Option<String>,
|
||||||
pub push_tags: bool,
|
|
||||||
pub git: Git,
|
pub git: Git,
|
||||||
pub schedule: Schedule,
|
pub schedule: Schedule
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Deserialize)]
|
#[derive(PartialEq)]
|
||||||
|
#[derive(Deserialize)]
|
||||||
pub struct Git {
|
pub struct Git {
|
||||||
pub ssh_identity_file: String,
|
pub ssh_identity_file: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Deserialize)]
|
#[derive(PartialEq)]
|
||||||
|
#[derive(Deserialize)]
|
||||||
pub struct Schedule {
|
pub struct Schedule {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub interval: Option<i32>,
|
pub interval: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_work_dir(work_dir: &Option<String>) -> String {
|
|
||||||
match work_dir {
|
|
||||||
None => {
|
|
||||||
if cfg!(windows) {
|
|
||||||
format!("{}\\refractr", env::var("TEMP").unwrap())
|
|
||||||
} else {
|
|
||||||
format!("/tmp/refractr")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(path) => path.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_config(paths: Vec<PathBuf>, refractr: &Refractr) -> Result<Vec<ConfigFile>, String> {
|
pub fn read_config(paths: Vec<PathBuf>, refractr: &Refractr) -> Result<Vec<ConfigFile>, String> {
|
||||||
let mut config_files: Vec<ConfigFile> = vec![];
|
let mut config_files: Vec<ConfigFile> = vec![];
|
||||||
for path in paths {
|
for path in paths {
|
||||||
common::verbose(
|
common::verbose(refractr.verbose, 1, format!("Reading config file: \"{}\"", String::from(path.to_string_lossy())));
|
||||||
refractr.verbose,
|
|
||||||
1,
|
|
||||||
format!(
|
|
||||||
"Reading config file: \"{}\"",
|
|
||||||
String::from(path.to_string_lossy())
|
|
||||||
),
|
|
||||||
);
|
|
||||||
let mut data = String::new();
|
let mut data = String::new();
|
||||||
let mut file = match File::open(path.as_path()) {
|
let mut file = match File::open(path.as_path()) {
|
||||||
Err(e) => {
|
Err(e) => return Err(format!("unable to open {}: {}", path.as_path().display(), e)),
|
||||||
return Err(format!(
|
Ok(file) => file
|
||||||
"unable to open {}: {}",
|
|
||||||
path.as_path().display(),
|
|
||||||
e
|
|
||||||
))
|
|
||||||
},
|
|
||||||
Ok(file) => file,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = file.read_to_string(&mut data) {
|
if let Err(e) = file.read_to_string(&mut data) {
|
||||||
return Err(format!(
|
return Err(format!("unable to read {}: {}", path.as_path().display(), e))
|
||||||
"unable to read {}: {}",
|
|
||||||
path.as_path().display(),
|
|
||||||
e
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let config: Config = match toml::from_str(&data) {
|
let config: Config = match toml::from_str(&data) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(e) => return Err(format!("issues parsing toml file {}: {}", path.as_path().display(), e))
|
||||||
return Err(format!(
|
|
||||||
"issues parsing toml file {}: {}",
|
|
||||||
path.as_path().display(),
|
|
||||||
e
|
|
||||||
))
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let config_file = ConfigFile {
|
let config_file = ConfigFile {
|
||||||
path: match fs::canonicalize(&path) {
|
path: match fs::canonicalize(&path) {
|
||||||
Err(_) => {
|
Err(_) => return Err(format!("cannot get absolute path of config file: {}", path.as_path().display())),
|
||||||
return Err(format!(
|
Ok(abs) => abs.to_string_lossy().to_string()
|
||||||
"cannot get absolute path of config file: {}",
|
|
||||||
path.as_path().display()
|
|
||||||
))
|
|
||||||
},
|
|
||||||
Ok(abs) => abs.to_string_lossy().to_string(),
|
|
||||||
},
|
},
|
||||||
file: match fs::metadata(&path) {
|
file: match fs::metadata(&path) {
|
||||||
Err(_) => {
|
Err(_) => return Err(format!("cannot obtain metadata for config file: {}", path.as_path().display())),
|
||||||
return Err(format!(
|
Ok(metadata) => metadata
|
||||||
"cannot obtain metadata for config file: {}",
|
|
||||||
path.as_path().display()
|
|
||||||
))
|
|
||||||
},
|
|
||||||
Ok(metadata) => metadata,
|
|
||||||
},
|
},
|
||||||
config: match verify_config(&config) {
|
config: match verify_config(&config) {
|
||||||
Err(e) => {
|
Err(e) => return Err(format!("invalid config {}: {}", path.as_path().display(), e)),
|
||||||
return Err(format!(
|
Ok(_) => config
|
||||||
"invalid config {}: {}",
|
}
|
||||||
path.as_path().display(),
|
|
||||||
e
|
|
||||||
))
|
|
||||||
},
|
|
||||||
Ok(_) => config,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut dup = false;
|
let mut dup = false;
|
||||||
for i in &config_files {
|
for i in &config_files {
|
||||||
if i.path == config_file.path {
|
if i.path == config_file.path {
|
||||||
common::warning(format!(
|
common::warning(format!("skipping config file \"{}\" as it was already read", path.as_path().display()));
|
||||||
"skipping config file \"{}\" as it was already read",
|
|
||||||
path.as_path().display()
|
|
||||||
));
|
|
||||||
dup = true;
|
dup = true;
|
||||||
break;
|
break;
|
||||||
} else if i.config == config_file.config {
|
} else if i.config == config_file.config {
|
||||||
common::warning(format!(
|
common::warning(format!("config files \"{}\" and \"{}\" appear to have the same config", i.path, config_file.path));
|
||||||
"config files \"{}\" and \"{}\" appear to have the same config",
|
|
||||||
i.path, config_file.path
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,26 +167,31 @@ fn verify_config(config: &Config) -> Result<(), String> {
|
||||||
match config.schedule.interval {
|
match config.schedule.interval {
|
||||||
Some(i) => {
|
Some(i) => {
|
||||||
if i < 60 {
|
if i < 60 {
|
||||||
return Err(format!("schedule is enabled, but less than 60"));
|
return Err(format!("schedule is enabled, but less than 60"))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => return Err(format!("schedule is enabled, but no interval was defined")),
|
None => return Err(format!("schedule is enabled, but no interval was defined"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let None = &config.work_dir {
|
match &config.work_dir {
|
||||||
|
Some(path) => format!("{}", path),
|
||||||
|
None => {
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
if let Err(e) = env::var("TEMP") {
|
match env::var("TEMP") {
|
||||||
return Err(format!("cannot determine the default temp dir: {}", e));
|
Ok(val) => val,
|
||||||
|
Err(_) => return Err(format!("cannot determine the default temp dir"))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
format!("/tmp/refractr")
|
||||||
}
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if !&config.from.starts_with("ssh://")
|
if !&config.from.starts_with("ssh://")
|
||||||
&& !&config.from.starts_with("https://")
|
&& !&config.from.starts_with("https://")
|
||||||
&& !&config.from.starts_with("http://")
|
&& !&config.from.starts_with("http://") {
|
||||||
{
|
return Err(format!("'from' value does not use a supported protocol"))
|
||||||
return Err(format!("'from' value does not use a supported protocol"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -2,37 +2,40 @@
|
||||||
# Example configuration for refractr
|
# Example configuration for refractr
|
||||||
# ***********************************
|
# ***********************************
|
||||||
|
|
||||||
|
[config]
|
||||||
# The "from" field is a string of the original/main repository you want to pull
|
# The "from" field is a string of the original/main repository you want to pull
|
||||||
# This field is REQUIRED and MUST start with "https://" or "ssh://"
|
# This field is REQUIRED
|
||||||
#from = "https://git.brysonsteck.xyz/brysonsteck/refractr"
|
#from = "https://git.brysonsteck.xyz/brysonsteck/refractr"
|
||||||
|
|
||||||
# The "to" field is a list of strings of the remotes you want to push the repo from the "from" field to
|
# The "to" field is a list of strings of the repositories you want to push the repo
|
||||||
# These repositories must exist on the remote server
|
# from the "from" field to. These repositories must exist on the remote server
|
||||||
# This field is REQUIRED and MUST be SSH remotes
|
# It's recommended that you use SSH to avoid HTTPS authentication
|
||||||
|
# This field is REQUIRED
|
||||||
#to = ["git@codeberg.org:brysonsteck/refractr.git", "git@github.com:brysonsteck/refractr.git"]
|
#to = ["git@codeberg.org:brysonsteck/refractr.git", "git@github.com:brysonsteck/refractr.git"]
|
||||||
|
|
||||||
# The "branches" field is a list of branches you want to mirror from the original repository
|
# The "branches" field is a list of branches you want to mirror from the original
|
||||||
|
# repository.
|
||||||
# This field is REQUIRED
|
# This field is REQUIRED
|
||||||
#branches = ["master"]
|
#branches = ["master"]
|
||||||
|
|
||||||
# The "work_dir" field is where refractr will write the clone to
|
# The "work_dir" field is where refractr will write the clone to
|
||||||
# This field is OPTIONAL, will default to /tmp/refractr on *NIX and $env:TEMP\refractr on Windows
|
# This field is OPTIONAL, will default to /tmp/refractr on *NIX and
|
||||||
|
# $env:TEMP\refractr on Windows
|
||||||
#work_dir = /tmp/refractr
|
#work_dir = /tmp/refractr
|
||||||
|
|
||||||
[git]
|
[git]
|
||||||
# The "ssh_identity_file" is your private SSH key that you will use to push updates from the original
|
# The "ssh_identity_file" is your private SSH key that you will use to push updates
|
||||||
# repository and, if your "from" field is an SSH remote, for cloning the original repository
|
# from the original repository.
|
||||||
# If you are running from Docker, you will need to copy your private key to your container or image
|
# This field is REQUIRED if you are using SSH to push, otherwise OPTIONAL
|
||||||
# This field is REQUIRED
|
|
||||||
#ssh_identity_file = "/path/to/.ssh/id_rsa"
|
#ssh_identity_file = "/path/to/.ssh/id_rsa"
|
||||||
|
|
||||||
[schedule]
|
[schedule]
|
||||||
# The "enabled" field turns on the schedule feature of refractr
|
# The "enabled" field turns on the schedule feature of refractr.
|
||||||
# This field is REQUIRED.
|
# This field is REQUIRED.
|
||||||
#enabled = false
|
#enabled = false
|
||||||
|
|
||||||
# The "interval" field is the amount of seconds refractor will wait before pulling updates from the
|
# The "interval" field is the amount of seconds refractor will wait before
|
||||||
# original repository if the schedule feature is enabled
|
# pulling updates from the original repository if the schedule feature is enabled.
|
||||||
# To avoid creating a DoS attack, this is set to only accept values of >=60
|
# This field is REQUIRED if "enabled" is set to true, otherwise OPTIONAL
|
||||||
# This field is REQUIRED if "enabled" is set to true, UNUSED if false
|
# To avoid overwhelming servers, this is set to only accept values of >=60
|
||||||
#interval = 300
|
#interval = 300
|
||||||
|
|
67
src/main.rs
67
src/main.rs
|
@ -15,12 +15,10 @@ use crate::refractr::Refractr;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
use std::sync::Arc;
|
|
||||||
#[cfg(target_family = "windows")]
|
|
||||||
use username;
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
use users;
|
use users;
|
||||||
|
#[cfg(target_family = "windows")]
|
||||||
|
use username;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "refractr")]
|
#[command(name = "refractr")]
|
||||||
|
@ -34,26 +32,11 @@ struct Args {
|
||||||
#[arg(short, long, help = "Specify the level of verbosity", action = clap::ArgAction::Count)]
|
#[arg(short, long, help = "Specify the level of verbosity", action = clap::ArgAction::Count)]
|
||||||
verbose: u8,
|
verbose: u8,
|
||||||
|
|
||||||
#[arg(
|
#[arg(short = 'e', long, help = "Output a full, commented config file and exit")]
|
||||||
short = 'e',
|
|
||||||
long,
|
|
||||||
help = "Output a full, commented config file and exit"
|
|
||||||
)]
|
|
||||||
create: bool,
|
create: bool,
|
||||||
|
|
||||||
#[arg(
|
#[arg(short = 's', long, help = "Exit on push errors instead of ignoring")]
|
||||||
short = 's',
|
|
||||||
long,
|
|
||||||
help = "Exit on push and unknown host errors instead of ignoring them"
|
|
||||||
)]
|
|
||||||
strict: bool,
|
strict: bool,
|
||||||
|
|
||||||
#[arg(
|
|
||||||
short = 'p',
|
|
||||||
long,
|
|
||||||
help = "Do not clean the working directories specified in config(s)"
|
|
||||||
)]
|
|
||||||
persist: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_config_default() -> &'static str {
|
fn get_config_default() -> &'static str {
|
||||||
|
@ -61,8 +44,7 @@ fn get_config_default() -> &'static str {
|
||||||
return "C:\\ProgramData\\refractr\\config.toml";
|
return "C:\\ProgramData\\refractr\\config.toml";
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
return "/etc/refractr/config.toml";
|
return "/etc/refractr/config.toml";
|
||||||
#[cfg(any(
|
#[cfg(any(target_os = "freebsd",
|
||||||
target_os = "freebsd",
|
|
||||||
target_os = "openbsd",
|
target_os = "openbsd",
|
||||||
target_os = "netbsd",
|
target_os = "netbsd",
|
||||||
target_os = "macos"
|
target_os = "macos"
|
||||||
|
@ -79,14 +61,12 @@ fn main() -> Result<(), String> {
|
||||||
"true" => true,
|
"true" => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
None => false,
|
None => false
|
||||||
},
|
},
|
||||||
persist: args.persist,
|
|
||||||
pid: process::id(),
|
pid: process::id(),
|
||||||
strict: args.strict,
|
strict: args.strict,
|
||||||
unix: cfg!(unix),
|
unix: cfg!(unix),
|
||||||
verbose: args.verbose,
|
verbose: args.verbose
|
||||||
run: Arc::new(AtomicBool::new(true))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// warn to avoid root/admin
|
// warn to avoid root/admin
|
||||||
|
@ -106,37 +86,22 @@ fn main() -> Result<(), String> {
|
||||||
Err(_) => common::warning(format!("failed to get process username")),
|
Err(_) => common::warning(format!("failed to get process username")),
|
||||||
}
|
}
|
||||||
|
|
||||||
common::verbose(
|
common::verbose(refractr.verbose, 1, format!("refractr started with level {} verbosity enabled", refractr.verbose.to_string()));
|
||||||
refractr.verbose,
|
|
||||||
1,
|
|
||||||
format!(
|
|
||||||
"refractr started with level {} verbosity enabled",
|
|
||||||
refractr.verbose.to_string()
|
|
||||||
),
|
|
||||||
);
|
|
||||||
common::verbose(refractr.verbose, 3, format!("Process ID: {}", refractr.pid));
|
common::verbose(refractr.verbose, 3, format!("Process ID: {}", refractr.pid));
|
||||||
common::verbose(
|
common::verbose(refractr.verbose, 3, format!("Running in Docker: {}", refractr.docker));
|
||||||
refractr.verbose,
|
common::verbose(refractr.verbose, 3, format!("System is UNIX(-like): {}", refractr.unix));
|
||||||
3,
|
|
||||||
format!("Running in Docker: {}", refractr.docker),
|
|
||||||
);
|
|
||||||
common::verbose(
|
|
||||||
refractr.verbose,
|
|
||||||
3,
|
|
||||||
format!("System is UNIX(-like): {}", refractr.unix),
|
|
||||||
);
|
|
||||||
common::verbose(refractr.verbose, 2, format!("Checking for create flag"));
|
common::verbose(refractr.verbose, 2, format!("Checking for create flag"));
|
||||||
if args.create {
|
if args.create {
|
||||||
common::verbose(refractr.verbose, 3, format!("Printing sample config"));
|
common::verbose(refractr.verbose, 3, format!("Printing sample config"));
|
||||||
let example = include_str!("example/config.toml");
|
let example = include_str!("example/config.toml");
|
||||||
println!("{}", example);
|
println!("{}", example);
|
||||||
return Ok(());
|
return Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut cfgs = vec![];
|
let mut cfgs = vec![];
|
||||||
match config::read_config(args.config, &refractr) {
|
match config::read_config(args.config, &refractr) {
|
||||||
Ok(c) => cfgs = c,
|
Ok(c) => cfgs = c,
|
||||||
Err(e) => common::error_quit(format!("{}", e), common::ExitCode::ConfigError),
|
Err(e) => common::error(format!("{}", e), common::ExitCode::ConfigError)
|
||||||
};
|
};
|
||||||
|
|
||||||
if refractr.verbose >= 2 {
|
if refractr.verbose >= 2 {
|
||||||
|
@ -146,14 +111,10 @@ fn main() -> Result<(), String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
common::verbose(
|
common::verbose(refractr.verbose, 1, format!("Config file(s) read successfully"));
|
||||||
refractr.verbose,
|
|
||||||
1,
|
|
||||||
format!("Config file(s) read successfully"),
|
|
||||||
);
|
|
||||||
match refractr.run(cfgs) {
|
match refractr.run(cfgs) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => common::error_quit(format!("{}", e.msg), e.code),
|
Err(e) => common::error(format!("{}", e.msg), e.code)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
468
src/refractr.rs
468
src/refractr.rs
|
@ -6,95 +6,62 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::common::{self, ExitCode, ReturnData};
|
use crate::common::{self, ReturnData, ExitCode};
|
||||||
use crate::config::{self, Config, ConfigFile};
|
use crate::config::{Config, ConfigFile};
|
||||||
|
|
||||||
use git2::build::RepoBuilder;
|
|
||||||
use git2::string_array::StringArray;
|
use git2::string_array::StringArray;
|
||||||
use git2::{CertificateCheckStatus, Cred, FetchOptions, PushOptions, RemoteCallbacks, Repository};
|
use git2::{FetchOptions, CertificateCheckStatus, Cred, PushOptions, RemoteCallbacks, Repository};
|
||||||
use git2::{Error, ErrorCode};
|
use git2::{Error, ErrorCode};
|
||||||
|
use git2::build::RepoBuilder;
|
||||||
use hex;
|
use hex;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Sha256, Digest};
|
||||||
use std::fs::{self, remove_dir_all};
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf, MAIN_SEPARATOR_STR};
|
use std::path::{Path, PathBuf, MAIN_SEPARATOR_STR};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
pub struct Refractr {
|
pub struct Refractr {
|
||||||
pub docker: bool,
|
pub docker: bool,
|
||||||
pub persist: bool,
|
|
||||||
pub pid: u32,
|
pub pid: u32,
|
||||||
pub strict: bool,
|
pub strict: bool,
|
||||||
pub unix: bool,
|
pub unix: bool,
|
||||||
pub verbose: u8,
|
pub verbose: u8
|
||||||
pub run: Arc<AtomicBool>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OpenedRepository {
|
struct OpenedRepository {
|
||||||
repo: Repository,
|
repo: Repository,
|
||||||
|
path: String,
|
||||||
remotes: Vec<String>,
|
remotes: Vec<String>,
|
||||||
cfg: Config,
|
cfg: Config,
|
||||||
ssh: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Refractr {
|
impl Refractr {
|
||||||
fn set_up_work_dir(&self, work_dir: PathBuf) -> Result<(), String> {
|
fn set_up_work_dir(&self, work_dir: PathBuf) -> Result<String, String> {
|
||||||
if let Err(e) = fs::create_dir_all(&work_dir) {
|
if let Err(e) = fs::create_dir_all(&work_dir) {
|
||||||
return Err(format!(
|
return Err(format!("could not create working directory: {}: {}", work_dir.to_string_lossy().to_string(), e))
|
||||||
"could not create working directory: {}: {}",
|
|
||||||
work_dir.to_string_lossy().to_string(),
|
|
||||||
e
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(work_dir.to_string_lossy().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_up_ssh(&self, key_path: String, strict: bool) -> Result<RemoteCallbacks, String> {
|
fn get_refs(&self, branches: &Vec<String>, tags: StringArray) -> Vec<String> {
|
||||||
let mut cb = RemoteCallbacks::new();
|
|
||||||
cb.credentials(move |_, _, _| Cred::ssh_key("git", None, Path::new(&key_path), None));
|
|
||||||
cb.certificate_check(move |cert, url| {
|
|
||||||
let sha256 = hex::encode(cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec());
|
|
||||||
if strict {
|
|
||||||
common::error_quit(
|
|
||||||
format!(
|
|
||||||
"unknown host {} with sha256 host key {}, exiting",
|
|
||||||
url, sha256
|
|
||||||
),
|
|
||||||
ExitCode::RemoteError,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
common::warning(format!(
|
|
||||||
"unknown host {} with sha256 host key {}, implicitly trusting",
|
|
||||||
url, sha256
|
|
||||||
));
|
|
||||||
common::warning(format!(
|
|
||||||
"to suppress this warning in the future, add this host to your known_hosts file"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(CertificateCheckStatus::CertificateOk)
|
|
||||||
});
|
|
||||||
Ok(cb)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_refs(&self, branches: &Vec<String>, tags: Option<StringArray>) -> Vec<String> {
|
|
||||||
let mut refs_branches = Vec::new();
|
let mut refs_branches = Vec::new();
|
||||||
for branch in branches {
|
for branch in branches {
|
||||||
refs_branches.push(format!("refs/heads/{}", branch));
|
refs_branches.push(format!("refs/heads/{}", branch));
|
||||||
}
|
}
|
||||||
if let Some(tags) = tags {
|
|
||||||
for tag in &tags {
|
for tag in &tags {
|
||||||
refs_branches.push(format!("refs/tags/{}", tag.unwrap()))
|
refs_branches.push(format!("refs/tags/{}", tag.unwrap()))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
refs_branches
|
refs_branches
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fast_forward(&self, repo: &Repository, branches: &Vec<String>) -> Result<(), Error> {
|
fn fast_forward(&self, repo_dir: &str, branches: &Vec<String>) -> Result<(), Error> {
|
||||||
common::verbose(self.verbose, 2, format!("Fast forwarding repo"));
|
let repo = Repository::open(repo_dir)?;
|
||||||
let mut fo = FetchOptions::new();
|
|
||||||
fo.download_tags(git2::AutotagOption::All);
|
common::verbose(self.verbose, 2, format!("Pulling origin"));
|
||||||
|
repo.find_remote("origin")?.fetch(&branches, None, None)?;
|
||||||
|
|
||||||
for branch in branches {
|
for branch in branches {
|
||||||
let refname = format!("refs/remotes/origin/{}", branch);
|
let refname = format!("refs/remotes/origin/{}", branch);
|
||||||
|
@ -105,51 +72,47 @@ impl Refractr {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(
|
fn fetch(&self, repo: &Repository, branches: &Vec<String>, ssh: bool, ssh_key: &String) -> Result<(), Error> {
|
||||||
&self,
|
let mut cb = RemoteCallbacks::new();
|
||||||
repo: &Repository,
|
|
||||||
branches: &Vec<String>,
|
|
||||||
ssh: bool,
|
|
||||||
ssh_key: &String,
|
|
||||||
strict: bool,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
common::verbose(self.verbose, 2, format!("Fetching repo"));
|
|
||||||
let mut fo = FetchOptions::new();
|
let mut fo = FetchOptions::new();
|
||||||
if ssh {
|
if ssh {
|
||||||
match self.set_up_ssh(ssh_key.clone(), strict.clone()) {
|
let key_string: String = ssh_key.clone();
|
||||||
Ok(cb) => {
|
cb.credentials(move |_,_,_| Cred::ssh_key(
|
||||||
fo.remote_callbacks(cb);
|
"git",
|
||||||
()
|
None,
|
||||||
},
|
Path::new(&key_string),
|
||||||
Err(e) => common::error_quit(
|
None));
|
||||||
format!("error setting up ssh: {}", e),
|
cb.certificate_check(|cert, url| {
|
||||||
ExitCode::ConfigError,
|
let mut sha256 = String::new();
|
||||||
),
|
for i in cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec() {
|
||||||
};
|
sha256.push_str(&hex::encode(i.to_string()));
|
||||||
|
}
|
||||||
|
common::warning(
|
||||||
|
format!("implicitly trusting unknown host {} with sha256 host key {}",
|
||||||
|
url,
|
||||||
|
hex::encode(cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec())));
|
||||||
|
common::warning(
|
||||||
|
format!("to ignore this error in the future, add this host to your known_hosts file"));
|
||||||
|
Ok(CertificateCheckStatus::CertificateOk)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
fo.download_tags(git2::AutotagOption::All);
|
fo.download_tags(git2::AutotagOption::All);
|
||||||
repo
|
fo.remote_callbacks(cb);
|
||||||
.find_remote("origin")?
|
repo.find_remote("origin")?.fetch(&branches, Some(&mut fo), None)?;
|
||||||
.fetch(&branches, Some(&mut fo), None)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_up_refs(&self, repo: &Repository, branches: &Vec<String>) -> Result<(), Error> {
|
fn set_up_refs(&self, repo: &Repository, branches: &Vec<String>) -> Result<(), Error> {
|
||||||
for branch in branches {
|
for branch in branches {
|
||||||
let mut fetch_head =
|
let mut fetch_head = repo.find_reference(format!("refs/remotes/origin/{}", branch).as_str())?;
|
||||||
repo.find_reference(format!("refs/remotes/origin/{}", branch).as_str())?;
|
|
||||||
fetch_head.rename(format!("refs/heads/{}", branch).as_str(), true, "")?;
|
fetch_head.rename(format!("refs/heads/{}", branch).as_str(), true, "")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_remotes<'a>(
|
fn make_remotes<'a> (&self, repo: &'a Repository, cfg: &ConfigFile) -> Result<Vec<String>, String> {
|
||||||
&self,
|
|
||||||
repo: &'a Repository,
|
|
||||||
cfg: &ConfigFile,
|
|
||||||
) -> Result<Vec<String>, String> {
|
|
||||||
// create remotes for each "to" repo
|
// create remotes for each "to" repo
|
||||||
let mut remote_list = Vec::new();
|
let mut remote_list = Vec::new();
|
||||||
for to in &cfg.config.to {
|
for to in &cfg.config.to {
|
||||||
|
@ -159,8 +122,7 @@ impl Refractr {
|
||||||
common::verbose(
|
common::verbose(
|
||||||
self.verbose,
|
self.verbose,
|
||||||
2,
|
2,
|
||||||
format!("Attempting to create remote {} for url {}", remote_id, to),
|
format!("Attempting to create remote {} for url {}", remote_id, to));
|
||||||
);
|
|
||||||
match repo.remote(remote_id.as_str(), to) {
|
match repo.remote(remote_id.as_str(), to) {
|
||||||
Ok(_) => remote_list.push(remote_id),
|
Ok(_) => remote_list.push(remote_id),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -170,70 +132,61 @@ impl Refractr {
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("failed to create remote: {}", e));
|
return Err(format!("failed to create remote: {}", e));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(remote_list)
|
Ok(remote_list)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_remotes(
|
fn push_remotes(&self, cfg: &Config, repo: &Repository, remote_list: &Vec<String>) -> Result<(), String> {
|
||||||
&self,
|
|
||||||
cfg: &Config,
|
|
||||||
repo: &Repository,
|
|
||||||
remote_list: &Vec<String>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
for id in remote_list {
|
for id in remote_list {
|
||||||
let mut remote = repo.find_remote(&id).unwrap();
|
let mut remote = repo.find_remote(&id).unwrap();
|
||||||
common::verbose(
|
common::verbose(
|
||||||
self.verbose,
|
self.verbose,
|
||||||
1,
|
1,
|
||||||
format!("Pushing to remote: {}", remote.url().unwrap()),
|
format!("Pushing to remote: {}", remote.url().unwrap()));
|
||||||
);
|
let mut callbacks = RemoteCallbacks::new();
|
||||||
let mut po = PushOptions::new();
|
callbacks.credentials(|_,_,_| Cred::ssh_key(
|
||||||
match self.set_up_ssh(cfg.git.ssh_identity_file.clone(), self.strict.clone()) {
|
"git",
|
||||||
Ok(cb) => {
|
None,
|
||||||
po.remote_callbacks(cb);
|
&Path::new(&cfg.git.ssh_identity_file),
|
||||||
()
|
None));
|
||||||
},
|
callbacks.certificate_check(|cert, url| {
|
||||||
Err(e) => common::error_quit(
|
let mut sha256 = String::new();
|
||||||
format!("error setting up ssh: {}", e),
|
for i in cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec() {
|
||||||
ExitCode::ConfigError,
|
sha256.push_str(&hex::encode(i.to_string()));
|
||||||
),
|
}
|
||||||
};
|
common::warning(
|
||||||
|
format!("implicitly trusting unknown host {} with sha256 host key {}",
|
||||||
|
url,
|
||||||
|
hex::encode(cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec())));
|
||||||
|
common::warning(
|
||||||
|
format!("to ignore this error in the future, add this host to your known_hosts file"));
|
||||||
|
Ok(CertificateCheckStatus::CertificateOk)
|
||||||
|
});
|
||||||
|
let mut push_options = PushOptions::new();
|
||||||
|
push_options.remote_callbacks(callbacks);
|
||||||
|
|
||||||
let mut refs = Vec::new();
|
let mut refs = Vec::new();
|
||||||
let mut refs_str = String::new();
|
let mut refs_str = String::new();
|
||||||
let strings = self.get_refs(
|
let tags = repo.tag_names(None).unwrap();
|
||||||
&cfg.branches,
|
let strings = self.get_refs(&cfg.branches, tags);
|
||||||
match cfg.push_tags {
|
|
||||||
true => Some(repo.tag_names(None).unwrap()),
|
|
||||||
false => None,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
for branch in &strings {
|
for branch in &strings {
|
||||||
refs.push(branch.as_str());
|
refs.push(branch.as_str());
|
||||||
refs_str.push_str(format!("{} ", branch).as_str());
|
refs_str.push_str(format!("{} ", branch).as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
common::verbose(self.verbose, 4, format!("ref list: {}", refs_str));
|
common::verbose(self.verbose, 4, format!("ref list: {}", refs_str));
|
||||||
match remote.push::<&str>(&refs, Some(&mut po)) {
|
match remote.push::<&str>(&refs, Some(&mut push_options)) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if self.strict {
|
if self.strict {
|
||||||
return Err(format!(
|
return Err(format!("failed to push to remote: {}: {}", remote.url().unwrap(), e))
|
||||||
"failed to push to remote: {}: {}",
|
|
||||||
remote.url().unwrap(),
|
|
||||||
e
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
common::warning(format!(
|
common::warning(format!("failed to push to remote: {}: {}", remote.url().unwrap(), e))
|
||||||
"failed to push to remote: {}: {}",
|
}
|
||||||
remote.url().unwrap(),
|
|
||||||
e
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,75 +199,50 @@ impl Refractr {
|
||||||
let r = running.clone();
|
let r = running.clone();
|
||||||
let count = repos.len();
|
let count = repos.len();
|
||||||
for i in 0..repos.len() {
|
for i in 0..repos.len() {
|
||||||
current_ints.push(i64::from(
|
current_ints.push(u64::from(repos[i].cfg.schedule.interval.unwrap().unsigned_abs()));
|
||||||
repos[i].cfg.schedule.interval.unwrap().unsigned_abs(),
|
};
|
||||||
));
|
|
||||||
}
|
|
||||||
let original_ints = current_ints.clone();
|
let original_ints = current_ints.clone();
|
||||||
|
|
||||||
ctrlc::set_handler(move || {
|
ctrlc::set_handler(move || {
|
||||||
r.store(false, Ordering::SeqCst);
|
r.store(false, Ordering::SeqCst);
|
||||||
})
|
}).expect("Failed to set ^C handler");
|
||||||
.expect("Failed to set ^C handler");
|
|
||||||
|
|
||||||
common::verbose(self.verbose, 1, format!("Starting scheduled loop"));
|
common::verbose(self.verbose, 1, format!("Starting scheduled loop"));
|
||||||
|
let min = *current_ints.iter().min().unwrap();
|
||||||
let mut do_break = false;
|
let mut do_break = false;
|
||||||
while !do_break {
|
while !do_break {
|
||||||
do_break = true;
|
do_break = true;
|
||||||
let min = *current_ints.iter().min().unwrap();
|
let sleep_int = time::Duration::from_secs(min);
|
||||||
let sleep_int = time::Duration::from_secs(min as u64);
|
|
||||||
let now = time::Instant::now();
|
let now = time::Instant::now();
|
||||||
|
|
||||||
common::verbose(
|
common::verbose(
|
||||||
self.verbose,
|
self.verbose,
|
||||||
2,
|
2,
|
||||||
format!("Sleeping for {} seconds", sleep_int.as_secs()),
|
format!("Sleeping for {} seconds", sleep_int.as_secs()));
|
||||||
);
|
|
||||||
while running.load(Ordering::SeqCst) {
|
while running.load(Ordering::SeqCst) {
|
||||||
thread::sleep(time::Duration::from_millis(200));
|
thread::sleep(time::Duration::from_secs(1));
|
||||||
if now.elapsed().as_secs() >= sleep_int.as_secs() {
|
if now.elapsed().as_secs() >= sleep_int.as_secs() {
|
||||||
common::verbose(self.verbose, 3, format!("Thread has awoken!"));
|
common::verbose(self.verbose, 3, format!("Thread has awoken!"));
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
current_ints[i] -= now.elapsed().as_secs() as i64;
|
current_ints[i] -= now.elapsed().as_secs();
|
||||||
common::verbose(
|
if i <= 0 {
|
||||||
self.verbose,
|
current_ints[i] = original_ints[i].clone();
|
||||||
4,
|
|
||||||
format!("checking repo: {}", repos[i].cfg.from),
|
|
||||||
);
|
|
||||||
if current_ints[i] <= 0 {
|
|
||||||
common::verbose(self.verbose, 4, format!("repo is ready for push"));
|
|
||||||
common::verbose(
|
common::verbose(
|
||||||
self.verbose,
|
self.verbose,
|
||||||
2,
|
2,
|
||||||
format!("Interval for {} has arrived, pulling", repos[i].cfg.from),
|
format!("Interval for {} has arrived, pulling", repos[i].cfg.from));
|
||||||
);
|
|
||||||
if let Err(e) = self.fetch(
|
|
||||||
&repos[i].repo,
|
|
||||||
&repos[i].cfg.branches,
|
|
||||||
repos[i].ssh,
|
|
||||||
&repos[i].cfg.git.ssh_identity_file,
|
|
||||||
self.strict.clone(),
|
|
||||||
) {
|
|
||||||
common::error_quit(
|
|
||||||
format!("failed to fetch repo {}: {}", repos[i].cfg.from, e),
|
|
||||||
ExitCode::FetchError,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = self.fast_forward(&repos[i].repo, &repos[i].cfg.branches);
|
let _ = self.fast_forward(&repos[i].path, &repos[i].cfg.branches);
|
||||||
if let Err(e) = self.push_remotes(&repos[i].cfg, &repos[i].repo, &repos[i].remotes) {
|
if let Err(e) = self.push_remotes(
|
||||||
common::error_quit(e, ExitCode::PushError)
|
&repos[i].cfg,
|
||||||
|
&repos[i].repo,
|
||||||
|
&repos[i].remotes) {
|
||||||
|
common::error(e, ExitCode::PushError)
|
||||||
};
|
};
|
||||||
current_ints[i] = original_ints[i].clone();
|
|
||||||
}
|
}
|
||||||
common::verbose(
|
|
||||||
self.verbose,
|
|
||||||
4,
|
|
||||||
format!("repo remaining time is now {}", current_ints[i]),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
do_break = false;
|
do_break = false;
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,220 +251,144 @@ impl Refractr {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean(&self, dirs: Vec<String>) -> Result<(), ReturnData> {
|
|
||||||
for dir in dirs {
|
|
||||||
common::verbose(self.verbose, 1, format!("removing working dir {}", dir));
|
|
||||||
if let Err(e) = remove_dir_all(&dir) {
|
|
||||||
common::warning(format!("could not clean up working dir: {}: {}", dir, e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(&self, cfgs: Vec<ConfigFile>) -> Result<(), ReturnData> {
|
pub fn run(&self, cfgs: Vec<ConfigFile>) -> Result<(), ReturnData> {
|
||||||
let r = self.run.clone();
|
|
||||||
ctrlc::set_handler(move ||
|
|
||||||
r.store(true, Ordering::SeqCst)
|
|
||||||
).expect("failed to set ^c handler");
|
|
||||||
|
|
||||||
common::verbose(self.verbose, 3, format!("Starting main refractr loop"));
|
common::verbose(self.verbose, 3, format!("Starting main refractr loop"));
|
||||||
let mut loop_repos = Vec::new();
|
let mut loop_repos = Vec::new();
|
||||||
let mut work_dirs = Vec::new();
|
|
||||||
|
|
||||||
for cfg in cfgs {
|
for cfg in cfgs {
|
||||||
// set up the working directory
|
// set up the working directory
|
||||||
common::verbose(self.verbose, 3, format!("Loading config: {}", cfg.path));
|
common::verbose(self.verbose, 3, format!("Loading config: {}", cfg.path));
|
||||||
let work_dir = config::get_work_dir(&cfg.config.work_dir);
|
let work_dir = self.set_up_work_dir(match &cfg.config.work_dir {
|
||||||
if let Err(e) = self.set_up_work_dir(PathBuf::from(&work_dir)) {
|
None => {
|
||||||
return Err(ReturnData {
|
if cfg!(windows) {
|
||||||
msg: e,
|
PathBuf::from(format!("{}\\refractr", env::var("TEMP").unwrap()))
|
||||||
code: ExitCode::FilesystemError,
|
} else {
|
||||||
});
|
PathBuf::from("/tmp/refractr")
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Some(path) => PathBuf::from(path)
|
||||||
|
});
|
||||||
|
let path_str = match work_dir {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => return Err(ReturnData {
|
||||||
|
code: ExitCode::FilesystemError,
|
||||||
|
msg: e
|
||||||
|
})
|
||||||
|
};
|
||||||
common::verbose(
|
common::verbose(
|
||||||
self.verbose,
|
self.verbose,
|
||||||
2,
|
2,
|
||||||
format!("Created working directory: {}", work_dir),
|
format!("Created working directory: {}", &path_str));
|
||||||
);
|
|
||||||
|
|
||||||
let repo_name = match &cfg.config.from.split("/").last() {
|
let repo_name = match &cfg.config.from.split("/").last() {
|
||||||
Some(split) => split.to_string(),
|
Some(split) => split.to_string(),
|
||||||
None => {
|
None => return Err(ReturnData {
|
||||||
return Err(ReturnData {
|
|
||||||
code: ExitCode::ParseError,
|
code: ExitCode::ParseError,
|
||||||
msg: format!("failed to parse repository name"),
|
msg: format!("failed to parse repository name")
|
||||||
})
|
})
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let ssh = cfg.config.from.starts_with("ssh://");
|
let ssh = cfg.config.from.starts_with("ssh://");
|
||||||
let mut builder = RepoBuilder::new();
|
let mut builder = RepoBuilder::new();
|
||||||
|
let mut cb = RemoteCallbacks::new();
|
||||||
let mut fo = FetchOptions::new();
|
let mut fo = FetchOptions::new();
|
||||||
|
|
||||||
// make initial clone
|
// make initial clone
|
||||||
if ssh {
|
if ssh {
|
||||||
match self.set_up_ssh(
|
let key_string = cfg.config.git.ssh_identity_file.clone();
|
||||||
cfg.config.git.ssh_identity_file.clone(),
|
cb.credentials(move |_,_,_| Cred::ssh_key(
|
||||||
self.strict.clone(),
|
"git",
|
||||||
) {
|
None,
|
||||||
Ok(cb) => {
|
Path::new(&key_string),
|
||||||
fo.remote_callbacks(cb);
|
None));
|
||||||
()
|
cb.certificate_check(|cert, url| {
|
||||||
},
|
let mut sha256 = String::new();
|
||||||
Err(e) => common::error_quit(
|
for i in cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec() {
|
||||||
format!("error setting up ssh: {}", e),
|
sha256.push_str(&hex::encode(i.to_string()));
|
||||||
ExitCode::ConfigError,
|
}
|
||||||
),
|
common::warning(
|
||||||
};
|
format!("implicitly trusting unknown host {} with sha256 host key {}",
|
||||||
|
url,
|
||||||
|
hex::encode(cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec())));
|
||||||
|
common::warning(
|
||||||
|
format!("to ignore this error in the future, add this host to your known_hosts file"));
|
||||||
|
Ok(CertificateCheckStatus::CertificateOk)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
fo.download_tags(git2::AutotagOption::All);
|
fo.download_tags(git2::AutotagOption::All);
|
||||||
|
fo.remote_callbacks(cb);
|
||||||
builder.fetch_options(fo);
|
builder.fetch_options(fo);
|
||||||
|
|
||||||
let repo_dir = format!("{}{}{}", &work_dir, MAIN_SEPARATOR_STR, repo_name);
|
let repo_dir = format!("{}{}{}", &path_str, MAIN_SEPARATOR_STR, repo_name);
|
||||||
common::verbose(
|
common::verbose(
|
||||||
self.verbose,
|
self.verbose,
|
||||||
1,
|
1,
|
||||||
format!("Cloning repository {} to {}", &cfg.config.from, &repo_dir),
|
format!("Cloning repository {} to {}", &cfg.config.from, &repo_dir));
|
||||||
);
|
|
||||||
let repo = match builder.clone(&cfg.config.from, Path::new(&repo_dir)) {
|
let repo = match builder.clone(&cfg.config.from, Path::new(&repo_dir)) {
|
||||||
Ok(repo) => repo,
|
Ok(repo) => repo,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if e.code() != ErrorCode::Exists {
|
if e.code() != ErrorCode::Exists {
|
||||||
common::error_quit(
|
common::error(format!("failed to clone repo to {}: {}", repo_dir, e), ExitCode::FilesystemError);
|
||||||
format!("failed to clone repo to {}: {}", repo_dir, e),
|
|
||||||
ExitCode::FilesystemError,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
common::warning(format!(
|
common::warning(format!("found existing repo at {}, attempting to use", repo_dir));
|
||||||
"found existing repo at {}, attempting to use",
|
match self.fast_forward(&repo_dir, &cfg.config.branches) {
|
||||||
repo_dir
|
Ok(_) => if let Ok(repo) = Repository::open(Path::new(&repo_dir)) {
|
||||||
));
|
repo
|
||||||
match Repository::open(Path::new(&repo_dir)) {
|
} else {
|
||||||
Ok(r) => {
|
|
||||||
if let Ok(rem) = r.find_remote("origin") {
|
|
||||||
match rem.url() {
|
|
||||||
Some(url) => {
|
|
||||||
if url != &cfg.config.from {
|
|
||||||
return Err(ReturnData {
|
return Err(ReturnData {
|
||||||
code: ExitCode::RepositoryError,
|
code: ExitCode::RepositoryError,
|
||||||
msg: format!(
|
msg: format!("failed to obtain existing repo")
|
||||||
"existing repo's origin does not match 'from' value in config: {}",
|
|
||||||
url
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
return Err(ReturnData {
|
|
||||||
code: ExitCode::RepositoryError,
|
|
||||||
msg: format!("could not obtain existing repo's origin: {}", repo_dir),
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
Err(e) => return Err(ReturnData {
|
||||||
}
|
|
||||||
// fetch updates for the repo
|
|
||||||
if let Err(e) = self.fetch(
|
|
||||||
&r,
|
|
||||||
&cfg.config.branches,
|
|
||||||
ssh,
|
|
||||||
&cfg.config.git.ssh_identity_file,
|
|
||||||
self.strict.clone(),
|
|
||||||
) {
|
|
||||||
common::error_quit(
|
|
||||||
format!("failed to fetch repo {}: {}", cfg.config.from, e),
|
|
||||||
ExitCode::FetchError,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// fast forward
|
|
||||||
match self.fast_forward(&r, &cfg.config.branches) {
|
|
||||||
Ok(_) => r,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(ReturnData {
|
|
||||||
code: ExitCode::RepositoryError,
|
code: ExitCode::RepositoryError,
|
||||||
msg: format!("failed to fast forward existing repo: {}", e),
|
msg: format!("failed to obtain existing repo: {}", e)
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
return Err(ReturnData {
|
|
||||||
code: ExitCode::RepositoryError,
|
|
||||||
msg: format!("failed to obtain existing repo: {}", e),
|
|
||||||
})
|
})
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = self.set_up_refs(&repo, &cfg.config.branches) {
|
self.set_up_refs(&repo, &cfg.config.branches).unwrap();
|
||||||
common::error_quit(
|
if let Err(e) = self.fetch(&repo,
|
||||||
format!("failed to set up refs: {}", e),
|
&cfg.config.branches,
|
||||||
ExitCode::RepositoryError,
|
ssh,
|
||||||
);
|
&cfg.config.git.ssh_identity_file) {
|
||||||
|
common::error(format!("failed to fetch repo {}: {}", cfg.config.from, e), ExitCode::FetchError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let remotes = match self.make_remotes(&repo, &cfg) {
|
let remotes = match self.make_remotes(&repo, &cfg) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => return Err(ReturnData {
|
||||||
return Err(ReturnData {
|
|
||||||
code: ExitCode::RemoteError,
|
code: ExitCode::RemoteError,
|
||||||
msg: e,
|
msg: e
|
||||||
})
|
})
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = self.push_remotes(&cfg.config, &repo, &remotes) {
|
if let Err(e) = self.push_remotes(&cfg.config, &repo, &remotes) {
|
||||||
common::error_quit(e, ExitCode::PushError);
|
common::error(e, ExitCode::PushError);
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.config.schedule.enabled {
|
if cfg.config.schedule.enabled {
|
||||||
loop_repos.push(OpenedRepository {
|
loop_repos.push(OpenedRepository {
|
||||||
repo,
|
repo,
|
||||||
|
path: repo_dir,
|
||||||
remotes,
|
remotes,
|
||||||
cfg: cfg.config,
|
cfg: cfg.config
|
||||||
ssh,
|
|
||||||
});
|
});
|
||||||
if !work_dirs.contains(&work_dir) {
|
|
||||||
work_dirs.push(work_dir);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let Err(e) = self.clean(vec![work_dir]) {
|
|
||||||
return Err(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.run.load(Ordering::SeqCst) {
|
|
||||||
common::error_quit(format!("exiting"), ExitCode::HaltError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// end for
|
|
||||||
|
|
||||||
let mut result = Ok(());
|
|
||||||
if loop_repos.len() >= 1 {
|
if loop_repos.len() >= 1 {
|
||||||
common::verbose(
|
common::verbose(
|
||||||
self.verbose,
|
self.verbose,
|
||||||
2,
|
2,
|
||||||
format!(
|
format!("{} configs have schedules enabled, setting up looper", loop_repos.len()));
|
||||||
"{} configs have schedules enabled, setting up looper",
|
return self.looper(loop_repos);
|
||||||
loop_repos.len()
|
|
||||||
),
|
|
||||||
);
|
|
||||||
result = self.looper(loop_repos);
|
|
||||||
} else {
|
} else {
|
||||||
common::verbose(
|
common::verbose(
|
||||||
self.verbose,
|
self.verbose,
|
||||||
2,
|
2,
|
||||||
format!("No scheduled configs found, exiting refractr"),
|
format!("No scheduled configs found, exiting refractr"));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.persist {
|
Ok(())
|
||||||
match result {
|
|
||||||
Ok(()) => return self.clean(work_dirs),
|
|
||||||
Err(_) => result = self.clean(work_dirs),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue