shellql 0.1.7-beta

A Vim- and tmux-inspired terminal database manager for developers
Documentation
name: Publish to crates.io

on:
  release:
    # Release workflow creates beta prereleases; include both prereleased and published.
    types: [published, prereleased]
  workflow_dispatch:
    inputs:
      tag:
        description: "Release tag to publish (e.g. v0.1.4-beta). Optional when run from release event."
        required: false
        type: string

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always

jobs:
  publish:
    runs-on: ubuntu-latest
    env:
      RELEASE_TAG_EVENT: ${{ github.event.release.tag_name }}
      RELEASE_TAG_INPUT: ${{ github.event.inputs.tag }}
      CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

    steps:
      - name: Resolve release tag
        id: vars
        shell: bash
        run: |
          set -euo pipefail

          TAG="${RELEASE_TAG_EVENT:-}"
          if [[ -z "$TAG" ]]; then
            TAG="${RELEASE_TAG_INPUT:-}"
          fi

          if [[ -z "$TAG" ]]; then
            echo "No release tag provided. Use release event or pass workflow input 'tag'." >&2
            exit 1
          fi
          if [[ ! "$TAG" =~ ^v ]]; then
            echo "Tag must start with 'v' (got: $TAG)" >&2
            exit 1
          fi

          echo "tag=$TAG" >> "$GITHUB_OUTPUT"

      - name: Validate token availability
        shell: bash
        run: |
          set -euo pipefail
          if [[ -z "${CARGO_REGISTRY_TOKEN}" ]]; then
            echo "Missing secret: CARGO_REGISTRY_TOKEN" >&2
            echo "Add a crates.io API token as a repository secret named CARGO_REGISTRY_TOKEN." >&2
            exit 1
          fi

      - name: Checkout source at tag
        uses: actions/checkout@v4
        with:
          ref: ${{ steps.vars.outputs.tag }}

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

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

      - name: Validate Cargo.toml version matches tag
        id: version
        shell: bash
        run: |
          set -euo pipefail

          VERSION="$(awk '
            $0=="[package]" { in_pkg=1; next }
            /^\[/ && $0!="[package]" { in_pkg=0 }
            in_pkg && $1=="version" {
              gsub(/"/, "", $3)
              print $3
              exit
            }
          ' Cargo.toml)"

          if [[ -z "$VERSION" ]]; then
            echo "Could not read package version from Cargo.toml" >&2
            exit 1
          fi

          EXPECTED_TAG="v${VERSION}"
          if [[ "$EXPECTED_TAG" != "${{ steps.vars.outputs.tag }}" ]]; then
            echo "Tag/version mismatch: tag=${{ steps.vars.outputs.tag }} Cargo.toml=${VERSION}" >&2
            exit 1
          fi

          echo "version=$VERSION" >> "$GITHUB_OUTPUT"

      - name: Skip if version already exists on crates.io
        id: exists
        shell: bash
        run: |
          set -euo pipefail

          VERSION="${{ steps.version.outputs.version }}"
          API_URL="https://crates.io/api/v1/crates/shellql/versions?page=1&per_page=100"

          # Keep this check very simple to avoid YAML/heredoc parsing edge cases.
          if curl -fsSL "$API_URL" | grep -q "\"num\":\"$VERSION\""; then
            EXISTS="true"
          else
            EXISTS="false"
          fi

          echo "exists=$EXISTS" >> "$GITHUB_OUTPUT"
          if [[ "$EXISTS" == "true" ]]; then
            echo "Version $VERSION already on crates.io; skipping publish."
          fi

      - name: Publish crate
        if: steps.exists.outputs.exists != 'true'
        run: cargo publish --locked