dd16d151db
Approved by: vd (maintainer) - Make the diff output work - Use cdiff if exists, otherwise stick with $EDITOR - Stop trying to remove CVSROOT; hasn't been checked out since modules support was removed by erwin
551 lines
12 KiB
Bash
Executable File
551 lines
12 KiB
Bash
Executable File
#!/bin/sh -e
|
|
#
|
|
# rmport - remove port(s) from the FreeBSD Ports Collection.
|
|
#
|
|
# Copyright 2006-2007 Vasil Dimov
|
|
# Copyright 2012-2012 Chris Rees
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
# SUCH DAMAGE.
|
|
#
|
|
# Authors:
|
|
# Originally written by Vasil Dimov <vd@FreeBSD.org>
|
|
# Others:
|
|
# Chris Rees <crees@FreeBSD.org>
|
|
#
|
|
# $FreeBSD$
|
|
#
|
|
# MAINTAINER= crees@FreeBSD.org
|
|
#
|
|
|
|
PORTSDIR=${PORTSDIR:-/usr/ports}
|
|
INDEX=${PORTSDIR}/`make -C ${PORTSDIR} -V INDEXFILE`
|
|
|
|
TODAY=`date -u -v+0d +%Y-%m-%d`
|
|
|
|
SED="sed -i .orig -E"
|
|
# use ~/.ssh/config to set up the desired username if different than $LOGNAME
|
|
PCVS=${PCVS:-cvs -d pcvs.freebsd.org:/home/pcvs}
|
|
|
|
if ! CDIFF=$(which cdiff) ; then
|
|
CDIFF=${EDITOR}
|
|
fi
|
|
|
|
log()
|
|
{
|
|
echo "==> $*" >&2
|
|
}
|
|
|
|
escape()
|
|
{
|
|
# escape characters that may appear in ports' names and
|
|
# break regular expressions
|
|
echo "${1}" |sed -E 's/(\+|\.)/\\\1/g'
|
|
}
|
|
|
|
pkgname()
|
|
{
|
|
make -C ${PORTSDIR}/${1} -V PKGNAME
|
|
}
|
|
|
|
ask()
|
|
{
|
|
question=${1}
|
|
|
|
answer=x
|
|
while [ "${answer}" != "y" -a "${answer}" != "n" ] ; do
|
|
read -p "${question} [yn] " answer
|
|
done
|
|
|
|
echo ${answer}
|
|
}
|
|
|
|
# return category/port if arg is directly port's directory on the filesystem
|
|
find_catport()
|
|
{
|
|
arg=${1}
|
|
|
|
if [ -d "${PORTSDIR}/${arg}" ] ; then
|
|
# arg is category/port
|
|
echo ${arg}
|
|
elif [ -d "${arg}" ] ; then
|
|
# arg is the port's directory somewhere in the filesystem
|
|
# either absolute or relative
|
|
|
|
# get the full path
|
|
rp=`realpath ${arg}`
|
|
|
|
category=`basename \`dirname ${rp}\``
|
|
port=`basename ${rp}`
|
|
echo ${category}/${port}
|
|
else
|
|
echo "What do you mean by \`${arg}'?" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
find_expired()
|
|
{
|
|
EXPVAR=EXPIRATION_DATE
|
|
|
|
find ${PORTSDIR} -mindepth 3 -maxdepth 3 -name "Makefile*" \
|
|
|xargs grep -H ${EXPVAR} \
|
|
|sed -E "s|${PORTSDIR}/?([^/]+/[^/]+)/Makefile:${EXPVAR}=[[:space:]]*([0-9-]{10})$|\2 \1|g" \
|
|
|perl -ne "if ((substr(\$_, 0, 10) cmp '${TODAY}') <= 0) { print(\$_); }" \
|
|
|while read expdate catport ; do \
|
|
echo -n "${expdate} ${catport}: " ; \
|
|
make -C ${PORTSDIR}/${catport} -V DEPRECATED ; \
|
|
done
|
|
}
|
|
|
|
# create temporary checkout directory
|
|
mkcodir()
|
|
{
|
|
log "creating temporary directory"
|
|
d=`mktemp -d -t rmport`
|
|
mkdir ${d}/CVS
|
|
cat > ${d}/CVS/Repository <<REPOSITORY
|
|
We need ./CVS directory in order to create a custom commit message template
|
|
(and put it in ./CVS/Template). Anyway cvs insists on CVS/Repository existence
|
|
although it (hopefully) does not care about its contents.
|
|
REPOSITORY
|
|
touch ${d}/CVS/Template
|
|
log "created ${d}"
|
|
echo "${d}"
|
|
}
|
|
|
|
# checkout common files from the repository
|
|
co_common()
|
|
{
|
|
log "getting ports/MOVED and ports/LEGAL from repository"
|
|
${PCVS} co ports/MOVED ports/LEGAL
|
|
}
|
|
|
|
# check if some ports depend on the given port
|
|
# XXX Very Little Chance (tm) for breaking INDEX exists:
|
|
# /usr/ports/INDEX may be outdated and not contain recently added dependencies
|
|
check_dep_core()
|
|
{
|
|
catport=${1}
|
|
alltorm=${2}
|
|
pkgname=`pkgname ${catport}`
|
|
|
|
rmpkgs=""
|
|
rmcatports=""
|
|
for torm in ${alltorm} ; do
|
|
rmpkgs="${rmpkgs:+${rmpkgs}|}`pkgname ${torm}`"
|
|
rmcatports="${rmcatports:+${rmcatports}|}${PORTSDIR}/${torm}/"
|
|
done
|
|
|
|
err=0
|
|
|
|
deps=`grep -E "${pkgname}" ${INDEX} |grep -vE "^(${rmpkgs})" || :`
|
|
if [ -n "${deps}" ] ; then
|
|
log "${catport}: some port(s) depend on ${pkgname}:"
|
|
echo "${deps}" >&2
|
|
err=1
|
|
fi
|
|
|
|
# check if some Makefiles mention the port to be deleted
|
|
portdir_grep="^[^#].*/`basename ${catport}`([[:space:]]|/|$)"
|
|
r="`find ${PORTSDIR} -mindepth 2 -maxdepth 3 \
|
|
\( -name "Makefile*" -or -path "*Mk/*.mk" \) \
|
|
|xargs grep -EH "${portdir_grep}" \
|
|
|grep -vE "^(${rmcatports})" || :`"
|
|
if [ -n "${r}" ] ; then
|
|
if [ ${err} -eq 1 ] ; then
|
|
echo >&2
|
|
fi
|
|
log "${catport}: some Makefiles mention ${portdir_grep}:"
|
|
echo "${r}" >&2
|
|
err=1
|
|
fi
|
|
|
|
return ${err}
|
|
}
|
|
|
|
check_dep()
|
|
{
|
|
catport=${1}
|
|
persist=${2}
|
|
alltorm=${3}
|
|
|
|
while : ; do
|
|
log "${catport}: checking dependencies"
|
|
|
|
err=0
|
|
|
|
res="`check_dep_core ${catport} "${alltorm}" 2>&1`" || err=1
|
|
|
|
if [ ${err} -eq 0 ] ; then
|
|
break
|
|
fi
|
|
|
|
echo "${res}" |${PAGER:-less}
|
|
|
|
if [ ${persist} -eq 0 ] ; then
|
|
break
|
|
fi
|
|
|
|
echo "" >&2
|
|
echo "you can ignore the above issues and proceed anyway." >&2
|
|
echo "if this is the case, then either:" >&2
|
|
echo " * these are false positives" >&2
|
|
echo " * you want to break something" >&2
|
|
echo " * your ${PORTSDIR} is out of date, consider setting PORTSDIR in environment" >&2
|
|
echo " to point to a newer instance of the ports tree" >&2
|
|
echo "or you can hit \`n' to repeat the check" >&2
|
|
answer=`ask "ignore the above issues"`
|
|
if [ "${answer}" = "y" ] ; then
|
|
break
|
|
fi
|
|
done
|
|
}
|
|
|
|
# query GNATS via query-pr-summary.cgi, format and return the result
|
|
get_PRs_www()
|
|
{
|
|
catport=${1}
|
|
synopsis=${2}
|
|
date_re='[0-9]{4}/[0-9]{2}/[0-9]{2}'
|
|
prnum_re='.+/[0-9]{5,6}'
|
|
synopsis_re='.{10,}'
|
|
|
|
log "${catport}: getting PRs having ${synopsis} in the synopsis"
|
|
|
|
url="http://www.freebsd.org/cgi/query-pr-summary.cgi?text=${synopsis}"
|
|
|
|
raw="`fetch -q -T 20 -o - "${url}"`"
|
|
|
|
if [ -z "${raw}" ] ; then
|
|
log "${catport}: empty result from URL: ${url}"
|
|
exit 1
|
|
fi
|
|
|
|
printf "%s" "${raw}" \
|
|
|sed -nE "s|.*>(${date_re})<.*>(${prnum_re})<.*>(${synopsis_re})</td.*|\1 \2 \3|p" \
|
|
|sort
|
|
}
|
|
|
|
# query GNATS via query-pr on freefall and return the result
|
|
get_PRs_freefall()
|
|
{
|
|
catport=${1}
|
|
synopsis=${2}
|
|
|
|
log "${catport}: getting PRs having ${synopsis} in the synopsis"
|
|
|
|
ssh freefall.freebsd.org "query-pr -qx -y '${synopsis}' || :"
|
|
}
|
|
|
|
# query GNATS and return the result
|
|
get_PRs()
|
|
{
|
|
catport=${1}
|
|
synopsis=${2}
|
|
|
|
get_PRs_freefall ${catport} ${synopsis}
|
|
}
|
|
|
|
# check if any PRs exist that are related to the port
|
|
check_PRs()
|
|
{
|
|
catport=${1}
|
|
synopsis=${2}
|
|
|
|
PRs="`get_PRs ${catport} "${synopsis}"`" || exit
|
|
|
|
if [ -n "${PRs}" ] ; then
|
|
log "${catport}: PRs found, related to ${synopsis}:"
|
|
printf "%s\n" "${PRs}" >&2
|
|
|
|
echo "you can skip ${catport} and continue with the rest or remove it anyway" >&2
|
|
answer=`ask "do you want to skip ${catport}?"`
|
|
if [ "${answer}" = "y" ] ; then
|
|
return 1
|
|
else
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# checkout port's specific files from the repository
|
|
co_port()
|
|
{
|
|
cat=${1}
|
|
port=${2}
|
|
|
|
log "${cat}/${port}: getting ${cat}/Makefile and port's files from repository"
|
|
${PCVS} co ports/${cat}/Makefile ports/${cat}/${port}
|
|
}
|
|
|
|
# check if anything about the port is mentioned in ports/LEGAL
|
|
check_LEGAL()
|
|
{
|
|
catport=${1}
|
|
pkgname=${2}
|
|
|
|
for checkstr in ${pkgname} ${catport} ; do
|
|
msg="${catport}: checking if ${checkstr} is in ports/LEGAL"
|
|
log "${msg}"
|
|
while grep -i ${checkstr} ports/LEGAL ; do
|
|
echo "" >&2
|
|
echo "${checkstr} is in ${PWD}/ports/LEGAL" >&2
|
|
echo "remove it and hit <enter> when ready" >&2
|
|
echo "or hit \`s' to skip this issue and continue anyway" >&2
|
|
read answer
|
|
if [ "${answer}" = "s" ] ; then
|
|
break
|
|
fi
|
|
log "${msg}"
|
|
done
|
|
done
|
|
}
|
|
|
|
# add port's entry to ports/MOVED
|
|
edit_MOVED()
|
|
{
|
|
catport=${1}
|
|
|
|
DEPRECATED="`make -C ${PORTSDIR}/${catport} -V DEPRECATED`"
|
|
DEPRECATED=${DEPRECATED:+: ${DEPRECATED}}
|
|
if [ -n "`make -C ${PORTSDIR}/${catport} -V EXPIRATION_DATE`" ] ; then
|
|
REASON="Has expired${DEPRECATED}"
|
|
else
|
|
REASON="Removed${DEPRECATED}"
|
|
fi
|
|
|
|
log "${catport}: adding entry to ports/MOVED"
|
|
|
|
echo "${catport}||${TODAY}|${REASON}" >> ports/MOVED
|
|
}
|
|
|
|
# remove port from category/Makefile
|
|
edit_Makefile()
|
|
{
|
|
cat=${1}
|
|
port=${2}
|
|
|
|
log "${cat}/${port}: removing from ${cat}/Makefile"
|
|
|
|
portesc=`escape ${port}`
|
|
|
|
${SED} -e "/^[[:space:]]*SUBDIR[[:space:]]*\+=[[:space:]]*${portesc}([[:space:]]+#.*)?$/d" \
|
|
ports/${cat}/Makefile
|
|
}
|
|
|
|
# remove port's files
|
|
rm_port()
|
|
{
|
|
catport=${1}
|
|
|
|
log "${catport}: removing port's files"
|
|
|
|
${PCVS} rm `find ports/${catport} -type f -not -path "*/CVS/*" -delete -print`
|
|
}
|
|
|
|
append_Template()
|
|
{
|
|
catport=${1}
|
|
|
|
msg=${catport}
|
|
|
|
EXPIRATION_DATE=`make -C ${PORTSDIR}/${catport} -V EXPIRATION_DATE`
|
|
if [ -n "${EXPIRATION_DATE}" ] ; then
|
|
msg="${EXPIRATION_DATE} ${msg}"
|
|
fi
|
|
|
|
DEPRECATED="`make -C ${PORTSDIR}/${catport} -V DEPRECATED`"
|
|
if [ -n "${DEPRECATED}" ] ; then
|
|
msg="${msg}: ${DEPRECATED}"
|
|
fi
|
|
|
|
log "${catport}: adding entry to commit message template"
|
|
|
|
echo "${msg}" >> ./CVS/Template
|
|
}
|
|
|
|
# diff
|
|
diff()
|
|
{
|
|
log "creating diff"
|
|
|
|
diffout=${codir}/diff
|
|
|
|
${PCVS} diff -uN ports > ${diffout} 2>&1 || :
|
|
|
|
read -p "hit <enter> to view cvs diff output" dummy
|
|
|
|
# give this to the outside world so it can be showed to the committer
|
|
# and removed when we are done
|
|
echo ${diffout}
|
|
}
|
|
|
|
# update, ask for confirmation and commit
|
|
commit()
|
|
{
|
|
log "running cvs update"
|
|
${PCVS} -fnq up ports 2>&1 |${PAGER:-less}
|
|
|
|
answer=`ask "do you want to commit?"`
|
|
|
|
if [ "${answer}" = "y" ] ; then
|
|
${PCVS} ci ports
|
|
fi
|
|
}
|
|
|
|
cleanup()
|
|
{
|
|
diffout=${1}
|
|
codir=${2}
|
|
|
|
log "cleaning up"
|
|
|
|
rm ${diffout}
|
|
|
|
rm CVS/Entries.Log CVS/Repository CVS/Template
|
|
rmdir CVS
|
|
|
|
# release cvs directories
|
|
${PCVS} rel -d ports
|
|
|
|
cd /
|
|
rmdir ${codir}
|
|
}
|
|
|
|
usage()
|
|
{
|
|
echo "Usage:" >&2
|
|
echo "" >&2
|
|
echo "find expired ports:" >&2
|
|
echo "${0} -F" >&2
|
|
echo "" >&2
|
|
echo "remove port(s):" >&2
|
|
echo "${0} category1/port1 [ category2/port2 ... ]" >&2
|
|
echo "" >&2
|
|
echo "remove all expired ports (as returned by -F):" >&2
|
|
echo "${0} -a" >&2
|
|
echo "" >&2
|
|
echo "just check dependencies:" >&2
|
|
echo "${0} -d category/port" >&2
|
|
echo "" >&2
|
|
echo "just check if any related PRs exist:" >&2
|
|
echo "${0} -p synopsis" >&2
|
|
|
|
exit 64
|
|
}
|
|
|
|
# main
|
|
|
|
if [ ${#} -eq 0 -o "${1}" = "-h" -o "${1}" = "--help" ] ; then
|
|
usage
|
|
fi
|
|
|
|
if [ ${1} = "-d" ] ; then
|
|
if [ ${#} -ne 2 ] ; then
|
|
usage
|
|
fi
|
|
catport=`find_catport ${2}`
|
|
check_dep ${catport} 0 ${catport}
|
|
exit
|
|
fi
|
|
|
|
if [ ${1} = "-p" ] ; then
|
|
if [ ${#} -ne 2 ] ; then
|
|
usage
|
|
fi
|
|
get_PRs "dummy" ${2}
|
|
exit
|
|
fi
|
|
|
|
if [ ${1} = "-F" ] ; then
|
|
if [ ${#} -ne 1 ] ; then
|
|
usage
|
|
fi
|
|
find_expired
|
|
exit
|
|
fi
|
|
|
|
if [ ${1} = "-a" ] ; then
|
|
if [ ${#} -ne 1 ] ; then
|
|
usage
|
|
fi
|
|
${0} `find_expired |cut -f 2 -d ' ' |cut -f 1 -d :`
|
|
exit
|
|
fi
|
|
|
|
codir=`mkcodir`
|
|
cd ${codir}
|
|
|
|
co_common
|
|
|
|
for catport in $* ; do
|
|
# convert to category/port
|
|
catport=`find_catport ${catport}`
|
|
cat=`dirname ${catport}`
|
|
port=`basename ${catport}`
|
|
# remove any trailing slashes
|
|
catport="${cat}/${port}"
|
|
pkgname=`pkgname ${catport}`
|
|
|
|
check_dep ${catport} 1 "${*}"
|
|
|
|
if ! check_PRs ${catport} ${port} ; then
|
|
continue
|
|
fi
|
|
|
|
co_port ${cat} ${port}
|
|
|
|
check_LEGAL ${catport} ${pkgname}
|
|
|
|
# everything seems ok, edit the files
|
|
|
|
edit_MOVED ${catport}
|
|
|
|
edit_Makefile ${cat} ${port}
|
|
|
|
rm_port ${catport}
|
|
|
|
append_Template ${catport}
|
|
done
|
|
|
|
# give a chance to the committer to edit files by hand and recreate/review
|
|
# the diff afterwards
|
|
answer=y
|
|
while [ "${answer}" = "y" ] ; do
|
|
diffout=$(diff)
|
|
|
|
${CDIFF} ${diffout}
|
|
|
|
echo "" >&2
|
|
echo "you can now edit files under ${codir}/ by hand" >&2
|
|
answer=`ask "do you want to recreate the diff?"`
|
|
done
|
|
|
|
commit
|
|
|
|
cleanup ${diffout} ${codir}
|
|
|
|
# EOF
|