name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
no-dependencies:
name: No Dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Reject unconditional normal and build dependencies
shell: bash
run: |
metadata=$(cargo metadata --format-version 1 --no-deps)
shipped=$(echo "$metadata" | jq -r '
.packages[] | select(.name == "unparser-shared") | .dependencies[]
| select(.kind != "dev" and (.optional != true)) | "\(.kind // "normal"): \(.name)"')
optional=$(echo "$metadata" | jq -r '
.packages[] | select(.name == "unparser-shared") | .dependencies[]
| select(.kind != "dev" and .optional == true) | .name')
dev=$(echo "$metadata" | jq -r '
.packages[] | select(.name == "unparser-shared") | .dependencies[]
| select(.kind == "dev") | .name')
if [ -n "$dev" ]; then
echo "Dev-dependencies (not shipped to consumers, allowed):"
echo "$dev" | sed 's/^/ /'
fi
if [ -n "$optional" ]; then
echo "Optional dependencies (gated behind a feature, not linked unless a consumer opts in):"
echo "$optional" | sed 's/^/ /'
fi
if [ -n "$shipped" ]; then
echo "$shipped" | sed 's/^/ /'
echo "::error::unparser-shared's crate root must have no unconditional normal or build dependencies — it is linked statically into three published cdylibs, so a dependency here is a dependency in all of them. A new capability's dependency must be optional = true and gated behind its own feature. See the crate docs' Scope section."
exit 1
fi
echo "No unconditional normal or build dependencies."
version-sync:
name: Version Consistency
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Verify Cargo.toml and html_root_url agree
shell: bash
run: |
manifest=$(grep -m1 '^version = ' Cargo.toml | sed 's/.*"\(.*\)".*/\1/')
doc_url=$(grep -m1 'html_root_url' src/lib.rs | sed 's|.*/unparser-shared/\([^"]*\)".*|\1|')
echo "Cargo.toml: $manifest"
echo "lib.rs html_root_url: $doc_url"
if [ "$manifest" != "$doc_url" ]; then
echo "::error::Version mismatch — bump Cargo.toml and src/lib.rs's html_root_url together, or docs.rs will resolve intra-doc links against the wrong version."
exit 1
fi
echo "Versions match: $manifest"
test:
name: Test
needs: [no-dependencies, version-sync]
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run tests (default features)
run: cargo test
- name: Run tests (all features)
run: cargo test --all-features
msrv:
name: MSRV
needs: [no-dependencies, version-sync]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Read rust-version from the manifest
id: msrv
shell: bash
run: |
version=$(grep -m1 '^rust-version = ' Cargo.toml | sed 's/.*"\(.*\)".*/\1/')
echo "toolchain=$version" >> "$GITHUB_OUTPUT"
echo "Declared MSRV: $version"
- name: Install the declared toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ steps.msrv.outputs.toolchain }}
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-msrv-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-msrv-
- name: Run tests (default features)
run: cargo test
- name: Run tests (all features)
run: cargo test --all-features
wasm:
name: Build for wasm32
needs: [no-dependencies, version-sync]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: ${{ runner.os }}-cargo-wasm-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-wasm-
- name: Build
run: cargo build --target wasm32-unknown-unknown
- name: Build for wasm32 (refine feature)
run: cargo build --target wasm32-unknown-unknown --features refine
lint:
name: Lint
needs: [no-dependencies, version-sync]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Check formatting
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --all-targets --all-features -- -D warnings
docs:
name: Docs
needs: [no-dependencies, version-sync]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Build documentation (default features)
env:
RUSTDOCFLAGS: -D warnings
run: cargo doc --no-deps
- name: Build documentation (all features)
env:
RUSTDOCFLAGS: -D warnings
run: cargo doc --all-features --no-deps
security-audit:
name: Security Audit
needs: [no-dependencies, version-sync]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install cargo-audit
uses: taiki-e/install-action@v2
with:
tool: cargo-audit
- name: Audit dependencies
run: cargo audit
package:
name: Package
needs: [test, lint, docs]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Verify the crate packages cleanly
run: cargo publish --dry-run
- name: Show what would ship
run: cargo package --list