Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 1 addition & 5 deletions .cursorignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
/.env
/.env-aws
/.env-local
/.env-ssh-demo
/.env-ukm
/passwords-delete-me.txt
/.env-files
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ docker-compose*.yml
# Keep only release files required by install/build paths
release/**
!release/common/
!release/common/bin/
!release/common/bin/*
!release/common/pg_ssl_entry_point.sh
!release/common/security.sh
!release/reporter_cli/
Expand All @@ -19,9 +21,15 @@ release/**

ui/custom/cookie/frontend/node_modules/

# Monorepo apps (Python sources required by release Dockerfiles)
!apps/
!apps/python/
!apps/python/**

# Dev tooling and docs
.git/
.cursor/
.backup/
tests/
docs/
openspec/
Expand Down
11 changes: 9 additions & 2 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ name: merge-validation
on:
pull_request:
branches: [main, develop]
paths:
- "apps/python/**"
- "tests/python/**"
- "live_test/**"
- "pyproject.toml"
- "Taskfile.yml"
- ".github/workflows/validate.yml"

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
Expand All @@ -18,7 +25,7 @@ jobs:
with:
python-version: "3.13"
- run: uv sync
- run: uv run ruff check .
- run: uv run ruff check apps/python tests/python live_test

type-check:
runs-on: ubuntu-latest
Expand All @@ -29,7 +36,7 @@ jobs:
with:
python-version: "3.13"
- run: uv sync
- run: uv run mypy lib reports sync_server
- run: uv run mypy

test-unit:
runs-on: ubuntu-latest
Expand Down
65 changes: 27 additions & 38 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,65 +1,54 @@
# Python
__pycache__/
.mypy_cache/
.pytest_cache
.pytest_cache/
*.py[cod]
*$py.class
*.so
.Python
venv/
ENV/
.venv
.venv/
.ruff_cache/
.coverage

# UV
.uv/
uv.lock

# IDE
.vscode/
/.vscode/
.idea/

# AI
.ai/
.claude/
.cline/
.clinerules/
.codex/
.cursor/
.junie/
.kilocode/
.kiro/
.pi/

*.iml
*.swp
*.swo
*~

# Environment variables
# AI (repo root)
/.ai/
/.todos/
/.claude/
/.cline/
/.clinerules/
/.codex/
/.cursor/
/.junie/
/.kilocode/
/.kiro/
/.opencode/
/.pi/
/CLAUDE.md
/openspec/
/opencode.json

# Environment
/.env-files/
.env
.env-local
.env-aws
.env-ukm
.env-ssh-demo
.env-reporter.privxssh.com

# OS
.DS_Store
Thumbs.db

# Other
/report_out
/.dev-pg-ssl
/passwords-delete-me.txt
/.coverage

# AI
/.opencode
/.cursor
/.pi
/openspec
/CLAUDE.md
/opencode.json
.todos/
/gep-signals.md
# Project (repo root)
/report_out/
/.backup/
/.dev-pg-ssl/
24 changes: 0 additions & 24 deletions Dockerfile

This file was deleted.

10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@ Starting points:

- **Reporter CLI** - Runs report commands and generates report output.
- Command: `bin/report`
- Implementation: `reports/`
- Implementation: `apps/python/reports/`

- **UI** - Web interface for running available reports.
- Command: `bin/serve_ui`
- Implementation: `ui/`
- Implementation: `apps/python/ui/`

- **Sync Server** - Populates audit and connection data in the data database on a schedule.
- Command: `bin/serve_sync`
- Implementation: `sync_server/`
- Implementation: `apps/python/sync_server/`

- **Admin CLI** - Administrative commands for database migrations, sync server back-fills, etc.
- Command: `bin/admin`
- Implementation: `administration/`
- Implementation: `apps/python/administration/`

- **Shared library** - Common functionality used by CLI, UI, and sync server.
- Implementation: `lib/`
- Implementation: `apps/python/lib/`

> **Executing commands**: When running directly from the repository use `bin/<command>`. In production, `serve_ui` and `serve_sync` would typically be run as containerized services.

Expand Down
18 changes: 11 additions & 7 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ version: "3"

silent: true

vars:
PYTHON_LINT_PATHS: apps/python tests/python live_test
PYTHON_TEST_DATABASE: tests/python/lib/database/models/test_schema_parity.py

tasks:
default:
desc: Show all available tasks
Expand All @@ -21,17 +25,17 @@ tasks:
lint:
desc: Run linting
cmds:
- uv run ruff check .
- uv run ruff check {{.PYTHON_LINT_PATHS}}

format:
desc: Run formatting
cmds:
- uv run ruff check . --fix && uv run ruff format .
- uv run ruff check {{.PYTHON_LINT_PATHS}} --fix && uv run ruff format {{.PYTHON_LINT_PATHS}}

type-check:
desc: Run type checking with mypy
cmds:
- uv run mypy lib reports sync_server
- uv run mypy

test:
desc: Run all tests with pytest
Expand All @@ -51,22 +55,22 @@ tasks:
test-database:
desc: Run database-only tests
cmds:
- uv run pytest -m database tests/lib/database/models/test_schema_parity.py
- uv run pytest -m database {{.PYTHON_TEST_DATABASE}}

test-cov:
desc: Run all tests with coverage report
cmds:
- uv run pytest --cov=lib --cov=sync_server --cov=reporter --cov-report=term-missing
- uv run pytest --cov --cov-report=term-missing

test-cov-unit:
desc: Run unit tests with coverage report
cmds:
- uv run pytest -m unit --cov=lib --cov=sync_server --cov=reporter --cov-report=term-missing
- uv run pytest -m unit --cov --cov-report=term-missing

test-cov-integration:
desc: Run integration tests with coverage report
cmds:
- uv run pytest -m integration --cov=lib --cov=sync_server --cov=reporter --cov-report=term-missing
- uv run pytest -m integration --cov --cov-report=term-missing

diagram:
desc: Convert Mermaid diagrams to SVG
Expand Down
26 changes: 26 additions & 0 deletions apps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# `apps/` — language-isolated components

Application code lives under `apps/<language>/`, not at the repository root. This keeps Python and future Go trees separate and avoids naming clashes (`lib/`, `sync_server/`, etc.).

## Layout

| Path | Contents |
| -------------------- | ------------------------------------------------------------------------------------- |
| [`python/`](python/) | Shared library, reports, administration, sync server, UI, backup daemon |
| `go/` (planned) | Go commands and `internal/` packages — see [`../Golang-notes.md`](../Golang-notes.md) |

All Python source is under `apps/python/`; there are no root-level `lib/` or `reports/` directories in the repository checkout.

## Production install (flat Python)

Installed environments use a **flat** layout under `/opt/reporter/` (e.g. `lib/`, `reports/`, `sync_server/`), not `apps/python/…`. That matches Hatch package names and existing config paths (`reports/config.toml`).

Go services ship as **binaries** (or containers), typically under `/opt/reporter/bin/`, not as a second `lib/` tree on the install root. Details: [`Golang-notes.md`](../Golang-notes.md).

## Tests

Python tests mirror this tree under [`tests/python/`](../tests/python/) (see [`docs/development/TESTING.md`](../docs/development/TESTING.md)).

## Developer entrypoints

Stable commands stay at repository root [`bin/`](../bin/); they forward to `apps/python/…/run` scripts. Install-only scripts are under [`release/common/bin/`](../release/common/bin/).
File renamed without changes.
File renamed without changes.
4 changes: 4 additions & 0 deletions apps/python/administration/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
# Dev implementation script for admin CLI.

uv run dotenv run -- admin "$@"
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 4 additions & 0 deletions apps/python/backup_server/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
# Dev implementation script for backup server.

uv run dotenv run backup-server "$@"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 3 additions & 1 deletion lib/_admin/main.py → apps/python/lib/_admin/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

import toml

from lib._shared.helpers import python_app_root

from .admin import run
from .cli_parser import build_parser

Expand All @@ -17,7 +19,7 @@


def main() -> None:
args_config_file = "administration/config.toml"
args_config_file = python_app_root() / "administration" / "config.toml"

with open(args_config_file) as f:
args_spec = toml.load(f)
Expand Down
4 changes: 4 additions & 0 deletions apps/python/lib/_admin/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
# Dev implementation script for admin CLI (Stage 3 target: apps/python/administration/run).

uv run dotenv run -- admin "$@"
File renamed without changes.
18 changes: 17 additions & 1 deletion lib/_backup/backup.py → apps/python/lib/_backup/backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@

from lib._backup.backup_archive import (
archive_members,
dry_run_messages,
is_backup_dev_mode,
latest_dump_per_db,
parse_running_reporter_containers,
write_archive,
)
from lib._shared.helpers import repo_root

# At runtime this module lives at /opt/reporter/lib/_backup/backup.py.
REPORTER_HOME_DEFAULT = Path(__file__).resolve().parents[2]
REPORTER_HOME_DEFAULT = repo_root(Path(__file__))
BACKUP_DIR_ENV = "BACKUP_DIR"
DEFAULT_BACKUP_DIR = ".backup"

Expand Down Expand Up @@ -66,6 +69,19 @@ def main() -> int:
backup_dir = resolve_backup_dir(reporter_home, os.getenv(BACKUP_DIR_ENV))
destination_dir = Path(args.destination).expanduser().resolve()

if is_backup_dev_mode():
dump_files = (
[path.name for path in backup_dir.glob("*.dump")] if backup_dir.is_dir() else []
)
members = archive_members(
reporter_home,
backup_dir,
latest_dump_per_db(dump_files),
)
for line in dry_run_messages(backup_dir, members):
print(line)
return 0

running = running_reporter_containers()
if running:
print(f"FATAL: Reporter containers are running: {', '.join(running)}", file=sys.stderr)
Expand Down
Loading
Loading