giteaucrat 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.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/bin/giteaucrat +6 -0
- data/config/giteaucrat.yml +5 -0
- data/giteaucrat.gemspec +26 -0
- data/lib/core_ext/grit/blame.rb +36 -0
- data/lib/giteaucrat/author.rb +144 -0
- data/lib/giteaucrat/common.rb +29 -0
- data/lib/giteaucrat/file.rb +109 -0
- data/lib/giteaucrat/repo.rb +121 -0
- data/lib/giteaucrat/runner.rb +105 -0
- data/lib/giteaucrat/version.rb +10 -0
- data/lib/giteaucrat.rb +16 -0
- metadata +117 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YmY4NGEwNmU2Zjg2NmZjZmExOGYwMjViNjFjNWE3YWE4YjI3NGI4ZQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NDRlODhhZTQxNWU0OTI4ZWVlMzVmZGY0MjkzNmZhY2U1ZWZmMzZhMQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZjRlNWVjNjI4ZDlhZGE3MDEzZWZjMWM4NzY3YzA2OGJhODhiNzdhZmViYzE5
|
10
|
+
ODhlMjc3ZTUxY2JkM2RiZDc4OTViN2IzYTllMDQwNTM0YzNiNmMzYmMxYjkw
|
11
|
+
NmE0YTRjNjE4YmU0MzY1NzIyNGFjNmY3YjBjMzIxMmZiYzRlMTg=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NTZhYWE2MDgzYmZkZGUyNTQ2MTFkNjdlMGRmMjI0YmY2MzI4MGRhZmNjNThj
|
14
|
+
NTZiOTg5MjYzZGE0NWJhZDY5ZDI4YTkyYjExMGE0OTdkNmZjMzI2NWNlZmM3
|
15
|
+
MTM4OTY0MDcyNDAxODgxMTQzOGY5MWRiNjJjNzM0ZDhhZmMxMDY=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Alexander Semyonov
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Giteaucrat. The Git Bureaucrat.
|
2
|
+
|
3
|
+
Giteaucrat provides you ability to automatically put copyright messages in all
|
4
|
+
your source code.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem install giteaucrat
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
Execute in your application directory:
|
15
|
+
|
16
|
+
# cd my_rails_application
|
17
|
+
$ giteaucrat copyrights
|
18
|
+
|
19
|
+
You can see all available options using:
|
20
|
+
|
21
|
+
$ giteaucrat help copyrights
|
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/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/bin/giteaucrat
ADDED
data/giteaucrat.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'giteaucrat/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'giteaucrat'
|
8
|
+
spec.version = Giteaucrat::VERSION
|
9
|
+
spec.authors = ['Alexander Semyonov']
|
10
|
+
spec.email = %w(al@semyonov.us)
|
11
|
+
spec.description = %q{Automatically update copyright messages using git}
|
12
|
+
spec.summary = %q{The Git bureaucrat}
|
13
|
+
spec.homepage = 'http://github.com/alsemyonov/giteaucrat'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = %w(lib)
|
20
|
+
|
21
|
+
spec.add_dependency 'thor'
|
22
|
+
spec.add_dependency 'grit'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
25
|
+
spec.add_development_dependency 'rake'
|
26
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'grit/blame'
|
4
|
+
|
5
|
+
module Grit
|
6
|
+
class Blame
|
7
|
+
def process_raw_blame(output)
|
8
|
+
lines, final = [], []
|
9
|
+
info, commits = {}, {}
|
10
|
+
|
11
|
+
# process the output
|
12
|
+
output.split("\n").each do |line|
|
13
|
+
if line[0, 1] == "\t"
|
14
|
+
lines << line[1, line.size]
|
15
|
+
elsif m = /^(\w{40}) (\d+) (\d+)/.match(line)
|
16
|
+
commit_id, old_lineno, lineno = m[1], m[2].to_i, m[3].to_i
|
17
|
+
commits[commit_id] = nil if !commits.key?(commit_id)
|
18
|
+
info[lineno] = [commit_id, old_lineno]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# load all commits in single call
|
23
|
+
@repo.batch(*(commits.keys - ['0'*40])).each do |commit|
|
24
|
+
commits[commit.id] = commit if commit
|
25
|
+
end
|
26
|
+
|
27
|
+
# get it together
|
28
|
+
info.sort.each do |lineno, (commit_id, old_lineno)|
|
29
|
+
commit = commits[commit_id]
|
30
|
+
final << BlameLine.new(lineno, old_lineno, commit, lines[lineno - 1])
|
31
|
+
end
|
32
|
+
|
33
|
+
@lines = final
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
################################################
|
4
|
+
# © Alexander Semyonov, 2013—2013 #
|
5
|
+
# Author: Alexander Semyonov <al@semyonov.us> #
|
6
|
+
################################################
|
7
|
+
|
8
|
+
require 'giteaucrat'
|
9
|
+
require 'set'
|
10
|
+
|
11
|
+
module Giteaucrat
|
12
|
+
class Author
|
13
|
+
include Common
|
14
|
+
|
15
|
+
# @param [Grit::Actor] grit_author
|
16
|
+
# @return [Author]
|
17
|
+
def self.find_by_git_person(grit_author)
|
18
|
+
name = grit_author.name.force_encoding('utf-8')
|
19
|
+
email = grit_author.email.force_encoding('utf-8')
|
20
|
+
author = all.find do |author|
|
21
|
+
author.has_name?(name) || author.has_email?(email)
|
22
|
+
end || new(name: name, email: email)
|
23
|
+
author.names << name
|
24
|
+
author.emails << email
|
25
|
+
author
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.all
|
29
|
+
@all ||= Set.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.new(attributes = {})
|
33
|
+
author = super(attributes)
|
34
|
+
all << author
|
35
|
+
author
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.check_multiple_emails!
|
39
|
+
all.each do |_, author|
|
40
|
+
author && author.check_multiple_emails!
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.to_yaml
|
45
|
+
all.map do |author|
|
46
|
+
author.to_hash
|
47
|
+
end.sort { |a, b| a[:name] <=> b[:name] }.to_yaml
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Boolean]
|
51
|
+
attr_accessor :ignored
|
52
|
+
|
53
|
+
# @return [Boolean]
|
54
|
+
def ignored?
|
55
|
+
!!@ignored
|
56
|
+
end
|
57
|
+
|
58
|
+
def check_multiple_emails!
|
59
|
+
if emails.count > 1 && !instance_variable_defined?(:@email)
|
60
|
+
STDERR.puts "#{name} has multiple emails:\n#{emails.map {|e| "* #{e}"}.join("\n")}\nPlease set right one in your giteaucrat.yml\n"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Set<String>]
|
65
|
+
def names
|
66
|
+
@names ||= Set.new
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param [<String>] names
|
70
|
+
# @return [Set<String>]
|
71
|
+
def names=(names)
|
72
|
+
names.each { |name| self.names << name.force_encoding('utf-8') }
|
73
|
+
self.names
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [String]
|
77
|
+
def name
|
78
|
+
@name || names.first
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param [String] name
|
82
|
+
def name=(name)
|
83
|
+
name = name.force_encoding('utf-8')
|
84
|
+
names << name
|
85
|
+
@name = name
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [Boolean]
|
89
|
+
# @param [String] name
|
90
|
+
def has_name?(name)
|
91
|
+
names.include?(name.force_encoding('utf-8'))
|
92
|
+
end
|
93
|
+
|
94
|
+
# @return [Set<String>]
|
95
|
+
def emails
|
96
|
+
@emails ||= Set.new
|
97
|
+
end
|
98
|
+
|
99
|
+
# @param [<String>] emails
|
100
|
+
# @return [Set<String>]
|
101
|
+
def emails=(emails)
|
102
|
+
emails.each { |email| self.emails << email.force_encoding('utf-8') }
|
103
|
+
self.emails
|
104
|
+
end
|
105
|
+
|
106
|
+
# @return [String]
|
107
|
+
def email
|
108
|
+
@email || emails.first
|
109
|
+
end
|
110
|
+
|
111
|
+
# @param [String] email
|
112
|
+
# @return [String]
|
113
|
+
def email=(email)
|
114
|
+
email = email.force_encoding('utf-8')
|
115
|
+
emails << email
|
116
|
+
@email = email
|
117
|
+
end
|
118
|
+
|
119
|
+
# @return [Boolean]
|
120
|
+
# @param [String] email
|
121
|
+
def has_email?(email)
|
122
|
+
emails.include?(email.force_encoding('utf-8'))
|
123
|
+
end
|
124
|
+
|
125
|
+
# @return [String]
|
126
|
+
def identifier
|
127
|
+
%(#{name} <#{email}>).force_encoding('utf-8')
|
128
|
+
end
|
129
|
+
|
130
|
+
# @return [Hash]
|
131
|
+
def to_hash
|
132
|
+
hash = {'name' => name.to_s, 'email' => email.to_s}
|
133
|
+
hash['names'] = names.to_a - [name] if names.size > 1
|
134
|
+
hash['emails'] = emails.to_a - [email] if emails.size > 1
|
135
|
+
hash['ignored'] = true if ignored?
|
136
|
+
hash
|
137
|
+
end
|
138
|
+
|
139
|
+
# @return [String]
|
140
|
+
def to_s
|
141
|
+
identifier
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
################################################
|
4
|
+
# © Alexander Semyonov, 2013—2013 #
|
5
|
+
# Author: Alexander Semyonov <al@semyonov.us> #
|
6
|
+
################################################
|
7
|
+
|
8
|
+
require 'giteaucrat'
|
9
|
+
|
10
|
+
module Giteaucrat
|
11
|
+
module Common
|
12
|
+
def initialize(attributes = {})
|
13
|
+
assign_attributes(attributes)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param [Hash] attributes
|
17
|
+
def assign_attributes(attributes)
|
18
|
+
attributes.each do |name, value|
|
19
|
+
writer = "#{name}="
|
20
|
+
value = value.force_encoding('utf-8') if value.respond_to?(:force_encoding)
|
21
|
+
if respond_to?(writer)
|
22
|
+
public_send(writer, value)
|
23
|
+
else
|
24
|
+
STDERR.puts("Unknown method #{self.class}##{writer}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
################################################
|
4
|
+
# © Alexander Semyonov, 2013—2013 #
|
5
|
+
# Author: Alexander Semyonov <al@semyonov.us> #
|
6
|
+
################################################
|
7
|
+
|
8
|
+
require 'giteaucrat'
|
9
|
+
require 'grit'
|
10
|
+
require 'core_ext/grit/blame'
|
11
|
+
|
12
|
+
module Giteaucrat
|
13
|
+
class File
|
14
|
+
include Common
|
15
|
+
|
16
|
+
# @return [String]
|
17
|
+
attr_accessor :name
|
18
|
+
|
19
|
+
# @return [Giteaucrat::Repo]
|
20
|
+
attr_accessor :repo
|
21
|
+
|
22
|
+
def authors
|
23
|
+
@authors ||= begin
|
24
|
+
blame = repo.git_repo.blame(name)
|
25
|
+
lines = blame.lines
|
26
|
+
commits = lines.map { |line| line.commit }.uniq.find_all do |commit|
|
27
|
+
!ignored_commit?(commit)
|
28
|
+
end
|
29
|
+
commits.inject(Set.new) do |authors, commit|
|
30
|
+
author = Author.find_by_git_person(commit.author)
|
31
|
+
authors << author unless author.ignored?
|
32
|
+
authors
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def write_copyright!
|
38
|
+
first, middle, last = comment
|
39
|
+
first = Regexp.escape(first)
|
40
|
+
middle = Regexp.escape(middle)
|
41
|
+
last = Regexp.escape(last)
|
42
|
+
contents = ::File.read(name)
|
43
|
+
ruler = "(#{first}(#{middle})*#{last}\n)"
|
44
|
+
coding = "(#{first}\s*.*coding:\s*utf-8\s*\n+)?"
|
45
|
+
header = /#{coding}#{ruler}(#{first}.*#{last}\n)*\2\n*/m
|
46
|
+
if repo.include_encoding?
|
47
|
+
contents.sub!(header, "#{copyright}\n\n")
|
48
|
+
else
|
49
|
+
contents.sub!(/(#{coding})/, "\1#{copyright}\n\n")
|
50
|
+
end
|
51
|
+
::File.write(name, contents)
|
52
|
+
end
|
53
|
+
|
54
|
+
def copyright
|
55
|
+
@copyright ||= begin
|
56
|
+
lines = []
|
57
|
+
lines << repo.copyright_label
|
58
|
+
|
59
|
+
if authors.size > 0
|
60
|
+
authors_label = (authors.size > 1) ? 'Authors: ' : 'Author: '
|
61
|
+
author_names = self.authors.map { |a| a.identifier }.sort
|
62
|
+
prepend = ' ' * authors_label.size
|
63
|
+
lines << "#{authors_label}#{author_names.shift}"
|
64
|
+
author_names.each do |author|
|
65
|
+
lines << "#{prepend}#{author}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
first, middle, last = self.comment
|
70
|
+
|
71
|
+
max_line_size = lines.max { |a, b| a.size <=> b.size }.size
|
72
|
+
max_line_size += 1 if max_line_size.odd?
|
73
|
+
|
74
|
+
width = first.size + 1 + max_line_size + 1 + last.size
|
75
|
+
ruler = "#{first}#{middle * ((width / middle.size - first.size - last.size))}#{last}"
|
76
|
+
lines = lines.map do |line|
|
77
|
+
blank = ' ' * (max_line_size - line.size) if line.size < max_line_size
|
78
|
+
"#{first} #{line}#{blank} #{last}"
|
79
|
+
end
|
80
|
+
|
81
|
+
lines.unshift(ruler)
|
82
|
+
lines << ruler
|
83
|
+
if repo.include_encoding?
|
84
|
+
lines.unshift("#{first} coding: utf-8\n")
|
85
|
+
end
|
86
|
+
lines.join("\n")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# @see Giteaucrat::Repo#ignored_commit?
|
91
|
+
def ignored_commit?(commit)
|
92
|
+
repo.ignored_commit?(commit)
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [<String, String, String>]
|
96
|
+
def comment
|
97
|
+
case ::File.extname(name)
|
98
|
+
when '.rb', '.coffee', '.sass'
|
99
|
+
%w(# # #)
|
100
|
+
when '.js', '.css', '.scss'
|
101
|
+
%w(/* * */)
|
102
|
+
when '.erl'
|
103
|
+
%w(% % %)
|
104
|
+
else
|
105
|
+
raise 'Unknown file type'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
################################################
|
4
|
+
# © Alexander Semyonov, 2013—2013 #
|
5
|
+
# Author: Alexander Semyonov <al@semyonov.us> #
|
6
|
+
################################################
|
7
|
+
|
8
|
+
require 'giteaucrat'
|
9
|
+
require 'grit'
|
10
|
+
|
11
|
+
module Giteaucrat
|
12
|
+
class Repo
|
13
|
+
include Common
|
14
|
+
|
15
|
+
def self.defaults=(options = {})
|
16
|
+
if options[:git]
|
17
|
+
Grit::Git.git_binary = options[:git]
|
18
|
+
end
|
19
|
+
if options[:git_timeout]
|
20
|
+
Grit::Git.git_timeout = options[:git_timeout]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def git_repo
|
25
|
+
@git_repo ||= Grit::Repo.new(path)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [String]
|
29
|
+
attr_accessor :path
|
30
|
+
attr_accessor :commit_keyword
|
31
|
+
attr_writer :copyright_year
|
32
|
+
attr_accessor :copyright_owner
|
33
|
+
attr_accessor :copyright_format
|
34
|
+
attr_accessor :license
|
35
|
+
attr_accessor :include_encoding
|
36
|
+
attr_writer :patterns
|
37
|
+
|
38
|
+
def commit!(message)
|
39
|
+
git_repo.commit_all(message)
|
40
|
+
end
|
41
|
+
|
42
|
+
def files
|
43
|
+
@files ||= begin
|
44
|
+
Dir.chdir(path) do
|
45
|
+
%w(app lib script spec).inject([]) do |files, path|
|
46
|
+
files + Dir[::File.join(path, '**/*.rb')]
|
47
|
+
end.map do |file_name|
|
48
|
+
File.new(name: file_name, repo: self)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def write_copyrights!
|
55
|
+
files.each do |file|
|
56
|
+
file.write_copyright!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def timeout
|
61
|
+
Grit::Git.git_timeout
|
62
|
+
end
|
63
|
+
|
64
|
+
def timeout=(timeout)
|
65
|
+
Grit::Git.git_timeout = timeout
|
66
|
+
end
|
67
|
+
|
68
|
+
def git
|
69
|
+
Grit::Git.git_binary
|
70
|
+
end
|
71
|
+
|
72
|
+
def git=(executable)
|
73
|
+
Grit::Git.git_binary = executable
|
74
|
+
end
|
75
|
+
|
76
|
+
def authors=(authors)
|
77
|
+
authors.each do |author_attributes|
|
78
|
+
Author.new(author_attributes)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def config=(*)
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [Number]
|
86
|
+
def copyright_year
|
87
|
+
@copyright_year || first_commit.authored_date.year
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return [Grit::Commit]
|
91
|
+
def first_commit
|
92
|
+
@first_commit ||= begin
|
93
|
+
count = git_repo.commit_count
|
94
|
+
git_repo.commits(git_repo.head.name, 1, count - 1).first
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [String]
|
99
|
+
def copyright_label
|
100
|
+
@copyright_label ||= copyright_format.
|
101
|
+
gsub('%{years}', [copyright_year, Time.now.year].compact.join('—')).
|
102
|
+
gsub('%{owner}', copyright_owner).
|
103
|
+
gsub('%{license}', license || '')
|
104
|
+
end
|
105
|
+
|
106
|
+
# @return [Regexp]
|
107
|
+
def ignored_commit_message
|
108
|
+
@ignored_commit_message ||= /^#{Regexp.escape(commit_keyword)}/
|
109
|
+
end
|
110
|
+
|
111
|
+
# @param [Grit::Commit] commit
|
112
|
+
# @return [Boolean]
|
113
|
+
def ignored_commit?(commit)
|
114
|
+
!commit || commit.message.match(ignored_commit_message)
|
115
|
+
end
|
116
|
+
|
117
|
+
def include_encoding?
|
118
|
+
!!include_encoding
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
################################################
|
4
|
+
# © Alexander Semyonov, 2013—2013 #
|
5
|
+
# Author: Alexander Semyonov <al@semyonov.us> #
|
6
|
+
################################################
|
7
|
+
|
8
|
+
require 'giteaucrat'
|
9
|
+
require 'thor'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
module Giteaucrat
|
13
|
+
class Runner < Thor
|
14
|
+
class_option :git, group: :git
|
15
|
+
class_option :timeout, default: 30, group: :git
|
16
|
+
class_option :path, default: Dir.pwd, desc: 'Path to project that needs some bureaucracy.'
|
17
|
+
class_option :config,
|
18
|
+
default: ::File.join(Dir.pwd, 'config', 'giteaucrat.yml'),
|
19
|
+
desc: 'Giteaucrat configuration',
|
20
|
+
group: :giteaucrat
|
21
|
+
class_option :commit_keyword,
|
22
|
+
aliases: %w(-i),
|
23
|
+
default: '#giteaucrat',
|
24
|
+
desc: 'Commit keyword to prepend giteaucrat commits (to ignore authorship of this commit)',
|
25
|
+
group: :giteaucrat
|
26
|
+
class_option :copyright_owner,
|
27
|
+
aliases: %w(-o),
|
28
|
+
default: `git config user.name`.chomp,
|
29
|
+
desc: 'Company or owner name to put in copyright header',
|
30
|
+
group: :giteaucrat
|
31
|
+
class_option :copyright_year,
|
32
|
+
aliases: %w(-y),
|
33
|
+
type: :numeric,
|
34
|
+
desc: 'Start year of copyright',
|
35
|
+
group: :giteaucrat
|
36
|
+
class_option :copyright_format,
|
37
|
+
aliases: %w(-f),
|
38
|
+
default: '© %{owner}, %{years}',
|
39
|
+
desc: 'Start year of copyright',
|
40
|
+
group: :giteaucrat
|
41
|
+
class_option :license,
|
42
|
+
aliases: %w(-l),
|
43
|
+
desc: 'License to put in copyright footer'
|
44
|
+
class_option :include_encoding,
|
45
|
+
type: :boolean,
|
46
|
+
default: true,
|
47
|
+
aliases: %w(-e),
|
48
|
+
desc: 'Put encoding in copyright footer',
|
49
|
+
group: :giteaucrat
|
50
|
+
class_option :commit,
|
51
|
+
type: :boolean,
|
52
|
+
aliases: %w(-c),
|
53
|
+
desc: 'Commit updated copyrights',
|
54
|
+
group: :giteaucrat
|
55
|
+
|
56
|
+
desc 'copyrights', 'Update copyright information in files'
|
57
|
+
|
58
|
+
def copyrights
|
59
|
+
repo.write_copyrights!
|
60
|
+
repo.commit("#{defaults[:commit_keyword]}: Update copyrights in source code") if options[:commit]
|
61
|
+
FileUtils.mkdir_p(::File.join(path, 'tmp'))
|
62
|
+
::File.write(::File.join(path, 'tmp', 'giteaucrat_authors.yml'), Author.to_yaml)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def defaults
|
68
|
+
@defaults ||= begin
|
69
|
+
YAML::ENGINE.yamler = 'syck'
|
70
|
+
Encoding.default_external = 'utf-8'
|
71
|
+
Encoding.default_internal = 'utf-8'
|
72
|
+
|
73
|
+
options = Thor::CoreExt::HashWithIndifferentAccess.new(self.options.dup)
|
74
|
+
options[:config] = ::File.join(options[:path], 'config', 'giteaucrat.yml')
|
75
|
+
|
76
|
+
if ::File.file?(options[:config])
|
77
|
+
config = YAML.load_file(options[:config]) || {}
|
78
|
+
options.merge!(config)
|
79
|
+
end
|
80
|
+
|
81
|
+
config = options.inject({}) do |config, (key, value)|
|
82
|
+
config[key] = value unless %w(config git path timeout).include?(key); config
|
83
|
+
end.to_yaml
|
84
|
+
|
85
|
+
unless ::File.file?(options[:config])
|
86
|
+
FileUtils.mkdir_p(::File.dirname(options[:config]))
|
87
|
+
::File.write(options[:config], config)
|
88
|
+
end
|
89
|
+
|
90
|
+
Dir.chdir(path)
|
91
|
+
Repo.defaults = {git: options[:git], git_timeout: options[:timeout]}
|
92
|
+
options
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def path
|
97
|
+
options[:path]
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return [Giteaucrat::Repo]
|
101
|
+
def repo
|
102
|
+
@repo ||= Repo.new(defaults)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/giteaucrat.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
################################################
|
4
|
+
# © Alexander Semyonov, 2013—2013 #
|
5
|
+
# Author: Alexander Semyonov <al@semyonov.us> #
|
6
|
+
################################################
|
7
|
+
|
8
|
+
require 'giteaucrat/version'
|
9
|
+
|
10
|
+
module Giteaucrat
|
11
|
+
autoload :Author, 'giteaucrat/author'
|
12
|
+
autoload :Common, 'giteaucrat/common'
|
13
|
+
autoload :File, 'giteaucrat/file'
|
14
|
+
autoload :Repo, 'giteaucrat/repo'
|
15
|
+
autoload :Runner, 'giteaucrat/runner'
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: giteaucrat
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexander Semyonov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-08-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: grit
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Automatically update copyright messages using git
|
70
|
+
email:
|
71
|
+
- al@semyonov.us
|
72
|
+
executables:
|
73
|
+
- giteaucrat
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- .gitignore
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- bin/giteaucrat
|
83
|
+
- config/giteaucrat.yml
|
84
|
+
- giteaucrat.gemspec
|
85
|
+
- lib/core_ext/grit/blame.rb
|
86
|
+
- lib/giteaucrat.rb
|
87
|
+
- lib/giteaucrat/author.rb
|
88
|
+
- lib/giteaucrat/common.rb
|
89
|
+
- lib/giteaucrat/file.rb
|
90
|
+
- lib/giteaucrat/repo.rb
|
91
|
+
- lib/giteaucrat/runner.rb
|
92
|
+
- lib/giteaucrat/version.rb
|
93
|
+
homepage: http://github.com/alsemyonov/giteaucrat
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
metadata: {}
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 2.0.6
|
114
|
+
signing_key:
|
115
|
+
specification_version: 4
|
116
|
+
summary: The Git bureaucrat
|
117
|
+
test_files: []
|