stout 0.2.1

A fast, Rust-based Homebrew-compatible package manager
name: Release

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: write

env:
  CARGO_TERM_COLOR: always

jobs:
  build:
    name: Build (${{ matrix.target }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            artifact: stout-x86_64-unknown-linux-gnu
          - os: ubuntu-latest
            target: x86_64-unknown-linux-musl
            artifact: stout-x86_64-unknown-linux-musl
          - os: ubuntu-24.04-arm
            target: aarch64-unknown-linux-gnu
            artifact: stout-aarch64-unknown-linux-gnu
          - os: macos-latest
            target: aarch64-apple-darwin
            artifact: stout-aarch64-apple-darwin
          - os: macos-13
            target: x86_64-apple-darwin
            artifact: stout-x86_64-apple-darwin

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

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

      - name: Install musl tools
        if: matrix.target == 'x86_64-unknown-linux-musl'
        run: |
          sudo apt-get update
          sudo apt-get install -y musl-tools

      - name: Build
        env:
          STOUT_GEN_MAN: "1"
        run: cargo build --release --target ${{ matrix.target }}

      - name: Generate completions
        shell: bash
        run: |
          mkdir -p target/completions
          ./target/${{ matrix.target }}/release/stout completions bash > target/completions/stout.bash
          ./target/${{ matrix.target }}/release/stout completions zsh > target/completions/_stout
          ./target/${{ matrix.target }}/release/stout completions fish > target/completions/stout.fish

      - name: Package
        shell: bash
        run: |
          mkdir -p dist
          cp target/${{ matrix.target }}/release/stout dist/
          cd dist
          tar -czvf ../${{ matrix.artifact }}.tar.gz stout
          cd ..
          if command -v shasum &> /dev/null; then
            shasum -a 256 ${{ matrix.artifact }}.tar.gz > ${{ matrix.artifact }}.tar.gz.sha256
          else
            sha256sum ${{ matrix.artifact }}.tar.gz > ${{ matrix.artifact }}.tar.gz.sha256
          fi

      - name: Upload artifact
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: ${{ matrix.artifact }}
          path: |
            ${{ matrix.artifact }}.tar.gz
            ${{ matrix.artifact }}.tar.gz.sha256

  # Build Debian packages (.deb)
  build-deb:
    name: Build Debian Package (${{ matrix.arch }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            arch: amd64
          - os: ubuntu-24.04-arm
            target: aarch64-unknown-linux-gnu
            arch: arm64

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

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

      - name: Install cargo-deb
        run: cargo install cargo-deb

      - name: Build release binary
        env:
          STOUT_GEN_MAN: "1"
        run: cargo build --release --target ${{ matrix.target }}

      - name: Generate completions
        run: |
          mkdir -p target/completions
          ./target/${{ matrix.target }}/release/stout completions bash > target/completions/stout.bash
          ./target/${{ matrix.target }}/release/stout completions zsh > target/completions/_stout
          ./target/${{ matrix.target }}/release/stout completions fish > target/completions/stout.fish

      - name: Build .deb package
        run: cargo deb --target ${{ matrix.target }} --no-build

      - name: Rename .deb with architecture
        run: |
          DEB_FILE=$(find target/${{ matrix.target }}/debian -name "*.deb" | head -1)
          VERSION=${GITHUB_REF#refs/tags/v}
          NEW_NAME="stout_${VERSION}_${{ matrix.arch }}.deb"
          cp "$DEB_FILE" "$NEW_NAME"
          sha256sum "$NEW_NAME" > "$NEW_NAME.sha256"

      - name: Upload .deb artifact
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: deb-${{ matrix.arch }}
          path: |
            *.deb
            *.deb.sha256

  # Build RPM packages (.rpm)
  build-rpm:
    name: Build RPM Package (${{ matrix.arch }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            arch: x86_64
          - os: ubuntu-24.04-arm
            target: aarch64-unknown-linux-gnu
            arch: aarch64

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

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

      - name: Install cargo-generate-rpm
        run: cargo install cargo-generate-rpm

      - name: Build release binary
        env:
          STOUT_GEN_MAN: "1"
        run: cargo build --release --target ${{ matrix.target }}

      - name: Generate completions
        run: |
          mkdir -p target/completions
          ./target/${{ matrix.target }}/release/stout completions bash > target/completions/stout.bash
          ./target/${{ matrix.target }}/release/stout completions zsh > target/completions/_stout
          ./target/${{ matrix.target }}/release/stout completions fish > target/completions/stout.fish

      - name: Build .rpm package
        run: cargo generate-rpm --target ${{ matrix.target }}

      - name: Rename .rpm with architecture
        run: |
          RPM_FILE=$(find target/${{ matrix.target }}/generate-rpm -name "*.rpm" | head -1)
          VERSION=${GITHUB_REF#refs/tags/v}
          NEW_NAME="stout-${VERSION}-1.${{ matrix.arch }}.rpm"
          cp "$RPM_FILE" "$NEW_NAME"
          sha256sum "$NEW_NAME" > "$NEW_NAME.sha256"

      - name: Upload .rpm artifact
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: rpm-${{ matrix.arch }}
          path: |
            *.rpm
            *.rpm.sha256

  release:
    name: Create Release
    needs: [build, build-deb, build-rpm]
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Download artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          path: artifacts

      - name: Collect release files
        run: |
          mkdir release
          find artifacts -type f \( -name "*.tar.gz" -o -name "*.sha256" -o -name "*.deb" -o -name "*.rpm" \) -exec mv {} release/ \;
          # Also include the install script
          cp install.sh release/
          ls -la release/

      - name: Create Release
        uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
        with:
          files: release/*
          generate_release_notes: true
          draft: false
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  publish-crates:
    name: Publish to crates.io
    needs: release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

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

      - name: Publish crates
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          # Publish crates in dependency order
          # Wait between publishes to allow crates.io to index

          echo "Publishing stout-index..."
          cargo publish -p stout-index --allow-dirty || echo "stout-index may already be published"
          sleep 30

          echo "Publishing stout-state..."
          cargo publish -p stout-state --allow-dirty || echo "stout-state may already be published"
          sleep 30

          echo "Publishing stout-resolve..."
          cargo publish -p stout-resolve --allow-dirty || echo "stout-resolve may already be published"
          sleep 30

          echo "Publishing stout-fetch..."
          cargo publish -p stout-fetch --allow-dirty || echo "stout-fetch may already be published"
          sleep 30

          echo "Publishing stout-install..."
          cargo publish -p stout-install --allow-dirty || echo "stout-install may already be published"
          sleep 30

          echo "Publishing stout-cask..."
          cargo publish -p stout-cask --allow-dirty || echo "stout-cask may already be published"
          sleep 30

          echo "Publishing stout-bundle..."
          cargo publish -p stout-bundle --allow-dirty || echo "stout-bundle may already be published"
          sleep 30

          echo "Publishing stout-audit..."
          cargo publish -p stout-audit --allow-dirty || echo "stout-audit may already be published"
          sleep 30

          echo "Publishing stout-mirror..."
          cargo publish -p stout-mirror --allow-dirty || echo "stout-mirror may already be published"
          sleep 30

          echo "Publishing stout..."
          cargo publish --allow-dirty || echo "stout may already be published"

  # Publish to npm
  publish-npm:
    name: Publish to npm
    needs: release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Setup Node.js
        uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org'

      - name: Update package version
        working-directory: packaging/npm
        run: |
          VERSION=${GITHUB_REF#refs/tags/v}
          npm version $VERSION --no-git-tag-version --allow-same-version

      - name: Copy LICENSE and README
        working-directory: packaging/npm
        run: |
          cp ../../LICENSE .
          # README already exists in npm package

      - name: Publish to npm
        working-directory: packaging/npm
        run: npm publish --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

  # Publish to PyPI
  publish-pypi:
    name: Publish to PyPI
    needs: release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Setup Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: '3.11'

      - name: Install build tools
        run: pip install build twine

      - name: Update package version
        working-directory: packaging/pypi
        run: |
          VERSION=${GITHUB_REF#refs/tags/v}
          sed -i "s/^version = .*/version = \"$VERSION\"/" pyproject.toml
          sed -i "s/^__version__ = .*/__version__ = \"$VERSION\"/" src/stout_pkg/__init__.py

      - name: Copy LICENSE
        working-directory: packaging/pypi
        run: cp ../../LICENSE .

      - name: Build package
        working-directory: packaging/pypi
        run: python -m build

      - name: Publish to PyPI
        working-directory: packaging/pypi
        run: twine upload dist/*
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}

  # Publish to RubyGems
  publish-gem:
    name: Publish to RubyGems
    needs: release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Setup Ruby
        uses: ruby/setup-ruby@319994f95fa847cf3fb3cd3dbe89f6dcde9f178f # v1.295.0
        with:
          ruby-version: '3.2'

      - name: Update gem version
        working-directory: packaging/gem
        run: |
          VERSION=${GITHUB_REF#refs/tags/v}
          sed -i "s/spec.version.*/spec.version       = \"$VERSION\"/" stout-pkg.gemspec
          sed -i "s/VERSION = .*/VERSION = \"$VERSION\"/" lib/stout_pkg/version.rb

      - name: Copy LICENSE
        working-directory: packaging/gem
        run: cp ../../LICENSE .

      - name: Build gem
        working-directory: packaging/gem
        run: gem build stout-pkg.gemspec

      - name: Publish to RubyGems
        working-directory: packaging/gem
        run: |
          mkdir -p ~/.gem
          echo -e "---\n:rubygems_api_key: ${{ secrets.RUBYGEMS_API_KEY }}" > ~/.gem/credentials
          chmod 0600 ~/.gem/credentials
          gem push stout-pkg-*.gem