vaulty 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e424ce87a9674979f10dad0855b27a2fa76763c4
4
+ data.tar.gz: bd26c4d66ac5b7d1da3b85fac301a3c761342fe0
5
+ SHA512:
6
+ metadata.gz: ea10a384993cae89b6ef4bf52375aaf2038dd8f87640890f082767281a8583fa2e28dd4bef28ce0b323850bb56858a3cac05e4614b09d8b294fe3b422494899c
7
+ data.tar.gz: '0994e597a90351fc5a33059df95f30f2a1198c2b5597e8909415a64f45d5382637a33e257b679e3b92279ad831e9f721d61325c09a6e63010b9b689b058130b7'
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ Gemfile.lock
2
+
3
+ html/
4
+ results.html
5
+ tmp/
6
+ coverage/
7
+
8
+ pkg/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,42 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.3
5
+ Exclude:
6
+ - 'bin/*'
7
+ - 'spec/**/*'
8
+
9
+ # Don't care about frozen strings
10
+ Style/FrozenStringLiteralComment:
11
+ Enabled: false
12
+
13
+ # Don't care about code comments (yet)
14
+ Style/Documentation:
15
+ Enabled: false
16
+
17
+ Metrics/LineLength:
18
+ Max: 90
19
+
20
+ Style/AlignHash:
21
+ EnforcedColonStyle: key
22
+ EnforcedHashRocketStyle: key
23
+
24
+ Style/AlignParameters:
25
+ Enabled: false
26
+
27
+ Style/IndentHash:
28
+ EnforcedStyle: consistent
29
+
30
+ # Only exclude the CLI app since we cannot split this up in the current form
31
+ Metrics/ClassLength:
32
+ Exclude:
33
+ - 'lib/vaulty/cli_app.rb'
34
+
35
+ Style/PercentLiteralDelimiters:
36
+ PreferredDelimiters:
37
+ default: ()
38
+ '%i': '()'
39
+ '%I': '()'
40
+ '%r': '{}'
41
+ '%w': '()'
42
+ '%W': '()'
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,11 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2017-04-19 16:33:02 +0200 using RuboCop version 0.48.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ Metrics/AbcSize:
11
+ Max: 25
data/.travis.yml ADDED
@@ -0,0 +1,34 @@
1
+ dist: trusty
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+
6
+ before_install:
7
+ - curl -sLo vault.zip https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip
8
+ - unzip vault.zip
9
+ - mkdir ~/bin
10
+ - mv vault ~/bin
11
+ - export PATH="~/bin:$PATH"
12
+ - gem update --remote bundler
13
+
14
+ install:
15
+ - bundle install --retry=3
16
+
17
+ env:
18
+ - VAULT_VERSION=0.7.0
19
+
20
+ rvm:
21
+ - 2.3.1
22
+ - 2.4.1
23
+
24
+ script:
25
+ - 'bundle exec rake spec'
26
+ - 'bundle exec rubocop'
27
+
28
+ addons:
29
+ code_climate:
30
+ repo_token:
31
+ secure: "nof51qBnGYKwvJGsFrECGdnsttHoihTFKBOPlJV7M9qIO5WKGdg/hHFTA1oIaq8njlrGs166IA9gD8IUFpVaesSVMr9W9l7LdgbcMNf+hmTLA8MmrIjTJMsyBSSXK5H7nmd36VS+XzFjhviYeVgSVppZdv+Rxyo3neieq6R6u/Ik3VLTp7RjA38Yh45iiRd549OO+A0ufZ9oOySuA03WPAiPWhyjjScK2L8+HYH5G6pEhhm6+GfN6DZU6QWbNPBCGEDk5RnLb1SfoxYDNlXZlEMre+gzO+78bquZVQOqEGF7VJUD/kDW8ZO4abEvWeDL6tlEsU5EnDAu5EIwDSZFVQUSbsqoyyX8r64EHzomaF4QAIuRZ7/5N8hJ6h+qAlxOJ3NML1+tALQriY5e5vh7ler1IjC3tozfGouMIoKFTe6FVBZir799Dm5jjVfvTDoYoLj1QcsplFtZw0s6lUQc3fNMFNPmS9t/xIDqPGNJvd5jCKYkdXs1bld9XdvjklyZglNp+cU8RMzEr6R9XvQGvjZSZqyO8IOOXMwwJpTNJ6Swt+kmAmogG5s30STE296+4Sp8/u9xfRoYbwKLbj7LwMl7IVnfQLqCfdJxVfRlCw3mxPn1mECXys2C+GFjqh0u/wPci1qAqyBBKnG1kSScfBdvuQqIoAs6nfDZFShLxlg="
32
+
33
+ after_success:
34
+ - bundle exec codeclimate-test-reporter
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ group :test do
5
+ gem 'aruba'
6
+ gem 'codeclimate-test-reporter', '~> 1.0.0'
7
+ gem 'coveralls', require: false
8
+ gem 'rdoc'
9
+ gem 'rspec'
10
+ gem 'rubocop'
11
+ gem 'simplecov'
12
+ gem 'yard'
13
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 PlayPass
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # Vaulty
2
+ [![Build Status](https://travis-ci.org/playpasshq/vaulty.svg?branch=master)](https://travis-ci.org/playpasshq/vaulty)
3
+ [![Coverage Status](https://coveralls.io/repos/github/playpasshq/vaulty/badge.svg?branch=master)](https://coveralls.io/github/playpasshq/vaulty?branch=master)
4
+ [![Code Climate](https://codeclimate.com/github/playpasshq/vaulty/badges/gpa.svg)](https://codeclimate.com/github/playpasshq/vaulty)
5
+ [![Issue Count](https://codeclimate.com/github/playpasshq/vaulty/badges/issue_count.svg)](https://codeclimate.com/github/playpasshq/vaulty)
6
+ [![Dependency Status](https://gemnasium.com/badges/github.com/playpasshq/vaulty.svg)](https://gemnasium.com/github.com/playpasshq/vaulty)
7
+ [![Inline docs](http://inch-ci.org/github/playpasshq/vaulty.svg)](http://inch-ci.org/github/playpasshq/vaulty)
8
+ [![git.legal](https://git.legal/projects/3808/badge.svg?key=f71d4e011a263b65c8f7 "Number of libraries approved")](https://git.legal/projects/3808)
9
+
10
+ Vaulty is a CLI application that makes it easier to manage Vault.
11
+ Only tested on the generic backend!
12
+
13
+ ## Project Status
14
+
15
+ Very alpha, only 3 commands are supported.
16
+
17
+ :include:vaulty.rdoc
18
+
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'rubygems/package_task'
3
+ require 'bundler/setup'
4
+ require 'bundler/gem_tasks'
5
+
6
+ begin
7
+ Bundler.setup(:default, :development)
8
+ rescue Bundler::BundlerError => e
9
+ $stderr.puts e.message
10
+ $stderr.puts 'Run `bundle install` to install missing gems'
11
+ exit e.status_code
12
+ end
13
+
14
+ require 'rake'
15
+ require 'rspec/core'
16
+ require 'rspec/core/rake_task'
17
+ require 'rubocop/rake_task'
18
+
19
+ RSpec::Core::RakeTask.new(:spec)
20
+ RuboCop::RakeTask.new(:rubocop)
21
+
22
+ desc 'Run RSpec with code coverage'
23
+ task :coverage do
24
+ ENV['COVERAGE'] = 'true'
25
+ Rake::Task['spec'].execute
26
+ end
27
+
28
+ task default: %i(spec rubocop)
29
+
30
+ require 'yard'
31
+ YARD::Rake::YardocTask.new
data/bin/vaulty ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require 'vaulty'
3
+
4
+ Vault.configure do |config|
5
+ config.address = ENV["VAULT_ADDR"] || 'http://127.0.0.1:8200'
6
+ end
7
+
8
+ exit Vaulty::CLIApp.run(ARGV)
@@ -0,0 +1,113 @@
1
+ module Vaulty
2
+ class Catacomb
3
+ attr_reader :path
4
+ # @param [String] the path to be used
5
+ def initialize(path)
6
+ @path = path
7
+ end
8
+
9
+ # Checks if the key is found in the data
10
+ # @param [String] key
11
+ # @return [Boolean]
12
+ #
13
+ def key?(key)
14
+ read.key?(key.to_sym)
15
+ end
16
+
17
+ # Checks if any of the provided keys are found in the data
18
+ # @param [Array<String>] keys
19
+ # @return [Boolean]
20
+ #
21
+ def keys?(keys)
22
+ matching_keys(keys).any?
23
+ end
24
+
25
+ # Returns matching keys
26
+ # @param [Array<String>] keys
27
+ # @return [Array<String>] keys that matched
28
+ #
29
+ def matching_keys(keys)
30
+ Array(keys).select { |key| key?(key) }
31
+ end
32
+
33
+ # Writes data to Vault with an optional confirm/msg
34
+ # @param [Hash] data
35
+ #
36
+ def write(data)
37
+ self.class.write(path, data)
38
+ reload
39
+ end
40
+
41
+ # Merges the data to Vault with an optional confirm/msg
42
+ # @param [Hash] data
43
+ #
44
+ def merge(data)
45
+ current = read
46
+ write(current.merge(data))
47
+ end
48
+
49
+ # Reads the data from Vault, always returns an Hash
50
+ # @return [Hash]
51
+ #
52
+ def read
53
+ @read ||= self.class.read(path)
54
+ end
55
+
56
+ # Clears the cached result from read and returns the result
57
+ # @return [Hash]
58
+ #
59
+ def reload
60
+ @read = nil
61
+ read
62
+ end
63
+
64
+ # Deletes a path (with all values) from Vault
65
+ # @param [Boolean] confirm
66
+ # @param [String] msg
67
+ # @return [True]
68
+ #
69
+ def delete
70
+ self.class.delete(path)
71
+ @read = nil
72
+ end
73
+
74
+ class << self
75
+ # Wrapper around Vault logical read, return always a hash
76
+ # @param [String] path
77
+ # @return [Hash]
78
+ #
79
+ def read(path)
80
+ secret = Vault.logical.read(path)
81
+ secret ? secret.data : {}
82
+ end
83
+
84
+ # Wrapper around Vault logical write
85
+ # @param [String] path
86
+ # @param [Hash] data
87
+ # @return [Hash] secret
88
+ #
89
+ def write(path, data)
90
+ secret = Vault.logical.write(path, data)
91
+ secret.is_a?(Vault::Secret) ? secret.data : {}
92
+ end
93
+
94
+ # Wrapper around Vault logical list
95
+ # @param [String] path
96
+ # @return [Array] secrets
97
+ #
98
+ def list(path)
99
+ list = Vault.logical.list(path)
100
+ cleaned_list = Array(list).map { |folder| folder.delete('/') }
101
+ cleaned_list.reject(&:empty?).uniq
102
+ end
103
+
104
+ # Wrapper around Vault logical delete
105
+ # @param [String] path
106
+ # @return [Boolean]
107
+ #
108
+ def delete(path)
109
+ Vault.logical.delete(path)
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,32 @@
1
+ module Vaulty
2
+ module CLI
3
+ class Add < Command
4
+ attr_reader :catacomb, :data
5
+ # @param [Catacomb] catacomb instance
6
+ # @param [Hash] data
7
+ def initialize(catacomb:, data:)
8
+ @catacomb = catacomb
9
+ @data = data
10
+ end
11
+
12
+ def call
13
+ banner("Current value #{catacomb.path.inspect}")
14
+ # Print out the current state
15
+ table(catacomb.read, highlight: { matching: matching_keys, color: :red })
16
+ # If we have matching keys and we don't want to continue, we exit
17
+ return if matching_keys.any? && prompt.no?('Existing secret found, overwrite?')
18
+ # Print we are writing and merge the content
19
+ banner("Writing data to #{catacomb.path.inspect}", color: :red)
20
+ catacomb.merge(data)
21
+ # Print the final state in a table highlight the values we added
22
+ table(catacomb.read, highlight: { matching: data.values, color: :green })
23
+ end
24
+
25
+ private
26
+
27
+ def matching_keys
28
+ @matching_keys ||= catacomb.matching_keys(data.keys)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ module Vaulty
2
+ module CLI
3
+ class Command
4
+ def banner(msg, color: :green)
5
+ Vaulty::Output::Banner.render(msg, color: color)
6
+ end
7
+
8
+ def table(data, highlight: {})
9
+ Vaulty::Output::Table.render(data, highlight: highlight)
10
+ end
11
+
12
+ def prompt
13
+ @prompt ||= TTY::Prompt.new
14
+ end
15
+
16
+ def self.call(*args)
17
+ new(*args).call
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ module Vaulty
2
+ module CLI
3
+ class Delete < Command
4
+ attr_reader :catacomb
5
+ def initialize(catacomb:)
6
+ @catacomb = catacomb
7
+ end
8
+
9
+ def call
10
+ banner("Current value #{catacomb.path.inspect}")
11
+ # Render the tree first so the user knows what we will delete
12
+ Vaulty::CLI::Tree.call(catacomb)
13
+ # Confirmation
14
+ return if prompt.no?('All above data will be ' \
15
+ 'recursively deleted! Are you sure?', color: :red)
16
+
17
+ banner(catacomb.path, color: :red)
18
+ # Start deleting
19
+ delete_recursively(vaulty_tree.children, [catacomb.path])
20
+ # Delete the route of the path
21
+ catacomb.delete
22
+ prompt.ok("Successfully deleted everything in path #{catacomb.path}")
23
+ end
24
+
25
+ private
26
+
27
+ def delete_recursively(tree, path = [])
28
+ Array(tree).each do |folder|
29
+ current_path = path + [folder.name]
30
+ Catacomb.delete(current_path.join('/')) unless folder.data.empty?
31
+ delete_recursively(folder.children, current_path) unless folder.children.empty?
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ module Vaulty
2
+ module CLI
3
+ class Tree < Command
4
+ class Empty < GLI::CustomExit
5
+ def initialize(path)
6
+ super("Path #{path.inspect} contains nothing", -1)
7
+ end
8
+ end
9
+
10
+ attr_reader :catacomb
11
+ # @param [Catacomb] catacomb instance
12
+ def initialize(catacomb:)
13
+ @catacomb = catacomb
14
+ end
15
+
16
+ def call
17
+ spinner = TTY::Spinner.new('Loading :spinner', format: :arrow_pulse, clear: true)
18
+ spinner.auto_spin
19
+ vaulty_tree = Vaulty::VaultTree.new(catacomb: catacomb).tree
20
+ spinner.stop
21
+
22
+ raise Empty, catacomb.path if vaulty_tree.empty?
23
+ Vaulty::Output::Tree.render(vaulty_tree)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,137 @@
1
+ module Vaulty
2
+ class CLIApp
3
+ extend GLI::App
4
+
5
+ program_desc 'Describe your application here'
6
+
7
+ version Vaulty::VERSION
8
+
9
+ subcommand_option_handling :normal
10
+ arguments :strict
11
+
12
+ accept(Array) do |value|
13
+ Hash[*value.split(':')]
14
+ end
15
+
16
+ desc 'Describe some switch here'
17
+ switch %i(s switch)
18
+
19
+ desc 'Describe some flag here'
20
+ default_value 'the default'
21
+ arg_name 'The name of the argument'
22
+ flag %i(f flagname)
23
+
24
+ desc 'Add a new key/value to the given `PATH`, multiple `key:value` can be provided'
25
+ arg(:path)
26
+ command :add do |c|
27
+ c.flag %i(secret s), desc: 'Key/Values to save', type: Array, multiple: true,
28
+ required: true, arg_name: 'key:secret'
29
+
30
+ c.action do |_global_options, options, _args|
31
+ data = options[:secret].reduce({}, :merge)
32
+ catacomb = options[:catacomb]
33
+ Vaulty::CLI::Add.call(catacomb: catacomb, data: data)
34
+ end
35
+ end
36
+
37
+ desc 'Deletes everything under the path recursively'
38
+ arg(:path)
39
+ command :delete do |c|
40
+ c.action do |_global_options, options, _args|
41
+ catacomb = options[:catacomb]
42
+ Vaulty::CLI::Delete.call(catacomb: catacomb)
43
+ end
44
+ end
45
+
46
+ desc 'Represents the path as a tree'
47
+ arg(:path)
48
+ command :tree do |c|
49
+ c.action do |_global_options, options, _args|
50
+ catacomb = options[:catacomb]
51
+ Vaulty::CLI::Tree.call(catacomb: catacomb)
52
+ end
53
+ end
54
+
55
+ desc 'Describe backup here'
56
+ arg_name 'Describe arguments to backup here'
57
+ command :backup do |c|
58
+ c.action do |_global_options, _options, _args|
59
+ puts 'backup command ran'
60
+ end
61
+ end
62
+
63
+ desc 'Describe convert here'
64
+ arg_name 'Describe arguments to convert here'
65
+ command :convert do |c|
66
+ c.action do |_global_options, _options, _args|
67
+ puts 'convert command ran'
68
+ end
69
+ end
70
+
71
+ desc 'Describe diff here'
72
+ arg_name 'Describe arguments to diff here'
73
+ command :diff do |c|
74
+ c.action do |_global_options, _options, _args|
75
+ puts 'diff command ran'
76
+ end
77
+ end
78
+
79
+ desc 'Describe generate here'
80
+ arg_name 'Describe arguments to generate here'
81
+ command :generate do |c|
82
+ c.action do |_global_options, _options, _args|
83
+ puts 'generate command ran'
84
+ end
85
+ end
86
+
87
+ desc 'Describe provision here'
88
+ arg_name 'Describe arguments to provision here'
89
+ command :provision do |c|
90
+ c.action do |_global_options, _options, _args|
91
+ puts 'provision command ran'
92
+ end
93
+ end
94
+
95
+ desc 'Describe remove here'
96
+ arg_name 'Describe arguments to remove here'
97
+ command :remove do |c|
98
+ c.action do |_global_options, _options, _args|
99
+ puts 'remove command ran'
100
+ end
101
+ end
102
+
103
+ desc 'Describe restore here'
104
+ arg_name 'Describe arguments to restore here'
105
+ command :restore do |c|
106
+ c.action do |_global_options, _options, _args|
107
+ puts 'restore command ran'
108
+ end
109
+ end
110
+
111
+ desc 'Describe write here'
112
+ arg_name 'Describe arguments to write here'
113
+ command :write do |c|
114
+ c.action do |_global_options, _options, _args|
115
+ puts 'write command ran'
116
+ end
117
+ end
118
+
119
+ pre do |_global, _command, options, args|
120
+ path = args.first
121
+ exit_now!('path must be provided') if path.nil? || path.empty?
122
+ options[:catacomb] = Vaulty::Catacomb.new(path)
123
+ end
124
+
125
+ post do |global, command, options, args|
126
+ # Post logic here
127
+ # Use skips_post before a command to skip this
128
+ # block on that command only
129
+ end
130
+
131
+ on_error do |_exception|
132
+ # Error logic here
133
+ # return false to skip default error handling
134
+ true
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,24 @@
1
+ module Vaulty
2
+ module Output
3
+ class Banner
4
+ attr_reader :msg, :prompt, :color
5
+
6
+ def initialize(msg, color: :blue)
7
+ @msg = msg
8
+ @color = "on_#{color}".to_sym
9
+ @prompt = TTY::Prompt.new
10
+ end
11
+
12
+ def render
13
+ now = Time.new.strftime('%H:%M:%S')
14
+ formatted = msg.ljust(72, ' ')
15
+ prompt.say("[#{now}] #{formatted}", color: %I(bold white #{color}))
16
+ puts
17
+ end
18
+
19
+ def self.render(*args)
20
+ new(*args).render
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,44 @@
1
+ module Vaulty
2
+ module Output
3
+ class HighlightFilter
4
+ attr_reader :matching, :color
5
+ def initialize(matching: [], color: :red)
6
+ @matching = matching
7
+ @color = color
8
+ end
9
+
10
+ def call(val, _row, _column)
11
+ matching.include?(val.strip) ? pastel.decorate(val, color) : val
12
+ end
13
+
14
+ def pastel
15
+ Pastel::Color.new(enabled: true)
16
+ end
17
+ end
18
+
19
+ class Table
20
+ DEFAULT_HEADER = %w(Key Value).freeze
21
+ attr_reader :data, :prompt, :header
22
+
23
+ def initialize(data, header: DEFAULT_HEADER, highlight: {})
24
+ @data = data
25
+ @prompt = TTY::Prompt.new
26
+ @filter = HighlightFilter.new(**highlight)
27
+ @header = header
28
+ end
29
+
30
+ def render
31
+ table = TTY::Table.new(data, header: header, style: :markdown)
32
+ renderer = table.render(:ascii) do |render|
33
+ render.padding = [0, 2, 0, 2]
34
+ render.filter = @filter
35
+ end
36
+ prompt.say renderer
37
+ end
38
+
39
+ def self.render(*args)
40
+ new(*args).render
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,47 @@
1
+ module Vaulty
2
+ module Output
3
+ class Tree
4
+ attr_reader :tree, :value_icon, :dir_icon
5
+
6
+ def initialize(tree, value_icon: "\u{1F511}", dir_icon: "\u{1F4C2}")
7
+ @tree = tree
8
+ @value_icon = value_icon
9
+ @dir_icon = dir_icon
10
+ end
11
+
12
+ def render
13
+ output_tree = flatten_tree(Array(tree), 0)
14
+ puts Hirb::Helpers::Tree.render(output_tree,
15
+ type: :directory,
16
+ multi_line_nodes: true)
17
+ end
18
+
19
+ def self.render(*args)
20
+ new(*args).render
21
+ end
22
+
23
+ private
24
+
25
+ def flatten_tree(subtree, level = 0)
26
+ subtree.map do |folder|
27
+ [
28
+ format_node(folder.name, level),
29
+ format_values(folder.data, level + 1),
30
+ flatten_tree(folder.children, level + 1)
31
+ ]
32
+ end.flatten.compact
33
+ end
34
+
35
+ def format_values(values, level)
36
+ Array(values).map do |value|
37
+ key_value = "#{value.key}:#{value.value}"
38
+ { value: [value_icon, key_value].compact.join(' '), level: level }
39
+ end
40
+ end
41
+
42
+ def format_node(value, level)
43
+ { value: [dir_icon, value].compact.join(' '), level: level }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,72 @@
1
+ module Vaulty
2
+ class VaultTree
3
+ class Folder
4
+ attr_reader :name, :data, :children
5
+ def initialize(name:, data:, children:)
6
+ @name = name
7
+ @data = data
8
+ @children = children
9
+ end
10
+
11
+ def empty?
12
+ data.empty? && children.empty?
13
+ end
14
+ end
15
+
16
+ class Value
17
+ attr_reader :key, :value
18
+ def initialize(key:, value:)
19
+ @key = key
20
+ @value = value
21
+ end
22
+ end
23
+
24
+ attr_reader :catacomb
25
+ def initialize(catacomb:)
26
+ @catacomb = catacomb
27
+ end
28
+
29
+ def tree
30
+ @tree ||= Folder.new(
31
+ name: catacomb.path,
32
+ data: read_values_for(catacomb.path),
33
+ children: find_subtree(catacomb.path)
34
+ )
35
+ end
36
+
37
+ def flatten
38
+ flatten_tree(Array(render))
39
+ end
40
+
41
+ private
42
+
43
+ def flatten_tree(subtree)
44
+ subtree.map do |folder|
45
+ {
46
+ name: folder.name,
47
+ data: folder.data.map(&:to_h),
48
+ children: flatten_tree(folder.children)
49
+ }
50
+ end
51
+ end
52
+
53
+ def find_subtree(base_path)
54
+ list = Catacomb.list(base_path)
55
+ list.map do |folder|
56
+ file_or_folder = [base_path, folder].join('/')
57
+ values = read_values_for(file_or_folder)
58
+ Folder.new(
59
+ name: folder,
60
+ data: values,
61
+ children: find_subtree(file_or_folder)
62
+ )
63
+ end
64
+ end
65
+
66
+ def read_values_for(path)
67
+ Catacomb.read(path).map do |key, value|
68
+ Value.new(key: key, value: value)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,3 @@
1
+ module Vaulty
2
+ VERSION = '0.0.1'.freeze
3
+ end
data/lib/vaulty.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'vaulty/version.rb'
2
+
3
+ # Add requires for other files you add to your project here, so
4
+ # you just need to require this one file in your bin file
5
+
6
+ require 'gli'
7
+ require 'vault'
8
+ require 'hirb'
9
+ require 'hashdiff'
10
+ require 'tty'
11
+ require 'tty-prompt'
12
+
13
+ require 'vaulty/cli_app'
14
+ require 'vaulty/catacomb'
15
+ require 'vaulty/vault_tree'
16
+
17
+ require 'vaulty/cli/command'
18
+ require 'vaulty/cli/add'
19
+ require 'vaulty/cli/tree'
20
+
21
+ require 'vaulty/output/banner'
22
+ require 'vaulty/output/table'
23
+ require 'vaulty/output/tree'
data/vaulty.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # Ensure we require the local version and not one we might have installed already
2
+ require File.join([File.dirname(__FILE__), 'lib', 'vaulty', 'version.rb'])
3
+ Gem::Specification.new do |s|
4
+ s.name = 'vaulty'
5
+ s.version = Vaulty::VERSION
6
+ s.author = ['Jan Stevens']
7
+ s.email = ['jan@playpass.be']
8
+ s.platform = Gem::Platform::RUBY
9
+
10
+ s.summary = 'A description of your project'
11
+ s.description = 'Vault CLI that is based on the Vault Ruby gem'
12
+ s.homepage = 'https://fritz.ninja'
13
+ s.license = 'MIT'
14
+
15
+ s.required_ruby_version = '~> 2.3'
16
+
17
+ s.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ s.bindir = 'bin'
21
+ s.require_paths << 'lib'
22
+ s.executables << 'vaulty'
23
+
24
+ s.add_dependency 'vault', '~> 0.9'
25
+ s.add_dependency 'hirb', '~> 0.7.3'
26
+ s.add_dependency 'tty', '~> 0.7.0'
27
+ s.add_dependency 'hashdiff', '~> 0.3.0'
28
+
29
+ s.add_development_dependency 'rake'
30
+
31
+ s.add_runtime_dependency 'gli', '2.16.0'
32
+ end
data/vaulty.rdoc ADDED
@@ -0,0 +1,90 @@
1
+ == vaulty - Describe your application here
2
+
3
+ v0.0.1
4
+
5
+ === Global Options
6
+ === -f|--flagname The name of the argument
7
+
8
+ Describe some flag here
9
+
10
+ [Default Value] the default
11
+
12
+
13
+ === --help
14
+ Show this message
15
+
16
+
17
+
18
+ === -s|--[no-]switch
19
+ Describe some switch here
20
+
21
+
22
+
23
+ === --version
24
+ Display the program version
25
+
26
+
27
+
28
+ === Commands
29
+ ==== Command: <tt>add </tt>
30
+ Add a new key/value to the given `PATH`, multiple `key:value` can be provided
31
+
32
+
33
+ ===== Options
34
+ ===== --secret|-s key:secret
35
+
36
+ Key/Values to save
37
+
38
+ [Default Value] None
39
+
40
+
41
+ ==== Command: <tt>backup Describe arguments to backup here</tt>
42
+ Describe backup here
43
+
44
+
45
+ ==== Command: <tt>convert Describe arguments to convert here</tt>
46
+ Describe convert here
47
+
48
+
49
+ ==== Command: <tt>delete </tt>
50
+ Deletes everything under the path recursively
51
+
52
+
53
+ ==== Command: <tt>diff Describe arguments to diff here</tt>
54
+ Describe diff here
55
+
56
+
57
+ ==== Command: <tt>generate Describe arguments to generate here</tt>
58
+ Describe generate here
59
+
60
+
61
+ ==== Command: <tt>help command</tt>
62
+ Shows a list of commands or help for one command
63
+
64
+ Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function
65
+ ===== Options
66
+ ===== -c
67
+ List commands one per line, to assist with shell completion
68
+
69
+
70
+
71
+ ==== Command: <tt>provision Describe arguments to provision here</tt>
72
+ Describe provision here
73
+
74
+
75
+ ==== Command: <tt>remove Describe arguments to remove here</tt>
76
+ Describe remove here
77
+
78
+
79
+ ==== Command: <tt>restore Describe arguments to restore here</tt>
80
+ Describe restore here
81
+
82
+
83
+ ==== Command: <tt>tree </tt>
84
+ Represents the path as a tree
85
+
86
+
87
+ ==== Command: <tt>write Describe arguments to write here</tt>
88
+ Describe write here
89
+
90
+
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vaulty
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jan Stevens
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: vault
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hirb
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.7.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.7.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: tty
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.7.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.7.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: hashdiff
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.3.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.3.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: gli
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 2.16.0
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 2.16.0
97
+ description: Vault CLI that is based on the Vault Ruby gem
98
+ email:
99
+ - jan@playpass.be
100
+ executables:
101
+ - vaulty
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".rubocop.yml"
108
+ - ".rubocop_todo.yml"
109
+ - ".travis.yml"
110
+ - Gemfile
111
+ - LICENSE
112
+ - README.md
113
+ - Rakefile
114
+ - bin/vaulty
115
+ - lib/vaulty.rb
116
+ - lib/vaulty/catacomb.rb
117
+ - lib/vaulty/cli/add.rb
118
+ - lib/vaulty/cli/command.rb
119
+ - lib/vaulty/cli/delete.rb
120
+ - lib/vaulty/cli/tree.rb
121
+ - lib/vaulty/cli_app.rb
122
+ - lib/vaulty/output/banner.rb
123
+ - lib/vaulty/output/table.rb
124
+ - lib/vaulty/output/tree.rb
125
+ - lib/vaulty/vault_tree.rb
126
+ - lib/vaulty/version.rb
127
+ - vaulty.gemspec
128
+ - vaulty.rdoc
129
+ homepage: https://fritz.ninja
130
+ licenses:
131
+ - MIT
132
+ metadata: {}
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: '2.3'
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubyforge_project:
150
+ rubygems_version: 2.6.11
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: A description of your project
154
+ test_files: []