gitdocs 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +6 -14
  2. data/.codeclimate.yml +26 -0
  3. data/.rubocop.yml +8 -2
  4. data/.travis.yml +8 -0
  5. data/CHANGELOG +13 -0
  6. data/Gemfile +1 -1
  7. data/README.md +7 -6
  8. data/Rakefile +31 -5
  9. data/bin/gitdocs +1 -0
  10. data/config.ru +6 -4
  11. data/gitdocs.gemspec +22 -19
  12. data/lib/gitdocs.rb +54 -16
  13. data/lib/gitdocs/browser_app.rb +34 -41
  14. data/lib/gitdocs/cli.rb +41 -32
  15. data/lib/gitdocs/configuration.rb +40 -101
  16. data/lib/gitdocs/git_notifier.rb +111 -0
  17. data/lib/gitdocs/initializer.rb +83 -0
  18. data/lib/gitdocs/manager.rb +90 -60
  19. data/lib/gitdocs/migration/004_add_index_for_path.rb +1 -1
  20. data/lib/gitdocs/notifier.rb +70 -104
  21. data/lib/gitdocs/rendering_helper.rb +3 -0
  22. data/lib/gitdocs/repository.rb +324 -307
  23. data/lib/gitdocs/repository/committer.rb +77 -0
  24. data/lib/gitdocs/repository/path.rb +157 -140
  25. data/lib/gitdocs/search.rb +40 -25
  26. data/lib/gitdocs/settings_app.rb +5 -3
  27. data/lib/gitdocs/share.rb +64 -0
  28. data/lib/gitdocs/synchronizer.rb +40 -0
  29. data/lib/gitdocs/version.rb +1 -1
  30. data/lib/gitdocs/views/_header.haml +2 -2
  31. data/lib/gitdocs/views/dir.haml +3 -3
  32. data/lib/gitdocs/views/edit.haml +1 -1
  33. data/lib/gitdocs/views/file.haml +1 -1
  34. data/lib/gitdocs/views/home.haml +3 -3
  35. data/lib/gitdocs/views/layout.haml +13 -13
  36. data/lib/gitdocs/views/revisions.haml +3 -3
  37. data/lib/gitdocs/views/search.haml +1 -1
  38. data/lib/gitdocs/views/settings.haml +6 -6
  39. data/test/integration/cli/full_sync_test.rb +83 -0
  40. data/test/integration/cli/share_management_test.rb +29 -0
  41. data/test/integration/cli/status_test.rb +14 -0
  42. data/test/integration/test_helper.rb +185 -151
  43. data/test/integration/{browse_test.rb → web/browse_test.rb} +11 -29
  44. data/test/integration/web/share_management_test.rb +46 -0
  45. data/test/support/git_factory.rb +276 -0
  46. data/test/unit/browser_app_test.rb +346 -0
  47. data/test/unit/configuration_test.rb +8 -70
  48. data/test/unit/git_notifier_test.rb +116 -0
  49. data/test/unit/gitdocs_test.rb +90 -0
  50. data/test/unit/manager_test.rb +36 -0
  51. data/test/unit/notifier_test.rb +60 -124
  52. data/test/unit/repository_committer_test.rb +111 -0
  53. data/test/unit/repository_path_test.rb +92 -76
  54. data/test/unit/repository_test.rb +243 -356
  55. data/test/unit/search_test.rb +15 -0
  56. data/test/unit/settings_app_test.rb +80 -0
  57. data/test/unit/share_test.rb +97 -0
  58. data/test/unit/test_helper.rb +17 -3
  59. metadata +114 -108
  60. data/lib/gitdocs/runner.rb +0 -108
  61. data/lib/gitdocs/server.rb +0 -62
  62. data/test/integration/full_sync_test.rb +0 -66
  63. data/test/integration/share_management_test.rb +0 -95
  64. data/test/integration/status_test.rb +0 -21
  65. data/test/unit/runner_test.rb +0 -122
@@ -0,0 +1,77 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Gitdocs
3
+ class Repository
4
+ class Committer
5
+ # @raise if the repository is not valid for commits
6
+ def initialize(root_dirname)
7
+ @root_dirname = root_dirname
8
+ @rugged = Rugged::Repository.new(root_dirname)
9
+ @grit = Grit::Repo.new(root_dirname)
10
+ @commit_message_path = File.expand_path('.gitmessage~', root_dirname)
11
+ rescue Rugged::OSError
12
+ raise(Gitdocs::Repository::InvalidError, 'No directory')
13
+ rescue Rugged::RepositoryError
14
+ raise(Gitdocs::Repository::InvalidError, 'Not a repository')
15
+ end
16
+
17
+ # @return [Boolean]
18
+ def commit
19
+ # Do this first to allow the message file to be deleted, if it exists.
20
+ message = read_and_delete_commit_message_file
21
+
22
+ mark_empty_directories
23
+
24
+ # FIXME: Consider a more appropriate location for the dirty check.
25
+ return false unless Gitdocs::Repository.new(@root_dirname).dirty?
26
+ Gitdocs.log_debug("Repo #{@root_dirname} is dirty")
27
+
28
+ # Commit any changes in the working directory.
29
+ Dir.chdir(@root_dirname) do
30
+ @rugged.index.add_all
31
+ @rugged.index.update_all
32
+ end
33
+ @rugged.index.write
34
+ Gitdocs.log_debug("Index to be committed #{@rugged.index}")
35
+
36
+ commit_result = @grit.commit_index(message)
37
+ Gitdocs.log_debug("Commit result: <#{commit_result.inspect}>")
38
+
39
+ true
40
+ end
41
+
42
+ # @param [String] message
43
+ # @return [void]
44
+ def write_commit_message(message)
45
+ return unless message
46
+ return if message.empty?
47
+
48
+ File.open(@commit_message_path, 'w') { |f| f.print(message) }
49
+ end
50
+
51
+ ##########################################################################
52
+
53
+ private
54
+
55
+ def mark_empty_directories
56
+ Find.find(@root_dirname).each do |path|
57
+ Find.prune if File.basename(path) == '.git'
58
+ if File.directory?(path) && Dir.entries(path).count == 2
59
+ FileUtils.touch(File.join(path, '.gitignore'))
60
+ end
61
+ end
62
+ end
63
+
64
+ # @return [String] either the message in the file, or the regular
65
+ # automatic commit message.
66
+ def read_and_delete_commit_message_file
67
+ return(
68
+ 'Auto-commit from gitdocs'
69
+ ) unless File.exist?(@commit_message_path)
70
+
71
+ message = File.read(@commit_message_path)
72
+ File.delete(@commit_message_path)
73
+ message
74
+ end
75
+ end
76
+ end
77
+ end
@@ -2,170 +2,187 @@
2
2
 
3
3
  # Class for executing File and Git operations on a specific path in the
4
4
  # repository.
5
- class Gitdocs::Repository::Path
6
- attr_reader :relative_path
7
-
8
- # @param [Gitdocs::Repository] repository
9
- # @param [String] relative_path
10
- def initialize(repository, relative_path)
11
- @repository = repository
12
- @relative_path = relative_path.gsub(/^\//, '')
13
- @absolute_path = File.join(
14
- File.absolute_path(@repository.root), @relative_path
15
- )
16
- end
5
+ module Gitdocs
6
+ class Repository
7
+ class Path
8
+ attr_reader :relative_path
9
+
10
+ # @param [Gitdocs::Repository] repository
11
+ # @param [String] relative_path
12
+ def initialize(repository, relative_path)
13
+ @repository = repository
14
+ @relative_path = relative_path.gsub(%r{^/}, '')
15
+ @absolute_path = File.join(
16
+ File.absolute_path(@repository.root), @relative_path
17
+ )
18
+ end
17
19
 
18
- def join(path_fragment)
19
- @relative_path = File.join(@relative_path, path_fragment)
20
- @absolute_path = File.join(@absolute_path, path_fragment)
21
- end
20
+ # @return [String]
21
+ def relative_dirname
22
+ result = File.dirname(@relative_path)
23
+ return '' if result == '.'
24
+ result
25
+ end
22
26
 
23
- # Write the content to the path and create any necessary directories.
24
- #
25
- # @param [String] content
26
- # @param [String] commit_message
27
- def write(content, commit_message)
28
- FileUtils.mkdir_p(File.dirname(@absolute_path))
29
- File.open(@absolute_path, 'w') { |f| f.puts(content) }
27
+ def join(path_fragment)
28
+ @relative_path = File.join(@relative_path, path_fragment)
29
+ @absolute_path = File.join(@absolute_path, path_fragment)
30
+ end
30
31
 
31
- @repository.write_commit_message(commit_message)
32
- end
32
+ # Write the content to the path and create any necessary directories.
33
+ #
34
+ # @param [String] content
35
+ # @return [void]
36
+ def write(content)
37
+ FileUtils.mkdir_p(File.dirname(@absolute_path))
38
+ File.open(@absolute_path, 'w') { |f| f.puts(content) }
39
+ end
33
40
 
34
- # Touch and path and create any necessary directories.
35
- def touch
36
- FileUtils.mkdir_p(File.dirname(@absolute_path))
37
- FileUtils.touch(@absolute_path)
38
- end
41
+ # Touch and path and create any necessary directories.
42
+ def touch
43
+ FileUtils.mkdir_p(File.dirname(@absolute_path))
44
+ FileUtils.touch(@absolute_path)
45
+ end
39
46
 
40
- # Create the path as a directory.
41
- def mkdir
42
- FileUtils.mkdir_p(@absolute_path)
43
- end
47
+ # Create the path as a directory.
48
+ def mkdir
49
+ FileUtils.mkdir_p(@absolute_path)
50
+ end
44
51
 
45
- # Remove the path, but only if it is a file.
46
- def remove
47
- return nil unless File.file?(@absolute_path)
48
- FileUtils.rm(@absolute_path)
49
- end
52
+ # Move file to the repository path
53
+ # @param [String] filename
54
+ def mv(filename)
55
+ FileUtils.mkdir_p(File.dirname(@absolute_path))
56
+ FileUtils.mv(filename, @absolute_path)
57
+ end
50
58
 
51
- # @return [Boolean]
52
- def text?
53
- return false unless File.file?(@absolute_path)
54
- mime_type = File.mime_type?(File.open(@absolute_path))
55
- !!(mime_type =~ /text\/|x-empty/) # rubocop:disable DoubleNegation
56
- end
59
+ # Remove the path, but only if it is a file.
60
+ def remove
61
+ return nil unless File.file?(@absolute_path)
62
+ FileUtils.rm(@absolute_path)
63
+ end
57
64
 
58
- # Returns file meta data based on relative file path
59
- #
60
- # @example
61
- # meta
62
- # => { :author => "Nick", :size => 1000, :modified => ... }
63
- #
64
- # @raise [RuntimeError] if the file is not found in any commits
65
- #
66
- # @return [Hash<:author => String, :size => Integer, modified => Time>]
67
- def meta
68
- commit = @repository.last_commit_for(@relative_path)
69
-
70
- # FIXME: This should actually just return an empty hash
71
- fail("File #{@relative_path} not found") unless commit
72
-
73
- {
74
- author: commit.author[:name],
75
- size: total_size,
76
- modified: commit.author[:time]
77
- }
78
- end
65
+ # @return [Boolean]
66
+ def text?
67
+ return false unless File.file?(@absolute_path)
68
+ mime_type = File.mime_type?(File.open(@absolute_path))
69
+ !!(mime_type =~ %r{text/|x-empty}) # rubocop:disable DoubleNegation
70
+ end
79
71
 
80
- def exist?
81
- File.exist?(@absolute_path)
82
- end
72
+ # Returns file meta data based on relative file path
73
+ #
74
+ # @example
75
+ # meta
76
+ # => { :author => "Nick", :size => 1000, :modified => ... }
77
+ #
78
+ # @raise [RuntimeError] if the file is not found in any commits
79
+ #
80
+ # @return [Hash<:author => String, :size => Integer, modified => Time>]
81
+ def meta
82
+ commit = @repository.last_commit_for(@relative_path)
83
+
84
+ # FIXME: This should actually just return an empty hash
85
+ fail("File #{@relative_path} not found") unless commit
86
+
87
+ {
88
+ author: commit.author[:name],
89
+ size: total_size,
90
+ modified: commit.author[:time]
91
+ }
92
+ end
83
93
 
84
- def directory?
85
- File.directory?(@absolute_path)
86
- end
94
+ def exist?
95
+ File.exist?(@absolute_path)
96
+ end
87
97
 
88
- def absolute_path(ref = nil)
89
- return @absolute_path unless ref
98
+ def directory?
99
+ File.directory?(@absolute_path)
100
+ end
90
101
 
91
- blob = @repository.blob_at(@relative_path, ref)
92
- content = blob ? blob.text : ''
93
- tmp_path = File.expand_path(File.basename(@relative_path), Dir.tmpdir)
94
- File.open(tmp_path, 'w') { |f| f.puts content }
95
- tmp_path
96
- end
102
+ def absolute_path(ref = nil)
103
+ return @absolute_path unless ref
97
104
 
98
- def readme_path
99
- return nil unless directory?
100
- Dir.glob(File.join(@absolute_path, 'README.{md}')).first
101
- end
105
+ blob = @repository.blob_at(@relative_path, ref)
106
+ content = blob ? blob.text : ''
107
+ tmp_path = File.expand_path(File.basename(@relative_path), Dir.tmpdir)
108
+ File.open(tmp_path, 'w') { |f| f.puts content }
109
+ tmp_path
110
+ end
102
111
 
103
- DirEntry = Struct.new(:name, :is_directory)
112
+ def readme_path
113
+ return nil unless directory?
114
+ Dir.glob(File.join(@absolute_path, 'README.{md}')).first
115
+ end
104
116
 
105
- # @return [Array<DirEntry>] entries in the directory
106
- # * excluding any git related directories
107
- # * sorted by filename, ignoring any leading '.'s
108
- def file_listing
109
- return nil unless directory?
117
+ DirEntry = Struct.new(:name, :is_directory)
110
118
 
111
- Dir.glob(File.join(@absolute_path, '{*,.*}'))
112
- .reject { |x| x.match(/\/\.(\.|git|gitignore|gitmessage~)?$/) }
113
- .sort_by { |x| File.basename(x).sub(/^\./, '') }
114
- .map { |x| DirEntry.new(File.basename(x), File.directory?(x)) }
115
- end
119
+ # @return [Array<DirEntry>] entries in the directory
120
+ # * excluding any git related directories
121
+ # * sorted by filename, ignoring any leading '.'s
122
+ def file_listing
123
+ return nil unless directory?
116
124
 
117
- def content
118
- return nil unless File.file?(@absolute_path)
119
- File.read(@absolute_path)
120
- end
125
+ Dir
126
+ .glob(File.join(@absolute_path, '{*,.*}'))
127
+ .reject { |x| x.match(%r{/\.(\.|git|gitignore|gitmessage~)?$}) }
128
+ .sort_by { |x| File.basename(x).sub(/^\./, '') }
129
+ .map { |x| DirEntry.new(File.basename(x), File.directory?(x)) }
130
+ end
121
131
 
122
- # Returns the revisions available for a particular file
123
- #
124
- # @param [String] file
125
- #
126
- # @return [Array<Hash>]
127
- def revisions
128
- @repository.commits_for(@relative_path, 100).map do |commit|
129
- {
130
- commit: commit.oid[0, 7],
131
- subject: commit.message.split("\n")[0],
132
- author: commit.author[:name],
133
- date: commit.author[:time]
134
- }
135
- end
136
- end
132
+ def content
133
+ return nil unless File.file?(@absolute_path)
134
+ File.read(@absolute_path)
135
+ end
137
136
 
138
- # Revert file to the specified ref
139
- #
140
- # @param [String] ref
141
- def revert(ref)
142
- return unless ref
137
+ # Returns the revisions available for a particular file
138
+ #
139
+ # @param [String] file
140
+ #
141
+ # @return [Array<Hash>]
142
+ def revisions
143
+ @repository.commits_for(@relative_path, 100).map do |commit|
144
+ {
145
+ commit: commit.oid[0, 7],
146
+ subject: commit.message.split("\n")[0],
147
+ author: commit.author[:name],
148
+ date: commit.author[:time]
149
+ }
150
+ end
151
+ end
143
152
 
144
- blob = @repository.blob_at(@relative_path, ref)
145
- # Silently fail if the file/ref do not existing in the repository.
146
- # Which is consistent with the original behaviour.
147
- # TODO: should consider throwing an exception on this condition
148
- return unless blob
153
+ # Revert file to the specified ref
154
+ #
155
+ # @param [String] ref
156
+ def revert(ref)
157
+ return unless ref
149
158
 
150
- write(blob.text, "Reverting '#{@relative_path}' to #{ref}")
151
- end
159
+ blob = @repository.blob_at(@relative_path, ref)
160
+ # Silently fail if the file/ref do not existing in the repository.
161
+ # Which is consistent with the original behaviour.
162
+ # TODO: should consider throwing an exception on this condition
163
+ return unless blob
164
+
165
+ write(blob.text)
166
+ end
152
167
 
153
- #############################################################################
168
+ ##########################################################################
154
169
 
155
- private
170
+ private
156
171
 
157
- def total_size
158
- result =
159
- if File.directory?(@absolute_path)
160
- Dir[File.join(@absolute_path, '**', '*')].reduce(0) do |size, filename|
161
- File.symlink?(filename) ? size : size + File.size(filename)
162
- end
163
- else
164
- File.symlink?(@absolute_path) ? 0 : File.size(@absolute_path)
165
- end
172
+ def total_size
173
+ result =
174
+ if File.directory?(@absolute_path)
175
+ Dir[File.join(@absolute_path, '**', '*')].reduce(0) do |size, filename|
176
+ File.symlink?(filename) ? size : size + File.size(filename)
177
+ end
178
+ else
179
+ File.symlink?(@absolute_path) ? 0 : File.size(@absolute_path)
180
+ end
166
181
 
167
- # HACK: A value of 0 breaks the table sort for some reason
168
- return -1 if result == 0
169
- result
182
+ # HACK: A value of 0 breaks the table sort for some reason
183
+ return -1 if result == 0
184
+ result
185
+ end
186
+ end
170
187
  end
171
188
  end
@@ -1,35 +1,50 @@
1
- class Gitdocs::Search
2
- RepoDescriptor = Struct.new(:name, :index)
3
- SearchResult = Struct.new(:file, :context)
1
+ # -*- encoding : utf-8 -*-
4
2
 
5
- # @param [Array<Gitdocs::Repository>] repositories
6
- def initialize(repositories)
7
- @repositories = repositories
8
- end
3
+ module Gitdocs
4
+ class Search
5
+ RepoDescriptor = Struct.new(:name, :index)
6
+ SearchResult = Struct.new(:file, :context)
9
7
 
10
- def search(term)
11
- results = {}
12
- @repositories.each_with_index do |repository, index|
13
- descriptor = RepoDescriptor.new(repository.root, index)
14
- results[descriptor] = search_repository(repository, term)
8
+ # @param [String] term
9
+ # @return (see #search)
10
+ def self.search(term)
11
+ new(Share.all.map { |x| Repository.new(x) }).search(term)
12
+ end
13
+
14
+ # @param [Array<Gitdocs::Repository>] repositories
15
+ def initialize(repositories)
16
+ @repositories = repositories
17
+ end
18
+
19
+ # @param [String] term
20
+ # @return [Hash<RepoDescriptor, Array<SearchResult>>]
21
+ def search(term)
22
+ results = {}
23
+ @repositories.each_with_index do |repository, index|
24
+ descriptor = RepoDescriptor.new(repository.root, index)
25
+ results[descriptor] = search_repository(repository, term)
26
+ end
27
+ results.delete_if { |_key, value| value.empty? }
15
28
  end
16
- results.delete_if { |_key, value| value.empty? }
17
- end
18
29
 
19
- private
30
+ private
20
31
 
21
- def search_repository(repository, term)
22
- return [] if term.nil? || term.empty?
32
+ # @param [Repository] repository
33
+ # @param [String] term
34
+ # @return [Array<SearchResult>]
35
+ def search_repository(repository, term)
36
+ return [] if term.nil? || term.empty?
23
37
 
24
- results = []
25
- repository.grep(term) do |file, context|
26
- result = results.find { |s| s.file == file }
27
- if result
28
- result.context += ' ... ' + context
29
- else
30
- results << SearchResult.new(file, context)
38
+ results = []
39
+ repository.grep(term) do |file, context|
40
+ result = results.find { |s| s.file == file }
41
+ if result
42
+ result.context += ' ... ' + context
43
+ else
44
+ results << SearchResult.new(file, context)
45
+ end
31
46
  end
47
+ results
32
48
  end
33
- results
34
49
  end
35
50
  end