inkhaven 1.2.3

Inkhaven — TUI literary work editor for Typst books
name: release

# Builds release binaries on every `v*` tag, packages them per platform,
# and publishes them to a GitHub Release. Users then install via:
#   * `cargo binstall inkhaven` (uses [package.metadata.binstall] in
#     Cargo.toml to fetch the matching prebuilt)
#   * Direct download from the Releases page
#   * `cargo install --git https://github.com/vulogov/blackInkhaven`
#     (compiles from source against vendored deps)
#
# Manual run is also possible via the Actions tab → "Run workflow".

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      tag:
        description: "Tag to release (e.g. v1.0.1)"
        required: true
        type: string

permissions:
  contents: write   # needed to create the Release and upload assets

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  # ----------------------------------------------------------------------
  # Build a release binary on each target platform. Each matrix job
  # produces one tarball / zip uploaded as a workflow artifact; the
  # `release` job below collects them and posts the GitHub Release.
  # ----------------------------------------------------------------------
  build:
    name: build (${{ matrix.target }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            archive: tar.gz
          - os: macos-latest
            target: aarch64-apple-darwin
            archive: tar.gz
          # macOS x86_64 (Intel) is temporarily disabled. The macos-13
          # runner pool is heavily capacity-constrained — v1.0.2's job
          # sat in `queued` for 3+ hours without picking up a runner.
          # Apple stopped selling Intel Macs in 2023; modern users are
          # on Apple Silicon (aarch64) above. Intel-Mac users can still
          # `cargo install --git`. Re-enable once GitHub-hosted macos-13
          # runner availability improves OR by adopting a self-hosted
          # Intel-Mac runner.
          # - os: macos-13
          #   target: x86_64-apple-darwin
          #   archive: tar.gz
          # Windows is temporarily disabled. yeslogic-fontconfig-sys
          # (pulled in transitively by bdslib) is a wrapper over the
          # POSIX fontconfig C library; on Windows it needs a vcpkg /
          # custom-toolchain install that hasn't been worked out yet.
          # Re-enable once the Windows build path is sorted — likely via
          # `vcpkg install fontconfig:x64-windows-static` + appropriate
          # env vars, or by trimming bdslib's font-using features. See
          # github issue / log for the failing v1.0.0 / v1.0.1 runs.
          # - os: windows-latest
          #   target: x86_64-pc-windows-msvc
          #   archive: zip
    steps:
      - name: Resolve tag
        id: tag
        shell: bash
        run: |
          if [ -n "${{ inputs.tag }}" ]; then
            echo "value=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
          else
            echo "value=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
          fi

      - uses: actions/checkout@v4
        with:
          ref: ${{ steps.tag.outputs.value }}

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      # Linux: install system libs that bdslib's transitive native deps
      # need. fontconfig comes in via yeslogic-fontconfig-sys; the xcb-*
      # set covers arboard's X11 clipboard backend; libssl-dev is a common
      # baseline. macOS and Windows runners use platform-native equivalents.
      - name: Install Linux build dependencies
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y --no-install-recommends \
            pkg-config \
            libfontconfig1-dev \
            libssl-dev \
            libxcb1-dev \
            libxcb-render0-dev \
            libxcb-shape0-dev \
            libxcb-xfixes0-dev \
            libasound2-dev

      # Caches `~/.cargo` + `target/` between runs. Critical: bdslib
      # pulls in duckdb / tantivy / fastembed / augurs, so a cold build
      # is 10–15 minutes. The cache key includes Cargo.lock so a real
      # dep change still rebuilds.
      - name: Cache cargo
        uses: Swatinem/rust-cache@v2
        with:
          key: ${{ matrix.target }}

      # Linux glibc target — Ubuntu's default toolchain is fine.
      # Windows / macOS pick the platform compilers automatically.
      - name: Build (release)
        run: cargo build --release --locked --target ${{ matrix.target }}

      # Strip & package. The on-disk layout inside the archive is
      # `inkhaven-<target>/inkhaven[.exe]` so the cargo-binstall
      # `bin-dir` in Cargo.toml ("{name}-{target}/{bin}{binary-ext}")
      # resolves cleanly.
      - name: Package (unix)
        if: matrix.os != 'windows-latest'
        shell: bash
        run: |
          set -euo pipefail
          DIR="inkhaven-${{ matrix.target }}"
          mkdir -p "$DIR"
          cp "target/${{ matrix.target }}/release/inkhaven" "$DIR/"
          cp README.md LICENSE "$DIR/" 2>/dev/null || true
          strip "$DIR/inkhaven" || true
          tar -czf "$DIR.tar.gz" "$DIR"
          ls -la "$DIR.tar.gz"

      - name: Package (windows)
        if: matrix.os == 'windows-latest'
        shell: pwsh
        run: |
          $dir = "inkhaven-${{ matrix.target }}"
          New-Item -ItemType Directory -Force -Path $dir | Out-Null
          Copy-Item "target/${{ matrix.target }}/release/inkhaven.exe" "$dir/"
          if (Test-Path README.md) { Copy-Item README.md "$dir/" }
          if (Test-Path LICENSE)   { Copy-Item LICENSE "$dir/" }
          Compress-Archive -Path $dir -DestinationPath "$dir.zip"

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: inkhaven-${{ matrix.target }}
          path: |
            inkhaven-${{ matrix.target }}.tar.gz
            inkhaven-${{ matrix.target }}.zip
          if-no-files-found: ignore
          retention-days: 7

  # ----------------------------------------------------------------------
  # Collect every artifact and publish a single GitHub Release.
  # ----------------------------------------------------------------------
  release:
    name: publish release
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Resolve tag
        id: tag
        shell: bash
        run: |
          if [ -n "${{ inputs.tag }}" ]; then
            echo "value=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
          else
            echo "value=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
          fi

      - uses: actions/checkout@v4
        with:
          ref: ${{ steps.tag.outputs.value }}

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

      - name: List artifacts
        run: ls -la dist

      - name: Create / update GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ steps.tag.outputs.value }}
          name: ${{ steps.tag.outputs.value }}
          draft: false
          prerelease: ${{ contains(steps.tag.outputs.value, '-') }}
          generate_release_notes: true
          files: |
            dist/inkhaven-*.tar.gz
            dist/inkhaven-*.zip
          body: |
            Inkhaven ${{ steps.tag.outputs.value }} — TUI literary editor.

            ## Install

            * `cargo binstall inkhaven` (downloads the prebuilt matching your host triple)
            * `cargo install --git https://github.com/vulogov/blackInkhaven --tag ${{ steps.tag.outputs.value }}` (compiles from source)
            * Or pick the right archive below and unpack it into `$PATH`.

            ## Docs

            * Quick start: [README](https://github.com/vulogov/blackInkhaven/blob/${{ steps.tag.outputs.value }}/README.md)
            * Full manual: [Documentation/](https://github.com/vulogov/blackInkhaven/tree/${{ steps.tag.outputs.value }}/Documentation)