between_meals 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +201 -0
- data/README.md +15 -22
- data/lib/between_meals/changes/change.rb +46 -0
- data/lib/between_meals/changes/cookbook.rb +82 -0
- data/lib/between_meals/changes/databag.rb +36 -0
- data/lib/between_meals/changes/role.rb +37 -0
- data/lib/between_meals/changeset.rb +48 -0
- data/lib/between_meals/knife.rb +197 -0
- data/lib/between_meals/repo/git.rb +204 -0
- data/lib/between_meals/repo/svn.rb +121 -0
- data/lib/between_meals/repo.rb +113 -0
- data/lib/between_meals/util.rb +74 -0
- metadata +121 -32
- checksums.yaml +0 -7
- data/.gitignore +0 -17
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -22
- data/Rakefile +0 -1
- data/between_meals.gemspec +0 -23
- data/lib/between_meals/version.rb +0 -3
- data/lib/between_meals.rb +0 -5
@@ -0,0 +1,197 @@
|
|
1
|
+
# vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'digest/md5'
|
6
|
+
require 'between_meals/util'
|
7
|
+
|
8
|
+
module BetweenMeals
|
9
|
+
# Knife does not have a usable API for using it as a lib
|
10
|
+
# This could be possibly refactored to touch its internals
|
11
|
+
# instead of shelling out
|
12
|
+
class Knife
|
13
|
+
include BetweenMeals::Util
|
14
|
+
|
15
|
+
def initialize(opts = {})
|
16
|
+
@logger = opts[:logger] || nil
|
17
|
+
@user = opts[:user] || ENV['USER']
|
18
|
+
@home = opts[:home] || ENV['HOME']
|
19
|
+
@host = opts[:host] || 'localhost'
|
20
|
+
@port = opts[:port] || 4000
|
21
|
+
@config = opts[:config] ||
|
22
|
+
"#{@home}/.chef/knife-#{@user}-taste-tester.rb"
|
23
|
+
@knife = opts[:bin] || 'knife'
|
24
|
+
@pem = opts[:pem] ||
|
25
|
+
"#{@home}/.chef/#{@user}-taste-tester.pem"
|
26
|
+
@role_dir = opts[:role_dir]
|
27
|
+
@cookbook_dirs = opts[:cookbook_dirs]
|
28
|
+
@databag_dir = opts[:databag_dir]
|
29
|
+
@checksum_dir = opts[:checksum_dir]
|
30
|
+
@client_key =
|
31
|
+
File.expand_path("#{@home}/.chef/#{@user}-taste-tester.pem")
|
32
|
+
end
|
33
|
+
|
34
|
+
def role_upload_all
|
35
|
+
roles = File.join(@role_dir, '*.rb')
|
36
|
+
exec!("#{@knife} role from file #{roles} -c #{@config}", @logger)
|
37
|
+
end
|
38
|
+
|
39
|
+
def role_upload(roles)
|
40
|
+
if roles.any?
|
41
|
+
roles = roles.map { |x| File.join(@role_dir, "#{x.name}.rb") }.join(' ')
|
42
|
+
exec!("#{@knife} role from file #{roles} -c #{@config}", @logger)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def role_delete(roles)
|
47
|
+
if roles.any?
|
48
|
+
roles.each do |role|
|
49
|
+
exec!(
|
50
|
+
"#{@knife} role delete #{role.name} --yes -c #{@config}", @logger
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def cookbook_upload_all
|
57
|
+
exec!("#{@knife} cookbook upload -a -c #{@config}", @logger)
|
58
|
+
end
|
59
|
+
|
60
|
+
def cookbook_upload(cookbooks)
|
61
|
+
if cookbooks.any?
|
62
|
+
cookbooks = cookbooks.map { |x| x.name }.join(' ')
|
63
|
+
exec!("#{@knife} cookbook upload #{cookbooks} -c #{@config}", @logger)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def cookbook_delete(cookbooks)
|
68
|
+
if cookbooks.any?
|
69
|
+
cookbooks.each do |cookbook|
|
70
|
+
exec!("#{@knife} cookbook delete #{cookbook.name}" +
|
71
|
+
" --purge --yes -c #{@config}", @logger)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def databag_upload_all
|
77
|
+
glob = File.join(@databag_dir, '*', '*.json')
|
78
|
+
items = Dir.glob(glob).map do |file|
|
79
|
+
BetweenMeals::Changes::Databag.new(
|
80
|
+
{ :status => :modified, :path => file }, @databag_dir
|
81
|
+
)
|
82
|
+
end
|
83
|
+
databag_upload(items)
|
84
|
+
end
|
85
|
+
|
86
|
+
def databag_upload(databags)
|
87
|
+
if databags.any?
|
88
|
+
databags.group_by { |x| x.name }.each do |dbname, dbs|
|
89
|
+
create_databag_if_missing(dbname)
|
90
|
+
dbitems = dbs.map do |x|
|
91
|
+
File.join(@databag_dir, dbname, "#{x.item}.json")
|
92
|
+
end.join(' ')
|
93
|
+
exec!("#{@knife} data bag from file #{dbname} #{dbitems}", @logger)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def databag_delete(databags)
|
99
|
+
if databags.any?
|
100
|
+
databags.group_by { |x| x.name }.each do |dbname, dbs|
|
101
|
+
dbs.each do |db|
|
102
|
+
exec!("#{@knife} data bag delete #{dbname} #{db.item}" +
|
103
|
+
" --yes -c #{@config}", @logger)
|
104
|
+
end
|
105
|
+
delete_databag_if_empty(dbname)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def write_user_config
|
111
|
+
cfg = <<-BLOCK
|
112
|
+
user = ENV['USER']
|
113
|
+
log_level :info
|
114
|
+
log_location STDOUT
|
115
|
+
node_name user
|
116
|
+
chef_server_url "http://#{@host}:#{@port}"
|
117
|
+
cache_type 'BasicFile'
|
118
|
+
client_key '#{@client_key}'
|
119
|
+
cache_options(:path => File.expand_path("#{@checksum_dir}"))
|
120
|
+
cookbook_path [
|
121
|
+
BLOCK
|
122
|
+
@cookbook_dirs.each do |dir|
|
123
|
+
cfg << " \"#{dir}\",\n"
|
124
|
+
end
|
125
|
+
cfg << "]\n"
|
126
|
+
unless File.directory?(File.dirname(@config))
|
127
|
+
Dir.mkdir(File.dirname(@config), 0755)
|
128
|
+
end
|
129
|
+
if !File.exists?(@config) ||
|
130
|
+
::Digest::MD5.hexdigest(cfg) !=
|
131
|
+
::Digest::MD5.hexdigest(File.read(@config))
|
132
|
+
@logger.info("Generating #{@config}")
|
133
|
+
File.write(@config, cfg)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Won't work with shorter keys
|
137
|
+
pem = <<-BLOCK
|
138
|
+
-----BEGIN PRIVATE KEY-----
|
139
|
+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCs4Ih8+R/2hcYS
|
140
|
+
tccwJHd0cXHcUibC2wGYmRwf1fKxXLADvfuLRVBHOI5Hgd/ZXF70dowC5mDQ03gr
|
141
|
+
ouk8e7RL72MCKzPuG2V92sh/FnyKhkNsHCOEKaRRiP9lHVbZkS9LEotCKF7eOkL0
|
142
|
+
SVkGWx8pVZzrOFmhZgHaOFJ2/2t1irUTRFqTikrRsP2KvnhHdDlnnbUumZWxSuEN
|
143
|
+
oN6aSAQEOkKbEOLSn/EIMzEb2jtks7L7wkRErajH094jGoZbQvLiwRHDeM0C9uG7
|
144
|
+
2sdQ45BG9EQOCdBzy1We5keqtJbXBcpwuBa0d1nQZIsGxnDb88+Kmh9h6k9/WmYN
|
145
|
+
zEQEeSSdAgMBAAECggEATFWQru4p6ObEwTo2y9EuVeJJzmkP6HZfzAu/WWdVFG/C
|
146
|
+
4MQgsCxY+DnGyVhViVq6KuO1iwpCsbLOmyYCKszMncMESs7czUSXmezjHwrEzz3d
|
147
|
+
w3zhSdhBUCdX7kP4N3VeFp4Hk5zT1viO2+MPRjkyF0RQV6S4HwY1xy+baiP6RRnS
|
148
|
+
dGhUYsdz6fjxSkYEQy3/xHm9VLT6ZDV4pN2aA+LOFeveHKcnOjKFCBy4WzkO6fvj
|
149
|
+
6H3jghxsHXoL7loCHfi9WX3xKjeXG/NjGbUfTH8P7IldUPha+ru/e8W/P+jjE1os
|
150
|
+
VkScWt08Vu6iTl1EkYeFxOMtSDZxeXNnDkPI0iDQgQKBgQDUMFYncQcZ4uLIfoZq
|
151
|
+
B+Lx7WJGlIwulOdobnraTd5lsrBHj1sC1+/f4cBSpJwUij2l3GdmmnOpuFAof5eu
|
152
|
+
mrBGu++5jy+0eIeT5O2d30O8GOBryJ+oAKI2/BPVCmM8d986wl5Esauycb++O7UO
|
153
|
+
RhpZFOCKbFvlNjhg+CdlvHSl7QKBgQDQkkvpnE//yWmhCPg27n7w3bTg5QPNrzTO
|
154
|
+
pF2iwvLK4XjRceTeW3P4f42HONzJNnmt5TexM9NbdE9g/exA5uNt59ZB5FeFiKAu
|
155
|
+
NmVXbmswPX6R/dlyidqzz1guGrL04e0dZehHZBNDr5Sio8IBjMWrpDIxjDJqEwUa
|
156
|
+
4qCu4e6jcQKBgQDN0FTAzRFmOnxenNsj3aJzpx27+DpAtI4A7aicNwuQ+VGjF5nf
|
157
|
+
mDRDpGU3xBLgmXZSewaQrx+hb/XQUnJ+Ge0BrylHg2tyUbav7U3N49F/kWGdKmwy
|
158
|
+
OOsfCkLyUbEP5fXQuNdXKj6wR0UE8EUeI0FLRsTFf3VjTsRAynLsa295wQKBgAo3
|
159
|
+
QDSfDWQP73aNw+qc3+bYVSW20erfLAz7DAMO3WmGha5sj7M8c3+2b64x4M6SNn+H
|
160
|
+
/KRXT4DpP4IWrd238WfOtTXhA1BtErtwuqH/rIxeVra74kyz59xqyXzond9UuZJ5
|
161
|
+
DVmB01e7X+Jfdv8wb/YqQrMelNGRQOzCMPCf7FphAoGAbUh5HzNF2aciQJGA6Qk8
|
162
|
+
zvgEHqbS0/QkJGOZ+UifPRanTDuGYQkPdHHOER4UghbM+Kz5rZbBicJ3bCyNOsah
|
163
|
+
IAMAEpsWX2s2A6phgMCx7kH6wMmoZn3hb7Thh9+PfR8Jtp2/7k+ibCeF4gEWUCs5
|
164
|
+
6wX4GR84dwyhG80yd4TP8Qo=
|
165
|
+
-----END PRIVATE KEY-----
|
166
|
+
BLOCK
|
167
|
+
|
168
|
+
unless File.exists?(@pem)
|
169
|
+
@logger.info("Generating #{@pem}")
|
170
|
+
File.write(@pem, pem)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def create_databag_if_missing(databag)
|
177
|
+
s = Mixlib::ShellOut.new("#{@knife} data bag list" +
|
178
|
+
" --format json -c #{@config}").run_command
|
179
|
+
s.error!
|
180
|
+
db = JSON.load(s.stdout)
|
181
|
+
unless db.include?(databag)
|
182
|
+
exec!("#{@knife} data bag create #{databag} -c #{@config}", @logger)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def delete_databag_if_empty(databag)
|
187
|
+
s = Mixlib::ShellOut.new("#{@knife} data bag show #{databag}" +
|
188
|
+
" --format json -c #{@config}").run_command
|
189
|
+
s.error!
|
190
|
+
db = JSON.load(s.stdout)
|
191
|
+
if db.empty?
|
192
|
+
exec!("#{@knife} data bag delete #{databag} --yes -c #{@config}",
|
193
|
+
@logger)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
# vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
|
2
|
+
|
3
|
+
require 'rugged'
|
4
|
+
require 'mixlib/shellout'
|
5
|
+
require 'between_meals/changeset'
|
6
|
+
|
7
|
+
module BetweenMeals
|
8
|
+
# Local checkout wrapper
|
9
|
+
class Repo
|
10
|
+
# Git provider
|
11
|
+
class Git < BetweenMeals::Repo
|
12
|
+
def setup
|
13
|
+
if File.exists?(File.expand_path(@repo_path))
|
14
|
+
@repo = Rugged::Repository.new(File.expand_path(@repo_path))
|
15
|
+
else
|
16
|
+
@repo = nil
|
17
|
+
end
|
18
|
+
@bin = 'git'
|
19
|
+
end
|
20
|
+
|
21
|
+
def exists?
|
22
|
+
@repo && !@repo.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def head_rev
|
26
|
+
@repo.head.target.oid
|
27
|
+
end
|
28
|
+
|
29
|
+
def last_msg
|
30
|
+
@repo.head.target.message
|
31
|
+
end
|
32
|
+
|
33
|
+
def last_msg=(msg)
|
34
|
+
@repo.head.target.amend(
|
35
|
+
{
|
36
|
+
:message => msg,
|
37
|
+
:update_ref => 'HEAD',
|
38
|
+
}
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def last_author
|
43
|
+
@repo.head.target.to_hash[:author]
|
44
|
+
end
|
45
|
+
|
46
|
+
def head_parents
|
47
|
+
@repo.head.target.parents
|
48
|
+
end
|
49
|
+
|
50
|
+
def checkout(url)
|
51
|
+
s = Mixlib::ShellOut.new(
|
52
|
+
"#{@bin} clone #{url} #{@repo} #{@repo_path}"
|
53
|
+
).run_command
|
54
|
+
s.error!
|
55
|
+
@repo = Rugged::Repository.new(File.expand_path(@repo_path))
|
56
|
+
end
|
57
|
+
|
58
|
+
# Return files changed between two revisions
|
59
|
+
def changes(start_ref, end_ref)
|
60
|
+
check_refs(start_ref, end_ref)
|
61
|
+
s = Mixlib::ShellOut.new(
|
62
|
+
"#{@bin} diff --name-status #{start_ref} #{end_ref}",
|
63
|
+
:cwd => File.expand_path(@repo_path)
|
64
|
+
)
|
65
|
+
s.run_command.error!
|
66
|
+
begin
|
67
|
+
parse_status(s.stdout).compact
|
68
|
+
rescue => e
|
69
|
+
# We've seen some weird non-reproducible failures here
|
70
|
+
@logger.error(
|
71
|
+
'Something went wrong. Please please report this output.'
|
72
|
+
)
|
73
|
+
@logger.error(e)
|
74
|
+
s.stdout.lines.each do |line|
|
75
|
+
@logger.error(line.strip)
|
76
|
+
end
|
77
|
+
exit(1)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def update
|
82
|
+
cmd = Mixlib::ShellOut.new(
|
83
|
+
"#{@bin} pull --rebase", :cwd => File.expand_path(@repo_path)
|
84
|
+
)
|
85
|
+
cmd.run_command
|
86
|
+
if cmd.exitstatus != 0
|
87
|
+
@logger.error('Something went wrong with git!')
|
88
|
+
@logger.error(cmd.stdout)
|
89
|
+
fail
|
90
|
+
end
|
91
|
+
cmd.stdout
|
92
|
+
end
|
93
|
+
|
94
|
+
# Return all files
|
95
|
+
def files
|
96
|
+
@repo.index.map { |x| { :path => x[:path], :status => :created } }
|
97
|
+
end
|
98
|
+
|
99
|
+
def status
|
100
|
+
cmd = Mixlib::ShellOut.new(
|
101
|
+
"#{@bin} status --porcelain 2>&1",
|
102
|
+
:cwd => File.expand_path(@repo_path)
|
103
|
+
)
|
104
|
+
cmd.run_command
|
105
|
+
if cmd.exitstatus != 0
|
106
|
+
@logger.error('Something went wrong with git!')
|
107
|
+
@logger.error(cmd.stdout)
|
108
|
+
fail
|
109
|
+
end
|
110
|
+
cmd.stdout
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def check_refs(start_ref, end_ref)
|
116
|
+
unless @repo.exists?(start_ref)
|
117
|
+
fail Changeset::ReferenceError
|
118
|
+
end
|
119
|
+
unless end_ref.nil?
|
120
|
+
unless @repo.exists?(end_ref)
|
121
|
+
fail Changeset::ReferenceError
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def parse_status(changes)
|
127
|
+
# man git-diff-files
|
128
|
+
# Possible status letters are:
|
129
|
+
#
|
130
|
+
# A: addition of a file
|
131
|
+
# C: copy of a file into a new one
|
132
|
+
# D: deletion of a file
|
133
|
+
# M: modification of the contents or mode of a file
|
134
|
+
# R: renaming of a file
|
135
|
+
# T: change in the type of the file
|
136
|
+
# U: file is unmerged (you must complete the merge before it can
|
137
|
+
# be committed)
|
138
|
+
# X: "unknown" change type (most probably a bug, please report it)
|
139
|
+
|
140
|
+
# rubocop:disable MultilineBlockChain
|
141
|
+
changes.lines.map do |line|
|
142
|
+
case line
|
143
|
+
when /^A\s+(\S+)$/
|
144
|
+
# A path
|
145
|
+
{
|
146
|
+
:status => :modified,
|
147
|
+
:path => Regexp.last_match(1)
|
148
|
+
}
|
149
|
+
when /^C(?:\d*)\s+(\S+)\s+(\S+)/
|
150
|
+
# C<numbers> path1 path2
|
151
|
+
{
|
152
|
+
:status => :modified,
|
153
|
+
:path => Regexp.last_match(2)
|
154
|
+
}
|
155
|
+
when /^D\s+(\S+)$/
|
156
|
+
# D path
|
157
|
+
{
|
158
|
+
:status => :deleted,
|
159
|
+
:path => Regexp.last_match(1)
|
160
|
+
}
|
161
|
+
when /^M(?:\d*)\s+(\S+)$/
|
162
|
+
# M<numbers> path
|
163
|
+
{
|
164
|
+
:status => :modified,
|
165
|
+
:path => Regexp.last_match(1)
|
166
|
+
}
|
167
|
+
when /^R(?:\d*)\s+(\S+)\s+(\S+)/
|
168
|
+
# R<numbers> path1 path2
|
169
|
+
[
|
170
|
+
{
|
171
|
+
:status => :deleted,
|
172
|
+
:path => Regexp.last_match(1)
|
173
|
+
},
|
174
|
+
{
|
175
|
+
:status => :modified,
|
176
|
+
:path => Regexp.last_match(2)
|
177
|
+
}
|
178
|
+
]
|
179
|
+
when /^T\s+(\S+)$/
|
180
|
+
# T path
|
181
|
+
[
|
182
|
+
{
|
183
|
+
:status => :deleted,
|
184
|
+
:path => Regexp.last_match(1)
|
185
|
+
},
|
186
|
+
{
|
187
|
+
:status => :modified,
|
188
|
+
:path => Regexp.last_match(1)
|
189
|
+
}
|
190
|
+
]
|
191
|
+
else
|
192
|
+
fail 'No match'
|
193
|
+
end
|
194
|
+
end.flatten.map do |x|
|
195
|
+
{
|
196
|
+
:status => x[:status],
|
197
|
+
:path => x[:path].sub("#{@repo_path}/", '')
|
198
|
+
}
|
199
|
+
end
|
200
|
+
# rubocop:enable MultilineBlockChain
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
|
2
|
+
|
3
|
+
require 'between_meals/repo'
|
4
|
+
require 'between_meals/changeset'
|
5
|
+
require 'mixlib/shellout'
|
6
|
+
|
7
|
+
module BetweenMeals
|
8
|
+
# Local checkout wrapper
|
9
|
+
class Repo
|
10
|
+
# SVN implementation
|
11
|
+
class Svn < BetweenMeals::Repo
|
12
|
+
def setup
|
13
|
+
@bin = 'svn'
|
14
|
+
end
|
15
|
+
|
16
|
+
def exists?
|
17
|
+
# this shuold be better
|
18
|
+
Dir.exists?(@repo_path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def head_rev
|
22
|
+
s = Mixlib::ShellOut.new("#{@bin} info #{@repo_path}").run_command
|
23
|
+
s.error!
|
24
|
+
s.stdout.each_line do |line|
|
25
|
+
m = line.match(/Last Changed Rev: (\d+)$/)
|
26
|
+
return m[1] if m
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def latest_revision
|
31
|
+
s = Mixlib::ShellOut.new("#{@bin} info #{@repo_path}").run_command
|
32
|
+
s.error!
|
33
|
+
s.stdout.each do |line|
|
34
|
+
m = line.match(/Revision: (\d+)$/)
|
35
|
+
return m[1] if m
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def checkout(url)
|
40
|
+
s = Mixlib::ShellOut.new(
|
41
|
+
"#{@bin} co --ignore-externals #{url} #{@repo_path}").run_command
|
42
|
+
s.error!
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return files changed between two revisions
|
46
|
+
def changes(start_ref, end_ref)
|
47
|
+
check_refs(start_ref, end_ref)
|
48
|
+
s = Mixlib::ShellOut.new(
|
49
|
+
"#{@bin} diff -r #{start_ref}:#{end_ref} --summarize #{@repo_path}")
|
50
|
+
s.run_command.error!
|
51
|
+
@logger.info("Diff between #{start_ref} and #{end_ref}")
|
52
|
+
s.stdout.lines.map do |line|
|
53
|
+
m = line.match(/^(\w)\w?\s+(\S+)$/)
|
54
|
+
fail "Could not parse line: #{line}" unless m
|
55
|
+
|
56
|
+
{
|
57
|
+
:status => m[1] == 'D' ? :deleted : :modified,
|
58
|
+
:path => m[2].sub("#{@repo_path}/", '')
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def update
|
64
|
+
cleanup
|
65
|
+
revert
|
66
|
+
up
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return all files
|
70
|
+
def files
|
71
|
+
s = Mixlib::ShellOut.new("#{@bin} ls --depth infinity #{@repo_path}")
|
72
|
+
s.run_command
|
73
|
+
s.error!
|
74
|
+
s.stdout.split("\n").map do |x|
|
75
|
+
{ :path => x, :status => :created }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def run(cmd)
|
82
|
+
Mixlib::ShellOut.new(cmd).run_command.error!
|
83
|
+
end
|
84
|
+
|
85
|
+
def revert
|
86
|
+
run("#{@bin} revert -R #{@repo_path}")
|
87
|
+
end
|
88
|
+
|
89
|
+
def up
|
90
|
+
run("#{@bin} update #{@repo_path}")
|
91
|
+
end
|
92
|
+
|
93
|
+
def cleanup
|
94
|
+
run("#{@bin} cleanup #{@repo_path}")
|
95
|
+
end
|
96
|
+
|
97
|
+
def first_revision
|
98
|
+
0
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def check_refs(start_ref, end_ref)
|
104
|
+
s = Mixlib::ShellOut.new(
|
105
|
+
"#{@bin} info -r #{start_ref}",
|
106
|
+
:cwd => @repo_path
|
107
|
+
).run_command
|
108
|
+
s.error!
|
109
|
+
if end_ref
|
110
|
+
s = Mixlib::ShellOut.new(
|
111
|
+
"#{@bin} info -r #{end_ref}",
|
112
|
+
:cwd => @repo_path
|
113
|
+
).run_command
|
114
|
+
s.error!
|
115
|
+
end
|
116
|
+
rescue
|
117
|
+
raise Changeset::ReferenceError
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
|
2
|
+
|
3
|
+
require 'mixlib/shellout'
|
4
|
+
|
5
|
+
module BetweenMeals
|
6
|
+
# Local checkout wrapper
|
7
|
+
class Repo
|
8
|
+
attr_reader :repo_path
|
9
|
+
attr_writer :bin
|
10
|
+
|
11
|
+
def initialize(repo_path, logger)
|
12
|
+
@repo_path = repo_path
|
13
|
+
@logger = logger
|
14
|
+
@repo = nil
|
15
|
+
@bin = nil
|
16
|
+
setup
|
17
|
+
rescue
|
18
|
+
@logger.warn("Unable to read repo from #{File.expand_path(repo_path)}")
|
19
|
+
exit(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.get(type, repo_path, logger)
|
23
|
+
case type
|
24
|
+
when 'svn'
|
25
|
+
require 'between_meals/repo/svn'
|
26
|
+
BetweenMeals::Repo::Svn.new(repo_path, logger)
|
27
|
+
when 'git'
|
28
|
+
require 'between_meals/repo/git'
|
29
|
+
BetweenMeals::Repo::Git.new(repo_path, logger)
|
30
|
+
else
|
31
|
+
fail "Do not know repo type #{type}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def exists?
|
36
|
+
fail 'Not implemented'
|
37
|
+
end
|
38
|
+
|
39
|
+
def status
|
40
|
+
fail 'Not implemented'
|
41
|
+
end
|
42
|
+
|
43
|
+
def setup
|
44
|
+
fail 'Not implemented'
|
45
|
+
end
|
46
|
+
|
47
|
+
def head_rev
|
48
|
+
fail 'Not implemented'
|
49
|
+
end
|
50
|
+
|
51
|
+
def head_msg
|
52
|
+
fail 'Not implemented'
|
53
|
+
end
|
54
|
+
|
55
|
+
def head_msg=
|
56
|
+
fail 'Not implemented'
|
57
|
+
end
|
58
|
+
|
59
|
+
def head_parents
|
60
|
+
fail 'Not implemented'
|
61
|
+
end
|
62
|
+
|
63
|
+
def latest_revision
|
64
|
+
fail 'Not implemented'
|
65
|
+
end
|
66
|
+
|
67
|
+
def create(_url)
|
68
|
+
fail 'Not implemented'
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return files changed between two revisions
|
72
|
+
def changes(_start_ref, _end_ref)
|
73
|
+
fail 'Not implemented'
|
74
|
+
end
|
75
|
+
|
76
|
+
def update
|
77
|
+
fail 'Not implemented'
|
78
|
+
end
|
79
|
+
|
80
|
+
# Return all files
|
81
|
+
def files
|
82
|
+
fail 'Not implemented'
|
83
|
+
end
|
84
|
+
|
85
|
+
def latest_revision
|
86
|
+
fail 'Not implemented'
|
87
|
+
end
|
88
|
+
|
89
|
+
def head
|
90
|
+
fail 'Not implemented'
|
91
|
+
end
|
92
|
+
|
93
|
+
def checkout
|
94
|
+
fail 'Not implemented'
|
95
|
+
end
|
96
|
+
|
97
|
+
def update
|
98
|
+
fail 'Not implemented'
|
99
|
+
end
|
100
|
+
|
101
|
+
def last_author
|
102
|
+
fail 'Not implemented'
|
103
|
+
end
|
104
|
+
|
105
|
+
def last_msg
|
106
|
+
fail 'Not implemented'
|
107
|
+
end
|
108
|
+
|
109
|
+
def last_msg=
|
110
|
+
fail 'Not implemented'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
|
2
|
+
|
3
|
+
require 'colorize'
|
4
|
+
require 'socket'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
module BetweenMeals
|
8
|
+
# A set of simple utility functions used throughout BetweenMeals
|
9
|
+
#
|
10
|
+
# Feel freeo to use... note that if you pass in a logger once
|
11
|
+
# you don't need to again, but be safe and always pass one in. :)
|
12
|
+
|
13
|
+
# Util classes need class vars :)
|
14
|
+
# rubocop:disable ClassVars
|
15
|
+
module Util
|
16
|
+
@@logger = nil
|
17
|
+
|
18
|
+
def time(logger = nil)
|
19
|
+
@@logger = logger if logger
|
20
|
+
t0 = Time.now
|
21
|
+
yield
|
22
|
+
info("Executed in #{format('%.2f', Time.now - t0)}s")
|
23
|
+
end
|
24
|
+
|
25
|
+
def exec!(command, logger = nil)
|
26
|
+
@@logger = logger if logger
|
27
|
+
c = execute(command)
|
28
|
+
c.error!
|
29
|
+
return c.status.exitstatus, c.stdout
|
30
|
+
end
|
31
|
+
|
32
|
+
def exec(command, logger = nil)
|
33
|
+
@@logger = logger if logger
|
34
|
+
c = execute(command)
|
35
|
+
return c.status.exitstatus, c.stdout
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def info(msg)
|
41
|
+
@@logger.info(msg) if @@logger
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute(command)
|
45
|
+
info("Running: #{command}")
|
46
|
+
c = Mixlib::ShellOut.new(command)
|
47
|
+
c.run_command
|
48
|
+
c.stdout.lines.each do |line|
|
49
|
+
info("STDOUT: #{line.strip}")
|
50
|
+
end
|
51
|
+
c.stderr.lines.each do |line|
|
52
|
+
info("STDERR: #{line.strip.red}")
|
53
|
+
end
|
54
|
+
return c
|
55
|
+
end
|
56
|
+
|
57
|
+
def port_open?(port)
|
58
|
+
begin
|
59
|
+
Timeout.timeout(1) do
|
60
|
+
begin
|
61
|
+
s = TCPSocket.new('localhost', port)
|
62
|
+
s.close
|
63
|
+
return true
|
64
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
65
|
+
return false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
rescue Timeout::Error
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
return false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|