diff --git a/lib/puppet/face/module/generate.rb b/lib/puppet/face/module/generate.rb index c78af7e72..d25696547 100644 --- a/lib/puppet/face/module/generate.rb +++ b/lib/puppet/face/module/generate.rb @@ -1,251 +1,251 @@ Puppet::Face.define(:module, '1.0.0') do action(:generate) do summary "Generate boilerplate for a new module." description <<-EOT Generates boilerplate for a new module by creating the directory structure and files recommended for the Puppet community's best practices. A module may need additional directories beyond this boilerplate if it provides plugins, files, or templates. EOT returns "Array of Pathname objects representing paths of generated files." examples <<-EOT Generate a new module in the current directory: $ puppet module generate puppetlabs-ssh We need to create a metadata.json file for this module. Please answer the following questions; if the question is not applicable to this module, feel free to leave it blank. Puppet uses Semantic Versioning (semver.org) to version modules. What version is this module? [0.1.0] --> Who wrote this module? [puppetlabs] --> - What license does this module code fall under? [Apache 2.0] + What license does this module code fall under? [Apache-2.0] --> How would you describe this module in a single sentence? --> Where is this module's source code repository? --> Where can others go to learn more about this module? --> Where can others go to file issues about this module? --> ---------------------------------------- { "name": "puppetlabs-ssh", "version": "0.1.0", "author": "puppetlabs", "summary": null, - "license": "Apache 2.0", + "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [ { "name": "puppetlabs-stdlib", "version_requirement": ">= 1.0.0" } ] } ---------------------------------------- About to generate this metadata; continue? [n/Y] --> Notice: Generating module at /Users/username/Projects/puppet/puppetlabs-ssh... Notice: Populating ERB templates... Finished; module generated in puppetlabs-ssh. puppetlabs-ssh/manifests puppetlabs-ssh/manifests/init.pp puppetlabs-ssh/metadata.json puppetlabs-ssh/README.md puppetlabs-ssh/spec puppetlabs-ssh/spec/spec_helper.rb puppetlabs-ssh/tests puppetlabs-ssh/tests/init.pp EOT option "--skip-interview" do summary "Bypass the interactive metadata interview" description <<-EOT Do not attempt to perform a metadata interview. Primarily useful for automatic execution of `puppet module generate`. EOT end arguments "" when_invoked do |name, options| # Since we only want to interview if it's being rendered to the console # (i.e. when invoked with `puppet module generate`), we can't do any work # here in the when_invoked block. The result of this block is then # passed to each renderer, which will handle it appropriately; by # returning a simple message like this, every renderer will simply output # the string. # Our `when_rendering :console` handler will ignore this value and # actually generate the module. # # All this is necessary because it is not possible at this point in time # to know what the destination of the output is. "This format is not supported by this action." end when_rendering :console do |_, name, options| Puppet::ModuleTool.set_option_defaults options begin # A default dependency for all newly generated modules is being # introduced as a substitute for the comments we used to include in the # Modulefile. While introducing a default dependency is less than # perfectly desirable, the cost is low, and the syntax is obtuse enough # to justify its inclusion. metadata = Puppet::ModuleTool::Metadata.new.update( 'name' => name, 'version' => '0.1.0', 'dependencies' => [ { 'name' => 'puppetlabs-stdlib', 'version_requirement' => '>= 1.0.0' } ] ) rescue ArgumentError msg = "Could not generate directory #{name.inspect}, you must specify a dash-separated username and module name." raise ArgumentError, msg, $!.backtrace end dest = Puppet::ModuleTool::Generate.destination(metadata) result = Puppet::ModuleTool::Generate.generate(metadata, options[:skip_interview]) path = dest.relative_path_from(Pathname.pwd) puts "Finished; module generated in #{path}." result.join("\n") end end end module Puppet::ModuleTool::Generate module_function def generate(metadata, skip_interview = false) interview(metadata) unless skip_interview destination = duplicate_skeleton(metadata) all_files = destination.basename + '**/*' return Dir[all_files.to_s] end def interview(metadata) puts "We need to create a metadata.json file for this module. Please answer the" puts "following questions; if the question is not applicable to this module, feel free" puts "to leave it blank." begin puts puts "Puppet uses Semantic Versioning (semver.org) to version modules." puts "What version is this module? [#{metadata.version}]" metadata.update 'version' => user_input(metadata.version) rescue Puppet.err "We're sorry, we could not parse that as a Semantic Version." retry end puts puts "Who wrote this module? [#{metadata.author}]" metadata.update 'author' => user_input(metadata.author) puts puts "What license does this module code fall under? [#{metadata.license}]" metadata.update 'license' => user_input(metadata.license) puts puts "How would you describe this module in a single sentence?" metadata.update 'summary' => user_input(metadata.summary) puts puts "Where is this module's source code repository?" metadata.update 'source' => user_input(metadata.source) puts puts "Where can others go to learn more about this module?#{ metadata.project_page && " [#{metadata.project_page}]" }" metadata.update 'project_page' => user_input(metadata.project_page) puts puts "Where can others go to file issues about this module?#{ metadata.issues_url && " [#{metadata.issues_url}]" }" metadata.update 'issues_url' => user_input(metadata.issues_url) puts puts '-' * 40 puts metadata.to_json puts '-' * 40 puts puts "About to generate this metadata; continue? [n/Y]" if user_input('Y') !~ /^y(es)?$/i puts "Aborting..." exit 0 end end def user_input(default=nil) print '--> ' input = STDIN.gets.chomp.strip input = default if input == '' return input end def destination(metadata) return @dest if defined? @dest @dest = Pathname.pwd + metadata.name raise ArgumentError, "#{@dest} already exists." if @dest.exist? return @dest end def duplicate_skeleton(metadata) dest = destination(metadata) puts Puppet.notice "Generating module at #{dest}..." FileUtils.cp_r skeleton_path, dest populate_templates(metadata, dest) return dest end def populate_templates(metadata, destination) Puppet.notice "Populating templates..." formatters = { :erb => proc { |data, ctx| ERB.new(data).result(ctx) }, :template => proc { |data, _| data }, } formatters.each do |type, block| templates = destination + "**/*.#{type}" Dir.glob(templates.to_s, File::FNM_DOTMATCH).each do |erb| path = Pathname.new(erb) content = block[path.read, binding] target = path.parent + path.basename(".#{type}") target.open('w') { |f| f.write(content) } path.unlink end end end def skeleton_path return @path if defined? @path path = Pathname(Puppet.settings[:module_skeleton_dir]) path = Pathname(__FILE__).dirname + '../../module_tool/skeleton/templates/generator' unless path.directory? @path = path end end