Files
Module-uo/CONTRIBUTING.md
wtclaude 62c8ee68b4
All checks were successful
PR Checks / client-build (pull_request) Successful in 14s
PR Checks / frozen-manifest (pull_request) Successful in 34s
PR Checks / server-tests (pull_request) Successful in 8m50s
chore(ci): scan this repo with SonarQube (phase 4, slice 0)
Until now this was the one part of the platform that had never been scanned.
The 75 files here arrived in the Phase 3 extraction and left their Sonar
history behind in core's project, so a whole module's worth of shipped code
has no dashboard at all.

Adds sonar-project.properties (project key Module-uo) and a sonarqube.yml
mirroring website's: push to main, never a PR gate, nothing waiting on the
quality gate.

Two things differ from core's config, both because this repo is shaped
differently:

  - There is no src/ to point sonar.sources at — the server half keeps
    boot.js/core.js/index.js at server/ root beside its subdirectories — so
    the whole tree is included and the non-source parts are excluded. That
    direction is deliberate: a new top-level server directory is scanned by
    default rather than silently unscanned.
  - server/scripts and client/scripts are IN. checkImports.js and
    checkExternals.js are the enforcement of MODULE_API.md 5.1 and 3.6, they
    carry their own test suites, and both have already shipped defects a
    reviewer missed. Build code that decides whether a release is allowed out
    is not throwaway code.

The workflow builds the client chunk before running either suite, for the
reason pr-checks.yml already calls load-bearing: build.test.js and
registration.test.js read dist/entry.js and SKIP without it, so the other
order reports coverage for a suite that quietly asked less than it looks like
it did.

Both suites run from the repo root rather than with --prefix, so the LCOV SF:
paths come out repo-root-relative and resolve against sonar.sources. That is
why the server suite's --require is spelled out here instead of reusing
`npm test --prefix server`, whose path is relative to server/.

Verified locally: 385 server test cases across 57 covered files and 40 client
cases, both LCOV and Generic Test Execution XML well-formed with
repo-root-relative paths.

Needs one-time setup in the Gitea UI before it can run — secret SONAR_TOKEN
and variable SONAR_HOST_URL, same as the other repos.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-12 02:36:11 -05:00

146 lines
6.8 KiB
Markdown

# Contributing to Runic Gateway — module-uo
Thanks for your interest in contributing! This repo is **module-uo**: the Ultima
Online module for the Runic Gateway website. Everything the site knows about a
*shard* — shard status, the spawn atlas, the marketplace, governors, clilocs, the
town crier and the uo-link integration — lives here rather than in core, so that
core stays game-agnostic and a second game can be a second module.
It is **one repo, one bundle**: the server half (routers, controllers, models, a
schema fragment) and the client half (SPA screens, nav registrations, a feature
provider) version together and release as a single artifact.
By participating you agree to abide by our
[Code of Conduct](CODE_OF_CONDUCT.md).
## Status: planning
**No module code exists yet.** The design of record is
[`website/MODULE_SYSTEM.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/website/MODULE_SYSTEM.md)
in the docs repo — read it before opening a PR here. It defines the module API
surface, the packaging layout, the state machine, the install/uninstall/purge
model, and the phases. **Phase 1 has to settle the contract
(`docs/website/MODULE_API.md`) before module code can be written against it**, and
Phase 3 is what fills this repo by extracting the UO half of `website/`.
If you want to change *what a module can do* — the API surface, the loader, the
delivery model — propose it against the plan in the docs repo first. If you want
to change *what module-uo does with that API*, a PR here is the right place.
## Ways to contribute
- **Report a bug** or **request a feature** through the
[issue tracker](https://gitea.whitlocktech.com/RunicGateway/Module-uo/issues)
(issue templates are provided).
- **Improve the code or docs** by opening a pull request (see below).
- **Never** report a security vulnerability in a public issue — see
[SECURITY.md](SECURITY.md).
## Development setup
**Prerequisites:** Node 20+, and a checkout of
[`RunicGateway/website`](https://gitea.whitlocktech.com/RunicGateway/website) to
run the module against — a module is not runnable on its own. Core loads modules
synchronously from the filesystem at boot, so a working tree of this repo placed
in core's modules directory is the development loop.
Once the code exists, both halves are plain npm packages:
```bash
npm ci --prefix server && npm test --prefix server
npm ci --prefix client && npm run build --prefix client
```
The client half builds with Vite **in library mode**, declaring `react`,
`react-dom` and `react-router-dom` as externals. Do not add React to the module's
bundle: there is exactly one React instance in the page and core owns it, exposed
on a `window.__rg` global that the module's externals resolve to.
### Two rules that are enforced in CI, not by review
- **Zero internal-file imports into core.** A module reaches core only through the
documented API surface — the `ctx` it is handed and the `register*` functions it
calls. If something you need is not on that surface, the surface gets extended
(a docs PR and a core PR) rather than reached around. Any `require`/`import`
that resolves outside this repo is a build failure.
- **The module owns one path segment.** Server routes keep the API paths core
already publishes; SPA pages live under `/uo/*`, `/admin/uo/*` and
`/player/uo/*`. A route that escapes the module's segment is a bug.
### Schema fragments, not migrations
There is no migration runner anywhere in this project, and modules do not get one.
`server/db/schema.sql` is an **idempotent** fragment — `CREATE TABLE IF NOT
EXISTS`, `ALTER TABLE … ADD COLUMN IF NOT EXISTS` — replayed by core's
`ensureSchema()` on every boot, exactly like core's own schema. Anything
destructive belongs in `server/db/purge.sql`, which only ever runs on an explicit
operator purge.
### The operator never builds anything
Installing a module is an admin-panel action or a mounted directory, never a build
step: production runs a prebuilt, pull-only image. `client/dist/` is produced by
this repo's CI and published in the release artifact. A change that only works
when someone builds on the server is not shippable.
## Branch & PR workflow
1. Branch from `main` with a descriptive name
(`feature/…`, `fix/…`, `docs/…`, `chore/…`).
2. Keep changes focused; small PRs are easier to review.
3. Push and open a pull request against `main`. Fill out the PR template,
including the **AI-assisted contributions** disclosure.
4. A maintainer will review; address feedback with follow-up commits.
`Module-uo` develops on its own `main` from its first commit — it has no `edge`
branch and no cutover, unlike `website`, whose module work accumulates on `edge`
and reaches `main` once.
### Static analysis runs after the merge, not on the PR
`.gitea/workflows/sonarqube.yml` scans `main` on push and reports to the
self-hosted SonarQube instance under the project key **`Module-uo`**. It is
deliberately non-blocking: it never gates a pull request, and a failing quality
gate does not fail the job. Check the dashboard when you want to; the things
that must not reach `main` are gated by `pr-checks.yml` instead.
It runs both suites from the repo root to produce coverage, and builds the
client chunk first — two of the client tests read `dist/entry.js` and skip
without it, which would leave this job reporting on a suite that quietly asked
less than it appears to.
### Commit messages
We use [Conventional Commits](https://www.conventionalcommits.org/) —
`type(scope): summary`.
## Keep the docs in sync
`RunicGateway/docs` is canonical, and this module's documentation lives there under
`modules/uo/` rather than in this repo. A behavior change here — a new route, a
changed table, a new nav entry, a different capability — is not complete until the
matching document reflects it. Changes to the module *system* itself belong in
`website/MODULE_SYSTEM.md` and `website/MODULE_API.md`.
## AI-assisted contributions (disclosure required)
This project is developed openly with AI assistance, and we ask the same
transparency of everyone. **If you used an AI tool** (Claude, Copilot, ChatGPT,
Cursor, etc.) to help produce a contribution, you must disclose it:
- Tick the AI-usage box in the pull-request template and name the tool(s).
- Mark AI-authored commits with a trailer, e.g.
`Co-Authored-By: Claude <noreply@anthropic.com>` or `Assisted-By: <tool>`.
- You remain responsible for every line you submit: review it, understand it,
and make sure it is correct and that you have the right to contribute it.
Disclosed AI assistance is welcome. Undisclosed AI-generated contributions are
not, and may be closed.
## License
Runic Gateway is licensed under the **GNU General Public License v3.0 or later**
(see [LICENSE.md](LICENSE.md)). By submitting a contribution you agree that it is
licensed under the same terms (inbound = outbound) and that you have the right to
contribute it.