#!/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" <