mirror of
https://github.com/oxynote/oxynote.git
synced 2026-10-17 02:49:18 +02:00
* overhaul the notif inbox * make hook summary abstract --------- Co-authored-by: David <SE.David@protonmail.com> Co-authored-by: david <58231312+davseby@users.noreply.github.com>
252 lines
12 KiB
Markdown
252 lines
12 KiB
Markdown
# Contributing
|
|
|
|
This guide covers running Oxynote from a source checkout and the commands
|
|
you need day to day. Everything is driven from the repository root through
|
|
`make`; the component-specific docs are linked at the end.
|
|
|
|
## Prerequisites
|
|
|
|
| Tool | Used for |
|
|
| --- | --- |
|
|
| [Docker](https://www.docker.com) with Compose | the dev stack (Postgres, Caddy, the app containers, …) and the Go database-layer tests, which start throwaway containers |
|
|
| [Go](https://go.dev) 1.27+ | `server/core` and `datagen` |
|
|
| [Node.js](https://nodejs.org) 24+ | `web`, `server/auth-realtime`, `e2e` and the production launcher |
|
|
| [pnpm](https://pnpm.io) | package manager for the Node packages. Any recent install works: pnpm switches to the version pinned in each package's `packageManager` field on its own |
|
|
| [make](https://www.gnu.org/software/make/) | orchestrates every setup, build and run command |
|
|
| [goreleaser](https://goreleaser.com) | builds the Go binaries and their docker images — needed by every target that starts a stack |
|
|
| [golangci-lint](https://golangci-lint.run) | lints the Go code (`make lint`, `make check-lint`) |
|
|
| [moq](https://github.com/matryer/moq) | only to regenerate the Go test mocks with `go generate`; the generated files are committed |
|
|
|
|
## First run
|
|
|
|
```sh
|
|
make setup # install dependencies + create the local env files from templates
|
|
make start # build all images + run the dev stack in the background
|
|
```
|
|
|
|
Then open http://localhost:8080 and sign up. `make stop` stops the stack.
|
|
|
|
## Everyday commands
|
|
|
|
### Running the dev stack
|
|
|
|
```sh
|
|
make run # foreground: streams all logs, ctrl-c stops the stack
|
|
make start # the same, in the background
|
|
make stop # stop the background stack
|
|
make dev # backend containers + the web dev server on the host
|
|
```
|
|
|
|
`make dev` is the frontend loop: the backend runs in containers and Nuxt
|
|
runs on the host with hot reload on http://localhost:3000. Ctrl-c stops the
|
|
dev server; `make stop` stops the containers.
|
|
|
|
All three targets rebuild the images before starting, so a code change is
|
|
picked up by simply running them again.
|
|
|
|
### Environment files
|
|
|
|
The dev stack reads `docker/env/*.local.env`, which `make setup` creates
|
|
from the committed `*.example.env` templates. The files are gitignored and
|
|
the defaults work out of the box. Integrations (GitHub app, Slack app, AI
|
|
assistant, email, changedetection.io) are optional and stay disabled until
|
|
their variables are set; the frontend hides features the deployment does
|
|
not offer. Search is built in: core keeps its index on the container's
|
|
volume and rebuilds it from Postgres when it is missing.
|
|
|
|
```sh
|
|
make check-env # report variables that drifted between the templates and your local files
|
|
make sync-env # rewrite the local files from the templates, keeping your values
|
|
make setup-force # setup, but the local files are recreated from the templates, values and all
|
|
```
|
|
|
|
`make setup` only copies a template when the local file is missing, so a
|
|
variable renamed or added in a template never reaches an existing local
|
|
file. The dev stack targets run `make check-env` first and refuse to start
|
|
on drift — `make sync-env` fixes it.
|
|
|
|
### Linting
|
|
|
|
```sh
|
|
make lint # fix lint, format and type issues in every component
|
|
make check-lint # the same gates, verification only (what CI runs)
|
|
```
|
|
|
|
### End-to-end tests
|
|
|
|
```sh
|
|
make e2e-dev # build the dev stack, run the Playwright suite, tear it down
|
|
make e2e-prod # the same suite against the production all-in-one image
|
|
```
|
|
|
|
Both are one-shot: every run builds the stack and drops it afterwards. To
|
|
iterate on tests, build the stack once with `make e2e-dev-stack-build` (or
|
|
`make e2e-prod-stack-build`), then run `pnpm test:dev` (or `pnpm test:prod`)
|
|
from `e2e/` as often as you like, and finish with
|
|
`make e2e-dev-stack-stop` (or `make e2e-prod-stack-stop`). The e2e stacks
|
|
use their own ports and throwaway state, so they run alongside the dev stack.
|
|
|
|
### Production image
|
|
|
|
```sh
|
|
make prod-build # build the all-in-one image
|
|
make prod-run # build it, then run the example deployment on :8080 (ctrl-c stops it)
|
|
make prod-stop # stop it
|
|
```
|
|
|
|
`make prod-run` also starts a Mailpit on http://localhost:8025 so the
|
|
signup verification mail is readable. Stop the dev stack first — both listen
|
|
on `:8080`.
|
|
|
|
## Ports
|
|
|
|
| Port | Dev stack |
|
|
| --- | --- |
|
|
| `8080` | front door: the app, with the APIs under `/core` and `/auth-realtime` |
|
|
| `8082` | changedetection.io |
|
|
| `8083` | Grafana |
|
|
| `8084` | Mailpit (dev email inbox) |
|
|
| `3000` | the web dev server, only with `make dev` |
|
|
|
|
The e2e dev stack listens on `18080` (Mailpit on `18025`) and the e2e prod
|
|
stack on `19080` (`19025`).
|
|
|
|
## Repository layout
|
|
|
|
- `web/` — Nuxt 4 + Vue 3 frontend; ships as both a web app (SSR) and an
|
|
Electron desktop app. See [web/README.md](web/README.md).
|
|
- `server/core/` — Go API server: storage, search, integrations, AI
|
|
pipelines. See [server/README.md](server/README.md).
|
|
- `server/auth-realtime/` — Node service running Better Auth and a
|
|
Hocuspocus (Yjs) real-time server.
|
|
- `datagen/` — demo-data generator (Go); demo/testing only.
|
|
- `e2e/` — Playwright end-to-end suite and the docker-compose stacks it
|
|
drives.
|
|
- `docker/` — dev docker-compose stack, Caddyfile, env templates, demo-data
|
|
configs.
|
|
- `docker/prod/` — the production all-in-one image and its example
|
|
deployment. See [docker/prod/README.md](docker/prod/README.md).
|
|
- `scripts/` — helpers the root Makefile calls.
|
|
|
|
## AI coding agents
|
|
|
|
Instructions for coding agents live in `AGENTS.md` at the repository root,
|
|
in each component directory, and in a few core packages with rules of their
|
|
own; the root file maps the components and links their files. Every
|
|
`CLAUDE.md` next to one is a one-line import that points Claude Code at the
|
|
`AGENTS.md` beside it — edit `AGENTS.md`, never the shim.
|
|
|
|
## Adding a data source
|
|
|
|
Decide first what you are adding. If the new type is compatible with an
|
|
existing processor, as MariaDB is with the MySQL one, it needs no
|
|
processor of its own, only the wiring in the steps below. Otherwise it
|
|
needs its own processor, client interface, routes and assistant tools.
|
|
|
|
1. **Create the type constant.** The type constant, the credential rules
|
|
and the runner that opens a client for a stored data source all live
|
|
in `server/core/internal/datasource/`. Add the constant, then follow
|
|
an existing type through the package: the credential check, the
|
|
runner's per-type construction and the runner method that returns the
|
|
client, which refuses types it does not accept. A type with its own
|
|
processor also gets its client interface there, with a mock
|
|
regenerated by `go generate` (moq is in the prerequisites).
|
|
2. **Write the processor.** `server/core/internal/datasource/processor/`
|
|
holds one processor per kind of source. It connects, tests the
|
|
connection, including that the credentials cannot write (every data
|
|
source is read-only), maps errors to a connection status, runs
|
|
queries and shapes their results. What a new source needs ranges
|
|
from a set of macro definitions to a whole client; reuse what the
|
|
existing processors share, such as time-range handling, the `$__`
|
|
macros, SQL metadata and result transformation, and add only what the
|
|
new source does differently.
|
|
3. **Add the assistant tools.** Make sure every action the data source
|
|
offers is also an assistant tool in
|
|
`server/core/internal/assistant/tools/`; the MCP server exposes the
|
|
same tools.
|
|
- If metric blocks draw from the new type, add a case to the query
|
|
handler in `server/core/internal/server/internal/datasource/` and
|
|
to the simulation checker in
|
|
`server/core/internal/datasource/simulation/`, which decides when a
|
|
metric block stops showing generated data; without a case it never
|
|
does.
|
|
4. **Add the type to the web app.** The frontend knows the types through
|
|
the enum in `web/app/utils/api/data-source/`. From there, follow an
|
|
existing type through `web/app/components/`: the status icon, the
|
|
settings section that lists connectable sources (without it the type
|
|
cannot be added) and the read-only warning in the connect dialog.
|
|
Their strings live in `web/i18n/locales/`. The help and warning links
|
|
are runtime config, declared in `web/nuxt.config.ts` and
|
|
`web/index.d.ts` and set in `docker/env/web.example.env` and the
|
|
production launcher's env mapping under `docker/prod/launcher/`.
|
|
- If metric blocks draw from the new type, add the query editor's
|
|
language support and the query help popover under
|
|
`editor/blocks/metrics/config-modal/`.
|
|
5. **Add demo data (optional).** `datagen/` fills the demo databases and
|
|
`docker/` runs them. Add a generator package and a block in the
|
|
datagen command that runs it when its env variables are set, a
|
|
compose service with its init script under `docker/demo/`, then
|
|
`make sync-env`.
|
|
6. **Extend the tests and docs.** Every place above that switches on the
|
|
type has a table-driven test with one row per type; extend the tables
|
|
in the packages you touched, and the unit test beside each web
|
|
component. The processor tests start a throwaway container per
|
|
database, so a new one needs a container in their `TestMain` and
|
|
Docker to run. Assistant tool descriptions are compared against a
|
|
golden file; regenerate it from `server/core/` with
|
|
`UPDATE_GOLDEN=1 go test -run Test_Info_toEino ./internal/assistant/tools/`
|
|
and review the diff. Update the supported-sources line in `README.md`,
|
|
and the prerequisites above if a new container became a test
|
|
dependency. The e2e suite creates no data sources and needs nothing.
|
|
|
|
## Adding a hook
|
|
|
|
Decide first whether the hook depends on an integration the deployment
|
|
may not have, the way GitHub tracking needs the GitHub app and the
|
|
website watcher needs changedetection.io. Such a hook also needs a
|
|
client under `server/core/internal/apps/`, a capability flag and the
|
|
gating in step 3.
|
|
|
|
1. **Create the type constant.** The type constant, its validation and
|
|
its human-readable name (used in notification text), and the switch
|
|
that turns a stored hook into a running processor all live in
|
|
`server/core/internal/document/hook/`. Add the constant, then follow
|
|
an existing type through the package. A hook with its own client gets
|
|
it through the input that hands processors their clients, declared
|
|
in the processor package and implemented in this one.
|
|
2. **Write the processor.** `server/core/internal/document/hook/processor/`
|
|
holds one processor per type. Its struct is the hook's settings, and
|
|
it computes a freshness score and a state from them, resets that
|
|
state, and tears down whatever it holds outside the deployment.
|
|
3. **Gate on the integration.** If the hook depends on an integration,
|
|
three places refuse or skip it when the deployment lacks it: the
|
|
manager in `server/core/internal/document/hook/manager/` skips it
|
|
while processing, the assistant's create refuses it, and the
|
|
capabilities endpoint in `server/core/internal/server/` reports
|
|
whether it is configured, so the web app can hide it.
|
|
4. **Add the assistant tools.** The hook tools in
|
|
`server/core/internal/assistant/tools/` take settings as one object
|
|
whose shape the type decides: add the type's schema variant and its
|
|
decoding case.
|
|
5. **Add the type to the web app.** The frontend knows the types, their
|
|
settings and their state through `web/app/utils/api/document.ts`.
|
|
Add a directory with the config menu under
|
|
`web/app/components/editor/hooks/`, then follow an existing type
|
|
through the menu content beside it (the component and props switches
|
|
and the add-menu entry, gated on the capability when the hook depends
|
|
on one) and through `NotificationRow.vue` and `notification.ts` in
|
|
`web/app/components/`.
|
|
Their strings live in `web/i18n/locales/`.
|
|
6. **Extend the tests.** Every place above that switches on the type has
|
|
a table-driven test with one row per type; extend the tables in the
|
|
packages you touched, and the unit test beside each web component.
|
|
Assistant tool descriptions are compared against a golden file;
|
|
regenerate it from `server/core/` with
|
|
`UPDATE_GOLDEN=1 go test -run Test_Info_toEino ./internal/assistant/tools/`
|
|
and review the diff. The e2e suite creates no hooks and needs nothing.
|
|
|
|
## Before opening a pull request
|
|
|
|
Run `make check-lint`. CI runs the same gates per component, plus the unit
|
|
tests and both end-to-end suites.
|