name: CI/CD
on:
push:
branches: [main, dev, 'release/**']
tags: ["v*", "v*-*"] pull_request:
branches: [main, dev]
workflow_dispatch:
schedule:
- cron: "0 0 * * 0"
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-D warnings"
CARGO_INCREMENTAL: 0
RUSTUP_MAX_RETRIES: 3
jobs:
lint:
name: Lint & Format
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
components: clippy, rustfmt, llvm-tools-preview
- name: Cache cargo registry
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Check formatting
run: cargo fmt --all -- --check
- name: Clippy (all features)
run: cargo clippy --all-targets --all-features -- -D warnings
test:
name: Test Suite
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: true
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [stable, 1.91.0] include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@v6
- name: Install Rust (${{ matrix.rust }})
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-${{ matrix.rust }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ matrix.rust }}-
- name: Run tests
run: cargo test --all-features --locked --no-fail-fast
- name: Run doctests
run: cargo test --doc --locked
- name: Build release
run: cargo build --release --locked --target ${{ matrix.target }}
security:
name: Security Scan
runs-on: ubuntu-latest
needs: test
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Security audit
uses: rustsec/audit-check@v2.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
build-matrix:
name: Build Artifacts
runs-on: ${{ matrix.os }}
needs: [lint, test, security]
permissions:
actions: write
contents: read
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact_name: rfgrep-linux-x86_64
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
artifact_name: rfgrep-linux-musl
- os: macos-latest
target: x86_64-apple-darwin
artifact_name: rfgrep-macos-x86_64
- os: macos-latest
target: aarch64-apple-darwin
artifact_name: rfgrep-macos-arm64
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact_name: rfgrep-windows-x86_64.exe
steps:
- uses: actions/checkout@v6
- name: Install Rust + target
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
target: ${{ matrix.target }}
- name: Install musl-tools
if: matrix.target == 'x86_64-unknown-linux-musl'
run: |
sudo apt-get update
sudo apt-get install -y musl-tools
- name: Build release
run: cargo build --release --locked --target ${{ matrix.target }}
- name: Strip symbols (Unix)
if: runner.os != 'Windows'
run: |
strip target/${{ matrix.target }}/release/rfgrep* || true
- name: Package artifact
run: |
mkdir -p release
cp target/${{ matrix.target }}/release/rfgrep* release/
tar czf ${{ matrix.artifact_name }}.tar.gz -C release .
- name: Upload artifacts
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.artifact_name }}.tar.gz
release:
name: Create Release
runs-on: ubuntu-latest
needs: [ build-matrix ]
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v6
- name: Download all artifacts
uses: actions/download-artifact@v7
with:
path: artifacts
pattern: rfgrep-*
merge-multiple: true
- name: Create checksums
run: |
cd artifacts
sha256sum * > SHA256SUMS
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
artifacts/*
generate_release_notes: true
draft: false
prerelease: ${{ contains(github.ref, '-') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish-crates-io:
name: Publish to Crates.io
runs-on: ubuntu-latest
needs: test
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Publish
run: |
cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
docker:
name: Build Docker Image
runs-on: ubuntu-latest
needs: build-matrix
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
ghcr.io/${{ github.repository_owner }}/rfgrep:${{ github.ref_name }}
ghcr.io/${{ github.repository_owner }}/rfgrep:latest
cache-from: type=gha
cache-to: type=gha,mode=max
notify:
name: Notify Status
runs-on: ubuntu-latest
needs: [release, publish-crates-io, docker]
if: always()
steps:
- name: Notify Slack
if: failure()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
channel: '#ci'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}