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 / soundsoftware / reposman-soundsoftware.rb @ 442:753f1380d6bc

History | View | Annotate | Download (11.6 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 28:12420e46bed9 chris
#                             if this option isn't set, reposman will register
44
#                             the repositories with local file paths in Redmine
45 0:513646585e45 Chris
#   -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
#   -t, --test                only show what should be done
54
#   -h, --help                show help and exit
55
#   -v, --verbose             verbose
56
#   -V, --version             print version and exit
57
#   -q, --quiet               no log
58
#
59
# == References
60 441:cbce1fd3b1b7 Chris
#
61 0:513646585e45 Chris
# You can find more information on the redmine's wiki : http://www.redmine.org/wiki/redmine/HowTos
62
63
64
require 'getoptlong'
65
require 'rdoc/usage'
66
require 'find'
67
require 'etc'
68
69
Version = "1.3"
70
SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem )
71
72
opts = GetoptLong.new(
73
                      ['--svn-dir',      '-s', GetoptLong::REQUIRED_ARGUMENT],
74
                      ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
75
                      ['--key',          '-k', GetoptLong::REQUIRED_ARGUMENT],
76
                      ['--owner',        '-o', GetoptLong::REQUIRED_ARGUMENT],
77
                      ['--group',        '-g', GetoptLong::REQUIRED_ARGUMENT],
78
                      ['--url',          '-u', GetoptLong::REQUIRED_ARGUMENT],
79
                      ['--command' ,     '-c', GetoptLong::REQUIRED_ARGUMENT],
80
                      ['--scm',                GetoptLong::REQUIRED_ARGUMENT],
81 13:80433603a2cd Chris
                      ['--http-user',          GetoptLong::REQUIRED_ARGUMENT],
82
                      ['--http-pass',          GetoptLong::REQUIRED_ARGUMENT],
83 0:513646585e45 Chris
                      ['--test',         '-t', GetoptLong::NO_ARGUMENT],
84
                      ['--verbose',      '-v', GetoptLong::NO_ARGUMENT],
85
                      ['--version',      '-V', GetoptLong::NO_ARGUMENT],
86
                      ['--help'   ,      '-h', GetoptLong::NO_ARGUMENT],
87
                      ['--quiet'  ,      '-q', GetoptLong::NO_ARGUMENT]
88
                      )
89
90
$verbose      = 0
91
$quiet        = false
92
$redmine_host = ''
93
$repos_base   = ''
94 13:80433603a2cd Chris
$http_user    = ''
95
$http_pass    = ''
96 0:513646585e45 Chris
$svn_owner    = 'root'
97
$svn_group    = 'root'
98
$use_groupid  = true
99
$svn_url      = false
100
$test         = false
101
$scm          = 'Subversion'
102
103
def log(text, options={})
104
  level = options[:level] || 0
105
  puts text unless $quiet or level > $verbose
106
  exit 1 if options[:exit]
107
end
108
109
def system_or_raise(command)
110
  raise "\"#{command}\" failed" unless system command
111
end
112
113
module SCM
114
115
  module Subversion
116
    def self.create(path)
117
      system_or_raise "svnadmin create #{path}"
118
    end
119
  end
120
121
  module Git
122
    def self.create(path)
123
      Dir.mkdir path
124
      Dir.chdir(path) do
125
        system_or_raise "git --bare init --shared"
126
        system_or_raise "git update-server-info"
127
      end
128
    end
129
  end
130
131
end
132
133
begin
134
  opts.each do |opt, arg|
135
    case opt
136
    when '--svn-dir';        $repos_base   = arg.dup
137
    when '--redmine-host';   $redmine_host = arg.dup
138
    when '--key';            $api_key      = arg.dup
139
    when '--owner';          $svn_owner    = arg.dup; $use_groupid = false;
140
    when '--group';          $svn_group    = arg.dup; $use_groupid = false;
141
    when '--url';            $svn_url      = arg.dup
142
    when '--scm';            $scm          = arg.dup.capitalize; log("Invalid SCM: #{$scm}", :exit => true) unless SUPPORTED_SCM.include?($scm)
143 13:80433603a2cd Chris
    when '--http-user';      $http_user    = arg.dup
144
    when '--http-pass';      $http_pass    = arg.dup
145 0:513646585e45 Chris
    when '--command';        $command =      arg.dup
146
    when '--verbose';        $verbose += 1
147
    when '--test';           $test = true
148
    when '--version';        puts Version; exit
149
    when '--help';           RDoc::usage
150
    when '--quiet';          $quiet = true
151
    end
152
  end
153
rescue
154
  exit 1
155
end
156
157
if $test
158
  log("running in test mode")
159
end
160
161
# Make sure command is overridden if SCM vendor is not handled internally (for the moment Subversion and Git)
162
if $command.nil?
163
  begin
164
    scm_module = SCM.const_get($scm)
165
  rescue
166
    log("Please use --command option to specify how to create a #{$scm} repository.", :exit => true)
167
  end
168
end
169
170
$svn_url += "/" if $svn_url and not $svn_url.match(/\/$/)
171
172
if ($redmine_host.empty? or $repos_base.empty?)
173
  RDoc::usage
174
end
175
176
unless File.directory?($repos_base)
177
  log("directory '#{$repos_base}' doesn't exists", :exit => true)
178
end
179
180
begin
181
  require 'active_resource'
182
rescue LoadError
183
  log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true)
184
end
185
186 37:94944d00e43c chris
class Project < ActiveResource::Base
187
  self.headers["User-agent"] = "Redmine repository manager/#{Version}"
188
end
189 0:513646585e45 Chris
190
log("querying Redmine for projects...", :level => 1);
191
192
$redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
193
$redmine_host.gsub!(/\/$/, '')
194
195
Project.site = "#{$redmine_host}/sys";
196 13:80433603a2cd Chris
Project.user = $http_user;
197
Project.password = $http_pass;
198 0:513646585e45 Chris
199
begin
200
  # Get all active projects that have the Repository module enabled
201
  projects = Project.find(:all, :params => {:key => $api_key})
202
rescue => e
203
  log("Unable to connect to #{Project.site}: #{e}", :exit => true)
204
end
205
206
if projects.nil?
207
  log('no project found, perhaps you forgot to "Enable WS for repository management"', :exit => true)
208
end
209
210
log("retrieved #{projects.size} projects", :level => 1)
211
212
def set_owner_and_rights(project, repos_path, &block)
213 441:cbce1fd3b1b7 Chris
  if mswin?
214 0:513646585e45 Chris
    yield if block_given?
215
  else
216
    uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project.identifier).gid : Etc.getgrnam($svn_group).gid)
217 34:09b1d4349da3 Chris
    right = project.is_public ? 02775 : 02770
218 0:513646585e45 Chris
    yield if block_given?
219
    Find.find(repos_path) do |f|
220
      File.chmod right, f
221
      File.chown uid, gid, f
222
    end
223
  end
224
end
225
226
def other_read_right?(file)
227
  (File.stat(file).mode & 0007).zero? ? false : true
228
end
229
230
def owner_name(file)
231
  mswin? ?
232
    $svn_owner :
233 441:cbce1fd3b1b7 Chris
    Etc.getpwuid( File.stat(file).uid ).name
234 0:513646585e45 Chris
end
235 441:cbce1fd3b1b7 Chris
236 0:513646585e45 Chris
def mswin?
237
  (RUBY_PLATFORM =~ /(:?mswin|mingw)/) || (RUBY_PLATFORM == 'java' && (ENV['OS'] || ENV['os']) =~ /windows/i)
238
end
239
240
projects.each do |project|
241
  log("treating project #{project.name}", :level => 1)
242
243
  if project.identifier.empty?
244
    log("\tno identifier for project #{project.name}")
245
    next
246
  elsif not project.identifier.match(/^[a-z0-9\-]+$/)
247
    log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
248
    next;
249
  end
250
251
  repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
252
253 28:12420e46bed9 chris
  create_repos = false
254
  # Logic required for SoundSoftware.ac.uk repositories:
255
  #
256
  # * If the project has a repository path declared already,
257
  #   - if it's a local path,
258
  #     - if it does not exist
259
  #       - if it has the right root
260
  #         - create it
261
  #   - else
262
  #     - leave alone (remote repository)
263
  # * else
264
  #   - create repository with same name as project
265
  #   - set to project
266
267
  if project.respond_to?(:repository)
268
269
    repos_url = project.repository.url;
270
    log("\texisting url for project #{project.identifier} is #{repos_url}");
271
272
    if repos_url.match(/^file:\//) || repos_url.match(/^\//)
273
274
      repos_url = repos_url.gsub(/^file:\/*/, "/");
275
      log("\tthis is a local file path, at #{repos_url}");
276
277
      if repos_url.slice(0, $repos_base.length) != $repos_base
278
        log("\tit is in the wrong place: replacing it");
279
        # leave repos_path set to our original suggestion
280
        create_repos = true
281
      else
282
        if !File.directory?(repos_url)
283
          log("\tit doesn't exist; we should create it");
284
          repos_path = repos_url
285
          create_repos = true
286
        else
287
          log("\tit exists and is in the right place");
288
        end
289
      end
290
    else
291
      log("\tthis is a remote path, leaving alone");
292
    end
293
  else
294
    log("\tproject #{project.identifier} has no repository registered")
295
#    if File.directory?(repos_path)
296
#      log("\trepository path #{repos_path} already exists, not creating")
297
#    else
298
      create_repos = true
299
#    end
300
  end
301
302
  if create_repos
303
304
    registration_url = repos_path
305
    if $svn_url
306
      registration_url = "#{$svn_url}#{project.identifier}"
307
    end
308 0:513646585e45 Chris
309
    if $test
310 28:12420e46bed9 chris
      log("\tproposal: create repository #{repos_path}")
311
      log("\tproposal: register repository #{repos_path} in Redmine with vendor #{$scm}, url #{registration_url}")
312 0:513646585e45 Chris
      next
313
    end
314
315 52:8c3409528d3a Chris
# No -- we need "other" users to be able to read it.  Access control
316
# is not handled through Unix user id anyway
317
#    project.is_public ? File.umask(0002) : File.umask(0007)
318
    File.umask(0002)
319
320 28:12420e46bed9 chris
    log("\taction: create repository #{repos_path}")
321 0:513646585e45 Chris
322
    begin
323 28:12420e46bed9 chris
      if !File.directory?(repos_path)
324
        set_owner_and_rights(project, repos_path) do
325
          if scm_module.nil?
326
            log("\trunning command: #{$command} #{repos_path}")
327
            system_or_raise "#{$command} #{repos_path}"
328
          else
329
            scm_module.create(repos_path)
330
          end
331 0:513646585e45 Chris
        end
332
      end
333
    rescue => e
334
      log("\tunable to create #{repos_path} : #{e}\n")
335
      next
336
    end
337
338 28:12420e46bed9 chris
    begin
339
      log("\taction: register repository #{repos_path} in Redmine with vendor #{$scm}, url #{registration_url}");
340
      project.post(:repository, :vendor => $scm, :repository => {:url => "#{registration_url}"}, :key => $api_key)
341
    rescue => e
342
      log("\trepository #{repos_path} not registered in Redmine: #{e.message}");
343 0:513646585e45 Chris
    end
344
    log("\trepository #{repos_path} created");
345
  end
346 37:94944d00e43c chris
end