Contributing
July 10, 2026 · View on GitHub
Thank you for your interest in contributing to yew-nav-link. This document
walks you from a fresh clone all the way to a merged pull request. Anything
that touches the public API also touches docs/REQUIREMENTS.md and
docs/ARCHITECTURE.md — keep all three in sync.
Code of conduct
Participation is governed by CODE_OF_CONDUCT.md.
Report unacceptable behaviour via the contact in that file.
Dev environment
You need:
- Rust stable 1.96+ plus a
nightlytoolchain (used only forrustfmt):rustup default stable rustup toolchain install nightly --component rustfmt rustup target add wasm32-unknown-unknown trunkfor the demo crate:cargo install --locked trunk- The CI tooling, mirrored locally so you can reproduce the pipeline:
cargo install --locked cargo-deny cargo-audit cargo-llvm-cov cargo-nextest \ git-cliff pip install --user reuse
The pre-commit hook in .hooks/pre-commit invokes most of these, so a single
git commit runs fmt --check, clippy --pedantic --nursery,
cargo deny check, cargo audit, and actionlint.
To wire it in once per clone:
git config core.hooksPath .hooks
Common tasks
| Task | Command |
|---|---|
| Build | cargo build --workspace --all-features |
| Format check | cargo +nightly fmt --all -- --check |
| Lint | cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic -W clippy::nursery |
| Tests | cargo nextest run --all-features |
| Doc tests | cargo test --doc --all-features |
| Coverage report | cargo llvm-cov --all-features --html |
| Build the demo | cd example && trunk build --release |
| Serve the demo | cd example && trunk serve then open http://127.0.0.1:3000 |
| Generate CHANGELOG preview | git-cliff --unreleased |
| Verify SPDX headers | reuse lint |
Workflow
1. Open or pick an issue
- Bugs: use the bug report template.
- Features: use the feature request template.
- Security: do not open a public issue — see
SECURITY.md. - Questions: GitHub Discussions or Issues with the
questionlabel. - Browse
good first issuefor bounded entry points.
2. Create a branch
Branch names match the issue number:
git checkout -b 123
3. Commit format
git commit -m "#123 feat: add custom class support"
Format is #<issue> <type>: <description> — conventional-commit type with a
colon, no (scope). Breaking changes carry a ! on the type (feat!,
fix!, refactor!).
| Type | Use for | Triggers in CHANGELOG |
|---|---|---|
feat | New public API | yes — Features |
fix | Bug fix | yes — Bug Fixes |
docs | Markdown / rustdoc only | yes — Documentation |
refactor | Internal restructuring, no behaviour change | yes — Refactoring |
ci | CI / release pipeline | yes — CI |
test | Test additions or modifications | no |
chore | Maintenance, dependency bumps, tooling | no |
git-cliff and release-plz read these prefixes (cliff.toml,
release-plz.toml), normalising the #<N> <type>: <desc> form into a
conventional-commit subject for the CHANGELOG and the version bump.
4. Open a pull request
- Title: conventional-commit form matching the commit, e.g.
#123 feat: add custom class support. The squash-merge uses the PR title as the subject onmain, and release-plz parses it for the changelog and version bump — a bare issue number would be dropped. - Body: must include
Closes #123so the issue auto-closes on merge. - Reviews: there is no required-reviewer rule on
main, but every PR must pass theCI Successaggregate status check before merge — that's enforced by branch protection. - Merge style: squash, with branch auto-deleted on merge. The PR title
becomes the conventional-commit subject on
main; the PR body becomes the commit message body. We keepmainlinear; force-pushes tomainand branch deletion are blocked. Rebase merge stays allowed for the rare case where intermediate commits carry independent value. Seedocs/BRANCHING.mdfor the full policy.
Code standards
| Rule | Requirement |
|---|---|
unsafe | Forbidden in src/ and tests/. |
unwrap() / expect() | Forbidden outside #[cfg(test)]. Use ?, Option::map_or_else, unwrap_or. |
Unnecessary clone() | Avoid. Pass references. |
| Public items | Every pub item carries a /// doc comment, plus a doctest where it makes sense. |
| Line width | 99 characters (max_width in .rustfmt.toml). |
| Trailing commas | Never (trailing_comma = "Never" in .rustfmt.toml). |
| Edition | Rust 2024. |
| MSRV | 1.96. |
CI overview
.github/workflows/ci.yml runs the following jobs on every PR. The
ci-success aggregate must come back green for branch protection to allow
merge.
| Job | Required | Notes |
|---|---|---|
Extract MSRV | yes | reads rust-version from Cargo.toml |
Check | yes | matrix: 3 toolchains × 3 OSes; also runs clippy (all + no-default features) |
Format | yes | nightly rustfmt --check |
Documentation | yes | cargo doc --all-features with RUSTDOCFLAGS=-D warnings |
Public API | yes | cargo public-api -sss must match docs/public-api.txt |
Semver Checks | yes | cargo semver-checks check-release against the last crates.io release |
Book | yes | mdbook build docs against docs/book.toml |
Security | yes | cargo deny check + cargo audit |
REUSE Compliance | yes | reuse lint |
Test | yes (skip allowed) | cargo nextest + doctests |
WASM Tests | yes | wasm-pack test --headless --chrome --firefox --test wasm |
Coverage | yes (skip allowed) | cargo llvm-cov → Codecov upload |
Benchmarks | yes (skip allowed) | cargo bench --no-run |
Example WASM build | yes | trunk build --release against example/ |
E2E | yes | Playwright suite under example/e2e/ on chromium + firefox |
Lighthouse | yes | thresholds: perf 0.85, a11y 0.9, best-practices 0.9, SEO 0.9 |
Actionlint | yes | lints all workflow YAML |
Changelog | yes (skip allowed) | runs git-cliff |
Three more workflows handle deployment side-effects on push to main:
.github/workflows/pages.ymldeploys the demo and the mdBook to https://raprogramm.github.io/yew-nav-link/ and https://raprogramm.github.io/yew-nav-link/book/..github/workflows/release-plz.ymlruns the release loop (see below)..github/workflows/release-attestations.ymlfires onrelease: publishedand attaches a CycloneDX SBOM plus a Sigstore-signed SLSA build-provenance attestation to the GitHub Release.
Releasing
Releases are fully autonomous from a contributor's perspective. The
maintainer does not edit Cargo.toml or CHANGELOG.md by hand —
release-plz does it. The loop:
- Land any typed commit on
main(feat,fix,docs,refactor,ci,deps—testandchoreare skipped). Release-plzworkflow opens (or updates) a single release PR titledchore: release vX.Y.Z. The version bump andCHANGELOG.mdsection are derived from the conventional commits since the last tag.CIruns on the release PR automatically — the workflow authenticates as the dedicated GitHub Apprelease-plz-yew-nav-link, so PRs it opens trigger downstream workflows the same way human PRs do. (The defaultGITHUB_TOKENdoes not; that is GitHub's anti-recursion policy.)- Maintainer reviews and squash-merges the release PR.
Release-plzworkflow runs again on the merge, tagsvX.Y.Z, publishes to crates.io, and creates the GitHub Release.Release attestationsfires on therelease: publishedevent, generates the SBOM and signed provenance, and attaches them.
Step 4 is the only human action in the chain. There is no manual
version-bump path; the Cargo.toml and CHANGELOG.md mutations are
exclusively the release PR's diff.
The App identity is configured once per repository — see
docs/RELEASE_PLZ_APP_SETUP.md. Two
repository secrets carry the credentials: RELEASE_PLZ_APP_ID and
RELEASE_PLZ_APP_PRIVATE_KEY. The App has exactly two repository
permissions: Contents: read & write and Pull requests: read &
write, scoped to this single repo.
For yanking, pre-releases, and the older manual-backfill recipe, see
RELEASE.md.
Recognition
All contributors are listed in AUTHORS.md. When your first
PR merges, add yourself to that list in a follow-up patch (or ask the
maintainer to do it).
References
README.md— overview and quick start.CODE_OF_CONDUCT.mdSECURITY.mddocs/REQUIREMENTS.md— formal functional and non-functional requirements.docs/ARCHITECTURE.md— design rationale.docs/ROADMAP.md— trajectory toward 0.10 and 1.0.
Questions?
Open a discussion or an issue with the question label.