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 @ 1576:d1de6986e429

History | View | Annotate | Download (6.09 KB)

1 217:ed8222a04634 chris
#!/usr/bin/env ruby
2
3
# == Synopsis
4
#
5 241:7658d21a1493 chris
# convert-external-repos: Update local Mercurial mirrors of external repos,
6
# by running an external command for each project requiring an update.
7 217:ed8222a04634 chris
#
8
# == Usage
9
#
10
#    convert-external-repos [OPTIONS...] -s [DIR] -r [HOST]
11
#
12
# == Arguments (mandatory)
13
#
14 241:7658d21a1493 chris
#   -s, --scm-dir=DIR         use DIR as base directory for repositories
15 217:ed8222a04634 chris
#   -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 241:7658d21a1493 chris
#   -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 217:ed8222a04634 chris
#
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 241:7658d21a1493 chris
                      ['--scm-dir',      '-s', GetoptLong::REQUIRED_ARGUMENT],
44 217:ed8222a04634 chris
                      ['--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 241:7658d21a1493 chris
                      ['--command' ,     '-c', GetoptLong::REQUIRED_ARGUMENT],
49 217:ed8222a04634 chris
                      ['--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 437:102056ec2de9 chris
$mirrordir    = '/var/mirror'
65
66 217:ed8222a04634 chris
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 241:7658d21a1493 chris
    when '--scm-dir';        $repos_base   = arg.dup
80 217:ed8222a04634 chris
    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 241:7658d21a1493 chris
    when '--command';        $command      = arg.dup
85 217:ed8222a04634 chris
    when '--verbose';        $verbose += 1
86
    when '--test';           $test = true
87
    when '--version';        puts Version; exit
88 1336:b61a51fb42b9 Chris
    when '--help';           puts "Read source for documentation"; exit
89 217:ed8222a04634 chris
    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 241:7658d21a1493 chris
if ($redmine_host.empty? or $repos_base.empty? or $command.empty?)
101 1336:b61a51fb42b9 Chris
  puts "Read source for documentation"; exit
102 217:ed8222a04634 chris
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 1336:b61a51fb42b9 Chris
  self.format = :xml
117 217:ed8222a04634 chris
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 1336:b61a51fb42b9 Chris
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 217:ed8222a04634 chris
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 1445:0c7b3bb73517 Chris
  elsif not project.identifier.match(/^[a-z0-9_\-]+$/)
150 217:ed8222a04634 chris
    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 241:7658d21a1493 chris
  unless File.directory?(repos_path)
170
    log("\tproject repo directory '#{repos_path}' doesn't exist")
171 217:ed8222a04634 chris
    next
172
  end
173
174 241:7658d21a1493 chris
  system($command, project.identifier, repos_path, external_url)
175 437:102056ec2de9 chris
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 217:ed8222a04634 chris
184
end