fynd 0.103.0

High-performance DeFi route-finding engine — embeddable library and CLI
name: Release

on:
  release:
    types: [created]

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always

jobs:
  publish-crates:
    name: Publish Crates
    # Pre-releases build a Docker image only; registry publishing happens on
    # full releases.
    if: ${{ !github.event.release.prerelease }}
    runs-on: ubuntu-latest
    timeout-minutes: 30
    env:
      CARGO_REGISTRY_TOKEN: ${{ secrets.CRATESIO_REGISTRY_TOKEN }}
    steps:
      - name: Checkout
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Setup toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Setup Rust Cache
        uses: Swatinem/rust-cache@aa7c1c80a07a27a84c0aa76d0cef0aad3830e330 # v2.7.8
        with:
          cache-on-failure: true

      # Publishes in dependency order, skipping versions already on crates.io so a
      # partially failed release run can be recovered by re-running the job. A single
      # retry (with an index re-check) covers crates.io index propagation lag between
      # consecutive publishes.
      - name: Publish crates
        run: |
          set -euo pipefail
          version="$(cargo metadata --format-version 1 --no-deps \
            | jq -r '.packages[] | select(.name == "fynd") | .version')"
          published() {
            curl -sf "https://crates.io/api/v1/crates/$1/${version}" \
              -H "User-Agent: fynd-ci (github.com/propeller-heads/fynd)" >/dev/null
          }
          for crate in fynd-core fynd-rpc-types fynd-client fynd-rpc fynd; do
            if published "${crate}"; then
              echo "${crate} ${version} already on crates.io, skipping"
              continue
            fi
            if cargo publish --locked --package "${crate}"; then
              continue
            fi
            echo "publish of ${crate} failed, re-checking index and retrying in 30s"
            sleep 30
            if ! published "${crate}"; then
              cargo publish --locked --package "${crate}"
            fi
          done

  publish-npm:
    name: Publish TypeScript packages to npm
    # Pre-releases build a Docker image only; registry publishing happens on
    # full releases (or manual dispatch).
    if: github.event_name == 'workflow_dispatch' || !github.event.release.prerelease
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      contents: read
      id-token: write
    steps:
      - name: Checkout
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          ref: ${{ github.event.release.tag_name || github.sha }}

      - name: Setup pnpm
        uses: pnpm/action-setup@9fd676a19091d4595eefd76e4bd31c97133911f1 # v4.2.0
        with:
          version: "10.30.3"

      - name: Setup Node
        uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
        with:
          node-version: "22"
          registry-url: "https://registry.npmjs.org"
          cache: "pnpm"
          cache-dependency-path: clients/typescript/pnpm-lock.yaml

      - name: Update npm (trusted publisher requires >=11.5.1)
        run: npm install -g npm@latest

      - name: Install dependencies
        run: pnpm --dir clients/typescript install --frozen-lockfile

      - name: Set client version from release tag
        if: ${{ github.event.release.tag_name != '' }}
        working-directory: clients/typescript/client
        run: npm version "${{ github.event.release.tag_name }}" --no-git-tag-version --allow-same-version

      - name: Build client
        run: pnpm --dir clients/typescript --filter @kayibal/fynd-client run build

      # Skip when this version is already on npm so a recovered/re-fired release
      # event doesn't fail the job on a duplicate publish.
      - name: Publish client
        working-directory: clients/typescript/client
        run: |
          set -euo pipefail
          name="$(jq -r .name package.json)"
          version="$(jq -r .version package.json)"
          if npm view "${name}@${version}" version >/dev/null 2>&1; then
            echo "${name}@${version} already on npm, skipping"
            exit 0
          fi
          npm publish --provenance --access public

  # Tells the downstream deployment repository that a new version is on crates.io, so it can
  # open its own dependency-update pull request without a person or a poll noticing first.
  # The repository is named by a secret; with no secret set, this job does nothing.
  notify-downstream:
    name: Notify the downstream deployment repository
    # publish-crates does not run for a pre-release, which skips this job with it.
    needs: publish-crates
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - name: Check that a downstream repository is configured
        id: config
        env:
          DOWNSTREAM_REPO: ${{ secrets.DOWNSTREAM_REPO }}
        run: |
          set -euo pipefail
          if [ -z "${DOWNSTREAM_REPO}" ]; then
            echo "DOWNSTREAM_REPO is empty, so there is nothing to notify." \
              >> "$GITHUB_STEP_SUMMARY"
            echo "configured=false" >> "$GITHUB_OUTPUT"
          else
            echo "configured=true" >> "$GITHUB_OUTPUT"
          fi

      - name: Generate a token for the downstream repository
        id: app-token
        if: steps.config.outputs.configured == 'true'
        uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}
          owner: ${{ github.repository_owner }}
          repositories: ${{ secrets.DOWNSTREAM_REPO }}

      - name: Send the release dispatch
        if: steps.config.outputs.configured == 'true'
        env:
          GH_TOKEN: ${{ steps.app-token.outputs.token }}
          OWNER: ${{ github.repository_owner }}
          DOWNSTREAM_REPO: ${{ secrets.DOWNSTREAM_REPO }}
          VERSION: ${{ github.event.release.tag_name }}
        run: |
          set -euo pipefail
          jq -n --arg v "${VERSION}" \
            '{event_type: "fynd-release", client_payload: {version: $v}}' \
            | gh api "repos/${OWNER}/${DOWNSTREAM_REPO}/dispatches" --method POST --input -
          echo "Sent the fynd-release dispatch for ${VERSION}." >> "$GITHUB_STEP_SUMMARY"