CI workflow (Leaflet/Leaflet)
The CI workflow from Leaflet/Leaflet, explained and optimized by Latchkey.
CI health: B - good
Point runs-on at Latchkey and get run de-duplication, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the CI workflow from the Leaflet/Leaflet repository, a real project running GitHub Actions. It is shown here with attribution under its BSD-2-Clause license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: CI
on:
push:
branches: [main]
tags: ['v*']
pull_request:
permissions:
contents: read
env:
NODE_VERSION: 24
jobs:
build-docs:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: ruby/setup-ruby@v1
with:
bundler-cache: true
working-directory: ./docs
- working-directory: ./docs
run: bundle exec jekyll build --strict_front_matter
lint:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- run: npm ci
- run: npm run lint
bundlemon:
if: github.repository_owner == 'Leaflet'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- run: npm ci
- run: npm run build
- run: npm run bundlemon
env:
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
test-ssr:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- run: npm ci
- run: npm run build
- run: node ./spec/ssr/ssr_node.js
- uses: denoland/setup-deno@v2
with:
deno-version: v2.x
- run: deno run ./spec/ssr/ssr_deno.js
test:
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
timeout-minutes: 8
env:
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.playwright-browsers
strategy:
fail-fast: false
matrix:
include:
- project: chromium
- project: firefox
- project: chromium-retina
- project: chromium
os: windows-latest
- project: webkit
os: macos-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- run: npm ci
- name: Cache Playwright browsers
uses: actions/cache@v6
with:
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
key: pw-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
- name: Install Playwright browser
run: npx playwright install ${{ matrix.project == 'chromium-retina' && 'chromium' || matrix.project }}
- name: Run tests on ${{ matrix.project }}
run: npx vitest --run --project=${{ matrix.project }}
- name: Run tests on ${{ matrix.project }} with touch
run: npx vitest --run --project=${{ matrix.project }}
env:
VITE_TOUCH: '1'
publish-artifacts:
permissions:
contents: write
if: github.repository_owner == 'Leaflet' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- run: npm ci
- run: npm run build
- name: Compress artifacts
working-directory: dist
run: zip -r leaflet.zip . ../CHANGELOG.md ../LICENSE ../README.md
- name: Publish development snapshot
uses: softprops/action-gh-release@v3
with:
tag_name: dev
name: Development snapshot
prerelease: true
files: dist/*
generate_release_notes: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish-npm:
if: github.repository_owner == 'Leaflet' && startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
registry-url: https://registry.npmjs.org
- run: npm ci
- run: npm run build
- name: Publish to NPM
run: |
TAG=$(echo $GITHUB_REF_NAME | grep -oP '^v\d+\.\d+\.\d+-?\K(\w+)?')
npm publish --tag ${TAG:-latest}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: CI on: push: branches: [main] tags: ['v*'] pull_request: permissions: contents: read env: NODE_VERSION: 24 concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build-docs: runs-on: latchkey-small timeout-minutes: 5 steps: - uses: actions/checkout@v7 - uses: ruby/setup-ruby@v1 with: bundler-cache: true working-directory: ./docs - working-directory: ./docs run: bundle exec jekyll build --strict_front_matter lint: runs-on: latchkey-small timeout-minutes: 5 steps: - uses: actions/checkout@v7 - uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: npm - run: npm ci - run: npm run lint bundlemon: if: github.repository_owner == 'Leaflet' runs-on: latchkey-small timeout-minutes: 5 steps: - uses: actions/checkout@v7 - uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: npm - run: npm ci - run: npm run build - run: npm run bundlemon env: CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} test-ssr: runs-on: latchkey-small timeout-minutes: 5 steps: - uses: actions/checkout@v7 - uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: npm - run: npm ci - run: npm run build - run: node ./spec/ssr/ssr_node.js - uses: denoland/setup-deno@v2 with: deno-version: v2.x - run: deno run ./spec/ssr/ssr_deno.js test: runs-on: ${{ matrix.os || 'ubuntu-latest' }} timeout-minutes: 8 env: PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.playwright-browsers strategy: fail-fast: false matrix: include: - project: chromium - project: firefox - project: chromium-retina - project: chromium os: windows-latest - project: webkit os: macos-latest steps: - uses: actions/checkout@v7 - uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: npm - run: npm ci - name: Cache Playwright browsers uses: actions/cache@v6 with: path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }} key: pw-${{ runner.os }}-${{ hashFiles('package-lock.json') }} - name: Install Playwright browser run: npx playwright install ${{ matrix.project == 'chromium-retina' && 'chromium' || matrix.project }} - name: Run tests on ${{ matrix.project }} run: npx vitest --run --project=${{ matrix.project }} - name: Run tests on ${{ matrix.project }} with touch run: npx vitest --run --project=${{ matrix.project }} env: VITE_TOUCH: '1' publish-artifacts: permissions: contents: write if: github.repository_owner == 'Leaflet' && github.ref == 'refs/heads/main' runs-on: latchkey-small timeout-minutes: 5 steps: - uses: actions/checkout@v7 - uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: npm - run: npm ci - run: npm run build - name: Compress artifacts working-directory: dist run: zip -r leaflet.zip . ../CHANGELOG.md ../LICENSE ../README.md - name: Publish development snapshot uses: softprops/action-gh-release@v3 with: tag_name: dev name: Development snapshot prerelease: true files: dist/* generate_release_notes: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} publish-npm: if: github.repository_owner == 'Leaflet' && startsWith(github.ref, 'refs/tags/v') runs-on: latchkey-small timeout-minutes: 5 steps: - uses: actions/checkout@v7 - uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: npm registry-url: https://registry.npmjs.org - run: npm ci - run: npm run build - name: Publish to NPM run: | TAG=$(echo $GITHUB_REF_NAME | grep -oP '^v\d+\.\d+\.\d+-?\K(\w+)?') npm publish --tag ${TAG:-latest} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
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.
3 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
- End-to-end and browser tests
This workflow runs 7 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.