# Automating Multi-Platform Tool Releases Using GitHub Actions

In this blog, we will explore how to leverage GitHub Actions for building and releasing tools written in languages such as Go, Python, Rust, and more and this will help you master the process of creating your tools binary releases that meet multi-platform needs.

---

## Prerequisites

Before starting, let’s get ready with the basic setup:

1. A GitHub repository with Python project (e.g., a tool or CLI app).
    
2. Installed Git and set up a local development environment.
    
3. A basic understanding of GitHub Actions YAML syntax.
    

---

## Setting Up Our Python Script

Let’s try to create a simple Python script named [`hello.py`](http://hello.py). This will be our example application:

```python
# hello.py
def main():
    print("Hello, GitHub Actions!")

if __name__ == "__main__":
    main()
```

Our sample application code is ready, Now, let’s push this to our GitHub repository.

---

## Writing the GitHub Actions Workflow 🔧

Let’s create a directory `.github/workflows` in the root of our repository and add a file named `release.yml`. This workflow is triggered whenever we push a new version tag, and it automates the build and release process.

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Likewise, GitLab uses the <code>.gitlab-ci.yml</code> file for CI/CD configurations and Bitbucket uses the <code>bitbucket-pipelines.yml</code> file, both typically placed in the root directory of the repository.</div>
</div>

Here’s the complete `release.yml` workflow:

```yaml
name: Release

on:
  push:
    tags:
      - 'v*'  # Trigger release when a new tag is pushed

jobs:
  release:
    name: Release - ${{ matrix.platform.release_for }}
    runs-on: ${{ matrix.platform.os }}
    strategy:
      matrix:
        platform:
          - release_for: Linux-x86_64
            os: ubuntu-latest
            pyinstaller_target: linux
            bin: hello
            name: hello-linux-amd64
          
          - release_for: Windows-x86_64
            os: windows-latest
            pyinstaller_target: windows
            bin: hello.exe
            name: hello-windows-amd64.exe
          
          - release_for: macOS-x86_64
            os: macos-latest
            pyinstaller_target: macos
            bin: hello
            name: hello-macos-amd64

    steps:
      - uses: actions/checkout@v2  # Checkout code

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.9'  # Set up the Python environment

      - name: Install PyInstaller
        run: |
          python -m pip install --upgrade pip
          pip install pyinstaller  # Install PyInstaller for packaging

      - name: Build Executable
        run: |
          # Build the executable with PyInstaller for each platform (adjusting accordingly)
          pyinstaller --onefile hello.py
        working-directory: .

      - name: Prepare assets
        shell: bash
        run: |
          # Prepare the release folder
          mkdir -p release
          
          # Move the binary from PyInstaller dist directory to release folder
          cp dist/hello* release/${{ matrix.platform.name }}

      - name: Upload binaries to GitHub release
        uses: svenstaro/upload-release-action@v2
        with:
          repo_token: ${{ secrets.GITHUB_TOKEN }}
          file: release/${{ matrix.platform.name }}
          asset_name: ${{ matrix.platform.name }}
          tag: ${{ github.ref }}
          overwrite: true
```

## Create and Push a GitHub Release Tag

For this workflow to trigger, we must create a version tag in this repository. Here’s how to do it via the CLI:

1. Commit all changes:
    
    ```bash
    git add .
    git commit -m "Add release workflow and script"
    ```
    
2. Create a new version tag:
    
    ```bash
    git tag -a v1.0.0 -m "First release with GitHub Actions"
    ```
    
3. Push the tag to trigger the workflow:
    
    ```bash
    git push origin --tags
    ```
    
4. This will start the release workflow in GitHub Actions. we can monitor the progress under the "Actions" tab of our repository.
    

---

## Now, Let’s understand the workflow 🔍

### What Does Each Step Do?

1. **Trigger on Tags**  
    The workflow is triggered when a tag matching the pattern `v*` is pushed. This ensures it only runs for versioned releases, like in our case we triggered with `git tag` and pushed the code in repository with tags.
    
    ```yaml
    on:
      push:
        tags:
          - 'v*'  # Trigger release when a new tag is pushed
    ```
    
2. **Matrix for Multi-Platform Builds**  
    Using `matrix.platform`, the workflow specifies OS and configuration settings for Linux, macOS, and Windows. For more details about matrix uses, there is an awesome guide from [Github](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow)
    
3. **Build with PyInstaller**  
    PyInstaller creates a single executable binary (`--onefile` option) tailored for each platform.
    
4. **Package Artifacts for Release**  
    The prepared binaries are moved to a `release/` directory with platform-specific names.
    
5. **Upload Release Assets**  
    The `upload-release-action` pushes the compiled binaries to the GitHub release, making them available for download.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1734485819570/44c646e2-436c-4286-b4ed-2745858010f9.png align="center")
    

---

After the workflow completes:

1. Navigate to the **Releases** section of the repository.
    
2. Find the latest release created by the workflow.
    
3. Download the platform-specific binary from the release assets.
    

---

## Wrapping Up 🎉

In this article, we demonstrated how to create platform-specific Python executables and automate their release using GitHub Actions. The example `release.yml` workflow builds, packages, and uploads binaries to GitHub for Windows, macOS, and Linux users seamlessly.

The same strategies in matrix platform can be used for the other language releases as well in order to have the platform specific binaries for your tools. GitHub Actions empowers developers to automate tedious tasks and streamline delivery pipelines, leaving more time for building features and solving user problems. Give it a try and let your tools shine across platforms!

Have feedback or questions? Drop them in the comments!
