chamber 3.0.0rc2 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +5 -5
  4. data/lib/chamber/binary/runner.rb +1 -19
  5. data/lib/chamber/commands/base.rb +4 -4
  6. data/lib/chamber/commands/initialize.rb +4 -4
  7. data/lib/chamber/commands/securable.rb +4 -5
  8. data/lib/chamber/commands/show.rb +0 -1
  9. data/lib/chamber/context_resolver.rb +4 -4
  10. data/lib/chamber/errors/disallowed_class.rb +1 -1
  11. data/lib/chamber/errors/invalid_key_type.rb +8 -0
  12. data/lib/chamber/errors/missing_index.rb +13 -0
  13. data/lib/chamber/errors/missing_setting.rb +13 -0
  14. data/lib/chamber/file_set.rb +0 -2
  15. data/lib/chamber/filters/decryption_filter.rb +4 -4
  16. data/lib/chamber/filters/encryption_filter.rb +4 -4
  17. data/lib/chamber/filters/environment_filter.rb +4 -4
  18. data/lib/chamber/filters/failed_decryption_filter.rb +3 -3
  19. data/lib/chamber/filters/namespace_filter.rb +3 -3
  20. data/lib/chamber/filters/secure_filter.rb +3 -3
  21. data/lib/chamber/filters/translate_secure_keys_filter.rb +3 -3
  22. data/lib/chamber/integrations/sinatra.rb +1 -1
  23. data/lib/chamber/keys/base.rb +11 -7
  24. data/lib/chamber/namespace_set.rb +2 -2
  25. data/lib/chamber/rails.rb +1 -1
  26. data/lib/chamber/settings.rb +18 -2
  27. data/lib/chamber/version.rb +1 -1
  28. data.tar.gz.sig +0 -0
  29. metadata +18 -26
  30. metadata.gz.sig +0 -0
  31. data/lib/chamber/adapters/cloud/circle_ci.rb +0 -85
  32. data/lib/chamber/adapters/cloud/heroku.rb +0 -74
  33. data/lib/chamber/binary/circle_ci.rb +0 -121
  34. data/lib/chamber/binary/heroku.rb +0 -109
  35. data/lib/chamber/binary/travis.rb +0 -32
  36. data/lib/chamber/commands/cloud/base.rb +0 -35
  37. data/lib/chamber/commands/cloud/clear.rb +0 -25
  38. data/lib/chamber/commands/cloud/compare.rb +0 -26
  39. data/lib/chamber/commands/cloud/pull.rb +0 -29
  40. data/lib/chamber/commands/cloud/push.rb +0 -44
  41. data/lib/chamber/commands/travis/secure.rb +0 -37
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9de7c74ea902d381006bd6b5f05bae3680d1beef940ae75e483f30c923bc310f
4
- data.tar.gz: b6f45eb5b668a5964e898ffb4caa5b9a3e9bc227736dd0e687e257cff0c99f84
3
+ metadata.gz: 9e37ed83e4c76419880c6abbfdc967f2e82a6fc8824b868904cae1fcb26878d0
4
+ data.tar.gz: a61415b6fcce618cbd452a7373b22b3ea1093cb8150b12df6798021743ff02d3
5
5
  SHA512:
6
- metadata.gz: b1ab7fedfaf6cb26ab3e97402966368f3e686751ca76e441a7d9db64b7fe354a289ac4ac330cac806fb7960f3b4750c1ce4f7799e548c4f0f549ee57826e8552
7
- data.tar.gz: 64cfa0683ca78d71cd620e38e60c4d2c25683c5217d74858995b2ee65eda704f7d2aca9395451ac743424bd75b8974ee088ed3563e192be5ab057f49ad458165
6
+ metadata.gz: c55d1d457aa0a4d908d4be0e790dc86f10895a2d6514613e1d2d27bfb6ebbde69a7d5e4bb4f0695e6f8da1d993f7d820644b3a975b88d2d91237936c15a6c5e3
7
+ data.tar.gz: d0cecb00b849c518ac20f1fefd6c76029e6dbd6a54a9676b28a94529a7df5da9e8a0ef8ea2301cfc9460a627ae168bbab3f07954a48931e5c9a9dd1d60d8f087
checksums.yaml.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -19,7 +19,7 @@ Chamber
19
19
  </a>
20
20
 
21
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" />
22
+ <img src="https://img.shields.io/github/actions/workflow/status/thekompanee/chamber/testing.yml?branch=master&label=CI&style=flat-square&logo=github" alt="Build Status" />
23
23
  </a>
24
24
 
25
25
  <a href="#" alt="Maintainability">
@@ -61,11 +61,11 @@ smtp_username: 'my_username'
61
61
  smtp_password: 'my_password'
62
62
  ```
63
63
 
64
- From there you can access your settings by using the special `Chamber.env`
64
+ From there you can access your settings by using the special `Chamber.dig`
65
65
  constant.
66
66
 
67
67
  ```ruby
68
- Chamber.env.smtp_password
68
+ Chamber.dig('smtp_password')
69
69
  # => 'my_password'
70
70
  ```
71
71
 
@@ -92,7 +92,7 @@ which you still access the same way because Chamber handles the decryption for
92
92
  you:
93
93
 
94
94
  ```ruby
95
- Chamber.env.smtp_password
95
+ Chamber.dig('smtp_password')
96
96
  # => 'my_password'
97
97
  ```
98
98
 
@@ -117,7 +117,7 @@ The names and logos for The Kompanee are trademarks of The Kompanee, Ltd.
117
117
  License
118
118
  --------------------------------------------------------------------------------
119
119
 
120
- Chamber is Copyright © 2014-2021 Jeff Felchner and Mark McEahern. It is free
120
+ Chamber is Copyright © 2014-2023 Jeff Felchner and Mark McEahern. It is free
121
121
  software, and may be redistributed under the terms specified in the
122
122
  [LICENSE][license] file.
123
123
 
@@ -2,9 +2,6 @@
2
2
 
3
3
  require 'thor'
4
4
  require 'chamber/rubinius_fix'
5
- require 'chamber/binary/travis'
6
- require 'chamber/binary/heroku'
7
- require 'chamber/binary/circle_ci'
8
5
  require 'chamber/commands/show'
9
6
  require 'chamber/commands/files'
10
7
  require 'chamber/commands/secure'
@@ -23,7 +20,7 @@ class Runner < Thor
23
20
  class_option :rootpath,
24
21
  type: :string,
25
22
  aliases: '-r',
26
- default: ENV['PWD'],
23
+ default: ENV.fetch('PWD', nil),
27
24
  desc: 'The root filepath of the application'
28
25
 
29
26
  class_option :basepath,
@@ -62,21 +59,6 @@ class Runner < Thor
62
59
 
63
60
  ################################################################################
64
61
 
65
- desc 'travis SUBCOMMAND ...ARGS', 'For manipulating Travis CI environment variables'
66
- subcommand 'travis', Chamber::Binary::Travis
67
-
68
- ################################################################################
69
-
70
- desc 'heroku SUBCOMMAND ...ARGS', 'For manipulating Heroku environment variables'
71
- subcommand 'heroku', Chamber::Binary::Heroku
72
-
73
- ################################################################################
74
-
75
- desc 'circleci SUBCOMMAND ...ARGS', 'For manipulating CircleCI environment variables'
76
- subcommand 'circleci', Chamber::Binary::CircleCi
77
-
78
- ################################################################################
79
-
80
62
  desc 'show', 'Displays the list of settings and their values'
81
63
 
82
64
  method_option :as_env,
@@ -6,15 +6,15 @@ require 'chamber/instance'
6
6
  module Chamber
7
7
  module Commands
8
8
  class Base
9
- def self.call(**args)
10
- new(**args).call
11
- end
12
-
13
9
  attr_accessor :chamber,
14
10
  :dry_run,
15
11
  :rootpath,
16
12
  :shell
17
13
 
14
+ def self.call(**args)
15
+ new(**args).call
16
+ end
17
+
18
18
  def initialize(shell: nil, rootpath: nil, dry_run: nil, **args)
19
19
  self.chamber = Chamber::Instance.new(rootpath: rootpath, **args)
20
20
  self.shell = shell
@@ -11,14 +11,14 @@ require 'chamber/commands/base'
11
11
  module Chamber
12
12
  module Commands
13
13
  class Initialize < Chamber::Commands::Base
14
- def self.call(**args)
15
- new(**args).call
16
- end
17
-
18
14
  attr_accessor :basepath,
19
15
  :namespaces,
20
16
  :signature
21
17
 
18
+ def self.call(**args)
19
+ new(**args).call
20
+ end
21
+
22
22
  def initialize(signature:, namespaces: [], **args)
23
23
  super(**args)
24
24
 
@@ -52,11 +52,10 @@ module Securable
52
52
  Shellwords.escape(filename)
53
53
  end
54
54
 
55
- `
56
- git ls-files --other --ignored --exclude-per-directory=.gitignore |
57
- sed -e "s|^|#{Shellwords.escape(rootpath.to_s)}/|" |
58
- grep --colour=never -E '#{shell_escaped_chamber_filenames.join('|')}'
59
- `.split("\n")
55
+ `git ls-files --other --ignored --exclude-per-directory=.gitignore`
56
+ .split("\n")
57
+ .map { |filename| "#{Shellwords.escape(rootpath.to_s)}/#{filename}" }
58
+ .select { |filename| shell_escaped_chamber_filenames.include?(filename) }
60
59
  end
61
60
  end
62
61
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pp'
4
3
  require 'chamber/commands/base'
5
4
 
6
5
  module Chamber
@@ -10,6 +10,10 @@ module Chamber
10
10
  class ContextResolver
11
11
  attr_accessor :options
12
12
 
13
+ def self.resolve(**args)
14
+ new(**args).resolve
15
+ end
16
+
13
17
  def initialize(**args)
14
18
  self.options = args
15
19
  end
@@ -48,10 +52,6 @@ class ContextResolver
48
52
  end
49
53
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Layout/LineLength
50
54
 
51
- def self.resolve(**args)
52
- new(**args).resolve
53
- end
54
-
55
55
  protected
56
56
 
57
57
  def resolve_namespaces(other)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Chamber
4
4
  module Errors
5
- class DisallowedClass < ::Psych::DisallowedClass
5
+ class DisallowedClass < ::ArgumentError
6
6
  end
7
7
  end
8
8
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chamber
4
+ module Errors
5
+ class InvalidKeyType < ::ArgumentError
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chamber
4
+ module Errors
5
+ class MissingIndex < ::IndexError
6
+ def initialize(missing_index, all_keys)
7
+ super(<<~HEREDOC.chomp)
8
+ You attempted to access setting '#{all_keys.join(':')}' but the index '#{missing_index}' in the array did not exist.
9
+ HEREDOC
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chamber
4
+ module Errors
5
+ class MissingSetting < ::KeyError
6
+ def initialize(missing_key, all_keys)
7
+ super(<<~HEREDOC.chomp)
8
+ You attempted to access setting '#{all_keys.join(':')}' but '#{missing_key}' did not exist.
9
+ HEREDOC
10
+ end
11
+ end
12
+ end
13
+ end
@@ -256,7 +256,6 @@ class FileSet
256
256
 
257
257
  private
258
258
 
259
- # rubocop:disable Performance/ChainArrayAllocation
260
259
  def all_files
261
260
  @all_files ||= file_globs
262
261
  .map { |fg| Pathname.glob(fg) }
@@ -264,7 +263,6 @@ class FileSet
264
263
  .uniq
265
264
  .sort
266
265
  end
267
- # rubocop:enable Performance/ChainArrayAllocation
268
266
 
269
267
  def non_namespaced_files
270
268
  @non_namespaced_files ||= all_files - namespaced_files
@@ -35,16 +35,16 @@ class DecryptionFilter
35
35
  :secure_key_token
36
36
  attr_reader :decryption_keys
37
37
 
38
+ def self.execute(**args)
39
+ new(**args).__send__(:execute)
40
+ end
41
+
38
42
  def initialize(data:, secure_key_prefix:, decryption_keys: {}, **_args)
39
43
  self.decryption_keys = (decryption_keys || {}).transform_keys(&:to_s)
40
44
  self.data = data.deep_dup
41
45
  self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
42
46
  end
43
47
 
44
- def self.execute(**args)
45
- new(**args).__send__(:execute)
46
- end
47
-
48
48
  protected
49
49
 
50
50
  def execute(raw_data = data)
@@ -28,16 +28,16 @@ class EncryptionFilter
28
28
  :secure_key_token
29
29
  attr_reader :encryption_keys
30
30
 
31
+ def self.execute(**args)
32
+ new(**args).__send__(:execute)
33
+ end
34
+
31
35
  def initialize(data:, secure_key_prefix:, encryption_keys: {}, **_args)
32
36
  self.encryption_keys = (encryption_keys || {}).transform_keys(&:to_s)
33
37
  self.data = data.deep_dup
34
38
  self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
35
39
  end
36
40
 
37
- def self.execute(**args)
38
- new(**args).__send__(:execute)
39
- end
40
-
41
41
  protected
42
42
 
43
43
  def execute(raw_data = data, namespace = nil)
@@ -7,6 +7,9 @@ require 'chamber/errors/environment_conversion'
7
7
  module Chamber
8
8
  module Filters
9
9
  class EnvironmentFilter
10
+ attr_accessor :data,
11
+ :secure_key_token
12
+
10
13
  ###
11
14
  # Internal: Allows the existing environment to be injected into the passed in
12
15
  # hash. The hash that is passed in is *not* modified, instead a new hash is
@@ -91,9 +94,6 @@ class EnvironmentFilter
91
94
  new(**args).__send__(:execute)
92
95
  end
93
96
 
94
- attr_accessor :data,
95
- :secure_key_token
96
-
97
97
  def initialize(data:, secure_key_prefix:, **_args)
98
98
  self.data = data
99
99
  self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
@@ -111,7 +111,7 @@ class EnvironmentFilter
111
111
  lambda do |key, value, environment_key|
112
112
  {
113
113
  key => convert_environment_value(environment_key,
114
- ENV[environment_key],
114
+ ENV.fetch(environment_key, nil),
115
115
  value),
116
116
  }
117
117
  end,
@@ -10,13 +10,13 @@ class FailedDecryptionFilter
10
10
 
11
11
  BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9+/]{342}==\z}.freeze
12
12
 
13
+ attr_accessor :data,
14
+ :secure_key_token
15
+
13
16
  def self.execute(**args)
14
17
  new(**args).__send__(:execute)
15
18
  end
16
19
 
17
- attr_accessor :data,
18
- :secure_key_token
19
-
20
20
  def initialize(data:, secure_key_prefix:, **_args)
21
21
  self.data = data.deep_dup
22
22
  self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
@@ -9,13 +9,13 @@ class NamespaceFilter
9
9
  using ::Chamber::Refinements::DeepDup
10
10
  using ::Chamber::Refinements::Hash
11
11
 
12
+ attr_accessor :data,
13
+ :namespaces
14
+
12
15
  def self.execute(**args)
13
16
  new(**args).__send__(:execute)
14
17
  end
15
18
 
16
- attr_accessor :data,
17
- :namespaces
18
-
19
19
  def initialize(data:, namespaces:, **_args)
20
20
  self.data = data.deep_dup
21
21
  self.namespaces = namespaces
@@ -7,13 +7,13 @@ module Filters
7
7
  class SecureFilter
8
8
  using ::Chamber::Refinements::DeepDup
9
9
 
10
+ attr_accessor :data,
11
+ :secure_key_token
12
+
10
13
  def self.execute(**args)
11
14
  new(**args).__send__(:execute)
12
15
  end
13
16
 
14
- attr_accessor :data,
15
- :secure_key_token
16
-
17
17
  def initialize(data:, secure_key_prefix:, **_args)
18
18
  self.data = data.deep_dup
19
19
  self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
@@ -7,13 +7,13 @@ module Filters
7
7
  class TranslateSecureKeysFilter
8
8
  using ::Chamber::Refinements::DeepDup
9
9
 
10
+ attr_accessor :data,
11
+ :secure_key_token
12
+
10
13
  def self.execute(**args)
11
14
  new(**args).__send__(:execute)
12
15
  end
13
16
 
14
- attr_accessor :data,
15
- :secure_key_token
16
-
17
17
  def initialize(data:, secure_key_prefix:, **_args)
18
18
  self.data = data.deep_dup
19
19
  self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
@@ -7,7 +7,7 @@ module Integrations
7
7
  module Sinatra
8
8
  def self.registered(app)
9
9
  app.configure do |inner_app|
10
- env = inner_app.environment || ENV['RACK_ENV']
10
+ env = inner_app.environment || ENV.fetch('RACK_ENV', nil)
11
11
  root = inner_app.root
12
12
 
13
13
  if defined?(Padrino)
@@ -3,14 +3,14 @@
3
3
  module Chamber
4
4
  module Keys
5
5
  class Base
6
- def self.resolve(**args)
7
- new(**args).resolve
8
- end
9
-
10
6
  attr_accessor :rootpath
11
7
  attr_reader :filenames,
12
8
  :namespaces
13
9
 
10
+ def self.resolve(**args)
11
+ new(**args).resolve
12
+ end
13
+
14
14
  def initialize(rootpath:, namespaces:, filenames: nil)
15
15
  self.rootpath = Pathname.new(rootpath)
16
16
  self.namespaces = namespaces
@@ -20,7 +20,11 @@ class Base
20
20
  def resolve
21
21
  key_paths.each_with_object({}) do |path, memo|
22
22
  namespace = namespace_from_path(path) || '__default'
23
- value = path.readable? ? path.read : ENV[environment_variable_from_path(path)]
23
+ value = if path.readable?
24
+ path.read
25
+ else
26
+ ENV.fetch(environment_variable_from_path(path), nil)
27
+ end
24
28
 
25
29
  memo[namespace.downcase.to_sym] = value if value
26
30
  end
@@ -39,13 +43,13 @@ class Base
39
43
  namespaces.map { |n| namespace_to_key_path(n) }
40
44
  end
41
45
 
42
- # rubocop:disable Performance/ChainArrayAllocation, Performance/MapCompact
46
+ # rubocop:disable Performance/MapCompact
43
47
  def filenames=(other)
44
48
  @filenames = Array(other)
45
49
  .map { |o| Pathname.new(o) }
46
50
  .compact
47
51
  end
48
- # rubocop:enable Performance/ChainArrayAllocation, Performance/MapCompact
52
+ # rubocop:enable Performance/MapCompact
49
53
 
50
54
  def namespaces=(other)
51
55
  @namespaces = other + %w{signature}
@@ -14,6 +14,8 @@ module Chamber
14
14
  class NamespaceSet
15
15
  include Enumerable
16
16
 
17
+ attr_reader :raw_namespaces
18
+
17
19
  ###
18
20
  # Internal: Allows for more compact NamespaceSet creation by giving a list of
19
21
  # namespace values.
@@ -28,8 +30,6 @@ class NamespaceSet
28
30
  new(namespace_values)
29
31
  end
30
32
 
31
- attr_reader :raw_namespaces
32
-
33
33
  ###
34
34
  # Internal: Creates a new NamespaceSet from arrays, hashes and sets.
35
35
  #
data/lib/chamber/rails.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'chamber/integrations/rails' if defined?(::Rails)
3
+ require 'chamber/integrations/rails' if defined?(Rails)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'chamber/errors/missing_index'
4
+ require 'chamber/errors/missing_setting'
3
5
  require 'chamber/namespace_set'
4
6
  require 'chamber/filters/namespace_filter'
5
7
  require 'chamber/filters/encryption_filter'
@@ -236,9 +238,13 @@ class Settings
236
238
  end
237
239
 
238
240
  def [](key)
239
- fail ::ArgumentError, 'Bracket access with anything other than a String is unsupported.' unless key.is_a?(::String)
241
+ fail ::Chamber::Errors::InvalidKeyType, 'Bracket access with anything other than a String is unsupported.' unless key.is_a?(::String)
240
242
 
241
243
  data.fetch(key)
244
+ rescue ::KeyError => error
245
+ missing_setting_name = error.message.gsub(/.*key not found: "([^"]+)".*/, '\1')
246
+
247
+ raise ::Chamber::Errors::MissingSetting.new(missing_setting_name, [])
242
248
  end
243
249
 
244
250
  def dig!(*args)
@@ -247,11 +253,21 @@ class Settings
247
253
 
248
254
  data_value.fetch(key)
249
255
  end
256
+ rescue ::KeyError => error
257
+ missing_setting_name = error.message.gsub(/.*key not found: "([^"]+)".*/, '\1')
258
+
259
+ raise ::Chamber::Errors::MissingSetting.new(missing_setting_name, args)
260
+ rescue ::IndexError => error
261
+ missing_index_number = error.message.gsub(/.*index (\d+) outside.*/, '\1')
262
+
263
+ raise ::Chamber::Errors::MissingIndex.new(missing_index_number, args)
250
264
  end
251
265
 
252
266
  def dig(*args)
253
267
  dig!(*args)
254
- rescue ::KeyError, ::IndexError # rubocop:disable Lint/ShadowedException
268
+ rescue ::Chamber::Errors::MissingSetting,
269
+ ::Chamber::Errors::MissingIndex
270
+
255
271
  nil
256
272
  end
257
273
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Chamber
4
- VERSION = '3.0.0rc2'
4
+ VERSION = '3.0.1'
5
5
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chamber
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0rc2
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - thekompanee
@@ -14,8 +14,8 @@ cert_chain:
14
14
  - |
15
15
  -----BEGIN CERTIFICATE-----
16
16
  MIIEdjCCAt6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAyMTAwLgYDVQQDDCdhY2Nv
17
- dW50c19ydWJ5Z2Vtcy9EQz10aGVrb21wYW5lZS9EQz1jb20wHhcNMjIwMzA1MjM0
18
- OTEzWhcNMjMwMzA1MjM0OTEzWjAyMTAwLgYDVQQDDCdhY2NvdW50c19ydWJ5Z2Vt
17
+ dW50c19ydWJ5Z2Vtcy9EQz10aGVrb21wYW5lZS9EQz1jb20wHhcNMjMwMzA2MDM0
18
+ ODUxWhcNMjYwMzA1MDM0ODUxWjAyMTAwLgYDVQQDDCdhY2NvdW50c19ydWJ5Z2Vt
19
19
  cy9EQz10aGVrb21wYW5lZS9EQz1jb20wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
20
20
  ggGKAoIBgQD0Z84PxtE0iiWCMTQbnit6D4w55GGBQZnhpWUCJwC0SpQ/jnT0Fsma
21
21
  g8oAIdDclLvLC9jzqSAmkOujlpkJMb5NabgkhKFwHi6cVW/gz/cVnISAv8LQTIM5
@@ -28,17 +28,17 @@ cert_chain:
28
28
  y7jqKMpOE1UCAwEAAaOBljCBkzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNV
29
29
  HQ4EFgQU7+XQuN042fZGvzLhYbIwDfsxZV8wLAYDVR0RBCUwI4EhYWNjb3VudHMr
30
30
  cnVieWdlbXNAdGhla29tcGFuZWUuY29tMCwGA1UdEgQlMCOBIWFjY291bnRzK3J1
31
- YnlnZW1zQHRoZWtvbXBhbmVlLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEA04F3jVFD
32
- BwHv8GVMkvUAc7r247lEEYfYuU/Iq0fivT1ugxN9pqT/ODwyPSdYy4Aqj8j4HHbM
33
- 2OQcKXb9SXjlIa/u5McPlhbsTQozs77bXOmrlAXN6shRJtTKSKm5ttmM/sDeks6p
34
- wdhM0KHu5PBFZQjWfJuqi0hH13l0qQH+8r2GzXTHMKNX+6m1cTAkP81OPFIekn0l
35
- boFRgsIr1j335pLV/+hgCRNSlU84E59YVVm+W9kP0Ym/n6051mBaaEMsWnm3td7a
36
- c7BNPTxfmZrtz3TVq9VvzdHad3/+1QdNl9+l3VdL7wZ3GKZLhyifn7dc5EXxiZHJ
37
- eDcSScq4x5NTMajXoJLKcoQPJDL7rUpPtvGj3v9O20RzHlWVDqVdzeYlswDjIqwe
38
- ZjvLRaDI6IVoq0skZju//VZLiN6slVhAYYQj0uka/T0DZieabVYDcT4BVpa9M7Gz
39
- CDW/VDWjvEEbsCIW0oYhtUrkqE8GLIdrpLUjefOERbS5TslD7lG/MH5k
31
+ YnlnZW1zQHRoZWtvbXBhbmVlLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEAIaR7p1yv
32
+ +nE+JX/7nItZ0YKE71L6JdAuAlUfvTuHq8S1dpKdewrm3ewNPGt2nK9svy8vxNUQ
33
+ AqT3KWIaGVwkslL/y/MRxBQHOOZz9XCMmUYXIencNXqNzkWLubcCK7uF1h8mRS46
34
+ jvGfiXfYDwi8QzqJA8IFEK/oyKCCKlNiIbz8Q4yHM4wTqLwjYQqlHiBEgCF2UHc6
35
+ 0tCTD8eixdZUeuexdB7M7F/M086y1wjWyYCeijqbKmTPqKb8n6AmLo0fqkNBVibJ
36
+ nYVEOECVzKt/f3PoG0Z2oMnZ6FKqpFiDpPqgAlRDmVznjpMdZR204/t79NoEVlLf
37
+ lxz9Q1u83TUBUPvDRzKg6Kq9P28JV1Ipnst0ecQZOsnoLjl1BmxUt29hQPJKjd11
38
+ Z6HMkN0PHJ6eG0Zl3/H4H8Xb+KreWlEx3sXXfZj6UscrdHAVffRQnM1E98PCqnRX
39
+ l5EwT4ShG/HorJMQSTY1EoBLZf54NrD5WlWcfM0CLrcvT7QM77dIqmue
40
40
  -----END CERTIFICATE-----
41
- date: 2022-03-06 00:00:00.000000000 Z
41
+ date: 2023-03-07 00:00:00.000000000 Z
42
42
  dependencies:
43
43
  - !ruby/object:Gem::Dependency
44
44
  name: thor
@@ -144,18 +144,8 @@ files:
144
144
  - README.md
145
145
  - bin/chamber
146
146
  - lib/chamber.rb
147
- - lib/chamber/adapters/cloud/circle_ci.rb
148
- - lib/chamber/adapters/cloud/heroku.rb
149
- - lib/chamber/binary/circle_ci.rb
150
- - lib/chamber/binary/heroku.rb
151
147
  - lib/chamber/binary/runner.rb
152
- - lib/chamber/binary/travis.rb
153
148
  - lib/chamber/commands/base.rb
154
- - lib/chamber/commands/cloud/base.rb
155
- - lib/chamber/commands/cloud/clear.rb
156
- - lib/chamber/commands/cloud/compare.rb
157
- - lib/chamber/commands/cloud/pull.rb
158
- - lib/chamber/commands/cloud/push.rb
159
149
  - lib/chamber/commands/comparable.rb
160
150
  - lib/chamber/commands/compare.rb
161
151
  - lib/chamber/commands/files.rb
@@ -165,7 +155,6 @@ files:
165
155
  - lib/chamber/commands/show.rb
166
156
  - lib/chamber/commands/sign.rb
167
157
  - lib/chamber/commands/travis.rb
168
- - lib/chamber/commands/travis/secure.rb
169
158
  - lib/chamber/commands/verify.rb
170
159
  - lib/chamber/configuration.rb
171
160
  - lib/chamber/context_resolver.rb
@@ -175,6 +164,9 @@ files:
175
164
  - lib/chamber/errors/decryption_failure.rb
176
165
  - lib/chamber/errors/disallowed_class.rb
177
166
  - lib/chamber/errors/environment_conversion.rb
167
+ - lib/chamber/errors/invalid_key_type.rb
168
+ - lib/chamber/errors/missing_index.rb
169
+ - lib/chamber/errors/missing_setting.rb
178
170
  - lib/chamber/errors/non_conforming_key.rb
179
171
  - lib/chamber/file.rb
180
172
  - lib/chamber/file_set.rb
@@ -226,9 +218,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
226
218
  version: 2.7.5
227
219
  required_rubygems_version: !ruby/object:Gem::Requirement
228
220
  requirements:
229
- - - ">"
221
+ - - ">="
230
222
  - !ruby/object:Gem::Version
231
- version: 1.3.1
223
+ version: '0'
232
224
  requirements: []
233
225
  rubygems_version: 3.1.6
234
226
  signing_key:
metadata.gz.sig CHANGED
Binary file
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'net/http'
5
- require 'uri'
6
-
7
- module Chamber
8
- module Adapters
9
- module Cloud
10
- class CircleCi
11
- API_HOST = 'circleci.com'
12
- API_PORT = 443
13
- API_BASE_URI = '/api/v1.1'
14
-
15
- attr_accessor :api_token,
16
- :project,
17
- :username,
18
- :vcs_type
19
-
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
- end
26
-
27
- def add_environment_variable(name, value)
28
- value = value.gsub(/\n/, '\n')
29
- request = ::Net::HTTP::Post.new(request_uri(resource: 'envvar'))
30
-
31
- request.basic_auth api_token, ''
32
- request['Content-Type'] = 'application/json'
33
- request.body = ::JSON.dump(name: name, value: value)
34
-
35
- response = ::JSON.parse(response(request).body)
36
-
37
- fail NameError, response['message'] if response['message']
38
-
39
- response['name']
40
- end
41
-
42
- # rubocop:disable Layout/MultilineAssignmentLayout
43
- def environment_variables
44
- @environment_variables ||= \
45
- begin
46
- request = ::Net::HTTP::Get.new(request_uri(resource: 'envvar'))
47
-
48
- request.basic_auth api_token, ''
49
- request['Content-Type'] = 'application/json'
50
-
51
- ::JSON
52
- .parse(response(request).body)
53
- .each_with_object({}) { |e, m| m[e['name']] = e['value'] }
54
- end
55
- end
56
- # rubocop:enable Layout/MultilineAssignmentLayout
57
-
58
- def remove_environment_variable(name)
59
- request = ::Net::HTTP::Delete.new(request_uri(resource: "envvar/#{name}"))
60
-
61
- request.basic_auth api_token, ''
62
- request['Content-Type'] = 'application/json'
63
-
64
- ::JSON.parse(response(request).body)['message'] == 'ok'
65
- end
66
-
67
- private
68
-
69
- def request_uri(resource:)
70
- "#{API_BASE_URI}/project/#{vcs_type}/#{username}/#{project}/#{resource}"
71
- end
72
-
73
- def response(request)
74
- connection.request(request)
75
- end
76
-
77
- def connection
78
- @connection ||= ::Net::HTTP.new(API_HOST, API_PORT).tap do |conn|
79
- conn.use_ssl = true
80
- end
81
- end
82
- end
83
- end
84
- end
85
- end
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'net/http'
5
- require 'uri'
6
-
7
- module Chamber
8
- module Adapters
9
- module Cloud
10
- class Heroku
11
- API_HOST = 'api.heroku.com'
12
- API_PORT = 443
13
- API_BASE_URI = ''
14
-
15
- attr_accessor :api_token,
16
- :app
17
-
18
- def initialize(api_token:, app:)
19
- self.api_token = api_token
20
- self.app = app
21
- end
22
-
23
- def add_environment_variable(name, value)
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({ name => value })
31
-
32
- response = ::JSON.parse(response(request).body)
33
-
34
- fail NameError, response['message'] if response['message']
35
-
36
- response
37
- end
38
-
39
- def environment_variables
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
50
- end
51
-
52
- def remove_environment_variable(name)
53
- add_environment_variable(name, nil)
54
- end
55
-
56
- private
57
-
58
- def config_vars_uri
59
- "#{API_BASE_URI}/apps/#{app}/config-vars"
60
- end
61
-
62
- def response(request)
63
- connection.request(request)
64
- end
65
-
66
- def connection
67
- @connection ||= ::Net::HTTP.new(API_HOST, API_PORT).tap do |conn|
68
- conn.use_ssl = true
69
- end
70
- end
71
- end
72
- end
73
- end
74
- end
@@ -1,121 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'thor'
4
- require 'chamber/commands/cloud/clear'
5
- require 'chamber/commands/cloud/push'
6
- require 'chamber/commands/cloud/pull'
7
- require 'chamber/commands/cloud/compare'
8
-
9
- module Chamber
10
- module Binary
11
- class CircleCi < Thor
12
- include Thor::Actions
13
-
14
- class_option :api_token,
15
- type: :string,
16
- aliases: '-t',
17
- required: true,
18
- desc: 'The API token to access your CircleCI project.'
19
-
20
- class_option :project,
21
- type: :string,
22
- aliases: '-p',
23
- required: true,
24
- desc: 'The project name in your VCS (eg Github).'
25
-
26
- class_option :username,
27
- type: :string,
28
- aliases: '-u',
29
- required: true,
30
- desc: 'The user/organization name in your VCS (eg Github).'
31
-
32
- class_option :vcs_type,
33
- type: :string,
34
- aliases: '-v',
35
- default: 'github',
36
- desc: 'The type of VCS your project is using.',
37
- enum: %w{github bitbucket}
38
-
39
- desc 'clear',
40
- 'Removes all CircleCi environment variables which match settings that Chamber ' \
41
- 'knows about'
42
-
43
- method_option :dry_run,
44
- type: :boolean,
45
- aliases: '-d',
46
- desc: 'Does not actually remove anything, but instead displays what ' \
47
- 'would change if cleared'
48
-
49
- def clear
50
- Commands::Cloud::Clear.call(**options
51
- .transform_keys(&:to_sym)
52
- .merge(shell: self, adapter: 'circle_ci'))
53
- end
54
-
55
- desc 'push',
56
- 'Sends settings to CircleCi so that they may be used in the application ' \
57
- 'once it is deployed'
58
-
59
- method_option :dry_run,
60
- type: :boolean,
61
- aliases: '-d',
62
- desc: 'Does not actually push anything to CircleCi, but instead ' \
63
- 'displays what would change if pushed'
64
-
65
- method_option :keys,
66
- type: :boolean,
67
- aliases: '-k',
68
- desc: 'Pushes private Chamber keys to CircleCi as environment ' \
69
- 'variables. Chamber will automatically detect it and ' \
70
- 'transparently decrypt your secure settings without any ' \
71
- 'further synchronization.'
72
-
73
- method_option :only_sensitive,
74
- type: :boolean,
75
- aliases: '-o',
76
- default: true,
77
- desc: 'When enabled, only settings contained in files which have ' \
78
- 'been gitignored or settings which are marked as "_secure" ' \
79
- 'will be pushed'
80
-
81
- def push
82
- Commands::Cloud::Push.call(**options
83
- .transform_keys(&:to_sym)
84
- .merge(shell: self, adapter: 'circle_ci'))
85
- end
86
-
87
- desc 'pull',
88
- 'Retrieves the environment variables for the application and stores them in a ' \
89
- 'temporary file'
90
-
91
- method_option :into,
92
- type: :string,
93
- desc: 'The file into which the CircleCi config information should be ' \
94
- 'stored. This file WILL BE OVERRIDDEN.'
95
-
96
- def pull
97
- Commands::Cloud::Pull.call(**options
98
- .transform_keys(&:to_sym)
99
- .merge(shell: self, adapter: 'circle_ci'))
100
- end
101
-
102
- desc 'compare',
103
- 'Displays the difference between what is currently stored in the ' \
104
- 'CircleCi application\'s config and what Chamber knows about locally'
105
-
106
- method_option :only_sensitive,
107
- type: :boolean,
108
- aliases: '-o',
109
- default: true,
110
- desc: 'When enabled, the diff will only consider settings ' \
111
- 'contained in files which have been gitignored or settings ' \
112
- 'which are marked as "_secure"'
113
-
114
- def compare
115
- Commands::Cloud::Compare.call(**options
116
- .transform_keys(&:to_sym)
117
- .merge(shell: self, adapter: 'circle_ci'))
118
- end
119
- end
120
- end
121
- end
@@ -1,109 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'thor'
4
- require 'chamber/commands/cloud/clear'
5
- require 'chamber/commands/cloud/push'
6
- require 'chamber/commands/cloud/pull'
7
- require 'chamber/commands/cloud/compare'
8
-
9
- module Chamber
10
- module Binary
11
- class Heroku < Thor
12
- include Thor::Actions
13
-
14
- class_option :app,
15
- type: :string,
16
- aliases: '-a',
17
- required: true,
18
- desc: 'The name of the Heroku application whose config values will ' \
19
- 'be affected'
20
-
21
- class_option :api_token,
22
- type: :string,
23
- aliases: '-t',
24
- required: true,
25
- desc: 'The API token to access your Heroku project.'
26
-
27
- desc 'clear',
28
- 'Removes all Heroku environment variables which match settings that ' \
29
- 'Chamber knows about'
30
-
31
- method_option :dry_run,
32
- type: :boolean,
33
- aliases: '-d',
34
- desc: 'Does not actually remove anything, but instead displays what ' \
35
- 'would change if cleared'
36
-
37
- def clear
38
- Commands::Cloud::Clear.call(**options
39
- .transform_keys(&:to_sym)
40
- .merge(shell: self, adapter: 'heroku'))
41
- end
42
-
43
- desc 'push',
44
- 'Sends settings to Heroku so that they may be used in the application ' \
45
- 'once it is deployed'
46
-
47
- method_option :dry_run,
48
- type: :boolean,
49
- aliases: '-d',
50
- desc: 'Does not actually push anything to Heroku, but instead ' \
51
- 'displays what would change if pushed'
52
-
53
- method_option :keys,
54
- type: :boolean,
55
- aliases: '-k',
56
- desc: 'Pushes private Chamber keys to Heroku as environment ' \
57
- 'variables. Chamber will automatically detect it and ' \
58
- 'transparently decrypt your secure settings without any ' \
59
- 'further synchronization.'
60
-
61
- method_option :only_sensitive,
62
- type: :boolean,
63
- aliases: '-o',
64
- default: true,
65
- desc: 'When enabled, only settings contained in files which have ' \
66
- 'been gitignored or settings which are marked as "_secure" ' \
67
- 'will be pushed'
68
-
69
- def push
70
- Commands::Cloud::Push.call(**options
71
- .transform_keys(&:to_sym)
72
- .merge(shell: self, adapter: 'heroku'))
73
- end
74
-
75
- desc 'pull',
76
- 'Retrieves the environment variables for the application and stores ' \
77
- 'them in a temporary file'
78
-
79
- method_option :into,
80
- type: :string,
81
- desc: 'The file into which the Heroku config information should be ' \
82
- 'stored. This file WILL BE OVERRIDDEN.'
83
-
84
- def pull
85
- Commands::Cloud::Pull.call(**options
86
- .transform_keys(&:to_sym)
87
- .merge(shell: self, adapter: 'heroku'))
88
- end
89
-
90
- desc 'compare',
91
- 'Displays the difference between what is currently stored in the ' \
92
- 'Heroku application\'s config and what Chamber knows about locally'
93
-
94
- method_option :only_sensitive,
95
- type: :boolean,
96
- aliases: '-o',
97
- default: true,
98
- desc: 'When enabled, the diff will only consider settings ' \
99
- 'contained in files which have been gitignored or settings ' \
100
- 'which are marked as "_secure"'
101
-
102
- def compare
103
- Commands::Cloud::Compare.call(**options
104
- .transform_keys(&:to_sym)
105
- .merge(shell: self, adapter: 'heroku'))
106
- end
107
- end
108
- end
109
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'thor'
4
- require 'chamber/commands/travis/secure'
5
-
6
- module Chamber
7
- module Binary
8
- class Travis < Thor
9
- desc 'secure',
10
- 'Uses your Travis CI public key to encrypt the settings you have ' \
11
- 'chosen not to commit to the repo'
12
-
13
- method_option :dry_run,
14
- type: :boolean,
15
- aliases: '-d',
16
- desc: 'Does not actually encrypt anything to .travis.yml, but ' \
17
- 'instead displays what values would be encrypted'
18
-
19
- method_option :only_sensitive,
20
- type: :boolean,
21
- aliases: '-o',
22
- default: true,
23
- desc: 'Does not encrypt settings into .travis.yml unless they are ' \
24
- 'contained in files which have been gitignored or settings ' \
25
- 'which are marked as "_secure"'
26
-
27
- def secure
28
- Commands::Travis::Secure.call(**options.transform_keys(&:to_sym).merge(shell: self))
29
- end
30
- end
31
- end
32
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'chamber/commands/base'
4
-
5
- module Chamber
6
- module Commands
7
- module Cloud
8
- class Base < Chamber::Commands::Base
9
- attr_accessor :adapter
10
-
11
- def initialize(adapter:, **args)
12
- super(**args)
13
-
14
- self.adapter = adapter_class(adapter).new(**args)
15
- end
16
-
17
- private
18
-
19
- def adapter_class(adapter_name)
20
- require "chamber/adapters/cloud/#{adapter_name}"
21
-
22
- @adapter_class ||= case adapter_name
23
- when 'circle_ci'
24
- Chamber::Adapters::Cloud::CircleCi
25
- when 'heroku'
26
- Chamber::Adapters::Cloud::Heroku
27
- else
28
- fail ArgumentError,
29
- "Invalid Chamber cloud adapter name: #{adapter_name}"
30
- end
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'chamber/commands/cloud/base'
4
-
5
- module Chamber
6
- module Commands
7
- module Cloud
8
- class Clear < Chamber::Commands::Cloud::Base
9
- def call
10
- chamber.to_environment.each_key do |key|
11
- next unless adapter.environment_variables.has_key?(key)
12
-
13
- if dry_run
14
- shell.say_status 'remove', key, :blue
15
- else
16
- shell.say_status 'remove', key, :green
17
-
18
- adapter.remove_environment_variable(key)
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'chamber/commands/cloud/base'
4
- require 'chamber/commands/comparable'
5
- require 'chamber/commands/securable'
6
-
7
- module Chamber
8
- module Commands
9
- module Cloud
10
- class Compare < Chamber::Commands::Cloud::Base
11
- include Chamber::Commands::Securable
12
- include Chamber::Commands::Comparable
13
-
14
- protected
15
-
16
- def first_settings_data
17
- ::JSON.pretty_generate(securable_environment_variables)
18
- end
19
-
20
- def second_settings_data
21
- ::JSON.pretty_generate(adapter.environment_variables)
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'chamber/commands/cloud/base'
5
-
6
- module Chamber
7
- module Commands
8
- module Cloud
9
- class Pull < Chamber::Commands::Cloud::Base
10
- attr_accessor :target_file
11
-
12
- def initialize(into:, **args)
13
- super
14
-
15
- self.target_file = into
16
- end
17
-
18
- def call
19
- if target_file
20
- shell.create_file(target_file,
21
- ::JSON.pretty_generate(adapter.environment_variables))
22
- else
23
- adapter.environment_variables
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'chamber/commands/cloud/base'
4
- require 'chamber/commands/securable'
5
- require 'chamber/keys/decryption'
6
-
7
- module Chamber
8
- module Commands
9
- module Cloud
10
- class Push < Chamber::Commands::Cloud::Base
11
- include Chamber::Commands::Securable
12
-
13
- attr_accessor :keys
14
-
15
- def initialize(keys:, **args)
16
- super(**args)
17
-
18
- self.keys = keys
19
- end
20
-
21
- def call
22
- environment_variables = if keys
23
- Keys::Decryption
24
- .new(rootpath: chamber.configuration.rootpath,
25
- namespaces: chamber.configuration.namespaces)
26
- .as_environment_variables
27
- else
28
- securable_environment_variables
29
- end
30
-
31
- environment_variables.each do |key, value|
32
- if dry_run
33
- shell.say_status 'push', key, :blue
34
- else
35
- shell.say_status 'push', key, :green
36
-
37
- adapter.add_environment_variable(key, value)
38
- end
39
- end
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'chamber/commands/base'
4
- require 'chamber/commands/travis'
5
- require 'chamber/commands/securable'
6
-
7
- module Chamber
8
- module Commands
9
- module Travis
10
- class Secure < Chamber::Commands::Base
11
- include Chamber::Commands::Travis
12
- include Chamber::Commands::Securable
13
-
14
- def call
15
- securable_environment_variables.each do |key, value|
16
- if dry_run
17
- shell.say_status 'encrypt', key, :blue
18
- else
19
- command = first_environment_variable?(key) ? '--override' : '--append'
20
-
21
- shell.say_status 'encrypt', key, :green
22
- travis_encrypt("#{command} #{key}=#{value}")
23
- end
24
- end
25
- end
26
-
27
- protected
28
-
29
- def first_environment_variable?(key)
30
- @first_environment_key ||= securable_environment_variables.first[0]
31
-
32
- @first_environment_key == key
33
- end
34
- end
35
- end
36
- end
37
- end