Skip to content
Latchkey

native-build workflow (arabcoders/ytptube)

The native-build workflow from arabcoders/ytptube, explained and optimized by Latchkey.

F

CI health: F - at risk

Run this on Latchkey for self-healing, caching, and up to 58% lower cost.

Grade your own workflow free or run it on Latchkey →
Source: arabcoders/ytptube.github/workflows/native-build.ymlLicense MITView source

What it does

This is the native-build workflow from the arabcoders/ytptube 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: native-build

on:
  workflow_dispatch:
    inputs:
      tag:
        required: true
        description: "Ref to build from (e.g. v1.0.0)"
      useWorkflowSpec:
        type: boolean
        default: false
        description: "Use app.spec from workflow branch instead of tag"
  push:
    tags:
      - "v*"

jobs:
  build:
    if: ${{ vars.REGISTRY == '' }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            arch: amd64
          - os: ubuntu-latest
            arch: arm64
          - os: macos-latest
            arch: arm64
          - os: windows-latest
            arch: amd64
          - os: windows-latest
            arch: arm64

    permissions:
      packages: write
      contents: write

    env:
      PYTHON_VERSION: "3.13"
      BUN_VERSION: latest
      NODE_VERSION: 22
      TAG_NAME: ${{ github.event.inputs.tag || github.ref_name }}

    steps:
      - name: Checkout source repo
        uses: actions/checkout@v4
        with:
          ref: ${{ env.TAG_NAME }}

      - name: Overwrite app.spec from workflow branch
        if: ${{ github.event.inputs.useWorkflowSpec == 'true' }}
        uses: actions/checkout@v4
        with:
          ref: ${{ github.ref }}
          sparse-checkout: |
            app.spec
          sparse-checkout-cone-mode: false
          path: temp-spec

      - name: Replace app.spec with version from workflow branch
        if: ${{ github.event.inputs.useWorkflowSpec == 'true' }}
        shell: bash
        run: cp temp-spec/app.spec ./app.spec

      - name: Update app/library/version.py
        shell: bash
        run: |
          VERSION="${TAG_NAME}"
          SHA=$(git rev-parse HEAD)
          DATE=$(date -u +"%Y%m%d")
          BRANCH=$(git branch --show-current)

          if [ -z "${BRANCH}" ]; then
            BRANCH="${TAG_NAME}"
          fi

          BRANCH=$(echo "${BRANCH}" | sed 's#/#-#g')

          echo "APP_VERSION=${VERSION}" >> "$GITHUB_ENV"
          echo "APP_SHA=${SHA}" >> "$GITHUB_ENV"
          echo "APP_DATE=${DATE}" >> "$GITHUB_ENV"
          echo "APP_BRANCH=${BRANCH}" >> "$GITHUB_ENV"

          sed -i.bak \
            -e "s/^APP_VERSION = \".*\"/APP_VERSION = \"${VERSION}\"/" \
            -e "s/^APP_COMMIT_SHA = \".*\"/APP_COMMIT_SHA = \"${SHA}\"/" \
            -e "s/^APP_BUILD_DATE = \".*\"/APP_BUILD_DATE = \"${DATE}\"/" \
            -e "s/^APP_BRANCH = \".*\"/APP_BRANCH = \"${BRANCH}\"/" \
            app/library/version.py

          rm -f app/library/version.py.bak

          echo "Updated version info:"
          cat app/library/version.py

      - name: Cache Python venv
        id: cache-python
        uses: actions/cache@v4
        with:
          path: .venv
          key: uv-${{ runner.os }}-${{ matrix.arch }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('uv.lock') }}
          restore-keys: |
            uv-${{ runner.os }}-${{ matrix.arch }}-${{ env.PYTHON_VERSION }}-

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          architecture: "x64"

      - name: Install Python dependencies
        shell: bash
        run: |
          pip install uv
          if [ ! -d .venv ]; then
            uv venv --system-site-packages --relocatable
          fi
          uv sync --link-mode=copy --active --extra installer

      - name: Cache bun installation
        id: cache-bun
        uses: actions/cache@v4
        with:
          path: ~/.bun/install/cache
          key: ${{ runner.os }}-${{ matrix.arch }}-bun-${{ hashFiles('**/bun.lock') }}
          restore-keys: |
            ${{ runner.os }}-${{ matrix.arch }}-bun-

      - name: Install bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: ${{ env.BUN_VERSION }}

      - name: Install Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Build frontend
        working-directory: ui
        shell: bash
        run: |
          bun install --production --prefer-offline --frozen-lockfile
          bun run generate

      - name: Clean build outputs (Unix)
        if: runner.os != 'Windows'
        shell: bash
        run: rm -rf build dist release || true

      - name: Clean build outputs (Windows)
        if: runner.os == 'Windows'
        shell: powershell
        run: |
          if (Test-Path build)   { Remove-Item -Recurse -Force build }
          if (Test-Path dist)    { Remove-Item -Recurse -Force dist }
          if (Test-Path release) { Remove-Item -Recurse -Force release }

      - name: Build native binary (Windows)
        if: matrix.os == 'windows-latest'
        shell: powershell
        run: |
          .\.venv\Scripts\activate
          uv run pyinstaller ./app.spec

      - name: Build native binary (Linux/macOS)
        if: matrix.os != 'windows-latest'
        shell: bash
        run: |
          . .venv/bin/activate
          uv run pyinstaller ./app.spec

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: app-${{ runner.os }}-${{ matrix.arch }}-${{ env.TAG_NAME }}
          path: dist/YTPTube/**

      - name: Package artifact (Unix)
        if: startsWith(env.TAG_NAME, 'v') && runner.os != 'Windows'
        shell: bash
        run: |
          mkdir -p release
          PKG_DIR="YTPTube-${{ env.TAG_NAME }}"
          ZIP_NAME="ytptube-${{ runner.os }}-${{ matrix.arch }}-${{ env.TAG_NAME }}.zip"

          if [ ! -f "dist/YTPTube/YTPTube" ]; then
            echo "Missing dist/YTPTube/YTPTube"
            find dist -maxdepth 3 -type f | head -n 200
            exit 1
          fi

          rm -rf "dist/${PKG_DIR}"
          mv "dist/YTPTube" "dist/${PKG_DIR}"

          zip -yr "release/${ZIP_NAME}" "dist/${PKG_DIR}"

      - name: Package artifact (Windows)
        if: startsWith(env.TAG_NAME, 'v') && runner.os == 'Windows'
        shell: powershell
        run: |
          $pkgDir = "YTPTube-${{ env.TAG_NAME }}"
          $zipName = "ytptube-${{ runner.os }}-${{ matrix.arch }}-${{ env.TAG_NAME }}.zip"
          New-Item -ItemType Directory -Force -Path release | Out-Null

          if (!(Test-Path "dist\YTPTube\YTPTube.exe")) {
            Write-Host "Missing dist\YTPTube\YTPTube.exe"
            Get-ChildItem -Recurse dist | Select-Object -First 200 | Format-Table -AutoSize
            exit 1
          }

          if (Test-Path "dist\$pkgDir") { Remove-Item -Recurse -Force "dist\$pkgDir" }
          Move-Item "dist\YTPTube" "dist\$pkgDir"

          Compress-Archive -Path "dist\$pkgDir" -DestinationPath "release\$zipName" -Force

      - name: Upload to GitHub Release
        if: startsWith(env.TAG_NAME, 'v')
        uses: softprops/action-gh-release@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ env.TAG_NAME }}
          files: release/*.zip

The same workflow, on Latchkey

Estimated ~20% faster on cache hits, plus fewer wasted runs and a safer supply chain. Added and changed lines are highlighted.

name: native-build
 
on:
  workflow_dispatch:
    inputs:
      tag:
        required: true
        description: "Ref to build from (e.g. v1.0.0)"
      useWorkflowSpec:
        type: boolean
        default: false
        description: "Use app.spec from workflow branch instead of tag"
  push:
    tags:
      - "v*"
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
jobs:
  build:
    timeout-minutes: 30
    if: ${{ vars.REGISTRY == '' }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            arch: amd64
          - os: ubuntu-latest
            arch: arm64
          - os: macos-latest
            arch: arm64
          - os: windows-latest
            arch: amd64
          - os: windows-latest
            arch: arm64
 
    permissions:
      packages: write
      contents: write
 
    env:
      PYTHON_VERSION: "3.13"
      BUN_VERSION: latest
      NODE_VERSION: 22
      TAG_NAME: ${{ github.event.inputs.tag || github.ref_name }}
 
    steps:
      - name: Checkout source repo
        uses: actions/checkout@v4
        with:
          ref: ${{ env.TAG_NAME }}
 
      - name: Overwrite app.spec from workflow branch
        if: ${{ github.event.inputs.useWorkflowSpec == 'true' }}
        uses: actions/checkout@v4
        with:
          ref: ${{ github.ref }}
          sparse-checkout: |
            app.spec
          sparse-checkout-cone-mode: false
          path: temp-spec
 
      - name: Replace app.spec with version from workflow branch
        if: ${{ github.event.inputs.useWorkflowSpec == 'true' }}
        shell: bash
        run: cp temp-spec/app.spec ./app.spec
 
      - name: Update app/library/version.py
        shell: bash
        run: |
          VERSION="${TAG_NAME}"
          SHA=$(git rev-parse HEAD)
          DATE=$(date -u +"%Y%m%d")
          BRANCH=$(git branch --show-current)
 
          if [ -z "${BRANCH}" ]; then
            BRANCH="${TAG_NAME}"
          fi
 
          BRANCH=$(echo "${BRANCH}" | sed 's#/#-#g')
 
          echo "APP_VERSION=${VERSION}" >> "$GITHUB_ENV"
          echo "APP_SHA=${SHA}" >> "$GITHUB_ENV"
          echo "APP_DATE=${DATE}" >> "$GITHUB_ENV"
          echo "APP_BRANCH=${BRANCH}" >> "$GITHUB_ENV"
 
          sed -i.bak \
            -e "s/^APP_VERSION = \".*\"/APP_VERSION = \"${VERSION}\"/" \
            -e "s/^APP_COMMIT_SHA = \".*\"/APP_COMMIT_SHA = \"${SHA}\"/" \
            -e "s/^APP_BUILD_DATE = \".*\"/APP_BUILD_DATE = \"${DATE}\"/" \
            -e "s/^APP_BRANCH = \".*\"/APP_BRANCH = \"${BRANCH}\"/" \
            app/library/version.py
 
          rm -f app/library/version.py.bak
 
          echo "Updated version info:"
          cat app/library/version.py
 
      - name: Cache Python venv
        id: cache-python
        uses: actions/cache@v4
        with:
          path: .venv
          key: uv-${{ runner.os }}-${{ matrix.arch }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('uv.lock') }}
          restore-keys: |
            uv-${{ runner.os }}-${{ matrix.arch }}-${{ env.PYTHON_VERSION }}-
 
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          architecture: "x64"
 
      - name: Install Python dependencies
        shell: bash
        run: |
          pip install uv
          if [ ! -d .venv ]; then
            uv venv --system-site-packages --relocatable
          fi
          uv sync --link-mode=copy --active --extra installer
 
      - name: Cache bun installation
        id: cache-bun
        uses: actions/cache@v4
        with:
          path: ~/.bun/install/cache
          key: ${{ runner.os }}-${{ matrix.arch }}-bun-${{ hashFiles('**/bun.lock') }}
          restore-keys: |
            ${{ runner.os }}-${{ matrix.arch }}-bun-
 
      - name: Install bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: ${{ env.BUN_VERSION }}
 
      - name: Install Node.js
        uses: actions/setup-node@v4
        with:
          cache: 'npm'
          node-version: ${{ env.NODE_VERSION }}
 
      - name: Build frontend
        working-directory: ui
        shell: bash
        run: |
          bun install --production --prefer-offline --frozen-lockfile
          bun run generate
 
      - name: Clean build outputs (Unix)
        if: runner.os != 'Windows'
        shell: bash
        run: rm -rf build dist release || true
 
      - name: Clean build outputs (Windows)
        if: runner.os == 'Windows'
        shell: powershell
        run: |
          if (Test-Path build)   { Remove-Item -Recurse -Force build }
          if (Test-Path dist)    { Remove-Item -Recurse -Force dist }
          if (Test-Path release) { Remove-Item -Recurse -Force release }
 
      - name: Build native binary (Windows)
        if: matrix.os == 'windows-latest'
        shell: powershell
        run: |
          .\.venv\Scripts\activate
          uv run pyinstaller ./app.spec
 
      - name: Build native binary (Linux/macOS)
        if: matrix.os != 'windows-latest'
        shell: bash
        run: |
          . .venv/bin/activate
          uv run pyinstaller ./app.spec
 
      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: app-${{ runner.os }}-${{ matrix.arch }}-${{ env.TAG_NAME }}
          path: dist/YTPTube/**
 
      - name: Package artifact (Unix)
        if: startsWith(env.TAG_NAME, 'v') && runner.os != 'Windows'
        shell: bash
        run: |
          mkdir -p release
          PKG_DIR="YTPTube-${{ env.TAG_NAME }}"
          ZIP_NAME="ytptube-${{ runner.os }}-${{ matrix.arch }}-${{ env.TAG_NAME }}.zip"
 
          if [ ! -f "dist/YTPTube/YTPTube" ]; then
            echo "Missing dist/YTPTube/YTPTube"
            find dist -maxdepth 3 -type f | head -n 200
            exit 1
          fi
 
          rm -rf "dist/${PKG_DIR}"
          mv "dist/YTPTube" "dist/${PKG_DIR}"
 
          zip -yr "release/${ZIP_NAME}" "dist/${PKG_DIR}"
 
      - name: Package artifact (Windows)
        if: startsWith(env.TAG_NAME, 'v') && runner.os == 'Windows'
        shell: powershell
        run: |
          $pkgDir = "YTPTube-${{ env.TAG_NAME }}"
          $zipName = "ytptube-${{ runner.os }}-${{ matrix.arch }}-${{ env.TAG_NAME }}.zip"
          New-Item -ItemType Directory -Force -Path release | Out-Null
 
          if (!(Test-Path "dist\YTPTube\YTPTube.exe")) {
            Write-Host "Missing dist\YTPTube\YTPTube.exe"
            Get-ChildItem -Recurse dist | Select-Object -First 200 | Format-Table -AutoSize
            exit 1
          }
 
          if (Test-Path "dist\$pkgDir") { Remove-Item -Recurse -Force "dist\$pkgDir" }
          Move-Item "dist\YTPTube" "dist\$pkgDir"
 
          Compress-Archive -Path "dist\$pkgDir" -DestinationPath "release\$zipName" -Force
 
      - name: Upload to GitHub Release
        if: startsWith(env.TAG_NAME, 'v')
        uses: softprops/action-gh-release@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ env.TAG_NAME }}
          files: release/*.zip
 

What changed

2 third-party actions are referenced by a movable tag. Pin them to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.

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.

Actions used in this workflow