##// END OF EJS Templates
Adds --scm option support to reposman. It can be used to register non subversion repositories in Redmine....
Jean-Philippe Lang -
r1807:0b74888fb8c6
parent child
Show More
@@ -1,264 +1,255
1 #!/usr/bin/ruby
1 #!/usr/bin/ruby
2
2
3 # == Synopsis
3 # == Synopsis
4 #
4 #
5 # reposman: manages your svn repositories with Redmine
5 # reposman: manages your svn repositories with Redmine
6 #
6 #
7 # == Usage
7 # == Usage
8 #
8 #
9 # reposman [ -h | --help ] [ -v | --verbose ] [ -V | --version ] [ -q | --quiet ] -s /var/svn -r redmine.host.org
9 # reposman [OPTIONS...] -s [DIR] -r [HOST]
10 # example: reposman --svn-dir=/var/svn --redmine-host=redmine.mydomain.foo
10 #
11 # reposman -s /var/svn -r redmine.mydomain.foo
11 # Examples:
12 # reposman --svn-dir=/var/svn --redmine-host=redmine.example.net
13 # reposman -s /var/svn -r redmine.example.net -u http://svn.example.net
12 #
14 #
13 # == Arguments (mandatory)
15 # == Arguments (mandatory)
14 #
15 # -s, --svn-dir=DIR
16 # use DIR as base directory for svn repositories
17 #
16 #
18 # -r, --redmine-host=HOST
17 # -s, --svn-dir=DIR use DIR as base directory for svn repositories
19 # assume Redmine is hosted on HOST.
18 # -r, --redmine-host=HOST assume Redmine is hosted on HOST. Examples:
20 # you can use :
19 # -r redmine.example.net
21 # * -r redmine.mydomain.foo (will add http://)
20 # -r http://redmine.example.net
22 # * -r http://redmine.mydomain.foo
21 # -r https://example.net/redmine
23 # * -r https://mydomain.foo/redmine
24 #
22 #
25 # == Options
23 # == Options
26 #
24 #
27 # -o, --owner=OWNER
25 # -o, --owner=OWNER owner of the repository. using the rails login
28 # owner of the repository. using the rails login allow user to browse
26 # allow user to browse the repository within
29 # the repository in Redmine even for private project
27 # Redmine even for private project
30 #
28 # -u, --url=URL the base url Redmine will use to access your
31 # -u, --url=URL
29 # repositories. This option is used to automatically
32 # the base url Redmine will use to access your repositories. This
30 # register the repositories in Redmine. The project
33 # will be used to register the repository in Redmine so that user
31 # identifier will be appended to this url. Examples:
34 # doesn't need to do anything. reposman will add the identifier to this url :
32 # -u https://example.net/svn
35 #
33 # -u file:///var/svn/
36 # -u https://my.svn.server/my/reposity/root # if the repository can be access by http
34 # if this option isn't set, reposman won't register
37 # -u file:///var/svn/ # if the repository is local
35 # the repositories in Redmine
38 # if this option isn't set, reposman won't register the repository
36 # -c, --command=COMMAND use this command instead of "svnadmin create" to
39 #
37 # create a repository. This option can be used to
40 # --scm
38 # create non-subversion repositories
41 # SCM vendor used to register the repository in Redmine (default: Subversion)
39 # --scm SCM vendor used to register the repository in
42 # Can be one of the other supported SCM: Bazaar, Cvs, Darcs, Filesystem, Git, Mercurial (case sensitive).
40 # Redmine (default: Subversion). Can be one of the
43 # This option should be used when using --command to create another kind
41 # other supported SCM: Bazaar, Darcs, Filesystem,
44 # of repository.
42 # Git, Mercurial (case sensitive).
45 #
43 # This option should be used when both options --url
46 # -c, --command=COMMAND
44 # and --command are used.
47 # the default is to create an subversion repository. You can use this command
45 # -f, --force force repository creation even if the project
48 # to create another kind of repository
46 # repository is already declared in Redmine
49 #
47 # -t, --test only show what should be done
50 # -f, --force
48 # -h, --help show help and exit
51 # force repository creation even if a repository is already declared in redmine.
49 # -v, --verbose verbose
52 #
50 # -V, --version print version and exit
53 # -t, --test
51 # -q, --quiet no log
54 # only show what should be done
55 #
56 # -h, --help:
57 # show help and exit
58 #
59 # -v, --verbose
60 # verbose
61 #
62 # -V, --version
63 # print version and exit
64 #
65 # -q, --quiet
66 # no log
67 #
68
52
69 require 'getoptlong'
53 require 'getoptlong'
70 require 'rdoc/usage'
54 require 'rdoc/usage'
71 require 'soap/wsdlDriver'
55 require 'soap/wsdlDriver'
72 require 'find'
56 require 'find'
73 require 'etc'
57 require 'etc'
74
58
75 Version = "1.0"
59 Version = "1.1"
60 SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem )
76
61
77 opts = GetoptLong.new(
62 opts = GetoptLong.new(
78 ['--svn-dir', '-s', GetoptLong::REQUIRED_ARGUMENT],
63 ['--svn-dir', '-s', GetoptLong::REQUIRED_ARGUMENT],
79 ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
64 ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
80 ['--owner', '-o', GetoptLong::REQUIRED_ARGUMENT],
65 ['--owner', '-o', GetoptLong::REQUIRED_ARGUMENT],
81 ['--url', '-u', GetoptLong::REQUIRED_ARGUMENT],
66 ['--url', '-u', GetoptLong::REQUIRED_ARGUMENT],
82 ['--scm', GetoptLong::REQUIRED_ARGUMENT],
83 ['--command' , '-c', GetoptLong::REQUIRED_ARGUMENT],
67 ['--command' , '-c', GetoptLong::REQUIRED_ARGUMENT],
68 ['--scm', GetoptLong::REQUIRED_ARGUMENT],
84 ['--test', '-t', GetoptLong::NO_ARGUMENT],
69 ['--test', '-t', GetoptLong::NO_ARGUMENT],
85 ['--force', '-f', GetoptLong::NO_ARGUMENT],
70 ['--force', '-f', GetoptLong::NO_ARGUMENT],
86 ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
71 ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
87 ['--version', '-V', GetoptLong::NO_ARGUMENT],
72 ['--version', '-V', GetoptLong::NO_ARGUMENT],
88 ['--help' , '-h', GetoptLong::NO_ARGUMENT],
73 ['--help' , '-h', GetoptLong::NO_ARGUMENT],
89 ['--quiet' , '-q', GetoptLong::NO_ARGUMENT]
74 ['--quiet' , '-q', GetoptLong::NO_ARGUMENT]
90 )
75 )
91
76
92 $verbose = 0
77 $verbose = 0
93 $quiet = false
78 $quiet = false
94 $redmine_host = ''
79 $redmine_host = ''
95 $repos_base = ''
80 $repos_base = ''
96 $svn_owner = 'root'
81 $svn_owner = 'root'
97 $use_groupid = true
82 $use_groupid = true
98 $svn_url = false
83 $svn_url = false
99 $test = false
84 $test = false
100 $command = "svnadmin create"
85 $command = "svnadmin create"
101 $force = false
86 $force = false
102 $scm = 'Subversion'
87 $scm = 'Subversion'
103
88
104 def log(text,level=0, exit=false)
89 def log(text,level=0, exit=false)
105 return if $quiet or level > $verbose
90 return if $quiet or level > $verbose
106 puts text
91 puts text
107 exit 1 if exit
92 exit 1 if exit
108 end
93 end
109
94
110 begin
95 begin
111 opts.each do |opt, arg|
96 opts.each do |opt, arg|
112 case opt
97 case opt
113 when '--svn-dir'; $repos_base = arg.dup
98 when '--svn-dir'; $repos_base = arg.dup
114 when '--redmine-host'; $redmine_host = arg.dup
99 when '--redmine-host'; $redmine_host = arg.dup
115 when '--owner'; $svn_owner = arg.dup; $use_groupid = false;
100 when '--owner'; $svn_owner = arg.dup; $use_groupid = false;
116 when '--url'; $svn_url = arg.dup
101 when '--url'; $svn_url = arg.dup
117 when '--scm'; $scm = arg.dup
102 when '--scm'; $scm = arg.dup; log("Invalid SCM: #{$scm}", 0, true) unless SUPPORTED_SCM.include?($scm)
118 when '--command'; $command = arg.dup
103 when '--command'; $command = arg.dup
119 when '--verbose'; $verbose += 1
104 when '--verbose'; $verbose += 1
120 when '--test'; $test = true
105 when '--test'; $test = true
121 when '--force'; $force = true
106 when '--force'; $force = true
122 when '--version'; puts Version; exit
107 when '--version'; puts Version; exit
123 when '--help'; RDoc::usage
108 when '--help'; RDoc::usage
124 when '--quiet'; $quiet = true
109 when '--quiet'; $quiet = true
125 end
110 end
126 end
111 end
127 rescue
112 rescue
128 exit 1
113 exit 1
129 end
114 end
130
115
131 if $test
116 if $test
132 log("running in test mode")
117 log("running in test mode")
133 end
118 end
134
119
120 # Make sure command is overridden if SCM vendor is not Subversion
121 if $scm != 'Subversion' && $command == 'svnadmin create'
122 log("Please use --command option to specify how to create a #{$scm} repository.", 0, true)
123 end
124
125
135 $svn_url += "/" if $svn_url and not $svn_url.match(/\/$/)
126 $svn_url += "/" if $svn_url and not $svn_url.match(/\/$/)
136
127
137 if ($redmine_host.empty? or $repos_base.empty?)
128 if ($redmine_host.empty? or $repos_base.empty?)
138 RDoc::usage
129 RDoc::usage
139 end
130 end
140
131
141 unless File.directory?($repos_base)
132 unless File.directory?($repos_base)
142 log("directory '#{$repos_base}' doesn't exists", 0, true)
133 log("directory '#{$repos_base}' doesn't exists", 0, true)
143 end
134 end
144
135
145 log("querying Redmine for projects...", 1);
136 log("querying Redmine for projects...", 1);
146
137
147 $redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
138 $redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
148 $redmine_host.gsub!(/\/$/, '')
139 $redmine_host.gsub!(/\/$/, '')
149
140
150 wsdl_url = "#{$redmine_host}/sys/service.wsdl";
141 wsdl_url = "#{$redmine_host}/sys/service.wsdl";
151
142
152 begin
143 begin
153 soap = SOAP::WSDLDriverFactory.new(wsdl_url).create_rpc_driver
144 soap = SOAP::WSDLDriverFactory.new(wsdl_url).create_rpc_driver
154 rescue => e
145 rescue => e
155 log("Unable to connect to #{wsdl_url} : #{e}", 0, true)
146 log("Unable to connect to #{wsdl_url} : #{e}", 0, true)
156 end
147 end
157
148
158 projects = soap.ProjectsWithRepositoryEnabled
149 projects = soap.ProjectsWithRepositoryEnabled
159
150
160 if projects.nil?
151 if projects.nil?
161 log('no project found, perhaps you forgot to "Enable WS for repository management"', 0, true)
152 log('no project found, perhaps you forgot to "Enable WS for repository management"', 0, true)
162 end
153 end
163
154
164 log("retrieved #{projects.size} projects", 1)
155 log("retrieved #{projects.size} projects", 1)
165
156
166 def set_owner_and_rights(project, repos_path, &block)
157 def set_owner_and_rights(project, repos_path, &block)
167 if RUBY_PLATFORM =~ /mswin/
158 if RUBY_PLATFORM =~ /mswin/
168 yield if block_given?
159 yield if block_given?
169 else
160 else
170 uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project.identifier).gid : 0)
161 uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project.identifier).gid : 0)
171 right = project.is_public ? 0775 : 0770
162 right = project.is_public ? 0775 : 0770
172 yield if block_given?
163 yield if block_given?
173 Find.find(repos_path) do |f|
164 Find.find(repos_path) do |f|
174 File.chmod right, f
165 File.chmod right, f
175 File.chown uid, gid, f
166 File.chown uid, gid, f
176 end
167 end
177 end
168 end
178 end
169 end
179
170
180 def other_read_right?(file)
171 def other_read_right?(file)
181 (File.stat(file).mode & 0007).zero? ? false : true
172 (File.stat(file).mode & 0007).zero? ? false : true
182 end
173 end
183
174
184 def owner_name(file)
175 def owner_name(file)
185 RUBY_PLATFORM =~ /mswin/ ?
176 RUBY_PLATFORM =~ /mswin/ ?
186 $svn_owner :
177 $svn_owner :
187 Etc.getpwuid( File.stat(file).uid ).name
178 Etc.getpwuid( File.stat(file).uid ).name
188 end
179 end
189
180
190 projects.each do |project|
181 projects.each do |project|
191 log("treating project #{project.name}", 1)
182 log("treating project #{project.name}", 1)
192
183
193 if project.identifier.empty?
184 if project.identifier.empty?
194 log("\tno identifier for project #{project.name}")
185 log("\tno identifier for project #{project.name}")
195 next
186 next
196 elsif not project.identifier.match(/^[a-z0-9\-]+$/)
187 elsif not project.identifier.match(/^[a-z0-9\-]+$/)
197 log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
188 log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
198 next;
189 next;
199 end
190 end
200
191
201 repos_path = $repos_base + "/" + project.identifier
192 repos_path = $repos_base + "/" + project.identifier
202
193
203 if File.directory?(repos_path)
194 if File.directory?(repos_path)
204
195
205 # we must verify that repository has the good owner and the good
196 # we must verify that repository has the good owner and the good
206 # rights before leaving
197 # rights before leaving
207 other_read = other_read_right?(repos_path)
198 other_read = other_read_right?(repos_path)
208 owner = owner_name(repos_path)
199 owner = owner_name(repos_path)
209 next if project.is_public == other_read and owner == $svn_owner
200 next if project.is_public == other_read and owner == $svn_owner
210
201
211 if $test
202 if $test
212 log("\tchange mode on #{repos_path}")
203 log("\tchange mode on #{repos_path}")
213 next
204 next
214 end
205 end
215
206
216 begin
207 begin
217 set_owner_and_rights(project, repos_path)
208 set_owner_and_rights(project, repos_path)
218 rescue Errno::EPERM => e
209 rescue Errno::EPERM => e
219 log("\tunable to change mode on #{repos_path} : #{e}\n")
210 log("\tunable to change mode on #{repos_path} : #{e}\n")
220 next
211 next
221 end
212 end
222
213
223 log("\tmode change on #{repos_path}");
214 log("\tmode change on #{repos_path}");
224
215
225 else
216 else
226 # if repository is already declared in redmine, we don't create
217 # if repository is already declared in redmine, we don't create
227 # unless user use -f with reposman
218 # unless user use -f with reposman
228 if $force == false and not project.repository.nil?
219 if $force == false and not project.repository.nil?
229 log("\trepository for project #{project.identifier} already exists in Redmine", 1)
220 log("\trepository for project #{project.identifier} already exists in Redmine", 1)
230 next
221 next
231 end
222 end
232
223
233 project.is_public ? File.umask(0002) : File.umask(0007)
224 project.is_public ? File.umask(0002) : File.umask(0007)
234
225
235 if $test
226 if $test
236 log("\tcreate repository #{repos_path}")
227 log("\tcreate repository #{repos_path}")
237 log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}") if $svn_url;
228 log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}") if $svn_url;
238 next
229 next
239 end
230 end
240
231
241 begin
232 begin
242 set_owner_and_rights(project, repos_path) do
233 set_owner_and_rights(project, repos_path) do
243 command = "#{$command} #{repos_path}"
234 command = "#{$command} #{repos_path}"
244 raise "#{command} failed" unless system( command )
235 raise "#{command} failed" unless system( command )
245 end
236 end
246 rescue => e
237 rescue => e
247 log("\tunable to create #{repos_path} : #{e}\n")
238 log("\tunable to create #{repos_path} : #{e}\n")
248 next
239 next
249 end
240 end
250
241
251 if $svn_url
242 if $svn_url
252 ret = soap.RepositoryCreated project.identifier, $scm, "#{$svn_url}#{project.identifier}"
243 ret = soap.RepositoryCreated project.identifier, $scm, "#{$svn_url}#{project.identifier}"
253 if ret > 0
244 if ret > 0
254 log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}");
245 log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}");
255 else
246 else
256 log("\trepository #{repos_path} not registered in Redmine. Look in your log to find why.");
247 log("\trepository #{repos_path} not registered in Redmine. Look in your log to find why.");
257 end
248 end
258 end
249 end
259
250
260 log("\trepository #{repos_path} created");
251 log("\trepository #{repos_path} created");
261 end
252 end
262
253
263 end
254 end
264
255
General Comments 0
You need to be logged in to leave comments. Login now