diff --git a/app.rb b/app.rb index 17f8f0a..e8a7876 100644 --- a/app.rb +++ b/app.rb @@ -733,6 +733,30 @@ post "/prjaddrepo/:id" do end get "/gitbld/:id/:git_id" do + prj = ProjectsActions.new(cfg.get_projects_path, db) + if prj.path.nil? + print_error_page(503, "Путь к проектам не существует") + else + repo = GitRepo.new(cfg.get_repo, db) + if repo.path.nil? + print_error_page(503, "Путь к репозиториям не существует") + else + prj_info = prj.get_project(params["id"]) + if prj_info.nil? + print_error_page(503, "Путь к проектам не существует") + else + git_info = repo.get_repo_short_info_by_id(params["git_id"].to_i) + @page_name = "#{prj_info[:projname]} - сборка #{git_info[:reponame]}" + @proj_name = prj_info[:projname] + @proj_descr = prj_info[:descr] + @git_name = git_info[:reponame] + + prj.build_projects_git(prj_info[:id], git_info[:id]) + + erb :prjbld + end + end + end end not_found do diff --git a/classes/gitinfo.rb b/classes/gitinfo.rb index e95f859..b2e3f39 100644 --- a/classes/gitinfo.rb +++ b/classes/gitinfo.rb @@ -82,7 +82,6 @@ class GitRepo repo = Rugged::Repository.new(git_path) db_info = @db.get_repo_info_by_name(reponame) unless db_info.nil? - db_info = db_info.first repos_data = { :reponame => db_info.reponame, :descr => db_info.descr, :public => db_info.public, :full => 1 } else result = create_git_db_only(reponame) diff --git a/classes/mock.rb b/classes/mock.rb index f38cfbf..3bd4d7c 100644 --- a/classes/mock.rb +++ b/classes/mock.rb @@ -1,11 +1,27 @@ +require_relative "spork" +require_relative "runner" + 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 unless File.exist? (path) Dir.mkdir(path) end @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 diff --git a/classes/projects.rb b/classes/projects.rb index bc3fba4..a267787 100644 --- a/classes/projects.rb +++ b/classes/projects.rb @@ -72,12 +72,14 @@ class ProjectsActions linked_prj = [] proj_repo_path = File.join(proj_path, PROJECTS_STRUCTURE[:REPO]) proj_repo = <<~PRJ_CFG + config_opts['dnf.conf'] += """ [#{proj_name}-repository] name=Project repository #{proj_name} baseurl=file://#{proj_repo_path} enabled=1 priority=80 skip_if_unavailable=True + """ PRJ_CFG linked_prj << proj_repo unless id.nil? @@ -238,4 +240,7 @@ class ProjectsActions @db.save_linked_projects(prj_id, item) end end + + def build_projects_git(prj_id, git_id) + end end diff --git a/classes/repomanage.rb b/classes/repomanage.rb index 4a44f68..adf4f5d 100644 --- a/classes/repomanage.rb +++ b/classes/repomanage.rb @@ -1,3 +1,5 @@ +require_relative "runner" + class RepoManager attr :path, :error, :last_status, :last_pid @@ -10,10 +12,14 @@ class RepoManager end def create_repo - %x(/usr/bin/createrepo_c --database --workers 1 "#{@path}") - result = $? - @last_status = result.exitstatus - @last_pid = result.pid - result + repo_path = File.join(@path, "repodata") + cmd_args = %Q(/usr/bin/createrepo_c --database --workers 1 "#{@path}") + if File.exist?(repo_path) + cmd_args = %Q(/usr/bin/createrepo_c --database --workers 1 --update "#{@path}") + end + cmd = Runner.new(cmd_args) + cmd.run + @last_status = cmd.exit_status + @last_pid = cmd.pid end end diff --git a/classes/runner.rb b/classes/runner.rb new file mode 100644 index 0000000..f2a84ac --- /dev/null +++ b/classes/runner.rb @@ -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] 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] 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] command to execute + # @return [Boolean] + def self.run?(*cmd) + Runner.new(*cmd).run? + end + + Error = Class.new(StandardError) + + # @param cmd [String,Array] 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 diff --git a/classes/spork.rb b/classes/spork.rb new file mode 100644 index 0000000..fc0d83a --- /dev/null +++ b/classes/spork.rb @@ -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 diff --git a/views/prjbld.erb b/views/prjbld.erb new file mode 100644 index 0000000..e59bf29 --- /dev/null +++ b/views/prjbld.erb @@ -0,0 +1,47 @@ +<%= erb :header %> +
+
+
+

+ <%= @proj_name %> +

+
<%= @proj_descr %>
+
git репозиторий <%= @git_name %>
+
+
+
+
Процесс сборки
+
+
+
+
+
+
+ +
+
+
+
+ +<%= erb :footer %> \ No newline at end of file