Update project conventions and infrastructure docs
This commit is contained in:
@@ -25,9 +25,9 @@ push via GitHub Actions.
|
|||||||
|
|
||||||
The Docker image is a multi-stage build:
|
The Docker image is a multi-stage build:
|
||||||
|
|
||||||
1. **Builder** — `hexpm/elixir:1.20.0-rc.4-erlang-28.4.2-debian-trixie-20260406-slim` with
|
1. **Builder** — `hexpm/elixir:1.20.0-rc.4-erlang-28.4.3-debian-trixie-20260421-slim` with
|
||||||
Node.js 25, compiles deps, builds assets (`mix assets.deploy`), generates an OTP release.
|
Node.js 25, compiles deps, builds assets (`mix assets.deploy`), generates an OTP release.
|
||||||
2. **Runner** — `debian:trixie-20260406-slim` with minimal runtime deps (`libstdc++6`,
|
2. **Runner** — `debian:trixie-20260421-slim` with minimal runtime deps (`libstdc++6`,
|
||||||
`openssl`, `libncurses6`, `ca-certificates`). Runs as unprivileged `nobody` user.
|
`openssl`, `libncurses6`, `ca-certificates`). Runs as unprivileged `nobody` user.
|
||||||
|
|
||||||
Fluxon UI (licensed dependency) is fetched during build via Docker build secrets
|
Fluxon UI (licensed dependency) is fetched during build via Docker build secrets
|
||||||
@@ -61,18 +61,24 @@ Configured inline in `compose.yaml`. Runs as a separate Docker Compose service
|
|||||||
|---------|-------|
|
|---------|-------|
|
||||||
| S3 endpoint | `https://nbg1.your-objectstorage.com` |
|
| S3 endpoint | `https://nbg1.your-objectstorage.com` |
|
||||||
| Bucket | `ffmusiclibrary` |
|
| Bucket | `ffmusiclibrary` |
|
||||||
| Sync interval | 24 hours |
|
| Sync interval | 60 minutes |
|
||||||
| Snapshot interval | 24 hours |
|
| Retention | 672 hours (28 days) |
|
||||||
| Retention | 168 hours (1 week) |
|
|
||||||
| Healthcheck | `litestream databases` every 30s (timeout 10s, 3 retries) |
|
| Healthcheck | `litestream databases` every 30s (timeout 10s, 3 retries) |
|
||||||
|
|
||||||
Credentials via environment: `LITESTREAM_ACCESS_KEY_ID`, `LITESTREAM_SECRET_ACCESS_KEY`.
|
Credentials via environment: `LITESTREAM_ACCESS_KEY_ID`, `LITESTREAM_SECRET_ACCESS_KEY`.
|
||||||
|
|
||||||
### Manual backup
|
### Manual backup
|
||||||
|
|
||||||
`mise run prod:backup` pulls the production database via `rsync` and saves it locally
|
Two manual options are available:
|
||||||
as `data/music_library_prod_<timestamp>.db`. Old backups can be cleaned with
|
|
||||||
`mise run prod:prune-backups`.
|
- `mise run prod:backup` — takes an atomic snapshot of the live database on the
|
||||||
|
production server using `sqlite3 .backup`, then rsyncs it locally as
|
||||||
|
`data/music_library_prod_<timestamp>.db`. Safe under concurrent writes.
|
||||||
|
- `mise run prod:litestream-backup` — restores the production database locally from
|
||||||
|
the S3 Litestream replica. Requires `LITESTREAM_ACCESS_KEY_ID` and
|
||||||
|
`LITESTREAM_SECRET_ACCESS_KEY`.
|
||||||
|
|
||||||
|
Old backups can be cleaned with `mise run prod:prune-backups`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -100,9 +100,9 @@ Rules extracted from commit history that are specific to this project and not al
|
|||||||
- **Alias nested modules at the top** rather than referencing them inline (e.g. `alias LastFm.Fixtures.RecentTracks` then `RecentTracks.get()`, not `LastFm.Fixtures.RecentTracks.get()`). Enforced by Credo's `AliasUsage` check.
|
- **Alias nested modules at the top** rather than referencing them inline (e.g. `alias LastFm.Fixtures.RecentTracks` then `RecentTracks.get()`, not `LastFm.Fixtures.RecentTracks.get()`). Enforced by Credo's `AliasUsage` check.
|
||||||
- **Markdown sanitization via MDEx (ammonia).** Use `Markdown.to_html/1` for user content. Annotate raw output with `# sobelow_skip ["XSS.Raw"]` and a comment explaining the sanitization.
|
- **Markdown sanitization via MDEx (ammonia).** Use `Markdown.to_html/1` for user content. Annotate raw output with `# sobelow_skip ["XSS.Raw"]` and a comment explaining the sanitization.
|
||||||
- **Sobelow runs on CI and pre-commit** in skip mode for security analysis.
|
- **Sobelow runs on CI and pre-commit** in skip mode for security analysis.
|
||||||
- **ExSlop checks run via Credo** for code quality: no narrator/boilerplate docs (use `@moduledoc false` instead), no obvious/step/narrator comments, no identity `case`/`with` patterns, no `Repo.all` then filter, no `Enum.map` with inline queries. Violations are caught by CI.
|
- **ExSlop checks run via Credo** for code quality: no narrator/boilerplate docs (use `@moduledoc false` instead), no obvious/step/narrator comments, no identity `case`/`with` patterns, no `Repo.all` then filter, no `Enum.map` with inline queries, no unaliased nested module use (nested modules must be aliased at the top). Violations are caught by CI.
|
||||||
- **All modules require `@moduledoc`.** The Credo `ModuleDoc` check is enforced in strict mode.
|
- **All modules require `@moduledoc`.** The Credo `ModuleDoc` check is enforced in strict mode.
|
||||||
- **The project does not use dialyzer**. If any skill suggests its usage, ignore it.
|
- **Dialyzer is enabled via `dialyxir` (dev/test only) with the `:no_opaque` flag.** Specs are required and checked. Opaque type checking is disabled to tolerate third-party opaque types (e.g., `Ecto.Changeset`).
|
||||||
- **Validate Docker builder image before updating versions.** When changing `ELIXIR_VERSION`, `OTP_VERSION`, or `DEBIAN_VERSION` in the Dockerfile, run `mise run dev:validate-docker-image` to confirm the generated `hexpm/elixir` tag exists on Docker Hub and supports both `linux/amd64` and `linux/arm64`.
|
- **Validate Docker builder image before updating versions.** When changing `ELIXIR_VERSION`, `OTP_VERSION`, or `DEBIAN_VERSION` in the Dockerfile, run `mise run dev:validate-docker-image` to confirm the generated `hexpm/elixir` tag exists on Docker Hub and supports both `linux/amd64` and `linux/arm64`.
|
||||||
|
|
||||||
## Reviews & Audits
|
## Reviews & Audits
|
||||||
|
|||||||
Reference in New Issue
Block a user