baltix 0.1.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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +44 -0
  3. data/.gitignore +10 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE +8 -0
  6. data/README.md +60 -0
  7. data/Rakefile +8 -0
  8. data/TODO +84 -0
  9. data/baltix.gemspec +39 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/exe/baltix +4 -0
  13. data/lib/baltix/actor/copy.rb +19 -0
  14. data/lib/baltix/actor/link.rb +20 -0
  15. data/lib/baltix/actor/spec.rb +25 -0
  16. data/lib/baltix/actor/touch.rb +17 -0
  17. data/lib/baltix/actor.rb +75 -0
  18. data/lib/baltix/cli.rb +173 -0
  19. data/lib/baltix/deps.rb +280 -0
  20. data/lib/baltix/dsl.rb +311 -0
  21. data/lib/baltix/extensions.rb +536 -0
  22. data/lib/baltix/i18n.rb +64 -0
  23. data/lib/baltix/loader/cmake.rb +11 -0
  24. data/lib/baltix/loader/git-version-gen.rb +36 -0
  25. data/lib/baltix/loader/mast.rb +139 -0
  26. data/lib/baltix/loader/pom.rb +27 -0
  27. data/lib/baltix/loader/rookbook.rb +26 -0
  28. data/lib/baltix/loader/yaml.rb +18 -0
  29. data/lib/baltix/loader.rb +192 -0
  30. data/lib/baltix/log.rb +73 -0
  31. data/lib/baltix/rake.rb +57 -0
  32. data/lib/baltix/scheme.erb.yaml +20 -0
  33. data/lib/baltix/source/base.rb +438 -0
  34. data/lib/baltix/source/fake.rb +17 -0
  35. data/lib/baltix/source/gem.rb +407 -0
  36. data/lib/baltix/source/gemfile.rb +35 -0
  37. data/lib/baltix/source/rakefile.rb +24 -0
  38. data/lib/baltix/source.rb +57 -0
  39. data/lib/baltix/space/spec.rb +11 -0
  40. data/lib/baltix/space.rb +424 -0
  41. data/lib/baltix/spec/rpm/name.rb +155 -0
  42. data/lib/baltix/spec/rpm/parser.rb +412 -0
  43. data/lib/baltix/spec/rpm/secondary.rb +170 -0
  44. data/lib/baltix/spec/rpm/spec_core.rb +580 -0
  45. data/lib/baltix/spec/rpm.erb +188 -0
  46. data/lib/baltix/spec/rpm.rb +822 -0
  47. data/lib/baltix/spec.rb +48 -0
  48. data/lib/baltix/version.rb +3 -0
  49. data/lib/baltix.rb +19 -0
  50. data/locale/en_US.UTF-8.yaml +27 -0
  51. data/locale/ru_RU.UTF-8.yaml +23 -0
  52. metadata +216 -0
@@ -0,0 +1,424 @@
1
+ require 'rubygems'
2
+
3
+ require 'baltix/version'
4
+ require 'baltix/log'
5
+ require 'baltix/source'
6
+ require 'baltix/spec'
7
+ require 'baltix/cli'
8
+
9
+ class Baltix::Space
10
+ include Baltix::Log
11
+
12
+ class InvalidSpaceFileError < StandardError; end
13
+
14
+ TYPES = {
15
+ sources: Baltix::Source
16
+ }
17
+
18
+ STATES = {
19
+ invalid: ->(_, source, _) { !source.valid? },
20
+ disabled: ->(space, source, _) { space.is_disabled?(source) },
21
+ duplicated: ->(_, _, dup) { dup },
22
+ }
23
+
24
+ TYPE_CHARS = {
25
+ fake: '·',
26
+ gem: '*',
27
+ gemfile: '&',
28
+ rakefile: '^',
29
+ }
30
+
31
+ STATUS_CHARS = {
32
+ invalid: 'X',
33
+ disabled: '-',
34
+ duplicated: '=',
35
+ valid: 'V',
36
+ }
37
+
38
+
39
+ Gem.load_yaml
40
+
41
+ @@space = {}
42
+ @@options = {}
43
+
44
+ # +options+ property returns the hash of the loaded options if any
45
+ #
46
+ # options #=> {...}
47
+ #
48
+ attr_reader :options, :state
49
+
50
+ # +name+ returns a default name of the space with a prefix if any. It returns name of a source when
51
+ # its root is the same as the space's root, or returns name defined in the spec if any.
52
+ # If no spec defined returns name of last folder in rootdir or "root" as a default main source name.
53
+ #
54
+ # space.name # => space-name
55
+ #
56
+ def name
57
+ return @name if @name
58
+
59
+ @name = spec&.name || main_source&.name
60
+ end
61
+
62
+ # +version+ returns a default version for the space. Returns version of a source when
63
+ # its root is the same as the space's root, or returns version defined in the spec if any,
64
+ # or returns default one.
65
+ #
66
+ # space.version # => 2.1.1
67
+ #
68
+ def version
69
+ return @version if @version
70
+
71
+ @version ||= main_source&.version || spec&.version
72
+ end
73
+
74
+ attr_writer :rootdir
75
+
76
+ # +rootdir+ returns the root dir for the space got from the options,
77
+ # defaulting to the current folder.
78
+ #
79
+ # rootdir #=> /root/dir/for/the/space
80
+ #
81
+ def rootdir
82
+ @rootdir ||= read_attribute(:rootdir) || Dir.pwd
83
+ end
84
+
85
+ # +main_source+ selects main source from the list of sources when source root dir is the space's one and
86
+ # source's name contains minimum name size among the matching sources
87
+ #
88
+ def main_source
89
+ return @main_source if @main_source
90
+
91
+ reals = valid_sources.select { |source| !source.is_a?(Baltix::Source::Fake) }
92
+ if spec && !spec.state.blank?
93
+ specen_source = reals.find { |real| spec.state["name"] === real.name }
94
+ end
95
+
96
+ root_source ||= valid_sources.sort {|x, y| x.name.size <=> y.name.size }.find { |source| source.rootdir == rootdir }
97
+ @main_source =
98
+ specen_source || root_source.is_a?(Baltix::Source::Fake) && reals.size == 1 && reals.first || root_source
99
+ end
100
+
101
+ def time_stamp
102
+ Time.now.strftime("%Y%m%d")
103
+ end
104
+
105
+ # +changes+ returns a list of open-struct formatted changes in the space or
106
+ # spec defined if any, otherwise returns blank array.
107
+ #
108
+ # space.changes # => []
109
+ #
110
+ def changes
111
+ @changes ||= spec&.changes || main_source&.respond_to?(:changes) && main_source.changes || []
112
+ end
113
+
114
+ # +summaries+ returns an open-struct formatted summaries with locales as keys
115
+ # in the space or spec defined if any, otherwise returns blank open struct.
116
+ #
117
+ # space.summaries # => #<OpenStruct en_US.UTF-8: ...>
118
+ #
119
+ def summaries
120
+ return @summaries if @summaries
121
+
122
+ if summaries = spec&.summaries || state.summaries
123
+ summaries
124
+ elsif summary = main_source&.summary
125
+ { Baltix::I18n.default_locale => summary }.to_os
126
+ end
127
+ end
128
+
129
+ # +licenses+ returns license list defined in all the valid sources found in the space.
130
+ #
131
+ # space.licenses => # ["MIT"]
132
+ #
133
+ def licenses
134
+ return @licenses if @licenses
135
+
136
+ licenses = valid_sources.map { |source| source.licenses rescue [] }.flatten.uniq
137
+
138
+ @licenses = !licenses.blank? && licenses || spec&.licenses || []
139
+ end
140
+
141
+ # +dependencies+ returns all the valid source dependencies list as an array of Gem::Dependency
142
+ # objects, otherwise returning blank array.
143
+ def dependencies
144
+ @dependencies ||= valid_sources.map do |source|
145
+ source.respond_to?(:dependencies) && source.dependencies || []
146
+ end.flatten.reject do |dep|
147
+ match_platform?(dep) || sources.any? do |s|
148
+ dep.name == s.name &&
149
+ dep.requirement.satisfied_by?(Gem::Version.new(s.version))
150
+ end
151
+ end
152
+ end
153
+
154
+ def match_platform? dep
155
+ if dep.respond_to?(:platforms)
156
+ (dep.platforms & options.skip_platforms).any?
157
+ end
158
+ end
159
+
160
+ def files
161
+ @files ||= valid_sources.map { |s| s.files rescue [] }.flatten.uniq
162
+ end
163
+
164
+ def executables
165
+ @executables ||= valid_sources.map { |s| s.executables rescue [] }.flatten.uniq
166
+ end
167
+
168
+ def docs
169
+ @docs ||= valid_sources.map { |s| s.docs rescue [] }.flatten.uniq
170
+ end
171
+
172
+ def compilables
173
+ @compilables ||= valid_sources.map { |s| s.extensions rescue [] }.flatten.uniq
174
+ end
175
+
176
+ # +sources+ returns all the sources in the space. It will load from the space sources,
177
+ # or by default will search sources in the provided folder or the current one.
178
+ #
179
+ # space.sources => # [#<Baltix::Source:...>, #<...>]
180
+ #
181
+ def sources
182
+ @sources ||= stat_sources.map { |x| x.first }
183
+ end
184
+
185
+ # +valid_sources+ returns all the valid sources based on the current source list.
186
+ #
187
+ # space.valid_sources => # [#<Baltix::Source:...>, #<...>]
188
+ #
189
+ # def valid_sources
190
+ # @valid_sources ||= sources.select do |source|
191
+ # source.valid? && is_regarded?(source)
192
+ # end
193
+ # end
194
+ def valid_sources
195
+ @valid_sources = stat_sources.map {|(source, status)| status == :valid && source || nil }.compact
196
+ end
197
+
198
+ # def is_regarded? source
199
+ # regarded_names.any? {|i| i === source.name } ||
200
+ # !ignored_names.any? {|i| i === source.name } &&
201
+ # !ignored_path_tokens.any? {|t| /\/#{t}\// =~ source.source_file }
202
+ # end
203
+
204
+ def ignored_names
205
+ @ignored_names ||= (read_attribute(:ignored_names) || [])
206
+ end
207
+
208
+ def regarded_names
209
+ @regarded_names ||= read_attribute(:regarded_names) || []
210
+ end
211
+
212
+ def ignored_path_tokens
213
+ @ignored_path_tokens ||= (read_attribute(:ignored_path_tokens) || [])
214
+ end
215
+
216
+ def spec_type
217
+ @spec_type ||= read_attribute(:spec_type) || spec && spec.class.to_s.split("::").last.downcase
218
+ end
219
+
220
+ def read_attribute attr
221
+ options.send(attr) || state.send(attr)
222
+ end
223
+
224
+ def options_for type
225
+ @@options[type] = type::OPTIONS.map do |option|
226
+ value = self.options[option] || self.respond_to?(option) && self.send(option) || nil
227
+
228
+ [ option, value ]
229
+ end.compact.to_os
230
+ end
231
+
232
+ # +spec+ property returns the hash of the loaded spec if any, it can be freely
233
+ # reassigned.
234
+ #
235
+ # spec #=> {...}
236
+ #
237
+ def spec
238
+ @spec ||= gen_spec
239
+ end
240
+
241
+ def spec= value
242
+ gen_spec(value)
243
+ end
244
+
245
+ def is_disabled? source
246
+ options.ignored_path_tokens.any? { |t| /\/#{t}\// =~ source.source_file } ||
247
+ options.regarded_names.all? { |i| !i.match?(source.name) } &&
248
+ options.ignored_names.any? { |i| i === source.name }
249
+ end
250
+
251
+ protected
252
+
253
+ def gen_spec spec_in = nil
254
+ spec_pre = spec_in || state.spec
255
+
256
+ @spec =
257
+ if spec_pre.is_a?(Baltix::Spec::Rpm)
258
+ spec_pre
259
+ elsif spec_pre.is_a?(String)
260
+ self.class.load(spec_pre)
261
+ elsif options&.spec_file
262
+ Baltix::Spec.load_from(source: IO.read(options.spec_file), options: options, space: self)
263
+ elsif @spec_type || options&.spec_type
264
+ Baltix::Spec.find(@spec_type || options.spec_type).new(options: options, space: self)
265
+ end
266
+
267
+ @spec.assign_space(self) if @spec
268
+
269
+ @spec
270
+ end
271
+
272
+ def initialize state_in = {}, options = {}
273
+ @options = Baltix::CLI::DEFAULT_OPTIONS.merge(options || {})
274
+ @state = (state_in || {}).to_os
275
+
276
+ baltix_log
277
+ end
278
+
279
+ # init log
280
+ def baltix_log
281
+ ios = DEFAULT_IO_NAMES.merge(%i(error warn info debug).map {|k| [k, options["#{k}_io"]]}.to_h)
282
+ Baltix::Log.setup(options.log_level.to_sym, ios)
283
+ end
284
+
285
+ def show_tree
286
+ info("Sources:")
287
+ stat_source_tree.each do |(path_in, stated_sources)|
288
+ stated_sources.each do |(source, status)|
289
+ path = source.source_path_from(rootdir) if source.source_file
290
+ stat = [STATUS_CHARS[status], TYPE_CHARS[source.type.to_sym]].join(" ")
291
+ namever = [source.name, source.version].compact.join(":")
292
+ info_in = "#{stat}#{namever} [#{path}]"
293
+
294
+ info(info_in)
295
+ end
296
+ end
297
+ end
298
+
299
+ # returns all the sources with their statuses, and sorted by a its rootdir value
300
+ #
301
+ def stat_sources &block
302
+ return @stat_sources if @stat_sources
303
+
304
+ @stat_sources =
305
+ read_attribute(:stat_sources) || Baltix::Source.search_in(rootdir, options).group_by do |x|
306
+ [x.name, x.version].compact.join(":")
307
+ end.map do |(full_name, v)|
308
+ sorten =
309
+ v.sort do |x,y|
310
+ c0 = ::Baltix::Source.loaders.index(x.loader) <=> ::Baltix::Source.loaders.index(y.loader)
311
+ c1 = c0 == 0 && x.name <=> y.name || c0
312
+ c2 = c1 == 0 && y.version <=> x.version || c1
313
+ c3 = c2 == 0 && y.platform <=> x.platform || c2
314
+ c4 = c3 == 0 && y.source_names.grep(/gemspec/).count <=> x.source_names.grep(/gemspec/).count
315
+
316
+ c2 == 0 && c3 == 0 && c4 == 0 && x.rootdir.size <=> y.rootdir.size || c4 != 0 && c4 || c3 != 0 && c3 || c2
317
+ end.reduce([[], 0]) do |(res, index), source|
318
+ dup = source.valid? && index > 0
319
+ dup_index = source.valid? && index + 1 || index
320
+
321
+ [res | [[source, source_status(source, dup)]], dup_index]
322
+ end.first
323
+
324
+ sorten[1..-1].each { |x| sorten.first.first.alias_to(x.first) }
325
+
326
+ sorten
327
+ end.flatten(1).sort_by {|(x, _)| x.rootdir.size }.each do |(source, status)|
328
+ block[source, status] if block_given?
329
+ end
330
+
331
+ show_tree
332
+
333
+ @stat_sources
334
+ end
335
+
336
+ # returns source tree, and sorted, and then grouped by a its rootdir value
337
+ #
338
+ def stat_source_tree
339
+ @stat_source_tree ||=
340
+ stat_sources.group_by {|(x, _)| x.rootdir }.map do |(path, sources)|
341
+ [File.join('.', path[rootdir.size..-1] || ''), sources]
342
+ end.to_h
343
+ end
344
+
345
+ # returns status for the source for the project
346
+ #
347
+ def source_status source, dup
348
+ %i(valid duplicated disabled invalid).reduce() do |res, status|
349
+ STATES[status][self, source, dup] && status || res
350
+ end
351
+ end
352
+
353
+ def context
354
+ @context ||= options[:context] || spec&.context || {}
355
+ end
356
+
357
+ def method_missing method, *args
358
+ value =
359
+ instance_variable_get(:"@#{method}") ||
360
+ (spec.send(method) rescue nil) ||
361
+ options&.[](method) ||
362
+ spec&.options&.[](method.to_s) ||
363
+ state[method]
364
+
365
+ instance_variable_set(:"@#{method}", value || super)
366
+
367
+ value
368
+ end
369
+
370
+ class << self
371
+ def load string
372
+ if Gem::Version.new(Psych::VERSION) >= Gem::Version.new("4.0.0")
373
+ YAML.load(string,
374
+ aliases: true,
375
+ permitted_classes: [
376
+ Baltix::Source::Fake,
377
+ Baltix::Source::Rakefile,
378
+ Baltix::Source::Gemfile,
379
+ Baltix::Source::Gem,
380
+ Baltix::Spec::Rpm,
381
+ Baltix::Spec::Rpm::Name,
382
+ Baltix::Spec::Rpm::Secondary,
383
+ Gem::Specification,
384
+ Gem::Version,
385
+ Gem::Dependency,
386
+ Gem::Requirement,
387
+ OpenStruct,
388
+ Symbol,
389
+ Time,
390
+ Date
391
+ ])
392
+ else
393
+ YAML.load(string)
394
+ end
395
+ end
396
+
397
+ def load_from! state: Dir[".space"].first, options: {}
398
+ # system_path_check # TODO required to generate spec rubocop
399
+
400
+ state_tmp =
401
+ case state
402
+ when IO, StringIO
403
+ load(state.readlines.join(""))
404
+ when String
405
+ raise InvalidSpaceFileError.new(state: state) if !File.file?(state)
406
+
407
+ load(IO.read(state))
408
+ when NilClass
409
+ else
410
+ raise InvalidSpaceFileError
411
+ end.to_os
412
+
413
+ @@space[state_tmp.name] = self.new(state_tmp, options)
414
+ end
415
+
416
+ def load_from state: Dir[".space"].first, options: {}
417
+ load_from!(state: state, options: options)
418
+ rescue InvalidSpaceFileError
419
+ @@space[nil] = new(nil, options)
420
+ end
421
+ end
422
+ end
423
+
424
+ require 'baltix/space/spec'
@@ -0,0 +1,155 @@
1
+ class Baltix::Spec::Rpm::Name
2
+ class InvalidAdoptedNameError < StandardError; end
3
+ class UnsupportedMatchError < StandardError; end
4
+
5
+ PREFICES = %w(gem ruby rubygem)
6
+ RULE = /^(?<full_name>(?:(?<prefix>#{PREFICES.join('|')})-)?(?<name>.*?))(?:-(?<suffix>doc|devel))?$/
7
+
8
+ attr_reader :name, :kind, :suffix
9
+ attr_accessor :support_name
10
+
11
+ def aliases
12
+ @aliases ||= []
13
+ end
14
+
15
+ def prefix
16
+ @prefix ||= kind == "lib" && self.class.default_prefix || nil
17
+ end
18
+
19
+ def self.default_prefix
20
+ "gem"
21
+ end
22
+
23
+ def support_name= value
24
+ case @support_name = value
25
+ when NilClass
26
+ @kind = kind == "exec" && "app" || @kind
27
+ else
28
+ @kind = kind == "app" && "exec" || @kind
29
+ end
30
+ end
31
+
32
+ def eql? other, deep = false
33
+ case other
34
+ when self.class
35
+ self.eql_by?(:kind, other) && self.eql_by?(:name, other)
36
+ when String, Symbol
37
+ (([ autoname, fullname ] | [ aliases ].flatten) & self.class.parse(other).aliases).any?
38
+ else
39
+ other.to_s == self.fullname
40
+ end || deep && self.eql_by?(:support_name, other)
41
+ end
42
+
43
+ def == other
44
+ eql?(other)
45
+ end
46
+
47
+ def === other
48
+ eql?(other)
49
+ end
50
+
51
+ def =~ re
52
+ to_s =~ re
53
+ end
54
+
55
+ def to_s
56
+ fullname
57
+ end
58
+
59
+ def merge other
60
+ options =
61
+ %w(prefix suffix name support_name kind).map do |prop|
62
+ [ prop.to_sym, other.send(prop) || self.send(prop) ]
63
+ end.to_h
64
+
65
+ self.class.new(options.merge(aliases: self.aliases | other.aliases))
66
+ end
67
+
68
+ def eql_by? value, other
69
+ case value
70
+ when :name
71
+ ([ self.name, self.aliases ].flatten & [ other.name, other.aliases ].flatten).any?
72
+ when :kind
73
+ self.kind == other.kind
74
+ when :support_name
75
+ self.support_name === (other.is_a?(self.class) && other.support_name || other)
76
+ else
77
+ raise(UnsupportedMatchError.new)
78
+ end
79
+ end
80
+
81
+ # +fullname+ returns newly reconstructed adopted full name based on the storen data.
82
+ # All the "." and "_" is replaced with "-", and "ruby" prefix with "gem".
83
+ #
84
+ # name.fullname #=> "gem-foo-bar-baz-doc"
85
+ #
86
+ def fullname
87
+ [ autoprefix, autoname, autosuffix ].compact.join("-")
88
+ end
89
+
90
+ def original_fullname
91
+ [ prefix, name, suffix ].compact.join("-")
92
+ end
93
+
94
+ def autoprefix
95
+ %w(exec app).include?(kind) || %w(app).include?(support_name&.kind) ? nil : self.class.default_prefix
96
+ end
97
+
98
+ def autoname
99
+ name&.downcase&.gsub(/[\._]/, "-")
100
+ end
101
+
102
+ def autosuffix
103
+ %w(doc devel).include?(kind) && kind || nil
104
+ end
105
+
106
+ protected
107
+
108
+ def initialize options = {}
109
+ @aliases = options.fetch(:aliases, []) | options.fetch(:name, "").gsub(/[\.\_]+/, "-").split(",")
110
+ @prefix = options[:prefix]
111
+ @suffix = options[:suffix]
112
+ @name = options[:name]
113
+ @support_name = options[:support_name]
114
+ @kind = options[:kind] && options[:kind].to_s ||
115
+ @suffix ||
116
+ @prefix && "lib" ||
117
+ @support_name && "exec" || "app"
118
+ end
119
+
120
+ class << self
121
+ def parse name_in, options_in = {}
122
+ m, kind =
123
+ if name_in.is_a?(self)
124
+ [name_in.original_fullname.match(RULE), name_in.kind]
125
+ else
126
+ [name_in.match(RULE)]
127
+ end
128
+
129
+ aliases_in = (options_in[:aliases] || []).flatten.uniq
130
+ subaliases = aliases_in - [ m["full_name"] ]
131
+ #aliases = subaliases | [ m["full_name"] ]
132
+
133
+ raise(InvalidAdoptedNameError) if !m
134
+
135
+ prefixed = subaliases.size >= aliases_in.size
136
+ options = {
137
+ prefix: prefixed && m["prefix"] || nil,
138
+ #prefix: subaliases.blank? && m["prefix"] || nil,
139
+ #prefix: m["prefix"],
140
+ suffix: m["suffix"],
141
+ kind: kind,
142
+ #name: m["name"],
143
+ name: prefixed && m["name"] || m["full_name"],
144
+ #name: subaliases.blank? && m["name"] || m["full_name"],
145
+ }.merge(options_in).merge({
146
+ aliases: subaliases | [ m["full_name"] ]
147
+ })
148
+
149
+ options[:name] = options[:name].blank? && options[:aliases].first || options[:name]
150
+ #binding.pry if name_in =~ /ruby/
151
+
152
+ new(options)
153
+ end
154
+ end
155
+ end