libdolt 0.13.0 → 0.14.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.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- libdolt (0.12.0)
4
+ libdolt (0.13.0)
5
5
  em_pessimistic (~> 0.1)
6
6
  em_rugged (~> 0.3)
7
7
  eventmachine (~> 1.0)
@@ -25,13 +25,16 @@ GEM
25
25
  eventmachine (1.0.0)
26
26
  github-markup (0.7.4)
27
27
  htmlentities (4.3.1)
28
- json (1.7.5)
28
+ json (1.7.6)
29
29
  makeup (0.3.0)
30
30
  github-markup (~> 0.7)
31
31
  htmlentities (~> 4.3)
32
32
  pygments.rb (~> 0.2)
33
+ metaclass (0.0.1)
33
34
  mime-types (1.19)
34
35
  minitest (2.12.1)
36
+ mocha (0.13.1)
37
+ metaclass (~> 0.0.1)
35
38
  posix-spawn (0.3.6)
36
39
  pygments.rb (0.3.2)
37
40
  posix-spawn (~> 0.3.6)
@@ -53,6 +56,7 @@ DEPENDENCIES
53
56
  em-minitest-spec (~> 1.1)
54
57
  libdolt!
55
58
  minitest (~> 2.0)
59
+ mocha
56
60
  rake (~> 0.9)
57
61
  redcarpet (= 2.2.0)
58
62
  tiltout (~> 1.4)
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+ #--
3
+ # Copyright (C) 2013 Gitorious AS
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+ require "when"
19
+ require "eventmachine"
20
+ require "em_pessimistic"
21
+ require "fileutils"
22
+
23
+ module Dolt
24
+ module Git
25
+ class Archiver
26
+ def initialize(work_dir, cache_dir)
27
+ # A hash of currently processing archiving jobs. It contains tuples of
28
+ # "#{repo.id}-#{oid}-#{format}" => promises representing the eventual
29
+ # completion of archiving tasks. When an archiving task is completed,
30
+ # its promise is removed from the hash. Previously generated tarballs
31
+ # can be found on disk.
32
+ @processing = {}
33
+ @work_dir = work_dir
34
+ @cache_dir = cache_dir
35
+ end
36
+
37
+ # Returns a promise that resolves with the filename of the generated
38
+ # tarball/zip file.
39
+ #
40
+ # repo - A repository object
41
+ # oid - A valid commit oid
42
+ # format - A symbol. If it is not :zip, tar.gz is assumed.
43
+ def archive(repo, oid, format = :tgz)
44
+ filename = cache_path(repo, oid, format)
45
+ return When.resolve(filename) if File.exists?(filename)
46
+ pending = pending_process(repo, oid, format)
47
+ return pending if pending
48
+ start_process(repo, oid, format)
49
+ end
50
+
51
+ private
52
+ def process_id(repo, oid, format)
53
+ "#{repo.id}-#{oid}-#{ext(format)}"
54
+ end
55
+
56
+ def pending_process(repo, oid, format)
57
+ @processing[process_id(repo, oid, format)]
58
+ end
59
+
60
+ def start_process(repo, oid, format)
61
+ @processing[process_id(repo, oid, format)] = When.defer do |d|
62
+ p = EMPessimistic::DeferrableChildProcess.open(cmd(repo, oid, format))
63
+
64
+ p.callback do |output, status|
65
+ filename = cache_path(repo, oid, format)
66
+ FileUtils.mv(work_path(repo, oid, format), filename)
67
+ d.resolve(filename)
68
+ end
69
+
70
+ p.errback do |output, status|
71
+ d.reject(Exception.new(output))
72
+ end
73
+ end
74
+ end
75
+
76
+ def cmd(repository, oid, format)
77
+ path_segment = repository.path_segment.gsub(/\//, "-")
78
+ cmd = "sh -c 'git --git-dir #{repository.full_repository_path} archive "
79
+ cmd += "--prefix='#{u(path_segment)}/' --format="
80
+ wpath = u(work_path(repository, oid, format))
81
+ cmd + (format.to_s == "zip" ? "zip #{u(oid)} > #{wpath}" : "tar #{u(oid)} | gzip -m > #{wpath}") + "'"
82
+ end
83
+
84
+ def cache_path(repository, oid, format)
85
+ File.join(@cache_dir, basename(repository, oid, format))
86
+ end
87
+
88
+ def work_path(repository, oid, format)
89
+ File.join(@work_dir, basename(repository, oid, format))
90
+ end
91
+
92
+ def basename(repository, oid, format)
93
+ path_segment = repository.path_segment.gsub(/\//, "-")
94
+ "#{path_segment}-#{oid}.#{ext(format)}"
95
+ end
96
+
97
+ def ext(format)
98
+ format.to_s == "zip" ? "zip" : "tar.gz"
99
+ end
100
+
101
+ # Unquote a string by stripping off any single or double quotes
102
+ def u(string)
103
+ string.gsub("'", '').gsub('"', '')
104
+ end
105
+ end
106
+ end
107
+ end
@@ -1,7 +1,6 @@
1
-
2
1
  # encoding: utf-8
3
2
  #--
4
- # Copyright (C) 2012 Gitorious AS
3
+ # Copyright (C) 2012-2013 Gitorious AS
5
4
  #
6
5
  # This program is free software: you can redistribute it and/or modify
7
6
  # it under the terms of the GNU Affero General Public License as published by
@@ -24,8 +23,9 @@ class Time; def to_json(*args); "\"#{iso8601}\""; end; end
24
23
 
25
24
  module Dolt
26
25
  class RepoActions
27
- def initialize(repo_resolver)
26
+ def initialize(repo_resolver, archiver = nil)
28
27
  @repo_resolver = repo_resolver
28
+ @archiver = archiver
29
29
  end
30
30
 
31
31
  def blob(repo, ref, path, &block)
@@ -71,6 +71,13 @@ module Dolt
71
71
  repo_action(repo, ref, path, :tree, :tree_history, ref, path, count, &block)
72
72
  end
73
73
 
74
+ def archive(repo, ref, format, &block)
75
+ repository = resolve_repository(repo)
76
+ d = @archiver.archive(repository, ref, format)
77
+ d.callback { |filename| block.call(nil, filename) }
78
+ d.errback { |err| block.call(err, nil) }
79
+ end
80
+
74
81
  def repositories
75
82
  repo_resolver.all
76
83
  end
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
  #--
3
- # Copyright (C) 2012 Gitorious AS
3
+ # Copyright (C) 2012-2013 Gitorious AS
4
4
  #
5
5
  # This program is free software: you can redistribute it and/or modify
6
6
  # it under the terms of the GNU Affero General Public License as published by
@@ -17,5 +17,5 @@
17
17
  #++
18
18
 
19
19
  module Dolt
20
- VERSION = "0.13.0"
20
+ VERSION = "0.14.0"
21
21
  end
data/libdolt.gemspec CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
28
28
  s.add_development_dependency "rake", "~> 0.9"
29
29
  s.add_development_dependency "redcarpet", "2.2.0"
30
30
  s.add_development_dependency "tiltout", "~>1.4"
31
+ s.add_development_dependency "mocha"
31
32
 
32
33
  s.files = `git ls-files`.split("\n")
33
34
  s.test_files = `git ls-files -- {test}/*`.split("\n")
@@ -0,0 +1,182 @@
1
+ # encoding: utf-8
2
+ #--
3
+ # Copyright (C) 2013 Gitorious AS
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+ require "test_helper"
19
+ require "mocha/setup"
20
+ require "libdolt/git/archiver"
21
+
22
+ class StubRepository
23
+ attr_reader :id, :full_repository_path, :path_segment
24
+ def initialize(path_segment)
25
+ @@counter ||= 0
26
+ @id = (@@counter += 1)
27
+ @full_repository_path = "/repos/#{path_segment}.git"
28
+ @path_segment = path_segment
29
+ end
30
+ end
31
+
32
+ class StubProcessStatus
33
+ attr_reader :exitstatus
34
+ def initialize(code)
35
+ @exitstatus = code
36
+ end
37
+ end
38
+
39
+ describe Dolt::Git::Archiver do
40
+ include EM::MiniTest::Spec
41
+
42
+ describe "archive" do
43
+ before do
44
+ @archiver = Dolt::Git::Archiver.new("/work", "/cache")
45
+ end
46
+
47
+ it "resolves with existing cached file" do
48
+ File.stubs(:exists?).with("/cache/gts-mainline-master.tar.gz").returns(true)
49
+ repo = StubRepository.new("gts/mainline")
50
+
51
+ @archiver.archive(repo, "master", :tar).then do |filename|
52
+ assert_equal "/cache/gts-mainline-master.tar.gz", filename
53
+ done!
54
+ end
55
+ wait!
56
+ end
57
+
58
+ it "generates tarball" do
59
+ repo = StubRepository.new("gts/mainline")
60
+
61
+ cmd = "sh -c 'git --git-dir /repos/gts/mainline.git archive --prefix='gts-mainline/' " +
62
+ "--format=tar master | gzip -m > /work/gts-mainline-master.tar.gz'"
63
+ d = EM::DefaultDeferrable.new
64
+ EMPessimistic::DeferrableChildProcess.expects(:open).with(cmd).returns(d)
65
+
66
+ @archiver.archive(repo, "master", :tar)
67
+ end
68
+
69
+ it "uses gzip format from string" do
70
+ repo = StubRepository.new("gts/mainline")
71
+
72
+ cmd = "sh -c 'git --git-dir /repos/gts/mainline.git archive --prefix='gts-mainline/' " +
73
+ "--format=tar master | gzip -m > /work/gts-mainline-master.tar.gz'"
74
+ d = EM::DefaultDeferrable.new
75
+ EMPessimistic::DeferrableChildProcess.expects(:open).with(cmd).returns(d)
76
+
77
+ @archiver.archive(repo, "master", "tar")
78
+ end
79
+
80
+ it "uses zip format from string" do
81
+ repo = StubRepository.new("gts/mainline")
82
+
83
+ cmd = "sh -c 'git --git-dir /repos/gts/mainline.git archive --prefix='gts-mainline/' " +
84
+ "--format=zip master > /work/gts-mainline-master.zip'"
85
+ d = EM::DefaultDeferrable.new
86
+ EMPessimistic::DeferrableChildProcess.expects(:open).with(cmd).returns(d)
87
+
88
+ @archiver.archive(repo, "master", "zip")
89
+ end
90
+
91
+ it "moves tarball when successfully generated" do
92
+ FileUtils.expects(:mv).with("/work/gts-mainline-master.tar.gz",
93
+ "/cache/gts-mainline-master.tar.gz")
94
+ repo = StubRepository.new("gts/mainline")
95
+ d = EM::DefaultDeferrable.new
96
+ EMPessimistic::DeferrableChildProcess.expects(:open).returns(d)
97
+ d.succeed("", StubProcessStatus.new(0))
98
+
99
+ @archiver.archive(repo, "master", :tar)
100
+ end
101
+
102
+ it "does not move tarball when raising error" do
103
+ FileUtils.expects(:mv).with("/work/gts-mainline-master.tar.gz",
104
+ "/cache/gts-mainline-master.tar.gz").never
105
+ repo = StubRepository.new("gts/mainline")
106
+ d = EM::DefaultDeferrable.new
107
+ EMPessimistic::DeferrableChildProcess.expects(:open).returns(d)
108
+ d.fail("", StubProcessStatus.new(1))
109
+
110
+ @archiver.archive(repo, "master", :tar)
111
+ end
112
+
113
+ it "resolves promise with generated filename" do
114
+ FileUtils.stubs(:mv)
115
+ repo = StubRepository.new("gts/mainline")
116
+ d = EM::DefaultDeferrable.new
117
+ EMPessimistic::DeferrableChildProcess.expects(:open).returns(d)
118
+ d.succeed("", StubProcessStatus.new(0))
119
+
120
+ @archiver.archive(repo, "master", :tar).then do |filename|
121
+ assert_equal "/cache/gts-mainline-master.tar.gz", filename
122
+ done!
123
+ end
124
+ wait!
125
+ end
126
+
127
+ it "rejects promise when failing to archive" do
128
+ repo = StubRepository.new("gts/mainline")
129
+ d = EM::DefaultDeferrable.new
130
+ EMPessimistic::DeferrableChildProcess.expects(:open).returns(d)
131
+ d.fail("It done failed", StubProcessStatus.new(1))
132
+
133
+ @archiver.archive(repo, "master", :tar).errback do |err|
134
+ assert_match "It done failed", err.message
135
+ done!
136
+ end
137
+ wait!
138
+ end
139
+
140
+ it "does not spawn multiple identical processes" do
141
+ FileUtils.stubs(:mv)
142
+ repo = StubRepository.new("gts/mainline")
143
+ d = EM::DefaultDeferrable.new
144
+ EMPessimistic::DeferrableChildProcess.expects(:open).once.returns(d)
145
+ callbacks = 0
146
+ blk = lambda do |filename|
147
+ assert_equal "/cache/gts-mainline-master.tar.gz", filename
148
+ callbacks += 1
149
+ done! if callbacks == 2
150
+ end
151
+
152
+ @archiver.archive(repo, "master", :tar).then(&blk)
153
+ @archiver.archive(repo, "master", :tar).then(&blk)
154
+
155
+ wait!
156
+ d.succeed("", StubProcessStatus.new(0))
157
+ end
158
+
159
+ it "spawns new process when format is different" do
160
+ FileUtils.stubs(:mv)
161
+ repo = StubRepository.new("gts/mainline")
162
+ d = EM::DefaultDeferrable.new
163
+ EMPessimistic::DeferrableChildProcess.expects(:open).twice.returns(d)
164
+ callbacks = 0
165
+
166
+ @archiver.archive(repo, "master", :tar).then do |filename|
167
+ assert_equal "/cache/gts-mainline-master.tar.gz", filename
168
+ callbacks += 1
169
+ done! if callbacks == 2
170
+ end
171
+
172
+ @archiver.archive(repo, "master", :zip).then do |filename|
173
+ assert_equal "/cache/gts-mainline-master.zip", filename
174
+ callbacks += 1
175
+ done! if callbacks == 2
176
+ end
177
+
178
+ wait!
179
+ d.succeed("", StubProcessStatus.new(0))
180
+ end
181
+ end
182
+ end
@@ -21,6 +21,7 @@ require "libdolt/view"
21
21
  class Blob
22
22
  attr_reader :content
23
23
  def initialize(content); @content = content; end
24
+ def text(max_lines, encoding); content; end
24
25
  end
25
26
 
26
27
  describe "blob template" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libdolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-04 00:00:00.000000000 Z
12
+ date: 2013-01-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: eventmachine
@@ -235,6 +235,22 @@ dependencies:
235
235
  - - ~>
236
236
  - !ruby/object:Gem::Version
237
237
  version: '1.4'
238
+ - !ruby/object:Gem::Dependency
239
+ name: mocha
240
+ requirement: !ruby/object:Gem::Requirement
241
+ none: false
242
+ requirements:
243
+ - - ! '>='
244
+ - !ruby/object:Gem::Version
245
+ version: '0'
246
+ type: :development
247
+ prerelease: false
248
+ version_requirements: !ruby/object:Gem::Requirement
249
+ none: false
250
+ requirements:
251
+ - - ! '>='
252
+ - !ruby/object:Gem::Version
253
+ version: '0'
238
254
  description: Dolt API for serving git trees and syntax highlighted blobs
239
255
  email:
240
256
  - christian@gitorious.org
@@ -249,6 +265,7 @@ files:
249
265
  - Readme.md
250
266
  - lib/libdolt.rb
251
267
  - lib/libdolt/disk_repo_resolver.rb
268
+ - lib/libdolt/git/archiver.rb
252
269
  - lib/libdolt/git/blame.rb
253
270
  - lib/libdolt/git/commit.rb
254
271
  - lib/libdolt/git/repository.rb
@@ -274,6 +291,7 @@ files:
274
291
  - lib/libdolt/view/tree.rb
275
292
  - lib/libdolt/view/urls.rb
276
293
  - libdolt.gemspec
294
+ - test/libdolt/git/archiver_test.rb
277
295
  - test/libdolt/git/blame_test.rb
278
296
  - test/libdolt/git/commit_test.rb
279
297
  - test/libdolt/git/repository_test.rb