395 lines
12 KiB
Markdown
395 lines
12 KiB
Markdown
# Felis Contributor Guide
|
|
|
|
This document is the practical entry point for contributors. It focuses on how to
|
|
run, test, and reason about the project while keeping changes small and aligned
|
|
with the current codebase.
|
|
|
|
## Project Shape
|
|
|
|
Felis is a Kubernetes-native Minecraft server control plane. The repository has
|
|
four main areas:
|
|
|
|
- `cmd/felis/`: the single Go CLI binary. It dispatches subcommands such as
|
|
`api`, `operator`, `migrate`, `reaper`, `restore`, `manifests`, and
|
|
`breakGlass`.
|
|
- `internal/`: backend packages for API handlers, store migrations, Kubernetes
|
|
rendering, operator reconciliation, build, backup, restore, and related domain
|
|
logic.
|
|
- `panel/`: the React/Vite web control panel.
|
|
- `plugins/`: Minecraft-side plugins and mods for Velocity, Paper, Fabric,
|
|
Forge, and NeoForge.
|
|
|
|
The codebase is intentionally split by responsibility. Prefer changing the
|
|
smallest owning module instead of adding broad abstractions or rebuilding nearby
|
|
code.
|
|
|
|
## Interface Copy
|
|
|
|
Interface copy must use a formal documentation style: objective, concise, and
|
|
precise. State the current condition, its cause or impact, and the available
|
|
action. Describe confirmed facts only; avoid conversational phrasing,
|
|
personification, and unsupported duration estimates. Chinese instructions should
|
|
use explicit verbs such as “执行”, “选择”, “查看”, and “配置”, with operation names
|
|
matching the actual UI labels. English and Chinese copy must retain the same
|
|
meaning and degree of formality. Update existing translation entries rather than
|
|
adding duplicate messages or components.
|
|
|
|
## Panel Visual Style
|
|
|
|
Reuse `PageHeader`, the `Card` family, and the shared form and button components.
|
|
Card titles, borders, corner radii, spacing, and save footers follow their shared
|
|
definitions; avoid redefining these styles in individual pages. Use white card
|
|
backgrounds in the light theme, restrained semantic colors, and 16px functional
|
|
icons. Use `CardFooter` for save actions and `Button` for interactive controls.
|
|
Tables, consoles, compact statistics, and status messages may retain spacing and
|
|
colors suited to their content. Preserve the shared page margins and bottom
|
|
spacing.
|
|
|
|
## Local Development
|
|
|
|
You can do most day-to-day development on macOS or Linux without a full cluster.
|
|
The full product needs Postgres and Kubernetes, but unit tests and frontend work
|
|
run locally.
|
|
|
|
Recommended local tools:
|
|
|
|
- Go matching `go.mod`
|
|
- Node.js and npm for `panel/`
|
|
- Optional: JDK/Gradle for plugin work
|
|
- Optional integration environment: a clean Linux VM or server with Docker,
|
|
k3s, and Postgres
|
|
|
|
Check tool versions:
|
|
|
|
```bash
|
|
go version
|
|
node --version
|
|
npm --version
|
|
java -version
|
|
```
|
|
|
|
## Backend Commands
|
|
|
|
Run these from the repository root:
|
|
|
|
```bash
|
|
cd /path/to/Felis
|
|
```
|
|
|
|
Run all Go tests:
|
|
|
|
```bash
|
|
go test ./...
|
|
```
|
|
|
|
Run a focused package:
|
|
|
|
```bash
|
|
go test ./internal/api
|
|
go test ./cmd/felis
|
|
```
|
|
|
|
The hermetic suites run against in-memory fakes; the business stores' SQL is
|
|
verified separately against a real Postgres, on a throwaway database whose name
|
|
must contain `pgint` (the harness drops and recreates its schema and replays the
|
|
embedded migrations):
|
|
|
|
```bash
|
|
FELIS_TEST_PG_URL='postgres://felis:***@127.0.0.1:5432/felis_pgint?sslmode=disable' \
|
|
go test -tags pgint ./internal/pgint/ -v
|
|
```
|
|
|
|
Run it after touching anything under `internal/api/pgrepo.go`, `internal/submit`,
|
|
`internal/build` or `internal/dbbackup` that speaks SQL: the fakes encode the
|
|
contract, and this suite exists to catch the drift between the fakes and the real
|
|
queries. The `felis db backup` and `restore` tests also run `pg_dump`, `pg_restore`
|
|
and `psql`, which must be the server's major version. For a server in a container,
|
|
run them in it, as production does in felis-postgres:
|
|
|
|
```bash
|
|
FELIS_TEST_PG_EXEC='docker exec -i <container>' FELIS_TEST_PG_URL=... go test -tags pgint ./internal/pgint/
|
|
```
|
|
|
|
Build the CLI:
|
|
|
|
```bash
|
|
go build -o /tmp/felis-dev ./cmd/felis
|
|
/tmp/felis-dev help
|
|
```
|
|
|
|
Render Kubernetes manifests without contacting a cluster:
|
|
|
|
```bash
|
|
/tmp/felis-dev manifests \
|
|
--felis-image registry.felis.svc:5000/felis:dev \
|
|
--velocity-cidr 10.0.0.5/32
|
|
```
|
|
|
|
`felis api`, `felis operator`, `felis migrate up`, and `felis reaper` are real
|
|
runtime commands. They need external services such as Postgres and/or a
|
|
Kubernetes config, so they are not the first choice for quick local iteration.
|
|
|
|
## Frontend Commands
|
|
|
|
Run these from the frontend workspace:
|
|
|
|
```bash
|
|
cd /path/to/Felis/panel
|
|
```
|
|
|
|
Install dependencies:
|
|
|
|
```bash
|
|
npm ci
|
|
```
|
|
|
|
Run against a real backend at `http://localhost:8080`:
|
|
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
Run with the local mock API:
|
|
|
|
```bash
|
|
npm run dev:mock
|
|
```
|
|
|
|
The mock dev server prints its accounts, link code, and reset command when it
|
|
starts. Use it for frontend work when you do not have the Go API and cluster
|
|
running.
|
|
|
|
Common frontend checks:
|
|
|
|
```bash
|
|
npm run typecheck
|
|
npm test
|
|
npm run build
|
|
```
|
|
|
|
## Mock API
|
|
|
|
The frontend mock API lives under `panel/dev/` and is loaded only by
|
|
`npm run dev:mock`. It must not leak into production code or business
|
|
components.
|
|
|
|
Run mock commands from the frontend workspace:
|
|
|
|
```bash
|
|
cd /path/to/Felis/panel
|
|
npm run dev:mock
|
|
```
|
|
|
|
Current mock accounts:
|
|
|
|
| Username | Password | Scenario |
|
|
| --- | --- | --- |
|
|
| `owner` | `devpassword` | admin, linked |
|
|
| `user` | `devpassword` | normal user, not linked |
|
|
| `linked` | `devpassword` | normal user, linked |
|
|
| `setup` | `devpassword` | admin, first-login password change |
|
|
|
|
Mock Minecraft link code:
|
|
|
|
```text
|
|
LINK1234
|
|
```
|
|
|
|
Reset mock state:
|
|
|
|
```bash
|
|
curl -X POST http://127.0.0.1:5173/api/v1/__mock/reset
|
|
```
|
|
|
|
Mock rules:
|
|
|
|
- Keep mock-only logic in `panel/dev/`.
|
|
- Do not import mock code from `panel/src/`.
|
|
- Keep response shapes aligned with `panel/src/lib/types.ts` and the Go API
|
|
handlers.
|
|
- Prefer realistic error codes over happy-path-only mocks.
|
|
- Do not present mock data as live production data.
|
|
|
|
## Full Integration Environment
|
|
|
|
Run integration/deploy commands from the repository root on the Linux host:
|
|
|
|
```bash
|
|
cd /path/to/Felis
|
|
```
|
|
|
|
The setup TUI is intended for a clean Linux host, not a typical macOS
|
|
development machine:
|
|
|
|
```bash
|
|
sudo felis setup
|
|
```
|
|
|
|
Useful overrides:
|
|
|
|
```bash
|
|
export FELIS_REPO_URL=<your fork url>
|
|
export FELIS_REF=<your branch> # pins the build; overrides the channel below
|
|
export FELIS_IMAGE=felis:dev
|
|
export FELIS_ROOT_DOMAIN=<node-ip>.nip.io
|
|
```
|
|
|
|
By default the installer builds the newest **published GitHub release**. Building the
|
|
development tip needs an opt-in, and installing from a private fork additionally needs a
|
|
token for the release lookup and the clone:
|
|
|
|
```bash
|
|
export FELIS_VERSION_BOOTSTRAP=dev # build main instead of the newest release
|
|
export FELIS_GITHUB_TOKEN=<token> # private forks only: read access to the fork
|
|
```
|
|
|
|
`dev` is also the escape hatch before the first `vX.Y.Z` tag exists: with no published
|
|
release the default channel has nothing to resolve and stops with that instruction.
|
|
|
|
The two channels differ in more than the version they pick. `release` **downloads** the
|
|
`felis-linux-<arch>` binary that CI published for that tag and builds only a thin image
|
|
around it, so the host needs neither a Go toolchain nor a checkout — the panel rides along
|
|
inside that same binary (`internal/panel` embeds it). `dev` clones and compiles. Either way
|
|
the Minecraft game stack (limbo, lobby, the Velocity plugin) is always built locally.
|
|
|
|
The download is best-effort by design: if the tag's assets are not uploaded yet — the release
|
|
workflow runs vet, tests and a full image build first — or this architecture has no published
|
|
asset, the installer warns and compiles **the same tag** from source. It never silently
|
|
switches you to a different commit. Setting `FELIS_REF` also forces the source path, since
|
|
naming a ref asks for that tree rather than a published artifact.
|
|
|
|
The channel decides the version stamp linked into the binary (`felis version`), which is
|
|
what `felis update` compares against upstream — release builds stamp the tag, dev builds
|
|
stamp `<latest-tag>+g<short-sha>`, and a pinned `FELIS_REF` stamps `v0.0.0+g<short-sha>`
|
|
because skipping channel resolution also skips the tag lookup. An unstamped build reports
|
|
`dev` and update reporting
|
|
is disabled for it, so build through `bootstrap.sh` (or the Dockerfile's `FELIS_VERSION`
|
|
build arg) rather than a bare `go build` when testing that path.
|
|
|
|
The setup flow wraps the host bootstrap, then continues to Owner account setup
|
|
and optional Cloudflare edge setup in the same command. The raw
|
|
`deploy/bootstrap.sh` script remains available for low-level host provisioning
|
|
when debugging the installer itself.
|
|
|
|
Use a VM or disposable Linux server for this. Treat it as an integration and
|
|
acceptance environment, while keeping normal coding and quick tests local.
|
|
|
|
## Plugin Development
|
|
|
|
Plugin docs live in `plugins/README.md`.
|
|
|
|
The plugin modules intentionally use separate Gradle builds:
|
|
|
|
```bash
|
|
# cwd: repository root
|
|
cd /path/to/Felis
|
|
|
|
gradle -p plugins/velocity build
|
|
gradle -p plugins/paper build
|
|
bash plugins/fabric/gradlew -p plugins/fabric build
|
|
bash plugins/forge/gradlew -p plugins/forge build
|
|
bash plugins/neoforge/gradlew -p plugins/neoforge build
|
|
```
|
|
|
|
Notes:
|
|
|
|
- Fabric/Forge/NeoForge use module wrappers.
|
|
- Velocity/Paper use system Gradle.
|
|
- Most modules target Java 17.
|
|
- Paper needs a Java 21 toolchain.
|
|
- First builds may be slow because Minecraft dependencies are downloaded and
|
|
remapped.
|
|
|
|
## Frontend Status
|
|
|
|
The panel is currently an early control-panel implementation, not a complete
|
|
product.
|
|
|
|
Reasonably usable today:
|
|
|
|
- Local-password login
|
|
- First-login password change
|
|
- User/admin route gates
|
|
- Dashboard shell
|
|
- My servers list
|
|
- Wake, stop, claim actions
|
|
- Read-only console log stream
|
|
- Account linking flow
|
|
- Admin create-server form
|
|
- Read-only image whitelist
|
|
- Mock API for local frontend work
|
|
|
|
Still shallow or missing:
|
|
|
|
- Server detail depth
|
|
- Log search/filter/download
|
|
- RCON command UX, if/when the backend path is ready
|
|
- Admin edit/delete/transfer flows
|
|
- Image whitelist mutations
|
|
- Backups and restore UI
|
|
- Fleet-wide ops data
|
|
- Stronger component and browser-level tests
|
|
|
|
When adding frontend features, keep UI state honest. If the backend endpoint does
|
|
not exist yet, use an explicit placeholder instead of fake live data.
|
|
|
|
## i18n Notes
|
|
|
|
i18n is not yet established as a full system. User-facing strings are currently
|
|
mostly inline in TSX and helper functions.
|
|
|
|
Good first targets:
|
|
|
|
- Error messages mapped from stable API error codes
|
|
- Navigation labels
|
|
- Page titles and primary actions
|
|
- Phase/status labels
|
|
- Empty/loading/error states
|
|
|
|
Recommended initial approach:
|
|
|
|
```text
|
|
panel/src/i18n/
|
|
index.ts
|
|
en.ts
|
|
zh-CN.ts
|
|
```
|
|
|
|
Use a small typed dictionary first. Add a larger library such as
|
|
`i18next/react-i18next` only when the project needs runtime language switching,
|
|
pluralization rules, external translation workflows, or more complex
|
|
localization behavior.
|
|
|
|
Do not translate code comments, internal logs, or mock-only terminal messages
|
|
unless there is a clear contributor need.
|
|
|
|
## Contribution Style
|
|
|
|
Follow the existing code. Keep changes narrow and easy to review.
|
|
|
|
Guidelines:
|
|
|
|
- Prefer minimal changes over rewrites.
|
|
- Do not add a new abstraction unless it removes real duplication or names a
|
|
strong local concept.
|
|
- Keep frontend mock code out of business components.
|
|
- Keep backend tests close to the package that owns the behavior.
|
|
- Preserve public API and persistence shapes unless the change explicitly needs a
|
|
contract update.
|
|
- Do not commit generated build output such as `panel/dist/`, `node_modules/`,
|
|
Gradle build directories, or local binaries.
|
|
|
|
AI-assisted work is welcome, but the contributor is responsible for the result.
|
|
Do not submit a PR that is entirely AI-generated and not personally reviewed.
|
|
Low-quality AI dumps, broad rewrites that ignore the current design, unverified
|
|
changes, or code the author cannot explain will not be accepted.
|
|
|
|
Before handing off a change, run the smallest meaningful checks:
|
|
|
|
```bash
|
|
go test ./...
|
|
cd panel && npm run typecheck && npm test && npm run build
|
|
```
|
|
|
|
If you cannot run a relevant check, say so explicitly in the handoff.
|