Listen to git commits for a specific repo and run a shell command

Compare changes

Choose any two refs to compare.

+325 -34
Cargo.lock
··· 47 47 checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" 48 48 dependencies = [ 49 49 "addr2line", 50 - "cfg-if", 50 + "cfg-if 1.0.3", 51 51 "libc", 52 52 "miniz_oxide", 53 53 "object", ··· 72 72 version = "2.9.4" 73 73 source = "registry+https://github.com/rust-lang/crates.io-index" 74 74 checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" 75 + 76 + [[package]] 77 + name = "block-buffer" 78 + version = "0.7.3" 79 + source = "registry+https://github.com/rust-lang/crates.io-index" 80 + checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" 81 + dependencies = [ 82 + "block-padding", 83 + "byte-tools", 84 + "byteorder", 85 + "generic-array", 86 + ] 87 + 88 + [[package]] 89 + name = "block-padding" 90 + version = "0.1.5" 91 + source = "registry+https://github.com/rust-lang/crates.io-index" 92 + checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" 93 + dependencies = [ 94 + "byte-tools", 95 + ] 75 96 76 97 [[package]] 77 98 name = "bumpalo" 78 99 version = "3.19.0" 79 100 source = "registry+https://github.com/rust-lang/crates.io-index" 80 101 checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 102 + 103 + [[package]] 104 + name = "byte-tools" 105 + version = "0.3.1" 106 + source = "registry+https://github.com/rust-lang/crates.io-index" 107 + checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" 108 + 109 + [[package]] 110 + name = "byteorder" 111 + version = "1.5.0" 112 + source = "registry+https://github.com/rust-lang/crates.io-index" 113 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 114 + 115 + [[package]] 116 + name = "bytes" 117 + version = "0.4.12" 118 + source = "registry+https://github.com/rust-lang/crates.io-index" 119 + checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" 120 + dependencies = [ 121 + "byteorder", 122 + "iovec", 123 + ] 81 124 82 125 [[package]] 83 126 name = "bytes" ··· 97 140 98 141 [[package]] 99 142 name = "cfg-if" 143 + version = "0.1.10" 144 + source = "registry+https://github.com/rust-lang/crates.io-index" 145 + checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 146 + 147 + [[package]] 148 + name = "cfg-if" 100 149 version = "1.0.3" 101 150 source = "registry+https://github.com/rust-lang/crates.io-index" 102 151 checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" ··· 124 173 checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" 125 174 126 175 [[package]] 176 + name = "digest" 177 + version = "0.8.1" 178 + source = "registry+https://github.com/rust-lang/crates.io-index" 179 + checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" 180 + dependencies = [ 181 + "generic-array", 182 + ] 183 + 184 + [[package]] 127 185 name = "displaydoc" 128 186 version = "0.2.5" 129 187 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 140 198 source = "registry+https://github.com/rust-lang/crates.io-index" 141 199 checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 142 200 dependencies = [ 143 - "cfg-if", 201 + "cfg-if 1.0.3", 144 202 ] 145 203 146 204 [[package]] ··· 170 228 "libc", 171 229 "windows-sys 0.59.0", 172 230 ] 231 + 232 + [[package]] 233 + name = "fake-simd" 234 + version = "0.1.2" 235 + source = "registry+https://github.com/rust-lang/crates.io-index" 236 + checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" 173 237 174 238 [[package]] 175 239 name = "fastrand" ··· 214 278 ] 215 279 216 280 [[package]] 281 + name = "fuchsia-zircon" 282 + version = "0.3.3" 283 + source = "registry+https://github.com/rust-lang/crates.io-index" 284 + checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 285 + dependencies = [ 286 + "bitflags 1.3.2", 287 + "fuchsia-zircon-sys", 288 + ] 289 + 290 + [[package]] 291 + name = "fuchsia-zircon-sys" 292 + version = "0.3.3" 293 + source = "registry+https://github.com/rust-lang/crates.io-index" 294 + checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 295 + 296 + [[package]] 217 297 name = "futures-channel" 218 298 version = "0.3.31" 219 299 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 264 344 ] 265 345 266 346 [[package]] 347 + name = "generic-array" 348 + version = "0.12.4" 349 + source = "registry+https://github.com/rust-lang/crates.io-index" 350 + checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" 351 + dependencies = [ 352 + "typenum", 353 + ] 354 + 355 + [[package]] 356 + name = "getrandom" 357 + version = "0.1.16" 358 + source = "registry+https://github.com/rust-lang/crates.io-index" 359 + checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 360 + dependencies = [ 361 + "cfg-if 1.0.3", 362 + "libc", 363 + "wasi 0.9.0+wasi-snapshot-preview1", 364 + ] 365 + 366 + [[package]] 267 367 name = "getrandom" 268 368 version = "0.2.16" 269 369 source = "registry+https://github.com/rust-lang/crates.io-index" 270 370 checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 271 371 dependencies = [ 272 - "cfg-if", 372 + "cfg-if 1.0.3", 273 373 "libc", 274 374 "wasi 0.11.1+wasi-snapshot-preview1", 275 375 ] ··· 280 380 source = "registry+https://github.com/rust-lang/crates.io-index" 281 381 checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 282 382 dependencies = [ 283 - "cfg-if", 383 + "cfg-if 1.0.3", 284 384 "libc", 285 385 "r-efi", 286 386 "wasi 0.14.4+wasi-0.2.4", ··· 299 399 checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" 300 400 dependencies = [ 301 401 "atomic-waker", 302 - "bytes", 402 + "bytes 1.10.1", 303 403 "fnv", 304 404 "futures-core", 305 405 "futures-sink", ··· 329 429 source = "registry+https://github.com/rust-lang/crates.io-index" 330 430 checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 331 431 dependencies = [ 332 - "bytes", 432 + "bytes 1.10.1", 333 433 "fnv", 334 434 "itoa", 335 435 ] ··· 340 440 source = "registry+https://github.com/rust-lang/crates.io-index" 341 441 checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 342 442 dependencies = [ 343 - "bytes", 443 + "bytes 1.10.1", 344 444 "http", 345 445 ] 346 446 ··· 350 450 source = "registry+https://github.com/rust-lang/crates.io-index" 351 451 checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 352 452 dependencies = [ 353 - "bytes", 453 + "bytes 1.10.1", 354 454 "futures-core", 355 455 "http", 356 456 "http-body", ··· 370 470 checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" 371 471 dependencies = [ 372 472 "atomic-waker", 373 - "bytes", 473 + "bytes 1.10.1", 374 474 "futures-channel", 375 475 "futures-core", 376 476 "h2", ··· 407 507 source = "registry+https://github.com/rust-lang/crates.io-index" 408 508 checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 409 509 dependencies = [ 410 - "bytes", 510 + "bytes 1.10.1", 411 511 "http-body-util", 412 512 "hyper", 413 513 "hyper-util", ··· 424 524 checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" 425 525 dependencies = [ 426 526 "base64", 427 - "bytes", 527 + "bytes 1.10.1", 428 528 "futures-channel", 429 529 "futures-core", 430 530 "futures-util", ··· 577 677 source = "registry+https://github.com/rust-lang/crates.io-index" 578 678 checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" 579 679 dependencies = [ 580 - "cfg-if", 680 + "cfg-if 1.0.3", 581 681 ] 582 682 583 683 [[package]] ··· 587 687 checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" 588 688 dependencies = [ 589 689 "bitflags 2.9.4", 590 - "cfg-if", 690 + "cfg-if 1.0.3", 691 + "libc", 692 + ] 693 + 694 + [[package]] 695 + name = "iovec" 696 + version = "0.1.4" 697 + source = "registry+https://github.com/rust-lang/crates.io-index" 698 + checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 699 + dependencies = [ 591 700 "libc", 592 701 ] 593 702 ··· 599 708 dependencies = [ 600 709 "socket2 0.3.19", 601 710 "widestring", 602 - "winapi", 711 + "winapi 0.3.9", 603 712 "winreg", 604 713 ] 605 714 ··· 642 751 checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" 643 752 644 753 [[package]] 754 + name = "kernel32-sys" 755 + version = "0.2.2" 756 + source = "registry+https://github.com/rust-lang/crates.io-index" 757 + checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 758 + dependencies = [ 759 + "winapi 0.2.8", 760 + "winapi-build", 761 + ] 762 + 763 + [[package]] 645 764 name = "lazy_static" 646 765 version = "1.5.0" 647 766 source = "registry+https://github.com/rust-lang/crates.io-index" 648 767 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 768 + 769 + [[package]] 770 + name = "lazycell" 771 + version = "1.3.0" 772 + source = "registry+https://github.com/rust-lang/crates.io-index" 773 + checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 649 774 650 775 [[package]] 651 776 name = "libc" ··· 725 850 726 851 [[package]] 727 852 name = "mio" 853 + version = "0.6.23" 854 + source = "registry+https://github.com/rust-lang/crates.io-index" 855 + checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" 856 + dependencies = [ 857 + "cfg-if 0.1.10", 858 + "fuchsia-zircon", 859 + "fuchsia-zircon-sys", 860 + "iovec", 861 + "kernel32-sys", 862 + "libc", 863 + "log", 864 + "miow", 865 + "net2", 866 + "slab", 867 + "winapi 0.2.8", 868 + ] 869 + 870 + [[package]] 871 + name = "mio" 728 872 version = "1.0.4" 729 873 source = "registry+https://github.com/rust-lang/crates.io-index" 730 874 checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" ··· 735 879 ] 736 880 737 881 [[package]] 882 + name = "mio-extras" 883 + version = "2.0.6" 884 + source = "registry+https://github.com/rust-lang/crates.io-index" 885 + checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" 886 + dependencies = [ 887 + "lazycell", 888 + "log", 889 + "mio 0.6.23", 890 + "slab", 891 + ] 892 + 893 + [[package]] 894 + name = "miow" 895 + version = "0.2.2" 896 + source = "registry+https://github.com/rust-lang/crates.io-index" 897 + checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" 898 + dependencies = [ 899 + "kernel32-sys", 900 + "net2", 901 + "winapi 0.2.8", 902 + "ws2_32-sys", 903 + ] 904 + 905 + [[package]] 738 906 name = "native-tls" 739 907 version = "0.2.14" 740 908 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 752 920 ] 753 921 754 922 [[package]] 923 + name = "net2" 924 + version = "0.2.39" 925 + source = "registry+https://github.com/rust-lang/crates.io-index" 926 + checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" 927 + dependencies = [ 928 + "cfg-if 0.1.10", 929 + "libc", 930 + "winapi 0.3.9", 931 + ] 932 + 933 + [[package]] 755 934 name = "object" 756 935 version = "0.36.7" 757 936 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 767 946 checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 768 947 769 948 [[package]] 949 + name = "opaque-debug" 950 + version = "0.2.3" 951 + source = "registry+https://github.com/rust-lang/crates.io-index" 952 + checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" 953 + 954 + [[package]] 770 955 name = "openssl" 771 956 version = "0.10.73" 772 957 source = "registry+https://github.com/rust-lang/crates.io-index" 773 958 checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" 774 959 dependencies = [ 775 960 "bitflags 2.9.4", 776 - "cfg-if", 961 + "cfg-if 1.0.3", 777 962 "foreign-types", 778 963 "libc", 779 964 "once_cell", ··· 827 1012 source = "registry+https://github.com/rust-lang/crates.io-index" 828 1013 checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" 829 1014 dependencies = [ 830 - "cfg-if", 1015 + "cfg-if 1.0.3", 831 1016 "instant", 832 1017 "libc", 833 1018 "redox_syscall", 834 1019 "smallvec", 835 - "winapi", 1020 + "winapi 0.3.9", 836 1021 ] 837 1022 838 1023 [[package]] ··· 903 1088 904 1089 [[package]] 905 1090 name = "rand" 1091 + version = "0.7.3" 1092 + source = "registry+https://github.com/rust-lang/crates.io-index" 1093 + checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1094 + dependencies = [ 1095 + "getrandom 0.1.16", 1096 + "libc", 1097 + "rand_chacha 0.2.2", 1098 + "rand_core 0.5.1", 1099 + "rand_hc", 1100 + ] 1101 + 1102 + [[package]] 1103 + name = "rand" 906 1104 version = "0.8.5" 907 1105 source = "registry+https://github.com/rust-lang/crates.io-index" 908 1106 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 909 1107 dependencies = [ 910 1108 "libc", 911 - "rand_chacha", 912 - "rand_core", 1109 + "rand_chacha 0.3.1", 1110 + "rand_core 0.6.4", 1111 + ] 1112 + 1113 + [[package]] 1114 + name = "rand_chacha" 1115 + version = "0.2.2" 1116 + source = "registry+https://github.com/rust-lang/crates.io-index" 1117 + checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1118 + dependencies = [ 1119 + "ppv-lite86", 1120 + "rand_core 0.5.1", 913 1121 ] 914 1122 915 1123 [[package]] ··· 919 1127 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 920 1128 dependencies = [ 921 1129 "ppv-lite86", 922 - "rand_core", 1130 + "rand_core 0.6.4", 1131 + ] 1132 + 1133 + [[package]] 1134 + name = "rand_core" 1135 + version = "0.5.1" 1136 + source = "registry+https://github.com/rust-lang/crates.io-index" 1137 + checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1138 + dependencies = [ 1139 + "getrandom 0.1.16", 923 1140 ] 924 1141 925 1142 [[package]] ··· 932 1149 ] 933 1150 934 1151 [[package]] 1152 + name = "rand_hc" 1153 + version = "0.2.0" 1154 + source = "registry+https://github.com/rust-lang/crates.io-index" 1155 + checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1156 + dependencies = [ 1157 + "rand_core 0.5.1", 1158 + ] 1159 + 1160 + [[package]] 935 1161 name = "redox_syscall" 936 1162 version = "0.2.16" 937 1163 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 947 1173 checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" 948 1174 dependencies = [ 949 1175 "base64", 950 - "bytes", 1176 + "bytes 1.10.1", 951 1177 "encoding_rs", 952 1178 "futures-channel", 953 1179 "futures-core", ··· 995 1221 checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 996 1222 dependencies = [ 997 1223 "cc", 998 - "cfg-if", 1224 + "cfg-if 1.0.3", 999 1225 "getrandom 0.2.16", 1000 1226 "libc", 1001 1227 "untrusted", ··· 1149 1375 ] 1150 1376 1151 1377 [[package]] 1378 + name = "sha-1" 1379 + version = "0.8.2" 1380 + source = "registry+https://github.com/rust-lang/crates.io-index" 1381 + checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" 1382 + dependencies = [ 1383 + "block-buffer", 1384 + "digest", 1385 + "fake-simd", 1386 + "opaque-debug", 1387 + ] 1388 + 1389 + [[package]] 1152 1390 name = "shlex" 1153 1391 version = "1.3.0" 1154 1392 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1172 1410 source = "registry+https://github.com/rust-lang/crates.io-index" 1173 1411 checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" 1174 1412 dependencies = [ 1175 - "cfg-if", 1413 + "cfg-if 1.0.3", 1176 1414 "libc", 1177 - "winapi", 1415 + "winapi 0.3.9", 1178 1416 ] 1179 1417 1180 1418 [[package]] ··· 1269 1507 "json", 1270 1508 "reqwest", 1271 1509 "trust-dns-resolver", 1510 + "ws", 1272 1511 ] 1273 1512 1274 1513 [[package]] ··· 1336 1575 checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" 1337 1576 dependencies = [ 1338 1577 "backtrace", 1339 - "bytes", 1578 + "bytes 1.10.1", 1340 1579 "io-uring", 1341 1580 "libc", 1342 - "mio", 1581 + "mio 1.0.4", 1343 1582 "pin-project-lite", 1344 1583 "slab", 1345 1584 "socket2 0.6.0", ··· 1372 1611 source = "registry+https://github.com/rust-lang/crates.io-index" 1373 1612 checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" 1374 1613 dependencies = [ 1375 - "bytes", 1614 + "bytes 1.10.1", 1376 1615 "futures-core", 1377 1616 "futures-sink", 1378 1617 "pin-project-lite", ··· 1401 1640 checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" 1402 1641 dependencies = [ 1403 1642 "bitflags 2.9.4", 1404 - "bytes", 1643 + "bytes 1.10.1", 1405 1644 "futures-util", 1406 1645 "http", 1407 1646 "http-body", ··· 1450 1689 checksum = "ca94d4e9feb6a181c690c4040d7a24ef34018d8313ac5044a61d21222ae24e31" 1451 1690 dependencies = [ 1452 1691 "async-trait", 1453 - "cfg-if", 1692 + "cfg-if 1.0.3", 1454 1693 "data-encoding", 1455 1694 "enum-as-inner", 1456 1695 "futures-channel", ··· 1460 1699 "ipnet", 1461 1700 "lazy_static", 1462 1701 "log", 1463 - "rand", 1702 + "rand 0.8.5", 1464 1703 "smallvec", 1465 1704 "thiserror", 1466 1705 "tinyvec", ··· 1474 1713 source = "registry+https://github.com/rust-lang/crates.io-index" 1475 1714 checksum = "ecae383baad9995efaa34ce8e57d12c3f305e545887472a492b838f4b5cfb77a" 1476 1715 dependencies = [ 1477 - "cfg-if", 1716 + "cfg-if 1.0.3", 1478 1717 "futures-util", 1479 1718 "ipconfig", 1480 1719 "lazy_static", ··· 1495 1734 checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1496 1735 1497 1736 [[package]] 1737 + name = "typenum" 1738 + version = "1.18.0" 1739 + source = "registry+https://github.com/rust-lang/crates.io-index" 1740 + checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 1741 + 1742 + [[package]] 1498 1743 name = "unicode-bidi" 1499 1744 version = "0.3.18" 1500 1745 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1556 1801 1557 1802 [[package]] 1558 1803 name = "wasi" 1804 + version = "0.9.0+wasi-snapshot-preview1" 1805 + source = "registry+https://github.com/rust-lang/crates.io-index" 1806 + checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1807 + 1808 + [[package]] 1809 + name = "wasi" 1559 1810 version = "0.11.1+wasi-snapshot-preview1" 1560 1811 source = "registry+https://github.com/rust-lang/crates.io-index" 1561 1812 checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" ··· 1575 1826 source = "registry+https://github.com/rust-lang/crates.io-index" 1576 1827 checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" 1577 1828 dependencies = [ 1578 - "cfg-if", 1829 + "cfg-if 1.0.3", 1579 1830 "once_cell", 1580 1831 "rustversion", 1581 1832 "wasm-bindgen-macro", ··· 1602 1853 source = "registry+https://github.com/rust-lang/crates.io-index" 1603 1854 checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe" 1604 1855 dependencies = [ 1605 - "cfg-if", 1856 + "cfg-if 1.0.3", 1606 1857 "js-sys", 1607 1858 "once_cell", 1608 1859 "wasm-bindgen", ··· 1659 1910 1660 1911 [[package]] 1661 1912 name = "winapi" 1913 + version = "0.2.8" 1914 + source = "registry+https://github.com/rust-lang/crates.io-index" 1915 + checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1916 + 1917 + [[package]] 1918 + name = "winapi" 1662 1919 version = "0.3.9" 1663 1920 source = "registry+https://github.com/rust-lang/crates.io-index" 1664 1921 checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" ··· 1666 1923 "winapi-i686-pc-windows-gnu", 1667 1924 "winapi-x86_64-pc-windows-gnu", 1668 1925 ] 1926 + 1927 + [[package]] 1928 + name = "winapi-build" 1929 + version = "0.1.1" 1930 + source = "registry+https://github.com/rust-lang/crates.io-index" 1931 + checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1669 1932 1670 1933 [[package]] 1671 1934 name = "winapi-i686-pc-windows-gnu" ··· 1802 2065 source = "registry+https://github.com/rust-lang/crates.io-index" 1803 2066 checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" 1804 2067 dependencies = [ 1805 - "winapi", 2068 + "winapi 0.3.9", 1806 2069 ] 1807 2070 1808 2071 [[package]] ··· 1816 2079 version = "0.6.1" 1817 2080 source = "registry+https://github.com/rust-lang/crates.io-index" 1818 2081 checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 2082 + 2083 + [[package]] 2084 + name = "ws" 2085 + version = "0.9.2" 2086 + source = "registry+https://github.com/rust-lang/crates.io-index" 2087 + checksum = "25fe90c75f236a0a00247d5900226aea4f2d7b05ccc34da9e7a8880ff59b5848" 2088 + dependencies = [ 2089 + "byteorder", 2090 + "bytes 0.4.12", 2091 + "httparse", 2092 + "log", 2093 + "mio 0.6.23", 2094 + "mio-extras", 2095 + "rand 0.7.3", 2096 + "sha-1", 2097 + "slab", 2098 + "url", 2099 + ] 2100 + 2101 + [[package]] 2102 + name = "ws2_32-sys" 2103 + version = "0.2.1" 2104 + source = "registry+https://github.com/rust-lang/crates.io-index" 2105 + checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 2106 + dependencies = [ 2107 + "winapi 0.2.8", 2108 + "winapi-build", 2109 + ] 1819 2110 1820 2111 [[package]] 1821 2112 name = "yoke"
+1
Cargo.toml
··· 9 9 json = "0.12.4" 10 10 trust-dns-resolver = "0.20.1" 11 11 reqwest = { version = "0.12.23", features = ["blocking"] } 12 + ws = "0.9.2"
+48 -4
README.md
··· 6 6 $ tangled-on-commit @vielle.dev/tangled-on-commit ./commit.sh 7 7 ``` 8 8 9 + > Created for linux. Should work fine on MacOS. Will not work on windows. 10 + > Note: The shell command is placed inside `/bin/sh -c "COMMAND"`, so make sure to escape quotes and backslashes. I reccomend executing a shell file and piping stdout and stderr to a log file 11 + 9 12 ## Installation 10 13 11 - // TODO 14 + Prerequisites: 15 + 16 + - `Git` 17 + - `Rust` (1.88+) 18 + - `Cargo` 19 + - `OpenSSL` (required for ssl connection) 20 + - `pkg-config` (required for ssl connection) 21 + 22 + 1. Clone this repo: 23 + `git clone git@tangled.sh:vielle.dev/tangled-on-commit` 24 + 2. Compile the binary: 25 + `cargo build --release` 26 + 3. Copy the binary to your path: 27 + `sudo cp ./target/release/tangled-on-commit /bin` 12 28 13 29 ## Usage 14 30 15 - // TODO 31 + ### CLI Arguments 16 32 17 - ## Configuration 33 + - `tangled-on-commit (-h | --help)` 34 + Displays this message 18 35 19 - // TODO 36 + - `tangled-on-commit` 37 + No specified handle, repo, or command. Falls back to config/env 38 + 39 + - `tangled-on-commit SHELL` 40 + Uses config/env for handle and repo 41 + 42 + - `tangled-on-commit @HANDLE SHELL` 43 + Uses config/env for repo 44 + 45 + - `tangled-on-commit REPO SHELL` 46 + Uses config/env for handle 47 + 48 + - `tangled-on-commit @HANDLE/REPO SHELL` 49 + `tangled-on-commit HANDLE REPO SHELL` 50 + No config/env 51 + 52 + ### JSON 53 + 54 + Loads the file `tangled-on-commit.json` from cwd if it exists. 55 + Reads keys \"handle\", \"repo_name\", and \"shell\". 56 + Unknown keys are ignored and any key can be ommitted 57 + JSON is used if the arguments aren't passed to the CLI 58 + 59 + ### Env 60 + 61 + Loads the environment variables `TANGLED_ON_COMMIT_HANDLE` and `TANGLED_ON_COMMIT_REPO_NAME` 62 + Shell cannot be set by environment variables. 63 + Env variables are used if relevant keys are ommitted an arguments aren't passed to the CLI.
+1
src/args.rs
··· 92 92 if val.contains("/") { 93 93 let entries: Vec<_> = val.split("/").collect(); 94 94 if entries.len() != 2 { 95 + println!("Invalid argument: `{}` is malformed", val); 95 96 return Err(()); 96 97 } 97 98 handle = Some(entries[0][1..].to_string());
+137 -15
src/did.rs
··· 4 4 #[derive(Debug)] 5 5 pub struct DidDoc { 6 6 pub did: String, 7 + pub pds: String, 7 8 } 8 9 9 10 fn get_txt_did(handle: &String) -> Result<String, ()> { 11 + println!(" Trying dns TXT for _atproto.{}", handle); 10 12 // create a txt resolver 11 13 let resolver = match Resolver::new(ResolverConfig::default(), ResolverOpts::default()) { 12 14 Ok(val) => val, 13 - Err(_) => return Err(()), 15 + Err(_) => { 16 + println!(" Couldn't create a DNS resolver"); 17 + return Err(()); 18 + } 14 19 }; 15 20 16 21 // resolve _atproto.handle to a TXT record 17 22 let txt_res = match resolver.txt_lookup("_atproto.".to_owned() + &handle) { 18 23 Ok(val) => val, 19 - Err(_) => return Err(()), // collect all entries and convert to strings 24 + Err(_) => { 25 + println!(" Couldn't resolve to a TXT record"); 26 + return Err(()); 27 + } // collect all entries and convert to strings 20 28 } 21 29 .into_iter() 22 30 .map(|x| x.to_string()) ··· 30 38 // only 1 did= can exist 31 39 // https://atproto.com/specs/handle#:~:text=If%20multiple%20valid%20records%20with%20different%20DIDs%20are%20present,%20resolution%20should%20fail. 32 40 if did_res.len() != 1 { 41 + println!(" Found too many DIDs for this handle"); 33 42 return Err(()); 34 43 } 35 - let did = did_res[0].clone(); 36 44 37 - return Ok(did.clone()); 45 + return Ok(did_res[0][4..].to_string()); 38 46 } 39 47 40 48 fn get_http_did(handle: &String) -> Result<String, ()> { 41 - let res = match reqwest::blocking::get("https://".to_owned() + handle + "/.well-known/atproto-did") { 42 - Ok(val) => val, 43 - Err(_) => return Err(()) 44 - }; 49 + println!( 50 + " Trying https for https://{}/.well-known/atproto-did", 51 + handle 52 + ); 53 + let res = 54 + match reqwest::blocking::get("https://".to_owned() + handle + "/.well-known/atproto-did") { 55 + Ok(val) => val, 56 + Err(_) => { 57 + println!(" GET request failed"); 58 + return Err(()); 59 + } 60 + }; 45 61 46 62 // as per spec, non 2xx code means failure 47 - if !res.status().is_success() { return Err(()) } 63 + if !res.status().is_success() { 64 + println!( 65 + " Got non 2xx status code: {}", 66 + res.status().as_str() 67 + ); 68 + return Err(()); 69 + } 48 70 49 71 let did_unparsed = match res.text() { 50 - Ok(val) => val, 51 - Err(_) => return Err(()) 72 + Ok(val) => val, 73 + Err(_) => { 74 + println!(" Missing or malformed body response"); 75 + return Err(()); 76 + } 52 77 }; 53 78 54 79 let did = did_unparsed.trim(); 55 80 56 - if !did.starts_with("did:") { return Err(()) }; 57 - return Ok(String::from(did)) 81 + if !did.starts_with("did:") { 82 + println!(" Did not find a DID"); 83 + return Err(()); 84 + }; 85 + return Ok(String::from(did)); 58 86 } 59 87 60 - pub fn get_did(handle: String) -> Result<DidDoc, ()> { 88 + fn parse_doc(did: String, text: String) -> Result<DidDoc, ()> { 89 + let text_json = match json::parse(&text) { 90 + Ok(val) => val, 91 + Err(_) => { 92 + println!(" Malformed DID document"); 93 + return Err(()); 94 + } 95 + }; 96 + 97 + for service in text_json["service"].members() { 98 + if service["id"] 99 + .as_str() 100 + .is_some_and(|x| x.ends_with("#atproto_pds")) 101 + && service["type"] 102 + .as_str() 103 + .is_some_and(|x| x == "AtprotoPersonalDataServer") 104 + && let Some(pds) = service["serviceEndpoint"].as_str() 105 + { 106 + return Ok(DidDoc { 107 + did: did, 108 + pds: pds.to_string(), 109 + }); 110 + } 111 + } 112 + 113 + println!(" Missing fields from DID document"); 114 + return Err(()); 115 + } 116 + 117 + fn get_plc_doc(plc: &str) -> Result<DidDoc, ()> { 118 + let res = match reqwest::blocking::get("https://plc.directory/did:plc:".to_owned() + plc) { 119 + Ok(val) => val, 120 + Err(_) => { 121 + println!(" GET request failed"); 122 + return Err(()); 123 + } 124 + }; 125 + 126 + if !res.status().is_success() { 127 + println!(" Got non 2xx status code: {}", res.status().as_str()); 128 + 129 + return Err(()); 130 + } 131 + 132 + return parse_doc( 133 + "did:plc:".to_owned() + plc, 134 + match res.text() { 135 + Ok(val) => val, 136 + Err(_) => { 137 + println!(" Missing or malformed body response"); 138 + return Err(()); 139 + } 140 + }, 141 + ); 142 + } 143 + 144 + fn get_web_doc(web: &str) -> Result<DidDoc, ()> { 145 + let res = match reqwest::blocking::get("https://".to_owned() + web + "/.well-known/did.json") { 146 + Ok(val) => val, 147 + Err(_) => { 148 + println!(" GET request failed"); 149 + return Err(()); 150 + } 151 + }; 152 + 153 + if !res.status().is_success() { 154 + println!(" Got non 2xx status code: {}", res.status().as_str()); 155 + return Err(()); 156 + } 157 + 158 + return parse_doc( 159 + "did:web:".to_owned() + web, 160 + match res.text() { 161 + Ok(val) => val, 162 + Err(_) => { 163 + println!(" Missing or malformed body response"); 164 + return Err(()); 165 + } 166 + }, 167 + ); 168 + } 169 + 170 + pub fn get_did(handle: &String) -> Result<DidDoc, ()> { 171 + println!(" Getting DID for {}", handle); 61 172 let did = if let Ok(did) = get_txt_did(&handle) { 62 173 did 63 174 } else { 64 175 if let Ok(did) = get_http_did(&handle) { 65 176 did 66 177 } else { 178 + println!(" Could not get a DID"); 67 179 return Err(()); 68 180 } 69 181 }; 70 182 71 - return Ok(DidDoc { did }); 183 + println!(" Getting DID document for {}", did); 184 + let did_doc = if did.starts_with("did:plc:") { 185 + get_plc_doc(&did[8..]) 186 + } else if did.starts_with("did:web:") { 187 + get_web_doc(&did[8..]) 188 + } else { 189 + println!(" Could not get a DID document"); 190 + Err(()) 191 + }; 192 + 193 + return did_doc; 72 194 }
+66
src/knot.rs
··· 1 + pub fn find_knot(did: &String, repo_name: &String, pds: &String) -> Result<String, ()> { 2 + // https://docs.bsky.app/docs/api/com-atproto-repo-list-records 3 + let mut cursor: Option<String> = None; 4 + 5 + loop { 6 + let url = format!( 7 + "{0}/xrpc/com.atproto.repo.listRecords?collection=sh.tangled.repo&repo={1}&cursor={2}", 8 + pds.clone(), 9 + did.clone(), 10 + cursor.clone().unwrap_or(String::new()) 11 + ); 12 + 13 + let res = match reqwest::blocking::get(url) { 14 + Ok(val) => val, 15 + Err(_) => { 16 + println!(" Failed to load repos."); 17 + return Err(()); 18 + } 19 + }; 20 + 21 + if !res.status().is_success() { 22 + println!(" Got non 2xx code when loading repos"); 23 + return Err(()); 24 + } 25 + 26 + let body = match res.text() { 27 + Ok(val) => match json::parse(&val) { 28 + Ok(val) => val, 29 + Err(_) => { 30 + println!(" Invalid or malformed response response"); 31 + return Err(()); 32 + } 33 + }, 34 + Err(_) => { 35 + println!(" Missing body from response"); 36 + return Err(()); 37 + } 38 + }; 39 + 40 + // must contain a records array 41 + if !body["records"].is_array() { 42 + println!(" Invalid or malformed response response"); 43 + return Err(()); 44 + } 45 + for record in body["records"].members() { 46 + if let Some(val) = record["value"]["name"].as_str() 47 + && val == repo_name 48 + { 49 + if let Some(knot) = record["value"]["knot"].as_str() { 50 + return Ok(String::from(knot)); 51 + } else { 52 + println!(" Repo didn't have a valid knot"); 53 + return Err(()); 54 + } 55 + } 56 + } 57 + 58 + if let Some(val) = body["cursor"].as_str() { 59 + cursor = Some(String::from(val)) 60 + } else { 61 + break; 62 + } 63 + } 64 + 65 + return Err(()); 66 + }
+65 -16
src/main.rs
··· 1 + use std::process::Command; 2 + 1 3 mod args; 2 4 mod did; 5 + mod knot; 3 6 4 7 fn main() -> Result<(), ()> { 5 8 // load configuration 6 9 let config = match args::load_config() { 7 10 Ok(res) => res, 8 - Err(_) => { 9 - // q 10 - return Err(()); 11 - } 11 + Err(_) => return Err(()), 12 12 }; 13 - println!("{:#?}", config); 14 13 15 14 // resolve handle to did 16 - let did_doc = match did::get_did(config.handle) { 15 + println!("Resolving {}", config.handle); 16 + let did_doc = match did::get_did(&config.handle) { 17 17 Ok(res) => res, 18 - Err(_) => { 19 - // q 20 - return Err(()); 21 - } 18 + Err(_) => return Err(()), 22 19 }; 23 - println!("{:#?}", did_doc); 20 + println!( 21 + " Found DID `{0}` hosted at `{1}`", 22 + did_doc.did, did_doc.pds 23 + ); 24 + 24 25 // resolve did+repoName to knotserver 26 + println!("Resolving @{0}/{1}", config.handle, config.repo_name); 27 + let knot_server = match knot::find_knot(&did_doc.did, &config.repo_name, &did_doc.pds) { 28 + Ok(val) => val, 29 + Err(_) => return Err(()), 30 + }; 31 + println!(" Found knot `{}`", knot_server); 25 32 26 33 // connect to /events on knotserver 34 + println!( 35 + "Connecting to `{}`", 36 + format!("wss://{}/events", knot_server) 37 + ); 27 38 28 - // on event: 29 - // parse json 30 - // validate meets expected schema (allow unknown vals) 31 - // filter by did and reponame 32 - // exec shell command in user shell (/bin/sh as fallback) 39 + match ws::connect(format!("ws://{}/events", knot_server), |_out| { 40 + println!(" Connection successful"); 41 + |msg: ws::Message| { 42 + // parse json 43 + let body = match json::parse(match msg.as_text() { 44 + Ok(val) => val, 45 + Err(_) => return Ok(()), 46 + }) { 47 + Ok(val) => val, 48 + Err(_) => { 49 + println!(" Invalid body. Skipping message"); 50 + return Ok(()); 51 + } 52 + }; 53 + 54 + // filter by did and reponame 55 + if let Some(repo_did) = body["event"]["repoDid"].as_str() 56 + && let Some(repo) = body["event"]["repoName"].as_str() 57 + && repo_did != &did_doc.did 58 + && repo != &config.repo_name 59 + { 60 + // repo doesnt match so skip 61 + return Ok(()); 62 + } 63 + 64 + println!(" Executing `/bin/sh -c {}`", &config.shell); 65 + 66 + // exec shell command in /bin/sh 67 + match Command::new("/bin/sh") 68 + .arg("-c") 69 + .arg(&config.shell) 70 + .output() 71 + { 72 + Ok(val) => val, 73 + Err(_) => return Ok(()), 74 + }; 75 + 76 + Ok(()) 77 + } 78 + }) { 79 + Ok(_) => {} 80 + Err(err) => println!(" Connection failed: {}", err), 81 + }; 33 82 34 83 return Ok(()); 35 84 }
+1 -1
tangled-on-commit.json
··· 1 1 { 2 2 "handle": "vielle.dev", 3 3 "repo_name": "tangled-on-commit", 4 - "shell": "./commit.sh" 4 + "shell": "echo \\\"Got a new commit\\!\\!\\\" 2>&1 > out.txt" 5 5 }