# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Setup

```bash
pip install -r requirements.txt
cp config/config.example.yaml config/config.yaml  # then edit workspace/URL
cp .env.example .env  # fill in credentials
```

All scripts must be run from the project root (`projects/sonar_scan/`).

## Common Commands

```bash
# Full daily pipeline: discover repos → detect changes → scan → generate reports
python src/main.py

# Scan only repos with recent commits (last N days, configurable via WEEKLY_SCAN_DAYS)
python src/weekly_scan.py

# Scan a specific subset of repos
python src/scan_specific_repos.py --repos repo1 repo2

# Clone missing repos and update all existing ones
python src/verify_repos.py

# Check status of high-priority repos
python src/check_top_repos_fast.py

# Generate trend analysis across historical scan data
python src/generate_trend_report.py
```

## Scheduled run (cron)

Weekly **Thursday 01:00** (server local time): `scripts/run_weekly_scan_recent.sh` runs `scan_recent.py` with `WEEKLY_SCAN_DAYS` (default **14** in the script; override by exporting before the script or editing the script). Loads `.env` from the project root. Logs: `logs/cron_weekly_scan_recent_YYYYMMDD_HHMMSS.log`.

When `SMTP_REPORT_HOST`, `SMTP_REPORT_USER`, `SMTP_REPORT_PASSWORD`, and `SMTP_REPORT_TO` are set in `.env`, a plain-text summary email is sent after each `scan_recent` run (including cron). Quote `SMTP_REPORT_PASSWORD` if it contains `#`. Set `SMTP_REPORT_ENABLED=false` to disable. Optional: `SMTP_REPORT_FROM`, `SMTP_REPORT_PORT` (default 587), `SMTP_REPORT_USE_SSL` / `SMTP_REPORT_STARTTLS`.

Example crontab line (adjust path if your clone is elsewhere):

```cron
0 1 * * 4 /home/batmin/projects/sonar_scan/scripts/run_weekly_scan_recent.sh
```

## Architecture

**Pipeline:** Config → Bitbucket API → Change Detection → Git clone/pull → SonarQube scan → PDF report → History update

**Core modules (`src/`):**

- `config_manager.py` — Merges `config/config.yaml` + `.env` + interactive prompts. Use dot-notation `config.get("bitbucket.workspace")`. Sensitive values are never written back to YAML.
- `bitbucket_client.py` — Read-only Bitbucket Cloud REST API 2.0 client. Rate-limited (100ms delay). Supports both API Token (for REST) and App Password (required for Git clone — API tokens don't work for Git).
- `git_manager.py` — SSH-based clone/update into `repos/`. Returns `(repo_path, is_first_clone)` tuple or a plain path.
- `sonarqube_scanner.py` — Wraps `sonar-scanner` CLI. Auto-generates `sonar-project.properties` if missing. Creates the SonarQube project via API if not found. Retries on concurrent scan errors (up to 3×, doubling delay from 60s).
- `report_generator.py` — ReportLab PDF generation. Output: `reports/{repo_slug}_{YYYYMMDD}_{HHmmss}.pdf`.
- `main.py` — `ScanOrchestrator` class drives the full pipeline. Change detection: if today's report already exists AND API shows no new commits → skip.

**State files:**
- `scan_history.json` — Per-repo last scan timestamp + success flag. Delete an entry to force re-scan.
- `sonar_scan.log` — Appended on each run.

## Configuration

`.env` takes precedence over `config/config.yaml`:

```
BITBUCKET_WORKSPACE=
BITBUCKET_USERNAME=
BITBUCKET_APP_PASSWORD=   # required for git clone
BITBUCKET_API_TOKEN=      # preferred for REST API calls
SONARQUBE_URL=https://scan.appmonitor.co.za/
SONARQUBE_TOKEN=
REPOS_DIR=./repos
REPORTS_DIR=./reports
HISTORY_FILE=./scan_history.json
SONAR_SCANNER_PATH=sonar-scanner   # Env overrides config; use "sonar-scanner" to invoke CLI when in PATH
WEEKLY_SCAN_DAYS=7
```

Additional repos from other workspaces are listed under `scanning.additional_repositories` in `config.yaml` as SSH URLs (`git@bitbucket.org:workspace/repo.git`) or `workspace/repo` strings.

## Key Behaviours

- **sonar-project.properties**: Auto-generated if missing. Project key format: `{workspace}_{repo_slug}` (lowercased, special chars → `_`). `sonar.host.url` is always injected at scan time.
- **Authentication split**: API token → REST API calls; App Password → Git clone. Both can coexist; when API token is set, App Password is not sent for API calls.
- **Scanner path (Windows)**: Auto-discovers `.bat` wrapper under `sonar-scanner/bin/` relative to project root if not in PATH.
- **Plugin download errors**: Ensure SonarQube URL has a trailing slash and pass `-Dsonar.scanner.download.timeout=300000`.
