# User: Vitalie Lazu # Date: Mar 2008 # Time: 12:08:42 PM require 'digest/md5' require 'sorted_inifile' require 'control_tool' # http://trac-hacks.org/wiki/GitPlugin # svn co http://trac-hacks.org/svn/gitplugin/0.10 gitplugin # cd gitplugin # python setup.py bdist_egg # cp egg to /usr/share/trac/plugins class Git < ControlTool def check logger.info("Git check") logger.warn("Dir '#{repos_dir}' is not writeable") unless test(?w, repos_dir) logger.warn("Dir '#{git_daemon_dir}' is not writeable") unless test(?w, git_daemon_dir) logger.warn("File #{gitosis_conf} is not writeable") unless test(?w, gitosis_conf) logger.info("Git check end") end # project_url, allow_anonymous, owner_id, owner_login, ssh_pub_keys def on_create(opts) self.settings = opts raise "NotAllowed" if project_name == "gitosis-admin" settings['owner_login'] ||= "NotPassed" dest_dir = dest_dir(settings['project_url']) if !test(?d, dest_dir) if init_repo # http checkout does not work without this, git - 1.5.4.4 #run("git-update-server-info") if settings['copy_vcs_from'] orig_repo_path = dest_dir(settings['copy_vcs_from']) if test(?d, orig_repo_path) cd orig_repo_path run("git-send-pack --all #{dest_dir}") end end ln_sf "#{base_dir}/../git/git-post-receive.rb", File.join(dest_dir, 'hooks', 'post-receive') create_breakout_ini(dest_dir, settings) update_gitosis_conf return url(dir_suffix(project_name)) else raise "Can not init repo" end else raise "Dest dir exists" end end def on_destroy(repository_name, suffix = nil) self.settings ||= {'project_url' => repository_name} raise "NotAllowed" if project_name == "gitosis-admin" lock_tmp_file do ini = SortedIniFile.new(gitosis_conf) ini["group #{project_name}"] = nil ini["group read:#{project_name}"] = nil ini["group write:#{project_name}"] = nil ini["repo #{project_name}"] = nil ini.write commit_gitosis_changes("Removed repository #{project_name}") end backup_dir = config['backup_dir'] + '/git' mkdir_p(backup_dir) unless test(?d, backup_dir) dir = fix_project_name(repository_name) + suffix # ruby can not move symlinks system("/bin/mv #{dest_dir(repository_name)} #{File.join(backup_dir, dir)}") remove_git_daemon_symlink end def upload_pub_keys @commit_message = "upload_pub_keys for #{project_name}" update_gitosis_conf end def update_permissions(repository_name, allow_anonymous) self.settings ||= {'project_url' => repository_name, 'allow_anonymous' => allow_anonymous} @commit_message = "update_permissions for #{project_name}" update_gitosis_conf end # End control center methods ###################################################### def init_repo mkdir_p repo_path cd repo_path run("git --bare init --shared=group") end def remove_git_daemon_symlink rm (git_daemon_repo_path) if test(?l, git_daemon_repo_path) end def dest_dir(repository_name) File.expand_path(File.join(repos_dir, repo_dir_name(repository_name))) end def repo_path dest_dir(settings['project_url']) end def repo_dir_name(repository_name) dir_suffix(fix_project_name(repository_name)) end def repos_dir config['git']['base_dir'] end alias vcs_dir dest_dir def dir_suffix(dir) dir + '.git' end def project_name fix_project_name(settings['project_url']) end def url(dir) config['git']['base_url'] + ':' + dir end def gitosis_dir config['git']['gitosis_dir'] end def gitosis_conf File.join(gitosis_dir, 'gitosis.conf') end def update_gitosis_conf lock_tmp_file do ini = SortedIniFile.new(gitosis_conf) ini["group #{project_name}"] = gitosis_group if settings['ssh_pub_keys'] special_gitosis_group(ini, :read) special_gitosis_group(ini, :write) if settings['allow_anonymous'] ini["repo #{project_name}"] = {'daemon' => 'yes'} ln_sf dest_dir(settings['project_url']), git_daemon_repo_path else remove_git_daemon_symlink end ini.write commit_gitosis_changes(@commit_message || "Created repository #{project_name}, owner login/id: #{settings['owner_login']}/#{settings['owner_id']}") end end # Set special group for git repository access # access -> :read or :write def special_gitosis_group(ini, access) key = "ssh_#{access}_keys" return unless settings[key] ini_key = "group #{access}:#{project_name}" if settings[key].size == 0 ini[ini_key] = nil else members = [] settings[key].each do |ssh_key| key_id = "f-" + Digest::MD5.hexdigest(ssh_key) members << key_id key_path = File.join(gitosis_dir, 'keydir', "#{key_id}.pub") write_ssh_key(ssh_key, key_path) end ini[ini_key] = { 'members' => members.sort * ' ', access == :read ? 'readonly' : 'writable' => project_name} end end def gitosis_group # add group gr = {'writable' => project_name} users = [] for row in settings['ssh_pub_keys'] user_id, ssh_key = row users << gitosis_user_id(user_id) write_ssh_key(ssh_key, user_ssh_key_file(user_id)) end gr['members'] = users.size > 0 ? users.join(' ') : nil gr end def write_ssh_key(ssh_key, dest_path) File.open(dest_path, "w") do |f| f.write(ssh_key.tr("\r\n", '') + "\n") end end def git_daemon_repo_path "#{git_daemon_dir}/#{repo_dir_name(settings['project_url'])}" end def commit_gitosis_changes(msg) dir = Dir.pwd cd gitosis_dir run("git-add .") run("git-commit -a -m \"#{msg}\"") run("git-push") run("git-pull") cd dir end def lock_tmp_file File.open(File.join(tmp_dir, 'git.lock'), "w") do |f| if f.flock(File::LOCK_EX) begin yield ensure f.close end end end end def git_daemon_dir config['git']['git_daemon_dir'] end def exists_rsa_key?(user_id) test(?f, user_ssh_key_file(user_id)) end def user_ssh_key_file(user_id) File.join(gitosis_dir, 'keydir', "#{gitosis_user_id user_id}.pub") end def gitosis_user_id(user_id) "u-#{user_id}" end def disk_space(settings) dir = dest_dir(settings['dir_name']) dir_disk_space(dir) end end # Sample Apache conf # #ServerName git.host.com # #DocumentRoot "/var/www/" #Alias /git/ "/opt/beta/git/" # # # Order allow,deny # Allow from all # # #ErrorLog /var/log/tools/git-error.log #LogLevel debug #CustomLog /var/log/tools/git-access.log combined # #SetHandler mod_python #PythonDebug On # # # DAV on # AuthType Basic # AuthName "Git Restricted Area" # PythonAccessHandler breakout.svn_access # PythonAuthenHandler breakout.svn_access # # #