diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000..ce0b8f6 --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,28 @@ +name: Audit + +on: + schedule: + - cron: "0 6 * * 1" + push: + branches: [main] + paths: ["Cargo.toml", "deny.toml", ".github/workflows/audit.yml"] + pull_request: + paths: ["Cargo.toml", "deny.toml", ".github/workflows/audit.yml"] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + +jobs: + deny: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + # Runs `cargo deny --all-features check` against deny.toml, in a container + # that brings its own Rust and cargo-deny. + - uses: EmbarkStudios/cargo-deny-action@v2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d76160d..c648cc9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,46 +7,65 @@ on: pull_request: paths-ignore: ["**.md"] +permissions: + contents: read + env: CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: 0 + RUST_BACKTRACE: 1 concurrency: cancel-in-progress: true group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} jobs: - check: - name: Check Suite + test: runs-on: ubuntu-latest steps: - - name: Checkout Repository - uses: actions/checkout@v7 + - uses: actions/checkout@v7 + + # Installs the toolchain, sets up Swatinem/rust-cache and the problem + # matchers, and sets RUSTFLAGS to `-D warnings`. + - uses: actions-rust-lang/setup-rust-toolchain@v1 + + - uses: taiki-e/install-action@nextest + + # `network-tests` calls the live Turnstile API with Cloudflare's dummy keys. + - run: cargo nextest run --features network-tests,idempotency - - name: Register Problem Matchers - uses: r7kamura/rust-problem-matchers@v1 + # nextest does not run doctests. + - run: cargo test --doc --all-features - - run: rustup toolchain install stable --profile minimal + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 - - name: Rust Cache - uses: Swatinem/rust-cache@v2 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + components: clippy, rustfmt - - name: Setup Rust - uses: dtolnay/rust-toolchain@stable + - run: cargo fmt --all --check - - name: Install Cargo Make - uses: davidB/rust-cargo-make@v1 + - run: cargo clippy --all-targets --all-features -- -D warnings - - name: Install nextest - uses: taiki-e/install-action@nextest + - run: cargo doc --no-deps --all-features + env: + RUSTDOCFLAGS: -D warnings - - name: Run Formatter - run: cargo make format-ci + features: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 - - name: Run Clippy - run: cargo make lint-ci + - uses: actions-rust-lang/setup-rust-toolchain@v1 - - name: Build - run: cargo build --release --all-features + - uses: taiki-e/install-action@cargo-hack - - name: Run Unit Tests - run: cargo make test-ci + # `--at-least-one-of` skips the TLS-less combinations that `compile_error!`. + # The excluded features only gate `#[cfg(test)]` code, which `check` skips. + - run: | + cargo hack check --feature-powerset \ + --exclude-features integration,network-tests \ + --at-least-one-of rustls-native-roots,rustls-webpki-roots,native-tls diff --git a/.gitignore b/.gitignore index ffc3118..0a2a065 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /Cargo.lock .env +/lcov.info diff --git a/Makefile.toml b/Makefile.toml deleted file mode 100644 index ecb347b..0000000 --- a/Makefile.toml +++ /dev/null @@ -1,78 +0,0 @@ -[tasks.setup] -script = ''' - echo # things required for `cargo make test` - cargo install cargo-nextest - - echo # things required by `cargo make coverage` - rustup component add llvm-tools-preview - cargo install cargo-llvm-cov -''' - -[tasks.lint] -install_crate = "clippy" -command = "cargo" -args = ["clippy", "--tests", "--examples", "--all-targets", "--all-features"] - -[tasks.lint-ci] -install_crate = "clippy" -command = "cargo" -args = [ - "clippy", - "--tests", - "--examples", - "--all-targets", - "--all-features", - "--", - "-D", - "warnings", -] - -[tasks.format] -install_crate = "rustfmt" -command = "cargo" -args = ["fmt", "--all"] - -[tasks.fmt] -alias = "format" - -[tasks.format-ci] -install_crate = "rustfmt" -command = "cargo" -args = ["fmt", "--all", "--", "--check"] - - -[tasks.test] -env = { "RUN_MODE" = "test", "RUST_LOG" = "info" } -command = "cargo" -args = ["nextest", "run", "--features", "network-tests", "${@}"] - -[tasks.test-ci] -env = { "RUN_MODE" = "ci", "RUST_LOG" = "info" } -command = "cargo" -args = ["nextest", "run", "--features", "network-tests"] - -[tasks.cov] -command = "cargo" -env = { "RUN_MODE" = "test" } -args = ["llvm-cov", "nextest", "${@}"] - -[tasks.cov-ci] -command = "cargo" -env = { "RUN_MODE" = "ci" } -args = ["llvm-cov", "nextest", "--lcov", "--output-path", "lcov.info"] - -[tasks.docs] -command = "cargo" -args = ["doc", "--no-deps", "--all-features", "--document-private-items"] - -[tasks.docs-watch] -command = "cargo" -args = ["doc", "--no-deps", "--all-features", "--document-private-items"] -watch = true - -[tasks.timings] -script = ''' - cargo clean - cargo build --release --quiet --timings - open /target/cargo-timings/cargo-timing.html -''' diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..7a2e98c --- /dev/null +++ b/deny.toml @@ -0,0 +1,30 @@ +# Policy for `cargo deny`, run by `just audit` and .github/workflows/audit.yml. +# https://embarkstudios.github.io/cargo-deny/ + +[graph] +# Each TLS backend pulls in a different subtree. +all-features = true + +[advisories] +# Accepted advisories go here, each with a note saying why. +ignore = [] + +[licenses] +allow = [ + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", + "BSD-3-Clause", + "CDLA-Permissive-2.0", # webpki-roots + "ISC", + "MIT", + "Unicode-3.0", +] + +[bans] +# Usually a transitive dependency's choice, so warn rather than fail. +multiple-versions = "warn" +wildcards = "deny" + +[sources] +unknown-registry = "deny" +unknown-git = "deny" diff --git a/justfile b/justfile new file mode 100644 index 0000000..47eb42a --- /dev/null +++ b/justfile @@ -0,0 +1,66 @@ +# Development commands for cf-turnstile. Run `just` to list them and `just setup` +# to install the tools they need. + +# The feature set CI tests with: the defaults plus the optional API surface. +test_features := "network-tests,idempotency" + +# At least one of these is required for the crate to build. +tls_backends := "rustls-native-roots,rustls-webpki-roots,native-tls" + +# List the available recipes. +default: + @just --list + +# Run everything CI runs. +verify: fmt-check lint test doc features audit + +# Build with all features enabled. +build *args: + cargo build --all-features {{ args }} + +# Type-check the crate, its tests and its examples. +check *args: + cargo check --all-targets --all-features {{ args }} + +# Format all code in place. +fmt: + cargo fmt --all + +# Fail if anything is unformatted. +fmt-check: + cargo fmt --all --check + +# Lint with clippy, denying warnings. +lint *args: + cargo clippy --all-targets --all-features {{ args }} -- -D warnings + +# Run the tests and doctests. Needs network access. +test *args: + cargo nextest run --features {{ test_features }} {{ args }} + cargo test --doc --all-features + +# Run only the tests that work offline. +test-offline *args: + cargo nextest run {{ args }} + +# Verify a real token. Needs the TURNSTILE_* environment variables. +test-integration: + cargo nextest run --features integration --no-capture test_integration + +# Check that every meaningful feature combination compiles. +features: + cargo hack check --feature-powerset \ + --exclude-features integration,network-tests \ + --at-least-one-of {{ tls_backends }} + +# Build the docs, denying warnings. +doc *args: + RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features {{ args }} + +audit: + cargo deny check + +# Install the components and tools the other recipes need. +setup: + rustup component add clippy rustfmt llvm-tools-preview + cargo install --locked cargo-nextest cargo-hack cargo-llvm-cov cargo-deny