diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..35049cbc --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --package xtask --" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccb9d4b2..da1ae863 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,6 @@ on: pull_request: branches: - main - merge_group: # ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel # and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency @@ -21,46 +20,58 @@ concurrency: # typos, and missing tests as early as possible. This allows us to fix these and resubmit the PR # without having to wait for the comprehensive matrix of tests to complete. jobs: - rustfmt: + # Lint the formatting of the codebase. + lint-formatting: + name: Check Formatting runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - with: - components: rustfmt - - run: cargo +nightly fmt --all --check + with: { components: rustfmt } + - run: cargo xtask lint-formatting - typos: + # Check for typos in the codebase. + # See + lint-typos: + name: Check Typos runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: crate-ci/typos@master + # Check for any disallowed dependencies in the codebase due to license / security issues. + # See dependencies: + name: Check Dependencies runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: EmbarkStudios/cargo-deny-action@v2 + # Check for any unused dependencies in the codebase. + # See cargo-machete: + name: Check Unused Dependencies runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - uses: bnjbvr/cargo-machete@v0.7.0 - clippy: + # Run cargo clippy. + lint-clippy: + name: Check Clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - with: - components: clippy - - uses: taiki-e/install-action@cargo-make + with: { components: clippy } - uses: Swatinem/rust-cache@v2 - - run: cargo make clippy + - run: cargo xtask lint-clippy - markdownlint: + # Run markdownlint on all markdown files in the repository. + lint-markdown: + name: Check Markdown runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -70,24 +81,27 @@ jobs: '**/*.md' '!target' + # Run cargo coverage. This will generate a coverage report and upload it to codecov. + # coverage: + name: Coverage Report runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: components: llvm-tools - - uses: taiki-e/install-action@v2 - with: - tool: cargo-llvm-cov,cargo-make + - uses: taiki-e/install-action@cargo-llvm-cov - uses: Swatinem/rust-cache@v2 - - run: cargo make coverage + - run: cargo xtask coverage - uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true + # Run cargo check. This is a fast way to catch any obvious errors in the code. check: + name: Check ${{ matrix.os }} ${{ matrix.toolchain }} strategy: fail-fast: false matrix: @@ -99,13 +113,13 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.toolchain }} - - uses: taiki-e/install-action@cargo-make - uses: Swatinem/rust-cache@v2 - - run: cargo make check - env: - RUST_BACKTRACE: full + - run: cargo xtask check + # Run cargo rustdoc with the same options that would be used by docs.rs, taking into account the + # package.metadata.docs.rs configured in Cargo.toml. https://github.com/dtolnay/cargo-docs-rs lint-docs: + name: Check Docs runs-on: ubuntu-latest env: RUSTDOCFLAGS: -Dwarnings @@ -114,47 +128,48 @@ jobs: - uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/install@cargo-docs-rs - uses: Swatinem/rust-cache@v2 - # Run cargo rustdoc with the same options that would be used by docs.rs, taking into account - # the package.metadata.docs.rs configured in Cargo.toml. - # https://github.com/dtolnay/cargo-docs-rs - - run: cargo +nightly docs-rs -p ratatui + - run: cargo xtask lint-docs - test-doc: - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - runs-on: ${{ matrix.os }} + # Run cargo test on the documentation of the crate. This will catch any code examples that don't + # compile, or any other issues in the documentation. + test-docs: + name: Test Docs + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - uses: taiki-e/install-action@cargo-make - uses: Swatinem/rust-cache@v2 - - run: cargo make test-doc - env: - RUST_BACKTRACE: full + - run: cargo xtask test-docs - test: + # Run cargo test on the libraries of the crate. + test-libs: + name: Test Libs ${{ matrix.toolchain }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + toolchain: ["1.74.0", "stable"] + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - run: cargo xtask test-libs + + # Run cargo test on all the backends. + test-backends: + name: Test ${{matrix.backend}} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - toolchain: ["1.74.0", "stable"] backend: [crossterm, termion, termwiz] exclude: # termion is not supported on windows - os: windows-latest backend: termion - runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.toolchain }} - - uses: taiki-e/install-action@v2 - with: - tool: cargo-make,nextest + - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - - run: cargo make test-backend ${{ matrix.backend }} - env: - RUST_BACKTRACE: full + - run: cargo xtask test-backend ${{ matrix.backend }} diff --git a/Cargo.lock b/Cargo.lock index 3138f4a1..1c965aae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,12 +53,55 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "anstream" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + [[package]] name = "anstyle" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.90" @@ -242,6 +285,38 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.23", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "cassowary" version = "0.3.0" @@ -331,6 +406,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap-cargo" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2ea69cefa96b848b73ad516ad1d59a195cdf9263087d977f648a818c8b43e" +dependencies = [ + "anstyle", + "cargo_metadata", + "clap", +] + +[[package]] +name = "clap-verbosity-flag" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e099138e1807662ff75e2cebe4ae2287add879245574489f9b1588eb5e5564ed" +dependencies = [ + "clap", + "log", ] [[package]] @@ -339,8 +436,22 @@ version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ + "anstream", "anstyle", "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -385,6 +496,12 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "compact_str" version = "0.8.0" @@ -607,6 +724,18 @@ dependencies = [ "litrs", ] +[[package]] +name = "duct" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ab5718d1224b63252cd0c6f74f6480f9ffeb117438a2e0f5cf6d9a4798929c" +dependencies = [ + "libc", + "once_cell", + "os_pipe", + "shared_child", +] + [[package]] name = "either" version = "1.13.0" @@ -1109,6 +1238,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -1490,6 +1625,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_pipe" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "overload" version = "0.1.1" @@ -2294,6 +2439,9 @@ name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] [[package]] name = "semver-parser" @@ -2378,6 +2526,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shared_child" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fa9338aed9a1df411814a5b2252f7cd206c55ae9bf2fa763f8de84603aa60c" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2505,6 +2663,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.26.3" @@ -3205,7 +3369,7 @@ checksum = "dfb128bacfa86734e07681fb6068e34c144698e84ee022d6e009145d1abb77b5" dependencies = [ "log", "ordered-float", - "strsim", + "strsim 0.10.0", "thiserror", "wezterm-dynamic-derive", ] @@ -3364,6 +3528,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "xtask" +version = "0.0.0" +dependencies = [ + "cargo_metadata", + "clap", + "clap-cargo", + "clap-verbosity-flag", + "color-eyre", + "duct", + "tracing", + "tracing-log", + "tracing-subscriber", +] + [[package]] name = "yansi" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 27b91829..67cae2b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] resolver = "2" -members = ["ratatui"] +members = ["ratatui", "xtask"] +default-members = ["ratatui"] [workspace.package] authors = ["Florian Dehau ", "The Ratatui Developers"] diff --git a/xtask/Cargo.lock b/xtask/Cargo.lock new file mode 100644 index 00000000..9b71ead2 --- /dev/null +++ b/xtask/Cargo.lock @@ -0,0 +1,248 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap-cargo" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2ea69cefa96b848b73ad516ad1d59a195cdf9263087d977f648a818c8b43e" +dependencies = [ + "anstyle", + "clap", +] + +[[package]] +name = "clap_builder" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "xtask" +version = "0.1.0" +dependencies = [ + "clap", + "clap-cargo", +] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 00000000..3b8210f6 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "xtask" +edition = "2021" +publish = false +license.workspace = true + +[dependencies] +cargo_metadata = "0.18.1" +clap = { version = "4.5.20", features = ["derive"] } +clap-cargo = { version = "0.14.1", features = ["cargo_metadata"] } +clap-verbosity-flag = "2.2.2" +color-eyre = "0.6.3" +duct = "0.13.7" +tracing = "0.1.40" +tracing-log = "0.2.0" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } diff --git a/xtask/README.md b/xtask/README.md new file mode 100644 index 00000000..3890acdb --- /dev/null +++ b/xtask/README.md @@ -0,0 +1,5 @@ +# xtask for ratatui + +See for details + +run with `cargo xtask ...` diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 00000000..64342373 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,329 @@ +use std::{fmt::Debug, io, process::Output, vec}; + +use cargo_metadata::MetadataCommand; +use clap::{Parser, Subcommand, ValueEnum}; +use clap_verbosity_flag::{InfoLevel, Verbosity}; +use color_eyre::{eyre::Context, Result}; +use duct::cmd; +use tracing::level_filters::LevelFilter; +use tracing_log::AsTrace; + +fn main() -> Result<()> { + color_eyre::install()?; + let args = Args::parse(); + tracing_subscriber::fmt() + .with_max_level(args.log_level()) + .without_time() + .init(); + + match args.run() { + Ok(_) => (), + Err(err) => { + tracing::error!("{err}"); + std::process::exit(1); + } + } + Ok(()) +} + +#[derive(Debug, Parser)] +#[command(bin_name = "cargo xtask", styles = clap_cargo::style::CLAP_STYLING)] +struct Args { + #[command(subcommand)] + command: Command, + + #[command(flatten)] + verbosity: Verbosity, +} + +impl Args { + fn run(self) -> Result<()> { + self.command.run() + } + + fn log_level(&self) -> LevelFilter { + self.verbosity.log_level_filter().as_trace() + } +} + +#[derive(Clone, Debug, Subcommand)] +enum Command { + /// Run CI checks (lint, build, test) + CI, + + /// Build the project + #[command(visible_alias = "b")] + Build, + + /// Run cargo check + #[command(visible_alias = "c")] + Check, + + /// Generate code coverage report + #[command(visible_alias = "cov")] + Coverage, + + /// Lint formatting, typos, clippy, and docs + #[command(visible_alias = "l")] + Lint, + + /// Run clippy on the project + #[command(visible_alias = "cl")] + LintClippy, + + /// Check documentation for errors and warnings + #[command(visible_alias = "d")] + LintDocs, + + /// Check for formatting issues in the project + #[command(visible_alias = "lf")] + LintFormatting, + + /// Lint markdown files + #[command(visible_alias = "md")] + LintMarkdown, + + /// Check for typos in the project + #[command(visible_alias = "lt")] + LintTypos, + + /// Fix clippy warnings in the project + #[command(visible_alias = "fc")] + FixClippy, + + /// Fix formatting issues in the project + #[command(visible_alias = "fmt")] + FixFormatting, + + /// Fix typos in the project + #[command(visible_alias = "typos")] + FixTypos, + + /// Run tests + #[command(visible_alias = "t")] + Test, + + /// Test backend + #[command(visible_alias = "tb")] + TestBackend { backend: Backend }, + + /// Run doc tests + #[command(visible_alias = "td")] + TestDocs, + + /// Run lib tests + #[command(visible_alias = "tl")] + TestLibs, +} + +#[derive(Clone, Debug, ValueEnum, PartialEq, Eq)] +enum Backend { + Crossterm, + Termion, + Termwiz, +} + +impl Command { + fn run(self) -> Result<()> { + match self { + Command::CI => ci(), + Command::Build => build(), + Command::Check => check(), + Command::Coverage => coverage(), + Command::Lint => lint(), + Command::LintClippy => lint_clippy(), + Command::LintDocs => lint_docs(), + Command::LintFormatting => lint_format(), + Command::LintTypos => lint_typos(), + Command::LintMarkdown => lint_markdown(), + Command::FixClippy => fix_clippy(), + Command::FixFormatting => fix_format(), + Command::FixTypos => fix_typos(), + Command::Test => test(), + Command::TestBackend { backend } => test_backend(backend), + Command::TestDocs => test_docs(), + Command::TestLibs => test_libs(), + } + } +} + +/// Run CI checks (lint, build, test) +fn ci() -> Result<()> { + lint()?; + build()?; + test()?; + Ok(()) +} + +/// Build the project +fn build() -> Result<()> { + run_cargo(vec!["build", "--all-targets", "--all-features"]) +} + +/// Run cargo check +fn check() -> Result<()> { + run_cargo(vec!["check", "--all-targets", "--all-features"]) +} + +/// Generate code coverage report +fn coverage() -> Result<()> { + run_cargo(vec![ + "llvm-cov", + "--lcov", + "--output-path", + "target/lcov.info", + "--all-features", + ]) +} + +/// Lint formatting, typos, clippy, and docs (and a soft fail on markdown) +fn lint() -> Result<()> { + lint_clippy()?; + lint_docs()?; + lint_format()?; + lint_typos()?; + if let Err(err) = lint_markdown() { + tracing::warn!("known issue: markdownlint is currently noisy and can be ignored: {err}"); + } + Ok(()) +} + +/// Run clippy on the project +fn lint_clippy() -> Result<()> { + run_cargo(vec![ + "clippy", + "--all-targets", + "--all-features", + "--tests", + "--benches", + "--", + "-D", + "warnings", + ]) +} + +/// Fix clippy warnings in the project +fn fix_clippy() -> Result<()> { + run_cargo(vec![ + "clippy", + "--all-targets", + "--all-features", + "--tests", + "--benches", + "--", + "-D", + "warnings", + "--fix", + ]) +} + +/// Check that docs build without errors using flags for docs.rs +fn lint_docs() -> Result<()> { + let meta = MetadataCommand::new() + .exec() + .wrap_err("failed to get cargo metadata")?; + for package in meta.workspace_default_packages() { + run_cargo_nightly(vec!["docs-rs", "--package", &package.name])?; + } + Ok(()) +} + +/// Lint formatting issues in the project +fn lint_format() -> Result<()> { + run_cargo_nightly(vec!["fmt", "--all", "--check"]) +} + +/// Fix formatting issues in the project +fn fix_format() -> Result<()> { + run_cargo_nightly(vec!["fmt", "--all"]) +} + +/// Lint markdown files using [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) +fn lint_markdown() -> Result<()> { + cmd!("markdownlint-cli2", "**/*.md", "!target").run_with_trace()?; + Ok(()) +} + +/// Check for typos in the project using [typos-cli](https://github.com/crate-ci/typos/) +fn lint_typos() -> Result<()> { + cmd!("typos").run_with_trace()?; + Ok(()) +} + +/// Fix typos in the project +fn fix_typos() -> Result<()> { + cmd!("typos", "-w").run_with_trace()?; + Ok(()) +} + +/// Run tests for libs, backends, and docs +fn test() -> Result<()> { + test_libs()?; + test_backend(Backend::Crossterm)?; + test_backend(Backend::Termion)?; + test_backend(Backend::Termwiz)?; + test_docs()?; // run last because it's slow + Ok(()) +} + +/// Run tests for the specified backend +fn test_backend(backend: Backend) -> Result<()> { + if cfg!(windows) && backend == Backend::Termion { + tracing::error!("termion backend is not supported on Windows"); + } + let backend = match backend { + Backend::Crossterm => "crossterm", + Backend::Termion => "termion", + Backend::Termwiz => "termwiz", + }; + run_cargo(vec![ + "test", + "--all-targets", + "--no-default-features", + "--features", + backend, + ]) +} + +/// Run doc tests for the workspace's default packages +fn test_docs() -> Result<()> { + run_cargo(vec!["test", "--doc", "--all-features"]) +} + +/// Run lib tests for the workspace's default packages +fn test_libs() -> Result<()> { + run_cargo(vec!["test", "--all-targets", "--all-features"]) +} + +/// Run a cargo subcommand with the default toolchain +fn run_cargo(args: Vec<&str>) -> Result<()> { + cmd("cargo", args).run_with_trace()?; + Ok(()) +} + +/// Run a cargo subcommand with the nightly toolchain +fn run_cargo_nightly(args: Vec<&str>) -> Result<()> { + cmd("cargo", args) + // CARGO env var is set because we're running in a cargo subcommand + .env_remove("CARGO") + .env("RUSTUP_TOOLCHAIN", "nightly") + .run_with_trace()?; + Ok(()) +} + +/// An extension trait for `duct::Expression` that logs the command being run +/// before running it. +trait ExpressionExt { + /// Run the command and log the command being run + fn run_with_trace(&self) -> io::Result; +} + +impl ExpressionExt for duct::Expression { + fn run_with_trace(&self) -> io::Result { + tracing::info!("running command: {:?}", self); + self.run().inspect_err(|_| { + // The command that was run may have scrolled off the screen, so repeat it here + tracing::error!("failed to run command: {:?}", self); + }) + } +}