├── .dockerignore ├── .gitattributes ├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── bin ├── cancelbot-azure-rust-lang2.sh ├── cancelbot-rust.sh ├── run.sh ├── sync-github.sh └── sync-mailgun.sh ├── cancelbot ├── .gitignore ├── Cargo.toml └── src │ ├── appveyor.rs │ ├── azure.rs │ ├── errors.rs │ ├── http.rs │ ├── main.rs │ └── travis.rs ├── crontab ├── deploy.sh ├── homu.toml.template ├── naglist.txt ├── nginx.conf.template ├── nginx.tmp.conf ├── promote-release ├── Cargo.toml └── src │ └── main.rs ├── rbars ├── Cargo.toml └── src │ └── main.rs ├── run-dev.sh ├── run-prod.sh ├── secrets.toml.example └── tq ├── Cargo.toml └── src └── main.rs /.dockerignore: -------------------------------------------------------------------------------- 1 | data 2 | target 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.toml.template linguist-language=TOML 2 | *.toml.example linguist-language=TOML 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | data 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: minimal 2 | script: docker build -t rust-central-station . 3 | before_deploy: 4 | - pip install --user awscli; export PATH=$PATH:$HOME/.local/bin 5 | deploy: 6 | provider: script 7 | script: sh deploy.sh 8 | on: 9 | branch: master 10 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "adler" 5 | version = "0.2.3" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" 8 | 9 | [[package]] 10 | name = "aho-corasick" 11 | version = "0.7.15" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" 14 | dependencies = [ 15 | "memchr", 16 | ] 17 | 18 | [[package]] 19 | name = "autocfg" 20 | version = "0.1.7" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" 23 | 24 | [[package]] 25 | name = "autocfg" 26 | version = "1.0.0" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 29 | 30 | [[package]] 31 | name = "backtrace" 32 | version = "0.2.3" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "346d7644f0b5f9bc73082d3b2236b69a05fd35cce0cfa3724e184e6a5c9e2a2f" 35 | dependencies = [ 36 | "backtrace-sys", 37 | "cfg-if 0.1.10", 38 | "dbghelp-sys", 39 | "kernel32-sys", 40 | "libc", 41 | "rustc-demangle", 42 | "winapi 0.2.8", 43 | ] 44 | 45 | [[package]] 46 | name = "backtrace-sys" 47 | version = "0.1.37" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "18fbebbe1c9d1f383a9cc7e8ccdb471b91c8d024ee9c2ca5b5346121fe8b4399" 50 | dependencies = [ 51 | "cc", 52 | "libc", 53 | ] 54 | 55 | [[package]] 56 | name = "base64" 57 | version = "0.10.1" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" 60 | dependencies = [ 61 | "byteorder", 62 | ] 63 | 64 | [[package]] 65 | name = "bitflags" 66 | version = "1.2.1" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 69 | 70 | [[package]] 71 | name = "block-buffer" 72 | version = "0.7.3" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" 75 | dependencies = [ 76 | "block-padding", 77 | "byte-tools", 78 | "byteorder", 79 | "generic-array", 80 | ] 81 | 82 | [[package]] 83 | name = "block-padding" 84 | version = "0.1.5" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" 87 | dependencies = [ 88 | "byte-tools", 89 | ] 90 | 91 | [[package]] 92 | name = "byte-tools" 93 | version = "0.3.1" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" 96 | 97 | [[package]] 98 | name = "byteorder" 99 | version = "1.3.4" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 102 | 103 | [[package]] 104 | name = "bytes" 105 | version = "0.4.12" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" 108 | dependencies = [ 109 | "byteorder", 110 | "iovec", 111 | ] 112 | 113 | [[package]] 114 | name = "cancelbot" 115 | version = "0.1.0" 116 | dependencies = [ 117 | "base64", 118 | "curl", 119 | "error-chain", 120 | "futures", 121 | "getopts", 122 | "rustc-serialize", 123 | "time", 124 | "tokio-core", 125 | "tokio-curl", 126 | ] 127 | 128 | [[package]] 129 | name = "cc" 130 | version = "1.0.65" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15" 133 | 134 | [[package]] 135 | name = "cfg-if" 136 | version = "0.1.10" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 139 | 140 | [[package]] 141 | name = "cfg-if" 142 | version = "1.0.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 145 | 146 | [[package]] 147 | name = "cloudabi" 148 | version = "0.0.3" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 151 | dependencies = [ 152 | "bitflags", 153 | ] 154 | 155 | [[package]] 156 | name = "crc32fast" 157 | version = "1.2.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" 160 | dependencies = [ 161 | "cfg-if 1.0.0", 162 | ] 163 | 164 | [[package]] 165 | name = "crossbeam-deque" 166 | version = "0.7.3" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" 169 | dependencies = [ 170 | "crossbeam-epoch", 171 | "crossbeam-utils", 172 | "maybe-uninit", 173 | ] 174 | 175 | [[package]] 176 | name = "crossbeam-epoch" 177 | version = "0.8.2" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" 180 | dependencies = [ 181 | "autocfg 1.0.0", 182 | "cfg-if 0.1.10", 183 | "crossbeam-utils", 184 | "lazy_static", 185 | "maybe-uninit", 186 | "memoffset", 187 | "scopeguard", 188 | ] 189 | 190 | [[package]] 191 | name = "crossbeam-queue" 192 | version = "0.2.3" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" 195 | dependencies = [ 196 | "cfg-if 0.1.10", 197 | "crossbeam-utils", 198 | "maybe-uninit", 199 | ] 200 | 201 | [[package]] 202 | name = "crossbeam-utils" 203 | version = "0.7.2" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 206 | dependencies = [ 207 | "autocfg 1.0.0", 208 | "cfg-if 0.1.10", 209 | "lazy_static", 210 | ] 211 | 212 | [[package]] 213 | name = "curl" 214 | version = "0.4.34" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "e268162af1a5fe89917ae25ba3b0a77c8da752bdc58e7dbb4f15b91fbd33756e" 217 | dependencies = [ 218 | "curl-sys", 219 | "libc", 220 | "openssl-probe", 221 | "openssl-sys", 222 | "schannel", 223 | "socket2", 224 | "winapi 0.3.7", 225 | ] 226 | 227 | [[package]] 228 | name = "curl-sys" 229 | version = "0.4.38+curl-7.73.0" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "498ecfb4f59997fd40023d62a9f1e506e768b2baeb59a1d311eb9751cdcd7e3f" 232 | dependencies = [ 233 | "cc", 234 | "libc", 235 | "libz-sys", 236 | "openssl-sys", 237 | "pkg-config", 238 | "vcpkg", 239 | "winapi 0.3.7", 240 | ] 241 | 242 | [[package]] 243 | name = "dbghelp-sys" 244 | version = "0.2.0" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" 247 | dependencies = [ 248 | "winapi 0.2.8", 249 | "winapi-build", 250 | ] 251 | 252 | [[package]] 253 | name = "digest" 254 | version = "0.8.1" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" 257 | dependencies = [ 258 | "generic-array", 259 | ] 260 | 261 | [[package]] 262 | name = "error-chain" 263 | version = "0.5.0" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "bd5c82c815138e278b8dcdeffc49f27ea6ffb528403e9dea4194f2e3dd40b143" 266 | dependencies = [ 267 | "backtrace", 268 | ] 269 | 270 | [[package]] 271 | name = "fake-simd" 272 | version = "0.1.2" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" 275 | 276 | [[package]] 277 | name = "filetime" 278 | version = "0.2.13" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe" 281 | dependencies = [ 282 | "cfg-if 1.0.0", 283 | "libc", 284 | "redox_syscall", 285 | "winapi 0.3.7", 286 | ] 287 | 288 | [[package]] 289 | name = "flate2" 290 | version = "1.0.19" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129" 293 | dependencies = [ 294 | "cfg-if 1.0.0", 295 | "crc32fast", 296 | "libc", 297 | "miniz_oxide", 298 | ] 299 | 300 | [[package]] 301 | name = "fnv" 302 | version = "1.0.7" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 305 | 306 | [[package]] 307 | name = "fs2" 308 | version = "0.4.3" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" 311 | dependencies = [ 312 | "libc", 313 | "winapi 0.3.7", 314 | ] 315 | 316 | [[package]] 317 | name = "fuchsia-cprng" 318 | version = "0.1.1" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 321 | 322 | [[package]] 323 | name = "fuchsia-zircon" 324 | version = "0.3.3" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 327 | dependencies = [ 328 | "bitflags", 329 | "fuchsia-zircon-sys", 330 | ] 331 | 332 | [[package]] 333 | name = "fuchsia-zircon-sys" 334 | version = "0.3.3" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 337 | 338 | [[package]] 339 | name = "futures" 340 | version = "0.1.30" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "4c7e4c2612746b0df8fed4ce0c69156021b704c9aefa360311c04e6e9e002eed" 343 | 344 | [[package]] 345 | name = "generic-array" 346 | version = "0.12.3" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" 349 | dependencies = [ 350 | "typenum", 351 | ] 352 | 353 | [[package]] 354 | name = "getopts" 355 | version = "0.2.21" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" 358 | dependencies = [ 359 | "unicode-width", 360 | ] 361 | 362 | [[package]] 363 | name = "handlebars" 364 | version = "1.1.0" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "d82e5750d8027a97b9640e3fefa66bbaf852a35228e1c90790efd13c4b09c166" 367 | dependencies = [ 368 | "lazy_static", 369 | "log", 370 | "pest", 371 | "pest_derive", 372 | "quick-error", 373 | "regex", 374 | "serde", 375 | "serde_json", 376 | "walkdir", 377 | ] 378 | 379 | [[package]] 380 | name = "hermit-abi" 381 | version = "0.1.17" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" 384 | dependencies = [ 385 | "libc", 386 | ] 387 | 388 | [[package]] 389 | name = "iovec" 390 | version = "0.1.4" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 393 | dependencies = [ 394 | "libc", 395 | ] 396 | 397 | [[package]] 398 | name = "itoa" 399 | version = "0.4.6" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 402 | 403 | [[package]] 404 | name = "kernel32-sys" 405 | version = "0.2.2" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 408 | dependencies = [ 409 | "winapi 0.2.8", 410 | "winapi-build", 411 | ] 412 | 413 | [[package]] 414 | name = "lazy_static" 415 | version = "1.4.0" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 418 | 419 | [[package]] 420 | name = "libc" 421 | version = "0.2.80" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" 424 | 425 | [[package]] 426 | name = "libz-sys" 427 | version = "1.1.2" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655" 430 | dependencies = [ 431 | "cc", 432 | "libc", 433 | "pkg-config", 434 | "vcpkg", 435 | ] 436 | 437 | [[package]] 438 | name = "lock_api" 439 | version = "0.3.4" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" 442 | dependencies = [ 443 | "scopeguard", 444 | ] 445 | 446 | [[package]] 447 | name = "log" 448 | version = "0.4.11" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 451 | dependencies = [ 452 | "cfg-if 0.1.10", 453 | ] 454 | 455 | [[package]] 456 | name = "lzma-sys" 457 | version = "0.1.17" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "bdb4b7c3eddad11d3af9e86c487607d2d2442d185d848575365c4856ba96d619" 460 | dependencies = [ 461 | "cc", 462 | "libc", 463 | "pkg-config", 464 | ] 465 | 466 | [[package]] 467 | name = "maplit" 468 | version = "1.0.2" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" 471 | 472 | [[package]] 473 | name = "maybe-uninit" 474 | version = "2.0.0" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 477 | 478 | [[package]] 479 | name = "memchr" 480 | version = "2.3.4" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 483 | 484 | [[package]] 485 | name = "memoffset" 486 | version = "0.5.6" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" 489 | dependencies = [ 490 | "autocfg 1.0.0", 491 | ] 492 | 493 | [[package]] 494 | name = "miniz_oxide" 495 | version = "0.4.3" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" 498 | dependencies = [ 499 | "adler", 500 | "autocfg 1.0.0", 501 | ] 502 | 503 | [[package]] 504 | name = "mio" 505 | version = "0.6.22" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" 508 | dependencies = [ 509 | "cfg-if 0.1.10", 510 | "fuchsia-zircon", 511 | "fuchsia-zircon-sys", 512 | "iovec", 513 | "kernel32-sys", 514 | "libc", 515 | "log", 516 | "miow", 517 | "net2", 518 | "slab", 519 | "winapi 0.2.8", 520 | ] 521 | 522 | [[package]] 523 | name = "mio-uds" 524 | version = "0.6.8" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" 527 | dependencies = [ 528 | "iovec", 529 | "libc", 530 | "mio", 531 | ] 532 | 533 | [[package]] 534 | name = "miow" 535 | version = "0.2.1" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 538 | dependencies = [ 539 | "kernel32-sys", 540 | "net2", 541 | "winapi 0.2.8", 542 | "ws2_32-sys", 543 | ] 544 | 545 | [[package]] 546 | name = "net2" 547 | version = "0.2.35" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" 550 | dependencies = [ 551 | "cfg-if 0.1.10", 552 | "libc", 553 | "winapi 0.3.7", 554 | ] 555 | 556 | [[package]] 557 | name = "num_cpus" 558 | version = "1.13.0" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 561 | dependencies = [ 562 | "hermit-abi", 563 | "libc", 564 | ] 565 | 566 | [[package]] 567 | name = "opaque-debug" 568 | version = "0.2.3" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" 571 | 572 | [[package]] 573 | name = "openssl-probe" 574 | version = "0.1.2" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" 577 | 578 | [[package]] 579 | name = "openssl-sys" 580 | version = "0.9.58" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" 583 | dependencies = [ 584 | "autocfg 1.0.0", 585 | "cc", 586 | "libc", 587 | "pkg-config", 588 | "vcpkg", 589 | ] 590 | 591 | [[package]] 592 | name = "parking_lot" 593 | version = "0.9.0" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" 596 | dependencies = [ 597 | "lock_api", 598 | "parking_lot_core", 599 | "rustc_version", 600 | ] 601 | 602 | [[package]] 603 | name = "parking_lot_core" 604 | version = "0.6.2" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" 607 | dependencies = [ 608 | "cfg-if 0.1.10", 609 | "cloudabi", 610 | "libc", 611 | "redox_syscall", 612 | "rustc_version", 613 | "smallvec", 614 | "winapi 0.3.7", 615 | ] 616 | 617 | [[package]] 618 | name = "pest" 619 | version = "2.1.3" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" 622 | dependencies = [ 623 | "ucd-trie", 624 | ] 625 | 626 | [[package]] 627 | name = "pest_derive" 628 | version = "2.1.0" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" 631 | dependencies = [ 632 | "pest", 633 | "pest_generator", 634 | ] 635 | 636 | [[package]] 637 | name = "pest_generator" 638 | version = "2.1.3" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" 641 | dependencies = [ 642 | "pest", 643 | "pest_meta", 644 | "proc-macro2", 645 | "quote", 646 | "syn", 647 | ] 648 | 649 | [[package]] 650 | name = "pest_meta" 651 | version = "2.1.3" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" 654 | dependencies = [ 655 | "maplit", 656 | "pest", 657 | "sha-1", 658 | ] 659 | 660 | [[package]] 661 | name = "pkg-config" 662 | version = "0.3.19" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 665 | 666 | [[package]] 667 | name = "proc-macro2" 668 | version = "1.0.24" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 671 | dependencies = [ 672 | "unicode-xid", 673 | ] 674 | 675 | [[package]] 676 | name = "promote-release" 677 | version = "0.1.0" 678 | dependencies = [ 679 | "curl", 680 | "flate2", 681 | "fs2", 682 | "rand", 683 | "serde_json", 684 | "tar", 685 | "toml", 686 | "xz2", 687 | ] 688 | 689 | [[package]] 690 | name = "quick-error" 691 | version = "1.2.3" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 694 | 695 | [[package]] 696 | name = "quote" 697 | version = "1.0.7" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 700 | dependencies = [ 701 | "proc-macro2", 702 | ] 703 | 704 | [[package]] 705 | name = "rand" 706 | version = "0.6.5" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 709 | dependencies = [ 710 | "autocfg 0.1.7", 711 | "libc", 712 | "rand_chacha", 713 | "rand_core 0.4.0", 714 | "rand_hc", 715 | "rand_isaac", 716 | "rand_jitter", 717 | "rand_os", 718 | "rand_pcg", 719 | "rand_xorshift", 720 | "winapi 0.3.7", 721 | ] 722 | 723 | [[package]] 724 | name = "rand_chacha" 725 | version = "0.1.1" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 728 | dependencies = [ 729 | "autocfg 0.1.7", 730 | "rand_core 0.3.1", 731 | ] 732 | 733 | [[package]] 734 | name = "rand_core" 735 | version = "0.3.1" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 738 | dependencies = [ 739 | "rand_core 0.4.0", 740 | ] 741 | 742 | [[package]] 743 | name = "rand_core" 744 | version = "0.4.0" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" 747 | 748 | [[package]] 749 | name = "rand_hc" 750 | version = "0.1.0" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 753 | dependencies = [ 754 | "rand_core 0.3.1", 755 | ] 756 | 757 | [[package]] 758 | name = "rand_isaac" 759 | version = "0.1.1" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 762 | dependencies = [ 763 | "rand_core 0.3.1", 764 | ] 765 | 766 | [[package]] 767 | name = "rand_jitter" 768 | version = "0.1.4" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" 771 | dependencies = [ 772 | "libc", 773 | "rand_core 0.4.0", 774 | "winapi 0.3.7", 775 | ] 776 | 777 | [[package]] 778 | name = "rand_os" 779 | version = "0.1.3" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 782 | dependencies = [ 783 | "cloudabi", 784 | "fuchsia-cprng", 785 | "libc", 786 | "rand_core 0.4.0", 787 | "rdrand", 788 | "winapi 0.3.7", 789 | ] 790 | 791 | [[package]] 792 | name = "rand_pcg" 793 | version = "0.1.2" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 796 | dependencies = [ 797 | "autocfg 0.1.7", 798 | "rand_core 0.4.0", 799 | ] 800 | 801 | [[package]] 802 | name = "rand_xorshift" 803 | version = "0.1.1" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 806 | dependencies = [ 807 | "rand_core 0.3.1", 808 | ] 809 | 810 | [[package]] 811 | name = "rbars" 812 | version = "0.1.0" 813 | dependencies = [ 814 | "handlebars", 815 | "serde_json", 816 | "toml", 817 | ] 818 | 819 | [[package]] 820 | name = "rdrand" 821 | version = "0.4.0" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 824 | dependencies = [ 825 | "rand_core 0.3.1", 826 | ] 827 | 828 | [[package]] 829 | name = "redox_syscall" 830 | version = "0.1.57" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 833 | 834 | [[package]] 835 | name = "regex" 836 | version = "1.4.2" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" 839 | dependencies = [ 840 | "aho-corasick", 841 | "memchr", 842 | "regex-syntax", 843 | "thread_local", 844 | ] 845 | 846 | [[package]] 847 | name = "regex-syntax" 848 | version = "0.6.21" 849 | source = "registry+https://github.com/rust-lang/crates.io-index" 850 | checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" 851 | 852 | [[package]] 853 | name = "rustc-demangle" 854 | version = "0.1.18" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" 857 | 858 | [[package]] 859 | name = "rustc-serialize" 860 | version = "0.3.24" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 863 | 864 | [[package]] 865 | name = "rustc_version" 866 | version = "0.2.3" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 869 | dependencies = [ 870 | "semver", 871 | ] 872 | 873 | [[package]] 874 | name = "ryu" 875 | version = "1.0.5" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 878 | 879 | [[package]] 880 | name = "same-file" 881 | version = "1.0.6" 882 | source = "registry+https://github.com/rust-lang/crates.io-index" 883 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 884 | dependencies = [ 885 | "winapi-util", 886 | ] 887 | 888 | [[package]] 889 | name = "schannel" 890 | version = "0.1.19" 891 | source = "registry+https://github.com/rust-lang/crates.io-index" 892 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 893 | dependencies = [ 894 | "lazy_static", 895 | "winapi 0.3.7", 896 | ] 897 | 898 | [[package]] 899 | name = "scoped-tls" 900 | version = "0.1.2" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" 903 | 904 | [[package]] 905 | name = "scopeguard" 906 | version = "1.1.0" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 909 | 910 | [[package]] 911 | name = "semver" 912 | version = "0.9.0" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 915 | dependencies = [ 916 | "semver-parser", 917 | ] 918 | 919 | [[package]] 920 | name = "semver-parser" 921 | version = "0.7.0" 922 | source = "registry+https://github.com/rust-lang/crates.io-index" 923 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 924 | 925 | [[package]] 926 | name = "serde" 927 | version = "1.0.117" 928 | source = "registry+https://github.com/rust-lang/crates.io-index" 929 | checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" 930 | 931 | [[package]] 932 | name = "serde_json" 933 | version = "1.0.59" 934 | source = "registry+https://github.com/rust-lang/crates.io-index" 935 | checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" 936 | dependencies = [ 937 | "itoa", 938 | "ryu", 939 | "serde", 940 | ] 941 | 942 | [[package]] 943 | name = "sha-1" 944 | version = "0.8.2" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" 947 | dependencies = [ 948 | "block-buffer", 949 | "digest", 950 | "fake-simd", 951 | "opaque-debug", 952 | ] 953 | 954 | [[package]] 955 | name = "slab" 956 | version = "0.4.2" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 959 | 960 | [[package]] 961 | name = "smallvec" 962 | version = "0.6.13" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" 965 | dependencies = [ 966 | "maybe-uninit", 967 | ] 968 | 969 | [[package]] 970 | name = "socket2" 971 | version = "0.3.17" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "2c29947abdee2a218277abeca306f25789c938e500ea5a9d4b12a5a504466902" 974 | dependencies = [ 975 | "cfg-if 1.0.0", 976 | "libc", 977 | "redox_syscall", 978 | "winapi 0.3.7", 979 | ] 980 | 981 | [[package]] 982 | name = "syn" 983 | version = "1.0.51" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "3b4f34193997d92804d359ed09953e25d5138df6bcc055a71bf68ee89fdf9223" 986 | dependencies = [ 987 | "proc-macro2", 988 | "quote", 989 | "unicode-xid", 990 | ] 991 | 992 | [[package]] 993 | name = "tar" 994 | version = "0.4.30" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "489997b7557e9a43e192c527face4feacc78bfbe6eed67fd55c4c9e381cba290" 997 | dependencies = [ 998 | "filetime", 999 | "libc", 1000 | "redox_syscall", 1001 | "xattr", 1002 | ] 1003 | 1004 | [[package]] 1005 | name = "thread_local" 1006 | version = "1.0.1" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 1009 | dependencies = [ 1010 | "lazy_static", 1011 | ] 1012 | 1013 | [[package]] 1014 | name = "time" 1015 | version = "0.1.44" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 1018 | dependencies = [ 1019 | "libc", 1020 | "wasi", 1021 | "winapi 0.3.7", 1022 | ] 1023 | 1024 | [[package]] 1025 | name = "tokio" 1026 | version = "0.1.22" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" 1029 | dependencies = [ 1030 | "bytes", 1031 | "futures", 1032 | "mio", 1033 | "num_cpus", 1034 | "tokio-codec", 1035 | "tokio-current-thread", 1036 | "tokio-executor", 1037 | "tokio-fs", 1038 | "tokio-io", 1039 | "tokio-reactor", 1040 | "tokio-sync", 1041 | "tokio-tcp", 1042 | "tokio-threadpool", 1043 | "tokio-timer", 1044 | "tokio-udp", 1045 | "tokio-uds", 1046 | ] 1047 | 1048 | [[package]] 1049 | name = "tokio-codec" 1050 | version = "0.1.2" 1051 | source = "registry+https://github.com/rust-lang/crates.io-index" 1052 | checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" 1053 | dependencies = [ 1054 | "bytes", 1055 | "futures", 1056 | "tokio-io", 1057 | ] 1058 | 1059 | [[package]] 1060 | name = "tokio-core" 1061 | version = "0.1.17" 1062 | source = "registry+https://github.com/rust-lang/crates.io-index" 1063 | checksum = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" 1064 | dependencies = [ 1065 | "bytes", 1066 | "futures", 1067 | "iovec", 1068 | "log", 1069 | "mio", 1070 | "scoped-tls", 1071 | "tokio", 1072 | "tokio-executor", 1073 | "tokio-io", 1074 | "tokio-reactor", 1075 | "tokio-timer", 1076 | ] 1077 | 1078 | [[package]] 1079 | name = "tokio-curl" 1080 | version = "0.1.11" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "b6d237aa125c29d772c3c650d073a58dd66782eebf27bd9172304e38f3481da8" 1083 | dependencies = [ 1084 | "curl", 1085 | "futures", 1086 | "libc", 1087 | "log", 1088 | "mio", 1089 | "scoped-tls", 1090 | "slab", 1091 | "tokio-core", 1092 | "tokio-io", 1093 | "winapi 0.3.7", 1094 | ] 1095 | 1096 | [[package]] 1097 | name = "tokio-current-thread" 1098 | version = "0.1.7" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" 1101 | dependencies = [ 1102 | "futures", 1103 | "tokio-executor", 1104 | ] 1105 | 1106 | [[package]] 1107 | name = "tokio-executor" 1108 | version = "0.1.10" 1109 | source = "registry+https://github.com/rust-lang/crates.io-index" 1110 | checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" 1111 | dependencies = [ 1112 | "crossbeam-utils", 1113 | "futures", 1114 | ] 1115 | 1116 | [[package]] 1117 | name = "tokio-fs" 1118 | version = "0.1.7" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" 1121 | dependencies = [ 1122 | "futures", 1123 | "tokio-io", 1124 | "tokio-threadpool", 1125 | ] 1126 | 1127 | [[package]] 1128 | name = "tokio-io" 1129 | version = "0.1.13" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" 1132 | dependencies = [ 1133 | "bytes", 1134 | "futures", 1135 | "log", 1136 | ] 1137 | 1138 | [[package]] 1139 | name = "tokio-reactor" 1140 | version = "0.1.12" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" 1143 | dependencies = [ 1144 | "crossbeam-utils", 1145 | "futures", 1146 | "lazy_static", 1147 | "log", 1148 | "mio", 1149 | "num_cpus", 1150 | "parking_lot", 1151 | "slab", 1152 | "tokio-executor", 1153 | "tokio-io", 1154 | "tokio-sync", 1155 | ] 1156 | 1157 | [[package]] 1158 | name = "tokio-sync" 1159 | version = "0.1.8" 1160 | source = "registry+https://github.com/rust-lang/crates.io-index" 1161 | checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" 1162 | dependencies = [ 1163 | "fnv", 1164 | "futures", 1165 | ] 1166 | 1167 | [[package]] 1168 | name = "tokio-tcp" 1169 | version = "0.1.4" 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" 1171 | checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" 1172 | dependencies = [ 1173 | "bytes", 1174 | "futures", 1175 | "iovec", 1176 | "mio", 1177 | "tokio-io", 1178 | "tokio-reactor", 1179 | ] 1180 | 1181 | [[package]] 1182 | name = "tokio-threadpool" 1183 | version = "0.1.18" 1184 | source = "registry+https://github.com/rust-lang/crates.io-index" 1185 | checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" 1186 | dependencies = [ 1187 | "crossbeam-deque", 1188 | "crossbeam-queue", 1189 | "crossbeam-utils", 1190 | "futures", 1191 | "lazy_static", 1192 | "log", 1193 | "num_cpus", 1194 | "slab", 1195 | "tokio-executor", 1196 | ] 1197 | 1198 | [[package]] 1199 | name = "tokio-timer" 1200 | version = "0.2.13" 1201 | source = "registry+https://github.com/rust-lang/crates.io-index" 1202 | checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" 1203 | dependencies = [ 1204 | "crossbeam-utils", 1205 | "futures", 1206 | "slab", 1207 | "tokio-executor", 1208 | ] 1209 | 1210 | [[package]] 1211 | name = "tokio-udp" 1212 | version = "0.1.6" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" 1215 | dependencies = [ 1216 | "bytes", 1217 | "futures", 1218 | "log", 1219 | "mio", 1220 | "tokio-codec", 1221 | "tokio-io", 1222 | "tokio-reactor", 1223 | ] 1224 | 1225 | [[package]] 1226 | name = "tokio-uds" 1227 | version = "0.2.7" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" 1230 | dependencies = [ 1231 | "bytes", 1232 | "futures", 1233 | "iovec", 1234 | "libc", 1235 | "log", 1236 | "mio", 1237 | "mio-uds", 1238 | "tokio-codec", 1239 | "tokio-io", 1240 | "tokio-reactor", 1241 | ] 1242 | 1243 | [[package]] 1244 | name = "toml" 1245 | version = "0.4.10" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" 1248 | dependencies = [ 1249 | "serde", 1250 | ] 1251 | 1252 | [[package]] 1253 | name = "tq" 1254 | version = "0.1.0" 1255 | dependencies = [ 1256 | "toml", 1257 | ] 1258 | 1259 | [[package]] 1260 | name = "typenum" 1261 | version = "1.12.0" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 1264 | 1265 | [[package]] 1266 | name = "ucd-trie" 1267 | version = "0.1.3" 1268 | source = "registry+https://github.com/rust-lang/crates.io-index" 1269 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" 1270 | 1271 | [[package]] 1272 | name = "unicode-width" 1273 | version = "0.1.8" 1274 | source = "registry+https://github.com/rust-lang/crates.io-index" 1275 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 1276 | 1277 | [[package]] 1278 | name = "unicode-xid" 1279 | version = "0.2.1" 1280 | source = "registry+https://github.com/rust-lang/crates.io-index" 1281 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1282 | 1283 | [[package]] 1284 | name = "vcpkg" 1285 | version = "0.2.10" 1286 | source = "registry+https://github.com/rust-lang/crates.io-index" 1287 | checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" 1288 | 1289 | [[package]] 1290 | name = "walkdir" 1291 | version = "2.3.1" 1292 | source = "registry+https://github.com/rust-lang/crates.io-index" 1293 | checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" 1294 | dependencies = [ 1295 | "same-file", 1296 | "winapi 0.3.7", 1297 | "winapi-util", 1298 | ] 1299 | 1300 | [[package]] 1301 | name = "wasi" 1302 | version = "0.10.0+wasi-snapshot-preview1" 1303 | source = "registry+https://github.com/rust-lang/crates.io-index" 1304 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1305 | 1306 | [[package]] 1307 | name = "winapi" 1308 | version = "0.2.8" 1309 | source = "registry+https://github.com/rust-lang/crates.io-index" 1310 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1311 | 1312 | [[package]] 1313 | name = "winapi" 1314 | version = "0.3.7" 1315 | source = "registry+https://github.com/rust-lang/crates.io-index" 1316 | checksum = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" 1317 | dependencies = [ 1318 | "winapi-i686-pc-windows-gnu", 1319 | "winapi-x86_64-pc-windows-gnu", 1320 | ] 1321 | 1322 | [[package]] 1323 | name = "winapi-build" 1324 | version = "0.1.1" 1325 | source = "registry+https://github.com/rust-lang/crates.io-index" 1326 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1327 | 1328 | [[package]] 1329 | name = "winapi-i686-pc-windows-gnu" 1330 | version = "0.4.0" 1331 | source = "registry+https://github.com/rust-lang/crates.io-index" 1332 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1333 | 1334 | [[package]] 1335 | name = "winapi-util" 1336 | version = "0.1.5" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1339 | dependencies = [ 1340 | "winapi 0.3.7", 1341 | ] 1342 | 1343 | [[package]] 1344 | name = "winapi-x86_64-pc-windows-gnu" 1345 | version = "0.4.0" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1348 | 1349 | [[package]] 1350 | name = "ws2_32-sys" 1351 | version = "0.2.1" 1352 | source = "registry+https://github.com/rust-lang/crates.io-index" 1353 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 1354 | dependencies = [ 1355 | "winapi 0.2.8", 1356 | "winapi-build", 1357 | ] 1358 | 1359 | [[package]] 1360 | name = "xattr" 1361 | version = "0.2.2" 1362 | source = "registry+https://github.com/rust-lang/crates.io-index" 1363 | checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" 1364 | dependencies = [ 1365 | "libc", 1366 | ] 1367 | 1368 | [[package]] 1369 | name = "xz2" 1370 | version = "0.1.6" 1371 | source = "registry+https://github.com/rust-lang/crates.io-index" 1372 | checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c" 1373 | dependencies = [ 1374 | "lzma-sys", 1375 | ] 1376 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "cancelbot", 4 | "promote-release", 5 | "rbars", 6 | "tq", 7 | ] 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | RUN apt-get update -y && \ 4 | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 5 | g++ \ 6 | curl \ 7 | ca-certificates \ 8 | libc6-dev \ 9 | make \ 10 | libssl-dev \ 11 | pkg-config \ 12 | python \ 13 | python3-venv \ 14 | python3-pip \ 15 | python3-setuptools \ 16 | git \ 17 | nginx \ 18 | letsencrypt \ 19 | cron \ 20 | ssh \ 21 | gnupg \ 22 | cmake \ 23 | logrotate \ 24 | file \ 25 | ssmtp \ 26 | locales \ 27 | zlib1g-dev \ 28 | ninja-build 29 | 30 | # Set the system locales 31 | RUN locale-gen en_US.UTF-8 32 | ENV LANG en_US.UTF-8 33 | ENV LANGUAGE en_US:en 34 | ENV LC_ALL en_US.UTF-8 35 | 36 | # Install Rust and Cargo 37 | RUN curl https://sh.rustup.rs | sh -s -- -y 38 | ENV PATH=$PATH:/root/.cargo/bin 39 | 40 | # Install homu, our integration daemon 41 | RUN git clone https://github.com/rust-lang/homu /homu && \ 42 | cd /homu && git reset --hard 0527db142c19b12df08d9a3d3e3bd566e36c0c22 43 | RUN pip3 install -e /homu 44 | 45 | # Install local programs used: 46 | # 47 | # * tq - a command line 'toml query' program, used to extract data from 48 | # secrets.toml 49 | # * rbars - a command line program to run a handlebars file through a toml 50 | # configuration, in our case used to expand templates using the values 51 | # in secrets.toml 52 | # * promote-release - cron job to download artifacts from travis/appveyor 53 | # archives and publish them (also generate manifests) 54 | # * cancelbot - bot that cancels AppVeyor/Travis builds if we don't need them. 55 | # This is how we keep a manageable queue on the two services 56 | COPY tq /tmp/tq 57 | RUN cargo install --path /tmp/tq && rm -rf /tmp/tq 58 | COPY rbars /tmp/rbars 59 | RUN cargo install --path /tmp/rbars && rm -rf /tmp/rbars 60 | COPY promote-release /tmp/promote-release 61 | RUN cargo install --path /tmp/promote-release && rm -rf /tmp/promote-release 62 | COPY cancelbot /tmp/cancelbot 63 | RUN cargo install --path /tmp/cancelbot && rm -rf /tmp/cancelbot 64 | 65 | # Install commands used by promote-release binary. The awscli package is used to 66 | # issue cloudfront invalidations. 67 | RUN pip3 install awscli 68 | RUN aws configure set preview.cloudfront true 69 | 70 | # Install our crontab which runs our various services on timers 71 | ADD crontab /etc/cron.d/rcs 72 | RUN chmod 0644 /etc/cron.d/rcs 73 | 74 | # Initialize our known set of ssh hosts so git doesn't prompt us later. 75 | RUN mkdir /root/.ssh && ssh-keyscan github.com >> /root/.ssh/known_hosts 76 | 77 | # Copy the source directory into the image so we can run scripts and template 78 | # configs from there 79 | COPY . /src/ 80 | 81 | CMD ["/src/bin/run.sh"] 82 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Archived repository! 2 | 3 | This repository was the home of some of the most important services hosted by 4 | the Rust Infrastructure Team, but over time those services were moved to new 5 | homes: 6 | 7 | * The release process is now developed and deployed in [rust-lang/promote-release]. 8 | * Bors is now developed, configured and deployed in [rust-lang/homu]. 9 | * Team synchronization is now developed and deployed in [rust-lang/sync-team]. 10 | * Cancelbot is not in use anymore. 11 | 12 | [rust-lang/promote-release]: https://github.com/rust-lang/promote-release 13 | [rust-lang/homu]: https://github.com/rust-lang/homu 14 | [rust-lang/sync-team]: https://github.com/rust-lang/sync-team 15 | 16 | # Rust Central Station 17 | 18 | Or otherwise just another name for the old buildmaster. 19 | 20 | This repo is hooked up to an automated docker build 21 | 22 | * https://hub.docker.com/r/alexcrichton/rust-central-station/ 23 | 24 | On the destination machine you can run it as: 25 | 26 | ./run-prod.sh 27 | 28 | Services currently provided are: 29 | 30 | * cancelbot for rust-lang/rust 31 | * cancelbot for rust-lang/cargo 32 | * homu 33 | * nginx in front of homu 34 | * ssl via letsencrypt 35 | 36 | Future services 37 | 38 | * signing Rust releases 39 | 40 | ## Architecture 41 | 42 | This is intended to be run as a container on the destination server, so the 43 | container here specifies everything about what's being run. 44 | 45 | * Secrets are stored in `secrets.toml` next to `secrets.toml.example` and are 46 | shared with the container. 47 | * Programs are provided in the container (`tq` and `rbars`) which will read the 48 | TOML configuration for use in shell scripts. 49 | * Everything pipes output to `logger` to collect output 50 | * Services are just run as simple daemons, not a lot of management. 51 | -------------------------------------------------------------------------------- /bin/cancelbot-azure-rust-lang2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | secrets=/data/secrets.toml 6 | 7 | exec cancelbot \ 8 | --azure-pipelines-token `tq cancelbot.azure-pipelines-2-token < $secrets` \ 9 | --azure-pipelines-org rust-lang2 \ 10 | --branch auto \ 11 | rust-lang/libc \ 12 | rust-lang/stdarch 13 | -------------------------------------------------------------------------------- /bin/cancelbot-rust.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | secrets=/data/secrets.toml 6 | 7 | exec cancelbot \ 8 | --azure-pipelines-token `tq cancelbot.azure-pipelines-token < $secrets` \ 9 | --azure-pipelines-org rust-lang \ 10 | --branch auto \ 11 | rust-lang-ci/rust 12 | -------------------------------------------------------------------------------- /bin/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | secrets=/data/secrets.toml 6 | 7 | # We mounted /var/log from a local log dir, but ubuntu expects it to be owned by 8 | # root:syslog, so change it here 9 | #chown root:syslog /var/log 10 | touch /var/log/cron.log 11 | 12 | # Background daemons we use here 13 | cron 14 | 15 | export RUST_BACKTRACE=1 16 | 17 | set -ex 18 | 19 | # Generate an initial letsencrypt certificate if one isn't already available. 20 | if [ -z "$DEV" ]; then 21 | if [ ! -d /etc/letsencrypt/renewal ]; then 22 | nginx -c /src/nginx.tmp.conf 23 | 24 | letsencrypt certonly \ 25 | --webroot \ 26 | --agree-tos \ 27 | -m `tq nginx.email < $secrets` \ 28 | -w /usr/share/nginx/html \ 29 | -d `tq nginx.hostname < $secrets`,`tq nginx.hostname_alias < $secrets` 30 | 31 | nginx -s stop 32 | fi 33 | 34 | # Configure/run nginx 35 | rbars $secrets /src/nginx.conf.template > /tmp/nginx.conf 36 | nginx -c /tmp/nginx.conf 37 | fi 38 | 39 | # Import the GPG key that's specified in the secrets file 40 | gpg --batch --import `tq dist.gpg-key < $secrets` 41 | 42 | # Configure and run homu 43 | rbars $secrets /src/homu.toml.template > /tmp/homu.toml 44 | homu -v -c /tmp/homu.toml 2>&1 | logger --tag homu 45 | -------------------------------------------------------------------------------- /bin/sync-github.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | export GITHUB_TOKEN="$(tq sync-github.token < /data/secrets.toml)" 5 | export RUST_LOG=sync_github=debug 6 | exec run-on-change https://team-api.infra.rust-lang.org/v1/teams.json sync-github --live 7 | -------------------------------------------------------------------------------- /bin/sync-mailgun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | export MAILGUN_API_TOKEN="$(tq mailgun.token < /data/secrets.toml)" 5 | export RUST_LOG=mailgun_mailmap=debug 6 | exec run-on-change https://team-api.infra.rust-lang.org/v1/lists.json sync-mailgun 7 | -------------------------------------------------------------------------------- /cancelbot/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /cancelbot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cancelbot" 3 | version = "0.1.0" 4 | authors = ["Alex Crichton "] 5 | license = "MIT OR Apache-2.0" 6 | 7 | [dependencies] 8 | base64 = "0.10" 9 | curl = "0.4" 10 | error-chain = "0.5" 11 | futures = "0.1" 12 | getopts = "0.2" 13 | rustc-serialize = "0.3" 14 | time = "0.1" 15 | tokio-core = "0.1" 16 | tokio-curl = "0.1" 17 | -------------------------------------------------------------------------------- /cancelbot/src/appveyor.rs: -------------------------------------------------------------------------------- 1 | #![allow(bad_style)] 2 | 3 | #[derive(RustcDecodable, Debug)] 4 | pub struct History { 5 | pub project: Project, 6 | pub builds: Vec, 7 | } 8 | 9 | #[derive(RustcDecodable, Debug)] 10 | pub struct Project { 11 | pub projectId: u32, 12 | pub accountId: u32, 13 | pub accountName: String, 14 | pub name: String, 15 | pub slug: String, 16 | pub repositoryName: String, 17 | pub repositoryType: String, 18 | } 19 | 20 | #[derive(RustcDecodable, Debug)] 21 | pub struct Build { 22 | pub buildId: u32, 23 | pub jobs: Vec, 24 | pub buildNumber: u32, 25 | pub version: String, 26 | pub message: String, 27 | pub branch: String, 28 | pub commitId: String, 29 | pub status: String, 30 | pub started: Option, 31 | pub finished: Option, 32 | pub created: String, 33 | pub updated: Option, 34 | } 35 | 36 | #[derive(RustcDecodable, Debug)] 37 | pub struct Job { 38 | pub jobId: String, 39 | pub status: String, 40 | } 41 | 42 | #[derive(RustcDecodable, Debug)] 43 | pub struct LastBuild { 44 | pub build: Build, 45 | } 46 | -------------------------------------------------------------------------------- /cancelbot/src/azure.rs: -------------------------------------------------------------------------------- 1 | #![allow(bad_style)] 2 | 3 | #[derive(RustcDecodable, Debug)] 4 | pub struct List { 5 | pub value: Vec, 6 | } 7 | 8 | #[derive(RustcDecodable, Debug, Clone)] 9 | pub struct Build { 10 | pub id: u32, 11 | pub status: String, 12 | pub _links: BuildLinks, 13 | } 14 | 15 | #[derive(RustcDecodable, Debug, Clone)] 16 | pub struct BuildLinks { 17 | pub timeline: Link, 18 | } 19 | 20 | #[derive(RustcDecodable, Debug, Clone)] 21 | pub struct Link { 22 | pub href: String, 23 | } 24 | 25 | #[derive(RustcDecodable, Debug)] 26 | pub struct Timeline { 27 | pub records: Vec, 28 | } 29 | 30 | #[derive(RustcDecodable, Debug)] 31 | pub struct Record { 32 | pub name: String, 33 | pub result: Option, 34 | pub r#type: String, 35 | } 36 | -------------------------------------------------------------------------------- /cancelbot/src/errors.rs: -------------------------------------------------------------------------------- 1 | #![allow(deprecated)] 2 | 3 | use std::io; 4 | use std::str; 5 | 6 | use curl; 7 | use rustc_serialize::json; 8 | use tokio_curl; 9 | 10 | error_chain! { 11 | types { 12 | BorsError, BorsErrorKind, BorsChainErr, BorsResult; 13 | } 14 | 15 | foreign_links { 16 | curl::Error, Curl; 17 | tokio_curl::PerformError, TokioCurl; 18 | json::DecoderError, Json; 19 | str::Utf8Error, NotUtf8; 20 | io::Error, Io; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /cancelbot/src/http.rs: -------------------------------------------------------------------------------- 1 | use std::str; 2 | use std::sync::{Arc, Mutex}; 3 | 4 | use curl::easy::{Easy, List}; 5 | use futures::Future; 6 | use rustc_serialize::json; 7 | use rustc_serialize::Decodable; 8 | use tokio_curl::Session; 9 | 10 | use errors::*; 11 | use MyFuture; 12 | 13 | static TRAVIS_API_BASE: &str = "https://api.travis-ci.com"; 14 | static APPVEYOR_API_BASE: &str = "https://ci.appveyor.com/api"; 15 | static AZURE_API_BASE: &str = "https://dev.azure.com"; 16 | static AGENT: &str = "User-Agent: cancelbot (github.com/rust-lang/rust-central-station)"; 17 | 18 | fn append_url(host: &str, url: &str) -> String { 19 | if url.starts_with("https://") { 20 | url.to_string() 21 | } else { 22 | format!("{}{}", host, url) 23 | } 24 | } 25 | 26 | #[allow(dead_code)] 27 | pub struct Response { 28 | easy: Easy, 29 | headers: Arc>>>, 30 | body: Arc>>, 31 | } 32 | 33 | pub fn travis_get(sess: &Session, url: &str, token: &str) -> MyFuture 34 | where 35 | T: Decodable + 'static, 36 | { 37 | let url = append_url(TRAVIS_API_BASE, url); 38 | let headers = vec![ 39 | format!("Authorization: token {}", token), 40 | format!("Accept: application/vnd.travis-ci.2+json"), 41 | ]; 42 | get_json(sess, &url, &headers) 43 | } 44 | 45 | pub fn travis_post(sess: &Session, url: &str, token: &str) -> MyFuture<()> { 46 | let headers = vec![ 47 | format!("Authorization: token {}", token), 48 | format!("Accept: application/vnd.travis-ci.2+json"), 49 | ]; 50 | 51 | let response = post(sess, &append_url(TRAVIS_API_BASE, url), &headers); 52 | Box::new(response.map(|_| ())) 53 | } 54 | 55 | pub fn appveyor_get(sess: &Session, url: &str, token: &str) -> MyFuture 56 | where 57 | T: Decodable + 'static, 58 | { 59 | let headers = vec![ 60 | format!("Authorization: Bearer {}", token), 61 | format!("Accept: application/json"), 62 | ]; 63 | 64 | get_json(sess, &append_url(APPVEYOR_API_BASE, url), &headers) 65 | } 66 | 67 | pub fn appveyor_delete(sess: &Session, url: &str, token: &str) -> MyFuture<()> { 68 | let headers = vec![ 69 | format!("Authorization: Bearer {}", token), 70 | format!("Accept: application/json"), 71 | ]; 72 | 73 | let response = delete(sess, &append_url(APPVEYOR_API_BASE, url), &headers); 74 | Box::new(response.map(|_| ())) 75 | } 76 | 77 | pub fn azure_pipelines_get(sess: &Session, url: &str, token: &str) -> MyFuture 78 | where 79 | T: Decodable + 'static, 80 | { 81 | let base64 = base64::encode(&format!(":{}", token)); 82 | let headers = vec![ 83 | format!("Authorization: Basic {}", base64), 84 | format!("Accept: application/json"), 85 | ]; 86 | 87 | get_json(sess, &append_url(AZURE_API_BASE, url), &headers) 88 | } 89 | 90 | pub fn azure_patch(sess: &Session, url: &str, token: &str, body: &str) -> MyFuture<()> { 91 | let base64 = base64::encode(&format!(":{}", token)); 92 | let headers = vec![ 93 | format!("Authorization: Basic {}", base64), 94 | format!("Accept: application/json"), 95 | format!("Content-Type: application/json"), 96 | ]; 97 | 98 | let response = patch(sess, &append_url(AZURE_API_BASE, url), &headers, body); 99 | Box::new(response.map(|_| ())) 100 | } 101 | 102 | pub fn get_json(sess: &Session, url: &str, headers: &[String]) -> MyFuture 103 | where 104 | T: Decodable + 'static, 105 | { 106 | let response = get(sess, url, headers); 107 | let ret = response.and_then(|response| { 108 | let body = response.body.lock().unwrap(); 109 | let json = try!(str::from_utf8(&body)); 110 | let ret = try!(json::decode(json).chain_err(|| { format!("failed to decode: {}", json) })); 111 | Ok(ret) 112 | }); 113 | Box::new(ret) 114 | } 115 | 116 | pub fn get(sess: &Session, url: &str, headers: &[String]) -> MyFuture { 117 | let mut handle = Easy::new(); 118 | let mut list = List::new(); 119 | t!(list.append(AGENT)); 120 | for header in headers { 121 | t!(list.append(header)); 122 | } 123 | 124 | t!(handle.http_headers(list)); 125 | t!(handle.get(true)); 126 | t!(handle.url(url)); 127 | 128 | perform(sess, handle, url) 129 | } 130 | 131 | pub fn delete(sess: &Session, url: &str, headers: &[String]) -> MyFuture { 132 | let mut handle = Easy::new(); 133 | let mut list = List::new(); 134 | t!(list.append(AGENT)); 135 | for header in headers { 136 | t!(list.append(header)); 137 | } 138 | 139 | t!(handle.http_headers(list)); 140 | t!(handle.custom_request("DELETE")); 141 | t!(handle.url(url)); 142 | 143 | perform(sess, handle, url) 144 | } 145 | 146 | pub fn post(sess: &Session, url: &str, headers: &[String]) -> MyFuture { 147 | let mut handle = Easy::new(); 148 | let mut list = List::new(); 149 | t!(list.append(AGENT)); 150 | for header in headers { 151 | t!(list.append(header)); 152 | } 153 | 154 | t!(handle.http_headers(list)); 155 | t!(handle.post(true)); 156 | t!(handle.url(url)); 157 | 158 | perform(sess, handle, url) 159 | } 160 | 161 | pub fn patch(sess: &Session, url: &str, headers: &[String], body: &str) -> MyFuture { 162 | let mut handle = Easy::new(); 163 | let mut list = List::new(); 164 | t!(list.append(AGENT)); 165 | for header in headers { 166 | t!(list.append(header)); 167 | } 168 | 169 | t!(handle.http_headers(list)); 170 | t!(handle.post_fields_copy(body.as_bytes())); 171 | t!(handle.custom_request("PATCH")); 172 | t!(handle.url(url)); 173 | 174 | perform(sess, handle, url) 175 | } 176 | 177 | pub fn perform(sess: &Session, mut easy: Easy, url: &str) -> MyFuture { 178 | println!("fetching: {}", url); 179 | let headers = Arc::new(Mutex::new(Vec::new())); 180 | let data = Arc::new(Mutex::new(Vec::new())); 181 | 182 | let (data2, headers2) = (data.clone(), headers.clone()); 183 | t!(easy.header_function(move |data| { 184 | headers2.lock().unwrap().push(data.to_owned()); 185 | true 186 | })); 187 | t!(easy.write_function(move |buf| { 188 | data2.lock().unwrap().extend_from_slice(&buf); 189 | Ok(buf.len()) 190 | })); 191 | 192 | let response = sess.perform(easy); 193 | let url = url.to_string(); 194 | let checked_response = response.map_err(|e| e.into()).and_then(move |mut easy| { 195 | println!("finished: {}", url); 196 | match t!(easy.response_code()) { 197 | 200 | 204 => Ok(Response { 198 | easy: easy, 199 | headers: headers, 200 | body: data, 201 | }), 202 | code => Err(format!( 203 | "not a 200 code: {}\n\n{}\n", 204 | code, 205 | String::from_utf8_lossy(&data.lock().unwrap()) 206 | ) 207 | .into()), 208 | } 209 | }); 210 | 211 | Box::new(checked_response) 212 | } 213 | -------------------------------------------------------------------------------- /cancelbot/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate curl; 2 | extern crate futures; 3 | extern crate getopts; 4 | extern crate rustc_serialize; 5 | extern crate time; 6 | extern crate tokio_core; 7 | extern crate tokio_curl; 8 | #[macro_use] 9 | extern crate error_chain; 10 | 11 | use std::collections::HashMap; 12 | use std::env; 13 | use std::sync::Arc; 14 | use std::time::Duration; 15 | 16 | use errors::*; 17 | use futures::Future; 18 | use getopts::Options; 19 | use tokio_core::reactor::{Core, Handle, Timeout}; 20 | use tokio_curl::Session; 21 | 22 | macro_rules! t { 23 | ($e:expr) => { 24 | match $e { 25 | Ok(e) => e, 26 | Err(e) => panic!("{} failed with {}", stringify!($e), e), 27 | } 28 | }; 29 | } 30 | 31 | type MyFuture = Box>; 32 | 33 | #[derive(Clone)] 34 | struct State { 35 | travis_token: Option, 36 | appveyor_token: Option, 37 | azure_pipelines_token: Option, 38 | session: Session, 39 | repos: Vec, 40 | branch: String, 41 | appveyor_account_name: Option, 42 | azure_pipelines_org: Option, 43 | } 44 | 45 | #[derive(Clone)] 46 | struct Repo { 47 | user: String, 48 | name: String, 49 | } 50 | 51 | mod appveyor; 52 | mod azure; 53 | mod errors; 54 | mod http; 55 | mod travis; 56 | 57 | fn main() { 58 | let args = env::args().skip(1).collect::>(); 59 | let mut opts = Options::new(); 60 | opts.reqopt("b", "branch", "branch to work with", "BRANCH"); 61 | opts.optopt("t", "travis", "travis token", "TOKEN"); 62 | opts.optopt("a", "appveyor", "appveyor token", "TOKEN"); 63 | opts.optopt("", "appveyor-account", "appveyor account name", "ACCOUNT"); 64 | opts.optopt("", "azure-pipelines-token", "", "TOKEN"); 65 | opts.optopt("", "azure-pipelines-org", "", "ORGANIZATION"); 66 | 67 | let usage = || -> ! { 68 | println!("{}", opts.usage("usage: ./foo -a ... -t ...")); 69 | std::process::exit(1); 70 | }; 71 | 72 | let matches = match opts.parse(&args) { 73 | Ok(matches) => matches, 74 | Err(e) => { 75 | println!("error: {}", e); 76 | usage(); 77 | } 78 | }; 79 | 80 | let mut core = t!(Core::new()); 81 | let handle = core.handle(); 82 | 83 | let state = State { 84 | travis_token: matches.opt_str("t"), 85 | appveyor_token: matches.opt_str("a"), 86 | repos: matches 87 | .free 88 | .iter() 89 | .map(|m| { 90 | let mut parts = m.splitn(2, '/'); 91 | Repo { 92 | user: parts.next().unwrap().to_string(), 93 | name: parts.next().unwrap().to_string(), 94 | } 95 | }) 96 | .collect(), 97 | session: Session::new(handle.clone()), 98 | branch: matches.opt_str("b").unwrap(), 99 | appveyor_account_name: matches.opt_str("appveyor-account"), 100 | azure_pipelines_token: matches.opt_str("azure-pipelines-token"), 101 | azure_pipelines_org: matches.opt_str("azure-pipelines-org"), 102 | }; 103 | 104 | core.run(state.check(&handle)).unwrap(); 105 | } 106 | 107 | impl State { 108 | fn check(&self, handle: &Handle) -> MyFuture<()> { 109 | println!( 110 | "--------------------------------------------------------\n\ 111 | {} - starting check", 112 | time::now().rfc822z() 113 | ); 114 | let travis = self.check_travis(); 115 | let travis = travis.then(|result| { 116 | println!("travis result {:?}", result); 117 | Ok(()) 118 | }); 119 | let appveyor = self.check_appveyor(); 120 | let appveyor = appveyor.then(|result| { 121 | println!("appveyor result {:?}", result); 122 | Ok(()) 123 | }); 124 | let azure_pipelines = self.check_azure_pipelines(); 125 | let azure_pipelines = azure_pipelines.then(|result| { 126 | println!("azure_pipelines result {:?}", result); 127 | Ok(()) 128 | }); 129 | 130 | let requests = travis 131 | .join(appveyor) 132 | .map(|_| ()) 133 | .join(azure_pipelines) 134 | .map(|_| ()); 135 | let timeout = t!(Timeout::new(Duration::new(30, 0), handle)); 136 | Box::new( 137 | requests 138 | .map(Ok) 139 | .select(timeout.map(Err).map_err(From::from)) 140 | .then(|res| match res { 141 | Ok((Ok(()), _timeout)) => Ok(()), 142 | Ok((Err(_), _requests)) => { 143 | println!("timeout, canceling requests"); 144 | Ok(()) 145 | } 146 | Err((e, _other)) => Err(e), 147 | }), 148 | ) 149 | } 150 | 151 | fn check_travis(&self) -> MyFuture<()> { 152 | let futures = if let Some(token) = &self.travis_token { 153 | let token = Arc::new(token.clone()); 154 | self.repos 155 | .iter() 156 | .map(|repo| self.check_travis_repo(repo.clone(), token.clone())) 157 | .collect::>() 158 | } else { 159 | Vec::new() 160 | }; 161 | Box::new(futures::collect(futures).map(|_| ())) 162 | } 163 | 164 | fn check_travis_repo(&self, repo: Repo, token: Arc) -> MyFuture<()> { 165 | let url = format!("/repos/{}/{}/builds", repo.user, repo.name); 166 | let history = http::travis_get(&self.session, &url, &token); 167 | 168 | let me = self.clone(); 169 | let cancel_old = history.and_then(move |list: travis::GetBuilds| { 170 | let mut futures = Vec::new(); 171 | let commits = list 172 | .commits 173 | .iter() 174 | .map(|c| (c.id, c)) 175 | .collect::>(); 176 | 177 | // we're only interested in builds that concern our branch 178 | let builds = list 179 | .builds 180 | .iter() 181 | .filter(|build| match commits.get(&build.commit_id) { 182 | Some(c) if c.branch != me.branch => false, 183 | Some(_) => true, 184 | None => false, 185 | }) 186 | .collect::>(); 187 | 188 | // figure out what the max build number is, then cancel everything 189 | // that came before that. 190 | let max = builds 191 | .iter() 192 | .map(|b| b.number.parse::().unwrap()) 193 | .max(); 194 | for build in builds.iter() { 195 | if !me.travis_build_running(build) { 196 | continue; 197 | } 198 | if build.number == max.unwrap_or(0).to_string() { 199 | futures.push(me.travis_cancel_if_jobs_failed(build, token.clone())); 200 | } else { 201 | println!( 202 | "travis cancelling {} in {} as it's not the latest", 203 | build.number, build.state 204 | ); 205 | futures.push(me.travis_cancel_build(build, token.clone())); 206 | } 207 | } 208 | futures::collect(futures) 209 | }); 210 | 211 | Box::new(cancel_old.map(|_| ())) 212 | } 213 | 214 | fn travis_cancel_if_jobs_failed( 215 | &self, 216 | build: &travis::Build, 217 | token: Arc, 218 | ) -> MyFuture<()> { 219 | let url = format!("/builds/{}", build.id); 220 | let build = http::travis_get(&self.session, &url, &token); 221 | let me = self.clone(); 222 | let cancel = build.and_then(move |b: travis::GetBuild| { 223 | let cancel = b.jobs.iter().any(|job| match &job.state[..] { 224 | "failed" | "errored" | "canceled" => true, 225 | _ => false, 226 | }); 227 | 228 | if cancel { 229 | println!("cancelling top build {} as a job failed", b.build.number); 230 | me.travis_cancel_build(&b.build, token) 231 | } else { 232 | Box::new(futures::finished(())) 233 | } 234 | }); 235 | 236 | Box::new(cancel.map(|_| ())) 237 | } 238 | 239 | fn travis_build_running(&self, build: &travis::Build) -> bool { 240 | match &build.state[..] { 241 | "passed" | "failed" | "canceled" | "errored" => false, 242 | _ => true, 243 | } 244 | } 245 | 246 | fn travis_cancel_build(&self, build: &travis::Build, token: Arc) -> MyFuture<()> { 247 | let url = format!("/builds/{}/cancel", build.id); 248 | http::travis_post(&self.session, &url, &token) 249 | } 250 | 251 | fn check_appveyor(&self) -> MyFuture<()> { 252 | let futures = if let Some(token) = &self.appveyor_token { 253 | let token = Arc::new(token.clone()); 254 | self.repos 255 | .iter() 256 | .map(|repo| self.check_appveyor_repo(repo.clone(), token.clone())) 257 | .collect::>() 258 | } else { 259 | Vec::new() 260 | }; 261 | Box::new(futures::collect(futures).map(|_| ())) 262 | } 263 | 264 | fn check_appveyor_repo(&self, repo: Repo, token: Arc) -> MyFuture<()> { 265 | let url = format!( 266 | "/projects/{}/{}/history?recordsNumber=10&branch={}", 267 | self.appveyor_account_name.as_ref().unwrap_or(&repo.name), 268 | repo.name, 269 | self.branch 270 | ); 271 | let history = http::appveyor_get(&self.session, &url, &token); 272 | 273 | let me = self.clone(); 274 | let repo2 = repo.clone(); 275 | let token2 = token.clone(); 276 | let cancel_old = history.and_then(move |history: appveyor::History| { 277 | let max = history.builds.iter().map(|b| b.buildNumber).max(); 278 | let mut futures = Vec::new(); 279 | for build in history.builds.iter() { 280 | if !me.appveyor_build_running(build) { 281 | continue; 282 | } 283 | if build.buildNumber < max.unwrap_or(0) { 284 | println!( 285 | "appveyor cancelling {} as it's not the latest", 286 | build.buildNumber 287 | ); 288 | futures.push(me.appveyor_cancel_build(&repo2, build, token2.clone())); 289 | } 290 | } 291 | futures::collect(futures) 292 | }); 293 | 294 | let me = self.clone(); 295 | let url = format!( 296 | "/projects/{}/{}/branch/{}", 297 | self.appveyor_account_name.as_ref().unwrap_or(&repo.name), 298 | repo.name, 299 | self.branch 300 | ); 301 | let last_build = http::appveyor_get(&self.session, &url, &token); 302 | let me = me.clone(); 303 | let cancel_if_failed = last_build.and_then(move |last: appveyor::LastBuild| { 304 | if !me.appveyor_build_running(&last.build) { 305 | return Box::new(futures::finished(())) as Box<_>; 306 | } 307 | for job in last.build.jobs.iter() { 308 | match &job.status[..] { 309 | "success" | "queued" | "starting" | "running" => continue, 310 | _ => {} 311 | } 312 | 313 | println!( 314 | "appveyor cancelling {} as a job is {}", 315 | last.build.buildNumber, job.status 316 | ); 317 | return me.appveyor_cancel_build(&repo, &last.build, token); 318 | } 319 | Box::new(futures::finished(())) 320 | }); 321 | 322 | Box::new(cancel_old.join(cancel_if_failed).map(|_| ())) 323 | } 324 | 325 | fn appveyor_build_running(&self, build: &appveyor::Build) -> bool { 326 | match &build.status[..] { 327 | "failed" | "cancelled" | "success" => false, 328 | _ => true, 329 | } 330 | } 331 | 332 | fn appveyor_cancel_build( 333 | &self, 334 | repo: &Repo, 335 | build: &appveyor::Build, 336 | token: Arc, 337 | ) -> MyFuture<()> { 338 | let url = format!( 339 | "/builds/{}/{}/{}", 340 | self.appveyor_account_name.as_ref().unwrap_or(&repo.name), 341 | repo.name, 342 | build.version 343 | ); 344 | http::appveyor_delete(&self.session, &url, &token) 345 | } 346 | 347 | fn check_azure_pipelines(&self) -> MyFuture<()> { 348 | let futures = if let Some(token) = &self.azure_pipelines_token { 349 | let token = Arc::new(token.clone()); 350 | self.repos 351 | .iter() 352 | .map(|repo| self.check_azure_pipelines_repo(repo.clone(), token.clone())) 353 | .collect::>() 354 | } else { 355 | Vec::new() 356 | }; 357 | Box::new(futures::collect(futures).map(|_| ())) 358 | } 359 | 360 | fn check_azure_pipelines_repo(&self, repo: Repo, token: Arc) -> MyFuture<()> { 361 | let url = format!( 362 | "/{}/{}/_apis/build/builds?api-version=5.0&repositoryType=GitHub&repositoryId={}/{}&branchName=refs/heads/{}", 363 | self.azure_pipelines_org.as_ref().unwrap_or(&repo.user), 364 | repo.name, 365 | repo.user, 366 | repo.name, 367 | self.branch, 368 | ); 369 | let history = http::azure_pipelines_get(&self.session, &url, &token); 370 | 371 | let me = self.clone(); 372 | let repo2 = repo.clone(); 373 | let cancel_old = history.and_then(move |list: azure::List| { 374 | let max = list.value.iter().map(|b| b.id).max(); 375 | let mut futures = Vec::new(); 376 | for (i, build) in list.value.iter().enumerate() { 377 | if !me.azure_build_running(build) { 378 | continue; 379 | } 380 | if build.id < max.unwrap_or(0) { 381 | println!("azure cancelling {} as it's not the latest", build.id); 382 | futures.push(me.azure_cancel_build(&repo2, build, token.clone())); 383 | continue; 384 | } 385 | if i != 0 { 386 | continue; 387 | } 388 | 389 | // If this is the first build look at the timeline (jobs) and if 390 | // anything failed then cancel the job. 391 | let timeline = 392 | http::azure_pipelines_get(&me.session, &build._links.timeline.href, &token); 393 | let repo3 = repo2.clone(); 394 | let me2 = me.clone(); 395 | let build = build.clone(); 396 | let token2 = token.clone(); 397 | let cancel_first = timeline.and_then(move |list: azure::Timeline| { 398 | if list.records.iter().any(|r| { 399 | r.result.as_ref().map(|s| s == "failed").unwrap_or(false) 400 | && r.r#type == "Job" 401 | }) { 402 | me2.azure_cancel_build(&repo3, &build, token2) 403 | } else { 404 | Box::new(futures::future::ok(())) 405 | } 406 | }); 407 | futures.push(Box::new(cancel_first)); 408 | } 409 | futures::collect(futures) 410 | }); 411 | 412 | Box::new(cancel_old.map(|_| ())) 413 | } 414 | 415 | fn azure_build_running(&self, build: &azure::Build) -> bool { 416 | match &build.status[..] { 417 | "cancelling" | "completed" => false, 418 | _ => true, 419 | } 420 | } 421 | 422 | fn azure_cancel_build( 423 | &self, 424 | repo: &Repo, 425 | build: &azure::Build, 426 | token: Arc, 427 | ) -> MyFuture<()> { 428 | let url = format!( 429 | "/{}/{}/_apis/build/builds/{}?api-version=5.0", 430 | self.azure_pipelines_org.as_ref().unwrap_or(&repo.user), 431 | repo.name, 432 | build.id, 433 | ); 434 | let body = "{\"status\":\"Cancelling\"}"; 435 | http::azure_patch(&self.session, &url, &token, body) 436 | } 437 | } 438 | -------------------------------------------------------------------------------- /cancelbot/src/travis.rs: -------------------------------------------------------------------------------- 1 | #[derive(RustcDecodable, Debug)] 2 | pub struct GetBuilds { 3 | pub builds: Vec, 4 | pub commits: Vec, 5 | } 6 | 7 | #[derive(RustcDecodable, Debug)] 8 | pub struct Build { 9 | pub id: u32, 10 | pub number: String, 11 | pub state: String, 12 | pub commit_id: u32, 13 | pub job_ids: Vec, 14 | } 15 | 16 | #[derive(RustcDecodable, Debug)] 17 | pub struct Commit { 18 | pub id: u32, 19 | pub branch: String, 20 | } 21 | 22 | #[derive(RustcDecodable, Debug)] 23 | pub struct GetBuild { 24 | pub commit: Commit, 25 | pub build: Build, 26 | pub jobs: Vec, 27 | } 28 | 29 | #[derive(RustcDecodable, Debug)] 30 | pub struct Job { 31 | pub id: u32, 32 | pub build_id: u32, 33 | pub allow_failure: bool, 34 | pub state: String, 35 | } 36 | -------------------------------------------------------------------------------- /crontab: -------------------------------------------------------------------------------- 1 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.cargo/bin 2 | 3 | # renewing ssl certs 4 | 24 * * * * root letsencrypt renew 2>&1 | logger --tag letsencrypt-renew 5 | 6 | # signing/hashing/promoting releases 7 | #0 0 * * * root promote-release /tmp/nightly nightly /data/secrets.toml 2>&1 | logger --tag release-nightly 8 | #20 3 * * * root promote-release /tmp/beta beta /data/secrets.toml 2>&1 | logger --tag release-beta 9 | 40 * * * * root promote-release /tmp/stable stable /data/secrets-dev.toml 2>&1 | logger --tag release-stable 10 | 11 | # cancelling appveyor/travis/azure builds if we don't need them 12 | */2 * * * * root /src/bin/cancelbot-rust.sh 2>&1 | logger --tag cancelbot-rust 13 | */2 * * * * root /src/bin/cancelbot-azure-rust-lang2.sh 2>&1 | logger --tag cancelbot-azure-rust-lang2 14 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | $(aws ecr get-login --no-include-email --region us-west-1) 5 | docker tag rust-central-station:latest 890664054962.dkr.ecr.us-west-1.amazonaws.com/rust-central-station:latest 6 | docker push 890664054962.dkr.ecr.us-west-1.amazonaws.com/rust-central-station:latest 7 | -------------------------------------------------------------------------------- /homu.toml.template: -------------------------------------------------------------------------------- 1 | max_priority = 9001 2 | 3 | [db] 4 | file = '/data/main.db' 5 | 6 | [github] 7 | 8 | # Information for securely interacting with GitHub. These are found/generated 9 | # under . 10 | 11 | # A GitHub personal access token 12 | access_token = "{{ homu.github.access-token }}" 13 | 14 | # A GitHub oauth application for this instance of homu: 15 | app_client_id = "{{ homu.github.app-client-id }}" 16 | app_client_secret = "{{ homu.github.app-client-secret }}" 17 | 18 | 19 | [git] 20 | 21 | # Use the local Git command. Required to use some advanced features. It also 22 | # speeds up Travis by reducing temporary commits 23 | local_git = true 24 | 25 | # SSH private key. Needed only when the local Git command is used 26 | ssh_key = """ 27 | {{ homu.ssh.ssh-key }} 28 | """ 29 | 30 | [web] 31 | 32 | host = '127.0.0.1' 33 | port = 7942 34 | 35 | [repo] 36 | -------------------------------------------------------------------------------- /naglist.txt: -------------------------------------------------------------------------------- 1 | aturon,aturon@mozilla.com 2 | alexcrichton,acrichton@mozilla.com 3 | brson,brson@mozilla.com 4 | wycats,wycats@gmail.com 5 | steveklabnik,steve@steveklabnik.com 6 | nikomatsakis,nmatsakis@mozilla.com 7 | erickt,erick.tryzelaar@gmail.com 8 | eddyb,edy.burt@gmail.com 9 | nrc,nrc@mozilla.com 10 | pnkfelix,pnkfelix@mozilla.com 11 | withoutboats,woboats@gmail.com 12 | sfackler,sfackler@gmail.com 13 | BurntSushi,jamslam@gmail.com 14 | Kimundi,loebel.marvin@gmail.com 15 | arielb1,ariel.byd@gmail.com 16 | dotdash,bsteinbr@gmail.com 17 | michaelwoerister,michaelwoerister@posteo.net 18 | jseyfried,jeffrey.seyfried@gmail.com 19 | japaric,japaricious@gmail.com 20 | vadimcn,vadimcn@gmail.com 21 | carols10cents,carol.nichols@gmail.com 22 | GuillaumeGomez,guillaume1.gomez@gmail.com 23 | jonathandturner,jturner@mozilla.com 24 | peschkaj,jeremiah.peschka@gmail.com 25 | frewsxcv,coreyf@rwell.org 26 | -------------------------------------------------------------------------------- /nginx.conf.template: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes 4; 3 | pid /run/nginx.pid; 4 | 5 | events { 6 | worker_connections 768; 7 | } 8 | 9 | http { 10 | sendfile on; 11 | tcp_nopush on; 12 | tcp_nodelay on; 13 | keepalive_timeout 65; 14 | types_hash_max_size 2048; 15 | 16 | include /etc/nginx/mime.types; 17 | default_type application/octet-stream; 18 | 19 | access_log /var/log/nginx/access.log; 20 | error_log /var/log/nginx/error.log; 21 | 22 | gzip on; 23 | gzip_disable "msie6"; 24 | 25 | server { 26 | listen 443 ssl http2; 27 | listen [::]:443 ssl http2; 28 | 29 | ssl_certificate /etc/letsencrypt/live/{{ nginx.hostname }}/fullchain.pem; 30 | ssl_certificate_key /etc/letsencrypt/live/{{ nginx.hostname }}/privkey.pem; 31 | ssl_session_timeout 1d; 32 | ssl_session_cache shared:SSL:50m; 33 | ssl_session_tickets off; 34 | 35 | ssl_protocols TLSv1.2; 36 | ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; 37 | ssl_prefer_server_ciphers on; 38 | 39 | add_header Strict-Transport-Security max-age=15768000; 40 | 41 | server_name {{ nginx.hostname }} {{ nginx.hostname_alias }}; 42 | 43 | access_log /var/log/nginx/{{ nginx.hostname }}.access.log; 44 | 45 | location ~ /.well-known { 46 | root /usr/share/nginx/html; 47 | allow all; 48 | } 49 | 50 | location /homu/ { 51 | proxy_pass http://localhost:7942/; 52 | } 53 | 54 | location / { 55 | proxy_pass http://localhost:7942/; 56 | } 57 | } 58 | 59 | server { 60 | listen 80; 61 | server_name {{ nginx.hostname }} {{ nginx.hostname_alias }}; 62 | return 301 https://$host$request_uri; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /nginx.tmp.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes 4; 3 | pid /run/nginx.pid; 4 | 5 | events { 6 | worker_connections 768; 7 | } 8 | 9 | http { 10 | sendfile on; 11 | tcp_nopush on; 12 | tcp_nodelay on; 13 | keepalive_timeout 65; 14 | types_hash_max_size 2048; 15 | 16 | include /etc/nginx/mime.types; 17 | default_type application/octet-stream; 18 | 19 | access_log /var/log/nginx/access.log; 20 | error_log /var/log/nginx/error.log; 21 | 22 | gzip on; 23 | gzip_disable "msie6"; 24 | 25 | server { 26 | listen 80; 27 | location ~ /.well-known { 28 | root /usr/share/nginx/html; 29 | allow all; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /promote-release/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "promote-release" 3 | version = "0.1.0" 4 | authors = ["Alex Crichton "] 5 | license = "MIT OR Apache-2.0" 6 | 7 | [dependencies] 8 | curl = "0.4" 9 | flate2 = "1" 10 | fs2 = "0.4" 11 | serde_json = "1" 12 | tar = "0.4" 13 | toml = "0.4" 14 | rand = "0.6" 15 | xz2 = "0.1" 16 | -------------------------------------------------------------------------------- /promote-release/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate curl; 2 | extern crate flate2; 3 | extern crate fs2; 4 | extern crate rand; 5 | #[macro_use] 6 | extern crate serde_json; 7 | extern crate tar; 8 | extern crate toml; 9 | extern crate xz2; 10 | 11 | use std::env; 12 | use std::fs::{self, File, OpenOptions}; 13 | use std::io::{self, Read, Write}; 14 | use std::path::{PathBuf, Path}; 15 | use std::process::Command; 16 | 17 | use curl::easy::Easy; 18 | use fs2::FileExt; 19 | 20 | macro_rules! t { 21 | ($e:expr) => (match $e { 22 | Ok(e) => e, 23 | Err(e) => panic!("{} failed with {:?}", stringify!($e), e), 24 | }) 25 | } 26 | 27 | struct Context { 28 | work: PathBuf, 29 | release: String, 30 | handle: Easy, 31 | secrets: toml::Value, 32 | date: String, 33 | current_version: Option, 34 | } 35 | 36 | // Called as: 37 | // 38 | // $prog work/dir release-channel path/to/secrets.toml 39 | fn main() { 40 | let mut secrets = String::new(); 41 | t!(t!(File::open(env::args().nth(3).unwrap())).read_to_string(&mut secrets)); 42 | 43 | Context { 44 | work: t!(env::current_dir()).join(env::args_os().nth(1).unwrap()), 45 | release: env::args().nth(2).unwrap(), 46 | secrets: t!(secrets.parse()), 47 | handle: Easy::new(), 48 | date: output(Command::new("date").arg("+%Y-%m-%d")).trim().to_string(), 49 | current_version: None, 50 | }.run() 51 | } 52 | 53 | impl Context { 54 | fn run(&mut self) { 55 | let _lock = self.lock(); 56 | self.update_repo(); 57 | 58 | let override_var = env::var("PROMOTE_RELEASE_OVERRIDE_BRANCH"); 59 | let branch = if let Ok(branch) = override_var.as_ref() { 60 | branch 61 | } else { 62 | match &self.release[..] { 63 | "nightly" => "master", 64 | "beta" => "beta", 65 | "stable" => "stable", 66 | _ => panic!("unknown release: {}", self.release), 67 | } 68 | }; 69 | self.do_release(branch); 70 | } 71 | 72 | /// Locks execution of concurrent invocations of this script in case one 73 | /// takes a long time to run. The call to `try_lock_exclusive` will fail if 74 | /// the lock is held already 75 | fn lock(&mut self) -> File { 76 | t!(fs::create_dir_all(&self.work)); 77 | let file = t!(OpenOptions::new() 78 | .read(true) 79 | .write(true) 80 | .create(true) 81 | .open(self.work.join(".lock"))); 82 | t!(file.try_lock_exclusive()); 83 | file 84 | } 85 | 86 | /// Update the rust repository we have cached, either cloning a fresh one or 87 | /// fetching remote references 88 | fn update_repo(&mut self) { 89 | // Clone/update the repo 90 | let dir = self.rust_dir(); 91 | if dir.is_dir() { 92 | println!("fetching"); 93 | run(Command::new("git") 94 | .arg("fetch") 95 | .arg("origin") 96 | .current_dir(&dir)); 97 | } else { 98 | println!("cloning"); 99 | run(Command::new("git") 100 | .arg("clone") 101 | .arg("https://github.com/rust-lang/rust") 102 | .arg(&dir)); 103 | } 104 | } 105 | 106 | /// Does a release for the `branch` specified. 107 | fn do_release(&mut self, branch: &str) { 108 | // Learn the precise rev of the remote branch, this'll guide what we 109 | // download. 110 | let rev = output(Command::new("git") 111 | .arg("rev-parse") 112 | .arg(format!("origin/{}", branch)) 113 | .current_dir(&self.rust_dir())); 114 | let rev = rev.trim(); 115 | println!("{} rev is {}", self.release, rev); 116 | 117 | // Download the current live manifest for the channel we're releasing. 118 | // Through that we learn the current version of the release. 119 | let manifest = self.download_manifest(); 120 | let previous_version = manifest["pkg"]["rust"]["version"] 121 | .as_str() 122 | .expect("rust version not a string"); 123 | println!("previous version: {}", previous_version); 124 | 125 | // If the previously released version is the same rev, then there's 126 | // nothing for us to do, nothing has changed. 127 | if previous_version.contains(&rev[..7]) { 128 | return println!("found rev in previous version, skipping"); 129 | } 130 | 131 | // During normal operations we don't want multiple releases to happen on the same channel 132 | // in the same day. This check prevents that, and it can be skipped by setting an 133 | // environment variable if the person doing the release really wants that. 134 | if std::env::var("PROMOTE_RELEASE_ALLOW_MULTIPLE_TODAY").is_err() && self.dated_manifest_exists() { 135 | println!( 136 | "another release on the {} channel was done today ({})", 137 | self.release, self.date 138 | ); 139 | println!("set PROMOTE_RELEASE_ALLOW_MULTIPLE_TODAY=1 to bypass the check"); 140 | return; 141 | } 142 | 143 | // We may still not do a release if the version number hasn't changed. 144 | // To learn about the current branch's version number we download 145 | // artifacts and look inside. 146 | // 147 | // If revisions of the current release and the current branch are 148 | // different and the versions are the same then there's nothing for us 149 | // to do. This represents a scenario where changes have been merged to 150 | // the stable/beta branch but the version bump hasn't happened yet. 151 | self.download_artifacts(&rev); 152 | if self.current_version_same(&previous_version) { 153 | return println!("version hasn't changed, skipping"); 154 | } 155 | 156 | self.assert_all_components_present(); 157 | 158 | // Ok we've now determined that a release needs to be done. Let's 159 | // configure rust, build a manifest and sign the artifacts we just downloaded, and upload the 160 | // signatures and manifest to the CI bucket. 161 | self.configure_rust(rev); 162 | self.sign_artifacts(); 163 | self.upload_signatures(&rev); 164 | 165 | // Merge all the signatures with the download files, and then sync that 166 | // whole dir up to the release archives 167 | for file in t!(self.build_dir().join("build/dist/").read_dir()) { 168 | let file = t!(file); 169 | t!(fs::copy(file.path(), self.dl_dir().join(file.file_name()))); 170 | } 171 | self.publish_archive(); 172 | self.publish_docs(); 173 | self.publish_release(); 174 | 175 | self.invalidate_cloudfront(); 176 | 177 | // Clean up after ourselves to avoid leaving gigabytes of artifacts 178 | // around. 179 | drop(fs::remove_dir_all(&self.dl_dir())); 180 | } 181 | 182 | fn configure_rust(&mut self, rev: &str) { 183 | let build = self.build_dir(); 184 | drop(fs::remove_dir_all(&build)); 185 | t!(fs::create_dir_all(&build)); 186 | let rust = self.rust_dir(); 187 | 188 | run(Command::new("git") 189 | .arg("reset") 190 | .arg("--hard") 191 | .arg(rev) 192 | .current_dir(&rust)); 193 | 194 | run(Command::new(rust.join("configure")) 195 | .current_dir(&build) 196 | .arg(format!("--release-channel={}", self.release))); 197 | let mut config = String::new(); 198 | let path = build.join("config.toml"); 199 | drop(File::open(&path).and_then(|mut f| f.read_to_string(&mut config))); 200 | let lines = config.lines().filter(|l| !l.starts_with("[dist]")); 201 | let mut new_config = String::new(); 202 | for line in lines { 203 | new_config.push_str(line); 204 | new_config.push_str("\n"); 205 | } 206 | new_config.push_str(&format!(" 207 | [dist] 208 | sign-folder = \"{}\" 209 | gpg-password-file = \"{}\" 210 | upload-addr = \"{}/{}\" 211 | ", 212 | self.dl_dir().display(), 213 | self.secrets["dist"]["gpg-password-file"].as_str().unwrap(), 214 | self.secrets["dist"]["upload-addr"].as_str().unwrap(), 215 | self.secrets["dist"]["upload-dir"].as_str().unwrap())); 216 | t!(t!(File::create(&path)).write_all(new_config.as_bytes())); 217 | } 218 | 219 | fn current_version_same(&mut self, prev: &str) -> bool { 220 | // nightly's always changing 221 | if self.release == "nightly" { 222 | return false 223 | } 224 | let prev_version = prev.split(' ').next().unwrap(); 225 | 226 | let current = t!(self.dl_dir().read_dir()).filter_map(|e| { 227 | let e = t!(e); 228 | let filename = e.file_name().into_string().unwrap(); 229 | if !filename.starts_with("rustc-") || !filename.ends_with(".tar.gz") { 230 | return None 231 | } 232 | println!("looking inside {} for a version", filename); 233 | 234 | let file = t!(File::open(&e.path())); 235 | let reader = flate2::read::GzDecoder::new(file); 236 | let mut archive = tar::Archive::new(reader); 237 | 238 | let entry = t!(archive.entries()).map(|e| t!(e)).filter(|e| { 239 | let path = t!(e.path()); 240 | match path.iter().skip(1).next() { 241 | Some(path) => path == Path::new("version"), 242 | None => false, 243 | } 244 | }).next(); 245 | let mut entry = match entry { 246 | Some(e) => e, 247 | None => return None, 248 | }; 249 | let mut contents = String::new(); 250 | t!(entry.read_to_string(&mut contents)); 251 | Some(contents) 252 | }).next().expect("no archives with a version"); 253 | 254 | println!("current version: {}", current); 255 | 256 | let current_version = current.split(' ').next().unwrap(); 257 | self.current_version = Some(current_version.to_string()); 258 | 259 | // The release process for beta looks like so: 260 | // 261 | // * Force push master branch to beta branch 262 | // * Send a PR to beta, updating release channel 263 | // 264 | // In the window between these two steps we don't actually have release 265 | // artifacts but this script may be run. Try to detect that case here if 266 | // the versions mismatch and panic. We'll try again later once that PR 267 | // has merged and everything should look good. 268 | if (current.contains("nightly") && !prev.contains("nightly")) || 269 | (current.contains("beta") && !prev.contains("beta")) { 270 | panic!("looks like channels are being switched -- was this branch \ 271 | just created and has a pending PR to change the release \ 272 | channel?"); 273 | } 274 | 275 | prev_version == current_version 276 | } 277 | 278 | /// Make sure this release comes with a minimum of components. 279 | /// 280 | /// Note that we already don't merge PRs in rust-lang/rust that don't 281 | /// build cargo, so this cannot realistically fail. 282 | fn assert_all_components_present(&self) { 283 | if self.release != "nightly" { 284 | return 285 | } 286 | let components = t!(self.dl_dir().read_dir()) 287 | .map(|e| t!(e)) 288 | .map(|e| e.file_name().into_string().unwrap()) 289 | .filter(|s| s.contains("x86_64-unknown-linux-gnu")) 290 | .collect::>(); 291 | println!("components in this nightly {:?}", components); 292 | assert!(components.iter().any(|s| s.starts_with("rustc-"))); 293 | assert!(components.iter().any(|s| s.starts_with("rust-std-"))); 294 | assert!(components.iter().any(|s| s.starts_with("cargo-"))); 295 | // For now, produce nightlies even if these are missing. 296 | // assert!(components.iter().any(|s| s.starts_with("rustfmt-"))); 297 | // assert!(components.iter().any(|s| s.starts_with("rls-"))); 298 | // assert!(components.iter().any(|s| s.starts_with("clippy-"))); 299 | } 300 | 301 | fn download_artifacts(&mut self, rev: &str) { 302 | let dl = self.dl_dir(); 303 | drop(fs::remove_dir_all(&dl)); 304 | t!(fs::create_dir_all(&dl)); 305 | 306 | let src = format!("s3://rust-lang-ci2/rustc-builds/{}/", rev); 307 | run(self.aws_s3() 308 | .arg("cp") 309 | .arg("--recursive") 310 | .arg("--only-show-errors") 311 | .arg(&src) 312 | .arg(format!("{}/", dl.display()))); 313 | 314 | let mut files = t!(dl.read_dir()); 315 | if files.next().is_none() { 316 | panic!("appears that this rev doesn't have any artifacts, \ 317 | is this a stable/beta branch awaiting a PR?"); 318 | } 319 | 320 | // Delete residue signature/hash files. These may come around for a few 321 | // reasons: 322 | // 323 | // 1. We died halfway through before uploading the manifest, in which 324 | // case we want to re-upload everything but we don't want to sign 325 | // signatures. 326 | // 327 | // 2. We're making a stable release. The stable release is first signed 328 | // with the dev key and then it's signed with the prod key later. We 329 | // want the prod key to overwrite the dev key signatures. 330 | // 331 | // Also, generate *.gz from *.xz if the former is missing. Since the gz 332 | // and xz tarballs have the same content, we did not deploy the gz files 333 | // from the CI. But rustup users may still expect to get gz files, so we 334 | // are recompressing the xz files as gz here. 335 | for file in t!(dl.read_dir()) { 336 | let file = t!(file); 337 | let path = file.path(); 338 | match path.extension().and_then(|s| s.to_str()) { 339 | // Delete signature/hash files... 340 | Some("asc") | 341 | Some("sha256") => { 342 | t!(fs::remove_file(&path)); 343 | } 344 | // Generate *.gz from *.xz... 345 | Some("xz") => { 346 | let gz_path = path.with_extension("gz"); 347 | if !gz_path.is_file() { 348 | println!("recompressing {}...", gz_path.display()); 349 | let xz = t!(File::open(path)); 350 | let mut xz = xz2::read::XzDecoder::new(xz); 351 | let gz = t!(File::create(gz_path)); 352 | let mut gz = flate2::write::GzEncoder::new(gz, flate2::Compression::best()); 353 | t!(io::copy(&mut xz, &mut gz)); 354 | } 355 | } 356 | _ => {} 357 | } 358 | } 359 | } 360 | 361 | /// Create manifest and sign the artifacts. 362 | fn sign_artifacts(&mut self) { 363 | let build = self.build_dir(); 364 | // This calls `src/tools/build-manifest` from the rustc repo. 365 | run(Command::new(self.rust_dir().join("x.py")) 366 | .current_dir(&build) 367 | .arg("dist") 368 | .arg("hash-and-sign")); 369 | } 370 | 371 | fn upload_signatures(&mut self, rev: &str) { 372 | let dst = format!("s3://rust-lang-ci2/rustc-builds/{}/", rev); 373 | run(self.aws_s3() 374 | .arg("cp") 375 | .arg("--recursive") 376 | .arg("--only-show-errors") 377 | .arg(self.build_dir().join("build/dist/")) 378 | .arg(&dst)); 379 | } 380 | 381 | fn publish_archive(&mut self) { 382 | let bucket = self.secrets["dist"]["upload-bucket"].as_str().unwrap(); 383 | let dir = self.secrets["dist"]["upload-dir"].as_str().unwrap(); 384 | let dst = format!("s3://{}/{}/{}/", bucket, dir, self.date); 385 | run(self.aws_s3() 386 | .arg("cp") 387 | .arg("--recursive") 388 | .arg("--only-show-errors") 389 | .arg("--metadata-directive") 390 | .arg("REPLACE") 391 | .arg("--cache-control") 392 | .arg("public") 393 | .arg(format!("{}/", self.dl_dir().display())) 394 | .arg(&dst)); 395 | } 396 | 397 | fn publish_docs(&mut self) { 398 | let (version, upload_dir) = match &self.release[..] { 399 | "stable" => { 400 | let vers = &self.current_version.as_ref().unwrap()[..]; 401 | (vers, "stable") 402 | } 403 | "beta" => ("beta", "beta"), 404 | "nightly" => ("nightly", "nightly"), 405 | _ => panic!(), 406 | }; 407 | 408 | // Pull out HTML documentation from one of the `rust-docs-*` tarballs. 409 | // For now we just arbitrarily pick x86_64-unknown-linux-gnu. 410 | let docs = self.work.join("docs"); 411 | drop(fs::remove_dir_all(&docs)); 412 | t!(fs::create_dir_all(&docs)); 413 | let target = "x86_64-unknown-linux-gnu"; 414 | 415 | // Unpack the regular documentation tarball. 416 | let tarball_prefix = format!("rust-docs-{}-{}", version, target); 417 | let tarball = format!("{}.tar.gz", self.dl_dir().join(&tarball_prefix).display()); 418 | let tarball_dir = format!("{}/rust-docs/share/doc/rust/html", tarball_prefix); 419 | run(Command::new("tar") 420 | .arg("xf") 421 | .arg(&tarball) 422 | .arg("--strip-components=6") 423 | .arg(&tarball_dir) 424 | .current_dir(&docs)); 425 | 426 | // Construct path to rustc documentation. 427 | let tarball_prefix = format!("rustc-docs-{}-{}", version, target); 428 | let tarball = format!("{}.tar.gz", self.dl_dir().join(&tarball_prefix).display()); 429 | 430 | // Only create and unpack rustc docs if artefacts include tarball. 431 | if Path::new(&tarball).exists() { 432 | let rustc_docs = docs.join("nightly-rustc"); 433 | t!(fs::create_dir_all(&rustc_docs)); 434 | 435 | // Construct the path that contains the documentation inside the tarball. 436 | let tarball_dir = format!("{}/rustc-docs/share/doc/rust/html", tarball_prefix); 437 | let tarball_dir_new = format!("{}/rustc", tarball_dir); 438 | 439 | if t!(Command::new("tar") 440 | .arg("tf") 441 | .arg(&tarball) 442 | .arg(&tarball_dir_new) 443 | .current_dir(&rustc_docs) 444 | .output()) 445 | .status 446 | .success() { 447 | // Unpack the rustc documentation into the new directory. 448 | run(Command::new("tar") 449 | .arg("xf") 450 | .arg(&tarball) 451 | .arg("--strip-components=7") 452 | .arg(&tarball_dir_new) 453 | .current_dir(&rustc_docs)); 454 | } else { 455 | // Unpack the rustc documentation into the new directory. 456 | run(Command::new("tar") 457 | .arg("xf") 458 | .arg(&tarball) 459 | .arg("--strip-components=6") 460 | .arg(&tarball_dir) 461 | .current_dir(&rustc_docs)); 462 | } 463 | 464 | } 465 | 466 | // Upload this to `/doc/$channel` 467 | let bucket = self.secrets["dist"]["upload-bucket"].as_str().unwrap(); 468 | let dst = format!("s3://{}/doc/{}/", bucket, upload_dir); 469 | run(self.aws_s3() 470 | .arg("sync") 471 | .arg("--delete") 472 | .arg("--only-show-errors") 473 | .arg(format!("{}/", docs.display())) 474 | .arg(&dst)); 475 | self.invalidate_docs(upload_dir); 476 | 477 | // Stable artifacts also go to `/doc/$version/ 478 | if upload_dir == "stable" { 479 | let dst = format!("s3://{}/doc/{}/", bucket, version); 480 | run(self.aws_s3() 481 | .arg("sync") 482 | .arg("--delete") 483 | .arg("--only-show-errors") 484 | .arg(format!("{}/", docs.display())) 485 | .arg(&dst)); 486 | self.invalidate_docs(&version); 487 | } 488 | } 489 | 490 | fn invalidate_docs(&self, dir: &str) { 491 | let distribution_id = self.secrets["dist"]["rustdoc-cf-distribution-id"] 492 | .as_str().unwrap(); 493 | let mut cmd = Command::new("aws"); 494 | self.aws_creds(&mut cmd); 495 | cmd.arg("cloudfront") 496 | .arg("create-invalidation") 497 | .arg("--distribution-id").arg(distribution_id); 498 | if dir == "stable" { 499 | cmd.arg("--paths").arg("/*"); 500 | } else { 501 | cmd.arg("--paths").arg(format!("/{0}/*", dir)); 502 | } 503 | run(&mut cmd); 504 | } 505 | 506 | fn publish_release(&mut self) { 507 | let bucket = self.secrets["dist"]["upload-bucket"].as_str().unwrap(); 508 | let dir = self.secrets["dist"]["upload-dir"].as_str().unwrap(); 509 | let dst = format!("s3://{}/{}/", bucket, dir); 510 | run(self.aws_s3() 511 | .arg("cp") 512 | .arg("--recursive") 513 | .arg("--only-show-errors") 514 | .arg(format!("{}/", self.dl_dir().display())) 515 | .arg(&dst)); 516 | } 517 | 518 | fn invalidate_cloudfront(&mut self) { 519 | let json = json!({ 520 | "Paths": { 521 | "Items": [ 522 | "/dist/*", 523 | ], 524 | "Quantity": 4, 525 | }, 526 | "CallerReference": format!("rct-{}", rand::random::()), 527 | }).to_string(); 528 | let dst = self.work.join("payload.json"); 529 | t!(t!(File::create(&dst)).write_all(json.as_bytes())); 530 | 531 | let distribution_id = self.secrets["dist"]["cloudfront-distribution-id"] 532 | .as_str().unwrap(); 533 | let mut cmd = Command::new("aws"); 534 | self.aws_creds(&mut cmd); 535 | run(cmd.arg("cloudfront") 536 | .arg("create-invalidation") 537 | .arg("--invalidation-batch").arg(format!("file://{}", dst.display())) 538 | .arg("--distribution-id").arg(distribution_id)); 539 | } 540 | 541 | fn rust_dir(&self) -> PathBuf { 542 | self.work.join("rust") 543 | } 544 | 545 | fn dl_dir(&self) -> PathBuf { 546 | self.work.join("dl") 547 | } 548 | 549 | fn build_dir(&self) -> PathBuf { 550 | self.work.join("build") 551 | } 552 | 553 | fn aws_s3(&self) -> Command { 554 | let mut cmd = Command::new("aws"); 555 | cmd.arg("s3"); 556 | self.aws_creds(&mut cmd); 557 | return cmd 558 | } 559 | 560 | fn aws_creds(&self, cmd: &mut Command) { 561 | let access = self.secrets["dist"]["aws-access-key-id"].as_str().unwrap(); 562 | let secret = self.secrets["dist"]["aws-secret-key"].as_str().unwrap(); 563 | cmd.env("AWS_ACCESS_KEY_ID", &access) 564 | .env("AWS_SECRET_ACCESS_KEY", &secret); 565 | } 566 | 567 | fn dated_manifest_exists(&mut self) -> bool { 568 | self.handle.reset(); 569 | t!(self.handle.get(true)); 570 | let addr = self.secrets["dist"]["upload-addr"].as_str().unwrap(); 571 | let upload_dir = self.secrets["dist"]["upload-dir"].as_str().unwrap(); 572 | let url = format!("{}/{}/{}/channel-rust-{}.toml", 573 | addr, 574 | upload_dir, 575 | self.date, 576 | self.release); 577 | println!("checking if manifest exists: {}", url); 578 | t!(self.handle.url(&url)); 579 | let mut result = Vec::new(); 580 | { 581 | let mut t = self.handle.transfer(); 582 | 583 | t!(t.write_function(|data| { 584 | result.extend_from_slice(data); 585 | Ok(data.len()) 586 | })); 587 | t!(t.perform()); 588 | } 589 | match t!(self.handle.response_code()) { 590 | 200 => true, 591 | 404 => false, 592 | other => panic!("unexpected response code: {}", other), 593 | } 594 | } 595 | 596 | fn download_manifest(&mut self) -> toml::Value { 597 | self.handle.reset(); 598 | t!(self.handle.get(true)); 599 | let addr = self.secrets["dist"]["upload-addr"].as_str().unwrap(); 600 | let upload_dir = self.secrets["dist"]["upload-dir"].as_str().unwrap(); 601 | let url = format!("{}/{}/channel-rust-{}.toml", 602 | addr, 603 | upload_dir, 604 | self.release); 605 | println!("downloading manifest from: {}", url); 606 | t!(self.handle.url(&url)); 607 | let mut result = Vec::new(); 608 | { 609 | let mut t = self.handle.transfer(); 610 | 611 | t!(t.write_function(|data| { 612 | result.extend_from_slice(data); 613 | Ok(data.len()) 614 | })); 615 | t!(t.perform()); 616 | } 617 | assert_eq!(t!(self.handle.response_code()), 200); 618 | t!(t!(String::from_utf8(result)).parse()) 619 | } 620 | } 621 | 622 | fn run(cmd: &mut Command) { 623 | println!("running {:?}", cmd); 624 | let status = t!(cmd.status()); 625 | if !status.success() { 626 | panic!("failed command:{:?}\n:{}", cmd, status); 627 | } 628 | } 629 | 630 | fn output(cmd: &mut Command) -> String { 631 | println!("running {:?}", cmd); 632 | let output = t!(cmd.output()); 633 | if !output.status.success() { 634 | panic!("failed command:{:?}\n:{}\n\n{}\n\n{}", cmd, output.status, 635 | String::from_utf8_lossy(&output.stdout), 636 | String::from_utf8_lossy(&output.stderr),); 637 | } 638 | 639 | String::from_utf8(output.stdout).unwrap() 640 | } 641 | -------------------------------------------------------------------------------- /rbars/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rbars" 3 | version = "0.1.0" 4 | authors = ["Alex Crichton "] 5 | license = "MIT OR Apache-2.0" 6 | 7 | [dependencies] 8 | toml = "0.4" 9 | handlebars = "1" 10 | serde_json = "1" 11 | -------------------------------------------------------------------------------- /rbars/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate toml; 2 | extern crate handlebars; 3 | extern crate serde_json; 4 | 5 | use std::fs::File; 6 | use std::io::Read; 7 | use std::env; 8 | 9 | use handlebars::Handlebars; 10 | use serde_json::Value as Json; 11 | use toml::Value; 12 | 13 | macro_rules! t { 14 | ($e:expr) => (match $e { 15 | Ok(e) => e, 16 | Err(e) => panic!("{} failed with {:?}", stringify!($e), e), 17 | }) 18 | } 19 | 20 | fn main() { 21 | let mut args = env::args().skip(1); 22 | let config_file = args.next().unwrap(); 23 | let template_file = args.next().unwrap(); 24 | 25 | let mut config = String::new(); 26 | t!(t!(File::open(&config_file)).read_to_string(&mut config)); 27 | let mut template = String::new(); 28 | t!(t!(File::open(&template_file)).read_to_string(&mut template)); 29 | 30 | let mut handlebars = Handlebars::new(); 31 | t!(handlebars.register_template_string("template", &template)); 32 | 33 | let data = convert(t!(config.parse())); 34 | let data = t!(handlebars.render("template", &data)); 35 | println!("{}", data); 36 | } 37 | 38 | // we cannot use `serde_json::to_value` because we want Datetime to be string. 39 | fn convert(toml: Value) -> Json { 40 | match toml { 41 | Value::String(s) => Json::String(s), 42 | Value::Integer(i) => i.into(), 43 | Value::Float(f) => f.into(), 44 | Value::Boolean(b) => Json::Bool(b), 45 | Value::Array(arr) => Json::Array(arr.into_iter().map(convert).collect()), 46 | Value::Table(table) => Json::Object(table.into_iter().map(|(k, v)| { 47 | (k, convert(v)) 48 | }).collect()), 49 | Value::Datetime(dt) => Json::String(dt.to_string()), 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /run-dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -ex 4 | 5 | docker build \ 6 | --tag rust-central-station \ 7 | --rm \ 8 | . 9 | 10 | exec docker run \ 11 | --volume `pwd`/data:/data \ 12 | --volume `pwd`/data/letsencrypt:/etc/letsencrypt \ 13 | --env DEV=1 \ 14 | --publish 8080:80 \ 15 | --rm \ 16 | rust-central-station \ 17 | "$@" 18 | -------------------------------------------------------------------------------- /run-prod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -ex 4 | 5 | docker pull alexcrichton/rust-central-station 6 | 7 | mkdir -p data/logs/nginx 8 | exec docker run \ 9 | --volume `pwd`/data:/data \ 10 | --volume `pwd`/data/letsencrypt:/etc/letsencrypt \ 11 | --volume `pwd`/data/logs:/var/log \ 12 | --publish 80:80 \ 13 | --publish 443:443 \ 14 | --rm \ 15 | --detach \ 16 | alexcrichton/rust-central-station 17 | -------------------------------------------------------------------------------- /secrets.toml.example: -------------------------------------------------------------------------------- 1 | # The travis/appveyor tokens for cancelbot to cancel builds on Travis/AppVeyor 2 | # that don't need to be running. 3 | [cancelbot] 4 | azure-pipelines-token = "azure-pipelines" 5 | azure-pipelines-2-token = "azure-pipelines2" 6 | 7 | # Homu's GH access token to write comments and such, as well as an OAuth 8 | # application to do things like rollups and synchronizations. 9 | [homu.github] 10 | access-token = "access" 11 | app-client-id = "id" 12 | app-client-secret = "secret" 13 | 14 | # Homu's SSH key to do git operations 15 | [homu.ssh] 16 | ssh-key = "key" 17 | 18 | # Shared webhook secrets for all homu's repos, probably generated with: 19 | # 20 | # openssl rand -hex 40 21 | [homu.repo-secrets] 22 | rust = "sekrit" 23 | cargo = "sekrit" 24 | libc = "sekrit" 25 | compiler-builtins = "sekrit" 26 | regex = "sekrit" 27 | stdarch = "sekrit" 28 | chalk = "sekrit" 29 | 30 | # Used to fetch a cert from letsencrypt 31 | [nginx] 32 | hostname = "buildbot.rust-lang.org" 33 | hostname_alias = "bors.rust-lang.org" 34 | email = "admin@rust-lang.org" 35 | 36 | # Used to synchronize mailing lists 37 | [mailgun] 38 | token = "mailgun" 39 | 40 | # Used to synchronize GitHub teams with the team repo. 41 | [sync-github] 42 | # The token needs the `admin:org` scope 43 | token = "github" 44 | 45 | # Distribution pieces used to configure releases. 46 | [dist] 47 | 48 | # File with the actual key as well as the path to a file with the password 49 | gpg-key = "/data/gpg.key" 50 | gpg-password-file = "/data/gpg.password" 51 | 52 | # Remote HTTP host artifacts will be uploaded to. Note that this is *not* the 53 | # same as what's configured in `config.toml` for rustbuild, it's just the *host* 54 | # that we're uploading to and going to be looking at urls from. 55 | # 56 | # This is used in a number of places such as: 57 | # 58 | # * downloading manifests 59 | # * urls in manifests 60 | # 61 | # and possibly more. Note that most urls end up appending `upload-dir` below to 62 | # this address specified. This address should not have a trailing slash. 63 | upload-addr = "https://static.rust-lang.org" 64 | 65 | # The S3 bucket and directory that release artifacts will be uploaded to. 66 | upload-bucket = "dev-static-rust-lang-org" 67 | upload-bucket-region = "us-west-1" 68 | upload-dir = "dist" 69 | 70 | # Credentials for S3 downloads/uploads. As of this writing the credentials need 71 | # to have permissions to: 72 | # 73 | # * upload/download/list to the `rust-lang-ci` bucket 74 | # * upload/download/list to the bucket specified above 75 | # * create a cloudfront invalidation of the id below 76 | aws-access-key-id = "key" 77 | aws-secret-key = "key" 78 | 79 | # CloudFront distribution that we're going to be invalidating. 80 | cloudfront-distribution-id = "id" 81 | -------------------------------------------------------------------------------- /tq/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tq" 3 | version = "0.1.0" 4 | authors = ["Alex Crichton "] 5 | license = "MIT OR Apache-2.0" 6 | 7 | [dependencies] 8 | toml = "0.4" 9 | -------------------------------------------------------------------------------- /tq/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate toml; 2 | 3 | use std::io::{self, Read}; 4 | use std::env; 5 | 6 | use toml::Value; 7 | 8 | fn main() { 9 | let mut input = String::new(); 10 | io::stdin().read_to_string(&mut input).unwrap(); 11 | 12 | let value: Value = input.parse().expect("failed to parse input"); 13 | 14 | for arg in env::args().skip(1) { 15 | let mut value = &value; 16 | for part in arg.split('.') { 17 | value = &value[part]; 18 | } 19 | match *value { 20 | Value::String(ref s) => println!("{}", s), 21 | Value::Integer(i) => println!("{}", i), 22 | Value::Float(f) => println!("{}", f), 23 | Value::Boolean(b) => println!("{}", b), 24 | Value::Datetime(ref s) => println!("{}", s), 25 | Value::Array(_) | Value::Table(_) => { 26 | panic!("cannot print array/table"); 27 | } 28 | } 29 | } 30 | } 31 | --------------------------------------------------------------------------------