From 3edad1a6fee6e7671b0eb92ea43d0b24bd6c4450 Mon Sep 17 00:00:00 2001 From: Justin Kovacich Date: Wed, 9 Sep 2026 10:37:43 -0400 Subject: [PATCH 01/10] docs(license): add the MIT and Apache-2.0 license texts `Cargo.toml` has declared `license = "MIT OR Apache-2.0"` for a while, but neither license text was ever committed. A manifest field is not what a customer's legal review looks for -- it looks for the file -- and GitHub's own API reported `license: null` for this repo because it keys off the file too, understating the project's posture. Both texts are copied verbatim from the sibling `uds_protocol` repo so the protocol libraries carry identical wording. They are also picked up by `cargo package`, so a published `.crate` now ships its own license terms. Neither file names a copyright holder -- that matches `uds_protocol` today, and is worth settling across all three protocol repos at once rather than diverging here. Co-Authored-By: Claude Opus 5 (1M context) --- LICENSE-APACHE | 176 +++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 23 +++++++ 2 files changed, 199 insertions(+) create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. From a11c26097e09ca93738a41c992911e6b18b393e3 Mon Sep 17 00:00:00 2001 From: Justin Kovacich Date: Wed, 9 Sep 2026 10:37:43 -0400 Subject: [PATCH 02/10] build(release): add cargo-release config and a gated crates.io publish workflow The crate has complete publish metadata and CI already proves publishability on every run (`cargo publish --dry-run` in the `package` job), but there was no way to actually cut a release. This adds the two missing pieces. `release.toml` configures cargo-release for a local version bump: a `v{version}` tag, a `chore(release):` commit, and `cargo test --locked --all-features` as the pre-release hook. `--all-features` rather than a bare `cargo test`, because `default = []` here, so the default-feature run exercises almost none of the crate. `publish = false` keeps a local `cargo release ` from ever reaching crates.io; the CI job uses the `cargo release publish` subcommand, which overrides that field. `release.yml` publishes on a `v*` tag push and is inert until deliberately enabled -- it needs both the canonical repository (so a fork pushing a tag can never publish under this crate's name) and the `SIMPLE_DOIP_PUBLISH_ENABLED` repo variable. Merging this publishes nothing. The job verifies the tag names the version the manifest actually carries before it spends it, since a crates.io publish can only be yanked, never undone. `--allow-branch '*'` is required because a tag-push run is in detached-HEAD state, which cargo-release's branch check rejects. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/release.yml | 62 +++++++++++++++++++++++++++++++++++ release.toml | 15 +++++++++ 2 files changed, 77 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 release.toml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0ce2869 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,62 @@ +# Tag-driven publish to crates.io. Push `v{version}` on main and this runs; +# nothing here fires on a normal push or PR merge. +# +# Publishing is GATED and inert until someone deliberately turns it on -- +# see the `if:` on the publish job. +name: Release + +on: + push: + tags: ["v*"] + +env: + CARGO_TERM_COLOR: always + +permissions: + contents: read + +jobs: + publish: + name: Publish to crates.io + runs-on: ubuntu-latest + # Two independent gates: + # + # 1. Repository -- a fork that pushes a `v*` tag must never publish under + # this crate's name, even if it somehow carries a token. + # 2. `SIMPLE_DOIP_PUBLISH_ENABLED` -- the deliberate switch. Set the repo + # variable to `true` once `CARGO_REGISTRY_TOKEN` is in secrets and the + # crate has a crates.io owner. Until then this job is skipped, so + # merging this workflow publishes nothing. + # + # Publishing needs the token and crates.io reachability only. It does not + # need the Kellnr proxy -- that fronts crates.io for dft-side *consumption* + # of the published crate, which is a separate concern. + if: >- + github.repository == 'luminartech/simple_doip' + && vars.SIMPLE_DOIP_PUBLISH_ENABLED == 'true' + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + + # A crates.io publish cannot be undone -- only yanked -- so confirm the + # tag names the version the manifest actually carries before spending it. + - name: Verify the tag matches the manifest version + run: | + manifest=$(cargo metadata --no-deps --format-version 1 \ + | jq -r '.packages[0].version') + tag="${GITHUB_REF_NAME#v}" + if [ "$manifest" != "$tag" ]; then + echo "::error::tag $GITHUB_REF_NAME wants $tag, Cargo.toml says $manifest" + exit 1 + fi + echo "publishing simple_doip $manifest" + + - run: cargo install cargo-release --version '^0.25' --locked + + # `--allow-branch '*'`: a tag-push run is in detached-HEAD state, which + # cargo-release's `allow-branch` check would otherwise reject. The gates + # above already decide whether this job runs at all. + - run: cargo release publish --allow-branch '*' --no-confirm --execute + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/release.toml b/release.toml new file mode 100644 index 0000000..97c6ed2 --- /dev/null +++ b/release.toml @@ -0,0 +1,15 @@ +# cargo-release config for the standalone simple_doip repo (single crate). +# +# `publish = false` keeps a local `cargo release ` from ever reaching +# crates.io -- a version bump is a local, reviewable commit. CI publishes with +# the `cargo release publish` *subcommand*, which overrides this field, so the +# release workflow still works with it set. +publish = false +registry = "crates-io" +tag-name = "v{{version}}" +tag-message = "simple_doip v{{version}}" +pre-release-commit-message = "chore(release): v{{version}}" +allow-branch = ["main"] +# `--all-features`, not a bare `cargo test`: `default = []` here, so the +# default-feature test run exercises almost none of the crate. +pre-release-hook = ["cargo", "test", "--locked", "--all-features"] From af5ff82613dc6981d43296cfafe6af7bd445a0a3 Mon Sep 17 00:00:00 2001 From: Justin Kovacich Date: Wed, 9 Sep 2026 13:38:34 -0400 Subject: [PATCH 03/10] build(cargo): document all features on docs.rs, and stop shipping repo tooling `default = []`, so docs.rs would have built the `no_std` core alone and published a reference containing no `client`, `server`, `codec`, `alloc` or `std` API -- most of what the README points a reader at. Every feature is additive, so `all-features = true` is safe and gives the published docs the whole surface. Verified with `cargo doc --no-deps --all-features`: the `client`, `server` and `message_codec` modules now render. `release.toml` and `rust-toolchain.toml` were also landing inside the published `.crate`. Neither is part of the library a consumer receives -- `rust-toolchain.toml` has no effect on a dependency at all -- so both move to `exclude`. The package drops from 77 files to 75. Co-Authored-By: Claude Opus 5 (1M context) --- Cargo.toml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d3d0363..a0932b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,21 @@ repository = "https://github.com/luminartech/simple_doip" readme = "README.md" keywords = ["doip", "iso13400", "automotive", "diagnostics", "no-std"] categories = ["network-programming", "embedded", "no-std"] -exclude = ["docs/", ".github/", ".vscode/"] +exclude = [ + "docs/", + ".github/", + ".vscode/", + # Repo-local tooling, not part of the library a consumer receives. + "release.toml", + "rust-toolchain.toml", +] + +# `default = []`, so docs.rs would otherwise build the no_std core alone and +# publish a reference with no `client`, `server`, `codec`, `alloc` or `std` +# API in it -- most of what the README points a reader at. Every feature is +# additive, so all of them can be on at once. +[package.metadata.docs.rs] +all-features = true [dependencies] automotive-wire-codec = "0.3" From 5ec8eb07e09c68e7718194200415c58c60fd95f2 Mon Sep 17 00:00:00 2001 From: Justin Kovacich Date: Wed, 9 Sep 2026 13:38:34 -0400 Subject: [PATCH 04/10] chore(vscode): point the debug configurations at the crate's actual name All six configurations passed `--package=doip`, the crate's name before it was renamed to `simple_doip`, so every one of them failed for anyone who clicked it. The launch config is excluded from the published crate but is visible to anyone who opens the repository. Co-Authored-By: Claude Opus 5 (1M context) --- .vscode/launch.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d6f0d78..7f5506b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,16 +7,16 @@ { "type": "lldb", "request": "launch", - "name": "Debug unit tests in library 'doip'", + "name": "Debug unit tests in library 'simple_doip'", "cargo": { "args": [ "test", "--no-run", "--lib", - "--package=doip" + "--package=simple_doip" ], "filter": { - "name": "doip", + "name": "simple_doip", "kind": "lib" } }, @@ -31,7 +31,7 @@ "args": [ "build", "--example=simple_client", - "--package=doip" + "--package=simple_doip" ], "filter": { "name": "simple_client", @@ -50,7 +50,7 @@ "test", "--no-run", "--example=simple_client", - "--package=doip" + "--package=simple_doip" ], "filter": { "name": "simple_client", @@ -68,7 +68,7 @@ "args": [ "build", "--example=echo_server", - "--package=doip" + "--package=simple_doip" ], "filter": { "name": "echo_server", @@ -87,7 +87,7 @@ "test", "--no-run", "--example=echo_server", - "--package=doip" + "--package=simple_doip" ], "filter": { "name": "echo_server", @@ -106,7 +106,7 @@ "test", "--no-run", "--test=integration_test", - "--package=doip" + "--package=simple_doip" ], "filter": { "name": "integration_test", From 2aff4da6b844a74604f3b5c30a743fa498dc21a6 Mon Sep 17 00:00:00 2001 From: Justin Kovacich Date: Wed, 9 Sep 2026 13:38:34 -0400 Subject: [PATCH 05/10] chore(deps): move off the yanked futures-util 0.3.28 `futures-util 0.3.28` is yanked on crates.io, so every `cargo publish` run warned about it. `cargo update -p futures-util` takes it (and `futures-task`) to 0.3.34. Lockfile only -- no manifest requirement changes, and consumers of the published crate resolve their own graph regardless. Verified `cargo check --locked` with `--all-features` and with `--no-default-features`. Co-Authored-By: Claude Opus 5 (1M context) --- Cargo.lock | 56 +++++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d48cd9d..fb7441a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,7 +31,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -108,9 +108,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" @@ -135,38 +135,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.5", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -176,7 +176,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -289,12 +288,6 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "proc-macro2" version = "1.0.106" @@ -389,7 +382,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -403,6 +396,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "thiserror" version = "2.0.18" @@ -420,7 +424,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -457,7 +461,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] From d3e6a48729073b386bc290018e0961c2a444c097 Mon Sep 17 00:00:00 2001 From: Justin Kovacich Date: Wed, 9 Sep 2026 13:46:55 -0400 Subject: [PATCH 06/10] docs: add a changelog reconstructed from the release history The crate is at 0.5.2 with three breaking changes behind it -- the 0.2.0 zero-copy `no_std` core, 0.4.0's `ServerConnectionHandler::diagnostic_message` signature, and 0.5.0's acknowledgement timeout -- and no record of any of them. Anyone arriving at 0.5.2 had no way to see what changed or what broke. Reconstructed from the version-bump commits and the pull requests that carried them, in Keep a Changelog form to match `uds_protocol`. Only v0.1.0 and v0.5.1 were ever tagged, so the intermediate versions have no comparison range to link; the file says so rather than carrying links that 404. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ef587fb --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,107 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/) (treating +`0.x` breaking changes as minor bumps, per the Cargo/SemVer convention for +pre-1.0 crates). + +This file was reconstructed from the commit and pull-request history when the +crate was prepared for publication, so entries before that point describe what +changed rather than what was announced at the time. + +## [Unreleased] + +### Added + +- `LICENSE-MIT` and `LICENSE-APACHE`. The manifest had declared + `MIT OR Apache-2.0` without carrying either text. +- `CHANGELOG.md`, `CONTRIBUTING.md`, `SECURITY.md`. +- `release.toml` and a tag-driven release workflow. + +### Changed + +- docs.rs now builds with all features, so the `client`, `server` and `codec` + API appears in the published documentation. `default = []`, so the default + build documents only the `no_std` core. +- `release.toml` and `rust-toolchain.toml` are no longer packaged into the + published crate. + +## [0.5.2] — 2026-09-03 + +### Fixed + +- A failed connection reported `Error::SocketNotBound` instead of the + underlying connect error, so the reason a connection failed was discarded + and no log level revealed it. + +## [0.5.1] — 2026-08-25 + +### Fixed + +- A response already buffered by the client was dropped when the connection + closed, instead of being delivered to the caller waiting for it. + +## [0.5.0] — 2026-08-25 + +### Changed + +- **Breaking:** the diagnostic-message response timeout is configurable on + `ClientOptions`, and the client now waits `A_DoIP_Diagnostic_Message` for an + acknowledgement rather than the 50 ms figure ISO 13400-2 places on a DoIP + *entity*. The old value timed out on any peer that took longer than 50 ms to + acknowledge. + +## [0.4.0] — 2026-08-19 + +### Changed + +- **Breaking:** `ServerConnectionHandler::diagnostic_message` takes + `&mut dyn ResponseWriter` and returns `Result<(), Error>`. It previously + returned a single `OwnedMessage`, so a handler could emit either the + `DiagnosticMessageAck` or the functional response but not both — which is + what DoIP prescribes, and what a UDS tester waits for. + +### Added + +- `ResponseWriter`, so a handler can emit an acknowledgement, any number of + "response pending" messages, and a final answer, awaiting work between them. +- `Server::run_server_with_listener`, for injecting a bound `TcpListener`. +- `Server::run_udp_responder`, answering vehicle-identification requests over + UDP. + +## [0.3.1] — 2026-08-12 + +### Changed + +- A routing-activation denial is returned to the caller as an error instead of + only being logged. + +## [0.3.0] — 2026-07-30 + +### Changed + +- `MessageError` is built on the `automotive-wire-codec` error fragments, which + makes that crate's error taxonomy part of this crate's public API. + +## [0.2.0] — 2026-07-30 + +### Changed + +- **Breaking:** the protocol core is `no_std` and zero-copy. Messages borrow + from the receive buffer instead of allocating, `alloc` gates the owned + mirrors, and `std` and the async layers became opt-in Cargo features + (`default = []`). + +## [0.1.0] + +Initial implementation: DoIP message types, framing, and an async client and +server over tokio. + + + +[Unreleased]: https://github.com/luminartech/simple_doip/compare/v0.5.1...HEAD +[0.5.1]: https://github.com/luminartech/simple_doip/compare/v0.1.0...v0.5.1 +[0.1.0]: https://github.com/luminartech/simple_doip/releases/tag/v0.1.0 From 2b4318043d42e7a91958d7c73c5ca502f331d407 Mon Sep 17 00:00:00 2001 From: Justin Kovacich Date: Wed, 9 Sep 2026 13:46:55 -0400 Subject: [PATCH 07/10] docs: state the security-reporting and contribution policies A public automotive diagnostics library with no stated way to report a vulnerability leaves a finder with only the public issue tracker. `SECURITY.md` routes reports through GitHub's private vulnerability reporting, which is now enabled on the repository. It also records the two protocol properties a report has to be assessed against, because both are ISO 13400-2's design rather than defects here: DoIP carries no transport security, and routing activation is addressing, not authentication. `CONTRIBUTING.md` says pull requests are welcome and gives the feature-aware test commands -- `default = []`, so a bare `cargo test` exercises only the `no_std` core, which is not obvious -- the bare-metal target check, what CI adds, the golden-vector rule for wire-format changes, and the dual-license terms contributions are accepted under. Co-Authored-By: Claude Opus 5 (1M context) --- CONTRIBUTING.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ SECURITY.md | 35 +++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f5751f4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,55 @@ +# Contributing + +Pull requests are welcome, as are bug reports and questions in the issue +tracker. + +[`ARCHITECTURE.md`](ARCHITECTURE.md) is the place to start for anything beyond +a small fix. It covers the feature-gated layering, the sans-io framing/decode +seam, the error taxonomy, and — in its last section — the invariants to +preserve when changing the crate. + +## Building and testing + +The minimum supported Rust version is **1.88**. `default = []`, so a bare +`cargo test` exercises only the `no_std` core: + +```sh +cargo test --all-features # everything +cargo test --features client,server # the async layers +cargo test --no-default-features # the no_std core alone +``` + +The `no_std` core must keep building for a bare-metal target: + +```sh +rustup target add thumbv7em-none-eabihf +cargo check --no-default-features --target thumbv7em-none-eabihf +``` + +CI additionally checks each feature individually, `cargo fmt`, and +`cargo clippy --all-features -- -D warnings -Dclippy::pedantic`, and builds the +documentation with `RUSTDOCFLAGS=-D warnings`. Running the three commands above +plus `cargo fmt` and clippy locally will catch almost everything before it gets +there. + +## Wire-format changes + +`tests/golden_vectors.rs` decodes and re-encodes byte sequences held as `.hex` +files in `tests/golden/`. A change that alters the bytes on the wire should +either fail one of those vectors or add a new one — a round-trip test written +against the crate's own output passes whenever `encode` and `decode` share the +same misreading. + +## Commits and pull requests + +Commit subjects follow [Conventional Commits](https://www.conventionalcommits.org/) +(`feat:`, `fix:`, `docs:`, `build:`, `chore:`, with a `!` for a breaking +change), because the changelog is organized around them. Say what changed and +why in the body. + +## Licensing + +Contributions are dual-licensed under +[MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE), at the user's option, +matching the crate itself. By opening a pull request you agree that your +contribution may be distributed under those terms. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..2f5d9ae --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,35 @@ +# Security policy + +## Reporting a vulnerability + +Report security issues through GitHub's private vulnerability reporting: open +the [Security tab](https://github.com/luminartech/simple_doip/security) and +choose **Report a vulnerability**. That opens a private advisory visible only +to the maintainers. + +Please do not open a public issue for a security report. + +A report is most useful with the crate version, the feature set enabled, and a +byte sequence or test case that reproduces the behavior. + +## Supported versions + +This crate is pre-1.0. Fixes land on the latest published version; there are no +maintained release branches. + +## Scope + +`simple_doip` implements the ISO 13400-2 transport. Two properties of that +protocol matter when assessing a report, because they are the specification's +design rather than defects in this crate: + +- **DoIP carries no transport security.** Connections on `TCP_PORT` (`13400`) + are in the clear. `TCP_TLS_PORT` (`3496`) is defined by ISO 13400-2, and + nothing in this crate uses it. +- **Routing activation is not authentication.** It is an addressing handshake. + Any access control over a diagnostic session belongs to the layers above + this one. + +What is in scope: anything that makes the crate misbehave on attacker-supplied +bytes — a panic, an out-of-bounds read, an unbounded allocation, a decode that +accepts a frame it should reject, or a hang reachable from the wire. From e850f838aab3af2eb38e2698e1211720c645bf9e Mon Sep 17 00:00:00 2001 From: Justin Kovacich Date: Wed, 9 Sep 2026 13:46:55 -0400 Subject: [PATCH 08/10] docs(readme): replace the status inventory with scope and limitations The README opened with ~45 lines of known-gap inventory written in a defect-report register, down to internal state names and private method paths. Accurate, but it is the crate's front page on crates.io, and the detail served a maintainer rather than someone deciding whether the crate fits. What survives is the five bounds that change how the crate is used -- no TLS, one connection at a time, no unsolicited announcement, the acknowledge-before- respond requirement, and the untracked per-connection logical address -- each in a line, pointing at ARCHITECTURE.md for the mechanics. Nothing is lost: ARCHITECTURE.md section 7.6 already carried all of it, and described itself as a repeat of the README. That relationship is now the other way round, so the section reads as the authority it has become. Also links the license files from the README's license section, now that they exist, and points at the new contributing and security policies. Co-Authored-By: Claude Opus 5 (1M context) --- ARCHITECTURE.md | 4 +-- README.md | 79 +++++++++++++++++++------------------------------ 2 files changed, 33 insertions(+), 50 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 1a8b3b9..1587f28 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -566,8 +566,8 @@ returning `true` breaks it. Either delete the field or make the loop honor it. ### 7.6 Other rough edges -These are documented in `README.md` under **Status** and are repeated here only -as a pointer: no TLS; no unsolicited UDP vehicle announcement at power-on +The README's **Scope and limitations** section names these for an integrator +choosing the crate; the mechanics are here. No TLS; no unsolicited UDP vehicle announcement at power-on (identification requests over UDP *are* answered, but only by `Server::run_udp_responder` on a socket the caller binds and drives — `run_server` binds TCP alone — and only the broadcast `0x0001` form, since `Payload::decode` diff --git a/README.md b/README.md index 850bd79..11fa10c 100644 --- a/README.md +++ b/README.md @@ -8,53 +8,29 @@ the feature-gated layering, the sans-io framing/decode seam, the error taxonomy, the relationship to `automotive-wire-codec`, and the known issues and deferred refactors a new maintainer should read before changing anything. -## Status - -The protocol core (framing, message encode/decode, golden-vector-tested -against the on-wire format) is solid and exercised by the test suite. The -async `client` and `server` layers work for the common case but have known -gaps a new integrator should know about before relying on them: - -- **No TLS.** Connections are established in the clear on `TCP_PORT` - (`13400`); `TCP_TLS_PORT` (`3496`) is defined per ISO 13400-2 but nothing in - this crate uses it. -- **No unsolicited UDP vehicle announcement.** The server never sends the UDP - vehicle-announcement broadcast on startup, so a tester learns of an entity - only by asking. Vehicle identification requests over UDP *are* answered, but - only by `Server::run_udp_responder`, on a `UdpSocket` the caller bound and - drives: `run_server` binds TCP alone, so an entity that starts through it - and nothing else is invisible to a discovery probe. `run_udp_responder` - answers the broadcast request form (`0x0001`) only; the directed - with-EID (`0x0002`) and with-VIN (`0x0003`) forms are declined, because - `Payload::decode` discards the EID/VIN bytes and the responder cannot tell - whether it is the addressee. -- **The server accepts one TCP connection at a time.** The accept loop in - `Server::run_server_with_listener` (which `Server::run_server` delegates to) - awaits each client's connection handling to completion before calling - `accept()` again, so a second client cannot connect while the first is still - being served — and one tester that connects and then stalls wedges that - entity until it disconnects. -- **Entity status requests and vehicle identification requests over TCP are - silently dropped.** `Server::handle_client_message` logs a warning and sends - no reply for either, so a tester that asks gets silence rather than an error - or a negative response. Identification requests are answered on the UDP path - only. -- **A `DiagnosticMessage` arriving while the client is waiting for an ACK is - silently discarded.** After `Client::send_diagnostic_message`, the inner - client is in its `AwaitAck` state; a `DiagnosticMessage` that arrives before - the acknowledgement is neither buffered nor forwarded to the update channel - (only a generic `trace!` of the received message marks its passing) — it is - simply dropped. This requires the peer to acknowledge - before it responds. A peer that responds first (or that coalesces both into - one burst the client reads out of order) will appear to never answer, and the - subsequent `receive_diagnostic_response` will time out. -- **`ClientConnectionInfo::logical_address` is always `0x0000`.** The server - does not yet track per-connection logical addresses, so this field is a - placeholder rather than the client's real address. -- The handler passed to `Server::new` is not validated. - -None of this blocks bare-metal or single-client use; it matters if you need -concurrent clients, unsolicited announcement, or TLS today. +## Scope and limitations + +The protocol core — framing, message encode/decode — is golden-vector tested +against the on-wire format. The async `client` and `server` cover the common +case, within these bounds: + +- **No TLS.** Connections are in the clear on `TCP_PORT` (`13400`); + `TCP_TLS_PORT` (`3496`) is defined by ISO 13400-2 and unused here. +- **The server serves one TCP connection at a time.** A second client cannot + connect while the first is being served. +- **No unsolicited UDP vehicle announcement.** A tester learns of an entity only + by asking, and identification is answered on the UDP path only, by + `Server::run_udp_responder` on a socket the caller binds and drives. +- **The client requires the peer to acknowledge before it responds.** A + `DiagnosticMessage` that arrives while the client is waiting for the + acknowledgement is dropped, so a peer that answers first appears never to + answer at all. +- **`ClientConnectionInfo::logical_address` is not yet tracked per connection.** + +[`ARCHITECTURE.md`](ARCHITECTURE.md) §7 has the mechanics behind each of these, +and the deferred work around them. + +None of this constrains bare-metal or single-client use. ## Quickstart @@ -156,6 +132,13 @@ semver: a breaking change in that crate is a breaking change here too. The minimum supported Rust version is **1.88**, bound by let-chain syntax used in this crate. +## Contributing + +Pull requests, bug reports and questions are welcome — see +[`CONTRIBUTING.md`](CONTRIBUTING.md). Security reports go through GitHub's +private vulnerability reporting; see [`SECURITY.md`](SECURITY.md). + ## License -Licensed under either of MIT or Apache-2.0 at your option. +Licensed under either of [MIT](LICENSE-MIT) or +[Apache-2.0](LICENSE-APACHE) at your option. From 7595cd59b14c37d098ad264f5d0291048fe64d1d Mon Sep 17 00:00:00 2001 From: Justin Kovacich Date: Wed, 9 Sep 2026 13:46:55 -0400 Subject: [PATCH 09/10] ci(release): publish through crates.io trusted publishing Replaces the `CARGO_REGISTRY_TOKEN` secret with `rust-lang/crates-io-auth-action`, which trades the workflow's GitHub OIDC identity for a publish token scoped to this crate and revoked when the job ends. Nothing long-lived is stored in the repository, so there is no token to provision, rotate, or leak, and no personal account's credential standing behind the crate. The trade-off is a prerequisite rather than a cost: a trusted publisher can only be configured on a crate that already exists, so the first version is published by hand. That is the intent regardless -- the first publish claims the name permanently. The job's gates are unchanged, and it gains only `id-token: write`. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/release.yml | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0ce2869..8e67fad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,18 +22,18 @@ jobs: # Two independent gates: # # 1. Repository -- a fork that pushes a `v*` tag must never publish under - # this crate's name, even if it somehow carries a token. + # this crate's name. # 2. `SIMPLE_DOIP_PUBLISH_ENABLED` -- the deliberate switch. Set the repo - # variable to `true` once `CARGO_REGISTRY_TOKEN` is in secrets and the - # crate has a crates.io owner. Until then this job is skipped, so + # variable to `true` once the crate exists on crates.io and a trusted + # publisher is configured for it. Until then this job is skipped, so # merging this workflow publishes nothing. - # - # Publishing needs the token and crates.io reachability only. It does not - # need the Kellnr proxy -- that fronts crates.io for dft-side *consumption* - # of the published crate, which is a separate concern. if: >- github.repository == 'luminartech/simple_doip' && vars.SIMPLE_DOIP_PUBLISH_ENABLED == 'true' + permissions: + # Mint the OIDC token that crates.io trades for a short-lived publish + # token. This is the whole reason no registry secret appears below. + id-token: write steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable @@ -52,6 +52,19 @@ jobs: fi echo "publishing simple_doip $manifest" + # Trusted publishing: crates.io trades this workflow's GitHub OIDC + # identity for a token scoped to this crate, which is revoked when the + # job ends. Nothing long-lived is stored in this repository. + # + # It requires a trusted publisher configured on the crate naming this + # repository and this workflow file (`release.yml`). That configuration + # can only be added to a crate that already exists, so the first version + # is published by hand -- which is the intent anyway, since the first + # publish claims the name permanently. + - name: Authenticate with crates.io + id: auth + uses: rust-lang/crates-io-auth-action@v1 + - run: cargo install cargo-release --version '^0.25' --locked # `--allow-branch '*'`: a tag-push run is in detached-HEAD state, which @@ -59,4 +72,4 @@ jobs: # above already decide whether this job runs at all. - run: cargo release publish --allow-branch '*' --no-confirm --execute env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} From 3870c3ce9b0f71668188d175d393ff3aad2ceae0 Mon Sep 17 00:00:00 2001 From: Justin Kovacich Date: Thu, 10 Sep 2026 09:51:14 -0400 Subject: [PATCH 10/10] build(release): hand versioning and publishing to release-plz `uds_protocol` and `automotive_wire_codec` both release with release-plz, off a `release-plz.toml` that is byte-identical between them and jobs that live in the org-wide reusable workflow. This repo was the odd one out with cargo-release plus a hand-rolled tag-driven `release.yml`, so adopt the same config verbatim and delete both. What that changes in practice: no version is bumped by hand and no tag is pushed by hand. A push to `main` maintains an open release PR; merging it publishes, tags, and cuts the GitHub release. The `Release & Publish` job in the reusable workflow turns itself off when `use-release-plz` is on, so the two paths cannot both fire. One thing is genuinely lost. The deleted `release.yml` published through crates.io trusted publishing, which stores no registry credential at all; the reusable workflow's release-plz job takes a `CARGO_REGISTRY_TOKEN` secret. That is the org's shape today and matching it is the point of this change, but it is a step back on that specific axis and worth revisiting in `rust_workflow` rather than here -- one `id-token: write` and a `crates-io-auth-action` step would give every repo on the shared workflow trusted publishing at once. `CONTRIBUTING.md` gains the release section this makes necessary: the PR title is what lands under squash-merge, so the title is what the version is computed from, and a `fix:` needs an explicit `!` to become the minor bump that a pre-1.0 breaking change calls for. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/release.yml | 75 ----------------------------------- CHANGELOG.md | 9 +++-- CONTRIBUTING.md | 32 +++++++++++++++ Cargo.toml | 2 +- release-plz.toml | 35 ++++++++++++++++ release.toml | 15 ------- 6 files changed, 74 insertions(+), 94 deletions(-) delete mode 100644 .github/workflows/release.yml create mode 100644 release-plz.toml delete mode 100644 release.toml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 8e67fad..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,75 +0,0 @@ -# Tag-driven publish to crates.io. Push `v{version}` on main and this runs; -# nothing here fires on a normal push or PR merge. -# -# Publishing is GATED and inert until someone deliberately turns it on -- -# see the `if:` on the publish job. -name: Release - -on: - push: - tags: ["v*"] - -env: - CARGO_TERM_COLOR: always - -permissions: - contents: read - -jobs: - publish: - name: Publish to crates.io - runs-on: ubuntu-latest - # Two independent gates: - # - # 1. Repository -- a fork that pushes a `v*` tag must never publish under - # this crate's name. - # 2. `SIMPLE_DOIP_PUBLISH_ENABLED` -- the deliberate switch. Set the repo - # variable to `true` once the crate exists on crates.io and a trusted - # publisher is configured for it. Until then this job is skipped, so - # merging this workflow publishes nothing. - if: >- - github.repository == 'luminartech/simple_doip' - && vars.SIMPLE_DOIP_PUBLISH_ENABLED == 'true' - permissions: - # Mint the OIDC token that crates.io trades for a short-lived publish - # token. This is the whole reason no registry secret appears below. - id-token: write - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - # A crates.io publish cannot be undone -- only yanked -- so confirm the - # tag names the version the manifest actually carries before spending it. - - name: Verify the tag matches the manifest version - run: | - manifest=$(cargo metadata --no-deps --format-version 1 \ - | jq -r '.packages[0].version') - tag="${GITHUB_REF_NAME#v}" - if [ "$manifest" != "$tag" ]; then - echo "::error::tag $GITHUB_REF_NAME wants $tag, Cargo.toml says $manifest" - exit 1 - fi - echo "publishing simple_doip $manifest" - - # Trusted publishing: crates.io trades this workflow's GitHub OIDC - # identity for a token scoped to this crate, which is revoked when the - # job ends. Nothing long-lived is stored in this repository. - # - # It requires a trusted publisher configured on the crate naming this - # repository and this workflow file (`release.yml`). That configuration - # can only be added to a crate that already exists, so the first version - # is published by hand -- which is the intent anyway, since the first - # publish claims the name permanently. - - name: Authenticate with crates.io - id: auth - uses: rust-lang/crates-io-auth-action@v1 - - - run: cargo install cargo-release --version '^0.25' --locked - - # `--allow-branch '*'`: a tag-push run is in detached-HEAD state, which - # cargo-release's `allow-branch` check would otherwise reject. The gates - # above already decide whether this job runs at all. - - run: cargo release publish --allow-branch '*' --no-confirm --execute - env: - CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} diff --git a/CHANGELOG.md b/CHANGELOG.md index ef587fb..57fee90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,15 +18,18 @@ changed rather than what was announced at the time. - `LICENSE-MIT` and `LICENSE-APACHE`. The manifest had declared `MIT OR Apache-2.0` without carrying either text. - `CHANGELOG.md`, `CONTRIBUTING.md`, `SECURITY.md`. -- `release.toml` and a tag-driven release workflow. +- `release-plz.toml`. Versioning, the changelog, tags, GitHub releases + and the crates.io publish are handled by release-plz through the + org-wide reusable workflow, matching `uds_protocol` and + `automotive_wire_codec`. There is no release workflow in this repo. ### Changed - docs.rs now builds with all features, so the `client`, `server` and `codec` API appears in the published documentation. `default = []`, so the default build documents only the `no_std` core. -- `release.toml` and `rust-toolchain.toml` are no longer packaged into the - published crate. +- `release-plz.toml` and `rust-toolchain.toml` are no longer packaged into + the published crate. ## [0.5.2] — 2026-09-03 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f5751f4..bf2e4bb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,6 +47,38 @@ Commit subjects follow [Conventional Commits](https://www.conventionalcommits.or change), because the changelog is organized around them. Say what changed and why in the body. +**The pull request title is the one that counts.** The repository merges by +squash with `squash_merge_commit_title: PR_TITLE`, so the PR title — not the +subjects of the commits on the branch — becomes the commit subject on `main`, +and that is what the changelog and the next version number are computed from. +A branch whose commits are immaculate still lands as whatever the PR title +says. CI lints the title for you. + +## Releases + +[release-plz](https://release-plz.dev) owns versioning, the changelog, tags, +GitHub releases, and the crates.io publish. There is no release workflow in +this repository and no version to bump by hand: a push to `main` maintains an +open release PR, and merging that PR publishes. `release-plz.toml` holds the +configuration, shared byte-for-byte with `uds_protocol` and +`automotive_wire_codec`. + +The version comes out of the squashed subjects since the last release: + +| Subject | Effect while `0.x` | +|---|---| +| `feat:` | minor | +| `fix:`, `perf:`, `refactor:`, `revert:`, `docs:` | patch | +| `chore:`, `ci:`, `build:`, `test:`, `style:` | nothing on their own | +| any of the above with `!`, or a `BREAKING CHANGE:` footer | minor | + +Pre-1.0 a breaking change is a minor bump, so `!` is what lifts a `fix:` out +of a patch. Reach for it whenever a caller has to change something to keep +working — including changes the compiler will not flag. A function that +starts emitting different bytes breaks a caller as surely as a renamed +argument does, and `cargo-semver-checks` inspects the API surface, so it will +not catch that one for you. + ## Licensing Contributions are dual-licensed under diff --git a/Cargo.toml b/Cargo.toml index a0932b0..5d47b84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ exclude = [ ".github/", ".vscode/", # Repo-local tooling, not part of the library a consumer receives. - "release.toml", + "release-plz.toml", "rust-toolchain.toml", ] diff --git a/release-plz.toml b/release-plz.toml new file mode 100644 index 0000000..3cd7368 --- /dev/null +++ b/release-plz.toml @@ -0,0 +1,35 @@ +# release-plz owns versioning, the changelog, tags, GitHub releases, and the +# crates.io publish. The jobs that run it live in the org-wide reusable +# workflow (`use-release-plz: true` in `.github/workflows/main.yml`), so there +# is no release workflow in this repository. +# +# The values below are byte-identical to `uds_protocol` and +# `automotive_wire_codec`. Keep them that way: the point of the shared config +# is that a reader of one protocol repo already knows the others. +# +# How a version gets decided: the repo squash-merges with +# `squash_merge_commit_title: PR_TITLE`, so the PR *title* is the commit +# subject on `main`, and these parsers read those subjects. `fix:` and `feat:` +# move the version; `chore`/`ci`/`build`/`test`/`style` are invisible to it. +# A breaking change needs an explicit `!` (`fix(messages)!: ...`) or a +# `BREAKING CHANGE:` footer -- see CONTRIBUTING.md. +[workspace] +changelog_update = true +git_release_enable = true + +[changelog] +commit_parsers = [ + { message = "^feat", group = "Added" }, + { message = "^fix", group = "Fixed" }, + { message = "^perf", group = "Performance" }, + { message = "^docs?", group = "Documentation" }, + { message = "^refactor", group = "Changed" }, + { message = "^revert", group = "Reverted" }, + { message = "^style", skip = true }, + { message = "^test", skip = true }, + { message = "^chore\\(release\\)", skip = true }, + { message = "^chore", skip = true }, + { message = "^ci", skip = true }, + { message = "^build", skip = true }, + { message = ".*", group = "Other" }, +] diff --git a/release.toml b/release.toml deleted file mode 100644 index 97c6ed2..0000000 --- a/release.toml +++ /dev/null @@ -1,15 +0,0 @@ -# cargo-release config for the standalone simple_doip repo (single crate). -# -# `publish = false` keeps a local `cargo release ` from ever reaching -# crates.io -- a version bump is a local, reviewable commit. CI publishes with -# the `cargo release publish` *subcommand*, which overrides this field, so the -# release workflow still works with it set. -publish = false -registry = "crates-io" -tag-name = "v{{version}}" -tag-message = "simple_doip v{{version}}" -pre-release-commit-message = "chore(release): v{{version}}" -allow-branch = ["main"] -# `--all-features`, not a bare `cargo test`: `default = []` here, so the -# default-feature test run exercises almost none of the crate. -pre-release-hook = ["cargo", "test", "--locked", "--all-features"]