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.
The nav has too many items for the old spacing: smaller type, tighter
gaps, icons only on xl+ screens, and the hamburger now takes over below
lg (the desktop row did not fit between md and lg anymore).
The Hungarian engine strings drop the hyphenated loanword forms:
Engine-ek -> Motorok, Saját Engine-jeink -> Saját motorjaink.
The engines listing is a handful of curated pages, not a stream — no feed
needed. Removes the route, controller action, RssService#engines_feed and
the footer link. The WikiService#pages alias stays (blog/howtos feeds use
it).
The engine pages' repo metadata (new in the wiki pages API) drives the
Explore button and card title links, with the wiki page as fallback. The
never-deployed /engines/:slug detail page and its store/api plumbing are
gone, and the engines RSS feed links to the repos too.
Frontend: /engines index + /engines/:slug detail routes, nav menu item and
en/hu translations. Engine pages are few, so the index uses an emphasized
poster-style design (dark slate, emerald accents, numbered full-width cards
with content preview) instead of the blog/howtos layouts. Slugs are the last
wiki path segment, since engine pages live scattered in the wiki tree.
API: /api/rss/engines feed linking to the site's engine pages, and a
WikiService#pages alias for #index — RssService called the alias-less name,
so the blog and howtos feeds were raising NoMethodError.
release.UpdatedAt does not exist on the Release interface (or in the API
response) — the release dates rendered as invalid values and vue-tsc failed
the build.
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.
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.
A trailing newline pasted into the Woodpecker secret broke the push URL
("credential url cannot be parsed"); strip all whitespace from the token in
both the mirror and the gem-publish steps.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Path-filtered push events hide the workflow for unrelated pushes and manual
restarts have no changed-files list; add event: manual so the mirror can be
triggered from the Woodpecker UI.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The credentials printf line contains ': ' which YAML reads as a mapping;
use a literal block scalar for that command.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- .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>
- 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>
- 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>
- the 7 catalog admin files (softwares, releases, external_links,
platform_links, images, files page, downloads) move to the engine's
app/admin; the host ActiveAdmin instance loads them via
ActiveAdmin.application.load_paths (single admin, URLs unchanged)
- engine initializers: Zeitwerk ignore for app/admin (production eager load)
and load_paths + watchable_dirs registration, guarded by defined?(ActiveAdmin)
so the admin-less dummy app boots
- images admin reads image owners from WarpEngine.config.image_owners; the
host registers the Member owner in config/initializers/warp_engine.rb;
the interim ImageUsage registry is gone
- fix: SoftwareImage.distinct.pluck clashed with its order(:position)
default scope on MySQL (unscope(:order)) — introduced in phase 0, caught
by the first authenticated /admin/images smoke test
- files page: download links use the public /file/ URL instead of the raw
container path; the picker's stored path comes from config
Verified: both suites green, zeitwerk:check (production) clean, JSON
baselines intact, authenticated admin smoke test on every page incl.
the 3-level nested software form and Files picker mode.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- update, files and the 6 /api catalog controllers now live in the engine on
a new WarpEngine::ApiController base (same rescue/mime behavior as host)
- engine routes serve /update, /file/*path, /api/software*, /api/builds*,
/api/image/:id, /api/download at unchanged public paths via the root mount;
host routes keep only TTG endpoints (events, members, wiki, rss, swagger)
- /update secret comes from WarpEngine.config.update_secret and an
unconfigured secret now rejects every request (previously an empty
UPDATE_SECRET env accepted empty secrets)
- apipie-rails is an engine dependency (DSL in engine controllers); dummy app
configures apipie with validation off, mirroring the host
- engine request specs: catalog controller specs moved from host plus new
/update auth contract spec
Verified: engine suite 53 green, host suite 6 green, /api/software and
/api/builds byte-identical to baselines, /update 401/400 behavior intact,
admin and TTG endpoints OK.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- all catalog services (update/software/highlighted/builds/file/file-manager/
download/image + SoftwareResponseBuilder), the SoftwareUpdater platform
services and their concerns, Blueprinter serializers (incl. TimestampFields)
and the 4 DTOs now live in the engine under WarpEngine::
- constantize dispatch strings use absolute names
(WarpEngine::SoftwareUpdater::<Platform>Service)
- container paths read from WarpEngine.config everywhere (FileService,
DownloadService, FileManagerService, ArchiveExtraction, ReleaseSerializer
path rewriting); FileManagerService base path is now lazy
- engine requires blueprinter itself; gemspec declares blueprinter + rubyzip
- engine test suite: spec/dummy app (mysql warp_engine_test, catalog-only
schema), rails_helper with engine-local factories; catalog model/service
specs and factories moved from the host
- host suite keeps TTG specs and loads catalog factories from the engine
Verified: engine suite 40 green, host suite 14 green, /api/software and
/api/builds byte-identical to baselines, admin OK.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>