#!/usr/bin/env ruby # duplicate-or-unpublished-pkgs - detect packages in multiple repos which may be # duplicates, and discrepancies between the repos and your local abslibre tree # # Copyright (C) 2020 bill-auger # # SPDX-License-Identifier: AGPL-3.0-or-later # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # # # NOTE: you should configure 'ABS_PATH' and install 'pacman-all.conf' to /etc DEBUG = false VERBOSE = false ABS_PATH = '/packages/abslibre' CFG = '--config=/etc/pacman-all.conf' REPO_NAME = (ARGV[0] || '') PKG_NAME = (ARGV[1] || '') REPO_PATH = (Dir.exist? ABS_PATH) ? "#{ABS_PATH}/#{REPO_NAME}" : '' PKG_PATH = (Dir.exist? ABS_PATH) ? "#{ABS_PATH}/#{REPO_NAME}/#{PKG_NAME}" : '' REPO_DIR = (! REPO_NAME.empty? && (Dir.exist? REPO_PATH)) ? (Dir.new REPO_PATH) : nil PKG_DIR = (! PKG_NAME .empty? && (Dir.exist? PKG_PATH )) ? (Dir.new PKG_PATH ) : nil UNPUBLISHED_STATE = '(unpublished)' COMMITTED_STATE = '(committed )' PUBLISHED_STATE = '(published )->' UNCOMMITTED_STATE = '(uncommitted)' DUPLICATE_STATE = '(duplicate )->' TERMINAL_W = `tput cols 2> /dev/null || echo 80`.to_i PKG_SEPARATOR = ''.ljust TERMINAL_W , '-' STATE_COL_W = PUBLISHED_STATE.length PKG_COL_W = ((TERMINAL_W - STATE_COL_W) / 2) - 2 @results = {} @local_pkg = '' (puts "abs dir does not exist" ; exit 1) if REPO_PATH.empty? (puts "no repo dir specified" ; exit 1) if REPO_NAME.empty? (puts "no such repo dir: '#{REPO_NAME}'" ; exit 1) if REPO_DIR .nil? (puts "no such package dir: '#{PKG_NAME}'" ; exit 1) if ! PKG_NAME .empty? && PKG_DIR .nil? def DEBUG_RAW_DATA repo_pkgs_data ; DEBUG && (print "repo_pkgs_data[#{repo_pkgs_data.class}|#{repo_pkgs_data.size}|]\n" ; pp repo_pkgs_data) ; end ; def DEBUG_PKG_DATA all_repo_pkgs ; DEBUG && (print "all_repo_pkgs[#{all_repo_pkgs.class}|#{all_repo_pkgs.size}|]\n" ; pp all_repo_pkgs) ; end ; def DEBUG_STATE_DATA this_repo_pkg , dup_repo_pkgs ; DEBUG && (print "this_repo_pkg=#{this_repo_pkg}\n" ; print "dup_repo_pkgs=#{dup_repo_pkgs}\n") ; end ; def collect_duplicate_pkgs pkg_name local_pkg = "#{REPO_NAME}/#{pkg_name}" repo_pkgs_data = pacman_pkg_data pkg_name all_repo_pkgs = [] DEBUG_RAW_DATA repo_pkgs_data until repo_pkgs_data.empty? do all_repo_pkgs << (repo_pkgs_data.shift.split ':')[1].strip + '/' + (repo_pkgs_data.shift.split ':')[1].strip + '-' + (repo_pkgs_data.shift.split ':')[1].strip end if ! all_repo_pkgs.empty? DEBUG_PKG_DATA all_repo_pkgs dup_repo_pkgs = all_repo_pkgs.reject { | repo_pkg | repo_pkg.start_with? "#{REPO_NAME}/" } this_repo_pkg = (all_repo_pkgs - dup_repo_pkgs).first DEBUG_STATE_DATA this_repo_pkg , dup_repo_pkgs if this_repo_pkg.nil? add_result UNPUBLISHED_STATE , local_pkg add_result COMMITTED_STATE , local_pkg if under_vcs? local_pkg else add_result PUBLISHED_STATE , local_pkg , this_repo_pkg add_result UNCOMMITTED_STATE , local_pkg unless under_vcs? local_pkg end dup_repo_pkgs.each { | dup_pkg | add_result DUPLICATE_STATE , local_pkg , dup_pkg } else add_result UNPUBLISHED_STATE , local_pkg add_result COMMITTED_STATE , local_pkg if under_vcs? local_pkg end end def pacman_pkg_data pkg_name `LANG=C pacman #{CFG} -Si #{pkg_name} 2> /dev/null | egrep 'Repository|Name|Version'`.lines :chomp => true end def under_vcs? local_pkg system "git -C #{ABS_PATH} ls-files --error-unmatch #{local_pkg}/PKGBUILD &> /dev/null" end def add_result state , local_pkg , repo_pkg='' ((@results[local_pkg] ||= {})[state] ||= []) << repo_pkg end def fit_to_width text , width ; (text.ljust width).slice 0 , width ; end ; ## main entry ## print "update package database \n" `sudo pacman #{CFG} -Sy 2> /dev/null` print "gathering package data for: #{"#{REPO_NAME}/#{PKG_NAME}".gsub /\/$/ , ''}\n" if PKG_NAME.empty? REPO_DIR.each_child { | pkg_name | collect_duplicate_pkgs pkg_name } else collect_duplicate_pkgs PKG_NAME end n_inconsistencies = 0 @results.keys.sort { | a , b | a <=> b }.each do | local_pkg | pkg_data = @results[local_pkg] next unless VERBOSE || pkg_data.size != 1 || pkg_data[PUBLISHED_STATE].nil? puts PKG_SEPARATOR ; n_inconsistencies = n_inconsistencies + 1 ; [ UNPUBLISHED_STATE , COMMITTED_STATE , PUBLISHED_STATE , UNCOMMITTED_STATE , DUPLICATE_STATE ].each do | state | next unless repo_pkgs = pkg_data[state] repo_pkgs.each do | repo_pkg | puts [ (fit_to_width local_pkg , PKG_COL_W ) , (fit_to_width state , STATE_COL_W) , (fit_to_width repo_pkg , PKG_COL_W ) ].join ' ' local_pkg = '' end end end puts "#{PKG_SEPARATOR}\n#{n_inconsistencies} inconsistencies detected"