-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Send Ecosystem Metrics to Dependabot-API on Update Job Completion #10905
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,8 @@ class ApiError < StandardError; end | |
class ApiClient | ||
extend T::Sig | ||
|
||
MAX_REQUEST_RETRIES = 3 | ||
|
||
sig { params(base_url: String, job_id: T.any(String, Integer), job_token: String).void } | ||
def initialize(base_url, job_id, job_token) | ||
@base_url = base_url | ||
|
@@ -43,7 +45,7 @@ def create_pull_request(dependency_change, base_commit_sha) | |
rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError | ||
retry_count ||= 0 | ||
retry_count += 1 | ||
raise if retry_count > 3 | ||
raise if retry_count > MAX_REQUEST_RETRIES | ||
|
||
sleep(rand(3.0..10.0)) | ||
retry | ||
|
@@ -72,7 +74,7 @@ def update_pull_request(dependency_change, base_commit_sha) | |
rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError | ||
retry_count ||= 0 | ||
retry_count += 1 | ||
raise if retry_count > 3 | ||
raise if retry_count > MAX_REQUEST_RETRIES | ||
|
||
sleep(rand(3.0..10.0)) | ||
retry | ||
|
@@ -92,7 +94,7 @@ def close_pull_request(dependency_names, reason) | |
rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError | ||
retry_count ||= 0 | ||
retry_count += 1 | ||
raise if retry_count > 3 | ||
raise if retry_count > MAX_REQUEST_RETRIES | ||
|
||
sleep(rand(3.0..10.0)) | ||
retry | ||
|
@@ -119,7 +121,7 @@ def record_update_job_error(error_type:, error_details:) | |
rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError | ||
retry_count ||= 0 | ||
retry_count += 1 | ||
raise if retry_count > 3 | ||
raise if retry_count > MAX_REQUEST_RETRIES | ||
|
||
sleep(rand(3.0..10.0)) | ||
retry | ||
|
@@ -154,7 +156,7 @@ def record_update_job_warning(warn_type:, warn_title:, warn_description:) | |
rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError | ||
retry_count ||= 0 | ||
retry_count += 1 | ||
raise if retry_count > 3 | ||
raise if retry_count > MAX_REQUEST_RETRIES | ||
|
||
sleep(rand(3.0..10.0)) | ||
retry | ||
|
@@ -180,7 +182,7 @@ def record_update_job_unknown_error(error_type:, error_details:) | |
rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError | ||
retry_count ||= 0 | ||
retry_count += 1 | ||
raise if retry_count > 3 | ||
raise if retry_count > MAX_REQUEST_RETRIES | ||
|
||
sleep(rand(3.0..10.0)) | ||
retry | ||
|
@@ -200,7 +202,7 @@ def mark_job_as_processed(base_commit_sha) | |
rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError | ||
retry_count ||= 0 | ||
retry_count += 1 | ||
raise if retry_count > 3 | ||
raise if retry_count > MAX_REQUEST_RETRIES | ||
|
||
sleep(rand(3.0..10.0)) | ||
retry | ||
|
@@ -224,7 +226,7 @@ def update_dependency_list(dependencies, dependency_files) | |
rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError | ||
retry_count ||= 0 | ||
retry_count += 1 | ||
raise if retry_count > 3 | ||
raise if retry_count > MAX_REQUEST_RETRIES | ||
|
||
sleep(rand(3.0..10.0)) | ||
retry | ||
|
@@ -243,7 +245,7 @@ def record_ecosystem_versions(ecosystem_versions) | |
rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError | ||
retry_count ||= 0 | ||
retry_count += 1 | ||
raise if retry_count > 3 | ||
raise if retry_count > MAX_REQUEST_RETRIES | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review Tip: Moved retry request count in constant variable since it is always equal |
||
sleep(rand(3.0..10.0)) | ||
retry | ||
|
@@ -274,8 +276,86 @@ def increment_metric(metric, tags:) | |
end | ||
end | ||
|
||
sig { params(ecosystem: T.nilable(Ecosystem)).void } | ||
def record_ecosystem_meta(ecosystem) | ||
return unless Dependabot::Experiments.enabled?(:enable_record_ecosystem_meta) | ||
|
||
return if ecosystem.nil? | ||
|
||
begin | ||
::Dependabot::OpenTelemetry.tracer.in_span("record_ecosystem_meta", kind: :internal) do |_span| | ||
api_url = "#{base_url}/update_jobs/#{job_id}/record_ecosystem_meta" | ||
|
||
body = { | ||
data: [ | ||
{ | ||
ecosystem: { | ||
name: ecosystem.name, | ||
package_manager: version_manager_json(ecosystem.package_manager), | ||
language: version_manager_json(ecosystem.language) | ||
} | ||
} | ||
] | ||
} | ||
|
||
retry_count = 0 | ||
|
||
begin | ||
response = http_client.post(api_url, json: body) | ||
raise ApiError, response.body if response.code >= 400 | ||
rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError, ApiError => e | ||
retry_count += 1 | ||
if retry_count <= MAX_REQUEST_RETRIES | ||
sleep(rand(3.0..10.0)) | ||
retry | ||
else | ||
Dependabot.logger.error( | ||
"Failed to record ecosystem meta after #{MAX_REQUEST_RETRIES} retries: #{e.message}" | ||
) | ||
end | ||
end | ||
end | ||
rescue StandardError => e | ||
Dependabot.logger.error("Failed to record ecosystem meta: #{e.message}") | ||
end | ||
end | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review Tip: Create metrics from |
||
private | ||
|
||
# Update return type to allow returning a Hash or nil | ||
sig do | ||
params(version_manager: T.nilable(Dependabot::Ecosystem::VersionManager)) | ||
.returns(T.nilable(T::Hash[String, T.untyped])) | ||
end | ||
def version_manager_json(version_manager) | ||
return nil unless version_manager | ||
|
||
{ | ||
name: version_manager.name, | ||
raw_version: version_manager.version.to_semver.to_s, | ||
version: version_manager.version.to_s, | ||
requirement: version_manager_requirement_json(version_manager) | ||
} | ||
end | ||
|
||
# Update return type to allow returning a Hash or nil | ||
sig do | ||
params(version_manager: Dependabot::Ecosystem::VersionManager) | ||
.returns(T.nilable(T::Hash[String, T.untyped])) | ||
end | ||
def version_manager_requirement_json(version_manager) | ||
requirement = version_manager.requirement | ||
return nil unless requirement | ||
|
||
{ | ||
raw_constraint: requirement.constraints.join(", "), | ||
min_raw_version: requirement.min_version&.to_semver.to_s, | ||
min_version: requirement.min_version&.to_s, | ||
max_raw_version: requirement.max_version&.to_semver.to_s, | ||
max_version: requirement.max_version&.to_s | ||
} | ||
end | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review Tip: Use common metrics creation functions for package manager and language since they have metrics in same structure. |
||
sig { returns(String) } | ||
attr_reader :base_url | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,9 @@ def upsert_pull_request_with_error_handling(dependency_change, group) | |
end | ||
rescue StandardError => e | ||
error_handler.handle_job_error(error: e, dependency_group: dependency_snapshot.job_group) | ||
ensure | ||
# record metrics for the ecosystem | ||
service.record_ecosystem_meta(dependency_snapshot.ecosystem) | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review Tip: Send metrics for group refresh operation after update process (success, or fail)
kbukum1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Having created the dependency_change, we need to determine the right strategy to apply it to the project: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,8 @@ def perform | |
service.create_pull_request(T.must(dependency_change), dependency_snapshot.base_commit_sha) | ||
rescue StandardError => e | ||
error_handler.handle_job_error(error: e, dependency_group: group) | ||
ensure | ||
service.record_ecosystem_meta(dependency_snapshot.ecosystem) | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review Tip: Send metrics for |
||
else | ||
Dependabot.logger.info("Nothing to update for Dependency Group: '#{group.name}'") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,6 +100,8 @@ def check_and_create_pr_with_error_handling(dependency) | |
) | ||
rescue StandardError => e | ||
error_handler.handle_dependency_error(error: e, dependency: dependency) | ||
ensure | ||
service.record_ecosystem_meta(dependency_snapshot.ecosystem) | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review Tip: Send metrics for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kbukum1: Not logging There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @abdulapopoola what do you think. Should we log all update processes? This may be easier to implement but for each update process we are going to send metrics even through there may be no update There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just wanted to mention, normally we discussed that when we were preparing the ADR and we thought it will be ok to start with only if there is update. We were thinking to add no-update later if we see is necessary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is fixed now. I am sending metrics if update process started for a dependency or a group even there is no update. |
||
|
||
# rubocop:disable Metrics/AbcSize | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -93,8 +93,10 @@ def dependencies | |
def check_and_create_pr_with_error_handling(dependency) | ||
check_and_create_pull_request(dependency) | ||
rescue URI::InvalidURIError => e | ||
error_handler.handle_dependency_error(error: Dependabot::DependencyFileNotResolvable.new(e.message), | ||
dependency: dependency) | ||
error_handler.handle_dependency_error( | ||
error: Dependabot::DependencyFileNotResolvable.new(e.message), | ||
dependency: dependency | ||
) | ||
rescue Dependabot::InconsistentRegistryResponse => e | ||
error_handler.log_dependency_error( | ||
dependency: dependency, | ||
|
@@ -104,6 +106,8 @@ def check_and_create_pr_with_error_handling(dependency) | |
) | ||
rescue StandardError => e | ||
process_dependency_error(e, dependency) | ||
ensure | ||
service.record_ecosystem_meta(dependency_snapshot.ecosystem) | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review Tip: Send metrics for |
||
|
||
# rubocop:disable Metrics/AbcSize | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Tip: This PR addresses type issues introduced in the previous PR by ensuring all versions consistently use
Dependabot::Version
instead of the more genericGem::Version
. This change provides more specific typing, improving clarity and reducing abstraction.