Skip to content

Commit 8dfde4f

Browse files
committed
Fix quality
1 parent 9486d1d commit 8dfde4f

33 files changed

Lines changed: 1737 additions & 433 deletions

.env

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# This file is committed to git no secrets.
2+
3+
#!/usr/bin/env bash
4+
5+
# Vault environment loader wrapper
6+
# Usage: source .env.vault (never run directly)
7+
8+
set -o pipefail
9+
10+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
11+
echo "This script must be sourced: source .env.vault" >&2
12+
exit 1
13+
fi
14+
15+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16+
HELPER_PATH="${VAULT_HELPER_PATH:-${SCRIPT_DIR}/scripts/vault-helper.sh}"
17+
18+
if [[ ! -r "$HELPER_PATH" ]]; then
19+
echo "Vault helper not found at $HELPER_PATH" >&2
20+
return 1
21+
fi
22+
23+
# shellcheck source=./scripts/vault-helper.sh
24+
source "$HELPER_PATH"
25+
26+
DEFAULT_VAULT_SECRET_DEFS=$'kv/Sonarqube/sonarqube|SONAR_TOKEN=SONAR_TOKEN SONAR_TOKEN=sonar_token SONAR_TOKEN=token\nkv/dependencytrack|DTRACK_API_KEY=DTRACK_API_KEY DTRACK_API_KEY=api_key DTRACK_API_KEY=token'
27+
DEFAULT_VAULT_REQUIRED_VARS="SONAR_TOKEN DTRACK_API_KEY"
28+
29+
if [[ -z "${VAULT_TOKEN:-}" ]]; then
30+
token_candidates=()
31+
if [[ -n "${VAULT_TOKEN_FILE:-}" ]]; then
32+
token_candidates+=("$VAULT_TOKEN_FILE")
33+
fi
34+
token_candidates+=("${HOME}/.vault-token" "/home/vscode/.vault-token" "/root/.vault-token")
35+
for token_path in "${token_candidates[@]}"; do
36+
if [[ -r "$token_path" ]]; then
37+
VAULT_TOKEN_FILE="$token_path"
38+
export VAULT_TOKEN_FILE
39+
break
40+
fi
41+
done
42+
fi
43+
44+
SECRET_DEFS="${VAULT_SECRET_PATHS:-$DEFAULT_VAULT_SECRET_DEFS}"
45+
REQUIRED_VARS="${VAULT_REQUIRED_VARS:-$DEFAULT_VAULT_REQUIRED_VARS}"
46+
47+
vault_helper::load_from_definitions "$SECRET_DEFS" "$REQUIRED_VARS" "$VAULT_TOKEN_FILE"
48+
49+
# Commented out for CI/automated testing
50+
# SONAR_TOKEN=""
51+
DTR_PROJECT_KEY=
52+
# DTRACK_API_KEY=""
53+
DTRACK_PROJECT=sonarqube-mcp
54+
DTRACK_PROJECT_VERSION=main

.gitignore

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,40 @@ celerybeat.pid
124124
*.sage.py
125125

126126
# Environments
127-
.env
127+
.env.vault
128+
.env.cloud
128129
.venv
129130
env/
130131
venv/
131132
ENV/
132133
env.bak/
133134
venv.bak/
134135

136+
# SonarQube scanner working directory
137+
dist/quality/sonar/scannerwork/
138+
139+
# Quality and Security Artifacts
140+
dist/quality/
141+
dist/security/
142+
dist/artifacts/
143+
144+
# Gitleaks reports
145+
*.gitleaks-report.json
146+
gitleaks-report.json
147+
148+
# SBOM reports
149+
*.sbom.json
150+
sbom.json
151+
cyclonedx-*.json
152+
153+
# Renovate reports
154+
renovate-report.json
155+
renovate-summary-*.txt
156+
157+
# Semgrep reports
158+
semgrep-report*.json
159+
semgrep-report*.sarif
160+
135161
# Spyder project settings
136162
.spyderproject
137163
.spyproject
@@ -328,4 +354,4 @@ tests/__pycache__/
328354
docs/start.md
329355
docs/typescript-translation-plan.md
330356

331-
# Note: test scripts in tests/ directory should be tracked in git
357+
# Note: test scripts in tests/ directory should be tracked in git

Makefile

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
# Claude Code API - Simple & Working
22

3+
SHELL := /bin/bash
4+
.DEFAULT_GOAL := help
5+
6+
# Directory structure
7+
ARTIFACTS_DIR ?= dist/artifacts
8+
QUALITY_DIR ?= dist/quality
9+
SECURITY_DIR ?= dist/security
10+
TESTS_DIR ?= dist/tests
11+
12+
BUILD_DIR := $(ARTIFACTS_DIR)/bin
13+
COVERAGE_DIR := $(QUALITY_DIR)/coverage
14+
SONAR_DIR := $(QUALITY_DIR)/sonar
15+
SBOM_DIR := $(SECURITY_DIR)/sbom
16+
GITLEAKS_DIR := $(SECURITY_DIR)/gitleaks
17+
18+
# Version info
19+
VERSION_FILE := $(shell cat VERSION 2>/dev/null || echo "1.0.0")
20+
VERSION ?= $(VERSION_FILE)
21+
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
22+
BUILD_DATE ?= $(shell date -u +'%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || "")
23+
24+
# Dependency-Track settings
25+
DTRACK_BASE_URL ?=
26+
DTRACK_API_KEY ?=
27+
DTRACK_PROJECT ?= claude-code-api
28+
DTRACK_PROJECT_VERSION ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "dev")
29+
330
# Python targets
431
install:
532
pip install -e .
@@ -56,6 +83,105 @@ kill:
5683
fi; \
5784
fi
5885

86+
# Quality and Security targets
87+
.PHONY: sonar sonar-cloud coverage-sonar sbom sbom-upload gitleaks fmt lint vet
88+
89+
sonar: ## Run sonar-scanner for SonarQube analysis
90+
@mkdir -p $(SONAR_DIR) $(COVERAGE_DIR)
91+
@echo "Generating coverage report for SonarQube..."
92+
@python -m pytest --cov=claude_code_api --cov-report=xml --cov-report=term-missing -v tests/
93+
@if command -v sonar-scanner >/dev/null 2>&1; then \
94+
if [ -f ".env.vault" ]; then \
95+
. ./.env.vault; \
96+
fi; \
97+
if [ -f ".env" ]; then \
98+
set -a; . ./.env; set +a; \
99+
fi; \
100+
if [ -n "$${VAULT_SECRET_PATHS:-}" ] || [ -n "$${VAULT_REQUIRED_VARS:-}" ]; then \
101+
if [ -f "./scripts/vault-helper.sh" ]; then \
102+
. ./scripts/vault-helper.sh; \
103+
vault_helper::load_from_definitions "$${VAULT_SECRET_PATHS:-}" "$${VAULT_REQUIRED_VARS:-}" "$${VAULT_TOKEN_FILE:-}"; \
104+
fi; \
105+
fi; \
106+
SONAR_HOST_URL="$${SONAR_HOST_URL:-$${SONAR_URL:-}}"; \
107+
if [ -z "$$SONAR_HOST_URL" ]; then \
108+
echo "SONAR_URL or SONAR_HOST_URL is required (e.g., https://sonarcloud.io or https://sonar.local)"; \
109+
exit 1; \
110+
fi; \
111+
case "$$SONAR_HOST_URL" in \
112+
http://*|https://*) ;; \
113+
*) echo "SONAR_URL must include http(s) scheme: $$SONAR_HOST_URL"; exit 1 ;; \
114+
esac; \
115+
if [ -z "$${SONAR_TOKEN:-}" ]; then \
116+
echo "SONAR_TOKEN not set - proceeding without authentication"; \
117+
fi; \
118+
sonar-scanner \
119+
-Dsonar.host.url=$$SONAR_HOST_URL \
120+
-Dsonar.token=$$SONAR_TOKEN \
121+
-Dsonar.projectVersion=$${VERSION:-1.0.0} \
122+
-Dsonar.working.directory=$(SONAR_DIR)/scannerwork; \
123+
else \
124+
echo "sonar-scanner not found. Install with: brew install sonar-scanner or download from https://docs.sonarqube.org/latest/analysis/scan/sonarscanner/"; \
125+
exit 1; \
126+
fi
127+
128+
sonar-cloud: ## Run sonar-scanner for SonarCloud (uses different token/env)
129+
@echo "Running SonarCloud scanner..."
130+
@./scripts/run-sonar-cloud.sh
131+
132+
coverage-sonar: ## Generate coverage for SonarQube
133+
@mkdir -p $(COVERAGE_DIR)
134+
@python -m pytest --cov=claude_code_api --cov-report=xml --cov-report=term-missing -v tests/
135+
@echo "Coverage XML generated: $(COVERAGE_DIR)/coverage.xml"
136+
137+
sbom: ## Generate SBOM with syft
138+
@mkdir -p $(SBOM_DIR)
139+
@if command -v syft >/dev/null 2>&1; then \
140+
syft dir:. -o cyclonedx-json=$(SBOM_DIR)/sbom.json; \
141+
echo "SBOM generated: $(SBOM_DIR)/sbom.json"; \
142+
else \
143+
echo "syft not found. Install with: brew install syft or visit https://github.com/anchore/syft"; \
144+
exit 1; \
145+
fi
146+
147+
sbom-upload: sbom ## Generate (if needed) and upload SBOM to Dependency-Track
148+
@./scripts/upload-sbom.sh $(SBOM_DIR)/sbom.json
149+
150+
gitleaks: ## Run gitleaks to detect secrets
151+
@mkdir -p $(GITLEAKS_DIR)
152+
@if command -v gitleaks >/dev/null 2>&1; then \
153+
gitleaks detect --source . --report-path $(GITLEAKS_DIR)/gitleaks-report.json; \
154+
else \
155+
echo "gitleaks not found. Install with: brew install gitleaks"; \
156+
exit 1; \
157+
fi
158+
159+
fmt: ## Format Python code with black
160+
@if command -v black >/dev/null 2>&1; then \
161+
black claude_code_api/ tests/; \
162+
else \
163+
echo "black not found. Install with: pip install black"; \
164+
fi
165+
166+
lint: ## Run Python linters (flake8, isort)
167+
@if command -v flake8 >/dev/null 2>&1; then \
168+
flake8 claude_code_api/ tests/; \
169+
else \
170+
echo "flake8 not found. Install with: pip install flake8"; \
171+
fi
172+
@if command -v isort >/dev/null 2>&1; then \
173+
isort --check-only claude_code_api/ tests/; \
174+
else \
175+
echo "isort not found. Install with: pip install isort"; \
176+
fi
177+
178+
vet: ## Run type checking with mypy
179+
@if command -v mypy >/dev/null 2>&1; then \
180+
mypy claude_code_api/; \
181+
else \
182+
echo "mypy not found. Install with: pip install mypy"; \
183+
fi
184+
59185
help:
60186
@echo "Claude Code API Commands:"
61187
@echo ""
@@ -70,14 +196,25 @@ help:
70196
@echo " make start-prod - Start Python API server (production)"
71197
@echo ""
72198
@echo "TypeScript API:"
73-
@echo " make install-js - Install TypeScript dependencies"
199+
@echo " make install-js - Install TypeScript dependencies"
74200
@echo " make test-js - Run TypeScript unit tests"
75201
@echo " make test-js-real - Run Python test suite against TypeScript API"
76202
@echo " make start-js - Start TypeScript API server (production)"
77203
@echo " make start-js-dev - Start TypeScript API server (development with reload)"
78204
@echo " make start-js-prod - Build and start TypeScript API server (production)"
79205
@echo " make build-js - Build TypeScript project"
80206
@echo ""
207+
@echo "Quality & Security:"
208+
@echo " make sonar - Run SonarQube analysis (generates coverage + scans)"
209+
@echo " make sonar-cloud - Run SonarCloud scanner (uses SONAR_CLOUD_TOKEN)"
210+
@echo " make coverage-sonar - Generate coverage XML for SonarQube"
211+
@echo " make sbom - Generate SBOM with syft"
212+
@echo " make sbom-upload - Upload SBOM to Dependency-Track"
213+
@echo " make gitleaks - Run gitleaks to detect secrets"
214+
@echo " make fmt - Format Python code with black"
215+
@echo " make lint - Run Python linters (flake8, isort)"
216+
@echo " make vet - Run type checking with mypy"
217+
@echo ""
81218
@echo "General:"
82219
@echo " make clean - Clean up Python cache files"
83220
@echo " make kill PORT=X - Kill process on specific port"

README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,12 @@ make start
5252

5353
## Supported Models
5454

55-
- `claude-opus-4-20250514` - Claude Opus 4 (Most powerful)
56-
- `claude-sonnet-4-20250514` - Claude Sonnet 4 (Latest Sonnet)
57-
- `claude-3-7-sonnet-20250219` - Claude Sonnet 3.7 (Advanced)
55+
Model definitions live in `claude_code_api/config/models.json`.
56+
Override with `CLAUDE_CODE_API_MODELS_PATH` to point at a custom JSON file.
57+
58+
- `claude-opus-4-5-20250929` - Claude Opus 4.5 (Most powerful)
59+
- `claude-sonnet-4-5-20250929` - Claude Sonnet 4.5 (Latest Sonnet)
60+
- `claude-haiku-4-5-20250929` - Claude Haiku 4.5 (Fast & cost-effective)
5861
- `claude-3-5-haiku-20241022` - Claude Haiku 3.5 (Fast & cost-effective)
5962

6063
## Quick Start
@@ -133,7 +136,7 @@ make check-claude # Check if Claude Code CLI is available
133136
curl -X POST http://localhost:8000/v1/chat/completions \
134137
-H "Content-Type: application/json" \
135138
-d '{
136-
"model": "claude-3-5-haiku-20241022",
139+
"model": "claude-sonnet-4-5-20250929",
137140
"messages": [
138141
{"role": "user", "content": "Hello!"}
139142
]
@@ -152,7 +155,7 @@ curl http://localhost:8000/v1/models
152155
curl -X POST http://localhost:8000/v1/chat/completions \
153156
-H "Content-Type: application/json" \
154157
-d '{
155-
"model": "claude-3-5-haiku-20241022",
158+
"model": "claude-sonnet-4-5-20250929",
156159
"messages": [
157160
{"role": "user", "content": "Tell me a joke"}
158161
],
@@ -273,4 +276,4 @@ Response:
273276

274277
## License
275278

276-
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
279+
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.

VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.0.0

0 commit comments

Comments
 (0)