view sbin/check-ports @ 140:17b2f4fa9c1b

- Refactor: check-ports now deals with missing local index files - Handle a "SharedLocalRepo" repository
author Franz Glasner <fzglas.hg@dom66.de>
date Wed, 30 Oct 2019 09:40:42 +0100
parents 302225cfb01b
children 6be3742d21f7
line wrap: on
line source

#!/bin/sh
# -*- indent-tabs-mode: nil; -*-
: 'Check the version status of installed ports and compare them to
version in remote repositories and the local ports index.

:Author:    Franz Glasner
:Copyright: (c) 2017-2019 Franz Glasner.
            All rights reserved.
:License:   BSD 3-Clause "New" or "Revised" License.
            See LICENSE for details.
            If you cannot find LICENSE see
            <https://opensource.org/licenses/BSD-3-Clause>
:ID:        @(#)@@PKGORIGIN@@ $HGid$

'

VERSION='@@VERSION@@'

#
# Configuration directory
#
: ${CONFIGDIR:=@@ETCDIR@@}

test -r "${CONFIGDIR}/pkgtools.conf" && . "${CONFIGDIR}/pkgtools.conf"

#
# Mapping configuration: installed package name -> original package name
# Note: This is independent of any repo
#
: ${PACKAGE_MAPPING:=${CONFIGDIR}/package-mapping.conf}

#
# Local repository with non-public packages and/or ports with changed
# OPTIONS (i.e. not using the defaults) or forks of official packages with
# other package names.
# This repo is strictly *local* to the host and/or jail.
#
: ${LOCAL_REPO:=LocalRepo}

#
# Shared local repository with non-public packages and/or ports with
# changed OPTIONS (i.e. not using the defaults).
# Contrary to LOCAL_REPO this repository may be shared.
#
: ${SHARED_LOCAL_REPO:=SharedLocalRepo}

#
# (Shared) repository with ports with default OPTIONS (i.e. unchanged)
# but newer than the packages in the "FreeBSD" repository.
# Can also contain non-FreeBSD *public* packages.
# May be shared.
# Some sort of a fast-track repository.
#
: ${LOCALBSDPORTS_REPO:=LocalBSDPorts}

#
# The official FreeBSD binary repository
#
: ${FREEBSD_REPO:=FreeBSD}

#
# Directly installed from ports
#
: ${PORTS_DIRECT_INSTALLED_REPO:=unknown-repository}

#
# For the workaround of the bug in pkg rquery -I
#
: ${PORTSDIR:=/usr/ports}
: ${INDEXDIR:=${PORTSDIR}}
: ${INDEXFILE:=@@INDEXFILE@@}


test_exists_local_index() {
    : 'Determine whether there exists a ports directory with an index
    file.

    Returns:
        status 0 iff the local index exists
    '
    pkg version -I -n DUMMY >/dev/null 2>/dev/null
}


get_immediate_index_version() {
    : 'Determine for package `_package` the version of the package in the
    local ports index.

    Args:
        _package: the package name to search for

    Input (Globals):
        INDEXDIR:  the directory where to search the index file
        INDEXFILE: the name of the index file

    Returns:
        0 on success, 1 on errors or if the package is not in the local
        ports index

    Output (stdout):
        the version number of `_package` in the local ports index

    '
    local _package _line _fqpn _n _lines

    _package="$1"

#    _val=$(pkg rquery -I "${_package}" | cut -f 1 -d '|')
#    _rv=$?
#    immediate_index_version=${_val##*-}
    #    return ${_rv}

    if [ -r "${INDEXDIR}/${INDEXFILE}" ] ; then
        #
        # Note: Direct piping does not set immediate_index_version at return correctly
        #       "_line" is set correctly and parsing works, but the return 0 seems to kill
        #       some of the previous effects.
        #
        # "grep" does a fast pre-selection, reading, parsing and comparing is done for
        # exact matching.
        #
        _lines=$(egrep '^'"${_package}" "${INDEXDIR}/${INDEXFILE}")
        while read _line ; do
            _fqpn="${_line%%|*}"
            _n=${_fqpn%-*}
	    if [ "${_package}" = "${_n}" ] ; then
                printf '%s' "${_fqpn##*-}"
                return 0
            fi
        done <<EOF1334TGH1
${_lines}
EOF1334TGH1
    fi
    return 1
}


get_mapping() {
    : 'Determine whether a package `_package` is essentially the same as
    another package.

    Args:
        _package: the new name of the package

    Returns:
        0 when a package mapping has been found, 1 otherwise

    Output (stdout):
        the name of the package on which `_package` is based on

    This command reads from the mapping database in in file
    `/usr/local/etc/local-bsdtools/package-mapping.conf`.
    Example::

        #
        # _package                         mapped_package_name
        #
        fmg-nextcloud-php71                nextcloud-php71
        fmg-nextcloud-twofactor_totp-php71 nextcloud-twofactor_totp-php71

    '
    local _package _n _mapped

    _package="$1"

    if [ -r "${PACKAGE_MAPPING}" ] ; then
        while read _n _mapped ; do
            if [ "${_n}" = "${_package}" ] ; then
                printf '%s' "${_mapped}"
                return 0
            fi
        done < ${PACKAGE_MAPPING}
    fi
    return 1
}


print_title() {
    : 'Print the output title line for a package

    Args:
        _package: the package name
        _version: the package version
        _repo:    the repository name

    Input (Globals).
        title_printed: a global that determines if the title really needs
                       to be printed.

                       If it is an empty string the the title is
                       really printed and the variable is set to
                       "yes".

    Output (Globals):
        title_printed: set to "yes" if the title has been printed

    '
    local _package _version _repo

    _package="$1"
    _version="$2"
    _repo="$3"
    if [ -z "${title_printed}" ]; then
        printf '%-36s %-17s (%s)\n' "${_package}" "${_version}" "${_repo}"
        title_printed=yes
    fi
}


print_detail_item() {
    : 'Print a detail item to stdout.

    The description `_descr` will not be printed if the label `_label`
    is ``?``.

    Args:
        _repo: the repository name
        _version: the version number to print to
        _label: the label (aka comparison character) to print to
        _descr: the description to print to
        _indent: (optional) extra indentation number (default 0)

    Output (stdout):
        the formatted detail line

    '
    local _repo _version _label _descr _indent
    local _real_descr

    _repo="$1"
    _version="$2"
    _label="$3"
    _descr="$4"
    _indent="$5"

    if [ -z "${_indent}" ]; then
        _indent="0"
    fi
    if [ "${_label}" = '?' ]; then
        _real_descr=''
    else
        _real_descr="${_descr}"
    fi

    printf '%-*s  %-15s: %-17s %s %s\n' $((_indent)) '' "${_repo}" "${_version}" "${_label}" "${_real_descr}"
}


check_ports() {
    : 'Implementation of main

    '
    local _ipackage _iversion _irepo _mapped_package_name _dummy
    local _print_detail _local_index_exists
    local _index_version _index_label _index_descr
    local _remote_version_FreeBSD _remote_label_FreeBSD _remote_descr_FreeBSD
    local _remote_version_LocalBSDPorts _remote_label_LocalBSDPorts _remote_descr_LocalBSDPorts
    local _remote_version_SharedLocalRepo _remote_label_SharedLocalRepo _remote_descr_SharedLocalRepo
    local _remote_version_LocalRepo _remote_label_LocalRepo _remote_descr_LocalRepo

    if test_exists_local_index; then
        _local_index_exists="1"
    fi
    pkg query '%n %v %R' |
        while read -r _ipackage _iversion _irepo; do
            title_printed=""
            _print_detail=""
            if [ -n "${_local_index_exists}" ]; then
                read -r _dummy _index_label _index_descr <<EOF_INDEX
$(pkg version -U -I -n "${_ipackage}" -v)
EOF_INDEX
            fi
            read -r _dummy  _remote_label_FreeBSD _remote_descr_FreeBSD <<EOF_FreeBSD
$(pkg version -U -R -r "${FREEBSD_REPO}" -n "${_ipackage}" -v)
EOF_FreeBSD
            read -r _dummy _remote_label_LocalBSDPorts _remote_descr_LocalBSDPorts <<EOF_LocalBSDPorts
$(pkg version -U -R -r "${LOCALBSDPORTS_REPO}" -n "${_ipackage}" -v)
EOF_LocalBSDPorts
            read -r _remote_fpname_SharedLocalRepo _remote_label_SharedLocalRepo _remote_descr_SharedLocalRepo <<EOF_SharedLocalRepo
$(pkg version -U -R -r "${SHARED_LOCAL_REPO}" -n "${_ipackage}" -v)
EOF_SharedLocalRepo
            read -r _remote_fpname_LocalRepo _remote_label_LocalRepo _remote_descr_LocalRepo <<EOF_LocalRepo
$(pkg version -U -R -r "${LOCAL_REPO}" -n "${_ipackage}" -v)
EOF_LocalRepo

            if [ -n "${option_verbose}" ]; then
                print_title "${_ipackage}" "${_iversion}" "${_irepo}"
            fi
            if get_mapping "${_ipackage}" >/dev/null;  then
                _print_detail="1"
            fi
            if [ -n "${option_alldata}" ]; then
                _print_detail="1"
            else
                if [ -n "${option_short}" ]; then
                    case "${_irepo}" in
                        "${FREEBSD_REPO}")
                            if [  -n "${_local_index_exists}" ]; then
                                if [ "${_index_label}" != '<' -a "${_index_label}" != '=' ]; then
                                    _print_detail=1
                                fi
                            fi
                            if [ "${_remote_label_FreeBSD}" != '=' -o "${_remote_label_SharedLocalRepo}" != '?' -o "${_remote_label_LocalRepo}" != '?' -o "${_remote_label_LocalBSDPorts}" != '?' ]; then
                                _print_detail=1
                            fi
                            ;;
                        "${LOCALBSDPORTS_REPO}")
                            if [  -n "${_local_index_exists}" ]; then
                                if [ "${_index_label}" != '=' ]; then
                                    _print_detail=1
                                fi
                            fi
                            if [ "${_remote_label_FreeBSD}" != '>' -o "${_remote_label_LocalRepo}" != '?' -o "${_remote_label_SharedLocalRepo}" != '?' -o "${_remote_label_LocalBSDPorts}" = '?' -o "${_remote_label_LocalBSDPorts}" = '<' ]; then
                                _print_detail=1
                            fi
                            ;;
                        "${SHARED_LOCAL_REPO}")
                            _print_detail=1
                            ;;
                        "${LOCAL_REPO}")
                            _print_detail=1
                            ;;
                        "${PORTS_DIRECT_INSTALLED_REPO}")
                            _print_detail=1
                            ;;
                        *)
                            echo "ERROR: unhandled repository: ${_irepo}" >&2
                            exit 1
                            ;;
                    esac
                else
                    if [ -n "${_local_index_exists}" ]; then
                        if [ "${_index_label}" != '?' -a "${_index_label}" != '=' ]; then
                            _print_detail=1
                        fi
                    fi
                    if [ "${_remote_label_FreeBSD}" != '?' -a "${_remote_label_FreeBSD}" != '=' ]; then
                        _print_detail=1
                    fi
                    if [ "${_remote_label_LocalBSDPorts}" != '?' -a "${_remote_label_LocalBSDPorts}" != '=' ]; then
                        _print_detail=1
                    fi
                    if [ "${_remote_label_SharedLocalRepo}" != '?' -a "${_remote_label_SharedLocalRepo}" != '=' ]; then
                        _print_detail=1
                    fi
                    if [ "${_remote_label_LocalRepo}" != '?' -a "${_remote_label_LocalRepo}" != '=' ]; then
                        _print_detail=1
                    fi
                fi
            fi
            if [ -n "${_print_detail}" ]; then
                print_title "${_ipackage}" "${_iversion}" "${_irepo}"
                if [ -n "${_local_index_exists}" ]; then
                    _index_version="$(get_immediate_index_version "${_ipackage}")"
                    print_detail_item "INDEX" "${_index_version}" "${_index_label}" "${_index_descr}"
                fi
                if [ -n "${option_alldata_FreeBSD}" -o "${_remote_label_FreeBSD}" != '?' ]; then
                    _remote_version_FreeBSD="$(pkg rquery -U -r "${FREEBSD_REPO}" '%v' "${_ipackage}")"
                    print_detail_item "${FREEBSD_REPO}" "${_remote_version_FreeBSD}" "${_remote_label_FreeBSD}" "${_remote_descr_FreeBSD}"
                fi
                if [ -n "${option_alldata_LocalBSDPorts}" -o "${_remote_label_LocalBSDPorts}" != '?' ]; then
                    _remote_version_LocalBSDPorts="$(pkg rquery -U -r "${LOCALBSDPORTS_REPO}" '%v' "${_ipackage}")"
                    print_detail_item "${LOCALBSDPORTS_REPO}" "${_remote_version_LocalBSDPorts}" "${_remote_label_LocalBSDPorts}" "${_remote_descr_LocalBSDPorts}"
                fi
                if [ -n "${option_alldata_SharedLocalRepo}" -o "${_remote_label_SharedLocalRepo}" != '?' ]; then
                    _remote_version_SharedLocalRepo="$(pkg rquery -U -r "${SHARED_LOCAL_REPO}" '%v' "${_ipackage}")"
                    print_detail_item "${SHARED_LOCAL_REPO}" "${_remote_version_SharedLocalRepo}" "${_remote_label_SharedLocalRepo}" "${_remote_descr_SharedLocalRepo}"
                fi
                if [ -n "${option_alldata_LocalRepo}" -o "${_remote_label_LocalRepo}" != '?' ]; then
                    _remote_version_LocalRepo="$(pkg rquery -U -r "${LOCAL_REPO}" '%v' "${_ipackage}")"
                    print_detail_item "${LOCAL_REPO}" "${_remote_version_LocalRepo}" "${_remote_label_LocalRepo}" "${_remote_descr_LocalRepo}"
                fi
                _mapped_package_name="$(get_mapping "${_ipackage}")"
                if [ -n "${_mapped_package_name}" ] ; then
	            printf '%18s %s %s (%s)\n' "--------------->" "${_mapped_package_name}" "$(pkg rquery -U '%v' "${_mapped_package_name}")" "$(pkg rquery -U '%R' "${_mapped_package_name}")"
                    if [ -n "${_local_index_exists}" ]; then
                        print_detail_item "INDEX" "$(get_immediate_index_version "${_mapped_package_name}")" "" ""
                    fi
                    print_detail_item "${FREEBSD_REPO}" "$(pkg rquery -U -r "${FREEBSD_REPO}" '%v' "${_mapped_package_name}")" "" ""
                    print_detail_item "${LOCALBSDPORTS_REPO}" "$(pkg rquery -U -r "${LOCALBSDPORTS_REPO}" '%v' "${_mapped_package_name}")" "" ""
                    print_detail_item "${SHARED_LOCAL_REPO}" "$(pkg rquery -U -r "${SHARED_LOCAL_REPO}" '%v' "${_mapped_package_name}")" "" ""
                    print_detail_item "${LOCAL_REPO}" "$(pkg rquery -U -r "${LOCAL_REPO}" '%v' "${_mapped_package_name}")" "" ""
                fi
            fi
        done
}


option_alldata=""
option_alldata_FreeBSD=""
option_alldata_LocalBSDPorts=""
opeion_alldata_SharedLocalRepo=""
option_alldata_LocalRepo=""
option_short=""
option_verbose=""

while getopts "VAasv" _opt ; do
    case ${_opt} in
	V)
            echo 'check-ports v${VERSION} (rv:@@HGREVISION@@)'
            exit 0
	    ;;
        A)
            # Print for every package the status of all repositories
            option_alldata="1"
            option_alldata_FreeBSD="1"
            option_alldata_LocalBSDPorts="1"
            option_alldata_SharedLocalRepo="1"
            option_alldata_LocalRepo="1"
            ;;
        a)
            # Print the data of all repos that have the package
            option_alldata="1"
            ;;
        s)
            # "short" output: if installed from FreeBSD repo: don't
            # report if only the index is newer
            option_short="1"
            ;;
        v)
            #
            # Print all titles and repo of every installed package always.
            # The output of the other repo status nevertheless depends on the
            # other flag settings.
            #
            option_verbose="1"
            ;;
        \?)
            exit 2
            ;;
        *)
            echo "option handling failed" >&2
            exit 2
            ;;
    esac
done

if [ -n "${option_short}" -a -n "${option_alldata}" ]; then
    echo "the -s option cannot be combined with -A or -a" >&2
    exit 2
fi

check_ports