summaryrefslogtreecommitdiff
path: root/duplicate-or-unpublished-pkgs
blob: e074f5457a0ec54eff1718d42a468520cd858c12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/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 <bill-auger@programmer.net>
#
# 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 <https://www.gnu.org/licenses/>.
#
#
# 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"