rlm-cli 1.3.1

Recursive Language Model (RLM) REPL for Claude Code - handles long-context tasks via chunking and recursive sub-LLM calls
Documentation
---
name: Publish to crates.io

"on":
  push:
    tags:
      - "v*.*.*"
  workflow_dispatch:

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always

jobs:
  publish:
    name: Publish to crates.io
    runs-on: ubuntu-latest
    environment: copilot
    permissions:
      contents: read
      # OIDC for crates.io Trusted Publishing and .crate attestation signing
      id-token: write
      # Provenance attestation for the published .crate
      attestations: write
    steps:
      - name: Checkout repository
        # yamllint disable-line rule:line-length
        uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10  # v6.0.3

      - name: Install Rust toolchain
        # yamllint disable-line rule:line-length
        uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9  # master
        with:
          toolchain: stable

      - name: Install cargo-deny
        # yamllint disable-line rule:line-length
        uses: taiki-e/install-action@7a79fe8c3a13344501c80d99cae481c1c9085912  # v2.67.18
        with:
          tool: cargo-deny

      - name: Verify package
        run: |
          cargo package --list
          cargo package --allow-dirty

      - name: Run pre-publish checks
        run: |
          cargo fmt -- --check
          cargo clippy --all-targets --all-features -- -D warnings
          cargo test --all-features
          cargo doc --no-deps --all-features
          cargo deny check

      - name: Dry run publish
        run: cargo publish --dry-run --allow-dirty

      # Trusted Publishing (OIDC): no long-lived registry token. Requires
      # the one-time crates.io setup — crate Settings -> Trusted Publishing
      # -> add GitHub repo zircote/rlm-rs, workflow publish.yml,
      # environment copilot.
      - name: Authenticate with crates.io
        id: auth
        if: startsWith(github.ref, 'refs/tags/v')
        # yamllint disable-line rule:line-length
        uses: rust-lang/crates-io-auth-action@bbd81622f20ce9e2dd9622e3218b975523e45bbe  # v1.0.4

      - name: Publish to crates.io
        if: startsWith(github.ref, 'refs/tags/v')
        run: cargo publish --token "$CARGO_REGISTRY_TOKEN"
        env:
          CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}

      # Attest the bytes the registry actually serves, not a local rebuild:
      # download the published .crate, assert it matches what cargo packaged
      # locally (cargo packaging is deterministic for a given commit — a
      # mismatch means something is wrong and must fail loudly), then attach
      # SLSA provenance to it.
      - name: Download published crate from registry
        id: crate
        if: startsWith(github.ref, 'refs/tags/v')
        run: |
          VERSION="${GITHUB_REF#refs/tags/v}"
          CRATE="rlm-cli-${VERSION}.crate"
          URL="https://static.crates.io/crates/rlm-cli/${CRATE}"
          downloaded=0
          for attempt in 1 2 3 4 5 6; do
            if curl -fsSL -o "${CRATE}" "$URL"; then
              downloaded=1
              break
            fi
            echo "crate not yet on CDN (attempt ${attempt}); retrying"
            sleep 10
          done
          if [ "$downloaded" -ne 1 ] || [ ! -s "${CRATE}" ]; then
            echo "::error::failed to download ${URL} after 6 attempts"
            exit 1
          fi
          LOCAL="target/package/${CRATE}"
          if [ ! -f "$LOCAL" ]; then
            echo "::error::locally packaged crate missing: ${LOCAL}"
            exit 1
          fi
          sha_remote=$(sha256sum "${CRATE}" | cut -d' ' -f1)
          sha_local=$(sha256sum "$LOCAL" | cut -d' ' -f1)
          if [ "$sha_remote" != "$sha_local" ]; then
            echo "::error::registry crate bytes differ from local package"
            exit 1
          fi
          echo "crate-path=${CRATE}" >> "$GITHUB_OUTPUT"

      - name: Attest crate provenance
        if: startsWith(github.ref, 'refs/tags/v')
        # yamllint disable-line rule:line-length
        uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32  # v4.1.0
        with:
          subject-path: ${{ steps.crate.outputs.crate-path }}