Commit Graph
23 Commits
Author SHA1 Message Date
mr.zeroandClaude Opus 5 19d142ef64 WarpEngine 0.7.0: a képtár és a CI is a hoszté, adapteren keresztül
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>
2026-08-23 00:56:01 +02:00
mr.zeroandClaude Opus 5 d943a09774 README: the registry example named the wrong org
`api/packages/tools/rubygems` was right until the org reshuffle moved the engine
to `engines`, and it has been copy-pasteable-but-broken since. The gemspec's
allowed_push_host and the CI's `gem push --host` both say `engines`; only the
README did not.

While there: pin the example to `~> 0.5` and say why the source is block-scoped.
A second *global* source leaves Bundler unable to say which gem came from where.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 11:20:53 +02:00
mr.zeroandClaude Opus 5 76427bab91 WarpEngine: let the host say who a browser is
The access policy asks who the caller is, and until now only a bearer token
could answer. That is what a desktop client carries — but a person clicking a
download link on the site carries a session instead, and the engine has no idea
what a session is. So a host that gated its catalog found its own signed-in
visitors refused at /api/download, which is a regression the shadowed route used
to hide.

c.subject_resolver is a callable taking the Rack request and returning the
host's subject: `->(request) { request.env["warden"]&.user }` for a Devise app.
Unset — every deployment today — a non-bearer request stays anonymous, exactly
as before. A resolver that raises is logged and treated as anonymous, because a
broken one turning every read into a 500 is worse than an anonymous request.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 10:36:30 +02:00
mr.zeroandClaude Opus 5 6c8b026590 WarpEngine 0.5.0: a catalog that can say a title is not yours
A desktop client reading /api/software had no way to learn that a title costs
money. There was nothing in the response to say so, no way to sign in, and no
way to be told "you do not own this" — so a store with paid titles could only
hand the client a 403 at download time and let it guess why.

The fix belongs here rather than in the client. A client serves more than one
store, so anything it knows about a particular one has to arrive from that
store's own API; a rule compiled into the client is a rule that breaks every
other catalog it reads. Three seams, each following the storage adapter's
shape — documented contract, default that is byte for byte the old behaviour,
one config key to replace it:

- **access policy** — visible_software_scope / access_for / authorize_download.
  Every catalog entry now carries an `access` block (gated, entitled, price,
  purchaseUrl, webUrl) and both /api/download and /file/* ask before serving.
  The vocabulary is deliberately generic: a word from one host's domain would
  make every client that reads it specific to that host.
- **client sign-in** — the device authorization grant (RFC 8628), over the
  host's own user model. The approval page stays the host's, because approving
  needs a session and HTML. Tokens are ApplicationTokens with a `catalog`
  scope, so publishing and reading stay separable.
- **service descriptor** — GET /api/service says what this deployment is and
  whether it has a sign-in at all, which is how a client stops guessing.

With no policy and no subject class configured — every deployment today — the
API is unchanged: /api/auth/* answers 404, /api/service reports auth: null, and
the 187 pre-existing examples pass untouched.

A policy that raises is treated as a refusal, not permission. An artifact
served because the gatekeeper crashed is the one failure mode this must not
have, so a broken policy empties the catalog and denies the download.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 10:26:28 +02:00
mr.zeroandClaude Opus 5 b652615e32 A version header on every WarpEngine response, and a movable pipeline link
Every WarpEngine API response now carries `WarpEngine-Version`, so a client can branch on
the engine's age without a round trip to ask. Set in a before_action rather than after:
`rescue_from` never reaches an after_action, and a client needs the version most when
something came back wrong. The name lives in `WarpEngine::VERSION_HEADER`. The host's own
endpoints — the store registry — do not carry it, because they are not the engine.

A software has one pipeline, and the newest assignment now wins. Two pipelines pointing at
the same software was not an error the database caught; it was a link that silently did
nothing, with the software still showing whichever row came first. Assigning a software
another pipeline holds therefore moves it, the admin says which pipeline it was taken
from, and `Pipeline#software_taken_from` carries that for anything else that cares.
Deliberately a callback and not a unique index: rows here are soft-deleted, and a unique
index counts deleted rows, so a pipeline removed last year would block its software from
ever being linked again.

The engine is 0.4.0. The site's /stores page and its screenshot follow the client's new
name, and the shot is a fresh one showing the greyed-out titles the client now lists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 19:46:29 +02:00
mr.zero 03a51a5b30 warp_engine 0.2.0: pluggable storage adapter and publish notifications
Two seams the hosts needed, both backward compatible.

Storage: artifacts are served through WarpEngine::Storage.adapter instead of
raw filesystem calls. The default :local adapter keeps the previous behaviour
byte for byte, including the path traversal guard. A host can now set
config.storage_adapter to any object answering file?/directory?/locate and
serve builds from an object store - FileService and /api/download both honour
a Location.redirect, so a signing adapter turns them into redirects.
DownloadService#create still returns an absolute path (nil when missing) for
existing callers; #locate is the new entry point that can also return a
redirect. Ingestion (upload, extraction, file manager) stays local for now.

Publish: PublishService emits ActiveSupport::Notifications
("warp_engine.publish") with platform/name/version/software/release, so hosts
can react to a new build without hanging callbacks on the models.
WarpEngine.instruments_publish? lets a host feature-detect and keep its
fallback for older engine versions.
2026-08-10 11:16:17 +02:00
mr.zero 2a94fc785f warp_engine: CiRepository -> Pipeline rename everywhere, ci_ service prefixes dropped, unknown platform allowed 2026-08-06 19:46:06 +02:00
mr.zero 05beb6230b warp_engine admin: CI Dashboard removed, Pipelines page with details sidebar, badge and Created fixes 2026-08-06 19:32:50 +02:00
mr.zero 161f923dc8 fixes 2026-08-06 19:23:04 +02:00
mr.zero d05ed3c515 readme: woodpecker ci management 2026-08-06 16:31:28 +02:00
mr.zeroandClaude Opus 4.6 5f8502a2ed Update README template path to match platform directory structure
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-08-06 10:34:06 +02:00
mr.zero 1c7498ca4a Serve Woodpecker pipeline configs from the engine (/build/config) 2026-08-06 01:14:22 +02:00
mr.zero b64c82aa13 Remove the droparea: artifacts arrive over /build/upload 2026-08-05 22:16:34 +02:00
mr.zero 4e2c45dc45 build endpoints 2026-08-05 20:12:35 +02:00
mr.zero 1e974bdcc9 Rename the update auth switch to application_token_source and drop its ENV default 2026-08-05 18:53:04 +02:00
mr.zero e2c6eacce1 db mode for update secrets 2026-08-05 18:46:49 +02:00
mr.zero 710594efda Add DB-backed application tokens for the update endpoint 2026-08-05 18:30:20 +02:00
mr.zero 60e196a03e Add a runnable example compose stack for WarpEngine
examples/compose boots everything the engine's workflow assumes: mysql, a
minimal Rails host consuming the engine as a path gem, an SSH drop area
sharing the softwares volume with the app, and — behind the ci profile —
gitea plus woodpecker (agent attached to the stack network so pipeline
steps reach droparea/app by service name).

host_app doubles as a reference for a brand-new host: Gemfile, the two
initializers, apipie + engine mounts in routes.rb; on first boot the
entrypoint runs the install generator and db:prepare.

The README gains a detailed bring-up walkthrough: quickstart, publishing
a release by hand over scp + /update (verified end to end from a clean
slate), and the full gitea/woodpecker OAuth wiring.
2026-08-05 06:56:12 +02:00
mr.zero d569c3366a Rewrite WarpEngine README for standalone consumers
The README is what the tools/warp_engine mirror shows: present the engine as
a standalone product installed from git or the gem registry, with the
monorepo workflow reduced to a short Development note.
2026-08-04 20:16:46 +02:00
mr.zeroandClaude Fable 5 6f72801846 Add CI split-mirror pipeline for WarpEngine
- .woodpecker.yaml: on master pushes touching libs/ruby/warp_engine, split the
  subtree and force-push it to the read-only tools/warp_engine mirror; on
  warp_engine-v* tags, build and push the gem to the Forgejo rubygems registry
- engine README documents the monorepo-first workflow and the mirror

Requires a `forge_token` Woodpecker secret (repository:write + package:write).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 19:47:15 +02:00
mr.zeroandClaude Fable 5 9f806ff57a Rewrite READMEs in English for the WarpEngine split
- root README: monorepo layout with libs/, the WarpEngine/host layering,
  rebuild note for the root build context, make api-test and snapshot docs
- warp_engine README translated to English

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 19:36:40 +02:00
mr.zeroandClaude Fable 5 0f5315f705 Phase 6: polish — apipie matcher, install generator, engine migrations, docs
- host apipie matcher now globs the engine controllers, so /api/docs and
  /api/swagger keep documenting the catalog endpoints
- rails g warp_engine:install: initializer template + a clean
  create_warp_engine_tables migration (signed bigint PKs) for new hosts;
  TTG never runs it
- engine append_migrations initializer: future catalog migrations in the
  engine's db/migrate run via the host's rails db:migrate
- README documents the updater contract, config surface, host expectations
  (admin JS picker, apipie matcher) and the soft-delete/resurrection behavior
- make api-test runs both suites plus a production zeitwerk:check

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 19:21:40 +02:00
mr.zeroandClaude Fable 5 0b1b3955ef Phase 1: warp_engine gem skeleton (path gem, mounted engine)
- libs/ruby/warp_engine: gemspec, WarpEngine::Engine (isolate_namespace with
  empty table_name_prefix), WarpEngine.configure surface (container paths,
  update_secret, image_owners), empty engine routes
- host Gemfile: path gem; host routes: mount WarpEngine::Engine => "/" as
  the last entry so host routes always win
- docker: api build context moved to repo root so libs/ is visible at
  bundle-install time; ./libs:/libs runtime mount; root .dockerignore to keep
  data/ and node_modules out of the build context

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 18:44:32 +02:00