diff options
author | Mark Longair <mhl@pobox.com> | 2013-12-06 12:59:20 +0000 |
---|---|---|
committer | Mark Longair <mhl@pobox.com> | 2013-12-06 16:14:07 +0000 |
commit | 6ada52492dda1cd3d27995d1b9d7a917beef5b1a (patch) | |
tree | fa251189ce478941f773c401ad5698cad27f8e8a /lib/tasks | |
parent | 14b8185e8051779a1ba500fe31bd2eb08c89edae (diff) |
Make 'rake themes:install' safer for developers
Previously, the themes:install rake task would remove the existing
theme with 'rm -rf' and re-clone the theme into place. This is
unfortunate for a developer who has been making changes to a theme
and then runs the rails-post-deploy script, since it calls the
themes:install task which will wipe out those changes. In
addition, when installing themes it would deliberately remove the
.git directory of the theme, so if you do want to work in that
theme you'd have to reinitialize the theme directory to be a git
repository again.
This commit changes the task so that now:
- If a theme directory is present but it isn't a git
repository, it's moved out of the way.
- If there's no theme directory at the expected location after
that step, the theme repository is cloned into place
- The task ensures that the origin remote points to the theme's
URL, and fetches from that remote.
- If there are any uncommitted changes in the theme repository
or the current commit appears not to have been pushed, the
task exits with a helpful error.
- The preferred branch or tag is checked out in the theme
repository as before.
(The uninstall, install and post_install hooks are run as before.)
This shouldn't make a difference to deployed instances of Alaveteli
but will be helpful for developers who want to work on developing
a theme.
Fixes #1111.
Diffstat (limited to 'lib/tasks')
-rw-r--r-- | lib/tasks/themes.rake | 138 |
1 files changed, 79 insertions, 59 deletions
diff --git a/lib/tasks/themes.rake b/lib/tasks/themes.rake index 65b142a63..4a864d141 100644 --- a/lib/tasks/themes.rake +++ b/lib/tasks/themes.rake @@ -1,104 +1,123 @@ +require Rails.root.join('commonlib', 'rblib', 'git') + namespace :themes do - def plugin_dir + # Alias the module so we don't need the MySociety prefix here + Git = MySociety::Git + + def all_themes_dir File.join(Rails.root,"lib","themes") end def theme_dir(theme_name) - File.join(plugin_dir, theme_name) + File.join(all_themes_dir, theme_name) end - def old_theme_dir(theme_name) + def old_all_themes_dir(theme_name) File.join(Rails.root, "vendor", "plugins", theme_name) end def possible_theme_dirs(theme_name) - [theme_dir(theme_name), old_theme_dir(theme_name)] - end - - def checkout(commitish) - puts "Checking out #{commitish}" if verbose - system "git checkout #{commitish}" + [theme_dir(theme_name), old_all_themes_dir(theme_name)] end - def checkout_tag(version) - checkout usage_tag(version) - end - - def checkout_remote_branch(branch) - checkout "origin/#{branch}" + def installed?(theme_name) + possible_theme_dirs(theme_name).any? { |dir| File.directory? dir } end def usage_tag(version) "use-with-alaveteli-#{version}" end - def install_theme_using_git(name, uri, verbose=false, options={}) - install_path = theme_dir(name) - Dir.chdir(plugin_dir) do - clone_command = "git clone #{uri} #{name}" - if system(clone_command) - Dir.chdir install_path do - # First try to checkout a specific branch of the theme - tag_checked_out = checkout_remote_branch(AlaveteliConfiguration::theme_branch) if AlaveteliConfiguration::theme_branch - if !tag_checked_out - # try to checkout a tag exactly matching ALAVETELI VERSION - tag_checked_out = checkout_tag(ALAVETELI_VERSION) - end - if ! tag_checked_out - # if we're on a hotfix release (four sequence elements or more), - # look for a usage tag matching the minor release (three sequence elements) - # and check that out if found - if hotfix_version = /^(\d+\.\d+\.\d+)(\.\d+)+/.match(ALAVETELI_VERSION) - base_version = hotfix_version[1] - tag_checked_out = checkout_tag(base_version) - end - end - if ! tag_checked_out - puts "No specific tag for this version: using HEAD" if verbose - end - puts "removing: .git .gitignore" if verbose - rm_rf %w(.git .gitignore) - end - else - rm_rf install_path - raise "#{clone_command} failed! Stopping." - end - end - end - def uninstall(theme_name, verbose=false) possible_theme_dirs(theme_name).each do |dir| if File.directory?(dir) run_hook(theme_name, 'uninstall', verbose) - puts "Removing '#{dir}'" if verbose - rm_r dir - else - puts "Plugin doesn't exist: #{dir}" end end end def run_hook(theme_name, hook_name, verbose=false) - hook_file = File.join(theme_dir(theme_name), "#{hook_name}.rb") + directory = theme_dir(theme_name) + hook_file = File.join(directory, "#{hook_name}.rb") if File.exist? hook_file - puts "Running #{hook_name} hook for #{theme_name}" if verbose + puts "Running #{hook_name} hook in #{directory}" if verbose load hook_file end end - def installed?(theme_name) - possible_theme_dirs(theme_name).any? { |dir| File.directory? dir } + def move_old_theme(old_theme_directory) + puts "There was an old-style theme at #{old_theme_directory}" if verbose + moved_directory = "#{old_theme_directory}-moved" + begin + File.rename old_theme_directory, moved_directory + rescue Errno::ENOTEMPTY, Errno::EEXIST + raise "Tried to move #{old_theme_directory} out of the way, " \ + "but #{moved_directory} already existed" + end + end + + def committishes_to_try + result = [] + theme_branch = AlaveteliConfiguration::theme_branch + result.push "origin/#{theme_branch}" if theme_branch + result.push usage_tag(ALAVETELI_VERSION) + hotfix_match = /^(\d+\.\d+\.\d+)(\.\d+)+/.match(ALAVETELI_VERSION) + result.push usage_tag(hotfix_match[1]) if hotfix_match + result + end + + def checkout_best_option(theme_name) + theme_directory = theme_dir theme_name + all_failed = true + committishes_to_try.each do |committish| + if Git.committish_exists? theme_directory, committish + puts "Checking out #{committish}" if verbose + Git.checkout theme_directory, committish + all_failed = false + break + else + puts "Failed to find #{committish}; skipping..." if verbose + end + end + puts "Falling to using HEAD instead" if all_failed and verbose end def install_theme(theme_url, verbose, deprecated=false) - FileUtils.mkdir_p plugin_dir + FileUtils.mkdir_p all_themes_dir deprecation_string = deprecated ? " using deprecated THEME_URL" : "" theme_name = theme_url_to_theme_name theme_url puts "Installing theme #{theme_name}#{deprecation_string} from #{theme_url}" + # Make sure any uninstall hooks have been run: uninstall(theme_name, verbose) if installed?(theme_name) - install_theme_using_git(theme_name, theme_url, verbose) + theme_directory = theme_dir theme_name + # Is there an old-style theme directory there? If so, move it + # out of the way so that there's no risk that work is lost: + if File.directory? theme_directory + unless Git.non_bare_repository? theme_directory + move_old_theme theme_directory + end + end + # If there isn't a directory there already, clone it into place: + unless File.directory? theme_directory + unless system "git", "clone", theme_url, theme_directory + raise "Cloning from #{theme_url} to #{theme_directory} failed" + end + end + # Set the URL for origin in case it has changed, and fetch from there: + Git.remote_set_url theme_directory, 'origin', theme_url + Git.fetch theme_directory, 'origin' + # Check that checking-out a new commit will be safe: + unless Git.status_clean theme_directory + raise "There were uncommitted changes in #{theme_directory}" + end + unless Git.is_HEAD_pushed? theme_directory + raise "The current work in #{theme_directory} is unpushed" + end + # Now try to checkout various commits in order of preference: + checkout_best_option theme_name + # Finally run the install hooks: run_hook(theme_name, 'install', verbose) run_hook(theme_name, 'post_install', verbose) end @@ -112,4 +131,5 @@ namespace :themes do install_theme(AlaveteliConfiguration::theme_url, verbose, deprecated=true) end end + end |