twin-cli 0.2.0

Git worktree wrapper with side effects (symlinks and hooks)
Documentation
name: Release

# Releasesへのファイル追加のために書き込み権限が必要
permissions:
  contents: write
  id-token: write  # Trusted Publishing用のOIDCトークン

on:
  push:
    tags:
      - v*

jobs:
  # リリース作成
  create-release:
    name: Create GitHub Release
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.get_version.outputs.version }}
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
      with:
        fetch-depth: 0  # 完全な履歴を取得(リリースノート生成用)
    
    - name: Get version from tag
      id: get_version
      run: |
        echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
    
    - name: Create Release
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        # Check if release already exists
        if gh release view '${{ github.ref_name }}' > /dev/null 2>&1; then
          echo "Release ${{ github.ref_name }} already exists, skipping creation"
        else
          # Generate release notes
          RELEASE_NOTES=$(gh api \
            -X POST \
            /repos/${{ github.repository }}/releases/generate-notes \
            -f tag_name='${{ github.ref_name }}' \
            -f target_commitish='${{ github.sha }}' \
            --jq .body)
          
          # Create release
          gh release create '${{ github.ref_name }}' \
            --title "Twin ${{ steps.get_version.outputs.version }}" \
            --notes "## Installation
          
          ### Cargo
          \`\`\`bash
          cargo install twin-cli
          \`\`\`
          
          ### Manual Installation
          Download the appropriate binary for your platform and add it to your PATH.
          
          ## What's Changed
          $RELEASE_NOTES" \
            --draft=false \
            --prerelease=false
        fi

  # 各プラットフォーム向けビルド
  build:
    name: Build for ${{ matrix.job.target }}
    needs: create-release
    runs-on: ${{ matrix.job.os }}
    strategy:
      fail-fast: false
      matrix:
        job:
          # Linux targets
          - { os: ubuntu-latest  , target: x86_64-unknown-linux-gnu       , use-cross: false , extension: ""   }
          - { os: ubuntu-latest  , target: x86_64-unknown-linux-musl      , use-cross: true  , extension: ""   }
          - { os: ubuntu-latest  , target: aarch64-unknown-linux-gnu      , use-cross: true  , extension: ""   }
          - { os: ubuntu-latest  , target: aarch64-unknown-linux-musl     , use-cross: true  , extension: ""   }
          
          # macOS targets
          - { os: macos-latest   , target: x86_64-apple-darwin            , use-cross: false , extension: ""   }
          - { os: macos-latest   , target: aarch64-apple-darwin           , use-cross: false , extension: ""   }
          
          # Windows targets
          - { os: windows-latest , target: x86_64-pc-windows-msvc         , use-cross: false , extension: .exe }
    
    steps:
    - name: Checkout
      uses: actions/checkout@v4

    # Rustのpackage名を取得して環境変数に入れておく
    - name: Extract crate information
      shell: bash
      run: |
        # Cargo.tomlからバイナリ名を取得([[bin]]セクションのname)
        echo "BINARY_NAME=twin" >> $GITHUB_ENV
        echo "VERSION=${{ needs.create-release.outputs.version }}" >> $GITHUB_ENV

    # Rustツールチェーンのインストール
    - name: Install Rust toolchain
      uses: dtolnay/rust-toolchain@stable
      with:
        targets: ${{ matrix.job.target }}

    # crossのインストール(必要な場合)
    - name: Install cross
      if: matrix.job.use-cross
      shell: bash
      run: |
        # cargoでcrossをインストール(最新版を確実に取得)
        cargo install cross --git https://github.com/cross-rs/cross --locked

    # ビルド(crossまたはcargoを使用)
    - name: Build with cross
      if: matrix.job.use-cross
      shell: bash
      run: |
        cross build --release --target ${{ matrix.job.target }}

    - name: Build with cargo
      if: ${{ !matrix.job.use-cross }}
      shell: bash
      run: |
        cargo build --release --target ${{ matrix.job.target }}

    # Windows用のパッケージング
    - name: Package (Windows)
      if: startsWith(matrix.job.os, 'windows')
      shell: powershell
      run: |
        $version = "${{ env.VERSION }}"
        $target = "${{ matrix.job.target }}"
        $dist = "twin-$version-$target"
        
        New-Item -ItemType Directory -Force -Path $dist
        Copy-Item "target/$target/release/${{ env.BINARY_NAME }}${{ matrix.job.extension }}" -Destination "$dist/"
        Copy-Item "README.md" -Destination "$dist/"
        if (Test-Path "LICENSE") { Copy-Item "LICENSE" -Destination "$dist/" }
        
        Compress-Archive -Path $dist -DestinationPath "twin-$version-$target.zip"
    
    # Unix系用のパッケージング
    - name: Package (Unix)
      if: ${{ !startsWith(matrix.job.os, 'windows') }}
      shell: bash
      run: |
        version="${{ env.VERSION }}"
        target="${{ matrix.job.target }}"
        dist="twin-$version-$target"
        
        mkdir -p "$dist"
        cp "target/$target/release/${{ env.BINARY_NAME }}${{ matrix.job.extension }}" "$dist/"
        cp README.md "$dist/"
        [ -f LICENSE ] && cp LICENSE "$dist/"
        
        tar czf "twin-$version-$target.tar.gz" "$dist"

    # アセットのアップロード
    - name: Upload Release Asset (Windows)
      if: startsWith(matrix.job.os, 'windows')
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      shell: bash
      run: |
        gh release upload '${{ github.ref_name }}' \
          "twin-${{ env.VERSION }}-${{ matrix.job.target }}.zip" \
          --clobber

    - name: Upload Release Asset (Unix)
      if: ${{ !startsWith(matrix.job.os, 'windows') }}
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      shell: bash
      run: |
        gh release upload '${{ github.ref_name }}' \
          "twin-${{ env.VERSION }}-${{ matrix.job.target }}.tar.gz" \
          --clobber

  # crates.ioへの公開(2回目以降、Trusted Publishing使用)
  publish-crates:
    name: Publish to crates.io
    needs: [create-release, build]
    runs-on: ubuntu-latest
    # v0.2.0以降で有効化(初回v0.1.0は手動公開のため)
    if: ${{ !startsWith(github.ref, 'refs/tags/v0.1.') }}
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Install Rust
      uses: dtolnay/rust-toolchain@stable
    
    - name: Verify package
      run: |
        cargo package --no-verify
        cargo package --list
    
    - name: Publish to crates.io
      # Trusted Publishingを使用(初回公開後に設定)
      env:
        CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
      run: |
        cargo publish --no-verify