GitHub Workflows – Rust

So this first version, (helped by Claude) persistently did the build but failed to do the release…..

permissions:
  contents: write
  actions: write

name: Build and Release Rust Binary

on:
  push:
    branches: [main]
    tags:
      - 'v*'
  pull_request:
    branches: [main]

jobs:
  build:
    name: Build on ${{ matrix.os }}
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            artifact_name: rmcp_nettools
            archive_name: rmcp_nettools.linux.tar.gz
          - os: windows-latest
            artifact_name: rmcp_nettools.exe
            archive_name: rmcp_nettools.windows.zip
          - os: macos-latest
            artifact_name: rmcp_nettools
            archive_name: rmcp_nettools.macos.tar.gz

    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v3

      - name: Install Rust
        uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable
          override: true

      - name: Build
        uses: actions-rs/cargo@v1
        with:
          command: build
          args: --release

      - name: List target directory (debug)
        run: ls -lh target/release
        shell: bash

      - name: Compress Binary (Unix)
        if: runner.os != 'Windows'
        run: |
          cd target/release
          tar -czvf "${{ matrix.archive_name }}" "${{ matrix.artifact_name }}"

      - name: Compress Binary (Windows)
        if: runner.os == 'Windows'
        run: |
          Compress-Archive -Path "target/release/${{ matrix.artifact_name }}" -DestinationPath "target/release/${{ matrix.archive_name }}"
        shell: pwsh

      - name: Upload Build Artifact
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.archive_name }}
          path: target/release/${{ matrix.archive_name }}

  release:
    name: Create and Upload Release
    needs: [build]
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest

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

      - name: Display downloaded files
        run: ls -R artifacts

      - name: Create Release
        id: create_release
        uses: softprops/action-gh-release@v1
        with:
          files: |
            artifacts/rmcp_nettools.linux.tar.gz/rmcp_nettools.linux.tar.gz
            artifacts/rmcp_nettools.windows.zip/rmcp_nettools.windows.zip
            artifacts/rmcp_nettools.macos.tar.gz/rmcp_nettools.macos.tar.gz
          tag_name: ${{ github.ref_name }}
          name: Release ${{ github.ref_name }}
          draft: false
          prerelease: false
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Then, after some more experimentation:

permissions:
  contents: write
  actions: write
name: Build and Release Rust Binary
on:
  push:
    branches: [main]
    tags:
      - 'v*'
  pull_request:
    branches: [main]
jobs:
  build:
    name: Build on ${{ matrix.os }}
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            artifact_name: mcp-netval
            archive_name: mcp-netval.linux.tar.gz
          - os: windows-latest
            artifact_name: mcp-netval.exe
            archive_name: mcp-netval.windows.zip
          - os: macos-latest
            artifact_name: mcp-netval
            archive_name: mcp-netval.macos.tar.gz
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v3
      - name: Install Rust
        uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable
          override: true
      - name: Build
        uses: actions-rs/cargo@v1
        with:
          command: build
          args: --release
      - name: List target directory (debug)
        run: ls -lh target/release
        shell: bash
      - name: Compress Binary (Unix)
        if: runner.os != 'Windows'
        run: |
          cd target/release
          tar -czvf "${{ matrix.archive_name }}" "${{ matrix.artifact_name }}"
      - name: Compress Binary (Windows)
        if: runner.os == 'Windows'
        run: |
          Compress-Archive -Path "target/release/${{ matrix.artifact_name }}" -DestinationPath "target/release/${{ matrix.archive_name }}"
        shell: pwsh
      - name: Upload Build Artifact
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.archive_name }}
          path: target/release/${{ matrix.archive_name }}
  release:
    name: Create and Upload Release
    needs: [build]
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Configure Git
        run: |
          git config user.name github-actions
          git config user.email github-actions@github.com
      - name: Generate tag
        id: tag
        run: |
          TAG_NAME="v$(date +'%Y%m%d%H%M%S')"
          echo "TAG_NAME=$TAG_NAME" >> $GITHUB_OUTPUT
          git tag $TAG_NAME
          git push origin $TAG_NAME
      - name: Download all workflow artifacts
        uses: actions/download-artifact@v4
        with:
          path: artifacts
      - name: Display downloaded files
        run: ls -R artifacts
      - name: Prepare release files
        run: |
          mkdir -p release_files
          find artifacts -type f -exec cp {} release_files/ \;
      - name: Create Release
        uses: softprops/action-gh-release@v1
        with:
          files: release_files/*
          tag_name: ${{ steps.tag.outputs.TAG_NAME }}
          name: Release ${{ steps.tag.outputs.TAG_NAME }}
          draft: false
          prerelease: false
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

This complete workflow solution:

  1. Builds your Rust binary across Linux, Windows, and macOS
  2. Automatically creates a new tag with timestamp whenever code is pushed to the main branch
  3. Creates a GitHub release using that tag
  4. Uploads all built binaries to the release

Key changes I made:

  • Changed the release job condition to run on main branch pushes: if: github.ref == 'refs/heads/main'
  • Added Git configuration for the automated commits
  • Added a step to generate a unique tag with timestamp
  • Simplified the file preparation with a find command that will handle nested directory structures
  • Made sure the tag is both created locally and pushed to the remote repository

This approach eliminates the need to manually create tags. Now every push to your main branch will automatically create a new release with all your binaries attached.