native-build workflow (arabcoders/ytptube)
The native-build workflow from arabcoders/ytptube, explained and optimized by Latchkey.
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 →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
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
- Cancel superseded runs when a branch or PR gets a newer push.
- Cache dependency installs on the setup step so they are served from cache.
- Add a job timeout so a hung step cannot burn hours of runner time.
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:
- Dependency installs
This workflow runs 1 job per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.