Skip to content

Latest commit

 

History

History
268 lines (177 loc) · 12.3 KB

File metadata and controls

268 lines (177 loc) · 12.3 KB

Contributing to TRUSCA

Thank you for your interest in contributing! TRUSCA is an Apache-2.0 licensed, self-hosted SCA portal, and we welcome contributions from the community — code, documentation, translations, bug reports, and design feedback.

This document describes how to set up the project locally, the conventions we follow, and what we expect in a pull request.

AI-assisted development. This project is developed with AI-assisted tooling (Claude Code) for scaffolding, refactoring, and review. Design decisions, code review, and accountability for every merged change remain human-owned by the maintainers listed in MAINTAINERS.md. Pull requests from contributors using similar tooling are welcome — please disclose in the PR description and treat the AI as a collaborator, not the author.


Table of Contents

  1. Code of Conduct
  2. Getting Started
  3. Development Workflow
  4. Coding Standards
  5. Testing & Coverage Gates
  6. Harness-First Principle
  7. Pull Request Process
  8. Commit Messages
  9. Internationalization (i18n)
  10. Documentation
  11. Security Issues
  12. License & DCO

Code of Conduct

This project adheres to the Contributor Covenant 2.1. By participating, you agree to uphold its terms. See Reporting for how to raise unacceptable behavior privately.


Getting Started

Prerequisites

  • Docker + Docker Compose V1 (the hyphenated docker-compose command — V2 / docker compose is not supported in our development environment)
  • Python 3.12 (backend)
  • Node.js 20 (frontend)
  • Git

Bootstrap the dev stack

git clone https://github.com/trustedoss/trusca.git
cd trusca
cp .env.example .env  # adjust as needed
docker-compose -f docker-compose.dev.yml up -d

After ~30 seconds, all five containers (postgres, redis, backend, celery-worker, frontend) should be healthy. The frontend is served on http://localhost:5173, the backend API on http://localhost:8000.

Running tests locally

# Backend — CI runs these as two jobs against separate databases. Locally,
# run them in one go or one at a time; a re-run against a database the other
# suite already wrote to can fail on leftover rows.
cd apps/backend
pytest tests/unit tests/integration --cov

# Frontend
cd apps/frontend
npm run test -- --coverage

Development Workflow

Branch model

  • main — protected, deployable. Direct pushes are disabled; everything goes through pull requests.
  • feature/<short-topic> — your working branch. Keep it small and focused.

Picking work

  • Browse open issues labeled good first issue or help wanted in the issue tracker.
  • For larger features, open a discussion or feature-request issue first so we can align on scope before you write code.

Keeping in sync

git fetch origin
git rebase origin/main

Rebase, don't merge — we keep main linear.


Coding Standards

We treat the codebase as a global commercial product, not a personal project. Be tasteful.

Backend (Python / FastAPI)

  • Style: ruff (lint + format). Run ruff check . && ruff format . before committing.
  • Types: mypy strict mode. Public functions must be fully annotated.
  • Async first: prefer async def for I/O-bound endpoints, services, and integrations. SQLAlchemy 2.0 async sessions are the default.
  • Errors: all 4xx / 5xx responses use RFC 7807 Problem Details (application/problem+json). Required fields: type, title, status, detail, instance. Domain extensions are snake_case.
  • Logging: structlog JSON lines, one event per line. request_id, user_id, team_id, and task_id are propagated automatically. Never log secrets, tokens, or full email addresses — use the mask_pii helper.
  • Configuration: call os.getenv() at runtime, not module load. Never cache env vars in module-level constants.
  • Database: PostgreSQL only — no SQLite, no in-memory. Schema changes require a new Alembic migration.
  • Migrations: forward-only. downgrade() is pass or raise NotImplementedError. Schema and data migrations are separate revisions. Breaking changes follow expand → migrate-data → contract.

Frontend (TypeScript / React 18)

  • Style: eslint flat config + prettier (run npm run lint && npm run format).
  • Types: strict TypeScript. any requires a justification comment.
  • Components: prefer shadcn/ui primitives. Custom UI must use Tailwind design tokens (see src/index.css) — never hardcode colors or sizes.
  • State: server state lives in TanStack Query; client UI state lives in Zustand. Don't mix.
  • i18n: every user-visible string goes through t(). No hardcoded English in JSX.

Docker / DevOps

  • Image tags: never :latest. Pin to a minor + patch version (e.g. node:20.18.1-alpine, postgres:17.2-alpine).
  • Compose: use docker-compose (V1, hyphenated). docker compose (V2) is not supported.
  • Third-party GitHub Actions: pin by commit SHA with the release in a trailing comment (uses: owner/action@<sha> # v1.2.3). A tag is mutable — @v1 runs whatever the owner moved it to, inside a job that can hold our registry credentials. Actions under actions/, github/ and docker/ stay on major tags.
  • Secrets: never commit. Use .env.example for the schema; real values go in .env (git-ignored) or GitHub Actions secrets.

Testing & Coverage Gates

We block PRs that lower test coverage. The thresholds are enforced in CI:

Scope Tool Threshold
Backend lines, whole tree coverage-gate (backend) ≥ 80% (fail_under=80 in pyproject.toml)
Backend lines, changed by the PR diff-cover in the same job ≥ 80%
Frontend lines vitest --coverage ≥ 80% lines / 70% branches (vite.config.ts)
E2E core scenarios Playwright (harness pattern) not run on pull requests

A change that lowers coverage below the floor will fail CI. Add tests for the lines you write.

The E2E row is the exception: the Playwright suite runs on the nightly schedule and on a manual workflow_dispatch, not on pull requests (.github/workflows/ci.yml). Opening a PR does not exercise it, so do not rely on it to catch a regression in a user-visible flow. If your change touches one, run the suite yourself or ask a maintainer to dispatch it.

What to test

  • Unit: pure functions, schemas, parsers, RBAC predicates.
  • Integration: anything that touches PostgreSQL, Redis, Celery, or an external integration. Use real services (via docker-compose), not mocks.
  • E2E: user-visible flows. Login, scan execution, report download, admin actions.

Mocks for external paid APIs (e.g., GitHub App, GCP) are acceptable. Mocks for our own database / queue are not.


Harness-First Principle

Write the test harness before the feature, not after.

Every new screen or domain area must ship with its harness — a class or module that exposes the domain in test-friendly verbs (auth.login(), scan.expectInProgress(), project.openVulnerabilitiesTab()). The feature implementation comes second.

Why:

  • Refactors stay cheap. UI restyles don't break tests because tests speak domain language, not selectors.
  • Tests document behavior. Reading the harness tells you what the feature is supposed to do.
  • Reviewers can read tests first to understand the change.

If you add a feature with no harness, the PR is incomplete. See apps/frontend/tests/_harness/PortalPage.ts for the UI pattern, and the shared fixtures in apps/backend/tests/conftest.py for the API side.


Pull Request Process

  1. Fork & branchgit checkout -b feature/my-change.
  2. Implement — follow the coding standards above. Keep PRs small (< 500 lines diff is the sweet spot; > 1000 lines should usually be split).
  3. Self-review — run lint, typecheck, tests locally. Fix all warnings, not just errors.
  4. Open the PR — fill out the pull request template completely. Empty checklists block review.
  5. CI must pass — all three jobs (lint, typecheck, test) on both backend and frontend matrices. We do not merge red.
  6. Review — at least one maintainer approval. Security-sensitive changes (auth, API keys, Trivy / external scanner integrations, OAuth, build gate) require additional review by a maintainer with the security role.
  7. Merge — maintainers merge via "Squash and merge" to keep main linear. The squash message uses the PR title — write good titles.

What gets a PR rejected

  • Coverage drop below 80%
  • New strings without i18n keys (or KO translations missing)
  • New endpoint without OpenAPI documentation
  • New feature without an updated Docusaurus page
  • docker compose (V2) usage, :latest tags, or module-level os.getenv() caching
  • Mocking the database in tests
  • Backwards-compat shims that have no current consumer

CLA

We do not require a Contributor License Agreement. Your contribution is licensed under Apache-2.0 by the act of submitting it (see License & DCO).


Commit Messages

We follow a relaxed Conventional Commits style:

<type>(<scope>): <short summary>

<body — what and why, not how>

<footer — refs, breaking changes>

Types: feat, fix, refactor, docs, test, chore, ci, build, perf, style.

Examples:

feat(auth): add refresh token rotation with reuse detection
fix(dt): retry on 502 with exponential backoff
docs(install): document upgrade path for v2.0.1

Squash-merged PRs inherit the PR title — make it conventional.


Internationalization (i18n)

Every user-visible string must exist in both English (apps/frontend/src/locales/en/*.json) and Korean (apps/frontend/src/locales/ko/*.json). PRs that add only English will be asked to add Korean before merge.

Translation conventions:

  • Keys are flat and dot-namespaced: auth.login.submit.
  • Korean translations follow the domain glossary in the docs site (docs-site/docs/reference/glossary.md).
  • Use ICU plural / select syntax for variable counts.

CI runs i18next-parser --fail-on-update to catch missing keys.


Documentation

Every user-facing feature ships with a Docusaurus page in docs-site/docs/. Backend API changes update the OpenAPI schema (FastAPI auto-generates this) and are reflected in the hosted API Reference at /reference/api.

The public roadmap and release history live in ROADMAP.md and CHANGELOG.md. Larger proposals go through a GitHub issue / discussion before a PR — see GOVERNANCE.md.


Security Issues

Do not open public issues for security vulnerabilities. See SECURITY.md for the responsible disclosure process and our response SLA.


License & DCO

By contributing to this project, you certify that:

  1. The contribution is your original work, or you have the right to submit it.
  2. You license your contribution under the Apache License 2.0.
  3. You understand that the project and your contribution are public.

This is the Developer Certificate of Origin (DCO) 1.1 in spirit. We do not require sign-offs in commits, but the same understanding applies.


Related documents


Thanks again for contributing — every PR, issue, translation, and design suggestion makes the project better.

— The TRUSCA maintainers