lib/bundles/inspec-init/cli.rb



# encoding: utf-8
# author: Christoph Hartmann

require 'pathname'

module Init
  class CLI < Inspec::BaseCLI
    namespace 'init'

    # TODO: find another solution, once https://github.com/erikhuda/thor/issues/261 is fixed
    def self.banner(command, _namespace = nil, _subcommand = false)
      "#{basename} #{subcommand_prefix} #{command.usage}"
    end

    def self.subcommand_prefix
      namespace
    end

    # read template directoy
    template_dir = File.join(File.dirname(__FILE__), 'templates')
    Dir.glob(File.join(template_dir, '*')) do |template|
      relative = Pathname.new(template).relative_path_from(Pathname.new(template_dir))

      # register command for the template
      desc "#{relative} NAME", "Create a new #{relative}"
      option :overwrite, type: :boolean, default: false,
         desc: 'Overwrites existing directory'
      define_method relative.to_s.to_sym do |name|
        generator(relative.to_s, { name: name }, options)
      end
    end

    private

    # 1. iterate over all files
    # 2. read content in erb
    # 3. write to target
    def generator(type, attributes = {}, options = {}) # rubocop:disable Metrics/AbcSize
      # path of this script
      dir = File.dirname(__FILE__)
      # look for template directory
      base_dir = File.join(dir, 'templates', type)
      # prepare glob for all subdirectories and files
      template = File.join(base_dir, '**', '{*,.*}')
      # generate target path
      target = Pathname.new(Dir.pwd).join(attributes[:name])
      puts "Create new #{type} at #{mark_text(target)}"

      # check that the directory does not exist
      if File.exist?(target) && !options['overwrite']
        error "#{mark_text(target)} exists already, use --overwrite"
        exit 1
      end

      # ensure that target directory is available
      FileUtils.mkdir_p(target)

      # iterate over files and write to target path
      Dir.glob(template) do |file|
        relative = Pathname.new(file).relative_path_from(Pathname.new(base_dir))
        destination = Pathname.new(target).join(relative)
        if File.directory?(file)
          li "Create directory #{mark_text(relative)}"
          FileUtils.mkdir_p(destination)
        elsif File.file?(file)
          li "Create file #{mark_text(relative)}"
          # read & render content
          content = render(File.read(file), attributes)
          # write file content
          File.write(destination, content)
        else
          puts "Ignore #{file}, because its not an file or directoy"
        end
      end
    end

    # This is a render helper to bind hash values to a ERB template
    def render(content, hash)
      # create a new binding class
      cls = Class.new do
        hash.each do |key, value|
          define_method key.to_sym do
            value
          end
        end
        # expose binding
        define_method :bind do
          binding
        end
      end
      ERB.new(content).result(cls.new.bind)
    end
  end

  # register the subcommand to Inspec CLI registry
  Inspec::Plugins::CLI.add_subcommand(Init::CLI, 'init', 'init TEMPLATE ...', 'Scaffolds a new project', {})
end