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 / convert-external-repos.rb @ 1567:3ad53f43483d

History | View | Annotate | Download (6.09 KB)

1
#!/usr/bin/env ruby
2

    
3
# == Synopsis
4
#
5
# convert-external-repos: Update local Mercurial mirrors of external repos,
6
# by running an external command for each project requiring an update.
7
#
8
# == Usage
9
#
10
#    convert-external-repos [OPTIONS...] -s [DIR] -r [HOST]
11
#     
12
# == Arguments (mandatory)
13
#
14
#   -s, --scm-dir=DIR         use DIR as base directory for repositories
15
#   -r, --redmine-host=HOST   assume Redmine is hosted on HOST. Examples:
16
#                             -r redmine.example.net
17
#                             -r http://redmine.example.net
18
#                             -r https://example.net/redmine
19
#   -k, --key=KEY             use KEY as the Redmine API key
20
#   -c, --command=COMMAND     use this command to update each external
21
#                             repository: command is called with the name
22
#                             of the project, the path to its repo, and
23
#                             its external repo url as its three args
24
#
25
# == Options
26
#
27
#   --http-user=USER          User for HTTP Basic authentication with Redmine WS
28
#   --http-pass=PASSWORD      Password for Basic authentication with Redmine WS
29
#   -t, --test                only show what should be done
30
#   -h, --help                show help and exit
31
#   -v, --verbose             verbose
32
#   -V, --version             print version and exit
33
#   -q, --quiet               no log
34

    
35

    
36
require 'getoptlong'
37
require 'find'
38
require 'etc'
39

    
40
Version = "1.0"
41

    
42
opts = GetoptLong.new(
43
                      ['--scm-dir',      '-s', GetoptLong::REQUIRED_ARGUMENT],
44
                      ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
45
                      ['--key',          '-k', GetoptLong::REQUIRED_ARGUMENT],
46
                      ['--http-user',          GetoptLong::REQUIRED_ARGUMENT],
47
                      ['--http-pass',          GetoptLong::REQUIRED_ARGUMENT],
48
                      ['--command' ,     '-c', GetoptLong::REQUIRED_ARGUMENT],
49
                      ['--test',         '-t', GetoptLong::NO_ARGUMENT],
50
                      ['--verbose',      '-v', GetoptLong::NO_ARGUMENT],
51
                      ['--version',      '-V', GetoptLong::NO_ARGUMENT],
52
                      ['--help'   ,      '-h', GetoptLong::NO_ARGUMENT],
53
                      ['--quiet'  ,      '-q', GetoptLong::NO_ARGUMENT]
54
                      )
55

    
56
$verbose      = 0
57
$quiet        = false
58
$redmine_host = ''
59
$repos_base   = ''
60
$http_user    = ''
61
$http_pass    = ''
62
$test         = false
63

    
64
$mirrordir    = '/var/mirror'
65

    
66
def log(text, options={})
67
  level = options[:level] || 0
68
  puts text unless $quiet or level > $verbose
69
  exit 1 if options[:exit]
70
end
71

    
72
def system_or_raise(command)
73
  raise "\"#{command}\" failed" unless system command
74
end
75

    
76
begin
77
  opts.each do |opt, arg|
78
    case opt
79
    when '--scm-dir';        $repos_base   = arg.dup
80
    when '--redmine-host';   $redmine_host = arg.dup
81
    when '--key';            $api_key      = arg.dup
82
    when '--http-user';      $http_user    = arg.dup
83
    when '--http-pass';      $http_pass    = arg.dup
84
    when '--command';        $command      = arg.dup
85
    when '--verbose';        $verbose += 1
86
    when '--test';           $test = true
87
    when '--version';        puts Version; exit
88
    when '--help';           puts "Read source for documentation"; exit 
89
    when '--quiet';          $quiet = true
90
    end
91
  end
92
rescue
93
  exit 1
94
end
95

    
96
if $test
97
  log("running in test mode")
98
end
99

    
100
if ($redmine_host.empty? or $repos_base.empty? or $command.empty?)
101
  puts "Read source for documentation"; exit
102
end
103

    
104
unless File.directory?($repos_base)
105
  log("directory '#{$repos_base}' doesn't exist", :exit => true)
106
end
107

    
108
begin
109
  require 'active_resource'
110
rescue LoadError
111
  log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true)
112
end
113

    
114
class Project < ActiveResource::Base
115
  self.headers["User-agent"] = "SoundSoftware external repository converter/#{Version}"
116
  self.format = :xml
117
end
118

    
119
log("querying Redmine for projects...", :level => 1);
120

    
121
$redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
122
$redmine_host.gsub!(/\/$/, '')
123

    
124
Project.site = "#{$redmine_host}/sys";
125
Project.user = $http_user;
126
Project.password = $http_pass;
127

    
128
begin
129
  # Get all active projects that have the Repository module enabled
130
  projects = Project.find(:all, :params => {:key => $api_key})
131
rescue ActiveResource::ForbiddenAccess
132
  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.")
133
rescue => e
134
  log("Unable to connect to #{Project.site}: #{e}", :exit => true)
135
end
136

    
137
if projects.nil?
138
  log('no project found, perhaps you forgot to "Enable WS for repository management"', :exit => true)
139
end
140

    
141
log("retrieved #{projects.size} projects", :level => 1)
142

    
143
projects.each do |project|
144
  log("treating project #{project.name}", :level => 1)
145

    
146
  if project.identifier.empty?
147
    log("\tno identifier for project #{project.name}")
148
    next
149
  elsif not project.identifier.match(/^[a-z0-9_\-]+$/)
150
    log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
151
    next
152
  end
153

    
154
  if !project.respond_to?(:repository) or !project.repository.is_external?
155
    log("\tproject #{project.identifier} does not use an external repository");
156
    next
157
  end
158

    
159
  external_url = project.repository.external_url;
160
  log("\tproject #{project.identifier} has external repository url #{external_url}");
161

    
162
  if !external_url.match(/^[a-z][a-z+]{0,8}[a-z]:\/\//)
163
    log("\tthis doesn't look like a plausible url to me, skipping")
164
    next
165
  end
166

    
167
  repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
168

    
169
  unless File.directory?(repos_path)
170
    log("\tproject repo directory '#{repos_path}' doesn't exist")
171
    next
172
  end
173

    
174
  system($command, project.identifier, repos_path, external_url)
175
  
176
  $cache_clearance_file = File.join($mirrordir, project.identifier, 'url_changed')
177
  if File.file?($cache_clearance_file)
178
    log("\tproject repo url has changed, requesting cache clearance")
179
    if project.post(:repository_cache, :key => $api_key)
180
      File.delete($cache_clearance_file)
181
    end
182
  end
183

    
184
end
185