openlark 0.15.0

飞书开放平台 Rust SDK - 企业级高覆盖率 API 客户端,极简依赖一条命令
Documentation
name: Release

on:
  push:
    tags:
      - 'v*'

env:
  CARGO_TERM_COLOR: always
  CARGO_TOOLCHAIN: stable
  CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse

jobs:
  # 验证构建和测试
  validate:
    runs-on: ubuntu-latest
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v4

      - uses: dtolnay/rust-toolchain@v1
        with:
          toolchain: ${{ env.CARGO_TOOLCHAIN }}
          components: rustfmt, clippy

      - name: Cache Cargo build files
        uses: Swatinem/rust-cache@v2

      - name: Install Protoc
        uses: arduino/setup-protoc@v3
        with:
          version: 23.4
          repo-token: ${{ secrets.GITHUB_TOKEN }}

      - name: Check format
        run: cargo fmt --all -- --check

      - name: Run clippy
        run: cargo clippy --workspace --all-targets --all-features

      - name: Run tests
        run: cargo test --lib --all-features

      - name: Validate public examples
        run: bash scripts/check-public-examples.sh

      - name: Run cargo doc
        run: cargo doc --no-deps --all-features --lib
        env:
          RUSTDOCFLAGS: "-D warnings"

  # 创建 GitHub Release
  github-release:
    needs: validate
    runs-on: ubuntu-latest
    timeout-minutes: 30
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4

      - name: Extract version from tag
        id: extract_version
        run: |
          VERSION=${GITHUB_REF#refs/tags/v}
          echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
          echo "Extracted version: ${VERSION}"

      - name: Detect prerelease flag
        id: release_kind
        run: |
          VERSION="${{ steps.extract_version.outputs.VERSION }}"
          if [[ "${VERSION}" == *-* ]]; then
            echo "PRERELEASE=true" >> $GITHUB_OUTPUT
            echo "Detected prerelease tag: ${VERSION}"
          else
            echo "PRERELEASE=false" >> $GITHUB_OUTPUT
            echo "Detected stable tag: ${VERSION}"
          fi

      - name: Extract changelog entry
        id: changelog
        run: |
          # 提取当前版本的 changelog 内容
          awk '/^## \['"${{ steps.extract_version.outputs.VERSION }}"'\]/{flag=1; next} /^## \[/{flag=0} flag' CHANGELOG.md > release_notes.md

          # 如果没有找到内容,使用默认说明
          if [ ! -s release_notes.md ]; then
            echo "Release version ${{ steps.extract_version.outputs.VERSION }}" > release_notes.md
            echo "" >> release_notes.md
            echo "See [CHANGELOG.md](CHANGELOG.md) for details." >> release_notes.md
          fi

          echo "Release notes content:"
          cat release_notes.md

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          name: Release v${{ steps.extract_version.outputs.VERSION }}
          body_path: release_notes.md
          draft: false
          prerelease: ${{ steps.release_kind.outputs.PRERELEASE }}
          token: ${{ secrets.GITHUB_TOKEN }}

  # 发布到 crates.io
  # 所有版本都发布到 crates.io(包括 RC、Beta)
  crates-io-release:
    needs: [validate, github-release]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: dtolnay/rust-toolchain@v1
        with:
          toolchain: ${{ env.CARGO_TOOLCHAIN }}

      - name: Cache Cargo build files
        uses: Swatinem/rust-cache@v2

      - name: Install Protoc
        uses: arduino/setup-protoc@v3
        with:
          version: 23.4
          repo-token: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract version from tag
        id: extract_version
        run: |
          VERSION=${GITHUB_REF#refs/tags/v}
          echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT

      - name: Verify Cargo.toml version matches tag
        run: |
          set -euo pipefail

          # workspace 使用 `version.workspace = true`,这里用 `cargo metadata` 获取实际发布包(openlark)版本
          CARGO_VERSION=$(
            cargo metadata --no-deps --format-version 1 \
              | python3 -c 'import json,sys; d=json.load(sys.stdin); print(next((p["version"] for p in d.get("packages", []) if p.get("name")=="openlark"), ""))'
          )
          TAG_VERSION="${{ steps.extract_version.outputs.VERSION }}"

          if [ -z "${CARGO_VERSION}" ]; then
            echo "❌ Failed to detect openlark version from cargo metadata"
            exit 1
          fi

          echo "Cargo (openlark) version: ${CARGO_VERSION}"
          echo "Tag version: ${TAG_VERSION}"

          if [ "${CARGO_VERSION}" != "${TAG_VERSION}" ]; then
            echo "❌ Version mismatch: openlark is ${CARGO_VERSION}, but tag is v${TAG_VERSION}"
            exit 1
          fi
          echo "✅ Version verification passed"

      - name: Verify crates.io token
        run: |
          if [ -z "${{ secrets.CRATES_IO_TOKEN }}" ]; then
            echo "❌ CRATES_IO_TOKEN not configured in repository secrets"
            echo "Please add CRATES_IO_TOKEN to repository secrets"
            exit 1
          fi
          echo "✅ CRATES_IO_TOKEN is configured"

      - name: Publish workspace crates in order
        run: |
          # Layer 1: Protocol
          echo "📦 Publishing openlark-protocol..."
          cargo publish -p openlark-protocol || echo "⚠️ openlark-protocol may already exist"
          sleep 30
          
          # Layer 2: Core
          echo "📦 Publishing openlark-core..."
          cargo publish -p openlark-core || echo "⚠️ openlark-core may already exist"
          sleep 30
          
          # Layer 3: Business crates (with longer sleep to avoid rate limits)
          for crate in openlark-auth openlark-security openlark-communication openlark-cardkit openlark-webhook openlark-docs openlark-hr openlark-ai openlark-application openlark-platform openlark-meeting openlark-helpdesk openlark-mail openlark-workflow openlark-analytics openlark-user; do
            echo "📦 Publishing ${crate}..."
            cargo publish -p "${crate}" || echo "⚠️ ${crate} may already exist or failed"
            echo "⏳ Waiting 60s to avoid rate limits..."
            sleep 60
          done
          
          # Layer 4: Client
          echo "📦 Publishing openlark-client..."
          cargo publish -p openlark-client || echo "⚠️ openlark-client may already exist"
          sleep 30
          
          # Layer 5: Root
          echo "📦 Publishing openlark..."
          cargo publish -p openlark || echo "⚠️ openlark may already exist"
          
          echo "✅ Publish process completed"
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}