norbert-braid 0.4.9
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/LICENSE +20 -0
- data/README.rdoc +27 -0
- data/Rakefile +17 -0
- data/bin/braid +223 -0
- data/braid.gemspec +26 -0
- data/lib/braid/command.rb +119 -0
- data/lib/braid/commands/add.rb +44 -0
- data/lib/braid/commands/diff.rb +11 -0
- data/lib/braid/commands/remove.rb +23 -0
- data/lib/braid/commands/setup.rb +33 -0
- data/lib/braid/commands/update.rb +78 -0
- data/lib/braid/config.rb +101 -0
- data/lib/braid/mirror.rb +168 -0
- data/lib/braid/operations.rb +297 -0
- data/lib/braid.rb +23 -0
- data/test/braid_test.rb +7 -0
- data/test/config_test.rb +62 -0
- data/test/mirror_test.rb +94 -0
- data/test/operations_test.rb +69 -0
- data/test/test_helper.rb +15 -0
- metadata +95 -0
data/lib/braid/config.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'yaml/store'
|
3
|
+
|
4
|
+
module Braid
|
5
|
+
class Config
|
6
|
+
class PathAlreadyInUse < BraidError
|
7
|
+
def message
|
8
|
+
"path already in use: #{super}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
class MirrorDoesNotExist < BraidError
|
12
|
+
def message
|
13
|
+
"mirror does not exist: #{super}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(config_file = CONFIG_FILE)
|
18
|
+
@db = YAML::Store.new(config_file)
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_from_options(url, options)
|
22
|
+
mirror = Mirror.new_from_options(url, options)
|
23
|
+
|
24
|
+
add(mirror)
|
25
|
+
mirror
|
26
|
+
end
|
27
|
+
|
28
|
+
def mirrors
|
29
|
+
@db.transaction(true) do
|
30
|
+
@db.roots
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def get(path)
|
35
|
+
@db.transaction(true) do
|
36
|
+
if attributes = @db[path.to_s.sub(/\/$/, '')]
|
37
|
+
Mirror.new(path, attributes)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def get!(path)
|
43
|
+
mirror = get(path)
|
44
|
+
raise MirrorDoesNotExist, path unless mirror
|
45
|
+
mirror
|
46
|
+
end
|
47
|
+
|
48
|
+
def add(mirror)
|
49
|
+
@db.transaction do
|
50
|
+
raise PathAlreadyInUse, mirror.path if @db[mirror.path]
|
51
|
+
write_mirror(mirror)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def remove(mirror)
|
56
|
+
@db.transaction do
|
57
|
+
@db.delete(mirror.path)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def update(mirror)
|
62
|
+
@db.transaction do
|
63
|
+
raise MirrorDoesNotExist, mirror.path unless @db[mirror.path]
|
64
|
+
@db.delete(mirror.path)
|
65
|
+
write_mirror(mirror)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def valid?
|
70
|
+
@db.transaction(true) do
|
71
|
+
!@db.roots.any? do |path|
|
72
|
+
@db[path]["url"].nil?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def migrate!
|
78
|
+
@db.transaction do
|
79
|
+
@db.roots.each do |path|
|
80
|
+
attributes = @db[path]
|
81
|
+
if attributes["local_branch"]
|
82
|
+
attributes["url"] = attributes.delete("remote")
|
83
|
+
attributes["remote"] = attributes.delete("local_branch")
|
84
|
+
attributes["squashed"] = attributes.delete("squash")
|
85
|
+
attributes["lock"] = attributes["revision"] # so far this has always been true
|
86
|
+
end
|
87
|
+
@db[path] = clean_attributes(attributes)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def write_mirror(mirror)
|
94
|
+
@db[mirror.path] = clean_attributes(mirror.attributes)
|
95
|
+
end
|
96
|
+
|
97
|
+
def clean_attributes(hash)
|
98
|
+
hash.reject { |k,v| v.nil? }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/braid/mirror.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
module Braid
|
2
|
+
class Mirror
|
3
|
+
TYPES = %w(git svn)
|
4
|
+
ATTRIBUTES = %w(url remote type branch squashed revision lock)
|
5
|
+
|
6
|
+
class UnknownType < BraidError
|
7
|
+
def message
|
8
|
+
"unknown type: #{super}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
class CannotGuessType < BraidError
|
12
|
+
def message
|
13
|
+
"cannot guess type: #{super}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
class PathRequired < BraidError
|
17
|
+
def message
|
18
|
+
"path is required"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
include Operations::VersionControl
|
23
|
+
|
24
|
+
attr_reader :path, :attributes
|
25
|
+
|
26
|
+
def initialize(path, attributes = {})
|
27
|
+
@path = path.sub(/\/$/, '')
|
28
|
+
@attributes = attributes
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.new_from_options(url, options = {})
|
32
|
+
url.sub!(/\/$/, '')
|
33
|
+
|
34
|
+
branch = options["branch"] || "master"
|
35
|
+
|
36
|
+
if type = options["type"] || extract_type_from_url(url)
|
37
|
+
raise UnknownType, type unless TYPES.include?(type)
|
38
|
+
else
|
39
|
+
raise CannotGuessType, url
|
40
|
+
end
|
41
|
+
|
42
|
+
unless path = options["path"] || extract_path_from_url(url)
|
43
|
+
raise PathRequired
|
44
|
+
end
|
45
|
+
|
46
|
+
if options["rails_plugin"]
|
47
|
+
path = "vendor/plugins/#{path}"
|
48
|
+
end
|
49
|
+
|
50
|
+
remote = "braid/#{path}".gsub("_", '-') # stupid git svn changes all _ to ., weird
|
51
|
+
squashed = !options["full"]
|
52
|
+
branch = nil if type == "svn"
|
53
|
+
|
54
|
+
attributes = { "url" => url, "remote" => remote, "type" => type, "branch" => branch, "squashed" => squashed }
|
55
|
+
self.new(path, attributes)
|
56
|
+
end
|
57
|
+
|
58
|
+
def ==(comparison)
|
59
|
+
path == comparison.path && attributes == comparison.attributes
|
60
|
+
end
|
61
|
+
|
62
|
+
def type
|
63
|
+
# override Object#type
|
64
|
+
attributes["type"]
|
65
|
+
end
|
66
|
+
|
67
|
+
def locked?
|
68
|
+
!!lock
|
69
|
+
end
|
70
|
+
|
71
|
+
def squashed?
|
72
|
+
!!squashed
|
73
|
+
end
|
74
|
+
|
75
|
+
def merged?(commit)
|
76
|
+
# tip from spearce in #git:
|
77
|
+
# `test z$(git merge-base A B) = z$(git rev-parse --verify A)`
|
78
|
+
commit = git.rev_parse(commit)
|
79
|
+
if squashed?
|
80
|
+
!!base_revision && git.merge_base(commit, base_revision) == commit
|
81
|
+
else
|
82
|
+
git.merge_base(commit, "HEAD") == commit
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def diff
|
87
|
+
remote_hash = git.rev_parse("#{base_revision}:")
|
88
|
+
local_hash = git.tree_hash(path)
|
89
|
+
remote_hash != local_hash ? git.diff_tree(remote_hash, local_hash, path) : ""
|
90
|
+
end
|
91
|
+
|
92
|
+
def fetch
|
93
|
+
unless type == "svn"
|
94
|
+
git.fetch(remote)
|
95
|
+
else
|
96
|
+
git_svn.fetch(remote)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def method_missing(name, *args)
|
102
|
+
if ATTRIBUTES.find { |attribute| name.to_s =~ /^(#{attribute})(=)?$/ }
|
103
|
+
unless $2
|
104
|
+
attributes[$1]
|
105
|
+
else
|
106
|
+
attributes[$1] = args[0]
|
107
|
+
end
|
108
|
+
else
|
109
|
+
raise NameError, "unknown attribute `#{name}'"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def base_revision
|
114
|
+
if revision
|
115
|
+
unless type == "svn"
|
116
|
+
git.rev_parse(revision)
|
117
|
+
else
|
118
|
+
git_svn.commit_hash(remote, revision)
|
119
|
+
end
|
120
|
+
else
|
121
|
+
inferred_revision
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def inferred_revision
|
126
|
+
local_commits = git.rev_list("HEAD", "-- #{path}").split("\n")
|
127
|
+
remote_hashes = git.rev_list("--pretty=format:\"%T\"", remote).split("commit ").map do |chunk|
|
128
|
+
chunk.split("\n", 2).map { |value| value.strip }
|
129
|
+
end
|
130
|
+
hash = nil
|
131
|
+
local_commits.each do |local_commit|
|
132
|
+
local_tree = git.tree_hash(path, local_commit)
|
133
|
+
if match = remote_hashes.find { |_, remote_tree| local_tree == remote_tree }
|
134
|
+
hash = match[0]
|
135
|
+
break
|
136
|
+
end
|
137
|
+
end
|
138
|
+
hash
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.extract_type_from_url(url)
|
142
|
+
return nil unless url
|
143
|
+
url.sub!(/\/$/, '')
|
144
|
+
|
145
|
+
# check for git:// and svn:// URLs
|
146
|
+
url_scheme = url.split(":").first
|
147
|
+
return url_scheme if TYPES.include?(url_scheme)
|
148
|
+
|
149
|
+
return "svn" if url[-6..-1] == "/trunk"
|
150
|
+
return "git" if url[-4..-1] == ".git"
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.extract_path_from_url(url)
|
154
|
+
return nil unless url
|
155
|
+
name = File.basename(url)
|
156
|
+
|
157
|
+
if File.extname(name) == ".git"
|
158
|
+
# strip .git
|
159
|
+
name[0..-5]
|
160
|
+
elsif name == "trunk"
|
161
|
+
# use parent
|
162
|
+
File.basename(File.dirname(url))
|
163
|
+
else
|
164
|
+
name
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,297 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'open4'
|
4
|
+
|
5
|
+
module Braid
|
6
|
+
module Operations
|
7
|
+
class ShellExecutionError < BraidError
|
8
|
+
def initialize(err = nil)
|
9
|
+
@err = err
|
10
|
+
end
|
11
|
+
|
12
|
+
def message
|
13
|
+
@err.to_s.split("\n").first
|
14
|
+
end
|
15
|
+
end
|
16
|
+
class VersionTooLow < BraidError
|
17
|
+
def initialize(command, version)
|
18
|
+
@command = command
|
19
|
+
@version = version.to_s.split("\n").first
|
20
|
+
end
|
21
|
+
|
22
|
+
def message
|
23
|
+
"#{@command} version too low: #{@version}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
class UnknownRevision < BraidError
|
27
|
+
def message
|
28
|
+
"unknown revision: #{super}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
class LocalChangesPresent < BraidError
|
32
|
+
def message
|
33
|
+
"local changes are present"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# The command proxy is meant to encapsulate commands such as git, git-svn and svn, that work with subcommands.
|
38
|
+
class Proxy
|
39
|
+
include Singleton
|
40
|
+
|
41
|
+
def self.command; name.split('::').last.downcase; end # hax!
|
42
|
+
|
43
|
+
def version
|
44
|
+
status, out, err = exec!("#{self.class.command} --version")
|
45
|
+
out.sub(/^.* version/, "").strip
|
46
|
+
end
|
47
|
+
|
48
|
+
def require_version(required)
|
49
|
+
required = required.split(".")
|
50
|
+
actual = version.split(".")
|
51
|
+
|
52
|
+
actual.each_with_index do |actual_piece, idx|
|
53
|
+
required_piece = required[idx]
|
54
|
+
|
55
|
+
return true unless required_piece
|
56
|
+
|
57
|
+
case (actual_piece <=> required_piece)
|
58
|
+
when -1
|
59
|
+
return false
|
60
|
+
when 1
|
61
|
+
return true
|
62
|
+
when 0
|
63
|
+
next
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
return actual.length >= required.length
|
68
|
+
end
|
69
|
+
|
70
|
+
def require_version!(required)
|
71
|
+
require_version(required) || raise(VersionTooLow.new(self.class.command, version))
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def command(name)
|
76
|
+
# stub
|
77
|
+
name
|
78
|
+
end
|
79
|
+
|
80
|
+
def invoke(arg, *args)
|
81
|
+
exec!("#{command(arg)} #{args.join(' ')}".strip)[1].strip # return stdout
|
82
|
+
end
|
83
|
+
|
84
|
+
def method_missing(name, *args)
|
85
|
+
invoke(name, *args)
|
86
|
+
end
|
87
|
+
|
88
|
+
def exec(cmd)
|
89
|
+
cmd.strip!
|
90
|
+
|
91
|
+
previous_lang = ENV['LANG']
|
92
|
+
ENV['LANG'] = 'C'
|
93
|
+
|
94
|
+
out, err = nil
|
95
|
+
status = Open4.popen4(cmd) do |pid, stdin, stdout, stderr|
|
96
|
+
out = stdout.read
|
97
|
+
err = stderr.read
|
98
|
+
end.exitstatus
|
99
|
+
[status, out, err]
|
100
|
+
|
101
|
+
ensure
|
102
|
+
ENV['LANG'] = previous_lang
|
103
|
+
end
|
104
|
+
|
105
|
+
def exec!(cmd)
|
106
|
+
status, out, err = exec(cmd)
|
107
|
+
raise ShellExecutionError, err unless status == 0
|
108
|
+
[status, out, err]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class Git < Proxy
|
113
|
+
def commit(message, *args)
|
114
|
+
status, out, err = exec("git commit -m #{message.inspect} --no-verify #{args.join(' ')}")
|
115
|
+
|
116
|
+
if status == 0
|
117
|
+
true
|
118
|
+
elsif out.match(/nothing.* to commit/)
|
119
|
+
false
|
120
|
+
else
|
121
|
+
raise ShellExecutionError, err
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def fetch(remote)
|
126
|
+
# open4 messes with the pipes of index-pack
|
127
|
+
system("git fetch -n #{remote} &> /dev/null")
|
128
|
+
raise ShellExecutionError, "could not fetch" unless $? == 0
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
def checkout(treeish)
|
133
|
+
# TODO debug
|
134
|
+
msg "Checking out '#{treeish}'."
|
135
|
+
invoke(:checkout, treeish)
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns the base commit or nil.
|
140
|
+
def merge_base(target, source)
|
141
|
+
invoke(:merge_base, target, source)
|
142
|
+
rescue ShellExecutionError
|
143
|
+
nil
|
144
|
+
end
|
145
|
+
|
146
|
+
def rev_parse(opt)
|
147
|
+
invoke(:rev_parse, opt)
|
148
|
+
rescue ShellExecutionError
|
149
|
+
raise UnknownRevision, opt
|
150
|
+
end
|
151
|
+
|
152
|
+
# Implies tracking.
|
153
|
+
def remote_add(remote, path, branch)
|
154
|
+
invoke(:remote, "add", "-t #{branch} -m #{branch}", remote, path)
|
155
|
+
true
|
156
|
+
end
|
157
|
+
|
158
|
+
# Checks git and svn remotes.
|
159
|
+
def remote_exists?(remote)
|
160
|
+
# TODO clean up and maybe return more information
|
161
|
+
!!File.readlines(".git/config").find { |line| line =~ /^\[(svn-)?remote "#{Regexp.escape(remote)}"\]/ }
|
162
|
+
end
|
163
|
+
|
164
|
+
def reset_hard(target)
|
165
|
+
invoke(:reset, "--hard", target)
|
166
|
+
true
|
167
|
+
end
|
168
|
+
|
169
|
+
# Implies no commit.
|
170
|
+
def merge_ours(opt)
|
171
|
+
invoke(:merge, "-s ours --no-commit", opt)
|
172
|
+
true
|
173
|
+
end
|
174
|
+
|
175
|
+
# Implies no commit.
|
176
|
+
def merge_subtree(opt)
|
177
|
+
# TODO which options are needed?
|
178
|
+
invoke(:merge, "-s subtree --no-commit --no-ff", opt)
|
179
|
+
true
|
180
|
+
end
|
181
|
+
|
182
|
+
def read_tree(treeish, prefix)
|
183
|
+
invoke(:read_tree, "--prefix=#{prefix}/ -u", treeish)
|
184
|
+
true
|
185
|
+
end
|
186
|
+
|
187
|
+
def rm_r(path)
|
188
|
+
invoke(:rm, "-r", path)
|
189
|
+
true
|
190
|
+
end
|
191
|
+
|
192
|
+
def tree_hash(path, treeish = "HEAD")
|
193
|
+
out = invoke(:ls_tree, treeish, "-d", path)
|
194
|
+
out.split[2]
|
195
|
+
end
|
196
|
+
|
197
|
+
def diff_tree(src_tree, dst_tree, prefix = nil)
|
198
|
+
cmd = "git diff-tree -p --binary #{src_tree} #{dst_tree}"
|
199
|
+
cmd << " --src-prefix=a/#{prefix}/ --dst-prefix=b/#{prefix}/" if prefix
|
200
|
+
status, out, err = exec!(cmd)
|
201
|
+
out
|
202
|
+
end
|
203
|
+
|
204
|
+
def status_clean?
|
205
|
+
status, out, err = exec("git status")
|
206
|
+
!out.split("\n").grep(/nothing to commit/).empty?
|
207
|
+
end
|
208
|
+
|
209
|
+
def ensure_clean!
|
210
|
+
status_clean? || raise(LocalChangesPresent)
|
211
|
+
end
|
212
|
+
|
213
|
+
def head
|
214
|
+
rev_parse("HEAD")
|
215
|
+
end
|
216
|
+
|
217
|
+
def branch
|
218
|
+
status, out, err = exec!("git branch | grep '*'")
|
219
|
+
out[2..-1]
|
220
|
+
end
|
221
|
+
|
222
|
+
def apply(diff, *args)
|
223
|
+
err = nil
|
224
|
+
status = Open4.popen4("git apply --index --whitespace=nowarn #{args.join(' ')} -") do |pid, stdin, stdout, stderr|
|
225
|
+
stdin.puts(diff)
|
226
|
+
stdin.close
|
227
|
+
|
228
|
+
err = stderr.read
|
229
|
+
end.exitstatus
|
230
|
+
raise ShellExecutionError, err unless status == 0
|
231
|
+
true
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
def command(name)
|
236
|
+
"#{self.class.command} #{name.to_s.gsub('_', '-')}"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
class GitSvn < Proxy
|
241
|
+
def self.command; "git svn"; end
|
242
|
+
|
243
|
+
def commit_hash(remote, revision)
|
244
|
+
out = invoke(:log, "--show-commit --oneline", "-r #{revision}", remote)
|
245
|
+
part = out.to_s.split(" | ")[1]
|
246
|
+
raise UnknownRevision, "r#{revision}" unless part
|
247
|
+
Git.instance.rev_parse(part) # FIXME ugly ugly ugly
|
248
|
+
end
|
249
|
+
|
250
|
+
def fetch(remote)
|
251
|
+
# open4 messes with the pipes of index-pack
|
252
|
+
system("git svn fetch #{remote} &> /dev/null")
|
253
|
+
raise ShellExecutionError, "could not fetch" unless $? == 0
|
254
|
+
true
|
255
|
+
end
|
256
|
+
|
257
|
+
def init(remote, path)
|
258
|
+
invoke(:init, "-R", remote, "--id=#{remote}", path)
|
259
|
+
true
|
260
|
+
end
|
261
|
+
|
262
|
+
private
|
263
|
+
def command(name)
|
264
|
+
"#{self.class.command} #{name}"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
class Svn < Proxy
|
269
|
+
def clean_revision(revision)
|
270
|
+
revision.to_i if revision
|
271
|
+
end
|
272
|
+
|
273
|
+
def head_revision(path)
|
274
|
+
# not using svn info because it's retarded and doesn't show the actual last changed rev for the url
|
275
|
+
# git svn has no clue on how to get the actual HEAD revision number on it's own
|
276
|
+
status, out, err = exec!("svn log -q --limit 1 #{path}")
|
277
|
+
out.split(/\n/).find { |x| x.match /^r\d+/ }.split(" | ")[0][1..-1].to_i
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
module VersionControl
|
282
|
+
def git
|
283
|
+
Git.instance
|
284
|
+
end
|
285
|
+
|
286
|
+
def git_svn
|
287
|
+
GitSvn.instance
|
288
|
+
end
|
289
|
+
|
290
|
+
def svn
|
291
|
+
Svn.instance
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
|
data/lib/braid.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
module Braid
|
4
|
+
VERSION = "0.4.9"
|
5
|
+
|
6
|
+
CONFIG_FILE = ".braids"
|
7
|
+
REQUIRED_GIT_VERSION = "1.5.4.5"
|
8
|
+
|
9
|
+
class BraidError < StandardError
|
10
|
+
def message
|
11
|
+
value = super
|
12
|
+
value if value != self.class.name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'braid/operations'
|
18
|
+
require 'braid/mirror'
|
19
|
+
require 'braid/config'
|
20
|
+
require 'braid/command'
|
21
|
+
Dir[File.dirname(__FILE__) + '/braid/commands/*'].each do |file|
|
22
|
+
require file
|
23
|
+
end
|
data/test/braid_test.rb
ADDED
data/test/config_test.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
describe_shared "Braid::Config, in general" do
|
4
|
+
db = "tmp.yml"
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@config = Braid::Config.new(db)
|
8
|
+
end
|
9
|
+
|
10
|
+
after(:each) do
|
11
|
+
FileUtils.rm(db) rescue nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "Braid::Config, when empty" do
|
16
|
+
it_should_behave_like "Braid::Config, in general"
|
17
|
+
|
18
|
+
it "should not get a mirror by name" do
|
19
|
+
@config.get("path").should.be.nil
|
20
|
+
lambda { @config.get!("path") }.should.raise(Braid::Config::MirrorDoesNotExist)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should add a mirror and its params" do
|
24
|
+
@mirror = build_mirror
|
25
|
+
@config.add(@mirror)
|
26
|
+
@config.get("path").path.should.not.be.nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "Braid::Config, with one mirror" do
|
31
|
+
it_should_behave_like "Braid::Config, in general"
|
32
|
+
|
33
|
+
before(:each) do
|
34
|
+
@mirror = build_mirror
|
35
|
+
@config.add(@mirror)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should get the mirror by name" do
|
39
|
+
@config.get("path").should == @mirror
|
40
|
+
@config.get!("path").should == @mirror
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should raise when trying to overwrite a mirror on add" do
|
44
|
+
lambda { @config.add(@mirror) }.should.raise(Braid::Config::PathAlreadyInUse)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should remove the mirror" do
|
48
|
+
@config.remove(@mirror)
|
49
|
+
@config.get("path").should.be.nil
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should update the mirror with new params" do
|
53
|
+
@mirror.branch = "other"
|
54
|
+
@config.update(@mirror)
|
55
|
+
@config.get("path").attributes.should == { "branch" => "other" }
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should raise when trying to update nonexistent mirror" do
|
59
|
+
@mirror.instance_variable_set("@path", "other")
|
60
|
+
lambda { @config.update(@mirror) }.should.raise(Braid::Config::MirrorDoesNotExist)
|
61
|
+
end
|
62
|
+
end
|