2008-11-11 16:54:10 +03:00
#!/usr/bin/env ruby
2007-10-14 21:27:04 +04:00
# == Synopsis
#
2008-09-15 23:37:43 +04:00
# reposman: manages your repositories with Redmine
2007-10-14 21:27:04 +04:00
#
# == Usage
#
2008-09-13 20:31:11 +04:00
# reposman [OPTIONS...] -s [DIR] -r [HOST]
2011-05-17 17:32:08 +04:00
#
2008-09-13 20:31:11 +04:00
# Examples:
2008-09-15 23:37:43 +04:00
# reposman --svn-dir=/var/svn --redmine-host=redmine.example.net --scm subversion
# reposman -s /var/git -r redmine.example.net -u http://svn.example.net --scm git
2007-10-14 21:27:04 +04:00
#
# == Arguments (mandatory)
#
2008-09-13 20:31:11 +04:00
# -s, --svn-dir=DIR use DIR as base directory for svn repositories
# -r, --redmine-host=HOST assume Redmine is hosted on HOST. Examples:
# -r redmine.example.net
# -r http://redmine.example.net
# -r https://example.net/redmine
2011-11-27 21:42:19 +04:00
# -k, --key=KEY use KEY as the Redmine API key (you can use the
# --key-file option as an alternative)
2007-10-14 21:27:04 +04:00
#
# == Options
2007-10-21 20:26:14 +04:00
#
2008-09-13 20:31:11 +04:00
# -o, --owner=OWNER owner of the repository. using the rails login
# allow user to browse the repository within
2009-12-20 12:44:28 +03:00
# Redmine even for private project. If you want to
# share repositories through Redmine.pm, you need
# to use the apache owner.
2009-02-16 22:33:43 +03:00
# -g, --group=GROUP group of the repository. (default: root)
2009-12-20 12:44:28 +03:00
# --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
2008-09-13 20:31:11 +04:00
# -u, --url=URL the base url Redmine will use to access your
# repositories. This option is used to automatically
# register the repositories in Redmine. 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
# -c, --command=COMMAND use this command instead of "svnadmin create" to
# create a repository. This option can be used to
2009-12-20 12:44:28 +03:00
# create repositories other than subversion and git
# kind.
# This command override the default creation for git
# and subversion.
2008-09-13 20:31:11 +04:00
# -f, --force force repository creation even if the project
# repository is already declared in Redmine
2011-11-27 21:42:19 +04:00
# --key-file=PATH path to a file that contains the Redmine API key
# (use this option instead of --key if you don't
# the key to appear in the command line)
2008-09-13 20:31:11 +04:00
# -t, --test only show what should be done
# -h, --help show help and exit
# -v, --verbose verbose
# -V, --version print version and exit
# -q, --quiet no log
2008-09-15 23:37:43 +04:00
#
# == References
2011-05-17 17:32:08 +04:00
#
2008-09-15 23:37:43 +04:00
# You can find more information on the redmine's wiki : http://www.redmine.org/wiki/redmine/HowTos
2007-10-14 21:27:04 +04:00
require 'getoptlong'
require 'rdoc/usage'
require 'find'
require 'etc'
2009-02-16 22:33:43 +03:00
Version = " 1.3 "
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
opts = GetoptLong . new (
[ '--svn-dir' , '-s' , GetoptLong :: REQUIRED_ARGUMENT ] ,
[ '--redmine-host' , '-r' , GetoptLong :: REQUIRED_ARGUMENT ] ,
2009-12-20 12:44:28 +03:00
[ '--key' , '-k' , GetoptLong :: REQUIRED_ARGUMENT ] ,
2011-11-27 21:42:19 +04:00
[ '--key-file' , GetoptLong :: REQUIRED_ARGUMENT ] ,
2007-10-21 20:26:14 +04:00
[ '--owner' , '-o' , GetoptLong :: REQUIRED_ARGUMENT ] ,
2009-02-16 22:33:43 +03:00
[ '--group' , '-g' , GetoptLong :: REQUIRED_ARGUMENT ] ,
2007-10-21 20:26:14 +04:00
[ '--url' , '-u' , GetoptLong :: REQUIRED_ARGUMENT ] ,
2008-09-13 20:31:11 +04:00
[ '--command' , '-c' , GetoptLong :: REQUIRED_ARGUMENT ] ,
[ '--scm' , GetoptLong :: REQUIRED_ARGUMENT ] ,
2007-11-18 21:51:48 +03:00
[ '--test' , '-t' , GetoptLong :: NO_ARGUMENT ] ,
2008-09-13 20:31:11 +04:00
[ '--force' , '-f' , GetoptLong :: NO_ARGUMENT ] ,
2007-10-14 21:27:04 +04:00
[ '--verbose' , '-v' , GetoptLong :: NO_ARGUMENT ] ,
[ '--version' , '-V' , GetoptLong :: NO_ARGUMENT ] ,
[ '--help' , '-h' , GetoptLong :: NO_ARGUMENT ] ,
[ '--quiet' , '-q' , GetoptLong :: NO_ARGUMENT ]
)
$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
2007-10-14 21:27:04 +04:00
begin
opts . each do | opt , arg |
case opt
2007-10-21 20:26:14 +04:00
when '--svn-dir' ; $repos_base = arg . dup
2007-10-14 21:27:04 +04:00
when '--redmine-host' ; $redmine_host = arg . dup
2009-12-20 12:44:28 +03:00
when '--key' ; $api_key = arg . dup
2011-11-27 21:42:19 +04:00
when '--key-file'
begin
$api_key = File . read ( arg ) . strip
rescue Exception = > e
$stderr . puts " Unable to read the key from #{ arg } : #{ e . message } "
exit 1
end
2007-12-15 15:23:39 +03:00
when '--owner' ; $svn_owner = arg . dup ; $use_groupid = false ;
2009-02-16 22:33:43 +03:00
when '--group' ; $svn_group = arg . dup ; $use_groupid = false ;
2007-10-21 20:26:14 +04:00
when '--url' ; $svn_url = arg . dup
2008-09-17 23:18:31 +04:00
when '--scm' ; $scm = arg . dup . capitalize ; log ( " Invalid SCM: #{ $scm } " , :exit = > true ) unless SUPPORTED_SCM . include? ( $scm )
2008-09-13 20:31:11 +04:00
when '--command' ; $command = arg . dup
2007-10-14 21:27:04 +04:00
when '--verbose' ; $verbose += 1
2007-11-18 21:51:48 +03:00
when '--test' ; $test = true
2008-09-13 20:31:11 +04:00
when '--force' ; $force = true
2007-10-14 21:27:04 +04:00
when '--version' ; puts Version ; exit
when '--help' ; RDoc :: usage
when '--quiet' ; $quiet = true
end
end
rescue
exit 1
end
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? )
RDoc :: usage
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