diff --git a/.github/workflows/publish_crates.yml b/.github/workflows/publish_crates.yml new file mode 100644 index 000000000..a8f0afff1 --- /dev/null +++ b/.github/workflows/publish_crates.yml @@ -0,0 +1,202 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: Publish crates + +on: + workflow_dispatch: + inputs: + tag: + description: "Tag to publish (manual run)" + required: true + type: string + dry_run: + description: "Build and verify crates without publishing to crates.io" + required: true + type: boolean + default: true + +permissions: + contents: read + +concurrency: + group: publish-crates-${{ inputs.tag }} + cancel-in-progress: false + +env: + CARGO_TERM_COLOR: always + RELEASE_TAG: ${{ inputs.tag }} + +jobs: + build_crates: + name: ${{ matrix.os }} (${{ matrix.arch }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + # Keep the OS/architecture matrix in sync with publish_wheel.yml. + include: + - {os: ubuntu-latest, arch: x86_64} + - {os: ubuntu-24.04-arm, arch: aarch64} + - {os: windows-latest, arch: AMD64} + - {os: macos-14, arch: arm64} + + steps: + - name: Check out release source + uses: actions/checkout@v5 + with: + ref: refs/tags/${{ inputs.tag }} + submodules: recursive + fetch-depth: 0 + fetch-tags: true + + - name: Set up uv + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0 + with: + python-version: "3.13" + activate-environment: true + + # Workspace publishing and dry-runs require Cargo 1.90 or newer. + - name: Set up Rust + run: rustup toolchain install stable --profile minimal + + - uses: ./.github/actions/detect-env-vars + id: env_vars + + # tvm-ffi-sys locates libtvm_ffi through tvm-ffi-config on PATH. + - name: Build and install TVM FFI + env: + CMAKE_BUILD_PARALLEL_LEVEL: ${{ steps.env_vars.outputs.cpu_count }} + run: uv pip install --verbose . + + # Apply the release version after the Python build so setuptools-scm sees a clean tree. + - name: Set crate versions from Git + shell: bash + run: &set_crate_versions | + # The crate version is the tag without its "v": 0.1.15, 0.1.15-rc0 or 0.1.14-post1. + if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-(rc|post)[0-9]+)?$ ]]; then + echo "::error::unsupported release tag '$RELEASE_TAG': expected vX.Y.Z, vX.Y.Z-rcN or vX.Y.Z-postN" + exit 1 + fi + export CRATE_VERSION="${RELEASE_TAG#v}" + echo "CRATE_VERSION=$CRATE_VERSION" >> "$GITHUB_ENV" + uv run --no-project --with tomlkit python - <<'PY' + import os + from pathlib import Path + + import tomlkit + + version = os.environ["CRATE_VERSION"] + crates = ("tvm-ffi", "tvm-ffi-sys", "tvm-ffi-macros") + for crate in crates: + path = Path("rust") / crate / "Cargo.toml" + manifest = tomlkit.parse(path.read_text(encoding="utf-8")) + manifest["package"]["version"] = version + for section in ("dependencies", "dev-dependencies", "build-dependencies"): + for name, dependency in manifest.get(section, {}).items(): + if name in crates: + # Exact pin: the three crates ship together. + dependency["version"] = f"={version}" + path.write_text(tomlkit.dumps(manifest), encoding="utf-8") + print(f"{crate}: {version}") + PY + # Cargo validates the derived SemVer and the updated dependency requirements. + cargo +stable metadata --manifest-path rust/Cargo.toml --no-deps --format-version 1 > /dev/null + + - name: Test crates + working-directory: rust + run: cargo +stable test --release --workspace + + # Packages and verifies the crates; only the release-version edits above are uncommitted. + - name: Dry-run crate publication + working-directory: rust + run: cargo +stable publish --dry-run --workspace --registry crates-io --allow-dirty + + # The dry run keeps its .crate files in a temporary directory; write them out again, + # without re-verifying, for the artifact upload. + - name: Package crates + working-directory: rust + run: cargo +stable package --workspace --registry crates-io --allow-dirty --no-verify + + - name: Upload crate artifacts + uses: actions/upload-artifact@v4 + with: + name: crates-${{ matrix.os }}-${{ matrix.arch }} + path: rust/target/package/*.crate + if-no-files-found: error + + upload_crates: + needs: [build_crates] + runs-on: ubuntu-latest + environment: crates-io + if: github.event_name == 'workflow_dispatch' && !inputs.dry_run + steps: + - name: Check out release source + uses: actions/checkout@v5 + with: + ref: refs/tags/${{ inputs.tag }} + submodules: recursive + fetch-depth: 0 + fetch-tags: true + + - name: Set up uv + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0 + with: + python-version: "3.13" + activate-environment: true + + - name: Set up Rust + run: rustup toolchain install stable --profile minimal + + - uses: ./.github/actions/detect-env-vars + id: env_vars + + - name: Build and install TVM FFI + env: + CMAKE_BUILD_PARALLEL_LEVEL: ${{ steps.env_vars.outputs.cpu_count }} + run: uv pip install --verbose . + + - name: Set crate versions from Git + shell: bash + run: *set_crate_versions + + # Verify all three crates before anything is uploaded; the publishes below skip re-verifying. + - name: Package and verify crates + working-directory: rust + run: cargo +stable package --workspace --registry crates-io --allow-dirty + + # Publish in dependency order, skipping versions already on crates.io so a partial run can be + # repeated. Needs a crates.io token with publish access to all three crates. + - name: Publish crates to crates.io + working-directory: rust + shell: bash + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: | + for crate in tvm-ffi-macros tvm-ffi-sys tvm-ffi; do + if lookup=$(cargo +stable info --registry crates-io "${crate}@${CRATE_VERSION}" 2>&1); then + echo "${crate}@${CRATE_VERSION} is already on crates.io; skipping." + continue + elif ! grep -q "could not find" <<< "${lookup}"; then + # Only a definite "not found" may fall through to publishing. + echo "${lookup}" + echo "::error::Could not determine whether ${crate}@${CRATE_VERSION} is on crates.io" + exit 1 + fi + echo "Publishing ${crate}@${CRATE_VERSION}" + cargo +stable publish --package "${crate}" --registry crates-io --allow-dirty --no-verify + done diff --git a/pyproject.toml b/pyproject.toml index 68c9480af..105953b22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,8 @@ dev = [ ] docs = [ "autodocsumm", - "breathe", + # 5.0.0 breaks C++ template specializations and constexpr constructors. + "breathe!=5.0.0", "exhale", "linkify-it-py", "matplotlib", diff --git a/rust/tvm-ffi-macros/Cargo.toml b/rust/tvm-ffi-macros/Cargo.toml index f8d29d406..6c646904d 100644 --- a/rust/tvm-ffi-macros/Cargo.toml +++ b/rust/tvm-ffi-macros/Cargo.toml @@ -19,6 +19,7 @@ name = "tvm-ffi-macros" description = "Procedural macro crate for tvm-ffi" +# Development placeholder; publish_crates.yml sets the release version from the Git tag. version = "0.1.0-alpha.0" edition = "2021" license = "Apache-2.0" diff --git a/rust/tvm-ffi-sys/Cargo.toml b/rust/tvm-ffi-sys/Cargo.toml index 5ae2d9542..04ad17441 100644 --- a/rust/tvm-ffi-sys/Cargo.toml +++ b/rust/tvm-ffi-sys/Cargo.toml @@ -19,6 +19,7 @@ name = "tvm-ffi-sys" description = "Low-level sys crate for tvm-ffi" +# Development placeholder; publish_crates.yml sets the release version from the Git tag. version = "0.1.0-alpha.0" edition = "2021" license = "Apache-2.0" diff --git a/rust/tvm-ffi/Cargo.toml b/rust/tvm-ffi/Cargo.toml index 3df4eaf98..1fbd1c8a4 100644 --- a/rust/tvm-ffi/Cargo.toml +++ b/rust/tvm-ffi/Cargo.toml @@ -19,6 +19,7 @@ name = "tvm-ffi" description = "tvm-ffi rust support" +# Development placeholder; publish_crates.yml sets the release version from the Git tag. version = "0.1.0-alpha.0" edition = "2021" license = "Apache-2.0" @@ -30,6 +31,7 @@ crate-type = ["lib"] [dependencies] paste = "1.0" +# Release CI also updates the internal dependency versions, including dev-dependencies. tvm-ffi-sys = { version = "0.1.0-alpha.0", path = "../tvm-ffi-sys" } tvm-ffi-macros = { version = "0.1.0-alpha.0", path = "../tvm-ffi-macros" }