Library/Mestral/mestral/cli.rb
# This code is free software; you can redistribute it and/or modify it under
# the terms of the new BSD License.
#
# Copyright (c) 2013, Sebastian Staudt
require 'thor'
require 'uri'
require 'mestral'
require 'mestral/repository'
require 'mestral/tape'
class Mestral::CLI < Thor
include Thor::Actions
class_option :debug, :type => :boolean,
:desc => 'Prints additional debug information'
desc 'add-tape <url>', 'Add a hook tape'
option :name, :desc => 'The name used to identify the tape'
def add_tape(url)
name = $1 if url =~ /(?:git|https?):\/\/github\.com\/(\w+\/\w+)(?:\.git)?$/
name ||= options[:name]
name ||= File.basename(url, '.git')
tape = Mestral::Tape.new name
if tape.exists?
if yes? "A tape with the name '#{tape.name}' already exists. Do you want to update it? [Y/n]"
update_tape tape
else
puts 'Aborted.'
end
else
url = File.expand_path url if URI(url).scheme.nil?
if tape.git_clone url
puts "Successfully cloned tape '#{tape.name}'."
else
if no? 'Cloning the tape failed. Do you still want to keep it? [y/N] '
tape.destroy
end
end
end
end
desc 'disable <hook> [<tape>] <hooklet>', 'Disable a hooklet for the given Git hook'
def disable(hook_name, tape_name, hooklet_name = nil)
hook = find_hook hook_name
return if hook.nil?
tape_name, hooklet_name = find_hooklet tape_name, hooklet_name
unless hook.hooklets.any? { |hooklet| hooklet.tape.name == tape_name && hooklet.name == hooklet_name }
puts "The hooklet '#{hooklet_name}' (#{tape_name}) is not enabled for the '#{hook_name}' hook."
return
end
repository.git "config --unset-all mestral.hooks.#{hook_name} #{tape_name}:#{hooklet_name}"
end
desc 'enable <hook> [<tape>] <hooklet>', 'Enable a hooklet for the given Git hook'
def enable(hook_name, tape_name, hooklet_name = nil)
hook = find_hook hook_name
return if hook.nil?
tape_name, hooklet_name = find_hooklet tape_name, hooklet_name
if hook.hooklets.any? { |hooklet| hooklet.tape.name == tape_name && hooklet.name == hooklet_name }
puts "The hooklet '#{hooklet_name}' (#{tape_name}) is already enabled for the '#{hook_name}' hook."
return
end
repository.git "config --add mestral.hooks.#{hook_name} #{tape_name}:#{hooklet_name}"
end
desc 'execute-hook <path>', 'Execute a hook'
def execute_hook(hook_path)
hook_name = File.basename hook_path
debug "Executing #{hook_name}..."
hook = repository.hook hook_name
if hook.nil?
puts "The Git hook '#{hook_name}' is not enabled."
return
end
exit(1) unless hook.execute
end
desc 'help [<command>]', 'Describe available commands or one specific command'
def help(*args)
super
end
desc 'list', 'List available hooks'
option :enabled, :type => :boolean, :desc => 'Only list hooks currently enabled in the current repository'
def list
if options[:enabled]
hooks = repository.hooks
else
hooks = Mestral::Tape.all.map(&:hooklets).flatten.compact
end
if hooks.empty?
if options[:enabled]
puts 'There are no enabled hooks in the current repository.'
else
puts 'There are no available hooks.'
puts "Use `#$0 add-tape` to add a hook tape."
end
else
puts "List of #{'enabled ' if options[:enabled]}Git hooks"
hooks.each do |hook|
puts " - #{hook.name}"
if hook.is_a? Mestral::Hook::Multi
hook.hooklets.each do |hooklet|
puts " - #{hooklet.name}"
end
end
end
end
end
desc 'update-tapes <tape>...', 'Updates one or more hook tapes'
def update_tapes(*tapes)
if tapes.empty?
tapes = Mestral::Tape.all
else
tapes.map! do |tape_name|
tape = Mestral::Tape.find tape_name
puts "Tape '#{tape_name}' does not exist. Ignoring." if tape.nil?
tape
end
end
tapes.compact.each &method(:update_tape)
end
desc 'upgrade', 'Upgrades the Mestral installation'
def upgrade
path = ENV['MESTRAL_PATH']
git_dir = File.join path, '.git'
`git --git-dir #{git_dir} fetch origin master 2>&1`
current_sha = `git --git-dir #{git_dir} rev-parse --short HEAD`
new_sha = `git --git-dir #{git_dir} rev-parse --short FETCH_HEAD`
if current_sha == new_sha
puts 'Mestral is already up-to-date.'
else
`git --git-dir #{git_dir} --work-tree #{path} reset --hard --quiet FETCH_HEAD`
puts "Updated Mestral from #{current_sha} to #{new_sha}"
end
end
no_commands do
def debug(message)
puts message if options[:debug]
end
def find_hook(hook_name)
hook = repository.hook hook_name
if hook.is_a? Mestral::Hook::Native
puts "The '#{hook_name}' hook is a native hook and cannot execute hooklets."
return
end
if hook.nil?
if yes? "The Git hook '#{hook_name}' is not enabled. Do you want to create it now? [Y/n]"
Mestral::Hook::Multi.install repository, hook_name
hook = repository.hook hook_name
end
end
hook
end
def find_hooklet(tape_name, hooklet_name = nil)
tapes = Mestral::Tape.all
if tapes.size == 1 && hooklet_name.nil?
hooklet_name = tape_name
tape_name = tapes.first.name
end
[ tape_name, hooklet_name ]
end
def repository
Mestral::Repository.current = Dir.pwd if Mestral::Repository.current.nil?
Mestral::Repository.current
end
def update_tape(tape)
current_sha = tape.sha
new_sha = tape.git_pull
if new_sha == current_sha
puts "Tape '#{tape.name}' is already up-to-date."
else
puts "Updated tape '#{tape.name}' from #{current_sha} to #{new_sha}"
end
end
end
end