# Project Tooling Guide

> **Language / tooling note:** This document covers tooling conventions that apply
> to all CleverThis projects. Examples use the **Python/nox** toolchain, which is
> what this specific project uses. Apply the equivalent tools for your language
> ecosystem — the principles are the same regardless of language.

---

## Principle: Always Use the Task Runner

Every project designates a **task runner** as the single entry point for all
quality operations — linting, formatting, type checking, testing, coverage,
security scanning, and building. Never invoke these tools directly from the
command line; always go through the task runner.

| Purpose | Python *(this project)* | JavaScript / TypeScript | Java / Kotlin | Go |
|---------|------------------------|------------------------|---------------|----|
| Task runner | `nox` | `npm run` / `pnpm run` | Gradle / Maven | `make` / `mage` |
| All gates (default) | `nox` | `npm run all` | `./gradlew check` | `make all` |
| Lint | `nox -s lint` | `npm run lint` | `./gradlew checkstyle` | `golangci-lint run` |
| Format | `nox -s format` | `npm run format` | `./gradlew spotlessApply` | `gofmt -w .` |
| Format check (CI) | `nox -s format -- --check` | `npm run format:check` | `./gradlew spotlessCheck` | `gofmt -l .` |
| Type check | `nox -s typecheck` | `npm run typecheck` | `./gradlew compileJava` | built-in |
| Unit tests | `nox -s unit_tests` | `npm run test:unit` | `./gradlew test` | `go test ./...` |
| Integration tests | `nox -s integration_tests` | `npm run test:integration` | `./gradlew integrationTest` | `go test -tags integration` |
| Coverage | `nox -s coverage_report` | `npm run test:coverage` | `./gradlew jacocoTestReport` | `go test -cover ./...` |
| Security scan | `nox -s security_scan` | `npm audit + semgrep` | `./gradlew dependencyCheck` | `gosec ./...` |
| Unused code | `nox -s dead_code` | `ts-prune` / `knip` | `./gradlew findbugs` | `deadcode ./...` |
| Complexity | `nox -s complexity` | `eslint complexity` | `./gradlew pmd` | `gocyclo` |
| Build | `nox -s build` | `npm run build` | `./gradlew build` | `go build ./...` |
| Documentation | `nox -s docs` | `npm run docs` | `./gradlew javadoc` | `godoc` |
| Benchmarks | `nox -s benchmark` | `npm run bench` | `./gradlew jmh` | `go test -bench` |

```bash
# Python/nox — run all default sessions (full quality gate):
nox

# Individual sessions:
nox -s lint              # linting + format check (ruff)
nox -s format            # auto-format code (ruff)
nox -s format -- --check # check formatting only — no changes (CI mode)
nox -s typecheck         # static type checking (Pyright)
nox -s unit_tests        # BDD unit tests (Behave)
nox -s integration_tests # integration tests (Robot Framework via pabot)
nox -s coverage_report   # generate coverage report (must be ≥ 97%)
nox -s security_scan     # security scan (bandit + semgrep + vulture)
nox -s dead_code         # unused code detection (vulture)
nox -s complexity        # code complexity analysis (Radon)
nox -s docs              # build documentation (MkDocs)
nox -s build             # build distributable artifact (wheel)
nox -s benchmark         # performance benchmarks (Airspeed Velocity)
```

If a session is missing from the task runner configuration: **add it before using it**.

---

## Project Environment and Dependency Management

Each language ecosystem has a standard way to manage environments and dependencies.
Use the idiomatic tool for your ecosystem — do not improvise with ad-hoc scripts.

| Language | Environment / package tool | Project manifest |
|----------|---------------------------|-----------------|
| Python *(this project)* | Hatch | `pyproject.toml` |
| JavaScript / TypeScript | npm / pnpm / yarn | `package.json` |
| Java / Kotlin | Gradle / Maven | `build.gradle` / `pom.xml` |
| Go | Go modules (built-in) | `go.mod` |
| Rust | Cargo (built-in) | `Cargo.toml` |

```bash
# Python/Hatch (this project):
hatch env create   # create and populate project environment
hatch build        # build the project
```

**Universal rule:** All project configuration lives in a single project manifest
at the repository root. No Makefiles, no wrapper shell scripts, no ad-hoc aliases.
Every command must be reproducible via the official toolchain without customisation.

---

## Pre-commit Hooks

All projects must install pre-commit hooks that enforce quality automatically
on every commit. **Never bypass hooks with `--no-verify`.**

```bash
# This project:
scripts/setup-dev.sh   # installs pre-commit hooks

# General pattern for any project:
pre-commit install      # if using the pre-commit framework directly
```

Hooks enforce: formatting, linting, type checking, security scanning,
and commit message format validation (via Commitizen).

---

## Commitizen (Conventional Changelog)

Commitizen validates and guides commit message format. It is language-agnostic
and works for any project since it operates on git commit messages, not source code.

```bash
# Install (works for any language project):
npm install -g commitizen@2.8.6 cz-customizable@4.0.0

# Use (replaces git commit for guided, validated messages):
git cz
```

Configuration lives in the project manifest (`[tool.commitizen]` in `pyproject.toml`
for Python; a `commitizen` key in `package.json` for Node.js projects).

---

## CI/CD Pipeline

CI triggers on push to branches and on pull requests. All task runner default
sessions run as parallel jobs. The `status-check` job consolidates required results
into a single branch-protection gate.

To check CI status:
1. Open the PR → go to the Checks tab
2. Each job appears as a separate named check
3. Click a failed job to view its logs
4. Look for "COVERAGE OK" or "COVERAGE FAILED" in the coverage job output

---

## Release Process

A release is triggered by pushing a version tag, which starts the release CI
workflow. The workflow builds the distributable artifact, optionally builds and
pushes a container image, and creates a Forgejo release with artifacts attached.

```bash
# Trigger a release by pushing a version tag:
git tag v1.2.3
git push origin v1.2.3
```

This project's `release.yml` workflow:
1. Builds the distributable artifact via the task runner (Python: `nox -s build` → wheel)
2. Builds a container image tagged with the version and `latest`
3. Pushes the container image to the configured registry
4. Creates a Forgejo release with the artifact as a downloadable file

Required repository secrets:

| Secret | Purpose |
|--------|---------|
| `CONTAINER_REGISTRY` | Container registry URL |
| `CONTAINER_REGISTRY_USER` | Registry authentication username |
| `CONTAINER_REGISTRY_PASSWORD` | Registry authentication password |
| `FORGEJO_TOKEN` | API token for creating Forgejo releases |
| `FORGEJO_URL` | Forgejo instance URL |

---

## Dependency Caching

CI jobs cache downloaded packages between runs, keyed on a hash of the project
manifest so caches are automatically invalidated when dependencies change.

| Language | Cache key |
|----------|-----------|
| Python | `pyproject.toml` hash |
| Node.js | `package-lock.json` / `pnpm-lock.yaml` hash |
| Java | Gradle user home or `.m2` directory hash |
| Go | Module cache keyed on `go.sum` hash |

---

## Project Configuration Files

Every project has a standard set of root-level configuration files:

| Purpose | Python *(this project)* | JavaScript | Java |
|---------|------------------------|------------|------|
| Project manifest | `pyproject.toml` | `package.json` | `build.gradle` / `pom.xml` |
| Task runner config | `noxfile.py` | scripts in `package.json` | `build.gradle` |
| Pre-commit hooks | `.pre-commit-config.yaml` | `.pre-commit-config.yaml` | `.pre-commit-config.yaml` |
| Test framework config | `robot.cfg` | `jest.config.js` / `vitest.config.ts` | `src/test/resources/` |
| CI/CD workflows | `.forgejo/workflows/` | `.forgejo/workflows/` | `.forgejo/workflows/` |

---

## Development Setup Checklist

```bash
# 1. Clone the repository
git clone <repo-url>
cd <repo>

# 2. Set up the dev environment (installs pre-commit hooks)
scripts/setup-dev.sh       # this project
# — or for other projects:
pre-commit install          # generic pre-commit setup

# 3. Verify everything works — run the full task runner suite
nox                         # Python/nox
# npm run all               # Node.js
# ./gradlew check           # Java/Gradle
# make all                  # Go / make-based projects

# 4. Install Commitizen (optional but recommended — any language)
npm install -g commitizen@2.8.6 cz-customizable@4.0.0
```

---

## Always Runnable

The repository must always be in a runnable, testable state. On a fresh clone,
a single command must run the full quality suite without any manual setup beyond
the standard environment tool:

```bash
# Python/nox:
nox

# Node.js:
npm install && npm run all

# Java/Gradle:
./gradlew check

# Go:
go test ./...
```
