release workflow (parcel-bundler/parcel)
The release workflow from parcel-bundler/parcel, explained and optimized by Latchkey.
CI health: B - good
Point runs-on at Latchkey and get job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the release workflow from the parcel-bundler/parcel 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: release
on:
workflow_call:
inputs:
release-command:
description: 'The command that will release packages as part of the final step'
required: true
type: string
type:
description: 'The type of release, usually corresponds to the dist-tag'
required: true
type: string
profile:
description: 'The profile to use when building the native binaries'
required: false
default: 'release'
type: string
secrets:
GHCR_TOKEN:
required: true
NPM_TOKEN:
required: true
permissions:
contents: read # for actions/checkout
jobs:
build-macos-windows:
strategy:
fail-fast: false
matrix:
include:
- name: aarch64-apple-darwin
os: macos-latest
target: aarch64-apple-darwin
- name: x86_64-apple-darwin
os: macos-latest
target: x86_64-apple-darwin
- name: windows-latest
os: windows-latest
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
target: ${{ matrix.target }}
- uses: bahmutov/npm-install@v1.8.35
- uses: Swatinem/rust-cache@v2
if: ${{ inputs.type != 'latest' }}
with:
shared-key: ${{ matrix.name }}
- name: Remove CommandLineTools SDKs
if: ${{ matrix.target == 'aarch64-apple-darwin' }}
run: sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*;
- name: Build native packages
run: yarn build-native-${{ inputs.profile }}
env:
RUST_TARGET: ${{ matrix.target }}
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: bindings-${{ matrix.name }}
path: packages/*/*/*.node
- name: Debug
if: ${{ runner.os == 'macOS' }}
run: ls -l packages/*/*/*.node
- name: Smoke test
if: ${{ !matrix.target }}
run: node -e "require('@parcel/rust')"
build-linux:
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
arch: x86_64
cflags: -mevex512
glibc: 2.26
docker: node:20-slim
args: ''
- target: armv7-unknown-linux-gnueabihf
arch: armhf
cflags: -mfpu=neon
glibc: 2.26
docker: node:20-slim
args: '--platform linux/arm/v7'
- target: aarch64-unknown-linux-gnu
arch: arm64
cflags: ''
glibc: 2.26
docker: node:20-slim
args: '--platform linux/arm64'
- target: x86_64-unknown-linux-musl
arch: x86_64
cflags: -msse4.2 -mevex512
glibc: ''
docker: node:20-alpine
args: ''
- target: aarch64-unknown-linux-musl
arch: aarch64
cflags: ''
glibc: ''
docker: node:20-alpine
args: '--platform linux/arm64'
name: ${{ matrix.target }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
target: ${{ matrix.target }}
- name: Install ziglang
uses: goto-bus-stop/setup-zig@v1
with:
version: 0.14.0
- name: Install cargo toolchains
uses: taiki-e/install-action@v2
env:
GITHUB_TOKEN: ${{ github.token }}
with:
tool: cargo-zigbuild
- uses: bahmutov/npm-install@v1.8.35
- uses: Swatinem/rust-cache@v2
if: ${{ inputs.type != 'latest' }}
with:
shared-key: ${{ matrix.target }}
- name: Build native packages
run: yarn build-native-${{ inputs.profile }}
env:
RUST_TARGET: ${{ matrix.target }}
ZIG_GLIBC: ${{ matrix.glibc }}
CFLAGS: ${{ matrix.cflags }}
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: bindings-${{ matrix.target }}
path: packages/*/*/*.node
- name: debug
run: ls -l packages/*/*/*.node
- name: Set up QEMU
if: ${{ matrix.arch != 'x86_64' }}
uses: docker/setup-qemu-action@v3
with:
platforms: arm64,arm
- name: Configure binfmt-support
if: ${{ matrix.arch != 'x86_64' }}
run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- name: Smoke test
uses: addnab/docker-run-action@v3
with:
image: ${{ matrix.docker }}
options: ${{ matrix.args }} -v ${{ github.workspace }}:/build -w /build
run: node -e "require('@parcel/rust')"
build-and-release:
runs-on: ubuntu-22.04
name: Build and release
needs:
- build-linux
- build-macos-windows
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: bahmutov/npm-install@v1.8.35
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Move bindings
run: for d in artifacts/bindings-*/*/*; do cp $d/*.node packages/$(basename $(dirname $d))/$(basename $d); done
- name: Debug
run: |
ls -l packages/*/*/*.node
- run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: ${{ inputs.release-command }}
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: release on: workflow_call: inputs: release-command: description: 'The command that will release packages as part of the final step' required: true type: string type: description: 'The type of release, usually corresponds to the dist-tag' required: true type: string profile: description: 'The profile to use when building the native binaries' required: false default: 'release' type: string secrets: GHCR_TOKEN: required: true NPM_TOKEN: required: true permissions: contents: read # for actions/checkout jobs: build-macos-windows: timeout-minutes: 30 strategy: fail-fast: false matrix: include: - name: aarch64-apple-darwin os: macos-latest target: aarch64-apple-darwin - name: x86_64-apple-darwin os: macos-latest target: x86_64-apple-darwin - name: windows-latest os: windows-latest name: ${{ matrix.name }} runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: stable target: ${{ matrix.target }} - uses: bahmutov/npm-install@v1.8.35 - uses: Swatinem/rust-cache@v2 if: ${{ inputs.type != 'latest' }} with: shared-key: ${{ matrix.name }} - name: Remove CommandLineTools SDKs if: ${{ matrix.target == 'aarch64-apple-darwin' }} run: sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*; - name: Build native packages run: yarn build-native-${{ inputs.profile }} env: RUST_TARGET: ${{ matrix.target }} - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: bindings-${{ matrix.name }} path: packages/*/*/*.node - name: Debug if: ${{ runner.os == 'macOS' }} run: ls -l packages/*/*/*.node - name: Smoke test if: ${{ !matrix.target }} run: node -e "require('@parcel/rust')" build-linux: timeout-minutes: 30 strategy: fail-fast: false matrix: include: - target: x86_64-unknown-linux-gnu arch: x86_64 cflags: -mevex512 glibc: 2.26 docker: node:20-slim args: '' - target: armv7-unknown-linux-gnueabihf arch: armhf cflags: -mfpu=neon glibc: 2.26 docker: node:20-slim args: '--platform linux/arm/v7' - target: aarch64-unknown-linux-gnu arch: arm64 cflags: '' glibc: 2.26 docker: node:20-slim args: '--platform linux/arm64' - target: x86_64-unknown-linux-musl arch: x86_64 cflags: -msse4.2 -mevex512 glibc: '' docker: node:20-alpine args: '' - target: aarch64-unknown-linux-musl arch: aarch64 cflags: '' glibc: '' docker: node:20-alpine args: '--platform linux/arm64' name: ${{ matrix.target }} runs-on: latchkey-small steps: - uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: stable target: ${{ matrix.target }} - name: Install ziglang uses: goto-bus-stop/setup-zig@v1 with: version: 0.14.0 - name: Install cargo toolchains uses: taiki-e/install-action@v2 env: GITHUB_TOKEN: ${{ github.token }} with: tool: cargo-zigbuild - uses: bahmutov/npm-install@v1.8.35 - uses: Swatinem/rust-cache@v2 if: ${{ inputs.type != 'latest' }} with: shared-key: ${{ matrix.target }} - name: Build native packages run: yarn build-native-${{ inputs.profile }} env: RUST_TARGET: ${{ matrix.target }} ZIG_GLIBC: ${{ matrix.glibc }} CFLAGS: ${{ matrix.cflags }} - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: bindings-${{ matrix.target }} path: packages/*/*/*.node - name: debug run: ls -l packages/*/*/*.node - name: Set up QEMU if: ${{ matrix.arch != 'x86_64' }} uses: docker/setup-qemu-action@v3 with: platforms: arm64,arm - name: Configure binfmt-support if: ${{ matrix.arch != 'x86_64' }} run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - name: Smoke test uses: addnab/docker-run-action@v3 with: image: ${{ matrix.docker }} options: ${{ matrix.args }} -v ${{ github.workspace }}:/build -w /build run: node -e "require('@parcel/rust')" build-and-release: timeout-minutes: 30 runs-on: latchkey-small name: Build and release needs: - build-linux - build-macos-windows steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: bahmutov/npm-install@v1.8.35 - name: Download artifacts uses: actions/download-artifact@v4 with: path: artifacts - name: Move bindings run: for d in artifacts/bindings-*/*/*; do cp $d/*.node packages/$(basename $(dirname $d))/$(basename $d); done - name: Debug run: | ls -l packages/*/*/*.node - run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: ${{ inputs.release-command }}
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.
7 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.
This workflow runs 3 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.