To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / extra / svn / reposman.rb @ 442:753f1380d6bc

History | View | Annotate | Download (10.8 KB)

1 0:513646585e45 Chris
#!/usr/bin/env ruby
2
3
# == Synopsis
4
#
5
# reposman: manages your repositories with Redmine
6
#
7
# == Usage
8
#
9
#    reposman [OPTIONS...] -s [DIR] -r [HOST]
10 441:cbce1fd3b1b7 Chris
#
11 0:513646585e45 Chris
#  Examples:
12
#    reposman --svn-dir=/var/svn --redmine-host=redmine.example.net --scm subversion
13
#    reposman -s /var/git -r redmine.example.net -u http://svn.example.net --scm git
14
#
15
# == Arguments (mandatory)
16
#
17
#   -s, --svn-dir=DIR         use DIR as base directory for svn repositories
18
#   -r, --redmine-host=HOST   assume Redmine is hosted on HOST. Examples:
19
#                             -r redmine.example.net
20
#                             -r http://redmine.example.net
21
#                             -r https://example.net/redmine
22
#   -k, --key=KEY             use KEY as the Redmine API key
23
#
24
# == Options
25
#
26
#   -o, --owner=OWNER         owner of the repository. using the rails login
27
#                             allow user to browse the repository within
28
#                             Redmine even for private project. If you want to
29
#                             share repositories through Redmine.pm, you need
30
#                             to use the apache owner.
31
#   -g, --group=GROUP         group of the repository. (default: root)
32
#   --scm=SCM                 the kind of SCM repository you want to create (and
33
#                             register) in Redmine (default: Subversion).
34
#                             reposman is able to create Git and Subversion
35
#                             repositories. For all other kind, you must specify
36
#                             a --command option
37
#   -u, --url=URL             the base url Redmine will use to access your
38
#                             repositories. This option is used to automatically
39
#                             register the repositories in Redmine. The project
40
#                             identifier will be appended to this url. Examples:
41
#                             -u https://example.net/svn
42
#                             -u file:///var/svn/
43
#                             if this option isn't set, reposman won't register
44
#                             the repositories in Redmine
45
#   -c, --command=COMMAND     use this command instead of "svnadmin create" to
46
#                             create a repository. This option can be used to
47
#                             create repositories other than subversion and git
48
#                             kind.
49
#                             This command override the default creation for git
50
#                             and subversion.
51 13:80433603a2cd Chris
#   --http-user=USER          User for HTTP Basic authentication with Redmine WS
52
#   --http-pass=PASSWORD      Password for Basic authentication with Redmine WS
53 0:513646585e45 Chris
#   -f, --force               force repository creation even if the project
54
#                             repository is already declared in Redmine
55
#   -t, --test                only show what should be done
56
#   -h, --help                show help and exit
57
#   -v, --verbose             verbose
58
#   -V, --version             print version and exit
59
#   -q, --quiet               no log
60
#
61
# == References
62 441:cbce1fd3b1b7 Chris
#
63 0:513646585e45 Chris
# You can find more information on the redmine's wiki : http://www.redmine.org/wiki/redmine/HowTos
64
65
66
require 'getoptlong'
67
require 'rdoc/usage'
68
require 'find'
69
require 'etc'
70
71
Version = "1.3"
72
SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem )
73
74
opts = GetoptLong.new(
75
                      ['--svn-dir',      '-s', GetoptLong::REQUIRED_ARGUMENT],
76
                      ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
77
                      ['--key',          '-k', GetoptLong::REQUIRED_ARGUMENT],
78
                      ['--owner',        '-o', GetoptLong::REQUIRED_ARGUMENT],
79
                      ['--group',        '-g', GetoptLong::REQUIRED_ARGUMENT],
80
                      ['--url',          '-u', GetoptLong::REQUIRED_ARGUMENT],
81
                      ['--command' ,     '-c', GetoptLong::REQUIRED_ARGUMENT],
82
                      ['--scm',                GetoptLong::REQUIRED_ARGUMENT],
83 13:80433603a2cd Chris
                      ['--http-user',          GetoptLong::REQUIRED_ARGUMENT],
84
                      ['--http-pass',          GetoptLong::REQUIRED_ARGUMENT],
85 0:513646585e45 Chris
                      ['--test',         '-t', GetoptLong::NO_ARGUMENT],
86
                      ['--force',        '-f', GetoptLong::NO_ARGUMENT],
87
                      ['--verbose',      '-v', GetoptLong::NO_ARGUMENT],
88
                      ['--version',      '-V', GetoptLong::NO_ARGUMENT],
89
                      ['--help'   ,      '-h', GetoptLong::NO_ARGUMENT],
90
                      ['--quiet'  ,      '-q', GetoptLong::NO_ARGUMENT]
91
                      )
92
93
$verbose      = 0
94
$quiet        = false
95
$redmine_host = ''
96
$repos_base   = ''
97 13:80433603a2cd Chris
$http_user    = ''
98
$http_pass    = ''
99 0:513646585e45 Chris
$svn_owner    = 'root'
100
$svn_group    = 'root'
101
$use_groupid  = true
102
$svn_url      = false
103
$test         = false
104
$force        = false
105
$scm          = 'Subversion'
106
107
def log(text, options={})
108
  level = options[:level] || 0
109
  puts text unless $quiet or level > $verbose
110
  exit 1 if options[:exit]
111
end
112
113
def system_or_raise(command)
114
  raise "\"#{command}\" failed" unless system command
115
end
116
117
module SCM
118
119
  module Subversion
120
    def self.create(path)
121
      system_or_raise "svnadmin create #{path}"
122
    end
123
  end
124
125
  module Git
126
    def self.create(path)
127
      Dir.mkdir path
128
      Dir.chdir(path) do
129
        system_or_raise "git --bare init --shared"
130
        system_or_raise "git update-server-info"
131
      end
132
    end
133
  end
134
135
end
136
137
begin
138
  opts.each do |opt, arg|
139
    case opt
140
    when '--svn-dir';        $repos_base   = arg.dup
141
    when '--redmine-host';   $redmine_host = arg.dup
142
    when '--key';            $api_key      = arg.dup
143
    when '--owner';          $svn_owner    = arg.dup; $use_groupid = false;
144
    when '--group';          $svn_group    = arg.dup; $use_groupid = false;
145
    when '--url';            $svn_url      = arg.dup
146
    when '--scm';            $scm          = arg.dup.capitalize; log("Invalid SCM: #{$scm}", :exit => true) unless SUPPORTED_SCM.include?($scm)
147 13:80433603a2cd Chris
    when '--http-user';      $http_user    = arg.dup
148
    when '--http-pass';      $http_pass    = arg.dup
149 0:513646585e45 Chris
    when '--command';        $command =      arg.dup
150
    when '--verbose';        $verbose += 1
151
    when '--test';           $test = true
152
    when '--force';          $force = true
153
    when '--version';        puts Version; exit
154
    when '--help';           RDoc::usage
155
    when '--quiet';          $quiet = true
156
    end
157
  end
158
rescue
159
  exit 1
160
end
161
162
if $test
163
  log("running in test mode")
164
end
165
166
# Make sure command is overridden if SCM vendor is not handled internally (for the moment Subversion and Git)
167
if $command.nil?
168
  begin
169
    scm_module = SCM.const_get($scm)
170
  rescue
171
    log("Please use --command option to specify how to create a #{$scm} repository.", :exit => true)
172
  end
173
end
174
175
$svn_url += "/" if $svn_url and not $svn_url.match(/\/$/)
176
177
if ($redmine_host.empty? or $repos_base.empty?)
178
  RDoc::usage
179
end
180
181
unless File.directory?($repos_base)
182
  log("directory '#{$repos_base}' doesn't exists", :exit => true)
183
end
184
185
begin
186
  require 'active_resource'
187
rescue LoadError
188
  log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true)
189
end
190
191 37:94944d00e43c chris
class Project < ActiveResource::Base
192
  self.headers["User-agent"] = "Redmine repository manager/#{Version}"
193
end
194 0:513646585e45 Chris
195
log("querying Redmine for projects...", :level => 1);
196
197
$redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
198
$redmine_host.gsub!(/\/$/, '')
199
200
Project.site = "#{$redmine_host}/sys";
201 13:80433603a2cd Chris
Project.user = $http_user;
202
Project.password = $http_pass;
203 0:513646585e45 Chris
204
begin
205
  # Get all active projects that have the Repository module enabled
206
  projects = Project.find(:all, :params => {:key => $api_key})
207
rescue => e
208
  log("Unable to connect to #{Project.site}: #{e}", :exit => true)
209
end
210
211
if projects.nil?
212
  log('no project found, perhaps you forgot to "Enable WS for repository management"', :exit => true)
213
end
214
215
log("retrieved #{projects.size} projects", :level => 1)
216
217
def set_owner_and_rights(project, repos_path, &block)
218 441:cbce1fd3b1b7 Chris
  if mswin?
219 0:513646585e45 Chris
    yield if block_given?
220
  else
221
    uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project.identifier).gid : Etc.getgrnam($svn_group).gid)
222
    right = project.is_public ? 0775 : 0770
223
    yield if block_given?
224
    Find.find(repos_path) do |f|
225
      File.chmod right, f
226
      File.chown uid, gid, f
227
    end
228
  end
229
end
230
231
def other_read_right?(file)
232
  (File.stat(file).mode & 0007).zero? ? false : true
233
end
234
235
def owner_name(file)
236
  mswin? ?
237
    $svn_owner :
238 441:cbce1fd3b1b7 Chris
    Etc.getpwuid( File.stat(file).uid ).name
239 0:513646585e45 Chris
end
240 441:cbce1fd3b1b7 Chris
241 0:513646585e45 Chris
def mswin?
242
  (RUBY_PLATFORM =~ /(:?mswin|mingw)/) || (RUBY_PLATFORM == 'java' && (ENV['OS'] || ENV['os']) =~ /windows/i)
243
end
244
245
projects.each do |project|
246
  log("treating project #{project.name}", :level => 1)
247
248
  if project.identifier.empty?
249
    log("\tno identifier for project #{project.name}")
250
    next
251
  elsif not project.identifier.match(/^[a-z0-9\-]+$/)
252
    log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
253
    next;
254
  end
255
256
  repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
257
258
  if File.directory?(repos_path)
259
    # we must verify that repository has the good owner and the good
260
    # rights before leaving
261
    other_read = other_read_right?(repos_path)
262
    owner      = owner_name(repos_path)
263
    next if project.is_public == other_read and owner == $svn_owner
264
265
    if $test
266
      log("\tchange mode on #{repos_path}")
267
      next
268
    end
269
270
    begin
271
      set_owner_and_rights(project, repos_path)
272
    rescue Errno::EPERM => e
273
      log("\tunable to change mode on #{repos_path} : #{e}\n")
274
      next
275
    end
276
277
    log("\tmode change on #{repos_path}");
278
279
  else
280
    # if repository is already declared in redmine, we don't create
281
    # unless user use -f with reposman
282
    if $force == false and project.respond_to?(:repository)
283
      log("\trepository for project #{project.identifier} already exists in Redmine", :level => 1)
284
      next
285
    end
286
287
    project.is_public ? File.umask(0002) : File.umask(0007)
288
289
    if $test
290
      log("\tcreate repository #{repos_path}")
291
      log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}") if $svn_url;
292
      next
293
    end
294
295
    begin
296
      set_owner_and_rights(project, repos_path) do
297
        if scm_module.nil?
298
          system_or_raise "#{$command} #{repos_path}"
299
        else
300
          scm_module.create(repos_path)
301
        end
302
      end
303
    rescue => e
304
      log("\tunable to create #{repos_path} : #{e}\n")
305
      next
306
    end
307
308
    if $svn_url
309
      begin
310
        project.post(:repository, :vendor => $scm, :repository => {:url => "#{$svn_url}#{project.identifier}"}, :key => $api_key)
311
        log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}");
312
      rescue => e
313
        log("\trepository #{repos_path} not registered in Redmine: #{e.message}");
314
      end
315
    end
316
    log("\trepository #{repos_path} created");
317
  end
318
end