You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by sc...@apache.org on 2026/09/11 22:30:48 UTC
(iceberg-rust) branch main updated: ci: check new crates are reserved on crates.io before merge (#3185)
This is an automated email from the ASF dual-hosted git repository.
CTTY pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-rust.git
The following commit(s) were added to refs/heads/main by this push:
new cb912c910 ci: check new crates are reserved on crates.io before merge (#3185)
cb912c910 is described below
commit cb912c91066f4fc9bca8b5cdebc40e79aef38d9f
Author: Kevin Liu <ke...@users.noreply.github.com>
AuthorDate: Fri Sep 11 15:30:44 2026 -0700
ci: check new crates are reserved on crates.io before merge (#3185)
* ci: check new crates are reserved on crates.io before merge
Trusted publishing can't create a crate, and the release workflow publishes
the whole workspace in one `cargo publish --workspace` run, which cargo
aborts if any crate already exists at the release version. So the existing
"publish the new crate by hand after the workflow fails" instructions no
longer work.
Add a lint step that fails if a publishable crate is missing from
crates.io, and rewrite the release guide appendix around reserving the
crate with a placeholder version when it is added.
Co-Authored-By: Claude Fable 5.1 <no...@anthropic.com>
* docs: reserve the crate name with a standalone placeholder crate
Overriding the version inside the workspace fails because iceberg depends
on the new crate at the workspace version. Verified with cargo publish
--dry-run.
Co-Authored-By: Claude Fable 5.1 <no...@anthropic.com>
* ci: match the existing publishable-crate filter; document partial publish recovery
Co-Authored-By: Claude Fable 5.1 <no...@anthropic.com>
* docs: clarify that reserving a crate needs a token with the publish-new scope
Owning the existing crates does not imply the token in ~/.cargo/credentials.toml
has publish-new, and crates.io reports a missing scope as a generic
authentication failure. Say so, and point at where to create the token.
Co-Authored-By: Claude Fable 5.1 <no...@anthropic.com>
* docs: fix stale first-publish note, empty placeholder, and owner invitations
- The release steps still said the publish workflow is expected to fail on
new crates and linked to a heading that no longer exists.
- `cargo new --lib` generates a non-empty lib.rs; use mkdir and touch.
- `cargo owner --add <user>` sends an invitation that must be accepted.
Co-Authored-By: Claude Fable 5.1 <no...@anthropic.com>
* ci: fail the crates.io check if the crate list is empty
A failed `cargo metadata` inside `for pkg in $(...)` never trips
`bash -e`, so the loop ran zero times and the step passed without
checking anything. Capture the list first and fail if it is empty.
Co-Authored-By: Claude Fable 5.1 <no...@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <no...@anthropic.com>
---
.github/workflows/ci.yml | 16 ++++++++
website/src/release.md | 101 +++++++++++++++++++++++++++++++++++------------
2 files changed, 92 insertions(+), 25 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 856026bd1..5bb2cc178 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -78,6 +78,22 @@ jobs:
- name: Check crates package LICENSE and NOTICE
run: make check-license-notice
+ # Trusted publishing cannot create a crate (https://crates.io/docs/trusted-publishing),
+ # so a new publishable crate must be reserved on crates.io before it is merged.
+ # See "Adding a new crate" in website/src/release.md.
+ - name: Check publishable crates exist on crates.io
+ run: |
+ pkgs=$(cargo metadata --no-deps --format-version=1 | jq -r '.packages[] | select(.publish != []) | .name')
+ # A failed `cargo metadata` would otherwise leave the loop empty and the step green.
+ [ -n "$pkgs" ] || { echo "::error::No publishable crates found in the workspace, expected at least one"; exit 1; }
+ for pkg in $pkgs; do
+ # --registry is required, otherwise cargo resolves the local workspace member.
+ if ! cargo info --registry crates-io "$pkg" >/dev/null; then
+ echo "::error::$pkg is not on crates.io. Reserve it before merging; see 'Adding a new crate' in website/src/release.md"
+ exit 1
+ fi
+ done
+
- name: Cargo Machete
run: cargo machete
diff --git a/website/src/release.md b/website/src/release.md
index 5e207bc41..4b97fc6f7 100644
--- a/website/src/release.md
+++ b/website/src/release.md
@@ -531,9 +531,8 @@ The creation of the final release tag triggers the publish workflow for crates.
Python packages are manually triggered later.
Please verify that the triggered workflows for the crates succeeded.
-Note, this workflow for crates is expected to fail if new crates are being published for the first time.
-In this instance, a committer must manually publish the crate in order to continue.
-See [publishing a crate for the first time](#publishing-a-crate-for-the-first-time) for what steps to take.
+New crates are reserved on crates.io when they are added, so the workflow should not fail on a new crate.
+If it does, see [if the Publish workflow fails on a missing crate](#if-the-publish-workflow-fails-on-a-missing-crate).
Once all the crates are published, Python publishing should start.
Python publishing is performed by a GitHub workflow, however the trigger is manual.
@@ -614,45 +613,97 @@ however it is in the community's best interest to publish it soon after.
## Appendix
-### Publishing a crate for the first time
+### Adding a new crate
-Publishing the Iceberg crates is automated using GitHub Actions,
-which authenticates with crates.io using trusted publishing.
+Every publishable crate must exist on crates.io before the pull request that adds it is merged.
+CI enforces this with a check that fails for any publishable crate missing from crates.io.
-Trusted publishing must first be configured for each crate.
-If the crate has never been published, crates.io rejects attempts to publish.
+The release workflow publishes crates with [trusted publishing](https://crates.io/docs/trusted-publishing), which cannot create a crate.
+crates.io requires the first version of a crate to be published manually with an API token ([announcement](https://blog.rust-lang.org/2025/07/11/crates-io-development-update-2025-07/)).
+Publishing the crate by hand after the release workflow fails does not work either: the workflow runs `cargo publish --workspace`, which refuses to run if any crate already exists at the release version.
-When a release workflow fails due to the presence of a new crate, a committer must perform steps enumerated below.
-Once complete, future versions can be published automatically using GitHub Actions.
+A committer therefore reserves the crate when it is added, in three steps: publish a placeholder version, configure owners, and configure trusted publishing.
-#### Initial crate publish
+#### Prerequisites
-Manually publish the crate using the following command.
-You **must** have the source code checked out matching the pushed Git tag for the release.
+Any crates.io account can publish an unclaimed name, and the first publisher becomes the crate's only owner.
+Reserve the crate promptly once the pull request is open, and have a committer do it, since the reserver must hand ownership to the project.
+
+The committer needs:
+
+- A crates.io account linked to their GitHub account.
+- Membership in the `apache/iceberg-private` GitHub team, with the `read:org` permission granted to crates.io, in order to add that team as an owner.
+- A crates.io API token with the `publish-new` and `change-owners` scopes, used with `cargo login`.
+ Existing tokens usually lack `publish-new`; create one at [crates.io/settings/tokens](https://crates.io/settings/tokens).
+
+Committers who already own the existing Iceberg crates meet the first two.
+List them with `cargo owner --list iceberg`.
+
+#### Step 1: Reserve the crate name
+
+Publish an empty placeholder crate as version `0.0.0`, so it never collides with a real release.
+The placeholder is created outside the repository; overriding the version inside the workspace does not work because other workspace crates depend on the new crate at the workspace version.
```shell
-cargo publish --package <package-name>
+mkdir -p /tmp/<package-name>/src
+cd /tmp/<package-name>
+touch src/lib.rs
+cat > Cargo.toml <<'EOF'
+[package]
+name = "<package-name>"
+version = "0.0.0"
+edition = "2024"
+license = "Apache-2.0"
+description = "Placeholder that reserves the crate name for Apache Iceberg Rust. See the repository for the real crate."
+repository = "https://github.com/apache/iceberg-rust"
+EOF
+cargo publish --dry-run
+cargo publish
```
-#### Configure crate permissions
+A `403 Forbidden: authentication failed` error means the token lacks the `publish-new` scope.
-After publishing succeeds, the crate must be configured to allow other committers to publish.
+#### Step 2: Configure crate owners
-Add the GitHub team that Apache Iceberg committers are a member of.
+Add the GitHub team that owns the existing Iceberg crates.
```shell
-cargo owner --add github:<github-team-org>:<github-team-name>
+cargo owner --add github:apache:iceberg-private <package-name>
```
-Additionally, add two PMC members (excluding yourself) as owners of the crate.
-A GitHub team cannot manage permissions for the crate, so it is important that individuals have ownership to continue being able to manage the crate should a PMC member become inactive.
-See the [Cargo documentation for `cargo owner`](https://doc.rust-lang.org/cargo/reference/publishing.html#cargo-owner) for reference.
+Then add two PMC members other than yourself as individual owners.
```shell
-cargo owner --add <github-handle>
+cargo owner --add <github-handle> <package-name>
```
-#### Configure trusted publishing
+This sends an invitation; ask each invitee to accept it on crates.io.
+
+Individual owners are required because a team owner can publish and yank versions but cannot add or remove owners.
+Without them, the crate could become unmanageable if a PMC member becomes inactive.
+See the [Cargo documentation for `cargo owner`](https://doc.rust-lang.org/cargo/reference/publishing.html#cargo-owner).
+
+#### Step 3: Configure trusted publishing
+
+As a crate owner, open the crate's settings on the crates.io website and add a trusted publisher, following the [crates.io trusted publishing documentation](https://crates.io/docs/trusted-publishing).
+Use an existing Iceberg crate as the reference:
-Review the [crates.io trusted publishing documentation](https://crates.io/docs/trusted-publishing) for the latest instructions on how to configure it.
-You should also review another already-existing crate for reference.
+- Repository: `apache/iceberg-rust`
+- Workflow: `publish.yml`
+- Environment: `publish`
+
+Then re-run CI on the pull request.
+The next release publishes the crate's first real version along with the rest of the workspace.
+
+#### If the Publish workflow fails on a missing crate
+
+Reserve the crate as above, using the `0.0.0` placeholder and never the release version.
+
+Then check the failed run's log for `Uploaded` lines to see which crates were already published, since cargo uploads crates one at a time in dependency order.
+
+- If nothing was uploaded, re-run the failed workflow run from the GitHub Actions UI. Re-running keeps the original tag as the workflow ref.
+- If some crates were uploaded, publish the rest from a clean checkout of the release tag, excluding each crate that was already published:
+
+```shell
+cargo publish --workspace --all-features --exclude <published-crate> --exclude <published-crate>
+```