comparison extra/soundsoftware/convert-external-repos.rb @ 524:1248a47e81b3 feature_36

Merge from branch "luisf"
author luisf <luis.figueira@eecs.qmul.ac.uk>
date Mon, 25 Jul 2011 14:39:38 +0100
parents 102056ec2de9
children b61a51fb42b9
comparison
equal deleted inserted replaced
519:3be6bc3c2a17 524:1248a47e81b3
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 'rdoc/usage'
38 require 'find'
39 require 'etc'
40
41 Version = "1.0"
42
43 opts = GetoptLong.new(
44 ['--scm-dir', '-s', GetoptLong::REQUIRED_ARGUMENT],
45 ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
46 ['--key', '-k', GetoptLong::REQUIRED_ARGUMENT],
47 ['--http-user', GetoptLong::REQUIRED_ARGUMENT],
48 ['--http-pass', GetoptLong::REQUIRED_ARGUMENT],
49 ['--command' , '-c', GetoptLong::REQUIRED_ARGUMENT],
50 ['--test', '-t', GetoptLong::NO_ARGUMENT],
51 ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
52 ['--version', '-V', GetoptLong::NO_ARGUMENT],
53 ['--help' , '-h', GetoptLong::NO_ARGUMENT],
54 ['--quiet' , '-q', GetoptLong::NO_ARGUMENT]
55 )
56
57 $verbose = 0
58 $quiet = false
59 $redmine_host = ''
60 $repos_base = ''
61 $http_user = ''
62 $http_pass = ''
63 $test = false
64
65 $mirrordir = '/var/mirror'
66
67 def log(text, options={})
68 level = options[:level] || 0
69 puts text unless $quiet or level > $verbose
70 exit 1 if options[:exit]
71 end
72
73 def system_or_raise(command)
74 raise "\"#{command}\" failed" unless system command
75 end
76
77 begin
78 opts.each do |opt, arg|
79 case opt
80 when '--scm-dir'; $repos_base = arg.dup
81 when '--redmine-host'; $redmine_host = arg.dup
82 when '--key'; $api_key = arg.dup
83 when '--http-user'; $http_user = arg.dup
84 when '--http-pass'; $http_pass = arg.dup
85 when '--command'; $command = arg.dup
86 when '--verbose'; $verbose += 1
87 when '--test'; $test = true
88 when '--version'; puts Version; exit
89 when '--help'; RDoc::usage
90 when '--quiet'; $quiet = true
91 end
92 end
93 rescue
94 exit 1
95 end
96
97 if $test
98 log("running in test mode")
99 end
100
101 if ($redmine_host.empty? or $repos_base.empty? or $command.empty?)
102 RDoc::usage
103 end
104
105 unless File.directory?($repos_base)
106 log("directory '#{$repos_base}' doesn't exist", :exit => true)
107 end
108
109 begin
110 require 'active_resource'
111 rescue LoadError
112 log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true)
113 end
114
115 class Project < ActiveResource::Base
116 self.headers["User-agent"] = "SoundSoftware external repository converter/#{Version}"
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 => e
132 log("Unable to connect to #{Project.site}: #{e}", :exit => true)
133 end
134
135 if projects.nil?
136 log('no project found, perhaps you forgot to "Enable WS for repository management"', :exit => true)
137 end
138
139 log("retrieved #{projects.size} projects", :level => 1)
140
141 projects.each do |project|
142 log("treating project #{project.name}", :level => 1)
143
144 if project.identifier.empty?
145 log("\tno identifier for project #{project.name}")
146 next
147 elsif not project.identifier.match(/^[a-z0-9\-]+$/)
148 log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
149 next
150 end
151
152 if !project.respond_to?(:repository) or !project.repository.is_external?
153 log("\tproject #{project.identifier} does not use an external repository");
154 next
155 end
156
157 external_url = project.repository.external_url;
158 log("\tproject #{project.identifier} has external repository url #{external_url}");
159
160 if !external_url.match(/^[a-z][a-z+]{0,8}[a-z]:\/\//)
161 log("\tthis doesn't look like a plausible url to me, skipping")
162 next
163 end
164
165 repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
166
167 unless File.directory?(repos_path)
168 log("\tproject repo directory '#{repos_path}' doesn't exist")
169 next
170 end
171
172 system($command, project.identifier, repos_path, external_url)
173
174 $cache_clearance_file = File.join($mirrordir, project.identifier, 'url_changed')
175 if File.file?($cache_clearance_file)
176 log("\tproject repo url has changed, requesting cache clearance")
177 if project.post(:repository_cache, :key => $api_key)
178 File.delete($cache_clearance_file)
179 end
180 end
181
182 end
183