oxipng/.github/workflows/oxipng.yml
Alejandro González a8846b897d Make CI Clippy static analysis checks more robust
I have identified two potential improvements for how we perform static analysis
on our code in our CI pipeline:

- The `giraffate/clippy-action` we currently use has not been updated to Node
  20, and GitHub has repeatedly indicated that they will phase out actions that
  do not support the latest Node versions. Despite my efforts to help with the
  update by submitting a pull request upstream, it has been ignored for months
  despite its perceived ease of review, raising concerns about the ongoing
  maintenance of the action. This situation suggests we should explore
  alternative methods for integrating Clippy with GitHub's UI.
- As evidenced by PR 632, thoroughly testing Rust crates for every possible
  feature combination is often overlooked due to the tedious nature of the task.
  Our current CI setup only checks two feature combinations, which is far from
  comprehensive.

To address the first improvement, these changes drop `clippy-action` entirely in
favor of utilizing GitHub's native CodeQL SARIF (Static Analysis Results
Interchange Format) file integration. Since Clippy cannot directly output lints
in SARIF, `clippy-sarif` is used to convert Clippy's JSON output to SARIF.
Additionally, `sarif-fmt` is added to turn SARIF into a human-friendly display
format in the workflow run logs.

For the second improvement, let's use `cargo hack` with the `--feature-powerset`
flag to run Clippy for every possible feature combination. This approach strikes
a good balance between CI runtime and thoroughness, as the number of feature
combinations grows superlinearly with the number of features: running `cargo
nextest` for every powerset element would lead to excessively long CI times.
2024-07-11 15:20:53 +12:00

190 lines
6 KiB
YAML

name: oxipng
on:
push:
pull_request:
types:
- opened
- synchronize
workflow_dispatch:
jobs:
ci:
name: CI
runs-on: ${{ matrix.os }}
timeout-minutes: 60
# Prevent tags and in-repo PRs from triggering this workflow more than once for a commit
if: github.ref_type != 'tag' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
strategy:
fail-fast: false
matrix:
target:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
- aarch64-unknown-linux-gnu
- aarch64-unknown-linux-musl
- x86_64-pc-windows-msvc
- i686-pc-windows-msvc
- x86_64-apple-darwin
- aarch64-apple-darwin
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-22.04
target-apt-arch: amd64
- target: x86_64-unknown-linux-musl
os: ubuntu-22.04
target-apt-arch: amd64
- target: aarch64-unknown-linux-gnu
os: ubuntu-22.04
target-apt-arch: arm64
- target: aarch64-unknown-linux-musl
os: ubuntu-22.04
target-apt-arch: arm64
- target: x86_64-pc-windows-msvc
os: windows-latest
- target: i686-pc-windows-msvc
os: windows-latest
- target: x86_64-apple-darwin
os: macos-12
- target: aarch64-apple-darwin
os: macos-14 # ARM64 runner
env:
CARGO_BUILD_TARGET: ${{ matrix.target }}
RUSTFLAGS: -Zlocation-detail=none
steps:
- name: Checkout source
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Ubuntu multiarch
if: startsWith(matrix.os, 'ubuntu') && matrix.target-apt-arch != 'amd64'
run: |
readonly DISTRO_CODENAME=jammy
sudo dpkg --add-architecture "${{ matrix.target-apt-arch }}"
sudo sed -i "s/^deb http/deb [arch=$(dpkg-architecture -q DEB_HOST_ARCH)] http/" /etc/apt/sources.list
sudo sed -i "s/^deb mirror/deb [arch=$(dpkg-architecture -q DEB_HOST_ARCH)] mirror/" /etc/apt/sources.list
for suite in '' '-updates' '-backports' '-security'; do
echo "deb [arch=${{ matrix.target-apt-arch }}] http://ports.ubuntu.com/ $DISTRO_CODENAME$suite main universe multiverse" | \
sudo tee -a /etc/apt/sources.list >/dev/null
done
- name: Install musl development files
if: endsWith(matrix.target, '-musl')
run: |
sudo apt-get -yq update
sudo apt-get -yq install musl-tools musl-dev:${{ matrix.target-apt-arch }}
- name: Install QEMU and AArch64 cross compiler
if: startsWith(matrix.target, 'aarch64-unknown-linux')
run: |
sudo apt-get -yq update
# libc6 must be present to run executables dynamically linked
# against glibc for the target architecture
sudo apt-get -yq install qemu-user gcc-aarch64-linux-gnu libc6:arm64
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@v2
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: nightly
targets: ${{ env.CARGO_BUILD_TARGET }}
components: clippy, rustfmt
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Install cargo-hack
if: matrix.target == 'x86_64-unknown-linux-gnu'
uses: taiki-e/install-action@cargo-hack
- name: Install clippy-sarif
if: matrix.target == 'x86_64-unknown-linux-gnu'
uses: taiki-e/install-action@v2
with:
tool: clippy-sarif
- name: Install sarif-fmt
if: matrix.target == 'x86_64-unknown-linux-gnu'
uses: taiki-e/install-action@v2
with:
tool: sarif-fmt
- name: Run rustfmt
if: matrix.target == 'x86_64-unknown-linux-gnu'
run: cargo fmt --check
- name: Run Clippy for all feature combinations
if: matrix.target == 'x86_64-unknown-linux-gnu'
run: >
cargo hack clippy --no-deps --all-targets --feature-powerset --exclude-features sanity-checks --message-format=json -- -D warnings
| clippy-sarif
| tee clippy-results.sarif
| sarif-fmt
- name: Run tests
run: |
cargo nextest run --release --features sanity-checks
cargo test --doc --release --features sanity-checks
- name: Build benchmarks
run: cargo bench --no-run
- name: Build docs
if: matrix.target == 'x86_64-unknown-linux-gnu'
run: cargo doc --release --no-deps
- name: Build CLI binary
run: cargo build --release
- name: Upload CLI binary as artifact
uses: actions/upload-artifact@v4
with:
name: Oxipng binary (${{ matrix.target }})
path: |
target/${{ env.CARGO_BUILD_TARGET }}/release/oxipng
target/${{ env.CARGO_BUILD_TARGET }}/release/oxipng.exe
- name: Upload analysis results to GitHub
uses: github/codeql-action/upload-sarif@v3
if: always() && matrix.target == 'x86_64-unknown-linux-gnu'
with:
sarif_file: clippy-results.sarif
category: clippy
msrv-check:
name: MSRV check
runs-on: ubuntu-latest
timeout-minutes: 30
# Prevent tags and in-repo PRs from triggering this workflow more than once for a commit
if: github.ref_type != 'tag' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
steps:
- name: Checkout source
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@v2
- name: Install MSRV Rust toolchain
uses: dtolnay/rust-toolchain@1.74.0
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Run tests
run: |
cargo nextest run --release --features sanity-checks
cargo test --doc --release --features sanity-checks