between_meals 0.0.1 → 0.0.2
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 +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
|