between_meals 0.0.5 → 0.0.6

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.
@@ -1,39 +1,50 @@
1
1
  # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
2
 
3
3
  # Copyright 2013-present Facebook
4
- #
4
+ #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
6
6
  # you may not use this file except in compliance with the License.
7
7
  # You may obtain a copy of the License at
8
- #
8
+ #
9
9
  # http://www.apache.org/licenses/LICENSE-2.0
10
- #
10
+ #
11
11
  # Unless required by applicable law or agreed to in writing, software
12
12
  # distributed under the License is distributed on an "AS IS" BASIS,
13
13
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
 
17
+ # Require openssl in order to make rugged work reliably
18
+ require 'openssl'
19
+
17
20
  require 'rugged'
18
21
  require 'mixlib/shellout'
19
22
  require 'between_meals/changeset'
23
+ require 'between_meals/repo/git/cmd'
20
24
 
21
25
  module BetweenMeals
22
- # Local checkout wrapper
23
26
  class Repo
24
- # Git provider
25
27
  class Git < BetweenMeals::Repo
26
28
  def setup
27
29
  if File.exists?(File.expand_path(@repo_path))
28
- @repo = Rugged::Repository.new(File.expand_path(@repo_path))
30
+ begin
31
+ @repo = Rugged::Repository.new(File.expand_path(@repo_path))
32
+ rescue
33
+ @repo = nil
34
+ end
29
35
  else
30
36
  @repo = nil
31
37
  end
32
38
  @bin = 'git'
39
+ @cmd = BetweenMeals::Repo::Git::Cmd.new(
40
+ :bin => @bin,
41
+ :cwd => @repo_path,
42
+ :logger => @logger,
43
+ )
33
44
  end
34
45
 
35
46
  def exists?
36
- @repo && !@repo.empty?
47
+ !@repo.nil?
37
48
  end
38
49
 
39
50
  def head_rev
@@ -58,34 +69,30 @@ module BetweenMeals
58
69
  end
59
70
 
60
71
  def head_parents
61
- @repo.head.target.parents
72
+ @repo.head.target.parents.map do |x|
73
+ { :rev => x.tree.oid, :time => x.time }
74
+ end
62
75
  end
63
76
 
64
77
  def checkout(url)
65
- s = Mixlib::ShellOut.new(
66
- "#{@bin} clone #{url} #{@repo} #{@repo_path}"
67
- ).run_command
68
- s.error!
78
+ @cmd.clone(url, @repo_path)
69
79
  @repo = Rugged::Repository.new(File.expand_path(@repo_path))
70
80
  end
71
81
 
72
82
  # Return files changed between two revisions
73
83
  def changes(start_ref, end_ref)
74
- check_refs(start_ref, end_ref)
75
- s = Mixlib::ShellOut.new(
76
- "#{@bin} diff --name-status #{start_ref} #{end_ref}",
77
- :cwd => File.expand_path(@repo_path)
78
- )
79
- s.run_command.error!
84
+ valid_ref?(start_ref)
85
+ valid_ref?(end_ref) if end_ref
86
+ stdout = @cmd.diff(start_ref, end_ref).stdout
80
87
  begin
81
- parse_status(s.stdout).compact
88
+ parse_status(stdout).compact
82
89
  rescue => e
83
90
  # We've seen some weird non-reproducible failures here
84
91
  @logger.error(
85
92
  'Something went wrong. Please please report this output.'
86
93
  )
87
94
  @logger.error(e)
88
- s.stdout.lines.each do |line|
95
+ stdout.lines.each do |line|
89
96
  @logger.error(line.strip)
90
97
  end
91
98
  exit(1)
@@ -93,16 +100,7 @@ module BetweenMeals
93
100
  end
94
101
 
95
102
  def update
96
- cmd = Mixlib::ShellOut.new(
97
- "#{@bin} pull --rebase", :cwd => File.expand_path(@repo_path)
98
- )
99
- cmd.run_command
100
- if cmd.exitstatus != 0
101
- @logger.error('Something went wrong with git!')
102
- @logger.error(cmd.stdout)
103
- fail
104
- end
105
- cmd.stdout
103
+ @cmd.pull.stdout
106
104
  end
107
105
 
108
106
  # Return all files
@@ -110,33 +108,35 @@ module BetweenMeals
110
108
  @repo.index.map { |x| { :path => x[:path], :status => :created } }
111
109
  end
112
110
 
113
- def status
114
- cmd = Mixlib::ShellOut.new(
115
- "#{@bin} status --porcelain 2>&1",
116
- :cwd => File.expand_path(@repo_path)
117
- )
118
- cmd.run_command
119
- if cmd.exitstatus != 0
120
- @logger.error('Something went wrong with git!')
121
- @logger.error(cmd.stdout)
122
- fail
111
+ def upstream?(rev, master = 'remotes/trunk')
112
+ if @cmd.merge_base(rev, master).stdout.strip == rev
113
+ return true
123
114
  end
124
- cmd.stdout
115
+ return false
116
+ rescue
117
+ return false
125
118
  end
126
119
 
127
- private
120
+ def status
121
+ @cmd.status.stdout.strip
122
+ end
123
+
124
+ def name
125
+ @cmd.config('user.name').stdout.strip
126
+ end
127
+
128
+ def email
129
+ @cmd.config('user.email').stdout.strip
130
+ end
128
131
 
129
- def check_refs(start_ref, end_ref)
130
- unless @repo.exists?(start_ref)
132
+ def valid_ref?(ref)
133
+ unless @repo.exists?(ref)
131
134
  fail Changeset::ReferenceError
132
135
  end
133
- unless end_ref.nil?
134
- unless @repo.exists?(end_ref)
135
- fail Changeset::ReferenceError
136
- end
137
- end
138
136
  end
139
137
 
138
+ private
139
+
140
140
  def parse_status(changes)
141
141
  # man git-diff-files
142
142
  # Possible status letters are:
@@ -0,0 +1,49 @@
1
+ # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
+
3
+ # Copyright 2013-present Facebook
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'between_meals/cmd'
18
+
19
+ module BetweenMeals
20
+ class Repo
21
+ class Git < BetweenMeals::Repo
22
+ class Cmd < BetweenMeals::Cmd
23
+ def config(key)
24
+ cmd("config #{key}")
25
+ end
26
+
27
+ def clone(url, repo_path)
28
+ cmd("clone #{url} #{repo_path}", '/tmp')
29
+ end
30
+
31
+ def diff(start_ref, end_ref)
32
+ cmd("diff --name-status #{start_ref} #{end_ref}")
33
+ end
34
+
35
+ def pull
36
+ cmd('pull --rebase')
37
+ end
38
+
39
+ def merge_base(rev, master)
40
+ cmd("merge-base #{rev} #{master}")
41
+ end
42
+
43
+ def status
44
+ cmd('status --porcelain 2>&1')
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,214 @@
1
+ # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
+
3
+ # Copyright 2013-present Facebook
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'pathname'
18
+ require 'mixlib/shellout'
19
+ require 'between_meals/changeset'
20
+ require 'between_meals/repo/hg/cmd'
21
+
22
+ module BetweenMeals
23
+ class Repo
24
+ class Hg < BetweenMeals::Repo
25
+ def setup
26
+ @bin = 'hg'
27
+ @cmd = BetweenMeals::Repo::Hg::Cmd.new(
28
+ :bin => @bin,
29
+ :cwd => @repo_path,
30
+ :logger => @logger,
31
+ )
32
+ end
33
+
34
+ def exists?
35
+ Dir.exists?(Pathname.new(@repo_path).join('.hg'))
36
+ end
37
+
38
+ def head_rev
39
+ @cmd.log('node').stdout
40
+ end
41
+
42
+ def checkout(url)
43
+ @cmd.clone(url, @repo_path)
44
+ end
45
+
46
+ # Return files changed between two revisions
47
+ def changes(start_ref, end_ref)
48
+ valid_ref?(start_ref)
49
+ valid_ref?(end_ref) if end_ref
50
+ stdout = @cmd.status(start_ref, end_ref).stdout
51
+ begin
52
+ parse_status(stdout).compact
53
+ rescue => e
54
+ # We've seen some weird non-reproducible failures here
55
+ @logger.error(
56
+ 'Something went wrong. Please please report this output.'
57
+ )
58
+ @logger.error(e)
59
+ stdout.lines.each do |line|
60
+ @logger.error(line.strip)
61
+ end
62
+ exit(1)
63
+ end
64
+ end
65
+
66
+ def update
67
+ @cmd.pull.stdout
68
+ rescue
69
+ @logger.error('Something went wrong with hg!')
70
+ @logger.error(cmd.stdout)
71
+ raise
72
+ end
73
+
74
+ # Return all files
75
+ def files
76
+ @cmd.manifest.stdout.split("\n").map do |x|
77
+ { :path => x, :status => :created }
78
+ end
79
+ end
80
+
81
+ def head_parents
82
+ [{
83
+ :time => Time.parse(@cmd.log('date|isodate', 'master').stdout),
84
+ :rev => @cmd.log('node', 'master').stdout,
85
+ }]
86
+ rescue
87
+ [{
88
+ :time => nil,
89
+ :rev => nil,
90
+ }]
91
+ end
92
+
93
+ def last_author
94
+ [
95
+ /^.*<(.*)>$/,
96
+ /^(.*@.*)$/,
97
+ ].each do |re|
98
+ m = @cmd.log('author').stdout.match(re)
99
+ return { :email => m[1] } if m
100
+ end
101
+ return { :email => nil }
102
+ end
103
+
104
+ def last_msg
105
+ @cmd.log('desc').stdout
106
+ rescue
107
+ nil
108
+ end
109
+
110
+ def last_msg=(msg)
111
+ if last_msg.strip != msg.strip
112
+ @cmd.amend(msg.strip)
113
+ end
114
+ end
115
+
116
+ def email
117
+ username[2]
118
+ rescue
119
+ nil
120
+ end
121
+
122
+ def name
123
+ username[1]
124
+ rescue
125
+ nil
126
+ end
127
+
128
+ def status
129
+ @cmd.status.stdout
130
+ end
131
+
132
+ def upstream?(rev)
133
+ # Check if commit is an ancestor of master
134
+ # Returns the diff if common ancestor is found,
135
+ # returns nothing if not
136
+ if @cmd.rev("'ancestor(master,#{rev}) & #{rev}'").stdout.empty?
137
+ return false
138
+ else
139
+ return true
140
+ end
141
+ end
142
+
143
+ def valid_ref?(ref)
144
+ @cmd.rev(ref)
145
+ return true
146
+ rescue
147
+ raise Changeset::ReferenceError
148
+ end
149
+
150
+ private
151
+
152
+ def username
153
+ @cmd.username.stdout.lines.first.strip.match(/^(.*?)(?:\s<(.*)>)?$/)
154
+ end
155
+
156
+ def parse_status(changes)
157
+ # The codes used to show the status of files are:
158
+ #
159
+ # M = modified
160
+ # A = added
161
+ # R = removed
162
+ # C = clean
163
+ # ! = missing (deleted by non-hg command, but still tracked)
164
+ # ? = not tracked
165
+ # I = ignored
166
+ # = origin of the previous file (with --copies)
167
+
168
+ # rubocop:disable MultilineBlockChain
169
+ changes.lines.map do |line|
170
+ case line
171
+ when /^A (\S+)$/
172
+ {
173
+ :status => :added,
174
+ :path => Regexp.last_match(1)
175
+ }
176
+ when /^C (\S+)$/
177
+ {
178
+ :status => :clean,
179
+ :path => Regexp.last_match(1)
180
+ }
181
+ when /^R (\S+)$/
182
+ {
183
+ :status => :deleted,
184
+ :path => Regexp.last_match(1)
185
+ }
186
+ when /^M (\S+)$/
187
+ {
188
+ :status => :modified,
189
+ :path => Regexp.last_match(1)
190
+ }
191
+ when /^! (\S+)$/
192
+ {
193
+ :status => :missing,
194
+ :path => Regexp.last_match(1)
195
+ }
196
+ when /^\? (\S+)$/
197
+ {
198
+ :status => :untracked,
199
+ :path => Regexp.last_match(1)
200
+ }
201
+ when /^I (\S+)$/
202
+ {
203
+ :status => :ignored,
204
+ :path => Regexp.last_match(1)
205
+ }
206
+ else
207
+ fail 'No match'
208
+ end
209
+ end
210
+ # rubocop:enable MultilineBlockChain
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,72 @@
1
+ # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
+
3
+ # Copyright 2013-present Facebook
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'between_meals/cmd'
18
+ require 'tempfile'
19
+
20
+ module BetweenMeals
21
+ class Repo
22
+ class Hg < BetweenMeals::Repo
23
+ class Cmd < BetweenMeals::Cmd
24
+ def rev(rev)
25
+ cmd("log -r #{rev}")
26
+ end
27
+
28
+ def log(template, rev = '.')
29
+ cmd("log -r #{rev} -l 1 -T '{#{template}}'")
30
+ end
31
+
32
+ def clone(url, repo_path)
33
+ cmd("clone #{url} #{repo_path}")
34
+ end
35
+
36
+ def pull
37
+ cmd('pull --rebase')
38
+ end
39
+
40
+ def manifest
41
+ cmd('manifest')
42
+ end
43
+
44
+ def username
45
+ cmd('config ui.username')
46
+ end
47
+
48
+ def amend(msg)
49
+ f = Tempfile.new('between_meals.hg.amend')
50
+ begin
51
+ f.write(msg)
52
+ f.flush
53
+ cmd("commit --amend -l #{f.path}")
54
+ ensure
55
+ f.close
56
+ f.unlink
57
+ end
58
+ end
59
+
60
+ def status(start_ref = nil, end_ref = nil)
61
+ if start_ref && end_ref
62
+ cmd("status --rev #{start_ref} --rev #{end_ref}")
63
+ elsif start_ref
64
+ cmd("status --rev #{start_ref}")
65
+ else
66
+ cmd('status')
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end