parent
91fa281237
commit
48b648f46b
@ -1,11 +1,27 @@
|
|||||||
|
require_relative "spork"
|
||||||
|
require_relative "runner"
|
||||||
|
|
||||||
class MockManager
|
class MockManager
|
||||||
attr :path, :error, :last_status, :last_pid
|
attr :path, :config, :error, :last_status, :last_pid, :prep_dir
|
||||||
|
|
||||||
def initialize(path)
|
def initialize(path, config, cfg_counter_path)
|
||||||
@error = nil
|
@error = nil
|
||||||
unless File.exist? (path)
|
unless File.exist? (path)
|
||||||
Dir.mkdir(path)
|
Dir.mkdir(path)
|
||||||
end
|
end
|
||||||
@path = path
|
@path = path
|
||||||
|
@config = config
|
||||||
|
cntr = 0
|
||||||
|
|
||||||
|
File.open(cfg_counter_path, "r+") do |f|
|
||||||
|
f.flock(File::LOCK_EX)
|
||||||
|
counter = f.gets.strip
|
||||||
|
i_counter = counter.to_i
|
||||||
|
i_counter = i_counter + 1
|
||||||
|
f.puts("#{i_counter}")
|
||||||
|
cnt = i_counter
|
||||||
|
end
|
||||||
|
tmp_name = (0...10).map { ("a".."z").to_a[rand(26)] }.join
|
||||||
|
@prep_dir = File.join(path, "#{cntr}_#{tmp_name}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
require "open3"
|
||||||
|
|
||||||
|
class Runner
|
||||||
|
attr_reader :cmd, :exit_status, :stdout, :stderr, :pid
|
||||||
|
|
||||||
|
# Run a command, return runner instance
|
||||||
|
# @param cmd [String,Array<String>] command to execute
|
||||||
|
def self.run(*cmd)
|
||||||
|
Runner.new(*cmd).run
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run a command, raise Runner::Error if it fails
|
||||||
|
# @param cmd [String,Array<String>] command to execute
|
||||||
|
# @raise [Runner::Error]
|
||||||
|
def self.run!(*cmd)
|
||||||
|
Runner.new(*cmd).run!
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run a command, return true if it succeeds, false if not
|
||||||
|
# @param cmd [String,Array<String>] command to execute
|
||||||
|
# @return [Boolean]
|
||||||
|
def self.run?(*cmd)
|
||||||
|
Runner.new(*cmd).run?
|
||||||
|
end
|
||||||
|
|
||||||
|
Error = Class.new(StandardError)
|
||||||
|
|
||||||
|
# @param cmd [String,Array<String>] command to execute
|
||||||
|
def initialize(cmd)
|
||||||
|
@cmd = cmd.is_a?(Array) ? cmd.join(" ") : cmd
|
||||||
|
@stdout = +""
|
||||||
|
@stderr = +""
|
||||||
|
@exit_status = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Boolean] success or failure?
|
||||||
|
def success?
|
||||||
|
exit_status.zero?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run the command, return self
|
||||||
|
# @return [Runner]
|
||||||
|
def run
|
||||||
|
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
|
||||||
|
until [stdout, stderr].all?(&:eof?)
|
||||||
|
readable = IO.select([stdout, stderr])
|
||||||
|
next unless readable&.first
|
||||||
|
|
||||||
|
readable.first.each do |stream|
|
||||||
|
data = +""
|
||||||
|
# rubocop:disable Lint/HandleExceptions
|
||||||
|
begin
|
||||||
|
stream.read_nonblock(1024, data)
|
||||||
|
rescue EOFError
|
||||||
|
# ignore, it's expected for read_nonblock to raise EOFError
|
||||||
|
# when all is read
|
||||||
|
end
|
||||||
|
|
||||||
|
if stream == stdout
|
||||||
|
@stdout << data
|
||||||
|
$stdout.write(data)
|
||||||
|
else
|
||||||
|
@stderr << data
|
||||||
|
$stderr.write(data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@exit_status = wait_thr.value.exitstatus
|
||||||
|
@pid = wait_thr.pid
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run the command and return stdout, raise if fails
|
||||||
|
# @return stdout [String]
|
||||||
|
# @raise [Runner::Error]
|
||||||
|
def run!
|
||||||
|
return run.stdout if run.success?
|
||||||
|
|
||||||
|
raise(Error, "command failed, exit: %d - stdout: %s / stderr: %s" % [exit_status, stdout, stderr])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run the command and return true if success, false if failure
|
||||||
|
# @return success [Boolean]
|
||||||
|
def run?
|
||||||
|
run.success?
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,80 @@
|
|||||||
|
# A way to cleanly handle process forking in Sinatra when using Passenger, aka "sporking some code".
|
||||||
|
# This will allow you to properly execute some code asynchronously, which otherwise does not work correctly.
|
||||||
|
#
|
||||||
|
# Written by Ron Evans
|
||||||
|
# More info at http://deadprogrammersociety.com
|
||||||
|
#
|
||||||
|
# Mostly lifted from the Spawn plugin for Rails (http://github.com/tra/spawn)
|
||||||
|
# but with all of the Rails stuff removed.... cause you are using Sinatra. If you are using Rails, Spawn is
|
||||||
|
# what you need. If you are using something else besides Sinatra that is Rack-based under Passenger, and you are having trouble with
|
||||||
|
# asynch processing, let me know if spork helped you.
|
||||||
|
#
|
||||||
|
module Spork
|
||||||
|
# things to close in child process
|
||||||
|
@@resources = []
|
||||||
|
def self.resources
|
||||||
|
@@resources
|
||||||
|
end
|
||||||
|
|
||||||
|
# set the resource to disconnect from in the child process (when forking)
|
||||||
|
def self.resource_to_close(resource)
|
||||||
|
@@resources << resource
|
||||||
|
end
|
||||||
|
|
||||||
|
# close all the resources added by calls to resource_to_close
|
||||||
|
def self.close_resources
|
||||||
|
@@resources.each do |resource|
|
||||||
|
resource.close if resource && resource.respond_to?(:close) && !resource.closed?
|
||||||
|
end
|
||||||
|
@@resources = []
|
||||||
|
end
|
||||||
|
|
||||||
|
# actually perform the fork... er, spork
|
||||||
|
# valid options are:
|
||||||
|
# :priority => to set the process priority of the child
|
||||||
|
# :logger => a logger object to use from the child
|
||||||
|
# :no_detach => true if you want to keep the child process under the parent control. usually you do NOT want this
|
||||||
|
def self.spork(options = {})
|
||||||
|
logger = options[:logger]
|
||||||
|
logger.debug "spork> parent PID = #{Process.pid}" if logger
|
||||||
|
child = fork do
|
||||||
|
begin
|
||||||
|
start = Time.now
|
||||||
|
logger.debug "spork> child PID = #{Process.pid}" if logger
|
||||||
|
|
||||||
|
# set the nice priority if needed
|
||||||
|
Process.setpriority(Process::PRIO_PROCESS, 0, options[:priority]) if options[:priority]
|
||||||
|
|
||||||
|
# disconnect from the rack
|
||||||
|
Spork.close_resources
|
||||||
|
|
||||||
|
# run the block of code that takes so long
|
||||||
|
yield
|
||||||
|
rescue => ex
|
||||||
|
logger.error "spork> Exception in child[#{Process.pid}] - #{ex.class}: #{ex.message}" if logger
|
||||||
|
ensure
|
||||||
|
logger.info "spork> child[#{Process.pid}] took #{Time.now - start} sec" if logger
|
||||||
|
# this form of exit doesn't call at_exit handlers
|
||||||
|
exit!(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# detach from child process (parent may still wait for detached process if they wish)
|
||||||
|
Process.detach(child) unless options[:no_detach]
|
||||||
|
|
||||||
|
return child
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Patch to work with passenger
|
||||||
|
if defined? Passenger::Rack::RequestHandler
|
||||||
|
class Passenger::Rack::RequestHandler
|
||||||
|
alias_method :orig_process_request, :process_request
|
||||||
|
|
||||||
|
def process_request(env, input, output)
|
||||||
|
Spork.resource_to_close(input)
|
||||||
|
Spork.resource_to_close(output)
|
||||||
|
orig_process_request(env, input, output)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,47 @@
|
|||||||
|
<%= erb :header %>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">
|
||||||
|
<h3 class="bg-secondary-subtle text-center border-bottom border-primary-subtle rounded-1 pb-1 mb-2">
|
||||||
|
<a href="/prjedit/<%= ERB::Util.url_encode(@proj_id) %>"><%= @proj_name %></a>
|
||||||
|
</h3>
|
||||||
|
<div class="pb-2"><%= @proj_descr %></div>
|
||||||
|
<div class="pb-2">git репозиторий <%= @git_name %></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="hstack gap-3">
|
||||||
|
<div class="p-2">Процесс сборки</div>
|
||||||
|
<div><button type="button" class="btn btn-outline-primary" id="startBtn"><i
|
||||||
|
class="bi bi-play-fill"></i></button>
|
||||||
|
</div>
|
||||||
|
<div><button type="button" class="btn btn-outline-primary" id="stopBtn"><i
|
||||||
|
class="bi bi-pause-fill"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ratio ratio-4x3">
|
||||||
|
<iframe src="/" title="Процесс сборки" id="bldframe" allowfullscreen></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var intervalId = null;
|
||||||
|
$(function () {
|
||||||
|
intervalId = setInterval(refreshiframe, 5000);
|
||||||
|
});
|
||||||
|
function refreshiframe() {
|
||||||
|
$('#bldframe').attr('src', $('#bldframe').attr('src'));
|
||||||
|
}
|
||||||
|
$("#startBtn").click(function () {
|
||||||
|
if (intervalId == null) {
|
||||||
|
intervalId = setInterval(refreshiframe, 5000);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$("#stopBtn").click(function () {
|
||||||
|
if (intervalId != null) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
intervalId = null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<%= erb :footer %>
|
Loading…
Reference in new issue