Set up GitHub Actions CI for Rust projects. Creates reusable workflows for clippy, rustfmt, tests, cargo-deny, and sqlx. Use when setting up CI, adding GitHub workflows, configuring Rust pipelines, or fixing CI issues in Rust projects.
Set up comprehensive CI for Rust projects using reusable workflows.
.github/workflows/
├── ci.yml # Orchestrator - triggers on push/PR, calls reusable workflows
├── _fmt.yml # Reusable: rustfmt check
├── _clippy.yml # Reusable: clippy lints
├── _test.yml # Reusable: cargo test
├── _deny.yml # Reusable: cargo-deny bans check (optional)
├── _sqlx.yml # Reusable: sqlx migrations + check (optional)
├── _e2e.yml # Reusable: end-to-end tests (optional)
└── _deploy.yml # Reusable: deployment (optional)
Naming convention: Underscore prefix (_workflow.yml) indicates reusable/private workflows.
Auto-detect project features:
migrations/ directory → suggest _sqlx.ymlCargo.toml for feature flags → suggest feature matrixe2e/ or tests/e2e/ directory → suggest _e2e.ymlAsk the user:
continue-on-error: true| Setting | Value |
|---------|-------|
| Toolchain action | actions-rust-lang/setup-rust-toolchain@v1 (NOT dtolnay) |
| Caching | Built-in via toolchain action (uses Swatinem/rust-cache) |
| Tool installation | cargo-binstall for additional tools |
| Clippy strictness | Always -D warnings |
| Concurrency | NO cancel-in-progress for PRs (want full failure context) |
on:
push:
branches: [main]
pull_request:
All PRs trigger CI. Only pushes to main (not feature branches).
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
fmt:
uses: ./.github/workflows/_fmt.yml
clippy:
uses: ./.github/workflows/_clippy.yml
test:
uses: ./.github/workflows/_test.yml
# Optional - add if cargo-deny is configured
deny:
uses: ./.github/workflows/_deny.yml
# Optional - add if project uses sqlx
sqlx:
uses: ./.github/workflows/_sqlx.yml
# Optional - add if e2e tests exist
e2e:
uses: ./.github/workflows/_e2e.yml
needs: [test] # Run after unit tests pass
# Optional - deployment after CI passes
deploy:
needs: [fmt, clippy, test] # Add other required jobs
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
uses: ./.github/workflows/_deploy.yml
secrets: inherit
name: Rustfmt
on:
workflow_call:
env:
CARGO_TERM_COLOR: always
jobs:
fmt:
name: Check Formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
components: rustfmt
- name: Check formatting
run: cargo fmt --all -- --check
name: Clippy
on:
workflow_call:
env:
CARGO_TERM_COLOR: always
jobs:
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
components: clippy
- name: Run clippy
run: cargo clippy --workspace --all-targets -- -D warnings
name: Test
on:
workflow_call:
env:
CARGO_TERM_COLOR: always
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
- name: Run tests
run: cargo test --workspace
name: Cargo Deny
on:
workflow_call:
jobs:
deny:
name: Cargo Deny
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install cargo-binstall
run: curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
- name: Install cargo-deny
run: cargo binstall --no-confirm cargo-deny
- name: Run cargo deny
run: cargo deny check bans
Note: Requires deny.toml in project root. Create minimal one:
[bans]
multiple-versions = "warn"
wildcards = "allow"
Only include if project has migrations/ directory.
name: SQLx Check
on:
workflow_call:
env:
CARGO_TERM_COLOR: always
jobs:
sqlx:
name: SQLx Check
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test_db
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/test_db
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
- name: Install cargo-binstall
run: curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
- name: Install sqlx-cli
run: cargo binstall --no-confirm sqlx-cli
- name: Run migrations
run: sqlx migrate run
- name: Check sqlx prepare
run: cargo sqlx prepare --workspace --check
Adjust migration path if not in root (e.g., cd crates/mydb && sqlx migrate run).
When project has multiple features worth testing separately:
# In _test.yml or _clippy.yml
jobs:
test:
name: Test (${{ matrix.features-name }})
runs-on: ubuntu-latest
strategy:
matrix:
include:
- features-name: "all"
features-args: "--all-features"
- features-name: "default"
features-args: ""
- features-name: "no-default"
features-args: "--no-default-features"
# Add specific feature combos as needed:
# - features-name: "feature-x-only"
# features-args: "--no-default-features --features feature-x"
steps:
# ... setup steps ...
- name: Run tests
run: cargo test --workspace ${{ matrix.features-args }}
When testing stable and nightly:
# In ci.yml - call the workflow twice with different inputs
jobs:
test-stable:
uses: ./.github/workflows/_test.yml
with:
rust-version: stable
test-nightly:
uses: ./.github/workflows/_test.yml
with:
rust-version: nightly
continue-on-error: true
# In _test.yml - accept rust-version input
on:
workflow_call:
inputs:
rust-version:
required: false
type: string
default: stable
continue-on-error:
required: false
type: boolean
default: false
jobs:
test:
name: Test (${{ inputs.rust-version }})
runs-on: ubuntu-latest
continue-on-error: ${{ inputs.continue-on-error }}
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: ${{ inputs.rust-version }}
- name: Run tests
run: cargo test --workspace
Add to _test.yml when tests need a database:
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test_db
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/test_db
steps:
# ... rest of steps
name: Deploy
on:
workflow_call:
secrets:
# Add required secrets
DEPLOY_TOKEN:
required: true
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Add deployment steps here
# Common patterns:
# - Fly.io: flyctl deploy
# - Docker: build and push to registry
# - AWS: deploy via CDK/Terraform
When setting up CI for a project:
.github/workflows/ directoryci.yml orchestrator_fmt.yml (always)_clippy.yml (always)_test.yml (always)migrations/ → add _sqlx.yml if present_deny.yml + deny.toml_e2e.yml if present_deploy.yml skeletonSearch for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer