rspec-mergify 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9719211e5ed0a1fa3ac6f1f642ae5076bcd3aa0d3ad1dd432d7bc5740178f7c2
4
- data.tar.gz: 6a2da476d401beac81bafcc30473e1a5daf792ec77faf9d8b8871846ff4994f9
3
+ metadata.gz: 379b00755c80dfcba26213f7747fd42f2739c39f2cc450e36123e1703b9312f4
4
+ data.tar.gz: 979aaf645e9f2ad882dd00f900a5168be6f5c3ff041360e79698860e7be3790d
5
5
  SHA512:
6
- metadata.gz: 778948de1e1f38ac758cfada2e563bbebf9623dead6ec0fbbd89f26a52ec100cbd7ad7c1e678c04025510101146e71df1424f2be447e6efc3829dfa8f2aca8b1
7
- data.tar.gz: 747f74f4ede1ede9e3186b53c98497eb9e12280e2e38b29f3ee56a1655e8e9c98e77a6e7f51dc169706c3793b830c20f9a51c390f38c07e389c0e28e883da919
6
+ metadata.gz: dc0c1dae123ed41a0ca6290ed79d4bcd82ec79989f8d8cf3e232e3e505592bd791d3387435d99721f5c42218839990e609d4d595ba4317e544e3b65255ed14c8
7
+ data.tar.gz: 1827d2eefe6a13d8efb9b6e10cc99da36e6e64b842cfee830cc872d687caabb72881b61cdffb496d74e6e8cf91ccfaf44f7bee866f31858fafc8998e23488016
data/README.md CHANGED
@@ -35,6 +35,8 @@ The plugin activates automatically when running in CI (detected via the `CI` env
35
35
  | `MERGIFY_TRACEPARENT` | W3C distributed trace context | — |
36
36
  | `MERGIFY_TEST_JOB_NAME` | Mergify test job name | — |
37
37
 
38
+ For detailed documentation, see the [official guide](https://docs.mergify.com/ci-insights/test-frameworks/rspec/).
39
+
38
40
  ## Development
39
41
 
40
42
  ### Prerequisites
@@ -8,6 +8,7 @@ require_relative 'resources/ci'
8
8
  require_relative 'resources/git'
9
9
  require_relative 'resources/github_actions'
10
10
  require_relative 'resources/jenkins'
11
+ require_relative 'resources/buildkite'
11
12
  require_relative 'resources/mergify'
12
13
  require_relative 'resources/rspec'
13
14
 
@@ -102,6 +103,7 @@ module Mergify
102
103
  Resources::Git.detect,
103
104
  Resources::GitHubActions.detect,
104
105
  Resources::Jenkins.detect,
106
+ Resources::Buildkite.detect,
105
107
  Resources::Mergify.detect,
106
108
  Resources::RSpec.detect
107
109
  ]
@@ -54,11 +54,52 @@ module Mergify
54
54
 
55
55
  private
56
56
 
57
- # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
58
57
  def fetch_quarantined_tests(api_url, token, owner, repo, branch_name)
59
58
  uri = URI("#{api_url}/v1/ci/#{owner}/repositories/#{repo}/quarantines")
60
- uri.query = URI.encode_www_form(branch: branch_name)
59
+ uri.query = URI.encode_www_form(branch: branch_name, per_page: 100)
60
+ collected = walk_paginated_quarantines(uri, token)
61
+ @quarantined_tests = collected if collected
62
+ rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED, SocketError => e
63
+ @init_error_msg = "Failed to connect to Mergify API: #{e.message}"
64
+ rescue JSON::ParserError => e
65
+ @init_error_msg = "Mergify API returned a malformed quarantine list: #{e.message}"
66
+ end
61
67
 
68
+ # Follows the RFC 5988 `next` link until exhausted. Returns the full list
69
+ # on success, or `nil` when the run was aborted (subscription missing or
70
+ # an error already recorded in @init_error_msg).
71
+ # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
72
+ def walk_paginated_quarantines(uri, token)
73
+ collected = []
74
+ # Guard against a server returning a `next` link that loops back to a
75
+ # URL we have already fetched.
76
+ seen = Set.new
77
+ while uri
78
+ if seen.include?(uri.to_s)
79
+ @init_error_msg = 'Mergify API returned a cyclic `next` link, aborting.'
80
+ return nil
81
+ end
82
+ seen.add(uri.to_s)
83
+
84
+ response = perform_request(uri, token)
85
+ case response.code.to_i
86
+ when 200
87
+ data = JSON.parse(response.body)
88
+ collected.concat(data.fetch('quarantined_tests', []).map { |t| t['test_name'] })
89
+ next_url = parse_next_link(response['Link'])
90
+ uri = next_url ? URI(next_url) : nil
91
+ when 402
92
+ return nil
93
+ else
94
+ @init_error_msg = "Mergify API returned HTTP #{response.code}"
95
+ return nil
96
+ end
97
+ end
98
+ collected
99
+ end
100
+ # rubocop:enable Metrics/MethodLength,Metrics/AbcSize
101
+
102
+ def perform_request(uri, token)
62
103
  http = Net::HTTP.new(uri.host, uri.port)
63
104
  http.use_ssl = uri.scheme == 'https'
64
105
  http.open_timeout = 10
@@ -66,23 +107,27 @@ module Mergify
66
107
 
67
108
  request = Net::HTTP::Get.new(uri)
68
109
  request['Authorization'] = "Bearer #{token}"
110
+ http.request(request)
111
+ end
69
112
 
70
- response = http.request(request)
71
- handle_response(response)
72
- rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED, SocketError => e
73
- @init_error_msg = "Failed to connect to Mergify API: #{e.message}"
113
+ # Parses RFC 8288 Link headers tolerantly: accepts both quoted
114
+ # (`rel="next"`) and token (`rel=next`) forms, and matches when `next`
115
+ # is one of several space-separated rel-types (`rel="next prev"`).
116
+ def parse_next_link(link_header)
117
+ return nil if link_header.nil? || link_header.empty?
118
+
119
+ link_header.split(',').each do |part|
120
+ match = part.strip.match(/\A<([^>]+)>\s*;\s*(.+)\z/)
121
+ next unless match && next_rel?(match[2])
122
+
123
+ return match[1]
124
+ end
125
+ nil
74
126
  end
75
- # rubocop:enable Metrics/MethodLength,Metrics/AbcSize
76
127
 
77
- def handle_response(response)
78
- case response.code.to_i
79
- when 200
80
- data = JSON.parse(response.body)
81
- @quarantined_tests = data.fetch('quarantined_tests', []).map { |t| t['test_name'] }
82
- when 402
83
- # No subscription — silently skip
84
- else
85
- @init_error_msg = "Mergify API returned HTTP #{response.code}"
128
+ def next_rel?(params)
129
+ params.scan(/rel\s*=\s*(?:"([^"]+)"|([^\s;,]+))/i).any? do |quoted, token|
130
+ (quoted || token).split.include?('next')
86
131
  end
87
132
  end
88
133
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'opentelemetry-sdk'
4
+ require_relative '../utils'
5
+ require_relative 'git'
6
+
7
+ module Mergify
8
+ module RSpec
9
+ module Resources
10
+ # Detects OpenTelemetry Resource attributes for Buildkite.
11
+ module Buildkite
12
+ module_function
13
+
14
+ BUILDKITE_MAPPING = {
15
+ 'cicd.pipeline.name' => [:to_s, 'BUILDKITE_PIPELINE_SLUG'],
16
+ 'cicd.pipeline.task.name' => [
17
+ :to_s,
18
+ lambda {
19
+ label = ENV.fetch('BUILDKITE_LABEL', nil)
20
+ label && !label.empty? ? label : ENV.fetch('BUILDKITE_STEP_KEY', nil)
21
+ }
22
+ ],
23
+ 'cicd.pipeline.run.id' => [:to_s, 'BUILDKITE_BUILD_ID'],
24
+ 'cicd.pipeline.run.url' => [:to_s, 'BUILDKITE_BUILD_URL'],
25
+ 'cicd.pipeline.run.attempt' => [
26
+ :to_i,
27
+ -> { ENV.fetch('BUILDKITE_RETRY_COUNT', '0').to_i + 1 }
28
+ ],
29
+ 'cicd.pipeline.runner.name' => [:to_s, 'BUILDKITE_AGENT_NAME'],
30
+ 'vcs.ref.head.name' => [:to_s, 'BUILDKITE_BRANCH'],
31
+ 'vcs.ref.base.name' => [:to_s, 'BUILDKITE_PULL_REQUEST_BASE_BRANCH'],
32
+ 'vcs.ref.head.revision' => [:to_s, 'BUILDKITE_COMMIT'],
33
+ 'vcs.repository.url.full' => [:to_s, 'BUILDKITE_REPO'],
34
+ 'vcs.repository.name' => [
35
+ :to_s,
36
+ lambda {
37
+ url = ENV.fetch('BUILDKITE_REPO', nil)
38
+ Utils.repository_name_from_url(url) if url
39
+ }
40
+ ]
41
+ }.freeze
42
+
43
+ def detect
44
+ return OpenTelemetry::SDK::Resources::Resource.create({}) if Utils.ci_provider != :buildkite
45
+
46
+ git_attrs = Utils.get_attributes(Git::GIT_MAPPING)
47
+ buildkite_attrs = Utils.get_attributes(BUILDKITE_MAPPING)
48
+ merged = git_attrs.merge(buildkite_attrs)
49
+ OpenTelemetry::SDK::Resources::Resource.create(merged)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -15,6 +15,7 @@ module Mergify
15
15
  'GITHUB_ACTIONS' => :github_actions,
16
16
  'CIRCLECI' => :circleci,
17
17
  'JENKINS_URL' => :jenkins,
18
+ 'BUILDKITE' => :buildkite,
18
19
  '_RSPEC_MERGIFY_TEST' => :rspec_mergify_suite
19
20
  }.freeze
20
21
 
@@ -101,12 +102,7 @@ module Mergify
101
102
  # Attributes whose env var is unset or whose callable returns nil are omitted.
102
103
  def get_attributes(mapping)
103
104
  mapping.each_with_object({}) do |(attr, (cast, env_or_callable)), result|
104
- value =
105
- if env_or_callable.respond_to?(:call)
106
- env_or_callable.call
107
- else
108
- ENV.fetch(env_or_callable, nil)
109
- end
105
+ value = env_or_callable.respond_to?(:call) ? env_or_callable.call : ENV.fetch(env_or_callable, nil)
110
106
 
111
107
  next if value.nil?
112
108
  next if value.respond_to?(:empty?) && value.empty?
@@ -130,6 +126,9 @@ module Mergify
130
126
  when :circleci
131
127
  url = ENV.fetch('CIRCLE_REPOSITORY_URL', nil)
132
128
  return repository_name_from_url(url) if url
129
+ when :buildkite
130
+ url = ENV.fetch('BUILDKITE_REPO', nil)
131
+ return repository_name_from_url(url) if url
133
132
  when :rspec_mergify_suite
134
133
  return 'Mergifyio/rspec-mergify'
135
134
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-mergify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mergify
@@ -67,6 +67,7 @@ files:
67
67
  - lib/mergify/rspec/flaky_detection.rb
68
68
  - lib/mergify/rspec/formatter.rb
69
69
  - lib/mergify/rspec/quarantine.rb
70
+ - lib/mergify/rspec/resources/buildkite.rb
70
71
  - lib/mergify/rspec/resources/ci.rb
71
72
  - lib/mergify/rspec/resources/git.rb
72
73
  - lib/mergify/rspec/resources/github_actions.rb