OSDN Git Service

Adds Filesystem adapter (patch #1393 by Paul R).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 8 Jun 2008 15:40:24 +0000 (15:40 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 8 Jun 2008 15:40:24 +0000 (15:40 +0000)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1508 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/helpers/repositories_helper.rb
app/models/repository/filesystem.rb [new file with mode: 0644]
doc/RUNNING_TESTS
lib/redmine.rb
lib/redmine/scm/adapters/abstract_adapter.rb
lib/redmine/scm/adapters/filesystem_adapter.rb [new file with mode: 0644]
test/fixtures/repositories/filesystem_repository.tar.gz [new file with mode: 0644]
test/unit/filesystem_adapter_test.rb [new file with mode: 0644]
test/unit/repository_filesystem_test.rb [new file with mode: 0644]
test/unit/repository_test.rb

index d2cc664..10f6e73 100644 (file)
@@ -98,4 +98,8 @@ module RepositoriesHelper
   def bazaar_field_tags(form, repository)
       content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
   end
+  
+  def filesystem_field_tags(form, repository)
+    content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
+  end
 end
diff --git a/app/models/repository/filesystem.rb b/app/models/repository/filesystem.rb
new file mode 100644 (file)
index 0000000..da096cc
--- /dev/null
@@ -0,0 +1,43 @@
+# redMine - project management software
+# Copyright (C) 2006-2007  Jean-Philippe Lang
+#
+# FileSystem adapter
+# File written by Paul Rivier, at Demotera.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require 'redmine/scm/adapters/filesystem_adapter'
+
+class Repository::Filesystem < Repository
+  attr_protected :root_url
+  validates_presence_of :url
+
+  def scm_adapter
+    Redmine::Scm::Adapters::FilesystemAdapter
+  end
+  
+  def self.scm_name
+    'Filesystem'
+  end
+  
+  def entries(path=nil, identifier=nil)
+    scm.entries(path, identifier)
+  end
+
+  def fetch_changesets
+    nil
+  end
+  
+end
index 7a5e2b9..18dfe69 100644 (file)
@@ -24,6 +24,10 @@ Git
 ---
 gunzip < test/fixtures/repositories/git_repository.tar.gz | tar -xv -C tmp/test
 
+FileSystem
+----------
+gunzip < test/fixtures/repositories/filesystem_repository.tar.gz | tar -xv -C tmp/test
+
 
 Running Tests
 =============
index 7b9832d..84eda9f 100644 (file)
@@ -11,7 +11,7 @@ rescue LoadError
   # RMagick is not available
 end
 
-REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar Git )
+REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar Git Filesystem )
 
 # Permissions
 Redmine::AccessControl.map do |map|
index 80058a2..bd77ce2 100644 (file)
@@ -100,6 +100,16 @@ module Redmine
           (path[-1,1] == "/") ? path : "#{path}/"
         end
         
+        def without_leading_slash(path)
+          path ||= ''
+          path.gsub(%r{^/+}, '')
+        end
+
+        def without_trailling_slash(path)
+          path ||= ''
+          (path[-1,1] == "/") ? path[0..-2] : path
+         end
+        
         def shell_quote(str)
           if RUBY_PLATFORM =~ /mswin/
             '"' + str.gsub(/"/, '\\"') + '"'
diff --git a/lib/redmine/scm/adapters/filesystem_adapter.rb b/lib/redmine/scm/adapters/filesystem_adapter.rb
new file mode 100644 (file)
index 0000000..c06b7e6
--- /dev/null
@@ -0,0 +1,94 @@
+# redMine - project management software
+# Copyright (C) 2006-2007  Jean-Philippe Lang
+#
+# FileSystem adapter
+# File written by Paul Rivier, at Demotera.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require 'redmine/scm/adapters/abstract_adapter'
+require 'find'
+
+module Redmine
+  module Scm
+    module Adapters    
+      class FilesystemAdapter < AbstractAdapter
+        
+
+        def initialize(url, root_url=nil, login=nil, password=nil)
+          @url = with_trailling_slash(url)
+        end
+
+        def format_path_ends(path, leading=true, trailling=true)
+          path = leading ? with_leading_slash(path) : 
+            without_leading_slash(path)
+          trailling ? with_trailling_slash(path) : 
+            without_trailling_slash(path) 
+        end
+
+        def info
+          info = Info.new({:root_url => target(),
+                            :lastrev => nil
+                          })
+          info
+        rescue CommandFailed
+          return nil
+        end
+        
+        def entries(path="", identifier=nil)
+          entries = Entries.new
+          Dir.new(target(path)).each do |e|
+            relative_path = format_path_ends((format_path_ends(path,
+                                                               false,
+                                                               true) + e),
+                                             false,false)
+            target = target(relative_path)
+            entries << 
+              Entry.new({ :name => File.basename(e),
+                          # below : list unreadable files, but dont link them.
+                          :path => File.readable?(target) ? relative_path : "",
+                          :kind => (File.directory?(target) ? 'dir' : 'file'),
+                          :size => if (File.directory?(target))
+                                     nil else File.size(target) end,
+                          :lastrev => 
+                          Revision.new({:time => (File.mtime(target)).localtime,
+                                       })
+                        }) if File.exist?(target) and # paranoid test
+              %w{file directory}.include?(File.ftype(target)) and # avoid special types
+              not File.basename(e).match(/^\.+$/) # avoid . and ..             
+          end
+          entries.sort_by_name
+        end
+        
+        def cat(path, identifier=nil)
+          File.new(target(path)).read
+        end
+
+        private
+        
+        # AbstractAdapter::target is implicitly made to quote paths.
+        # Here we do not shell-out, so we do not want quotes.
+        def target(path=nil)
+          #Prevent the use of ..
+          if path and !path.match(/(^|\/)\.\.(\/|$)/)
+            return "#{self.url}#{without_leading_slash(path)}"
+          end
+          return self.url
+        end
+        
+      end
+    end
+  end
+end
diff --git a/test/fixtures/repositories/filesystem_repository.tar.gz b/test/fixtures/repositories/filesystem_repository.tar.gz
new file mode 100644 (file)
index 0000000..7e8a4ac
Binary files /dev/null and b/test/fixtures/repositories/filesystem_repository.tar.gz differ
diff --git a/test/unit/filesystem_adapter_test.rb b/test/unit/filesystem_adapter_test.rb
new file mode 100644 (file)
index 0000000..720d1e9
--- /dev/null
@@ -0,0 +1,42 @@
+
+require File.dirname(__FILE__) + '/../test_helper'
+
+
+class FilesystemAdapterTest < Test::Unit::TestCase
+  
+  REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/filesystem_repository'  
+  
+  if File.directory?(REPOSITORY_PATH)    
+    def setup
+      @adapter = Redmine::Scm::Adapters::FilesystemAdapter.new(REPOSITORY_PATH)
+    end
+    
+    def test_entries
+      assert_equal 2, @adapter.entries.size
+      assert_equal ["dir", "test"], @adapter.entries.collect(&:name)
+      assert_equal ["dir", "test"], @adapter.entries(nil).collect(&:name)
+      assert_equal ["dir", "test"], @adapter.entries("/").collect(&:name)
+      ["dir", "/dir", "/dir/", "dir/"].each do |path|
+        assert_equal ["subdir", "dirfile"], @adapter.entries(path).collect(&:name)
+      end
+      # If y try to use "..", the path is ignored
+      ["/../","dir/../", "..", "../", "/..", "dir/.."].each do |path|
+        assert_equal ["dir", "test"], @adapter.entries(path).collect(&:name), ".. must be ignored in path argument"
+      end
+    end
+    
+    def test_cat
+      assert_equal "TEST CAT\n", @adapter.cat("test")
+      assert_equal "TEST CAT\n", @adapter.cat("/test")
+      # Revision number is ignored
+      assert_equal "TEST CAT\n", @adapter.cat("/test", 1)
+    end
+    
+  else
+    puts "Filesystem test repository NOT FOUND. Skipping unit tests !!! See doc/RUNNING_TESTS."
+    def test_fake; assert true end
+  end
+  
+end
+
+
diff --git a/test/unit/repository_filesystem_test.rb b/test/unit/repository_filesystem_test.rb
new file mode 100644 (file)
index 0000000..6b643f9
--- /dev/null
@@ -0,0 +1,54 @@
+# redMine - project management software
+# Copyright (C) 2006-2007  Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require File.dirname(__FILE__) + '/../test_helper'
+
+class RepositoryFilesystemTest < Test::Unit::TestCase
+  fixtures :projects
+  
+  # No '..' in the repository path
+  REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/filesystem_repository'
+  
+  def setup
+    @project = Project.find(1)
+    Setting.enabled_scm << 'Filesystem' unless Setting.enabled_scm.include?('Filesystem')
+    assert @repository = Repository::Filesystem.create(:project => @project, :url => REPOSITORY_PATH)
+  end
+  
+  if File.directory?(REPOSITORY_PATH)  
+    def test_fetch_changesets
+      @repository.fetch_changesets
+      @repository.reload
+      
+      assert_equal 0, @repository.changesets.count
+      assert_equal 0, @repository.changes.count
+    end
+        
+    def test_entries
+      assert_equal 2, @repository.entries("", 2).size
+      assert_equal 2, @repository.entries("dir", 3).size
+    end
+
+    def test_cat
+      assert_equal "TEST CAT\n", @repository.scm.cat("test")
+    end
+
+  else
+    puts "Filesystem test repository NOT FOUND. Skipping unit tests !!! See doc/RUNNING_TESTS."
+    def test_fake; assert true end
+  end
+end
index 270b0be..03d836e 100644 (file)
@@ -51,6 +51,8 @@ class RepositoryTest < Test::Unit::TestCase
     repository = Repository::Subversion.new(:project => Project.find(3), :url => "svn://localhost")
     assert !repository.save
     assert_equal :activerecord_error_invalid, repository.errors.on(:type)
+    # re-enable Subversion for following tests
+    Setting.delete_all
   end
   
   def test_scan_changesets_for_issue_ids