diffcrypt 0.2.0 → 0.4.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: edd41bfcd4f86767e1d8b98d4c161c38983962e273a6e5b486c0f6465f159d80
4
- data.tar.gz: fecb2bda4f1f7cbd53fea2fd333f723bd3c1ac9e8c206930c6783795a6260d10
3
+ metadata.gz: e40a357c62cf93d9c49f336c7ad456d4f7510bd8389f3fb20c4913b91f4ef5f2
4
+ data.tar.gz: 4fb0296c2eaf901c59bdec117f974203500596f6347a9b8c12632b7dbe39ab77
5
5
  SHA512:
6
- metadata.gz: 7fe1aedc3eec0f98370479a8a813e15a9458213e70d49345ae90234cead04e47b880a602af54ff8d43a1a6e4d54e7bef4a2955eb3f88fde68762bb08de16a555
7
- data.tar.gz: 6a020043e24b13d22414de6f810bde69bb578adc0041d34e33c8f3d220d68903ed8b3726bc7302138fbdc00ebfe8c2e7a59bca4e776519a3d898fc4cd4d51845
6
+ metadata.gz: d368c674a915b706b876a5a022c7003e3f7ecf30b38b581f2e7debf261d47102d759c27d317b85a71904bace22fd1a0306d9175b2c9496592375d786d3fe4fc1
7
+ data.tar.gz: 9e24c98075aab9c2f8e0550e757adea3df5b7f1e341f7d93d5644ee21e6a9ee9a352f7450a1a63afd609d876406c6efbe4aaff56e6c978c413259588625eedf5
@@ -4,8 +4,23 @@ jobs:
4
4
  build:
5
5
  docker:
6
6
  - image: circleci/ruby:2.6.6
7
+ working_directory: /mnt/ramdisk
7
8
  steps:
8
9
  - checkout
9
10
  - run: bundle install
10
- - run: bundle exec rake test
11
- - run: bundle exec rubocop
11
+ - run:
12
+ name: Setup Code Climate test-reporter
13
+ command: |
14
+ # download test reporter as a static binary
15
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
16
+ chmod +x ./cc-test-reporter
17
+ - run:
18
+ name: rake test
19
+ command: |
20
+ ./cc-test-reporter before-build
21
+ bundle exec rake test
22
+ ./cc-test-reporter after-build --coverage-input-type lcov --exit-code $?
23
+ - run:
24
+ name: rubocop
25
+ command: bundle exec rubocop
26
+ when: always
@@ -9,8 +9,12 @@ Style/Documentation:
9
9
  Metrics/MethodLength:
10
10
  Exclude:
11
11
  - test/**/*_test.rb
12
+ TrailingCommaInArrayLiteral:
13
+ EnforcedStyleForMultiline: consistent_comma
12
14
  Style/TrailingCommaInArguments:
13
15
  EnforcedStyleForMultiline: consistent_comma
16
+ Style/AccessorGrouping:
17
+ EnforcedStyle: separated
14
18
 
15
19
  Layout/LineLength:
16
20
  Exclude:
@@ -0,0 +1,95 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+
9
+
10
+ ## [0.4.0] - 2020-10-01
11
+
12
+ ### Changed
13
+
14
+ - Encryptor can now use other ciphers than the default
15
+
16
+ ### Dependencies
17
+
18
+ - simplecov: 0.17.0 -> 0.18.0 (#20)
19
+ - rubocop: 0.88.0 -> 0.92.0 (#24)
20
+
21
+
22
+
23
+ ## [0.3.3] - 2020-07-25
24
+
25
+ ### Fixed
26
+
27
+ - Explicit FileUtils require to avoid potentially warning logs
28
+
29
+
30
+
31
+ ## [0.3.2] - 2020-07-20
32
+
33
+ ### Added
34
+
35
+ - CLI: `diffcrypt generate-key` command to generate a new key for a cipher
36
+ - Internal: Library now generates and publishes code coverage publically on Code Climate
37
+
38
+ ### Changed
39
+
40
+ - Only support ruby 2.5+ since 2.4 is no longer maintained
41
+
42
+ ### Removed
43
+
44
+ - No longer generate and store a checksum. Backwards compatible since it wasn't used
45
+
46
+
47
+
48
+ ## [0.3.1] - 2020-07-08
49
+
50
+ ### Fixed
51
+
52
+ - Thor deprecation error no longer shows on CLI failure
53
+
54
+ ### Changed
55
+
56
+ - Thor 0.20+ can now be used alongside this gem
57
+
58
+
59
+
60
+ ## [0.3.0] - 2020-06-30
61
+
62
+ ## Added
63
+
64
+ - CLI: Use diffcrypt from command line of any project without requiring ruby integration
65
+ - CLI: `diffcrypt encrypt` Directly encrypt any file and output the contents
66
+ - CLI: `diffcrypt decrypt` Directly decrypt any file and output the contents
67
+
68
+
69
+
70
+ ## [0.2.0] - 2020-06-28
71
+
72
+ ### Added
73
+
74
+ - Store client, cipher and checksum in file metadata
75
+
76
+ ### Fixed
77
+
78
+ - Only attenpt to decrypt original content if it exists
79
+
80
+
81
+
82
+ ## [0.1.1] - 2020-06-28
83
+
84
+ ### Fixed
85
+
86
+ - Converting rails native credentials files would fail on first run
87
+
88
+
89
+
90
+ ## [0.1.0] - 2020-06-28
91
+
92
+ ### Added
93
+
94
+ - First release!
95
+ - Rails support via monkey patch
data/Gemfile CHANGED
@@ -7,4 +7,6 @@ gemspec
7
7
 
8
8
  gem 'minitest', '~> 5.0'
9
9
  gem 'rake', '~> 13.0'
10
- gem 'rubocop', '~> 0.86'
10
+ gem 'rubocop', '~> 0.92.0'
11
+ gem 'simplecov', '~> 0.19.0', require: false # CodeClimate not compatible with 0.18+ yet - https://github.com/codeclimate/test-reporter/issues/413
12
+ gem 'simplecov-lcov', '< 0.8'
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Diffcrypt
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/diffcrypt.svg)](https://rubygems.org/gems/diffcrypt)
4
+ [![CircleCI](https://circleci.com/gh/marcqualie/diffcrypt.svg?style=svg)](https://circleci.com/gh/marcqualie/diffcrypt)
5
+
6
+
3
7
  Diffable encrypted files that you can safely commit into your repo.
4
8
 
5
9
 
@@ -16,7 +20,7 @@ And then execute:
16
20
 
17
21
  $ bundle install
18
22
 
19
- Or install it yourself as:
23
+ Or install it globally (to use the CLI from any project):
20
24
 
21
25
  $ gem install diffcrypt
22
26
 
@@ -24,8 +28,24 @@ Or install it yourself as:
24
28
 
25
29
  ## Usage
26
30
 
31
+ There are a few ways to use the library, depending on how advanced your use case is.
32
+
33
+
34
+ ### CLI
35
+
36
+ The easiest way to get started is to use the CLI.
37
+
38
+ ```shell
39
+ diffcrypt decrypt -k $(cat test/fixtures/aes-128-gcm.key) test/fixtures/example.yml.enc
40
+ diffcrypt encrypt -k $(cat test/fixtures/aes-128-gcm.key) test/fixtures/example.yml
41
+ ```
42
+
43
+
44
+ ### Ruby
27
45
 
28
- ### Encrypt existing file
46
+ A direct API is exposed so `Diffcrypt::Encryptor` can be used in any ruby project.
47
+
48
+ **NOTE:** This API may change any time until v1.0
29
49
 
30
50
  ```ruby
31
51
  encryptor = Diffcrypt::Encryptor.new('99e1f86b9e61f24c56ff4108dd415091')
@@ -34,24 +54,22 @@ encrypted = encryptor.encrypt(yaml)
34
54
  File.write('tmp/example.yml.enc', encrypted)
35
55
  ```
36
56
 
37
- ### Decrypt a file
38
-
39
57
  ```ruby
40
58
  encryptor = Diffcrypt::Encryptor.new('99e1f86b9e61f24c56ff4108dd415091')
41
59
  yaml = File.read('test/fixtures/example.yml.enc')
42
60
  config = YAML.safe_load(encryptor.decrypt(yaml))
43
61
  ```
44
62
 
45
- ### Rails
63
+ ### Ruby on Rails
46
64
 
47
65
  Currently there is not native support for rails, but ActiveSupport can be monkeypatched to override
48
- the built in encrypter.
66
+ the built in encrypter. All existing `rails credentials:edit` also work with this method.
49
67
 
50
68
  ```ruby
51
69
  require 'diffcrypt/rails/encrypted_configuration'
52
70
  module Rails
53
71
  class Application
54
- def encrypted(path, key_path: 'config/master.key', env_key: 'RAILS_MASTER_KEY')
72
+ def encrypted(path, key_path: 'config/aes-128-gcm.key', env_key: 'RAILS_MASTER_KEY')
55
73
  Diffcrypt::Rails::EncryptedConfiguration.new(
56
74
  config_path: Rails.root.join(path),
57
75
  key_path: Rails.root.join(key_path),
@@ -65,6 +83,17 @@ end
65
83
 
66
84
 
67
85
 
86
+ ## Converting between ciphers
87
+
88
+ Sometimes you may want to rotate the cipher used on a file. You cab do this rogramtically using the ruby code above, or you can also chain the CLI commands like so:
89
+
90
+ ```shell
91
+ diffcrypt decrypt -k $(cat test/fixtures/aes-128-gcm.key) test/fixtures/example.yml.enc > test/fixtures/example.128.yml \
92
+ && diffcrypt encrypt --cipher aes-256-gcm -k $(cat test/fixtures/aes-256-gcm.key) test/fixtures/example.128.yml > test/fixtures/example.256.yml.enc && rm test/fixtures/example.128.yml
93
+ ```
94
+
95
+
96
+
68
97
  ## Development
69
98
 
70
99
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,17 @@
1
+ # Security Policy
2
+
3
+
4
+
5
+ ## Supported Versions
6
+
7
+ Since the internal APIs may change dramatically until v1.0, here is a list of the versions that are supported.
8
+
9
+ | Version | Supported |
10
+ | ------- | ------------------ |
11
+ | 0.3.x | :white_check_mark: |
12
+
13
+
14
+
15
+ ## Reporting a Vulnerability
16
+
17
+ Please email security@marcqualie.com to report any security issues.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'thor'
5
+
6
+ require_relative '../lib/diffcrypt/cli'
7
+
8
+ Diffcrypt::CLI.start(ARGV)
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.description = 'Diffable encrypted configuration files that can be safely committed into a git repository'
13
13
  spec.homepage = 'https://github.com/marcqualie/diffcrypt'
14
14
  spec.license = 'MIT'
15
- spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
16
16
 
17
17
  # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
18
18
 
@@ -22,12 +22,13 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  # Specify which files should be added to the gem when it is released.
24
24
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ spec.files = Dir.chdir(::File.expand_path(__dir__)) do
26
26
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
27
  end
28
- spec.bindir = 'exe'
29
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.bindir = 'bin'
29
+ spec.executables = %w[diffcrypt]
30
30
  spec.require_paths = ['lib']
31
31
 
32
32
  spec.add_runtime_dependency 'activesupport', '~> 6.0.0'
33
+ spec.add_runtime_dependency 'thor', '>= 0.20', '< 2'
33
34
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './encryptor'
4
+ require_relative './file'
5
+ require_relative './version'
6
+
7
+ module Diffcrypt
8
+ class CLI < Thor
9
+ desc 'decrypt <path>', 'Decrypt a file'
10
+ method_option :key, aliases: %i[k], required: true
11
+ def decrypt(path)
12
+ file = File.new(path)
13
+ ensure_file_exists(file)
14
+ say file.decrypt(key)
15
+ end
16
+
17
+ desc 'encrypt <path>', 'Encrypt a file'
18
+ method_option :key, aliases: %i[k], required: true
19
+ method_option :cipher, default: Encryptor::DEFAULT_CIPHER
20
+ def encrypt(path)
21
+ file = File.new(path)
22
+ ensure_file_exists(file)
23
+ say file.encrypt(key, cipher: options[:cipher])
24
+ end
25
+
26
+ desc 'generate-key', 'Generate a 32 bit key'
27
+ method_option :cipher, default: Encryptor::DEFAULT_CIPHER
28
+ def generate_key
29
+ say Encryptor.generate_key(options[:cipher])
30
+ end
31
+
32
+ desc 'version', 'Show client version'
33
+ def version
34
+ say Diffcrypt::VERSION
35
+ end
36
+
37
+ no_commands do
38
+ def key
39
+ options[:key]
40
+ end
41
+
42
+ def encryptor
43
+ @encryptor ||= Encryptor.new(key)
44
+ end
45
+
46
+ # @param [Diffcrypt::File] path
47
+ def ensure_file_exists(file)
48
+ abort('[ERROR] File does not exist') unless file.exists?
49
+ end
50
+
51
+ def self.exit_on_failure?
52
+ true
53
+ end
54
+ end
55
+ end
56
+ end
@@ -8,17 +8,20 @@ require 'yaml'
8
8
 
9
9
  require 'active_support/message_encryptor'
10
10
 
11
+ require_relative './version'
12
+
11
13
  module Diffcrypt
12
14
  class Encryptor
13
- CIPHER = 'aes-128-gcm'
15
+ DEFAULT_CIPHER = 'aes-128-gcm'
14
16
 
15
- def self.generate_key
16
- SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
17
+ def self.generate_key(cipher = DEFAULT_CIPHER)
18
+ SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(cipher))
17
19
  end
18
20
 
19
- def initialize(key)
21
+ def initialize(key, cipher: DEFAULT_CIPHER)
20
22
  @key = key
21
- @encryptor ||= ActiveSupport::MessageEncryptor.new([key].pack('H*'), cipher: CIPHER)
23
+ @cipher = cipher
24
+ @encryptor ||= ActiveSupport::MessageEncryptor.new([key].pack('H*'), cipher: cipher)
22
25
  end
23
26
 
24
27
  # @param [String] contents The raw YAML string to be encrypted
@@ -48,8 +51,7 @@ module Diffcrypt
48
51
  data = encrypt_data contents, original_encrypted_contents
49
52
  YAML.dump(
50
53
  'client' => "diffcrypt-#{Diffcrypt::VERSION}",
51
- 'cipher' => CIPHER,
52
- 'checksum' => Digest::MD5.hexdigest(Marshal.dump(data)),
54
+ 'cipher' => @cipher,
53
55
  'data' => data,
54
56
  )
55
57
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './encryptor'
4
+
5
+ module Diffcrypt
6
+ class File
7
+ attr_reader :file
8
+
9
+ def initialize(path)
10
+ @path = ::File.absolute_path path
11
+ end
12
+
13
+ def encrypted?
14
+ to_yaml['cipher']
15
+ end
16
+
17
+ def cipher
18
+ to_yaml['cipher'] || Encryptor::DEFAULT_CIPHER
19
+ end
20
+
21
+ # @return [Boolean]
22
+ def exists?
23
+ ::File.exist?(@path)
24
+ end
25
+
26
+ # @return [String] Raw contents of the file
27
+ def read
28
+ @read ||= ::File.read(@path)
29
+ end
30
+
31
+ def encrypt(key, cipher: DEFAULT_CIPHER)
32
+ return read if encrypted?
33
+
34
+ Encryptor.new(key, cipher: cipher).encrypt(read)
35
+ end
36
+
37
+ # TODO: Add a test to verify this does descrypt properly
38
+ def decrypt(key)
39
+ return read unless encrypted?
40
+
41
+ Encryptor.new(key, cipher: cipher).decrypt(read)
42
+ end
43
+
44
+ def to_yaml
45
+ @to_yaml ||= YAML.safe_load(read)
46
+ end
47
+ end
48
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'fileutils'
3
4
  require 'pathname'
4
5
  require 'tmpdir'
5
6
 
@@ -8,6 +9,8 @@ require 'active_support/core_ext/hash'
8
9
  require 'active_support/core_ext/module/delegation'
9
10
  require 'active_support/core_ext/object/inclusion'
10
11
 
12
+ require 'diffcrypt/file'
13
+
11
14
  module Diffcrypt
12
15
  module Rails
13
16
  class EncryptedConfiguration
@@ -20,13 +23,13 @@ module Diffcrypt
20
23
  delegate_missing_to :options
21
24
 
22
25
  def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
23
- @content_path = Pathname.new(File.absolute_path(config_path)).yield_self do |path|
26
+ @content_path = Pathname.new(::File.absolute_path(config_path)).yield_self do |path|
24
27
  path.symlink? ? path.realpath : path
25
28
  end
29
+ @diffcrypt_file = Diffcrypt::File.new(@content_path)
26
30
  @key_path = Pathname.new(key_path)
27
31
  @env_key = env_key
28
32
  @raise_if_missing_key = raise_if_missing_key
29
- @active_support_encryptor = ActiveSupport::MessageEncryptor.new([key].pack('H*'), cipher: Encryptor::CIPHER)
30
33
  end
31
34
 
32
35
  # Determines if file is using the diffable format, or still
@@ -50,7 +53,7 @@ module Diffcrypt
50
53
  deserialize(contents)
51
54
 
52
55
  IO.binwrite "#{content_path}.tmp", encrypt(contents, original_encrypted_contents)
53
- FileUtils.mv "#{content_path}.tmp", content_path
56
+ ::FileUtils.mv "#{content_path}.tmp", content_path
54
57
  end
55
58
 
56
59
  def config
@@ -72,7 +75,7 @@ module Diffcrypt
72
75
  # rubocop:disable Metrics/AbcSize
73
76
  def writing(contents)
74
77
  tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
75
- tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
78
+ tmp_path = Pathname.new ::File.join(Dir.tmpdir, tmp_file)
76
79
  tmp_path.binwrite contents
77
80
 
78
81
  yield tmp_path
@@ -81,7 +84,7 @@ module Diffcrypt
81
84
 
82
85
  write(updated_contents, content_path_diffable? && content_path.binread)
83
86
  ensure
84
- FileUtils.rm(tmp_path) if tmp_path&.exist?
87
+ ::FileUtils.rm(tmp_path) if tmp_path&.exist?
85
88
  end
86
89
  # rubocop:enable Metrics/AbcSize
87
90
 
@@ -102,9 +105,18 @@ module Diffcrypt
102
105
  end
103
106
  end
104
107
 
108
+ # Rails applications with an existing credentials file, the inbuilt active support encryptor should be used
109
+ # @return [ActiveSupport::MessageEncryptor]
110
+ def active_support_encryptor
111
+ @active_support_encryptor = ActiveSupport::MessageEncryptor.new(
112
+ [key].pack('H*'),
113
+ cipher: @diffcrypt_file.cipher,
114
+ )
115
+ end
116
+
105
117
  # @return [Encryptor]
106
118
  def encryptor
107
- @encryptor ||= Encryptor.new key
119
+ @encryptor ||= Encryptor.new key, cipher: @diffcrypt_file.cipher
108
120
  end
109
121
 
110
122
  def read_env_key
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Diffcrypt
4
- VERSION = '0.2.0'
4
+ VERSION = '0.4.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: diffcrypt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc Qualie
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-28 00:00:00.000000000 Z
11
+ date: 2020-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -24,11 +24,32 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 6.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0.20'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '2'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0.20'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '2'
27
47
  description: Diffable encrypted configuration files that can be safely committed into
28
48
  a git repository
29
49
  email:
30
50
  - marc@marcqualie.com
31
- executables: []
51
+ executables:
52
+ - diffcrypt
32
53
  extensions: []
33
54
  extra_rdoc_files: []
34
55
  files:
@@ -36,17 +57,23 @@ files:
36
57
  - ".github/dependabot.yml"
37
58
  - ".gitignore"
38
59
  - ".rubocop.yml"
60
+ - CHANGELOG.md
39
61
  - Gemfile
40
62
  - LICENSE.txt
41
63
  - README.md
42
64
  - Rakefile
65
+ - SECURITY.md
43
66
  - bin/console
67
+ - bin/diffcrypt
44
68
  - bin/setup
45
69
  - diffcrypt.gemspec
46
70
  - lib/diffcrypt.rb
71
+ - lib/diffcrypt/cli.rb
47
72
  - lib/diffcrypt/encryptor.rb
73
+ - lib/diffcrypt/file.rb
48
74
  - lib/diffcrypt/rails/encrypted_configuration.rb
49
75
  - lib/diffcrypt/version.rb
76
+ - tmp/.keep
50
77
  homepage: https://github.com/marcqualie/diffcrypt
51
78
  licenses:
52
79
  - MIT
@@ -61,7 +88,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
61
88
  requirements:
62
89
  - - ">="
63
90
  - !ruby/object:Gem::Version
64
- version: 2.3.0
91
+ version: 2.5.0
65
92
  required_rubygems_version: !ruby/object:Gem::Requirement
66
93
  requirements:
67
94
  - - ">="