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 @ 1554:e5c9809534a2
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
|