From ae416436e833da6e7638bebe8b0b71349eedb3a5 Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Sun, 27 Sep 2026 19:09:37 +0300 Subject: [PATCH 1/3] build!: ship the binary in the structured-proxy package The binary was a second package, structured-proxy-cli, which release-plz treated as a second release: a changelog of the whole project history, links to tags that never exist, and a second crate on crates.io. The project has one package and one release. - The `structured-proxy` binary is back in the structured-proxy package, behind a `cli` feature that is off by default; clap and tracing-subscriber are optional dependencies of that feature. A crate that adds the library compiles neither. - `cargo install structured-proxy --features cli` installs the binary; without the feature cargo names it. - The cli/ package and the workspace are gone; release-plz lists the one package with its v{version} tag and the root CHANGELOG.md. - CI builds and tests the binary on the default and injected-verifier legs (all-features includes it), checks that the default build links no CLI-only crate, and dry-runs the publish of the one package. The release workflow builds the binary with `--features cli,redis`; the artefacts, deb and rpm packaging are unchanged. BREAKING CHANGE: `cargo install structured-proxy` installs the binary only with `--features cli`. Closes #112 --- .github/workflows/ci.yml | 37 +++++++++++-------------- .github/workflows/release.yml | 7 ++--- Cargo.toml | 34 ++++++++++++++++------- README.md | 17 ++++++++---- cli/Cargo.toml | 43 ----------------------------- packaging/rpm/structured-proxy.spec | 2 +- release-plz.toml | 13 --------- {cli/src => src}/main.rs | 0 {cli/tests => tests}/cli.rs | 0 9 files changed, 55 insertions(+), 98 deletions(-) delete mode 100644 cli/Cargo.toml rename {cli/src => src}/main.rs (100%) rename {cli/tests => tests}/cli.rs (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a199b49..48ac535 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,8 +38,8 @@ jobs: # reaches for `--all-features`, docs.rs and cargo-semver-checks # included) can produce that combination. - name: rust_crypto - # Pure-Rust default backend (RustCrypto / rsa). - flags: "--features redis" + # Pure-Rust default backend (RustCrypto / rsa), with the binary. + flags: "--features redis,cli" - name: aws_lc_rs # Opt-in constant-time backend (aws-lc, C FFI); disables the default. flags: "--no-default-features --features aws_lc_rs,redis" @@ -48,8 +48,8 @@ jobs: # injects its own `hooks::TokenVerifier`. Links no JWT crypto (and # so no `rsa`), which is only true as long as nothing outside the # `builtin_jwt` gate reaches for jsonwebtoken — this leg is what - # keeps that honest. - flags: "--no-default-features --features redis" + # keeps that honest. The binary builds without a verifier too. + flags: "--no-default-features --features redis,cli" - name: all_features # Both backends at once, the build cargo-semver-checks and docs.rs # take. `aws_lc_rs` wins the tie; this leg keeps that wiring honest. @@ -78,50 +78,45 @@ jobs: # The default and injected-verifier builds are pure Rust: no C crypto # (ring, aws-lc) on any target. The aws_lc_rs and all-features legs link # aws-lc by choice. - # The library and the CLI package carry the same feature names (the CLI - # forwards its own to the library), so each leg's flags select the same - # backend in both when applied to the whole workspace. - name: No C crypto if: matrix.backend.name == 'rust_crypto' || matrix.backend.name == 'injected_verifier' run: | - tree=$(cargo tree --workspace -e normal --target all --prefix none --format '{p}' ${{ matrix.backend.flags }}) + tree=$(cargo tree -e normal --target all --prefix none --format '{p}' ${{ matrix.backend.flags }}) if printf '%s\n' "$tree" | grep -E '^(ring|aws-lc-rs|aws-lc-sys) v'; then echo "::error::C crypto is linked into the ${{ matrix.backend.name }} build" exit 1 fi - # A crate that depends on the library must not compile the binary's - # dependencies; they belong to the structured-proxy-cli package. - - name: No CLI dependencies in the library - if: matrix.backend.name == 'all_features' + # `cargo add structured-proxy` takes the default features: the library + # must compile none of the binary's dependencies, which sit behind `cli`. + - name: No CLI dependencies by default + if: matrix.backend.name == 'rust_crypto' run: | - tree=$(cargo tree -p structured-proxy -e normal --target all --prefix none --format '{p}' --all-features) + tree=$(cargo tree -e normal --target all --prefix none --format '{p}') if printf '%s\n' "$tree" | grep -E '^(clap|tracing-subscriber) v'; then - echo "::error::the library links a CLI-only dependency" + echo "::error::the default build links a CLI-only dependency" exit 1 fi - name: Clippy - run: cargo clippy --workspace --all-targets ${{ matrix.backend.flags }} + run: cargo clippy --all-targets ${{ matrix.backend.flags }} - name: Build - run: cargo build --release --workspace ${{ matrix.backend.flags }} + run: cargo build --release ${{ matrix.backend.flags }} - name: Test env: # Exercises the rate-limit reconciliation against the Redis service. SHIELD_REDIS_TEST_URL: redis://127.0.0.1:6379/ - run: cargo nextest run --workspace ${{ matrix.backend.flags }} + run: cargo nextest run ${{ matrix.backend.flags }} # nextest does not run doctests; cargo does. - name: Doc tests - run: cargo test --doc --workspace ${{ matrix.backend.flags }} + run: cargo test --doc ${{ matrix.backend.flags }} - # Packages and verifies both: the CLI against the library as packaged - # here, not the version on crates.io. - name: Publish dry-run if: matrix.backend.name == 'rust_crypto' - run: cargo publish --dry-run --workspace + run: cargo publish --dry-run security-audit: name: Security Audit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 74c0ce0..6f94dc7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,10 +60,9 @@ jobs: # `redis` is compiled in so the packaged `shield.redis_url` config # works (multi-instance shared rate limits) instead of silently # falling back to per-process counters. It is a pure-Rust dependency, - # so it stays musl-static-clean. The binary is the structured-proxy-cli - # package's; it lands in the workspace target directory under its - # own name, as before. - cargo build --release -p structured-proxy-cli --target ${{ matrix.target }} --features redis --bin structured-proxy + # so it stays musl-static-clean. The binary sits behind the `cli` + # feature. + cargo build --release --target ${{ matrix.target }} --features cli,redis --bin structured-proxy strip target/${{ matrix.target }}/release/structured-proxy || true - name: Package diff --git a/Cargo.toml b/Cargo.toml index 6c75bf4..c1f5163 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,7 @@ -# The library lives at the root; the binary is its own package in cli/, so a -# crate that depends on the library compiles none of the binary's dependencies. -[workspace] -members = ["cli"] - -# Both packages are released together, at one version. -[workspace.package] -version = "4.3.0" - [package] name = "structured-proxy" description = "Universal gRPC→REST transcoding proxy — config-driven, works with any gRPC service" -version.workspace = true +version = "4.3.0" edition = "2021" authors = ["Dmitry Prudnikov "] license = "Apache-2.0" @@ -30,6 +21,16 @@ features = ["redis"] name = "structured_proxy" path = "src/lib.rs" +# The standalone proxy: `cargo install structured-proxy --features cli`. Behind +# the `cli` feature, so a crate that depends on the library compiles none of +# its dependencies. +[[bin]] +name = "structured-proxy" +path = "src/main.rs" +required-features = ["cli"] +# Its crate name is the library's; the library's rustdoc is the one to keep. +doc = false + [dependencies] # HTTP framework axum = { version = "0.8", features = ["macros"] } @@ -115,6 +116,10 @@ getrandom = "0.4" # Envoy ext_authz `Authorization/Check` messages (External AuthZ). envoy-types = "0.7" +# The binary's command line and log output (the `cli` feature). +clap = { version = "4", features = ["derive"], optional = true } +tracing-subscriber = { version = "0.3", features = ["env-filter", "json"], optional = true } + [features] default = ["rust_crypto"] @@ -143,6 +148,10 @@ aws_lc_rs = ["builtin_jwt", "jsonwebtoken/aws_lc_rs"] # Shared Redis-backed rate-limit store for multi-instance deployments. redis = ["dep:redis"] +# The `structured-proxy` binary. Off by default: the library links none of its +# dependencies unless asked. The release packages build it with `cli,redis`. +cli = ["dep:clap", "dep:tracing-subscriber"] + [dev-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread"] } tower = { version = "0.5", features = ["util"] } @@ -170,6 +179,11 @@ tokio-stream = "0.1" # error-details integration test without a protoc binary. protox = "0.9" +# Runs the built binary, so it needs the binary built. +[[test]] +name = "cli" +required-features = ["cli"] + # The built-in verifier's cost with and without the claims cache. [[bench]] name = "jwt_verify" diff --git a/README.md b/README.md index 3678d26..25131b4 100644 --- a/README.md +++ b/README.md @@ -42,13 +42,17 @@ Works with **any** gRPC service via proto descriptor files. No code generation, ## Quick Start ```bash -# Install the binary (the structured-proxy-cli package) -cargo install structured-proxy-cli +# Install the binary (it sits behind the `cli` feature) +cargo install structured-proxy --features cli # Run with your service config structured-proxy --config my-service.yaml ``` +Prebuilt static Linux binaries and deb/rpm packages are attached to each GitHub +release. To embed the proxy in your own service instead, add the library with +`cargo add structured-proxy` (see [Library Usage](#library-usage)). + ## Configuration ```yaml @@ -570,10 +574,11 @@ answered by the CORS layer; any other `OPTIONS` request reaches its route. ## Library Usage -The `structured-proxy` crate is the library alone: the binary lives in the -`structured-proxy-cli` package, so a service that embeds the proxy compiles -none of the command-line dependencies (`clap`, `tracing-subscriber`). The -library starts no runtime and installs no logger of its own; it runs on the +`cargo add structured-proxy` adds the library alone: the binary and its +command-line dependencies (`clap`, `tracing-subscriber`) sit behind the `cli` +feature, which is off by default, so a service that embeds the proxy compiles +none of them. The library starts no runtime and installs no logger of its own; +it runs on the embedder's tokio runtime and logs through `tracing` to whatever subscriber the embedder sets up. diff --git a/cli/Cargo.toml b/cli/Cargo.toml deleted file mode 100644 index 7c71f38..0000000 --- a/cli/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "structured-proxy-cli" -description = "The structured-proxy binary: a config-driven gRPC→REST transcoding proxy" -version.workspace = true -edition = "2021" -authors = ["Dmitry Prudnikov "] -license = "Apache-2.0" -repository = "https://github.com/structured-world/structured-proxy" -homepage = "https://github.com/structured-world/structured-proxy" -readme = "../README.md" -keywords = ["grpc", "rest", "proxy", "transcoding", "protobuf"] -categories = ["network-programming", "web-programming::http-server", "command-line-utilities"] - -# Installed and packaged under the name it has always had, so deb/rpm -# packages, the systemd unit and existing configs stay the same. -[[bin]] -name = "structured-proxy" -path = "src/main.rs" -# Its crate name is the library's (`structured_proxy`), so its rustdoc would -# overwrite the library's in a workspace `cargo doc`; a binary has no API to -# document. -doc = false - -[dependencies] -# The library, without its default features: this package's own features pick -# the backends, so `--no-default-features` here reaches the library too. -structured-proxy = { path = "..", version = "4.3.0", default-features = false } - -clap = { version = "4", features = ["derive"] } -tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } -tokio = { version = "1", features = ["macros", "rt-multi-thread"] } -anyhow = "1" - -[features] -# The binary the release packages ship: pure-Rust JWT crypto and the shared -# Redis rate-limit store, so `cargo install structured-proxy-cli` gives the -# same build. -default = ["rust_crypto", "redis"] -# The library's features of the same names (see its Cargo.toml). -rust_crypto = ["structured-proxy/rust_crypto"] -aws_lc_rs = ["structured-proxy/aws_lc_rs"] -redis = ["structured-proxy/redis"] diff --git a/packaging/rpm/structured-proxy.spec b/packaging/rpm/structured-proxy.spec index 65d0bad..14e315b 100644 --- a/packaging/rpm/structured-proxy.spec +++ b/packaging/rpm/structured-proxy.spec @@ -1,7 +1,7 @@ # structured-proxy RPM spec. # # Build expects a pre-compiled musl-static `structured-proxy` binary in SOURCES/. -# The CI release pipeline runs `cargo build --release -p structured-proxy-cli --target $TARGET-unknown-linux-musl --bin structured-proxy`, +# The CI release pipeline runs `cargo build --release --target $TARGET-unknown-linux-musl --features cli,redis --bin structured-proxy`, # strips the result, copies it (and the packaging assets) into the rpmbuild # tree, then invokes `rpmbuild -bb`. diff --git a/release-plz.toml b/release-plz.toml index f7dde11..2162bf7 100644 --- a/release-plz.toml +++ b/release-plz.toml @@ -1,18 +1,5 @@ -# The library and the CLI share one version (`[workspace.package]`) and are -# released together. [[package]] name = "structured-proxy" -version_group = "structured-proxy" # Preserve the existing tag scheme (v1.0.0, v1.0.1, ...) instead of the # release-plz default of "{package}-v{version}", so tag history stays continuous. git_tag_name = "v{{ version }}" - -[[package]] -name = "structured-proxy-cli" -version_group = "structured-proxy" -# Published to crates.io with the library. The library's `v{version}` tag and -# GitHub release stand for both (the release workflow builds the binary from -# that tag); the CLI's own changes are recorded in its own changelog. -git_tag_enable = false -git_release_enable = false -changelog_path = "cli/CHANGELOG.md" diff --git a/cli/src/main.rs b/src/main.rs similarity index 100% rename from cli/src/main.rs rename to src/main.rs diff --git a/cli/tests/cli.rs b/tests/cli.rs similarity index 100% rename from cli/tests/cli.rs rename to tests/cli.rs From ca1c24c35a28ce2c8983c3c8c41e2ae08edbeabb Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Sun, 27 Sep 2026 19:20:46 +0300 Subject: [PATCH 2/3] fix(build): keep the redis store in the cli feature The former CLI package enabled `redis` by default and the release packages build with it, but `cli` pulled only the command-line dependencies. A binary installed with `--features cli` therefore ran `shield.sync` with a warning and per-instance limits only. - `cli` now carries `redis`, so `cargo install structured-proxy --features cli` builds what the release packages ship; CI and the release workflow build with `--features cli` alone. - A CLI test runs the binary with `shield.sync` and fails on the "not compiled in" fallback warning. - The default-build check also rejects `redis`. --- .github/workflows/ci.yml | 9 +-- .github/workflows/release.yml | 11 ++-- Cargo.toml | 5 +- README.md | 3 +- packaging/rpm/structured-proxy.spec | 2 +- tests/cli.rs | 97 ++++++++++++++++++++++------- 6 files changed, 91 insertions(+), 36 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 48ac535..86f7568 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,8 +38,9 @@ jobs: # reaches for `--all-features`, docs.rs and cargo-semver-checks # included) can produce that combination. - name: rust_crypto - # Pure-Rust default backend (RustCrypto / rsa), with the binary. - flags: "--features redis,cli" + # Pure-Rust default backend (RustCrypto / rsa), with the binary + # (`cli` carries `redis`). + flags: "--features cli" - name: aws_lc_rs # Opt-in constant-time backend (aws-lc, C FFI); disables the default. flags: "--no-default-features --features aws_lc_rs,redis" @@ -49,7 +50,7 @@ jobs: # so no `rsa`), which is only true as long as nothing outside the # `builtin_jwt` gate reaches for jsonwebtoken — this leg is what # keeps that honest. The binary builds without a verifier too. - flags: "--no-default-features --features redis,cli" + flags: "--no-default-features --features cli" - name: all_features # Both backends at once, the build cargo-semver-checks and docs.rs # take. `aws_lc_rs` wins the tie; this leg keeps that wiring honest. @@ -93,7 +94,7 @@ jobs: if: matrix.backend.name == 'rust_crypto' run: | tree=$(cargo tree -e normal --target all --prefix none --format '{p}') - if printf '%s\n' "$tree" | grep -E '^(clap|tracing-subscriber) v'; then + if printf '%s\n' "$tree" | grep -E '^(clap|tracing-subscriber|redis) v'; then echo "::error::the default build links a CLI-only dependency" exit 1 fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6f94dc7..7594f1f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,12 +57,11 @@ jobs: - name: Build static binary run: | - # `redis` is compiled in so the packaged `shield.redis_url` config - # works (multi-instance shared rate limits) instead of silently - # falling back to per-process counters. It is a pure-Rust dependency, - # so it stays musl-static-clean. The binary sits behind the `cli` - # feature. - cargo build --release --target ${{ matrix.target }} --features cli,redis --bin structured-proxy + # The binary sits behind the `cli` feature, which carries `redis` so + # `shield.sync` works (multi-instance shared rate limits) instead of + # falling back to per-process counters. Redis is a pure-Rust + # dependency, so the build stays musl-static-clean. + cargo build --release --target ${{ matrix.target }} --features cli --bin structured-proxy strip target/${{ matrix.target }}/release/structured-proxy || true - name: Package diff --git a/Cargo.toml b/Cargo.toml index c1f5163..7c0be87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -149,8 +149,9 @@ aws_lc_rs = ["builtin_jwt", "jsonwebtoken/aws_lc_rs"] redis = ["dep:redis"] # The `structured-proxy` binary. Off by default: the library links none of its -# dependencies unless asked. The release packages build it with `cli,redis`. -cli = ["dep:clap", "dep:tracing-subscriber"] +# dependencies unless asked. It carries `redis`, so `cargo install +# structured-proxy --features cli` builds what the release packages ship. +cli = ["dep:clap", "dep:tracing-subscriber", "redis"] [dev-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread"] } diff --git a/README.md b/README.md index 25131b4..7fa7fa3 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,8 @@ Works with **any** gRPC service via proto descriptor files. No code generation, ## Quick Start ```bash -# Install the binary (it sits behind the `cli` feature) +# Install the binary: the `cli` feature builds what the release packages ship, +# Redis-backed shared rate limits included cargo install structured-proxy --features cli # Run with your service config diff --git a/packaging/rpm/structured-proxy.spec b/packaging/rpm/structured-proxy.spec index 14e315b..2b35221 100644 --- a/packaging/rpm/structured-proxy.spec +++ b/packaging/rpm/structured-proxy.spec @@ -1,7 +1,7 @@ # structured-proxy RPM spec. # # Build expects a pre-compiled musl-static `structured-proxy` binary in SOURCES/. -# The CI release pipeline runs `cargo build --release --target $TARGET-unknown-linux-musl --features cli,redis --bin structured-proxy`, +# The CI release pipeline runs `cargo build --release --target $TARGET-unknown-linux-musl --features cli --bin structured-proxy`, # strips the result, copies it (and the packaging assets) into the rpmbuild # tree, then invokes `rpmbuild -bb`. diff --git a/tests/cli.rs b/tests/cli.rs index b343339..f448ab0 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -75,14 +75,39 @@ fn an_invalid_config_fails_before_listening() { assert!(!out.status.success()); } -#[test] -fn a_valid_config_starts_the_proxy() { - // A port that was free a moment ago; the proxy binds it itself. - let port = TcpListener::bind("127.0.0.1:0") +/// A port that was free a moment ago; the proxy binds it itself. +fn free_port() -> u16 { + TcpListener::bind("127.0.0.1:0") .unwrap() .local_addr() .unwrap() - .port(); + .port() +} + +/// The response to `GET /health/live` once the proxy listens on `port`. +/// Liveness does not depend on the upstream, so it answers as soon as the +/// listener is up. +fn live(port: u16) -> String { + let deadline = Instant::now() + Duration::from_secs(20); + loop { + if let Ok(mut stream) = TcpStream::connect(("127.0.0.1", port)) { + stream + .write_all( + b"GET /health/live HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n", + ) + .unwrap(); + let mut response = String::new(); + stream.read_to_string(&mut response).unwrap(); + return response; + } + assert!(Instant::now() < deadline, "the proxy never listened"); + std::thread::sleep(Duration::from_millis(50)); + } +} + +#[test] +fn a_valid_config_starts_the_proxy() { + let port = free_port(); let path = std::env::temp_dir().join(format!( "structured-proxy-cli-test-{}.yaml", std::process::id() @@ -106,23 +131,51 @@ fn a_valid_config_starts_the_proxy() { .unwrap(), ); - // Liveness does not depend on the upstream, so it answers as soon as the - // listener is up. - let deadline = Instant::now() + Duration::from_secs(20); - let response = loop { - if let Ok(mut stream) = TcpStream::connect(("127.0.0.1", port)) { - stream - .write_all( - b"GET /health/live HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n", - ) - .unwrap(); - let mut response = String::new(); - stream.read_to_string(&mut response).unwrap(); - break response; - } - assert!(Instant::now() < deadline, "the proxy never listened"); - std::thread::sleep(Duration::from_millis(50)); - }; + let response = live(port); + std::fs::remove_file(&path).unwrap(); + assert!(response.starts_with("HTTP/1.1 200"), "{response}"); +} + +#[test] +fn the_binary_keeps_the_shared_rate_limit_store() { + // The installed binary is the one the release packages ship, so + // `shield.sync` works in it: without Redis compiled in, the proxy would + // warn and fall back to per-instance limits. + let port = free_port(); + let dir = std::env::temp_dir(); + let id = std::process::id(); + let path = dir.join(format!("structured-proxy-cli-test-sync-{id}.yaml")); + let log_path = dir.join(format!("structured-proxy-cli-test-sync-{id}.log")); + std::fs::write( + &path, + format!( + "listen:\n http: \"127.0.0.1:{port}\"\n\ + upstream:\n default: \"http://127.0.0.1:9\"\n\ + descriptors: []\n\ + shield:\n enabled: true\n\ + \x20 profiles:\n anon: {{ rate: \"60/min\" }}\n\ + \x20 rules:\n - pattern: \"/api/**\"\n key: {{ type: ip }}\n profile: \"anon\"\n\ + \x20 sync: {{ redis_url: \"redis://127.0.0.1:9/\", interval_ms: 500 }}\n" + ), + ) + .unwrap(); + let log = std::fs::File::create(&log_path).unwrap(); + let proxy = Running( + Command::new(BIN) + .arg("--config") + .arg(&path) + .env("RUST_LOG", "warn") + .stdout(log) + .stderr(Stdio::null()) + .spawn() + .unwrap(), + ); + + let response = live(port); + drop(proxy); + let output = std::fs::read_to_string(&log_path).unwrap(); std::fs::remove_file(&path).unwrap(); + std::fs::remove_file(&log_path).unwrap(); assert!(response.starts_with("HTTP/1.1 200"), "{response}"); + assert!(!output.contains("not compiled in"), "{output}"); } From 8089d45ec43006ad5c0d7abcebe85b814d35aaa8 Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Sun, 27 Sep 2026 19:21:38 +0300 Subject: [PATCH 3/3] ci: verify the packaged binary in the publish dry-run The binary requires the `cli` feature, so a dry-run with default features compiled only the library of the packaged crate. With `--features cli` it builds the binary from the package as `cargo install` would. --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86f7568..c5a611e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -115,9 +115,11 @@ jobs: - name: Doc tests run: cargo test --doc ${{ matrix.backend.flags }} + # With `cli`, so the packaged crate's binary compiles too: without the + # feature cargo verifies the library alone. - name: Publish dry-run if: matrix.backend.name == 'rust_crypto' - run: cargo publish --dry-run + run: cargo publish --dry-run --features cli security-audit: name: Security Audit