docs: finalize hacking guidelines
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I2d3a5a9d745630f5da305664f0bdc66e6a6a6964
This commit is contained in:
parent
273d0244aa
commit
520489ab48
1 changed files with 260 additions and 16 deletions
278
docs/HACKING.md
278
docs/HACKING.md
|
|
@ -1,11 +1,12 @@
|
||||||
# Hacking Pinakes
|
# Hacking Pinakes
|
||||||
|
|
||||||
Pinakes is a lot of things. One of the things it aims to be is _complete_. To be
|
Pinakes is a lot of things. It also _plans_ to be a lot of things. One of those
|
||||||
complete in features, to be complete in documentation and to be complete in
|
things, as you might have noticed, is _complete_. To be complete in features, to
|
||||||
hackability. This document covers, very comprehensively, how you may:
|
be complete in documentation and to be complete in hackability. This document
|
||||||
|
covers, very comprehensively, how you may:
|
||||||
|
|
||||||
- Build Pinakes
|
|
||||||
- Develop Pinakes
|
- Develop Pinakes
|
||||||
|
- Build Pinakes
|
||||||
- Contribute to Pinakes
|
- Contribute to Pinakes
|
||||||
|
|
||||||
for developers as well as:
|
for developers as well as:
|
||||||
|
|
@ -14,19 +15,262 @@ for developers as well as:
|
||||||
|
|
||||||
for Pinakes maintainers and packagers.
|
for Pinakes maintainers and packagers.
|
||||||
|
|
||||||
## Building Pinakes
|
## Development Environment
|
||||||
|
|
||||||
Pinakes is built with Rust (nightly edition) and various crates. The most
|
|
||||||
_notable_ crate among those is Dioxus, which provides its own toolkit. The UI
|
|
||||||
for Pinakes is usually _not_ built with the Dioxus CLI but instead with
|
|
||||||
`cargo build`. This also applies to distributable build results.
|
|
||||||
|
|
||||||
|
[Nix]: https://nixos.org
|
||||||
[Direnv]: https://direnv.net
|
[Direnv]: https://direnv.net
|
||||||
|
|
||||||
To build Pinakes, simply pick the components you want and build them with
|
Pinakes uses [Nix] with flakes for pure, reproducible developer environments.
|
||||||
`cargo build --release --package <component>`. A Nix shell is provided for
|
All of the build dependencies that you need, from Rust to GTK and webkit, are
|
||||||
reproducible developer environments and you may obtain all build dependencies by
|
provided in the devshell. In addition to _not_ requiring system libraries, we'd
|
||||||
simply running `nix develop` or `direnv allow` if you use [Direnv]. Nix is a
|
like to _actively discourage you_ from relying on system libraries.
|
||||||
cross-platform build tool and works on most Linux distributions as well as
|
|
||||||
Darwin. While distro-specific package managers _might_ work, Nix is the only
|
[Direnv] may be preferred by some users as an alternative to `nix develop`.
|
||||||
supported one.
|
|
||||||
|
```bash
|
||||||
|
# Enter the dev shell
|
||||||
|
$ nix develop
|
||||||
|
|
||||||
|
# Or with direnv (recommended)
|
||||||
|
$ direnv allow
|
||||||
|
```
|
||||||
|
|
||||||
|
Nix is the only supported way to get all dependencies. Distro package managers
|
||||||
|
_may_ work for the core crates but are not supported for the UI.
|
||||||
|
|
||||||
|
## Building Pinakes
|
||||||
|
|
||||||
|
Pinakes is a Cargo workspace with multiple crates targeting multiple aspects of
|
||||||
|
the projects. The user-facing crates are provided in `packages/*`.
|
||||||
|
`pinakes-server` and `pinakes-tui` can be built normally with `cargo` while the
|
||||||
|
UI crate, i.e., `pinakes-ui` **must** be built with the Dioxus CLI (`dx`) so
|
||||||
|
that the SCSS stylesheets are compiled correctly. Everything else should work
|
||||||
|
perfectly fine with the standard `cargo build`.
|
||||||
|
|
||||||
|
[Just]: https://just.systems
|
||||||
|
|
||||||
|
To make the things a _little_ bit more convenient, this project uses [Just] as
|
||||||
|
its command runner and provides a few recipes for common tasks.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build all crates (server + TUI + UI)
|
||||||
|
$ just build-all
|
||||||
|
|
||||||
|
# Build individual components
|
||||||
|
$ just build-server # pinakes-server (HTTP API)
|
||||||
|
$ just build-tui # pinakes-tui (terminal UI)
|
||||||
|
$ just build-ui # pinakes-ui (Dioxus desktop/web UI, requires dx)
|
||||||
|
```
|
||||||
|
|
||||||
|
The dependencies required for the recipes, including Just itself, is provided by
|
||||||
|
the Nix devshell. You may also run the cargo equivalents of those commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Core crates
|
||||||
|
$ cargo build -p pinakes-server
|
||||||
|
$ cargo build -p pinakes-tui
|
||||||
|
|
||||||
|
# The UI crate must be built with dx, not cargo.
|
||||||
|
$ dx build -p pinakes-ui
|
||||||
|
```
|
||||||
|
|
||||||
|
Release builds follow the same pattern, but with `--release`. Nothing else
|
||||||
|
should be required for the most part.
|
||||||
|
|
||||||
|
## Running Pinakes
|
||||||
|
|
||||||
|
For the time being, UI clients require the server to be running before they can
|
||||||
|
function. Offline sync is planned, but likely come later alongside cross-device
|
||||||
|
sync. All UIs connect to the server over HTTP.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Copy and edit the example config (first time only)
|
||||||
|
$ cp pinakes.example.toml pinakes.toml
|
||||||
|
|
||||||
|
# 2. Start the server (defaults to 127.0.0.1:3000)
|
||||||
|
$ cargo run -p pinakes-server -- pinakes.toml
|
||||||
|
|
||||||
|
# 3a. Run the TUI client
|
||||||
|
$ cargo run -p pinakes-tui
|
||||||
|
|
||||||
|
# 3b. Run the desktop/web UI
|
||||||
|
$ cargo run -p pinakes-ui
|
||||||
|
|
||||||
|
# Connect TUI to a non-default server address
|
||||||
|
$ cargo run -p pinakes-tui -- --server http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
The GUI reads `PINAKES_SERVER_URL` if set; otherwise it assumes
|
||||||
|
`http://127.0.0.1:3000`. You may change the host and port if you require.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
[cargo-nextest]: https://nexte.st
|
||||||
|
|
||||||
|
Pinakes boasts, or well, attempts to boast a very comprehensive testing suite to
|
||||||
|
ensure no regressions are introduced while in development. For fast parallel
|
||||||
|
test execution, we use [cargo-nextest] and while you are recommended to use it
|
||||||
|
over `cargo test` both should be supported. For CI and package tests, only
|
||||||
|
`cargo-nextest` is supported.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all workspace tests (preferred)
|
||||||
|
$ just test
|
||||||
|
|
||||||
|
# Equivalent without Just
|
||||||
|
$ cargo nextest run --workspace
|
||||||
|
|
||||||
|
# Tests for a single crate
|
||||||
|
$ cargo nextest run -p pinakes-core
|
||||||
|
$ cargo nextest run -p pinakes-server
|
||||||
|
|
||||||
|
# Run a specific test by name and show output
|
||||||
|
$ cargo test -p pinakes-core -- test_name --nocapture
|
||||||
|
```
|
||||||
|
|
||||||
|
## Linting and Formatting
|
||||||
|
|
||||||
|
### Formatting
|
||||||
|
|
||||||
|
There is a treewide formatter provided within the `flake.nix` that invokes the
|
||||||
|
recommended formatter tooling across the various filetypes, including database
|
||||||
|
migrations and Markdown sources. While a `just fmt` recipe is provided, it will
|
||||||
|
not cover most of the files. Still, you may format the Rust sources as such:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Format all code
|
||||||
|
$ just fmt # or: cargo fmt
|
||||||
|
|
||||||
|
# Check formatting without modifying files
|
||||||
|
$ cargo fmt --check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linting
|
||||||
|
|
||||||
|
For now lints only cover Rust sources, and are provided by the Clippy tool. You
|
||||||
|
may, as usual, invoke it with `just lint`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run Clippy
|
||||||
|
$ just lint # or: cargo clippy --workspace
|
||||||
|
|
||||||
|
# Treat Clippy warnings as errors (used in CI)
|
||||||
|
$ cargo clippy --workspace -- -D warnings # `-D warnings` is recommended
|
||||||
|
```
|
||||||
|
|
||||||
|
All Clippy warnings, besides some of the really annoying ones or false
|
||||||
|
positives, must be resolved at the source. **Do not** use `#[allow(...)]` or
|
||||||
|
`#[expect(...)]` to silence warnings unless there is a documented reason.
|
||||||
|
|
||||||
|
It may be okay, at times, to suppress lints but you should focus on _resolving_
|
||||||
|
them rather than suppressing them. _If_ suppressing, prefer `#[expect]` and
|
||||||
|
provide the `reason` parameter.
|
||||||
|
|
||||||
|
## Generating API Documentation
|
||||||
|
|
||||||
|
The REST API documentation is generated from OpenAPI annotations in
|
||||||
|
`pinakes-server` using `cargo xtask`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate the API documentation at docs/api/
|
||||||
|
$ just docs # or: cargo xtask docs
|
||||||
|
```
|
||||||
|
|
||||||
|
This writes:
|
||||||
|
|
||||||
|
- `docs/api.md` - Index linking all generated files
|
||||||
|
- `docs/api/<tag>.md` - One Markdown file per API tag
|
||||||
|
- `docs/api/openapi.json` - Full OpenAPI 3.0 specification
|
||||||
|
|
||||||
|
Please do not edit files under `docs/api/` by hand; they are regenerated on each
|
||||||
|
run.
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
There are many conventions that are in play within the Pinakes codebase. You'll
|
||||||
|
get used and adjust to most of them naturally, but here are some of the general
|
||||||
|
conventions written down for convenience.
|
||||||
|
|
||||||
|
### General
|
||||||
|
|
||||||
|
1. **Make illegal states unrepresentable**; ıse ADTs and newtype wrappers to
|
||||||
|
encode constraints in the type system. Parse and validate at system
|
||||||
|
boundaries (user input, external APIs); trust internal types everywhere else.
|
||||||
|
2. This one is kind of obvious, but please use the `tracing` crate with
|
||||||
|
structured fields. Do not use `println!` or `eprintln!` in library or server
|
||||||
|
code.
|
||||||
|
3. All storage operations must be implemented for both the SQLite and PostgreSQL
|
||||||
|
backends. Neither backend is optional.
|
||||||
|
|
||||||
|
Naming is kind of obvious, but I'd like to remind you that we use `snake_case`
|
||||||
|
for functions and variables, `PascalCase` for types and traits,
|
||||||
|
`SCREAMING_SNAKE_CASE` for constants. There is not much else to it.
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
- **`unwrap()` and `expect()` are banned** in non-test code. Use `?`,
|
||||||
|
`ok_or_else`, `match`, or `if let` instead.
|
||||||
|
- The only accepted exception is when a failure is truly impossible (e.g., a
|
||||||
|
hardcoded regex that is known-valid at compile time). In that case, add
|
||||||
|
`#[expect(clippy::expect_used, reason = "...")]` with a clear explanation.
|
||||||
|
- Wrap storage errors with the `db_ctx` helper so error messages include the
|
||||||
|
operation and relevant ID:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
stmt.execute(params).map_err(db_ctx("insert_media", &media_id))?;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disallowed Types and APIs
|
||||||
|
|
||||||
|
Pinakes uses hashers from `rustc_hash`, i.e., `FxHashMap` and `FxHashSet` over
|
||||||
|
the `std` equivalents due to their faster nature without random state. This is
|
||||||
|
enforced by clippy.
|
||||||
|
|
||||||
|
As Pinakes uses Rust 1.90+, the now-deprecated `once_cell` crate is also banned.
|
||||||
|
Use the stdlib equivalents:
|
||||||
|
|
||||||
|
- `once_cell::unsync::OnceCell` -> `std::cell::OnceCell`
|
||||||
|
- `once_cell::sync::OnceCell` -> `std::sync::OnceLock`
|
||||||
|
- `once_cell::unsync::Lazy` -> `std::cell::LazyCell`
|
||||||
|
- `once_cell::sync::Lazy` -> `std::sync::LazyLock`
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
1. Fork the repository and create a feature branch.
|
||||||
|
2. Enter the dev shell: `nix develop` (or `direnv allow`).
|
||||||
|
3. Make your changes; keep commits focused and descriptive. We use **scoped
|
||||||
|
commits**.
|
||||||
|
4. Run `just fmt` and `just lint` before opening a pull request.
|
||||||
|
5. Run `just test` to verify nothing is broken.
|
||||||
|
6. Open a pull request against `main`. Describe what you changed and why.
|
||||||
|
|
||||||
|
For significant changes, consider opening an issue first to discuss the
|
||||||
|
approach.
|
||||||
|
|
||||||
|
## Packaging and Distribution
|
||||||
|
|
||||||
|
Pinakes is released under EUPL v1.2. For distributable release builds, please
|
||||||
|
respect the license. You may create release builds for the user-facing crates
|
||||||
|
using `cargo build --release` for server and TUI components. Use `dx` instead
|
||||||
|
for the GUI:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Server
|
||||||
|
$ cargo build --release -p pinakes-server
|
||||||
|
|
||||||
|
# TUI
|
||||||
|
$ cargo build --release -p pinakes-tui
|
||||||
|
|
||||||
|
# GUI
|
||||||
|
$ dx build --release -p pinakes-ui
|
||||||
|
```
|
||||||
|
|
||||||
|
The resulting binaries are self-contained except for system libraries required
|
||||||
|
by the UI (GTK3, libsoup, webkit2gtk). Database migrations run automatically on
|
||||||
|
first server startup via the `refinery` crate; no manual migration step is
|
||||||
|
needed.
|
||||||
|
|
||||||
|
SQLite (the default backend) requires no external database server. PostgreSQL
|
||||||
|
deployments need the `pg_trgm` extension enabled on the target database. It is
|
||||||
|
generally advisable to document those requirements for the users, even though
|
||||||
|
they do not require additional packaging steps.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue