Skip to content

Commit

Permalink
Merge pull request #20 from veracross/cli
Browse files Browse the repository at this point in the history
Create a basic CLI for rendering templates
  • Loading branch information
sixfeetover authored Jan 2, 2019
2 parents 95f6afe + aa1e2c1 commit 5e3b05b
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 8 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ Pre-existing copies of files generated by Consult (such as `secrets.yml`, `datab

If this gem is included in a Rails, the templates will render on Rails boot. Configuration or credential changes can be picked up by restarting your app.

### CLI

Render templates on demand with the CLI. By default, this will bypass template TTLs to force rendering and provide verbose output. See `consult --help` for options.

```bash
$ bundle exec consult
Consult: Rendered my_config
Consult: Rendered secrets
```

### Configuration

```yaml
Expand Down
7 changes: 7 additions & 0 deletions bin/consult
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env ruby

require_relative '../lib/consult/cli'

cli = Consult::CLI.instance
cli.parse
cli.render
7 changes: 3 additions & 4 deletions consult.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,18 @@ Gem::Specification.new do |spec|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
f.match(%r{^(test|spec|features)/})
end
spec.bindir = 'exe'
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.bindir = 'bin'
spec.executables = ['consult']
spec.require_paths = ['lib']

spec.add_dependency 'activesupport', '> 4', '< 6'
spec.add_dependency 'diplomat', '~> 2.0.2'
spec.add_dependency 'vault', '>= 0.10.0', '< 1.0.0'

spec.add_development_dependency 'bundler', '~> 1.16'
spec.add_development_dependency 'byebug'
spec.add_development_dependency 'guard'
spec.add_development_dependency 'guard-rspec'
spec.add_development_dependency 'pry'
spec.add_development_dependency 'pry-byebug'
spec.add_development_dependency 'rake', '~> 10.0'
spec.add_development_dependency 'rspec', '~> 3.0'
spec.add_development_dependency 'simplecov'
Expand Down
11 changes: 7 additions & 4 deletions lib/consult.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@ module Consult
CONSUL_DISK_TOKEN = Pathname.new("#{Dir.home}/.consul-token").freeze

class << self
attr_reader :config, :templates
def load(config_dir: nil)
attr_reader :config, :templates, :force_render

def load(config_dir: nil, force_render: false, verbose: nil)
root directory: config_dir
yaml = root.join('config', 'consult.yml')

@all_config = yaml.exist? ? YAML.safe_load(ERB.new(yaml.read).result, [], [], true).to_h : {}
@all_config.deep_symbolize_keys!

@config = @all_config[:shared].to_h.deep_merge @all_config[env&.to_sym].to_h
@templates = @config[:templates]&.map { |name, config| Template.new(name, config) } || []
@templates = @config[:templates]&.map { |name, config| Template.new(name, config.merge(verbose: verbose)) } || []

@force_render = force_render

configure_consul
configure_vault
Expand Down Expand Up @@ -71,7 +74,7 @@ def env

# Return only the templates that are relevant for the current environment
def active_templates
templates.select(&:should_render?)
force_render ? templates : templates.select(&:should_render?)
end

# Render templates.
Expand Down
55 changes: 55 additions & 0 deletions lib/consult/cli.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

$stdout.sync = true

require 'singleton'
require 'optparse'

require_relative '../consult'

module Consult
class CLI
include Singleton

attr_reader :opts

def parse(args = ARGV)
@opts = parse_options(args)
Consult.load @opts
end

def render
Consult.render!
end

def parse_options(argv)
opts = {
config_dir: Dir.pwd,
force_render: true,
verbose: true
}

@parser = OptionParser.new do |o|
o.on '-d', '--directory=DIR', 'Path to directory containing the config directory' do |arg|
opts[:config_dir] = arg
end

o.on '-f', '--[no-]force', TrueClass, 'Ignore template TTLs and force rendering' do |arg|
opts[:force_render] = arg
end

o.on '-v', '--quiet', FalseClass, 'Silence output' do |arg|
opts[:verbose] = arg
end
end

@parser.on_tail "-h", "--help", "Show help" do
puts @parser
exit 1
end

@parser.parse! argv
opts
end
end
end
5 changes: 5 additions & 0 deletions lib/consult/template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def render(save: true)
result = renderer.result(binding)

File.open(dest, 'w') { |f| f << result } if save
puts "Consult: Rendered #{name}" if verbose?
result
rescue StandardError => e
STDERR.puts "Error rendering template: #{name}"
Expand Down Expand Up @@ -55,6 +56,10 @@ def expired?
dest.mtime < (Time.now - @config[:ttl].to_i)
end

def verbose?
@config[:verbose]
end

def ordered_locations
@config.keys & LOCATIONS
end
Expand Down
28 changes: 28 additions & 0 deletions spec/bin/cli_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

require 'consult/cli'

RSpec.describe Consult::CLI do
let(:cli) { Consult::CLI.instance }

describe 'options' do
it 'parses --directory' do
options = cli.parse ['--directory', 'spec/support']
expect(cli.opts[:config_dir]).to eq 'spec/support'
end

it 'parses --force' do
options = cli.parse ['--force']
expect(cli.opts[:force_render]).to be true
end

it 'parses --quiet' do
options = cli.parse ['--quiet']
expect(cli.opts[:verbose]).to be false
end
end

it 'renders' do
expect {cli.render}.not_to raise_exception
end
end
12 changes: 12 additions & 0 deletions spec/consult_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,16 @@
expect(FileUtils.compare_file("spec/support/expected/#{template}", "spec/support/rendered/#{template}")).to be true
end
end

it 'obeys TTLs' do
Consult.load config_dir: directory, force_render: false
Consult.render!
expect(Consult.active_templates).not_to eq(Consult.templates)
end

it 'can force template rendering' do
Consult.load config_dir: directory, force_render: true
Consult.render!
expect(Consult.active_templates).to eq Consult.templates
end
end
6 changes: 6 additions & 0 deletions spec/lib/template_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@
expect(template.should_render?).to be(true)
end

it 'supports verbose output' do
t = Consult::Template.new 'verbose', config.merge(verbose: true)
expect(t.verbose?).to be(true)
expect { t.render }.to output(/Consult: Rendered verbose/i).to_stdout_from_any_process
end

it 'outputs render failures to stderr' do
expect { fail_template.render }.to output(/Error rendering template*/).to_stderr_from_any_process
end
Expand Down

0 comments on commit 5e3b05b

Please sign in to comment.