name: CI on: push: pull_request: workflow_dispatch: jobs: ci: name: Lint, test, and build # This label must match your Forgejo runner's label runs-on: docker # Use a clean Debian container so tools are predictable container: debian:stable-slim env: PYTHONDONTWRITEBYTECODE: "1" PIP_DISABLE_PIP_VERSION_CHECK: "1" UV_SYSTEM_PYTHON: "1" steps: - name: Install build tooling run: | set -euo pipefail apt-get update DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ git ca-certificates python3 python3-venv python3-pip python3-setuptools \ python3-wheel sqlite3 update-ca-certificates || true - name: Checkout repository (manual) run: | set -euo pipefail if [ -f Makefile ] || [ -d .git ]; then echo "Repository present in workspace; skipping clone" exit 0 fi REMOTE_URL="${CI_REPOSITORY_URL:-}" if [ -z "$REMOTE_URL" ]; then if [ -n "${GITHUB_SERVER_URL:-}" ] && [ -n "${GITHUB_REPOSITORY:-}" ]; then REMOTE_URL="${GITHUB_SERVER_URL%/}/${GITHUB_REPOSITORY}.git" elif [ -n "${GITHUB_REPOSITORY:-}" ]; then REMOTE_URL="https://git.jordanwages.com/${GITHUB_REPOSITORY}.git" else echo "Unable to determine repository URL from CI environment" >&2 exit 1 fi fi AUTH_URL="$REMOTE_URL" if [ -n "${GITHUB_TOKEN:-}" ]; then ACTOR="${GITHUB_ACTOR:-oauth2}" AUTH_URL=$(printf '%s' "$REMOTE_URL" | sed -E "s#^https://#https://${ACTOR}:${GITHUB_TOKEN}@#") fi echo "Cloning from: $REMOTE_URL" if ! git clone --depth 1 "$AUTH_URL" .; then echo "Auth clone failed; trying anonymous clone..." >&2 git clone --depth 1 "$REMOTE_URL" . fi if [ -n "${GITHUB_SHA:-}" ]; then git fetch --depth 1 origin "$GITHUB_SHA" || true git checkout -q "$GITHUB_SHA" || true elif [ -n "${GITHUB_REF_NAME:-}" ]; then git fetch --depth 1 origin "$GITHUB_REF_NAME" || true git checkout -q "$GITHUB_REF_NAME" || true fi - name: Set up venv and install deps run: | set -euo pipefail # Prefer persistent cache if runner provides /cache USE_CACHE=0 if [ -d /cache ] && [ -w /cache ]; then export PIP_CACHE_DIR=/cache/pip mkdir -p "$PIP_CACHE_DIR" REQ_HASH=$(sha256sum requirements.txt | awk '{print $1}') PYVER=$(python3 -c 'import sys;print(".".join(map(str, sys.version_info[:2])))') CACHE_VENV="/cache/venv-${REQ_HASH}-py${PYVER}" if [ -d "$CACHE_VENV" ]; then echo "Using cached virtualenv: $CACHE_VENV" ln -s "$CACHE_VENV" .venv USE_CACHE=1 else echo "Creating cached virtualenv: $CACHE_VENV" python3 -m venv "$CACHE_VENV" ln -s "$CACHE_VENV" .venv fi fi . .venv/bin/activate python -m pip install --upgrade pip if [ "$USE_CACHE" = "1" ]; then # Ensure required packages are present; pip will use cache pip install -r requirements.txt pytest || pip install -r requirements.txt pytest else pip install -r requirements.txt pytest fi - name: Format check (black) run: | . .venv/bin/activate black --check . - name: Lint (flake8) run: | . .venv/bin/activate flake8 . - name: Run tests (pytest) run: | . .venv/bin/activate pytest -q --maxfail=1 - name: Build sample reports (no artifact upload) run: | set -euo pipefail . .venv/bin/activate python - <<'PY' import sqlite3, pathlib db = pathlib.Path('database/ngxstat.db') db.parent.mkdir(parents=True, exist_ok=True) conn = sqlite3.connect(db) cur = conn.cursor() cur.execute('''CREATE TABLE IF NOT EXISTS logs ( id INTEGER PRIMARY KEY, ip TEXT, host TEXT, time TEXT, request TEXT, status INTEGER, bytes_sent INTEGER, referer TEXT, user_agent TEXT, cache_status TEXT )''') cur.execute("INSERT INTO logs (ip, host, time, request, status, bytes_sent, referer, user_agent, cache_status) VALUES ('127.0.0.1','example.com','2024-01-01 10:00:00','GET / HTTP/1.1',200,100,'-','curl','MISS')") cur.execute("INSERT INTO logs (ip, host, time, request, status, bytes_sent, referer, user_agent, cache_status) VALUES ('127.0.0.1','example.com','2024-01-01 10:05:00','GET /about HTTP/1.1',200,100,'-','curl','MISS')") conn.commit(); conn.close() PY python scripts/generate_reports.py global python scripts/generate_reports.py hourly python scripts/generate_reports.py index tar -czf ngxstat-reports.tar.gz -C output . echo "Built sample reports archive: ngxstat-reports.tar.gz"