liri 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,258 @@
1
+ =begin
2
+ Este modulo es el punto de entrada del programa principal
3
+ =end
4
+ require 'all_libraries'
5
+
6
+ module Liri
7
+ class Manager
8
+ UDP_REQUEST_DELAY = 3
9
+ attr_reader :agents
10
+
11
+ class << self
12
+ # Inicia la ejecución del Manager
13
+ # @param stop [Boolean] el valor true es para que no se ejecute infinitamente el método en el test unitario.
14
+ def run(stop = false)
15
+ return unless valid_project
16
+ Liri.create_folders('manager')
17
+
18
+ Liri.set_logger(Liri::MANAGER_LOGS_FOLDER_PATH, 'liri-manager.log')
19
+ Liri.logger.info("Proceso Manager iniciado")
20
+ puts "Presione Ctrl + c para terminar el proceso Manager manualmente\n\n"
21
+
22
+ user, password = get_credentials
23
+
24
+ source_code = compress_source_code
25
+
26
+ manager_data = manager_data(user, password, source_code)
27
+
28
+ all_tests = get_all_tests(source_code)
29
+
30
+ test_result = Liri::Manager::TestResult.new
31
+
32
+ manager = Manager.new(Liri.udp_port, Liri.tcp_port, all_tests, test_result)
33
+
34
+ threads = []
35
+ threads << manager.start_client_socket_to_search_agents(manager_data) # Enviar peticiones broadcast a toda la red para encontrar Agents
36
+ manager.start_server_socket_to_process_tests(threads[0]) # Esperar y enviar los test unitarios a los Agents
37
+
38
+ Liri.init_exit(stop, threads, 'Manager')
39
+ Liri.logger.info("Proceso Manager terminado")
40
+ rescue SignalException => e
41
+ Liri.logger.info("Exception(#{e}) Proceso Manager terminado manualmente")
42
+ Liri.kill(threads)
43
+ end
44
+
45
+ def test_samples_by_runner
46
+ Liri.setup.test_samples_by_runner
47
+ end
48
+
49
+ private
50
+ def valid_project
51
+ if File.exist?(File.join(Dir.pwd, 'Gemfile'))
52
+ true
53
+ else
54
+ Liri.logger.info("No se encuentra un archivo Gemfile por lo que se asume que el directorio actual no corresponde a un proyecto Ruby")
55
+ Liri.logger.info("Liri sólo puede ejecutarse en proyectos Ruby")
56
+ Liri.logger.info("Proceso Manager terminado")
57
+ false
58
+ end
59
+ end
60
+
61
+ def compressed_file_folder_path
62
+ File.join(Liri::Common.setup_folder_path, '/manager')
63
+ end
64
+
65
+ def decompressed_file_folder_path
66
+ compressed_file_folder_path
67
+ end
68
+
69
+ def get_credentials
70
+ credential = Liri::Manager::Credential.new(Liri::SETUP_FOLDER_PATH)
71
+ credential.get
72
+ end
73
+
74
+ def compress_source_code
75
+ source_code = Liri::Common::SourceCode.new(Liri::MANAGER_FOLDER_PATH, Liri.compression_class, Liri.unit_test_class)
76
+ Liri::Common::Benchmarking.start(start_msg: "Comprimiendo código fuente. Espere... ") do
77
+ source_code.compress_folder
78
+ end
79
+ puts ''
80
+ source_code
81
+ end
82
+
83
+ def manager_data(user, password, source_code)
84
+ # puts "User: #{user} Password: #{password}"
85
+ [user, password, source_code.compressed_file_path].join(';')
86
+ end
87
+
88
+ def get_all_tests(source_code)
89
+ all_tests = {}
90
+ Liri::Common::Benchmarking.start(start_msg: "Obteniendo conjunto de pruebas. Espere... ") do
91
+ all_tests = source_code.all_tests
92
+ end
93
+ puts ''
94
+ all_tests
95
+ end
96
+ end
97
+
98
+ def initialize(udp_port, tcp_port, all_tests, test_result)
99
+ @udp_port = udp_port
100
+ @udp_socket = UDPSocket.new
101
+ @tcp_port = tcp_port
102
+
103
+ @all_tests = all_tests
104
+ @all_tests_count = all_tests.size
105
+ @all_tests_results = {}
106
+ @all_tests_results_count = 0
107
+ @all_tests_processing_count = 0
108
+ @agents = {}
109
+
110
+ @agents_search_processing_enabled = true
111
+ @test_processing_enabled = true
112
+
113
+ @test_result = test_result
114
+ @semaphore = Mutex.new
115
+ end
116
+
117
+ # Inicia un cliente udp que hace un broadcast en toda la red para iniciar una conexión con los Agent que estén escuchando
118
+ def start_client_socket_to_search_agents(user_data)
119
+ # El cliente udp se ejecuta en bucle dentro de un hilo, esto permite realizar otras tareas mientras este hilo sigue sondeando
120
+ # la red para obtener mas Agents. Una vez que los tests terminan de ejecutarse, este hilo será finalizado.
121
+ Thread.new do
122
+ puts "\nBuscando Agentes... Espere"
123
+ Liri.logger.info("Se emite un broadcast cada #{UDP_REQUEST_DELAY} segundos en el puerto UDP: #{@udp_port}
124
+ (Se mantiene escaneando la red para encontrar Agents)
125
+ ")
126
+ while @agents_search_processing_enabled
127
+ @udp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
128
+ @udp_socket.send(user_data, 0, '<broadcast>', @udp_port)
129
+ sleep(UDP_REQUEST_DELAY) # Se pausa un momento antes de efectuar nuevamente la petición broadcast
130
+ end
131
+ end
132
+ end
133
+
134
+ # Inicia un servidor tcp para procesar los pruebas después de haberse iniciado la conexión a través de udp
135
+ def start_server_socket_to_process_tests(search_agents_thread)
136
+ begin
137
+ tcp_socket = TCPServer.new(@tcp_port) # se hace un bind al puerto dado
138
+ rescue Errno::EADDRINUSE => e
139
+ Liri.logger.error("Exception(#{e}) Puerto TCP #{@tcp_port} ocupado.")
140
+ Thread.kill(search_agents_thread)
141
+ Thread.exit
142
+ end
143
+
144
+ Liri.logger.info("En espera para establecer conexión con los Agents en el puerto TCP: #{@tcp_port}
145
+ (Se espera que algún Agent se conecte para ejecutar las pruebas como respuesta al broadcast UDP)
146
+ ")
147
+ # El siguiente bucle permite que varios clientes es decir Agents se conecten
148
+ # De: http://www.w3big.com/es/ruby/ruby-socket-programming.html
149
+ while @test_processing_enabled
150
+ Thread.start(tcp_socket.accept) do |client|
151
+ agent_ip_address = client.remote_address.ip_address
152
+ response = client.recvfrom(1000).first
153
+
154
+ if @all_tests.empty?
155
+ # No importa lo que le haga, el broadcast udp no se muere al instante y el agente sigue respondiendo
156
+ # Las siguientes dos lineas son para que se deje de hacer el broadcast pero aun asi se llegan a hacer
157
+ # 3 a 4 broadcast antes de que se finalize el proceso, al parecer el broadcast va a tener que quedar asi
158
+ # y mejorar el codigo para que se envien test pendientes para eso hay que llevar una lista de test pendientes
159
+ # tests enviados sin resultados, tests finalizados, si se recibe respuesta al broadcast se trata de enviar primero test pendientes
160
+ # luego test enviados sin resultados o sino ignorar
161
+ Thread.kill(search_agents_thread)
162
+ @agents_search_processing_enabled = false
163
+ Liri.logger.info("Se termina cualquier proceso pendiente con el Agent #{agent_ip_address}")
164
+ Liri.logger.info(response)
165
+ client.close
166
+ Thread.exit
167
+ end
168
+
169
+ puts "\nConexión iniciada con el Agente: #{agent_ip_address}"
170
+ Liri.logger.info("Respuesta al broadcast recibida del Agent: #{agent_ip_address} en el puerto TCP: #{@tcp_port}
171
+ => Agent #{agent_ip_address}: #{response}
172
+ ")
173
+
174
+ while @all_tests.any?
175
+ tests = samples
176
+ break if tests.empty?
177
+ begin
178
+ client.puts(tests.to_json) # Este envía un hash bastante grande al cliente y siempre llega, pero la respuesta del cliente a veces no llega todo, porque?
179
+ response = client.recvfrom(1000).first
180
+ rescue Errno::EPIPE => e
181
+ # Esto al parecer se da cuando el Agent ya cerró las conexiones y el Manager intenta contactar
182
+ Liri.logger.error("Exception(#{e}) El Agent #{agent_ip_address} ya terminó la conexión")
183
+ # Si el Agente ya no responde es mejor romper el bucle para que no quede colgado
184
+ break
185
+ end
186
+ # TODO A veces se tiene un error de parseo JSON, de ser asi los resultados no pueden procesarse,
187
+ # hay que arreglar esto, mientras, se captura el error para que no falle
188
+ begin
189
+ tests_result = response
190
+ Liri.logger.debug("Resultados de la ejecución de las pruebas recibidas del Agent #{agent_ip_address}:")
191
+ Liri.logger.debug("RAW:")
192
+ Liri.logger.debug(tests_result)
193
+ json_tests_result = JSON.parse(tests_result)
194
+ Liri.logger.debug("JSON:")
195
+ Liri.logger.debug(json_tests_result)
196
+ process_tests_result(tests, json_tests_result)
197
+ rescue JSON::ParserError => e
198
+ Liri.logger.error("Exception(#{e}) Error de parseo JSON")
199
+ end
200
+ end
201
+
202
+ update_processing_statuses
203
+ puts ''
204
+ Liri.logger.info("Se termina la conexión con el Agent #{agent_ip_address}")
205
+ begin
206
+ client.puts('exit') # Se envía el string exit para que el Agent sepa que el proceso terminó
207
+ client.close # se desconecta el cliente
208
+ rescue Errno::EPIPE => e
209
+ # Esto al parecer se da cuando el Agent ya cerró las conexiones y el Manager intenta contactar
210
+ Liri.logger.error("Exception(#{e}) El Agent #{agent_ip_address} ya terminó la conexión")
211
+ # Si el Agente ya no responde es mejor terminar el hilo. Aunque igual quedará colgado el Manager
212
+ # mientras sigan pruebas pendientes
213
+ Thread.exit
214
+ end
215
+ end
216
+ end
217
+
218
+ Liri.clean_folder(Liri::MANAGER_FOLDER_PATH)
219
+ @test_result.print_summary
220
+ end
221
+
222
+ def update_processing_statuses
223
+ @semaphore.synchronize {
224
+ @test_processing_enabled = false if @all_tests_count == @all_tests_results_count
225
+ @agents_search_processing_enabled = false if @all_tests_count == @all_tests_processing_count
226
+ }
227
+ end
228
+
229
+ def update_all_tests_results_count(new_count)
230
+ @all_tests_results_count += new_count
231
+ end
232
+
233
+ def update_all_tests_processing_count(new_count)
234
+ @all_tests_processing_count += new_count
235
+ end
236
+
237
+ def samples
238
+ _samples = nil
239
+ # Varios hilos no deben acceder simultaneamente al siguiente bloque porque actualiza variables compartidas
240
+ @semaphore.synchronize {
241
+ _samples = @all_tests.sample!(Manager.test_samples_by_runner)
242
+ puts ''
243
+ Liri.logger.info("Cantidad de pruebas pendientes: #{@all_tests.size}")
244
+ update_all_tests_processing_count(_samples.size)
245
+ }
246
+ _samples
247
+ end
248
+
249
+ def process_tests_result(tests, tests_result)
250
+ # Varios hilos no deben acceder simultaneamente al siguiente bloque porque actualiza variables compartidas
251
+ @semaphore.synchronize {
252
+ update_all_tests_results_count(tests.size)
253
+ @test_result.print_process(tests_result)
254
+ @test_result.update(tests_result)
255
+ }
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,70 @@
1
+ =begin
2
+ Este modulo se encarga de manejar la configuración
3
+ =end
4
+ require 'yaml'
5
+ require 'json'
6
+
7
+ module Liri
8
+ class Manager
9
+ class Setup
10
+ FILE_NAME = 'liri-config.yml'
11
+ TEMPLATE_PATH = File.join(File.dirname(File.dirname(File.dirname(__FILE__))), 'template/liri-config.yml')
12
+
13
+ def initialize(folder_path)
14
+ # Crea la carpeta en donde se guardarán los datos relativos a liri, ya sean archivos comprimidos,
15
+ # archivos descomprimidos, configuraciones, etc.
16
+ Dir.mkdir(folder_path) unless Dir.exist?(folder_path)
17
+
18
+ @file_path = File.join(folder_path, '/', FILE_NAME)
19
+ end
20
+
21
+ # Crea un archivo de configuración en la raiz del proyecto desde un template
22
+ def create
23
+ File.open(@file_path, "w") do |output_file|
24
+ File.foreach(TEMPLATE_PATH) do |input_line|
25
+ output_file.write(input_line)
26
+ end
27
+ end
28
+ true
29
+ end
30
+
31
+ # Retorna los datos del archivo de configuración
32
+ def load
33
+ if File.exist?(@file_path)
34
+ data = YAML.load(File.read(@file_path))
35
+ JSON.parse(data.to_json, object_class: OpenStruct)
36
+ else
37
+ raise Liri::FileNotFoundError.new(@file_path)
38
+ end
39
+ end
40
+
41
+ # Borra el archivo de configuración
42
+ def delete
43
+ if File.exist?(@file_path)
44
+ File.delete(@file_path)
45
+ true
46
+ else
47
+ false
48
+ end
49
+ end
50
+
51
+ def set(value, *keys)
52
+ data = YAML.load(File.read(@file_path))
53
+ keys = keys.first
54
+ aux = data
55
+ keys.each_with_index do |key, index|
56
+ if (keys[index + 1])
57
+ aux = data[key]
58
+ else
59
+ aux[key] = value
60
+ end
61
+ end
62
+ File.open(@file_path, 'w') { |f| f.write data.to_yaml }
63
+ end
64
+
65
+ def path
66
+ @file_path
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,38 @@
1
+ # Procesa el resultado de las pruebas unitarias
2
+ module Liri
3
+ class Manager
4
+ class TestResult
5
+ def initialize
6
+ @example_quantity = 0
7
+ @failure_quantity = 0
8
+ @passed_quantity = 0
9
+ end
10
+ def update(test_result)
11
+ @example_quantity += test_result['example_quantity']
12
+ @failure_quantity += test_result['failure_quantity']
13
+ @passed_quantity += (@example_quantity - @failure_quantity)
14
+ end
15
+
16
+ def print_process(test_result)
17
+ passed_quantity = test_result['example_quantity'] - test_result['failure_quantity']
18
+ passed_quantity.times do
19
+ print '.'
20
+ end
21
+
22
+ test_result['failure_quantity'].times do
23
+ print 'F'
24
+ end
25
+ end
26
+
27
+ def print_summary
28
+ print_examples_and_failures
29
+ end
30
+
31
+ private
32
+
33
+ def print_examples_and_failures
34
+ puts "#{@example_quantity} examples, #{@failure_quantity} failures\n\n"
35
+ end
36
+ end
37
+ end
38
+ end
data/lib/task.rb ADDED
@@ -0,0 +1,15 @@
1
+ =begin
2
+ Este módulo ejecuta tareas de apoyo
3
+ =end
4
+
5
+ module Liri
6
+ module Task
7
+ class << self
8
+ def tests_count
9
+ source_code = Liri::Common::SourceCode.new('', Liri.compression_class, Liri.unit_test_class)
10
+ source_code.all_tests.size
11
+ end
12
+ end
13
+ end
14
+ end
15
+
data/liri.gemspec ADDED
@@ -0,0 +1,58 @@
1
+ require_relative 'lib/liri.rb'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = Liri::NAME
5
+ spec.version = Liri::VERSION
6
+ spec.authors = ["Rodrigo Fernández", "Leslie López"]
7
+ spec.email = ["rofaccess@gmail.com", "leslyee.05@gmail.com"]
8
+
9
+ spec.summary = "TFG Project"
10
+ spec.description = "Test distributor executor"
11
+ spec.homepage = "https://github.com/rofaccess/tfg"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/rofaccess/tfg/liri"
19
+ spec.metadata["changelog_uri"] = "https://github.com/rofaccess/tfg/blob/master/liri/README.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
24
+ # La siguiente línea debe comentarse al momento de ejecutar liri m dentro del proyecto Liri porque al tratar de ejecutar pruebas de este código fuente
25
+ # con agentes ejecutandose en distribuciones Linux y Ubuntu, por algún motivo ocurre el siguiente error:
26
+ #
27
+ # Esta línea es crítica, debe estar si o sí habilitada al compilar la gema
28
+ # Esta línea define que carpetas se excluyen del .gem generado al compilar la gema
29
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|documents|installers)/}) }
30
+ end
31
+ spec.bindir = "exe"
32
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
+ spec.require_paths = ["lib"]
34
+
35
+ # Rubyzip is a ruby library for reading and writing zip files. https://github.com/rubyzip/rubyzip
36
+ # Se usa para comprimir el código fuente en el Manager y descomprimir el código fuente recibido en el Agent
37
+ spec.add_runtime_dependency('rubyzip', '~> 2')
38
+
39
+ # The complete solution for Ruby command-line executables. https://github.com/commander-rb/commander
40
+ # Se usa para pasar comandos a la gema Liri
41
+ spec.add_runtime_dependency('commander', '~> 4')
42
+
43
+ # HighLine was designed to ease the tedious tasks of doing console input and output with low-level methods like gets and puts. https://github.com/JEG2/highline
44
+ # Se usa para pedir password en el Manager
45
+ spec.add_runtime_dependency('highline', '~> 2')
46
+
47
+ # Net::SCP is a pure-Ruby implementation of the SCP protocol. This operates over SSH (and requires the Net::SSH library), and allows files and directory trees to be copied to and from a remote server. https://github.com/net-ssh/net-scp
48
+ # Se usa para obtener el código fuente del Manager desde el Agent
49
+ spec.add_runtime_dependency('net-scp', '~> 3')
50
+
51
+ # A Ruby gem for converting seconds into human-readable format. https://github.com/digaev/to_duration
52
+ # Se usa para convertir un tiempo en segundos a un formato más legible
53
+ spec.add_runtime_dependency('to_duration', '~> 1')
54
+
55
+ # Ruby internationalization and localization (i18n) solution. https://github.com/ruby-i18n/i18n
56
+ # Este es un requerimiento de la gema to_duration
57
+ spec.add_runtime_dependency('i18n', '~> 1')
58
+ end
@@ -0,0 +1,28 @@
1
+ # Este archivo .yml define ciertas configuraciones para la ejecución del sistema
2
+ # Configuración del Nombre del archivo de código fuente comprimido enviado a los agentes
3
+ # Obs.: Puede que no sea útil para el usuario poder cambiar este nombre
4
+ compressed_file_name: compressed_source_code
5
+ # Configuración de la cantidad de tests a ejecutar por tandas
6
+ test_samples_by_runner: 100
7
+ log:
8
+ stdout:
9
+ # Configuración que define si se muestra el log de ejecución del programa en línea de comando. Puede ser true o false.
10
+ # Por defecto siempre se imprimirá el log en un archivo dentro de la carpeta logs
11
+ show: true
12
+ # Define los colores del texto del log. Puede ser none (no muestra colores), severity (colorea código de error),
13
+ # severity_date (colorea código de error y fecha), full (colorea el texto entero).
14
+ colorize: full
15
+ file:
16
+ colorize: full
17
+ library:
18
+ # Configuración de la librería de compresión a utilizar para comprimir el código fuente
19
+ # Valores soportados: Sólo Zip hasta ahora
20
+ compression: Zip
21
+ # Configuración de la librería de pruebas unitarias a ejecutar
22
+ # Valores soportados: Sólo Rspec hasta ahora
23
+ unit_test: Rspec
24
+ ports:
25
+ # Configuración del puerto a través del cual se realizará la primera comunicación entre Manager y Agent
26
+ udp: 2000
27
+ # Configuración del puerto a través del cual el Agent y el Manager intercambiarán las pruebas a ejecutar y los resultados de esa ejecución
28
+ tcp: 2500
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: liri
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rodrigo Fernández
8
+ - Leslie López
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2022-05-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rubyzip
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: commander
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '4'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '4'
42
+ - !ruby/object:Gem::Dependency
43
+ name: highline
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '2'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '2'
56
+ - !ruby/object:Gem::Dependency
57
+ name: net-scp
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '3'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3'
70
+ - !ruby/object:Gem::Dependency
71
+ name: to_duration
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '1'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '1'
84
+ - !ruby/object:Gem::Dependency
85
+ name: i18n
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '1'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1'
98
+ description: Test distributor executor
99
+ email:
100
+ - rofaccess@gmail.com
101
+ - leslyee.05@gmail.com
102
+ executables:
103
+ - liri
104
+ extensions: []
105
+ extra_rdoc_files: []
106
+ files:
107
+ - ".gitignore"
108
+ - ".rakeTasks"
109
+ - ".rspec"
110
+ - ".rubocop.yml"
111
+ - ".ruby-gemset"
112
+ - ".ruby-version"
113
+ - ".travis.yml"
114
+ - CODE_OF_CONDUCT.md
115
+ - Gemfile
116
+ - Gemfile.lock
117
+ - LICENSE.txt
118
+ - README.md
119
+ - Rakefile
120
+ - bin/bundle
121
+ - bin/coderay
122
+ - bin/commander
123
+ - bin/htmldiff
124
+ - bin/ldiff
125
+ - bin/liri
126
+ - bin/rake
127
+ - bin/rspec
128
+ - bin/rubocop
129
+ - bin/ruby-parse
130
+ - bin/ruby-rewrite
131
+ - bin/yard
132
+ - bin/yardoc
133
+ - bin/yri
134
+ - config/locales/to_duration_es.yml
135
+ - exe/liri
136
+ - lib/agent/agent.rb
137
+ - lib/agent/runner.rb
138
+ - lib/all_libraries.rb
139
+ - lib/common/benchmarking.rb
140
+ - lib/common/compressor/zip.rb
141
+ - lib/common/log.rb
142
+ - lib/common/source_code.rb
143
+ - lib/common/unit_test/rspec.rb
144
+ - lib/exe/agent.rb
145
+ - lib/exe/manager.rb
146
+ - lib/hash_extend.rb
147
+ - lib/liri.rb
148
+ - lib/manager/credential.rb
149
+ - lib/manager/manager.rb
150
+ - lib/manager/setup.rb
151
+ - lib/manager/test_result.rb
152
+ - lib/task.rb
153
+ - liri.gemspec
154
+ - template/liri-config.yml
155
+ homepage: https://github.com/rofaccess/tfg
156
+ licenses:
157
+ - MIT
158
+ metadata:
159
+ allowed_push_host: https://rubygems.org
160
+ homepage_uri: https://github.com/rofaccess/tfg
161
+ source_code_uri: https://github.com/rofaccess/tfg/liri
162
+ changelog_uri: https://github.com/rofaccess/tfg/blob/master/liri/README.md
163
+ post_install_message:
164
+ rdoc_options: []
165
+ require_paths:
166
+ - lib
167
+ required_ruby_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: 2.3.0
172
+ required_rubygems_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ requirements: []
178
+ rubygems_version: 3.1.4
179
+ signing_key:
180
+ specification_version: 4
181
+ summary: TFG Project
182
+ test_files: []