cordy 0.1.7

A cross-platform TUI coding agent in Rust — hot-swap any model/provider mid-conversation, MCP, skills, sub-agents, background jobs, and an OpenCode-inspired interface.
name: Release

on:
  push:
    tags: ["v*"]

permissions:
  contents: write   # create the release
  id-token: write   # cosign keyless signing (OIDC)

env:
  CARGO_TERM_COLOR: always

jobs:
  build:
    name: build ${{ matrix.target }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - { os: ubuntu-latest,  target: x86_64-unknown-linux-gnu }
          - { os: macos-latest,   target: aarch64-apple-darwin }  # Apple Silicon (native)
          - { os: macos-latest,   target: x86_64-apple-darwin }   # Intel Mac (cross-compiled)
          - { os: windows-latest, target: x86_64-pc-windows-msvc }
    steps:
      - uses: actions/checkout@v4

      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}
      - uses: Swatinem/rust-cache@v2
        with:
          key: ${{ matrix.target }}

      - name: Build
        run: cargo build --release --target ${{ matrix.target }}

      - name: Package
        shell: bash
        run: |
          set -euo pipefail
          tag="${GITHUB_REF_NAME}"
          name="cordy-${tag}-${{ matrix.target }}"
          staging="$name"
          mkdir "$staging"
          cp README.md README.ru.md LICENSE CHANGELOG.md "$staging"/
          if [ "${RUNNER_OS}" = "Windows" ]; then
            cp "target/${{ matrix.target }}/release/cordy.exe" "$staging"/
            7z a "$name.zip" "$staging" >/dev/null
            echo "ASSET=$name.zip" >> "$GITHUB_ENV"
          else
            cp "target/${{ matrix.target }}/release/cordy" "$staging"/
            tar czf "$name.tar.gz" "$staging"
            echo "ASSET=$name.tar.gz" >> "$GITHUB_ENV"
          fi

      - uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.target }}
          path: ${{ env.ASSET }}
          if-no-files-found: error

  release:
    name: publish
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0   # full history for the changelog

      - name: Collect artifacts
        uses: actions/download-artifact@v4
        with:
          path: artifacts
          merge-multiple: true

      - name: Checksums
        run: |
          set -euo pipefail
          cd artifacts
          sha256sum cordy-* > SHA256SUMS
          cat SHA256SUMS

      - name: Sign checksums (cosign, keyless)
        uses: sigstore/cosign-installer@v3
      - run: |
          cd artifacts
          cosign sign-blob --yes SHA256SUMS \
            --output-signature SHA256SUMS.sig \
            --output-certificate SHA256SUMS.pem

      - name: Generate release notes
        run: |
          set -euo pipefail
          ver="${GITHUB_REF_NAME#v}"
          # Pull this version's hand-written section out of CHANGELOG.md (everything between its
          # `## [x.y.z]` heading and the next `## [` heading or the link-reference block). Literal
          # `index` matching avoids awk treating the `[` brackets as a regex character class.
          awk -v ver="$ver" '
            index($0, "## [" ver "]") == 1 { flag=1; next }
            flag && index($0, "## [") == 1 { flag=0 }
            flag && substr($0, 1, 1) == "[" { flag=0 }
            flag { print }
          ' CHANGELOG.md > RELEASE_NOTES.md
          if [ ! -s RELEASE_NOTES.md ]; then
            printf 'Release %s\n' "$ver" > RELEASE_NOTES.md
          fi
          echo "----- release notes -----"
          cat RELEASE_NOTES.md

      - name: Publish release
        uses: softprops/action-gh-release@v2
        with:
          body_path: RELEASE_NOTES.md
          files: |
            artifacts/cordy-*
            artifacts/SHA256SUMS
            artifacts/SHA256SUMS.sig
            artifacts/SHA256SUMS.pem
          fail_on_unmatched_files: true

  crates:
    name: publish to crates.io
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: Check the tag matches Cargo.toml
        run: |
          tag="${GITHUB_REF_NAME#v}"
          ver="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')"
          echo "tag=$tag  crate=$ver"
          [ "$tag" = "$ver" ] || { echo "::error::tag v$tag does not match Cargo.toml version $ver"; exit 1; }
      - name: cargo publish
        # --allow-dirty: the verify build can regenerate Cargo.lock on the runner; that lone change
        # must not abort the publish (the tag↔Cargo.toml version match is already asserted above).
        run: cargo publish --allow-dirty
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}