diff --git a/.github/scripts/cargo-publish-idempotent.sh b/.github/scripts/cargo-publish-idempotent.sh new file mode 100755 index 00000000..5521b014 --- /dev/null +++ b/.github/scripts/cargo-publish-idempotent.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: © 2026 Phala Network +# +# SPDX-License-Identifier: Apache-2.0 +# +# Wraps `cargo publish -p $1` so that "already exists on crates.io" is treated +# as success. Lets a partially-failed release be retried by pushing the same +# tag, without getting stuck on the first crate. + +set -euo pipefail + +crate=${1:?missing crate name} + +if output=$(cargo publish -p "$crate" 2>&1); then + echo "$output" + exit 0 +fi + +echo "$output" + +if grep -q "already exists on crates.io index" <<<"$output"; then + echo "::notice::$crate is already published at this version; treating as success" + exit 0 +fi + +exit 1 diff --git a/.github/workflows/rust-sdk-release.yml b/.github/workflows/rust-sdk-release.yml index 586e3838..4f6a0a97 100644 --- a/.github/workflows/rust-sdk-release.yml +++ b/.github/workflows/rust-sdk-release.yml @@ -2,55 +2,98 @@ # # SPDX-License-Identifier: Apache-2.0 -name: Release-plz +name: Publish SDK to crates.io on: push: - branches: - - master + tags: ['dstack-sdk-v*'] jobs: - release-plz-pr: - name: Release-plz PR + publish: runs-on: ubuntu-latest - if: ${{ github.repository_owner == 'Dstack-TEE' }} + environment: sdk-release permissions: + id-token: write contents: write - pull-requests: write - concurrency: - group: release-plz-${{ github.ref }} - cancel-in-progress: false steps: - uses: actions/checkout@v5 - with: - fetch-depth: 0 - persist-credentials: false - - uses: dtolnay/rust-toolchain@stable - - name: Run release-plz - uses: release-plz/action@v0.5 - with: - command: release-pr + + - name: Extract version from tag + id: ver + run: | + tag="${GITHUB_REF_NAME}" + version="${tag#dstack-sdk-v}" + if [[ -z "$version" || "$version" == "$tag" ]]; then + echo "::error::tag '$tag' does not start with 'dstack-sdk-v'" + exit 1 + fi + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "Publishing version: $version" + + - name: Verify Cargo.toml versions match tag env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ steps.ver.outputs.version }} + run: | + python3 <<'PY' + import os, sys, tomllib - release-plz-release: - name: Release-plz release - runs-on: ubuntu-latest - if: ${{ github.repository_owner == 'Dstack-TEE' }} - environment: sdk-release - permissions: - contents: write - pull-requests: read - id-token: write - steps: - - uses: actions/checkout@v5 - with: - fetch-depth: 0 - persist-credentials: false - - uses: dtolnay/rust-toolchain@stable - - name: Run release-plz - uses: release-plz/action@v0.5 - with: - command: release + want = os.environ["VERSION"] + + def pkg_version(path): + with open(path, "rb") as f: + return tomllib.load(f)["package"]["version"] + + def ws_dep_version(path, name): + with open(path, "rb") as f: + dep = tomllib.load(f)["workspace"]["dependencies"][name] + return dep["version"] if isinstance(dep, dict) else dep + + checks = [ + ("sdk/rust/types/Cargo.toml [package.version]", + pkg_version("sdk/rust/types/Cargo.toml")), + ("sdk/rust/Cargo.toml [package.version]", + pkg_version("sdk/rust/Cargo.toml")), + ("Cargo.toml [workspace.dependencies.dstack-sdk-types.version]", + ws_dep_version("Cargo.toml", "dstack-sdk-types")), + ] + + fail = False + for label, got in checks: + ok = got == want + fail = fail or not ok + print(f" {'OK ' if ok else 'BAD'} {label}: {got}") + + if fail: + print(f"\ntag is dstack-sdk-v{want}; bump all three to {want} before tagging.", + file=sys.stderr) + sys.exit(1) + PY + + - uses: rust-lang/crates-io-auth-action@v1 + id: auth + + - name: Publish dstack-sdk-types + env: + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} + VERSION: ${{ steps.ver.outputs.version }} + run: .github/scripts/cargo-publish-idempotent.sh dstack-sdk-types + + - name: Publish dstack-sdk + env: + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} + VERSION: ${{ steps.ver.outputs.version }} + run: .github/scripts/cargo-publish-idempotent.sh dstack-sdk + + - name: Create GitHub Release env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ github.token }} + VERSION: ${{ steps.ver.outputs.version }} + run: | + if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + echo "::notice::GitHub Release $GITHUB_REF_NAME already exists; skipping" + exit 0 + fi + gh release create "$GITHUB_REF_NAME" \ + --title "dstack-sdk $VERSION" \ + --notes "Published to crates.io: [dstack-sdk@$VERSION](https://crates.io/crates/dstack-sdk/$VERSION), [dstack-sdk-types@$VERSION](https://crates.io/crates/dstack-sdk-types/$VERSION)" \ + --verify-tag diff --git a/Cargo.toml b/Cargo.toml index d913838c..7c83d291 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ resolver = "2" # Internal dependencies ra-rpc = { path = "ra-rpc", default-features = false } ra-tls = { path = "ra-tls" } -dstack-sdk-types = { path = "sdk/rust/types", version = "0.1.1", default-features = false } +dstack-sdk-types = { path = "sdk/rust/types", version = "0.1.2", default-features = false } dstack-gateway-rpc = { path = "gateway/rpc" } dstack-kms-rpc = { path = "kms/rpc" } dstack-guest-agent-rpc = { path = "guest-agent/rpc" } diff --git a/release-plz.toml b/release-plz.toml deleted file mode 100644 index 3fc0bd23..00000000 --- a/release-plz.toml +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-FileCopyrightText: © 2026 Phala Network -# -# SPDX-License-Identifier: Apache-2.0 - -# release-plz only manages the public SDK crates. Every other workspace -# member is internal and must never be published to crates.io. - -[workspace] -release = false -pr_branch_prefix = "release-plz/" -pr_labels = ["release"] -semver_check = true -changelog_update = true - -[[package]] -name = "dstack-sdk-types" -release = true - -[[package]] -name = "dstack-sdk" -release = true