Class: Dco::CLI
- Inherits:
-
Thor
- Object
- Thor
- Dco::CLI
- Defined in:
- lib/dco/cli.rb
Overview
Class Method Summary collapse
-
.exit_on_failure? ⇒ Boolean
Because this isn't the default and exit statuses are what the cool kids do.
Instance Method Summary collapse
- #check(branch = nil) ⇒ Object
- #disable ⇒ Object
- #enable ⇒ Object
- #process_commit_message(tmp_path = nil) ⇒ Object
- #sign(branch = nil) ⇒ Object
Class Method Details
.exit_on_failure? ⇒ Boolean
Because this isn't the default and exit statuses are what the cool kids do.
26 27 28 |
# File 'lib/dco/cli.rb', line 26 def self.exit_on_failure? true end |
Instance Method Details
#check(branch = nil) ⇒ Object
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/dco/cli.rb', line 276 def check(branch=nil) branch ||= current_branch log = ([:all] || branch == [:base]) ? repo.log : repo.log.between([:base], branch) bad_commits = [] log.each do |commit| sign_off = has_sign_off?(commit) if !sign_off # No sign-off at all, tsk tsk. bad_commits << [commit, :no_sign_off] elsif ![:allow_author_mismatch] && sign_off != "#{commit..name} <#{commit..email}>" # The signer-off and commit author don't match. bad_commits << [commit, :author_mismatch] end end if bad_commits.empty? # Yay! say("All commits are signed off", :green) unless [:quiet] else # Something bad happened. unless [:quiet] say("N: No Sign-off M: Author mismatch", :red) bad_commits.each do |commit, reason| reason_string = {no_sign_off: 'N', author_mismatch: 'M'}[reason] say("#{reason_string} #{format_commit(commit)}", :red) end end exit 1 end end |
#disable ⇒ Object
174 175 176 177 178 179 180 181 182 183 |
# File 'lib/dco/cli.rb', line 174 def disable assert_repo! unless our_hook? raise Thor::Error.new('commit-msg hook is external, not removing') end if File.exist?(HOOK_PATH) File.unlink(HOOK_PATH) end say('DCO auto-sign-off disabled', :green) end |
#enable ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/dco/cli.rb', line 158 def enable assert_repo! unless our_hook? raise Thor::Error.new('commit-msg hook already exists, not overwriting') end say("#{DCO_TEXT}\n\n", :yellow) unless confirm?("Do you, #{git_identity}, certify that all future commits to this repository will be under the terms of the Developer Certificate of Origin? [yes/no]") raise Thor::Error.new('Not enabling auto-sign-off without approval') end IO.write(HOOK_PATH, HOOK_SCRIPT) # 755 is what the defaults from `git init` use so probably good enough. File.chmod(00755, HOOK_PATH) say('DCO auto-sign-off enabled', :green) end |
#process_commit_message(tmp_path = nil) ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/dco/cli.rb', line 75 def (tmp_path=nil) # Set the repo path if passed. self.repo_path = [:repo] if [:repo] # If a path is passed use it as a tmpfile, otherwise assume filter mode. commit_msg = tmp_path ? IO.read(tmp_path) : STDIN.read unless has_sign_off?(commit_msg) # If we're in filter mode and not on-behalf-of, do a final check of the author. if !tmp_path && ![:behalf] && ENV['GIT_AUTHOR_EMAIL'] != repo_config['user.email'] # Something went wrong, refuse to rewrite. STDOUT.write(commit_msg) raise Thor::Error.new("Author mismatch on commit #{ENV['GIT_COMMIT']}: #{ENV['GIT_AUTHOR_EMAIL']} vs #{repo_config['user.email']}") end commit_msg << "\n" unless commit_msg.end_with?("\n") commit_msg << "\nSigned-off-by: #{ENV['GIT_AUTHOR_NAME']} <#{ENV['GIT_AUTHOR_EMAIL']}>\n" if [:behalf] # This requires loading the actual repo config, which is slower. commit_msg << "Sign-off-executed-by: #{git_identity}\n" commit_msg << "Approved-at: #{[:behalf]}\n" end IO.write(tmp_path, commit_msg) if tmp_path end # Always display the replacement commit message if we're in filter mode. STDOUT.write(commit_msg) unless tmp_path end |
#sign(branch = nil) ⇒ Object
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/dco/cli.rb', line 189 def sign(branch=nil) # What two branches are we using? base_branch = [:base] branch ||= current_branch if base_branch == branch # This should also catch people trying to sign-off on master. raise Thor::Error.new("Cannot use #{branch} for both the base and target branch") end # First check for a stored ref under refs/original/. begin repo.show("refs/original/refs/heads/#{branch}") # If this doesn't error, a backup ref is present. unless confirm?("An existing backup of branch #{branch} is present from a previous filter-branch. Do you want to remove this backup and continue? [yes/no]") raise Thor::Error.new('Backup ref present, not continuing') end # Clear the backup. File.unlink(".git/refs/original/refs/heads/#{branch}") rescue Git::GitExecuteError # This means there was no backup, keep going. end # Next examine all the commits we will be touching. commits = repo.log.between(base_branch, branch).to_a.select {|commit| !has_sign_off?(commit) } if commits.empty? raise Thor::Error.new("Branch #{branch} has no commits which require sign-off") end if ![:behalf] && commits.any? {|commit| commit..email != repo_config['user.email'] } raise Thor::Error.new("Branch #{branch} contains commits not authored by you. Please use the --behalf flag when signing off for another contributor") end # Display the DCO text. say("#{DCO_TEXT}\n\n", :yellow) unless [:behalf] # Display the list of commits. say("Going to sign-off the following commits:") commits.each do |commit| say("* #{format_commit(commit)}") end # Get confirmation. confirm_msg = if [:behalf] "Do you, #{git_identity}, certify that these commits are contributed under the terms of the Developer Certificate of Origin as evidenced by #{[:behalf]}? [yes/no]" else "Do you, #{git_identity}, certify that these commits are contributed under the terms of the Developer Certificate of Origin? [yes/no]" end unless confirm?(confirm_msg) raise Thor::Error.new('Not signing off on commits without approval') end # Stash if needed. did_stash = false status = repo.status unless status.changed.empty? && status.added.empty? && status.deleted.empty? say("Stashing uncommited changes before continuing") repo.lib.send(:command, 'stash', ['save', 'dco sign temp stash']) did_stash = true end # Run the filter branch. Here be dragons. Yes, I'm calling a private method. I'm sorry. filter_cmd = [Thor::Util.ruby_command, File.('../../../bin/dco', __FILE__), 'process_commit_message', '--repo', repo.dir.path] if [:behalf] filter_cmd << '--behalf' filter_cmd << [:behalf] end begin output = repo.lib.send(:command, 'filter-branch', ['--msg-filter', Shellwords.join(filter_cmd), "#{base_branch}..#{branch}"]) say(output) ensure if did_stash # If we had a stash, make sure to replay it. say("Unstashing previous changes") # For whatever reason, the git gem doesn't expose this. repo.lib.send(:command, 'stash', ['pop']) end end # Hopefully that worked. say("Sign-off complete", :green) say("Don't forget to use --force when pushing this branch to your git server (eg. git push --force origin #{branch})", :green) # TODO I could detect the actual remote for this branch, if any. end |