# Deployment Guide ## Local Development ### Prerequisites - **Python 3.13+** (required by `pyproject.toml`) - **[uv](https://docs.astral.sh/uv/)** — Fast Python package manager - Google Sheets API credentials (service account JSON) ### Setup ```bash git clone cd fuj-management # Install dependencies uv sync # Configure credentials mkdir -p .secret cp /path/to/credentials.json .secret/fuj-management-bot-credentials.json # Optional: Set Fio API token for richer bank data export FIO_API_TOKEN=your_token_here # Start the web dashboard make web # → Flask server at http://localhost:5001 ``` ### Makefile Targets | Target | Command | Description | |--------|---------|-------------| | `help` | `make help` | List all available targets | | `venv` | `make venv` | Sync virtual environment with pyproject.toml | | `fees` | `make fees` | Print fee calculation table | | `match` | `make match` | (Legacy) Direct bank matching | | `web` | `make web` | Start Flask dashboard on port 5001 | | `sync` | `make sync` | Sync last 30 days of bank transactions | | `sync-2026` | `make sync-2026` | Sync full year 2026 transactions | | `infer` | `make infer` | Auto-fill Person/Purpose in the sheet | | `reconcile` | `make reconcile` | Print CLI balance report | | `test` | `make test` | Run test suite | | `test-v` | `make test-v` | Run tests with verbose output | | `image` | `make image` | Build Docker image | | `run` | `make run` | Run Docker container locally | The Makefile includes **automatic venv management**: targets that need Python depend on `.venv/.last_sync`, which triggers `uv sync` when `pyproject.toml` changes. --- ## Docker Container ### Building ```bash make image # → docker build -t fuj-management:latest -f build/Dockerfile . ``` ### Dockerfile Details **Base image**: `python:3.13-alpine` **Build stages**: 1. Install system packages (`bash`, `tzdata`) 2. Set timezone to `Europe/Prague` 3. Install Python dependencies via pip 4. Copy application files (`app.py`, `scripts/`, `templates/`, `Makefile`) 5. Copy entrypoint script **Exposed port**: 5001 **Health check**: `wget -q -O /dev/null http://localhost:5001/` every 60s ### Running Locally via Docker ```bash make run # → docker run -it --rm -p 5001:5001 fuj-management:latest # With credentials and environment: docker run -it --rm \ -p 5001:5001 \ -v $(pwd)/.secret:/app/.secret:ro \ -e FIO_API_TOKEN=your_token \ -e BANK_ACCOUNT=CZ8520100000002800359168 \ fuj-management:latest ``` ### Entrypoint The `build/entrypoint.sh` script simply runs: ```bash exec python3 /app/app.py ``` This uses Flask's built-in server directly. For a production deployment, consider adding gunicorn or waitress (noted as a TODO in the entrypoint). ### Environment Variables | Variable | Default | Description | |----------|---------|-------------| | `BANK_ACCOUNT` | `CZ8520100000002800359168` | IBAN for QR code generation | | `FIO_API_TOKEN` | *(none)* | Fio REST API token | | `PYTHONUNBUFFERED` | `1` (set in Dockerfile) | Ensures real-time log output | --- ## CI/CD Pipeline ### Gitea Actions The project uses two Gitea Actions workflows: #### 1. Build and Push (`build.yaml`) **Triggers**: - Push of any tag - Manual dispatch (with custom tag input) **Steps**: 1. Checkout code 2. Login to Gitea container registry (`gitea.home.hrajfrisbee.cz`) 3. Build Docker image using `build/Dockerfile` 4. Push to `gitea.home.hrajfrisbee.cz//:` **Tag resolution**: Uses the git tag name. For manual dispatch, uses the provided input. #### 2. Deploy to Kubernetes (`kubernetes-deploy.yaml`) **Triggers**: - Push to any branch - Manual dispatch **Steps**: 1. Checkout code 2. Install kubectl 3. Retrieve Kanidm token from HashiCorp Vault: - Authenticate to Vault via AppRole (`VAULT_ROLE_ID` / `VAULT_SECRET_ID`) - Fetch API token from `secret/data/gitea/gitea-ci` 4. Exchange API token for K8s OIDC token via Kanidm: - POST to `https://idm.home.hrajfrisbee.cz/oauth2/token` - Token exchange using `urn:ietf:params:oauth:grant-type:token-exchange` 5. Configure kubectl with the OIDC token 6. Run `kubectl auth whoami` and `kubectl get ns` (deploy commands are commented out — WIP) **Required secrets**: | Secret | Purpose | |--------|---------| | `REGISTRY_TOKEN` | Docker registry authentication | | `VAULT_ROLE_ID` | HashiCorp Vault AppRole role ID | | `VAULT_SECRET_ID` | HashiCorp Vault AppRole secret ID | | `K8S_CA_CERT` | Kubernetes cluster CA certificate | ### Infrastructure Topology ``` Gitea (git push / tag) │ ├── build.yaml → Docker Build → Gitea Container Registry │ (gitea.home.hrajfrisbee.cz) │ └── kubernetes-deploy.yaml → Vault → Kanidm → K8s Cluster (192.168.0.31:6443) ``` This is a self-hosted infrastructure stack: - **Gitea** for git hosting and CI/CD - **HashiCorp Vault** for secret management - **Kanidm** for identity/OIDC - **Kubernetes** for container orchestration --- ## Credentials Management ### Google Sheets API The system uses a **Google Cloud service account** for accessing the Payments Google Sheet. The credentials file must be: - Stored at `.secret/fuj-management-bot-credentials.json` - In Google Cloud service account JSON format - The service account must be shared (as editor) on the target Google Sheet For local development with OAuth2 (personal Google account), the system also supports the OAuth2 installed app flow — it will generate a `token.pickle` file on first use. ### Fio Bank API Optional. Set the `FIO_API_TOKEN` environment variable. The token is generated in Fio internetbanking under Settings → API. **Rate limit**: 1 request per 30 seconds per token. --- *Deployment documentation generated from comprehensive code analysis on 2026-03-03.*