CI workflow (vitejs/vite)
The CI workflow from vitejs/vite, explained and optimized by Latchkey.
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.
What it does
This is the CI workflow from the vitejs/vite 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: CI
env:
# 7 GiB by default on GitHub, setting to 6 GiB
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
NODE_OPTIONS: --max-old-space-size=6144
# install playwright binary manually (because pnpm only runs install script once)
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
# Vitest auto retry on flaky segfault
VITEST_SEGFAULT_RETRY: 3
# Remove default permissions of GITHUB_TOKEN for security
# https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
permissions: {}
on:
push:
branches:
- main
- release/*
- feat/*
- fix/*
- perf/*
- "v[0-9]+" # v1, v2, ...
- "v[0-9]+.[0-9]+" # v4.0, v4.1, ...
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
cancel-in-progress: true
jobs:
changed:
name: Get changed files
runs-on: ubuntu-slim
outputs:
should_skip: ${{ steps.changed-files.outputs.only_changed == 'true' }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
# Assume PRs are less than 50 commits
fetch-depth: 50
persist-credentials: false
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
with:
files: |
docs/**
.github/**
!.github/workflows/ci.yml
packages/create-vite/template**
**.md
test:
needs: changed
if: needs.changed.outputs.should_skip != 'true'
timeout-minutes: 20
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node_version: [20, 22, 24, 26]
include:
# Active LTS + other OS
- os: macos-latest
node_version: 24
- os: windows-latest
# use older minor to workaround https://github.com/nodejs/node/issues/63638
node_version: 24.15.0
fail-fast: false
name: "Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }}"
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
persist-credentials: false
- name: Install pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- name: Set node version to ${{ matrix.node_version }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: ${{ matrix.node_version }}
cache: "pnpm"
- name: Install deps
run: pnpm install
# Install playwright's binary under custom directory to cache
- name: (non-windows) Set Playwright path and Get playwright version
if: runner.os != 'Windows'
run: |
echo "PLAYWRIGHT_BROWSERS_PATH=$HOME/.cache/playwright-bin" >> $GITHUB_ENV
PLAYWRIGHT_VERSION="$(pnpm ls --depth 0 --json -w playwright-chromium | jq --raw-output '.[0].devDependencies["playwright-chromium"].version')"
echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV
- name: (windows) Set Playwright path and Get playwright version
if: runner.os == 'Windows'
run: |
echo "PLAYWRIGHT_BROWSERS_PATH=$HOME\.cache\playwright-bin" >> $env:GITHUB_ENV
$env:PLAYWRIGHT_VERSION="$(pnpm ls --depth 0 --json -w playwright-chromium | jq --raw-output '.[0].devDependencies["playwright-chromium"].version')"
echo "PLAYWRIGHT_VERSION=$env:PLAYWRIGHT_VERSION" >> $env:GITHUB_ENV
- name: Cache Playwright's binary
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
key: ${{ runner.os }}-playwright-bin-v1-${{ env.PLAYWRIGHT_VERSION }}
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
restore-keys: |
${{ runner.os }}-playwright-bin-v1-
- name: Install Playwright
# does not need to explicitly set chromium after https://github.com/microsoft/playwright/issues/14862 is solved
run: pnpm playwright install chromium
- name: Build
run: pnpm run build
- name: Test unit
run: pnpm run test-unit
- name: Test serve
run: pnpm run test-serve
- name: Test build
run: pnpm run test-build
test-passed:
if: (!cancelled() && !failure())
needs: test
runs-on: ubuntu-slim
name: Build & Test Passed or Skipped
steps:
- run: echo "Build & Test Passed or Skipped"
test-failed:
if: (!cancelled() && failure())
needs: test
runs-on: ubuntu-slim
name: Build & Test Failed
steps:
- run: echo "Build & Test Failed"
lint:
timeout-minutes: 10
runs-on: ubuntu-latest
name: "Lint: node-24, ubuntu-latest"
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
persist-credentials: false
- name: Install pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- name: Set node version to 24
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: 24
cache: "pnpm"
- name: Install deps
run: pnpm install
- name: Build
run: pnpm run build
- name: Lint
run: pnpm run lint
- name: Check formatting
run: pnpm format && git diff --exit-code
- name: Typecheck
run: pnpm run typecheck
- name: Test docs
run: pnpm run test-docs
# From https://github.com/rhysd/actionlint/blob/main/docs/usage.md#use-actionlint-on-github-actions
- name: Check workflow files
run: |
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
./actionlint -color -shellcheck=""
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: CI env: # 7 GiB by default on GitHub, setting to 6 GiB # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources NODE_OPTIONS: --max-old-space-size=6144 # install playwright binary manually (because pnpm only runs install script once) PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" # Vitest auto retry on flaky segfault VITEST_SEGFAULT_RETRY: 3 # Remove default permissions of GITHUB_TOKEN for security # https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs permissions: {} on: push: branches: - main - release/* - feat/* - fix/* - perf/* - "v[0-9]+" # v1, v2, ... - "v[0-9]+.[0-9]+" # v4.0, v4.1, ... pull_request: workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.event.number || github.sha }} cancel-in-progress: true jobs: changed: timeout-minutes: 30 name: Get changed files runs-on: ubuntu-slim outputs: should_skip: ${{ steps.changed-files.outputs.only_changed == 'true' }} steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 with: # Assume PRs are less than 50 commits fetch-depth: 50 persist-credentials: false - name: Get changed files id: changed-files uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 with: files: | docs/** .github/** !.github/workflows/ci.yml packages/create-vite/template** **.md test: needs: changed if: needs.changed.outputs.should_skip != 'true' timeout-minutes: 20 runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] node_version: [20, 22, 24, 26] include: # Active LTS + other OS - os: macos-latest node_version: 24 - os: windows-latest # use older minor to workaround https://github.com/nodejs/node/issues/63638 node_version: 24.15.0 fail-fast: false name: "Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }}" steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 with: persist-credentials: false - name: Install pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Set node version to ${{ matrix.node_version }} uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: ${{ matrix.node_version }} cache: "pnpm" - name: Install deps run: pnpm install # Install playwright's binary under custom directory to cache - name: (non-windows) Set Playwright path and Get playwright version if: runner.os != 'Windows' run: | echo "PLAYWRIGHT_BROWSERS_PATH=$HOME/.cache/playwright-bin" >> $GITHUB_ENV PLAYWRIGHT_VERSION="$(pnpm ls --depth 0 --json -w playwright-chromium | jq --raw-output '.[0].devDependencies["playwright-chromium"].version')" echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV - name: (windows) Set Playwright path and Get playwright version if: runner.os == 'Windows' run: | echo "PLAYWRIGHT_BROWSERS_PATH=$HOME\.cache\playwright-bin" >> $env:GITHUB_ENV $env:PLAYWRIGHT_VERSION="$(pnpm ls --depth 0 --json -w playwright-chromium | jq --raw-output '.[0].devDependencies["playwright-chromium"].version')" echo "PLAYWRIGHT_VERSION=$env:PLAYWRIGHT_VERSION" >> $env:GITHUB_ENV - name: Cache Playwright's binary uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6 with: key: ${{ runner.os }}-playwright-bin-v1-${{ env.PLAYWRIGHT_VERSION }} path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }} restore-keys: | ${{ runner.os }}-playwright-bin-v1- - name: Install Playwright # does not need to explicitly set chromium after https://github.com/microsoft/playwright/issues/14862 is solved run: pnpm playwright install chromium - name: Build run: pnpm run build - name: Test unit run: pnpm run test-unit - name: Test serve run: pnpm run test-serve - name: Test build run: pnpm run test-build test-passed: timeout-minutes: 30 if: (!cancelled() && !failure()) needs: test runs-on: ubuntu-slim name: Build & Test Passed or Skipped steps: - run: echo "Build & Test Passed or Skipped" test-failed: timeout-minutes: 30 if: (!cancelled() && failure()) needs: test runs-on: ubuntu-slim name: Build & Test Failed steps: - run: echo "Build & Test Failed" lint: timeout-minutes: 10 runs-on: latchkey-small name: "Lint: node-24, ubuntu-latest" steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 with: persist-credentials: false - name: Install pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Set node version to 24 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: 24 cache: "pnpm" - name: Install deps run: pnpm install - name: Build run: pnpm run build - name: Lint run: pnpm run lint - name: Check formatting run: pnpm format && git diff --exit-code - name: Typecheck run: pnpm run typecheck - name: Test docs run: pnpm run test-docs # From https://github.com/rhysd/actionlint/blob/main/docs/usage.md#use-actionlint-on-github-actions - name: Check workflow files run: | bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) ./actionlint -color -shellcheck=""
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. - 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
- End-to-end and browser tests
This workflow runs 5 jobs (8 with the matrix expanded) per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.