diff --git a/.github/DOCS.md b/.github/DOCS.md
new file mode 100644
index 0000000..e932784
--- /dev/null
+++ b/.github/DOCS.md
@@ -0,0 +1,23 @@
+# Github config and workflows
+
+In this folder there is configuration for codecoverage, dependabot, and ci
+workflows that check the library more deeply than the default configurations.
+
+This folder can be or was merged using a --allow-unrelated-histories merge
+strategy from which provides a
+reasonably sensible base for writing your own ci on. By using this strategy
+the history of the CI repo is included in your repo, and future updates to
+the CI can be merged later.
+
+To perform this merge run:
+
+```shell
+git remote add ci https://github.com/jonhoo/rust-ci-conf.git
+git fetch ci
+git merge --allow-unrelated-histories ci/main
+```
+
+An overview of the files in this project is available at:
+, which contains some
+rationale for decisions and runs through an example of solving minimal version
+and OpenSSL issues.
diff --git a/.github/codecov.yml b/.github/codecov.yml
new file mode 100644
index 0000000..cd5ce8f
--- /dev/null
+++ b/.github/codecov.yml
@@ -0,0 +1,21 @@
+# ref: https://docs.codecov.com/docs/codecovyml-reference
+coverage:
+ # Hold ourselves to a high bar
+ range: 85..100
+ round: down
+ precision: 1
+ status:
+ # ref: https://docs.codecov.com/docs/commit-status
+ project:
+ default:
+ # Avoid false negatives
+ threshold: 1%
+
+# Test files aren't important for coverage
+ignore:
+ - "tests"
+
+# Make comments less noisy
+comment:
+ layout: "files"
+ require_changes: true
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..d0f091e
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,19 @@
+version: 2
+updates:
+ - package-ecosystem: github-actions
+ directory: /
+ schedule:
+ interval: daily
+ - package-ecosystem: cargo
+ directory: /
+ schedule:
+ interval: daily
+ ignore:
+ - dependency-name: "*"
+ # patch and minor updates don't matter for libraries as consumers of this library build
+ # with their own lockfile, rather than the version specified in this library's lockfile
+ # remove this ignore rule if your package has binaries to ensure that the binaries are
+ # built with the exact set of dependencies and those are up to date.
+ update-types:
+ - "version-update:semver-patch"
+ - "version-update:semver-minor"
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
new file mode 100644
index 0000000..5251954
--- /dev/null
+++ b/.github/workflows/check.yml
@@ -0,0 +1,126 @@
+# This workflow runs whenever a PR is opened or updated, or a commit is pushed to main. It runs
+# several checks:
+# - fmt: checks that the code is formatted according to rustfmt
+# - clippy: checks that the code does not contain any clippy warnings
+# - doc: checks that the code can be documented without errors
+# - hack: check combinations of feature flags
+# - msrv: check that the msrv specified in the crate is correct
+permissions:
+ contents: read
+# This configuration allows maintainers of this repo to create a branch and pull request based on
+# the new branch. Restricting the push trigger to the main branch ensures that the PR only gets
+# built once.
+on:
+ push:
+ branches: [main]
+ pull_request:
+# If new code is pushed to a PR branch, then cancel in progress workflows for that PR. Ensures that
+# we don't waste CI time, and returns results quicker https://github.com/jonhoo/rust-ci-conf/pull/5
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+name: check
+jobs:
+ fmt:
+ runs-on: ubuntu-latest
+ name: stable / fmt
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ with:
+ components: rustfmt
+ - name: cargo fmt --check
+ run: cargo fmt --check
+ clippy:
+ runs-on: ubuntu-latest
+ name: ${{ matrix.toolchain }} / clippy
+ permissions:
+ contents: read
+ checks: write
+ strategy:
+ fail-fast: false
+ matrix:
+ # Get early warning of new lints which are regularly introduced in beta channels.
+ toolchain: [stable, beta]
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install ${{ matrix.toolchain }}
+ uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: ${{ matrix.toolchain }}
+ components: clippy
+ - name: cargo clippy
+ uses: giraffate/clippy-action@v1
+ with:
+ reporter: 'github-pr-check'
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ semver:
+ runs-on: ubuntu-latest
+ name: semver
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ with:
+ components: rustfmt
+ - name: cargo-semver-checks
+ uses: obi1kenobi/cargo-semver-checks-action@v2
+ doc:
+ # run docs generation on nightly rather than stable. This enables features like
+ # https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html which allows an
+ # API be documented as only available in some specific platforms.
+ runs-on: ubuntu-latest
+ name: nightly / doc
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install nightly
+ uses: dtolnay/rust-toolchain@nightly
+ - name: Install cargo-docs-rs
+ uses: dtolnay/install@cargo-docs-rs
+ - name: cargo docs-rs
+ run: cargo docs-rs
+ hack:
+ # cargo-hack checks combinations of feature flags to ensure that features are all additive
+ # which is required for feature unification
+ runs-on: ubuntu-latest
+ name: ubuntu / stable / features
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ - name: cargo install cargo-hack
+ uses: taiki-e/install-action@cargo-hack
+ # intentionally no target specifier; see https://github.com/jonhoo/rust-ci-conf/pull/4
+ # --feature-powerset runs for every combination of features
+ - name: cargo hack
+ run: cargo hack --feature-powerset check
+ msrv:
+ # check that we can build using the minimal rust version that is specified by this crate
+ runs-on: ubuntu-latest
+ # we use a matrix here just because env can't be used in job names
+ # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability
+ strategy:
+ matrix:
+ msrv: ["1.56.1"] # 2021 edition requires 1.56
+ name: ubuntu / ${{ matrix.msrv }}
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install ${{ matrix.msrv }}
+ uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: ${{ matrix.msrv }}
+ - name: cargo +${{ matrix.msrv }} check
+ run: cargo check
diff --git a/.github/workflows/nostd.yml b/.github/workflows/nostd.yml
new file mode 100644
index 0000000..c12227a
--- /dev/null
+++ b/.github/workflows/nostd.yml
@@ -0,0 +1,30 @@
+# This workflow checks whether the library is able to run without the std library (e.g., embedded).
+# This entire file should be removed if this crate does not support no-std. See check.yml for
+# information about how the concurrency cancellation and workflow triggering works
+permissions:
+ contents: read
+on:
+ push:
+ branches: [main]
+ pull_request:
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+name: no-std
+jobs:
+ nostd:
+ runs-on: ubuntu-latest
+ name: ${{ matrix.target }}
+ strategy:
+ matrix:
+ target: [thumbv7m-none-eabi, aarch64-unknown-none]
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ - name: rustup target add ${{ matrix.target }}
+ run: rustup target add ${{ matrix.target }}
+ - name: cargo check
+ run: cargo check --target ${{ matrix.target }} --no-default-features
diff --git a/.github/workflows/safety.yml b/.github/workflows/safety.yml
new file mode 100644
index 0000000..83da574
--- /dev/null
+++ b/.github/workflows/safety.yml
@@ -0,0 +1,84 @@
+# This workflow runs checks for unsafe code. In crates that don't have any unsafe code, this can be
+# removed. Runs:
+# - miri - detects undefined behavior and memory leaks
+# - address sanitizer - detects memory errors
+# - leak sanitizer - detects memory leaks
+# - loom - Permutation testing for concurrent code https://crates.io/crates/loom
+# See check.yml for information about how the concurrency cancellation and workflow triggering works
+permissions:
+ contents: read
+on:
+ push:
+ branches: [main]
+ pull_request:
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+name: safety
+jobs:
+ sanitizers:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install nightly
+ uses: dtolnay/rust-toolchain@nightly
+ - run: |
+ # to get the symbolizer for debug symbol resolution
+ sudo apt install llvm
+ # to fix buggy leak analyzer:
+ # https://github.com/japaric/rust-san#unrealiable-leaksanitizer
+ # ensure there's a profile.dev section
+ if ! grep -qE '^[ \t]*[profile.dev]' Cargo.toml; then
+ echo >> Cargo.toml
+ echo '[profile.dev]' >> Cargo.toml
+ fi
+ # remove pre-existing opt-levels in profile.dev
+ sed -i '/^\s*\[profile.dev\]/,/^\s*\[/ {/^\s*opt-level/d}' Cargo.toml
+ # now set opt-level to 1
+ sed -i '/^\s*\[profile.dev\]/a opt-level = 1' Cargo.toml
+ cat Cargo.toml
+ name: Enable debug symbols
+ - name: cargo test -Zsanitizer=address
+ # only --lib --tests b/c of https://github.com/rust-lang/rust/issues/53945
+ run: cargo test --lib --tests --all-features --target x86_64-unknown-linux-gnu
+ env:
+ ASAN_OPTIONS: "detect_odr_violation=0:detect_leaks=0"
+ RUSTFLAGS: "-Z sanitizer=address"
+ - name: cargo test -Zsanitizer=leak
+ if: always()
+ run: cargo test --all-features --target x86_64-unknown-linux-gnu
+ env:
+ LSAN_OPTIONS: "suppressions=lsan-suppressions.txt"
+ RUSTFLAGS: "-Z sanitizer=leak"
+ miri:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - run: |
+ echo "NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)" >> "$GITHUB_ENV"
+ - name: Install ${{ env.NIGHTLY }}
+ uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: ${{ env.NIGHTLY }}
+ components: miri
+ - name: cargo miri test
+ run: cargo miri test
+ env:
+ MIRIFLAGS: ""
+ loom:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ - name: cargo test --test loom
+ run: cargo test --release --test loom
+ env:
+ LOOM_MAX_PREEMPTIONS: 2
+ RUSTFLAGS: "--cfg loom"
diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml
new file mode 100644
index 0000000..02aa275
--- /dev/null
+++ b/.github/workflows/scheduled.yml
@@ -0,0 +1,58 @@
+# Run scheduled (rolling) jobs on a nightly basis, as your crate may break independently of any
+# given PR. E.g., updates to rust nightly and updates to this crates dependencies. See check.yml for
+# information about how the concurrency cancellation and workflow triggering works
+permissions:
+ contents: read
+on:
+ push:
+ branches: [main]
+ pull_request:
+ schedule:
+ - cron: '7 7 * * *'
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+name: rolling
+jobs:
+ # https://twitter.com/mycoliza/status/1571295690063753218
+ nightly:
+ runs-on: ubuntu-latest
+ name: ubuntu / nightly
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install nightly
+ uses: dtolnay/rust-toolchain@nightly
+ - name: cargo generate-lockfile
+ if: hashFiles('Cargo.lock') == ''
+ run: cargo generate-lockfile
+ - name: cargo test --locked
+ run: cargo test --locked --all-features --all-targets
+ # https://twitter.com/alcuadrado/status/1571291687837732873
+ update:
+ # This action checks that updating the dependencies of this crate to the latest available that
+ # satisfy the versions in Cargo.toml does not break this crate. This is important as consumers
+ # of this crate will generally use the latest available crates. This is subject to the standard
+ # Cargo semver rules (i.e cargo does not update to a new major version unless explicitly told
+ # to).
+ runs-on: ubuntu-latest
+ name: ubuntu / beta / updated
+ # There's no point running this if no Cargo.lock was checked in in the first place, since we'd
+ # just redo what happened in the regular test job. Unfortunately, hashFiles only works in if on
+ # steps, so we repeat it.
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install beta
+ if: hashFiles('Cargo.lock') != ''
+ uses: dtolnay/rust-toolchain@beta
+ - name: cargo update
+ if: hashFiles('Cargo.lock') != ''
+ run: cargo update
+ - name: cargo test
+ if: hashFiles('Cargo.lock') != ''
+ run: cargo test --locked --all-features --all-targets
+ env:
+ RUSTFLAGS: -D deprecated
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..f7540ae
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,156 @@
+# This is the main CI workflow that runs the test suite on all pushes to main and all pull requests.
+# It runs the following jobs:
+# - required: runs the test suite on ubuntu with stable and beta rust toolchains
+# - minimal: runs the test suite with the minimal versions of the dependencies that satisfy the
+# requirements of this crate, and its dependencies
+# - os-check: runs the test suite on mac and windows
+# - coverage: runs the test suite and collects coverage information
+# See check.yml for information about how the concurrency cancellation and workflow triggering works
+permissions:
+ contents: read
+on:
+ push:
+ branches: [main]
+ pull_request:
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+name: test
+jobs:
+ required:
+ runs-on: ubuntu-latest
+ name: ubuntu / ${{ matrix.toolchain }}
+ strategy:
+ matrix:
+ # run on stable and beta to ensure that tests won't break on the next version of the rust
+ # toolchain
+ toolchain: [stable, beta]
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install ${{ matrix.toolchain }}
+ uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: ${{ matrix.toolchain }}
+ - name: cargo generate-lockfile
+ # enable this ci template to run regardless of whether the lockfile is checked in or not
+ if: hashFiles('Cargo.lock') == ''
+ run: cargo generate-lockfile
+ # https://twitter.com/jonhoo/status/1571290371124260865
+ - name: cargo test --locked
+ run: cargo test --locked --all-features --all-targets
+ # https://github.com/rust-lang/cargo/issues/6669
+ - name: cargo test --doc
+ run: cargo test --locked --all-features --doc
+ minimal:
+ # This action chooses the oldest version of the dependencies permitted by Cargo.toml to ensure
+ # that this crate is compatible with the minimal version that this crate and its dependencies
+ # require. This will pickup issues where this create relies on functionality that was introduced
+ # later than the actual version specified (e.g., when we choose just a major version, but a
+ # method was added after this version).
+ #
+ # This particular check can be difficult to get to succeed as often transitive dependencies may
+ # be incorrectly specified (e.g., a dependency specifies 1.0 but really requires 1.1.5). There
+ # is an alternative flag available -Zdirect-minimal-versions that uses the minimal versions for
+ # direct dependencies of this crate, while selecting the maximal versions for the transitive
+ # dependencies. Alternatively, you can add a line in your Cargo.toml to artificially increase
+ # the minimal dependency, which you do with e.g.:
+ # ```toml
+ # # for minimal-versions
+ # [target.'cfg(any())'.dependencies]
+ # openssl = { version = "0.10.55", optional = true } # needed to allow foo to build with -Zminimal-versions
+ # ```
+ # The optional = true is necessary in case that dependency isn't otherwise transitively required
+ # by your library, and the target bit is so that this dependency edge never actually affects
+ # Cargo build order. See also
+ # https://github.com/jonhoo/fantoccini/blob/fde336472b712bc7ebf5b4e772023a7ba71b2262/Cargo.toml#L47-L49.
+ # This action is run on ubuntu with the stable toolchain, as it is not expected to fail
+ runs-on: ubuntu-latest
+ name: ubuntu / stable / minimal-versions
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ - name: Install nightly for -Zminimal-versions
+ uses: dtolnay/rust-toolchain@nightly
+ - name: rustup default stable
+ run: rustup default stable
+ - name: cargo update -Zminimal-versions
+ run: cargo +nightly update -Zminimal-versions
+ - name: cargo test
+ run: cargo test --locked --all-features --all-targets
+ os-check:
+ # run cargo test on mac and windows
+ runs-on: ${{ matrix.os }}
+ name: ${{ matrix.os }} / stable
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [macos-latest, windows-latest]
+ steps:
+ # if your project needs OpenSSL, uncomment this to fix Windows builds.
+ # it's commented out by default as the install command takes 5-10m.
+ # - run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append
+ # if: runner.os == 'Windows'
+ # - run: vcpkg install openssl:x64-windows-static-md
+ # if: runner.os == 'Windows'
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ - name: cargo generate-lockfile
+ if: hashFiles('Cargo.lock') == ''
+ run: cargo generate-lockfile
+ - name: cargo test
+ run: cargo test --locked --all-features --all-targets
+ coverage:
+ # use llvm-cov to build and collect coverage and outputs in a format that
+ # is compatible with codecov.io
+ #
+ # note that codecov as of v4 requires that CODECOV_TOKEN from
+ #
+ # https://app.codecov.io/gh///settings
+ #
+ # is set in two places on your repo:
+ #
+ # - https://github.com/jonhoo/guardian/settings/secrets/actions
+ # - https://github.com/jonhoo/guardian/settings/secrets/dependabot
+ #
+ # (the former is needed for codecov uploads to work with Dependabot PRs)
+ #
+ # PRs coming from forks of your repo will not have access to the token, but
+ # for those, codecov allows uploading coverage reports without a token.
+ # it's all a little weird and inconvenient. see
+ #
+ # https://github.com/codecov/feedback/issues/112
+ #
+ # for lots of more discussion
+ runs-on: ubuntu-latest
+ name: ubuntu / stable / coverage
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ with:
+ components: llvm-tools-preview
+ - name: cargo install cargo-llvm-cov
+ uses: taiki-e/install-action@cargo-llvm-cov
+ - name: cargo generate-lockfile
+ if: hashFiles('Cargo.lock') == ''
+ run: cargo generate-lockfile
+ - name: cargo llvm-cov
+ run: cargo llvm-cov --locked --all-features --lcov --output-path lcov.info
+ - name: Record Rust version
+ run: echo "RUST=$(rustc --version)" >> "$GITHUB_ENV"
+ - name: Upload to codecov.io
+ uses: codecov/codecov-action@v4
+ with:
+ fail_ci_if_error: true
+ token: ${{ secrets.CODECOV_TOKEN }}
+ env_vars: OS,RUST