chamber 2.13.0 → 3.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +101 -26
  5. data/lib/chamber.rb +77 -16
  6. data/lib/chamber/adapters/cloud/circle_ci.rb +16 -13
  7. data/lib/chamber/adapters/cloud/heroku.rb +40 -13
  8. data/lib/chamber/binary/circle_ci.rb +28 -14
  9. data/lib/chamber/binary/heroku.rb +34 -14
  10. data/lib/chamber/binary/runner.rb +40 -29
  11. data/lib/chamber/binary/travis.rb +10 -4
  12. data/lib/chamber/commands/base.rb +10 -16
  13. data/lib/chamber/commands/cloud/base.rb +3 -3
  14. data/lib/chamber/commands/cloud/pull.rb +2 -2
  15. data/lib/chamber/commands/cloud/push.rb +7 -7
  16. data/lib/chamber/commands/comparable.rb +2 -2
  17. data/lib/chamber/commands/compare.rb +6 -9
  18. data/lib/chamber/commands/initialize.rb +26 -22
  19. data/lib/chamber/commands/securable.rb +12 -9
  20. data/lib/chamber/commands/secure.rb +2 -2
  21. data/lib/chamber/commands/show.rb +8 -8
  22. data/lib/chamber/commands/sign.rb +2 -2
  23. data/lib/chamber/commands/verify.rb +2 -2
  24. data/lib/chamber/configuration.rb +6 -3
  25. data/lib/chamber/context_resolver.rb +7 -7
  26. data/lib/chamber/encryption_methods/ssl.rb +12 -12
  27. data/lib/chamber/file.rb +20 -15
  28. data/lib/chamber/file_set.rb +18 -8
  29. data/lib/chamber/files/signature.rb +16 -14
  30. data/lib/chamber/filters/decryption_filter.rb +19 -15
  31. data/lib/chamber/filters/encryption_filter.rb +14 -11
  32. data/lib/chamber/filters/environment_filter.rb +21 -20
  33. data/lib/chamber/filters/failed_decryption_filter.rb +9 -6
  34. data/lib/chamber/filters/insecure_filter.rb +4 -5
  35. data/lib/chamber/filters/namespace_filter.rb +13 -9
  36. data/lib/chamber/filters/secure_filter.rb +9 -7
  37. data/lib/chamber/filters/translate_secure_keys_filter.rb +9 -7
  38. data/lib/chamber/instance.rb +48 -31
  39. data/lib/chamber/key_pair.rb +7 -7
  40. data/lib/chamber/keys/base.rb +13 -13
  41. data/lib/chamber/keys/decryption.rb +3 -3
  42. data/lib/chamber/keys/encryption.rb +3 -3
  43. data/lib/chamber/namespace_set.rb +2 -4
  44. data/lib/chamber/refinements/array.rb +20 -0
  45. data/lib/chamber/refinements/deep_dup.rb +58 -0
  46. data/lib/chamber/refinements/enumerable.rb +36 -0
  47. data/lib/chamber/refinements/hash.rb +51 -0
  48. data/lib/chamber/settings.rb +81 -66
  49. data/lib/chamber/types/secured.rb +21 -23
  50. data/lib/chamber/version.rb +1 -1
  51. data/templates/settings.yml +2 -0
  52. metadata +44 -51
  53. metadata.gz.sig +0 -0
  54. data/lib/chamber/core_ext/hash.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e231bd41a0611bd30e8c2cbb2d8cc1178ecc1f570649be797468612fc63e922
4
- data.tar.gz: 19fb0f2721ec1fe8a24ea7244f42f58d2f82e9ca216414b126ec1d72665481ba
3
+ metadata.gz: 3119f8787d3c63913a0ab69a0114cbf6d43a154ad1703aef610a2b02720b0cbc
4
+ data.tar.gz: ed25f8e4a9f93045c94aba4776fa6a0296f9db9f1312e4a1dadeb35343eba140
5
5
  SHA512:
6
- metadata.gz: 45b656980d6e7ecf49f1ae008639506ec0462192a4ec7b58e577c8607db07333f5f2cf646314a86daa5c61c41b5abac6aac50a72ea34b2f69e8282a8e8766310
7
- data.tar.gz: 1ed02191209f650c8f3db3110fc7f7b743b466881a74089115bd00ba4001bd19e0aee8e8c373c5e8622339bf2cb4cc931b52067428da025d519da9f12aaf8c38
6
+ metadata.gz: acc3d5daf9e6570ccf16e76b8433e8d0912cdd85f4858f6634ee37531aa6a1c53b47621afeb21c0b9524eea6f8af3a02b52872f843b28a3f94d3e20bfa582880
7
+ data.tar.gz: 39a117062b19f734066f11c405730e499cc0325e6b2e3fd208aa68a930645e28f6b88f4f98e43133e8b70457d82ac3860f426b1385611b0d4b924003f79f0490
Binary file
data.tar.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -1,5 +1,33 @@
1
- # Chamber
2
- [![Gem Version](https://img.shields.io/gem/v/chamber.svg)](https://rubygems.org/gems/chamber) ![Rubygems Rank Overall](https://img.shields.io/gem/rt/chamber.svg) ![Rubygems Rank Daily](https://img.shields.io/gem/rd/chamber.svg) ![Rubygems Downloads](https://img.shields.io/gem/dv/chamber/stable.svg) [![Build Status](https://img.shields.io/travis/thekompanee/chamber/master.svg)](http://travis-ci.org/thekompanee/chamber) [![Code Climate](https://codeclimate.com/github/thekompanee/chamber.svg)](https://codeclimate.com/github/thekompanee/chamber) [![Code Climate](https://codeclimate.com/github/thekompanee/chamber/coverage.svg)](https://codeclimate.com/github/thekompanee/chamber)
1
+ Chamber
2
+ ================================================================================
3
+
4
+ <div align="center">
5
+ <a href="https://rubygems.org/gems/chamber" alt="RubyGems Version">
6
+ <img src="https://img.shields.io/gem/v/chamber.svg?style=flat-square&label=current-version" alt="RubyGems Version" />
7
+ </a>
8
+
9
+ <a href="https://rubygems.org/gems/chamber" alt="RubyGems Rank Overall">
10
+ <img src="https://img.shields.io/gem/rt/chamber.svg?style=flat-square&label=total-rank" alt="RubyGems Rank Overall" />
11
+ </a>
12
+
13
+ <a href="https://rubygems.org/gems/chamber" alt="RubyGems Rank Daily">
14
+ <img src="https://img.shields.io/gem/rd/chamber.svg?style=flat-square&label=daily-rank" alt="RubyGems Rank Daily" />
15
+ </a>
16
+
17
+ <a href="https://rubygems.org/gems/chamber" alt="RubyGems Downloads">
18
+ <img src="https://img.shields.io/gem/dt/chamber.svg?style=flat-square&label=total-downloads" alt="RubyGems Downloads" />
19
+ </a>
20
+
21
+ <a href="https://github.com/thekompanee/chamber/actions?query=workflow%3ABuild" alt="Build Status">
22
+ <img src="https://img.shields.io/github/workflow/status/thekompanee/chamber/Build?label=CI&style=flat-square&logo=github" alt="Build Status" />
23
+ </a>
24
+
25
+ <a href="#" alt="Maintainability">
26
+ <img src="https://img.shields.io/codeclimate/maintainability/thekompanee/chamber?style=flat-square&label=grade" alt="Maintainability" />
27
+ </a>
28
+ </div>
29
+
30
+ <br>
3
31
 
4
32
  Chamber is the auto-encrypting, extremely organizable, Heroku-loving,
5
33
  CLI-having, non-extra-repo-needing, non-Rails-specific-ing, CI-serving
@@ -9,17 +37,73 @@ We looked at all of the options out there and thought something was still
9
37
  missing, so we wrote Chamber. We made it with lots of ❤ and we hope you like it
10
38
  as much as we do.
11
39
 
12
- ## What Sets Chamber Apart?
40
+ What Sets Chamber Apart
41
+ --------------------------------------------------------------------------------
13
42
 
14
43
  For an idea of how Chamber compares to other popular libraries, check out our
15
44
  [Gem Comparison][comparison].
16
45
 
17
- ## Basic Usage
46
+ Basic Usage
47
+ --------------------------------------------------------------------------------
48
+
49
+ Before starting this guide, make sure you [install chamber][installation].
50
+
51
+ Once your app is initialized, you should have a `settings.yml` file somewhere.
52
+ A lot of times it's the root of your project and sometimes it's in a framework
53
+ specific location.
54
+
55
+ Inside of here you can define any settings you'd like like so:
56
+
57
+ ```yaml
58
+ # settings.yml
59
+
60
+ smtp_username: 'my_username'
61
+ smtp_password: 'my_password'
62
+ ```
63
+
64
+ From there you can access your settings by using the special `Chamber.env`
65
+ constant.
66
+
67
+ ```ruby
68
+ Chamber.env.smtp_password
69
+ # => 'my_password'
70
+ ```
71
+
72
+ If you want to encrypt a setting, prefix the setting name with `_secure_` like
73
+ so:
74
+
75
+ ```ruby
76
+ # settings.yml
77
+
78
+ smtp_username: 'my_username'
79
+ _secure_smtp_password: 'my_password'
80
+ ```
81
+
82
+ And then run `chamber secure`. Your settings file will have an encrypted value:
83
+
84
+ ```ruby
85
+ # settings.yml
86
+
87
+ smtp_username: 'my_username'
88
+ _secure_smtp_password: JL5hAVux4tERpv49QPWxy9H0VC2Rnk7V8/e8+1XOwPcXcoH/a7Lh253UY/v9m8nI/Onb+ZG9nZ082J4M/BmLa+f7jwMEwufIqbUhUah9eKIW8xcxlppBYpl7JVGf2HJF5TfCN44gMQNgGNzboCQXKqRyeGFm4u772Sg9V2gEx/q7qJ6F4jg7v/cltCFLmJfXA2SHA5Dai4p9L4IvMVVJGm34k5j7KOegNqpVWs2RY99cagjPuzc9VM2XSUsXgqcUJdmH8YtPW8Kqkyg0oYlRh6VQWABlWXwTZz74QjTTjqtqfoELIoFTMBDh+cCvuUTAE5m06LhlqauVrB4UnBsd5g==
89
+ ```
90
+
91
+ which you still access the same way because Chamber handles the decryption for
92
+ you:
93
+
94
+ ```ruby
95
+ Chamber.env.smtp_password
96
+ # => 'my_password'
97
+ ```
98
+
99
+ Full Reference
100
+ --------------------------------------------------------------------------------
18
101
 
19
- You can view our Basic Usage Guide [here][basic-usage]. Otherwise, for the full
102
+ There's so much to Chamber, we couldn't put it all in the README. For the full
20
103
  Chamber guide, visit the [wiki][wiki].
21
104
 
22
- ## Credits
105
+ Credits
106
+ --------------------------------------------------------------------------------
23
107
 
24
108
  Chamber was written by [Jeff Felchner][jeff-profile] and
25
109
  [Mark McEahern][mark-profile]
@@ -30,27 +114,18 @@ Chamber is maintained and funded by [The Kompanee, Ltd.][kompanee-site]
30
114
 
31
115
  The names and logos for The Kompanee are trademarks of The Kompanee, Ltd.
32
116
 
33
- ## License
117
+ License
118
+ --------------------------------------------------------------------------------
34
119
 
35
- Chamber is Copyright © 2014-2019 Jeff Felchner and Mark McEahern. It is free
120
+ Chamber is Copyright © 2014-2021 Jeff Felchner and Mark McEahern. It is free
36
121
  software, and may be redistributed under the terms specified in the
37
122
  [LICENSE][license] file.
38
123
 
39
- [accessing]: https://github.com/thekompanee/chamber/wiki/Accessing-Settings
40
- [basic-usage]: https://github.com/thekompanee/chamber/wiki/Basic-Usage
41
- [cli]: https://github.com/thekompanee/chamber/wiki/CLI-Overview
42
- [commit-hook]: https://github.com/thekompanee/chamber/wiki/Git-Commit-Hooks
43
- [comparison]: https://github.com/thekompanee/chamber/wiki/Gem-Comparison
44
- [encryption]: https://github.com/thekompanee/chamber/wiki/Encryption-Basics
45
- [env-vars]: https://github.com/thekompanee/chamber/wiki/Environment-Variables
46
- [heroku]: https://github.com/thekompanee/chamber/wiki/Heroku
47
- [inch]: https://inch-ci.org/github/thekompanee/chamber
48
- [jeff-profile]: https://github.com/jfelchner
49
- [kompanee-logo]: https://kompanee-public-assets.s3.amazonaws.com/readmes/kompanee-horizontal-black.png
50
- [kompanee-site]: http://www.thekompanee.com
51
- [license]: https://github.com/thekompanee/chamber/blob/master/LICENSE.txt
52
- [mark-profile]: https://github.com/m5rk
53
- [namespace-keys]: https://github.com/thekompanee/chamber/wiki/Namespaced-Key-Pairs
54
- [plain-ruby]: https://github.com/thekompanee/chamber/wiki/Installation#in-a-ruby-project-or-ruby-gem
55
- [travis]: https://github.com/thekompanee/chamber/wiki/TravisCI
56
- [wiki]: https://github.com/thekompanee/chamber/wiki
124
+ [comparison]: https://github.com/thekompanee/chamber/wiki/Gem-Comparison
125
+ [jeff-profile]: https://github.com/jfelchner
126
+ [kompanee-logo]: https://kompanee-public-assets.s3.amazonaws.com/readmes/kompanee-horizontal-black.png
127
+ [kompanee-site]: http://www.thekompanee.com
128
+ [license]: https://github.com/thekompanee/chamber/blob/master/LICENSE.txt
129
+ [mark-profile]: https://github.com/m5rk
130
+ [wiki]: https://github.com/thekompanee/chamber/wiki
131
+ [installation]: https://github.com/thekompanee/chamber/wiki/Installation
@@ -7,12 +7,8 @@ require 'chamber/rails'
7
7
  module Chamber
8
8
  attr_writer :instance
9
9
 
10
- def load(options = {})
11
- self.instance = Instance.new(options)
12
- end
13
-
14
- def to_s(options = {})
15
- instance.to_s(options)
10
+ def load(**args)
11
+ self.instance = Instance.new(**args)
16
12
  end
17
13
 
18
14
  def env
@@ -20,24 +16,89 @@ module Chamber
20
16
  end
21
17
 
22
18
  def instance
23
- @instance ||= Instance.new({})
19
+ @instance ||= Instance.new
24
20
  end
25
21
 
26
- def method_missing(name, *args)
27
- return instance.public_send(name, *args) if instance.respond_to?(name)
22
+ def [](key)
23
+ instance.[](key)
24
+ end
28
25
 
29
- super
26
+ def dig!(*args)
27
+ instance.dig!(*args)
30
28
  end
31
29
 
32
- def respond_to_missing?(name, include_private = false)
33
- instance.respond_to?(name, include_private)
30
+ def dig(*args)
31
+ instance.dig(*args)
34
32
  end
35
33
 
36
- module_function :load,
37
- :to_s,
34
+ def configuration
35
+ instance.configuration
36
+ end
37
+
38
+ def decrypt(value, **args)
39
+ instance.decrypt(value, **args)
40
+ end
41
+
42
+ def encrypt(value, **args)
43
+ instance.encrypt(value, **args)
44
+ end
45
+
46
+ def files
47
+ instance.files
48
+ end
49
+
50
+ def filenames
51
+ instance.filenames
52
+ end
53
+
54
+ def namespaces
55
+ instance.namespaces
56
+ end
57
+
58
+ def secure
59
+ instance.secure
60
+ end
61
+
62
+ def sign
63
+ instance.sign
64
+ end
65
+
66
+ def verify
67
+ instance.verify
68
+ end
69
+
70
+ def to_environment
71
+ instance.to_environment
72
+ end
73
+
74
+ def to_hash
75
+ instance.to_hash
76
+ end
77
+
78
+ def to_s(**args)
79
+ return '' unless @instance
80
+
81
+ instance.to_s(**args)
82
+ end
83
+
84
+ module_function :[],
85
+ :configuration,
86
+ :decrypt,
87
+ :dig!,
88
+ :dig,
89
+ :encrypt,
38
90
  :env,
91
+ :filenames,
92
+ :files,
39
93
  :instance,
40
94
  :instance=,
41
- :method_missing,
42
- :respond_to_missing?
95
+ :load,
96
+ :namespaces,
97
+ :respond_to_missing?,
98
+ :secure,
99
+ :sign,
100
+ :to_environment,
101
+ :to_hash,
102
+ :to_s,
103
+ :verify
43
104
  end
@@ -17,11 +17,11 @@ class CircleCi
17
17
  :username,
18
18
  :vcs_type
19
19
 
20
- def initialize(options = {})
21
- self.api_token = options.fetch(:api_token)
22
- self.project = options.fetch(:project)
23
- self.username = options.fetch(:username)
24
- self.vcs_type = options.fetch(:vcs_type)
20
+ def initialize(api_token:, project:, username:, vcs_type:)
21
+ self.api_token = api_token
22
+ self.project = project
23
+ self.username = username
24
+ self.vcs_type = vcs_type
25
25
  end
26
26
 
27
27
  def add_environment_variable(name, value)
@@ -39,18 +39,21 @@ class CircleCi
39
39
  response['name']
40
40
  end
41
41
 
42
+ # rubocop:disable Layout/MultilineAssignmentLayout
42
43
  def environment_variables
43
- @environment_variables ||= begin
44
- request = ::Net::HTTP::Get.new(request_uri(resource: 'envvar'))
44
+ @environment_variables ||= \
45
+ begin
46
+ request = ::Net::HTTP::Get.new(request_uri(resource: 'envvar'))
45
47
 
46
- request.basic_auth api_token, ''
47
- request['Content-Type'] = 'application/json'
48
+ request.basic_auth api_token, ''
49
+ request['Content-Type'] = 'application/json'
48
50
 
49
- ::JSON
50
- .parse(response(request).body)
51
- .each_with_object({}) { |e, m| m[e['name']] = e['value'] }
52
- end
51
+ ::JSON
52
+ .parse(response(request).body)
53
+ .each_with_object({}) { |e, m| m[e['name']] = e['value'] }
54
+ end
53
55
  end
56
+ # rubocop:enable Layout/MultilineAssignmentLayout
54
57
 
55
58
  def remove_environment_variable(name)
56
59
  request = ::Net::HTTP::Delete.new(request_uri(resource: "envvar/#{name}"))
@@ -8,38 +8,65 @@ module Chamber
8
8
  module Adapters
9
9
  module Cloud
10
10
  class Heroku
11
- attr_accessor :app
11
+ API_HOST = 'api.heroku.com'
12
+ API_PORT = 443
13
+ API_BASE_URI = ''
12
14
 
13
- def initialize(options = {})
14
- self.app = options.fetch(:app)
15
+ attr_accessor :api_token,
16
+ :app
17
+
18
+ def initialize(api_token:, app:)
19
+ self.api_token = api_token
20
+ self.app = app
15
21
  end
16
22
 
17
- def add_environment_variable(name, value)
18
- value = value.shellescape unless value =~ /\n/
23
+ def add_environment_variable(name, value) # rubocop:disable Metrics/AbcSize
24
+ value = value.gsub(/\n/, '\n') if value
25
+ request = ::Net::HTTP::Patch.new(config_vars_uri)
26
+
27
+ request['Authorization'] = "Bearer #{api_token}"
28
+ request['Accept'] = 'application/vnd.heroku+json; version=3'
29
+ request['Content-Type'] = 'application/json'
30
+ request.body = ::JSON.dump(::Hash[name, value])
19
31
 
20
- response = heroku(%Q{config:set #{name}="#{value}"})
32
+ response = ::JSON.parse(response(request).body)
21
33
 
22
- fail NameError, "The variable name '#{name}' is invalid" if response.match?(/invalid/)
34
+ fail ::NameError, response['message'] if response['message']
23
35
 
24
36
  response
25
37
  end
26
38
 
27
39
  def environment_variables
28
- @environment_variables ||= ::JSON.parse(heroku('config --json'))
40
+ request = ::Net::HTTP::Get.new(config_vars_uri)
41
+
42
+ request['Authorization'] = "Bearer #{api_token}"
43
+ request['Accept'] = 'application/vnd.heroku+json; version=3'
44
+
45
+ response = ::JSON.parse(response(request).body)
46
+
47
+ fail ::NameError, response['message'] if response['message']
48
+
49
+ response
29
50
  end
30
51
 
31
52
  def remove_environment_variable(name)
32
- heroku("config:unset #{name}")
53
+ add_environment_variable(name, nil)
33
54
  end
34
55
 
35
56
  private
36
57
 
37
- def heroku(command)
38
- Bundler.with_clean_env { `heroku #{command}#{app_option} 2>&1` }
58
+ def config_vars_uri
59
+ "#{API_BASE_URI}/apps/#{app}/config-vars"
60
+ end
61
+
62
+ def response(request)
63
+ connection.request(request)
39
64
  end
40
65
 
41
- def app_option
42
- app ? " --app='#{app}'" : ''
66
+ def connection
67
+ @connection ||= ::Net::HTTP.new(API_HOST, API_PORT).tap do |conn|
68
+ conn.use_ssl = true
69
+ end
43
70
  end
44
71
  end
45
72
  end
@@ -5,11 +5,13 @@ require 'chamber/commands/cloud/clear'
5
5
  require 'chamber/commands/cloud/push'
6
6
  require 'chamber/commands/cloud/pull'
7
7
  require 'chamber/commands/cloud/compare'
8
+ require 'chamber/refinements/hash'
8
9
 
9
10
  module Chamber
10
11
  module Binary
11
- class CircleCi < Thor
12
- include Thor::Actions
12
+ class CircleCi < ::Thor
13
+ include ::Thor::Actions
14
+ using ::Chamber::Refinements::Hash
13
15
 
14
16
  class_option :api_token,
15
17
  type: :string,
@@ -36,8 +38,9 @@ class CircleCi < Thor
36
38
  desc: 'The type of VCS your project is using.',
37
39
  enum: %w{github bitbucket}
38
40
 
39
- desc 'clear', 'Removes all CircleCi environment variables which match settings that ' \
40
- 'Chamber knows about'
41
+ desc 'clear',
42
+ 'Removes all CircleCi environment variables which match settings that Chamber ' \
43
+ 'knows about'
41
44
 
42
45
  method_option :dry_run,
43
46
  type: :boolean,
@@ -46,11 +49,14 @@ class CircleCi < Thor
46
49
  'would change if cleared'
47
50
 
48
51
  def clear
49
- Commands::Cloud::Clear.call(options.merge(shell: self, adapter: 'circle_ci'))
52
+ Commands::Cloud::Clear.call(**options
53
+ .deep_transform_keys(&:to_sym)
54
+ .merge(shell: self, adapter: 'circle_ci'))
50
55
  end
51
56
 
52
- desc 'push', 'Sends settings to CircleCi so that they may be used in the application ' \
53
- 'once it is deployed'
57
+ desc 'push',
58
+ 'Sends settings to CircleCi so that they may be used in the application ' \
59
+ 'once it is deployed'
54
60
 
55
61
  method_option :dry_run,
56
62
  type: :boolean,
@@ -75,11 +81,14 @@ class CircleCi < Thor
75
81
  'will be pushed'
76
82
 
77
83
  def push
78
- Commands::Cloud::Push.call(options.merge(shell: self, adapter: 'circle_ci'))
84
+ Commands::Cloud::Push.call(**options
85
+ .deep_transform_keys(&:to_sym)
86
+ .merge(shell: self, adapter: 'circle_ci'))
79
87
  end
80
88
 
81
- desc 'pull', 'Retrieves the environment variables for the application and stores ' \
82
- 'them in a temporary file'
89
+ desc 'pull',
90
+ 'Retrieves the environment variables for the application and stores them in a ' \
91
+ 'temporary file'
83
92
 
84
93
  method_option :into,
85
94
  type: :string,
@@ -87,11 +96,14 @@ class CircleCi < Thor
87
96
  'stored. This file WILL BE OVERRIDDEN.'
88
97
 
89
98
  def pull
90
- Commands::Cloud::Pull.call(options.merge(shell: self, adapter: 'circle_ci'))
99
+ Commands::Cloud::Pull.call(**options
100
+ .deep_transform_keys(&:to_sym)
101
+ .merge(shell: self, adapter: 'circle_ci'))
91
102
  end
92
103
 
93
- desc 'compare', 'Displays the difference between what is currently stored in the ' \
94
- 'CircleCi application\'s config and what Chamber knows about locally'
104
+ desc 'compare',
105
+ 'Displays the difference between what is currently stored in the ' \
106
+ 'CircleCi application\'s config and what Chamber knows about locally'
95
107
 
96
108
  method_option :only_sensitive,
97
109
  type: :boolean,
@@ -102,7 +114,9 @@ class CircleCi < Thor
102
114
  'which are marked as "_secure"'
103
115
 
104
116
  def compare
105
- Commands::Cloud::Compare.call(options.merge(shell: self, adapter: 'circle_ci'))
117
+ Commands::Cloud::Compare.call(**options
118
+ .deep_transform_keys(&:to_sym)
119
+ .merge(shell: self, adapter: 'circle_ci'))
106
120
  end
107
121
  end
108
122
  end