woodpecker integration
This commit is contained in:
@@ -52,6 +52,25 @@ ActiveAdmin.register WarpEngine::ApplicationToken, as: "Application Token" do
|
||||
f.actions
|
||||
end
|
||||
|
||||
action_item :rotate, only: :show do
|
||||
if WarpEngine.woodpecker_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
|
||||
end
|
||||
|
||||
member_action :rotate, method: :post do
|
||||
result = WarpEngine::CiSecretSyncService.new.rotate(resource)
|
||||
if result[:rotated]
|
||||
session[:warp_engine_plain_token] = result[:new_token].plain_token
|
||||
redirect_to resource_path(result[:new_token]),
|
||||
notice: "Token rotated and synced to #{result.dig(:sync_result, :synced)&.size || 0} repo(s)"
|
||||
else
|
||||
redirect_to resource_path(resource),
|
||||
alert: "Rotation failed: #{result[:reason]}"
|
||||
end
|
||||
end
|
||||
|
||||
show do
|
||||
if (plain = controller.instance_variable_get(:@plain_token))
|
||||
panel "⚠️ Token — shown only once, copy it now!" do
|
||||
@@ -80,6 +99,19 @@ ActiveAdmin.register WarpEngine::ApplicationToken, as: "Application Token" do
|
||||
create! do |success, _failure|
|
||||
success.html do
|
||||
session[:warp_engine_plain_token] = resource.plain_token
|
||||
|
||||
if WarpEngine.woodpecker_configured?
|
||||
service = WarpEngine::CiSecretSyncService.new
|
||||
repos = service.repos_for_token(resource)
|
||||
if repos.any?
|
||||
result = service.provision(resource.plain_token, repos: repos)
|
||||
flash[:notice] = "Token created and synced to #{result[:synced].size} repo(s)."
|
||||
if result[:failed].any?
|
||||
flash[:alert] = "Failed to sync to #{result[:failed].size} repo(s)."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
redirect_to resource_path(resource) and return
|
||||
end
|
||||
end
|
||||
@@ -90,10 +122,12 @@ ActiveAdmin.register WarpEngine::ApplicationToken, as: "Application Token" do
|
||||
show!
|
||||
end
|
||||
|
||||
# Revoke = soft delete, audit-nyommal.
|
||||
def destroy
|
||||
if WarpEngine.woodpecker_configured?
|
||||
WarpEngine::CiSecretSyncService.new.deprovision(resource)
|
||||
end
|
||||
resource.revoke!
|
||||
redirect_to collection_path, notice: "Token revoked."
|
||||
redirect_to collection_path, notice: "Token revoked and Woodpecker secrets cleaned up."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
ActiveAdmin.register_page "CI Dashboard" do
|
||||
menu priority: 11, label: "🚀 CI Dashboard"
|
||||
|
||||
content do
|
||||
if WarpEngine.woodpecker_configured?
|
||||
begin
|
||||
entries = WarpEngine::CiPipelineService.new.dashboard
|
||||
rescue => e
|
||||
entries = []
|
||||
div class: "flash flash_alert" do
|
||||
"Error connecting to Woodpecker: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
entries.group_by { |e| e[:repo].platform }.sort.each do |platform, group|
|
||||
panel platform.titleize do
|
||||
table_for group do
|
||||
column("Repository") { |e| link_to e[:repo].full_name, admin_ci_repository_path(e[:repo]) }
|
||||
column("Software") { |e| e[:repo].software ? link_to(e[:repo].software.title, admin_software_path(e[:repo].software)) : "-" }
|
||||
column("Status") { |e|
|
||||
if e[:pipeline]
|
||||
status_tag e[:pipeline]["status"],
|
||||
class: e[:pipeline]["status"] == "success" ? "ok" : "error"
|
||||
else
|
||||
status_tag "unknown", class: "warning"
|
||||
end
|
||||
}
|
||||
column("Branch") { |e| e.dig(:pipeline, "branch") || "-" }
|
||||
column("When") { |e| e.dig(:pipeline, "created_at") || "-" }
|
||||
column("Actions") { |e|
|
||||
if e[:repo].active
|
||||
text_node link_to("Trigger", trigger_admin_ci_repository_path(e[:repo]),
|
||||
method: :post, class: "member_link")
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if entries.empty?
|
||||
para "No CI repositories tracked. Sync repos from the CI Repos page.",
|
||||
style: "color:#999;text-align:center;padding:40px 0;"
|
||||
end
|
||||
else
|
||||
para "Woodpecker is not configured. Set woodpecker_url and woodpecker_api_token in the WarpEngine initializer.",
|
||||
style: "color:#999;text-align:center;padding:40px 0;"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,118 @@
|
||||
ActiveAdmin.register WarpEngine::CiRepository, as: "CI Repository" do
|
||||
actions :index, :show, :edit, :update
|
||||
|
||||
menu priority: 10, label: "🔧 CI Repos"
|
||||
|
||||
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 ? "ok" : "warning") }
|
||||
column("Pipeline") { |r|
|
||||
if r.last_pipeline_status
|
||||
status_tag r.last_pipeline_status,
|
||||
class: r.last_pipeline_status == "success" ? "ok" : "error"
|
||||
else
|
||||
"-"
|
||||
end
|
||||
}
|
||||
column :last_pipeline_at
|
||||
actions defaults: true do |repo|
|
||||
if repo.active && WarpEngine.woodpecker_configured?
|
||||
item "Trigger", trigger_admin_ci_repository_path(repo), 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 -"
|
||||
end
|
||||
f.actions
|
||||
end
|
||||
|
||||
show do
|
||||
attributes_table 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") }
|
||||
row :last_pipeline_status
|
||||
row :last_pipeline_at
|
||||
row :created_at
|
||||
row :updated_at
|
||||
end
|
||||
|
||||
if WarpEngine.woodpecker_configured? && resource.active
|
||||
panel "Recent Pipelines" do
|
||||
begin
|
||||
pipelines = WarpEngine::CiPipelineService.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" ? "ok" : "error" }
|
||||
column("Branch") { |p| p["branch"] }
|
||||
column("Message") { |p| p["message"]&.truncate(60) }
|
||||
column("Created") { |p| p["created_at"] }
|
||||
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
|
||||
repo = WarpEngine::CiRepository.find(params[:id])
|
||||
WarpEngine::CiPipelineService.new.trigger(repo)
|
||||
redirect_to resource_path(repo), notice: "Pipeline triggered for #{repo.full_name}"
|
||||
rescue => e
|
||||
redirect_to resource_path(repo), alert: "Trigger failed: #{e.message}"
|
||||
end
|
||||
|
||||
collection_action :sync, method: :post do
|
||||
result = WarpEngine::CiRepoSyncService.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_ci_repositories_path, method: :post
|
||||
end
|
||||
end
|
||||
|
||||
controller do
|
||||
def scoped_collection
|
||||
super.includes(:software)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user