October 21, 2024

Releasing on GitHub Actions

I wanted to make a "CLI Tool" for orchestrating a "Website" deployment to a web host and I wanted to build and use this CLI tool in GitHub actions. GitHub has a ton of flexibility and features that should make both publishing the tool and deploying the website straightforward. This is the process I found that works for me.

Goals

  • Manually initiate a production deploy of my website from main or a specific commit
  • Tag the deploy with an auto incrementing version number
  • Create a release with a change log
  • Build and host the CLI on the cli's repo
  • Download and use this cli on the website's repo

CLI Tool

The tool lets me deploy to a specific Render.com service. I've called it render-deploy. I made it for fun. I need to build and host the cli and make it available to the other repository.

My first thought was a workflow artifact might be easiest to use, however these wont work for us;

  1. The artifacts only last a small number of days
  2. It's difficult to determine the latest build to download the files elsewhere

The next place you can attach files is in a github release.

Releases are deployable software iterations you can package and make available for a wider audience to download and use.

Which is perfect. You can create a release from a tag, attach files and provide change logs. Perfect. GitHub Actions has no release support built in and doesn't currently have an action for it. Currently ncipollo/release-action seems to be the best supported and active action to create releases. If you give it a tag it will do the rest.

In order to create a tag I found mathieudutour/github-tag-action which borrows from @semantic-release/commit-analyzer to manage your versions based on the commit messages. When run it will pull tags, analyze your commit messages and bump the version a patch, minor or major version. It can also generate a change log and even recommends ncipollo's release-action.

The commit analyzer looks for angular's commit message format which is basically type(scope): subject. And you add BREAKING CHANGE: two lines down if there is a breaking change. It's straightforward.

I landed on a workflow that builds and releases the main branch, and bumps the tags as necessary.

name: Build and Release

on:
  push:
    branches: [ main ]
jobs:
  build:
    permissions: write-all
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Run tests
      run: cargo test --verbose
    - name: Build
      run: cargo build --release
    - name: 'Tar files'
      run: tar -cvzf render-deploy-linux.tar.gz -C target/release render-deploy
    - name: Upload artifact
      uses: actions/upload-artifact@v4
      with:
        if-no-files-found: error
        path: ./render-deploy-linux.tar.gz
        name: render-deploy-linux.tar.gz
    - name: Bump version and push tag
      id: tag_version
      uses: mathieudutour/github-tag-action@v6.2
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        fetch_all_tags: true
    - name: Create a GitHub release
      uses: ncipollo/release-action@v1
      with:
        tag: ${{ steps.tag_version.outputs.new_tag }}
        name: Release ${{ steps.tag_version.outputs.new_tag }}
        body: ${{ steps.tag_version.outputs.changelog }}
        artifacts: ./render-deploy-linux.tar.gz

Every commit will be published as a new version which is fine for this use case.

Website

For the website I need to download the latest build of the cli tool, cut a release and then deploy.

The workflow_dispatch trigger allows you to manually trigger workflows on a branch or commit. I used a "concurrency group" to ensure deploys happen in order and don't clobber eachother.

I found robinraju/release-downloader which does what it says on the tin. I pointed it at the latest release on my CLI's repo and it downloaded the file by name. I had trouble with it's built in untaring but I bet with experimentation I could have gotten it to work.

mathieudutour/github-tag-action was used again for tag management and ncipollo/release-action was used again to create the release. The rest is fairly straightforward.

name: Production Deploy
run-name: Production Deploy
on:
  workflow_dispatch:
concurrency:
  group: deploy-production
  cancel-in-progress: false
jobs:
  deploy-production:
    runs-on: ubuntu-latest
    environment: Production
    permissions: write-all
    steps:
      - name: Download Deploy Tool
        uses: robinraju/release-downloader@v1
        with:
          repository: reconbot/render-deploy
          latest: true
          fileName: render-deploy-linux.tar.gz
      - name: install deploy tool
        run: tar -xvzf render-deploy-linux.tar.gz
      - name: Bump version and push tag
        id: tag_version
        uses: mathieudutour/github-tag-action@v6.2
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          fetch_all_tags: true
      - name: Create a GitHub release
        uses: ncipollo/release-action@v1
        with:
          tag: ${{ steps.tag_version.outputs.new_tag }}
          name: Release ${{ steps.tag_version.outputs.new_tag }}
          body: ${{ steps.tag_version.outputs.changelog }}
      - name: Deploy Web
        run: ./render-deploy -w production-remix-web
        env:
          RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }}
      - name: Deploy Worker
        run: ./render-deploy -w production-remix-worker
        env:
          RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }}

The only downside I've found is I don't always use angular's conventional commit messages for version control. And if you don't, you don't get any change log. I'm sure there's a way to list commits instead. Additionally I see that my use of Environments for secrets automatically creates Deployments. I'm not familiar with these features but it seems to provide a similar experience to releases but probably requires more configuration on my part.

Roborooter.com © 2024
Powered by ⚡️ and 🤖.