Két dolog volt beépítve az engine-be, ami nem az övé. A **képtár** eddig `WarpEngine::Image` volt, pedig a modell teljesen általános: a teletypegames-ben a tagok arcképét is ez hordozza, nem csak a katalógus borítóit. Az `Image` modell, a feltöltött fájlok, az `/api/image/:id` végpont és az admin oldal ezért átkerült a hosztba, az engine pedig adapteren szól hozzá (`WarpEngine::Images`): `url_for` adja a katalógus JSON `imageUrl`-jét, `select_options` a software-form képválasztóját, `build_from_upload` a "tölts fel új képet" ágat. Az alapértelmezés az `Image` osztály, tehát a default útvonal bitre a régi. A `SoftwareImage` (a katalógus-kapcsolat) maradt az engine-ben, és **az `images` tábla nem mozdult**: az engine csak abbahagyta a létrehozását, a generátor írja meg hoszt-kódként. A **CI** eddig végig Woodpecker volt: kliens, aláírás-ellenőrzés, pipeline-receptek, repo-szinkron, secret-kiosztás. Mindez egy adapter mögé került (`WarpEngine.ci`), a Woodpecker-implementáció pedig az engine-ben maradt `WarpEngine::CI::Woodpecker` néven — kliens, adapter, httpsig-ellenőrző és a platformonkénti pipeline-receptek, mert a YAML-dialektus a szolgáltatóé. Az engine saját kódja már nem nevez szolgáltatót: `CI::Repo` és `CI::Run` értékeket kap, `CI::ConnectionError`/`ApiError`/`NotConfigured` hibákat dob, a `Pipeline` pedig `remote_repo_id`-t ad a történelmi `woodpecker_repo_id` kolumna fölött (a tábla itt sem mozdult). `c.ci_adapter = :none` azt jelenti, hogy ez a hoszt nem buildel: az `/api/ci/*` 503, a `/build/config` elutasít, az admin akciók elbújnak. Mindkét seam a hoszt initializerében van kimondva, nem alapértelmezésre hagyva — a hoszt megnevezi, mi a képtára és mi a CI-ja. Törés a 0.6-hoz képest: `image_container_path`, `image_owners`, `ci_platforms`, `ci_update_server`, `ci_extension_public_key(_url)`, `woodpecker_url`, `woodpecker_api_token`, `woodpecker_repo_owner` és a `WarpEngine.woodpecker_configured?` megszűnt; a helyük `c.image_class_name` / `c.image_adapter` és `c.ci_adapter`. Az `/api/ci/*` `latest_run`/`trigger` válasza a normalizált `CI::Run` alakot adja (number, status, branch, message, createdAt, url), a `pipelines` lista pedig `repo_id`-t is közöl a megtartott `woodpecker_repo_id` mellett. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
123 lines
4.5 KiB
Markdown
123 lines
4.5 KiB
Markdown
# Teletype Games
|
|
|
|
Monorepo for the Teletype Games portal: a Vue 3 frontend, a Rails 8 API host app,
|
|
and the reusable **WarpEngine** software-catalog engine.
|
|
|
|
## Project structure
|
|
|
|
```
|
|
apps/
|
|
frontend/ # Vue 3 + Vite + TypeScript + Tailwind SPA
|
|
api/ # Rails 8 host app: TTG-specific API + ActiveAdmin shell
|
|
libs/
|
|
ruby/warp_engine/ # WarpEngine: mountable Rails engine (catalog, updater, admin resources)
|
|
```
|
|
|
|
The API is split in two layers:
|
|
|
|
- **WarpEngine** (`libs/ruby/warp_engine`) owns the software catalog: models
|
|
(softwares, releases, release assets, software-image links, platform links,
|
|
download stats), the CI-callable `/build/*` publishing endpoints, the public
|
|
read-only JSON API (`/api/software*`, `/api/builds*`, `/api/download`,
|
|
`/file/*`) and the catalog ActiveAdmin resources.
|
|
See its [README](libs/ruby/warp_engine/README.md).
|
|
- **The host app** (`apps/api`) owns everything TTG-specific: members, events,
|
|
the image library (`Image`, `/api/image/:id`, the admin page — the engine only
|
|
links to it through an adapter), the store registry, wiki proxy, RSS feeds,
|
|
Devise/ActiveAdmin authentication, theming and assets. It consumes WarpEngine
|
|
as a path gem and mounts it at `/`.
|
|
|
|
## The store registry
|
|
|
|
`GET /api/stores` lists the stores a client can install from. The desktop
|
|
graphical client reads it on first run, which is why it is **public**: a client has
|
|
nobody to log in as.
|
|
|
|
```json
|
|
[
|
|
{ "name": "Teletype Games", "catalogUrl": "https://teletypegames.org", "storeRepositoryUrl": null }
|
|
]
|
|
```
|
|
|
|
A `Store` row has two required fields — `name` and `catalog_url` — plus an
|
|
**optional** `store_repository_url`, maintained from **ActiveAdmin ▸ 🛒 Stores**;
|
|
`db/seeds.rb` creates our own.
|
|
|
|
**A store does not need a repository.** The store engine's own defaults already
|
|
cover the host-to-asset mapping, the install modes, the platforms and the
|
|
behaviour; what they cannot know is identity — a slug, a name and a catalog — and
|
|
that is what this row carries. With no repository the client derives the slug from
|
|
the catalog host, writes a small config and installs. Given one, it reads that
|
|
repository's `config.json` and points it at `catalog_url`, and that file remains the
|
|
authority on how the store behaves; a repository without a config file is treated
|
|
as no repository at all.
|
|
|
|
Every WarpEngine API response also carries a `WarpEngine-Version` header — the registry
|
|
above is the host's own endpoint and does not, because it is not part of the engine.
|
|
|
|
This lives in the host app **on purpose, not in WarpEngine**. The engine serves
|
|
one catalog and has no business knowing which stores exist for it; who ships a
|
|
store for a catalog is a property of the site.
|
|
|
|
| | |
|
|
|---|---|
|
|
| Model | `apps/api/app/models/store.rb` |
|
|
| Endpoint | `apps/api/app/controllers/api/stores_controller.rb` |
|
|
| Admin | `apps/api/app/admin/stores.rb` |
|
|
| Client | [`warp-engine-client`](https://git.teletypegames.org/stores/warp-engine-client) |
|
|
|
|
## Development environment
|
|
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
| Service | URL |
|
|
|---|---|
|
|
| Frontend | `http://${WEBAPP_DOMAIN}` |
|
|
| API | `http://${WEBAPP_DOMAIN}/api` |
|
|
| Admin | `http://${WEBAPP_DOMAIN}/admin` |
|
|
| API docs | `http://${WEBAPP_DOMAIN}/api/swagger` |
|
|
|
|
The api image is built from the repo root (so the `libs/` path gems are visible
|
|
during `bundle install`) and mounts `./libs` at runtime. After changing the
|
|
compose file or the Gemfile, rebuild with `docker compose up -d --build api`.
|
|
|
|
## Testing
|
|
|
|
```bash
|
|
make api-test
|
|
```
|
|
|
|
Runs the host suite (`apps/api`, `softwares_test` DB), the WarpEngine suite
|
|
(dummy app, `warp_engine_test` DB) and a production-mode `zeitwerk:check`.
|
|
Individually:
|
|
|
|
```bash
|
|
docker exec -e RAILS_ENV=test api bundle exec rspec # host
|
|
docker exec -w /libs/ruby/warp_engine -e RAILS_ENV=test api bundle exec rspec # engine
|
|
```
|
|
|
|
JSON contract baselines for `/api/software` and `/api/builds` live in
|
|
`apps/api/spec/snapshots/` — diff against them after refactors.
|
|
|
|
## Linting
|
|
|
|
### Backend (RuboCop)
|
|
|
|
```bash
|
|
docker compose run --rm --no-deps api bundle exec rubocop # check
|
|
docker compose run --rm --no-deps api bundle exec rubocop -A # autofix
|
|
```
|
|
|
|
Config: `apps/api/.rubocop.yml` (rubocop-rails-omakase preset)
|
|
|
|
### Frontend (ESLint)
|
|
|
|
```bash
|
|
docker compose run --rm --no-deps frontend npm run lint # check
|
|
docker compose run --rm --no-deps frontend npm run lint:fix # autofix
|
|
```
|
|
|
|
Config: `apps/frontend/eslint.config.js` (ESLint 9 flat config, Vue + TypeScript)
|