dcr 0.6.8

DCR is a utility for managing C/C++ projects in a Cargo-like style.
name: Release

on:
  push:
    tags:
      - "v*"
  workflow_dispatch:
    inputs:
      tag:
        description: "Tag to build (e.g. v0.6.7)"
        required: true
      run_linux:
        description: "Build Linux"
        type: boolean
        default: true
      run_bsd:
        description: "Build BSD"
        type: boolean
        default: true
      run_macos:
        description: "Build macOS"
        type: boolean
        default: true
      run_windows:
        description: "Build Windows"
        type: boolean
        default: true
      run_publish:
        description: "Publish to crates.io"
        type: boolean
        default: false
      run_brew:
        description: "Update Homebrew tap"
        type: boolean
        default: false

permissions:
  contents: write

jobs:
  # ── Resolve Tag ────────────────────────────────────────────────────────────
  setup:
    runs-on: ubuntu-latest
    outputs:
      tag: ${{ steps.tag.outputs.value }}
      version: ${{ steps.tag.outputs.version }}
      prerelease: ${{ steps.tag.outputs.prerelease }}
    steps:
      - name: Resolve tag
        id: tag
        run: |
          if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
            TAG="${{ github.event.inputs.tag }}"
          else
            TAG="${GITHUB_REF_NAME}"
          fi
          VERSION="${TAG#v}"
          [[ "$TAG" == *"-"* ]] && PRERELEASE="true" || PRERELEASE="false"
          echo "value=${TAG}"             >> "$GITHUB_OUTPUT"
          echo "version=${VERSION}"       >> "$GITHUB_OUTPUT"
          echo "prerelease=${PRERELEASE}" >> "$GITHUB_OUTPUT"

  # ── Release Notes ──────────────────────────────────────────────────────────
  notes:
    needs: setup
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.setup.outputs.tag }}

      - name: Prepare Release Notes
        shell: bash
        run: |
          set -euo pipefail
          tag="${{ needs.setup.outputs.version }}"
          notes_file=".github/release-notes.md"
          if grep -q "^## \\[${tag}\\]" CHANGELOG.md; then
            awk -v ver="$tag" '
              $0 ~ "^## \\[" ver "\\]" {found=1; next}
              found && $0 ~ "^## \\[" {exit}
              found {print}
            ' CHANGELOG.md | sed -e '${/^$/d;}' > "$notes_file"
          else
            echo "No CHANGELOG section for $tag." > "$notes_file"
            git log -1 --pretty=format:"%s%n%n%b" >> "$notes_file"
          fi

      - name: Upload Notes Artifact
        uses: actions/upload-artifact@v4
        with:
          name: release-notes
          path: .github/release-notes.md

  # ── Linux ──────────────────────────────────────────────────────────────────
  build-linux:
    if: ${{ github.event_name == 'push' || github.event.inputs.run_linux == 'true' }}
    needs: [setup, notes]
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
          - target: aarch64-unknown-linux-gnu
          - target: x86_64-unknown-linux-musl
          - target: aarch64-unknown-linux-musl
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.setup.outputs.tag }}

      - name: Download Release Notes
        uses: actions/download-artifact@v4
        with:
          name: release-notes
          path: .github

      - name: Install cargo-zigbuild
        run: |
          pip install cargo-zigbuild
          echo "$HOME/.local/bin" >> $GITHUB_PATH

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

      - name: Build
        run: |
          if [ "${{ matrix.target }}" != "x86_64-unknown-linux-gnu" ]; then
            cargo zigbuild --release --target ${{ matrix.target }}
          else
            cargo build --release --target ${{ matrix.target }}
          fi

      - name: Prepare Artifact
        run: |
          ASSET="dcr-${{ matrix.target }}-${{ needs.setup.outputs.version }}"
          cp target/${{ matrix.target }}/release/dcr "$ASSET"
          echo "ASSET_NAME=${ASSET}" >> "$GITHUB_ENV"

      - name: Upload Release Asset
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.setup.outputs.tag }}
          files: ${{ env.ASSET_NAME }}
          body_path: .github/release-notes.md
          prerelease: ${{ needs.setup.outputs.prerelease == 'true' }}

  # ── macOS ──────────────────────────────────────────────────────────────────
  build-macos:
    if: ${{ github.event_name == 'push' || github.event.inputs.run_macos == 'true' }}
    needs: [setup, notes]
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: macos-15-intel
            target: x86_64-apple-darwin
          - os: macos-latest
            target: aarch64-apple-darwin
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.setup.outputs.tag }}

      - name: Download Release Notes
        uses: actions/download-artifact@v4
        with:
          name: release-notes
          path: .github

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

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

      - name: Prepare Artifact
        run: |
          ASSET="dcr-${{ matrix.target }}-${{ needs.setup.outputs.version }}"
          cp target/${{ matrix.target }}/release/dcr "$ASSET"
          echo "ASSET_NAME=${ASSET}" >> "$GITHUB_ENV"

      - name: Upload Release Asset
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.setup.outputs.tag }}
          files: ${{ env.ASSET_NAME }}
          body_path: .github/release-notes.md
          prerelease: ${{ needs.setup.outputs.prerelease == 'true' }}

# ── BSD ────────────────────────────────────────────────────────────────────
  build-bsd:
    if: ${{ github.event_name == 'push' || github.event.inputs.run_bsd == 'true' }}
    needs: [setup, notes]
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-freebsd
          # При необходимости сюда можно докинуть x86_64-unknown-netbsd
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.setup.outputs.tag }}

      - name: Download Release Notes
        uses: actions/download-artifact@v4
        with:
          name: release-notes
          path: .github

      - name: Install cargo-zigbuild
        run: |
          pip install cargo-zigbuild
          echo "$HOME/.local/bin" >> $GITHUB_PATH

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

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

      - name: Prepare Artifact
        run: |
          ASSET="dcr-${{ matrix.target }}-${{ needs.setup.outputs.version }}"
          cp target/${{ matrix.target }}/release/dcr "$ASSET"
          echo "ASSET_NAME=${ASSET}" >> "$GITHUB_ENV"

      - name: Upload Release Asset
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.setup.outputs.tag }}
          files: ${{ env.ASSET_NAME }}
          body_path: .github/release-notes.md
          prerelease: ${{ needs.setup.outputs.prerelease == 'true' }}

  # ── Windows ────────────────────────────────────────────────────────────────
  build-windows:
    if: ${{ github.event_name == 'push' || github.event.inputs.run_windows == 'true' }}
    needs: [setup, notes]
    runs-on: windows-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-pc-windows-msvc
          - target: aarch64-pc-windows-msvc
          - target: x86_64-pc-windows-gnu
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.setup.outputs.tag }}

      - name: Download Release Notes
        uses: actions/download-artifact@v4
        with:
          name: release-notes
          path: .github

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

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

      - name: Prepare Artifact
        shell: pwsh
        run: |
          $asset = "dcr-${{ matrix.target }}-${{ needs.setup.outputs.version }}.exe"
          Copy-Item "target/${{ matrix.target }}/release/dcr.exe" "$asset"
          "ASSET_NAME=$asset" >> $env:GITHUB_ENV

      - name: Upload Release Asset
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.setup.outputs.tag }}
          files: ${{ env.ASSET_NAME }}
          body_path: .github/release-notes.md
          prerelease: ${{ needs.setup.outputs.prerelease == 'true' }}

  # ── crates.io ──────────────────────────────────────────────────────────────
  publish-crates:
    if: |
      (github.event_name == 'push' && needs.setup.outputs.prerelease == 'false') ||
      (github.event_name == 'workflow_dispatch' && github.event.inputs.run_publish == 'true')
    needs: [setup, build-linux, build-macos, build-windows, build-bsd]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.setup.outputs.tag }}

      - uses: dtolnay/rust-toolchain@stable

      - name: Publish to crates.io
        run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

  # ── Homebrew tap ───────────────────────────────────────────────────────────
  publish-brew:
    if: |
      (github.event_name == 'push' && needs.setup.outputs.prerelease == 'false') ||
      (github.event_name == 'workflow_dispatch' && github.event.inputs.run_brew == 'true')
    needs: [setup, build-linux, build-macos]
    runs-on: ubuntu-latest
    steps:
      - name: Download release assets and compute sha256
        id: sha
        run: |
          VERSION="${{ needs.setup.outputs.version }}"
          BASE="https://github.com/dexoron/dcr/releases/download/v${VERSION}"

          download_sha() {
            curl -fsSL "${BASE}/$1" | sha256sum | awk '{print $1}'
          }

          echo "linux_x86=$(download_sha "dcr-x86_64-unknown-linux-gnu-${VERSION}")"     >> "$GITHUB_OUTPUT"
          echo "linux_arm=$(download_sha "dcr-aarch64-unknown-linux-gnu-${VERSION}")"    >> "$GITHUB_OUTPUT"
          echo "macos_x86=$(download_sha "dcr-x86_64-apple-darwin-${VERSION}")"          >> "$GITHUB_OUTPUT"
          echo "macos_arm=$(download_sha "dcr-aarch64-apple-darwin-${VERSION}")"         >> "$GITHUB_OUTPUT"

      - name: Checkout homebrew-dexoron
        uses: actions/checkout@v4
        with:
          repository: dexoron/homebrew-dexoron
          token: ${{ secrets.HOMEBREW_TAP_TOKEN }}

      - name: Update Formula/dcr.rb
        run: |
          VERSION="${{ needs.setup.outputs.version }}"
          cat > Formula/dcr.rb <<FORMULA
          class Dcr < Formula
            desc "Cargo-like utility to manage C/C++ projects"
            homepage "https://dcr.dexoron.su"
            version "${VERSION}"

            on_macos do
              on_intel do
                url "https://github.com/dexoron/dcr/releases/download/v#{version}/dcr-x86_64-apple-darwin-#{version}"
                sha256 "${{ steps.sha.outputs.macos_x86 }}"
              end
              on_arm do
                url "https://github.com/dexoron/dcr/releases/download/v#{version}/dcr-aarch64-apple-darwin-#{version}"
                sha256 "${{ steps.sha.outputs.macos_arm }}"
              end
            end

            on_linux do
              on_intel do
                url "https://github.com/dexoron/dcr/releases/download/v#{version}/dcr-x86_64-unknown-linux-gnu-#{version}"
                sha256 "${{ steps.sha.outputs.linux_x86 }}"
              end
              on_arm do
                url "https://github.com/dexoron/dcr/releases/download/v#{version}/dcr-aarch64-unknown-linux-gnu-#{version}"
                sha256 "${{ steps.sha.outputs.linux_arm }}"
              end
            end

            def install
              bin.install Dir["dcr*"].first => "dcr"
            end

            test do
              system "#{bin}/dcr", "--version"
            end
          end
          FORMULA

      - name: Commit and push
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add Formula/dcr.rb
          git diff --staged --quiet && echo "No changes" && exit 0
          git commit -m "dcr ${{ needs.setup.outputs.version }}"
          git push