warp_engine: CiRepository -> Pipeline rename everywhere, ci_ service prefixes dropped, unknown platform allowed

This commit is contained in:
2026-08-06 19:46:06 +02:00
parent 05beb6230b
commit 2a94fc785f
22 changed files with 223 additions and 199 deletions
@@ -1,35 +0,0 @@
module WarpEngine
class CiPipelineService
def initialize(client: WoodpeckerClient.new)
@client = client
end
def trigger(repo, branch: "main")
@client.trigger_pipeline(repo.woodpecker_repo_id, branch: branch)
end
# Fetches one page of a repo's pipelines; the newest one refreshes the
# repo's cached last_pipeline_* columns (Pipelines index and the CI API).
def list_pipelines(repo, page: 1)
pipelines = @client.list_pipelines(repo.woodpecker_repo_id, page: page)
refresh_last_pipeline(repo, pipelines.first) if page == 1 && pipelines.is_a?(Array)
pipelines
end
def pipeline_detail(repo, number)
@client.get_pipeline(repo.woodpecker_repo_id, number)
end
private
# Woodpecker returns unix epoch seconds in "created".
def refresh_last_pipeline(repo, pipeline)
return unless pipeline && repo.persisted?
repo.update_columns(
last_pipeline_status: pipeline["status"],
last_pipeline_at: pipeline["created"] ? Time.zone.at(pipeline["created"]) : nil
)
end
end
end
@@ -0,0 +1,35 @@
module WarpEngine
class PipelineService
def initialize(client: WoodpeckerClient.new)
@client = client
end
def trigger(pipeline, branch: "main")
@client.trigger_pipeline(pipeline.woodpecker_repo_id, branch: branch)
end
# Fetches one page of a pipeline's runs; the newest one refreshes the
# cached last_pipeline_* columns (Pipelines index and the CI API).
def list_pipelines(pipeline, page: 1)
runs = @client.list_pipelines(pipeline.woodpecker_repo_id, page: page)
refresh_last_pipeline(pipeline, runs.first) if page == 1 && runs.is_a?(Array)
runs
end
def pipeline_detail(pipeline, number)
@client.get_pipeline(pipeline.woodpecker_repo_id, number)
end
private
# Woodpecker returns unix epoch seconds in "created".
def refresh_last_pipeline(pipeline, run)
return unless run && pipeline.persisted?
pipeline.update_columns(
last_pipeline_status: run["status"],
last_pipeline_at: run["created"] ? Time.zone.at(run["created"]) : nil
)
end
end
end
@@ -1,5 +1,5 @@
module WarpEngine
class CiRepoSyncService
class PipelineSyncService
def initialize(client: WoodpeckerClient.new)
@client = client
end
@@ -10,7 +10,7 @@ module WarpEngine
remote_ids = remote_repos.map { |r| r["id"] }
remote_repos.each do |remote|
record = CiRepository.unscoped.find_or_initialize_by(
record = Pipeline.unscoped.find_or_initialize_by(
woodpecker_repo_id: remote["id"]
)
@@ -22,9 +22,9 @@ module WarpEngine
deleted_at: nil
)
if record.platform.blank? || record.platform == "unknown"
if record.platform.blank? || record.platform == Pipeline::UNKNOWN_PLATFORM
sw = Software.find_by(name: remote["name"])
record.platform = sw&.platform || "unknown"
record.platform = sw&.platform || Pipeline::UNKNOWN_PLATFORM
record.software = sw if sw
end
@@ -33,7 +33,7 @@ module WarpEngine
results[was_new ? :created : :updated] << record
end
CiRepository.where.not(woodpecker_repo_id: remote_ids).find_each do |orphan|
Pipeline.where.not(woodpecker_repo_id: remote_ids).find_each do |orphan|
orphan.update!(active: false) if orphan.active?
results[:deactivated] << orphan
end
@@ -48,7 +48,7 @@ module WarpEngine
def deactivate(repo_id)
@client.deactivate_repo(repo_id)
record = CiRepository.find_by!(woodpecker_repo_id: repo_id)
record = Pipeline.find_by!(woodpecker_repo_id: repo_id)
record.update!(active: false)
end
@@ -56,14 +56,14 @@ module WarpEngine
def sync_single(repo_id)
remote = @client.get_repo(repo_id)
record = CiRepository.unscoped.find_or_initialize_by(woodpecker_repo_id: repo_id)
record = Pipeline.unscoped.find_or_initialize_by(woodpecker_repo_id: repo_id)
record.assign_attributes(
repo_name: remote["name"], repo_owner: remote["owner"],
active: remote["active"], deleted_at: nil
)
if record.platform.blank?
sw = Software.find_by(name: remote["name"])
record.platform = sw&.platform || "unknown"
record.platform = sw&.platform || Pipeline::UNKNOWN_PLATFORM
record.software = sw if sw
end
record.save!
@@ -1,40 +1,40 @@
module WarpEngine
class CiSecretSyncService
class SecretSyncService
SECRET_NAME = "application_token".freeze
def initialize(client: WoodpeckerClient.new)
@client = client
end
def provision(plain_token, repos:)
def provision(plain_token, pipelines:)
results = { synced: [], failed: [] }
repos.each do |repo|
if secret_exists?(repo.woodpecker_repo_id)
@client.update_secret(repo.woodpecker_repo_id, SECRET_NAME, value: plain_token)
pipelines.each do |pipeline|
if secret_exists?(pipeline.woodpecker_repo_id)
@client.update_secret(pipeline.woodpecker_repo_id, SECRET_NAME, value: plain_token)
else
@client.create_secret(repo.woodpecker_repo_id, name: SECRET_NAME, value: plain_token)
@client.create_secret(pipeline.woodpecker_repo_id, name: SECRET_NAME, value: plain_token)
end
results[:synced] << repo
results[:synced] << pipeline
rescue WoodpeckerClient::ApiError, WoodpeckerClient::ConnectionError => e
Rails.logger.error("[CiSecretSyncService] failed for #{repo.full_name}: #{e.message}")
results[:failed] << { repo: repo, error: e.message }
Rails.logger.error("[SecretSyncService] failed for #{pipeline.full_name}: #{e.message}")
results[:failed] << { pipeline: pipeline, error: e.message }
end
results
end
def deprovision(application_token)
repos_for_token(application_token).each do |repo|
@client.delete_secret(repo.woodpecker_repo_id, SECRET_NAME)
pipelines_for_token(application_token).each do |pipeline|
@client.delete_secret(pipeline.woodpecker_repo_id, SECRET_NAME)
rescue WoodpeckerClient::ApiError => e
Rails.logger.warn("[CiSecretSyncService] delete failed for #{repo.full_name}: #{e.message}")
Rails.logger.warn("[SecretSyncService] delete failed for #{pipeline.full_name}: #{e.message}")
end
end
def rotate(application_token)
repos = repos_for_token(application_token)
return { rotated: false, reason: "no repos" } if repos.empty?
pipelines = pipelines_for_token(application_token)
return { rotated: false, reason: "no pipelines" } if pipelines.empty?
new_token = ApplicationToken.create!(
name: "#{application_token.name} (rotated #{Date.current})",
@@ -45,26 +45,26 @@ module WarpEngine
unrestricted: application_token.unrestricted?
)
result = provision(new_token.plain_token, repos: repos)
result = provision(new_token.plain_token, pipelines: pipelines)
if result[:synced].any?
application_token.revoke!
{ rotated: true, new_token: new_token, sync_result: result }
else
new_token.revoke!
{ rotated: false, reason: "all repos failed", sync_result: result }
{ rotated: false, reason: "all pipelines failed", sync_result: result }
end
end
def repos_for_token(application_token)
def pipelines_for_token(application_token)
if application_token.unrestricted?
CiRepository.active.to_a
Pipeline.active.to_a
else
software_ids = Software.where(
owner_type: application_token.owner_type,
owner_id: application_token.owner_id
).pluck(:id)
CiRepository.active.where(software_id: software_ids).to_a
Pipeline.active.where(software_id: software_ids).to_a
end
end