Skip to main content

Command Palette

Search for a command to run...

Automating Multi-Platform Tool Releases Using GitHub Actions

Releasing software for multiple platforms can be challenging without automation. With GitHub Actions, we can streamline this process.

Updated
4 min read
Automating Multi-Platform Tool Releases Using GitHub Actions
H

Specialized in uncovering vulnerabilities within software supply chains and dependency ecosystems. Creator of SCAGoat and other open-source security tools. Speaker at Black Hat, DEF CON, and AppSec conferences with research on malicious package detection, dependency confusion, and CI/CD security.

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. This will be our example application:

# 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.

💡
Likewise, GitLab uses the .gitlab-ci.yml file for CI/CD configurations and Bitbucket uses the bitbucket-pipelines.yml file, both typically placed in the root directory of the repository.

Here’s the complete release.yml workflow:

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:

     git add .
     git commit -m "Add release workflow and script"
    
  2. Create a new version tag:

     git tag -a v1.0.0 -m "First release with GitHub Actions"
    
  3. Push the tag to trigger the workflow:

     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.

     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

  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.


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!

More from this blog

B

blog.harekrishnarai.me

17 posts

Exploring supply chain security, SCA tools, open-source risks, and real-world case studies to build safer software ecosystems