gitlab-qa 10.3.0 → 12.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.gitlab/changelog_config.yml +13 -0
  4. data/.gitlab/merge_request_templates/Release.md +13 -34
  5. data/.gitlab-ci.yml +3 -1
  6. data/.rubocop.yml +13 -2
  7. data/.rubocop_todo.yml +57 -97
  8. data/Dangerfile +1 -5
  9. data/Gemfile.lock +47 -39
  10. data/README.md +1 -2
  11. data/docs/release_process.md +1 -1
  12. data/docs/running_against_remote_grid.md +42 -3
  13. data/docs/what_tests_can_be_run.md +5 -2
  14. data/gitlab-qa.gemspec +5 -3
  15. data/lib/gitlab/qa/component/base.rb +9 -9
  16. data/lib/gitlab/qa/component/gitaly.rb +7 -5
  17. data/lib/gitlab/qa/component/gitaly_cluster.rb +15 -9
  18. data/lib/gitlab/qa/component/gitlab.rb +48 -21
  19. data/lib/gitlab/qa/component/mail_hog.rb +1 -0
  20. data/lib/gitlab/qa/component/praefect.rb +41 -31
  21. data/lib/gitlab/qa/component/selenoid.rb +14 -7
  22. data/lib/gitlab/qa/component/specs.rb +11 -5
  23. data/lib/gitlab/qa/component/staging.rb +4 -4
  24. data/lib/gitlab/qa/component/telegraf.rb +2 -1
  25. data/lib/gitlab/qa/docker/engine.rb +6 -3
  26. data/lib/gitlab/qa/docker/volumes.rb +1 -1
  27. data/lib/gitlab/qa/release.rb +4 -4
  28. data/lib/gitlab/qa/runner.rb +10 -3
  29. data/lib/gitlab/qa/runtime/env.rb +47 -62
  30. data/lib/gitlab/qa/runtime/logger.rb +1 -1
  31. data/lib/gitlab/qa/runtime/omnibus_configuration.rb +1 -0
  32. data/lib/gitlab/qa/runtime/omnibus_configurations/decomposition_single_db.rb +1 -2
  33. data/lib/gitlab/qa/runtime/omnibus_configurations/object_storage_gcs.rb +2 -1
  34. data/lib/gitlab/qa/runtime/scenario.rb +1 -5
  35. data/lib/gitlab/qa/scenario/actable.rb +4 -4
  36. data/lib/gitlab/qa/scenario/test/instance/airgapped.rb +2 -4
  37. data/lib/gitlab/qa/scenario/test/instance/deployment_base.rb +2 -1
  38. data/lib/gitlab/qa/scenario/test/integration/gitaly_cluster.rb +0 -2
  39. data/lib/gitlab/qa/scenario/test/integration/group_saml.rb +1 -1
  40. data/lib/gitlab/qa/scenario/test/integration/ldap.rb +5 -6
  41. data/lib/gitlab/qa/scenario/test/integration/mtls.rb +20 -6
  42. data/lib/gitlab/qa/scenario/test/integration/oauth.rb +13 -4
  43. data/lib/gitlab/qa/scenario/test/integration/praefect.rb +16 -10
  44. data/lib/gitlab/qa/scenario/test/integration/registry_with_cdn.rb +5 -2
  45. data/lib/gitlab/qa/scenario/test/omnibus/update_from_previous.rb +1 -1
  46. data/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb +1 -3
  47. data/lib/gitlab/qa/scenario/test/sanity/version.rb +1 -1
  48. data/lib/gitlab/qa/support/config_scripts.rb +1 -1
  49. data/lib/gitlab/qa/support/gitlab_version_info.rb +30 -10
  50. data/lib/gitlab/qa/support/shell_command.rb +1 -0
  51. data/lib/gitlab/qa/test_logger.rb +2 -2
  52. data/lib/gitlab/qa/version.rb +1 -1
  53. data/lib/gitlab/qa.rb +0 -1
  54. data/support/data/admin_access_token_seed.rb +5 -1
  55. data/support/data/license_usage_seed.rb +3 -1
  56. metadata +16 -45
  57. data/bin/slack +0 -14
  58. data/exe/gitlab-qa-report +0 -10
  59. data/lib/gitlab/qa/report/base_test_results.rb +0 -39
  60. data/lib/gitlab/qa/report/find_set_dri.rb +0 -43
  61. data/lib/gitlab/qa/report/generate_test_session.rb +0 -275
  62. data/lib/gitlab/qa/report/gitlab_issue_client.rb +0 -190
  63. data/lib/gitlab/qa/report/gitlab_issue_dry_client.rb +0 -28
  64. data/lib/gitlab/qa/report/j_unit_test_results.rb +0 -27
  65. data/lib/gitlab/qa/report/json_test_results.rb +0 -29
  66. data/lib/gitlab/qa/report/prepare_stage_reports.rb +0 -86
  67. data/lib/gitlab/qa/report/relate_failure_issue.rb +0 -374
  68. data/lib/gitlab/qa/report/report_as_issue.rb +0 -176
  69. data/lib/gitlab/qa/report/report_results.rb +0 -64
  70. data/lib/gitlab/qa/report/results_in_issues.rb +0 -126
  71. data/lib/gitlab/qa/report/results_in_testcases.rb +0 -111
  72. data/lib/gitlab/qa/report/results_reporter_shared.rb +0 -70
  73. data/lib/gitlab/qa/report/summary_table.rb +0 -43
  74. data/lib/gitlab/qa/report/test_result.rb +0 -184
  75. data/lib/gitlab/qa/report/update_screenshot_path.rb +0 -63
  76. data/lib/gitlab/qa/reporter.rb +0 -131
  77. data/lib/gitlab/qa/runtime/omnibus_configurations/packages.rb +0 -17
  78. data/lib/gitlab/qa/runtime/token_finder.rb +0 -44
  79. data/lib/gitlab/qa/slack/post_to_slack.rb +0 -30
  80. data/lib/gitlab/qa/system_logs/finders/json_log_finder.rb +0 -65
  81. data/lib/gitlab/qa/system_logs/finders/rails/api_log_finder.rb +0 -21
  82. data/lib/gitlab/qa/system_logs/finders/rails/application_log_finder.rb +0 -21
  83. data/lib/gitlab/qa/system_logs/finders/rails/exception_log_finder.rb +0 -21
  84. data/lib/gitlab/qa/system_logs/finders/rails/graphql_log_finder.rb +0 -21
  85. data/lib/gitlab/qa/system_logs/log_types/log.rb +0 -38
  86. data/lib/gitlab/qa/system_logs/log_types/rails/api_log.rb +0 -34
  87. data/lib/gitlab/qa/system_logs/log_types/rails/application_log.rb +0 -27
  88. data/lib/gitlab/qa/system_logs/log_types/rails/exception_log.rb +0 -23
  89. data/lib/gitlab/qa/system_logs/log_types/rails/graphql_log.rb +0 -30
  90. data/lib/gitlab/qa/system_logs/shared_fields.rb +0 -29
  91. data/lib/gitlab/qa/system_logs/system_logs_formatter.rb +0 -65
@@ -1,131 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'optparse'
4
-
5
- module Gitlab
6
- module QA
7
- class Reporter
8
- # rubocop:disable Metrics/AbcSize
9
- # rubocop:disable Metrics/PerceivedComplexity
10
- # rubocop:disable Metrics/CyclomaticComplexity
11
- def self.invoke(args)
12
- report_options = {}
13
- slack_options = {}
14
-
15
- options = OptionParser.new do |opts|
16
- opts.banner = 'Usage: gitlab-qa-reporter [options]'
17
-
18
- opts.on('--prepare-stage-reports FILES', 'Prepare separate reports for each Stage from the provided JUnit XML files') do |files|
19
- report_options[:prepare_stage_reports] = true
20
- report_options[:input_files] = files if files
21
- end
22
-
23
- opts.on('--report-results FILES', String, 'Report test results from JUnit XML files in GitLab test cases and results issues') do |files|
24
- report_options[:report_results] = true
25
- report_options[:input_files] = files if files
26
- end
27
-
28
- opts.on('--relate-failure-issue FILES', String, 'Relate test failures to failure issues from RSpec JSON files') do |files|
29
- report_options[:relate_failure_issue] = true
30
- report_options[:input_files] = files if files
31
- end
32
-
33
- opts.on('--max-diff-ratio DIFF_RATO', Float, 'Max stacktrace diff ratio for QA failure issues detection. Used by with --relate-failure-issue') do |value|
34
- report_options[:max_diff_ratio] = value
35
- end
36
-
37
- opts.on('-p', '--project PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --relate-failure-issue') do |value|
38
- report_options[:project] = value
39
- end
40
-
41
- opts.on('--results-issue-project RESULTS_ISSUE_PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --report-results') do |value|
42
- report_options[:results_issue_project] = value
43
- end
44
-
45
- opts.on('--test-case-project TEST_CASE_PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --report-results') do |value|
46
- report_options[:test_case_project] = value
47
- end
48
-
49
- opts.on('--generate-test-session FILES', String, 'Generate test session report') do |files|
50
- report_options[:generate_test_session] = true
51
- report_options[:input_files] = files if files
52
- end
53
-
54
- opts.on('-t', '--token ACCESS_TOKEN', String, 'A valid access token. Required by --report-results and --relate-failure-issue') do |value|
55
- report_options[:token] = value
56
- end
57
-
58
- opts.on('--post-to-slack MSG', 'Post message to slack') do |msg|
59
- slack_options[:post_to_slack] = true
60
- slack_options[:message] = msg
61
- end
62
-
63
- opts.on('--include-summary-table FILES', 'Create a results summary table to post to slack. To be used with --post-to-slack.') do |files|
64
- raise 'This option should be used with --post-to-slack.' unless slack_options[:post_to_slack]
65
-
66
- slack_options[:message] = slack_options[:message] + "\n\n" + Gitlab::QA::Report::SummaryTable.create(input_files: files)
67
- end
68
-
69
- opts.on('--include-system-log-errors FILES', String, 'Include errors from system logs in failure issues. To be used with --relate-failure-issue') do |files|
70
- raise 'This option should be used with --relate-failure-issue.' unless report_options[:relate_failure_issue]
71
-
72
- report_options[:system_logs] = files if files
73
- end
74
-
75
- opts.on('--update-screenshot-path FILES', "Update the path to screenshots to container's host") do |files|
76
- report_options[:update_screenshot_path] = true
77
- report_options[:files] = files
78
- end
79
-
80
- opts.on('--dry-run', "Perform a dry-run (don't create or update issues or test cases)") do |files|
81
- report_options[:dry_run] = true
82
- end
83
-
84
- opts.on_tail('-v', '--version', 'Show the version') do
85
- require 'gitlab/qa/version'
86
- puts "#{$PROGRAM_NAME} : #{VERSION}"
87
- exit
88
- end
89
-
90
- opts.on_tail('-h', '--help', 'Show the usage') do
91
- puts opts
92
- exit
93
- end
94
-
95
- opts.parse(args)
96
- end
97
-
98
- if args.any?
99
- if report_options.delete(:prepare_stage_reports)
100
- Gitlab::QA::Report::PrepareStageReports.new(**report_options).invoke!
101
-
102
- elsif report_options.delete(:relate_failure_issue)
103
- report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
104
- Gitlab::QA::Report::RelateFailureIssue.new(**report_options).invoke!
105
-
106
- elsif report_options.delete(:report_results)
107
- report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
108
- Gitlab::QA::Report::ReportResults.new(**report_options).invoke!
109
-
110
- elsif report_options.delete(:generate_test_session)
111
- report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
112
- Gitlab::QA::Report::GenerateTestSession.new(**report_options).invoke!
113
-
114
- elsif slack_options.delete(:post_to_slack)
115
- Gitlab::QA::Slack::PostToSlack.new(**slack_options).invoke!
116
-
117
- elsif report_options.delete(:update_screenshot_path)
118
- Gitlab::QA::Report::UpdateScreenshotPath.new(**report_options).invoke!
119
-
120
- end
121
- else
122
- puts options
123
- exit 1
124
- end
125
- end
126
- # rubocop:enable Metrics/CyclomaticComplexity
127
- # rubocop:enable Metrics/PerceivedComplexity
128
- # rubocop:enable Metrics/AbcSize
129
- end
130
- end
131
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module Runtime
6
- module OmnibusConfigurations
7
- class Packages < Default
8
- def configuration
9
- <<~OMNIBUS
10
- gitlab_rails['packages_enabled'] = true
11
- OMNIBUS
12
- end
13
- end
14
- end
15
- end
16
- end
17
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module Runtime
6
- class TokenFinder
7
- def self.find_token!(token, suffix: nil)
8
- new(token, suffix).find_token!
9
- end
10
-
11
- attr_reader :token, :suffix
12
-
13
- def initialize(token, suffix)
14
- @token = token
15
- @suffix = suffix
16
- end
17
-
18
- def find_token!
19
- find_token_from_attrs || find_token_from_env || find_token_from_file
20
- end
21
-
22
- def find_token_from_attrs
23
- token
24
- end
25
-
26
- def find_token_from_env
27
- Env.qa_access_token
28
- end
29
-
30
- def find_token_from_file
31
- @token_from_file ||= File.read(token_file_path).strip
32
- rescue Errno::ENOENT
33
- fail "Please provide a valid access token with the `-t/--token` option, the `GITLAB_QA_ACCESS_TOKEN` environment variable, or in the `#{token_file_path}` file!"
34
- end
35
-
36
- private
37
-
38
- def token_file_path
39
- @token_file_path ||= File.expand_path("../api_token#{"_#{suffix}" if suffix}", __dir__)
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module Slack
6
- class PostToSlack
7
- def initialize(message:, icon_emoji: Runtime::Env.slack_icon_emoji, channel: Runtime::Env.slack_qa_channel)
8
- @channel = channel
9
- @message = message
10
- @icon_emoji = icon_emoji
11
- end
12
-
13
- def invoke!
14
- Runtime::Env.require_slack_qa_channel! unless @channel
15
- Runtime::Env.require_ci_slack_webhook_url!
16
-
17
- params = {}
18
- params['channel'] = @channel
19
- params['username'] = "GitLab QA Bot"
20
- params['icon_emoji'] = @icon_emoji
21
- params['text'] = @message
22
-
23
- url = Runtime::Env.ci_slack_webhook_url
24
-
25
- Support::HttpRequest.make_http_request(method: 'post', url: url, params: params, show_response: true)
26
- end
27
- end
28
- end
29
- end
30
- end
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
-
5
- module Gitlab
6
- module QA
7
- module SystemLogs
8
- module Finders
9
- class JsonLogFinder
10
- def initialize(base_path, file_path)
11
- @base_path = base_path
12
- @file_path = file_path
13
- end
14
-
15
- def find(correlation_id)
16
- log_file_path = "#{@base_path}/#{@file_path}"
17
- logs = []
18
-
19
- if File.exist?(log_file_path) && !correlation_id.nil?
20
- File.foreach(log_file_path) do |line|
21
- begin
22
- json_line = JSON.parse(line, symbolize_names: true)
23
- rescue JSON::ParserError
24
- Runtime::Logger.debug("JsonLogFinder#find attempted to parse invalid JSON: #{line}")
25
-
26
- next
27
- end
28
-
29
- if (json_line[:correlation_id])&.casecmp?(correlation_id)
30
- normalized_line = normalize_keys(json_line)
31
- logs << new_log(normalized_line)
32
- end
33
- end
34
- end
35
-
36
- logs
37
- end
38
-
39
- def new_log(data)
40
- raise 'abstract method new_log must be defined!'
41
- end
42
-
43
- private
44
-
45
- def normalize_keys(json_line)
46
- normalized_hash = {}
47
-
48
- json_line.each_key do |old_key|
49
- key_string = old_key.to_s
50
-
51
- if key_string.include?('.')
52
- normalized_key = key_string.tr('.', '_').to_sym
53
- normalized_hash[normalized_key] = json_line[old_key]
54
- else
55
- normalized_hash[old_key] = json_line[old_key]
56
- end
57
- end
58
-
59
- normalized_hash
60
- end
61
- end
62
- end
63
- end
64
- end
65
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module SystemLogs
6
- module Finders
7
- module Rails
8
- class ApiLogFinder < JsonLogFinder
9
- def initialize(base_path, file_path = 'gitlab-rails/api_json.log')
10
- super(base_path, file_path)
11
- end
12
-
13
- def new_log(data)
14
- LogTypes::Rails::ApiLog.new(data)
15
- end
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module SystemLogs
6
- module Finders
7
- module Rails
8
- class ApplicationLogFinder < JsonLogFinder
9
- def initialize(base_path, file_path = 'gitlab-rails/application_json.log')
10
- super(base_path, file_path)
11
- end
12
-
13
- def new_log(data)
14
- LogTypes::Rails::ApplicationLog.new(data)
15
- end
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module SystemLogs
6
- module Finders
7
- module Rails
8
- class ExceptionLogFinder < JsonLogFinder
9
- def initialize(base_path, file_path = 'gitlab-rails/exceptions_json.log')
10
- super(base_path, file_path)
11
- end
12
-
13
- def new_log(data)
14
- LogTypes::Rails::ExceptionLog.new(data)
15
- end
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module SystemLogs
6
- module Finders
7
- module Rails
8
- class GraphqlLogFinder < JsonLogFinder
9
- def initialize(base_path, file_path = 'gitlab-rails/graphql_json.log')
10
- super(base_path, file_path)
11
- end
12
-
13
- def new_log(data)
14
- LogTypes::Rails::GraphqlLog.new(data)
15
- end
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module SystemLogs
6
- module LogTypes
7
- class Log
8
- def initialize(name, data)
9
- @name = name
10
- @data = data
11
- end
12
-
13
- attr_reader :name, :data
14
-
15
- def summary_fields
16
- [
17
- :severity,
18
- :correlation_id,
19
- :time,
20
- :message
21
- ]
22
- end
23
-
24
- def summary
25
- summary = {}
26
-
27
- summary_fields.each do |field|
28
- value = data[field]
29
- summary[field] = value unless value.nil?
30
- end
31
-
32
- summary
33
- end
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module SystemLogs
6
- module LogTypes
7
- module Rails
8
- class ApiLog < Log
9
- include SharedFields::Exception
10
- include SharedFields::Meta
11
-
12
- def initialize(data)
13
- super('Rails API', data)
14
- end
15
-
16
- def summary_fields
17
- super.concat(
18
- [
19
- :method,
20
- :path,
21
- :status,
22
- :params,
23
- :api_error
24
- ],
25
- exception_fields,
26
- meta_fields
27
- )
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end
34
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module SystemLogs
6
- module LogTypes
7
- module Rails
8
- class ApplicationLog < Log
9
- include SharedFields::Exception
10
- include SharedFields::Meta
11
-
12
- def initialize(data)
13
- super('Rails Application', data)
14
- end
15
-
16
- def summary_fields
17
- super.concat(
18
- exception_fields,
19
- meta_fields
20
- )
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module SystemLogs
6
- module LogTypes
7
- module Rails
8
- class ExceptionLog < Log
9
- include SharedFields::Exception
10
-
11
- def initialize(data)
12
- super('Rails Exceptions', data)
13
- end
14
-
15
- def summary_fields
16
- super.concat(exception_fields)
17
- end
18
- end
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module SystemLogs
6
- module LogTypes
7
- module Rails
8
- class GraphqlLog < Log
9
- include SharedFields::Meta
10
-
11
- def initialize(data)
12
- super('Rails GraphQL', data)
13
- end
14
-
15
- def summary_fields
16
- super.concat(
17
- [
18
- :operation_name,
19
- :query_string,
20
- :variables
21
- ],
22
- meta_fields
23
- )
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end
30
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module SystemLogs
6
- module SharedFields
7
- module Meta
8
- def meta_fields
9
- [
10
- :meta_user,
11
- :meta_project,
12
- :meta_caller_id
13
- ]
14
- end
15
- end
16
-
17
- module Exception
18
- def exception_fields
19
- [
20
- :exception_class,
21
- :exception_message,
22
- :exception_backtrace
23
- ]
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module SystemLogs
6
- class SystemLogsFormatter
7
- NUM_OF_LOG_SECTIONS = 4
8
-
9
- def initialize(base_paths, correlation_id)
10
- @base_paths = base_paths
11
- @correlation_id = correlation_id
12
- end
13
-
14
- def system_logs_summary_markdown
15
- log_sections = Array.new(NUM_OF_LOG_SECTIONS) { [] }
16
-
17
- @base_paths.each do |base_path|
18
- all_logs = [
19
- Finders::Rails::ApiLogFinder.new(base_path).find(@correlation_id),
20
- Finders::Rails::ExceptionLogFinder.new(base_path).find(@correlation_id),
21
- Finders::Rails::ApplicationLogFinder.new(base_path).find(@correlation_id),
22
- Finders::Rails::GraphqlLogFinder.new(base_path).find(@correlation_id)
23
- ]
24
-
25
- create_log_summary_sections!(all_logs, log_sections)
26
- end
27
-
28
- log_sections.prepend('### System Logs') unless log_sections.all?(&:empty?)
29
- log_sections.join("\n").rstrip
30
- end
31
-
32
- private
33
-
34
- def create_log_summary_sections!(all_logs, sections)
35
- sections.zip(all_logs) do |section, logs|
36
- unless logs.empty?
37
- section_title = "\n#### #{logs.first.name}"
38
- section.append(section_title) unless section.include?(section_title)
39
- section.append(create_log_summaries(logs))
40
- end
41
- end
42
- end
43
-
44
- def create_log_summaries(logs)
45
- section = []
46
-
47
- logs.each do |log|
48
- log_summary = <<~MARKDOWN.chomp
49
- <details><summary>Click to expand</summary>
50
-
51
- ```json
52
- #{JSON.pretty_generate(log.summary)}
53
- ```
54
- </details>
55
- MARKDOWN
56
-
57
- section.append(log_summary)
58
- end
59
-
60
- section.join("\n\n")
61
- end
62
- end
63
- end
64
- end
65
- end