Contributing
First Time Setup
Prerequisites
| Tool | Version | For |
|---|---|---|
| Rust | stable, >= 1.95 (rust-toolchain.toml) | aAPI, eDd server, eDb, dApp backend |
| Bun | 1.4.2 (CI) | wApp, eDd client, dApp frontend |
| Docker | with Compose | eDb, images |
sqlx-cli | 0.9.0 | eDb migrations, .sqlx/ |
| Tauri prerequisites | Linux: webkit2gtk-4.1, libsoup-3 | dApp only |
mdbook, mdbook-mermaid | 0.5.4, 0.17.1 (CI) | docs only |
# For working with database stuffs
cargo install sqlx-cli --version 0.9.0 --locked --no-default-features --features rustls,postgres,sqlx-toml
# For working on docs/
cargo install mdbook@0.5.4 mdbook-mermaid@0.17.1 --locked # optional
Clone
git clone https://github.com/freeCodeCamp/exam-platform.git
cd exam-platform
bun install
docker network create platform
bun install also installs the Husky pre-commit hook.
Environment
Copy every example, then fill blanks:
for d in apps/auth-api apps/examiner-dashboard apps/web-app apps/desktop-app databases/examiner; do
cp -n "$d/.env.example" "$d/.env"
done
.env is read automatically only by:
Reads .env automatically | Does not |
|---|---|
| Bun (wApp, eDd client, dApp) | cargo run, cargo test, cargo clippy |
| Vite (dApp frontend) | rust-analyzer |
docker compose (from its directory) | |
sqlx-cli (from its directory) |
Rust apps read only the process environment. Export the app’s .env into the shell before cargo:
set -a; source apps/auth-api/.env; set +a
cargo run -p auth-api
Or use direnv: echo dotenv > .envrc && direnv allow in each app directory, then run cargo from there. .envrc is not in .gitignore; do not commit it.
| File | Set |
|---|---|
databases/examiner/.env | EDB_PASSWORD, EDB_MIGRATOR_PASSWORD, EDB_EDD_PASSWORD, EDB_AAPI_PASSWORD, and the same in EDB_ADMIN_URL, EDB_MIGRATOR_URL |
apps/auth-api/.env | EDB_DATABASE_URL (as aapi, with EDB_AAPI_PASSWORD) |
apps/examiner-dashboard/.env | EDB_DATABASE_URL (as edd, with EDB_EDD_PASSWORD), COOKIE_KEY (openssl rand -hex 64), GOOGLE_OAUTH_ID, GOOGLE_OAUTH_SECRET (Auth) |
| Others | Defaults work for ENVIRONMENT=development. SENTRY_DSN stays empty |
All variables: Environment Variables.
eDb
cd databases/examiner
docker compose up -d --wait
set -a; source .env; set +a
cargo run -p edb --bin edb-migrate
Run edb-migrate again after pulling new migrations. Reset with docker compose down -v.
Editor
VS Code: install recommended extensions (.vscode/extensions.json). Settings run clippy on save, set ENVIRONMENT=development for rust-analyzer, and link both Cargo workspaces.
Development
Run
| App | Command | Port |
|---|---|---|
| aAPI | cargo run -p auth-api | 127.0.0.1:13001 |
| eDd server | cargo run -p examiner-dashboard | 127.0.0.1:13002 |
| eDd client | bun --filter examiner-dashboard-client dev | 127.0.0.1:13005 |
| wApp | bun --filter web-app dev | 127.0.0.1:13003 |
| dApp | bun run dev:desktop | localhost:1420 |
| eDb | docker compose up -d in databases/examiner/ | 127.0.0.1:15432 |
| Docs | mdbook watch (or mdbook serve) | localhost:3000 |
cargo run rows need their .env exported first (Environment). bun run dev starts wApp and eDd client together. eDd client proxies /api to eDd server. wApp and dApp call aAPI at AUTH_API_URL.
Images: docker compose -f apps/<app>/compose.yaml up --build. Build context is repo root.
Workspaces
| Workspace | Manifest | Members |
|---|---|---|
| Cargo | Cargo.toml | auth-api, examiner-dashboard, edb, libs/* |
| Cargo | apps/desktop-app/Cargo.toml | dApp backend. Separate so servers never resolve Tauri |
| Bun | package.json | web-app, examiner-dashboard-client, desktop-app |
Shared versions live in package.json catalogs: reference with catalog: or catalog:astro, do not pin in members.
Check
CI (.github/workflows/ci.yml) runs only jobs whose paths changed. Run the same locally before pushing:
# Root Cargo workspace (eDb running; source databases/examiner/.env for EDB_ADMIN_URL)
cargo fmt --all --check
cargo clippy --workspace --all-targets --locked
cargo clippy -p runtime --no-default-features --all-targets --locked
DATABASE_URL=$EDB_ADMIN_URL cargo test --workspace --locked
# dApp backend
cargo fmt --all --check --manifest-path apps/desktop-app/Cargo.toml
cargo clippy --workspace --all-targets --locked --manifest-path apps/desktop-app/Cargo.toml
cargo test --workspace --locked --manifest-path apps/desktop-app/Cargo.toml
# Bun
bun run check
bun run build
# Deploy Action (migration gate, rollback)
(cd deploy && bun test)
Clippy all is deny. Format Rust with rustfmt (rustfmt.toml), eDd client with bun --filter examiner-dashboard-client fmt (oxfmt).
Database Changes
eDb schema, migrations and queries live in crate edb (databases/examiner/). Run every step from that directory with eDb up (docker compose up -d --wait). Only add, release, then remove: drop or rename only once no running or previous eDd or aAPI image uses the object. Never edit an applied migration.
sqlx-cli reads EDB_MIGRATOR_URL from its .env; cargo does not, so export it first.
- Create a reversible migration pair:
cargo sqlx migrate add -r -s <name> # migrations/NNNN_<name>.{up,down}.sql
- Write
upanddownSQL, naming the schema (edd.users;sharedif both apps write the table, see eDb schemas), and grant each app role what it needs on new tables.downis for local development only; production never runs it. - Apply it to local eDb:
cargo run -p edb --bin edb-migrate # or: cargo sqlx migrate run
To iterate on uncommitted migration: cargo sqlx migrate revert, make edits, then run it again.
- Add or change queries in
src/edd/,src/aapi/orsrc/shared/(one module per table group, each animpl Databaseblock ofquery!/query_as!).SQLX_OFFLINEistruein.cargo/config.toml, socargo check -p edbchecks them against committed.sqlx/, and fails on a new or changed query. To validate against local eDb, override it:SQLX_OFFLINE=false cargo check -p edb. - Test with
#[sqlx::test(migrations = false)], callingtesting::setupfirst: it bootstraps roles and schemas, then migrates asedb_migrator. Query throughtesting::database_asto run as the app’s role.#[sqlx::test]readsDATABASE_URL, notsqlx.toml, and creates a database per test:
DATABASE_URL=$EDB_ADMIN_URL cargo test -p edb
- Refresh offline query data:
cargo sqlx prepare
The Husky pre-commit hook runs this and stages .sqlx/ when eDb files are staged. This step never blocks the commit: if it warns with cargo sqlx prepare failed; .sqlx/ may be stale, manually run step 6.
- Commit migration, queries and
.sqlx/together. Local (.cargo/config.toml), Docker and CI builds all useSQLX_OFFLINE=true. CI fails on a stale.sqlx/: offline clippy on a missing query,cargo sqlx prepare --checkon one whose types changed.
Commits and PRs
Conventional Commits: release-please reads them to version each app (Releases).
<type>(<scope>): <summary>
| Part | Values |
|---|---|
| type | feat, fix, docs, chore, dev, refactor. feat and fix trigger release; others do not |
| scope | App or area: aAPI, eDd, eDb, wApp, dApp, docs, .github. Omit when repo-wide |