gitdocs 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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