Project

General

Profile

Patch #1783 ยป autorepo.diff

pietro abate, 2008-08-13 16:42

View differences:

app/apis/sys_api.rb
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 17

  
18
class ResultElement < ActionWebService::Struct
19
    member :project, Project
20
    member :repository, Repository
21
end
22

  
18 23
class SysApi < ActionWebService::API::Base
19 24
  api_method :projects,
20 25
             :expects => [],
21
             :returns => [[Project]]
26
             :returns => [[ResultElement]],
22 27
  api_method :repository_created,
23 28
             :expects => [:string, :string],
24 29
             :returns => [:int]
app/controllers/repositories_controller.rb
35 35
    if !@repository
36 36
      @repository = Repository.factory(params[:repository_scm])
37 37
      @repository.project = @project
38
      @repository.auto = params[:repository_auto]
38 39
    end
39 40
    if request.post?
40 41
      @repository.attributes = params[:repository]
app/controllers/sys_controller.rb
23 23
  before_invocation :check_enabled
24 24
  
25 25
  # Returns the projects list, with their repositories
26
  # Bug ! the list of repositories is not returned
26 27
  def projects
27
    Project.find(:all, :include => :repository)
28
    projects = Project.find(:all, :include => :repository)
29
    resultArray = []
30
    projects.each do |project|
31
       resultElement = ResultElement.new
32
        resultElement.project = project
33
       resultElement.repository = project.repository
34
       resultArray << resultElement
35
    end
36
    resultArray
28 37
  end
29 38

  
30 39
  # Registers a repository for the given project identifier
31
  # (Subversion specific)
32 40
  def repository_created(identifier, url)
33 41
    project = Project.find_by_identifier(identifier)
34
    # Do not create the repository if the project has already one
35
    return 0 unless project && project.repository.nil?
36
    logger.debug "Repository for #{project.name} was created"
37
    repository = Repository.factory('Subversion', :project => project, :url => url)
38
    repository.save
42
    if project && project.repository.auto
43
       repository = project.repository
44
       repository.url = url
45
       logger.debug "Repository for #{project.name} was created"
46
       repository.save
47
    end
39 48
    repository.id || 0
40 49
  end
41 50

  
app/helpers/repositories_helper.rb
42 42
    str
43 43
  end
44 44
  
45
  def repository_field_tags(form, repository)    
46
    method = repository.class.name.demodulize.underscore + "_field_tags"
47
    send(method, form, repository) if repository.is_a?(Repository) && respond_to?(method)
48
  end
49
  
45
  def repository_field_tags(form, repository)
46
      if repository
47
         method = repository.class.name.demodulize.underscore + "_field_tags"
48
         if !repository.auto || (repository.nil? || repository.new_record?)
49
             send(method, form, repository) if repository.is_a?(Repository) && respond_to?(method)
50
         end
51
      end
52

  
53
  def repository_auto(repository)
54
     check_box ('repository', 'auto',
55
               :checked => (repository.nil? || repository.new_record?) ? false : repository.auto,
56
               :disabled => (repository && !repository.new_record?),
57
                :onclick => "if (this.checked) {Element.hide('url_fields');} else {Element.show('url_fields');}" )
58
  end 
59

  
50 60
  def scm_select_tag(repository)
51 61
    container = [[]]
52 62
    REDMINE_SUPPORTED_SCM.each {|scm| container << ["Repository::#{scm}".constantize.scm_name, scm]}
53 63
    select_tag('repository_scm', 
54 64
               options_for_select(container, repository.class.name.demodulize),
55 65
               :disabled => (repository && !repository.new_record?),
56
               :onchange => remote_function(:url => { :controller => 'repositories', :action => 'edit', :id => @project }, :method => :get, :with => "Form.serialize(this.form)")
66
               :onchange => remote_function(:url => { :controller => 'repositories', :action => 'edit', :id => @project }, :method => :get, :with => "Form.serialize(this.form)") 
57 67
               )
58 68
  end
59 69
  
app/models/repository/bazaar.rb
19 19

  
20 20
class Repository::Bazaar < Repository
21 21
  attr_protected :root_url
22
  validates_presence_of :url
22
  validates_presence_of :url, :unless => Proc.new { |repository| repository.auto }
23 23

  
24 24
  def scm_adapter
25 25
    Redmine::Scm::Adapters::BazaarAdapter
app/models/repository/cvs.rb
19 19
require 'digest/sha1'
20 20

  
21 21
class Repository::Cvs < Repository
22
  validates_presence_of :url, :root_url
22
  validates_presence_of :url, :root_url, :unless => Proc.new { |repository| repository.auto }
23 23

  
24 24
  def scm_adapter
25 25
    Redmine::Scm::Adapters::CvsAdapter
app/models/repository/darcs.rb
18 18
require 'redmine/scm/adapters/darcs_adapter'
19 19

  
20 20
class Repository::Darcs < Repository
21
  validates_presence_of :url
21
  validates_presence_of :url, :unless => Proc.new { |repository| repository.auto }
22 22

  
23 23
  def scm_adapter
24 24
    Redmine::Scm::Adapters::DarcsAdapter
app/models/repository/git.rb
19 19

  
20 20
class Repository::Git < Repository
21 21
  attr_protected :root_url
22
  validates_presence_of :url
22
  validates_presence_of :url, :unless => Proc.new { |repository| repository.auto }
23 23

  
24 24
  def scm_adapter
25 25
    Redmine::Scm::Adapters::GitAdapter
app/models/repository/mercurial.rb
19 19

  
20 20
class Repository::Mercurial < Repository
21 21
  attr_protected :root_url
22
  validates_presence_of :url
22
  validates_presence_of :url, :unless => Proc.new { |repository| repository.auto }
23 23

  
24 24
  def scm_adapter
25 25
    Redmine::Scm::Adapters::MercurialAdapter
app/models/repository/subversion.rb
19 19

  
20 20
class Repository::Subversion < Repository
21 21
  attr_protected :root_url
22
  validates_presence_of :url
23
  validates_format_of :url, :with => /^(http|https|svn|svn\+ssh|file):\/\/.+/i
22
  validates_presence_of :url, :unless => Proc.new { |repository| repository.auto }
23
  validates_format_of :url, :with => /^(http|https|svn|svn\+ssh|file):\/\/.+/i, :unless => Proc.new { |repository| repository.auto }
24 24

  
25 25
  def scm_adapter
26 26
    Redmine::Scm::Adapters::SubversionAdapter
app/views/projects/settings/_repository.rhtml
7 7

  
8 8
<div class="box tabular">
9 9
<p><label><%= l(:label_scm) %></label><%= scm_select_tag(@repository) %></p>
10
<%= repository_field_tags(f, @repository) if @repository %>
10
<p><label><%= l(:label_scm_auto) %></label><%= repository_auto(@repository) %></p>
11
<div id="url_fields"><%= repository_field_tags(f, @repository) %></div>
11 12
</div>
12 13

  
13 14
<div class="contextual">
db/migrate/094_add_automatic_repository_column.rb
1
class AddAutomaticRepositoryColumn < ActiveRecord::Migration
2
  def self.up
3
      add_column :repositories, :auto, :boolean, :default => false, :null => false
4
  end
5

  
6
  def self.down
7
      remove_column :repositories, :auto
8
  end
9
end
extra/svn/reposmangen.rb
1
#!/usr/bin/ruby
2

  
3
# == Synopsis
4
#
5
# reposman: manages your svn repositories with Redmine
6
#
7
# == Usage
8
#
9
#     reposman [ -h | --help ] [ -v | --verbose ] [ -V | --version ] [ -q | --quiet ] -s /var/svn -r redmine.host.org
10
#     example: reposman --svn-dir=/var/svn --redmine-host=redmine.mydomain.foo
11
#              reposman -s /var/svn -r redmine.mydomain.foo
12
#
13
# == Arguments (mandatory)
14
# 
15
# -s, --svn-dir=DIR
16
#    use DIR as base directory for svn repositories
17
#
18
# -r, --redmine-host=HOST
19
#    assume Redmine is hosted on HOST.
20
#    you can use :
21
#    * -r redmine.mydomain.foo        (will add http://)
22
#    * -r http://redmine.mydomain.foo
23
#    * -r https://mydomain.foo/redmine
24
#
25
# == Options
26
#
27
# -o, --owner=OWNER
28
#    owner of the repository. using the rails login allow user to browse
29
#    the repository in Redmine even for private project
30
#
31
# -u, --url=URL
32
#    the base url Redmine will use to access your repositories. This
33
#    will be used to register the repository in Redmine so that user
34
#    doesn't need to do anything. reposman will add the identifier to this url :
35
#
36
#    -u https://my.svn.server/my/reposity/root # if the repository can be access by http
37
#    -u file:///var/svn/                       # if the repository is local
38
#    if this option isn't set, reposman won't register the repository
39
#
40
# -t, --test
41
#    only show what should be done
42
#
43
# -h, --help:
44
#    show help and exit
45
#
46
# -v, --verbose
47
#    verbose
48
#
49
# -V, --version
50
#    print version and exit
51
#
52
# -q, --quiet
53
#    no log
54
#
55

  
56
require 'getoptlong'
57
require 'rdoc/usage'
58
require 'soap/wsdlDriver'
59
require 'find'
60
require 'etc'
61
require 'yaml'
62

  
63
Version = "1.0"
64

  
65
opts = GetoptLong.new(
66
                      ['--conf',         '-c', GetoptLong::REQUIRED_ARGUMENT],
67
                      ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
68
                      ['--owner',        '-o', GetoptLong::REQUIRED_ARGUMENT],
69
                      ['--url',          '-u', GetoptLong::REQUIRED_ARGUMENT],
70
                      ['--test',         '-t', GetoptLong::NO_ARGUMENT],
71
                      ['--verbose',      '-v', GetoptLong::NO_ARGUMENT],
72
                      ['--version',      '-V', GetoptLong::NO_ARGUMENT],
73
                      ['--help'   ,      '-h', GetoptLong::NO_ARGUMENT],
74
                      ['--quiet'  ,      '-q', GetoptLong::NO_ARGUMENT]
75
                      )
76

  
77
$conffile     = "config.yml"
78
$verbose      = 0
79
$quiet        = false
80
$redmine_host = ''
81
$vcs_owner    = 'root'
82
$use_groupid  = true
83
$test         = false
84

  
85
def log(text,level=0, exit=false)
86
  return if $quiet or level > $verbose
87
  puts text
88
  exit 1 if exit
89
end
90

  
91
begin
92
  opts.each do |opt, arg|
93
    case opt
94
    when '--conf';           $conffile     = arg.dup
95
    when '--redmine-host';   $redmine_host = arg.dup
96
    when '--owner';          $vcs_owner    = arg.dup; $use_groupid = false;
97
    when '--verbose';        $verbose += 1
98
    when '--test';           $test = true
99
    when '--version';        puts Version; exit
100
    when '--help';           RDoc::usage
101
    when '--quiet';          $quiet = true
102
    end
103
  end
104
rescue
105
  exit 1
106
end
107

  
108
if File.exist?($conffile)
109
    config = open($conffile) {|f| YAML.load(f) }
110
else
111
    config = {
112
	    "Git" => {"url"  => "http://example.org/git",
113
		      "base" => "/path/to/git" } , 
114
	    "Subversion" => {
115
		     "url"  => "http://subversion.example.org/",
116
		     "base" => "/path/to/subversion" }
117
    }
118
    open('config.yml', 'w') {|f| YAML.dump(config, f)}
119
    log("Creating stub config file. Please edit it.",-1,true)
120
end
121

  
122
log("running in test mode") if $test
123

  
124
RDoc::usage if $redmine_host.empty?
125

  
126
log("querying Redmine for projects...", 1);
127

  
128
$redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
129
$redmine_host.gsub!(/\/$/, '')
130

  
131
wsdl_url = "#{$redmine_host}/sys/service.wsdl";
132

  
133
begin
134
  soap = SOAP::WSDLDriverFactory.new(wsdl_url).create_rpc_driver
135
rescue => e
136
  log("Unable to connect to #{wsdl_url} : #{e}", 0, true)
137
end
138

  
139
projects = soap.Projects
140

  
141
if projects.nil?
142
  log('no project found, perhaps you forgot to "Enable WS for repository management"', 0, true)
143
end
144

  
145
log("retrieved #{projects.size} projects", 1)
146

  
147
def set_owner_and_rights(project, repos_path, &block)
148
  if RUBY_PLATFORM =~ /mswin/
149
    yield if block_given?
150
  else
151
    uid = Etc.getpwnam($vcs_owner).uid
152
    # if the gid is 0 I get an exception...
153
    #gid = $use_groupid ? Etc.getgrnam(project.identifier).gid : 0
154
    gid = 0 
155
    right = project.is_public ? 0775 : 0770
156
    yield if block_given?
157
    Find.find(repos_path) do |f|
158
      File.chmod right, f
159
      File.chown uid, gid, f
160
    end
161
  end
162
end
163

  
164
def other_read_right?(file)
165
  (File.stat(file).mode & 0007).zero? ? false : true
166
end
167

  
168
def owner_name(file)
169
  RUBY_PLATFORM =~ /mswin/ ?
170
    $vcs_owner :
171
    Etc.getpwuid( File.stat(file).uid ).name  
172
end
173

  
174
projects.each do |element|
175
  project = element.project
176
  repository = element.repository
177

  
178
  repos_type = repository["type"]
179
  repos_base = config["#{repos_type}"]["base"]
180
  repos_url  = config["#{repos_type}"]["url"]
181
  repos_url += "/" if repos_url and not repos_url.match(/\/$/)
182
  repos_path = repos_base + "/" + project.identifier 
183

  
184
  unless File.directory?(repos_base)
185
    log("directory '#{repos_base}' doesn't exists", 0, true)
186
  end
187

  
188
  log("treating project #{project.name}", 1)
189

  
190
  if project.identifier.empty?
191
    log("\tno identifier for project #{project.name}")
192
    next
193
  elsif not project.identifier.match(/^[a-z0-9\-]+$/)
194
    log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
195
    next;
196
  end
197
  
198
  if repository.auto
199
      if File.directory?(repos_path)
200

  
201
	# we must verify that repository has the good owner and the good
202
	# rights before leaving
203
	other_read = other_read_right?(repos_path)
204
	owner      = owner_name(repos_path)
205
	next if project.is_public == other_read and owner == $vcs_owner
206

  
207
	if $test
208
	  log("\tchange mode on #{repos_path}")
209
	  next
210
	end
211

  
212
	begin
213
	  set_owner_and_rights(project, repos_path)
214
	rescue Errno::EPERM => e
215
	  log("\tunable to change mode on #{repos_path} : #{e}\n")
216
	  next
217
	end
218

  
219
	log("\tmode change on #{repos_path}");
220

  
221
      else
222
	# the directory repos_path doesn't exist
223
	project.is_public ? File.umask(0002) : File.umask(0007)
224

  
225
	if $test
226
	  log("\tcreate repository #{repos_path}")
227
	  log("\trepository #{repos_path} registered in Redmine with url #{repos_url}#{project.identifier}") if repos_url;
228
	  next
229
	end
230

  
231
	begin
232
	  Dir.mkdir(repos_path) unless test(?d, repos_path)
233
	  set_owner_and_rights(project, repos_path) do
234
	    case "#{repos_type}"
235
	      when "Subversion":
236
		raise "svnadmin create #{repos_path} failed" unless system("svnadmin", "create", repos_path)
237
	      when "Git":
238
		Dir.chdir(repos_path) do
239
		  raise "git create #{repos_path} failed" unless system("git-init-db", "--shared")
240
		end
241
	      else
242
		raise "unknown cvs type #{repos_type}"
243
	    end
244
	  end
245
	rescue => e
246
	  Dir.rmdir(repos_path)
247
	  log("\tunable to create #{repos_path} : #{e}\n")
248
	  next
249
	end
250

  
251
	if repos_url
252
	  ret = soap.RepositoryCreated project.identifier, "#{repos_url}#{project.identifier}"
253
	  if ret > 0
254
	    log("\trepository #{repos_path} registered in Redmine with url #{repos_url}#{project.identifier}");
255
	  else
256
	    log("\trepository #{repos_path} not registered in Redmine. Look in your log to find why.");
257
	  end
258
	end
259

  
260
	log("\trepository #{repos_path} created");
261
      end
262
  end
263
end
264

  
lang/en.yml
501 501
label_general: General
502 502
label_more: More
503 503
label_scm: SCM
504
label_scm_auto: Create Repository
504 505
label_plugins: Plugins
505 506
label_ldap_authentication: LDAP authentication
506 507
label_downloads_abbr: D/L
lib/redmine/scm/adapters/abstract_adapter.rb
24 24
      end
25 25
      
26 26
      class AbstractAdapter #:nodoc:
27
        def initialize(url, root_url=nil, login=nil, password=nil)
27
	def initialize(url=nil, root_url=nil, login=nil, password=nil)
28 28
          @url = url
29 29
          @login = login if login && !login.empty?
30 30
          @password = (password || "") if @login
    (1-1/1)