diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0c2b407..1593235 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,5 @@ +# Overview + * If you have found a discrepancy in documented and observed behaviour, that is a bug. Feel free to [report it as an issue](https://github.com/mpalmer/action-validator/issues), providing @@ -8,3 +10,84 @@ request](https://github.com/mpalmer/action-validator/pulls). * At all times, abide by the Code of Conduct (CODE_OF_CONDUCT.md). + +--- + +# Environment Setup + +## Install Rust +Firstly, you'll need make any changes to the core functionality of this project. We recommend use `rustup`, on the recommendation of the rust team. You can find the installation instructions [here](https://www.rust-lang.org/tools/install). + +To confirm that rust is installed, run the `cargo` command. If you don't receive the help docs output, you may need to add rust to your shell rc file. + +## Git Submodule Setup +This repository uses [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). Specifically for the use of [schemastore](https://github.com/SchemaStore/schemastore). + +To setup the git submodule after cloning this repo to your local, you'll want to run the following commands: +1. `git submodule init` +2. `git submodule update` + +It should look similar to the output below. + +```bash +❯ git submodule init +Submodule 'src/schemastore' (https://github.com/SchemaStore/schemastore) registered for path 'src +/schemastore' +❯ git submodule update +Cloning into '/Users/someuser/action-validator/src/schemastore'... +Submodule path 'src/schemastore': checked out 'd3e6ab7727380b214acbab05570fb09a3e5d2dfc' +``` + +At this point, you should be all set to `cargo run`! If you run into any issues here, please [create an issue](https://github.com/mpalmer/action-validator/issues/new/choose). + +# Running the Validator Locally + +## `cargo run [FILE] -- [OPTIONS]` +`cargo run` will create a _debug_ executable and run the project. If this is your first time running the command, cargo will compile the development binary with `cargo build`. This will install all of the dependencies and create the debug binary `action-validator` in the `/target/debug/` directory. `cargo run` will then invoke this binary after creation. + +One caveat if you're running with `cargo run`: if you want to supply the program with options, you need to use the `--` operator between `cargo run` and your provided options. This let's cargo know which flags are meant for cargo, and which are meant for the executable. + +## `cargo build` && `./target/debug/action-validator [OPTIONS]` +As discussed in the prior section, `cargo build` install dependencies (if they're not cached) and build the development binary. This binary can then be invoked directly by running `./target/debug/action-validator`. This does **not** require the use of the `--` operator in between the binary and any provided options. + +## Try It Yourself! + +Run the command `cargo run -- --help`. You should see an output similar to the following. +```bash +❯ cargo run -- --help + Finished dev [unoptimized + debuginfo] target(s) in 0.05s + Running `target/debug/action-validator --help` +A validator for GitHub Action and Workflow YAML files + +Usage: action-validator [OPTIONS] [path_to_action_yaml]... + +Arguments: + [path_to_action_yaml]... Input file + +Options: + -v, --verbose Be more verbose + -h, --help Print help information + -V, --version Print version information +``` + +# Writing Tests +All tests live in the `tests` directory. Currently, this project implements snapshot testing, +but that's not to say you couldn't write unit or integration tests with the current structure. +To run the tests, simply run `cargo test` from the root directory. If you want to test a specific +feature, you can add the `-F {feature}` flag (e.g. `cargo test -F remote-checks`). + +## Unit/Integration Tests +As of this writing, there are no unit or integration tests. If you are looking to write some, please +follow the directions in [this guide](https://doc.rust-lang.org/book/ch11-01-writing-tests.html). + +## Snapshot Tests +A snapshot test is performed when we execute the cli and capture `stdout`, `stderr`, and/or an exit code. +When the tests is run, the results of the test must exactly match those of the previous run. For this project, +the snapshot tests are named in the format `{next_id}_{whats_being_tested}` (e.g. `011_remote_checks_failure`). + +If you have made changes which will change the output of the program and cause snapshots to fail, you can run +`cargo test -F save-snapshots`. This feature causes the executed command to save the `stdout`, `stderr`, and/or +exit code to the specified testing directory. + +If you are writing a net new test, you will need to create the test directory with your workflow or action file. +Once you're done, you can save the results to that directy by running `cargo test -F save-snapshots`. diff --git a/Cargo.lock b/Cargo.lock index dc638ed..87ed8cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,9 +6,13 @@ version = 3 name = "action-validator" version = "0.0.0-git" dependencies = [ + "assert_cmd", "clap", "console_error_panic_hook", "glob", + "regex", + "reqwest", + "rstest", "serde", "serde-wasm-bindgen", "serde_json", @@ -30,9 +34,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -46,11 +50,25 @@ dependencies = [ "libc", ] +[[package]] +name = "assert_cmd" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9834fcc22e0874394a010230586367d4a3e9f11b560f469262678547e1d2575e" +dependencies = [ + "bstr", + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" @@ -79,17 +97,35 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bstr" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" +dependencies = [ + "memchr", + "once_cell", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -111,9 +147,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.32" +version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" +checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" dependencies = [ "bitflags", "clap_derive", @@ -126,9 +162,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.21" +version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" dependencies = [ "heck", "proc-macro-error", @@ -139,9 +175,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" dependencies = [ "os_str_bytes", ] @@ -166,6 +202,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -174,9 +220,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cxx" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" +checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72" dependencies = [ "cc", "cxxbridge-flags", @@ -186,9 +232,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" +checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613" dependencies = [ "cc", "codespan-reporting", @@ -201,32 +247,59 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" +checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97" [[package]] name = "cxxbridge-macro" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" +checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "downcast-rs" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + [[package]] name = "erased-serde" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ca605381c017ec7a5fef5e548f1cfaa419ed0f6df6367339300db74c92aa7d" +checksum = "4f2b0c2380453a92ea8b6c8e5f64ecaafccddde8ceab55ff7a8ac1029f894569" dependencies = [ "serde", ] @@ -262,21 +335,145 @@ dependencies = [ "regex", ] +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" + +[[package]] +name = "futures-executor" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" + +[[package]] +name = "futures-macro" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" + +[[package]] +name = "futures-task" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "js-sys", @@ -287,9 +484,28 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "h2" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] [[package]] name = "hashbrown" @@ -299,9 +515,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -312,6 +528,83 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.53" @@ -338,11 +631,10 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -357,39 +649,57 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "io-lifetimes" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.45.0", ] +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] -name = "itoa" -version = "0.4.8" +name = "itertools" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" @@ -419,6 +729,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.139" @@ -450,22 +766,52 @@ dependencies = [ ] [[package]] -name = "matches" -version = "0.1.9" +name = "memchr" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "memchr" -version = "2.4.1" +name = "mime" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.45.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", @@ -473,18 +819,73 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "openssl" +version = "0.10.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] [[package]] name = "os_str_bytes" @@ -536,6 +937,51 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "predicates" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "itertools", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" + +[[package]] +name = "predicates-tree" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -562,9 +1008,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -608,42 +1054,138 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" -version = "1.5.4" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "reqwest" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rstest" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07f2d176c472198ec1e6551dc7da28f1c089652f66a7b722676c2238ebc0edf" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7229b505ae0706e64f37ffc54a9c163e11022a6636d58fe1f3f52018257ff9f7" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "rustc_version", + "syn", + "unicode-ident", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] [[package]] name = "rustix" -version = "0.36.6" +version = "0.36.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] [[package]] name = "scoped-tls" @@ -653,15 +1195,44 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scratch" -version = "1.0.3" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "3a382c72b4ba118526e187430bb4963cd6d55051ebf13d9b25574d379cc98d20" dependencies = [ "serde_derive", ] @@ -679,9 +1250,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "1ef476a5790f0f6decbc66726b6e5d63680ed518283e64c7df415989d880954f" dependencies = [ "proc-macro2", "quote", @@ -690,23 +1261,35 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.68" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ - "itoa 0.4.8", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", "ryu", "serde", ] [[package]] name = "serde_yaml" -version = "0.9.17" +version = "0.9.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567" +checksum = "f82e6c8c047aa50a7328632d067bcae6ef38772a79e28daf32f735e0e4f3dd10" dependencies = [ "indexmap", - "itoa 1.0.5", + "itoa", "ryu", "serde", "unsafe-libyaml", @@ -714,9 +1297,28 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.7" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] [[package]] name = "strsim" @@ -726,56 +1328,148 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.42.0", +] + [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] +[[package]] +name = "termtree" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" + [[package]] name = "tinyvec" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "windows-sys 0.45.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] @@ -788,9 +1482,9 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unsafe-libyaml" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7ed8ba44ca06be78ea1ad2c3682a43349126c8818054231ee6f4748012aed2" +checksum = "ad2024452afd3874bf539695e04af6732ba06517424dbf958fdb16a01f3bef6c" [[package]] name = "uritemplate-next" @@ -803,13 +1497,12 @@ dependencies = [ [[package]] name = "url" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", ] @@ -847,17 +1540,42 @@ dependencies = [ "uuid", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" @@ -1005,44 +1723,77 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] diff --git a/Cargo.toml b/Cargo.toml index 3dfd387..11220c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,11 @@ include = [ "/src/schemastore/src/schemas/json/github-action.json" ] version = "0.0.0-git" -authors = ["Matt Palmer "] +authors = [ + "Matt Palmer ", + "Ben Heidemann ", + "Austin Ward " +] edition = "2021" # If this is changed, .github/workflows/qa.yml build matrix needs updating as well rust-version = "1.60.0" @@ -21,6 +25,8 @@ crate-type = ["cdylib", "rlib"] [features] js = ["console_error_panic_hook", "valico/js"] +remote-checks = ["reqwest"] +save-snapshots = [] [dependencies] clap = { version = "4.0", features = ["derive"] } @@ -40,9 +46,13 @@ wasm-bindgen = "0.2.63" console_error_panic_hook = { version = "0.1.6", optional = true } serde-wasm-bindgen = "0.4.5" +regex = "1.7.1" +reqwest = { version = "0.11.14", features = ["blocking"], optional = true } [dev-dependencies] wasm-bindgen-test = "0.3.13" +rstest = "0.16.0" +assert_cmd = "2.0.8" [profile.release] # Tell `rustc` to optimize for small code size. diff --git a/src/lib.rs b/src/lib.rs index 0aad0a8..a23744f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,14 +2,16 @@ mod config; mod log; mod schemas; mod utils; +mod validators; mod validation_error; mod validation_state; -use config::{ActionType, RunConfig}; + use validation_error::ValidationError; use validation_state::ValidationState; pub use crate::config::CliConfig; +use config::{ActionType, RunConfig}; use crate::schemas::{validate_as_action, validate_as_workflow}; #[cfg(not(feature = "js"))] use glob::glob; @@ -148,7 +150,7 @@ fn run(config: &RunConfig) -> ValidationState { validate_paths(&doc, &mut state); validate_job_needs(&doc, &mut state); - + validators::job_uses::validate(&doc, &mut state); state } }, diff --git a/src/validators/job_uses.rs b/src/validators/job_uses.rs new file mode 100644 index 0000000..54b79c0 --- /dev/null +++ b/src/validators/job_uses.rs @@ -0,0 +1,109 @@ +use regex::{Regex, Captures}; + +use crate::validation_state::ValidationState; +use crate::validators::models; + +/// A simple enum providing exhaustive matching to [`_action_type`]. +enum ActionType { + Action, + Docker, + Path, +} + +/// Validates all `jobs..steps[*].uses` values in the provided workflow file(s). This +/// validator has remote checks which will only run if the `remote-checks` feature flag is enabled. +/// If the feature flag is disabled, then this validation confirms the shape of the uses statement +/// matches GitHub's expected format +/// ([more here](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses)). +/// +/// If the feature flag is enabled, the above checks will be validated in addition to the remote +/// checks. The [`models::Action`] and [`models::Docker`] structs both implement remote checks. +/// +/// # Arguments +/// * doc - The parsed workflow document, to be validated. +/// * state - The [`ValidationState`] to which will be used to provide validation errors. +pub fn validate(doc: &serde_json::Value, state: &mut ValidationState) { + // If this regex doesn't compile, that should be considered a compile-time + // error. As such, we should unwrap to purposefully panic in the event of + // a malformed regex. + let pattern = vec![ + models::Action::PATTERN, + models::Docker::PATTERN, + models::Path::PATTERN, + ].join("|"); + let r = Regex::new(format!(r"(?x)^{pattern}$").as_str()).unwrap(); + + let default_map = &serde_json::Map::::new(); + let jobs_step_uses = doc["jobs"] + .as_object() + .unwrap_or(default_map) + .iter() + .flat_map(|(job_name, job)| { + Some((job_name, job["steps"].as_array()?.iter())) + }) + .flat_map(|(job_name, steps)| { + steps.map(|step| { + Some((job_name.to_owned(), step["uses"].as_str()?)) + }) + }) + .filter_map(|o| o) + .collect::>(); + + for (job_name, uses) in jobs_step_uses { + let origin = format!("jobs/{job_name}/steps/uses/{uses}"); + let captures_op = &r.captures(uses); + + let uses_type = vec![ + ActionType::Action, ActionType::Docker, ActionType::Path, + ] + .into_iter() + .find_map(|action_type| { + Some(_action_type(action_type, &origin, captures_op.as_ref()?)) + }) + .unwrap_or(Box::new(models::Invalid{ + uses: String::from(uses), + origin: origin.to_owned(), + })); + + if let Err(v) = uses_type.validate() { + state.errors.push(v); + } + } +} + +/// Matches on the provided `action_type` and extracts the `captures` named capture groups for that +/// implementation of the `Uses` trait. +/// +/// # Arguments +/// * `action_type` - An enum indicating if the action type is `Action`, `Docker`, or `Path`. +/// * `origin` - The origin path of the `uses` string being validated from the workflow. +/// * `captures` - The capture group which matched the validation regex. +fn _action_type<'a>( + action_type: ActionType, + origin: &String, + captures: &Captures<'a>, +) -> Box> { + let origin = origin.to_owned(); + let uses = String::from(&captures[0]); + match action_type { + ActionType::Path => Box::new(models::Path{uses, origin}), + ActionType::Docker => Box::new(models::Docker { + uses, + origin, + // The `image` capture group is guranteed to exist when `Docker` does. + image: String::from(captures.name("image").unwrap().as_str()), + url: captures.name("url").map(|v| String::from(v.as_str())), + tag: captures.name("tag").map(|v| String::from(v.as_str())), + }), + ActionType::Action => Box::new(models::Action { + uses, + origin, + // The `owner`, `repo`, and `reference` capture groups are guranteed + // to exist when `Action` does. + owner: String::from(captures.name("owner").unwrap().as_str()), + repo: String::from(captures.name("repo").unwrap().as_str()), + reference: String::from(captures.name("ref").unwrap().as_str()), + path: captures.name("path").map(|v| String::from(v.as_str())), + }), + } +} diff --git a/src/validators/mod.rs b/src/validators/mod.rs new file mode 100644 index 0000000..6377967 --- /dev/null +++ b/src/validators/mod.rs @@ -0,0 +1,2 @@ +pub mod models; +pub mod job_uses; diff --git a/src/validators/models.rs b/src/validators/models.rs new file mode 100644 index 0000000..97a3249 --- /dev/null +++ b/src/validators/models.rs @@ -0,0 +1,201 @@ +use crate::validation_error::ValidationError; + +#[cfg(feature="remote-checks")] +use reqwest::{blocking::Response, StatusCode, blocking::Client}; + +/// A method to perform a synchronous get request and return it's result. +/// +/// # Arguments +/// * url - The FQDN where the GET request will be sent. +#[cfg(feature="remote-checks")] +fn _get_request(url: String) -> Result { + Client::new().get(url).send() +} + +/// This trait provides a common method for all action type implementations to validate said action +/// type. An implementation of `validate` should return a [`ValidationError`] if one were to occur, +/// the validation error should be pushed onto the validation error stack. +pub trait Uses<'a>: std::fmt::Debug { + fn validate(&self) -> Result<(), ValidationError>; +} + +#[derive(Debug)] +pub struct Action { + pub uses: String, + pub origin: String, + pub owner: String, + pub repo: String, + pub path: Option, + pub reference: String, +} + +impl Action { + pub const PATTERN: &str = r"(?P.{0}(?P[^/]*)/(?P[^/]*)(/(?P.*))?@(?P.*))"; +} + +impl Uses<'_> for Action { + #[cfg(not(feature="remote-checks"))] + fn validate(&self) -> Result<(), ValidationError> { + Ok(()) + } + + /// Remote-check validation logic for GitHub Actions. If the destructured action is able to be + /// retrieved from the repositories tree, then the action exists. Otherwise, the action is + /// formatted properly but does not exist. + #[cfg(feature="remote-checks")] + fn validate(&self) -> Result<(), ValidationError> { + let response = _get_request(format!( + "https://github.com/{0}/{1}/tree/{2}/{3}", + self.owner, + self.repo, + self.reference, + self.path.as_ref().unwrap_or(&String::new()), + )); + if let Some(r) = response.ok() { + if r.status() == 200 { + return Ok(()); + } + return Err(ValidationError::Unknown { + code: "action_not_found".into(), + detail: Some(format!("Could not find action: {}", self.uses)), + path: self.origin.to_owned(), + title: "Action Not Found".into(), + }); + } + + Ok(()) + } +} + +#[derive(Debug)] +pub struct Docker { + pub uses: String, + pub origin: String, + pub image: String, + pub url: Option, + pub tag: Option, +} + +impl Docker { + pub const PATTERN: &str = r"(?P.{0}(?:docker://)(?P([^/:]+)\.([^/:]+)/)?(?P[^:]+)(?::(?P.+))?)"; +} + +impl Uses<'_> for Docker { + #[cfg(not(feature="remote-checks"))] + fn validate(&self) -> Result<(), ValidationError> { + Ok(()) + } + + /// Remote-check validation logic for Docker images. If the destructured image is able to be + /// retrieved from Docker Hub or using the registries v2 endpoints, then the image exists. If + /// the image results in a 401 (UNAUTHORIZED), the image _could_ exist. In this case, + /// action-validator will assume the image exists. + #[cfg(feature="remote-checks")] + fn validate(&self) -> Result<(), ValidationError> { + let mut image = self.image.to_owned(); + + // If there's no prefix and the image is in docker hub, then it means the image is a + // library image. + if self.url.is_none() && !image.contains("/") { + image = format!("library/{image}"); + } + + let url = match (self.url.as_ref(), self.tag.as_ref()) { + // Lookup V2 protocol tag + (Some(url), Some(tag)) => format!("https://{url}/v2/{image}/manifests/{tag}"), + // Lookup V2 protocol image + (Some(url), None) => format!("https://{url}/v2/{image}/tags/list"), + // Lookup docker hub tag + (None, Some(tag)) => { + format!("https://registry.hub.docker.com/v2/repositories/{image}/tags/{tag}") + }, + // Lookup docker hub image + (None, None) => { + format!("https://registry.hub.docker.com/v2/repositories/{image}") + }, + }; + + if let Some(r) = _get_request(url).ok() { + let status = r.status(); + if status == StatusCode::OK { + return Ok(()); + } else if status == StatusCode::UNAUTHORIZED { + // In this case, pull access requires authorization. There are simple cases that + // only require the bearer token retrieval followed by manifest access, but there + // are also others that require user credentials. For now, we should assume that + // the tag tag is valid. We could perform authentication, but that would requrie + // user creds and adds a whole lot of scope to this feature. + return Ok(()); + } else if status == StatusCode::NOT_FOUND { + return Err(ValidationError::Unknown { + code: "docker_action_not_found".into(), + detail: Some(format!("Could not find the docker action: {}", self.uses)), + path: self.origin.to_owned(), + title: "Docker Action Not Found".into(), + }); + } else { + return Err(ValidationError::Unknown { + code: "unexpected_server_response".into(), + detail: Some(format!("Unexpected server response: {}", status)), + path: self.origin.to_owned(), + title: "Docker Action Not Found".into(), + }); + } + } + + // The remote-check request failed. If the user has the `remote-checks` feature enable, + // they likely want to know about these failures. We can mark this as an error. + Err(ValidationError::Unknown { + code: "unexpected_server_response".into(), + detail: Some(format!("Could not find docker action: {}", self.uses)), + path: self.origin.to_owned(), + title: "Docker Action Not Found".into(), + }) + } +} + +#[derive(Debug)] +pub struct Path { + pub uses: String, + pub origin: String, +} + +impl Path { + pub const PATTERN: &str = r"(?P.{0}\./([^/]+/?)+)"; +} + +impl Uses<'_> for Path { + /// Validates that the supplied path exists. + fn validate(&self) -> Result<(), ValidationError> { + if std::path::Path::new(self.uses.as_str()).exists() { + return Ok(()); + } + Err(ValidationError::Unknown { + code: "action_not_found".into(), + detail: Some(format!("The action path does not exist: {}", self.uses)), + path: self.origin.to_owned(), + title: "Action Not Found".into(), + }) + } +} + +#[derive(Debug)] +pub struct Invalid { + pub uses: String, + pub origin: String, +} + +impl Uses<'_> for Invalid { + /// If the provided action is not a [`Docker`], [`Action`], or [`Path`], the action is invalid. + /// When this [`Uses`] implementation is created, the validation will always return a + /// validation error. + fn validate(&self) -> Result<(), ValidationError> { + let uses = self.uses.to_owned(); + Err(ValidationError::InvalidGlob { + code: "invalid_uses".into(), + detail: Some(format!("The `uses` {uses} is invalid.")), + path: self.origin.to_owned(), + title: "Invalid Uses".into(), + }) + } +} diff --git a/test/004_failing_globs/stdout b/test/004_failing_globs/stdout deleted file mode 100644 index 4179a2f..0000000 --- a/test/004_failing_globs/stdout +++ /dev/null @@ -1 +0,0 @@ -Fatal error validating 004_failing_globs/glob.yml diff --git a/test/007_funky_syntax/stdout b/test/007_funky_syntax/stdout deleted file mode 100644 index f57da30..0000000 --- a/test/007_funky_syntax/stdout +++ /dev/null @@ -1 +0,0 @@ -Fatal error validating 007_funky_syntax/rust-check.yml diff --git a/test/008_job_dependencies/stdout b/test/008_job_dependencies/stdout deleted file mode 100644 index 73dc3ee..0000000 --- a/test/008_job_dependencies/stdout +++ /dev/null @@ -1 +0,0 @@ -Fatal error validating 008_job_dependencies/test.yml diff --git a/test/009_multi_file/stdout b/test/009_multi_file/stdout deleted file mode 100644 index ec1a3fd..0000000 --- a/test/009_multi_file/stdout +++ /dev/null @@ -1 +0,0 @@ -Fatal error validating 009_multi_file/xinvalid.yml diff --git a/test/001_basic_workflow/test.yml b/tests/001_basic_workflow/test.yml similarity index 100% rename from test/001_basic_workflow/test.yml rename to tests/001_basic_workflow/test.yml diff --git a/test/001_basic_workflow/validation_state.snap.json b/tests/001_basic_workflow/validation_state.snap.json similarity index 100% rename from test/001_basic_workflow/validation_state.snap.json rename to tests/001_basic_workflow/validation_state.snap.json diff --git a/test/002_basic_action/action.yml b/tests/002_basic_action/action.yml similarity index 100% rename from test/002_basic_action/action.yml rename to tests/002_basic_action/action.yml diff --git a/test/002_basic_action/validation_state.snap.json b/tests/002_basic_action/validation_state.snap.json similarity index 100% rename from test/002_basic_action/validation_state.snap.json rename to tests/002_basic_action/validation_state.snap.json diff --git a/test/003_successful_globs/glob.yml b/tests/003_successful_globs/glob.yml similarity index 88% rename from test/003_successful_globs/glob.yml rename to tests/003_successful_globs/glob.yml index 6608e8b..2981f5b 100644 --- a/test/003_successful_globs/glob.yml +++ b/tests/003_successful_globs/glob.yml @@ -3,7 +3,7 @@ name: Test on: push: paths: - - 003_successful_globs/* + - ./tests/003_successful_globs/* defaults: run: diff --git a/test/003_successful_globs/validation_state.snap.json b/tests/003_successful_globs/validation_state.snap.json similarity index 100% rename from test/003_successful_globs/validation_state.snap.json rename to tests/003_successful_globs/validation_state.snap.json diff --git a/test/004_failing_globs/exitcode b/tests/004_failing_globs/exitcode similarity index 100% rename from test/004_failing_globs/exitcode rename to tests/004_failing_globs/exitcode diff --git a/test/004_failing_globs/glob.yml b/tests/004_failing_globs/glob.yml similarity index 89% rename from test/004_failing_globs/glob.yml rename to tests/004_failing_globs/glob.yml index 7c4d27e..fa2a7c8 100644 --- a/test/004_failing_globs/glob.yml +++ b/tests/004_failing_globs/glob.yml @@ -3,7 +3,7 @@ name: Bad globs, no biscuit on: push: paths: - - 004_bad_globs/*.txt + - ./tests/004_bad_globs/*.txt defaults: run: diff --git a/test/004_failing_globs/stderr b/tests/004_failing_globs/stderr similarity index 70% rename from test/004_failing_globs/stderr rename to tests/004_failing_globs/stderr index 2171874..f7e987a 100644 --- a/test/004_failing_globs/stderr +++ b/tests/004_failing_globs/stderr @@ -3,13 +3,13 @@ Validation failed: ValidationState { Workflow, ), file_path: Some( - "004_failing_globs/glob.yml", + "./tests/004_failing_globs/glob.yml", ), errors: [ NoFilesMatchingGlob { code: "glob_not_matched", detail: Some( - "Glob \"004_bad_globs/*.txt\" in /on/push/paths does not match any files", + "Glob \"./tests/004_bad_globs/*.txt\" in /on/push/paths does not match any files", ), path: "/on/push/paths", title: "Glob does not match any files", diff --git a/tests/004_failing_globs/stdout b/tests/004_failing_globs/stdout new file mode 100644 index 0000000..2455d96 --- /dev/null +++ b/tests/004_failing_globs/stdout @@ -0,0 +1 @@ +Fatal error validating ./tests/004_failing_globs/glob.yml diff --git a/test/004_failing_globs/validation_state.snap.json b/tests/004_failing_globs/validation_state.snap.json similarity index 100% rename from test/004_failing_globs/validation_state.snap.json rename to tests/004_failing_globs/validation_state.snap.json diff --git a/test/005_conditional_step_in_action/action.yml b/tests/005_conditional_step_in_action/action.yml similarity index 100% rename from test/005_conditional_step_in_action/action.yml rename to tests/005_conditional_step_in_action/action.yml diff --git a/test/005_conditional_step_in_action/validation_state.snap.json b/tests/005_conditional_step_in_action/validation_state.snap.json similarity index 100% rename from test/005_conditional_step_in_action/validation_state.snap.json rename to tests/005_conditional_step_in_action/validation_state.snap.json diff --git a/test/006_workflow_dispatch_inputs_options/test.yml b/tests/006_workflow_dispatch_inputs_options/test.yml similarity index 100% rename from test/006_workflow_dispatch_inputs_options/test.yml rename to tests/006_workflow_dispatch_inputs_options/test.yml diff --git a/test/006_workflow_dispatch_inputs_options/validation_state.snap.json b/tests/006_workflow_dispatch_inputs_options/validation_state.snap.json similarity index 100% rename from test/006_workflow_dispatch_inputs_options/validation_state.snap.json rename to tests/006_workflow_dispatch_inputs_options/validation_state.snap.json diff --git a/test/007_funky_syntax/exitcode b/tests/007_funky_syntax/exitcode similarity index 100% rename from test/007_funky_syntax/exitcode rename to tests/007_funky_syntax/exitcode diff --git a/test/007_funky_syntax/rust-check.yml b/tests/007_funky_syntax/rust-check.yml similarity index 100% rename from test/007_funky_syntax/rust-check.yml rename to tests/007_funky_syntax/rust-check.yml diff --git a/test/007_funky_syntax/stderr b/tests/007_funky_syntax/stderr similarity index 91% rename from test/007_funky_syntax/stderr rename to tests/007_funky_syntax/stderr index 17c1d62..fbafbf5 100644 --- a/test/007_funky_syntax/stderr +++ b/tests/007_funky_syntax/stderr @@ -3,7 +3,7 @@ Validation failed: ValidationState { Workflow, ), file_path: Some( - "007_funky_syntax/rust-check.yml", + "./tests/007_funky_syntax/rust-check.yml", ), errors: [ Parse { diff --git a/tests/007_funky_syntax/stdout b/tests/007_funky_syntax/stdout new file mode 100644 index 0000000..a8b577f --- /dev/null +++ b/tests/007_funky_syntax/stdout @@ -0,0 +1 @@ +Fatal error validating ./tests/007_funky_syntax/rust-check.yml diff --git a/test/007_funky_syntax/validation_state.snap.json b/tests/007_funky_syntax/validation_state.snap.json similarity index 100% rename from test/007_funky_syntax/validation_state.snap.json rename to tests/007_funky_syntax/validation_state.snap.json diff --git a/test/008_job_dependencies/exitcode b/tests/008_job_dependencies/exitcode similarity index 100% rename from test/008_job_dependencies/exitcode rename to tests/008_job_dependencies/exitcode diff --git a/test/008_job_dependencies/stderr b/tests/008_job_dependencies/stderr similarity index 88% rename from test/008_job_dependencies/stderr rename to tests/008_job_dependencies/stderr index a8d64bf..d859143 100644 --- a/test/008_job_dependencies/stderr +++ b/tests/008_job_dependencies/stderr @@ -3,7 +3,7 @@ Validation failed: ValidationState { Workflow, ), file_path: Some( - "008_job_dependencies/test.yml", + "./tests/008_job_dependencies/test.yml", ), errors: [ UnresolvedJob { diff --git a/tests/008_job_dependencies/stdout b/tests/008_job_dependencies/stdout new file mode 100644 index 0000000..3576abf --- /dev/null +++ b/tests/008_job_dependencies/stdout @@ -0,0 +1 @@ +Fatal error validating ./tests/008_job_dependencies/test.yml diff --git a/test/008_job_dependencies/test.yml b/tests/008_job_dependencies/test.yml similarity index 100% rename from test/008_job_dependencies/test.yml rename to tests/008_job_dependencies/test.yml diff --git a/test/008_job_dependencies/validation_state.snap.json b/tests/008_job_dependencies/validation_state.snap.json similarity index 100% rename from test/008_job_dependencies/validation_state.snap.json rename to tests/008_job_dependencies/validation_state.snap.json diff --git a/test/009_multi_file/exitcode b/tests/009_multi_file/exitcode similarity index 100% rename from test/009_multi_file/exitcode rename to tests/009_multi_file/exitcode diff --git a/test/009_multi_file/stderr b/tests/009_multi_file/stderr similarity index 92% rename from test/009_multi_file/stderr rename to tests/009_multi_file/stderr index b4fe130..6e52470 100644 --- a/test/009_multi_file/stderr +++ b/tests/009_multi_file/stderr @@ -3,7 +3,7 @@ Validation failed: ValidationState { Workflow, ), file_path: Some( - "009_multi_file/xinvalid.yml", + "./tests/009_multi_file/xinvalid.yml", ), errors: [ Parse { diff --git a/tests/009_multi_file/stdout b/tests/009_multi_file/stdout new file mode 100644 index 0000000..8e003c7 --- /dev/null +++ b/tests/009_multi_file/stdout @@ -0,0 +1 @@ +Fatal error validating ./tests/009_multi_file/xinvalid.yml diff --git a/test/009_multi_file/valid.yml b/tests/009_multi_file/valid.yml similarity index 100% rename from test/009_multi_file/valid.yml rename to tests/009_multi_file/valid.yml diff --git a/test/009_multi_file/validation_state.snap.json b/tests/009_multi_file/validation_state.snap.json similarity index 100% rename from test/009_multi_file/validation_state.snap.json rename to tests/009_multi_file/validation_state.snap.json diff --git a/test/009_multi_file/xinvalid.yml b/tests/009_multi_file/xinvalid.yml similarity index 100% rename from test/009_multi_file/xinvalid.yml rename to tests/009_multi_file/xinvalid.yml diff --git a/tests/010_remote_checks_ok/test.yml b/tests/010_remote_checks_ok/test.yml new file mode 100644 index 0000000..88adf73 --- /dev/null +++ b/tests/010_remote_checks_ok/test.yml @@ -0,0 +1,78 @@ +name: Test + +on: + push: + pull_request: + branches: + - main + +defaults: + run: + shell: bash + +jobs: + check: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + default: true + + - name: Check Formatting + uses: actions-rs/cargo@v1 + with: + command: fmt + args: -- --check + + - name: Check with Clippy + uses: actions-rs/clippy-check@v1 + with: + args: -- -Dwarnings + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Shellcheck + uses: ludeeus/action-shellcheck@master + + - name: Install shfmt + uses: mfinelli/setup-shfmt@master + + - name: Run shfmt + run: shfmt -d bin/* + + + build: + strategy: + matrix: + rust-toolchain: + - stable + - nightly + os: + - ubuntu-latest + - macos-latest + - windows-latest + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust-toolchain }} + override: true + default: true + + - name: Build + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --all-features diff --git a/tests/011_remote_checks_failure/exitcode b/tests/011_remote_checks_failure/exitcode new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/011_remote_checks_failure/exitcode @@ -0,0 +1 @@ +1 diff --git a/tests/011_remote_checks_failure/stderr b/tests/011_remote_checks_failure/stderr new file mode 100644 index 0000000..c9bc9dc --- /dev/null +++ b/tests/011_remote_checks_failure/stderr @@ -0,0 +1,18 @@ +Validation failed: ValidationState { + action_type: Some( + Workflow, + ), + file_path: Some( + "./tests/011_remote_checks_failure/test.yml", + ), + errors: [ + Unknown { + code: "action_not_found", + detail: Some( + "Could not find action: actions/checkouts@v2", + ), + path: "jobs/build/steps/uses/actions/checkouts@v2", + title: "Action Not Found", + }, + ], +} diff --git a/tests/011_remote_checks_failure/stdout b/tests/011_remote_checks_failure/stdout new file mode 100644 index 0000000..79ef8c0 --- /dev/null +++ b/tests/011_remote_checks_failure/stdout @@ -0,0 +1 @@ +Fatal error validating ./tests/011_remote_checks_failure/test.yml diff --git a/tests/011_remote_checks_failure/test.yml b/tests/011_remote_checks_failure/test.yml new file mode 100644 index 0000000..6a5105e --- /dev/null +++ b/tests/011_remote_checks_failure/test.yml @@ -0,0 +1,78 @@ +name: Test + +on: + push: + pull_request: + branches: + - main + +defaults: + run: + shell: bash + +jobs: + check: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + default: true + + - name: Check Formatting + uses: actions-rs/cargo@v1 + with: + command: fmt + args: -- --check + + - name: Check with Clippy + uses: actions-rs/clippy-check@v1 + with: + args: -- -Dwarnings + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Shellcheck + uses: ludeeus/action-shellcheck@master + + - name: Install shfmt + uses: mfinelli/setup-shfmt@master + + - name: Run shfmt + run: shfmt -d bin/* + + + build: + strategy: + matrix: + rust-toolchain: + - stable + - nightly + os: + - ubuntu-latest + - macos-latest + - windows-latest + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkouts@v2 + + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust-toolchain }} + override: true + default: true + + - name: Build + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --all-features diff --git a/test/run b/tests/run similarity index 100% rename from test/run rename to tests/run diff --git a/test/run.mjs b/tests/run.mjs similarity index 100% rename from test/run.mjs rename to tests/run.mjs diff --git a/tests/snapshot_tests.rs b/tests/snapshot_tests.rs new file mode 100644 index 0000000..9f56ff0 --- /dev/null +++ b/tests/snapshot_tests.rs @@ -0,0 +1,140 @@ +use std::{fs::{self, File}, ffi::{OsString, OsStr}}; +use std::io::prelude::*; + +use rstest::rstest; +use assert_cmd::Command; + + +#[derive(Debug)] +struct SnapshotTest { + test_dir: String, + exitcode: i32, + stderr: String, + stdout: String, + test_files: Vec, +} + +impl SnapshotTest { + fn new(test_dir: String) -> Self { + let stderr = fs::read_to_string( + format!("./tests/{test_dir}/stderr"), + ).unwrap_or(String::from("")); + + let stdout = fs::read_to_string( + format!("./tests/{test_dir}/stdout"), + ).unwrap_or(String::from("")); + + let exitcode: i32 = fs::read_to_string( + format!("./tests/{test_dir}/exitcode"), + ).map(|s| { + s + .strip_suffix("\n") + .unwrap_or(s.as_str()) + .parse::() + .unwrap_or(0) + }).unwrap_or(0); + + let test_files = Self::_get_files(&test_dir); + + SnapshotTest { + test_dir, + exitcode, + stderr, + stdout, + test_files, + } + } + + #[cfg(not(feature = "save-snapshots"))] + fn execute(self) { + Command::cargo_bin( + env!("CARGO_PKG_NAME"), + ) + .expect("binary to execute") + .args(self.test_files) + .assert() + .stdout(self.stdout) + .stderr(self.stderr) + .code(self.exitcode); + } + + #[cfg(feature = "save-snapshots")] + fn execute(&self) { + let test_dir = self.test_dir.to_owned(); + let test_files = self.test_files.to_owned(); + let result = Command::cargo_bin( + env!("CARGO_PKG_NAME"), + ) + .expect("binary to execute") + .args(test_files).ok().unwrap_or_else(|e| e.as_output().unwrap().to_owned()); + + if !result.stdout.is_empty() { + self._save_contents( + format!("./tests/{test_dir}/stdout"), + result.stdout, + ); + } + if !result.stderr.is_empty() { + self._save_contents( + format!("./tests/{test_dir}/stderr"), + result.stderr, + ); + } + if let Some(exitcode) = result.status.code() { + if exitcode > 0 { + self._save_contents( + format!("./tests/{test_dir}/exitcode"), + format!("{exitcode}\n").into(), + ); + } + } + } + + fn _save_contents( + &self, + file_name: String, + contents: Vec, + ) { + println!("Saving {}", file_name); + let res = File::create(file_name).unwrap().write_all( + &contents, + ); + assert!(res.is_ok(), "{:?}", res); + } + + fn _get_files(test_dir: &String) -> Vec { + let yml = Some(OsStr::new("yml")); + fs::read_dir( + format!("./tests/{test_dir}"), + ) + .unwrap() + .filter_map(Result::ok) + .filter(|f| f.path().extension() == yml) + .map(|f| f.path().into_os_string()) + .collect::>() + } +} + + +#[rstest] +#[case("001_basic_workflow")] +#[case("002_basic_action")] +#[case("003_successful_globs")] +#[case("004_failing_globs")] +#[case("005_conditional_step_in_action")] +#[case("006_workflow_dispatch_inputs_options")] +#[case("007_funky_syntax")] +#[case("008_job_dependencies")] +#[case("009_multi_file")] +#[cfg(not(feature = "remote-checks"))] +fn snapshot(#[case] dir_name: String) { + SnapshotTest::new(dir_name).execute(); +} + +#[rstest] +#[case("010_remote_checks_ok")] +#[case("011_remote_checks_failure")] +#[cfg(feature = "remote-checks")] +fn snapshot_remote_checks(#[case] dir_name: String) { + SnapshotTest::new(dir_name).execute(); +}