commenting

This commit is contained in:
Chris
2026-01-28 12:36:44 -08:00
parent 83d7e674e8
commit 0da0c02e26
7 changed files with 97 additions and 32 deletions

View File

@@ -1,11 +1,13 @@
class ProjectForks::Create
extend LightService::Organizer
def self.call(parent_project:, pull_request:)
def self.call(parent_project:, pull_request:, current_user:)
with(parent_project:, pull_request:).reduce(
ProjectForks::ForkProject,
ProjectForks::InitializeFromCanineConfig,
ProjectForks::PostForkComment
execute(->(ctx) { ctx[:project] = ctx.project_fork.child_project }).
Projects::DeployLatestCommit,
ProjectForks::PostForkComment,
)
end
end

View File

@@ -4,30 +4,6 @@ class ProjectForks::PostForkComment
executed do |context|
project_fork = context.project_fork
parent_project = project_fork.parent_project
client = Git::Client.from_project(parent_project)
comment_body = build_comment_body(project_fork)
client.add_pull_request_comment(project_fork.number, comment_body)
rescue StandardError => e
Rails.logger.error("Failed to post fork comment: #{e.message}")
# Don't fail the entire workflow if comment posting fails
end
def self.build_comment_body(project_fork)
project_url = Rails.application.routes.url_helpers.project_url(project_fork.child_project)
<<~COMMENT
🚀 **Preview environment created!**
A preview environment has been created for this pull request.
**Project:** [#{project_fork.child_project.name}](#{project_url})
The deployment is in progress. Once complete, you'll be able to access the preview at the project link above.
---
*Deployed by [Canine](#{Rails.application.routes.url_helpers.root_url})*
COMMENT
ProjectForks::PostCommentJob.perform_later(project_fork)
end
end

View File

@@ -60,7 +60,8 @@ class Projects::ProjectForksController < Projects::BaseController
result = ProjectForks::Create.call(
parent_project: @project,
pull_request:
pull_request:,
current_user:
)
if result.success?
Projects::DeployLatestCommit.execute(project: result.project_fork.child_project, current_user:)

View File

@@ -0,0 +1,70 @@
class ProjectForks::PostCommentJob < ApplicationJob
RETRY_DELAY = 30.seconds
MAX_ATTEMPTS = 20
def perform(project_fork, attempt: 1)
if certificates_ready?(project_fork)
post_comment(project_fork)
elsif attempt < MAX_ATTEMPTS
self.class.set(wait: RETRY_DELAY).perform_later(project_fork, attempt: attempt + 1)
else
Rails.logger.warn("ProjectForks::PostCommentJob: Max attempts reached for project_fork #{project_fork.id}, posting comment anyway")
post_comment(project_fork)
end
end
private
def certificates_ready?(project_fork)
child_project = project_fork.child_project
web_services_with_domains = child_project.services.web_service.joins(:domains).distinct
return true if web_services_with_domains.empty?
connection = K8::Connection.new(child_project, nil, allow_anonymous: true)
web_services_with_domains.all? do |service|
ingress = K8::Stateless::Ingress.new(service)
ingress.connect(connection).certificate_status
end
rescue StandardError => e
Rails.logger.error("ProjectForks::PostCommentJob: Error checking certificate status: #{e.message}")
false
end
def post_comment(project_fork)
parent_project = project_fork.parent_project
client = Git::Client.from_project(parent_project)
comment_body = build_comment_body(project_fork)
client.add_pull_request_comment(project_fork.number, comment_body)
rescue StandardError => e
Rails.logger.error("ProjectForks::PostCommentJob: Failed to post comment: #{e.message}")
end
def build_comment_body(project_fork)
project_url = Rails.application.routes.url_helpers.project_url(project_fork.child_project)
preview_urls = project_fork.urls
urls_section = if preview_urls.any?
urls_list = preview_urls.map { |url| "- #{url}" }.join("\n")
<<~URLS
**Preview URLs:**
#{urls_list}
URLS
else
""
end
<<~COMMENT
🚀 **Preview environment ready!**
A preview environment has been deployed for this pull request.
**Project:** [#{project_fork.child_project.name}](#{project_url})
#{urls_section}
---
*Deployed by [Canine](#{Rails.application.routes.url_helpers.root_url})*
COMMENT
end
end

View File

@@ -113,7 +113,7 @@ class Git::Github::Client < Git::Client
end
def add_pull_request_comment(pr_number, body)
client.add_comment(repository_url, pr_number, body)
client.add_comment(repository_url, pr_number, "[canine] #{body}")
end
def get_file(file_path, branch)

View File

@@ -158,7 +158,7 @@ class Git::Gitlab::Client < Git::Client
response = HTTParty.post(
"#{gitlab_api_base}/projects/#{encoded_url}/merge_requests/#{pr_number}/notes",
headers: { "Authorization" => "Bearer #{access_token}", "Content-Type" => "application/json" },
body: { body: body }.to_json
body: { body: "[canine] #{body}" }.to_json
)
raise "Failed to add comment: #{response.body}" unless response.success?

View File

@@ -16,12 +16,27 @@ RSpec.describe Git::Gitlab::Client do
end
context 'when webhook already exists' do
let(:webhook) { { 'id' => 123 } }
before do
allow(client).to receive(:webhook_exists?).and_return(true)
allow(client).to receive(:webhook).and_return(webhook)
end
it 'returns early without creating webhook' do
expect(HTTParty).not_to receive(:post)
it 'updates the existing webhook' do
expect(HTTParty).to receive(:put).with(
"#{gitlab_api_base}/projects/#{client.encoded_url}/hooks/#{webhook['id']}",
headers: { "Authorization" => "Bearer #{access_token}", "Content-Type" => "application/json" },
body: {
url: webhook_url,
name: "canine-webhook",
push_events: true,
merge_requests_events: true,
enable_ssl_verification: true,
token: described_class::GITLAB_WEBHOOK_SECRET
}.to_json
).and_return(success_response)
client.register_webhook!
end
end
@@ -39,6 +54,7 @@ RSpec.describe Git::Gitlab::Client do
url: webhook_url,
name: "canine-webhook",
push_events: true,
merge_requests_events: true,
enable_ssl_verification: true,
token: described_class::GITLAB_WEBHOOK_SECRET
}.to_json