To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
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 |
|