2008-11-11 16:54:10 +03:00
#!/usr/bin/env ruby
2007-10-14 21:27:04 +04:00
2012-05-07 01:16:54 +04:00
require 'optparse'
2007-10-14 21:27:04 +04:00
require 'find'
require 'etc'
2012-05-07 01:16:54 +04:00
Version = " 1.4 "
2008-09-13 20:31:11 +04:00
SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem )
2007-10-14 21:27:04 +04:00
$verbose = 0
$quiet = false
$redmine_host = ''
$repos_base = ''
2007-10-21 20:26:14 +04:00
$svn_owner = 'root'
2009-02-16 22:33:43 +03:00
$svn_group = 'root'
2007-12-15 15:23:39 +03:00
$use_groupid = true
2007-10-21 20:26:14 +04:00
$svn_url = false
2007-11-18 21:51:48 +03:00
$test = false
2008-09-13 20:31:11 +04:00
$force = false
$scm = 'Subversion'
2007-10-14 21:27:04 +04:00
2008-09-17 23:18:31 +04:00
def log ( text , options = { } )
level = options [ :level ] || 0
2008-09-14 23:03:46 +04:00
puts text unless $quiet or level > $verbose
2008-09-17 23:18:31 +04:00
exit 1 if options [ :exit ]
2007-10-14 21:27:04 +04:00
end
2008-09-15 23:37:43 +04:00
def system_or_raise ( command )
raise " \" #{ command } \" failed " unless system command
end
module SCM
module Subversion
def self . create ( path )
system_or_raise " svnadmin create #{ path } "
end
end
module Git
def self . create ( path )
Dir . mkdir path
Dir . chdir ( path ) do
system_or_raise " git --bare init --shared "
2008-09-17 23:38:20 +04:00
system_or_raise " git update-server-info "
2008-09-15 23:37:43 +04:00
end
end
end
end
2012-05-07 01:16:54 +04:00
def read_key_from_file ( filename )
begin
$api_key = File . read ( filename ) . strip
rescue Exception = > e
$stderr . puts " Unable to read the key from #{ filename } : #{ e . message } "
exit 1
2007-10-14 21:27:04 +04:00
end
end
2012-05-07 01:16:54 +04:00
def set_scm ( scm )
2012-05-12 16:54:46 +04:00
$scm = scm . capitalize
2012-05-07 01:16:54 +04:00
unless SUPPORTED_SCM . include? ( $scm )
log ( " Invalid SCM: #{ $scm } \n Valid SCM are: #{ SUPPORTED_SCM . join ( ', ' ) } " , :exit = > true )
end
end
optparse = OptionParser . new do | opts |
opts . banner = " Usage: reposman.rb [OPTIONS...] -s [DIR] -r [HOST] -k [KEY] "
opts . separator ( " " )
2012-05-12 16:50:15 +04:00
opts . separator ( " Manages your repositories with Redmine. " )
opts . separator ( " " )
2012-05-07 01:16:54 +04:00
opts . separator ( " Required arguments: " )
2012-05-12 16:47:51 +04:00
opts . on ( " -s " , " --svn-dir DIR " , " use DIR as base directory for svn repositories " ) { | v | $repos_base = v }
opts . on ( " -r " , " --redmine-host HOST " , " assume Redmine is hosted on HOST. Examples: " ,
2012-05-07 01:18:31 +04:00
" -r redmine.example.net " ,
" -r http://redmine.example.net " ,
" -r https://redmine.example.net " ) { | v | $redmine_host = v }
opts . on ( " -k " , " --key KEY " , " use KEY as the Redmine API key " ,
" (you can use --key-file option as an alternative) " ) { | v | $api_key = v }
2012-05-07 01:16:54 +04:00
opts . separator ( " " )
opts . separator ( " Options: " )
opts . on ( " -o " , " --owner OWNER " , " owner of the repository. using the rails login " ,
" allows users to browse the repository within " ,
" Redmine even for private projects. If you want to " ,
" share repositories through Redmine.pm, you need " ,
" to use the apache owner. " ) { | v | $svn_owner = v ; $use_groupid = false }
opts . on ( " -g " , " --group GROUP " , " group of the repository (default: root) " ) { | v | $svn_group = v ; $use_groupid = false }
opts . on ( " -u " , " --url URL " , " the base url Redmine will use to access your " ,
" repositories. This option is used to register " ,
" the repositories in Redmine automatically. The " ,
" project identifier will be appended to this url. " ,
" Examples: " ,
" -u https://example.net/svn " ,
" -u file:///var/svn/ " ,
" if this option isn't set, reposman won't register " ,
" the repositories in Redmine " ) { | v | $svn_url = v }
opts . on ( " --scm SCM " , " the kind of SCM repository you want to create " ,
" (and register) in Redmine (default: Subversion). " ,
" reposman is able to create Git and Subversion " ,
" repositories. " ,
" For all other kind, you must specify a --command " ,
" option " ) { | v | set_scm ( v ) }
opts . on ( " -c " , " --command COMMAND " , " use this command instead of `svnadmin create` to " ,
" create a repository. This option can be used to " ,
" create repositories other than subversion and git " ,
" kind. " ,
" This command override the default creation for " ,
" git and subversion. " ) { | v | $command = v }
opts . on ( " --key-file FILE " , " path to a file that contains the Redmine API key " ,
" (use this option instead of --key if you don't " ,
" want the key to appear in the command line) " ) { | v | read_key_from_file ( v ) }
opts . on ( " -t " , " --test " , " only show what should be done " ) { $test = true }
opts . on ( " -f " , " --force " , " force repository creation even if the project " , " repository is already declared in Redmine " ) { $force = true }
opts . on ( " -v " , " --verbose " , " verbose " ) { $verbose += 1 }
opts . on ( " -V " , " --version " , " show version and exit " ) { puts Version ; exit }
2012-05-07 01:18:31 +04:00
opts . on ( " -h " , " --help " , " show help and exit " ) { puts opts ; exit 1 }
opts . on ( " -q " , " --quiet " , " no log " ) { $quiet = true }
2012-05-07 01:16:54 +04:00
opts . separator ( " " )
opts . separator ( " Examples: " )
opts . separator ( " reposman.rb --svn-dir=/var/svn --redmine-host=redmine.host " )
opts . separator ( " reposman.rb -s /var/git -r redmine.host -u http://git.host --scm git " )
opts . separator ( " " )
opts . separator ( " You can find more information on the redmine's wiki: \n http://www.redmine.org/projects/redmine/wiki/HowTos " )
opts . summary_width = 25
end
optparse . parse!
2007-11-18 21:51:48 +03:00
if $test
log ( " running in test mode " )
end
2008-09-15 23:37:43 +04:00
# Make sure command is overridden if SCM vendor is not handled internally (for the moment Subversion and Git)
if $command . nil?
begin
scm_module = SCM . const_get ( $scm )
rescue
2008-09-17 23:18:31 +04:00
log ( " Please use --command option to specify how to create a #{ $scm } repository. " , :exit = > true )
2008-09-15 23:37:43 +04:00
end
2008-09-13 20:31:11 +04:00
end
2007-10-21 20:26:14 +04:00
$svn_url += " / " if $svn_url and not $svn_url . match ( / \/ $ / )
2007-10-14 21:27:04 +04:00
if ( $redmine_host . empty? or $repos_base . empty? )
2012-05-07 01:16:54 +04:00
puts " Some arguments are missing. Use reposman.rb --help for getting help. "
exit 1
2007-10-14 21:27:04 +04:00
end
unless File . directory? ( $repos_base )
2008-09-17 23:18:31 +04:00
log ( " directory ' #{ $repos_base } ' doesn't exists " , :exit = > true )
2007-10-14 21:27:04 +04:00
end
2009-02-11 01:03:25 +03:00
begin
2010-02-06 17:20:39 +03:00
require 'active_resource'
2009-02-11 01:03:25 +03:00
rescue LoadError
log ( " This script requires activeresource. \n Run 'gem install activeresource' to install it. " , :exit = > true )
end
2010-10-25 01:00:05 +04:00
class Project < ActiveResource :: Base
self . headers [ " User-agent " ] = " Redmine repository manager/ #{ Version } "
2011-11-27 21:21:00 +04:00
self . format = :xml
2010-10-25 01:00:05 +04:00
end
2009-02-11 01:03:25 +03:00
2008-09-17 23:18:31 +04:00
log ( " querying Redmine for projects... " , :level = > 1 ) ;
2007-10-14 21:27:04 +04:00
$redmine_host . gsub! ( / ^ / , " http:// " ) unless $redmine_host . match ( " ^https?:// " )
$redmine_host . gsub! ( / \/ $ / , '' )
2009-02-11 01:03:25 +03:00
Project . site = " #{ $redmine_host } /sys " ;
2007-10-14 21:27:04 +04:00
begin
2009-02-11 01:03:25 +03:00
# Get all active projects that have the Repository module enabled
2009-12-20 12:44:28 +03:00
projects = Project . find ( :all , :params = > { :key = > $api_key } )
2011-11-27 21:48:22 +04:00
rescue ActiveResource :: ForbiddenAccess
log ( " Request was denied by your Redmine server. Make sure that 'WS for repository management' is enabled in application settings and that you provided the correct API key. " )
2007-10-14 21:27:04 +04:00
rescue = > e
2009-02-11 01:03:25 +03:00
log ( " Unable to connect to #{ Project . site } : #{ e } " , :exit = > true )
2007-10-14 21:27:04 +04:00
end
if projects . nil?
2011-11-27 21:48:22 +04:00
log ( 'No project found, perhaps you forgot to "Enable WS for repository management"' , :exit = > true )
2007-10-14 21:27:04 +04:00
end
2008-09-17 23:18:31 +04:00
log ( " retrieved #{ projects . size } projects " , :level = > 1 )
2007-10-14 21:27:04 +04:00
2007-10-21 20:26:14 +04:00
def set_owner_and_rights ( project , repos_path , & block )
2011-03-27 20:58:21 +04:00
if mswin?
2007-10-21 20:26:14 +04:00
yield if block_given?
else
2009-02-16 22:33:43 +03:00
uid , gid = Etc . getpwnam ( $svn_owner ) . uid , ( $use_groupid ? Etc . getgrnam ( project . identifier ) . gid : Etc . getgrnam ( $svn_group ) . gid )
2007-11-18 21:51:48 +03:00
right = project . is_public ? 0775 : 0770
2007-10-21 20:26:14 +04:00
yield if block_given?
Find . find ( repos_path ) do | f |
File . chmod right , f
File . chown uid , gid , f
end
end
end
def other_read_right? ( file )
( File . stat ( file ) . mode & 0007 ) . zero? ? false : true
end
def owner_name ( file )
2010-03-12 21:15:19 +03:00
mswin? ?
2007-10-21 20:26:14 +04:00
$svn_owner :
2011-05-17 17:32:08 +04:00
Etc . getpwuid ( File . stat ( file ) . uid ) . name
2007-10-21 20:26:14 +04:00
end
2011-05-17 17:32:08 +04:00
2010-03-12 21:15:19 +03:00
def mswin?
( RUBY_PLATFORM =~ / (:?mswin|mingw) / ) || ( RUBY_PLATFORM == 'java' && ( ENV [ 'OS' ] || ENV [ 'os' ] ) =~ / windows /i )
end
2007-10-14 21:27:04 +04:00
2007-10-21 20:26:14 +04:00
projects . each do | project |
2008-09-17 23:18:31 +04:00
log ( " treating project #{ project . name } " , :level = > 1 )
2007-10-21 20:26:14 +04:00
if project . identifier . empty?
log ( " \t no identifier for project #{ project . name } " )
2007-10-14 21:27:04 +04:00
next
2012-03-27 18:01:23 +04:00
elsif not project . identifier . match ( / ^[a-z0-9 \ -_]+$ / )
2007-10-21 20:26:14 +04:00
log ( " \t invalid identifier for project #{ project . name } : #{ project . identifier } " ) ;
2007-10-14 21:27:04 +04:00
next ;
end
2008-09-17 23:47:36 +04:00
repos_path = File . join ( $repos_base , project . identifier ) . gsub ( File :: SEPARATOR , File :: ALT_SEPARATOR || File :: SEPARATOR )
2007-10-14 21:27:04 +04:00
if File . directory? ( repos_path )
2007-10-21 20:26:14 +04:00
# we must verify that repository has the good owner and the good
# rights before leaving
other_read = other_read_right? ( repos_path )
owner = owner_name ( repos_path )
next if project . is_public == other_read and owner == $svn_owner
2007-10-14 21:27:04 +04:00
2007-11-18 21:51:48 +03:00
if $test
log ( " \t change mode on #{ repos_path } " )
next
end
2007-10-14 21:27:04 +04:00
begin
2007-10-21 20:26:14 +04:00
set_owner_and_rights ( project , repos_path )
2007-10-14 21:27:04 +04:00
rescue Errno :: EPERM = > e
log ( " \t unable to change mode on #{ repos_path } : #{ e } \n " )
next
end
log ( " \t mode change on #{ repos_path } " ) ;
else
2008-09-13 20:31:11 +04:00
# if repository is already declared in redmine, we don't create
# unless user use -f with reposman
2009-02-11 01:03:25 +03:00
if $force == false and project . respond_to? ( :repository )
2008-09-17 23:18:31 +04:00
log ( " \t repository for project #{ project . identifier } already exists in Redmine " , :level = > 1 )
2008-09-13 20:31:11 +04:00
next
end
2007-11-18 21:51:48 +03:00
project . is_public ? File . umask ( 0002 ) : File . umask ( 0007 )
if $test
log ( " \t create repository #{ repos_path } " )
log ( " \t repository #{ repos_path } registered in Redmine with url #{ $svn_url } #{ project . identifier } " ) if $svn_url ;
next
end
2007-10-14 21:27:04 +04:00
begin
2007-10-21 20:26:14 +04:00
set_owner_and_rights ( project , repos_path ) do
2008-09-15 23:37:43 +04:00
if scm_module . nil?
system_or_raise " #{ $command } #{ repos_path } "
else
scm_module . create ( repos_path )
end
2007-10-21 20:26:14 +04:00
end
2007-10-14 21:27:04 +04:00
rescue = > e
log ( " \t unable to create #{ repos_path } : #{ e } \n " )
next
end
2007-10-21 20:26:14 +04:00
if $svn_url
2009-02-11 01:03:25 +03:00
begin
2009-12-20 12:44:28 +03:00
project . post ( :repository , :vendor = > $scm , :repository = > { :url = > " #{ $svn_url } #{ project . identifier } " } , :key = > $api_key )
2007-10-21 20:26:14 +04:00
log ( " \t repository #{ repos_path } registered in Redmine with url #{ $svn_url } #{ project . identifier } " ) ;
2009-02-11 01:03:25 +03:00
rescue = > e
log ( " \t repository #{ repos_path } not registered in Redmine: #{ e . message } " ) ;
2007-10-21 20:26:14 +04:00
end
end
2007-10-14 21:27:04 +04:00
log ( " \t repository #{ repos_path } created " ) ;
end
end