annotate extra/soundsoftware/convert-external-repos.rb @ 217:ed8222a04634 feature_73

* Start on external repo conversion script
author Chris Cannam <chris.cannam@soundsoftware.ac.uk>
date Fri, 11 Feb 2011 15:32:41 +0000
parents
children 7658d21a1493
rev   line source
chris@217 1 #!/usr/bin/env ruby
chris@217 2
chris@217 3 # == Synopsis
chris@217 4 #
chris@217 5 # convert-external-repos: Update local Mercurial mirrors of external repos
chris@217 6 #
chris@217 7 # This command is specific to the SoundSoftware Redmine instance and
chris@217 8 # its use of Mercurial repositories. It makes use of the hg convert extension.
chris@217 9 #
chris@217 10 # == Usage
chris@217 11 #
chris@217 12 # convert-external-repos [OPTIONS...] -s [DIR] -r [HOST]
chris@217 13 #
chris@217 14 # == Arguments (mandatory)
chris@217 15 #
chris@217 16 # -h, --hg-dir=DIR use DIR as base directory for repositories
chris@217 17 # -r, --redmine-host=HOST assume Redmine is hosted on HOST. Examples:
chris@217 18 # -r redmine.example.net
chris@217 19 # -r http://redmine.example.net
chris@217 20 # -r https://example.net/redmine
chris@217 21 # -k, --key=KEY use KEY as the Redmine API key
chris@217 22 #
chris@217 23 # == Options
chris@217 24 #
chris@217 25 # --http-user=USER User for HTTP Basic authentication with Redmine WS
chris@217 26 # --http-pass=PASSWORD Password for Basic authentication with Redmine WS
chris@217 27 # -t, --test only show what should be done
chris@217 28 # -h, --help show help and exit
chris@217 29 # -v, --verbose verbose
chris@217 30 # -V, --version print version and exit
chris@217 31 # -q, --quiet no log
chris@217 32
chris@217 33
chris@217 34 require 'getoptlong'
chris@217 35 require 'rdoc/usage'
chris@217 36 require 'find'
chris@217 37 require 'etc'
chris@217 38
chris@217 39 Version = "1.0"
chris@217 40
chris@217 41 opts = GetoptLong.new(
chris@217 42 ['--hg-dir', '-h', GetoptLong::REQUIRED_ARGUMENT],
chris@217 43 ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
chris@217 44 ['--key', '-k', GetoptLong::REQUIRED_ARGUMENT],
chris@217 45 ['--http-user', GetoptLong::REQUIRED_ARGUMENT],
chris@217 46 ['--http-pass', GetoptLong::REQUIRED_ARGUMENT],
chris@217 47 ['--test', '-t', GetoptLong::NO_ARGUMENT],
chris@217 48 ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
chris@217 49 ['--version', '-V', GetoptLong::NO_ARGUMENT],
chris@217 50 ['--help' , '-h', GetoptLong::NO_ARGUMENT],
chris@217 51 ['--quiet' , '-q', GetoptLong::NO_ARGUMENT]
chris@217 52 )
chris@217 53
chris@217 54 $verbose = 0
chris@217 55 $quiet = false
chris@217 56 $redmine_host = ''
chris@217 57 $repos_base = ''
chris@217 58 $http_user = ''
chris@217 59 $http_pass = ''
chris@217 60 $test = false
chris@217 61
chris@217 62 def log(text, options={})
chris@217 63 level = options[:level] || 0
chris@217 64 puts text unless $quiet or level > $verbose
chris@217 65 exit 1 if options[:exit]
chris@217 66 end
chris@217 67
chris@217 68 def system_or_raise(command)
chris@217 69 raise "\"#{command}\" failed" unless system command
chris@217 70 end
chris@217 71
chris@217 72 begin
chris@217 73 opts.each do |opt, arg|
chris@217 74 case opt
chris@217 75 when '--hg-dir'; $repos_base = arg.dup
chris@217 76 when '--redmine-host'; $redmine_host = arg.dup
chris@217 77 when '--key'; $api_key = arg.dup
chris@217 78 when '--http-user'; $http_user = arg.dup
chris@217 79 when '--http-pass'; $http_pass = arg.dup
chris@217 80 when '--verbose'; $verbose += 1
chris@217 81 when '--test'; $test = true
chris@217 82 when '--version'; puts Version; exit
chris@217 83 when '--help'; RDoc::usage
chris@217 84 when '--quiet'; $quiet = true
chris@217 85 end
chris@217 86 end
chris@217 87 rescue
chris@217 88 exit 1
chris@217 89 end
chris@217 90
chris@217 91 if $test
chris@217 92 log("running in test mode")
chris@217 93 end
chris@217 94
chris@217 95 if ($redmine_host.empty? or $repos_base.empty?)
chris@217 96 RDoc::usage
chris@217 97 end
chris@217 98
chris@217 99 unless File.directory?($repos_base)
chris@217 100 log("directory '#{$repos_base}' doesn't exist", :exit => true)
chris@217 101 end
chris@217 102
chris@217 103 begin
chris@217 104 require 'active_resource'
chris@217 105 rescue LoadError
chris@217 106 log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true)
chris@217 107 end
chris@217 108
chris@217 109 class Project < ActiveResource::Base
chris@217 110 self.headers["User-agent"] = "SoundSoftware external repository converter/#{Version}"
chris@217 111 end
chris@217 112
chris@217 113 log("querying Redmine for projects...", :level => 1);
chris@217 114
chris@217 115 $redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
chris@217 116 $redmine_host.gsub!(/\/$/, '')
chris@217 117
chris@217 118 Project.site = "#{$redmine_host}/sys";
chris@217 119 Project.user = $http_user;
chris@217 120 Project.password = $http_pass;
chris@217 121
chris@217 122 begin
chris@217 123 # Get all active projects that have the Repository module enabled
chris@217 124 projects = Project.find(:all, :params => {:key => $api_key})
chris@217 125 rescue => e
chris@217 126 log("Unable to connect to #{Project.site}: #{e}", :exit => true)
chris@217 127 end
chris@217 128
chris@217 129 if projects.nil?
chris@217 130 log('no project found, perhaps you forgot to "Enable WS for repository management"', :exit => true)
chris@217 131 end
chris@217 132
chris@217 133 log("retrieved #{projects.size} projects", :level => 1)
chris@217 134
chris@217 135 projects.each do |project|
chris@217 136 log("treating project #{project.name}", :level => 1)
chris@217 137
chris@217 138 if project.identifier.empty?
chris@217 139 log("\tno identifier for project #{project.name}")
chris@217 140 next
chris@217 141 elsif not project.identifier.match(/^[a-z0-9\-]+$/)
chris@217 142 log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
chris@217 143 next
chris@217 144 end
chris@217 145
chris@217 146 if !project.respond_to?(:repository) or !project.repository.is_external?
chris@217 147 log("\tproject #{project.identifier} does not use an external repository");
chris@217 148 next
chris@217 149 end
chris@217 150
chris@217 151 external_url = project.repository.external_url;
chris@217 152 log("\tproject #{project.identifier} has external repository url #{external_url}");
chris@217 153
chris@217 154 if !external_url.match(/^[a-z][a-z+]{0,8}[a-z]:\/\//)
chris@217 155 log("\tthis doesn't look like a plausible url to me, skipping")
chris@217 156 next
chris@217 157 end
chris@217 158
chris@217 159 repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
chris@217 160
chris@217 161 unless File.directory?($repos_path)
chris@217 162 log("project repo directory '#{$repos_path}' doesn't exist")
chris@217 163 next
chris@217 164 end
chris@217 165
chris@217 166 # We need to handle different source repository types separately.
chris@217 167 #
chris@217 168 # The convert extension cannot convert directly from a remote git
chris@217 169 # repo; we have to mirror to a local repo first. Incremental
chris@217 170 # conversions do work though.
chris@217 171 #
chris@217 172 # We can of course convert directly from remote Subversion repos,
chris@217 173 # but we need to keep track of that -- you can ask to convert into a
chris@217 174 # repo that has already been used (for Mercurial) and it'll do so
chris@217 175 # happily; we don't want that.
chris@217 176 #
chris@217 177 # Converting from a remote Hg repo should be fine!
chris@217 178 #
chris@217 179
chris@217 180
chris@217 181
chris@217 182 end
chris@217 183