rspec_junit_formatter 0.6.2 → 0.7.1

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: 5284f5c3c374244eebe552b1284e12e846f0db274a6758ab2b3bf988828a5ecb
4
- data.tar.gz: bba94018c3622f87b5a25d31d1dfcf6f3f52ab306234c67f32cfdb5526168835
3
+ metadata.gz: f8938b63b79c5d0cfd77464773645cca9a0c72b11c5d3f1fe7fc92d0279c4021
4
+ data.tar.gz: 9fc9a172d1292bb89a368e889f2f90165e47b087dd5c39fe7a154d9480ca3f61
5
5
  SHA512:
6
- metadata.gz: 130f2aa5ff8c848fd971dd432a946452561eb8509bd9503525f4716d71acc7b7ac031ce81d0468eba463c0a808cb58e3c8121d9b139613af113397a1ef422126
7
- data.tar.gz: db4e33312c94d947595627d342be7d3b483b4ca1c98fb53672851c58637728e11352d4a3102101a4f6cf81b0cf240a272dadd92c77c1861f0a3140727f52cd41
6
+ metadata.gz: e4c83896373201abf5ec66c1cc8dc858e44eb3def5546646e2ca72d36ea2dd70cb44d42b9db2d51d094daa46b3248fd4a189edced96f1db1a2319d4f38bdfff6
7
+ data.tar.gz: f3619e7aaef1751d2dc433c41e2c6608e77d31483e550fa8ce47ad95352f21c7052b10fa8b447b1f00d2a21b5d883be017939f120162fb5f635238088230aeca
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Build results](https://github.com/sj26/rspec_junit_formatter/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/sj26/rspec_junit_formatter/actions/workflows/ci.yml?branch=main)
4
4
  [![Gem version](http://img.shields.io/gem/v/rspec_junit_formatter.svg)](https://rubygems.org/gems/rspec_junit_formatter)
5
5
 
6
- [RSpec][rspec] 2 & 3 results that your CI can read. [Jenkins][jenkins-junit], [Buildkite][buildkite-junit], [CircleCI][circleci-junit], [Gitlab][gitlab-junit], and probably more, too.
6
+ [RSpec][rspec] 3 results that your CI can read. [Jenkins][jenkins-junit], [Buildkite][buildkite-junit], [CircleCI][circleci-junit], [Gitlab][gitlab-junit], and probably more, too.
7
7
 
8
8
  [rspec]: http://rspec.info/
9
9
  [jenkins-junit]: https://jenkins.io/doc/pipeline/steps/junit/
@@ -11,20 +11,10 @@
11
11
  [circleci-junit]: https://circleci.com/docs/2.0/collect-test-data/
12
12
  [gitlab-junit]: https://docs.gitlab.com/ee/ci/unit_test_reports.html#ruby-example
13
13
 
14
- ## Legacy release
15
-
16
- **0.6.2 is the final release for older Ruby and RSpec versions.** It retains Ruby >= 2.3 and RSpec >= 2, < 4 (except 2.12.0), with the latest encoding and aggregate failure fixes. See [tested versus permitted versions](#tested-versus-permitted-versions) for coverage and limitations.
17
-
18
- The next release will require Ruby >= 3.3 and RSpec >= 3.13. If you must keep an older Ruby **or** an older RSpec, use:
19
-
20
- ```ruby
21
- gem "rspec_junit_formatter", "0.6.2", require: false
22
- ```
23
-
24
- Then run `bundle update rspec_junit_formatter`. Without Bundler, use `gem install rspec_junit_formatter -v 0.6.2`. Upgrading Ruby alone is not enough if your project must keep an older RSpec.
25
-
26
14
  ## Usage
27
15
 
16
+ Requires Ruby >= 3.3 and RSpec >= 3.13, < 4. For older versions, see [legacy support](#supported-versions-and-legacy-upgrades).
17
+
28
18
  Install the gem:
29
19
 
30
20
  ```sh
@@ -111,110 +101,21 @@ Note that this example captures all output from every example all the time, pote
111
101
 
112
102
  [xml-charsets]: https://www.w3.org/TR/xml/#charsets
113
103
 
114
- ## Development
104
+ ## Supported versions and legacy upgrades
115
105
 
116
- Use Ruby 3.4 (see [`.ruby-version`](.ruby-version)) for maintenance. Ruby 3.4 is in [normal maintenance](https://www.ruby-lang.org/en/downloads/branches/). This selects the development Ruby without changing the installed gem's requirements. Keep Ruby requirements out of the root Gemfile: Appraisal copies them into legacy Gemfiles.
106
+ We support [maintained Ruby versions](https://www.ruby-lang.org/en/downloads/branches/), including security maintenance, and [the current stable RSpec series](https://rspec.info/blog/2024/11/introducing-the-monorepo/). Minor releases may raise the minimum versions when upstream support ends.
117
107
 
118
- Install the Bundler version recorded in `Gemfile.lock`, then test the locked development bundle:
108
+ For Ruby < 3.3 or RSpec < 3.13, use [0.6.2](https://rubygems.org/gems/rspec_junit_formatter/versions/0.6.2), the final legacy release:
119
109
 
120
- ```sh
121
- gem install bundler -v 4.0.23
122
- bundle install
123
- bundle exec rake spec
110
+ ```ruby
111
+ gem "rspec_junit_formatter", "0.6.2", require: false
124
112
  ```
125
113
 
126
- The [development workflow](.github/workflows/development.yml) tests this bundle separately from the [compatibility workflow](.github/workflows/ci.yml). Use `bundle update --all` to refresh the development lockfile.
114
+ Run `bundle update rspec_junit_formatter`, or install without Bundler using `gem install rspec_junit_formatter -v 0.6.2`. This release permits Ruby >= 2.3 and RSpec >= 2, < 4, except 2.12.0; see its [tested combinations](https://github.com/sj26/rspec_junit_formatter/blob/v0.6.2/README.md#tested-versus-permitted-versions).
127
115
 
128
- Legacy bundles are defined by [Appraisal][appraisal] and have separate Gemfiles. Select a compatible Ruby and Bundler, then run an individual bundle without the root lockfile, for example on Ruby 2.6:
129
-
130
- ```sh
131
- BUNDLE_GEMFILE=gemfiles/rspec_2_x.gemfile bundle install
132
- BUNDLE_GEMFILE=gemfiles/rspec_2_x.gemfile bundle exec rake spec
133
- ```
116
+ ## Contributing
134
117
 
135
- Plain `bundle exec rake` also runs all Appraisals outside CI. Not all historical RSpec versions run on modern Ruby, so use the combinations in the compatibility workflow rather than running all Appraisals on the maintainer Ruby.
136
-
137
- [appraisal]: https://github.com/thoughtbot/appraisal
138
-
139
- ### Tested versus permitted versions
140
-
141
- The [gemspec](rspec_junit_formatter.gemspec) permits Ruby >= 2.3 and `rspec-core` >= 2, < 4, excluding 2.12.0. These requirements allow installation; they do not mean every Ruby/RSpec combination is tested or works together.
142
-
143
- The CI workflows test these combinations:
144
-
145
- | Ruby | RSpec | Bundle |
146
- | --- | --- | --- |
147
- | 2.6–3.4 | 3.4–3.13, each minor series | Legacy Appraisals |
148
- | 2.6–3.1 | 2.14 (`< 2.99`) and 3.0–3.3, each minor series | Legacy Appraisals |
149
- | 3.4 and 4.0 | Current 3.13, pinned by `Gemfile.lock` | Development |
150
-
151
- Ruby 4.0 coverage initially uses only the current RSpec development bundle, not the historical Appraisals. Ruby 2.3–2.5 remain permitted but are not covered by CI. RSpec versions allowed by the gemspec but outside the Appraisal ranges are also untested. Historical RSpec versions below 3.4 are not compatible with Ruby 3.2 or newer. Retaining installation permission does not imply that an end-of-life Ruby still receives upstream security updates.
152
-
153
- ## Releasing
154
-
155
- Releases use the [Release workflow](https://github.com/sj26/rspec_junit_formatter/actions/workflows/release.yml)
156
- and [RubyGems trusted publishing](https://guides.rubygems.org/trusted-publishing/).
157
- GitHub Actions builds **unsigned gems**: it needs no personal signing key, persistent
158
- RubyGems API key, or maintainer machine. Local `bundle exec rake build`, `install`,
159
- and `release` tasks remain available; local releases may retain the maintainer's
160
- traditional gem signatures. Do not publish the same version through both paths.
161
-
162
- 1. Bump the gemspec version and update the changelog in a reviewed PR. Merge it
163
- into `main`. Use a new stable `X.Y.Z` version; never reuse a published version.
164
- 2. Tag that exact commit as `vX.Y.Z` and push the tag. Record its full commit SHA
165
- with `git rev-parse 'vX.Y.Z^{commit}'`. Pushing a tag alone does not publish.
166
- 3. Run the Release workflow **from `main`**, supplying the tag and full SHA. Leave
167
- `publish` unchecked for validation only. It checks tag/version/commit agreement
168
- and main ancestry, runs the compatibility and development suites at that SHA,
169
- builds the gem, rejects embedded signatures, and tests a clean installation
170
- outside the checkout. PRs also exercise the package build/install check, but
171
- their artifacts can never be published by this workflow.
172
- 4. Run again with the same tag and SHA and `publish` checked. After the checks pass,
173
- review the commit and package SHA-256 in the build log, then approve the
174
- `release` environment deployment. Sam can approve his own run. The publish job
175
- downloads that run's exact immutable artifact, uses short-lived OIDC credentials,
176
- and does not check out or build project code.
177
- 5. The workflow verifies that RubyGems serves identical bytes before publishing a
178
- GitHub release with the same gem. Check both the
179
- [RubyGems version](https://rubygems.org/gems/rspec_junit_formatter/versions) and
180
- [GitHub release](https://github.com/sj26/rspec_junit_formatter/releases).
181
-
182
- ### One-time configuration
183
-
184
- The GitHub `release` environment must require approval by `sj26`, allow
185
- self-approval, disable admin bypass, and allow deployments only from the `main`
186
- branch. The **Immutable release tags** repository ruleset blocks updates and
187
- deletion of `v*` tags, with no bypass actors. Under the existing gem's
188
- **Trusted publishers** on RubyGems, configure:
189
-
190
- | Field | Value |
191
- | --- | --- |
192
- | Repository owner | `sj26` |
193
- | Repository name | `rspec_junit_formatter` |
194
- | Workflow filename | `release.yml` |
195
- | Environment | `release` |
196
-
197
- Leave reusable-workflow repository fields blank. This workflow grants
198
- `id-token: write` only to the environment-protected publish job. Do not add a
199
- RubyGems API key or personal signing key to repository secrets.
200
-
201
- ### Recovery
202
-
203
- Release runs are serialized and never auto-cancel an active publication. If a
204
- push succeeds but a later step fails, **re-run failed jobs in the original run**
205
- while its `release-gem` artifact is retained (30 days). This reuses the validated
206
- bytes and requires environment approval again. An existing RubyGems version is
207
- accepted only if its SHA-256 matches, and its downloaded bytes must also match.
208
- An incomplete GitHub draft can resume; an existing asset must match byte-for-byte
209
- and is never overwritten. A published GitHub release missing its gem fails closed
210
- and needs manual inspection rather than silently changing a public release.
211
-
212
- If the tag moved, a digest differs, or the original artifact expired, stop and
213
- investigate. Do not delete/recreate tags, yank/re-push gems, or overwrite release
214
- assets to force a retry. A fresh run rebuilds and is safe only if it reproduces
215
- the same bytes; otherwise recover the original artifact or use a new version
216
- through the normal review process. Validation-only runs do not test OIDC exchange
217
- or publication; only a real, explicitly approved new release can prove those paths.
118
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development, testing and release instructions.
218
119
 
219
120
  ## License
220
121
 
@@ -46,12 +46,7 @@ private
46
46
  end
47
47
 
48
48
  def error_count
49
- # Introduced in rspec 3.6
50
- if @summary_notification.respond_to?(:errors_outside_of_examples_count)
51
- @summary_notification.errors_outside_of_examples_count
52
- else
53
- 0
54
- end
49
+ @summary_notification.errors_outside_of_examples_count
55
50
  end
56
51
 
57
52
  def result_of(notification)
@@ -90,11 +85,13 @@ private
90
85
  def failure_for(notification)
91
86
  exception = exception_for(notification)
92
87
 
93
- if aggregate_failure?(exception)
88
+ details = if aggregate_failure?(exception)
94
89
  strip_diff_colors(exception.message)
95
90
  else
96
91
  strip_diff_colors(notification.message_lines.join("\n")) << "\n" << notification.formatted_backtrace.join("\n")
97
92
  end
93
+
94
+ details.gsub(/ +$/, '')
98
95
  end
99
96
 
100
97
  def exception_for(notification)
@@ -125,19 +122,8 @@ private
125
122
 
126
123
  # Completely gross hack for absolutely forcing off colorising for the
127
124
  # duration of a block.
128
- if RSpec.configuration.respond_to?(:color_mode=)
129
- def without_color(&block)
130
- swap_rspec_configuration(:color_mode, :off, &block)
131
- end
132
- elsif RSpec.configuration.respond_to?(:color=)
133
- def without_color(&block)
134
- swap_rspec_configuration(:color, false, &block)
135
- end
136
- else
137
- warn 'rspec_junit_formatter cannot prevent colorising due to an unexpected RSpec.configuration format'
138
- def without_color
139
- yield
140
- end
125
+ def without_color(&block)
126
+ swap_rspec_configuration(:color_mode, :off, &block)
141
127
  end
142
128
 
143
129
  def stdout_for(example_notification)
@@ -149,23 +135,9 @@ private
149
135
  end
150
136
 
151
137
  def aggregate_failure?(exception)
152
- # Introduced in rspec 3.3
138
+ # rspec-expectations is optional; the formatter only depends on rspec-core.
153
139
  return false unless defined?(RSpec::Expectations::MultipleExpectationsNotMetError)
154
140
 
155
141
  exception.is_a?(RSpec::Expectations::MultipleExpectationsNotMetError)
156
142
  end
157
143
  end
158
-
159
- # rspec-core 3.0.x forgot to mark this as a module function which causes:
160
- #
161
- # NoMethodError: undefined method `wrap' for RSpec::Core::Notifications::NullColorizer:Class
162
- # .../rspec-core-3.0.4/lib/rspec/core/notifications.rb:229:in `add_shared_group_line'
163
- # .../rspec-core-3.0.4/lib/rspec/core/notifications.rb:157:in `message_lines'
164
- #
165
- if defined?(RSpec::Core::Notifications::NullColorizer) && RSpec::Core::Notifications::NullColorizer.is_a?(Class) && !RSpec::Core::Notifications::NullColorizer.respond_to?(:wrap)
166
- RSpec::Core::Notifications::NullColorizer.class_eval do
167
- def self.wrap(*args)
168
- new.wrap(*args)
169
- end
170
- end
171
- end
@@ -9,8 +9,6 @@ require "rspec/core/formatters/base_formatter"
9
9
  # Dumps rspec results as a JUnit XML file.
10
10
  # Based on XML schema: http://windyroad.org/dl/Open%20Source/JUnit.xsd
11
11
  class RSpecJUnitFormatter < RSpec::Core::Formatters::BaseFormatter
12
- # rspec 2 and 3 implements are in separate files.
13
-
14
12
  private
15
13
 
16
14
  def xml_dump
@@ -164,9 +162,13 @@ private
164
162
  ?>.freeze => "&gt;".freeze,
165
163
  ).freeze
166
164
 
165
+ def utf8(text)
166
+ text.to_s.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "\uFFFD")
167
+ end
168
+
167
169
  def escape(text)
168
170
  # Make sure it's utf-8, replace illegal characters with ruby-like escapes, and replace special and discouraged characters with entities
169
- text.to_s.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "\uFFFD").gsub(ILLEGAL_REGEXP, ILLEGAL_REPLACEMENT).gsub(DISCOURAGED_REGEXP, DISCOURAGED_REPLACEMENTS)
171
+ utf8(text).gsub(ILLEGAL_REGEXP, ILLEGAL_REPLACEMENT).gsub(DISCOURAGED_REGEXP, DISCOURAGED_REPLACEMENTS)
170
172
  end
171
173
 
172
174
  STRIP_DIFF_COLORS_BLOCK_REGEXP = /^ ( [ ]* ) Diff: (?: \e\[ 0 m )? (?: \n \1 \e\[ \d+ (?: ; \d+ )* m .* )* /x
@@ -182,14 +184,12 @@ private
182
184
  # We also only want to target the diff hunks because the failure message
183
185
  # itself might legitimately contain ansi escape codes.
184
186
  #
185
- string.sub(STRIP_DIFF_COLORS_BLOCK_REGEXP) { |match| match.gsub(STRIP_DIFF_COLORS_CODES_REGEXP, "".freeze) }
187
+ # Transcode before matching: the original message may be invalid or use an
188
+ # encoding incompatible with the ASCII regexp. Leave XML escaping until later.
189
+ utf8(string).sub(STRIP_DIFF_COLORS_BLOCK_REGEXP) { |match| match.gsub(STRIP_DIFF_COLORS_CODES_REGEXP, "".freeze) }
186
190
  end
187
191
  end
188
192
 
189
193
  RspecJunitFormatter = RSpecJUnitFormatter
190
194
 
191
- if Gem::Version.new(RSpec::Core::Version::STRING) >= Gem::Version.new("3")
192
- require "rspec_junit_formatter/rspec3"
193
- else
194
- require "rspec_junit_formatter/rspec2"
195
- end
195
+ require "rspec_junit_formatter/rspec3"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec_junit_formatter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Cochran
@@ -15,26 +15,20 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '2'
18
+ version: '3.13'
19
19
  - - "<"
20
20
  - !ruby/object:Gem::Version
21
21
  version: '4'
22
- - - "!="
23
- - !ruby/object:Gem::Version
24
- version: 2.12.0
25
22
  type: :runtime
26
23
  prerelease: false
27
24
  version_requirements: !ruby/object:Gem::Requirement
28
25
  requirements:
29
26
  - - ">="
30
27
  - !ruby/object:Gem::Version
31
- version: '2'
28
+ version: '3.13'
32
29
  - - "<"
33
30
  - !ruby/object:Gem::Version
34
31
  version: '4'
35
- - - "!="
36
- - !ruby/object:Gem::Version
37
- version: 2.12.0
38
32
  - !ruby/object:Gem::Dependency
39
33
  name: bundler
40
34
  requirement: !ruby/object:Gem::Requirement
@@ -120,7 +114,6 @@ files:
120
114
  - LICENSE
121
115
  - README.md
122
116
  - lib/rspec_junit_formatter.rb
123
- - lib/rspec_junit_formatter/rspec2.rb
124
117
  - lib/rspec_junit_formatter/rspec3.rb
125
118
  homepage: https://github.com/sj26/rspec_junit_formatter
126
119
  licenses:
@@ -134,7 +127,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
134
127
  requirements:
135
128
  - - ">="
136
129
  - !ruby/object:Gem::Version
137
- version: 2.3.0
130
+ version: 3.3.0
138
131
  required_rubygems_version: !ruby/object:Gem::Requirement
139
132
  requirements:
140
133
  - - ">="
@@ -1,86 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class RSpecJUnitFormatter < RSpec::Core::Formatters::BaseFormatter
4
- attr_reader :started
5
-
6
- def start(example_count)
7
- @started = Time.now
8
- super
9
- end
10
-
11
- def dump_summary(duration, example_count, failure_count, pending_count)
12
- super
13
- xml_dump
14
- end
15
-
16
- private
17
-
18
- def result_of(example)
19
- example.execution_result[:status].to_sym
20
- end
21
-
22
- def example_group_file_path_for(example)
23
- meta = example.metadata
24
- while meta[:example_group]
25
- meta = meta[:example_group]
26
- end
27
- meta[:file_path]
28
- end
29
-
30
- def classname_for(example)
31
- fp = example_group_file_path_for(example)
32
- fp.sub(%r{\.[^/.]+\Z}, "").gsub("/", ".").gsub(/\A\.+|\.+\Z/, "")
33
- end
34
-
35
- def duration_for(example)
36
- example.execution_result[:run_time]
37
- end
38
-
39
- def description_for(example)
40
- example.full_description
41
- end
42
-
43
- def exception_for(example)
44
- example.execution_result[:exception]
45
- end
46
-
47
- def failure_type_for(example)
48
- exception_for(example).class.name
49
- end
50
-
51
- def failure_message_for(example)
52
- strip_diff_colors(exception_for(example).to_s)
53
- end
54
-
55
- def failure_for(example)
56
- exception = exception_for(example)
57
- message = strip_diff_colors(exception.message)
58
- backtrace = format_backtrace(exception.backtrace, example)
59
-
60
- if shared_group = find_shared_group(example)
61
- backtrace << "Shared Example Group: \"#{shared_group.metadata[:shared_group_name]}\" called from #{shared_group.metadata[:example_group][:location]}"
62
- end
63
-
64
- "#{message}\n#{backtrace.join("\n")}"
65
- end
66
-
67
- def error_count
68
- 0
69
- end
70
-
71
- def find_shared_group(example)
72
- group_and_parent_groups(example).find { |group| group.metadata[:shared_group_name] }
73
- end
74
-
75
- def group_and_parent_groups(example)
76
- example.example_group.parent_groups + [example.example_group]
77
- end
78
-
79
- def stdout_for(example)
80
- example.metadata[:stdout]
81
- end
82
-
83
- def stderr_for(example)
84
- example.metadata[:stderr]
85
- end
86
- end