├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── http.rs ├── main.rs ├── shared.rs ├── terraform.rs ├── terragrunt.rs └── utils.rs └── tests ├── hashicorp-348FFC4C.asc ├── hashicorp-72D7468F.asc ├── main.rs ├── special.txt ├── terraform_0.13.1_SHA256SUMS ├── terraform_0.13.1_SHA256SUMS.348FFC4C.sig └── terraform_0.13.1_SHA256SUMS.72D7468F.sig /.gitattributes: -------------------------------------------------------------------------------- 1 | tests/*.asc text eol=lf 2 | tests/special.txt text eol=lf 3 | tests/*SHA256SUMS text eol=lf 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | check: 14 | runs-on: ubuntu-18.04 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v3 18 | 19 | - name: Setup rust toolchain 20 | uses: actions-rs/toolchain@v1 21 | with: 22 | profile: minimal 23 | toolchain: stable 24 | components: rustfmt, clippy 25 | 26 | - name: Check for errors 27 | run: cargo check 28 | 29 | - name: Check code formatting 30 | run: cargo fmt --all -- --check 31 | 32 | - name: Run clippy lints 33 | run: cargo clippy -- -D warnings 34 | 35 | build: 36 | needs: check 37 | runs-on: ${{ matrix.runner }} 38 | strategy: 39 | matrix: 40 | include: 41 | - target: x86_64-unknown-linux-gnu 42 | runner: ubuntu-18.04 43 | - target: x86_64-apple-darwin 44 | runner: macos-10.15 45 | - target: x86_64-pc-windows-msvc 46 | runner: windows-2019 47 | steps: 48 | - name: Checkout code 49 | uses: actions/checkout@v3 50 | 51 | - name: Setup rust toolchain 52 | uses: actions-rs/toolchain@v1 53 | with: 54 | profile: minimal 55 | toolchain: stable 56 | target: ${{ matrix.target }} 57 | 58 | - name: Run debug build 59 | run: cargo build --target ${{ matrix.target }} 60 | 61 | - name: Run all tests 62 | run: cargo test --target ${{ matrix.target }} 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "aes" 13 | version = "0.7.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" 16 | dependencies = [ 17 | "cfg-if", 18 | "cipher", 19 | "cpufeatures", 20 | "opaque-debug", 21 | ] 22 | 23 | [[package]] 24 | name = "aho-corasick" 25 | version = "0.7.18" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 28 | dependencies = [ 29 | "memchr", 30 | ] 31 | 32 | [[package]] 33 | name = "assert_cmd" 34 | version = "2.0.4" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "93ae1ddd39efd67689deb1979d80bad3bf7f2b09c6e6117c8d1f2443b5e2f83e" 37 | dependencies = [ 38 | "bstr", 39 | "doc-comment", 40 | "predicates", 41 | "predicates-core", 42 | "predicates-tree", 43 | "wait-timeout", 44 | ] 45 | 46 | [[package]] 47 | name = "autocfg" 48 | version = "1.1.0" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 51 | 52 | [[package]] 53 | name = "base64" 54 | version = "0.13.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 57 | 58 | [[package]] 59 | name = "base64ct" 60 | version = "1.0.1" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" 63 | 64 | [[package]] 65 | name = "bitfield" 66 | version = "0.13.2" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 69 | 70 | [[package]] 71 | name = "bitflags" 72 | version = "1.3.2" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 75 | 76 | [[package]] 77 | name = "block-buffer" 78 | version = "0.9.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 81 | dependencies = [ 82 | "block-padding", 83 | "generic-array", 84 | ] 85 | 86 | [[package]] 87 | name = "block-buffer" 88 | version = "0.10.2" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" 91 | dependencies = [ 92 | "generic-array", 93 | ] 94 | 95 | [[package]] 96 | name = "block-modes" 97 | version = "0.8.1" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" 100 | dependencies = [ 101 | "block-padding", 102 | "cipher", 103 | ] 104 | 105 | [[package]] 106 | name = "block-padding" 107 | version = "0.2.1" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" 110 | 111 | [[package]] 112 | name = "blowfish" 113 | version = "0.8.0" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "fe3ff3fc1de48c1ac2e3341c4df38b0d1bfb8fdf04632a187c8b75aaa319a7ab" 116 | dependencies = [ 117 | "byteorder", 118 | "cipher", 119 | "opaque-debug", 120 | ] 121 | 122 | [[package]] 123 | name = "bstr" 124 | version = "0.2.17" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" 127 | dependencies = [ 128 | "lazy_static", 129 | "memchr", 130 | "regex-automata", 131 | ] 132 | 133 | [[package]] 134 | name = "buf_redux" 135 | version = "0.8.4" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" 138 | dependencies = [ 139 | "memchr", 140 | "safemem", 141 | ] 142 | 143 | [[package]] 144 | name = "bumpalo" 145 | version = "3.10.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" 148 | 149 | [[package]] 150 | name = "byteorder" 151 | version = "1.4.3" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 154 | 155 | [[package]] 156 | name = "bytes" 157 | version = "1.1.0" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" 160 | 161 | [[package]] 162 | name = "bzip2" 163 | version = "0.4.3" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" 166 | dependencies = [ 167 | "bzip2-sys", 168 | "libc", 169 | ] 170 | 171 | [[package]] 172 | name = "bzip2-sys" 173 | version = "0.1.11+1.0.8" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" 176 | dependencies = [ 177 | "cc", 178 | "libc", 179 | "pkg-config", 180 | ] 181 | 182 | [[package]] 183 | name = "cast5" 184 | version = "0.10.0" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "f69790da27038b52ffcf09e7874e1aae353c674d65242549a733ad9372e7281f" 187 | dependencies = [ 188 | "byteorder", 189 | "cipher", 190 | "opaque-debug", 191 | ] 192 | 193 | [[package]] 194 | name = "cc" 195 | version = "1.0.73" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 198 | dependencies = [ 199 | "jobserver", 200 | ] 201 | 202 | [[package]] 203 | name = "cfb-mode" 204 | version = "0.7.1" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "750dfbb1b1f84475c1a92fed10fa5e76cb11adc0cda5225f137c5cac84e80860" 207 | dependencies = [ 208 | "cipher", 209 | ] 210 | 211 | [[package]] 212 | name = "cfg-if" 213 | version = "1.0.0" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 216 | 217 | [[package]] 218 | name = "chrono" 219 | version = "0.4.19" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 222 | dependencies = [ 223 | "libc", 224 | "num-integer", 225 | "num-traits", 226 | "winapi", 227 | ] 228 | 229 | [[package]] 230 | name = "cipher" 231 | version = "0.3.0" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" 234 | dependencies = [ 235 | "generic-array", 236 | ] 237 | 238 | [[package]] 239 | name = "circular" 240 | version = "0.3.0" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "b0fc239e0f6cb375d2402d48afb92f76f5404fd1df208a41930ec81eda078bea" 243 | 244 | [[package]] 245 | name = "clear_on_drop" 246 | version = "0.2.5" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "38508a63f4979f0048febc9966fadbd48e5dab31fd0ec6a3f151bbf4a74f7423" 249 | dependencies = [ 250 | "cc", 251 | ] 252 | 253 | [[package]] 254 | name = "const-oid" 255 | version = "0.7.1" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" 258 | 259 | [[package]] 260 | name = "constant_time_eq" 261 | version = "0.1.5" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 264 | 265 | [[package]] 266 | name = "cpufeatures" 267 | version = "0.2.2" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" 270 | dependencies = [ 271 | "libc", 272 | ] 273 | 274 | [[package]] 275 | name = "crc24" 276 | version = "0.1.6" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "fd121741cf3eb82c08dd3023eb55bf2665e5f60ec20f89760cf836ae4562e6a0" 279 | 280 | [[package]] 281 | name = "crc32fast" 282 | version = "1.3.2" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 285 | dependencies = [ 286 | "cfg-if", 287 | ] 288 | 289 | [[package]] 290 | name = "crossbeam-utils" 291 | version = "0.8.10" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" 294 | dependencies = [ 295 | "cfg-if", 296 | "once_cell", 297 | ] 298 | 299 | [[package]] 300 | name = "crypto-bigint" 301 | version = "0.3.2" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" 304 | dependencies = [ 305 | "generic-array", 306 | "subtle", 307 | ] 308 | 309 | [[package]] 310 | name = "crypto-common" 311 | version = "0.1.5" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f" 314 | dependencies = [ 315 | "generic-array", 316 | "typenum", 317 | ] 318 | 319 | [[package]] 320 | name = "curve25519-dalek" 321 | version = "3.2.0" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" 324 | dependencies = [ 325 | "byteorder", 326 | "digest 0.9.0", 327 | "rand_core 0.5.1", 328 | "subtle", 329 | "zeroize", 330 | ] 331 | 332 | [[package]] 333 | name = "darling" 334 | version = "0.10.2" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" 337 | dependencies = [ 338 | "darling_core", 339 | "darling_macro", 340 | ] 341 | 342 | [[package]] 343 | name = "darling_core" 344 | version = "0.10.2" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" 347 | dependencies = [ 348 | "fnv", 349 | "ident_case", 350 | "proc-macro2", 351 | "quote", 352 | "strsim", 353 | "syn", 354 | ] 355 | 356 | [[package]] 357 | name = "darling_macro" 358 | version = "0.10.2" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" 361 | dependencies = [ 362 | "darling_core", 363 | "quote", 364 | "syn", 365 | ] 366 | 367 | [[package]] 368 | name = "der" 369 | version = "0.5.1" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" 372 | dependencies = [ 373 | "const-oid", 374 | "crypto-bigint", 375 | "pem-rfc7468", 376 | ] 377 | 378 | [[package]] 379 | name = "derive_builder" 380 | version = "0.9.0" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0" 383 | dependencies = [ 384 | "darling", 385 | "derive_builder_core", 386 | "proc-macro2", 387 | "quote", 388 | "syn", 389 | ] 390 | 391 | [[package]] 392 | name = "derive_builder_core" 393 | version = "0.9.0" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" 396 | dependencies = [ 397 | "darling", 398 | "proc-macro2", 399 | "quote", 400 | "syn", 401 | ] 402 | 403 | [[package]] 404 | name = "des" 405 | version = "0.7.0" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "ac41dd49fb554432020d52c875fc290e110113f864c6b1b525cd62c7e7747a5d" 408 | dependencies = [ 409 | "byteorder", 410 | "cipher", 411 | "opaque-debug", 412 | ] 413 | 414 | [[package]] 415 | name = "difflib" 416 | version = "0.4.0" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" 419 | 420 | [[package]] 421 | name = "digest" 422 | version = "0.9.0" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 425 | dependencies = [ 426 | "generic-array", 427 | ] 428 | 429 | [[package]] 430 | name = "digest" 431 | version = "0.10.3" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" 434 | dependencies = [ 435 | "block-buffer 0.10.2", 436 | "crypto-common", 437 | "subtle", 438 | ] 439 | 440 | [[package]] 441 | name = "dirs" 442 | version = "4.0.0" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" 445 | dependencies = [ 446 | "dirs-sys", 447 | ] 448 | 449 | [[package]] 450 | name = "dirs-sys" 451 | version = "0.3.7" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" 454 | dependencies = [ 455 | "libc", 456 | "redox_users", 457 | "winapi", 458 | ] 459 | 460 | [[package]] 461 | name = "doc-comment" 462 | version = "0.3.3" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 465 | 466 | [[package]] 467 | name = "ed25519" 468 | version = "1.5.2" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" 471 | dependencies = [ 472 | "signature", 473 | ] 474 | 475 | [[package]] 476 | name = "ed25519-dalek" 477 | version = "1.0.1" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" 480 | dependencies = [ 481 | "curve25519-dalek", 482 | "ed25519", 483 | "rand 0.7.3", 484 | "serde", 485 | "sha2 0.9.9", 486 | "zeroize", 487 | ] 488 | 489 | [[package]] 490 | name = "either" 491 | version = "1.7.0" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" 494 | 495 | [[package]] 496 | name = "encoding_rs" 497 | version = "0.8.31" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" 500 | dependencies = [ 501 | "cfg-if", 502 | ] 503 | 504 | [[package]] 505 | name = "fastrand" 506 | version = "1.7.0" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" 509 | dependencies = [ 510 | "instant", 511 | ] 512 | 513 | [[package]] 514 | name = "flate2" 515 | version = "1.0.24" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" 518 | dependencies = [ 519 | "crc32fast", 520 | "miniz_oxide", 521 | ] 522 | 523 | [[package]] 524 | name = "float-cmp" 525 | version = "0.9.0" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" 528 | dependencies = [ 529 | "num-traits", 530 | ] 531 | 532 | [[package]] 533 | name = "fnv" 534 | version = "1.0.7" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 537 | 538 | [[package]] 539 | name = "form_urlencoded" 540 | version = "1.0.1" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 543 | dependencies = [ 544 | "matches", 545 | "percent-encoding", 546 | ] 547 | 548 | [[package]] 549 | name = "futures-channel" 550 | version = "0.3.21" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" 553 | dependencies = [ 554 | "futures-core", 555 | ] 556 | 557 | [[package]] 558 | name = "futures-core" 559 | version = "0.3.21" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" 562 | 563 | [[package]] 564 | name = "futures-io" 565 | version = "0.3.21" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" 568 | 569 | [[package]] 570 | name = "futures-sink" 571 | version = "0.3.21" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" 574 | 575 | [[package]] 576 | name = "futures-task" 577 | version = "0.3.21" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" 580 | 581 | [[package]] 582 | name = "futures-util" 583 | version = "0.3.21" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" 586 | dependencies = [ 587 | "futures-core", 588 | "futures-io", 589 | "futures-task", 590 | "memchr", 591 | "pin-project-lite", 592 | "pin-utils", 593 | "slab", 594 | ] 595 | 596 | [[package]] 597 | name = "generic-array" 598 | version = "0.14.5" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" 601 | dependencies = [ 602 | "typenum", 603 | "version_check 0.9.4", 604 | ] 605 | 606 | [[package]] 607 | name = "getrandom" 608 | version = "0.1.16" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 611 | dependencies = [ 612 | "cfg-if", 613 | "libc", 614 | "wasi 0.9.0+wasi-snapshot-preview1", 615 | ] 616 | 617 | [[package]] 618 | name = "getrandom" 619 | version = "0.2.7" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" 622 | dependencies = [ 623 | "cfg-if", 624 | "libc", 625 | "wasi 0.11.0+wasi-snapshot-preview1", 626 | ] 627 | 628 | [[package]] 629 | name = "git2" 630 | version = "0.14.4" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" 633 | dependencies = [ 634 | "bitflags", 635 | "libc", 636 | "libgit2-sys", 637 | "log", 638 | "openssl-probe", 639 | "openssl-sys", 640 | "url", 641 | ] 642 | 643 | [[package]] 644 | name = "h2" 645 | version = "0.3.13" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" 648 | dependencies = [ 649 | "bytes", 650 | "fnv", 651 | "futures-core", 652 | "futures-sink", 653 | "futures-util", 654 | "http", 655 | "indexmap", 656 | "slab", 657 | "tokio", 658 | "tokio-util", 659 | "tracing", 660 | ] 661 | 662 | [[package]] 663 | name = "hashbrown" 664 | version = "0.12.2" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" 667 | 668 | [[package]] 669 | name = "hermit-abi" 670 | version = "0.1.19" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 673 | dependencies = [ 674 | "libc", 675 | ] 676 | 677 | [[package]] 678 | name = "hex" 679 | version = "0.4.3" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 682 | 683 | [[package]] 684 | name = "hmac" 685 | version = "0.12.1" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 688 | dependencies = [ 689 | "digest 0.10.3", 690 | ] 691 | 692 | [[package]] 693 | name = "http" 694 | version = "0.2.8" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" 697 | dependencies = [ 698 | "bytes", 699 | "fnv", 700 | "itoa", 701 | ] 702 | 703 | [[package]] 704 | name = "http-body" 705 | version = "0.4.5" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 708 | dependencies = [ 709 | "bytes", 710 | "http", 711 | "pin-project-lite", 712 | ] 713 | 714 | [[package]] 715 | name = "httparse" 716 | version = "1.7.1" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" 719 | 720 | [[package]] 721 | name = "httpdate" 722 | version = "1.0.2" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 725 | 726 | [[package]] 727 | name = "hyper" 728 | version = "0.14.20" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" 731 | dependencies = [ 732 | "bytes", 733 | "futures-channel", 734 | "futures-core", 735 | "futures-util", 736 | "h2", 737 | "http", 738 | "http-body", 739 | "httparse", 740 | "httpdate", 741 | "itoa", 742 | "pin-project-lite", 743 | "socket2", 744 | "tokio", 745 | "tower-service", 746 | "tracing", 747 | "want", 748 | ] 749 | 750 | [[package]] 751 | name = "hyper-rustls" 752 | version = "0.23.0" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" 755 | dependencies = [ 756 | "http", 757 | "hyper", 758 | "rustls", 759 | "tokio", 760 | "tokio-rustls", 761 | ] 762 | 763 | [[package]] 764 | name = "ident_case" 765 | version = "1.0.1" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 768 | 769 | [[package]] 770 | name = "idna" 771 | version = "0.2.3" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 774 | dependencies = [ 775 | "matches", 776 | "unicode-bidi", 777 | "unicode-normalization", 778 | ] 779 | 780 | [[package]] 781 | name = "indexmap" 782 | version = "1.9.1" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 785 | dependencies = [ 786 | "autocfg", 787 | "hashbrown", 788 | ] 789 | 790 | [[package]] 791 | name = "instant" 792 | version = "0.1.12" 793 | source = "registry+https://github.com/rust-lang/crates.io-index" 794 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 795 | dependencies = [ 796 | "cfg-if", 797 | ] 798 | 799 | [[package]] 800 | name = "ipnet" 801 | version = "2.5.0" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" 804 | 805 | [[package]] 806 | name = "itertools" 807 | version = "0.10.3" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" 810 | dependencies = [ 811 | "either", 812 | ] 813 | 814 | [[package]] 815 | name = "itoa" 816 | version = "1.0.2" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" 819 | 820 | [[package]] 821 | name = "jobserver" 822 | version = "0.1.24" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" 825 | dependencies = [ 826 | "libc", 827 | ] 828 | 829 | [[package]] 830 | name = "js-sys" 831 | version = "0.3.58" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" 834 | dependencies = [ 835 | "wasm-bindgen", 836 | ] 837 | 838 | [[package]] 839 | name = "keccak" 840 | version = "0.1.2" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" 843 | 844 | [[package]] 845 | name = "lazy_static" 846 | version = "1.4.0" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 849 | dependencies = [ 850 | "spin", 851 | ] 852 | 853 | [[package]] 854 | name = "libc" 855 | version = "0.2.126" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" 858 | 859 | [[package]] 860 | name = "libgit2-sys" 861 | version = "0.13.4+1.4.2" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1" 864 | dependencies = [ 865 | "cc", 866 | "libc", 867 | "libz-sys", 868 | "openssl-sys", 869 | "pkg-config", 870 | ] 871 | 872 | [[package]] 873 | name = "libm" 874 | version = "0.2.2" 875 | source = "registry+https://github.com/rust-lang/crates.io-index" 876 | checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" 877 | 878 | [[package]] 879 | name = "libz-sys" 880 | version = "1.1.8" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" 883 | dependencies = [ 884 | "cc", 885 | "libc", 886 | "pkg-config", 887 | "vcpkg", 888 | ] 889 | 890 | [[package]] 891 | name = "log" 892 | version = "0.4.17" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 895 | dependencies = [ 896 | "cfg-if", 897 | ] 898 | 899 | [[package]] 900 | name = "matches" 901 | version = "0.1.9" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" 904 | 905 | [[package]] 906 | name = "md-5" 907 | version = "0.9.1" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" 910 | dependencies = [ 911 | "block-buffer 0.9.0", 912 | "digest 0.9.0", 913 | "opaque-debug", 914 | ] 915 | 916 | [[package]] 917 | name = "memchr" 918 | version = "2.5.0" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 921 | 922 | [[package]] 923 | name = "mime" 924 | version = "0.3.16" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 927 | 928 | [[package]] 929 | name = "miniz_oxide" 930 | version = "0.5.3" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" 933 | dependencies = [ 934 | "adler", 935 | ] 936 | 937 | [[package]] 938 | name = "mio" 939 | version = "0.8.4" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" 942 | dependencies = [ 943 | "libc", 944 | "log", 945 | "wasi 0.11.0+wasi-snapshot-preview1", 946 | "windows-sys", 947 | ] 948 | 949 | [[package]] 950 | name = "nom" 951 | version = "4.2.3" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" 954 | dependencies = [ 955 | "memchr", 956 | "version_check 0.1.5", 957 | ] 958 | 959 | [[package]] 960 | name = "normalize-line-endings" 961 | version = "0.3.0" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" 964 | 965 | [[package]] 966 | name = "num-bigint-dig" 967 | version = "0.8.1" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "566d173b2f9406afbc5510a90925d5a2cd80cae4605631f1212303df265de011" 970 | dependencies = [ 971 | "byteorder", 972 | "lazy_static", 973 | "libm", 974 | "num-integer", 975 | "num-iter", 976 | "num-traits", 977 | "rand 0.8.5", 978 | "serde", 979 | "smallvec", 980 | "zeroize", 981 | ] 982 | 983 | [[package]] 984 | name = "num-derive" 985 | version = "0.3.3" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" 988 | dependencies = [ 989 | "proc-macro2", 990 | "quote", 991 | "syn", 992 | ] 993 | 994 | [[package]] 995 | name = "num-integer" 996 | version = "0.1.45" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 999 | dependencies = [ 1000 | "autocfg", 1001 | "num-traits", 1002 | ] 1003 | 1004 | [[package]] 1005 | name = "num-iter" 1006 | version = "0.1.43" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" 1009 | dependencies = [ 1010 | "autocfg", 1011 | "num-integer", 1012 | "num-traits", 1013 | ] 1014 | 1015 | [[package]] 1016 | name = "num-traits" 1017 | version = "0.2.15" 1018 | source = "registry+https://github.com/rust-lang/crates.io-index" 1019 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 1020 | dependencies = [ 1021 | "autocfg", 1022 | "libm", 1023 | ] 1024 | 1025 | [[package]] 1026 | name = "num_cpus" 1027 | version = "1.13.1" 1028 | source = "registry+https://github.com/rust-lang/crates.io-index" 1029 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 1030 | dependencies = [ 1031 | "hermit-abi", 1032 | "libc", 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "num_threads" 1037 | version = "0.1.6" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" 1040 | dependencies = [ 1041 | "libc", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "once_cell" 1046 | version = "1.13.0" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" 1049 | 1050 | [[package]] 1051 | name = "opaque-debug" 1052 | version = "0.3.0" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 1055 | 1056 | [[package]] 1057 | name = "openssl-probe" 1058 | version = "0.1.5" 1059 | source = "registry+https://github.com/rust-lang/crates.io-index" 1060 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1061 | 1062 | [[package]] 1063 | name = "openssl-src" 1064 | version = "111.22.0+1.1.1q" 1065 | source = "registry+https://github.com/rust-lang/crates.io-index" 1066 | checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853" 1067 | dependencies = [ 1068 | "cc", 1069 | ] 1070 | 1071 | [[package]] 1072 | name = "openssl-sys" 1073 | version = "0.9.75" 1074 | source = "registry+https://github.com/rust-lang/crates.io-index" 1075 | checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" 1076 | dependencies = [ 1077 | "autocfg", 1078 | "cc", 1079 | "libc", 1080 | "openssl-src", 1081 | "pkg-config", 1082 | "vcpkg", 1083 | ] 1084 | 1085 | [[package]] 1086 | name = "password-hash" 1087 | version = "0.3.2" 1088 | source = "registry+https://github.com/rust-lang/crates.io-index" 1089 | checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" 1090 | dependencies = [ 1091 | "base64ct", 1092 | "rand_core 0.6.3", 1093 | "subtle", 1094 | ] 1095 | 1096 | [[package]] 1097 | name = "pbkdf2" 1098 | version = "0.10.1" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" 1101 | dependencies = [ 1102 | "digest 0.10.3", 1103 | "hmac", 1104 | "password-hash", 1105 | "sha2 0.10.2", 1106 | ] 1107 | 1108 | [[package]] 1109 | name = "pem-rfc7468" 1110 | version = "0.3.1" 1111 | source = "registry+https://github.com/rust-lang/crates.io-index" 1112 | checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30" 1113 | dependencies = [ 1114 | "base64ct", 1115 | ] 1116 | 1117 | [[package]] 1118 | name = "percent-encoding" 1119 | version = "2.1.0" 1120 | source = "registry+https://github.com/rust-lang/crates.io-index" 1121 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 1122 | 1123 | [[package]] 1124 | name = "pgp" 1125 | version = "0.8.0" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "c0c63db779c3f090b540dfa0484f8adc2d380e3aa60cdb0f3a7a97454e22edc5" 1128 | dependencies = [ 1129 | "aes", 1130 | "base64", 1131 | "bitfield", 1132 | "block-modes", 1133 | "block-padding", 1134 | "blowfish", 1135 | "buf_redux", 1136 | "byteorder", 1137 | "cast5", 1138 | "cfb-mode", 1139 | "chrono", 1140 | "cipher", 1141 | "circular", 1142 | "clear_on_drop", 1143 | "crc24", 1144 | "derive_builder", 1145 | "des", 1146 | "digest 0.9.0", 1147 | "ed25519-dalek", 1148 | "flate2", 1149 | "generic-array", 1150 | "hex", 1151 | "lazy_static", 1152 | "log", 1153 | "md-5", 1154 | "nom", 1155 | "num-bigint-dig", 1156 | "num-derive", 1157 | "num-traits", 1158 | "rand 0.8.5", 1159 | "ripemd160", 1160 | "rsa", 1161 | "sha-1", 1162 | "sha2 0.9.9", 1163 | "sha3", 1164 | "signature", 1165 | "smallvec", 1166 | "thiserror", 1167 | "twofish", 1168 | "x25519-dalek", 1169 | "zeroize", 1170 | ] 1171 | 1172 | [[package]] 1173 | name = "pico-args" 1174 | version = "0.5.0" 1175 | source = "registry+https://github.com/rust-lang/crates.io-index" 1176 | checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" 1177 | 1178 | [[package]] 1179 | name = "pin-project-lite" 1180 | version = "0.2.9" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 1183 | 1184 | [[package]] 1185 | name = "pin-utils" 1186 | version = "0.1.0" 1187 | source = "registry+https://github.com/rust-lang/crates.io-index" 1188 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1189 | 1190 | [[package]] 1191 | name = "pkcs1" 1192 | version = "0.3.3" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" 1195 | dependencies = [ 1196 | "der", 1197 | "pkcs8", 1198 | "zeroize", 1199 | ] 1200 | 1201 | [[package]] 1202 | name = "pkcs8" 1203 | version = "0.8.0" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" 1206 | dependencies = [ 1207 | "der", 1208 | "spki", 1209 | "zeroize", 1210 | ] 1211 | 1212 | [[package]] 1213 | name = "pkg-config" 1214 | version = "0.3.25" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" 1217 | 1218 | [[package]] 1219 | name = "ppv-lite86" 1220 | version = "0.2.16" 1221 | source = "registry+https://github.com/rust-lang/crates.io-index" 1222 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 1223 | 1224 | [[package]] 1225 | name = "predicates" 1226 | version = "2.1.1" 1227 | source = "registry+https://github.com/rust-lang/crates.io-index" 1228 | checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" 1229 | dependencies = [ 1230 | "difflib", 1231 | "float-cmp", 1232 | "itertools", 1233 | "normalize-line-endings", 1234 | "predicates-core", 1235 | "regex", 1236 | ] 1237 | 1238 | [[package]] 1239 | name = "predicates-core" 1240 | version = "1.0.3" 1241 | source = "registry+https://github.com/rust-lang/crates.io-index" 1242 | checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" 1243 | 1244 | [[package]] 1245 | name = "predicates-tree" 1246 | version = "1.0.5" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" 1249 | dependencies = [ 1250 | "predicates-core", 1251 | "termtree", 1252 | ] 1253 | 1254 | [[package]] 1255 | name = "proc-macro2" 1256 | version = "1.0.40" 1257 | source = "registry+https://github.com/rust-lang/crates.io-index" 1258 | checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" 1259 | dependencies = [ 1260 | "unicode-ident", 1261 | ] 1262 | 1263 | [[package]] 1264 | name = "quote" 1265 | version = "1.0.20" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" 1268 | dependencies = [ 1269 | "proc-macro2", 1270 | ] 1271 | 1272 | [[package]] 1273 | name = "rand" 1274 | version = "0.7.3" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1277 | dependencies = [ 1278 | "getrandom 0.1.16", 1279 | "libc", 1280 | "rand_chacha 0.2.2", 1281 | "rand_core 0.5.1", 1282 | "rand_hc", 1283 | ] 1284 | 1285 | [[package]] 1286 | name = "rand" 1287 | version = "0.8.5" 1288 | source = "registry+https://github.com/rust-lang/crates.io-index" 1289 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1290 | dependencies = [ 1291 | "libc", 1292 | "rand_chacha 0.3.1", 1293 | "rand_core 0.6.3", 1294 | ] 1295 | 1296 | [[package]] 1297 | name = "rand_chacha" 1298 | version = "0.2.2" 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" 1300 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1301 | dependencies = [ 1302 | "ppv-lite86", 1303 | "rand_core 0.5.1", 1304 | ] 1305 | 1306 | [[package]] 1307 | name = "rand_chacha" 1308 | version = "0.3.1" 1309 | source = "registry+https://github.com/rust-lang/crates.io-index" 1310 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1311 | dependencies = [ 1312 | "ppv-lite86", 1313 | "rand_core 0.6.3", 1314 | ] 1315 | 1316 | [[package]] 1317 | name = "rand_core" 1318 | version = "0.5.1" 1319 | source = "registry+https://github.com/rust-lang/crates.io-index" 1320 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1321 | dependencies = [ 1322 | "getrandom 0.1.16", 1323 | ] 1324 | 1325 | [[package]] 1326 | name = "rand_core" 1327 | version = "0.6.3" 1328 | source = "registry+https://github.com/rust-lang/crates.io-index" 1329 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 1330 | dependencies = [ 1331 | "getrandom 0.2.7", 1332 | ] 1333 | 1334 | [[package]] 1335 | name = "rand_hc" 1336 | version = "0.2.0" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1339 | dependencies = [ 1340 | "rand_core 0.5.1", 1341 | ] 1342 | 1343 | [[package]] 1344 | name = "redox_syscall" 1345 | version = "0.2.13" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" 1348 | dependencies = [ 1349 | "bitflags", 1350 | ] 1351 | 1352 | [[package]] 1353 | name = "redox_users" 1354 | version = "0.4.3" 1355 | source = "registry+https://github.com/rust-lang/crates.io-index" 1356 | checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" 1357 | dependencies = [ 1358 | "getrandom 0.2.7", 1359 | "redox_syscall", 1360 | "thiserror", 1361 | ] 1362 | 1363 | [[package]] 1364 | name = "regex" 1365 | version = "1.6.0" 1366 | source = "registry+https://github.com/rust-lang/crates.io-index" 1367 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 1368 | dependencies = [ 1369 | "aho-corasick", 1370 | "memchr", 1371 | "regex-syntax", 1372 | ] 1373 | 1374 | [[package]] 1375 | name = "regex-automata" 1376 | version = "0.1.10" 1377 | source = "registry+https://github.com/rust-lang/crates.io-index" 1378 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 1379 | 1380 | [[package]] 1381 | name = "regex-syntax" 1382 | version = "0.6.27" 1383 | source = "registry+https://github.com/rust-lang/crates.io-index" 1384 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 1385 | 1386 | [[package]] 1387 | name = "remove_dir_all" 1388 | version = "0.5.3" 1389 | source = "registry+https://github.com/rust-lang/crates.io-index" 1390 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1391 | dependencies = [ 1392 | "winapi", 1393 | ] 1394 | 1395 | [[package]] 1396 | name = "reqwest" 1397 | version = "0.11.11" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" 1400 | dependencies = [ 1401 | "base64", 1402 | "bytes", 1403 | "encoding_rs", 1404 | "futures-core", 1405 | "futures-util", 1406 | "h2", 1407 | "http", 1408 | "http-body", 1409 | "hyper", 1410 | "hyper-rustls", 1411 | "ipnet", 1412 | "js-sys", 1413 | "lazy_static", 1414 | "log", 1415 | "mime", 1416 | "percent-encoding", 1417 | "pin-project-lite", 1418 | "rustls", 1419 | "rustls-pemfile", 1420 | "serde", 1421 | "serde_json", 1422 | "serde_urlencoded", 1423 | "tokio", 1424 | "tokio-rustls", 1425 | "tower-service", 1426 | "url", 1427 | "wasm-bindgen", 1428 | "wasm-bindgen-futures", 1429 | "web-sys", 1430 | "webpki-roots", 1431 | "winreg", 1432 | ] 1433 | 1434 | [[package]] 1435 | name = "ring" 1436 | version = "0.16.20" 1437 | source = "registry+https://github.com/rust-lang/crates.io-index" 1438 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 1439 | dependencies = [ 1440 | "cc", 1441 | "libc", 1442 | "once_cell", 1443 | "spin", 1444 | "untrusted", 1445 | "web-sys", 1446 | "winapi", 1447 | ] 1448 | 1449 | [[package]] 1450 | name = "ripemd160" 1451 | version = "0.9.1" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" 1454 | dependencies = [ 1455 | "block-buffer 0.9.0", 1456 | "digest 0.9.0", 1457 | "opaque-debug", 1458 | ] 1459 | 1460 | [[package]] 1461 | name = "rsa" 1462 | version = "0.6.1" 1463 | source = "registry+https://github.com/rust-lang/crates.io-index" 1464 | checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" 1465 | dependencies = [ 1466 | "byteorder", 1467 | "digest 0.10.3", 1468 | "num-bigint-dig", 1469 | "num-integer", 1470 | "num-iter", 1471 | "num-traits", 1472 | "pkcs1", 1473 | "pkcs8", 1474 | "rand_core 0.6.3", 1475 | "smallvec", 1476 | "subtle", 1477 | "zeroize", 1478 | ] 1479 | 1480 | [[package]] 1481 | name = "rustls" 1482 | version = "0.20.6" 1483 | source = "registry+https://github.com/rust-lang/crates.io-index" 1484 | checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" 1485 | dependencies = [ 1486 | "log", 1487 | "ring", 1488 | "sct", 1489 | "webpki", 1490 | ] 1491 | 1492 | [[package]] 1493 | name = "rustls-pemfile" 1494 | version = "1.0.0" 1495 | source = "registry+https://github.com/rust-lang/crates.io-index" 1496 | checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" 1497 | dependencies = [ 1498 | "base64", 1499 | ] 1500 | 1501 | [[package]] 1502 | name = "ryu" 1503 | version = "1.0.10" 1504 | source = "registry+https://github.com/rust-lang/crates.io-index" 1505 | checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" 1506 | 1507 | [[package]] 1508 | name = "safemem" 1509 | version = "0.3.3" 1510 | source = "registry+https://github.com/rust-lang/crates.io-index" 1511 | checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" 1512 | 1513 | [[package]] 1514 | name = "same-file" 1515 | version = "1.0.6" 1516 | source = "registry+https://github.com/rust-lang/crates.io-index" 1517 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1518 | dependencies = [ 1519 | "winapi-util", 1520 | ] 1521 | 1522 | [[package]] 1523 | name = "sct" 1524 | version = "0.7.0" 1525 | source = "registry+https://github.com/rust-lang/crates.io-index" 1526 | checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" 1527 | dependencies = [ 1528 | "ring", 1529 | "untrusted", 1530 | ] 1531 | 1532 | [[package]] 1533 | name = "semver" 1534 | version = "1.0.12" 1535 | source = "registry+https://github.com/rust-lang/crates.io-index" 1536 | checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1" 1537 | 1538 | [[package]] 1539 | name = "serde" 1540 | version = "1.0.139" 1541 | source = "registry+https://github.com/rust-lang/crates.io-index" 1542 | checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" 1543 | 1544 | [[package]] 1545 | name = "serde_json" 1546 | version = "1.0.82" 1547 | source = "registry+https://github.com/rust-lang/crates.io-index" 1548 | checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" 1549 | dependencies = [ 1550 | "itoa", 1551 | "ryu", 1552 | "serde", 1553 | ] 1554 | 1555 | [[package]] 1556 | name = "serde_urlencoded" 1557 | version = "0.7.1" 1558 | source = "registry+https://github.com/rust-lang/crates.io-index" 1559 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1560 | dependencies = [ 1561 | "form_urlencoded", 1562 | "itoa", 1563 | "ryu", 1564 | "serde", 1565 | ] 1566 | 1567 | [[package]] 1568 | name = "sha-1" 1569 | version = "0.9.8" 1570 | source = "registry+https://github.com/rust-lang/crates.io-index" 1571 | checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" 1572 | dependencies = [ 1573 | "block-buffer 0.9.0", 1574 | "cfg-if", 1575 | "cpufeatures", 1576 | "digest 0.9.0", 1577 | "opaque-debug", 1578 | ] 1579 | 1580 | [[package]] 1581 | name = "sha1" 1582 | version = "0.10.1" 1583 | source = "registry+https://github.com/rust-lang/crates.io-index" 1584 | checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f" 1585 | dependencies = [ 1586 | "cfg-if", 1587 | "cpufeatures", 1588 | "digest 0.10.3", 1589 | ] 1590 | 1591 | [[package]] 1592 | name = "sha2" 1593 | version = "0.9.9" 1594 | source = "registry+https://github.com/rust-lang/crates.io-index" 1595 | checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" 1596 | dependencies = [ 1597 | "block-buffer 0.9.0", 1598 | "cfg-if", 1599 | "cpufeatures", 1600 | "digest 0.9.0", 1601 | "opaque-debug", 1602 | ] 1603 | 1604 | [[package]] 1605 | name = "sha2" 1606 | version = "0.10.2" 1607 | source = "registry+https://github.com/rust-lang/crates.io-index" 1608 | checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" 1609 | dependencies = [ 1610 | "cfg-if", 1611 | "cpufeatures", 1612 | "digest 0.10.3", 1613 | ] 1614 | 1615 | [[package]] 1616 | name = "sha3" 1617 | version = "0.9.1" 1618 | source = "registry+https://github.com/rust-lang/crates.io-index" 1619 | checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" 1620 | dependencies = [ 1621 | "block-buffer 0.9.0", 1622 | "digest 0.9.0", 1623 | "keccak", 1624 | "opaque-debug", 1625 | ] 1626 | 1627 | [[package]] 1628 | name = "signature" 1629 | version = "1.5.0" 1630 | source = "registry+https://github.com/rust-lang/crates.io-index" 1631 | checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4" 1632 | 1633 | [[package]] 1634 | name = "slab" 1635 | version = "0.4.6" 1636 | source = "registry+https://github.com/rust-lang/crates.io-index" 1637 | checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" 1638 | 1639 | [[package]] 1640 | name = "smallvec" 1641 | version = "1.9.0" 1642 | source = "registry+https://github.com/rust-lang/crates.io-index" 1643 | checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" 1644 | 1645 | [[package]] 1646 | name = "socket2" 1647 | version = "0.4.4" 1648 | source = "registry+https://github.com/rust-lang/crates.io-index" 1649 | checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" 1650 | dependencies = [ 1651 | "libc", 1652 | "winapi", 1653 | ] 1654 | 1655 | [[package]] 1656 | name = "spin" 1657 | version = "0.5.2" 1658 | source = "registry+https://github.com/rust-lang/crates.io-index" 1659 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 1660 | 1661 | [[package]] 1662 | name = "spki" 1663 | version = "0.5.4" 1664 | source = "registry+https://github.com/rust-lang/crates.io-index" 1665 | checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" 1666 | dependencies = [ 1667 | "base64ct", 1668 | "der", 1669 | ] 1670 | 1671 | [[package]] 1672 | name = "strsim" 1673 | version = "0.9.3" 1674 | source = "registry+https://github.com/rust-lang/crates.io-index" 1675 | checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" 1676 | 1677 | [[package]] 1678 | name = "subtle" 1679 | version = "2.4.1" 1680 | source = "registry+https://github.com/rust-lang/crates.io-index" 1681 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 1682 | 1683 | [[package]] 1684 | name = "syn" 1685 | version = "1.0.98" 1686 | source = "registry+https://github.com/rust-lang/crates.io-index" 1687 | checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" 1688 | dependencies = [ 1689 | "proc-macro2", 1690 | "quote", 1691 | "unicode-ident", 1692 | ] 1693 | 1694 | [[package]] 1695 | name = "synstructure" 1696 | version = "0.12.6" 1697 | source = "registry+https://github.com/rust-lang/crates.io-index" 1698 | checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" 1699 | dependencies = [ 1700 | "proc-macro2", 1701 | "quote", 1702 | "syn", 1703 | "unicode-xid", 1704 | ] 1705 | 1706 | [[package]] 1707 | name = "tempfile" 1708 | version = "3.3.0" 1709 | source = "registry+https://github.com/rust-lang/crates.io-index" 1710 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 1711 | dependencies = [ 1712 | "cfg-if", 1713 | "fastrand", 1714 | "libc", 1715 | "redox_syscall", 1716 | "remove_dir_all", 1717 | "winapi", 1718 | ] 1719 | 1720 | [[package]] 1721 | name = "termtree" 1722 | version = "0.2.4" 1723 | source = "registry+https://github.com/rust-lang/crates.io-index" 1724 | checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" 1725 | 1726 | [[package]] 1727 | name = "terve" 1728 | version = "0.8.0" 1729 | dependencies = [ 1730 | "assert_cmd", 1731 | "bytes", 1732 | "dirs", 1733 | "git2", 1734 | "hex", 1735 | "pgp", 1736 | "pico-args", 1737 | "predicates", 1738 | "regex", 1739 | "reqwest", 1740 | "same-file", 1741 | "semver", 1742 | "sha2 0.10.2", 1743 | "tempfile", 1744 | "zip", 1745 | ] 1746 | 1747 | [[package]] 1748 | name = "thiserror" 1749 | version = "1.0.31" 1750 | source = "registry+https://github.com/rust-lang/crates.io-index" 1751 | checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" 1752 | dependencies = [ 1753 | "thiserror-impl", 1754 | ] 1755 | 1756 | [[package]] 1757 | name = "thiserror-impl" 1758 | version = "1.0.31" 1759 | source = "registry+https://github.com/rust-lang/crates.io-index" 1760 | checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" 1761 | dependencies = [ 1762 | "proc-macro2", 1763 | "quote", 1764 | "syn", 1765 | ] 1766 | 1767 | [[package]] 1768 | name = "time" 1769 | version = "0.3.11" 1770 | source = "registry+https://github.com/rust-lang/crates.io-index" 1771 | checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" 1772 | dependencies = [ 1773 | "itoa", 1774 | "libc", 1775 | "num_threads", 1776 | "time-macros", 1777 | ] 1778 | 1779 | [[package]] 1780 | name = "time-macros" 1781 | version = "0.2.4" 1782 | source = "registry+https://github.com/rust-lang/crates.io-index" 1783 | checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" 1784 | 1785 | [[package]] 1786 | name = "tinyvec" 1787 | version = "1.6.0" 1788 | source = "registry+https://github.com/rust-lang/crates.io-index" 1789 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 1790 | dependencies = [ 1791 | "tinyvec_macros", 1792 | ] 1793 | 1794 | [[package]] 1795 | name = "tinyvec_macros" 1796 | version = "0.1.0" 1797 | source = "registry+https://github.com/rust-lang/crates.io-index" 1798 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1799 | 1800 | [[package]] 1801 | name = "tokio" 1802 | version = "1.20.0" 1803 | source = "registry+https://github.com/rust-lang/crates.io-index" 1804 | checksum = "57aec3cfa4c296db7255446efb4928a6be304b431a806216105542a67b6ca82e" 1805 | dependencies = [ 1806 | "autocfg", 1807 | "bytes", 1808 | "libc", 1809 | "memchr", 1810 | "mio", 1811 | "num_cpus", 1812 | "once_cell", 1813 | "pin-project-lite", 1814 | "socket2", 1815 | "winapi", 1816 | ] 1817 | 1818 | [[package]] 1819 | name = "tokio-rustls" 1820 | version = "0.23.4" 1821 | source = "registry+https://github.com/rust-lang/crates.io-index" 1822 | checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" 1823 | dependencies = [ 1824 | "rustls", 1825 | "tokio", 1826 | "webpki", 1827 | ] 1828 | 1829 | [[package]] 1830 | name = "tokio-util" 1831 | version = "0.7.3" 1832 | source = "registry+https://github.com/rust-lang/crates.io-index" 1833 | checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" 1834 | dependencies = [ 1835 | "bytes", 1836 | "futures-core", 1837 | "futures-sink", 1838 | "pin-project-lite", 1839 | "tokio", 1840 | "tracing", 1841 | ] 1842 | 1843 | [[package]] 1844 | name = "tower-service" 1845 | version = "0.3.2" 1846 | source = "registry+https://github.com/rust-lang/crates.io-index" 1847 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 1848 | 1849 | [[package]] 1850 | name = "tracing" 1851 | version = "0.1.35" 1852 | source = "registry+https://github.com/rust-lang/crates.io-index" 1853 | checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" 1854 | dependencies = [ 1855 | "cfg-if", 1856 | "pin-project-lite", 1857 | "tracing-core", 1858 | ] 1859 | 1860 | [[package]] 1861 | name = "tracing-core" 1862 | version = "0.1.28" 1863 | source = "registry+https://github.com/rust-lang/crates.io-index" 1864 | checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" 1865 | dependencies = [ 1866 | "once_cell", 1867 | ] 1868 | 1869 | [[package]] 1870 | name = "try-lock" 1871 | version = "0.2.3" 1872 | source = "registry+https://github.com/rust-lang/crates.io-index" 1873 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1874 | 1875 | [[package]] 1876 | name = "twofish" 1877 | version = "0.6.0" 1878 | source = "registry+https://github.com/rust-lang/crates.io-index" 1879 | checksum = "728f6b7e784825d272fe9d2a77e44063f4197a570cbedc6fdcc90a6ddac91296" 1880 | dependencies = [ 1881 | "byteorder", 1882 | "cipher", 1883 | "opaque-debug", 1884 | ] 1885 | 1886 | [[package]] 1887 | name = "typenum" 1888 | version = "1.15.0" 1889 | source = "registry+https://github.com/rust-lang/crates.io-index" 1890 | checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" 1891 | 1892 | [[package]] 1893 | name = "unicode-bidi" 1894 | version = "0.3.8" 1895 | source = "registry+https://github.com/rust-lang/crates.io-index" 1896 | checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" 1897 | 1898 | [[package]] 1899 | name = "unicode-ident" 1900 | version = "1.0.1" 1901 | source = "registry+https://github.com/rust-lang/crates.io-index" 1902 | checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" 1903 | 1904 | [[package]] 1905 | name = "unicode-normalization" 1906 | version = "0.1.21" 1907 | source = "registry+https://github.com/rust-lang/crates.io-index" 1908 | checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" 1909 | dependencies = [ 1910 | "tinyvec", 1911 | ] 1912 | 1913 | [[package]] 1914 | name = "unicode-xid" 1915 | version = "0.2.3" 1916 | source = "registry+https://github.com/rust-lang/crates.io-index" 1917 | checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" 1918 | 1919 | [[package]] 1920 | name = "untrusted" 1921 | version = "0.7.1" 1922 | source = "registry+https://github.com/rust-lang/crates.io-index" 1923 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 1924 | 1925 | [[package]] 1926 | name = "url" 1927 | version = "2.2.2" 1928 | source = "registry+https://github.com/rust-lang/crates.io-index" 1929 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 1930 | dependencies = [ 1931 | "form_urlencoded", 1932 | "idna", 1933 | "matches", 1934 | "percent-encoding", 1935 | ] 1936 | 1937 | [[package]] 1938 | name = "vcpkg" 1939 | version = "0.2.15" 1940 | source = "registry+https://github.com/rust-lang/crates.io-index" 1941 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1942 | 1943 | [[package]] 1944 | name = "version_check" 1945 | version = "0.1.5" 1946 | source = "registry+https://github.com/rust-lang/crates.io-index" 1947 | checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" 1948 | 1949 | [[package]] 1950 | name = "version_check" 1951 | version = "0.9.4" 1952 | source = "registry+https://github.com/rust-lang/crates.io-index" 1953 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1954 | 1955 | [[package]] 1956 | name = "wait-timeout" 1957 | version = "0.2.0" 1958 | source = "registry+https://github.com/rust-lang/crates.io-index" 1959 | checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" 1960 | dependencies = [ 1961 | "libc", 1962 | ] 1963 | 1964 | [[package]] 1965 | name = "want" 1966 | version = "0.3.0" 1967 | source = "registry+https://github.com/rust-lang/crates.io-index" 1968 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1969 | dependencies = [ 1970 | "log", 1971 | "try-lock", 1972 | ] 1973 | 1974 | [[package]] 1975 | name = "wasi" 1976 | version = "0.9.0+wasi-snapshot-preview1" 1977 | source = "registry+https://github.com/rust-lang/crates.io-index" 1978 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1979 | 1980 | [[package]] 1981 | name = "wasi" 1982 | version = "0.11.0+wasi-snapshot-preview1" 1983 | source = "registry+https://github.com/rust-lang/crates.io-index" 1984 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1985 | 1986 | [[package]] 1987 | name = "wasm-bindgen" 1988 | version = "0.2.81" 1989 | source = "registry+https://github.com/rust-lang/crates.io-index" 1990 | checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" 1991 | dependencies = [ 1992 | "cfg-if", 1993 | "wasm-bindgen-macro", 1994 | ] 1995 | 1996 | [[package]] 1997 | name = "wasm-bindgen-backend" 1998 | version = "0.2.81" 1999 | source = "registry+https://github.com/rust-lang/crates.io-index" 2000 | checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" 2001 | dependencies = [ 2002 | "bumpalo", 2003 | "lazy_static", 2004 | "log", 2005 | "proc-macro2", 2006 | "quote", 2007 | "syn", 2008 | "wasm-bindgen-shared", 2009 | ] 2010 | 2011 | [[package]] 2012 | name = "wasm-bindgen-futures" 2013 | version = "0.4.31" 2014 | source = "registry+https://github.com/rust-lang/crates.io-index" 2015 | checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" 2016 | dependencies = [ 2017 | "cfg-if", 2018 | "js-sys", 2019 | "wasm-bindgen", 2020 | "web-sys", 2021 | ] 2022 | 2023 | [[package]] 2024 | name = "wasm-bindgen-macro" 2025 | version = "0.2.81" 2026 | source = "registry+https://github.com/rust-lang/crates.io-index" 2027 | checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" 2028 | dependencies = [ 2029 | "quote", 2030 | "wasm-bindgen-macro-support", 2031 | ] 2032 | 2033 | [[package]] 2034 | name = "wasm-bindgen-macro-support" 2035 | version = "0.2.81" 2036 | source = "registry+https://github.com/rust-lang/crates.io-index" 2037 | checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" 2038 | dependencies = [ 2039 | "proc-macro2", 2040 | "quote", 2041 | "syn", 2042 | "wasm-bindgen-backend", 2043 | "wasm-bindgen-shared", 2044 | ] 2045 | 2046 | [[package]] 2047 | name = "wasm-bindgen-shared" 2048 | version = "0.2.81" 2049 | source = "registry+https://github.com/rust-lang/crates.io-index" 2050 | checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" 2051 | 2052 | [[package]] 2053 | name = "web-sys" 2054 | version = "0.3.58" 2055 | source = "registry+https://github.com/rust-lang/crates.io-index" 2056 | checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" 2057 | dependencies = [ 2058 | "js-sys", 2059 | "wasm-bindgen", 2060 | ] 2061 | 2062 | [[package]] 2063 | name = "webpki" 2064 | version = "0.22.0" 2065 | source = "registry+https://github.com/rust-lang/crates.io-index" 2066 | checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" 2067 | dependencies = [ 2068 | "ring", 2069 | "untrusted", 2070 | ] 2071 | 2072 | [[package]] 2073 | name = "webpki-roots" 2074 | version = "0.22.4" 2075 | source = "registry+https://github.com/rust-lang/crates.io-index" 2076 | checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf" 2077 | dependencies = [ 2078 | "webpki", 2079 | ] 2080 | 2081 | [[package]] 2082 | name = "winapi" 2083 | version = "0.3.9" 2084 | source = "registry+https://github.com/rust-lang/crates.io-index" 2085 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2086 | dependencies = [ 2087 | "winapi-i686-pc-windows-gnu", 2088 | "winapi-x86_64-pc-windows-gnu", 2089 | ] 2090 | 2091 | [[package]] 2092 | name = "winapi-i686-pc-windows-gnu" 2093 | version = "0.4.0" 2094 | source = "registry+https://github.com/rust-lang/crates.io-index" 2095 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2096 | 2097 | [[package]] 2098 | name = "winapi-util" 2099 | version = "0.1.5" 2100 | source = "registry+https://github.com/rust-lang/crates.io-index" 2101 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 2102 | dependencies = [ 2103 | "winapi", 2104 | ] 2105 | 2106 | [[package]] 2107 | name = "winapi-x86_64-pc-windows-gnu" 2108 | version = "0.4.0" 2109 | source = "registry+https://github.com/rust-lang/crates.io-index" 2110 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2111 | 2112 | [[package]] 2113 | name = "windows-sys" 2114 | version = "0.36.1" 2115 | source = "registry+https://github.com/rust-lang/crates.io-index" 2116 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 2117 | dependencies = [ 2118 | "windows_aarch64_msvc", 2119 | "windows_i686_gnu", 2120 | "windows_i686_msvc", 2121 | "windows_x86_64_gnu", 2122 | "windows_x86_64_msvc", 2123 | ] 2124 | 2125 | [[package]] 2126 | name = "windows_aarch64_msvc" 2127 | version = "0.36.1" 2128 | source = "registry+https://github.com/rust-lang/crates.io-index" 2129 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 2130 | 2131 | [[package]] 2132 | name = "windows_i686_gnu" 2133 | version = "0.36.1" 2134 | source = "registry+https://github.com/rust-lang/crates.io-index" 2135 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 2136 | 2137 | [[package]] 2138 | name = "windows_i686_msvc" 2139 | version = "0.36.1" 2140 | source = "registry+https://github.com/rust-lang/crates.io-index" 2141 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 2142 | 2143 | [[package]] 2144 | name = "windows_x86_64_gnu" 2145 | version = "0.36.1" 2146 | source = "registry+https://github.com/rust-lang/crates.io-index" 2147 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 2148 | 2149 | [[package]] 2150 | name = "windows_x86_64_msvc" 2151 | version = "0.36.1" 2152 | source = "registry+https://github.com/rust-lang/crates.io-index" 2153 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 2154 | 2155 | [[package]] 2156 | name = "winreg" 2157 | version = "0.10.1" 2158 | source = "registry+https://github.com/rust-lang/crates.io-index" 2159 | checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" 2160 | dependencies = [ 2161 | "winapi", 2162 | ] 2163 | 2164 | [[package]] 2165 | name = "x25519-dalek" 2166 | version = "1.1.1" 2167 | source = "registry+https://github.com/rust-lang/crates.io-index" 2168 | checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" 2169 | dependencies = [ 2170 | "curve25519-dalek", 2171 | "rand_core 0.5.1", 2172 | "zeroize", 2173 | ] 2174 | 2175 | [[package]] 2176 | name = "zeroize" 2177 | version = "1.5.6" 2178 | source = "registry+https://github.com/rust-lang/crates.io-index" 2179 | checksum = "20b578acffd8516a6c3f2a1bdefc1ec37e547bb4e0fb8b6b01a4cafc886b4442" 2180 | dependencies = [ 2181 | "zeroize_derive", 2182 | ] 2183 | 2184 | [[package]] 2185 | name = "zeroize_derive" 2186 | version = "1.3.2" 2187 | source = "registry+https://github.com/rust-lang/crates.io-index" 2188 | checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" 2189 | dependencies = [ 2190 | "proc-macro2", 2191 | "quote", 2192 | "syn", 2193 | "synstructure", 2194 | ] 2195 | 2196 | [[package]] 2197 | name = "zip" 2198 | version = "0.6.2" 2199 | source = "registry+https://github.com/rust-lang/crates.io-index" 2200 | checksum = "bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d" 2201 | dependencies = [ 2202 | "aes", 2203 | "byteorder", 2204 | "bzip2", 2205 | "constant_time_eq", 2206 | "crc32fast", 2207 | "crossbeam-utils", 2208 | "flate2", 2209 | "hmac", 2210 | "pbkdf2", 2211 | "sha1", 2212 | "time", 2213 | "zstd", 2214 | ] 2215 | 2216 | [[package]] 2217 | name = "zstd" 2218 | version = "0.10.2+zstd.1.5.2" 2219 | source = "registry+https://github.com/rust-lang/crates.io-index" 2220 | checksum = "5f4a6bd64f22b5e3e94b4e238669ff9f10815c27a5180108b849d24174a83847" 2221 | dependencies = [ 2222 | "zstd-safe", 2223 | ] 2224 | 2225 | [[package]] 2226 | name = "zstd-safe" 2227 | version = "4.1.6+zstd.1.5.2" 2228 | source = "registry+https://github.com/rust-lang/crates.io-index" 2229 | checksum = "94b61c51bb270702d6167b8ce67340d2754b088d0c091b06e593aa772c3ee9bb" 2230 | dependencies = [ 2231 | "libc", 2232 | "zstd-sys", 2233 | ] 2234 | 2235 | [[package]] 2236 | name = "zstd-sys" 2237 | version = "1.6.3+zstd.1.5.2" 2238 | source = "registry+https://github.com/rust-lang/crates.io-index" 2239 | checksum = "fc49afa5c8d634e75761feda8c592051e7eeb4683ba827211eb0d731d3402ea8" 2240 | dependencies = [ 2241 | "cc", 2242 | "libc", 2243 | ] 2244 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "terve" 3 | version = "0.8.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | regex = "~1.6" 8 | pico-args = "~0.5" 9 | zip = "~0.6" 10 | tempfile = "~3.3" 11 | dirs = "~4.0" 12 | semver = "~1.0" 13 | sha2 = "~0.10" 14 | hex = "~0.4" 15 | pgp = "~0.8" 16 | bytes = "~1.1" 17 | 18 | [dependencies.reqwest] 19 | version = "~0.11" 20 | default-features = false 21 | features = ["blocking", "rustls-tls"] 22 | 23 | [dependencies.git2] 24 | version = "~0.14" 25 | default-features = false 26 | features = ["https", "vendored-libgit2", "vendored-openssl"] 27 | 28 | [dev-dependencies] 29 | assert_cmd = "~2.0" 30 | predicates = "~2.1" 31 | same-file = "~1.0" 32 | 33 | [profile.release] 34 | codegen-units = 1 35 | lto = true 36 | panic = "abort" 37 | strip = true 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jukka Palomäki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terve 👋 2 | 3 | ![Release](https://img.shields.io/github/v/release/superblk/terve) 4 | ![License](https://img.shields.io/github/license/superblk/terve) 5 | ![OS](https://img.shields.io/badge/os-Linux%20%7C%20macOS%20%7C%20Windows-ff69b4) 6 | 7 | ⚠️ This tool is deprecated and is no longer maintained. Use e.g. [asdf](https://github.com/asdf-vm/asdf) or [tfenv](https://github.com/tfutils/tfenv) + [tgenv](https://github.com/tgenv/tgenv) instead. 8 | 9 | Unified, minimal [terraform](https://www.terraform.io/downloads.html) and [terragrunt](https://github.com/gruntwork-io/terragrunt/releases) version manager. 10 | 11 | ## Features 12 | 13 | - Minimal by design: no shims, no magic, quiet, but extendable thru scripting 14 | - SHA256 checksums are checked for terraform and terragrunt binary downloads 15 | - PGP signatures are checked for terraform binary downloads (if Hashicorp's public key is configured) 16 | 17 | ## Supported platforms 18 | 19 | - Linux (amd64, arm64) 20 | - NOTE: only terraform `0.11.15`, `0.12.{30,31}`, `0.13.5+` and terragrunt `0.28.12+` ship linux arm64 binaries 21 | - macOS (amd64, arm64) 22 | - NOTE: only terraform `1.1.0+` and terragrunt `0.28.12+` ship macOS arm64 binaries 23 | - Windows (amd64) 24 | 25 | ⚠️ A pre-built terve binary is not yet available for macOS arm64, because Apple M1-based GitHub runners are not yet available, see this [issue](https://github.com/actions/virtual-environments/issues/2187) 26 | 27 | ## Setup 28 | 29 | 1. [Download](https://github.com/superblk/terve/releases/latest) terve for your platform, check file integrity, and install the binary somewhere in your `PATH`, e.g. `/usr/local/bin/terve` 30 | - On Linux/macOS, file integrity can be checked in the directory where you downloaded the binary and `SHA256SUMS`, like so: 31 | 32 | $ ls 33 | SHA256SUMS terve_linux_amd64 34 | $ sha256sum -c --ignore-missing 2>/dev/null SHA256SUMS 35 | terve_linux_amd64: OK 36 | 37 | - On Linux/macOS, be sure to make the binary executable: `chmod +x terve` 38 | 1. Create the `~/.terve` directory tree by running `terve --bootstrap` 39 | 1. Add the `~/.terve/bin` directory to `PATH` (using e.g. `.bashrc` or Windows' control panel) 40 | 1. Copy Hashicorp's [PGP public key](https://www.hashicorp.com/security) in `~/.terve/etc/terraform.asc` (read-only, mode `0444` on Linux/macOS) 41 | - This public key is used to verify terraform binary download PGP signatures 42 | - If not installed (or bad file permissions), terve will log a warning for terraform installs 43 | 1. [Install your desired versions of terraform and terragrunt](#install) 44 | 1. [Select your desired versions of terraform and terragrunt](#select) 45 | 46 | ## How it works 47 | 48 | Terve uses **hard** links to configure selected terraform/terragrunt binary versions. 49 | 50 | All files are kept in directory `~/.terve` like so (example directory tree for Linux): 51 | 52 | ```txt 53 | /home/whoami/.terve 54 | ├── bin 55 | │   ├── terraform 56 | │   └── terragrunt 57 | ├── etc 58 | │   └── terraform.asc 59 | ├── opt 60 | │   ├── terraform 61 | │   │   ├── 0.1.0 62 | │   │   ├── 1.0.2 63 | │   │   └── 1.0.3 64 | │   └── terragrunt 65 | │   ├── 0.0.4 66 | │   ├── 0.1.0 67 | │   └── 0.31.2 68 | └── var 69 | ├── terraform 70 | │   └── version 71 | └── terragrunt 72 | └── version 73 | ``` 74 | 75 | - `bin` – _selected_ terraform and terragrunt binaries (hard-linked to files in `opt`) 76 | - `etc` – configuration 77 | - `opt` – _installed_ terraform and terragrunt binaries, version is encoded in file name 78 | - `var` – variable data, currently only holders for selected version strings 79 | 80 | ## Usage 81 | 82 | Managed `` is `tf` (long form: `terraform`) or `tg` (long form: `terragrunt`). 83 | 84 | Install, select and remove are idempotent, and can be run multiple times for a version without error. 85 | 86 | ### List 87 | 88 | Lists installed or available (remote) versions, sorted latest first. 89 | 90 | Syntax: `terve l[ist] [spec]` where `spec` is `r[emote]` 91 | 92 | - `terve l tf` lists installed (local) terraform versions 93 | - `terve l tf r` lists available (remote) terraform versions 94 | - `terve l tf r | tac` lists available terraform versions, _sorted oldest first_ 95 | - `terve l tg r | grep 0.29.` lists available terragrunt 0.29.x versions 96 | 97 | 💡 List remote does not return pre-release versions (e.g. terraform `0.15.0-rc2`), but such versions can be installed/selected/removed (for testing). 98 | 99 | ### Install 100 | 101 | Installs a specific version. 102 | 103 | Syntax: `terve i[nstall] ` 104 | 105 | - `terve i tf 0.12.31` installs terraform version 0.12.31 106 | - `terve i tf "$(terve l tf r | head -n1)"` installs latest version of terraform 107 | - `terve i tf "$(cat .terraform-version)"` installs terraform version defined in `.terraform-version` 108 | - `terve i tg "$(cat .terragrunt-version)"` installs terragrunt version defined in `.terragrunt-version` 109 | - `terve l tg r | grep 0.29. | xargs -n1 -P4 terve i tg` installs all available terragrunt 0.29.x versions 110 | 111 | ⚠️ terragrunt releases < `0.18.1` do not ship `SHA256SUMS` files, so their file integrity cannot be checked 112 | 113 | ### Select 114 | 115 | Selects an installed version for use. 116 | 117 | Syntax: `terve s[elect] ` 118 | 119 | - `terve s tf 0.12.31` selects terraform version 0.12.31 120 | - `terve s tf "$(cat .terraform-version)"` selects terraform version defined in `.terraform-version` 121 | 122 | ### Remove 123 | 124 | Removes an installed version. 125 | 126 | Syntax: `terve r[emove] ` 127 | 128 | - `terve r tf 0.12.31` removes terraform version 0.12.31 129 | - `terve l tf | grep 0.11. | xargs -n1 terve r tf` removes all installed terraform 0.11.x versions 130 | 131 | 💡 Removing a version does not reset current selection 132 | 133 | ### Which 134 | 135 | Tells which version is currently selected. 136 | 137 | Syntax: `terve w[hich] ` 138 | 139 | ```shell 140 | $ terve w tf 141 | 0.15.5 142 | ``` 143 | 144 | ## Optional shell extensions 145 | 146 | ### Terraform switch (Linux and macOS) 147 | 148 | Simple switch to specified terraform version. 149 | 150 | ```sh 151 | function tfv { 152 | local version="$1" 153 | if [ -z "$version" ]; then 154 | echo "Usage: tfv , e.g. tfv 1.2.3" 155 | return 1 156 | fi 157 | terve i tf "$version" >/dev/null && \ 158 | terve s tf "$version" >/dev/null && \ 159 | echo "Switched to terraform $version" 160 | } 161 | ``` 162 | 163 | ### Terra(form|grunt) use (Linux and macOS) 164 | 165 | Use terraform and terragrunt versions defined in `.terraform-version` and `.terragrunt-version` (in current working directory or any parent directory) 166 | 167 | ```sh 168 | #!/bin/sh 169 | 170 | upcat() { 171 | file="$1" 172 | while [ "$PWD" != "/" ]; do 173 | if [ -r "$file" ]; then 174 | cat "$file" 175 | break 176 | fi 177 | cd .. 178 | done 179 | } 180 | 181 | tf_version="$(upcat .terraform-version)" 182 | tg_version="$(upcat .terragrunt-version)" 183 | 184 | if [ -z "$tf_version" ]; then 185 | echo "ERROR: No .terraform-version file found" 186 | exit 1 187 | fi 188 | 189 | if [ -z "$tg_version" ]; then 190 | echo "ERROR: No .terragrunt-version file found" 191 | exit 2 192 | fi 193 | 194 | terve i tf "$tf_version" && terve s tf "$tf_version" 195 | terve i tg "$tg_version" && terve s tg "$tg_version" 196 | ``` 197 | 198 | ## Development 199 | 200 | You need [cargo](https://rustup.rs/) (Rust's build tool). To run all tests, run `cargo test`. 201 | 202 | Visual Studio Code with [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer) provides a good IDE experience. 203 | 204 | To build a release binary, run `cargo build --release`. Binary is then found in `target/release/`. 205 | -------------------------------------------------------------------------------- /src/http.rs: -------------------------------------------------------------------------------- 1 | use std::{fs::File, time::Duration}; 2 | 3 | use bytes::Bytes; 4 | use reqwest::blocking::Client; 5 | 6 | pub struct HttpClient { 7 | client: Client, 8 | } 9 | 10 | impl HttpClient { 11 | pub fn new() -> Result { 12 | let client = Client::builder() 13 | .user_agent(HTTP_USER_AGENT) 14 | .connect_timeout(CONNECT_TIMEOUT) 15 | .https_only(true) 16 | .build()?; 17 | Ok(HttpClient { client }) 18 | } 19 | 20 | pub fn download_file(&self, url: &str, mut dest_file: &File) -> Result { 21 | let num_bytes = self 22 | .client 23 | .get(url) 24 | .header("Accept", "application/octet-stream") 25 | .send()? 26 | .error_for_status()? 27 | .copy_to(&mut dest_file)?; 28 | Ok(num_bytes) 29 | } 30 | 31 | pub fn get_bytes(&self, url: &str) -> Result { 32 | let bytes = self 33 | .client 34 | .get(url) 35 | .header("Accept", "application/octet-stream") 36 | .send()? 37 | .error_for_status()? 38 | .bytes()?; 39 | Ok(bytes) 40 | } 41 | 42 | pub fn get_text(&self, url: &str) -> Result { 43 | let text = self 44 | .client 45 | .get(url) 46 | .header("Accept", "text/plain") 47 | .send()? 48 | .error_for_status()? 49 | .text()?; 50 | Ok(text) 51 | } 52 | } 53 | 54 | const CONNECT_TIMEOUT: Duration = Duration::from_secs(10); 55 | 56 | const HTTP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); 57 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use dirs::home_dir; 2 | use pico_args::Arguments; 3 | use semver::Version; 4 | use shared::{Action, Binary, DotDir}; 5 | use std::{ 6 | env::consts::{ARCH, OS}, 7 | str::FromStr, 8 | }; 9 | use std::{error::Error, process}; 10 | use terraform::TF_GIT_REPOSITORY_URL; 11 | use terragrunt::TG_GIT_REPOSITORY_URL; 12 | use utils::{eprintln, println}; 13 | 14 | mod http; 15 | mod shared; 16 | mod terraform; 17 | mod terragrunt; 18 | mod utils; 19 | 20 | fn main() { 21 | process::exit(match run() { 22 | Ok(s) => { 23 | if !s.is_empty() { 24 | println(&s); 25 | } 26 | 0 27 | } 28 | Err(e) => { 29 | eprintln(e); 30 | 1 31 | } 32 | }); 33 | } 34 | 35 | fn run() -> Result> { 36 | let mut args = Arguments::from_env(); 37 | 38 | if args.contains(["-h", "--help"]) { 39 | return Ok(USAGE_HELP_MSG.to_string()); 40 | } 41 | 42 | if args.contains(["-v", "--version"]) { 43 | return Ok(TERVE_VERSION.to_string()); 44 | } 45 | 46 | if let Some(home) = home_dir() { 47 | let dot_dir = DotDir::bootstrap(&home)?; 48 | 49 | if args.contains(["-b", "--bootstrap"]) { 50 | return Ok(format!("Created {}", dot_dir.root.display())); 51 | } 52 | 53 | let (action, binary, version, os, arch) = get_params(args)?; 54 | 55 | match (action, binary, version) { 56 | (Action::List, binary, None) => shared::list_installed_versions(binary, dot_dir), 57 | (Action::List, Binary::Terraform, Some(v)) if v.is_remote() => { 58 | shared::list_available_versions(TF_GIT_REPOSITORY_URL) 59 | } 60 | (Action::List, Binary::Terragrunt, Some(v)) if v.is_remote() => { 61 | shared::list_available_versions(TG_GIT_REPOSITORY_URL) 62 | } 63 | (Action::Install, Binary::Terraform, Some(v)) if v.is_semver() => { 64 | terraform::install_binary_version(v, dot_dir, os, arch) 65 | } 66 | (Action::Install, Binary::Terragrunt, Some(v)) if v.is_semver() => { 67 | terragrunt::install_binary_version(v, dot_dir, os, arch) 68 | } 69 | (Action::Select, binary, Some(v)) if v.is_semver() => { 70 | shared::select_binary_version(binary, v, dot_dir) 71 | } 72 | (Action::Remove, binary, Some(v)) if v.is_semver() => { 73 | shared::remove_binary_version(binary, v, dot_dir) 74 | } 75 | (Action::Which, binary, None) => shared::get_selected_version(binary, dot_dir), 76 | _ => Err(INVALID_ARGS_MSG.into()), 77 | } 78 | } else { 79 | Err("Unable to resolve user home directory".into()) 80 | } 81 | } 82 | 83 | type Params = (Action, Binary, Option, String, String); 84 | 85 | fn get_params(mut args: Arguments) -> Result> { 86 | let action: Action = match args.subcommand()? { 87 | Some(s) => Action::from_str(&s)?, 88 | None => return Err(INVALID_ARGS_MSG.into()), 89 | }; 90 | 91 | let binary: Binary = match args.subcommand()? { 92 | Some(s) => Binary::from_str(&s)?, 93 | None => return Err(INVALID_ARGS_MSG.into()), 94 | }; 95 | 96 | let version: Option = args.subcommand()?; 97 | 98 | let os = match OS { 99 | "linux" => "linux", 100 | "macos" => "darwin", 101 | "windows" => "windows", 102 | other => panic!("Unsupported OS: {}", other), 103 | }; 104 | 105 | let arch = match ARCH { 106 | "x86_64" => "amd64", 107 | "aarch64" => "arm64", 108 | other => panic!("Unsupported architecture: {}", other), 109 | }; 110 | 111 | Ok((action, binary, version, os.to_string(), arch.to_string())) 112 | } 113 | 114 | trait VersionQualifier { 115 | fn is_remote(&self) -> bool; 116 | fn is_semver(&self) -> bool; 117 | } 118 | 119 | impl VersionQualifier for String { 120 | fn is_remote(&self) -> bool { 121 | self == "r" || self == "remote" 122 | } 123 | 124 | fn is_semver(&self) -> bool { 125 | Version::parse(self).is_ok() 126 | } 127 | } 128 | 129 | const TERVE_VERSION: &str = env!("CARGO_PKG_VERSION"); 130 | 131 | const USAGE_HELP_MSG: &str = "\ 132 | Unified terraform and terragrunt version manager 133 | 134 | See https://github.com/superblk/terve for documentation 135 | 136 | USAGE: 137 | terve [] 138 | 139 | ACTION: 140 | l, list Lists versions 141 | i, install Installs given version 142 | s, select Selects installed version 143 | r, remove Removes installed version 144 | w, which Prints selected version 145 | 146 | BINARY: 147 | tf, terraform Terraform (https://www.terraform.io/) 148 | tg, terragrunt Terragrunt (https://terragrunt.gruntwork.io/) 149 | 150 | VERSION: 151 | r, remote Available (remote) versions 152 | x.y.z Semantic version string, e.g. 0.15.4 153 | 154 | FLAGS: 155 | -h, --help Prints this help message 156 | -v, --version Prints application version 157 | -b, --bootstrap Creates ~/.terve directory tree 158 | 159 | EXAMPLES: 160 | terve l tf Lists installed terraform versions 161 | terve l tf r Lists available terraform versions 162 | terve i tf 0.15.4 Installs terraform 0.15.4 163 | terve s tf 0.15.4 Selects terraform 0.15.4 164 | terve r tf 0.15.4 Removes terraform 0.15.4 165 | terve w tf Prints selected terraform version 166 | "; 167 | 168 | const INVALID_ARGS_MSG: &str = "Invalid arguments. Run 'terve --help' for usage"; 169 | -------------------------------------------------------------------------------- /src/shared.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | fmt::Display, 4 | fs::{create_dir_all, hard_link, read_dir, read_to_string, remove_file, write}, 5 | path::{Path, PathBuf}, 6 | str::FromStr, 7 | }; 8 | 9 | use semver::{Prerelease, Version}; 10 | 11 | use crate::utils::{git_list_remote_tags, to_sorted_multiline_string}; 12 | 13 | pub enum Action { 14 | List, 15 | Install, 16 | Select, 17 | Remove, 18 | Which, 19 | } 20 | 21 | pub enum Binary { 22 | Terraform, 23 | Terragrunt, 24 | } 25 | 26 | impl FromStr for Action { 27 | type Err = String; 28 | 29 | fn from_str(a: &str) -> Result { 30 | match a { 31 | "l" | "list" => Ok(Action::List), 32 | "i" | "install" => Ok(Action::Install), 33 | "s" | "select" => Ok(Action::Select), 34 | "r" | "remove" => Ok(Action::Remove), 35 | "w" | "which" => Ok(Action::Which), 36 | _ => Err( 37 | "Action must be one of: l[ist], i[nstall], s[elect], r[emove] or w[hich]" 38 | .to_string(), 39 | ), 40 | } 41 | } 42 | } 43 | 44 | impl FromStr for Binary { 45 | type Err = String; 46 | 47 | fn from_str(a: &str) -> Result { 48 | match a { 49 | "tf" | "terraform" => Ok(Binary::Terraform), 50 | "tg" | "terragrunt" => Ok(Binary::Terragrunt), 51 | _ => Err("Binary must be one of: tf, tg, terraform or terragrunt".to_string()), 52 | } 53 | } 54 | } 55 | 56 | impl Display for Binary { 57 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 58 | match *self { 59 | Binary::Terraform => write!(f, "terraform"), 60 | Binary::Terragrunt => write!(f, "terragrunt"), 61 | } 62 | } 63 | } 64 | 65 | impl AsRef for Binary { 66 | fn as_ref(&self) -> &Path { 67 | let path = match *self { 68 | Binary::Terraform => { 69 | if cfg!(unix) { 70 | "terraform" 71 | } else { 72 | "terraform.exe" 73 | } 74 | } 75 | Binary::Terragrunt => { 76 | if cfg!(unix) { 77 | "terragrunt" 78 | } else { 79 | "terragrunt.exe" 80 | } 81 | } 82 | }; 83 | Path::new(path) 84 | } 85 | } 86 | 87 | pub struct DotDir { 88 | pub root: PathBuf, 89 | pub bin: PathBuf, 90 | pub etc: PathBuf, 91 | pub opt: PathBuf, 92 | pub var: PathBuf, 93 | } 94 | 95 | impl DotDir { 96 | pub fn bootstrap(home_dir: &Path) -> Result> { 97 | let root = home_dir.join(".terve"); 98 | let bin = root.join("bin"); 99 | let etc = root.join("etc"); 100 | let opt = root.join("opt"); 101 | let var = root.join("var"); 102 | create_dir_all(&bin)?; 103 | create_dir_all(&etc)?; 104 | create_dir_all(opt.join(Binary::Terraform))?; 105 | create_dir_all(opt.join(Binary::Terragrunt))?; 106 | create_dir_all(var.join(Binary::Terraform))?; 107 | create_dir_all(var.join(Binary::Terragrunt))?; 108 | Ok(DotDir { 109 | root, 110 | bin, 111 | etc, 112 | opt, 113 | var, 114 | }) 115 | } 116 | } 117 | 118 | pub fn list_available_versions(git_repo_url: &str) -> Result> { 119 | let mut versions: Vec = git_list_remote_tags(git_repo_url)? 120 | .iter() 121 | .map(|t| t.trim_start_matches('v')) 122 | .filter_map(|s| Version::parse(s).ok()) 123 | .filter(|v| v.pre == Prerelease::EMPTY) 124 | .collect(); 125 | let result = to_sorted_multiline_string(&mut versions); 126 | Ok(result) 127 | } 128 | 129 | pub fn list_installed_versions(binary: Binary, dot_dir: DotDir) -> Result> { 130 | let opt_dir = dot_dir.opt.join(binary); 131 | let mut installed_versions: Vec = read_dir(&opt_dir)? 132 | .filter_map(|r| Some(r.ok()?.path())) 133 | .filter_map(|p| Some(p.strip_prefix(&opt_dir).ok()?.to_owned())) 134 | .filter_map(|p| Version::parse(p.to_string_lossy().as_ref()).ok()) 135 | .collect(); 136 | let result = to_sorted_multiline_string(&mut installed_versions); 137 | Ok(result) 138 | } 139 | 140 | pub fn select_binary_version( 141 | binary: Binary, 142 | version: String, 143 | dot_dir: DotDir, 144 | ) -> Result> { 145 | let opt_file_path = dot_dir.opt.join(&binary).join(&version); 146 | if !opt_file_path.exists() { 147 | return Err(format!("{0} version {1} is not installed", binary, version).into()); 148 | } 149 | let bin_file_path = dot_dir.bin.join(&binary); 150 | if bin_file_path.exists() { 151 | remove_file(&bin_file_path)?; 152 | } 153 | hard_link(&opt_file_path, &bin_file_path)?; 154 | let version_file_path = dot_dir.var.join(&binary).join("version"); 155 | write(version_file_path, &version)?; 156 | Ok(format!("Selected {} {}", binary, version)) 157 | } 158 | 159 | pub fn remove_binary_version( 160 | binary: Binary, 161 | version: String, 162 | dot_dir: DotDir, 163 | ) -> Result> { 164 | let opt_file_path = dot_dir.opt.join(&binary).join(&version); 165 | if opt_file_path.exists() { 166 | remove_file(&opt_file_path)?; 167 | } 168 | Ok(format!("Removed {} {}", binary, version)) 169 | } 170 | 171 | pub fn get_selected_version(binary: Binary, dot_dir: DotDir) -> Result> { 172 | let bin_file_path = dot_dir.bin.join(&binary); 173 | let version_file_path = dot_dir.var.join(&binary).join("version"); 174 | let result = if bin_file_path.exists() && version_file_path.exists() { 175 | read_to_string(version_file_path)? 176 | } else { 177 | "".to_string() 178 | }; 179 | Ok(result) 180 | } 181 | -------------------------------------------------------------------------------- /src/terraform.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, fs::File, io::copy}; 2 | 3 | use pgp::{types::KeyTrait, Deserializable, SignedPublicKey, StandaloneSignature}; 4 | use regex::Regex; 5 | use zip::ZipArchive; 6 | 7 | use crate::{ 8 | http::HttpClient, 9 | shared::{Binary, DotDir}, 10 | utils::{check_sha256_sum, regex_capture_group, verify_detached_pgp_signature, wprintln}, 11 | }; 12 | 13 | use std::env::consts::EXE_SUFFIX; 14 | 15 | pub fn install_binary_version( 16 | version: String, 17 | dot_dir: DotDir, 18 | os: String, 19 | arch: String, 20 | ) -> Result> { 21 | let opt_file_path = dot_dir.opt.join(Binary::Terraform).join(&version); 22 | if !opt_file_path.exists() { 23 | let zip_download_url = format!( 24 | "{0}/{1}/terraform_{1}_{2}_{3}.zip", 25 | TF_RELEASES_DOWNLOAD_URL, version, os, arch 26 | ); 27 | let tmp_zip_file = tempfile::tempfile()?; 28 | let http_client = HttpClient::new()?; 29 | http_client.download_file(&zip_download_url, &tmp_zip_file)?; 30 | verify_download_integrity(&version, &dot_dir, &os, &arch, &http_client, &tmp_zip_file)?; 31 | let mut zip_archive = ZipArchive::new(tmp_zip_file)?; 32 | let file_name = format!("terraform{}", EXE_SUFFIX); 33 | let mut binary_in_zip = zip_archive.by_name(&file_name)?; 34 | let mut opt_file = File::create(&opt_file_path)?; 35 | copy(&mut binary_in_zip, &mut opt_file)?; 36 | #[cfg(unix)] 37 | { 38 | use std::fs::{set_permissions, Permissions}; 39 | use std::os::unix::fs::PermissionsExt; 40 | set_permissions(&opt_file_path, Permissions::from_mode(0o755))?; 41 | } 42 | } 43 | Ok(format!("Installed terraform {}", version)) 44 | } 45 | 46 | fn verify_download_integrity( 47 | version: &str, 48 | dot_dir: &DotDir, 49 | os: &str, 50 | arch: &str, 51 | http_client: &HttpClient, 52 | zip_file: &File, 53 | ) -> Result<(), Box> { 54 | let shasums_download_url = format!( 55 | "{0}/{1}/terraform_{1}_SHA256SUMS", 56 | TF_RELEASES_DOWNLOAD_URL, version 57 | ); 58 | let shasums = http_client.get_text(&shasums_download_url)?; 59 | let pgp_public_key_path = dot_dir.etc.join("terraform.asc"); 60 | if pgp_public_key_path.is_file() && pgp_public_key_path.metadata()?.permissions().readonly() { 61 | let pgp_public_key_file = File::open(pgp_public_key_path)?; 62 | let (public_key, _) = SignedPublicKey::from_armor_single(pgp_public_key_file)?; 63 | let pgp_key_id = &hex::encode(public_key.fingerprint()).to_uppercase()[32..]; 64 | let shasums_sig_download_url = format!( 65 | "{0}/{1}/terraform_{1}_SHA256SUMS.{2}.sig", 66 | TF_RELEASES_DOWNLOAD_URL, version, &pgp_key_id 67 | ); 68 | let signature_bytes = http_client.get_bytes(&shasums_sig_download_url)?; 69 | let signature = StandaloneSignature::from_bytes(&signature_bytes[..])?; 70 | verify_detached_pgp_signature(shasums.as_bytes(), &signature, &public_key)?; 71 | } else { 72 | wprintln( 73 | "Skipping PGP signature verification. See https://github.com/superblk/terve#setup", 74 | ); 75 | } 76 | let sha256_regex = 77 | Regex::new(format!(r"([a-f0-9]+)\s+terraform_{}_{}_{}.zip", version, os, arch).as_str())?; 78 | let expected_sha256 = regex_capture_group(&sha256_regex, 1, &shasums)?; 79 | check_sha256_sum(zip_file, &expected_sha256)?; 80 | Ok(()) 81 | } 82 | 83 | pub const TF_GIT_REPOSITORY_URL: &str = "https://github.com/hashicorp/terraform"; 84 | 85 | const TF_RELEASES_DOWNLOAD_URL: &str = "https://releases.hashicorp.com/terraform"; 86 | -------------------------------------------------------------------------------- /src/terragrunt.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | fs::File, 4 | io::{copy, Seek, SeekFrom}, 5 | }; 6 | 7 | use crate::{ 8 | http::HttpClient, 9 | shared::{Binary, DotDir}, 10 | utils::{check_sha256_sum, regex_capture_group, wprintln}, 11 | }; 12 | use regex::Regex; 13 | use reqwest::StatusCode; 14 | use std::env::consts::EXE_SUFFIX; 15 | 16 | pub fn install_binary_version( 17 | version: String, 18 | dot_dir: DotDir, 19 | os: String, 20 | arch: String, 21 | ) -> Result> { 22 | let opt_file_path = dot_dir.opt.join(Binary::Terragrunt).join(&version); 23 | if !opt_file_path.exists() { 24 | let file_name = format!("terragrunt_{}_{}{}", os, arch, EXE_SUFFIX); 25 | let file_download_url = format!("{}/v{}/{}", TG_RELEASES_DOWNLOAD_URL, version, file_name); 26 | let shasums_download_url = 27 | format!("{0}/v{1}/SHA256SUMS", TG_RELEASES_DOWNLOAD_URL, version); 28 | let mut tmp_file = tempfile::tempfile()?; 29 | let http_client = HttpClient::new()?; 30 | http_client.download_file(&file_download_url, &tmp_file)?; 31 | match http_client.get_text(&shasums_download_url) { 32 | Ok(shasums) => { 33 | let sha256_regex = Regex::new(format!(r"([a-f0-9]+)\s+{}", file_name).as_str())?; 34 | let expected_sha256 = regex_capture_group(&sha256_regex, 1, &shasums)?; 35 | check_sha256_sum(&tmp_file, &expected_sha256)?; 36 | } 37 | Err(e) if e.status() == Some(StatusCode::NOT_FOUND) => { 38 | wprintln("Skipping SHA256 file integrity check. See https://github.com/superblk/terve#install"); 39 | } 40 | Err(other) => { 41 | return Err(other.into()); 42 | } 43 | } 44 | tmp_file.seek(SeekFrom::Start(0))?; 45 | let mut opt_file = File::create(&opt_file_path)?; 46 | copy(&mut tmp_file, &mut opt_file)?; 47 | #[cfg(unix)] 48 | { 49 | use std::fs::{set_permissions, Permissions}; 50 | use std::os::unix::fs::PermissionsExt; 51 | set_permissions(&opt_file_path, Permissions::from_mode(0o755))?; 52 | } 53 | } 54 | Ok(format!("Installed terragrunt {}", version)) 55 | } 56 | 57 | pub const TG_GIT_REPOSITORY_URL: &str = "https://github.com/gruntwork-io/terragrunt"; 58 | 59 | const TG_RELEASES_DOWNLOAD_URL: &str = 60 | "https://github.com/gruntwork-io/terragrunt/releases/download"; 61 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use git2::{Direction, Remote}; 2 | use pgp::{types::KeyTrait, SignedPublicKey, StandaloneSignature}; 3 | use regex::Regex; 4 | use semver::Version; 5 | use sha2::{Digest, Sha256}; 6 | use std::{ 7 | error::Error, 8 | fs::File, 9 | io::{copy, stderr, stdout, Seek, SeekFrom, Write}, 10 | }; 11 | 12 | pub fn check_sha256_sum(mut file: &File, expected_sha256: &str) -> Result<(), Box> { 13 | file.seek(SeekFrom::Start(0))?; 14 | let mut sha256 = Sha256::new(); 15 | copy(&mut file, &mut sha256)?; 16 | let result = sha256.finalize(); 17 | let actual_sha256 = hex::encode(result); 18 | if actual_sha256 != expected_sha256 { 19 | return Err(format!( 20 | "File sha256 checksum mismatch: expected '{}', got '{}'", 21 | expected_sha256, actual_sha256 22 | ) 23 | .into()); 24 | } 25 | Ok(()) 26 | } 27 | 28 | pub fn regex_capture_group( 29 | regex: &Regex, 30 | group: usize, 31 | text: &str, 32 | ) -> Result> { 33 | let result = regex 34 | .captures(text) 35 | .ok_or("Regex capture group failed")? 36 | .get(group) 37 | .ok_or("Regex capture group not found")? 38 | .as_str() 39 | .to_string(); 40 | Ok(result) 41 | } 42 | 43 | pub fn to_sorted_multiline_string(versions: &mut Vec) -> String { 44 | versions.sort(); 45 | versions.dedup(); 46 | versions.reverse(); 47 | let result = versions 48 | .iter() 49 | .map(|v| v.to_string()) 50 | .collect::>() 51 | .join(NEWLINE); 52 | result 53 | } 54 | 55 | pub fn verify_detached_pgp_signature( 56 | content: &[u8], 57 | signature: &StandaloneSignature, 58 | public_key: &SignedPublicKey, 59 | ) -> Result<(), Box> { 60 | if public_key.is_signing_key() && signature.verify(&public_key, content).is_ok() { 61 | return Ok(()); 62 | } else { 63 | for sub_key in &public_key.public_subkeys { 64 | if sub_key.is_signing_key() && signature.verify(sub_key, content).is_ok() { 65 | return Ok(()); 66 | } 67 | } 68 | } 69 | Err("PGP signature verification failed".into()) 70 | } 71 | 72 | pub fn git_list_remote_tags(repo_url: &str) -> Result, Box> { 73 | let mut remote = Remote::create_detached(repo_url)?; 74 | remote.connect(Direction::Fetch)?; 75 | let result = remote 76 | .list()? 77 | .iter() 78 | .map(|h| h.name().to_string()) 79 | .filter(|r| r.starts_with("refs/tags/") && !r.ends_with("^{}")) 80 | .map(|s| s.trim_start_matches("refs/tags/").to_owned()) 81 | .collect(); 82 | remote.disconnect()?; 83 | Ok(result) 84 | } 85 | 86 | // We do not use vanilla println macros because: 87 | // https://github.com/rust-lang/rust/issues/46016 88 | // https://stackoverflow.com/a/37558917 89 | 90 | pub fn println(msg: &str) { 91 | let stdout = stdout(); 92 | let mut lock = stdout.lock(); 93 | let _ = write!(&mut lock, "{}{}", msg, NEWLINE); 94 | } 95 | 96 | pub fn eprintln(e: Box) { 97 | let stderr = stderr(); 98 | let mut lock = stderr.lock(); 99 | let _ = write!(&mut lock, "ERROR: {}{}", e, NEWLINE); 100 | } 101 | 102 | pub fn wprintln(msg: &str) { 103 | let stderr = stderr(); 104 | let mut lock = stderr.lock(); 105 | let _ = write!(&mut lock, "WARNING: {}{}", msg, NEWLINE); 106 | } 107 | 108 | #[cfg(unix)] 109 | const NEWLINE: &str = "\n"; 110 | 111 | #[cfg(windows)] 112 | const NEWLINE: &str = "\r\n"; 113 | 114 | #[cfg(test)] 115 | mod tests { 116 | 117 | use std::fs::read_to_string; 118 | 119 | use pgp::Deserializable; 120 | 121 | use super::*; 122 | 123 | #[test] 124 | fn test_sha256_match() { 125 | let file = File::open("tests/special.txt").expect("failed to open test file"); 126 | check_sha256_sum( 127 | &file, 128 | "b93e557fb1f4b32346b3e035985c25017356d99cce0b98140fbbd225fe57f185", 129 | ) 130 | .expect("expected sha256 to match"); 131 | } 132 | 133 | #[test] 134 | fn test_sha256_mismatch() { 135 | let file = File::open("tests/special.txt").expect("failed to open test file"); 136 | check_sha256_sum( 137 | &file, 138 | "a93e557fb1f4b32346b3e035985c25017356d99cce0b98140fbbd225fe57f185", 139 | ) 140 | .expect_err("expected sha256 to mismatch"); 141 | } 142 | 143 | #[test] 144 | fn test_regex_capture() { 145 | let str_match = "abc123 hai"; 146 | let str_no_match = "nope"; 147 | let regex = Regex::new(r"([a-z0-9]+) hai").unwrap(); 148 | assert_eq!( 149 | regex_capture_group(®ex, 1, &str_match).unwrap(), 150 | "abc123" 151 | ); 152 | assert!(regex_capture_group(®ex, 1, &str_no_match).is_err()); 153 | } 154 | 155 | #[test] 156 | fn test_version_sort() { 157 | let mut versions = vec!["0.13.4", "0.15.4", "0.1.0", "0.13.4"] 158 | .into_iter() 159 | .filter_map(|s| Version::parse(s).ok()) 160 | .collect(); 161 | assert_eq!( 162 | format!("0.15.4{0}0.13.4{0}0.1.0", NEWLINE), 163 | to_sorted_multiline_string(&mut versions) 164 | ); 165 | } 166 | 167 | #[test] 168 | fn test_pgp_verify_match() { 169 | let content = read_to_string("tests/terraform_0.13.1_SHA256SUMS").unwrap(); 170 | let public_key = 171 | SignedPublicKey::from_armor_single(File::open("tests/hashicorp-72D7468F.asc").unwrap()) 172 | .unwrap() 173 | .0; 174 | let signature = StandaloneSignature::from_bytes( 175 | File::open("tests/terraform_0.13.1_SHA256SUMS.72D7468F.sig").unwrap(), 176 | ) 177 | .unwrap(); 178 | assert!(verify_detached_pgp_signature(content.as_bytes(), &signature, &public_key).is_ok()); 179 | } 180 | 181 | #[test] 182 | fn test_pgp_verify_mismatch() { 183 | let content = read_to_string("tests/terraform_0.13.1_SHA256SUMS").unwrap(); 184 | let public_key = 185 | SignedPublicKey::from_armor_single(File::open("tests/hashicorp-72D7468F.asc").unwrap()) 186 | .unwrap() 187 | .0; 188 | let signature = StandaloneSignature::from_bytes( 189 | File::open("tests/terraform_0.13.1_SHA256SUMS.348FFC4C.sig").unwrap(), 190 | ) 191 | .unwrap(); 192 | assert!( 193 | verify_detached_pgp_signature(content.as_bytes(), &signature, &public_key).is_err() 194 | ); 195 | } 196 | 197 | #[test] 198 | fn test_git_list_remote_tags() { 199 | let tags = git_list_remote_tags("https://github.com/gruntwork-io/terragrunt").unwrap(); 200 | assert!(tags.contains(&"v0.29.7".to_string())); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /tests/hashicorp-348FFC4C.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mQENBFMORM0BCADBRyKO1MhCirazOSVwcfTr1xUxjPvfxD3hjUwHtjsOy/bT6p9f 4 | W2mRPfwnq2JB5As+paL3UGDsSRDnK9KAxQb0NNF4+eVhr/EJ18s3wwXXDMjpIifq 5 | fIm2WyH3G+aRLTLPIpscUNKDyxFOUbsmgXAmJ46Re1fn8uKxKRHbfa39aeuEYWFA 6 | 3drdL1WoUngvED7f+RnKBK2G6ZEpO+LDovQk19xGjiMTtPJrjMjZJ3QXqPvx5wca 7 | KSZLr4lMTuoTI/ZXyZy5bD4tShiZz6KcyX27cD70q2iRcEZ0poLKHyEIDAi3TM5k 8 | SwbbWBFd5RNPOR0qzrb/0p9ksKK48IIfH2FvABEBAAG0K0hhc2hpQ29ycCBTZWN1 9 | cml0eSA8c2VjdXJpdHlAaGFzaGljb3JwLmNvbT6JAU4EEwEKADgWIQSRpuf4XQXG 10 | VjC+8YlRhS2HNI/8TAUCXn0BIQIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAK 11 | CRBRhS2HNI/8TJITCACT2Zu2l8Jo/YLQMs+iYsC3gn5qJE/qf60VWpOnP0LG24rj 12 | k3j4ET5P2ow/o9lQNCM/fJrEB2CwhnlvbrLbNBbt2e35QVWvvxwFZwVcoBQXTXdT 13 | +G2cKS2Snc0bhNF7jcPX1zau8gxLurxQBaRdoL38XQ41aKfdOjEico4ZxQYSrOoC 14 | RbF6FODXj+ZL8CzJFa2Sd0rHAROHoF7WhKOvTrg1u8JvHrSgvLYGBHQZUV23cmXH 15 | yvzITl5jFzORf9TUdSv8tnuAnNsOV4vOA6lj61Z3/0Vgor+ZByfiznonPHQtKYtY 16 | kac1M/Dq2xZYiSf0tDFywgUDIF/IyS348wKmnDGjuQENBFMORM0BCADWj1GNOP4O 17 | wJmJDjI2gmeok6fYQeUbI/+Hnv5Z/cAK80Tvft3noy1oedxaDdazvrLu7YlyQOWA 18 | M1curbqJa6ozPAwc7T8XSwWxIuFfo9rStHQE3QUARxIdziQKTtlAbXI2mQU99c6x 19 | vSueQ/gq3ICFRBwCmPAm+JCwZG+cDLJJ/g6wEilNATSFdakbMX4lHUB2X0qradNO 20 | J66pdZWxTCxRLomPBWa5JEPanbosaJk0+n9+P6ImPiWpt8wiu0Qzfzo7loXiDxo/ 21 | 0G8fSbjYsIF+skY+zhNbY1MenfIPctB9X5iyW291mWW7rhhZyuqqxN2xnmPPgFmi 22 | QGd+8KVodadHABEBAAGJATwEGAECACYCGwwWIQSRpuf4XQXGVjC+8YlRhS2HNI/8 23 | TAUCXn0BRAUJEvOKdwAKCRBRhS2HNI/8TEzUB/9pEHVwtTxL8+VRq559Q0tPOIOb 24 | h3b+GroZRQGq/tcQDVbYOO6cyRMR9IohVJk0b9wnnUHoZpoA4H79UUfIB4sZngma 25 | enL/9magP1uAHxPxEa5i/yYqR0MYfz4+PGdvqyj91NrkZm3WIpwzqW/KZp8YnD77 26 | VzGVodT8xqAoHW+bHiza9Jmm9Rkf5/0i0JY7GXoJgk4QBG/Fcp0OR5NUWxN3PEM0 27 | dpeiU4GI5wOz5RAIOvSv7u1h0ZxMnJG4B4MKniIAr4yD7WYYZh/VxEPeiS/E1CVx 28 | qHV5VVCoEIoYVHIuFIyFu1lIcei53VD6V690rmn0bp4A5hs+kErhThvkok3c 29 | =+mCN 30 | -----END PGP PUBLIC KEY BLOCK----- 31 | -------------------------------------------------------------------------------- /tests/hashicorp-72D7468F.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | 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 120 | ZF5q4h4I33PSGDdSvGXn9UMY5Isjpg== 121 | =7pIB 122 | -----END PGP PUBLIC KEY BLOCK----- 123 | -------------------------------------------------------------------------------- /tests/main.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::prelude::*; 2 | use dirs::home_dir; 3 | use predicates::{ 4 | prelude::*, 5 | str::{contains, is_empty, starts_with}, 6 | }; 7 | use same_file::is_same_file; 8 | use std::{env::consts::EXE_SUFFIX, path::PathBuf, process::Command}; 9 | use tempfile::tempdir; 10 | 11 | #[test] 12 | fn test_terraform_workflow() { 13 | let home_dir = get_home_dir(); 14 | 15 | // List installed versions (none initially) 16 | terve(&home_dir) 17 | .arg("l") 18 | .arg("tf") 19 | .assert() 20 | .success() 21 | .code(0) 22 | .stdout(is_empty()); 23 | 24 | // List remote versions 25 | terve(&home_dir) 26 | .arg("l") 27 | .arg("tf") 28 | .arg("r") 29 | .assert() 30 | .success() 31 | .code(0) 32 | .stdout(contains("1.2.4").and(contains("1.1.3"))); 33 | 34 | // Try install non-existent version 35 | terve(&home_dir) 36 | .arg("i") 37 | .arg("tf") 38 | .arg("1.0.0-nope.1") 39 | .assert() 40 | .failure() 41 | .code(1) 42 | .stderr(starts_with("ERROR: HTTP status client error")); 43 | 44 | // Install some version 45 | terve(&home_dir) 46 | .arg("i") 47 | .arg("tf") 48 | .arg("1.2.4") 49 | .assert() 50 | .success() 51 | .code(0) 52 | .stdout(contains("Installed terraform 1.2.4")); 53 | 54 | // Assert idempotency by running install twice for another version 55 | for _ in 1..=2 { 56 | terve(&home_dir) 57 | .arg("i") 58 | .arg("tf") 59 | .arg("1.1.3") 60 | .assert() 61 | .success() 62 | .code(0) 63 | .stdout(contains("Installed terraform 1.1.3")); 64 | } 65 | 66 | // Try to select a non-installed version 67 | terve(&home_dir) 68 | .arg("s") 69 | .arg("tf") 70 | .arg("0.14.10") 71 | .assert() 72 | .failure() 73 | .code(1) 74 | .stderr(contains( 75 | "ERROR: terraform version 0.14.10 is not installed", 76 | )); 77 | 78 | // Assert idempotency by running select twice 79 | for _ in 1..=2 { 80 | terve(&home_dir) 81 | .arg("s") 82 | .arg("tf") 83 | .arg("1.1.3") 84 | .assert() 85 | .success() 86 | .code(0) 87 | .stdout(contains("Selected terraform 1.1.3")); 88 | } 89 | 90 | // List installed versions (expecting two) 91 | terve(&home_dir) 92 | .arg("l") 93 | .arg("tf") 94 | .assert() 95 | .success() 96 | .code(0) 97 | .stdout(contains("1.2.4").and(contains("1.1.3"))); 98 | 99 | // Show selected version 100 | terve(&home_dir) 101 | .arg("w") 102 | .arg("tf") 103 | .assert() 104 | .success() 105 | .code(0) 106 | .stdout(contains("1.1.3")); 107 | 108 | // Assert hard link points to expected terraform binary 109 | 110 | let hard_link_path = home_dir 111 | .join(".terve") 112 | .join("bin") 113 | .join(format!("terraform{}", EXE_SUFFIX)); 114 | 115 | let opt_file_path = home_dir 116 | .join(".terve") 117 | .join("opt") 118 | .join(format!("terraform{}", EXE_SUFFIX)) 119 | .join("1.1.3"); 120 | 121 | assert!(is_same_file(&hard_link_path, &opt_file_path).unwrap()); 122 | 123 | Command::new(hard_link_path) 124 | .arg("--version") 125 | .assert() 126 | .success() 127 | .code(0) 128 | .stdout(contains("1.1.3")); 129 | 130 | // Assert idempotency by running remove twice 131 | for _ in 1..=2 { 132 | terve(&home_dir) 133 | .arg("r") 134 | .arg("tf") 135 | .arg("1.1.3") 136 | .assert() 137 | .success() 138 | .code(0) 139 | .stdout(contains("Removed terraform 1.1.3")); 140 | } 141 | 142 | // Assert same version is still selected 143 | terve(&home_dir) 144 | .arg("w") 145 | .arg("tf") 146 | .assert() 147 | .success() 148 | .code(0) 149 | .stdout(contains("1.1.3")); 150 | 151 | // Remove the other version 152 | terve(&home_dir) 153 | .arg("r") 154 | .arg("tf") 155 | .arg("1.2.4") 156 | .assert() 157 | .success() 158 | .code(0) 159 | .stdout(contains("Removed terraform 1.2.4")); 160 | 161 | // Assert no installed versions 162 | terve(&home_dir) 163 | .arg("l") 164 | .arg("tf") 165 | .assert() 166 | .success() 167 | .code(0) 168 | .stdout(is_empty()); 169 | } 170 | 171 | #[test] 172 | fn test_terragrunt_workflow() { 173 | let home_dir = get_home_dir(); 174 | 175 | // List installed versions (none initially) 176 | terve(&home_dir) 177 | .arg("l") 178 | .arg("tg") 179 | .assert() 180 | .success() 181 | .code(0) 182 | .stdout(is_empty()); 183 | 184 | // List remote versions 185 | terve(&home_dir) 186 | .arg("l") 187 | .arg("tg") 188 | .arg("r") 189 | .assert() 190 | .success() 191 | .code(0) 192 | .stdout(contains("0.38.4").and(contains("0.28.24"))); 193 | 194 | // Try install non-existent version 195 | terve(&home_dir) 196 | .arg("i") 197 | .arg("tg") 198 | .arg("0.666.6-nope.1") 199 | .assert() 200 | .failure() 201 | .code(1) 202 | .stderr(starts_with("ERROR: HTTP status client error")); 203 | 204 | // Assert idempotency by running install twice 205 | for _ in 1..=2 { 206 | terve(&home_dir) 207 | .arg("i") 208 | .arg("tg") 209 | .arg("0.38.4") 210 | .assert() 211 | .success() 212 | .code(0) 213 | .stdout(contains("Installed terragrunt 0.38.4")); 214 | } 215 | 216 | // Install another version 217 | terve(&home_dir) 218 | .arg("i") 219 | .arg("tg") 220 | .arg("0.28.24") 221 | .assert() 222 | .success() 223 | .code(0) 224 | .stdout(contains("Installed terragrunt 0.28.24")); 225 | 226 | // Try select non-installed version 227 | terve(&home_dir) 228 | .arg("s") 229 | .arg("tg") 230 | .arg("0.28.2") 231 | .assert() 232 | .failure() 233 | .code(1) 234 | .stderr(contains( 235 | "ERROR: terragrunt version 0.28.2 is not installed", 236 | )); 237 | 238 | // Assert idempotency by running select twice 239 | for _ in 1..=2 { 240 | terve(&home_dir) 241 | .arg("s") 242 | .arg("tg") 243 | .arg("0.38.4") 244 | .assert() 245 | .success() 246 | .code(0) 247 | .stdout(contains("Selected terragrunt 0.38.4")); 248 | } 249 | 250 | if cfg!(linux) { 251 | // Install version for which sha256 checksum is not available 252 | terve(&home_dir) 253 | .arg("i") 254 | .arg("tg") 255 | .arg("0.18.0") 256 | .assert() 257 | .success() 258 | .code(0) 259 | .stderr(contains("WARNING: Skipping SHA256 file integrity check")) 260 | .stdout(contains("Installed terragrunt 0.18.0")); 261 | 262 | // Test for https://github.com/superblk/terve/issues/21 263 | let old_terragrunt_version = home_dir 264 | .join(".terve") 265 | .join("opt") 266 | .join(format!("terragrunt{}", EXE_SUFFIX)) 267 | .join("0.18.0"); 268 | 269 | Command::new(&old_terragrunt_version) 270 | .arg("--version") 271 | .assert() 272 | .success() 273 | .code(0) 274 | .stdout(contains("0.18.0")); 275 | 276 | // Remove this version 277 | terve(&home_dir) 278 | .arg("r") 279 | .arg("tg") 280 | .arg("0.18.0") 281 | .assert() 282 | .success() 283 | .code(0) 284 | .stdout(contains("Removed terragrunt 0.18.0")); 285 | } 286 | 287 | // Assert both installed versions are listed 288 | terve(&home_dir) 289 | .arg("l") 290 | .arg("tg") 291 | .assert() 292 | .success() 293 | .code(0) 294 | .stdout(contains("0.38.4").and(contains("0.28.24"))); 295 | 296 | // Assert correct version is selected 297 | terve(&home_dir) 298 | .arg("w") 299 | .arg("tg") 300 | .assert() 301 | .success() 302 | .code(0) 303 | .stdout(contains("0.38.4")); 304 | 305 | let hard_link_path = home_dir 306 | .join(".terve") 307 | .join("bin") 308 | .join(format!("terragrunt{}", EXE_SUFFIX)); 309 | 310 | let opt_file_path = home_dir 311 | .join(".terve") 312 | .join("opt") 313 | .join(format!("terragrunt{}", EXE_SUFFIX)) 314 | .join("0.38.4"); 315 | 316 | assert!(is_same_file(&hard_link_path, &opt_file_path).unwrap()); 317 | 318 | Command::new(hard_link_path) 319 | .arg("--version") 320 | .assert() 321 | .success() 322 | .code(0) 323 | .stdout(contains("terragrunt version v0.38.4")); 324 | 325 | // Assert idempotency by running remove twice 326 | for _ in 1..=2 { 327 | terve(&home_dir) 328 | .arg("r") 329 | .arg("tg") 330 | .arg("0.38.4") 331 | .assert() 332 | .success() 333 | .code(0) 334 | .stdout(contains("Removed terragrunt 0.38.4")); 335 | } 336 | 337 | // Assert same version is still selected 338 | terve(&home_dir) 339 | .arg("w") 340 | .arg("tg") 341 | .assert() 342 | .success() 343 | .code(0) 344 | .stdout(contains("0.38.4")); 345 | 346 | // Remove the other version 347 | terve(&home_dir) 348 | .arg("r") 349 | .arg("tg") 350 | .arg("0.28.24") 351 | .assert() 352 | .success() 353 | .code(0) 354 | .stdout(contains("Removed terragrunt 0.28.24")); 355 | 356 | // Assert no installed versions 357 | terve(&home_dir) 358 | .arg("l") 359 | .arg("tg") 360 | .assert() 361 | .success() 362 | .code(0) 363 | .stdout(is_empty()); 364 | } 365 | 366 | fn terve(home_dir: &PathBuf) -> Command { 367 | let mut cmd = Command::cargo_bin("terve").unwrap(); 368 | cmd.env("HOME", &home_dir.as_os_str()); 369 | cmd 370 | } 371 | 372 | fn get_home_dir() -> PathBuf { 373 | if cfg!(unix) { 374 | tempdir() 375 | .expect("failed to create fake home dir") 376 | .into_path() 377 | } else { 378 | home_dir().unwrap() 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /tests/special.txt: -------------------------------------------------------------------------------- 1 | A 2 | B 3 | C 4 | 123 5 | -------------------------------------------------------------------------------- /tests/terraform_0.13.1_SHA256SUMS: -------------------------------------------------------------------------------- 1 | fe5d1b6e22892c5dcc8b44d2a26ea1e29d90af6fcb1472f3881ca3c08c8a8084 terraform_0.13.1_darwin_amd64.zip 2 | 6e32abdbd5d1b5ca9cabff39fed603f00b70eff68797ea76bcea2ea45d4dd314 terraform_0.13.1_freebsd_386.zip 3 | fd556e3ff585c0cefd794783d5d5c05fba5ed793211991c5d98add0c6a4b67ab terraform_0.13.1_freebsd_amd64.zip 4 | b015f1a9cdbbe1df060c62ddb6dffe79d12dda64921cbc95eb59d0ef6715d2e2 terraform_0.13.1_freebsd_arm.zip 5 | 9e031e17121f524d8166d6f728ff3ccb64fed82e18d63f431c2fd5deb2707084 terraform_0.13.1_linux_386.zip 6 | f7b842d1c06045b496fd00db83520e83a974a294b070dbaf88cb1013c5f02caf terraform_0.13.1_linux_amd64.zip 7 | c15548aa1ec77e968af78c02847672b252145ef7aea1350de9cdbec55fd12924 terraform_0.13.1_linux_arm.zip 8 | e5db1c231ee0abd891d3b0ce737ccce8bd17e9eb2fb98b07d2acc50815ebd3bb terraform_0.13.1_openbsd_386.zip 9 | 8ab1de5f9dcfa97e2b8608ca6ab8b357585bb4273ddc2ff44cb9b818905a6879 terraform_0.13.1_openbsd_amd64.zip 10 | d4adbc7427261bb7c052861a96369c19c48878f72e10c9900fc0e26544a4daa6 terraform_0.13.1_solaris_amd64.zip 11 | af1babe4ad7aa2e88fbf12e3ee176846db859617c572f3480de59f9284460457 terraform_0.13.1_windows_386.zip 12 | fa7b17147baf4aca478ae83b25c14c1baa064a118a21e0e7b34e3d70e1732b6d terraform_0.13.1_windows_amd64.zip 13 | -------------------------------------------------------------------------------- /tests/terraform_0.13.1_SHA256SUMS.348FFC4C.sig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superblk/terve/e62665ad3bc0084c26a7a159080b5e04957469f9/tests/terraform_0.13.1_SHA256SUMS.348FFC4C.sig -------------------------------------------------------------------------------- /tests/terraform_0.13.1_SHA256SUMS.72D7468F.sig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superblk/terve/e62665ad3bc0084c26a7a159080b5e04957469f9/tests/terraform_0.13.1_SHA256SUMS.72D7468F.sig --------------------------------------------------------------------------------