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>
This commit is contained in:
@@ -57,7 +57,7 @@ ActiveAdmin.register WarpEngine::ApplicationToken, as: "Application Token" do
|
||||
end
|
||||
|
||||
action_item :rotate, only: :show do
|
||||
if WarpEngine.woodpecker_configured?
|
||||
if WarpEngine.ci.configured?
|
||||
link_to "Rotate Token", rotate_admin_application_token_path(resource),
|
||||
method: :post, data: { confirm: "This will revoke the current token, create a new one, and push it to Woodpecker. Continue?" }
|
||||
end
|
||||
@@ -101,7 +101,7 @@ ActiveAdmin.register WarpEngine::ApplicationToken, as: "Application Token" do
|
||||
success.html do
|
||||
session[:warp_engine_plain_token] = resource.plain_token
|
||||
|
||||
if WarpEngine.woodpecker_configured?
|
||||
if WarpEngine.ci.configured?
|
||||
service = WarpEngine::SecretSyncService.new
|
||||
pipelines = service.pipelines_for_token(resource)
|
||||
if pipelines.any?
|
||||
@@ -124,7 +124,7 @@ ActiveAdmin.register WarpEngine::ApplicationToken, as: "Application Token" do
|
||||
end
|
||||
|
||||
def destroy
|
||||
if WarpEngine.woodpecker_configured?
|
||||
if WarpEngine.ci.configured?
|
||||
WarpEngine::SecretSyncService.new.deprovision(resource)
|
||||
end
|
||||
resource.revoke!
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
ActiveAdmin.register WarpEngine::Image, as: "Image" do
|
||||
permit_params :file_upload
|
||||
|
||||
menu parent: "🌀 WarpEngine", priority: 5, label: "🖼️ Images"
|
||||
|
||||
used_ids = -> {
|
||||
WarpEngine::SoftwareImage.unscope(:order).distinct.pluck(:image_id) +
|
||||
WarpEngine.config.image_owners.flat_map { |o| o[:image_ids].call }
|
||||
}
|
||||
|
||||
scope :all, default: true
|
||||
scope("In use") { |scope| scope.where(id: used_ids.call) }
|
||||
scope("Orphan") { |scope| scope.where.not(id: used_ids.call) }
|
||||
|
||||
batch_action :delete_orphans, confirm: "Delete all selected orphan images and their files?" do |ids|
|
||||
orphan_ids = ids.map(&:to_i) - used_ids.call
|
||||
WarpEngine::Image.where(id: orphan_ids).find_each do |img|
|
||||
File.delete(img.file_path) if File.exist?(img.file_path)
|
||||
img.destroy
|
||||
end
|
||||
redirect_to admin_images_path, notice: "Deleted #{orphan_ids.size} orphan image(s)."
|
||||
end
|
||||
|
||||
index do
|
||||
selectable_column
|
||||
id_column
|
||||
column :original_filename
|
||||
column :content_type
|
||||
column(:preview) do |img|
|
||||
if File.exist?(img.file_path)
|
||||
image_tag("/api/image/#{img.id}", style: "max-height:60px;max-width:120px;object-fit:contain;")
|
||||
end
|
||||
end
|
||||
column(:usage) do |img|
|
||||
uses = []
|
||||
uses << "#{img.software_images.size} software(s)" if img.software_images.any?
|
||||
uses.concat(WarpEngine.config.image_owners.filter_map { |o| o[:usage_label].call(img) })
|
||||
uses.any? ? uses.join(", ") : status_tag("orphan", class: "warning")
|
||||
end
|
||||
column :created_at
|
||||
actions
|
||||
end
|
||||
|
||||
filter :original_filename
|
||||
filter :content_type
|
||||
|
||||
show do
|
||||
attributes_table do
|
||||
row :id
|
||||
row :original_filename
|
||||
row :filename
|
||||
row :content_type
|
||||
row(:preview) do |img|
|
||||
if File.exist?(img.file_path)
|
||||
image_tag("/api/image/#{img.id}", style: "max-height:300px;max-width:100%;object-fit:contain;")
|
||||
else
|
||||
"File not found on disk"
|
||||
end
|
||||
end
|
||||
row :created_at
|
||||
row :updated_at
|
||||
end
|
||||
end
|
||||
|
||||
form html: { multipart: true } do |f|
|
||||
f.inputs do
|
||||
f.input :file_upload, as: :file, label: "Image File"
|
||||
end
|
||||
f.actions
|
||||
end
|
||||
|
||||
controller do
|
||||
def scoped_collection
|
||||
super.includes(:software_images)
|
||||
end
|
||||
end
|
||||
end
|
||||
+14
-15
@@ -33,7 +33,7 @@ ActiveAdmin.register WarpEngine::Pipeline, as: "Pipeline" do
|
||||
}
|
||||
column :last_pipeline_at
|
||||
actions defaults: true do |pipeline|
|
||||
if pipeline.active && WarpEngine.woodpecker_configured?
|
||||
if pipeline.active && WarpEngine.ci.configured?
|
||||
item "Trigger", trigger_admin_pipeline_path(pipeline), method: :post, class: "member_link"
|
||||
end
|
||||
end
|
||||
@@ -59,7 +59,7 @@ ActiveAdmin.register WarpEngine::Pipeline, as: "Pipeline" do
|
||||
sidebar "Details", only: :show do
|
||||
attributes_table_for resource do
|
||||
row :id
|
||||
row :woodpecker_repo_id
|
||||
row("Repo id") { |r| r.remote_repo_id }
|
||||
row :repo_owner
|
||||
row :repo_name
|
||||
row :platform
|
||||
@@ -74,22 +74,21 @@ ActiveAdmin.register WarpEngine::Pipeline, as: "Pipeline" do
|
||||
|
||||
show do
|
||||
panel "Pipelines" do
|
||||
if !WarpEngine.woodpecker_configured?
|
||||
para "Woodpecker is not configured. Set woodpecker_url and woodpecker_api_token in the WarpEngine initializer.",
|
||||
if !WarpEngine.ci.configured?
|
||||
para "No CI provider is configured. Set c.ci_adapter in the WarpEngine initializer.",
|
||||
style: "color:#999;"
|
||||
elsif !resource.active
|
||||
para "This repository is inactive.", style: "color:#999;"
|
||||
else
|
||||
begin
|
||||
pipelines = WarpEngine::PipelineService.new.list_pipelines(resource, page: 1)
|
||||
if pipelines.is_a?(Array) && pipelines.any?
|
||||
table_for pipelines.first(10) do
|
||||
column("Number") { |p| p["number"] }
|
||||
column("Status") { |p| status_tag p["status"], class: p["status"] == "success" ? "yes" : "no" }
|
||||
column("Branch") { |p| p["branch"] }
|
||||
column("Message") { |p| p["message"]&.truncate(60) }
|
||||
|
||||
column("Created") { |p| p["created"] ? Time.zone.at(p["created"]).strftime("%Y-%m-%d %H:%M") : "-" }
|
||||
runs = WarpEngine::PipelineService.new.runs(resource, page: 1)
|
||||
if runs.any?
|
||||
table_for runs.first(10) do
|
||||
column("Number") { |r| r.number }
|
||||
column("Status") { |r| status_tag r.status, class: r.success? ? "yes" : "no" }
|
||||
column("Branch") { |r| r.branch }
|
||||
column("Message") { |r| r.message&.truncate(60) }
|
||||
column("Created") { |r| r.created_at ? r.created_at.strftime("%Y-%m-%d %H:%M") : "-" }
|
||||
end
|
||||
else
|
||||
para "No pipelines found.", style: "color:#999;"
|
||||
@@ -118,8 +117,8 @@ ActiveAdmin.register WarpEngine::Pipeline, as: "Pipeline" do
|
||||
end
|
||||
|
||||
action_item :sync_repos, only: :index do
|
||||
if WarpEngine.woodpecker_configured?
|
||||
link_to "Sync from Woodpecker", sync_admin_pipelines_path, method: :post
|
||||
if WarpEngine.ci.configured?
|
||||
link_to "Sync from #{WarpEngine.ci.name}", sync_admin_pipelines_path, method: :post
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ ActiveAdmin.register WarpEngine::Software, as: "Software" do
|
||||
column(:releases) { |sw| sw.releases.size }
|
||||
column(:image) do |sw|
|
||||
si = sw.software_images.detect(&:is_default?) || sw.software_images.first
|
||||
if si&.image && File.exist?(si.image.file_path)
|
||||
image_tag "/api/image/#{si.image_id}", style: "max-height:40px;max-width:80px;object-fit:contain;"
|
||||
if si&.image && WarpEngine::Images.available?(si.image)
|
||||
image_tag WarpEngine::Images.url_for(si.image_id), style: "max-height:40px;max-width:80px;object-fit:contain;"
|
||||
end
|
||||
end
|
||||
column :created_at
|
||||
@@ -114,10 +114,10 @@ ActiveAdmin.register WarpEngine::Software, as: "Software" do
|
||||
f.inputs "Images" do
|
||||
f.has_many :software_images, allow_destroy: true, new_record: true do |si|
|
||||
img_hint = if si.object.image_id.present?
|
||||
si.template.image_tag("/api/image/#{si.object.image_id}", style: "max-height:80px;max-width:160px;object-fit:contain;margin-top:6px;").html_safe
|
||||
si.template.image_tag(WarpEngine::Images.url_for(si.object.image_id), style: "max-height:80px;max-width:160px;object-fit:contain;margin-top:6px;").html_safe
|
||||
end
|
||||
si.input :image_id, as: :select, label: "Image",
|
||||
collection: WarpEngine::Image.order(:original_filename).map { |img| [ img.original_filename, img.id ] },
|
||||
collection: WarpEngine::Images.select_options,
|
||||
include_blank: "— select —",
|
||||
hint: img_hint
|
||||
si.input :file_upload, as: :file, label: "Upload new image"
|
||||
|
||||
Reference in New Issue
Block a user