Change the pattern matching for our copyright header so that it doesn't croak
[arvados-dev.git] / git / hooks / coding-standards.sh
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 def blacklist bl
21   if ($newrev[0,6] ==  '000000')
22     # A branch is being deleted. Do not check old commits for DCO signoff!
23     all_revs    = []
24   elsif ($oldrev[0,6] ==  '000000')
25     if $refname != 'refs/heads/master'
26       # A new branch was pushed. Check all new commits in this branch.
27       all_revs  = `git log --pretty=format:%H master..#{$newrev}`.split("\n")
28     else
29       # When does this happen?
30       all_revs  = [$newrev]
31     end
32   else
33     all_revs    = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
34   end
35
36   all_revs.each do |rev|
37     bl.each do |b|
38       if rev == b
39         puts "Revision #{b} is blacklisted, you must remove it from your branch (possibly using git rebase) before you can push."
40         exit 1
41       end
42     end
43   end
44 end
45
46 blacklist ['26d74dc0524c87c5dcc0c76040ce413a4848b57a']
47
48 # Only enforce policy on the master branch
49 exit 0 if $refname != 'refs/heads/master'
50
51 puts "Enforcing Policies... \n(#{$refname}) (#{$oldrev[0,6]}) (#{$newrev[0,6]})"
52
53 $regex = /\[ref: (\d+)\]/
54
55 $broken_commit_message = /Please enter a commit message to explain why this merge is necessary/
56 $wrong_way_merge_master = /Merge( remote-tracking)? branch '([^\/]+\/)?master' into/
57 $merge_master = /Merge branch '[^']+'((?! into)| into master)/
58 $pull_merge = /Merge branch 'master' of /
59 $refs_or_closes_or_no_issue = /(refs #|closes #|fixes #|no issue #)/i
60
61 # enforced custom commit message format
62 def check_message_format
63   all_revs    = `git rev-list --first-parent #{$oldrev}..#{$newrev}`.split("\n")
64   merge_revs  = `git rev-list --first-parent --min-parents=2 #{$oldrev}..#{$newrev}`.split("\n")
65   # single_revs = `git rev-list --first-parent --max-parents=1 #{$oldrev}..#{$newrev}`.split("\n")
66   broken = false
67   no_ff = false
68
69   merge_revs.each do |rev|
70     message = `git cat-file commit #{rev} | sed '1,/^$/d'`
71     if $wrong_way_merge_master.match(message)
72       puts "\n[POLICY] Only non-fast-forward merges into master are allowed. Please"
73       puts "reset your master branch:"
74       puts "  git reset --hard origin/master"
75       puts "and then merge your branch with the --no-ff option:"
76       puts "  git merge your-branch --no-ff\n"
77       puts "Remember to add a reference to an issue number in the merge commit!\n"
78       puts "\n******************************************************************\n"
79       puts "\nOffending commit: #{rev}\n"
80       puts "\nOffending commit message:\n"
81       puts message
82       puts "\n******************************************************************\n"
83       puts "\n\n"
84       broken = true
85       no_ff = true
86     elsif $pull_merge.match(message)
87       puts "\n[POLICY] This appears to be a git pull merge of remote master into local"
88       puts "master.  In order to maintain a linear first-parent history of master,"
89       puts "please reset your branch and remerge or rebase using the latest master.\n"
90       puts "\n******************************************************************\n"
91       puts "\nOffending commit: #{rev}\n"
92       puts "\nOffending commit message:\n\n"
93       puts message
94       puts "\n******************************************************************\n"
95       puts "\n\n"
96       broken = true
97     elsif not $merge_master.match(message) and not
98       puts "\n[POLICY] This does not appear to be a merge of a feature"
99       puts "branch into master.  Merges must follow the format"
100       puts "\"Merge branch 'feature-branch'\".\n"
101       puts "\n******************************************************************\n"
102       puts "\nOffending commit: #{rev}\n"
103       puts "\nOffending commit message:\n\n"
104       puts message
105       puts "\n******************************************************************\n"
106       puts "\n\n"
107       broken = true
108     end
109   end
110
111   all_revs.each do |rev|
112     message = `git cat-file commit #{rev} | sed '1,/^$/d'`
113     if $broken_commit_message.match(message)
114       puts "\n[POLICY] Rejected broken commit message for including boilerplate"
115       puts "instruction text.\n"
116       puts "\n******************************************************************\n"
117       puts "\nOffending commit: #{rev}\n"
118       puts "\nOffending commit message:\n\n"
119       puts message
120       puts "\n******************************************************************\n"
121       puts "\n\n"
122       broken = true
123     end
124
125     # Do not test when the commit is a no_ff merge (which will be rejected), because
126     # this test will complain about *every* commit in the merge otherwise, obscuring
127     # the real reason for the rejection (the no_ff merge)
128     if not no_ff and not $refs_or_closes_or_no_issue.match(message)
129       puts "\n[POLICY] All commits to master must include an issue using \"refs #\" or"
130       puts "\"closes #\", or specify \"no issue #\"\n"
131       puts "\n******************************************************************\n"
132       puts "\nOffending commit: #{rev}\n"
133       puts "\nOffending commit message:\n\n"
134       puts message
135       puts "\n******************************************************************\n"
136       puts "\n\n"
137       broken = true
138     end
139   end
140
141   if broken
142     exit 1
143   end
144 end
145
146 check_message_format