conf_conf 1.0.2 → 2.0.2
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 +4 -4
- data/.gitignore +4 -0
- data/README.md +227 -39
- data/bin/conf_conf +136 -0
- data/conf_conf.gemspec +18 -10
- data/lib/conf_conf.rb +51 -42
- data/lib/conf_conf/cli.rb +9 -0
- data/lib/conf_conf/cli/developers.rb +48 -0
- data/lib/conf_conf/cli/environments.rb +61 -0
- data/lib/conf_conf/cli/root.rb +72 -0
- data/lib/conf_conf/cli/variables.rb +85 -0
- data/lib/conf_conf/configuration.rb +57 -0
- data/lib/conf_conf/project.rb +20 -0
- data/lib/conf_conf/project/developer.rb +38 -0
- data/lib/conf_conf/project/developers.rb +48 -0
- data/lib/conf_conf/project/environment.rb +53 -0
- data/lib/conf_conf/project/environment/storage.rb +58 -0
- data/lib/conf_conf/project/environments.rb +24 -0
- data/lib/conf_conf/remote.rb +40 -0
- data/lib/conf_conf/user.rb +38 -0
- data/spec/project/developers_spec.rb +25 -0
- data/spec/project/environment/storage_spec.rb +91 -0
- data/spec/project/environment_spec.rb +21 -0
- data/spec/spec_helper.rb +163 -0
- metadata +126 -9
- data/spec/conf_conf_spec.rb +0 -68
data/conf_conf.gemspec
CHANGED
@@ -3,15 +3,23 @@ lib = File.expand_path('../lib', __FILE__)
|
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
|
-
s.name
|
7
|
-
s.version
|
8
|
-
s.licenses
|
9
|
-
s.authors
|
10
|
-
s.email
|
11
|
-
s.homepage
|
12
|
-
s.description
|
13
|
-
s.summary
|
14
|
-
s.files
|
6
|
+
s.name = 'conf_conf'
|
7
|
+
s.version = '2.0.2'
|
8
|
+
s.licenses = ['MIT']
|
9
|
+
s.authors = ['James Kassemi']
|
10
|
+
s.email = ['jkassemi@gmail.com']
|
11
|
+
s.homepage = 'https://github.com/jkassemi/conf_conf'
|
12
|
+
s.description = 'Verify correctness of environment variables'
|
13
|
+
s.summary = 'A simple pattern and utility for verifying the correctness of the environment variables at application boot so we can fail fast when there\'s a configuration problem.'
|
14
|
+
s.files = `git ls-files`.split($/)
|
15
|
+
s.executables = ['conf_conf']
|
15
16
|
|
16
|
-
s.
|
17
|
+
s.add_runtime_dependency 'httpi', '~> 2.2.4'
|
18
|
+
s.add_runtime_dependency 'thor', '~> 0.19.1'
|
19
|
+
s.add_runtime_dependency 'colorize', '~> 0.7.3'
|
20
|
+
s.add_runtime_dependency 'highline', '~> 1.6.21'
|
21
|
+
s.add_runtime_dependency 'rbnacl-libsodium', '~> 0.5.0.1'
|
22
|
+
s.add_runtime_dependency 'multi_json', '~> 1.10.1'
|
23
|
+
s.add_runtime_dependency 'dotenv', '~> 0.11.1'
|
24
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
17
25
|
end
|
data/lib/conf_conf.rb
CHANGED
@@ -1,68 +1,77 @@
|
|
1
|
+
require 'multi_json'
|
1
2
|
require 'ostruct'
|
2
3
|
|
3
|
-
|
4
|
+
require 'conf_conf/project'
|
5
|
+
require 'conf_conf/configuration'
|
6
|
+
require 'conf_conf/project/developer'
|
7
|
+
require 'conf_conf/project/developers'
|
8
|
+
require 'conf_conf/project/environment'
|
9
|
+
require 'conf_conf/project/environment/storage'
|
10
|
+
require 'conf_conf/project/environments'
|
4
11
|
|
5
12
|
module ConfConf
|
13
|
+
VERSION = '2.0.2'
|
14
|
+
|
6
15
|
class MissingConfigurationValueError < StandardError; end;
|
16
|
+
class InconsistentConfigurationError < StandardError
|
17
|
+
attr_reader :inconsistencies
|
7
18
|
|
8
|
-
|
9
|
-
|
10
|
-
OpenStruct.new(Configuration.new(&block).parsed_values)
|
19
|
+
def initialize(inconsistencies)
|
20
|
+
@inconsistencies = inconsistencies
|
11
21
|
end
|
22
|
+
end
|
12
23
|
|
13
|
-
|
14
|
-
|
15
|
-
|
24
|
+
class << self
|
25
|
+
#TODO: This could be cleaned up considerably.
|
26
|
+
def configuration(environment_name=nil, &block)
|
27
|
+
if environment_name
|
28
|
+
# Load ENV
|
29
|
+
project = ConfConf::Project.new
|
30
|
+
environment = project.environments[environment_name]
|
16
31
|
|
17
|
-
|
18
|
-
|
32
|
+
environment.variables.each do |name, value|
|
33
|
+
ENV[name] = value
|
34
|
+
end
|
19
35
|
end
|
20
|
-
end
|
21
|
-
end
|
22
36
|
|
23
|
-
|
24
|
-
|
37
|
+
# Run configuration block, if given
|
38
|
+
configuration = ConfConf::Configuration.new
|
25
39
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
40
|
+
if block
|
41
|
+
configuration.run(block)
|
42
|
+
references = configuration.references
|
43
|
+
else
|
44
|
+
references = {}
|
45
|
+
end
|
30
46
|
|
31
|
-
|
32
|
-
|
33
|
-
|
47
|
+
if environment_name
|
48
|
+
# Find references to variables that aren't defaulted here
|
49
|
+
inconsistencies = project.inconsistencies(environment)
|
34
50
|
|
35
|
-
|
36
|
-
|
51
|
+
inconsistencies.each do |inconsistency|
|
52
|
+
if references[inconsistency] && references[inconsistency].default_value?
|
53
|
+
inconsistencies.delete(inconsistency)
|
54
|
+
end
|
55
|
+
end
|
37
56
|
|
38
|
-
|
39
|
-
|
57
|
+
if inconsistencies.length > 0
|
58
|
+
raise ConfConf::InconsistentConfigurationError.new(inconsistencies)
|
59
|
+
end
|
40
60
|
end
|
41
61
|
|
42
|
-
|
62
|
+
OpenStruct.new(configuration.parsed_values)
|
43
63
|
end
|
44
|
-
end
|
45
64
|
|
46
|
-
|
47
|
-
|
48
|
-
environment_value || default_value
|
49
|
-
end
|
65
|
+
def rails_configuration(environment_name=nil, &block)
|
66
|
+
configuration = configuration(environment_name, block)
|
50
67
|
|
51
|
-
|
52
|
-
|
53
|
-
if options.has_key? :default
|
54
|
-
options[:default]
|
55
|
-
else
|
56
|
-
raise MissingConfigurationValueError.new("Please set #{environment_key} or supply a default value")
|
68
|
+
configuration.parsed_values.each do |name, value|
|
69
|
+
Rails.configuration.send("#{key}=", value)
|
57
70
|
end
|
58
71
|
end
|
59
72
|
|
60
|
-
def
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
def environment_key
|
65
|
-
options[:from] || key.to_s.upcase
|
73
|
+
def load(environment_name)
|
74
|
+
configuration(environment_name)
|
66
75
|
end
|
67
76
|
end
|
68
77
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class ConfConf::CLI::Developers < Thor
|
2
|
+
desc 'key', 'Your developer key'
|
3
|
+
def key
|
4
|
+
developer = ConfConf::Project::Developer.current
|
5
|
+
|
6
|
+
puts MultiJson.dump(developer.pretty_public_key, pretty: true)
|
7
|
+
end
|
8
|
+
|
9
|
+
desc 'permit <key>', 'Give <key> access to environment configurations'
|
10
|
+
def permit(key)
|
11
|
+
project = ConfConf::Project.new
|
12
|
+
developer = ConfConf::Project::Developer.new(key)
|
13
|
+
developers = project.developers
|
14
|
+
|
15
|
+
developers.add(developer)
|
16
|
+
developers.save
|
17
|
+
|
18
|
+
project.environments.to_a.each do |environment|
|
19
|
+
environment.save
|
20
|
+
end
|
21
|
+
|
22
|
+
puts MultiJson.dump(developers.keys.to_a, pretty: true)
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'revoke <key>', 'Deny <key> access to the environment configurations'
|
26
|
+
def revoke(key)
|
27
|
+
project = ConfConf::Project.new
|
28
|
+
developer = ConfConf::Project::Developer.new(key)
|
29
|
+
developers = project.developers
|
30
|
+
|
31
|
+
developers.remove(developer)
|
32
|
+
developers.save
|
33
|
+
|
34
|
+
project.environments.to_a.each do |environment|
|
35
|
+
environment.save
|
36
|
+
end
|
37
|
+
|
38
|
+
puts MultiJson.dump(developers.keys.to_a, pretty: true)
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'list', 'List of keys with access to the environment configurations'
|
42
|
+
def list
|
43
|
+
project = ConfConf::Project.new
|
44
|
+
developers = project.developers
|
45
|
+
|
46
|
+
puts MultiJson.dump(developers.keys.to_a, pretty: true)
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ConfConf::CLI
|
2
|
+
class Environments < Thor
|
3
|
+
desc 'list', 'Show environments'
|
4
|
+
def list
|
5
|
+
project = ConfConf::Project.new
|
6
|
+
puts MultiJson.dump(project.environments.to_a.collect(&:name), pretty: true)
|
7
|
+
end
|
8
|
+
|
9
|
+
desc 'add <environment>', 'Set up new environment'
|
10
|
+
long_desc <<-LONG
|
11
|
+
> $ conf_conf env add staging
|
12
|
+
LONG
|
13
|
+
def add(environment_name)
|
14
|
+
project = ConfConf::Project.new
|
15
|
+
environment = project.environments[environment_name]
|
16
|
+
environment.save
|
17
|
+
|
18
|
+
puts MultiJson.dump(project.environments.to_a.collect(&:name), pretty: true)
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'remove <environment>', 'Removes an existing environment'
|
22
|
+
def remove(environment_name)
|
23
|
+
project = ConfConf::Project.new
|
24
|
+
project.environments.remove(environment_name)
|
25
|
+
|
26
|
+
puts MultiJson.dump(project.environments.to_a.collect(&:name), pretty: true)
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'check (<environment>)', 'Check environment consistency'
|
30
|
+
def check(environment_name=nil)
|
31
|
+
project = ConfConf::Project.new
|
32
|
+
|
33
|
+
all_environment_variable_names = Set.new
|
34
|
+
|
35
|
+
project.environments.to_a.each do |environment|
|
36
|
+
all_environment_variable_names += environment.variables.keys
|
37
|
+
end
|
38
|
+
|
39
|
+
if environment_name.nil?
|
40
|
+
environments = project.environments.to_a
|
41
|
+
else
|
42
|
+
environments = [project.environments[environment_name]]
|
43
|
+
end
|
44
|
+
|
45
|
+
environment_warnings = {}
|
46
|
+
|
47
|
+
environments.each do |environment|
|
48
|
+
diff = all_environment_variable_names - environment.variables.keys
|
49
|
+
|
50
|
+
if diff.length > 0
|
51
|
+
diff.each do |key|
|
52
|
+
environment_warnings[environment.name] ||= {:missing => []}
|
53
|
+
environment_warnings[environment.name][:missing] << key
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
puts MultiJson.dump(environment_warnings, pretty: true)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module ConfConf
|
2
|
+
module CLI
|
3
|
+
class Root < Thor
|
4
|
+
desc 'environments', 'Manage available environments'
|
5
|
+
subcommand 'environments', ConfConf::CLI::Environments
|
6
|
+
|
7
|
+
desc 'variables', 'Configure environment variables'
|
8
|
+
subcommand 'variables', ConfConf::CLI::Variables
|
9
|
+
|
10
|
+
desc 'developers', 'Configure access credentials'
|
11
|
+
subcommand 'developers', ConfConf::CLI::Developers
|
12
|
+
|
13
|
+
desc 'init', 'Initialize conf_conf project'
|
14
|
+
long_desc <<-LONG
|
15
|
+
> $ conf_conf init
|
16
|
+
|
17
|
+
Initialize this system user's ~/.conf_conf.json
|
18
|
+
LONG
|
19
|
+
def init
|
20
|
+
account = ConfConf::Project::Developer.current
|
21
|
+
FileUtils.mkdir_p('config/conf_conf')
|
22
|
+
FileUtils.mkdir_p('config/conf_conf/environments')
|
23
|
+
puts MultiJson.dump(account: account, pretty: true)
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'export <environment>', '.env compatible export for <environment>'
|
27
|
+
def export(environment_name)
|
28
|
+
project = ConfConf::Project.new
|
29
|
+
environment = project.environments[environment_name]
|
30
|
+
|
31
|
+
environment.variables.each do |variable_name, variable_value|
|
32
|
+
puts "#{variable_name}=#{variable_value}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'import <environment>', 'Import from .env into <environment>'
|
37
|
+
option :dotenv, default: true
|
38
|
+
def import(environment_name)
|
39
|
+
require 'dotenv'
|
40
|
+
|
41
|
+
project = ConfConf::Project.new
|
42
|
+
environment = project.environments[environment_name]
|
43
|
+
dotenv_environment = Dotenv::Environment.new('.env')
|
44
|
+
|
45
|
+
dotenv_environment.each do |k,v|
|
46
|
+
environment.set(k, v)
|
47
|
+
end
|
48
|
+
|
49
|
+
environment.save
|
50
|
+
puts MultiJson.dump(environment.variables, pretty: true)
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'info', 'Summary of configurations'
|
54
|
+
def info
|
55
|
+
project = ConfConf::Project.new
|
56
|
+
|
57
|
+
summary = {}
|
58
|
+
summary[:environments] = project.environments.to_a.collect(&:name)
|
59
|
+
summary[:variables] = {}
|
60
|
+
|
61
|
+
project.environments.to_a.each do |environment|
|
62
|
+
environment.variables.each do |variable_name, variable_value|
|
63
|
+
summary[:variables][variable_name] ||= []
|
64
|
+
summary[:variables][variable_name] << environment.name
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
puts MultiJson.dump(summary, pretty: true)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
class ConfConf::CLI::Variables < Thor
|
2
|
+
desc 'set', 'Set configuration values in the given environment'
|
3
|
+
option :env, default: false, desc: 'environment to set the config var'
|
4
|
+
long_desc <<-LONG
|
5
|
+
Sets Config Values in the given Environment.
|
6
|
+
|
7
|
+
Examples:
|
8
|
+
|
9
|
+
> $ conf_conf set X=1
|
10
|
+
|
11
|
+
Sets the Config Key `X` with Config Value `1` for all Environments.
|
12
|
+
|
13
|
+
> $ conf_conf set --env=production X=1
|
14
|
+
|
15
|
+
Sets the Config Key `X` with Config Value `1` for `production` Environment.
|
16
|
+
Sets the Config Key `X` with the Config Value `1` for the
|
17
|
+
`production` Environment.
|
18
|
+
|
19
|
+
> $ conf_conf set --env=production X=1 Y=2 Z=3
|
20
|
+
|
21
|
+
Sets the Config Keys `X`, `Y`, and `Z` with the Config
|
22
|
+
Values `1`, `2`, and `3`, respectively. Config Values are saved to
|
23
|
+
the `production` Environment.
|
24
|
+
LONG
|
25
|
+
def set(*env_variable_args)
|
26
|
+
project = ConfConf::Project.new
|
27
|
+
developers = project.developers
|
28
|
+
|
29
|
+
developer = ConfConf::Project::Developer.current
|
30
|
+
developers.add(developer)
|
31
|
+
|
32
|
+
if options[:env]
|
33
|
+
environments = [project.environments[options[:env]]]
|
34
|
+
else
|
35
|
+
environments = project.environments.to_a
|
36
|
+
end
|
37
|
+
|
38
|
+
return if environments.length == 0
|
39
|
+
|
40
|
+
env_variable_args.each do |env_variable|
|
41
|
+
config_key, config_value = env_variable.split("=").map(&:strip)
|
42
|
+
|
43
|
+
environments.each do |environment|
|
44
|
+
environment.set(config_key, config_value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
environments.map(&:save)
|
49
|
+
end
|
50
|
+
|
51
|
+
desc 'remove', 'Remove a configuration value'
|
52
|
+
option :env, default: 'development', desc: 'environment to remove the config var from'
|
53
|
+
long_desc <<-LONG
|
54
|
+
Examples:
|
55
|
+
|
56
|
+
> $ conf_conf remove X
|
57
|
+
|
58
|
+
Removes the `X` Environment Variable
|
59
|
+
|
60
|
+
> $ conf_conf remove --env=production X
|
61
|
+
|
62
|
+
Removes `X` from production only
|
63
|
+
LONG
|
64
|
+
def remove(*name_args)
|
65
|
+
project = ConfConf::Project.new
|
66
|
+
|
67
|
+
all_environments = project.environments.to_a
|
68
|
+
|
69
|
+
if options[:env]
|
70
|
+
environments = [project.environments[options[:env]]]
|
71
|
+
else
|
72
|
+
environments = all_environments
|
73
|
+
end
|
74
|
+
|
75
|
+
return if environments.length == 0
|
76
|
+
|
77
|
+
name_args.each do |name|
|
78
|
+
environments.each do |environment|
|
79
|
+
environment.remove(name)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
environments.map(&:save)
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module ConfConf
|
2
|
+
class Configuration
|
3
|
+
attr_reader :parsed_values
|
4
|
+
attr_reader :references
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@parsed_values = {}
|
8
|
+
@references = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(block)
|
12
|
+
instance_eval(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def config(key, options={})
|
16
|
+
reference = Reference.new(key, options)
|
17
|
+
@references[reference.environment_key] = reference
|
18
|
+
|
19
|
+
value = reference.value
|
20
|
+
|
21
|
+
if block_given?
|
22
|
+
value = yield(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
@parsed_values[key] = value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
class Reference < Struct.new(:key, :options)
|
31
|
+
def value
|
32
|
+
environment_value || default_value
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_value?
|
36
|
+
options[:default]
|
37
|
+
end
|
38
|
+
|
39
|
+
def environment_key
|
40
|
+
options[:from] || key.to_s.upcase
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def default_value
|
45
|
+
if options.has_key? :default
|
46
|
+
options[:default]
|
47
|
+
else
|
48
|
+
raise ConfConf::MissingConfigurationValueError.new("Please set #{environment_key} or supply a default value")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def environment_value
|
53
|
+
ENV[environment_key]
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|