diggit 1.0.3 → 2.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bdc49eaf85ab5ea705046d562d66d05aec279b07
4
- data.tar.gz: 8046df957be6fcc9a9296256c9bee91562f8ee7e
3
+ metadata.gz: ab5b0bd0d5959100238fb8bdcd2d6471a660bc94
4
+ data.tar.gz: 41bcf9e0cea8e6110faded4f51b37caa8475adf2
5
5
  SHA512:
6
- metadata.gz: 6b42a77ecfb10bc8f95e952d58f1a8bae42395b017038fea82fd5240a2f238645ec9007172dfdc54671e945674bc90bcc96946fadae62238a2781cc9a39c7d52
7
- data.tar.gz: f2626d93e3f2e1179d10f16f5cfcb3ddfcb292f99614518925e60349e1c5814bdf721beed37cb6851822ddba08ba6e5595de7a0fd50855f62f5faa3d48cc7247
6
+ metadata.gz: 2d11da8a99ed2706e882bbdaf9a95e011f46fb3f3e1941c6728311de15b7796ab624ce62ac0220e778094f6a8f29e0b798e79efa6c5916de8cce471a8f9b0b8f
7
+ data.tar.gz: f7272bb6679af26829ee4ccbc1b013f2f6d6c1e2026b01e0501335a0cd726e82b117c42fc8f2a28fbc24bae2ef988c86b37fd54f06581f3b78520cee010850a1
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Diggit
2
2
 
3
- A ruby tool to analyse Git repositories
3
+ A ruby tool to analyze Git repositories
4
4
 
5
5
  # Installation
6
6
 
@@ -36,10 +36,6 @@ A join is performed after all analyses of all repositories have been performed.
36
36
 
37
37
  ## Running analyses
38
38
 
39
- Once diggit is configured you can perform the analyses. First, you have to clone the repositories by using `dgit perform clones`. Then you can launch the analyses by using `dgit perform analyses`. Finally, the joins are executed via the command `dgit perform joins`.
39
+ Once diggit is configured you can perform the analyses. First, you have to clone the repositories by using `dgit clones perform`. Then you can launch the analyses by using `dgit analyses perform`. Finally, the joins are executed via the command `dgit joins perform`. You can use the `mode` option to handle the cleaning of joins or analyses.
40
40
 
41
- At all time, you can check the status of your diggit folder by using `dgit status`. If you want more info on the status of a given repository, you can use the `dgit sources info https://github.com/jrfaller/diggit.git` command.
42
-
43
- ## Cleaning up
44
-
45
- If something is going wrong, you can always delete the results of the joins by using the command `dgit clean joins` and of the analysis with the command `dgit clean analyses`.
41
+ At all time, you can check the status of your diggit folder by using `dgit status`.
data/bin/dgit CHANGED
@@ -1,6 +1,191 @@
1
1
  #!/usr/bin/env ruby
2
2
  # encoding: utf-8
3
3
 
4
- require_relative '../lib/diggit_cli.rb'
4
+ require 'gli'
5
+ require_relative '../lib/dgit'
5
6
 
6
- Diggit::Cli::DiggitCli.start(ARGV)
7
+ include GLI::App
8
+
9
+ program_desc 'A git repository analysis tool.'
10
+
11
+ version Diggit::VERSION
12
+
13
+ subcommand_option_handling :normal
14
+ arguments :strict
15
+
16
+ desc 'Init a diggit folder.'
17
+ skips_pre
18
+ command :init do |c|
19
+ c.action do |_global_options, _options, _args|
20
+ Diggit::Dig.init_dir
21
+ Log.ok "Diggit folder initialized."
22
+ end
23
+ end
24
+
25
+ desc 'Display the status of the diggit folder.'
26
+ command :status do |c|
27
+ c.action do |_global_options, _options, _args|
28
+ Log.info "Config"
29
+ Log.info "======"
30
+ Log.info "- Analyses: #{Diggit::Dig.it.config.get_analyses.join(', ')}"
31
+ Log.info "- Joins: #{Diggit::Dig.it.config.get_joins.join(', ')}"
32
+ Log.info ""
33
+ Log.info "Journal"
34
+ Log.info "======="
35
+ Log.info "- New sources:"
36
+ Log.indent do
37
+ Log.ok "* Ok: #{Diggit::Dig.it.journal.sources_by_state(:new).size}"
38
+ Log.error "* Error: #{Diggit::Dig.it.journal.sources_by_state(:new, true).size}"
39
+ end
40
+ Log.info "- Cloned sources:"
41
+ Log.indent do
42
+ Log.ok "* Ok: #{Diggit::Dig.it.journal.sources_by_state(:cloned).size}"
43
+ Log.error "* Error: #{Diggit::Dig.it.journal.sources_by_state(:cloned, true).size}"
44
+ end
45
+ end
46
+ end
47
+
48
+ desc 'Manage the sources of the diggit folder.'
49
+ command :sources do |c|
50
+ c.desc 'List the sources.'
51
+ c.command :list do |list|
52
+ list.action do |_global_options, _options, _args|
53
+ sources = Diggit::Dig.it.journal.sources
54
+ sources.each_index do |idx|
55
+ msg = "#{idx} #{sources[idx].url} (#{sources[idx].state})"
56
+ sources[idx].error? ? Log.error(msg) : Log.ok(msg)
57
+ end
58
+ end
59
+ end
60
+ c.desc 'Add a source.'
61
+ c.arg_name 'url'
62
+ c.command :add do |add|
63
+ add.action do |_global_options, _options, args|
64
+ Diggit::Dig.it.journal.add_source args[0]
65
+ end
66
+ end
67
+ c.desc 'Import sources from a file.'
68
+ c.arg_name 'file'
69
+ c.command :import do |import|
70
+ import.action do |_global_options, _options, args|
71
+ File.open(args[0]).each { |line| Diggit::Dig.it.journal.add_source(line) }
72
+ end
73
+ end
74
+ c.default_command :list
75
+ end
76
+
77
+ desc 'Manage the joins of the diggit folder.'
78
+ command :joins do |c|
79
+ c.desc 'List the joins'
80
+ c.command :list do |list|
81
+ list.action do |_global_options, _options, _args|
82
+ Diggit::Dig.it.config.get_joins.each { |a| Log.info a.name }
83
+ end
84
+ end
85
+ c.desc 'Add add join.'
86
+ c.arg_name 'name'
87
+ c.command :add do |add|
88
+ add.action do |_global_options, _options, args|
89
+ Diggit::Dig.it.config.add_join args[0]
90
+ end
91
+ end
92
+ c.desc 'Perform joins.'
93
+ c.command :perform do |perform|
94
+ perform.flag [:s, :sources], desc: "list of sources", type: Array, default_value: []
95
+ perform.flag [:a, :analyses], desc: "list of analyses", type: Array, default_value: []
96
+ perform.flag [:m, :mode], desc: "running mode",
97
+ must_match: { "run" => :run, "clean" => :clean, "rerun" => :rerun }, default_value: :run
98
+ perform.action do |_global_options, options, _args|
99
+ Diggit::Dig.it.join(options[:s], options[:a], options[:m])
100
+ end
101
+ end
102
+ c.default_command :list
103
+ end
104
+
105
+ desc 'Manage the analyses of the diggit folder.'
106
+ command :analyses do |c|
107
+ c.desc 'List the analyses'
108
+ c.command :list do |list|
109
+ list.action do |_global_options, _options, _args|
110
+ Diggit::Dig.it.config.get_analyses.each { |a| Log.info a.name }
111
+ end
112
+ end
113
+ c.desc 'Add an analysis.'
114
+ c.arg_name 'name'
115
+ c.command :add do |add|
116
+ add.action do |_global_options, _options, args|
117
+ Diggit::Dig.it.config.add_analysis args[0]
118
+ end
119
+ end
120
+ c.desc 'Perform analyses.'
121
+ c.command :perform do |perform|
122
+ perform.flag [:s, :sources], desc: "list of sources", type: Array, default_value: []
123
+ perform.flag [:a, :analyses], desc: "list of analyses", type: Array, default_value: []
124
+ perform.flag [:m, :mode], desc: "running mode",
125
+ must_match: { "run" => :run, "clean" => :clean, "rerun" => :rerun }, default_value: :run
126
+ perform.action do |_global_options, options, _args|
127
+ Diggit::Dig.it.analyze(options[:s], options[:a], options[:m])
128
+ end
129
+ end
130
+ c.default_command :list
131
+ end
132
+
133
+ desc 'Manage clone actions.'
134
+ command :clone do |c|
135
+ c.desc 'Perform the clones.'
136
+ c.command :perform do |perform|
137
+ perform.flag [:s, :sources], desc: "list of sources", type: Array, default_value: []
138
+ perform.action do |_global_options, options, _args|
139
+ Diggit::Dig.it.clone(*options[:s])
140
+ end
141
+ end
142
+ c.default_command :perform
143
+ end
144
+
145
+ desc 'Display errors.'
146
+ command :errors do |c|
147
+ c.desc 'Display the list of errors.'
148
+ c.command :list do |list|
149
+ list.action do |_global_options, _options, _args|
150
+ sources = Diggit::Dig.it.journal.sources
151
+ sources.each_index do |idx|
152
+ msg = "#{idx} #{sources[idx].url} (#{sources[idx].state})"
153
+ Log.error msg if sources[idx].error?
154
+ end
155
+ end
156
+ end
157
+ c.desc 'Display a source error.'
158
+ c.arg_name 'id'
159
+ c.command :show do |show|
160
+ show.action do |_global_options, _options, args|
161
+ source = Diggit::Dig.it.journal.sources_by_ids(args[0].to_i)[0]
162
+ Log.ok "Error summary for source #{args[0]}"
163
+ error = source.error
164
+ Log.info "URL: #{source.url}"
165
+ Log.info "State: #{source.state}"
166
+ Log.info "Error:"
167
+ Log.indent do
168
+ Log.error error[:message]
169
+ Log.info error[:backtrace].join("\n")
170
+ end
171
+ end
172
+ end
173
+ c.default_command :list
174
+ end
175
+
176
+ pre do |_global, _command, _options, _args|
177
+ Diggit::Dig.init
178
+ end
179
+
180
+ post do |_global, _command, _options, _args|
181
+ # Post logic here, skips_post to skip commands
182
+ end
183
+
184
+ on_error do |exception|
185
+ Log.error "Error running diggit."
186
+ Log.error exception.message
187
+ Log.info exception.backtrace.join("\n")
188
+ false
189
+ end
190
+
191
+ exit run(ARGV)
data/lib/dgit.rb ADDED
@@ -0,0 +1,6 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative "dgit/core"
4
+ require_relative "dgit/plugins"
5
+ require_relative "dgit/version"
6
+ require_relative "dgit/formatador"
data/lib/dgit/core.rb ADDED
@@ -0,0 +1,479 @@
1
+ # encoding: utf-8
2
+
3
+ require 'oj'
4
+ require 'rugged'
5
+ require 'singleton'
6
+ require_relative 'formatador'
7
+
8
+ class String
9
+ def underscore
10
+ gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
11
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2'). tr("-", "_").downcase
12
+ end
13
+
14
+ def camel_case
15
+ return self if self !~ /_/ && self =~ /[A-Z]+.*/
16
+ split('_').map(&:capitalize).join
17
+ end
18
+
19
+ def id
20
+ gsub(/[^[\w-]]+/, "_")
21
+ end
22
+ end
23
+
24
+ module Diggit
25
+ class Source
26
+ attr_reader :url, :repository
27
+ attr_accessor :entry
28
+
29
+ def initialize(url)
30
+ @url = url
31
+ @entry = Journal.new_source_entry
32
+ @repository = nil
33
+ end
34
+
35
+ def id
36
+ @url.id
37
+ end
38
+
39
+ def folder
40
+ Dig.it.file_path("sources/#{id}")
41
+ end
42
+
43
+ def error?
44
+ !(@entry[:last_error].nil? || @entry[:last_error].empty?)
45
+ end
46
+
47
+ def error
48
+ @entry[:last_error]
49
+ end
50
+
51
+ def error=(error)
52
+ @entry[:last_error] = error
53
+ end
54
+
55
+ def state
56
+ @entry[:state]
57
+ end
58
+
59
+ def state=(state)
60
+ @entry[:state] = state
61
+ end
62
+
63
+ def new?
64
+ @entry[:state] == :new
65
+ end
66
+
67
+ def cloned?
68
+ @entry[:state] == :cloned
69
+ end
70
+
71
+ def add_performed_analysis(name)
72
+ @entry[:performed_analyses] << name
73
+ end
74
+
75
+ def analysis_performed?(name)
76
+ @entry[:performed_analyses].include?(name)
77
+ end
78
+
79
+ def analyses_performed?(*names)
80
+ (names - @entry[:performed_analyses]).empty?
81
+ end
82
+
83
+ def del_performed_analysis(name)
84
+ @entry[:performed_analyses].delete_if { |a| a == name }
85
+ end
86
+
87
+ def add_ongoing_analysis(name)
88
+ @entry[:ongoing_analyses].include?(name)
89
+ end
90
+
91
+ def del_ongoing_analysis(name)
92
+ @entry[:ongoing_analyses].delete_if { |a| a == name }
93
+ end
94
+
95
+ def analysis_ongoing?(name)
96
+ @entry[:ongoing_analyses].include?(name)
97
+ end
98
+
99
+ def analyses_ongoing?(*names)
100
+ (names - @entry[:ongoing_analyses]).empty?
101
+ end
102
+
103
+ def analysis?(name)
104
+ analysis_ongoing?(name) || analysis_performed?(name)
105
+ end
106
+
107
+ def del_analysis(name)
108
+ del_ongoing_analysis(name)
109
+ del_performed_analysis(name)
110
+ end
111
+
112
+ def clone
113
+ if File.exist?(folder)
114
+ Rugged::Repository.new(folder)
115
+ else
116
+ Rugged::Repository.clone_at(url, folder)
117
+ end
118
+ self.state = :cloned
119
+ rescue => e
120
+ Log.error "Error cloning #{url}."
121
+ self.error = Journal.dump_error(e)
122
+ end
123
+
124
+ def load_repository
125
+ fail "Source not cloned #{url}." if new?
126
+ @repository = Rugged::Repository.new(folder)
127
+ end
128
+ end
129
+
130
+ class Journal
131
+ def initialize(hash)
132
+ @sources = {}
133
+ @workspace = {}
134
+ hash[:urls].each do |u|
135
+ s = Source.new(u)
136
+ s.entry = hash[:sources][u] if !hash[:sources].nil? && hash[:sources].key?(u)
137
+ @sources[u] = s
138
+ end
139
+ @workspace = hash[:workspace]
140
+ @workspace = Journal.new_workspace_entry if hash[:workspace].nil? || hash[:workspace].empty?
141
+ end
142
+
143
+ def sources
144
+ @sources.values
145
+ end
146
+
147
+ def sources_by_state(state, error = false)
148
+ @sources.select { |_u, s| s.state == state && s.error? == error }.values
149
+ end
150
+
151
+ def sources_by_ids(*ids)
152
+ return sources if ids.empty?
153
+ source_array = sources
154
+ result = []
155
+ ids.each do |id|
156
+ fail "No such source index #{id}." if id >= source_array.length
157
+ result << source_array[id]
158
+ end
159
+ result
160
+ end
161
+
162
+ def update_source(source)
163
+ fail "No such source #{source}." unless @sources.key?(source.url)
164
+ @sources[source.url] = source
165
+ Dig.it.save_journal
166
+ end
167
+
168
+ def add_source(url)
169
+ @sources[url] = Source.new(url) unless @sources.key?(url)
170
+ Dig.it.save_journal
171
+ end
172
+
173
+ def join?(name)
174
+ @workspace[:performed_joins].include?(name)
175
+ end
176
+
177
+ def add_join(name)
178
+ @workspace[:performed_joins] << name
179
+ Dig.it.save_journal
180
+ end
181
+
182
+ def join_error?
183
+ !@workspace[:last_error].nil?
184
+ end
185
+
186
+ def join_error
187
+ @workspace[:last_error]
188
+ end
189
+
190
+ def join_error=(error)
191
+ @workspace[:last_error] = Journal.dump_error(error)
192
+ Dig.it.save_journal
193
+ end
194
+
195
+ def to_hash
196
+ entry_hash = {}
197
+ @sources.each { |entry| entry_hash[entry[0]] = entry[1].entry }
198
+ { urls: @sources.keys, sources: entry_hash, workspace: @workspace }
199
+ end
200
+
201
+ def self.new_source_entry
202
+ { state: :new, performed_analyses: [], error_analyses: [], ongoing_analyses: [], last_error: {} }
203
+ end
204
+
205
+ def self.new_workspace_entry
206
+ { performed_joins: [], last_error: {} }
207
+ end
208
+
209
+ def self.dump_error(error)
210
+ { name: error.class.name, message: error.to_s, backtrace: error.backtrace }
211
+ end
212
+ end
213
+
214
+ class Config
215
+ attr_reader :analyses, :joins
216
+
217
+ def initialize(hash)
218
+ @analyses = []
219
+ @joins = []
220
+ hash[:analyses].each { |a| load_analysis(a) }
221
+ hash[:joins].each { |j| load_join(j) }
222
+ end
223
+
224
+ def to_hash
225
+ { analyses: @analyses.map(&:name), joins: @joins.map(&:name) }
226
+ end
227
+
228
+ def add_analysis(name)
229
+ load_analysis(name) unless @analyses.map(&:name).include?(name)
230
+ Dig.it.save_config
231
+ end
232
+
233
+ def load_analysis(name)
234
+ @analyses << Dig.it.plugin_loader.load_plugin(name, :analysis)
235
+ end
236
+
237
+ def del_analysis(name)
238
+ @analyses.delete_if { |a| a.name == name }
239
+ Dig.it.save_config
240
+ end
241
+
242
+ def get_analyses(*names)
243
+ return analyses if names.empty?
244
+ analyses.select { |a| names.include?(a.name) }
245
+ end
246
+
247
+ def add_join(name)
248
+ load_join(name) unless @joins.map(&:name).include?(name)
249
+ Dig.it.save_config
250
+ end
251
+
252
+ def load_join(name)
253
+ @joins << Dig.it.plugin_loader.load_plugin(name, :join)
254
+ end
255
+
256
+ def del_join(name)
257
+ @joins.delete_if { |j| j.name == name }
258
+ Dig.it.save_config
259
+ end
260
+
261
+ def get_joins(*names)
262
+ return joins if names.empty?
263
+ joins.select { |j| joins.include?(j.name) }
264
+ end
265
+
266
+ def self.empty_config
267
+ { analyses: [], joins: [] }
268
+ end
269
+ end
270
+
271
+ class PluginLoader
272
+ include Singleton
273
+
274
+ PLUGINS_TYPES = [:addon, :analysis, :join]
275
+
276
+ def load_plugin(name, type, instance = false)
277
+ plugin = search_plugin(name, type)
278
+ if plugin
279
+ if instance
280
+ return plugin.new(Dig.it.options)
281
+ else
282
+ return plugin
283
+ end
284
+ else
285
+ fail "Plugin #{name} not found."
286
+ end
287
+ end
288
+
289
+ def search_plugin(name, type)
290
+ plugin = nil
291
+ if @plugins.key?(name)
292
+ plugin = @plugins[name]
293
+ else
294
+ fail "Unknown plugin type #{type}." unless PLUGINS_TYPES.include?(type)
295
+ if load_file(name, type)
296
+ plugin = Object.const_get(name.camel_case)
297
+ base_class = Object.const_get("Diggit::#{type.to_s.camel_case}")
298
+ if plugin < base_class
299
+ @plugins[name] = plugin
300
+ else
301
+ fail "Plugin #{name} not of kind #{type}."
302
+ end
303
+ end
304
+ end
305
+ plugin
306
+ end
307
+
308
+ def load_file(name, type)
309
+ f_glob = PluginLoader.plugin_path(name, type, File.expand_path('../..', File.dirname(File.realpath(__FILE__))))
310
+ f_home = PluginLoader.plugin_path(name, type, File.expand_path(Dig::DGIT_FOLDER, Dir.home))
311
+ f_local = PluginLoader.plugin_path(name, type, Dig.it.folder)
312
+ found = true
313
+ if File.exist?(f_local)
314
+ require f_local
315
+ elsif File.exist?(f_home)
316
+ require f_home
317
+ elsif File.exist?(f_glob)
318
+ require f_glob
319
+ else
320
+ found = false
321
+ end
322
+ found
323
+ end
324
+
325
+ def self.plugin_path(name, type, root)
326
+ File.expand_path("#{name}.rb", File.expand_path(type.to_s, File.expand_path('plugins', root)))
327
+ end
328
+
329
+ def initialize
330
+ @plugins = {}
331
+ end
332
+ end
333
+
334
+ class Dig
335
+ DGIT_FOLDER = ".dgit"
336
+ DGIT_SOURCES = "sources"
337
+ DGIT_CONFIG = "config"
338
+ DGIT_OPTIONS = "options"
339
+ DGIT_JOURNAL = "journal"
340
+
341
+ attr_reader :config, :options, :journal, :plugin_loader, :folder
342
+
343
+ @diggit = nil
344
+
345
+ def self.it
346
+ fail "Diggit has not been initialized." if @diggit.nil?
347
+ @diggit
348
+ end
349
+
350
+ def self.init(folder = '.')
351
+ @diggit = Dig.new(folder)
352
+ @diggit.load_options
353
+ @diggit.load_config
354
+ @diggit.load_journal
355
+ @diggit
356
+ end
357
+
358
+ def self.init_dir(folder = '.')
359
+ dgit_folder = File.expand_path(DGIT_FOLDER, folder)
360
+ FileUtils.mkdir(dgit_folder)
361
+ Oj.to_file(File.expand_path(DGIT_CONFIG, dgit_folder), Config.empty_config)
362
+ Oj.to_file(File.expand_path(DGIT_OPTIONS, dgit_folder), {})
363
+ FileUtils.touch(File.expand_path(DGIT_SOURCES, dgit_folder))
364
+ Oj.to_file(File.expand_path(DGIT_JOURNAL, dgit_folder), {})
365
+ FileUtils.mkdir(File.expand_path('sources', folder))
366
+ end
367
+
368
+ def initialize(folder)
369
+ fail "Folder #{folder} is not a diggit folder." unless File.exist?(File.expand_path(DGIT_FOLDER, folder))
370
+ @plugin_loader = PluginLoader.instance
371
+ @folder = folder
372
+ end
373
+
374
+ def load_journal
375
+ url_array = []
376
+ IO.readlines(config_path(DGIT_SOURCES)).each { |l| url_array << l.strip }
377
+ saved_hash = Oj.load_file(config_path(DGIT_JOURNAL))
378
+ hash = { urls: url_array, sources: saved_hash[:sources], workspace: saved_hash[:workspace] }
379
+ @journal = Journal.new(hash)
380
+ end
381
+
382
+ def save_journal
383
+ hash = @journal.to_hash
384
+ File.open(config_path(DGIT_SOURCES), "w") { |f| hash[:urls].each { |u| f.puts(u) } }
385
+ Oj.to_file(config_path(DGIT_JOURNAL), { sources: hash[:sources], workspace: hash[:workspace] })
386
+ end
387
+
388
+ def load_options
389
+ @options = Oj.load_file(config_path(DGIT_OPTIONS))
390
+ end
391
+
392
+ def save_options
393
+ Oj.to_file(config_path(DGIT_OPTIONS), options)
394
+ end
395
+
396
+ def load_config
397
+ @config = Config.new(Oj.load_file(config_path(DGIT_CONFIG)))
398
+ end
399
+
400
+ def save_config
401
+ config_hash = @config.to_hash
402
+ Oj.to_file(config_path(DGIT_CONFIG), config_hash)
403
+ end
404
+
405
+ def clone(*source_ids)
406
+ @journal.sources_by_ids(*source_ids).select(&:new?).each(&:clone)
407
+ ensure
408
+ save_journal
409
+ end
410
+
411
+ def analyze(source_ids = [], analyses = [], mode = :run)
412
+ @journal.sources_by_ids(*source_ids).select(&:cloned?).each do |s|
413
+ @config.get_analyses(*analyses).each do |klass|
414
+ a = klass.new(@options)
415
+ s.load_repository
416
+ a.source = s
417
+ clean_analysis(s, a) if clean_mode?(mode) && s.analysis?(a.name)
418
+ run_analysis(s, a) if run_mode?(mode) && !s.analysis_performed?(a.name)
419
+ end
420
+ end
421
+ end
422
+
423
+ def clean_mode?(mode)
424
+ mode == :rerun || mode == :clean
425
+ end
426
+
427
+ def run_mode?(mode)
428
+ mode == :rerun || mode == :run
429
+ end
430
+
431
+ def clean_analysis(s, a)
432
+ a.clean
433
+ s.del_analysis(a.name)
434
+ ensure
435
+ save_journal
436
+ end
437
+
438
+ def run_analysis(s, a)
439
+ s.add_ongoing_analysis(a.name)
440
+ a.run
441
+ s.del_ongoing_analysis(a.name)
442
+ s.add_performed_analysis(a.name)
443
+ rescue => e
444
+ Log.error "Error applying analysis #{a.name} on #{s.url}"
445
+ s.error = Journal.dump_error(e)
446
+ ensure
447
+ save_journal
448
+ end
449
+
450
+ def join(source_ids = [], joins = [], mode = :run)
451
+ @config.get_joins(*joins).each do |klass|
452
+ j = klass.new(@options)
453
+ j.clean if clean_mode?(mode)
454
+ source_array = @journal.sources_by_ids(*source_ids)
455
+ .select { |s| s.cloned? && s.analyses_performed?(*klass.required_analyses) }
456
+ run_join(j, source_array) if run_mode?(mode) && !source_array.empty?
457
+ end
458
+ end
459
+
460
+ def run_join(j, source_array)
461
+ j.sources = source_array
462
+ j.run
463
+ @journal.add_join(j.name)
464
+ rescue => e
465
+ Log.error "Error applying join #{j.name}"
466
+ @journal.join_error = e
467
+ ensure
468
+ save_journal
469
+ end
470
+
471
+ def config_path(name)
472
+ File.expand_path(name, File.expand_path(DGIT_FOLDER, @folder))
473
+ end
474
+
475
+ def file_path(name)
476
+ File.expand_path(name, @folder)
477
+ end
478
+ end
479
+ end