buildkite-test_collector 2.6.0 → 2.10.0

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: 0e8ff4ceff4d825291a8878bf19a912362d45c36d6750c5e14628f446cec0bbd
4
- data.tar.gz: 215441296a3c91547b0c43b062893239fbe5bae5ca2e1a80cca7b2ec65277e77
3
+ metadata.gz: 497ced90aac96227b8e4d2fc9ad1685eb4f554742b597568efa7e0f8bb1a0879
4
+ data.tar.gz: 709f96eaa2af7732b73c2838862bb98f350e9f8cda5aa7e209b00e96e00ab127
5
5
  SHA512:
6
- metadata.gz: d31e59e76f2dc92ef5951f9d97663f14cb84d9ba1256c8586931146151222c7ce3769a8913517910312d0a75a7cafb3488f0b9690257c4fc6686fbf37cbed9ee
7
- data.tar.gz: 995fc9e592b74a41385058c4eac868e9639f6ef9bfb2ba9068cd156fd6ab5d90bf40aca86a9ab9c8560cf7c9ba80c4f172747a948e4e219976c680d035eae0af
6
+ metadata.gz: d559f0b297b7402a3f57203ea6226ee861b81b72682896c8a49d13ecaa036ca6ba6b3cf6b22e5693d0de8133500b4dd0b84a10db171d17c93237a0d2873e2704
7
+ data.tar.gz: e6d23fe2be97f26f4c1f753585340220d46ea926ed490878a4b02627b3488da57a859a1e3948796e84055dce99d01380e60ca77e96c33970c03ba38683259b7c
@@ -0,0 +1,18 @@
1
+ agents:
2
+ queue: hosted
3
+
4
+ steps:
5
+ - block: "OK to release?"
6
+
7
+ - command: ".buildkite/steps/release-gem"
8
+ label: ":rubygems:"
9
+ if: build.tag != null
10
+ plugins:
11
+ - rubygems-oidc#v0.2.0:
12
+ role: "rg_oidc_akr_fy1x4px4yjwd1rdhkkda"
13
+ - docker#v5.12.0:
14
+ image: "ruby:3.4"
15
+ environment:
16
+ - GEM_HOST_API_KEY
17
+ - BUILDKITE_TAG
18
+
@@ -0,0 +1,56 @@
1
+ #!/bin/bash
2
+
3
+ set -euo pipefail
4
+
5
+ if [ -z "${GEM_HOST_API_KEY}" ]; then
6
+ echo "GEM_HOST_API_KEY environment variable not found"
7
+ exit 1
8
+ fi
9
+
10
+ if [ -z "${BUILDKITE_TAG}" ]; then
11
+ echo "BUILDKITE_TAG environment variable not found"
12
+ exit 1
13
+ fi
14
+
15
+ cd $(dirname $0)/../..
16
+
17
+ echo "--- Inspecting tag and version"
18
+
19
+ echo "BUILDKITE_TAG: ${BUILDKITE_TAG}"
20
+
21
+ if [[ ! "${BUILDKITE_TAG}" == v* ]]; then
22
+ echo "We will only try to publish to rubygems when the tag starts with 'v'"
23
+ exit 1
24
+ fi
25
+
26
+ VERSION=$(echo "${BUILDKITE_TAG}" | sed "s/^v//")
27
+ GEM_FILENAME="buildkite-test_collector-${VERSION}.gem"
28
+
29
+ echo "Version to release: ${VERSION}"
30
+
31
+ echo "--- Building gem"
32
+
33
+ gem build buildkite-test_collector.gemspec
34
+
35
+ if [ ! -f "${GEM_FILENAME}" ]; then
36
+ echo
37
+ echo "ERROR: Expected compiled gem to be '${GEM_FILENAME}' but file not found"
38
+ echo "Does the gemspec specify version '${BUILDKITE_TAG}'?"
39
+ echo
40
+ echo "Gem files found:"
41
+ echo
42
+ ls *.gem
43
+ echo
44
+ exit 1
45
+ fi
46
+
47
+ echo "--- Check if version already exists on rubygems.org"
48
+
49
+ if [ $(curl -s -o /dev/null -w "%{http_code}" https://rubygems.org/api/v2/rubygems/buildkite-test_collector/versions/${VERSION}.json) == "200" ]; then
50
+ echo "Gem version ${VERSION} already found on rubygems, skipping release"
51
+ exit 1
52
+ fi
53
+
54
+ echo "--- publish gem"
55
+
56
+ gem push "${GEM_FILENAME}"
data/.rspec CHANGED
@@ -1,3 +1,3 @@
1
1
  --format documentation
2
2
  --color
3
- --require spec_helper
3
+ --require ./spec/spec_helper.rb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v2.10.0
4
+
5
+ * Add support for Cucumber
6
+
7
+ ## v2.9.0
8
+
9
+ * `Buildkite::TestCollector.tag_execution(key, value)` by @pda in https://github.com/buildkite/test-collector-ruby/pull/240
10
+
11
+ **Full Changelog**: https://github.com/buildkite/test-collector-ruby/compare/v2.8.0...v2.9.0
12
+
13
+ ## v2.8.0
14
+
15
+ * Buildkite::TestCollector.tags: specify tags for all executions by @pda in https://github.com/buildkite/test-collector-ruby/pull/235
16
+ * Add Ruby 3.4 to Buildkite build matrix by @gchan in https://github.com/buildkite/test-collector-ruby/pull/236
17
+
18
+ **Full Changelog**: https://github.com/buildkite/test-collector-ruby/compare/v2.7.2...v2.8.0
19
+
20
+ ## v2.7.2
21
+
22
+ - Fixes RSpec library hook bug introduced in v2.7.1 #230 - @malclocke
23
+
24
+ ## v2.7.1
25
+
26
+ - Removes test links from job logs
27
+ - Rename Test Analytics to Test Engine
28
+
29
+ ## v2.7.0
30
+
31
+ Features:
32
+
33
+ - Add configurable span filters #220 - @catkins
34
+
35
+ Fixes:
36
+
37
+ - Correctly report HTTP error during upload #223 - @zhming0
38
+
39
+ ## v2.6.1
40
+
41
+ - Fix missing failed examples if rspec hooks fail #221 - @zhming0
42
+
3
43
  ## v2.6.0
4
44
 
5
45
  - Trace spans can be filtered by minimum duration #215 - @pda
data/CODEOWNERS CHANGED
@@ -1 +1 @@
1
- * @buildkite/test-analytics
1
+ * @buildkite/test-engine
data/Gemfile.lock CHANGED
@@ -1,37 +1,84 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- buildkite-test_collector (2.6.0)
4
+ buildkite-test_collector (2.10.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- activesupport (7.0.7.2)
10
- concurrent-ruby (~> 1.0, >= 1.0.2)
9
+ activesupport (8.0.1)
10
+ base64
11
+ benchmark (>= 0.3)
12
+ bigdecimal
13
+ concurrent-ruby (~> 1.0, >= 1.3.1)
14
+ connection_pool (>= 2.2.5)
15
+ drb
11
16
  i18n (>= 1.6, < 2)
17
+ logger (>= 1.4.2)
12
18
  minitest (>= 5.1)
13
- tzinfo (~> 2.0)
14
- concurrent-ruby (1.2.2)
15
- diff-lcs (1.4.4)
16
- i18n (1.14.1)
19
+ securerandom (>= 0.3)
20
+ tzinfo (~> 2.0, >= 2.0.5)
21
+ uri (>= 0.13.1)
22
+ base64 (0.2.0)
23
+ benchmark (0.4.0)
24
+ bigdecimal (3.1.9)
25
+ builder (3.3.0)
26
+ concurrent-ruby (1.3.5)
27
+ connection_pool (2.5.0)
28
+ cucumber (9.2.1)
29
+ builder (~> 3.2)
30
+ cucumber-ci-environment (> 9, < 11)
31
+ cucumber-core (> 13, < 14)
32
+ cucumber-cucumber-expressions (~> 17.0)
33
+ cucumber-gherkin (> 24, < 28)
34
+ cucumber-html-formatter (> 20.3, < 22)
35
+ cucumber-messages (> 19, < 25)
36
+ diff-lcs (~> 1.5)
37
+ mini_mime (~> 1.1)
38
+ multi_test (~> 1.1)
39
+ sys-uname (~> 1.2)
40
+ cucumber-ci-environment (10.0.1)
41
+ cucumber-core (13.0.3)
42
+ cucumber-gherkin (>= 27, < 28)
43
+ cucumber-messages (>= 20, < 23)
44
+ cucumber-tag-expressions (> 5, < 7)
45
+ cucumber-cucumber-expressions (17.1.0)
46
+ bigdecimal
47
+ cucumber-gherkin (27.0.0)
48
+ cucumber-messages (>= 19.1.4, < 23)
49
+ cucumber-html-formatter (21.9.0)
50
+ cucumber-messages (> 19, < 28)
51
+ cucumber-messages (22.0.0)
52
+ cucumber-tag-expressions (6.1.2)
53
+ diff-lcs (1.6.2)
54
+ drb (2.2.1)
55
+ ffi (1.17.2)
56
+ i18n (1.14.7)
17
57
  concurrent-ruby (~> 1.0)
18
- minitest (5.19.0)
19
- rake (13.0.6)
20
- rspec (3.10.0)
21
- rspec-core (~> 3.10.0)
22
- rspec-expectations (~> 3.10.0)
23
- rspec-mocks (~> 3.10.0)
24
- rspec-core (3.10.1)
25
- rspec-support (~> 3.10.0)
26
- rspec-expectations (3.10.1)
58
+ logger (1.7.0)
59
+ mini_mime (1.1.5)
60
+ minitest (5.25.4)
61
+ multi_test (1.1.0)
62
+ rake (13.2.1)
63
+ rspec (3.13.0)
64
+ rspec-core (~> 3.13.0)
65
+ rspec-expectations (~> 3.13.0)
66
+ rspec-mocks (~> 3.13.0)
67
+ rspec-core (3.13.2)
68
+ rspec-support (~> 3.13.0)
69
+ rspec-expectations (3.13.3)
27
70
  diff-lcs (>= 1.2.0, < 2.0)
28
- rspec-support (~> 3.10.0)
29
- rspec-mocks (3.10.2)
71
+ rspec-support (~> 3.13.0)
72
+ rspec-mocks (3.13.2)
30
73
  diff-lcs (>= 1.2.0, < 2.0)
31
- rspec-support (~> 3.10.0)
32
- rspec-support (3.10.3)
74
+ rspec-support (~> 3.13.0)
75
+ rspec-support (3.13.2)
76
+ securerandom (0.4.1)
77
+ sys-uname (1.3.1)
78
+ ffi (~> 1.1)
33
79
  tzinfo (2.0.6)
34
80
  concurrent-ruby (~> 1.0)
81
+ uri (1.0.2)
35
82
 
36
83
  PLATFORMS
37
84
  ruby
@@ -39,6 +86,7 @@ PLATFORMS
39
86
  DEPENDENCIES
40
87
  activesupport (>= 4.2)
41
88
  buildkite-test_collector!
89
+ cucumber (~> 9.0)
42
90
  rake (~> 13.0)
43
91
  rspec (~> 3.0)
44
92
  rspec-core (~> 3.10)
data/README.md CHANGED
@@ -3,9 +3,9 @@
3
3
  **DEPRECATION NOTICE**
4
4
  Versions prior to 2.1.x are unsupported and will not work after mid-2023. Please upgrade to the latest version.
5
5
 
6
- Official [Buildkite Test Analytics](https://buildkite.com/test-analytics) collectors for Ruby test frameworks ✨
6
+ Official [Buildkite Test Engine](https://buildkite.com/platform/test-engine) collectors for Ruby test frameworks ✨
7
7
 
8
- āš’ **Supported test frameworks:** RSpec, Minitest, and [more coming soon](https://github.com/buildkite/test-collector-ruby/issues?q=is%3Aissue+is%3Aopen+label%3A%22test+frameworks%22).
8
+ āš’ **Supported test frameworks:** RSpec, Minitest, Cucumber, and [more coming soon](https://github.com/buildkite/test-collector-ruby/issues?q=is%3Aissue+is%3Aopen+label%3A%22test+frameworks%22).
9
9
 
10
10
  šŸ“¦ **Supported CI systems:** Buildkite, GitHub Actions, CircleCI, Codeship, and others via the `BUILDKITE_ANALYTICS_*` environment variables.
11
11
 
@@ -63,47 +63,30 @@ Run your tests locally:
63
63
  BUILDKITE_ANALYTICS_TOKEN=xyz rake
64
64
  ```
65
65
 
66
- ### Step 3
67
-
68
- Add the `BUILDKITE_ANALYTICS_TOKEN` secret to your CI, push your changes to a branch, and open a pull request šŸŽ‰
66
+ #### Cucumber
69
67
 
70
- ```bash
71
- git checkout -b add-buildkite-test-analytics
72
- git commit -am "Add Buildkite Test Analytics"
73
- git push origin add-buildkite-test-analytics
74
- ```
75
-
76
- ### VCR
77
- If your test suites use [VCR](https://github.com/vcr/vcr) to stub network requests, you'll need to modify the config to allow actual network requests to Test Analytics.
68
+ Add the following code to your Cucumber setup file:
78
69
 
79
- ```
80
- VCR.configure do |c|
81
- c.ignore_hosts "analytics-api.buildkite.com"
82
- end
70
+ ```ruby
71
+ # features/support/env.rb
72
+ require 'buildkite/test_collector'
73
+ Buildkite::TestCollector.configure(hook: :cucumber)
83
74
  ```
84
75
 
85
- ## šŸ—Øļø Annotations
86
-
87
- This gem allows adding custom annotations to the span data sent to Buildkite using the [.annotate](https://github.com/buildkite/test-collector-ruby/blob/d9fe11341e4aa470e766febee38124b644572360/lib/buildkite/test_collector.rb#L64) method. For example:
76
+ Run your tests locally:
88
77
 
89
- ```ruby
90
- Buildkite::TestCollector.annotate("User logged in successfully")
78
+ ```shell
79
+ BUILDKITE_ANALYTICS_TOKEN=xyz cucumber
91
80
  ```
92
81
 
93
- This is particularly useful for tests that generate a lot of span data such as system/feature tests.
94
-
95
- ## šŸ·ļø Tagging duplicate test executions with a prefix/suffix
82
+ ### Step 3
96
83
 
97
- For builds that execute the same test multiple times - such as when running the same test suite against multiple versions of ruby/rails - it's possible to tag each test execution with a prefix/suffix. This prefix/suffix is displayed for each execution on the test show page to differentiate the build environment. The prefix/suffix is specified using these environment variables:
84
+ Add the `BUILDKITE_ANALYTICS_TOKEN` secret to your CI, push your changes to a branch, and open a pull request šŸŽ‰
98
85
 
99
- ```
100
- BUILDKITE_ANALYTICS_EXECUTION_NAME_PREFIX
101
- BUILDKITE_ANALYTICS_EXECUTION_NAME_SUFFIX
102
- ```
103
86
 
104
- ## šŸ”œ Roadmap
87
+ ## More information
105
88
 
106
- See the [GitHub 'enhancement' issues](https://github.com/buildkite/test-collector-ruby/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) for planned features. Pull requests are always welcome, and we’ll give you feedback and guidance if you choose to contribute šŸ’š
89
+ For more use cases such as custom tags, annotations, and span tracking, please visit our [official Ruby collector documentation](https://buildkite.com/docs/test-engine/ruby-collectors) for details.
107
90
 
108
91
  ## āš’ Developing
109
92
 
@@ -119,7 +102,7 @@ And run the tests:
119
102
  bundle exec rspec
120
103
  ```
121
104
 
122
- Useful resources for developing collectors include the [Buildkite Test Analytics docs](https://buildkite.com/docs/test-analytics).
105
+ Useful resources for developing collectors include the [Buildkite Test Engine docs](https://buildkite.com/docs/test-engine).
123
106
 
124
107
  See [DESIGN.md](DESIGN.md) for an overview of the design of this gem.
125
108
 
@@ -131,14 +114,15 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/buildk
131
114
 
132
115
  1. Bump the version in `version.rb` and run `bundle` to update the `Gemfile.lock`.
133
116
  1. Update the CHANGELOG.md with your new version and a description of your changes.
134
- 1. Git tag your changes and push
135
- ```
136
- git tag v.x.x.x
137
- git push --tags
138
- ```
117
+
139
118
  Once your PR is merged to `main`:
140
119
 
141
- 1. Run `rake release` from `main`.
120
+ 1. Git tag the merge commit and push
121
+ ```
122
+ git tag vX.X.X
123
+ git push origin vX.X.X
124
+ ```
125
+ 1. Visit the [release pipeline](https://buildkite.com/buildkite/test-collector-ruby-release) to unblock it and confirm the new version is pushed to rubygems.org
142
126
  1. Create a [new release in github](https://github.com/buildkite/test-collector-ruby/releases).
143
127
 
144
128
  ## šŸ“œ MIT License
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Buildkite"]
9
9
  spec.email = ["support+analytics@buildkite.com"]
10
10
 
11
- spec.summary = "Track test executions and report to Buildkite Test Analytics"
11
+ spec.summary = "Track test executions and report to Buildkite Test Engine"
12
12
  spec.homepage = "https://github.com/buildkite/test-collector-ruby"
13
13
  spec.license = "MIT"
14
14
 
@@ -27,4 +27,9 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency "activesupport", ">= 4.2"
28
28
  spec.add_development_dependency "rspec-core", '~> 3.10'
29
29
  spec.add_development_dependency "rspec-expectations", '~> 3.10'
30
+
31
+ # When running the legacy CI builds against versions of Ruby pre 2.7 we cannot include cucumber 9 as it's not supported.
32
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7')
33
+ spec.add_development_dependency "cucumber", '~> 9.0'
34
+ end
30
35
  end
data/buildkite.yaml CHANGED
@@ -1,22 +1,32 @@
1
- steps:
2
- - label: ":rspec: Tests :ruby: 3.3-rc"
3
- command:
4
- - "bundle"
5
- - "bundle exec rake"
6
- plugins:
7
- - docker#v3.7.0:
8
- image: "445615400570.dkr.ecr.us-east-1.amazonaws.com/ecr-public/docker/library/ruby:3.3-rc"
9
- soft_fail: true
1
+ agents:
2
+ queue: hosted
10
3
 
4
+ steps:
11
5
  - label: ":rspec: Tests :ruby: {{matrix}}"
12
6
  command:
7
+ - "gem install bundler:2.3.25"
13
8
  - "bundle"
14
9
  - "bundle exec rake"
10
+ - "bundle exec cucumber"
15
11
  plugins:
12
+ - aws-assume-role-with-web-identity#v1.2.0:
13
+ role-arn: "${OIDC_ASSUME_ROLE_ARN:-arn:aws:iam::445615400570:role/pipeline-buildkite-test-collector-ruby-build}"
14
+ session-tags:
15
+ - organization_slug
16
+ - organization_id
17
+ - pipeline_slug
18
+ - build_branch
19
+
20
+ - ecr#v2.9.0:
21
+ login: true
22
+ account_ids: "public.ecr.aws"
23
+ region: "us-east-1"
16
24
  - docker#v3.7.0:
17
- image: "445615400570.dkr.ecr.us-east-1.amazonaws.com/ecr-public/docker/library/ruby:{{matrix}}"
25
+ image: "public.ecr.aws/docker/library/ruby:{{matrix}}"
18
26
  matrix:
19
27
  - "latest"
28
+ - "3.4"
29
+ - "3.3"
20
30
  - "3.2"
21
31
  - "3.1"
22
32
  - "3.0"
@@ -30,6 +40,17 @@ steps:
30
40
  - "bundle"
31
41
  - "bundle exec rake"
32
42
  plugins:
43
+ - aws-assume-role-with-web-identity#v1.2.0:
44
+ role-arn: "${OIDC_ASSUME_ROLE_ARN:-arn:aws:iam::445615400570:role/pipeline-buildkite-test-collector-ruby-build}"
45
+ session-tags:
46
+ - organization_slug
47
+ - organization_id
48
+ - pipeline_slug
49
+ - build_branch
50
+ - ecr#v2.9.0:
51
+ login: true
52
+ account_ids: "public.ecr.aws"
53
+ region: "us-east-1"
33
54
  - docker#v3.7.0:
34
55
  # Images for older Ruby versions aren't available on AWS ECR
35
56
  # so fall back to Docker Hub
data/cucumber.yml ADDED
@@ -0,0 +1 @@
1
+ default: --publish-quiet
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Buildkite::TestCollector::CucumberPlugin
4
+ class Trace
5
+ attr_accessor :scenario, :failure_reason, :failure_expanded
6
+ attr_reader :history, :tags
7
+
8
+ FILE_PATH_REGEX = /^(.*?\.(rb|feature))/
9
+
10
+ def initialize(scenario, history:, failure_reason: nil, failure_expanded: [], tags: nil)
11
+ @scenario = scenario
12
+ @history = history
13
+ @failure_reason = failure_reason
14
+ @failure_expanded = failure_expanded
15
+ @tags = tags
16
+ end
17
+
18
+ def result
19
+ if scenario.passed?
20
+ 'passed'
21
+ elsif scenario.failed?
22
+ 'failed'
23
+ else
24
+ 'skipped'
25
+ end
26
+ end
27
+
28
+ def as_hash
29
+ parser = Gherkin::Parser.new
30
+ document = parser.parse(File.read(file_name))
31
+ feature_name = document.feature.name
32
+
33
+ strip_invalid_utf8_chars(
34
+ scope: feature_name,
35
+ name: scenario.name,
36
+ location: scenario.location&.to_s,
37
+ file_name: file_name,
38
+ result: result,
39
+ failure_reason: failure_reason,
40
+ failure_expanded: failure_expanded,
41
+ history: history,
42
+ tags: tags,
43
+ ).select { |_, v| !v.nil? }
44
+ end
45
+
46
+ private
47
+
48
+ def file_name
49
+ @file_name ||= scenario.location&.to_s[FILE_PATH_REGEX]
50
+ end
51
+
52
+ def strip_invalid_utf8_chars(object)
53
+ case object
54
+ when Hash
55
+ object.transform_values { |v| strip_invalid_utf8_chars(v) }
56
+ when Array
57
+ object.map { |v| strip_invalid_utf8_chars(v) }
58
+ when String
59
+ object.encode('UTF-8', invalid: :replace, undef: :replace)
60
+ else
61
+ object
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,4 +1,5 @@
1
1
  module Buildkite::TestCollector
2
2
  class Error < StandardError; end
3
3
  class TimeoutError < ::Timeout::Error; end
4
+ class UnsupportedFrameworkError < Error; end
4
5
  end
@@ -4,19 +4,18 @@ require "net/http"
4
4
 
5
5
  module Buildkite::TestCollector
6
6
  class HTTPClient
7
- attr :authorization_header
8
- def initialize(url)
7
+ def initialize(url:, api_token:)
9
8
  @url = url
10
- @authorization_header = "Token token=\"#{Buildkite::TestCollector.api_token}\""
9
+ @api_token = api_token
11
10
  end
12
11
 
13
- def post_json(data)
14
- contact_uri = URI.parse(url)
12
+ def post_upload(data:, run_env:, tags:)
13
+ endpoint_uri = URI.parse(url)
15
14
 
16
- http = Net::HTTP.new(contact_uri.host, contact_uri.port)
17
- http.use_ssl = contact_uri.scheme == "https"
15
+ http = Net::HTTP.new(endpoint_uri.host, endpoint_uri.port)
16
+ http.use_ssl = endpoint_uri.scheme == "https"
18
17
 
19
- contact = Net::HTTP::Post.new(contact_uri.path, {
18
+ request = Net::HTTP::Post.new(endpoint_uri.path, {
20
19
  "Authorization" => authorization_header,
21
20
  "Content-Type" => "application/json",
22
21
  "Content-Encoding" => "gzip",
@@ -25,7 +24,8 @@ module Buildkite::TestCollector
25
24
  data_set = data.map(&:as_hash)
26
25
 
27
26
  body = {
28
- run_env: Buildkite::TestCollector::CI.env,
27
+ run_env: run_env,
28
+ tags: tags,
29
29
  format: "json",
30
30
  data: data_set
31
31
  }.to_json
@@ -36,27 +36,37 @@ module Buildkite::TestCollector
36
36
  writer.write(body)
37
37
  writer.close
38
38
 
39
- contact.body = compressed_body.string
39
+ request.body = compressed_body.string
40
40
 
41
- http.request(contact)
41
+ response = http.request(request)
42
+
43
+ if response.is_a?(Net::HTTPSuccess)
44
+ response
45
+ else
46
+ raise "HTTP Request Failed: #{response.code} #{response.message}"
47
+ end
42
48
  end
43
49
 
44
50
  def metadata
45
- contact_uri = URI.parse("#{url}/metadata")
51
+ endpoint_uri = URI.parse("#{url}/metadata")
46
52
 
47
- http = Net::HTTP.new(contact_uri.host, contact_uri.port)
48
- http.use_ssl = contact_uri.scheme == "https"
53
+ http = Net::HTTP.new(endpoint_uri.host, endpoint_uri.port)
54
+ http.use_ssl = endpoint_uri.scheme == "https"
49
55
 
50
- contact = Net::HTTP::Get.new(contact_uri.path, {
56
+ request = Net::HTTP::Get.new(endpoint_uri.path, {
51
57
  "Authorization" => authorization_header,
52
58
  "Content-Type" => "application/json"
53
59
  })
54
60
 
55
- http.request(contact)
61
+ http.request(request)
56
62
  end
57
63
 
58
64
  private
59
65
 
60
- attr :url
66
+ attr_reader :url
67
+
68
+ def authorization_header
69
+ "Token token=\"#{@api_token}\""
70
+ end
61
71
  end
62
72
  end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cucumber'
4
+
5
+ require_relative '../cucumber_plugin/trace'
6
+
7
+ # Use Buildkite's uploader for Cucumber as well
8
+ Buildkite::TestCollector.uploader = Buildkite::TestCollector::Uploader
9
+
10
+ # -------------------------------------------------
11
+ # Hooks
12
+ # -------------------------------------------------
13
+
14
+ Before do |scenario|
15
+ tracer = Buildkite::TestCollector::Tracer.new(
16
+ min_duration: Buildkite::TestCollector.trace_min_duration,
17
+ )
18
+
19
+ tags = {}
20
+
21
+ # _buildkite prefix reduces chance of collisions in this almost-global (per-fiber) namespace.
22
+ Thread.current[:_buildkite_tracer] = tracer
23
+ Thread.current[:_buildkite_tags] = tags
24
+ end
25
+
26
+ After do |scenario|
27
+ tracer = Thread.current[:_buildkite_tracer]
28
+ tags = Thread.current[:_buildkite_tags]
29
+
30
+ Thread.current[:_buildkite_tracer] = nil
31
+ Thread.current[:_buildkite_tags] = nil
32
+
33
+ if tracer
34
+ tracer.finalize
35
+
36
+ failure_reason = nil
37
+ failure_expanded = []
38
+
39
+ if scenario.failed?
40
+ exception = scenario.exception
41
+ if exception
42
+ failure_reason = exception.message
43
+ failure_expanded << {
44
+ backtrace: exception.backtrace,
45
+ }
46
+ end
47
+ end
48
+
49
+ trace = Buildkite::TestCollector::CucumberPlugin::Trace.new(
50
+ scenario,
51
+ history: tracer.history,
52
+ failure_reason: failure_reason,
53
+ failure_expanded: failure_expanded,
54
+ tags: tags,
55
+ )
56
+
57
+ Buildkite::TestCollector.uploader.traces[scenario.location.to_s] = trace
58
+
59
+ if Buildkite::TestCollector.session
60
+ Buildkite::TestCollector.session.add_example_to_send_queue(scenario.location.to_s)
61
+ end
62
+ end
63
+ end
64
+
65
+ at_exit do
66
+ if Buildkite::TestCollector.session
67
+ Buildkite::TestCollector.session.send_remaining_data
68
+ Buildkite::TestCollector.session.close
69
+ end
70
+ end
71
+
72
+ # Initialise shared tracing & session behaviour once hooks file is loaded.
73
+ Buildkite::TestCollector.enable_tracing!
74
+ Buildkite::TestCollector.session = Buildkite::TestCollector::Session.new
@@ -5,14 +5,12 @@ require "rspec/expectations"
5
5
 
6
6
  require_relative "../rspec_plugin/reporter"
7
7
  require_relative "../rspec_plugin/trace"
8
- require_relative "../test_links_plugin/formatter"
9
8
 
10
9
  Buildkite::TestCollector.uploader = Buildkite::TestCollector::Uploader
11
10
 
12
11
  RSpec.configure do |config|
13
12
  config.before(:suite) do
14
13
  config.add_formatter Buildkite::TestCollector::RSpecPlugin::Reporter
15
- config.add_formatter Buildkite::TestCollector::TestLinksPlugin::Formatter
16
14
  end
17
15
 
18
16
  config.around(:each) do |example|
@@ -20,16 +18,29 @@ RSpec.configure do |config|
20
18
  min_duration: Buildkite::TestCollector.trace_min_duration,
21
19
  )
22
20
 
23
- # The _buildkite prefix here is added as a safeguard against name collisions
24
- # as we are in the main thread
21
+ tags = {}
22
+
23
+ # _buildkite prefix reduces chance of collisions in this almost-global (per-fiber) namespace.
25
24
  Thread.current[:_buildkite_tracer] = tracer
26
- example.run
27
- Thread.current[:_buildkite_tracer] = nil
25
+ Thread.current[:_buildkite_tags] = tags
26
+
27
+ # example.run can raise errors (including from other middleware/hooks) so clean up in `ensure`.
28
+ begin
29
+ example.run
30
+ ensure
31
+ Thread.current[:_buildkite_tracer] = nil
32
+ Thread.current[:_buildkite_tags] = nil
28
33
 
29
- tracer.finalize
34
+ tracer.finalize
30
35
 
31
- trace = Buildkite::TestCollector::RSpecPlugin::Trace.new(example, history: tracer.history)
32
- Buildkite::TestCollector.uploader.traces[example.id] = trace
36
+ trace = Buildkite::TestCollector::RSpecPlugin::Trace.new(
37
+ example,
38
+ history: tracer.history,
39
+ tags: tags,
40
+ )
41
+
42
+ Buildkite::TestCollector.uploader.traces[example.id] = trace
43
+ end
33
44
  end
34
45
 
35
46
  config.after(:suite) do
@@ -5,6 +5,7 @@ module Buildkite::TestCollector::MinitestPlugin
5
5
  attr_accessor :example
6
6
  attr_writer :failure_reason, :failure_expanded
7
7
  attr_reader :history
8
+ attr_reader :tags
8
9
 
9
10
  RESULT_CODES = {
10
11
  '.' => 'passed',
@@ -15,9 +16,10 @@ module Buildkite::TestCollector::MinitestPlugin
15
16
 
16
17
  FILE_PATH_REGEX = /^(.*?\.(rb|feature))/
17
18
 
18
- def initialize(example, history:)
19
+ def initialize(example, history:, tags: nil)
19
20
  @example = example
20
21
  @history = history
22
+ @tags = tags
21
23
  end
22
24
 
23
25
  def result
@@ -38,6 +40,7 @@ module Buildkite::TestCollector::MinitestPlugin
38
40
  failure_reason: failure_reason,
39
41
  failure_expanded: failure_expanded,
40
42
  history: history,
43
+ tags: tags,
41
44
  ).select { |_, value| !value.nil? }
42
45
  end
43
46
 
@@ -14,18 +14,29 @@ module Buildkite::TestCollector::MinitestPlugin
14
14
  min_duration: Buildkite::TestCollector.trace_min_duration,
15
15
  )
16
16
 
17
- # The _buildkite prefix here is added as a safeguard against name collisions
18
- # as we are in the main thread
17
+ tags = {}
18
+
19
+ # _buildkite prefix reduces chance of collisions in this almost-global (per-fiber) namespace.
19
20
  Thread.current[:_buildkite_tracer] = tracer
21
+ Thread.current[:_buildkite_tags] = tags
20
22
  end
21
23
 
22
24
  def after_teardown
23
25
  tracer = Thread.current[:_buildkite_tracer]
26
+ tags = Thread.current[:_buildkite_tags]
27
+
28
+ Thread.current[:_buildkite_tracer] = nil
29
+ Thread.current[:_buildkite_tags] = nil
30
+
24
31
  if !tracer.nil?
25
- Thread.current[:_buildkite_tracer] = nil
26
32
  tracer.finalize
27
33
 
28
- trace = Buildkite::TestCollector::MinitestPlugin::Trace.new(self, history: tracer.history)
34
+ trace = Buildkite::TestCollector::MinitestPlugin::Trace.new(
35
+ self,
36
+ history: tracer.history,
37
+ tags: tags,
38
+ )
39
+
29
40
  Buildkite::TestCollector.uploader.traces[trace.source_location] = trace
30
41
  end
31
42
 
@@ -4,14 +4,16 @@ module Buildkite::TestCollector::RSpecPlugin
4
4
  class Trace
5
5
  attr_accessor :example, :failure_reason, :failure_expanded
6
6
  attr_reader :history
7
+ attr_reader :tags
7
8
 
8
9
  FILE_PATH_REGEX = /^(.*?\.(rb|feature))/
9
10
 
10
- def initialize(example, history:, failure_reason: nil, failure_expanded: [])
11
+ def initialize(example, history:, failure_reason: nil, failure_expanded: [], tags: nil)
11
12
  @example = example
12
13
  @history = history
13
14
  @failure_reason = failure_reason
14
15
  @failure_expanded = failure_expanded
16
+ @tags = tags
15
17
  end
16
18
 
17
19
  def result
@@ -32,6 +34,7 @@ module Buildkite::TestCollector::RSpecPlugin
32
34
  failure_reason: failure_reason,
33
35
  failure_expanded: failure_expanded,
34
36
  history: history,
37
+ tags: tags,
35
38
  ).select { |_, value| !value.nil? }
36
39
  end
37
40
 
@@ -58,7 +58,6 @@ module Buildkite::TestCollector
58
58
  def initialize(min_duration: nil)
59
59
  @top = Span.new(:top, MonotonicTime.call, nil, {})
60
60
  @stack = [@top]
61
- @min_duration = min_duration
62
61
  end
63
62
 
64
63
  def enter(section, **detail)
@@ -69,17 +68,17 @@ module Buildkite::TestCollector
69
68
 
70
69
  def leave
71
70
  current_span.end_at = MonotonicTime.call
72
- duration = current_span.duration
73
71
  @stack.pop
74
- current_span.children.pop if @min_duration && duration < @min_duration
72
+
73
+ current_span.children.pop unless retain_span?(current_span.children.last)
74
+
75
75
  nil # avoid ambiguous return type/value
76
76
  end
77
77
 
78
78
  def backfill(section, duration, **detail)
79
- return if @min_duration && duration < @min_duration
80
79
  now = MonotonicTime.call
81
80
  new_entry = Span.new(section, now - duration, now, detail)
82
- current_span.children << new_entry
81
+ current_span.children << new_entry if retain_span?(new_entry)
83
82
  end
84
83
 
85
84
  def current_span
@@ -95,5 +94,15 @@ module Buildkite::TestCollector
95
94
  def history
96
95
  @top.as_hash
97
96
  end
97
+
98
+ private
99
+
100
+ def retain_span?(span)
101
+ return true unless Buildkite::TestCollector.span_filters
102
+
103
+ Buildkite::TestCollector.span_filters.all? do |filter|
104
+ filter.call(span)
105
+ end
106
+ end
98
107
  end
99
108
  end
@@ -25,7 +25,8 @@ module Buildkite::TestCollector
25
25
  OpenSSL::SSL::SSLError,
26
26
  OpenSSL::SSL::SSLErrorWaitReadable,
27
27
  EOFError,
28
- Errno::ETIMEDOUT
28
+ Errno::ETIMEDOUT,
29
+ # TODO: some retries for server-side error would be great.
29
30
  ]
30
31
 
31
32
  def self.tracer
@@ -35,16 +36,23 @@ module Buildkite::TestCollector
35
36
  def self.upload(data)
36
37
  return false unless Buildkite::TestCollector.api_token
37
38
 
38
- http = Buildkite::TestCollector::HTTPClient.new(Buildkite::TestCollector.url)
39
+ http = Buildkite::TestCollector::HTTPClient.new(
40
+ url: Buildkite::TestCollector.url,
41
+ api_token: Buildkite::TestCollector.api_token,
42
+ )
39
43
 
40
44
  Thread.new do
41
- response = begin
45
+ begin
42
46
  upload_attempts ||= 0
43
- http.post_json(data)
47
+ http.post_upload(
48
+ data: data,
49
+ run_env: Buildkite::TestCollector::CI.env,
50
+ tags: Buildkite::TestCollector.tags,
51
+ )
52
+
44
53
  rescue *Buildkite::TestCollector::Uploader::RETRYABLE_UPLOAD_ERRORS => e
45
- if (upload_attempts += 1) < MAX_UPLOAD_ATTEMPTS
46
- retry
47
- end
54
+ retry if (upload_attempts += 1) < MAX_UPLOAD_ATTEMPTS
55
+
48
56
  rescue StandardError => e
49
57
  $stderr.puts e
50
58
  $stderr.puts "#{Buildkite::TestCollector::NAME} #{Buildkite::TestCollector::VERSION} experienced an error when sending your data, you may be missing some executions for this run."
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Buildkite
4
4
  module TestCollector
5
- VERSION = "2.6.0"
5
+ VERSION = "2.10.0"
6
6
  NAME = "buildkite-test_collector"
7
7
  end
8
8
  end
@@ -28,7 +28,6 @@ module Buildkite
28
28
  module TestCollector
29
29
  DEFAULT_URL = "https://analytics-api.buildkite.com/v1/uploads"
30
30
  DEFAULT_UPLOAD_BATCH_SIZE = 500
31
-
32
31
  class << self
33
32
  attr_accessor :api_token
34
33
  attr_accessor :url
@@ -37,16 +36,23 @@ module Buildkite
37
36
  attr_accessor :tracing_enabled
38
37
  attr_accessor :artifact_path
39
38
  attr_accessor :env
39
+ attr_accessor :tags
40
40
  attr_accessor :batch_size
41
41
  attr_accessor :trace_min_duration
42
+ attr_accessor :span_filters
42
43
  end
43
44
 
44
- def self.configure(hook:, token: nil, url: nil, tracing_enabled: true, artifact_path: nil, env: {})
45
+ def self.configure(hook:, token: nil, url: nil, tracing_enabled: true, artifact_path: nil, env: {}, tags: {})
46
+ if hook.to_sym == :cucumber && Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7')
47
+ raise UnsupportedFrameworkError.new("Cucumber is only supported in versions of Ruby >= 2.7")
48
+ end
49
+
45
50
  self.api_token = (token || ENV["BUILDKITE_ANALYTICS_TOKEN"])&.strip
46
51
  self.url = url || DEFAULT_URL
47
52
  self.tracing_enabled = tracing_enabled
48
53
  self.artifact_path = artifact_path
49
54
  self.env = env
55
+ self.tags = tags
50
56
  self.batch_size = ENV.fetch("BUILDKITE_ANALYTICS_UPLOAD_BATCH_SIZE") { DEFAULT_UPLOAD_BATCH_SIZE }.to_i
51
57
 
52
58
  trace_min_ms_string = ENV["BUILDKITE_ANALYTICS_TRACE_MIN_MS"]
@@ -54,6 +60,11 @@ module Buildkite
54
60
  Float(trace_min_ms_string) / 1000
55
61
  end
56
62
 
63
+ self.span_filters = []
64
+ unless self.trace_min_duration.nil?
65
+ self.span_filters << MinDurationSpanFilter.new(self.trace_min_duration)
66
+ end
67
+
57
68
  self.hook_into(hook)
58
69
  end
59
70
 
@@ -70,6 +81,18 @@ module Buildkite
70
81
  tracer&.leave
71
82
  end
72
83
 
84
+ # Set a key=value tag on the current test execution.
85
+ def self.tag_execution(key, value)
86
+ tags = Thread.current[:_buildkite_tags]
87
+ raise "_buildkite_tags not available" unless tags
88
+
89
+ unless key.is_a?(String) && value.is_a?(String)
90
+ raise ArgumentError, "tag key and value expected string"
91
+ end
92
+
93
+ tags[key] = value
94
+ end
95
+
73
96
  def self.enable_tracing!
74
97
  return unless self.tracing_enabled
75
98
 
@@ -84,5 +107,15 @@ module Buildkite
84
107
  Buildkite::TestCollector::Uploader.tracer&.backfill(:sql, finish - start, **{ query: payload[:sql] })
85
108
  end
86
109
  end
110
+
111
+ class MinDurationSpanFilter
112
+ def initialize(min_duration)
113
+ @min_duration = min_duration
114
+ end
115
+
116
+ def call(span)
117
+ span.duration > @min_duration
118
+ end
119
+ end
87
120
  end
88
121
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: buildkite-test_collector
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.0
4
+ version: 2.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Buildkite
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-03-21 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -52,13 +51,28 @@ dependencies:
52
51
  - - "~>"
53
52
  - !ruby/object:Gem::Version
54
53
  version: '3.10'
55
- description:
54
+ - !ruby/object:Gem::Dependency
55
+ name: cucumber
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '9.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '9.0'
56
68
  email:
57
69
  - support+analytics@buildkite.com
58
70
  executables: []
59
71
  extensions: []
60
72
  extra_rdoc_files: []
61
73
  files:
74
+ - ".buildkite/pipeline.release.yml"
75
+ - ".buildkite/steps/release-gem"
62
76
  - ".gitignore"
63
77
  - ".rspec"
64
78
  - CHANGELOG.md
@@ -74,10 +88,13 @@ files:
74
88
  - bin/setup
75
89
  - buildkite-test_collector.gemspec
76
90
  - buildkite.yaml
91
+ - cucumber.yml
77
92
  - lib/buildkite/test_collector.rb
78
93
  - lib/buildkite/test_collector/ci.rb
94
+ - lib/buildkite/test_collector/cucumber_plugin/trace.rb
79
95
  - lib/buildkite/test_collector/error.rb
80
96
  - lib/buildkite/test_collector/http_client.rb
97
+ - lib/buildkite/test_collector/library_hooks/cucumber.rb
81
98
  - lib/buildkite/test_collector/library_hooks/minitest.rb
82
99
  - lib/buildkite/test_collector/library_hooks/rspec.rb
83
100
  - lib/buildkite/test_collector/minitest_plugin.rb
@@ -88,7 +105,6 @@ files:
88
105
  - lib/buildkite/test_collector/rspec_plugin/reporter.rb
89
106
  - lib/buildkite/test_collector/rspec_plugin/trace.rb
90
107
  - lib/buildkite/test_collector/session.rb
91
- - lib/buildkite/test_collector/test_links_plugin/formatter.rb
92
108
  - lib/buildkite/test_collector/tracer.rb
93
109
  - lib/buildkite/test_collector/uploader.rb
94
110
  - lib/buildkite/test_collector/uuid.rb
@@ -100,7 +116,6 @@ licenses:
100
116
  metadata:
101
117
  homepage_uri: https://github.com/buildkite/test-collector-ruby
102
118
  source_code_uri: https://github.com/buildkite/test-collector-ruby
103
- post_install_message:
104
119
  rdoc_options: []
105
120
  require_paths:
106
121
  - lib
@@ -115,8 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
130
  - !ruby/object:Gem::Version
116
131
  version: '0'
117
132
  requirements: []
118
- rubygems_version: 3.4.10
119
- signing_key:
133
+ rubygems_version: 3.6.7
120
134
  specification_version: 4
121
- summary: Track test executions and report to Buildkite Test Analytics
135
+ summary: Track test executions and report to Buildkite Test Engine
122
136
  test_files: []
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Buildkite::TestCollector::TestLinksPlugin
4
- class Formatter
5
- RSpec::Core::Formatters.register self, :dump_failures
6
-
7
- def initialize(output)
8
- @output = output
9
- end
10
-
11
- def dump_failures(notification)
12
- # Do not display summary if no failed examples
13
- return if notification.failed_examples.empty?
14
-
15
- # Check if a Test Analytics token is set
16
- return unless Buildkite::TestCollector.api_token
17
-
18
- metadata = fetch_metadata
19
-
20
- # return if metadata was not fetched successfully
21
- return if metadata.nil?
22
-
23
- # return if suite url is nil
24
- return if metadata['suite_url'].nil?
25
-
26
- @output << "\n\nšŸ”„ \x1b[31mTest Analytics failures šŸ”„\n"
27
- @output << '_____________________________'
28
- @output << "\n\n"
29
-
30
- @output << notification.failed_examples.map do |example|
31
- failed_example_output(example, metadata['suite_url'])
32
- end.join("\n")
33
-
34
- @output << "\n\n"
35
- end
36
-
37
- private
38
-
39
- def generate_scope_name_digest(scope, name)
40
- Digest::SHA256.hexdigest(scope + name)
41
- end
42
-
43
- def failed_example_output(example, url)
44
- scope = example.example_group.metadata[:full_description]
45
- name = example.description
46
- scope_name_digest = generate_scope_name_digest(scope, name)
47
- test_url = "#{url}/tests/#{scope_name_digest}"
48
- "šŸ”— \x1b[4m\x1b[37m#{%(\x1b]1339;url=#{test_url};content="#{scope} #{name}"\x07)}\x1b[m"
49
- end
50
-
51
- def fetch_metadata
52
- return unless Buildkite::TestCollector.api_token
53
-
54
- http = Buildkite::TestCollector::HTTPClient.new(Buildkite::TestCollector.url)
55
- response = http.metadata
56
-
57
- JSON.parse(response.body) if response.code == '200'
58
- rescue StandardError => e
59
- # We don't need to output anything here
60
- end
61
- end
62
- end