Contributing Guide
A PR opened against main gets sent back: main only receives release merges, and day-to-day work goes to dev. There are a few rules like that one — none of them live in the code, and you meet them by tripping over them.
Before you start
- Fork the repo and clone it locally.
- Development happens on the
devbranch;mainonly accepts release merges — target your PR atdev, notmain.devis merged intomainand tagged only at release time (see CHANGELOG.md). - File bugs / feature requests through one of the three GitHub Issue templates (Bug report / Feature request / Question) — the repo has blank issues disabled. Do not open a public issue for a security vulnerability; see "Security issues" below.
Local development environment
SmartAdmin has two halves: backend/ (.NET 10 kernel + sample host + tests) and web/ (the Vue 3 + Naive UI frontend), and you can change either independently or both together. web/ is an npm workspace. packages/admin is the source of the frontend package smart-admin-web, which uses #/ to point at its own src/. template is the thin shell app: in dev it runs straight off the kernel source with hot reload, and the e2e suite runs against it too.
Backend (run from the repo root; the solution file is .slnx, not .sln):
dotnet build backend/SmartAdmin.slnx -c Release
dotnet test backend/SmartAdmin.slnx # xUnit v3 + WebApplicationFactory, defaults to SQLite
dotnet test backend/SmartAdmin.slnx -- --filter-class "*DataScopeTests*" # run a single test class
dotnet run --project backend/samples/MinimalHost # zero-config run, http://localhost:5100The suite runs on Microsoft.Testing.Platform, and everything after -- goes straight to the test executable itself, so filtering is --filter-class (repeatable, OR'd together) rather than the VSTest --filter "FullyQualifiedName~...".
Running tests against MySQL (matches one leg of the CI matrix):
SMART_TEST_DBTYPE=MySql SMART_TEST_MYSQL="Server=127.0.0.1;Port=3306;User ID=root;Password=root;AllowPublicKeyRetrieval=true;SSL Mode=None;" dotnet test backend/SmartAdmin.slnxFrontend (run from the web/ directory):
npm run dev # the template's Vite, :5173, proxies /api, /openapi and /hub to backend :5100 (override with SMART_API_TARGET)
npm run build # builds the package, then the template; each runs vue-tsc --noEmit && vite build
npm run lint # oxlint (lint:fix to autofix)
npm run format:check # prettier, check only (format to autofix)
npm test # vitest, the package's unit tests
npm run typecheck # vue-tsc --noEmit, package and template
npm run test:e2e # Playwright, starts its own backend and template
npm run gen:api # regenerate the package's schema.d.ts from a running backend's /openapi/v1.json
npm run gen:icons # regenerate the offline icon subset after the package uses a new ph:* icon (a unit test catches a stale subset)If running both sides separately is a hassle, dev-start.bat at the repo root launches backend + frontend together in two separate windows (running npm install in web/ before Vite starts); dev-stop.bat stops them.
Don't hand-edit schema.d.ts
web/packages/admin/src/api/schema.d.ts is a contract file generated from the backend's OpenAPI, exported by the package as KernelPaths. If you change an endpoint, run npm run gen:api first (requires the backend to be running) — don't hand-write this file.
Centralized package versioning
Backend dependency versions are all collected in backend/Directory.Packages.props's <PackageVersion> — add or bump dependencies there, not by pinning a version in an individual .csproj. Shared build/NuGet metadata (author, repo URL, license, etc.) lives in backend/Directory.Build.props.
Commit messages: Chinese Conventional Commits
Code, comments, docs and git commits are all in Chinese, formatted as type(scope): 主题. type and scope stay lowercase English, because the release tooling parses them against a fixed vocabulary:
fix(web): 无权限用户的按钮不再渲染
feat(backend): 新增定向通知投递
docs: 根目录配置与脚本文件的注释翻译
refactor(services): 登录流程拆成可覆写的虚方法步骤Common type values: feat / fix / docs / refactor / test / chore. scope is usually web / backend, or a more specific module name. The full convention is in Commit Convention.
Running tests: both legs need to be green
CI runs on every PR (the four-database backend matrix, the frontend, the template smoke test); running the checks locally first saves a round trip. Before touching backend/**, run dotnet build backend/SmartAdmin.slnx -c Release and dotnet test backend/SmartAdmin.slnx. The default leg uses SQLite, and TestDb.cs derives an isolated database per test from env vars like SMART_TEST_DBTYPE, so tests don't interfere with each other; when you touch the data layer, run the MySQL leg too by setting SMART_TEST_DBTYPE=MySql and a SMART_TEST_MYSQL connection string. The Redis contract tests in RedisCacheTests skip silently unless SMART_TEST_REDIS points at a Redis, so set it when you change caching.
For web/**, run npm run lint → npm run format:check → npm test (vitest) → npm run build (build already includes vue-tsc type checking, so there's no need to run typecheck separately) — the same checks as CI's frontend job. If you change templates/**, also run pwsh templates/smoke-test.ps1: it packs the kernel into a local feed, scaffolds dotnet new smart-app against it, then builds and runs the result — the first command a consumer runs after getting the package.
The replaceability contract tests are a contract, not an ordinary test
ReplaceabilityTests locks in the replaceability guarantees around TryAdd coverage, virtual-method overriding, and business-assembly mounting. For the full, current list of exactly what it guarantees, see The Replaceability Model. When you change DI registration or SmartAdminSetup-related code and this suite goes red, it usually means you've broken a consumer's replacement path — don't bypass or delete the tests; figure out which guarantee got broken first.
PR workflow
- Branch off
devfor your feature. - Keep each change focused on one thing; follow the commit conventions above.
- Run the build/test/lint for the relevant side locally.
- Open a PR targeting
dev; CI runs the checks for you, so just say in the description which ones you ran locally. - If you're using Claude Code or another AI agent to help develop, the repo has conventions for issue triage, domain docs, and business-development skills — see Agent Skills and AI-Assisted Development.
Security issues
Do not report security vulnerabilities through a public issue. SmartAdmin distributes as NuGet and npm packages with built-in auth, RBAC, and multi-org data permissions — a public report would disclose a 0-day to every downstream consumer before a patch exists.
Please use GitHub's private vulnerability reporting instead: open a security advisory, visible to maintainers only. Maintainers will respond within 7 days and coordinate the fix and disclosure timeline with you. See SECURITY.md for details.
License
SmartAdmin is open-sourced under the Apache License 2.0; code you submit is contributed under the same license by default.