CI workflow (lovell/sharp)
The CI workflow from lovell/sharp, 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 CI workflow from the lovell/sharp 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: CI
on:
- push
- pull_request
permissions: {}
jobs:
lint:
permissions:
contents: read
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "24"
- run: npm install --ignore-scripts
- run: npm run build:dist
- run: npm run lint-cpp
- run: npm run lint-js
- run: npm run lint-types
- run: npm run lint-publish
build-native:
permissions:
contents: read
needs: lint
name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}"
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
container: rockylinux/rockylinux:8-ubi-init
nodejs_arch: x64
nodejs_version: "^20.9.0"
nodejs_version_major: 20
platform: linux-x64
- os: ubuntu-24.04
container: rockylinux/rockylinux:8-ubi-init
nodejs_arch: x64
nodejs_version: "^22"
nodejs_version_major: 22
platform: linux-x64
package: true
- os: ubuntu-24.04
container: rockylinux/rockylinux:8-ubi-init
nodejs_arch: x64
nodejs_version: "^24"
nodejs_version_major: 24
platform: linux-x64
- os: ubuntu-24.04
container: node:20-alpine3.20
nodejs_version_major: 20
platform: linuxmusl-x64
- os: ubuntu-24.04
container: node:22-alpine3.20
nodejs_version_major: 22
platform: linuxmusl-x64
package: true
- os: ubuntu-24.04
container: node:24-alpine3.22
nodejs_version_major: 24
platform: linuxmusl-x64
- os: ubuntu-24.04-arm
container: rockylinux/rockylinux:8-ubi-init
nodejs_arch: arm64
nodejs_version: "^20.9.0"
nodejs_version_major: 20
platform: linux-arm64
- os: ubuntu-24.04-arm
container: rockylinux/rockylinux:8-ubi-init
nodejs_arch: arm64
nodejs_version: "^22"
nodejs_version_major: 22
platform: linux-arm64
package: true
- os: ubuntu-24.04-arm
container: rockylinux/rockylinux:8-ubi-init
nodejs_arch: arm64
nodejs_version: "^24"
nodejs_version_major: 24
platform: linux-arm64
- os: macos-15-intel
nodejs_arch: x64
nodejs_version: "^20.9.0"
nodejs_version_major: 20
platform: darwin-x64
- os: macos-15-intel
nodejs_arch: x64
nodejs_version: "^22"
nodejs_version_major: 22
platform: darwin-x64
package: true
- os: macos-15-intel
nodejs_arch: x64
nodejs_version: "^24"
nodejs_version_major: 24
platform: darwin-x64
- os: macos-15
nodejs_arch: arm64
nodejs_version: "^20.9.0"
nodejs_version_major: 20
platform: darwin-arm64
- os: macos-15
nodejs_arch: arm64
nodejs_version: "^22"
nodejs_version_major: 22
platform: darwin-arm64
package: true
- os: macos-15
nodejs_arch: arm64
nodejs_version: "^24"
nodejs_version_major: 24
platform: darwin-arm64
- os: windows-2022
nodejs_arch: x86
nodejs_version: "^20.9.0"
nodejs_version_major: 20
platform: win32-ia32
package: true
- os: windows-2022
nodejs_arch: x64
nodejs_version: "^20.9.0"
nodejs_version_major: 20
platform: win32-x64
- os: windows-2022
nodejs_arch: x64
nodejs_version: "^22"
nodejs_version_major: 22
platform: win32-x64
package: true
- os: windows-2022
nodejs_arch: x64
nodejs_version: "^24"
nodejs_version_major: 24
platform: win32-x64
- os: windows-11-arm
nodejs_arch: arm64
nodejs_version: "^20.9.0"
nodejs_version_major: 20
platform: win32-arm64
- os: windows-11-arm
nodejs_arch: arm64
nodejs_version: "^22"
nodejs_version_major: 22
platform: win32-arm64
package: true
- os: windows-11-arm
nodejs_arch: arm64
nodejs_version: "^24"
nodejs_version_major: 24
platform: win32-arm64
steps:
- name: Dependencies (Rocky Linux glibc)
if: contains(matrix.container, 'rockylinux')
run: |
dnf install -y gcc-toolset-14-gcc-c++ make git python3.12 fontconfig
echo "/opt/rh/gcc-toolset-14/root/usr/bin" >> $GITHUB_PATH
- name: Dependencies (Linux musl)
if: contains(matrix.container, 'alpine')
run: apk add build-base git python3 fontconfig --update-cache
- name: Dependencies (Python 3.11 - macOS, Windows)
if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows')
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.12"
- name: Dependencies (Node.js)
if: "!contains(matrix.platform, 'linuxmusl')"
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: ${{ matrix.nodejs_version }}
architecture: ${{ matrix.nodejs_arch }}
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- run: npm install
- run: npm run build:dist
- run: npm run build
- run: npm run test-unit
- if: matrix.package
run: |
npm run package-from-local-build
npm run lint-publish -- npm/${{ matrix.platform }}
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: matrix.package
with:
name: ${{ matrix.platform }}
path: npm/${{ matrix.platform }}
retention-days: 1
if-no-files-found: error
build-linuxmusl-arm64:
permissions:
contents: read
needs: lint
name: "build-linuxmusl-arm64 [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}"
runs-on: ubuntu-24.04-arm
container:
image: ${{ matrix.container }}
volumes:
- /opt:/opt:rw,rshared
- /opt:/__e/node24:ro,rshared
strategy:
fail-fast: false
matrix:
include:
- container: node:20-alpine3.20
nodejs_version_major: 20
- container: node:22-alpine3.20
nodejs_version_major: 22
package: true
- container: node:24-alpine3.22
nodejs_version_major: 24
steps:
- name: Allow Linux musl containers on ARM64 runners # https://github.com/actions/runner/issues/801#issuecomment-2394425757
shell: sh
run: |
sed -i "/^ID=/s/alpine/NotpineForGHA/" /etc/os-release
apk add nodejs --update-cache
mkdir /opt/bin
ln -s /usr/bin/node /opt/bin/node
- name: Dependencies
run: apk add build-base git python3 fontconfig --update-cache
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- run: npm install
- run: npm run build:dist && npm run build
- run: npm run test-unit
- if: matrix.package
run: |
npm run package-from-local-build
npm run lint-publish -- npm/linuxmusl-arm64
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: matrix.package
with:
name: linuxmusl-arm64
path: npm/linuxmusl-arm64
retention-days: 1
if-no-files-found: error
build-qemu:
permissions:
contents: read
needs: lint
name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] [package]"
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
include:
- platform: linux-arm
base_image: "--platform=linux/arm/v7 arm32v7/debian:bookworm"
compiler_flags: "-marm -mcpu=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard"
nodejs_arch: armv6l
nodejs_hostname: unofficial-builds.nodejs.org
nodejs_version: "22.22.3"
nodejs_version_major: 22
- platform: linux-s390x
base_image: "--platform=linux/s390x s390x/debian:bookworm"
nodejs_arch: s390x
nodejs_hostname: nodejs.org
nodejs_version: "22.22.3"
nodejs_version_major: 22
- platform: linux-ppc64
base_image: "--platform=linux/ppc64le ppc64le/debian:bookworm"
nodejs_arch: ppc64le
nodejs_hostname: nodejs.org
nodejs_version: "22.22.3"
nodejs_version_major: 22
- platform: linux-riscv64
base_image: "--platform=linux/riscv64 riscv64/debian:trixie"
compiler_flags: "-march=rv64gc"
nodejs_arch: riscv64
nodejs_hostname: unofficial-builds.nodejs.org
nodejs_version: "22.22.3"
nodejs_version_major: 22
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0
with:
arch: none
distro: none
base_image: ${{ matrix.base_image }}
env: |
CFLAGS: "${{ matrix.compiler_flags }}"
CXXFLAGS: "${{ matrix.compiler_flags }}"
run: |
apt-get update
apt-get install -y curl g++ git libatomic1 make python3 xz-utils
mkdir /opt/nodejs
curl --silent https://${{ matrix.nodejs_hostname }}/download/release/v${{ matrix.nodejs_version}}/node-v${{ matrix.nodejs_version}}-linux-${{ matrix.nodejs_arch }}.tar.xz | tar xJC /opt/nodejs --strip-components=1
export PATH=$PATH:/opt/nodejs/bin
npm install
npm run build:dist
npm run build
node --test test/unit/io.js test/unit/raw.js
npm run package-from-local-build
npm run lint-publish -- npm/${{ matrix.platform }}
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ matrix.platform }}
path: npm/${{ matrix.platform }}
retention-days: 1
if-no-files-found: error
build-emscripten:
permissions:
contents: read
needs: lint
name: "build-wasm32 [package]"
runs-on: ubuntu-24.04
container: "emscripten/emsdk:6.0.1"
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Dependencies
run: apt-get update && apt-get install -y pkg-config
- name: Dependencies (Node.js)
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "22"
- run: npm install
- run: npm run build:dist && emmake npm run build
- name: Verify emscripten versions match
run: |
EMSCRIPTEN_VERSION_LIBVIPS=$(node -p "require('@img/sharp-libvips-dev-wasm32/versions').emscripten")
EMSCRIPTEN_VERSION_SHARP=$(emcc -dumpversion)
echo "libvips built with emscripten $EMSCRIPTEN_VERSION_LIBVIPS"
echo "sharp built with emscripten $EMSCRIPTEN_VERSION_SHARP"
test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP"
- run: emmake npm run test-unit
- run: emmake npm run package-from-local-build
- run: npm run lint-publish -- npm/wasm32
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: wasm32
path: npm/wasm32
retention-days: 1
if-no-files-found: error
release:
permissions:
contents: write
id-token: write
runs-on: ubuntu-24.04
needs:
- build-native
- build-linuxmusl-arm64
- build-qemu
- build-emscripten
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: npm
- name: Create npm workspace tarball
run: tar -vcaf npm-workspace.tar.xz --directory npm --exclude=from-local-build.js .
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '24'
- name: Populate distribution directory
run: npm run build:dist
- name: Populate WebAssembly wrapper packages
run: npm run package-wasm-wrappers
- name: Create release notes
run: npm run package-release-notes
- name: Create GitHub release for tag
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create \
--notes-file release-notes.md \
${{ contains(github.ref, '-rc') && '--prerelease --latest=false' || '' }} \
${GITHUB_REF_NAME} \
npm-workspace.tar.xz
- name: Publish platform-specific npm packages
if: startsWith(github.ref, 'refs/tags/v')
run: cd npm && npm publish --workspaces --tag=${{ contains(github.ref, '-rc') && 'next' || 'latest' }}
- name: Publish sharp npm package
if: startsWith(github.ref, 'refs/tags/v')
run: npm publish --tag=${{ contains(github.ref, '-rc') && 'next' || 'latest' }}
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: CI on: - push - pull_request permissions: {} concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: lint: timeout-minutes: 30 permissions: contents: read runs-on: latchkey-small steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: cache: 'npm' node-version: "24" - run: npm install --ignore-scripts - run: npm run build:dist - run: npm run lint-cpp - run: npm run lint-js - run: npm run lint-types - run: npm run lint-publish build-native: timeout-minutes: 30 permissions: contents: read needs: lint name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}" runs-on: ${{ matrix.os }} container: ${{ matrix.container }} strategy: fail-fast: false matrix: include: - os: ubuntu-24.04 container: rockylinux/rockylinux:8-ubi-init nodejs_arch: x64 nodejs_version: "^20.9.0" nodejs_version_major: 20 platform: linux-x64 - os: ubuntu-24.04 container: rockylinux/rockylinux:8-ubi-init nodejs_arch: x64 nodejs_version: "^22" nodejs_version_major: 22 platform: linux-x64 package: true - os: ubuntu-24.04 container: rockylinux/rockylinux:8-ubi-init nodejs_arch: x64 nodejs_version: "^24" nodejs_version_major: 24 platform: linux-x64 - os: ubuntu-24.04 container: node:20-alpine3.20 nodejs_version_major: 20 platform: linuxmusl-x64 - os: ubuntu-24.04 container: node:22-alpine3.20 nodejs_version_major: 22 platform: linuxmusl-x64 package: true - os: ubuntu-24.04 container: node:24-alpine3.22 nodejs_version_major: 24 platform: linuxmusl-x64 - os: ubuntu-24.04-arm container: rockylinux/rockylinux:8-ubi-init nodejs_arch: arm64 nodejs_version: "^20.9.0" nodejs_version_major: 20 platform: linux-arm64 - os: ubuntu-24.04-arm container: rockylinux/rockylinux:8-ubi-init nodejs_arch: arm64 nodejs_version: "^22" nodejs_version_major: 22 platform: linux-arm64 package: true - os: ubuntu-24.04-arm container: rockylinux/rockylinux:8-ubi-init nodejs_arch: arm64 nodejs_version: "^24" nodejs_version_major: 24 platform: linux-arm64 - os: macos-15-intel nodejs_arch: x64 nodejs_version: "^20.9.0" nodejs_version_major: 20 platform: darwin-x64 - os: macos-15-intel nodejs_arch: x64 nodejs_version: "^22" nodejs_version_major: 22 platform: darwin-x64 package: true - os: macos-15-intel nodejs_arch: x64 nodejs_version: "^24" nodejs_version_major: 24 platform: darwin-x64 - os: macos-15 nodejs_arch: arm64 nodejs_version: "^20.9.0" nodejs_version_major: 20 platform: darwin-arm64 - os: macos-15 nodejs_arch: arm64 nodejs_version: "^22" nodejs_version_major: 22 platform: darwin-arm64 package: true - os: macos-15 nodejs_arch: arm64 nodejs_version: "^24" nodejs_version_major: 24 platform: darwin-arm64 - os: windows-2022 nodejs_arch: x86 nodejs_version: "^20.9.0" nodejs_version_major: 20 platform: win32-ia32 package: true - os: windows-2022 nodejs_arch: x64 nodejs_version: "^20.9.0" nodejs_version_major: 20 platform: win32-x64 - os: windows-2022 nodejs_arch: x64 nodejs_version: "^22" nodejs_version_major: 22 platform: win32-x64 package: true - os: windows-2022 nodejs_arch: x64 nodejs_version: "^24" nodejs_version_major: 24 platform: win32-x64 - os: windows-11-arm nodejs_arch: arm64 nodejs_version: "^20.9.0" nodejs_version_major: 20 platform: win32-arm64 - os: windows-11-arm nodejs_arch: arm64 nodejs_version: "^22" nodejs_version_major: 22 platform: win32-arm64 package: true - os: windows-11-arm nodejs_arch: arm64 nodejs_version: "^24" nodejs_version_major: 24 platform: win32-arm64 steps: - name: Dependencies (Rocky Linux glibc) if: contains(matrix.container, 'rockylinux') run: | dnf install -y gcc-toolset-14-gcc-c++ make git python3.12 fontconfig echo "/opt/rh/gcc-toolset-14/root/usr/bin" >> $GITHUB_PATH - name: Dependencies (Linux musl) if: contains(matrix.container, 'alpine') run: apk add build-base git python3 fontconfig --update-cache - name: Dependencies (Python 3.11 - macOS, Windows) if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows') uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" - name: Dependencies (Node.js) if: "!contains(matrix.platform, 'linuxmusl')" uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: cache: 'npm' node-version: ${{ matrix.nodejs_version }} architecture: ${{ matrix.nodejs_arch }} - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - run: npm install - run: npm run build:dist - run: npm run build - run: npm run test-unit - if: matrix.package run: | npm run package-from-local-build npm run lint-publish -- npm/${{ matrix.platform }} - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: matrix.package with: name: ${{ matrix.platform }} path: npm/${{ matrix.platform }} retention-days: 1 if-no-files-found: error build-linuxmusl-arm64: timeout-minutes: 30 permissions: contents: read needs: lint name: "build-linuxmusl-arm64 [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}" runs-on: latchkey-small-arm container: image: ${{ matrix.container }} volumes: - /opt:/opt:rw,rshared - /opt:/__e/node24:ro,rshared strategy: fail-fast: false matrix: include: - container: node:20-alpine3.20 nodejs_version_major: 20 - container: node:22-alpine3.20 nodejs_version_major: 22 package: true - container: node:24-alpine3.22 nodejs_version_major: 24 steps: - name: Allow Linux musl containers on ARM64 runners # https://github.com/actions/runner/issues/801#issuecomment-2394425757 shell: sh run: | sed -i "/^ID=/s/alpine/NotpineForGHA/" /etc/os-release apk add nodejs --update-cache mkdir /opt/bin ln -s /usr/bin/node /opt/bin/node - name: Dependencies run: apk add build-base git python3 fontconfig --update-cache - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - run: npm install - run: npm run build:dist && npm run build - run: npm run test-unit - if: matrix.package run: | npm run package-from-local-build npm run lint-publish -- npm/linuxmusl-arm64 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: matrix.package with: name: linuxmusl-arm64 path: npm/linuxmusl-arm64 retention-days: 1 if-no-files-found: error build-qemu: timeout-minutes: 30 permissions: contents: read needs: lint name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] [package]" runs-on: latchkey-small strategy: fail-fast: false matrix: include: - platform: linux-arm base_image: "--platform=linux/arm/v7 arm32v7/debian:bookworm" compiler_flags: "-marm -mcpu=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard" nodejs_arch: armv6l nodejs_hostname: unofficial-builds.nodejs.org nodejs_version: "22.22.3" nodejs_version_major: 22 - platform: linux-s390x base_image: "--platform=linux/s390x s390x/debian:bookworm" nodejs_arch: s390x nodejs_hostname: nodejs.org nodejs_version: "22.22.3" nodejs_version_major: 22 - platform: linux-ppc64 base_image: "--platform=linux/ppc64le ppc64le/debian:bookworm" nodejs_arch: ppc64le nodejs_hostname: nodejs.org nodejs_version: "22.22.3" nodejs_version_major: 22 - platform: linux-riscv64 base_image: "--platform=linux/riscv64 riscv64/debian:trixie" compiler_flags: "-march=rv64gc" nodejs_arch: riscv64 nodejs_hostname: unofficial-builds.nodejs.org nodejs_version: "22.22.3" nodejs_version_major: 22 steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 with: arch: none distro: none base_image: ${{ matrix.base_image }} env: | CFLAGS: "${{ matrix.compiler_flags }}" CXXFLAGS: "${{ matrix.compiler_flags }}" run: | apt-get update apt-get install -y curl g++ git libatomic1 make python3 xz-utils mkdir /opt/nodejs curl --silent https://${{ matrix.nodejs_hostname }}/download/release/v${{ matrix.nodejs_version}}/node-v${{ matrix.nodejs_version}}-linux-${{ matrix.nodejs_arch }}.tar.xz | tar xJC /opt/nodejs --strip-components=1 export PATH=$PATH:/opt/nodejs/bin npm install npm run build:dist npm run build node --test test/unit/io.js test/unit/raw.js npm run package-from-local-build npm run lint-publish -- npm/${{ matrix.platform }} - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ matrix.platform }} path: npm/${{ matrix.platform }} retention-days: 1 if-no-files-found: error build-emscripten: timeout-minutes: 30 permissions: contents: read needs: lint name: "build-wasm32 [package]" runs-on: latchkey-small container: "emscripten/emsdk:6.0.1" steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Dependencies run: apt-get update && apt-get install -y pkg-config - name: Dependencies (Node.js) uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: cache: 'npm' node-version: "22" - run: npm install - run: npm run build:dist && emmake npm run build - name: Verify emscripten versions match run: | EMSCRIPTEN_VERSION_LIBVIPS=$(node -p "require('@img/sharp-libvips-dev-wasm32/versions').emscripten") EMSCRIPTEN_VERSION_SHARP=$(emcc -dumpversion) echo "libvips built with emscripten $EMSCRIPTEN_VERSION_LIBVIPS" echo "sharp built with emscripten $EMSCRIPTEN_VERSION_SHARP" test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP" - run: emmake npm run test-unit - run: emmake npm run package-from-local-build - run: npm run lint-publish -- npm/wasm32 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wasm32 path: npm/wasm32 retention-days: 1 if-no-files-found: error release: timeout-minutes: 30 permissions: contents: write id-token: write runs-on: latchkey-small needs: - build-native - build-linuxmusl-arm64 - build-qemu - build-emscripten steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: npm - name: Create npm workspace tarball run: tar -vcaf npm-workspace.tar.xz --directory npm --exclude=from-local-build.js . - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: cache: 'npm' node-version: '24' - name: Populate distribution directory run: npm run build:dist - name: Populate WebAssembly wrapper packages run: npm run package-wasm-wrappers - name: Create release notes run: npm run package-release-notes - name: Create GitHub release for tag if: startsWith(github.ref, 'refs/tags/v') env: GH_TOKEN: ${{ github.token }} run: | gh release create \ --notes-file release-notes.md \ ${{ contains(github.ref, '-rc') && '--prerelease --latest=false' || '' }} \ ${GITHUB_REF_NAME} \ npm-workspace.tar.xz - name: Publish platform-specific npm packages if: startsWith(github.ref, 'refs/tags/v') run: cd npm && npm publish --workspaces --tag=${{ contains(github.ref, '-rc') && 'next' || 'latest' }} - name: Publish sharp npm package if: startsWith(github.ref, 'refs/tags/v') run: npm publish --tag=${{ contains(github.ref, '-rc') && 'next' || 'latest' }}
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
- Network fetches
This workflow runs 6 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.