#!/bin/bash
# git-debpush -- create & push a git tag with metadata for an ftp-master upload
#
# Copyright (C) 2019 Sean Whitton
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
set -e${DGIT_TEST_DEBPUSH_DEBUG-x}
set -o pipefail
# DEBUG
# Principles of operation
# - do not invoke dgit, anything involving any tarballs, no network
# except `git push`
# - do not look at the working tree, like `git push` `git tag`, and so
# we can later add functionality to debpush any branch
# - we are always in split brain mode, because this means the push won't
# fail because dgit needs to append commits
# - if there is no previous tag created by this script, require a quilt
# mode; if there is a previous tag, and no quilt mode provided, assume
# same quilt mode
# Other notes (which should be converted to a manpage/usage)
# - arguments after '--' passed to `git push`
# ---- Helper functions
cleanup() {
if [ -d "$TEMP" ]; then
rm -rf "$TEMP"
fi
}
# ---- Parse command line
us="$(basename $0)"
fail () { echo >&2 "$us: $*"; exit 127; }
badusage () { fail "bad usage: $*"; }
getopt=$(getopt -s bash -o 'nfu:' \
-l 'no-push,force,branch:,remote:,distro:,quilt:,gbp,dpm,baredebian,\
baredebian+git,baredebian+tarball,linear' \
-n "$us" -- "$@")
eval "set - $getopt"
set -e${DGIT_TEST_DEBPUSH_DEBUG-x}
git_tag_opts=()
pushing=true
distro=debian
quilt_mode=""
while true; do
case "$1" in
'-n'|'--no-push')
pushing=false
shift
continue
;;
'-u')
git_tag_opts+=(-u "$2")
shift 2
continue
;;
'-f'|'--force')
force='yes'
shift
continue
;;
'--gbp')
quilt_mode='gbp'
shift
continue
;;
'--dpm')
quilt_mode='dpm'
shift
continue
;;
'--baredebian'|'--baredebian+git')
quilt_mode=baredebian
shift
continue
;;
'--baredebian+tarball')
quilt_mode=baredebian+tarball
shift
continue
;;
'--branch') branch=$2; shift 2; continue ;;
'--remote') remote=$2; shift 2; continue ;;
'--distro') distro=$2; shift 2; continue ;;
'--quilt') quilt_mode=$2; shift 2; continue ;;
'--')
shift
break
;;
*)
badusage "unknown option $1"
;;
esac
done
if [ $# != 0 ]; then badusage 'no positional arguments allowed'; fi
case "$quilt_mode" in
'linear'|'auto'|'smash'|'nofix'|'gbp'|'dpm'|'unapplied'|'baredebian'|'baredebian+tarball')
;;
'baredebian+git')
quilt_mode="baredebian"
;;
*)
badusage " invalid quilt mode: $quilt_mode"
;;
esac
remoteconfigs=()
push_branch=()
if [ ! "$branch" ]; then
branch=HEAD
branchref="$(git symbolic-ref -q HEAD || test $? = 1)"
case "$branchref" in
refs/heads/*)
b=${branchref#refs/heads/}
remoteconfigs+=( branch.$b.pushRemote branch.$b.remote )
push_branch+=("$b")
;;
esac
fi
remoteconfigs+=(remote.pushDefault)
if $pushing && [ ! "$remote" ]; then
for c in "${remoteconfigs[@]}"; do
remote=$(git config "$c" || test $? = 1)
if [ "x$remote" ]; then break; fi
done
if [ ! "$remote" ]; then
fail "pushing, but could not determine remote, so need --remote="
fi
fi
# ---- Gather source package information
TEMP=$(mktemp -d)
trap cleanup EXIT
mkdir "$TEMP/debian"
git cat-file blob HEAD:debian/changelog >"$TEMP/debian/changelog"
version=$(cd $TEMP; dpkg-parsechangelog -SVersion)
source=$(cd $TEMP; dpkg-parsechangelog -SSource)
target=$(cd $TEMP; dpkg-parsechangelog -SDistribution)
rm -rf "$TEMP"
trap - EXIT
# ---- Useful sanity checks
if [ "$force" != "yes" ]; then
if [ "$target" = "UNRELEASED" ]; then
fail "UNRELEASED changelog"
fi
# TODO additional checks we might do:
#
# - are we uploading to a different suite from the last tag
# (e.g. unstable after experimental)? user should pass option to
# confirm
#
# - walking backwards from $branch, if there is an archive/ strictly
# before we reach most recent debian/ tag, error, this might be a
# push of the dgit view to the maintainer branch
#
# - check for UNRELEASED changelog
fi
# ---- Create the git tag
get_file_from_ref () {
local path=$1
if git ls-tree --name-only -r "$branch" \
| grep -Eq "^$path$"; then
git cat-file blob $branch:$path
fi
}
format="$(get_file_from_ref debian/source/format)"
case "$format" in
'3.0 (quilt)') upstream=true ;;
'3.0 (native)') upstream=false ;;
'1.0'|'')
if get_file_from_ref debian/source/options | grep '^-sn *$'; then
upstream=false
elif get_file_from_ref debian/source/options | grep '^-sk *$'; then
upstream=true
else
fail 'xxxx see sn /sk docs'
fi
;;
*)
fail "unsupported debian/source/format $format"
;;
esac
if $upstream; then
upstream_tag=$(git deborig --just-print --version="$version" \
| head -n1)
upstream_committish=$(git rev-parse ${upstream_tag}^{})
upstream_info=" upstream-tag=$upstream_tag upstream=$upstream_committish"
fi
# convert according to DEP-14 rules
git_version=$(echo $version | tr ':~' '%_' | sed 's/\.(?=\.|$|lock$)/.#/g')
debian_tag="$distro/$git_version"
if [ "x$quilt_mode" = "x" ] && [ "$format" = "3.0 (quilt)" ]; then
set +o pipefail # perl will SIGPIPE git-log here
tag=$(git log --pretty=format:'%D' --decorate=full "$branch" \
| perl -wne 'use Dpkg::Version;
@pieces = split /, /, $_;
@debian_tag_vs = sort {version_compare($b, $a)}
map { m|tag: refs/tags/debian/(.+)| ? $1 : () } @pieces;
if (@debian_tag_vs) { print "debian/$debian_tag_vs[0]\n"; exit }')
if [ "x$tag" != "x" ]; then
quilt_mode=$(git cat-file -p $(git rev-parse "$tag") \
| perl -wne \
'm/^\[dgit.*--quilt=([a-z+]+).*\]$/;
if ($1) { print "$1\n"; exit }')
fi
set -o pipefail
fi
quilt_mode_text=""
if [ "$format" = "3.0 (quilt)" ]; then
if [ "x$quilt_mode" = "x" ]; then
echo >&2 "$us: could not determine the git branch layout"
echo >&2 "$us: please supply a --quilt= argument"
exit 1
else
quilt_mode_text=" --quilt=$quilt_mode"
fi
fi
git tag "${git_tag_opts[@]}" -s -F- "$debian_tag" "$branch" <