Another bugfix for the check-copyright-headers.rb script.
[arvados-dev.git] / git / hooks / check-copyright-headers.rb
1 #!/usr/bin/env ruby
2
3 # Copyright (C) The Arvados Authors. All rights reserved.
4 #
5 # SPDX-License-Identifier: AGPL-3.0
6
7 # This script can be installed as a git update hook.
8
9 # It can also be installed as a gitolite 'hooklet' in the
10 # hooks/common/update.secondary.d/ directory.
11
12 # NOTE: this script runs under the same assumptions as the 'update' hook, so
13 # the starting directory must be maintained and arguments must be passed on.
14
15 $refname = ARGV[0]
16 $oldrev  = ARGV[1]
17 $newrev  = ARGV[2]
18 $user    = ENV['USER']
19
20 puts "Enforcing copyright headers..."
21 puts "(#{$refname}) (#{$oldrev[0,6]}) (#{$newrev[0,6]})"
22
23 def load_licenseignore
24   $licenseignore = `git show #{$newrev}:.licenseignore 2>/dev/null`.gsub(/\./,'\\.').gsub(/\*/,'.*').gsub(/\?/,'.').split("\n")
25 end
26
27 def check_file(filename, header, broken)
28   ignore = false
29   $licenseignore.each do |li|
30     if filename =~ /#{li}/
31       ignore = true
32     end
33   end
34   return broken if ignore
35
36   if header !~ /SPDX-License-Identifier:/
37     if not broken
38       puts "\nERROR\n"
39     end
40     puts "missing or invalid copyright header in file #{filename}"
41     broken = true
42   end
43   return broken
44 end
45
46 # enforce copyright headers
47 def check_copyright_headers
48   if ($newrev[0,6] ==  '000000')
49     # A branch is being deleted. Do not check old commits for DCO signoff!
50     all_revs    = []
51   elsif ($oldrev[0,6] ==  '000000')
52     if $refname != 'refs/heads/master'
53       # A new branch was pushed. Check all new commits in this branch.
54       puts "git rev-list --objects master..#{$newrev} | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)'| sed -n 's/^blob //p'"
55       blob_revs  = `git rev-list --objects master..#{$newrev} | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)'| sed -n 's/^blob //p'`.split("\n")
56       commit_revs  = `git rev-list --objects master..#{$newrev} | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)'| sed -n 's/^commit //p'`.split("\n")
57       all_revs = blob_revs + commit_revs
58     else
59       # When does this happen?
60       puts "UNEXPECTED ERROR"
61       exit 1
62     end
63   else
64     blob_revs = `git rev-list --objects #{$oldrev}..#{$newrev} --not --branches='*' | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)'| sed -n 's/^blob //p'`.split("\n")
65     commit_revs  = `git rev-list --objects #{$oldrev}..#{$newrev} | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)'| sed -n 's/^commit //p'`.split("\n")
66     all_revs = blob_revs + commit_revs
67   end
68
69   broken = false
70
71   all_revs.each do |rev|
72     ignore = false
73     tmp = rev.split(' ')
74     if tmp[2].nil?
75       # git object of type 'commit'
76        # This could be a new file that was added in this commit
77       # If this wasn't a bare repo, we could run the following to get the list of new files in this commit:
78       #    new_files = `git show #{tmp[0]} --name-only --diff-filter=A --pretty=""`.split("\n")
79       # Instead, we just look at all the files touched in the commit and check the diff to see
80       # see if it is a new file. This could prove brittle...
81       files = `git show #{tmp[0]} --name-only --pretty=""`.split("\n")
82       files.each do |f|
83         filename = f
84         commit = `git show #{tmp[0]} -- #{f}`
85         if commit =~ /^new file mode \d{6}\nindex 000000/
86           headerCount = 0
87           lineCount = 0
88           header = ""
89           previousLine = ""
90           commit.each_line do |line|
91             if ((headerCount == 0) and (line =~ /Copyright.*All rights reserved./))
92               header = previousLine
93               header += line
94               headerCount = 1
95             elsif ((headerCount > 0) and (headerCount < 3))
96               header += line
97               headerCount += 1
98             elsif (headerCount == 3)
99               break
100             end
101             previousLine = line
102             lineCount += 1
103             if lineCount > 50
104               break
105             end
106           end
107           broken = check_file(filename, header, broken)
108         end
109       end
110     else
111       # git object of type 'blob'
112       filename = tmp[2]
113       header = `git show #{tmp[0]} | head -n20 | egrep -A3 -B1 'Copyright.*All rights reserved.'`
114       broken = check_file(filename, header, broken)
115     end
116   end
117
118   if broken
119     puts
120     puts "[POLICY] all files must contain copyright headers, for more information see"
121     puts
122     puts "          https://arvados.org/projects/arvados/wiki/Coding_Standards#Copyright-headers"
123     puts
124     puts "Enforcing copyright headers: FAIL"
125     exit 1
126
127   end
128   puts "Enforcing copyright headers: PASS"
129 end
130
131 load_licenseignore
132 check_copyright_headers