coretilus 0.7.0

A parody of GNU Coreutils — a playful collection of typo-triggered command-line tools written in Rust
Documentation
name: Release Binaries

on:
  push:
    tags:
      - "v*" # Trigger only when a tag starting with "v" is pushed, e.g. v1.2.3

permissions:
  contents: write

jobs:
  checks:
    name: Pre-release checks
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

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

      - name: Verify tag matches Cargo.toml version
        shell: bash
        run: |
          TAG_VERSION="${GITHUB_REF_NAME#v}"
          CARGO_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')

          echo "Tag version: $TAG_VERSION"
          echo "Cargo.toml version: $CARGO_VERSION"

          if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
            echo "❌ Version mismatch: tag ($TAG_VERSION) != Cargo.toml ($CARGO_VERSION)"
            exit 1
          fi

          echo "✅ Tag and Cargo.toml version match."

  build:
    name: Build for ${{ matrix.os }}/${{ matrix.arch }}
    runs-on: ${{ matrix.os }}
    needs: checks
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            arch: x86_64
            target: x86_64-unknown-linux-gnu
            ext: ""
          - os: windows-latest
            arch: x86_64
            target: x86_64-pc-windows-msvc
            ext: ".exe"
          - os: macos-latest
            arch: aarch64
            target: aarch64-apple-darwin
            ext: ""
          - os: macos-latest
            arch: x86_64
            target: x86_64-apple-darwin
            ext: ""
          - os: ubuntu-latest
            arch: aarch64
            target: aarch64-unknown-linux-gnu
            ext: ""

    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

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

      - name: Add target
        run: rustup target add ${{ matrix.target }}

      - name: Install ARM toolchain and et up Cargo config for cross-compilation
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |
          sudo apt update && sudo apt install -y gcc-aarch64-linux-gnu
          mkdir -p .cargo
          echo '[target.aarch64-unknown-linux-gnu]' > .cargo/config.toml
          echo 'linker = "aarch64-linux-gnu-gcc"' >> .cargo/config.toml
          rustup target add aarch64-unknown-linux-gnu

      - name: Install Deps to build linux packages
        if: matrix.os == 'ubuntu-latest'
        run: |
          cargo install cargo-deb cargo-generate-rpm

      - name: Build binary
        run: cargo build --release --target ${{ matrix.target }}

      - name: Prepare artifact (all OS)
        shell: bash
        run: |
          mkdir -p artifacts
          BIN_DIR="target/${{ matrix.target }}/release"
          BIN_LIST=$(find "$BIN_DIR" -maxdepth 1 -type f -perm -111 | xargs -n1 basename)
          echo "Found binaries: $BIN_LIST"
          case "${{ matrix.os }}" in
            windows-latest)
              ARCHIVE_NAME="coretilus-${{ github.ref_name }}-windows_${{ matrix.arch }}.zip"
              BIN_ARRAY=""
              for bin in $BIN_LIST; do
                BIN_ARRAY+="'$BIN_DIR/$bin',"
              done
              BIN_ARRAY=${BIN_ARRAY%,}
              pwsh -Command "Compress-Archive -Path @($BIN_ARRAY) -DestinationPath 'artifacts/$ARCHIVE_NAME' -Force"
              ;;
            ubuntu-latest)
              ARCHIVE_NAME="coretilus-${{ github.ref_name }}-linux_${{ matrix.arch }}.tar.gz"
              tar -czf "artifacts/$ARCHIVE_NAME" -C "$BIN_DIR" $BIN_LIST
              cargo deb --target ${{ matrix.target }}
              cp -v target/debian/*.deb artifacts/
              cargo generate-rpm --target ${{ matrix.target }}
              cp -v target/${{ matrix.target }}/generate-rpm/*.rpm artifacts/
              ;;
            macos-latest)
              ARCHIVE_NAME="coretilus-${{ github.ref_name }}-macos_${{ matrix.arch }}.tar.gz"
              tar -czf "artifacts/$ARCHIVE_NAME" -C "$BIN_DIR" $BIN_LIST
              ;;
            *)
              echo "Unsupported OS: ${{ matrix.os }}"
              exit 1
              ;;
          esac

          echo "Created artifact: artifacts/$ARCHIVE_NAME"

      - name: Upload artifact
        uses: actions/upload-artifact@v7
        with:
          name: ${{ matrix.target }}
          path: artifacts/

  release:
    name: Create GitHub Release
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: write

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

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v3
        with:
          files: artifacts/**/*
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  publish-crates:
    name: Publish to crates.io
    runs-on: ubuntu-latest
    needs: release
    permissions:
      id-token: write
      contents: read
    environment:
      name: crates-io
      url: https://crates.io/crates/coretilus
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7
      - name: Setup Rust toolchain
        uses: dtolnay/rust-toolchain@stable
      - uses: rust-lang/crates-io-auth-action@v1
        id: auth
      - name: Publish to crates.io
        uses: katyo/publish-crates@v2
        with:
          registry-token: ${{ steps.auth.outputs.token }}

  publish-homebrew:
    name: Publish/Update Homebrew formula
    runs-on: ubuntu-latest
    needs: release
    steps:
      - name: Extract version from branch/tag
        id: version
        run: |
          TAG="${GITHUB_REF_NAME}"
          VERSION="${TAG#v}"
          echo "version=${VERSION}" >> $GITHUB_OUTPUT
          echo "tag=${TAG}" >> $GITHUB_OUTPUT
          echo "Version: ${VERSION}, Tag: ${TAG}"

      - name: Download and extract release assets
        id: assets
        run: |
          set -x
          VERSION="${{ steps.version.outputs.version }}"
          TAG="${{ steps.version.outputs.tag }}"
          BASE_URL="https://github.com/${{ github.repository }}/releases/download/${TAG}"

          download_and_extract() {
            local file=$1
            local arch=$2

            echo "Downloading ${BASE_URL}/${file}..."
            if curl -fsSL "${BASE_URL}/${file}" -o "${file}"; then
              SHA=$(sha256sum "${file}" | cut -d' ' -f1)
              echo "✅ SHA256 ${arch}: ${SHA}"

              mkdir -p "temp_${arch}"
              tar -xzf "${file}" -C "temp_${arch}"

              BINS=$(find "temp_${arch}" -maxdepth 1 -type f -perm -111 -exec basename {} \; | tr "\n" " ")
              echo "✅ Binaires trouvés (${arch}): ${BINS}"

              echo "${SHA}|${BINS}"
            else
              echo "ERROR: Failed to download ${file}"
              exit 1
            fi
          }

          ARM64_DATA=$(download_and_extract "coretilus-${TAG}-macos_aarch64.tar.gz" "arm64")
          echo $ARM64_DATA
          SHA_ARM64=$(echo "$ARM64_DATA" | tail -1 | cut -d'|' -f1)
          BINS_ARM64=$(echo "$ARM64_DATA" | tail -1 | cut -d'|' -f2)

          X86_64_DATA=$(download_and_extract "coretilus-${TAG}-macos_x86_64.tar.gz" "x86_64")
          echo $X86_64_DATA
          SHA_X86_64=$(echo "$X86_64_DATA" | tail -1 | cut -d'|' -f1)
          BINS_X86_64=$(echo "$X86_64_DATA" | tail -1 | cut -d'|' -f2)

          echo "sha_arm64=${SHA_ARM64}" >> $GITHUB_OUTPUT
          echo "sha_x86_64=${SHA_X86_64}" >> $GITHUB_OUTPUT

          echo "binaries=${BINS_ARM64}" >> $GITHUB_OUTPUT

      - name: Checkout tap repository
        uses: actions/checkout@v7
        with:
          repository: titilambert/homebrew-tap
          ssh-key: ${{ secrets.TAP_DEPLOY_KEY }}
          path: tap

      - name: Generate formula from template
        run: |
          set -x
          VERSION="${{ steps.version.outputs.version }}"
          TAG="${{ steps.version.outputs.tag }}"
          TEMPLATE="tap/templates/coretilus.rb.template"
          FORMULA="tap/Formula/coretilus.rb"
          BINARIES=$(echo '${{ steps.assets.outputs.binaries }}' | tr " " "\n")

          if [ ! -f "$TEMPLATE" ]; then
            echo "❌ Error: Template not found at $TEMPLATE"
            exit 1
          fi

          mkdir -p "tap/Formula"
          BINARIES_INSTALL=""
          BINARIES_TEST=""

          for binary in $BINARIES; do
            BINARIES_INSTALL="${BINARIES_INSTALL}    bin.install \"${binary}\"\n"
            BINARIES_TEST="${BINARIES_TEST}    assert_match \"${binary}\", shell_output(\"#{bin}/${binary} --version 2>&1\", 0)\n"
          done

          cp "$TEMPLATE" "$FORMULA"

          sed -i "s/{{VERSION}}/${VERSION}/g" "$FORMULA"
          sed -i "s/{{SHA256_ARM64}}/${{ steps.assets.outputs.sha_arm64 }}/g" "$FORMULA"
          sed -i "s/{{SHA256_X86_64}}/${{ steps.assets.outputs.sha_x86_64 }}/g" "$FORMULA"

          awk -v version="$VERSION" \
              -v sha_arm64="${{ steps.assets.outputs.sha_arm64 }}" \
              -v sha_x86_64="${{ steps.assets.outputs.sha_x86_64 }}" \
              -v bins_install="$BINARIES_INSTALL" \
              -v bins_test="$BINARIES_TEST" '
          {
            gsub(/\{\{VERSION\}\}/, version)
            gsub(/\{\{SHA256_ARM64\}\}/, sha_arm64)
            gsub(/\{\{SHA256_X86_64\}\}/, sha_x86_64)
            gsub(/\{\{BINARIES_INSTALL\}\}/, bins_install)
            gsub(/\{\{BINARIES_TEST\}\}/, bins_test)
            print
          }' "$TEMPLATE" > "$FORMULA"

                  echo "✅ Formula generated:"
          cat "$FORMULA"

      - name: Commit and push to tap repository
        run: |
          cd tap
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add Formula/coretilus.rb
          git commit -m "chore: bump coretilus to ${{ steps.version.outputs.tag }}"
          git tag -a "coretilus-${{ steps.version.outputs.tag }}" -m "Coretilus ${{ steps.version.outputs.tag }}"
          git push origin main
          git push origin "coretilus-${{ steps.version.outputs.tag }}"