yuki-cli 0.1.2

CLI client for the Yuki bookkeeping SOAP API
Documentation
name: Release

on:
  push:
    tags:
      - 'v[0-9]*.[0-9]*.[0-9]*'
  workflow_dispatch:
    inputs:
      dry_run:
        description: 'Dry run mode (skip actual publishing)'
        required: false
        default: true
        type: boolean
      skip_crates_io:
        description: 'Skip publishing to crates.io (if already published)'
        required: false
        default: false
        type: boolean
      skip_pypi:
        description: 'Skip publishing to PyPI (if already published)'
        required: false
        default: false
        type: boolean

permissions:
  contents: write

env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

jobs:
  test:
    name: Run tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: dtolnay/rust-toolchain@stable

      - name: Verify Cargo.lock is up to date
        run: cargo check --locked

      - name: Run tests
        run: make test

  build:
    name: Build ${{ matrix.target }}
    needs: test
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
          - os: ubuntu-latest
            target: aarch64-unknown-linux-gnu
          - os: macos-latest
            target: x86_64-apple-darwin
          - os: macos-latest
            target: aarch64-apple-darwin
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4

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

      - name: Install cross-compilation tools
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu

      - name: Install maturin
        run: pip install maturin

      - name: Build wheel
        shell: bash
        run: |
          case "${{ matrix.target }}" in
            *-gnu)
              pip install ziglang
              maturin build --release --target ${{ matrix.target }} --compatibility manylinux_2_28 --zig
              ;;
            *)
              maturin build --release --target ${{ matrix.target }}
              ;;
          esac

      - name: Build binary
        env:
          CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
        run: cargo build --release --target ${{ matrix.target }}

      - name: Verify binary
        if: ${{ !contains(matrix.target, 'aarch64-unknown-linux') }}
        run: ./target/${{ matrix.target }}/release/yuki --help

      - name: Create release archive
        shell: bash
        run: |
          VERSION=${GITHUB_REF#refs/tags/}
          ARCHIVE_NAME="yuki-cli-${VERSION}-${{ matrix.target }}"

          mkdir -p release-package
          cp "target/${{ matrix.target }}/release/yuki" release-package/yuki
          tar -czf "${ARCHIVE_NAME}.tar.gz" -C release-package yuki

          if [[ "${{ runner.os }}" == "macOS" ]]; then
            shasum -a 256 "${ARCHIVE_NAME}.tar.gz" > "${ARCHIVE_NAME}.tar.gz.sha256"
          else
            sha256sum "${ARCHIVE_NAME}.tar.gz" > "${ARCHIVE_NAME}.tar.gz.sha256"
          fi

          rm -rf release-package

      - name: Upload wheel
        uses: actions/upload-artifact@v4
        with:
          path: target/wheels/*
          name: wheel-${{ matrix.target }}

      - name: Upload release archives
        uses: actions/upload-artifact@v4
        with:
          path: |
            yuki-cli-*-${{ matrix.target }}.tar.gz*
          name: release-${{ matrix.target }}

  sdist:
    name: Build source distribution
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4

      - name: Install maturin
        run: pip install maturin

      - name: Build sdist
        run: maturin sdist

      - uses: actions/upload-artifact@v4
        with:
          path: target/wheels/*.tar.gz
          name: sdist

  release:
    runs-on: ubuntu-latest
    needs: [build, sdist]
    steps:
      - uses: actions/checkout@v4

      - uses: dtolnay/rust-toolchain@stable

      - name: Install uv
        uses: astral-sh/setup-uv@v5
        with:
          enable-cache: false

      - name: Publish to crates.io
        if: ${{ inputs.dry_run != true && inputs.skip_crates_io != true }}
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: cargo publish --locked

      - name: Test crates.io publish (dry run)
        if: ${{ inputs.dry_run == true && inputs.skip_crates_io != true }}
        run: |
          echo "DRY RUN: Would publish to crates.io"
          cargo publish --dry-run --locked

      - name: Skip crates.io publishing
        if: ${{ inputs.skip_crates_io == true }}
        run: echo "Skipping crates.io publishing as requested"

      - name: Download all artifacts
        uses: actions/download-artifact@v4
        with:
          path: artifacts

      - name: Publish to PyPI
        if: ${{ inputs.dry_run != true && inputs.skip_pypi != true }}
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
        run: |
          uv tool run twine upload artifacts/wheel-*/*.whl artifacts/sdist/*.tar.gz

      - name: Test PyPI upload (dry run)
        if: ${{ inputs.dry_run == true && inputs.skip_pypi != true }}
        run: |
          echo "DRY RUN: Would upload to PyPI:"
          find artifacts/wheel-* -name "*.whl" -type f | sort
          find artifacts/sdist -name "*.tar.gz" -type f | sort
          uv tool run twine check artifacts/wheel-*/*.whl artifacts/sdist/*.tar.gz

      - name: Skip PyPI publishing
        if: ${{ inputs.skip_pypi == true }}
        run: echo "Skipping PyPI publishing as requested"

      - name: Create Release
        if: ${{ inputs.dry_run != true }}
        uses: softprops/action-gh-release@v2
        with:
          generate_release_notes: true
          files: |
            artifacts/release-*/yuki-cli-*.tar.gz
            artifacts/release-*/yuki-cli-*.tar.gz.sha256

      - name: Dry Run Summary
        if: ${{ inputs.dry_run == true }}
        run: |
          echo "Dry run complete. Artifacts built but nothing published."
          echo "Archives:"
          find artifacts/release-* -type f | sort