diff options
author | Chris Alfonso <calfonso@redhat.com> | 2008-07-07 17:20:43 -0400 |
---|---|---|
committer | Chris Alfonso <calfonso@redhat.com> | 2008-07-08 10:43:59 -0400 |
commit | 40598cb7bfeb6b1042482fe91701770b179c1803 (patch) | |
tree | 57d9b019a845fbcf4a7fcb8ccbce836bfc45174c /genome-sync | |
parent | 9b9d481f6684777e90bb144193251244d14926ae (diff) | |
download | tools-40598cb7bfeb6b1042482fe91701770b179c1803.tar.gz tools-40598cb7bfeb6b1042482fe91701770b179c1803.tar.xz tools-40598cb7bfeb6b1042482fe91701770b179c1803.zip |
Renaming everying everest to genome
Diffstat (limited to 'genome-sync')
-rw-r--r-- | genome-sync/Makefile | 35 | ||||
-rw-r--r-- | genome-sync/Rakefile | 43 | ||||
-rw-r--r-- | genome-sync/bin/genome-sync | 373 | ||||
-rw-r--r-- | genome-sync/extra/genome-sync.spec | 54 | ||||
-rw-r--r-- | genome-sync/lib/genome-sync.rb | 43 |
5 files changed, 548 insertions, 0 deletions
diff --git a/genome-sync/Makefile b/genome-sync/Makefile new file mode 100644 index 0000000..0f457d8 --- /dev/null +++ b/genome-sync/Makefile @@ -0,0 +1,35 @@ +NAME := genome-sync +SPECFILE = extra/$(NAME).spec +VERSION := $(shell rpm -q --qf "%{VERSION}\n" --specfile $(SPECFILE)| head -1) +RELEASE := $(shell rpm -q --qf "%{RELEASE}\n" --specfile $(SPECFILE)| head -1) +UPSTREAM_NAME = $(PROJECT) + +TAG = $(subst .,_,$(NAME)-$(VERSION)-$(RELEASE)) + +CVS = cvs +RPMBUILD = rpmbuild +INSTALL = /usr/bin/install +INSTALL_DIR = $(INSTALL) --verbose -d -m 755 + +RPM_TOPDIR = /tmp/$(NAME)-$(VERSION)-$(RELEASE)-build +_RPM_OPTS = --define "_topdir $(RPM_TOPDIR)" \ + --define "_builddir %{_topdir}" \ + --define "_sourcedir $(shell pwd)/pkg" \ + --define "_specdir $(shell pwd)" \ + --define "_rpmdir $(shell pwd)" \ + --define "_srcrpmdir $(shell pwd)" \ + --define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' +RPM_OPTS = $(strip $(_RPM_OPTS)) + +rpm: clean gem $(RPM_TOPDIR) $(SPECFILE) + $(RPMBUILD) --clean $(RPM_OPTS) -bb $(SPECFILE) + +gem: + rake package + +clean: + @rm -rfv *~ *.rpm $(RPM_TOPDIR) $(ARCHIVE) + rake clobber package + +$(RPM_TOPDIR): + @$(INSTALL_DIR) $@ diff --git a/genome-sync/Rakefile b/genome-sync/Rakefile new file mode 100644 index 0000000..e651e09 --- /dev/null +++ b/genome-sync/Rakefile @@ -0,0 +1,43 @@ +# -*- ruby -*-
+require 'rubygems'
+Gem::manage_gems
+require 'rake/gempackagetask'
+require './lib/genome-sync'
+
+spec = Gem::Specification.new do |s|
+ s.platform = Gem::Platform::RUBY
+ s.name = GenomeSync::AppName
+ s.version = GenomeSync::Version::STRING
+ s.author = "Red Hat IT"
+ s.email = "genome-list@redhat.com"
+ s.summary = "A tool for syncronizing Genome Repositories"
+ s.files = ["lib/#{GenomeSync::AppName}.rb", "extra/#{GenomeSync::AppName}.spec" ]
+ s.executables = [GenomeSync::AppName]
+ s.require_path = "lib"
+ s.add_dependency('git', '>= 1.0.7.1')
+ s.add_dependency('main')
+ s.add_dependency('highline')
+ #s.test_files = Dir.glob('tests/*.rb')
+ #s.has_rdoc = true
+ #s.extra_rdoc_files = ["README"]
+end
+
+Rake::GemPackageTask.new(spec) do |pkg|
+ pkg.need_tar = true
+end
+
+task :default => "pkg/#{spec.name}-#{spec.version}.gem" do
+ puts "generated latest version"
+end
+
+desc "Regenerate Documentation"
+task :doc do |t|
+ system('rdoc lib/ README --main README --inline-source')
+end
+
+#desc "Run Unit Tests"
+#task :test do |t|
+# require File.dirname(__FILE__) + '/tests/all_tests.rb'
+#end
+
+# vim: syntax=Ruby
diff --git a/genome-sync/bin/genome-sync b/genome-sync/bin/genome-sync new file mode 100644 index 0000000..909c43d --- /dev/null +++ b/genome-sync/bin/genome-sync @@ -0,0 +1,373 @@ +#!/usr/bin/env ruby +require 'open-uri' +require 'fileutils' +require 'git' +require 'main' +require 'highline/import' +require 'genome-sync' +#http://www.mail-archive.com/capistrano@googlegroups.com/msg01822.html +HighLine.track_eof = false + +include GenomeSync + +Main { + version Version::STRING + description "A tool for syncronizing Genome Repo machines." + + # Normally I wouldn't use class variables but I think it's warranted within + # the 'main' DSL. If there are any other global config variables feel free + # to put them here. + APP_NAME = "genome-sync" + DEFAULT_WORKING_DIR = File.join(ENV["HOME"], "." + APP_NAME) + + @@repo_path_prefix = "/pub/git" + @@puppetmaster_base_dir = "/etc/puppet/modules/main" + + # Main hook + def handle_exception e + puts e.message + end unless $DEBUG + + option('verbose', 'v') + + option('workingdir', 'w') { + description "The working directory to sync" + default DEFAULT_WORKING_DIR + attr + argument_required + } + + def run + require 'rbconfig' + script = File.join(Config::CONFIG['bindir'], APP_NAME) + exec("#{script} --help") + end + + mode('clean') { + def run() + verbose("Removing #{workingdir}") + FileUtils.rm_rf(workingdir) + end + } + + mode('start') { + description "Guides the user through the syncronization process" + + option('repo', 'r'){ + description "Genome Repository to syncronize with" + required + argument_required + } + + mode('quick') { + description "Perform's hard reset to a given Genome Repo." + + def run + start do |git| + git_action(git, :non_interactive => true) do |b| + git.reset_hard(b) + end + end + end + } + + def run + start(:interactive => true) do |git| + # Begin YACW (Yet Another Cheesy Wizard) + choose do |menu| + menu.prompt = "What would you like to do?" + + menu.choice "'reset --hard' to the remote" do + git_action(git) do |b| + git.reset_hard(b) + end + end + + menu.choice "merge" do + git_action(git) do |b| + git.merge(b) + end + end + + menu.choice "rebase" do + git_action(git) do |b| + git.rebase(b) + end + end + + menu.choice "something even more exciting" do + git_action(git) do |b| + fork do + say("Type 'exit' when you are done to continue") + git.chdir {exec('bash')} + end + + Process.wait2 + end + end + end + end + end + } + + mode('save') { + description "Push the changes in the working directory to #{@@repo_path_prefix}.\n" + + "The puppet modules will have their 'master' branches\n" + + "checked out to #{@@puppetmaster_base_dir}." + + def run + working_git_dirs.each do |d| + repo_base_path = d.sub(workingdir, "") # Chop off the working dir + dest = File.join(@@repo_path_prefix, repo_base_path) + bare_repo_name = File.basename(dest) + bare_repo_parent_dir = File.dirname(dest) + + unless File.directory?(dest) + verbose("Making #{bare_repo_parent_dir}") + FileUtils.mkdir_p(bare_repo_parent_dir) + + verbose("Cloning #{d} to #{dest}") + + # Handle the special puppet module case + if repo_base_path =~ %r-^/?puppet- + + # For the correct code to make it to the puppetmaster we need to + # make sure 'master' is the active branch. + Git.open(d).checkout('master') + + puppetmaster_dir = File.join(@@puppetmaster_base_dir, bare_repo_name) + verbose("Checking out 'master' at #{puppetmaster_dir}") + Git.clone(d, puppetmaster_dir) + # HACK: I couldn't get the git gem to clone the working dir for the + # puppetmaster and the bare repo for gitweb. This is a workaround. + FileUtils.mv(File.join(puppetmaster_dir, ".git"), dest) + + # Lay down hook to update the puppetmaster + hook_file = File.join(dest, "hooks/post-receive") + File.open(hook_file, "w") do |f| + verbose("Laying down #{hook_file}") + f.puts(puppet_post_receive_hook) + end + File.chmod(0744, hook_file) + else + Git.clone(d, + bare_repo_name, + :path => bare_repo_parent_dir, + :bare => true) + end + end + + verbose("Pushing all branches in #{d} to #{dest}:") + git = Git.open(d) + git.branches.local.each do |b| + b.checkout + verbose("#{b}") + git.push(dest,b, :force => true) + end + verbose("done.") + end + end + } + + ############################################################################## + # These methods are available to all modes since their in the global namespace + ############################################################################## + + def verbose(msg) + puts msg if params['verbose'].given? + end + + # I couldn't find a way to 'checkout -b --force' so this will have to do for + # now. It would be better to work this into the git gem at some point. + def checkout_b(git, branch, opts = {}) + verbose("Setting #{branch.name} to #{git.object(branch).sha}") + git.branch(branch.name).checkout + git.reset_hard(branch) + end + + def working_git_dirs + Dir[workingdir + "/**/.git"].map {|g| File.dirname(g)} + end + + # Just a helper method. Handle recovery + def git_action_safe(g, b) + local_branches = g.branches.local + + # See if the branch already exists + if lb = local_branches.find {|l| l.name == b.name} + lb.checkout + begin + yield(b) + rescue Git::GitExecuteError => e + fork do + puts e.message + puts "Type 'exit' when everything is fixed to continue '#{APP_NAME}'" + g.chdir {exec('bash')} + end + + Process.wait2 + end + else # We must create the branch + checkout_b(g,b) + end + end + + # Abstracts the UI for all git modes. Regardless or whether you are + # merging, rebasing, etc we want the user to be presented with the same choices. + # Namely, they can select a branch if there are multiple remote branches and with + # that branch they can do whatever action is contained in the 'block'--all the while + # recovering from git failures. + def git_action(g, opts={}, &block) + remote_branches = g.branches.find_all do |b| + (b.remote.to_s == @genome_repo) && b.name != 'HEAD' + end + + # This is a horrible hack. The interactiveness should not be this method's + # concern. + if opts[:non_interactive] + remote_branches.each do |b| + git_action_safe(g, b, &block) + end + else + # Don't bother asking the user for a branch if there is only one + if remote_branches.size == 1 + git_action_safe(g, remote_branches[0], &block) + else + choose do |m| + m.prompt = "Select branch." + + + m.choices(*remote_branches) do |b| + git_action_safe(g, b, &block) + end + + m.choice("all branches") do + remote_branches.each do |b| + git_action_safe(g, b, &block) + end + end + end + end + end + end + + def start(opts={}) + # Setup + verbose "making #{workingdir}" + FileUtils.mkdir_p(workingdir) + + @genome_repo = params['repo'].value + + verbose("Finding available repos on #{@genome_repo}") + open("http://#{@genome_repo}/git/gitweb.cgi?a=project_index") do |f| + # Sometimes gitweb attaches the owner's name of the repo. The regex + # should grab just the part we need. + @git_repo_paths = f.readlines.map {|line| line.strip.match(/([^\s]*)/)[1]} + end + + # Iterate over each repo and guide the user through the sync process + @git_repo_paths.each do |p| + next unless agree("Sync #{p}? (y/n)", true) if opts[:interactive] + + clone_dir = File.join(workingdir, p) + verbose "clone_dir = #{clone_dir}" + + # Handle the case where the repo path is foo/bar + dirs = File.split(p) + if dirs.size > 1 + subdirs = dirs[0..-2] + repo_parent_dir = workingdir + File::SEPARATOR + File.join(subdirs) + verbose "Making #{repo_parent_dir}" + FileUtils.mkdir_p(repo_parent_dir) + end + + repo_path = "#{@@repo_path_prefix}/#{p}" + repo_url = "git://#{@genome_repo + repo_path}" + + # We need to clone from the remote if the local clone doesn't exist + git = if File.directory?(clone_dir) + verbose "Opening #{clone_dir}" + Git.open(clone_dir) + else + # NOTE: For symplicity's sake I decided not to bother cloning from /pub/git + # locally if the repo does not exist in the working dir. This would + # make the tool have to detect if the local clone is out sync. While + # this isn't hard, I don't feel like messing with it at the moment. + verbose("Cloning #{repo_url} to #{clone_dir}") + g = Git.clone(repo_url, clone_dir) + + g.branches.remote.each do |r| + next if r.name == 'HEAD' + checkout_b(g, r) + end + + # Don't bother asking the user anything else about this repo if we + # don't even have a local public copy yet. + unless File.directory?(repo_path) + verbose("No local public repo found at #{repo_path}") + next + end + + g # value from if + end + + ############################## + # Setup Remotes: + ############################## + + # Don't add the remote if it already exists + # Note: By convention the remote must be the same as the genome repo fqdn + git.add_remote(@genome_repo, repo_url) unless git.remotes.find {|r| r.name == @genome_repo} + verbose("Fetching #{@genome_repo}") + git.fetch(@genome_repo) + + # If we have a public repo make sure we have a remote that points to it + localcache_remote_name = 'localcache' + if File.directory?(repo_path) # This dir will exist if the user has ever used 'save' + # Add a remote if we don't have one + unless git.remotes.find {|r| r.name == localcache_remote_name} + git.add_remote(localcache_remote_name, repo_path) + end + + verbose("Fetching #{localcache_remote_name}") + git.fetch(localcache_remote_name) + + # reset hard all our local branches + local_public_branches = git.branches.remote.find_all do |b| + next if b.name == 'HEAD' + b.remote.name == localcache_remote_name + end + + local_public_branches.each {|b| checkout_b(git, b, :force => true)} + end + + yield(git) + end + end + + # I _really_ don't like putting this in this tool. I couldn't figure out a + # way to lay it down with puppet. + def puppet_post_receive_hook + <<-HOOK +#!/bin/sh + +update_working_dir() { + GIT_DIR=`pwd` + GIT_WORK_TREE="/etc/puppet/modules/main/`/bin/basename $GIT_DIR`" + + pushd $GIT_WORK_TREE + git --git-dir=$GIT_DIR reset --hard $1 + echo "$GIT_WORK_TREE updated." + popd +} + +while read oldrev newrev ref; do + # We only care when master gets updated + if [[ $ref == 'refs/heads/master' ]] + then + update_working_dir $newrev + fi +done + HOOK + end +} diff --git a/genome-sync/extra/genome-sync.spec b/genome-sync/extra/genome-sync.spec new file mode 100644 index 0000000..300f030 --- /dev/null +++ b/genome-sync/extra/genome-sync.spec @@ -0,0 +1,54 @@ +%define ruby_sitelib %(ruby -rrbconfig -e "puts Config::CONFIG['sitelibdir']") +%define gemdir %(ruby -rubygems -e 'puts Gem::dir' 2>/dev/null) +%define gemname genome-sync +%define geminstdir %{gemdir}/gems/%{gemname}-%{version} + +Summary: A tool for syncronizing Genome Repositories +Name: rubygem-%{gemname} +Version: 1.0.0 +Release: 5%{?dist} +Group: Development/Languages +License: GPLv2 +URL: https://fedorahosting.org/genome +Source0: %{gemname}-%{version}.gem +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +Requires: rubygems +Requires: rubygem(git) >= 1.0.7.1 +Requires: rubygem(main) +Requires: rubygem(highline) +BuildRequires: rubygems +BuildArch: noarch +Provides: rubygem(%{gemname}) = %{version} + +%description +A tool for syncronizing Genome Repositories + + +%prep + +%build + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}%{gemdir} +gem install --local --install-dir %{buildroot}%{gemdir} \ + --force %{SOURCE0} +mkdir -p %{buildroot}/%{_bindir} +mv %{buildroot}%{gemdir}/bin/* %{buildroot}/%{_bindir} +rmdir %{buildroot}%{gemdir}/bin +find %{buildroot}%{geminstdir}/bin -type f | xargs chmod a+x + +%clean +rm -rf %{buildroot} + +%files +%defattr(-, root, root, -) +%{_bindir}/genome-sync +%{gemdir}/gems/%{gemname}-%{version}/ +%{gemdir}/cache/%{gemname}-%{version}.gem +%{gemdir}/specifications/%{gemname}-%{version}.gemspec + + +%changelog +* Tue Jun 24 2008 <bleanhar@redhat.com> - 1.0.0-1 +- Initial package diff --git a/genome-sync/lib/genome-sync.rb b/genome-sync/lib/genome-sync.rb new file mode 100644 index 0000000..f4c370c --- /dev/null +++ b/genome-sync/lib/genome-sync.rb @@ -0,0 +1,43 @@ +# Copyright (C) 2008 Red Hat, Inc + +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# a long with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Copyright (C) 2008 Red Hat, Inc + +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# a long with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module GenomeSync + AppName = "genome-sync" + + module Version + MAJOR = 1 + MINOR = 0 + BUILD = 0 + + STRING = [MAJOR, MINOR, BUILD].join(".") + end +end |