funera 0.3.0

Flexible and security-oriented LLM agent framework for Rust — ReAct loop, tools, skills, middleware, security
Documentation
name: CD

on:
  workflow_dispatch:
    inputs:
      bump:
        description: 'Version bump level'
        required: true
        type: choice
        default: patch
        options:
          - patch
          - minor
          - major
      skip_bump:
        description: 'Skip version bump (publish current version only)'
        required: false
        type: boolean
        default: false

env:
  CARGO_TERM_COLOR: always

concurrency: release

jobs:
  release:
    environment: crates.io
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write

    steps:
      - uses: actions/checkout@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          fetch-depth: 0

      - uses: Swatinem/rust-cache@v2

      - name: Configure git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

      - name: Install cargo-release
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-release

      - name: Bump version
        if: ${{ !inputs.skip_bump }}
        run: cargo release ${{ inputs.bump }} --execute --no-confirm

      - name: Verify formatting
        run: cargo fmt --check --all

      - name: Run clippy
        run: cargo clippy --all-features -- -D warnings

      - name: Test funera_core
        run: cargo test --lib --all-features -p funera-core

      - name: Test funera-orchestrate
        run: cargo test --lib --features deepseek,tool,security,sandbox,middleware,skill,funera-builtin-tools -p funera-orchestrate

      - name: Build docs
        run: cargo doc --no-deps --all-features

      - name: Semver check
        uses: obi1kenobi/cargo-semver-checks-action@v2
        continue-on-error: true

      - name: Cleanup semver-check artifacts
        if: always()
        run: rm -rf semver-checks

      # `main` is protected by the `main-protect` ruleset (pushes must come
      # through a pull request; only repository admins may bypass), so a direct
      # `git push origin main` is declined. Instead: push the release commit to
      # a `release/vX.Y.Z` branch, open a PR, and merge it through the normal
      # protected flow. Tags are not protected and are pushed afterwards.
      - name: Open release PR and merge
        if: ${{ !inputs.skip_bump }}
        run: |
          # `cargo pkgid` yields `funera-core@X.Y.Z`; extract the bare version
          # so tags/branches are named vX.Y.Z (no `vfunera-core@` prefix).
          VERSION="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "funera-core") | .version')"
          BRANCH="release/v${VERSION}"
          git push origin "HEAD:${BRANCH}"
          PR_URL="$(gh pr create --base main --head "${BRANCH}" \
            --title "chore: release v${VERSION}" \
            --body "Automated release prepared by the CD workflow (version bump to v${VERSION}).")"
          gh pr merge --merge --delete-branch "${PR_URL}"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Push tag
        if: ${{ !inputs.skip_bump }}
        run: |
          VERSION="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "funera-core") | .version')"
          git push origin "refs/tags/v${VERSION}"

      - name: Publish funera_core
        run: cargo publish -p funera-core --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Publish funera_builtin_tools
        run: |
          sleep 15
          cargo publish -p funera-builtin-tools --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Publish funera-orchestrate
        run: |
          sleep 15
          cargo publish -p funera-orchestrate --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Publish funera (root crate)
        run: |
          sleep 15
          cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Create GitHub Release
        run: |
          VERSION="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "funera-core") | .version')"
          git fetch --tags
          gh release create "v$VERSION" --generate-notes
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}