Skip to content
Latchkey

Update Homebrew Tap workflow (BenedictKing/ccx)

The Update Homebrew Tap workflow from BenedictKing/ccx, explained and optimized by Latchkey.

A

CI health: A - excellent

Point runs-on at Latchkey and get job timeouts, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: BenedictKing/ccx.github/workflows/update-homebrew.ymlLicense MITView source

What it does

This is the Update Homebrew Tap workflow from the BenedictKing/ccx repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
name: Update Homebrew Tap

permissions:
  contents: read

on:
  release:
    types: [published]
  workflow_dispatch:
    inputs:
      tag:
        description: "Release tag to publish to Homebrew, for example v2.8.17"
        required: true
        type: string

jobs:
  update-homebrew:
    runs-on: ubuntu-latest
    steps:
      - name: Get release metadata
        id: release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          EVENT_NAME: ${{ github.event_name }}
          RELEASE_TAG: ${{ github.event.release.tag_name }}
          INPUT_TAG: ${{ inputs.tag }}
        run: |
          if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
            TAG="$INPUT_TAG"
          else
            TAG="$RELEASE_TAG"
          fi

          if [ -z "$TAG" ]; then
            echo "Error: release tag is empty" >&2
            exit 1
          fi

          API="https://api.github.com/repos/${{ github.repository }}/releases/tags/${TAG}"
          RELEASE_JSON=$(curl -sL -H "Authorization: token ${GH_TOKEN}" "$API")
          RELEASE_TAG_FOUND=$(echo "$RELEASE_JSON" | jq -r '.tag_name')
          if [ "$RELEASE_TAG_FOUND" = "null" ] || [ -z "$RELEASE_TAG_FOUND" ]; then
            echo "Error: release ${TAG} not found" >&2
            exit 1
          fi

          if [ "$(echo "$RELEASE_JSON" | jq -r '.draft')" != "false" ]; then
            echo "Error: release ${TAG} is still a draft" >&2
            exit 1
          fi

          if [ "$(echo "$RELEASE_JSON" | jq -r '.prerelease')" = "true" ]; then
            echo "Skipping Homebrew update for prerelease ${TAG}"
            echo "skip=true" >> $GITHUB_OUTPUT
            exit 0
          fi

          if ! echo "$TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
            echo "Error: release tag must look like vX.Y.Z, got: ${TAG}" >&2
            exit 1
          fi

          VERSION="${TAG#v}"

          echo "skip=false" >> $GITHUB_OUTPUT
          echo "tag=$TAG" >> $GITHUB_OUTPUT
          echo "version=$VERSION" >> $GITHUB_OUTPUT
          echo "api=$API" >> $GITHUB_OUTPUT

      - name: Download DMG SHA256 assets
        id: sha
        if: steps.release.outputs.skip != 'true'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          API: ${{ steps.release.outputs.api }}
          VERSION: ${{ steps.release.outputs.version }}
        run: |
          for ARCH in arm64 amd64; do
            ASSET_NAME="CCX-Desktop-${VERSION}-darwin-${ARCH}.dmg.sha256"
            DOWNLOAD_URL=$(curl -sL -H "Authorization: token ${GH_TOKEN}" "$API" \
              | jq -r --arg name "$ASSET_NAME" '.assets[] | select(.name == $name) | .browser_download_url')
            if [ -z "$DOWNLOAD_URL" ] || [ "$DOWNLOAD_URL" = "null" ]; then
              echo "Error: asset ${ASSET_NAME} not found" >&2
              exit 1
            fi

            SHA=$(curl -sL -H "Authorization: token ${GH_TOKEN}" "$DOWNLOAD_URL" | cut -d' ' -f1)
            if ! echo "$SHA" | grep -Eq '^[a-f0-9]{64}$'; then
              echo "Error: invalid SHA256 for ${ASSET_NAME}: ${SHA}" >&2
              exit 1
            fi

            case "$ARCH" in
              arm64) OUTPUT_NAME="sha_arm" ;;
              amd64) OUTPUT_NAME="sha_intel" ;;
            esac
            echo "${OUTPUT_NAME}=${SHA}" >> $GITHUB_OUTPUT
            echo "Downloaded SHA256 for ${ARCH}: ${SHA}"
          done

      - name: Trigger Homebrew tap update
        if: steps.release.outputs.skip != 'true'
        env:
          VERSION: ${{ steps.release.outputs.version }}
          SHA_ARM: ${{ steps.sha.outputs.sha_arm }}
          SHA_INTEL: ${{ steps.sha.outputs.sha_intel }}
          TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
        run: |
          if [ -z "$TAP_TOKEN" ]; then
            echo "Error: HOMEBREW_TAP_TOKEN is not configured" >&2
            exit 1
          fi

          curl --fail-with-body -X POST \
            -H "Authorization: token ${TAP_TOKEN}" \
            -H "Accept: application/vnd.github.v3+json" \
            "https://api.github.com/repos/BenedictKing/homebrew-ccx/dispatches" \
            -d "{
              \"event_type\": \"update-cask\",
              \"client_payload\": {
                \"version\": \"${VERSION}\",
                \"sha_arm\": \"${SHA_ARM}\",
                \"sha_intel\": \"${SHA_INTEL}\"
              }
            }"
          echo "Dispatched update-cask to homebrew-ccx: v${VERSION}"

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

name: Update Homebrew Tap
 
permissions:
  contents: read
 
on:
  release:
    types: [published]
  workflow_dispatch:
    inputs:
      tag:
        description: "Release tag to publish to Homebrew, for example v2.8.17"
        required: true
        type: string
 
jobs:
  update-homebrew:
    timeout-minutes: 30
    runs-on: latchkey-small
    steps:
      - name: Get release metadata
        id: release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          EVENT_NAME: ${{ github.event_name }}
          RELEASE_TAG: ${{ github.event.release.tag_name }}
          INPUT_TAG: ${{ inputs.tag }}
        run: |
          if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
            TAG="$INPUT_TAG"
          else
            TAG="$RELEASE_TAG"
          fi
 
          if [ -z "$TAG" ]; then
            echo "Error: release tag is empty" >&2
            exit 1
          fi
 
          API="https://api.github.com/repos/${{ github.repository }}/releases/tags/${TAG}"
          RELEASE_JSON=$(curl -sL -H "Authorization: token ${GH_TOKEN}" "$API")
          RELEASE_TAG_FOUND=$(echo "$RELEASE_JSON" | jq -r '.tag_name')
          if [ "$RELEASE_TAG_FOUND" = "null" ] || [ -z "$RELEASE_TAG_FOUND" ]; then
            echo "Error: release ${TAG} not found" >&2
            exit 1
          fi
 
          if [ "$(echo "$RELEASE_JSON" | jq -r '.draft')" != "false" ]; then
            echo "Error: release ${TAG} is still a draft" >&2
            exit 1
          fi
 
          if [ "$(echo "$RELEASE_JSON" | jq -r '.prerelease')" = "true" ]; then
            echo "Skipping Homebrew update for prerelease ${TAG}"
            echo "skip=true" >> $GITHUB_OUTPUT
            exit 0
          fi
 
          if ! echo "$TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
            echo "Error: release tag must look like vX.Y.Z, got: ${TAG}" >&2
            exit 1
          fi
 
          VERSION="${TAG#v}"
 
          echo "skip=false" >> $GITHUB_OUTPUT
          echo "tag=$TAG" >> $GITHUB_OUTPUT
          echo "version=$VERSION" >> $GITHUB_OUTPUT
          echo "api=$API" >> $GITHUB_OUTPUT
 
      - name: Download DMG SHA256 assets
        id: sha
        if: steps.release.outputs.skip != 'true'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          API: ${{ steps.release.outputs.api }}
          VERSION: ${{ steps.release.outputs.version }}
        run: |
          for ARCH in arm64 amd64; do
            ASSET_NAME="CCX-Desktop-${VERSION}-darwin-${ARCH}.dmg.sha256"
            DOWNLOAD_URL=$(curl -sL -H "Authorization: token ${GH_TOKEN}" "$API" \
              | jq -r --arg name "$ASSET_NAME" '.assets[] | select(.name == $name) | .browser_download_url')
            if [ -z "$DOWNLOAD_URL" ] || [ "$DOWNLOAD_URL" = "null" ]; then
              echo "Error: asset ${ASSET_NAME} not found" >&2
              exit 1
            fi
 
            SHA=$(curl -sL -H "Authorization: token ${GH_TOKEN}" "$DOWNLOAD_URL" | cut -d' ' -f1)
            if ! echo "$SHA" | grep -Eq '^[a-f0-9]{64}$'; then
              echo "Error: invalid SHA256 for ${ASSET_NAME}: ${SHA}" >&2
              exit 1
            fi
 
            case "$ARCH" in
              arm64) OUTPUT_NAME="sha_arm" ;;
              amd64) OUTPUT_NAME="sha_intel" ;;
            esac
            echo "${OUTPUT_NAME}=${SHA}" >> $GITHUB_OUTPUT
            echo "Downloaded SHA256 for ${ARCH}: ${SHA}"
          done
 
      - name: Trigger Homebrew tap update
        if: steps.release.outputs.skip != 'true'
        env:
          VERSION: ${{ steps.release.outputs.version }}
          SHA_ARM: ${{ steps.sha.outputs.sha_arm }}
          SHA_INTEL: ${{ steps.sha.outputs.sha_intel }}
          TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
        run: |
          if [ -z "$TAP_TOKEN" ]; then
            echo "Error: HOMEBREW_TAP_TOKEN is not configured" >&2
            exit 1
          fi
 
          curl --fail-with-body -X POST \
            -H "Authorization: token ${TAP_TOKEN}" \
            -H "Accept: application/vnd.github.v3+json" \
            "https://api.github.com/repos/BenedictKing/homebrew-ccx/dispatches" \
            -d "{
              \"event_type\": \"update-cask\",
              \"client_payload\": {
                \"version\": \"${VERSION}\",
                \"sha_arm\": \"${SHA_ARM}\",
                \"sha_intel\": \"${SHA_INTEL}\"
              }
            }"
          echo "Dispatched update-cask to homebrew-ccx: v${VERSION}"
 

What changed

What Latchkey heals here

This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:

This workflow runs 1 job per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.