polisher 0.7.1 → 0.8.1

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.
@@ -0,0 +1,23 @@
1
+ # Polisher Git Based Project Representation
2
+ #
3
+ # Licensed under the MIT license
4
+ # Copyright (C) 2013-2014 Red Hat, Inc.
5
+
6
+ require 'polisher/vendor'
7
+ require 'polisher/git/repo'
8
+
9
+ module Polisher
10
+ module Git
11
+ # Git based project representation
12
+ class Project < Repo
13
+ include HasVendoredDeps
14
+
15
+ # Override vendored to ensure repo is
16
+ # cloned before retrieving modules
17
+ def vendored
18
+ clone unless cloned?
19
+ super
20
+ end
21
+ end # class Project
22
+ end # module Git
23
+ end # module Polisher
@@ -0,0 +1,74 @@
1
+ # Polisher Git Repo Representation
2
+ #
3
+ # Licensed under the MIT license
4
+ # Copyright (C) 2013-2014 Red Hat, Inc.
5
+
6
+ require 'awesome_spawn'
7
+ require 'polisher/core'
8
+ require 'polisher/git_cache'
9
+
10
+ module Polisher
11
+ module Git
12
+ # Git Repository
13
+ class Repo
14
+ extend ConfHelpers
15
+
16
+ # TODO use ruby git api
17
+ conf_attr :git_cmd, '/usr/bin/git'
18
+
19
+ attr_accessor :url
20
+
21
+ def initialize(args={})
22
+ @url = args[:url]
23
+ end
24
+
25
+ def path
26
+ GitCache.path_for(@url)
27
+ end
28
+
29
+ # Clobber the git repo
30
+ def clobber!
31
+ FileUtils.rm_rf path
32
+ end
33
+
34
+ def clone
35
+ AwesomeSpawn.run "#{git_cmd} clone #{url} #{path}"
36
+ end
37
+
38
+ def cloned?
39
+ File.directory?(path)
40
+ end
41
+
42
+ def in_repo
43
+ Dir.chdir path do
44
+ yield
45
+ end
46
+ end
47
+
48
+ def file_paths
49
+ in_repo { Dir['**/*'] }
50
+ end
51
+
52
+ # Note be careful when invoking:
53
+ def reset!
54
+ in_repo { AwesomeSpawn.run "#{git_cmd} reset HEAD~ --hard" }
55
+ self
56
+ end
57
+
58
+ def pull
59
+ in_repo { AwesomeSpawn.run "#{git_cmd} pull" }
60
+ self
61
+ end
62
+
63
+ def checkout(tgt)
64
+ in_repo { AwesomeSpawn.run "#{git_cmd} checkout #{tgt}" }
65
+ self
66
+ end
67
+
68
+ def commit(msg)
69
+ in_repo { AwesomeSpawn.run "#{git_cmd} commit -m '#{msg}'" }
70
+ self
71
+ end
72
+ end # class Repo
73
+ end # module Git
74
+ end # module Polisher
@@ -7,29 +7,23 @@ require 'xmlrpc/client'
7
7
  XMLRPC::Config::ENABLE_NIL_PARSER = true
8
8
  XMLRPC::Config::ENABLE_NIL_CREATE = true
9
9
 
10
+ require 'polisher/core'
11
+
10
12
  module Polisher
11
13
  class Koji
12
- KOJI_URL = 'koji.fedoraproject.org/kojihub'
13
- KOJI_TAG = 'f21'
14
+ extend ConfHelpers
14
15
 
15
- # Get/Set the koji url
16
- def self.koji_url(value=nil)
17
- @koji_url ||= KOJI_URL
18
- @koji_url = value unless value.nil?
19
- @koji_url
20
- end
16
+ # TODO Koji#build (on class or instance?)
21
17
 
22
- # Get/Set the koji tag to use
23
- def self.koji_tag(value=nil)
24
- @koji_tag ||= KOJI_TAG
25
- @koji_tag = value unless value.nil?
26
- @koji_tag
27
- end
18
+ # TODO Koji#diff(tag1, tag2)
19
+
20
+ conf_attr :koji_url, 'koji.fedoraproject.org/kojihub'
21
+ conf_attr :koji_tag, 'f21'
28
22
 
29
23
  # Retrieve shared instance of xmlrpc client to use
30
24
  def self.client
31
25
  @client ||= begin
32
- url = self.koji_url.split('/')
26
+ url = koji_url.split('/')
33
27
  XMLRPC::Client.new(url[0..-2].join('/'),
34
28
  "/#{url.last}")
35
29
  end
@@ -60,7 +54,7 @@ module Polisher
60
54
  # koji xmlrpc call
61
55
  builds =
62
56
  self.client.call('listTagged',
63
- self.koji_tag, nil, false, nil, false,
57
+ koji_tag, nil, false, nil, false,
64
58
  "rubygem-#{name}")
65
59
  versions = builds.collect { |b| b['version'] }
66
60
  bl.call(:koji, name, versions) unless(bl.nil?)
@@ -0,0 +1,44 @@
1
+ # Polisher RPM Patch Representation
2
+ #
3
+ # Licensed under the MIT license
4
+ # Copyright (C) 2014 Red Hat, Inc.
5
+
6
+ module Polisher
7
+ module RPM
8
+ class Patch
9
+ attr_accessor :title
10
+ attr_accessor :content
11
+
12
+ def initialize(args={})
13
+ @title = args[:title]
14
+ @content = args[:content]
15
+ end
16
+
17
+ def spec_line(n=0)
18
+ "Patch#{n}: #{title}"
19
+ end
20
+
21
+ def self.from(diff)
22
+ return diff.collect { |d| self.from(d) } if diff.is_a?(Array)
23
+
24
+ result = {}
25
+
26
+ in_diff = nil
27
+ diff.each_line do |line|
28
+ if line =~ /diff -r ([^\s]+)+ ([^\s]+)+$/
29
+ result[in_diff] = diff if in_diff
30
+ in_diff = $1.gsub(/a\//, '')
31
+ diff = ''
32
+ elsif line =~ /Only in.*$/
33
+ in_diff = nil
34
+
35
+ else
36
+ diff += line
37
+ end
38
+ end
39
+
40
+ result.collect { |t,c| self.new :title => t, :content => c }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,188 @@
1
+ # Polisher RPM Requirement Represenation
2
+ #
3
+ # Licensed under the MIT license
4
+ # Copyright (C) 2013-2014 Red Hat, Inc.
5
+
6
+ require 'gem2rpm'
7
+ require 'versionomy'
8
+ require 'active_support/core_ext'
9
+
10
+ require 'polisher/core'
11
+ require 'polisher/gem'
12
+
13
+ module Polisher
14
+ module RPM
15
+ class Requirement
16
+ # Bool indiciating if req is a BR
17
+ attr_accessor :br
18
+
19
+ # Name of requirement
20
+ attr_accessor :name
21
+
22
+ # Condition, eg >=, =, etc
23
+ attr_accessor :condition
24
+
25
+ # Version number
26
+ attr_accessor :version
27
+
28
+ # Requirement string
29
+ def str
30
+ sp = self.specifier
31
+ sp.nil? ? "#{@name}" : "#{@name} #{sp}"
32
+ end
33
+
34
+ # Specified string
35
+ def specifier
36
+ @version.nil? ? nil : "#{@condition} #{@version}"
37
+ end
38
+
39
+ # Instantiate / return new rpm spec requirements from string
40
+ def self.parse(str, opts={})
41
+ stra = str.split
42
+ br = str.include?('BuildRequires')
43
+ name = condition = version = nil
44
+
45
+ if str.include?('Requires')
46
+ name = stra[1]
47
+ condition = stra[2]
48
+ version = stra[3]
49
+
50
+ else
51
+ name = stra[0]
52
+ condition = stra[1]
53
+ version = stra[2]
54
+
55
+ end
56
+
57
+ req = self.new({:name => name,
58
+ :condition => condition,
59
+ :version => version,
60
+ :br => br}.merge(opts))
61
+ req
62
+ end
63
+
64
+ # Instantiate / return new rpm spec requirements from gem dependency.
65
+ #
66
+ # Because a gem dependency may result in multiple spec requirements
67
+ # this will always return an array of Requirement instances
68
+ def self.from_gem_dep(gem_dep, br=false)
69
+ gem_dep.requirement.to_s.split(',').collect { |req|
70
+ expanded = Gem2Rpm::Helpers.expand_requirement [req.split]
71
+ expanded.collect { |e|
72
+ self.new :name => "rubygem(#{gem_dep.name})",
73
+ :condition => e.first.to_s,
74
+ :version => e.last.to_s,
75
+ :br => br
76
+ }
77
+ }.flatten
78
+ end
79
+
80
+ def initialize(args={})
81
+ @br = args[:br] || false
82
+ @name = args[:name]
83
+ @condition = args[:condition]
84
+ @version = args[:version]
85
+
86
+ @name.strip! unless @name.nil?
87
+ @condition.strip! unless @condition.nil?
88
+ @version.strip! unless @version.nil?
89
+ end
90
+
91
+ def ==(other)
92
+ @br == other.br &&
93
+ @name == other.name &&
94
+ @condition == other.condition &&
95
+ @version == other.version
96
+ end
97
+
98
+ # Greatest Common Denominator,
99
+ # Max version in list that is less than the local version
100
+ def gcd(versions)
101
+ lversion = Versionomy.parse(self.version)
102
+ versions.collect { |v| Versionomy.parse(v) }.
103
+ sort { |a,b| a <=> b }.reverse.
104
+ find { |v| v < lversion }.to_s
105
+ end
106
+
107
+ # Minimum gem version which satisfies this dependency
108
+ def min_satisfying_version
109
+ return "0.0" if self.version.nil? ||
110
+ self.condition == '<' ||
111
+ self.condition == '<='
112
+ return self.version if self.condition == '=' ||
113
+ self.condition == '>='
114
+ Versionomy.parse(self.version).bump(:tiny).to_s # self.condition == '>'
115
+ end
116
+
117
+ # Max gem version which satisfies this dependency
118
+ #
119
+ # Can't automatically deduce in '<' case, so if that is the conditional
120
+ # we require a version list, and will return the gcd from it
121
+ def max_satisfying_version(versions=nil)
122
+ return Float::INFINITY if self.version.nil? ||
123
+ self.condition == '>' ||
124
+ self.condition == '>='
125
+ return self.version if self.condition == '=' ||
126
+ self.condition == '<='
127
+
128
+ raise ArgumentError if versions.nil?
129
+ self.gcd(versions)
130
+ end
131
+
132
+ # Minimum gem version for which this dependency fails
133
+ def min_failing_version
134
+ raise ArgumentError if self.version.nil?
135
+ return "0.0" if self.condition == '>' ||
136
+ self.condition == '>='
137
+ return self.version if self.condition == '<'
138
+ Versionomy.parse(self.version).bump(:tiny).to_s # self.condition == '<=' and '='
139
+ end
140
+
141
+ # Max gem version for which this dependency fails
142
+ #
143
+ # Can't automatically deduce in '>=', and '=' cases, so if that is the
144
+ # conditional we require a version list, and will return the gcd from it
145
+ def max_failing_version(versions=nil)
146
+ raise ArgumentError if self.version.nil? ||
147
+ self.condition == '<=' ||
148
+ self.condition == '<'
149
+ return self.version if self.condition == '>'
150
+
151
+ raise ArgumentError if versions.nil?
152
+ self.gcd(versions)
153
+ end
154
+
155
+ # Return bool indicating if requirement matches specified
156
+ # depedency.
157
+ #
158
+ # Comparison mechanism will depend on type of class
159
+ # passed to this. Valid types include
160
+ # - Polisher::RPM::Requirements
161
+ # - ::Gem::Dependency
162
+ def matches?(dep)
163
+ return self == dep if dep.is_a?(self.class)
164
+ raise ArgumentError unless dep.is_a?(::Gem::Dependency)
165
+
166
+ return false if !self.gem? || self.gem_name != dep.name
167
+ return true if self.version.nil?
168
+
169
+ Gem2Rpm::Helpers.expand_requirement([dep.requirement.to_s.split]).
170
+ any?{ |req|
171
+ req.first == self.condition && req.last.to_s == self.version
172
+ }
173
+ end
174
+
175
+ # Whether or not this requirement specified a ruby gem dependency
176
+ def gem?
177
+ !!(self.str =~ RPM::Spec::SPEC_GEM_REQ_MATCHER)
178
+ end
179
+
180
+ # Return the name of the gem which this requirement is for.
181
+ # Returns nil if this is not a gem requirement
182
+ def gem_name
183
+ # XXX need to explicitly run regex here to get $1
184
+ !!(self.str =~ RPM::Spec::SPEC_GEM_REQ_MATCHER) ? $1 : nil
185
+ end
186
+ end # class Requirement
187
+ end # module RPM
188
+ end
@@ -0,0 +1,381 @@
1
+ # Polisher RPM Spec Represenation
2
+ #
3
+ # Licensed under the MIT license
4
+ # Copyright (C) 2013-2014 Red Hat, Inc.
5
+
6
+ require 'gem2rpm'
7
+ require 'versionomy'
8
+ require 'active_support/core_ext'
9
+
10
+ require 'polisher/core'
11
+ require 'polisher/gem'
12
+ require 'polisher/rpm/requirement'
13
+
14
+ module Polisher
15
+ module RPM
16
+ class Spec
17
+ AUTHOR = "#{ENV['USER']} <#{ENV['USER']}@localhost.localdomain>"
18
+
19
+ COMMENT_MATCHER = /^\s*#.*/
20
+ GEM_NAME_MATCHER = /^%global\s*gem_name\s(.*)$/
21
+ SPEC_NAME_MATCHER = /^Name:\s*rubygem-(.*)$/
22
+ SPEC_VERSION_MATCHER = /^Version:\s*(.*)$/
23
+ SPEC_RELEASE_MATCHER = /^Release:\s*(.*)$/
24
+ SPEC_REQUIRES_MATCHER = /^Requires:\s*(.*)$/
25
+ SPEC_BUILD_REQUIRES_MATCHER = /^BuildRequires:\s*(.*)$/
26
+ SPEC_GEM_REQ_MATCHER = /^.*\s*rubygem\((.*)\)(\s*(.*))?$/
27
+ SPEC_SUBPACKAGE_MATCHER = /^%package\s(.*)$/
28
+ SPEC_CHANGELOG_MATCHER = /^%changelog$/
29
+ SPEC_FILES_MATCHER = /^%files$/
30
+ SPEC_SUBPKG_FILES_MATCHER = /^%files\s*(.*)$/
31
+ SPEC_CHECK_MATCHER = /^%check$/
32
+
33
+ FILE_MACRO_MATCHERS =
34
+ [/^%doc\s/, /^%config\s/, /^%attr\s/,
35
+ /^%verify\s/, /^%docdir.*/, /^%dir\s/,
36
+ /^%defattr.*/, /^%exclude\s/, /^%{gem_instdir}\/+/]
37
+
38
+ FILE_MACRO_REPLACEMENTS =
39
+ {"%{_bindir}" => 'bin',
40
+ "%{gem_libdir}" => 'lib'}
41
+
42
+ attr_accessor :metadata
43
+
44
+ # Return the currently configured author
45
+ def self.current_author
46
+ ENV['POLISHER_AUTHOR'] || AUTHOR
47
+ end
48
+
49
+ def initialize(metadata={})
50
+ @metadata = metadata
51
+ end
52
+
53
+ # Dispatch all missing methods to lookup calls in rpm spec metadata
54
+ def method_missing(method, *args, &block)
55
+ # proxy to metadata
56
+ if @metadata.has_key?(method)
57
+ @metadata[method]
58
+
59
+ else
60
+ super(method, *args, &block)
61
+ end
62
+ end
63
+
64
+ # Return boolean indicating if spec has a %check section
65
+ def has_check?
66
+ @metadata.has_key?(:has_check) && @metadata[:has_check]
67
+ end
68
+
69
+ # Return all the Requires for the specified gem
70
+ def requirements_for_gem(gem_name)
71
+ @metadata[:requires].nil? ? [] :
72
+ @metadata[:requires].select { |r| r.gem_name == gem_name }
73
+ end
74
+
75
+ # Return all the BuildRequires for the specified gem
76
+ def build_requirements_for_gem(gem_name)
77
+ @metadata[:build_requires].nil? ? [] :
78
+ @metadata[:build_requires].select { |r| r.gem_name == gem_name }
79
+ end
80
+
81
+ # Return bool indicating if this spec specifies all the
82
+ # requirements in the specified gem dependency
83
+ #
84
+ # @param [Gem::Dependency] gem_dep dependency which to retreive / compare
85
+ # requirements
86
+ def has_all_requirements_for?(gem_dep)
87
+ reqs = self.requirements_for_gem gem_dep.name
88
+ # create a spec requirement dependency for each expanded subrequirement,
89
+ # verify we can find a match for that
90
+ gem_dep.requirement.to_s.split(',').all? { |greq|
91
+ Gem2Rpm::Helpers.expand_requirement([greq.split]).all? { |ereq|
92
+ tereq = Requirement.new :name => "rubygem(#{gem_dep.name})",
93
+ :condition => ereq.first,
94
+ :version => ereq.last.to_s
95
+ reqs.any? { |req| req.matches?(tereq)}
96
+ }
97
+ }
98
+ end
99
+
100
+ # Return all gem Requires
101
+ def gem_requirements
102
+ @metadata[:requires].nil? ? [] :
103
+ @metadata[:requires].select { |r| r.gem? }
104
+ end
105
+
106
+ # Return all gem BuildRequires
107
+ def gem_build_requirements
108
+ @metadata[:build_requires].nil? ? [] :
109
+ @metadata[:build_requires].select { |r| r.gem? }
110
+ end
111
+
112
+ # Return all non gem Requires
113
+ def non_gem_requirements
114
+ @metadata[:requires].nil? ? [] :
115
+ @metadata[:requires].select { |r| !r.gem? }
116
+ end
117
+
118
+ # Return all non gem BuildRequires
119
+ def non_gem_build_requirements
120
+ @metadata[:build_requires].nil? ? [] :
121
+ @metadata[:build_requires].select { |r| !r.gem? }
122
+ end
123
+
124
+ # Return all gem requirements _not_ in the specified gem
125
+ def extra_gem_requirements(gem)
126
+ gem_reqs = gem.deps.collect { |d| requirements_for_gem(d.name) }.flatten
127
+ gem_requirements - gem_reqs
128
+ end
129
+
130
+ # Return all gem build requirements _not_ in the specified gem
131
+ def extra_gem_build_requirements(gem)
132
+ gem_reqs = gem.deps.collect { |d| requirements_for_gem(d.name) }.flatten
133
+ gem_build_requirements - gem_reqs
134
+ end
135
+
136
+ # Parse the specified rpm spec and return new RPM::Spec instance from metadata
137
+ #
138
+ # @param [String] string contents of spec to parse
139
+ # @return [Polisher::RPM::Spec] spec instantiated from rpmspec metadata
140
+ def self.parse(spec)
141
+ in_subpackage = false
142
+ in_changelog = false
143
+ in_files = false
144
+ subpkg_name = nil
145
+ meta = {:contents => spec}
146
+ spec.each_line { |l|
147
+ if l =~ COMMENT_MATCHER
148
+ ;
149
+
150
+ # TODO support optional gem prefix
151
+ elsif l =~ GEM_NAME_MATCHER
152
+ meta[:gem_name] = $1.strip
153
+ meta[:gem_name] = $1.strip
154
+
155
+ elsif l =~ SPEC_NAME_MATCHER &&
156
+ $1.strip != "%{gem_name}"
157
+ meta[:gem_name] = $1.strip
158
+
159
+ elsif l =~ SPEC_VERSION_MATCHER
160
+ meta[:version] = $1.strip
161
+
162
+ elsif l =~ SPEC_RELEASE_MATCHER
163
+ meta[:release] = $1.strip
164
+
165
+ elsif l =~ SPEC_SUBPACKAGE_MATCHER
166
+ subpkg_name = $1.strip
167
+ in_subpackage = true
168
+
169
+ elsif l =~ SPEC_REQUIRES_MATCHER &&
170
+ !in_subpackage
171
+ meta[:requires] ||= []
172
+ meta[:requires] << RPM::Requirement.parse($1.strip)
173
+
174
+ elsif l =~ SPEC_BUILD_REQUIRES_MATCHER &&
175
+ !in_subpackage
176
+ meta[:build_requires] ||= []
177
+ meta[:build_requires] << RPM::Requirement.parse($1.strip)
178
+
179
+ elsif l =~ SPEC_CHANGELOG_MATCHER
180
+ in_changelog = true
181
+
182
+ elsif l =~ SPEC_FILES_MATCHER
183
+ subpkg_name = nil
184
+ in_files = true
185
+
186
+ elsif l =~ SPEC_SUBPKG_FILES_MATCHER
187
+ subpkg_name = $1.strip
188
+ in_files = true
189
+
190
+ elsif l =~ SPEC_CHECK_MATCHER
191
+ meta[:has_check] = true
192
+
193
+ elsif in_changelog
194
+ meta[:changelog] ||= ""
195
+ meta[:changelog] << l
196
+
197
+ elsif in_files
198
+ tgt = subpkg_name.nil? ? meta[:gem_name] : subpkg_name
199
+ meta[:files] ||= {}
200
+ meta[:files][tgt] ||= []
201
+
202
+ sl = l.strip.unrpmize
203
+ meta[:files][tgt] << sl unless sl.blank?
204
+ end
205
+ }
206
+
207
+ meta[:changelog_entries] = meta[:changelog] ?
208
+ meta[:changelog].split("\n\n") : []
209
+ meta[:changelog_entries].collect! { |c| c.strip }.compact!
210
+
211
+ self.new meta
212
+ end
213
+
214
+ # Update RPM::Spec metadata to new gem
215
+ #
216
+ # @param [Polisher::Gem] new_source new gem to update rpmspec to
217
+ def update_to(new_source)
218
+ update_deps_from(new_source)
219
+ update_files_from(new_source)
220
+ update_metadata_from(new_source)
221
+ end
222
+
223
+ private
224
+
225
+ # Update spec dependencies from new source
226
+ def update_deps_from(new_source)
227
+ @metadata[:requires] =
228
+ non_gem_requirements +
229
+ extra_gem_requirements(new_source) +
230
+ new_source.deps.collect { |r|
231
+ RPM::Requirement.from_gem_dep(r)
232
+ }.flatten
233
+
234
+ @metadata[:build_requires] =
235
+ non_gem_build_requirements +
236
+ extra_gem_build_requirements(new_source) +
237
+ new_source.dev_deps.collect { |r|
238
+ RPM::Requirement.from_gem_dep(r, true)
239
+ }.flatten
240
+ end
241
+
242
+ # Internal helper to update spec files from new source
243
+ def update_files_from(new_source)
244
+ to_add = new_source.file_paths
245
+ @metadata[:files] ||= {}
246
+ @metadata[:files].each { |pkg,spec_files|
247
+ (new_source.file_paths & to_add).each { |gem_file|
248
+ # skip files already included in spec or in dir in spec
249
+ has_file = spec_files.any? { |sf|
250
+ gem_file.gsub(sf,'') != gem_file
251
+ }
252
+
253
+ to_add.delete(gem_file)
254
+ to_add << gem_file.rpmize if !has_file &&
255
+ !Gem.ignorable_file?(gem_file)
256
+ }
257
+ }
258
+
259
+ @metadata[:new_files] = to_add
260
+ end
261
+
262
+ # Internal helper to update spec metadata from new source
263
+ def update_metadata_from(new_source)
264
+ # update to new version
265
+ @metadata[:version] = new_source.version
266
+ @metadata[:release] = "1%{?dist}"
267
+
268
+ # add changelog entry
269
+ changelog_entry = <<EOS
270
+ * #{Time.now.strftime("%a %b %d %Y")} #{RPM::Spec.current_author} - #{@metadata[:version]}-1
271
+ - Update to version #{new_source.version}
272
+ EOS
273
+ @metadata[:changelog_entries] ||= []
274
+ @metadata[:changelog_entries].unshift changelog_entry.rstrip
275
+ end
276
+
277
+ public
278
+
279
+ # Return properly formatted rpmspec as string
280
+ #
281
+ # @return [String] string representation of rpm spec
282
+ def to_string
283
+ contents = @metadata[:contents]
284
+
285
+ # replace version / release
286
+ contents.gsub!(SPEC_VERSION_MATCHER, "Version: #{@metadata[:version]}")
287
+ contents.gsub!(SPEC_RELEASE_MATCHER, "Release: #{@metadata[:release]}")
288
+
289
+ # add changelog entry
290
+ cp = contents.index SPEC_CHANGELOG_MATCHER
291
+ cpn = contents.index "\n", cp
292
+ contents = contents[0...cpn+1] +
293
+ @metadata[:changelog_entries].join("\n\n")
294
+
295
+ # update requires/build requires
296
+ rp = contents.index SPEC_REQUIRES_MATCHER
297
+ brp = contents.index SPEC_BUILD_REQUIRES_MATCHER
298
+ tp = rp < brp ? rp : brp
299
+
300
+ pp = contents.index SPEC_SUBPACKAGE_MATCHER
301
+ pp = -1 if pp.nil?
302
+
303
+ lrp = contents.rindex SPEC_REQUIRES_MATCHER, pp
304
+ lbrp = contents.rindex SPEC_BUILD_REQUIRES_MATCHER, pp
305
+ ltp = lrp > lbrp ? lrp : lbrp
306
+
307
+ ltpn = contents.index "\n", ltp
308
+
309
+ contents.slice!(tp...ltpn)
310
+ contents.insert tp,
311
+ (@metadata[:requires].collect { |r| "Requires: #{r.str}" } +
312
+ @metadata[:build_requires].collect { |r| "BuildRequires: #{r.str}" }).join("\n")
313
+
314
+ # add new files
315
+ fp = contents.index SPEC_FILES_MATCHER
316
+ lfp = contents.index SPEC_SUBPKG_FILES_MATCHER, fp + 1
317
+ lfp = contents.index SPEC_CHANGELOG_MATCHER if lfp.nil?
318
+
319
+ contents.insert lfp - 1, @metadata[:new_files].join("\n") + "\n"
320
+
321
+ # return new contents
322
+ contents
323
+ end
324
+
325
+ # Compare this spec to a sepecified upstream gem source
326
+ # and return result.
327
+ #
328
+ # upstream_source should be an instance of Polisher::Gem,
329
+ # Polisher::Gemfile, or other class defining a 'deps'
330
+ # accessor that returns an array of Gem::Requirement dependencies
331
+ #
332
+ # Result will be a hash containing the shared dependencies as
333
+ # well as those that differ and their respective differences
334
+ def compare(upstream_source)
335
+ same = {}
336
+ diff = {}
337
+ upstream_source.deps.each do |d|
338
+ spec_reqs = self.requirements_for_gem(d.name)
339
+ spec_reqs_specifier = spec_reqs.empty? ? nil :
340
+ spec_reqs.collect { |req| req.specifier }
341
+
342
+ if spec_reqs.nil?
343
+ diff[d.name] = {:spec => nil,
344
+ :upstream => d.requirement.to_s}
345
+
346
+ elsif !spec_reqs.any? { |req| req.matches?(d) } ||
347
+ !self.has_all_requirements_for?(d)
348
+ diff[d.name] = {:spec => spec_reqs_specifier,
349
+ :upstream => d.requirement.to_s}
350
+
351
+ elsif !diff.has_key?(d.name)
352
+ same[d.name] = {:spec => spec_reqs_specifier,
353
+ :upstream => d.requirement.to_s }
354
+ end
355
+ end
356
+
357
+ @metadata[:requires].each do |req|
358
+ next unless req.gem?
359
+
360
+ upstream_dep = upstream_source.deps.find { |d| d.name == req.gem_name }
361
+
362
+ if upstream_dep.nil?
363
+ diff[req.gem_name] = {:spec => req.specifier,
364
+ :upstream => nil}
365
+
366
+ elsif !req.matches?(upstream_dep)
367
+ diff[req.gem_name] = {:spec => req.specifier,
368
+ :upstream => upstream_dep.requirement.to_s }
369
+
370
+ elsif !diff.has_key?(req.gem_name)
371
+ same[req.gem_name] = {:spec => req.specifier,
372
+ :upstream => upstream_dep.requirement.to_s }
373
+ end
374
+ end unless @metadata[:requires].nil?
375
+
376
+ {:same => same, :diff => diff}
377
+ end
378
+
379
+ end # class Spec
380
+ end # module RPM
381
+ end # module Polisher