lib/gitlab_graph/plugin.rb in danger-gitlab_graph-0.0.7 vs lib/gitlab_graph/plugin.rb in danger-gitlab_graph-0.1.0

- old
+ new

@@ -1,68 +1,116 @@ # frozen_string_literal: true -require 'svggraph' +require "svggraph" + module Danger # This plugin retrieves a certain metric from previous job runs and displays them as a graph # # @example Extract values from job "test" with the given regex # - # gitlab_graph.report_metric(/performance: ([0-9]+)s/, "test") + # gitlab_graph.report_metric([{ + # regex: /took ([0-9]+)/, + # series_name: "Performance", + # job_name: "test1" + # }, { + # regex: /slept ([0-9]+)/, + # series_name: "IDLE time", + # job_name: "test1" + # }]) # + # outputs a graph similiar to this ![sample graph](spec/support/fixtures/graph-simple-expected.svg) # @see kingjan1999/danger-gitlab_graph # @tags gitlab, graph, performance # class DangerGitlabGraph < Plugin - # Creates and comments a graph based on a certain metric, extracted via regex - # @return [Array<String>] - # - def report_metric(extraction_regex, job_name, prev_pipeline_count = 10, graph_options = {}) - pipeline_id = ENV["CI_PIPELINE_ID"] - project_id = ENV["CI_PROJECT_ID"] + # Gathers metric data from current and pevious pipelines + # @return [Arrray<{pipeline_id => int, :metric => float}>] + def gather_metric(extraction_config, prev_pipeline_count = 10) + pipeline_id = ENV["CI_PIPELINE_ID"].to_i + project_id = ENV.fetch("CI_PROJECT_ID", nil) target_branch = gitlab.branch_for_merge - _, new_metric = extract_metric_from_pipeline(project_id, pipeline_id, extraction_regex, job_name) - unless new_metric - warn("No updated metric found for job #{job_name}") - return + begin + new_metric = extract_metric_from_pipeline(project_id, pipeline_id, extraction_config[:regex], extraction_config[:job_name]) + rescue JobNotFoundException + warn("Job #{extraction_config[:job_name]} for metric extraction of #{extraction_config[:series_name]} not found in current pipeline") + return [] end + unless new_metric[:metric] + warn("No updated metric #{extraction_config[:series_name]} found for job #{extraction_config[:job_name]}") + return [] + end + previous_target_branch_pipelines = gitlab.api.pipelines(project_id, { - status: 'success', + status: "success", ref: target_branch, per_page: prev_pipeline_count }) - previous_metrics = previous_target_branch_pipelines.collect { |pipeline| extract_metric_from_pipeline(project_id, pipeline.id, extraction_regex, job_name) } - previous_metrics = previous_metrics.select { |val| val[1] } - # create graph + previous_metrics = previous_target_branch_pipelines.collect do |pipeline| + extract_metric_from_pipeline(project_id, pipeline.id, extraction_config[:regex], extraction_config[:job_name]).merge(hash: pipeline.sha) + rescue JobNotFoundException + return { pipeline_id: pipeline.id, metric: false, hash: pipeline.sha } + end - data = previous_metrics.collect { |val| val[1] } - data = data + [new_metric] + previous_metrics + [new_metric.merge(hash: ENV.fetch("CI_COMMIT_SHA", "invalid_hash"))] + end - fields = previous_metrics.collect { |pipeline| "Run #{pipeline[0]}" } - fields += [pipeline_id] + # Creates and comments a graph based on a certain metric, extracted via regex + # @param [Array<hash>] extraction_configs Hash-Array: {:regex, :job_name, :series_name} + # @param [int] prev_pipeline_count + # @param [hash] graph_options see svg-graph doc + # @return [void] + def report_metric(extraction_configs, prev_pipeline_count = 10, graph_options = {}) + project_id = ENV.fetch("CI_PROJECT_ID", nil) + fields = nil + all_data = [] + extraction_configs.each do |extraction_config| + all_metrics = gather_metric(extraction_config, prev_pipeline_count) + + if all_metrics.length.zero? + next + end + + if fields and all_metrics.length != fields.length + warn("Not all metrics could be found in an equal amount of jobs. Unable to plot #{extraction_config[:series_name]}") + next + end + + data = all_metrics.collect { |val| val[:metric] } + + fields ||= all_metrics.collect { |pipeline| pipeline[:hash][0..7] } + + all_data.push({ data: data, title: extraction_config[:series_name] }) + end + + if all_data.empty? + return + end + default_graph_options = { - :width => 640, - :height => 480, - :graph_title => "Performance Metric", - :show_graph_title => true, - :x_title => "Pipeline Runs", - :y_title => "Metric Value", - :show_y_title => true, - :show_x_title => true, - :number_format => "%.2fs", - :fields => fields + width: 640, + height: 480, + graph_title: "Performance Metric", + show_graph_title: true, + x_title: "Commit", + y_title: "Metric Value", + show_y_title: true, + show_x_title: true, + number_format: "%.2fs", + fields: fields } + # create graph g = SVG::Graph::Line.new(default_graph_options.merge(graph_options)) - g.add_data(:data => data) + all_data.each { |elem| g.add_data(data: elem[:data], title: elem[:title]) } - temp_file = Tempfile.new(%w[graph .svg]) + temp_file = Tempfile.new(%w(graph .svg)) begin temp_file.write(g.burn_svg_only) uploaded_file = gitlab.api.upload_file(project_id, temp_file.path) markdown(uploaded_file.markdown) ensure @@ -74,18 +122,21 @@ private def extract_metric_from_pipeline(project_id, pipeline_id, extraction_regex, job_name) all_jobs = gitlab.api.pipeline_jobs(project_id, pipeline_id) target_job = all_jobs.find { |x| x.name == job_name } - return false unless target_job + raise JobNotFoundException, "job #{job_name} not found in pipeline #{pipeline_id}" unless target_job job_trace = gitlab.api.job_trace(project_id, target_job.id) metric_matches = job_trace.match(extraction_regex) - unless metric_matches.captures - return pipeline_id, false + unless metric_matches&.captures + return { pipeline_id: pipeline_id, metric: false } end - [pipeline_id, metric_matches.captures[0].to_f] + { pipeline_id: pipeline_id, metric: metric_matches.captures[0].to_f } + end + + class JobNotFoundException < StandardError end end end