Build workflow (plotly/plotly.py)
The Build workflow from plotly/plotly.py, 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 plotly/plotly.py 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: Build
on:
push:
branches:
- main
pull_request:
types: [opened, reopened, synchronize]
jobs:
plotlyjs-dev-build:
name: plotly.js dev build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Set up Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: "22"
- name: Set up uv
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
- name: Install dependencies
run: |
uv venv
source .venv/bin/activate
uv sync --extra dev_optional
- name: Update plotly.js to dev
run: |
source .venv/bin/activate
python commands.py updateplotlyjsdev
- name: Test core (excluding nodev)
run: |
source .venv/bin/activate
pytest -k 'not nodev' tests/test_core
- name: Build source distribution packages
run: |
source .venv/bin/activate
uv sync --extra dev_build
python -m build --sdist --wheel -o dist
- name: Upload dist artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: plotlyjs-dev-build-dist
path: dist/
if-no-files-found: error
full-build:
name: Full prod build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Set up Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: "22"
- name: Set up uv
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
- name: PyPI Build
run: |
uv venv
source .venv/bin/activate
uv sync --extra dev_build
cd js
npm ci
npm run build
cd ..
python -m build --sdist --wheel -o dist
cp -R dist output
git status
- name: Zip output
run: |
tar czf output.tgz output
- name: Upload output artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: full-build-output
path: output.tgz
if-no-files-found: error
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: branches: - main pull_request: types: [opened, reopened, synchronize] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: plotlyjs-dev-build: timeout-minutes: 30 name: plotly.js dev build runs-on: latchkey-small steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.12" - name: Set up Node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: cache: 'npm' node-version: "22" - name: Set up uv uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 - name: Install dependencies run: | uv venv source .venv/bin/activate uv sync --extra dev_optional - name: Update plotly.js to dev run: | source .venv/bin/activate python commands.py updateplotlyjsdev - name: Test core (excluding nodev) run: | source .venv/bin/activate pytest -k 'not nodev' tests/test_core - name: Build source distribution packages run: | source .venv/bin/activate uv sync --extra dev_build python -m build --sdist --wheel -o dist - name: Upload dist artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: plotlyjs-dev-build-dist path: dist/ if-no-files-found: error full-build: timeout-minutes: 30 name: Full prod build runs-on: latchkey-small steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.12" - name: Set up Node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: cache: 'npm' node-version: "22" - name: Set up uv uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 - name: PyPI Build run: | uv venv source .venv/bin/activate uv sync --extra dev_build cd js npm ci npm run build cd .. python -m build --sdist --wheel -o dist cp -R dist output git status - name: Zip output run: | tar czf output.tgz output - name: Upload output artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: full-build-output path: output.tgz if-no-files-found: error
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:
- Dependency installs
This workflow runs 2 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.