EN /HU | Belépés

WarpEngine

A WarpEngine mountolható Rails engine, amely bármelyik Rails-appból retró szoftverkatalógust csinál. Egy játékrepó egyetlen soros .woodpecker.yaml jelölőt visz, a motor kiszolgálja a teljes CI-pipeline-t, a buildek HTTP-n töltődnek fel, egy publikálási hívás pedig upserteli a katalógust. Publikus, read-only JSON API és opcionális ActiveAdmin resource-ok is járnak hozzá.

Amit kapsz

  • Katalógus domainSoftware, Release, ReleaseAsset, ExternalLink, PlatformLink, SoftwareImage, Download. Mind soft delete-tel, download tracking-gel.
  • CI-pipeline-szerver — Woodpecker configuration extension. A játékrepókban csak egy platform marker van. A builder image-ek szerver oldalon élnek, így egy image bump egyszerre ér el minden repót.
  • Pluggable CI — 0.7-től minden CI-funkció egyetlen adapteren megy át. A Woodpecker az engine-nel érkezik WarpEngine::CI::Woodpecker néven, és ez a default; a :none kikapcsolja a CI-t, más szerver pedig a hoszt által adott objektum.
  • Build endpoint-okPOST /build/upload és POST /build/publish, shared secret-tel vagy adatbázisbeli application token-ekkel hitelesítve.
  • Publikus JSON API — katalógus, kiemelt cím, platformonkénti build mátrix, download tracking, statikus fájlszerver a webes buildekhez.
  • A hoszt képtára — 0.6-tól az Image modell, a fájljai, az /api/image/:id endpoint-ja és az admin oldala a hosztban él; az engine egy szoftvert egy képhez köt, és adapteren keresztül publikálja az URL-t. Ugyanaz a tábla ezután avatarokat vagy bármi mást is kiszolgálhat, ami a site-nak kell.
  • Pluggable access — a hoszt ad egy policy-t, a katalógus pedig árakat, jogosultságokat és gated download-okat kap, és ezt az API-ban is közli, így egy kliens a fizetős címet fizetősként tudja mutatni ahelyett, hogy letöltéskor bukna el. Default: a nyílt katalógus.
  • Client sign-in — RFC 8628 device authorization grant a hoszt user modelljén, böngésző nélküli klienseknek. Konfig nélkül kikapcsolva.
  • Opcionális admin — ActiveAdmin resource-ok (katalógusszerkesztő, fájlkezelő, download statisztika, application token-ek). ActiveAdmin nélkül az engine headless módban fut.
  • CI-kezelés — API token-nel az admin vezérli a CI-szervert: repo sync, pipeline history kézi trigger-rel, CI secret-ként kiosztott application token-ek.
  • Asset-ellenőrzőlista — 0.8-tól a rake warp_engine:builds:check szoftverenként és kiadásonként kiírja, mely assetek vannak meg, és melyeket tudná még a platform pipeline-ja leszállítani. Lásd: Mely assetek hiányoznak.

Támogatott platformok

Platformonként két lista, és a különbség számít. Az elfogadott assetek az, amit a frissítő befogad egy kiadás publikálásakor (WarpEngine::Platform#expected_kinds). A pipeline ezeket építi az, amit a Woodpecker-sablonok ténylegesen elő is állítanak. Ahol eltérnek, ott a maradék kézi feltöltés.

Platform Felirat Elfogadott assetek A pipeline ezeket építi
godot Godot html, win_x86, win_x64, linux_x64, mac_universal mindet
tic80 TIC-80 cartridge, source, html, docs, win_x64, linux_x64, mac_x64 mindet
bevy Bevy html, win_x64, linux_x64, linux_arm64 mindet
love LÖVE html, win_x64, linux_x64, mac_universal mindet
ebitengine Ebitengine html, win_x86, win_x64, linux_x64, linux_arm64, mac_x64, mac_arm64 html, win_x86, win_x64, linux_x64, linux_arm64
phaser Phaser html mindet
c64 C64 cartridge mindet

Az Ebitengine az egyetlen hiányosság: egy macOS-bináris osxcrosst igényel, amit a linuxos builder image nem visz, így a mac_x64 és a mac_arm64 mindig csak kézzel érkezik.

Mely assetek hiányoznak

Egy kiadás akkor teljes, ha minden olyan asset-típust visz, amit a platformja pipeline-ja elő tud állítani — és semmi nem kényszeríti ki. Elbukik egy buildlépés, egy platform új célt kap, egy címet laptopról publikálnak, és a katalógus hét assetből hárommal szolgál ki egy kiadást. A motor ellenőrzőlistát szállít hozzá:

TXT
$ bin/rails warp_engine:builds:check

WarpEngine release asset coverage — latest release per software
expected: the kinds a platform's updater registers · [ ] the CI builds it · [-] the CI does not, upload it by hand

rabbitroller  ebitengine  v1.1.1  3/7
  [x] html
  [x] win_x64
  [x] linux_x64
  [ ] win_x86
  [ ] linux_arm64
  [-] mac_x64
  [-] mac_arm64

The CI can build these and the release does not have them:
  rabbitroller v1.1.1 (ebitengine): win_x86, linux_arm64

12 softwares, 12 releases, 61/78 assets present, 11 missing from CI-built kinds
4 softwares are missing an asset the CI builds
2 assets are expected but this CI does not build them
Jelölés Jelentés
[x] a kiadásnak megvan ez az assetje
[ ] a platform elvárja, és a pipeline meg is építi — valódi hiány, és ez az a sor, amivel érdemes kezdeni valamit
[-] elvárt, de ez a CI nem tudja megépíteni (lásd a fenti táblázatot) — kézi feltöltés, vagy semmi
[+] a kiadás olyan assetet visz, amit a platform nem vár el

A beállítások környezeti változók, így shellből komponálhatók:

Változó Hatás
NAME=rabbitroller egyetlen szoftver
PLATFORM=ebitengine egyetlen platform
RELEASES=all minden kiadás, nem csak a szoftverenkénti legfrissebb
ONLY=missing csak azok a kiadások, amelyekből hiányzik egy CI által épített asset
STRICT=1 nem nulla kilépési kód, ha bármi hiányzik — ütemezett ellenőrzéshez

A „legfrissebb" a legfrissebb nem dev- verzió — ugyanaz a szabály, amit a katalógus API is használ, amikor megnevezi egy cím legutóbbi kiadását. A számok is objektumok: a WarpEngine::AssetCoverage a #rows, #missing_rows és #totals metódusokra válaszol, a WarpEngine::AssetCoverageChecklist pedig kirendereli őket.

Mindkét hoszton elérhető: a nyilvános oldalon és a Teletype Orbitban.

Végpontok

Nyilvános API

Végpont Mire való
GET /api/service Mi ez a telepítés: verzió, kapuz-e a katalógus, hogyan lehet belépni.
GET /api/software Teljes katalógus kiadásokkal, assetekkel, linkekkel, letöltésszámokkal és egy access blokkal.
GET /api/software/highlighted A kiemelt cím.
GET /api/builds Platformonként elvárt asset-típusok.
GET /api/softwares/:name/builds Kiadásonként a meglévő és a hiányzó buildassetek.
GET /api/image/:id Képek. 0.6 óta a hoszt végpontja — a motor közli a címet, a hoszt szolgálja ki.
GET /api/download?path= Kiszolgál egy artifactot, és naplózza a letöltést.
GET /file/*path Statikus buildkimenet (böngészőben játszható játékok, dokumentáció).
POST /api/auth/device Elindít egy eszközbelépést, visszaadja a kódpárt. 404, ha nincs kliensidentitás beállítva.
POST /api/auth/device/token Lekérdezi egy eszközbelépés tokenjét.
DELETE /api/auth/token Visszavonja a kérésen érkező bearer tokent — kilépés.

Minden olvasó végpont elfogad egy opcionális Authorization: Bearer … fejlécet; egyik sem követeli meg. A token csak azt változtatja meg, amit a hozzáférési szabályzattól kérdezünk. Az anonim hívó támogatott hívó.

Minden válasz visz egy WarpEngine-Version fejlécet, amely az akció lefutása előtt kerül rá — így a hibaválaszokon is rajta van, és egy kliens plusz kérés nélkül tud a motorverzió szerint ágazni.

SH
$ curl -sI https://teletypegames.org/api/software | grep -i warpengine
WarpEngine-Version: 0.5.0

A hosztalkalmazás saját végpontjai (például az áruház-nyilvántartás) nem viszik — azok nem részei a motornak.

Build / CI

Végpont Mire való
POST /build/upload Egy artifact (multipart file, opcionális sha256). X-Update-Secret kell hozzá.
POST /build/publish A feltöltött artifactok regisztrálása kiadásként. X-Update-Secret kell hozzá.
POST /build/config Woodpecker-konfigurációs kiterjesztés; a pipeline-YAML-t szolgálja ki. Aláírás-ellenőrzött (RFC 9421, ed25519).
GET /build/config?platform= A generált YAML nyilvános előnézete.

Hozzáférés és identitás

Két seam került be a 0.5.0-ban, mindkettő a storage adapter mintájára: dokumentált contract, a régi viselkedéssel azonos default, és egyetlen config kulcs a cseréjéhez. Ha egyiket sem állítod be, semmi nem változik.

Ki mit láthat és tölthet le

RUBY
c.access_policy = MyStore::AccessPolicy.new   # :open (alapérték) = a katalógus, ahogy volt

A szabályzat három kérdésre válaszol:

Metódus Kérdés
visible_software_scope mely címeket listázza a GET /api/software
access_for mit tudunk meg egy kliensként az egyes címekről
authorize_download átadható-e ez az artifact

Az authorize_download kérdést az /api/download és a /file/* is felteszi, mielőtt bármit kiszolgálna, így egy hosztnak már nem kell árnyékolnia ezeket az útvonalakat.

Minden katalógusbejegyzés visz egy access blokkot, a nyílt szabályzat alatt is — egy kliensnek soha nem kell találgatnia, hogy a katalógus hallgat-e, vagy a cím egyszerűen nincs kapuzva:

JSON
"access": { "gated": true, "entitled": false,
            "price": { "amountCents": 1490, "currency": "EUR" },
            "purchaseUrl": "https://shop.example/games/slug",
            "webUrl": "https://shop.example/play/slug" }

A mezőnevek szándékosan általánosak, mert egy kliens több katalógust olvas.

Ha a policy exception-t dob, az elutasításnak számít: üres katalógus, megtagadott letöltés, logolva. Egy artifactot soha nem szabad azért kiszolgálni, mert a gatekeeper crashelt.

Client sign-in

Egy desktop kliensnek nincs cookie jar-ja és nincs böngésző session-je, ezért az engine login form helyett a device authorization grant-et (RFC 8628) használja.

RUBY
c.access_token_owner_class  = "User"      # nil (alapérték): sehol nincs belépés
c.identity_verification_url = "/devices"  # a te oldalad, ahol valaki beírja a kódot
c.subject_resolver = ->(request) { request.env["warden"]&.user }   # böngésző felismerése
  1. a kliens POST-ol az /api/auth/device végpontra, és megmutatja a userCode értéket;
  2. az ember megnyitja az ellenőrzőoldalt, és beírja azt a kódot;
  3. a hoszt oldala meghívja a WarpEngine::DeviceGrantService#approve(user_code:, subject:) metódust — a jóváhagyáshoz session és HTML kell, ami nem az engine dolga;
  4. a kliens következő lekérdezése elviszi a tokent. Pontosan egyszer adjuk át.

A token egy WarpEngine::ApplicationToken a catalog scope-pal, így a publish és a kliens credential-ök egyetlen táblában is szétválaszthatók maradnak. A subject_resolver fedi le a másik esetet: egy böngésző session-t visz, nem bearer token-t, és enélkül egy gated áruház a saját bejelentkezett látogatóit utasítaná vissza.

Ha az access_token_owner_class nincs beállítva, az /api/auth/* 404-et ad, az /api/service pedig auth: null-t jelent, így a kliens nem kínál sign-in-t.

A képtár

A kép nem katalógusfogalom — ugyanaz a képtár szolgálja ki a borítókat, a képernyőképeket és az avatarokat —, ezért a 0.6.0-tól a motoré csak a kapcsolat (SoftwareImage: sorrendezett, egy alapértelmezett), miközben a modell, a fájlok, a végpont és az adminoldal a hoszté. A rails g warp_engine:install kiír egy működő készletet (app/models/image.rb, app/controllers/api/images_controller.rb, egy create_images migrációt), a motor pedig adapteren keresztül éri el:

RUBY
c.image_class_name = "Image"          # alapérték
c.image_adapter    = MyLibrary.new    # vagy cseréld le teljesen az adaptert
Metódus Mire használjuk
model_name / model az az osztály, amelyhez a SoftwareImage#image tartozik
url_for(image_id) az imageUrl és az images[].url a katalógus JSON-jában, valamint az adminelőnézetek
select_options a képválasztó a szoftverűrlapon
build_from_upload(upload) „új kép feltöltése" ugyanezen az űrlapon
label_for(record), available?(record) az admin felirata és a „megvan-e a fájl" ellenőrzés

Az alapértelmezett adapter az /api/image/<id> címet publikálja — azt, amit a kliensek mindig is használtak —, ezért a generált útvonal pontosan ezt szolgálja ki. Az images tábla nem költözött: a motor egyszerűen abbahagyta a létrehozását, és a software_images.image_id továbbra is ugyanazokra a sorokra mutat.

A Woodpecker használata a WarpEngine-nel

Egy játékrepónak nincs saját pipeline-ja. A Woodpecker minden pipeline-indításkor POST-olja az egysoros jelölőt a /build/config végpontra, és visszakapja a teljes YAML-t.

  1. A Woodpecker úgy van beállítva, hogy a WarpEngine a konfigurációs kiterjesztése.
  2. A repó .woodpecker.yaml fájljában csak a platform: van (és a name:, ha a szoftvernév eltér a repó nevétől).
  3. Egy push elküldi a jelölőt a POST /build/config végpontra.
  4. A CI-adapter rendereli a lib/warp_engine/ci/woodpecker/platforms/<platform>/pipeline.yaml.erb sablont a beállított builder image-dzsel — a YAML-nyelvjárás a szolgáltatóé, tehát az adapterrel együtt él.
  5. A pipeline lefut: verzió → build → feltöltés → publikálás.

Egy repó úgy lép ki ebből, hogy valódi pipeline-YAML-t commitol: a nem jelölő konfiguráció 204 No Content választ kap, és változatlanul fut.

Egy játékrepó beállítása

YAML
# .woodpecker.yaml
platform: godot
name: mygame     # opcionális, alapból a repó neve
JSON
// metadata.json — a TIC-80-on kívül minden platformon ugyanez
{
  "name": "mygame",
  "version": "1.0.0",
  "title": "My Godot Game",
  "author": "You",
  "desc": "A cool game",
  "license": "MIT"
}
Platform Forráselvárások Mit épít a pipeline
godot exportbeállítások: Web, Windows x86, Windows x64, Linux x64, Mac universal godot --headless --export-release előbeállításonként
bevy szabványos Cargo-projekt; az assets/ a natív binárisok mellé kerül becsomagolva WASM, linux-x64, win-x64 (mingw)
love szabványos LÖVE-projekt love.js webes build, Windows exe, Linux AppImage, univerzális macOS .app
ebitengine szabványos Go-projekt WASM, win x86/x64, linux x64, mac x64/arm64
phaser JS-fájlok a src/ alatt szintaxisellenőrzés, a Phaser-runtime letöltése, webes zip
c64 main.asm ACME-fordítás .prg kazettává
tic80 <name>.inc, amely felsorolja az inc/ alatti darabokat összefűzés, luacheck, minifikálás, LDoc, HTML + kazetta + natív binárisok

A TIC-80-nak nem kell metadata.json — a metaadat a Lua-fejléc:

LUA
-- title: My TIC Game
-- name: mygame
-- author: You
-- desc: A TIC-80 game
-- version: 1.0.0
function TIC() end

A jelölő beállításai

Kulcs Kötelező Leírás
platform igen godot, tic80, bevy, love, ebitengine, phaser, c64
name nem Szoftvernév; alapból a repó neve

Pipeline előnézete

SH
curl "https://your-host/build/config?platform=godot"

Titkok és verziók

Minden generált pipeline egyetlen Woodpecker-secretet vár, az application_token értéket, amelyet X-Update-Secret fejlécként küld a /build/upload és a /build/publish végpontokra.

A verziózás mindenhol ugyanaz: a main/master ágon a verzió a metadata.json fájlból jön (TIC-80 esetén a -- version: sorból); más ágakon előtagot kap, pl. dev-1.0.0-my-feature.

CI-kezelés (Woodpecker API)

Csak ott aktív, ahol az adapternek van url és api_token értéke (WarpEngine.ci.configured?). Addig az adminoldalak elrejtik magukat.

  • RepószinkronAdmin → Pipelines → Sync from Woodpecker (a gomb az adapter nevét viseli). Tükrözi a repólistát Pipeline rekordokba, név alapján párosítja az egyes repókat egy Software rekorddal, és örökli annak platformját. A Woodpeckerből eltűnt repók deaktiválódnak. A linkek kézzel szerkeszthetők maradnak.
  • Pipeline-ok — minden bejegyzés felsorolja a legutóbbi futásait (státusz, ág, üzenet, idő) egy kézi Trigger gombbal. A legfrissebb futás frissíti a listaoldalon gyorsítótárazott státuszt. Egy szoftver adminoldala hivatkozik a pipeline-jaira.
  • Secret-kiosztás — egy token létrehozása kiosztja az application_token secretet a tulajdonos repóira (korlátozás nélküli tokenek esetén minden aktív repóra); a törlés eltávolítja; a Rotate létrehoz egy cserét, mindenhova kiosztja, és egy lépésben visszavonja a régit.

A PAT-nak Woodpecker-példányadminhoz kell tartoznia — a repók listázása csak admin által hívható végpont, minden más 403 User not authorized választ ad. Vedd fel a felhasználót a WOODPECKER_ADMIN listába, majd lépj ki és vissza: az adminjelző a bejelentkezéskor íródik, a szerver újraindítása nem elég.

Buildhitelesítés

Az X-Update-Secret kétféle hitelesítő adat egyikét viszi, amit az application_token_source választ ki. A módok kizárják egymást.

Mód Hitelesítő adat
:env (alapérték) a közös titok az update_secret értékben; ha nincs beállítva, minden kérés elutasításra kerül
:database WarpEngine::ApplicationToken rekordok: tulajdonos (pl. AdminUser), jogosultságok (update a publikáláshoz, upload a feltöltéshez), opcionális lejárat. Az adminban jönnek létre; a nyílt token egyszer jelenik meg, csak a SHA256-lenyomata tárolódik. A last_used_at követi a használatot.

Cutover warning: a :database módra váltás azonnal érvényteleníti a shared secret-et. Előbb hozd létre a token-eket.

Telepítés egy hosztba

RUBY
# Gemfile — gitből:
gem "warp_engine", git: "https://git.teletypegames.org/engines/warp_engine.git"

# ...vagy a Forgejo rubygems-regiszteréből (taggelt kiadások):
source "https://git.teletypegames.org/api/packages/engines/rubygems" do
  gem "warp_engine"
end
SH
rails g warp_engine:install   # initializer, képtár, migrációk
rails db:migrate
RUBY
# config/routes.rb
namespace :api do
  get "image/:id", to: "images#show"   # a képtár a tiéd (lásd lentebb)
end

# a mount maradjon az utolsó, hogy a hoszt saját útvonalai nyerjenek
mount WarpEngine::Engine => "/"

A generátor kiírja a config/initializers/warp_engine.rb fájlt:

RUBY
Rails.application.config.to_prepare do
  WarpEngine.configure do |c|
    c.file_container_path = ENV.fetch("FILE_CONTAINER_PATH", "/softwares")

    # The image library is the host's; "Image" is the adapter's default.
    # c.image_class_name = "Media::Picture"
    # c.image_adapter    = MyImageLibrary.new

    c.update_secret = ENV["UPDATE_SECRET"]

    # c.application_token_source = :database
    # c.application_token_owner_class = "AdminUser"

    # One adapter carries the whole CI side: server, recipes, signing key.
    c.ci_adapter = WarpEngine::CI::Woodpecker::Adapter.new(
      url:            ENV["WOODPECKER_URL"],
      api_token:      ENV["WOODPECKER_API_TOKEN"],
      repo_owner:     ENV["WOODPECKER_REPO_OWNER"],
      public_key_url: "https://ci.example.org/api/signature/public-key",
      update_server:  "https://catalog.example.org",
      platforms: {
        "godot"      => { builder: "registry.example/godot-builder:4.6" },
        "tic80"      => { builder: "registry.example/tic80-builder:1.0",
                          exporter: "registry.example/tic80pro:1.0" },
        "bevy"       => { builder: "registry.example/bevy-builder:1.0" },
        "love"       => { builder: "registry.example/love-builder:1.0" },
        "ebitengine" => { builder: "registry.example/ebitengine-builder:1.0" },
        "phaser"     => { builder: "registry.example/phaser-builder:1.0" },
        "c64"        => { builder: "registry.example/c64-builder:1.0" }
      }
    )

    # c.max_upload_size = 500 * 1024 * 1024
    # c.enforce_software_ownership = true
  end
end
Kulcs Alapérték Leírás
file_container_path ENV["FILE_CONTAINER_PATH"] vagy /softwares Hol tárolódnak a buildartifactok
update_secret ENV["UPDATE_SECRET"] Közös titok a /build/* végpontokhoz
application_token_source :env :env vagy :database, kizárólagosan
application_token_owner_class nil Tulajdonosmodell a :database módhoz
max_upload_size 500 MB Korlát a /build/upload végponthoz
enforce_software_ownership false Adatbázistoken-tulajdonos szerinti elszigetelés
ci_adapter :woodpecker Melyik CI-szerver építi a játékokat; a :none kikapcsolja a CI-t, vagy egy hosztobjektum
image_class_name "Image" A képtár mögötti hosztmodell
image_adapter nil Lecseréli az alapértelmezett hosztmodell-adaptert
access_policy :open Hozzáférési szabályzat objektum
access_token_owner_class nil Felhasználómodell az eszközbelépéshez; nil = nincs belépés

Adminintegráció

Az ActiveAdmin-példány a hoszté (hitelesítés, téma, útvonalak); a WarpEngine hozzáfűzi az erőforrásait az ActiveAdmin.application.load_paths listához. Egy új hosztnak két dolog kell:

  • a fájlválasztó JS a kiadási assetek útvonalmezőihez (iframe → /admin/files?picker=1&field=<dom_id>) az active_admin.js fájljában;
  • apipie-dokumentáció esetén a "#{WarpEngine::Engine.root}/app/controllers/**/*.rb" bejegyzés az api_controllers_matcher beállításban.

Viselkedési megjegyzések

  • Minden modell soft delete-tel dolgozik (default_scope { where(deleted_at: nil) }).
  • A JSON shape stabil, és bug-compatible a korábbi Go backenddel: Go-féle nulla timestamp-ek, camelCase kulcsok, legacy flat path field-ek.
  • Model extension point-ok: ActiveSupport.on_load(:warp_engine_<model>).

Tesztelés

SH
bundle install
bundle exec rake app:db:prepare RAILS_ENV=test
bundle exec rspec