Build workflow (Syncplay/syncplay)
The Build workflow from Syncplay/syncplay, explained and optimized by Latchkey.
CI health: D - needs work
Point runs-on at Latchkey and get caching, run de-duplication, job timeouts, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the Build workflow from the Syncplay/syncplay repository, a real project running GitHub Actions. It is shown here with attribution under its Apache-2.0 license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: Build
on: [push, pull_request]
jobs:
windows:
name: Build for Windows
runs-on: windows-2022
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.8'
architecture: x86
- name: Check Python install
run: |
which python
python --version
python -c "import struct; print(struct.calcsize('P') * 8)"
which pip
pip --version
- name: Install Python dependencies
run: |
pip3 install -U setuptools wheel pip
pip3 install -r requirements.txt
pip3 install -r requirements_gui.txt
pip3 install py2exe
- name: Check Python dependencies
run: |
python3 -c "from PySide2 import __version__; print(__version__)"
python3 -c "from PySide2.QtCore import __version__; print(__version__)"
python3 -c "from PySide2.QtCore import QLibraryInfo; print(QLibraryInfo.location(QLibraryInfo.LibrariesPath))"
- name: Build
run: |
$ver = (findstr version .\syncplay\__init__.py).split("'")[1]
echo $ver
echo "VER=$ver" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
python buildPy2exe.py
New-Item -Path syncplay_v$ver -Name "syncplay.ini" -Value " "
- name: Prepare for deployment
run: dir
- name: Deploy portable
uses: actions/upload-artifact@v4
with:
name: Syncplay_${{ env.VER }}_Portable
path: |
syncplay_v${{ env.VER }}
- name: Deploy installer
uses: actions/upload-artifact@v4
with:
name: Syncplay-${{ env.VER }}-Setup.exe
path: |
Syncplay-${{ env.VER }}-Setup.exe
macos:
name: Build for macOS
runs-on: macos-15-intel
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Python
run: |
wget https://www.python.org/ftp/python/3.10.6/python-3.10.6-macos11.pkg
sudo installer -verbose -pkg ./python-3.10.6-macos11.pkg -target /
echo "/Library/Frameworks/Python.framework/Versions/3.10/bin" >> $GITHUB_PATH
- name: Check Python install
run: |
which python3
python3 --version
which pip3
pip3 --version
file $(which python3)
- name: Install Python dependencies
run: |
pip3 install -U pip setuptools==70.3.0 wheel
pip3 install -r requirements.txt
pip3 install -r requirements_gui.txt
pip3 install py2app
- name: Install universal2 dependencies
env:
CFLAGS: -arch x86_64 -arch arm64
ARCHFLAGS: -arch x86_64 -arch arm64
run: |
pip3 uninstall zope.interface -y
pip3 install --no-binary :all: zope.interface
pip3 uninstall cffi -y
pip3 install --no-binary :all: cffi
pip3 uninstall cryptography -y
pip3 download --platform macosx_10_10_universal2 --only-binary :all: --no-deps --dest . cryptography
pip3 install --no-cache-dir --no-index --find-links . cryptography
pip3 uninstall charset-normalizer -y
pip3 download --platform macosx_10_9_universal2 --only-binary :all: --no-deps --dest . charset-normalizer
pip3 install --no-cache-dir --no-index --find-links . charset-normalizer
- name: Check Python dependencies
run: |
python3 -c "from PySide6 import __version__; print(__version__)"
python3 -c "from PySide6.QtCore import __version__; print(__version__)"
python3 -c "from PySide6.QtCore import QLibraryInfo; print(QLibraryInfo.path(QLibraryInfo.LibrariesPath))"
python3 -c "import ssl; print(ssl)"
python3 -c "from py2app.recipes import pyside6"
python3 -c 'from distutils.sysconfig import get_config_var; print(get_config_var("LDLIBRARY"))'
- name: Build
run: |
python3 buildPy2app.py py2app
- name: Prepare for deployment
run: |
ls -al
export VER="$(cat syncplay/__init__.py | awk '/version/ {gsub("\047", "", $3); print $NF}')"
echo "VER=$VER" >> $GITHUB_ENV
mkdir dist_actions
ci/macos-deploy.sh
ls -al dist_actions
- name: Deploy
uses: actions/upload-artifact@v4
with:
name: Syncplay_${{ env.VER }}.dmg
path: |
dist_actions/Syncplay_${{ env.VER }}.dmg
deb:
name: Build Debian package
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build
run: ci/deb-script.sh
- name: Build server
run: ci/deb-server-script.sh
- name: Test
run: ci/deb-installation-test.sh
- name: Prepare for deployment
run: |
ls -al
export VER="$(cat syncplay/__init__.py | awk '/version/ {gsub("\047", "", $3); print $NF}')"
echo "VER=$VER" >> $GITHUB_ENV
mkdir dist_actions
mv /tmp/syncplay.deb dist_actions/syncplay_${VER}.deb
mv /tmp/syncplay-server.deb dist_actions/syncplay-server_${VER}.deb
ls -al dist_actions
- name: Deploy full deb
uses: actions/upload-artifact@v4
with:
name: syncplay.deb
path: |
dist_actions/syncplay_*.deb
- name: Deploy server deb
uses: actions/upload-artifact@v4
with:
name: syncplay-server.deb
path: |
dist_actions/syncplay-server_*.deb
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: Build on: [push, pull_request] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: windows: timeout-minutes: 30 name: Build for Windows runs-on: windows-2022 steps: - name: Checkout uses: actions/checkout@v2 - name: Setup Python uses: actions/setup-python@v2 with: cache: 'pip' python-version: '3.8' architecture: x86 - name: Check Python install run: | which python python --version python -c "import struct; print(struct.calcsize('P') * 8)" which pip pip --version - name: Install Python dependencies run: | pip3 install -U setuptools wheel pip pip3 install -r requirements.txt pip3 install -r requirements_gui.txt pip3 install py2exe - name: Check Python dependencies run: | python3 -c "from PySide2 import __version__; print(__version__)" python3 -c "from PySide2.QtCore import __version__; print(__version__)" python3 -c "from PySide2.QtCore import QLibraryInfo; print(QLibraryInfo.location(QLibraryInfo.LibrariesPath))" - name: Build run: | $ver = (findstr version .\syncplay\__init__.py).split("'")[1] echo $ver echo "VER=$ver" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append python buildPy2exe.py New-Item -Path syncplay_v$ver -Name "syncplay.ini" -Value " " - name: Prepare for deployment run: dir - name: Deploy portable uses: actions/upload-artifact@v4 with: name: Syncplay_${{ env.VER }}_Portable path: | syncplay_v${{ env.VER }} - name: Deploy installer uses: actions/upload-artifact@v4 with: name: Syncplay-${{ env.VER }}-Setup.exe path: | Syncplay-${{ env.VER }}-Setup.exe macos: timeout-minutes: 30 name: Build for macOS runs-on: macos-15-intel steps: - name: Checkout uses: actions/checkout@v2 - name: Setup Python run: | wget https://www.python.org/ftp/python/3.10.6/python-3.10.6-macos11.pkg sudo installer -verbose -pkg ./python-3.10.6-macos11.pkg -target / echo "/Library/Frameworks/Python.framework/Versions/3.10/bin" >> $GITHUB_PATH - name: Check Python install run: | which python3 python3 --version which pip3 pip3 --version file $(which python3) - name: Install Python dependencies run: | pip3 install -U pip setuptools==70.3.0 wheel pip3 install -r requirements.txt pip3 install -r requirements_gui.txt pip3 install py2app - name: Install universal2 dependencies env: CFLAGS: -arch x86_64 -arch arm64 ARCHFLAGS: -arch x86_64 -arch arm64 run: | pip3 uninstall zope.interface -y pip3 install --no-binary :all: zope.interface pip3 uninstall cffi -y pip3 install --no-binary :all: cffi pip3 uninstall cryptography -y pip3 download --platform macosx_10_10_universal2 --only-binary :all: --no-deps --dest . cryptography pip3 install --no-cache-dir --no-index --find-links . cryptography pip3 uninstall charset-normalizer -y pip3 download --platform macosx_10_9_universal2 --only-binary :all: --no-deps --dest . charset-normalizer pip3 install --no-cache-dir --no-index --find-links . charset-normalizer - name: Check Python dependencies run: | python3 -c "from PySide6 import __version__; print(__version__)" python3 -c "from PySide6.QtCore import __version__; print(__version__)" python3 -c "from PySide6.QtCore import QLibraryInfo; print(QLibraryInfo.path(QLibraryInfo.LibrariesPath))" python3 -c "import ssl; print(ssl)" python3 -c "from py2app.recipes import pyside6" python3 -c 'from distutils.sysconfig import get_config_var; print(get_config_var("LDLIBRARY"))' - name: Build run: | python3 buildPy2app.py py2app - name: Prepare for deployment run: | ls -al export VER="$(cat syncplay/__init__.py | awk '/version/ {gsub("\047", "", $3); print $NF}')" echo "VER=$VER" >> $GITHUB_ENV mkdir dist_actions ci/macos-deploy.sh ls -al dist_actions - name: Deploy uses: actions/upload-artifact@v4 with: name: Syncplay_${{ env.VER }}.dmg path: | dist_actions/Syncplay_${{ env.VER }}.dmg deb: timeout-minutes: 30 name: Build Debian package runs-on: latchkey-small steps: - name: Checkout uses: actions/checkout@v2 - name: Build run: ci/deb-script.sh - name: Build server run: ci/deb-server-script.sh - name: Test run: ci/deb-installation-test.sh - name: Prepare for deployment run: | ls -al export VER="$(cat syncplay/__init__.py | awk '/version/ {gsub("\047", "", $3); print $NF}')" echo "VER=$VER" >> $GITHUB_ENV mkdir dist_actions mv /tmp/syncplay.deb dist_actions/syncplay_${VER}.deb mv /tmp/syncplay-server.deb dist_actions/syncplay-server_${VER}.deb ls -al dist_actions - name: Deploy full deb uses: actions/upload-artifact@v4 with: name: syncplay.deb path: | dist_actions/syncplay_*.deb - name: Deploy server deb uses: actions/upload-artifact@v4 with: name: syncplay-server.deb path: | dist_actions/syncplay-server_*.deb
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - 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.
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:
- Network fetches
This workflow runs 3 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.