chiark / gitweb /
git-debpush: Change comment heading
[dgit.git] / git-debpush
1 #!/bin/bash
2
3 # git-debpush -- create & push a git tag with metadata for an ftp-master upload
4 #
5 # Copyright (C) 2019 Sean Whitton
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 set -e$DGIT_TEST_DEBPUSH_DEBUG
21 set -o pipefail
22
23 # DESIGN PRINCIPLES
24 #
25 # - do not invoke dgit, do anything involving any tarballs, no network
26 #   access except `git push` right at the end
27 #
28 # - do not look at the working tree, like `git push` `git tag`
29 #
30 # - we are always in split brain mode, because that fits this workflow,
31 #   and avoids pushes failing just because dgit in the intermediary
32 #   service wants to append commits
33 #
34 # - if there is no previous tag created by this script, require a quilt
35 #   mode; if there is a previous tag, and no quilt mode provided, assume
36 #   same quilt mode as in previous tag created by this script
37
38 # ---- Helper functions and variables
39
40 us="$(basename $0)"
41
42 cleanup() {
43     if [ -d "$temp" ]; then
44         rm -rf "$temp"
45     fi
46 }
47
48 fail () {
49     echo >&2 "$us: $*";
50     exit 127;
51 }
52
53 badusage () {
54     fail "bad usage: $*";
55 }
56
57 get_file_from_ref () {
58     local path=$1
59
60     if git ls-tree --name-only -r "$branch" \
61             | grep -Eq "^$path$"; then
62         git cat-file blob $branch:$path
63     fi
64 }
65
66 # ---- Parse command line
67
68 getopt=$(getopt -s bash -o 'nfu:' \
69               -l 'no-push,force,branch:,remote:,distro:,quilt:,gbp,dpm,\
70 baredebian,baredebian+git,baredebian+tarball' \
71               -n "$us" -- "$@")
72 eval "set - $getopt"
73 set -e$DGIT_TEST_DEBPUSH_DEBUG
74
75 git_tag_opts=()
76 pushing=true
77 force=false
78 distro=debian
79 quilt_mode=""
80 branch="HEAD"
81
82 while true; do
83     case "$1" in
84         '-n'|'--no-push') pushing=false;           shift;   continue ;;
85         '-u')             git_tag_opts+=(-u "$2"); shift 2; continue ;;
86         '-f'|'--force')   force=true;              shift;   continue ;;
87         '--gbp')          quilt_mode='gbp';        shift;   continue ;;
88         '--dpm')          quilt_mode='dpm';        shift;   continue ;;
89         '--branch')       branch=$2;               shift 2; continue ;;
90         '--remote')       remote=$2;               shift 2; continue ;;
91         '--distro')       distro=$2;               shift 2; continue ;;
92         '--quilt')        quilt_mode=$2;           shift 2; continue ;;
93
94         '--baredebian'|'--baredebian+git')
95             quilt_mode=baredebian;         shift; continue ;;
96         '--baredebian+tarball')
97             quilt_mode=baredebian+tarball; shift; continue ;;
98
99         '--') shift; break ;;
100         *) badusage "unknown option $1" ;;
101     esac
102 done
103
104 if [ $# != 0 ]; then
105     badusage 'no positional arguments allowed'
106 fi
107
108 case "$quilt_mode" in
109     linear|auto|smash|nofix|gbp|dpm|unapplied|baredebian|baredebian+tarball|'') ;;
110     baredebian+git) quilt_mode="baredebian" ;;
111     *) badusage "invalid quilt mode: $quilt_mode" ;;
112 esac
113
114 # ---- Gather git information
115
116 remoteconfigs=()
117 push_branch=()
118
119 # Maybe $branch is a symbolic ref.  If so, resolve it
120 branchref="$(git symbolic-ref -q $branch || test $? = 1)"
121 if [ "x$branchref" != "x" ]; then
122    branch="$branchref"
123 fi
124 # If $branch is the name of a branch but it does not start with
125 # 'refs/heads/', prepend 'refs/heads/', so that we can know later
126 # whether we are tagging a branch or some other kind of committish
127 case "$branch" in
128     refs/heads/*) ;;
129     *)
130         branchref="$(git for-each-ref --format='%(objectname)' \
131                          '[r]efs/heads/$branch')"
132         if [ "x$branchref" != "x" ]; then
133             branch="refs/heads/$branch"
134         fi
135         ;;
136 esac
137
138 # If our tag will point at a branch, push that branch, and add its
139 # pushRemote and remote to the things we'll check if the user didn't
140 # supply a remote
141 case "$branch" in
142     refs/heads/*)
143         b=${branch#refs/heads/}
144         push_branch+=("$b")
145         remoteconfigs+=( branch.$b.pushRemote branch.$b.remote )
146         ;;
147 esac
148
149 # also check, if the branch does not have its own pushRemote or
150 # remote, whether there's a default push remote configured
151 remoteconfigs+=(remote.pushDefault)
152
153 if $pushing && [ "x$remote" = "x" ]; then
154     for c in "${remoteconfigs[@]}"; do
155         remote=$(git config "$c" || test $? = 1)
156         if [ "x$remote" != "x" ]; then break; fi
157     done
158     if [ "x$remote" = "x" ]; then
159         fail "pushing, but could not determine remote, so need --remote="
160     fi
161 fi
162
163 # ---- Gather source package information
164
165 temp=$(mktemp -d)
166 trap cleanup EXIT
167 mkdir "$temp/debian"
168 git cat-file blob "$branch":debian/changelog >"$temp/debian/changelog"
169 version=$(cd $temp; dpkg-parsechangelog -SVersion)
170 source=$(cd $temp; dpkg-parsechangelog -SSource)
171 target=$(cd $temp; dpkg-parsechangelog -SDistribution)
172 rm -rf "$temp"
173 trap - EXIT
174
175 # ---- Useful sanity checks
176
177 if ! $force; then
178
179     if [ "$target" = "UNRELEASED" ]; then
180         fail "UNRELEASED changelog"
181     fi
182
183     # TODO additional checks we might do:
184     #
185     # - are we uploading to a different suite from the last tag
186     #   (e.g. unstable after experimental)?  user should pass option to
187     #   confirm
188     #
189     # - walking backwards from $branch, if there is an archive/ strictly
190     #   before we reach most recent debian/ tag, error, this might be a
191     #   push of the dgit view to the maintainer branch
192
193 fi
194
195 # ---- Create the git tag
196
197 format="$(get_file_from_ref debian/source/format)"
198 case "$format" in
199     '3.0 (quilt)')  upstream=true ;;
200     '3.0 (native)') upstream=false ;;
201     '1.0'|'')
202         if get_file_from_ref debian/source/options | grep '^-sn *$'; then
203             upstream=false
204         elif get_file_from_ref debian/source/options | grep '^-sk *$'; then
205             upstream=true
206         else
207             fail 'please see "SOURCE FORMAT 1.0" in git-debpush(1)'
208         fi
209         ;;
210     *)
211         fail "unsupported debian/source/format $format"
212         ;;
213 esac
214
215 upstream_info=""
216 if $upstream; then
217     # xxx want way to override this
218     upstream_tag=$(git deborig --just-print --version="$version" \
219                        | head -n1)
220     upstream_committish=$(git rev-parse ${upstream_tag}^{})
221     upstream_info=" upstream-tag=$upstream_tag upstream=$upstream_committish"
222 fi
223
224 # convert according to DEP-14 rules
225 git_version=$(echo $version | tr ':~' '%_' | sed 's/\.(?=\.|$|lock$)/.#/g')
226
227 debian_tag="$distro/$git_version"
228
229 # If the user didn't supply a quilt mode, look for it in a previous
230 # tag made by this script
231 if [ "x$quilt_mode" = "x" ] && [ "$format" = "3.0 (quilt)" ]; then
232     set +o pipefail             # perl will SIGPIPE git-log(1) here
233     tag=$(git log --pretty=format:'%D' --decorate=full "$branch" \
234         | perl -wne 'use Dpkg::Version;
235                 @pieces = split /, /, $_;
236                 @debian_tag_vs = sort {version_compare($b, $a)}
237                     map { m|tag: refs/tags/debian/(.+)| ? $1 : () } @pieces;
238                 if (@debian_tag_vs) { print "debian/$debian_tag_vs[0]\n"; exit }')
239     if [ "x$tag" != "x" ]; then
240         quilt_mode=$(git cat-file -p $(git rev-parse "$tag") \
241                          | perl -wne \
242                                 'm/^\[dgit.*--quilt=([a-z+]+).*\]$/;
243                                  if ($1) { print "$1\n"; exit }')
244     fi
245     set -o pipefail
246 fi
247
248 quilt_mode_text=""
249 if [ "$format" = "3.0 (quilt)" ]; then
250     if [ "x$quilt_mode" = "x" ]; then
251         echo >&2 "$us: could not determine the git branch layout"
252         echo >&2 "$us: please supply a --quilt= argument"
253         exit 1
254     else
255         quilt_mode_text=" --quilt=$quilt_mode"
256     fi
257 fi
258
259 git tag "${git_tag_opts[@]}" -s -F- "$debian_tag" "$branch" <<EOF
260 $source release $version for $target
261
262 [dgit distro=$distro split$quilt_mode_text]
263 [dgit please-upload$upstream_info]
264 EOF
265
266 # ---- Do a git push
267
268 if $pushing; then
269     # xxx when user can specify upstream_tag, must cope with spaces
270     git push "$remote" "${push_branch[@]}" $upstream_tag "$debian_tag"
271 fi