license_header 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|