@ondeinference / ed / commits / d335f0e

Publish Ed's crates with cargo-workspaces

`cargo publish --dry-run --locked -p ed-agent-tauri` cannot pass, ever. Packaging rewrites the path dependency on ed-agent into a registry requirement, and `^1.0.0` has nothing to resolve against until ed-agent is really on crates.io, which only happens later, in the publish job. So preflight failed the v1.0.0 release on a check no release can satisfy. A [patch.crates-io] entry does not rescue it either, because packaging ignores [patch] on purpose: a downstream consumer would not have one. cargo-workspaces already solves this. It publishes in dependency order and waits between crates, and its dry run warns about an unpublished sibling rather than exiting non-zero, so preflight still packages and verifies ed-agent for real. The publish job loses the hand-rolled half of the same job: two "is it published yet" probes, two token mints, two waits on the index, 55 lines of it, for `plan --skip-published` and one publish call. That plan step also makes a re-run resume instead of starting over, since it lists only the crates still missing from crates.io. scripts/crates-io-published.py has no callers left. Leaving it in place, it is still worth having by hand. Co-Authored-By: siGit Code <noreply@sigit.si>

Seto Elkahfi committed Sep 11, 2026 at 08:18 UTC d335f0e3df7f0674b084dd13686595c57bcb09bc
1 file changed +38 -53
.github/workflows/release-sdk-rust.yml
+38 -53
@@ -57,8 +57,18 @@ jobs:
57 - run: cargo fmt --all -- --check
58 - run: cargo test --locked --workspace
59 - run: cargo clippy --locked --workspace --all-targets -- -D warnings
60 - - run: cargo publish --dry-run --locked -p ed-agent
61 - - run: cargo publish --dry-run --locked -p ed-agent-tauri
60 + # Publishing Ed means publishing two crates in dependency order, and plain
61 + # `cargo publish --dry-run` cannot cover the second one: packaging
62 + # rewrites ed-agent-tauri's path dependency on ed-agent into a registry
63 + # requirement, and `^1.0.0` cannot resolve until ed-agent is actually on
64 + # crates.io. A [patch] does not help, since packaging ignores it by
65 + # design. cargo-workspaces knows the order and its dry run warns about
66 + # the unpublished sibling rather than failing on it, so ed-agent still
67 + # gets a real packaging and verification pass here.
68 + - name: Install cargo-workspaces
69 + run: cargo install cargo-workspaces --version 0.4.2 --locked
70 + - name: Dry-run the workspace publish
71 + run: cargo workspaces publish --publish-as-is --dry-run --locked --allow-branch "*" --yes
72
73 publish:
74 needs: preflight
@@ -83,58 +93,33 @@ jobs:
93 - uses: dtolnay/rust-toolchain@stable
94 with:
95 toolchain: ${{ env.RUST_TOOLCHAIN }}
86 - - id: version
96 + - name: Install cargo-workspaces
97 + run: cargo install cargo-workspaces --version 0.4.2 --locked
98 + # `plan --skip-published` is what makes a re-run safe: it lists only the
99 + # crates still missing from crates.io, so a run that dies after ed-agent
100 + # went up publishes just ed-agent-tauri the second time.
101 + - name: Plan the publish
102 + id: plan
103 run: |
88 - echo "value=$(cargo metadata --no-deps --format-version 1 | python3 -c 'import json,sys; print(next(p["version"] for p in json.load(sys.stdin)["packages"] if p["name"] == "ed-agent"))')" >> "$GITHUB_OUTPUT"
89 - - name: Check whether ed-agent is already published
90 - id: core-published
91 - env:
92 - VERSION: ${{ steps.version.outputs.value }}
93 - run: |
94 - if python3 scripts/crates-io-published.py "$VERSION" --crate ed-agent; then
95 - echo "skip=true" >> "$GITHUB_OUTPUT"
96 - else
97 - status=$?
98 - test "$status" = 1 || exit "$status"
99 - echo "skip=false" >> "$GITHUB_OUTPUT"
100 - fi
101 - - name: Authenticate to publish ed-agent
102 - if: steps.core-published.outputs.skip != 'true'
103 - id: core-auth
104 + cargo workspaces plan --skip-published --json > plan.json
105 + cat plan.json
106 + echo "count=$(python3 -c 'import json; print(len(json.load(open("plan.json"))))')" >> "$GITHUB_OUTPUT"
107 + - name: Authenticate to crates.io
108 + if: steps.plan.outputs.count != '0'
109 + id: auth
110 uses: rust-lang/crates-io-auth-action@v1
105 - - name: Publish ed-agent
106 - if: steps.core-published.outputs.skip != 'true'
107 - env:
108 - CARGO_REGISTRY_TOKEN: ${{ steps.core-auth.outputs.token }}
109 - run: cargo publish --locked -p ed-agent
110 - - name: Verify ed-agent reached crates.io
111 - if: steps.core-published.outputs.skip != 'true'
112 - env:
113 - VERSION: ${{ steps.version.outputs.value }}
114 - run: python3 scripts/crates-io-published.py "$VERSION" --crate ed-agent --wait
115 - - name: Check whether ed-agent-tauri is already published
116 - id: tauri-published
111 + - name: Publish workspace crates
112 + if: steps.plan.outputs.count != '0'
113 env:
118 - VERSION: ${{ steps.version.outputs.value }}
114 + TOKEN: ${{ steps.auth.outputs.token }}
115 run: |
120 - if python3 scripts/crates-io-published.py "$VERSION" --crate ed-agent-tauri; then
121 - echo "skip=true" >> "$GITHUB_OUTPUT"
122 - else
123 - status=$?
124 - test "$status" = 1 || exit "$status"
125 - echo "skip=false" >> "$GITHUB_OUTPUT"
126 - fi
127 - - name: Authenticate to publish ed-agent-tauri
128 - if: steps.tauri-published.outputs.skip != 'true'
129 - id: tauri-auth
130 - uses: rust-lang/crates-io-auth-action@v1
131 - - name: Publish ed-agent-tauri
132 - if: steps.tauri-published.outputs.skip != 'true'
133 - env:
134 - CARGO_REGISTRY_TOKEN: ${{ steps.tauri-auth.outputs.token }}
135 - run: cargo publish --locked -p ed-agent-tauri
136 - - name: Verify ed-agent-tauri reached crates.io
137 - if: steps.tauri-published.outputs.skip != 'true'
138 - env:
139 - VERSION: ${{ steps.version.outputs.value }}
140 - run: python3 scripts/crates-io-published.py "$VERSION" --crate ed-agent-tauri --wait
116 + cargo workspaces publish \
117 + --publish-as-is \
118 + --yes \
119 + --locked \
120 + --allow-branch "*" \
121 + --publish-interval 15 \
122 + --token "$TOKEN"
123 + - name: Nothing to publish
124 + if: steps.plan.outputs.count == '0'
125 + run: echo "Every publishable crate at this version is already on crates.io."