Files
warp_engine/app/admin/pipelines.rb
T
mr.zeroandClaude Opus 5 f4983c6329 The pipeline admin form could never save
`ActiveAdmin.register WarpEngine::Pipeline` declared no `permit_params`, so every edit
handed unpermitted attributes to the model and Rails raised ForbiddenAttributesError. That
is not new: the form has been unable to save for as long as it has existed. My flash
message on `update` sat at the top of the traceback and made it look like the cause, which
it was not — and it is gone anyway, because overriding an ActiveAdmin action to say
something is a poor trade for what it can break. The move is written to the log instead.

Adding a spec that would have caught it, in the host app, because that is where the
ActiveAdmin instance lives: it signs in, PUTs the form, and checks both that the record
saves and that the software link moves off the pipeline that had it. Driven the same way
by hand against the development database first — 302, the link moved, the previous holder
left without one.

The engine's other admin resources were checked for the same omission: downloads and
releases are read-only and the file manager posts to its own routes, so pipelines was the
only one affected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 20:41:16 +02:00

135 lines
4.9 KiB
Ruby

ActiveAdmin.register WarpEngine::Pipeline, as: "Pipeline" do
actions :index, :show, :edit, :update
# Without this the edit form cannot save at all: ActiveAdmin hands unpermitted params to
# the model and Rails raises ForbiddenAttributesError. The two fields here are the two
# the form offers; everything else about a pipeline comes from the Woodpecker sync.
permit_params :platform, :software_id
menu parent: "🌀 WarpEngine", priority: 10, label: "🚀 Pipelines"
config.sort_order = "repo_name_asc"
config.batch_actions = false
scope :all, default: true
scope("Active") { |s| s.where(active: true) }
scope("Inactive") { |s| s.where(active: false) }
WarpEngine::PlatformLink::SUPPORTED_PLATFORMS.each do |p|
scope(p.capitalize) { |s| s.where(platform: p) }
end
index do
id_column
column :repo_owner
column :repo_name
column :platform
column("Software") { |r| r.software ? link_to(r.software.title, admin_software_path(r.software)) : "-" }
column(:active) { |r| status_tag(r.active ? "active" : "inactive", class: r.active ? "yes" : "no") }
column("Pipeline") { |r|
if r.last_pipeline_status
status_tag r.last_pipeline_status,
class: r.last_pipeline_status == "success" ? "yes" : "no"
else
"-"
end
}
column :last_pipeline_at
actions defaults: true do |pipeline|
if pipeline.active && WarpEngine.woodpecker_configured?
item "Trigger", trigger_admin_pipeline_path(pipeline), method: :post, class: "member_link"
end
end
end
filter :repo_name
filter :platform, as: :select, collection: WarpEngine::PlatformLink::SUPPORTED_PLATFORMS
filter :active
form do |f|
f.inputs do
f.input :platform, as: :select, collection: WarpEngine::PlatformLink::SUPPORTED_PLATFORMS
f.input :software_id, as: :select,
collection: WarpEngine::Software.order(:title).map { |s| [ s.title, s.id ] },
include_blank: "- none -",
hint: "One pipeline per software. Picking one that another pipeline already " \
"has moves the link here — that pipeline is left without a software, " \
"and the move is written to the log."
end
f.actions
end
sidebar "Details", only: :show do
attributes_table_for resource do
row :id
row :woodpecker_repo_id
row :repo_owner
row :repo_name
row :platform
row("Software") { |r| r.software ? link_to(r.software.title, admin_software_path(r.software)) : "-" }
row(:active) { |r| status_tag(r.active ? "active" : "inactive", class: r.active ? "yes" : "no") }
row :last_pipeline_status
row :last_pipeline_at
row :created_at
row :updated_at
end
end
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.",
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) }
# Woodpecker returns unix epoch seconds in "created"
column("Created") { |p| p["created"] ? Time.zone.at(p["created"]).strftime("%Y-%m-%d %H:%M") : "-" }
end
else
para "No pipelines found.", style: "color:#999;"
end
rescue => e
para "Error fetching pipelines: #{e.message}", style: "color:red;"
end
end
end
end
member_action :trigger, method: :post do
pipeline = WarpEngine::Pipeline.find(params[:id])
WarpEngine::PipelineService.new.trigger(pipeline)
redirect_to resource_path(pipeline), notice: "Pipeline triggered for #{pipeline.full_name}"
rescue => e
redirect_to resource_path(pipeline), alert: "Trigger failed: #{e.message}"
end
collection_action :sync, method: :post do
result = WarpEngine::PipelineSyncService.new.sync_all
redirect_to collection_path,
notice: "Synced: #{result[:created].size} new, #{result[:updated].size} updated, #{result[:deactivated].size} deactivated"
rescue => e
redirect_to collection_path, alert: "Sync failed: #{e.message}"
end
action_item :sync_repos, only: :index do
if WarpEngine.woodpecker_configured?
link_to "Sync from Woodpecker", sync_admin_pipelines_path, method: :post
end
end
controller do
def scoped_collection
super.includes(:software)
end
end
end