From 7bacce554a7bfa36e0260fab9b213ad62345ef8e Mon Sep 17 00:00:00 2001 From: Kaan Barmore-Genc Date: Tue, 30 Jun 2026 19:48:18 -0500 Subject: [PATCH 1/2] CI: verify crates publish in isolation when a manifest changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workspace test build passes even when a crate is missing a per-crate feature, because Cargo unifies features across the workspace. cargo publish verifies each crate compiled on its own, so it catches those gaps — this is how the 11.4.0 release broke (impl needed syn's "printing" feature). Add a job running cargo publish --workspace --dry-run, gated on Cargo.toml changes via dorny/paths-filter so it only runs when a manifest actually changed. --- .github/workflows/test.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eaef2f5..6d9f804 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,3 +29,29 @@ jobs: files: lcov.info fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} + publish-dry-run: + name: publish dry-run + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Detect manifest changes + uses: dorny/paths-filter@v3 + id: changes + with: + filters: | + manifests: + - '**/Cargo.toml' + - name: Install Rust + if: steps.changes.outputs.manifests == 'true' + run: rustup toolchain install stable + - uses: Swatinem/rust-cache@v2 + if: steps.changes.outputs.manifests == 'true' + - name: Verify crates publish in isolation + # cargo publish compiles each packaged crate on its own, catching + # missing per-crate features that workspace feature unification hides + # during a normal build (e.g. syn's "printing" feature). --workspace + # resolves inter-crate deps from the locally packaged versions, so this + # passes before the bumped versions are on crates.io. Only runs when a + # Cargo.toml changed, since that's the only thing that affects it. + if: steps.changes.outputs.manifests == 'true' + run: cargo publish --workspace --dry-run --all-features From a89f8b46ebf6a76ca8bffeae3f38ff0abd70e334 Mon Sep 17 00:00:00 2001 From: Kaan Barmore-Genc Date: Tue, 30 Jun 2026 19:52:28 -0500 Subject: [PATCH 2/2] Release: publish the whole workspace in one step cargo publish --workspace publishes all members in dependency order, verifying every crate before uploading any, and waits for the index internally. This removes the manual publish-per-crate + sleep 20s hack and prevents partial releases: if one crate fails verification, none are uploaded (the 11.4.0 failure uploaded utils then broke on impl). --- .github/workflows/release.yml | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 108be3c..a95786e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,28 +25,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - # The crates need to be published in order. - - name: Publish utils - run: cargo publish --token ${CRATES_TOKEN} - env: - CRATES_TOKEN: ${{ secrets.CRATES_IO_TOKEN }} - working-directory: utils - - name: wait for utils to finish publishing - # If we try to publish the next crate too quickly, it will fail to - # publish because it's not up on crates.io fully yet. We need to wait a - # bit until the previous crate is done publishing. - run: sleep 20s - - name: Publish impl - run: cargo publish --token ${CRATES_TOKEN} - env: - CRATES_TOKEN: ${{ secrets.CRATES_IO_TOKEN }} - working-directory: impl - - name: wait for impl to finish publishing - # If we try to publish the next crate too quickly, it will fail to - # publish because it's not up on crates.io fully yet. We need to wait a - # bit until the previous crate is done publishing. - run: sleep 20s - - name: Publish main crate - run: cargo publish --token ${CRATES_TOKEN} + - name: Publish all crates + # cargo publish --workspace packages and verifies every crate before + # uploading any of them, in dependency order, waiting for the index + # between crates. If any crate fails verification nothing is uploaded, + # so a broken crate can't leave a partial release behind (which is what + # happened in 11.4.0). Replaces the old publish-one-by-one + sleep hack. + run: cargo publish --workspace --token ${CRATES_TOKEN} env: CRATES_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}