license_header 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +29 -0
- data/bin/license_header +107 -0
- data/lib/license_header/auditor.rb +142 -0
- data/lib/license_header/version.rb +3 -0
- data/lib/license_header.rb +6 -0
- metadata +108 -0
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            # LicenseHeader
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            TODO: Write a gem description
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ## Installation
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Add this line to your application's Gemfile:
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                gem 'license_header'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            And then execute:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                $ bundle
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            Or install it yourself as:
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                $ gem install license_header
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ## Usage
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            TODO: Write usage instructions here
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            ## Contributing
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            1. Fork it
         | 
| 26 | 
            +
            2. Create your feature branch (`git checkout -b my-new-feature`)
         | 
| 27 | 
            +
            3. Commit your changes (`git commit -am 'Add some feature'`)
         | 
| 28 | 
            +
            4. Push to the branch (`git push origin my-new-feature`)
         | 
| 29 | 
            +
            5. Create new Pull Request
         | 
    
        data/bin/license_header
    ADDED
    
    | @@ -0,0 +1,107 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'license_header'
         | 
| 4 | 
            +
            require 'optparse'
         | 
| 5 | 
            +
            require 'highline/import'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            command = nil
         | 
| 8 | 
            +
            options = { :exceptions => [] }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            @prompt = true
         | 
| 11 | 
            +
            def getYyn(text)
         | 
| 12 | 
            +
              if @prompt
         | 
| 13 | 
            +
                response = ask("#{text} (Y/y/n) ") { |q| 
         | 
| 14 | 
            +
                  q.overwrite = true
         | 
| 15 | 
            +
                  q.echo = true
         | 
| 16 | 
            +
                  q.character = true
         | 
| 17 | 
            +
                  q.validate { |r| r =~ /^[Yyn]$/ } 
         | 
| 18 | 
            +
                }
         | 
| 19 | 
            +
                if response == 'Y'
         | 
| 20 | 
            +
                  @prompt = false
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
                return response.downcase == 'y'
         | 
| 23 | 
            +
              else
         | 
| 24 | 
            +
                return true
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            opts = OptionParser.new do |opts|
         | 
| 29 | 
            +
              opts.banner = "Usage: #{File.basename $0} <action> [options] <filespec>"
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              opts.separator 'Actions:'
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              opts.on('-a', '--audit', "Print a list of files needing license updates") do
         | 
| 34 | 
            +
                command = :audit
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              opts.on('-u', '--update', "Update files requiring license headers") do
         | 
| 38 | 
            +
                command = :update
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              opts.on('-r', '--remove', "Remove license headers from files") do
         | 
| 42 | 
            +
                command = :remove
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              opts.separator 'Common options:'
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              opts.on('-f FILE', '--header-file=FILE', "File containing header text") do |arg|
         | 
| 48 | 
            +
                options[:header] = arg
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              opts.on('-x MASK', '--exclude=MASK', "Exclude pattern") do |arg|
         | 
| 52 | 
            +
                pattern = Regexp.escape(arg).gsub('\*','.*?')
         | 
| 53 | 
            +
                re = /^#{pattern}$/i
         | 
| 54 | 
            +
                options[:exceptions] << re
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              opts.on('-y', '--yes', "Assume yes reponses to all prompts") do
         | 
| 58 | 
            +
                @prompt = false
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              opts.on('-v', '--verbose', "Run verbosely") do
         | 
| 62 | 
            +
                options[:verbose] = true
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
              opts.on_tail('-h', '--help', "Show this message") do
         | 
| 66 | 
            +
                puts opts
         | 
| 67 | 
            +
                exit
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
            end
         | 
| 70 | 
            +
            opts.parse!
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            if command.nil? or options[:header].nil? or ARGV.empty?
         | 
| 73 | 
            +
              puts opts
         | 
| 74 | 
            +
              exit
         | 
| 75 | 
            +
            end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            targets = ARGV.collect do |spec|
         | 
| 78 | 
            +
              File.directory?(spec) ? File.join(spec,"**","*") : spec
         | 
| 79 | 
            +
            end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            auditor = LicenseHeader::Auditor.new(options)
         | 
| 82 | 
            +
            files = auditor.audit(*targets)
         | 
| 83 | 
            +
            invalid = (files[:missing]+files[:invalid]).sort
         | 
| 84 | 
            +
            present = (files[:present]+files[:valid]).sort
         | 
| 85 | 
            +
            case command
         | 
| 86 | 
            +
            when :audit
         | 
| 87 | 
            +
              $stderr.puts "#{files[:valid].length} files have correct headers"
         | 
| 88 | 
            +
              $stderr.puts files[:valid].join("\n") if options[:verbose]
         | 
| 89 | 
            +
              $stderr.puts "#{files[:missing].length} files have missing headers"
         | 
| 90 | 
            +
              $stderr.puts files[:missing].join("\n") if options[:verbose]
         | 
| 91 | 
            +
              $stderr.puts "#{files[:invalid].length} files have invalid headers"
         | 
| 92 | 
            +
              $stderr.puts files[:invalid].join("\n") if options[:verbose]
         | 
| 93 | 
            +
            when :update
         | 
| 94 | 
            +
              $stderr.puts "#{invalid.length} files have missing or incorrect headers"
         | 
| 95 | 
            +
              auditor.process_files(:update, *  invalid) do |file, format|
         | 
| 96 | 
            +
                response = getYyn("Update header in #{file}?")
         | 
| 97 | 
            +
                $stderr.puts "Updating #{file}..." if response
         | 
| 98 | 
            +
                response
         | 
| 99 | 
            +
              end
         | 
| 100 | 
            +
            when :remove
         | 
| 101 | 
            +
              $stderr.puts "#{present.length} files have headers"
         | 
| 102 | 
            +
              auditor.process_files(:remove, *present) do |file, format|
         | 
| 103 | 
            +
                response = getYyn("Remove header from #{file}?")
         | 
| 104 | 
            +
                $stderr.puts "Cleaning up #{file}..." if response
         | 
| 105 | 
            +
                response
         | 
| 106 | 
            +
              end
         | 
| 107 | 
            +
            end
         | 
| @@ -0,0 +1,142 @@ | |
| 1 | 
            +
            require 'fileutils'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Module to assist in making sure that all files have the right license
         | 
| 4 | 
            +
            # block as a header.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module LicenseHeader
         | 
| 7 | 
            +
              # For each language you can define three variables
         | 
| 8 | 
            +
              #
         | 
| 9 | 
            +
              # :pre will be prepended to the header
         | 
| 10 | 
            +
              # :each will be prepended to each line of the header
         | 
| 11 | 
            +
              # :post will be appended to the end of the file
         | 
| 12 | 
            +
              #
         | 
| 13 | 
            +
              # Only :each is required; if the other two are not provided they will be
         | 
| 14 | 
            +
              # ignored
         | 
| 15 | 
            +
              LANGUAGE_SYNTAX = { 
         | 
| 16 | 
            +
                :css        => { :pre => '/* ',  :each => ' * ', :post => '*/',  :exts => %w(.css .scss)        },
         | 
| 17 | 
            +
                :erb        => { :pre => '<%#',  :each => '',    :post => '%>',  :exts => %w(.erb)              },
         | 
| 18 | 
            +
                :haml       => { :pre => '-#',   :each => '  ',                  :exts => %w(.haml)             },
         | 
| 19 | 
            +
                :html       => { :pre => '<!--', :each => '',    :post => '-->', :exts => %w(.html)             },
         | 
| 20 | 
            +
                :javascript => { :pre => '/* ',  :each => ' * ', :post => '*/',  :exts => %w(.js .json)         },
         | 
| 21 | 
            +
                :ruby       => {                 :each => '# ',                  :exts => %w(.rb .rake .coffee) },
         | 
| 22 | 
            +
              }
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              class Auditor
         | 
| 25 | 
            +
                attr_accessor :exceptions, :header, :verbose
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                # Should set some sensible defaults here for license location, file
         | 
| 28 | 
            +
                # mappings, etc
         | 
| 29 | 
            +
                def initialize(configuration)
         | 
| 30 | 
            +
                  @verbose = configuration[:verbose]
         | 
| 31 | 
            +
                  @header = configuration[:header]
         | 
| 32 | 
            +
                  @exceptions = configuration[:exceptions]
         | 
| 33 | 
            +
                  initialize_headers
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                # Create a list of all files grouped by type and return the map
         | 
| 37 | 
            +
                def audit(*patterns)
         | 
| 38 | 
            +
                  result = Hash.new { |h,k| h[k] = [] }
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  Dir.glob(patterns).each do |entry|
         | 
| 41 | 
            +
                    # Skip directories and files that match the exceptions
         | 
| 42 | 
            +
                    next if File.directory?(entry)
         | 
| 43 | 
            +
                    if @exceptions.any? { |ex| ex.match(entry) }
         | 
| 44 | 
            +
                      $stderr.puts "Skipping #{entry}" if @verbose
         | 
| 45 | 
            +
                      next
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    # Now to get down to business
         | 
| 49 | 
            +
                    format = determine_format(entry)
         | 
| 50 | 
            +
                    result[evaluate_header(entry, format)] << entry
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  return result
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def evaluate_header(path, format)
         | 
| 57 | 
            +
                  if format.nil? or format[:header].nil?
         | 
| 58 | 
            +
                    :not_applicable
         | 
| 59 | 
            +
                  else
         | 
| 60 | 
            +
                    file_content = read_file(path)
         | 
| 61 | 
            +
                    header_content = format[:header]
         | 
| 62 | 
            +
                    index = file_content.find_index { |l| l =~ /---  [E]ND AVALON LICENSE BLOCK  ---/ }
         | 
| 63 | 
            +
                    if index.nil?
         | 
| 64 | 
            +
                      :missing
         | 
| 65 | 
            +
                    else
         | 
| 66 | 
            +
                      file_heading = file_content[0, header_content.size]
         | 
| 67 | 
            +
                      if file_heading.eql? header_content
         | 
| 68 | 
            +
                        :valid
         | 
| 69 | 
            +
                      else
         | 
| 70 | 
            +
                        :present
         | 
| 71 | 
            +
                      end
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                def process_files(action, *files)
         | 
| 77 | 
            +
                  files.each do |file|
         | 
| 78 | 
            +
                    format = determine_format(file)
         | 
| 79 | 
            +
                    source_file = read_file(file)
         | 
| 80 | 
            +
                    confirm = block_given? ? yield(file) : true
         | 
| 81 | 
            +
                    if confirm and self.send(:"#{action.to_s}_license!", source_file, format)
         | 
| 82 | 
            +
                      write_file(file, source_file)
         | 
| 83 | 
            +
                    end
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                def update_license!(source_file, format)
         | 
| 88 | 
            +
                  remove_license!(source_file, format)
         | 
| 89 | 
            +
                  header_content = format[:header]
         | 
| 90 | 
            +
                  header_content.reverse.each { |line| source_file.insert(0, line) }
         | 
| 91 | 
            +
                  return true
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                def remove_license!(source_file, format)
         | 
| 95 | 
            +
                  end_of_license = source_file.find_index { |l| l =~ /---  [E]ND AVALON LICENSE BLOCK  ---/ }
         | 
| 96 | 
            +
                  if end_of_license.nil?
         | 
| 97 | 
            +
                    return false
         | 
| 98 | 
            +
                  else
         | 
| 99 | 
            +
                    extra_lines = format[:post].nil? ? 1 : 2
         | 
| 100 | 
            +
                    source_file.shift(end_of_license+extra_lines+1)
         | 
| 101 | 
            +
                    return true
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                private
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                def determine_format(file)
         | 
| 108 | 
            +
                  @headers.values.find { |syntax| syntax[:exts].include?(File.extname(file)) }
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                def read_file(file)
         | 
| 112 | 
            +
                  File.read(file).chomp.split(/\n/)
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                def write_file(file, content)
         | 
| 116 | 
            +
                  File.open("#{file}.tmp", "w") { |tmpfile| tmpfile.puts(content.join("\n")) }
         | 
| 117 | 
            +
                  FileUtils.rm(file)
         | 
| 118 | 
            +
                  FileUtils.mv("#{file}.tmp", file)
         | 
| 119 | 
            +
                end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                # Here we need to take the stock header and create two different versions -
         | 
| 122 | 
            +
                # one for Ruby based content using the # notation and another for
         | 
| 123 | 
            +
                # Javascript that uses /* */ syntax
         | 
| 124 | 
            +
                def initialize_headers
         | 
| 125 | 
            +
                  @headers = LANGUAGE_SYNTAX.clone
         | 
| 126 | 
            +
                  base = File.read(@header)
         | 
| 127 | 
            +
                  # Break each line down so we can do easy manipulation to create our new
         | 
| 128 | 
            +
                  # versions
         | 
| 129 | 
            +
                  license_terms = base.split(/\n/)
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                  @headers.each_pair do |lang,syntax|
         | 
| 132 | 
            +
                    syntax[:header] = []
         | 
| 133 | 
            +
                    syntax[:header] << syntax[:pre] unless syntax[:pre].nil?
         | 
| 134 | 
            +
                    syntax[:header] << "#{syntax[:each]} --- BEGIN AVALON LICENSE BLOCK ---"
         | 
| 135 | 
            +
                    syntax[:header] += license_terms.collect {|line| syntax[:each] + line }
         | 
| 136 | 
            +
                    syntax[:header] << "#{syntax[:each]} ---  #{'E'}ND AVALON LICENSE BLOCK  ---"
         | 
| 137 | 
            +
                    syntax[:header] << syntax[:post] unless syntax[:post].nil?
         | 
| 138 | 
            +
                    syntax[:header] << ""
         | 
| 139 | 
            +
                  end
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
              end
         | 
| 142 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,108 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: license_header
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.0.1
         | 
| 5 | 
            +
              prerelease: 
         | 
| 6 | 
            +
            platform: ruby
         | 
| 7 | 
            +
            authors:
         | 
| 8 | 
            +
            - Nathan Rogers
         | 
| 9 | 
            +
            - Michael Klein
         | 
| 10 | 
            +
            - Chris Colvard
         | 
| 11 | 
            +
            autorequire: 
         | 
| 12 | 
            +
            bindir: bin
         | 
| 13 | 
            +
            cert_chain: []
         | 
| 14 | 
            +
            date: 2013-04-18 00:00:00.000000000 Z
         | 
| 15 | 
            +
            dependencies:
         | 
| 16 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 17 | 
            +
              name: highline
         | 
| 18 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 19 | 
            +
                none: false
         | 
| 20 | 
            +
                requirements:
         | 
| 21 | 
            +
                - - ! '>='
         | 
| 22 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 23 | 
            +
                    version: '0'
         | 
| 24 | 
            +
              type: :runtime
         | 
| 25 | 
            +
              prerelease: false
         | 
| 26 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 27 | 
            +
                none: false
         | 
| 28 | 
            +
                requirements:
         | 
| 29 | 
            +
                - - ! '>='
         | 
| 30 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 31 | 
            +
                    version: '0'
         | 
| 32 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 33 | 
            +
              name: bundler
         | 
| 34 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 35 | 
            +
                none: false
         | 
| 36 | 
            +
                requirements:
         | 
| 37 | 
            +
                - - ~>
         | 
| 38 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 39 | 
            +
                    version: '1.3'
         | 
| 40 | 
            +
              type: :development
         | 
| 41 | 
            +
              prerelease: false
         | 
| 42 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 43 | 
            +
                none: false
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - ~>
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '1.3'
         | 
| 48 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 49 | 
            +
              name: rake
         | 
| 50 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                none: false
         | 
| 52 | 
            +
                requirements:
         | 
| 53 | 
            +
                - - ! '>='
         | 
| 54 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 55 | 
            +
                    version: '0'
         | 
| 56 | 
            +
              type: :development
         | 
| 57 | 
            +
              prerelease: false
         | 
| 58 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 59 | 
            +
                none: false
         | 
| 60 | 
            +
                requirements:
         | 
| 61 | 
            +
                - - ! '>='
         | 
| 62 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 63 | 
            +
                    version: '0'
         | 
| 64 | 
            +
            description: License header block auditing/updating
         | 
| 65 | 
            +
            email:
         | 
| 66 | 
            +
            - rogersna@indiana.edu
         | 
| 67 | 
            +
            executables:
         | 
| 68 | 
            +
            - license_header
         | 
| 69 | 
            +
            extensions: []
         | 
| 70 | 
            +
            extra_rdoc_files: []
         | 
| 71 | 
            +
            files:
         | 
| 72 | 
            +
            - lib/license_header/auditor.rb
         | 
| 73 | 
            +
            - lib/license_header/version.rb
         | 
| 74 | 
            +
            - lib/license_header.rb
         | 
| 75 | 
            +
            - bin/license_header
         | 
| 76 | 
            +
            - README.md
         | 
| 77 | 
            +
            homepage: 
         | 
| 78 | 
            +
            licenses: []
         | 
| 79 | 
            +
            post_install_message: 
         | 
| 80 | 
            +
            rdoc_options: []
         | 
| 81 | 
            +
            require_paths:
         | 
| 82 | 
            +
            - lib
         | 
| 83 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 84 | 
            +
              none: false
         | 
| 85 | 
            +
              requirements:
         | 
| 86 | 
            +
              - - ! '>='
         | 
| 87 | 
            +
                - !ruby/object:Gem::Version
         | 
| 88 | 
            +
                  version: '0'
         | 
| 89 | 
            +
                  segments:
         | 
| 90 | 
            +
                  - 0
         | 
| 91 | 
            +
                  hash: 3735959797291095628
         | 
| 92 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 93 | 
            +
              none: false
         | 
| 94 | 
            +
              requirements:
         | 
| 95 | 
            +
              - - ! '>='
         | 
| 96 | 
            +
                - !ruby/object:Gem::Version
         | 
| 97 | 
            +
                  version: '0'
         | 
| 98 | 
            +
                  segments:
         | 
| 99 | 
            +
                  - 0
         | 
| 100 | 
            +
                  hash: 3735959797291095628
         | 
| 101 | 
            +
            requirements: []
         | 
| 102 | 
            +
            rubyforge_project: 
         | 
| 103 | 
            +
            rubygems_version: 1.8.23
         | 
| 104 | 
            +
            signing_key: 
         | 
| 105 | 
            +
            specification_version: 3
         | 
| 106 | 
            +
            summary: This gem will assist in making sure that all files have the right license
         | 
| 107 | 
            +
              block as a header.
         | 
| 108 | 
            +
            test_files: []
         |