A task library for maintaining an open-source library.
The description to use if none is set
The server to release to by default
The default license for the project in SPDX form: spdx.org/licenses
The version to use if one cannot be read from the source
The path to the data directory for the Prestigio library.
The file suffixes to include in documentation
Environment variable overrides for settings
The file that contains the project's dependencies
Words in the package/gem name that should not be included in deriving paths, file names, etc.
Paths
The version of this library
Pattern for extracting a version constant
The gemserver to push gems to
The gem's authors in the form of strings in the format: `Name <email>`
The public cetificates that can be used to verify signed gems
The Gem::RequestSet that describes the gem's dependencies
The descriotion of the gem
The Array of project files that are in the bin/ directory and are executable.
A FileMap of the paths to this project's extension config scripts.
The URI of the project's homepage as a String
The licenses the project is distributed under; usual practice is to list the SPDX name: spdx.org/licenses
The name of the gem the task will build
The options Hash the task lib was created with
The files which should be distributed with the project as a Rake::FileList
The rsync-compatible target to publish documentation to.
The files which should be used to generate documentation as a Rake::FileList
The README of the project as an RDoc::Markup::Document
The version of Ruby required by this gem, in Gem version format.
The summary description of the gem.
The title of the library for things like docs, gemspec, etc.
The Gem::Version of the current library, extracted from the top-level namespace.
Returns true if Rake::DevEiate has already been set up.
# File lib/rake/deveiate.rb, line 138
def self::already_setup?
        Rake::Task.task_defined?( 'deveiate' )
end
						Create the devEiate tasks for a gem with the given name.
# File lib/rake/deveiate.rb, line 144
def initialize( name, **options, &block )
        @name          = validate_gemname( name )
        @options       = options
        @rakefile      = PROJECT_DIR + 'Rakefile'
        @manifest_file = DEFAULT_MANIFEST_FILE.dup
        @project_files = self.read_manifest
        @executables   = self.find_executables
        @readme_file   = self.find_readme
        @history_file  = self.find_history_file
        @readme        = self.parse_readme
        @rdoc_files    = self.make_rdoc_filelist
        @cert_files    = Rake::FileList[ CERTS_DIR + '*.pem' ]
        @licenses      = [ DEFAULT_LICENSE ]
        @version_from  = env( :version_from, as_pathname: true ) ||
                LIB_DIR + "%s.rb" % [ version_file_from(name) ]
        @docs_dir      = DOCS_DIR.dup
        @title         = self.extract_default_title
        @authors       = self.extract_authors
        @homepage      = self.extract_homepage
        @description   = self.extract_description || DEFAULT_DESCRIPTION
        @summary       = nil
        @dependencies  = self.find_dependencies
        @extensions    = Rake::FileList.new
        @version       = nil
        @publish_to    = nil
        @required_ruby_version = nil
        super()
        self.load_task_libraries
        if block
                if block.arity.nonzero?
                        block.call( self )
                else
                        self.instance_exec( self, &block )
                end
        end
end
						Set up common development tasks
# File lib/rake/deveiate.rb, line 130
def self::setup( name, **options, &block )
        tasklib = self.new( name, **options, &block )
        tasklib.define_tasks
        return tasklib
end
						Return the Rake::FileList that's used in lieu of the manifest file if it isn't present.
# File lib/rake/deveiate.rb, line 556
def default_manifest
        return DEFAULT_PROJECT_FILES.dup
end
						Set up tasks for debugging the task library.
# File lib/rake/deveiate.rb, line 378
def define_debug_tasks
        task( :base_debug ) do
                self.output_documentation_debugging
                self.output_project_files_debugging
                self.output_dependency_debugging
        end
        task :debug => :base_debug
end
						Set up a simple default task
# File lib/rake/deveiate.rb, line 335
def define_default_tasks
        # task used to indicate that rake-deveiate has already been setup once; for
        # global rakefiles.
        task :deveiate do
                # no-op
        end
        desc "The task that runs by default"
        task( :default => :spec )
        desc "Check in the current changes"
        task :checkin => [ :precheckin, :check, :test ]
        task :commit => :checkin
        task :ci => :checkin
        task :precheckin
        desc "Sanity-check the project"
        task :check
        desc "Update the history file"
        task :update_history
        desc "Package up and push a release"
        task :release => [ :prerelease, :gem, :release_gem, :postrelease ]
        task :prerelease
        task :release_gem
        task :postrelease
        desc "Run all the project's tests"
        task :test
        task :spec
        task :integration
        desc "Set up the project for development"
        task :setup do
                self.install_dependencies
        end
end
						Task-definition hook.
# File lib/rake/deveiate.rb, line 326
def define_tasks
        self.define_default_tasks
        self.define_debug_tasks
        super if defined?( super )
end
						Extract the default title from the README if possible, or derive it from the gem name.
# File lib/rake/deveiate.rb, line 425
def extract_default_title
        return self.name unless self.readme&.table_of_contents&.first
        title = self.readme.table_of_contents.first.text
        title ||= self.name
end
						Extract a description from the README if possible. Returns nil if not.
# File lib/rake/deveiate.rb, line 439
def extract_description
        parts = self.readme&.parts or return nil
        desc_para = parts.find {|part| part.is_a?(RDoc::Markup::Paragraph) }&.text or return nil
        formatter = RDoc::Markup::ToHtml.new( RDoc::Options.new )
        html = formatter.convert( desc_para )
        return html.gsub( /<.*?>/, '' ).strip
end
						Extract the URI of the homepage from the `home` item of the first NOTE-type list in the README. Returns nil if no such URI could be found.
# File lib/rake/deveiate.rb, line 487
def extract_homepage
        return fail_extraction( :homepage, "no README" ) unless self.readme
        list = self.readme.parts.find {|part| RDoc::Markup::List === part && part.type == :NOTE } or
                return fail_extraction(:homepage, "No NOTE list")
        item = list.items.find {|item| item.label.include?('home') } or
                return fail_extraction(:homepage, "No `home` item")
        return item.parts.first.text
end
						Extract a summary from the README if possible. Returns nil if not.
# File lib/rake/deveiate.rb, line 433
def extract_summary
        return self.description.split( /(?<=\.)\s+/ ).first.gsub( /\n/, ' ' )
end
						Load the gemdeps file if it exists, and return a Gem::RequestSet with the regular dependencies contained in it.
# File lib/rake/deveiate.rb, line 652
def find_dependencies
        unless GEMDEPS_FILE.readable?
                self.prompt.warn "Deps file (%s) is missing or unreadable, assuming no dependencies." %
                        [ GEMDEPS_FILE ]
                return []
        end
        finder = Rake::DevEiate::GemDepFinder.new( GEMDEPS_FILE )
        finder.load
        return finder.dependencies
end
						Return an Array of the paths for the executables contained in the project files.
# File lib/rake/deveiate.rb, line 500
def find_executables
        paths = self.project_files.find_all do |path|
                path.start_with?( 'bin/' )
        end
        return paths.map {|path| path[%r{^bin/(.*)}, 1] }
end
						Find the history file in the list of project files and return it as a Pathname.
# File lib/rake/deveiate.rb, line 589
def find_history_file
        file = self.project_files.find {|file| file =~ /^History\.(md|rdoc)$/ }
        if file
                return Pathname( file )
        else
                self.prompt.warn "No History.{md,rdoc} found in the project files."
                return DEFAULT_HISTORY_FILE
        end
end
						Find the README file in the list of project files and return it as a Pathname.
# File lib/rake/deveiate.rb, line 576
def find_readme
        file = self.project_files.find {|file| file =~ /^README\.(md|rdoc)$/ }
        if file
                return Pathname( file )
        else
                self.prompt.warn "No README found in the project files."
                return DEFAULT_README_FILE
        end
end
						Find the file that contains the VERSION constant and return it as a Gem::Version.
# File lib/rake/deveiate.rb, line 517
def find_version
        version_file = self.version_from
        unless version_file.readable?
                self.prompt.warn "Version could not be read from %s" % [ version_file ]
                return nil
        end
        version_line = version_file.readlines.find {|l| l =~ VERSION_PATTERN } or
                abort "Can't read the VERSION from #{version_file}!"
        version = version_line[ VERSION_PATTERN, :version ] or
                abort "Couldn't find a semantic version in %p" % [ version_line ]
        return Gem::Version.new( version )
end
						Generate a TTY::Table from the current dependency list and return it.
# File lib/rake/deveiate.rb, line 624
def generate_dependencies_table
        table = TTY::Table.new( header: ['Gem', 'Version', 'Type'] )
        self.dependencies.each do |dep|
                table << [ dep.name, dep.requirement.to_s, dep.type ]
        end
        return table
end
						Generate a TTY::Table from the current project files and return it.
# File lib/rake/deveiate.rb, line 601
def generate_project_files_table
        columns = [
                self.project_files.sort,
                self.rdoc_files.sort
        ]
        max_length = columns.map( &:length ).max
        columns.each do |col|
                self.trace "Filling out columns %d-%d" % [ col.length, max_length ]
                next if col.length == max_length
                col.fill( '', col.length .. max_length - 1 )
        end
        table = TTY::Table.new(
                header: ['Project', 'Documentation'],
                rows: columns.transpose,
        )
        return table
end
						Returns true if the manifest file exists and is readable.
# File lib/rake/deveiate.rb, line 535
def has_manifest?
        return self.manifest_file.readable?
end
						Return the character used to build headings give the filename of the file to be generated.
# File lib/rake/deveiate.rb, line 674
def header_char_for( filename )
        case File.extname( filename )
        when '.md' then return '#'
        when '.rdoc' then return '='
        when ''
                if filename == 'Rakefile'
                        return '#'
                end
        end
        raise "Don't know what header character is appropriate for %s" % [ filename ]
end
						The file that provides high-level change history
# File lib/rake/deveiate.rb, line 236
attr_pathname :history_file
						Return a copy of the given text prefixed by spaces number of spaces.
# File lib/rake/deveiate.rb, line 782
def indent( text, spaces=4 )
        prefix = ' ' * spaces
        return text.gsub( /(?<=\A|\n)/m, prefix )
end
						Install the gems listed in the gem dependencies file.
# File lib/rake/deveiate.rb, line 666
def install_dependencies
        self.prompt.say "Installing dependencies"
        ruby '-S', 'gem', 'i', '-Ng'
end
						Load the template at the specified template_path, and render it with suitable settings for the given target_filename.
# File lib/rake/deveiate.rb, line 701
def load_and_render_template( template_path, target_filename )
        template = self.read_template( template_path )
        header_char = self.header_char_for( target_filename )
        return template.result_with_hash(
                header_char: header_char,
                project: self
        )
end
						Load the deveiate task libraries.
# File lib/rake/deveiate.rb, line 297
def load_task_libraries
        taskdir = Pathname( __FILE__.delete_suffix('.rb') )
        tasklibs = Rake::FileList[ taskdir + '*.rb' ].pathmap( '%-2d/%n' )
        self.trace( "Loading task libs: %p" % [ tasklibs ] )
        tasklibs.each do |lib|
                require( lib )
        end
        self.class.constants.
                map {|c| self.class.const_get(c) }.
                select {|c| c.respond_to?(:instance_methods) }.
                select {|c| c.instance_methods(false).include?(:define_tasks) }.
                each do |mod|
                        self.trace "Loading tasks from %p" % [ mod ]
                        extend( mod )
                end
        self.setup( self.name, **self.options )
end
						Make a Rake::FileList of the files that should be used to generate documentation.
# File lib/rake/deveiate.rb, line 563
def make_rdoc_filelist
        list = self.project_files.dup
        list.exclude do |fn|
                fn =~ %r:^(spec|data)/: || !fn.end_with?( *DOCUMENTATION_SUFFIXES )
        end
        return list
end
						The file to read the list of distribution files from
# File lib/rake/deveiate.rb, line 240
attr_pathname :manifest_file
						Output debugging about the project's dependencies.
# File lib/rake/deveiate.rb, line 769
def output_dependency_debugging
        self.prompt.say( "Dependencies", color: :bright_green )
        table = self.generate_dependencies_table
        if table.empty?
                self.prompt.warn( "None." )
        else
                self.prompt.say( table.render(:unicode, padding: [0,1]) )
        end
        self.prompt.say( "\n" )
end
						Output debugging information about documentation.
# File lib/rake/deveiate.rb, line 733
def output_documentation_debugging
        summary = self.extract_summary
        description = self.extract_description
        homepage = self.extract_homepage
        self.prompt.say( "Documentation", color: :bright_green )
        self.prompt.say( "Authors:" )
        self.authors.each do |author|
                self.prompt.say( " • " )
                self.prompt.say( author, color: :bold )
        end
        self.prompt.say( "Summary: " )
        self.prompt.say( summary, color: :bold )
        self.prompt.say( "Description:" )
        self.prompt.say( "   " + description, color: :bold )
        self.prompt.say( "Homepage:" )
        self.prompt.say( "   " + homepage, color: :bold )
        self.prompt.say( "\n" )
end
						Output debugging info related to the list of project files the build operates on.
# File lib/rake/deveiate.rb, line 756
def output_project_files_debugging
        self.prompt.say( "Project files:", color: :bright_green )
        table = self.generate_project_files_table
        if table.empty?
                self.prompt.warn( "None." )
        else
                self.prompt.say( table.render(:unicode, padding: [0,1]) )
        end
        self.prompt.say( "\n" )
end
						Parse the README into an RDoc::Markup::Document and return it
# File lib/rake/deveiate.rb, line 636
def parse_readme
        return nil unless self.readme_file.readable?
        case self.readme_file.extname
        when '.md'
                return RDoc::Markdown.parse( self.readme_file.read )
        when '.rdoc'
                return RDoc::Markup.parse( self.readme_file.read )
        else
                raise "Can't parse %s: unhandled format %p" % [ self.readme_file, README_FILE.extname ]
        end
end
						Fetch the Pastel object, creating it if necessary.
# File lib/rake/deveiate.rb, line 400
def pastel
        return @pastel ||= begin
                pastel = Pastel.new( enabled: $stdout.tty? )
                pastel.alias_color( :headline, :bold, :white, :on_black )
                pastel.alias_color( :success, :bold, :green )
                pastel.alias_color( :error, :bold, :red )
                pastel.alias_color( :warning, :yellow )
                pastel.alias_color( :added, :green )
                pastel.alias_color( :removed, :red )
                pastel.alias_color( :prompt, :cyan )
                pastel.alias_color( :even_row, :bold )
                pastel.alias_color( :odd_row, :reset )
                pastel
        end
end
						Fetch the TTY-Prompt, creating it if necessary.
# File lib/rake/deveiate.rb, line 394
def prompt
        return @prompt ||= TTY::Prompt.new( output: $stderr )
end
						The project's Rakefile
# File lib/rake/deveiate.rb, line 228
attr_pathname :rakefile
						Read the manifest file if there is one, falling back to a default list if there isn't a manifest.
# File lib/rake/deveiate.rb, line 542
def read_manifest
        if self.has_manifest?
                entries = self.manifest_file.readlines.map( &:chomp )
                return Rake::FileList[ *entries ]
        else
                self.prompt.warn "No manifest (%s): falling back to a default list" %
                        [ self.manifest_file ]
                return self.default_manifest
        end
end
						Read a template with the given name from the data directory and return it as an ERB object.
# File lib/rake/deveiate.rb, line 690
def read_template( name )
        name = "%s.erb" % [ name ] unless name.to_s.end_with?( '.erb' )
        template_path = DEVEIATE_DATADIR + name
        template_src = template_path.read( encoding: 'utf-8' )
        return ERB.new( template_src, trim_mode: '-' )
end
						The file that will be the main page of documentation
# File lib/rake/deveiate.rb, line 232
attr_pathname :readme_file
						Post-loading callback.
# File lib/rake/deveiate.rb, line 320
def setup( name, **options )
        # No-op
end
						Output args to $stderr if tracing is enabled.
# File lib/rake/deveiate.rb, line 418
def trace( *args )
        Rake.application.trace( *args ) if Rake.application.options.trace
end
						The pathname of the file to read the version from
# File lib/rake/deveiate.rb, line 224
attr_pathname :version_from
						Yield an IO to the block open to a file that will replace filename if the block returns successfully.
# File lib/rake/deveiate.rb, line 714
def write_replacement_file( filename, **opts )
        path = Pathname( filename ).expand_path
        mode = path.stat.mode
        owner = path.stat.uid
        group = path.stat.gid
        tmpfile = Tempfile.create( path.basename.to_s, **opts ) do |fh|
                yield( fh )
                newfile = Pathname( fh.path ).expand_path
                newfile.rename( path )
                path.chown( owner, group )
                path.chmod( mode )
        end
end