ci workflow (BurntSushi/ripgrep)
The ci workflow from BurntSushi/ripgrep, explained and optimized by Latchkey.
CI health: C - fair
Point runs-on at Latchkey and get run de-duplication, job timeouts, 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 BurntSushi/ripgrep repository, a real project running GitHub Actions. It is shown here with attribution under its Unlicense license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: ci
on:
pull_request:
push:
branches:
- master
schedule:
- cron: '00 01 * * *'
# The section is needed to drop write-all permissions that are granted on
# `schedule` event. By specifying any permission explicitly all others are set
# to none. By using the principle of least privilege the damage a compromised
# workflow can do (because of an injection or compromised third party tool or
# action) is restricted. Currently the workflow doesn't need any additional
# permission except for pulling the code. Adding labels to issues, commenting
# on pull-requests, etc. may need additional permissions:
#
# Syntax for this section:
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
#
# Reference for how to assign permissions on a job-by-job basis:
# https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
#
# Reference for available permissions that we can enable if needed:
# https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token
permissions:
# to fetch code (actions/checkout)
contents: read
jobs:
test:
name: test
env:
# For some builds, we use cross to test on 32-bit and big-endian
# systems.
CARGO: cargo
# When CARGO is set to CROSS, this is set to `--target matrix.target`.
# Note that we only use cross on Linux, so setting a target on a
# different OS will just use normal cargo.
TARGET_FLAGS:
# When CARGO is set to CROSS, TARGET_DIR includes matrix.target.
TARGET_DIR: ./target
# Bump this as appropriate. We pin to a version to make sure CI
# continues to work as cross releases in the past have broken things
# in subtle ways.
CROSS_VERSION: v0.2.5
# Emit backtraces on panics.
RUST_BACKTRACE: 1
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- build: pinned
os: ubuntu-latest
rust: 1.85.0
- build: stable
os: ubuntu-latest
rust: stable
- build: beta
os: ubuntu-latest
rust: beta
- build: nightly
os: ubuntu-latest
rust: nightly
- build: stable-musl
os: ubuntu-latest
rust: stable
target: x86_64-unknown-linux-musl
- build: stable-x86
os: ubuntu-latest
rust: stable
target: i686-unknown-linux-gnu
- build: stable-aarch64
os: ubuntu-latest
rust: stable
target: aarch64-unknown-linux-gnu
- build: stable-aarch64-musl
os: ubuntu-latest
rust: stable
target: aarch64-unknown-linux-musl
- build: stable-arm-gnueabihf
os: ubuntu-latest
rust: stable
target: armv7-unknown-linux-gnueabihf
- build: stable-arm-musleabihf
os: ubuntu-latest
rust: stable
target: armv7-unknown-linux-musleabihf
- build: stable-arm-musleabi
os: ubuntu-latest
rust: stable
target: armv7-unknown-linux-musleabi
- build: stable-powerpc64
os: ubuntu-latest
rust: stable
target: powerpc64-unknown-linux-gnu
- build: stable-s390x
os: ubuntu-latest
rust: stable
target: s390x-unknown-linux-gnu
- build: stable-riscv64
os: ubuntu-latest
rust: stable
target: riscv64gc-unknown-linux-gnu
- build: macos
os: macos-latest
rust: nightly
- build: win-msvc
os: windows-latest
rust: nightly
- build: win-gnu
os: windows-latest
rust: nightly-x86_64-gnu
- build: winaarch64-msvc
os: windows-11-arm
rust: nightly
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install packages (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
ci/ubuntu-install-packages
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- name: Use Cross
if: matrix.os == 'ubuntu-latest' && matrix.target != ''
run: |
# In the past, new releases of 'cross' have broken CI. So for now, we
# pin it. We also use their pre-compiled binary releases because cross
# has over 100 dependencies and takes a bit to compile.
dir="$RUNNER_TEMP/cross-download"
mkdir "$dir"
echo "$dir" >> $GITHUB_PATH
cd "$dir"
curl -LO "https://github.com/cross-rs/cross/releases/download/$CROSS_VERSION/cross-x86_64-unknown-linux-musl.tar.gz"
tar xf cross-x86_64-unknown-linux-musl.tar.gz
echo "CARGO=cross" >> $GITHUB_ENV
echo "TARGET_FLAGS=--target ${{ matrix.target }}" >> $GITHUB_ENV
echo "TARGET_DIR=./target/${{ matrix.target }}" >> $GITHUB_ENV
- name: Show command used for Cargo
run: |
echo "cargo command is: ${{ env.CARGO }}"
echo "target flag is: ${{ env.TARGET_FLAGS }}"
echo "target dir is: ${{ env.TARGET_DIR }}"
- name: Build ripgrep and all crates
run: ${{ env.CARGO }} build --verbose --workspace ${{ env.TARGET_FLAGS }}
- name: Build ripgrep with PCRE2
run: ${{ env.CARGO }} build --verbose --workspace --features pcre2 ${{ env.TARGET_FLAGS }}
# This is useful for debugging problems when the expected build artifacts
# (like shell completions and man pages) aren't generated.
- name: Show build.rs stderr
shell: bash
run: |
set +x
stderr="$(find "${{ env.TARGET_DIR }}/debug" -name stderr -print0 | xargs -0 ls -t | head -n1)"
if [ -s "$stderr" ]; then
echo "===== $stderr ===== "
cat "$stderr"
echo "====="
fi
set -x
- name: Run tests with PCRE2 (sans cross)
if: matrix.target == ''
run: ${{ env.CARGO }} test --verbose --workspace --features pcre2 ${{ env.TARGET_FLAGS }}
- name: Run tests without PCRE2 (with cross)
# These tests should actually work, but they almost double the runtime.
# Every integration test spins up qemu to run 'rg', and when PCRE2 is
# enabled, every integration test is run twice: one with the default
# regex engine and once with PCRE2.
if: matrix.target != ''
run: ${{ env.CARGO }} test --verbose --workspace ${{ env.TARGET_FLAGS }}
- name: Test zsh shell completions (Unix, sans cross)
# We could test this when using Cross, but we'd have to execute the
# 'rg' binary (done in test-complete) with qemu, which is a pain and
# doesn't really gain us much. If shell completion works in one place,
# it probably works everywhere.
if: matrix.target == '' && !startsWith(matrix.os, 'windows')
shell: bash
run: ci/test-complete
- name: Print hostname detected by grep-cli crate
shell: bash
run: ${{ env.CARGO }} test --manifest-path crates/cli/Cargo.toml ${{ env.TARGET_FLAGS }} --lib print_hostname -- --nocapture
- name: Print available short flags
shell: bash
run: ${{ env.CARGO }} test --bin rg ${{ env.TARGET_FLAGS }} flags::defs::tests::available_shorts -- --nocapture
# Setup and compile on the wasm32-wasip1 target
wasm:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- name: Add wasm32-wasip1 target
run: rustup target add wasm32-wasip1
- name: Basic build
run: cargo build --verbose
rustfmt:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
components: rustfmt
- name: Check formatting
run: cargo fmt --all --check
docs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- name: Check documentation
env:
RUSTDOCFLAGS: -D warnings
run: cargo doc --no-deps --document-private-items --workspace
fuzz_testing:
name: Compile Fuzz Test Targets
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install required packages (Ubuntu)
run: |
sudo apt-get update
sudo apt-get install g++ --yes
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- name: Install Fuzzer
run: cargo install cargo-fuzz
working-directory: fuzz
- name: Verify fuzz targets build
run: cargo check
working-directory: fuzz
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: ci on: pull_request: push: branches: - master schedule: - cron: '00 01 * * *' # The section is needed to drop write-all permissions that are granted on # `schedule` event. By specifying any permission explicitly all others are set # to none. By using the principle of least privilege the damage a compromised # workflow can do (because of an injection or compromised third party tool or # action) is restricted. Currently the workflow doesn't need any additional # permission except for pulling the code. Adding labels to issues, commenting # on pull-requests, etc. may need additional permissions: # # Syntax for this section: # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions # # Reference for how to assign permissions on a job-by-job basis: # https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs # # Reference for available permissions that we can enable if needed: # https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token permissions: # to fetch code (actions/checkout) contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: test: timeout-minutes: 30 name: test env: # For some builds, we use cross to test on 32-bit and big-endian # systems. CARGO: cargo # When CARGO is set to CROSS, this is set to `--target matrix.target`. # Note that we only use cross on Linux, so setting a target on a # different OS will just use normal cargo. TARGET_FLAGS: # When CARGO is set to CROSS, TARGET_DIR includes matrix.target. TARGET_DIR: ./target # Bump this as appropriate. We pin to a version to make sure CI # continues to work as cross releases in the past have broken things # in subtle ways. CROSS_VERSION: v0.2.5 # Emit backtraces on panics. RUST_BACKTRACE: 1 runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - build: pinned os: ubuntu-latest rust: 1.85.0 - build: stable os: ubuntu-latest rust: stable - build: beta os: ubuntu-latest rust: beta - build: nightly os: ubuntu-latest rust: nightly - build: stable-musl os: ubuntu-latest rust: stable target: x86_64-unknown-linux-musl - build: stable-x86 os: ubuntu-latest rust: stable target: i686-unknown-linux-gnu - build: stable-aarch64 os: ubuntu-latest rust: stable target: aarch64-unknown-linux-gnu - build: stable-aarch64-musl os: ubuntu-latest rust: stable target: aarch64-unknown-linux-musl - build: stable-arm-gnueabihf os: ubuntu-latest rust: stable target: armv7-unknown-linux-gnueabihf - build: stable-arm-musleabihf os: ubuntu-latest rust: stable target: armv7-unknown-linux-musleabihf - build: stable-arm-musleabi os: ubuntu-latest rust: stable target: armv7-unknown-linux-musleabi - build: stable-powerpc64 os: ubuntu-latest rust: stable target: powerpc64-unknown-linux-gnu - build: stable-s390x os: ubuntu-latest rust: stable target: s390x-unknown-linux-gnu - build: stable-riscv64 os: ubuntu-latest rust: stable target: riscv64gc-unknown-linux-gnu - build: macos os: macos-latest rust: nightly - build: win-msvc os: windows-latest rust: nightly - build: win-gnu os: windows-latest rust: nightly-x86_64-gnu - build: winaarch64-msvc os: windows-11-arm rust: nightly steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install packages (Ubuntu) if: matrix.os == 'ubuntu-latest' run: | ci/ubuntu-install-packages - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - name: Use Cross if: matrix.os == 'ubuntu-latest' && matrix.target != '' run: | # In the past, new releases of 'cross' have broken CI. So for now, we # pin it. We also use their pre-compiled binary releases because cross # has over 100 dependencies and takes a bit to compile. dir="$RUNNER_TEMP/cross-download" mkdir "$dir" echo "$dir" >> $GITHUB_PATH cd "$dir" curl -LO "https://github.com/cross-rs/cross/releases/download/$CROSS_VERSION/cross-x86_64-unknown-linux-musl.tar.gz" tar xf cross-x86_64-unknown-linux-musl.tar.gz echo "CARGO=cross" >> $GITHUB_ENV echo "TARGET_FLAGS=--target ${{ matrix.target }}" >> $GITHUB_ENV echo "TARGET_DIR=./target/${{ matrix.target }}" >> $GITHUB_ENV - name: Show command used for Cargo run: | echo "cargo command is: ${{ env.CARGO }}" echo "target flag is: ${{ env.TARGET_FLAGS }}" echo "target dir is: ${{ env.TARGET_DIR }}" - name: Build ripgrep and all crates run: ${{ env.CARGO }} build --verbose --workspace ${{ env.TARGET_FLAGS }} - name: Build ripgrep with PCRE2 run: ${{ env.CARGO }} build --verbose --workspace --features pcre2 ${{ env.TARGET_FLAGS }} # This is useful for debugging problems when the expected build artifacts # (like shell completions and man pages) aren't generated. - name: Show build.rs stderr shell: bash run: | set +x stderr="$(find "${{ env.TARGET_DIR }}/debug" -name stderr -print0 | xargs -0 ls -t | head -n1)" if [ -s "$stderr" ]; then echo "===== $stderr ===== " cat "$stderr" echo "=====" fi set -x - name: Run tests with PCRE2 (sans cross) if: matrix.target == '' run: ${{ env.CARGO }} test --verbose --workspace --features pcre2 ${{ env.TARGET_FLAGS }} - name: Run tests without PCRE2 (with cross) # These tests should actually work, but they almost double the runtime. # Every integration test spins up qemu to run 'rg', and when PCRE2 is # enabled, every integration test is run twice: one with the default # regex engine and once with PCRE2. if: matrix.target != '' run: ${{ env.CARGO }} test --verbose --workspace ${{ env.TARGET_FLAGS }} - name: Test zsh shell completions (Unix, sans cross) # We could test this when using Cross, but we'd have to execute the # 'rg' binary (done in test-complete) with qemu, which is a pain and # doesn't really gain us much. If shell completion works in one place, # it probably works everywhere. if: matrix.target == '' && !startsWith(matrix.os, 'windows') shell: bash run: ci/test-complete - name: Print hostname detected by grep-cli crate shell: bash run: ${{ env.CARGO }} test --manifest-path crates/cli/Cargo.toml ${{ env.TARGET_FLAGS }} --lib print_hostname -- --nocapture - name: Print available short flags shell: bash run: ${{ env.CARGO }} test --bin rg ${{ env.TARGET_FLAGS }} flags::defs::tests::available_shorts -- --nocapture # Setup and compile on the wasm32-wasip1 target wasm: timeout-minutes: 30 runs-on: latchkey-small steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: stable - name: Add wasm32-wasip1 target run: rustup target add wasm32-wasip1 - name: Basic build run: cargo build --verbose rustfmt: timeout-minutes: 30 runs-on: latchkey-small steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: stable components: rustfmt - name: Check formatting run: cargo fmt --all --check docs: timeout-minutes: 30 runs-on: latchkey-small steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: stable - name: Check documentation env: RUSTDOCFLAGS: -D warnings run: cargo doc --no-deps --document-private-items --workspace fuzz_testing: timeout-minutes: 30 name: Compile Fuzz Test Targets runs-on: latchkey-small steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install required packages (Ubuntu) run: | sudo apt-get update sudo apt-get install g++ --yes - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: stable - name: Install Fuzzer run: cargo install cargo-fuzz working-directory: fuzz - name: Verify fuzz targets build run: cargo check working-directory: fuzz
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.
- Add a job timeout so a hung step cannot burn hours of runner time.
1 third-party action is referenced by a movable tag. Pin it 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
- Network fetches
This workflow runs 5 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.