Thanks for your interest in contributing! This document covers the local workflow, conventions, and CI gates a change must pass.
By participating in this project you agree to uphold its Code of Conduct. Not sure where to ask something? See SUPPORT.md for the right channel.
| Tool | Version |
|---|---|
| C++ compiler | C++17 (MSVC 2022 / GCC 9+ / Clang 10+) |
| CMake | 3.21+ |
| Conan | 2.x |
| Node.js | 18+ (frontends only) |
| PostgreSQL / Redis | 14+ / 7+ (optional — see memory-only tests below) |
The canonical build is Conan 2 + CMake presets — the same flow CI runs:
conan install . --output-folder=build/linux-release --build=missing \
-s build_type=Release -s compiler.cppstd=17
cmake --preset linux-release # or windows-msvc / macos-arm64
cmake --build --preset linux-release
ctest --test-dir build/linux-release --output-on-failureUseful variants:
- No external DB: configure with
-DFULLA_MEMORY_TESTS_ONLY=ONto run the suite against the in-memory storage backend (what Windows CI does). - Debug / sanitizers: use the
-debug,-asan,-tsanpreset variants (sanitizers are Linux/macOS only). - By category:
ctest -R Unit|Integration|E2E|Security|Performance(labels are defined intests/common/test_categories.h). manage.ps1/manage.shwrap the same flow as convenience commands.
Frontends:
cd frontends/admin && npm install && npm run dev # or frontends/user
npx playwright test # E2E- Async style: repository/service methods take
std::function<void(result)> &&callbackas the last parameter. Async continuations must captureauto self = shared_from_this()— never[this]or[&]. Never useCoroMapper. - ORM models (
libs/storage-postgres/src/models/**) are generated bydrogon_ctl— never hand-edit them; regenerate via the ORM scripts inscripts/backend/. - DB access goes through the async Mapper + Criteria combo; raw SQL is
reserved for DDL,
UPDATE ... RETURNING, and documented batch operations. - Errors are stable string codes (e.g.
AUTH_INVALID_CREDENTIALS) registered in theErrorCatalog— never bare integers. - Architecture: Domain-layer packages (
libs/common,libs/oauth2,libs/identity) must not include Drogon headers.tools/arch-guardenforces this in CI. - No emoji in code or program output (use
[+]/[-]/[!]).
Six log levels mirror Drogon/Trantor (trace < debug < info < warn < error < fatal). Full definitions and typical use cases are in docs/operate/observability.md §3.2. Quick guide:
| Level | When to use |
|---|---|
trace |
Function args/return values, SQL strings, per-iteration detail — deep debugging only |
debug |
Variable values, branch decisions, internal-state dumps — dev troubleshooting |
info |
Startup/shutdown, login, task completion, key flow checkpoints, audit-worthy normal events |
warn |
Recoverable failure, degraded-but-working path, default value used, resource near threshold |
error |
Single operation/request failed (DB error callback, unhandled exception), service still up |
fatal |
Service cannot continue (startup config/migration failure) — must be rare |
Rules:
- Domain layer must log through the
ILoggerport (fulla::common::ports::ILogger), not Drogon'sLOG_*macros, so tests can assert on output viaFakeLogger. The adapter/infra layers (libs/drogon,libs/storage-*,apps/server) may useLOG_*directly.LogLevel::Traceroutes toLOG_TRACE. - Pick the level by impact, not verbosity: a caught exception that still succeeds is
warn, noterror; a routine cache-miss or 401 is usuallydebug/info, notwarn. When an operation fails outright with no fallback, useerror. - Production runs at
infoby default;trace/debugare enabled on demand. Adjust viaapp.log.log_levelin the Drogon config (values:TRACE/DEBUG/INFO/WARN/ERROR/FATAL). - Test output is minimal by default:
ctestprints only failed tests' logs plus the pass/fail/total summary. Add--verbose(-V) to see every test, or-Q(manage.sh test-backend -q) for summary only.
Known audit residue: a handful of call sites remain at a level that's a policy judgement rather than a clear-cut bug (e.g. the
[METRIC]log-based metric emissions atinfo, routine 401/403 denials atwarn). These are intentionally left for a deliberate follow-up decision; when you touch such a site, consider whether its level matches the table above.
- Commits follow Conventional Commits:
feat:,fix:,docs:,test:,build:,ci:,refactor:(scopes welcome, e.g.feat(auth): ...). Release notes are generated from them. - Branch names:
feat/<topic>,fix/<topic>,docs/<topic>. - Target branch is
master; every PR must pass all CI checks before merge. CHANGELOG.mdentries are written in English only — the changelog feeds the GitHub release notes verbatim (curated breaking-change entries are spliced into them), so it must stay single-language.
The docs are bilingual: docs/ is the English canonical and
website/i18n/zh-CN/docusaurus-plugin-content-docs/current/ is the Chinese
mirror (identical layout/URLs; both feed fulla.dev).
A PR that changes a document under docs/ must update its Chinese
counterpart in the same PR (and vice versa). Full rules:
docs/documentation-governance.md §4.
A PR runs ci.yml + security.yml:
| Gate | What it checks |
|---|---|
| Static Checks | arch-guard layering, migration hygiene, api-diff (public SDK headers follow SemVer), test naming, manage.sh/manage.ps1 parity, OpenAPI validity |
| Build & Test | Linux (GCC + PostgreSQL + Redis), Windows (MSVC, memory storage), macOS (Clang ARM64) |
| SDK Smoke | examples/full-stack-host consumed via real find_package |
| Frontend | Vitest property tests |
| Security | secret & sensitive-file hygiene, dependency EOL check |
If you change public SDK headers (include/fulla/**) intentionally,
update tools/api-diff/api-baseline.txt in the same PR and call it out in
the description — breaking changes require a major version bump.
- Usage questions / discussions: GitHub Discussions (see SUPPORT.md for the full routing table).
- Bugs / features: open a GitHub issue with reproduction steps.
- Security vulnerabilities: do not open a public issue — see SECURITY.md.