require "fileutils" require_relative "db" require_relative "repomanage" require_relative "mock" require_relative "utilities" require "digest" PROJECTS_STRUCTURE = { :REPO => "repo", :LOGS => "logs", :CONFIGS => "configs", :SRCPRP => "srcprp", :SIGNED => "signed", :SRC => "src", } class ProjectsActions attr :path, :error, :db def initialize(path, db) @path = nil @error = nil @db = db if File.absolute_path?(path) if File.exist?(path) @path = path end else apath = File.realpath(path) if File.exist?(apath) @path = apath end end end def get_projects @db.proj_list end def get_project(id) @db.proj(id.to_i) end def get_project_path(id) @error = nil fname = nil prj = @db.proj(id) if prj.nil? @error = "Проекта с id = #{id} не существует" else fname = File.expand_path("#{prj[:projname]}.prj", @path) end fname end def get_project_config(id) @error = nil fname = nil prj = @db.proj(id) if prj.nil? @error = "Проекта с id = #{id} не существует" else fname = File.expand_path("#{prj[:projname]}.prj", @path) fname = File.join(fname, PROJECTS_STRUCTURE[:CONFIGS], "#{prj[:projname]}.cfg") end fname end def get_project_repo(id) proj_path = get_project_path(id) File.join(proj_path, PROJECTS_STRUCTURE[:REPO]) end def get_project_path_git(id, gitname) proj_path = get_project_path(id) File.join(proj_path, PROJECTS_STRUCTURE[:SRC], gitname) end def generate_linked_repos(id, proj_path, proj_name, linked_repo_tpl) 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? link_prj_list = @db.get_projects_links(id) unless link_prj_list.nil? link_prj_list.each do |item| internal_repo = ProjectsActions.new(@path, @db) internal_path = internal_repo.get_project_path(item[:proj_id_repository]) internal_repo_path = File.join(internal_path, PROJECTS_STRUCTURE[:REPO]) internal_proj_info = internal_repo.get_project(item[:proj_id_repository]) proj_repo = <<~PRJ_CFG [#{internal_proj_info[:projname]}-repository] name=Project repository #{internal_proj_info[:projname]} baseurl=file://#{internal_repo_path} enabled=1 skip_if_unavailable=True PRJ_CFG linked_prj << proj_repo end end end File.open(linked_repo_tpl, "w") { |f| f << linked_prj.join("\n\n\n") } end def regenerate_linked_repos(id) proj_path = get_project_path(id) config_path = File.join(proj_path, PROJECTS_STRUCTURE[:CONFIGS]) prj_incl_path = File.join(config_path, "repos_include.tpl") prj_info = get_project(id) unless prj_info.nil? generate_linked_repos(id, proj_path, prj_info[:projname], prj_incl_path) end end def generate_config(id, configuration_path, proj_path, proj_name) proj_conf_path = File.join(proj_path, PROJECTS_STRUCTURE[:CONFIGS], "#{proj_name}.cfg") conf_path = File.join(proj_path, PROJECTS_STRUCTURE[:CONFIGS]) prj_incl_path = File.join(conf_path, "repos_include.tpl") proj_config = <<~PRJ_CFG include("#{configuration_path}") include("#{prj_incl_path}") config_opts['plugin_conf']['ccache_enable'] = False config_opts['plugin_conf']['ccache_opts']['max_cache_size'] = '4G' config_opts['plugin_conf']['ccache_opts']['hashdir'] = True config_opts['plugin_conf']['ccache_opts']['debug'] = False config_opts['plugin_conf']['ccache_opts']['show_stats'] = True config_opts['plugin_conf']['package_state_enable'] = True config_opts['plugin_conf']['procenv_enable'] = False config_opts['plugin_conf']['root_cache_enable'] = True config_opts['plugin_conf']['showrc_enable'] = True config_opts['plugin_conf']['yum_cache_enable'] = True config_opts['chroot_setup_cmd'] += " gcc gcc-c++ make" PRJ_CFG File.open(proj_conf_path, "w") { |f| f << proj_config } generate_linked_repos(id, proj_path, proj_name, prj_incl_path) end def create_project(name, description, configuration, nopublic) @error = nil ret_val = 0 project_name = sanitize_rcptname(name) fname = File.expand_path("#{project_name}.prj", @path) if File.exist?(fname) @error = "Проект с таким именем уже существует: #{project_name}" ret_val = 1 else created = false #begin Dir.mkdir(fname) PROJECTS_STRUCTURE.each_pair do |key, value| new_path = File.join(fname, value) Dir.mkdir(new_path) end if File.exist?(configuration) generate_config(nil, configuration, fname, project_name) @error = @db.proj_create(project_name, description, nopublic) if @error.nil? created = true end repo_path = File.join(fname, PROJECTS_STRUCTURE[:REPO]) repoman = RepoManager.new(repo_path) repoman.create_repo else ret_val = 1 @error = "Конфигурация #{configuration} не существует" end #rescue => e # ret_val = 1 # @error = e.message #end unless created FileUtils.rm_rf(fname, secure: true) end end ret_val end def get_project_gits(id, repo) res = @db.get_gits_for_projects(id) res_sync = res.map do |item| prj_p = get_project_path(id) path = File.join(prj_p, PROJECTS_STRUCTURE[:SRC], item[:reponame]) item[:is_repo_synced] = repo.is_repos_sync(item[:reponame], path) item end res end def add_git_to_project(prj_id, git_id, repo, git_name) path = get_project_path(prj_id) if @error.nil? path = File.join(path, PROJECTS_STRUCTURE[:SRC], git_name) err = repo.clone_repo_master(git_id, path) if err.nil? @db.save_git_project(prj_id, git_id) end else err = @error end err end def renew_git_to_project(prj_id, git_id, repo, git_name) path = get_project_path(prj_id) if @error.nil? path = File.join(path, PROJECTS_STRUCTURE[:SRC], git_name) err = repo.clone_repo_master(git_id, path) else err = @error end err end def get_related_projects_list(prj_id) links_list = [] links = @db.get_projects_links(prj_id) unless links.nil? links_list = links.map do |item| prj_info = @db.proj(item[:proj_id_repository]) if prj_info.nil? item[:list_state] = false else item[:list_state] = true item[:prj_info] = prj_info end item end.select { |item| item[:list_state] } end links_list end def delete_linked_projects(prj_id) @db.delete_linked_projects(prj_id) end def save_linked_projects(prj_id, new_ids, delete_ids) delete_ids.each { |item| @db.delete_linked_projects_with_id(prj_id, item) } new_ids.each do |item| @db.save_linked_projects(prj_id, item) end end def find_spec_file(prj_id, git_id) spec_file = "" proj_path = get_project_path(prj_id) git_name = @db.get_repo_info_by_id(git_id) git_source = File.join(proj_path, PROJECTS_STRUCTURE[:SRC], git_name[:reponame]) spec = @db.get_project_repo_spec(prj_id, git_id) if spec.nil? spec_files = get_spec_files_in_dir(git_source) unless spec_files.nil? spec_file = spec_files.first end else spec_file = spec[:spec_name] end spec_file end def build_projects_git(prj_id, git_id, counter_file) bld_id = 0 build_ok = true proj_path = get_project_path(prj_id) git_name = @db.get_repo_info_by_id(git_id) prep_script = @db.get_git_recips(git_id) prepare_path = File.join(proj_path, PROJECTS_STRUCTURE[:SRCPRP], git_name[:reponame]) spec_file = find_spec_file(prj_id, git_id) repo_lock = File.join(proj_path, PROJECTS_STRUCTURE[:CONFIGS], ".repolock") if File.exist?(prepare_path) lockf_path = File.join(prepare_path, "lock") File.open(lockf_path, File::RDWR | File::CREAT) do |f| result = f.flock(File::LOCK_EX | File::LOCK_NB) if result == false #Файл заблокирован считать id и вывести сведения о сборке build_ok = false build_id = f.gets unless build_id.nil? build_id = build_id.strip.to_i end if build_id > 0 build_info = @db.get_build_task_process_log(build_id) unless build_info.nil? bld_id = build_info[:id] end end else #Сборка завершилась, но каталог не подчистился FileUtils.rm_rf(prepare_path) f.flock(File::LOCK_UN) build_ok = true end end end #Верная ситуация if build_ok Dir.mkdir(prepare_path) lockf_path = File.join(prepare_path, "lock") File.open(lockf_path, File::RDWR | File::CREAT) do |f| f.flock(File::LOCK_EX) f.rewind #Начинаем сборку build_path = File.join(proj_path, PROJECTS_STRUCTURE[:LOGS], git_name[:reponame]) repo_path = File.join(proj_path, PROJECTS_STRUCTURE[:REPO]) git_source = File.join(proj_path, PROJECTS_STRUCTURE[:SRC], git_name[:reponame]) @db.create_build_task(prj_id, git_id, build_path) build_id = @db.last_id f.puts(build_id) f.flush mock = MockManager.new(prepare_path, get_project_config(prj_id), counter_file, @db, build_path, repo_path, git_source, build_id, prep_script, spec_file, repo_lock, git_id) bld_id = build_id @db.update_build_task_error_log(build_id, mock.get_build_process_log) mock.build_task end end bld_id end def delete_git_from_project(prj_id, git_id) @error = nil builds_lst = db.get_builds_for_project_git(prj_id, git_id) active_build = false builds_lst.each do |item| if item[:state] == 0 active_build = true break end end if active_build @error = "Нельзя удалить git репозиторий с незавершенными сборками" else proj_path = get_project_path(prj_id) git_name = @db.get_repo_info_by_id(git_id) git_source = File.join(proj_path, PROJECTS_STRUCTURE[:SRC], git_name[:reponame]) FileUtils.rm_rf(git_source, secure: true) @db.delete_git_from_project(prj_id, git_id) end @error end def delete_project(prj_id) @error = nil builds_lst = db.get_builds_for_project(prj_id) active_build = false builds_lst.each do |item| if item[:state] == 0 active_build = true break end end if active_build @error = "Нельзя удалить git репозиторий с незавершенными сборками" else linked = @db.projects_with_current_as_link(prj_id) if linked.nil? || linked.length == 0 proj_path = get_project_path(prj_id) FileUtils.rm_rf(proj_path, secure: true) @db.delete_project(prj_id) else @error = "На текущий проект ссылаются другие проекты. Удаление запрещено" end end @error end def sign_project(prj_id, key_path, password, url, tpl_dir) @error = nil proj_path = get_project_path(prj_id) sign_repo_path = File.join(proj_path, PROJECTS_STRUCTURE[:SIGNED]) repo_path = File.join(proj_path, PROJECTS_STRUCTURE[:REPO]) repo_sign = RepoManager.new(sign_repo_path) repo_key = RepoManagerKeys.new(key_path) if password.nil? password = repo_key.check_password_exists end if password.nil? @error = "Не указан пароль для подписи" else repo_lock = File.join(proj_path, PROJECTS_STRUCTURE[:CONFIGS], ".repolock") sign_lock = File.join(proj_path, PROJECTS_STRUCTURE[:CONFIGS], ".signlock") prj = @db.proj(prj_id) if repo_key.check_key_exists File.open(sign_lock, File::RDWR | File::CREAT) do |s| s.flock(File::LOCK_EX) File.open(repo_lock, File::RDWR | File::CREAT) do |f| f.flock(File::LOCK_EX) rpm_list = get_rpms_list(repo_path) if prj[:public] == 0 rpm_list = rpm_list.reject do |item| block = false block = true if item =~ /\.src\.rpm$/ || item =~ /SRPMS/ || item =~ /Debug/ || item =~ /(debuginfo.+rpm$)|(debugsource.+rpm$)/ block end end rpm_signed_list = get_rpms_list(sign_repo_path) rpm_list = rpm_list.select do |item| sign_repo_path_rpm = File.join(sign_repo_path, item) unless File.exist?(sign_repo_path_rpm) file_path_full = File.join(repo_path, item) unless File.exist?(File.dirname(sign_repo_path_rpm)) FileUtils.mkdir_p(File.dirname(sign_repo_path_rpm)) end FileUtils.cp_r(file_path_full, File.dirname(sign_repo_path_rpm), verbose: false, remove_destination: false) sha256 = Digest::SHA256.file(file_path_full) rpm_info = @db.get_rpm_info_by_hash(sha256.hexdigest) unless rpm_info.nil? @db.update_rpm_sign(rpm_info[:id], sign_repo_path_rpm) end repo_key.sign_package(sign_repo_path_rpm, password) end end repo_url = "http://localhost/" if prj[:remote_address].nil? || prj[:remote_address].strip == "" repo_url = url else repo_url = prj[:remote_address] end if repo_url[-1] != "/" repo_url = repo_url + "/" end repo_sign.repoview(repo_url, prj[:projname], tpl_dir) repo_sign.create_repo pub_key = repo_key.get_publick_key proj_repo_key = File.join(sign_repo_path, "#{prj[:projname]}-gpg-key") unless File.exist?(proj_repo_key) FileUtils.cp_r(pub_key, proj_repo_key, verbose: false, remove_destination: false) end end end else @error = "Ключ для подписи отсутствует" end end @error end def set_address(prj_id, address) @error = nil if address.nil? address = "" else address = address.strip end @db.set_project_address(prj_id, address) @error end def get_sign_path(id) path = get_project_path(id) File.join(path, PROJECTS_STRUCTURE[:SIGNED]) end end