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: []
|