diff --git a/README.md b/README.md index cfe0798..3a246f0 100644 --- a/README.md +++ b/README.md @@ -1,79 +1,68 @@ # matomo-bootstrap -[![GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub%20Sponsors-blue?logo=github)](https://github.com/sponsors/kevinveenbirkenbach) [![Patreon](https://img.shields.io/badge/Support-Patreon-orange?logo=patreon)](https://www.patreon.com/c/kevinveenbirkenbach) [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20me%20a%20Coffee-Funding-yellow?logo=buymeacoffee)](https://buymeacoffee.com/kevinveenbirkenbach) [![PayPal](https://img.shields.io/badge/Donate-PayPal-blue?logo=paypal)](https://s.veen.world/paypaldonate) - - -Headless bootstrap tooling for **Matomo** -Automates **installation** (via recorded Playwright flow) and **API token provisioning** for fresh Matomo instances. - -This tool is designed for **CI, containers, and reproducible environments**, where no interactive browser access is available. +Headless bootstrap tooling for **Matomo**. Automates **first-time installation** and **API token provisioning** for fresh Matomo instances. --- ## Features -* 🚀 **Fully headless Matomo installation** - - * Drives the official Matomo web installer using **Playwright** - * Automatically skips installation if Matomo is already installed -* 🔐 **API token provisioning** - - * Creates an *app-specific token* via authenticated Matomo session - * Compatible with Matomo 5.3.x Docker images -* 🧪 **E2E-tested** - - * Docker-based end-to-end tests included -* ❄️ **First-class Nix support** - - * Flake-based packaging - * Reproducible CLI and dev environments -* 🐍 **Standard Python CLI** - - * Installable via `pip` - * Clean stdout (token only), logs on stderr +- 🚀 **Fully headless Matomo installation** + - Drives the official Matomo web installer using **Playwright** + - Automatically skips the installer if Matomo is already installed +- 🔐 **API token provisioning** + - Creates an **app-specific token** via an authenticated Matomo session + - Compatible with **Matomo 5.3.x** Docker images +- 🧪 **E2E-tested** + - Docker-based end-to-end tests included +- ❄️ **First-class Nix support** + - Flake-based packaging and pinned `flake.lock` + - Uses `nixpkgs` browsers via `playwright-driver` (no Playwright downloads) +- 🧼 **Token-only stdout contract** + - **stdout contains only the token** (safe for scripting) + - Logs go to **stderr** --- ## Requirements -* A running Matomo instance (e.g. Docker) -* For fresh installs: - - * Chromium (managed automatically by Playwright) +- A running Matomo instance (e.g. via Docker) +- For fresh installs: + - Chromium (provided by Playwright or by the Playwright base container image) --- ## Installation -### Using **Nix** (recommended) +### Nix (recommended) -If you use **Nix** with flakes: +Run directly from the repository: ```bash nix run github:kevinveenbirkenbach/matomo-bootstrap ``` -Install Playwright’s Chromium browser (one-time): - -```bash -nix run github:kevinveenbirkenbach/matomo-bootstrap#matomo-bootstrap-playwright-install -``` - -This installs Chromium into the user cache used by Playwright. +In Nix mode, browsers are provided via `nixpkgs` (`playwright-driver`) and Playwright downloads are disabled. --- -### Using **Python / pip** +### Python / pip -Requires **Python ≥ 3.10** +Requires **Python ≥ 3.10**: ```bash pip install matomo-bootstrap +python -m playwright install chromium ``` -Install Chromium for Playwright: +--- + +### Docker image (GHCR) + +Pull the prebuilt image: ```bash -python -m playwright install chromium +docker pull ghcr.io/kevinveenbirkenbach/matomo-bootstrap:stable +# or: +docker pull ghcr.io/kevinveenbirkenbach/matomo-bootstrap:latest ``` --- @@ -86,11 +75,12 @@ python -m playwright install chromium matomo-bootstrap \ --base-url http://127.0.0.1:8080 \ --admin-user administrator \ - --admin-password AdminSecret123! \ - --admin-email administrator@example.org + --admin-password 'AdminSecret123!' \ + --admin-email administrator@example.org \ + --token-description my-ci-token ``` -On success, the command prints **only the API token** to stdout: +On success, the command prints **only the token** to stdout: ```text 6c7a8c2b0e9e4a3c8e1d0c4e8a6b9f21 @@ -98,14 +88,14 @@ On success, the command prints **only the API token** to stdout: --- -### Environment Variables +### Environment variables All options can be provided via environment variables: ```bash export MATOMO_URL=http://127.0.0.1:8080 export MATOMO_ADMIN_USER=administrator -export MATOMO_ADMIN_PASSWORD=AdminSecret123! +export MATOMO_ADMIN_PASSWORD='AdminSecret123!' export MATOMO_ADMIN_EMAIL=administrator@example.org export MATOMO_TOKEN_DESCRIPTION=my-ci-token @@ -114,9 +104,9 @@ matomo-bootstrap --- -### Debug Mode +### Debug mode -Enable verbose logs (stderr only): +Enable verbose logs (**stderr only**): ```bash matomo-bootstrap --debug @@ -124,27 +114,172 @@ matomo-bootstrap --debug --- -## How It Works +## Docker Compose integration (one-shot bootstrap) -1. **Reachability check** +### Why “one-shot”? - * Waits until Matomo responds over HTTP (any status) -2. **Installation (if needed)** +The bootstrap container is meant to: - * Uses a recorded Playwright flow to complete the Matomo web installer -3. **Authentication** +1. Run once, +2. Print the token to stdout, +3. Exit with code `0`. - * Logs in using the `Login.logme` controller -4. **Token creation** +You should **not** start it automatically on every `docker compose up`. +Instead, start Matomo normally, then run the bootstrap via `docker compose run`. - * Calls `UsersManager.createAppSpecificTokenAuth` -5. **Output** - - * Prints the token to stdout (safe for scripting) +The cleanest Compose pattern is to put `bootstrap` behind a **profile**. --- -## End-to-End Tests +### Example `docker-compose.yml` (recommended: `profiles`) + +```yaml +services: + db: + image: mariadb:11 + container_name: matomo-db + restart: unless-stopped + environment: + MARIADB_DATABASE: matomo + MARIADB_USER: matomo + MARIADB_PASSWORD: matomo_pw + MARIADB_ROOT_PASSWORD: root_pw + volumes: + - mariadb_data:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "mariadb-admin ping -uroot -proot_pw --silent"] + interval: 5s + timeout: 3s + retries: 60 + + matomo: + image: matomo:5.3.2 + container_name: matomo + restart: unless-stopped + depends_on: + db: + condition: service_healthy + ports: + - "${MATOMO_PORT:-8080}:80" + environment: + MATOMO_DATABASE_HOST: db + MATOMO_DATABASE_ADAPTER: mysql + MATOMO_DATABASE_USERNAME: matomo + MATOMO_DATABASE_PASSWORD: matomo_pw + MATOMO_DATABASE_DBNAME: matomo + volumes: + - matomo_data:/var/www/html + healthcheck: + test: ["CMD-SHELL", "wget -qO- http://127.0.0.1/ >/dev/null || exit 1"] + interval: 10s + timeout: 5s + retries: 60 + + bootstrap: + # This prevents automatic startup during a normal `docker compose up` + profiles: ["bootstrap"] + + # Option A: use the published image (recommended) + image: ghcr.io/kevinveenbirkenbach/matomo-bootstrap:1.0.1 + + # Option B: build locally from the repository checkout + # build: + # context: . + # dockerfile: Dockerfile + # image: matomo-bootstrap:local + + container_name: matomo-bootstrap + depends_on: + matomo: + condition: service_started + environment: + # inside the compose network, Matomo is reachable via the service name + MATOMO_URL: "http://matomo" + + MATOMO_ADMIN_USER: "administrator" + MATOMO_ADMIN_PASSWORD: "AdminSecret123!" + MATOMO_ADMIN_EMAIL: "administrator@example.org" + MATOMO_TOKEN_DESCRIPTION: "docker-compose-bootstrap" + + # Values used by the recorded installer flow + MATOMO_SITE_NAME: "Matomo (docker-compose)" + MATOMO_SITE_URL: "http://127.0.0.1:${MATOMO_PORT:-8080}" + MATOMO_TIMEZONE: "Germany - Berlin" + + # Optional stability knobs + MATOMO_TIMEOUT: "30" + MATOMO_PLAYWRIGHT_HEADLESS: "1" + MATOMO_PLAYWRIGHT_NAV_TIMEOUT_MS: "60000" + MATOMO_PLAYWRIGHT_SLOWMO_MS: "0" + + restart: "no" + +volumes: + mariadb_data: + matomo_data: +``` + +--- + +### Commands + +Start DB + Matomo **without** bootstrap: + +```bash +docker compose up -d db matomo +``` + +Run bootstrap once (prints token to stdout): + +```bash +docker compose --profile bootstrap run --rm bootstrap +``` + +Re-run bootstrap (creates a new token by default): + +```bash +docker compose --profile bootstrap run --rm bootstrap +``` + +--- + +## Idempotency / avoiding new tokens on every run + +By default, `UsersManager.createAppSpecificTokenAuth` creates a new token each time. + +If you want strictly idempotent runs in automation, you can provide an existing token +and make the bootstrap return it instead of creating a new one: + +```bash +export MATOMO_BOOTSTRAP_TOKEN_AUTH="0123456789abcdef..." +matomo-bootstrap +``` + +> This is useful for CI re-runs or configuration management tools. + +--- + +## How it works + +1. **Reachability check** + + * waits until Matomo responds via HTTP (any status is considered “reachable”) +2. **Installation (if needed)** + + * uses a recorded Playwright flow to complete the Matomo web installer +3. **Authentication** + + * logs in using Matomo’s `Login.logme` controller (cookie session) +4. **Token creation** + + * calls `UsersManager.createAppSpecificTokenAuth` +5. **Output** + + * prints the token to stdout (token-only contract) + +--- + +## End-to-end tests Run the full E2E cycle locally: @@ -157,27 +292,18 @@ This will: 1. Start Matomo + MariaDB via Docker 2. Install Matomo headlessly 3. Create an API token -4. Validate the token via Matomo API +4. Validate the token via the Matomo API 5. Tear everything down again --- -## Project Status - -* ✔ Stable for CI / automation -* ✔ Tested against Matomo 5.3.x Docker images -* ⚠ Installer flow is UI-recorded (robust, but may need updates for future Matomo UI changes) - ---- - ## Author **Kevin Veen-Birkenbach** -🌐 [https://www.veen.world/](https://www.veen.world/) +[https://www.veen.world/](https://www.veen.world/) --- ## License -MIT License -See [LICENSE](LICENSE) +MIT — see [LICENSE](LICENSE)