chiark / gitweb /
git-debpush: new script
[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-x}
21 set -o pipefail
22
23 # DEBUG
24
25 # Principles of operation
26
27 # - do not invoke dgit, anything involving any tarballs, no network
28 #   except `git push`
29
30 # - do not look at the working tree, like `git push` `git tag`, and so
31 #   we can later add functionality to debpush any branch
32
33 # - we are always in split brain mode, because this means the push won't
34 #   fail because dgit needs to append commits
35
36 # - if there is no previous tag created by this script, require a quilt
37 #   mode; if there is a previous tag, and no quilt mode provided, assume
38 #   same quilt mode
39
40 # Other notes (which should be converted to a manpage/usage)
41
42 # - arguments after '--' passed to `git push`
43
44 # ---- Helper functions
45
46 cleanup() {
47     if [ -d "$TEMP" ]; then
48         rm -rf "$TEMP"
49     fi
50 }
51
52 # ---- Parse command line
53
54 us="$(basename $0)"
55
56 fail () { echo >&2 "$us: $*"; exit 127; }
57 badusage () { fail "bad usage: $*"; }
58
59 getopt=$(getopt -s bash -o 'nfu:' \
60               -l 'no-push,force,branch:,remote:,distro:,quilt:,gbp,dpm,baredebian,\
61 baredebian+git,baredebian+tarball,linear' \
62               -n "$us" -- "$@")
63 eval "set - $getopt"
64 set -e${DGIT_TEST_DEBPUSH_DEBUG-x}
65
66 git_tag_opts=()
67 pushing=true
68 distro=debian
69
70 quilt_mode=""
71 while true; do
72     case "$1" in
73         '-n'|'--no-push')
74             pushing=false
75             shift
76             continue
77             ;;
78         '-u')
79             git_tag_opts+=(-u "$2")
80             shift 2
81             continue
82             ;;
83         '-f'|'--force')
84             force='yes'
85             shift
86             continue
87             ;;
88         '--gbp')
89             quilt_mode='gbp'
90             shift
91             continue
92             ;;
93         '--dpm')
94             quilt_mode='dpm'
95             shift
96             continue
97             ;;
98         '--baredebian'|'--baredebian+git')
99             quilt_mode=baredebian
100             shift
101             continue
102             ;;
103         '--baredebian+tarball')
104             quilt_mode=baredebian+tarball
105             shift
106             continue
107             ;;
108         '--branch') branch=$2;     shift 2; continue ;;
109         '--remote') remote=$2;     shift 2; continue ;;
110         '--distro') distro=$2;     shift 2; continue ;;
111         '--quilt')  quilt_mode=$2; shift 2; continue ;;
112         '--')
113             shift
114             break
115             ;;
116         *)
117             badusage "unknown option $1"
118             ;;
119
120     esac
121 done
122
123 if [ $# != 0 ]; then badusage 'no positional arguments allowed'; fi
124
125 case "$quilt_mode" in
126     'linear'|'auto'|'smash'|'nofix'|'gbp'|'dpm'|'unapplied'|'baredebian'|'baredebian+tarball')
127         ;;
128     'baredebian+git')
129         quilt_mode="baredebian"
130         ;;
131     *)
132         badusage " invalid quilt mode: $quilt_mode"
133         ;;
134 esac
135
136 remoteconfigs=()
137 push_branch=()
138
139 if [ ! "$branch" ]; then
140     branch=HEAD
141     branchref="$(git symbolic-ref -q HEAD || test $? = 1)"
142     case "$branchref" in
143         refs/heads/*)
144             b=${branchref#refs/heads/}
145             remoteconfigs+=( branch.$b.pushRemote branch.$b.remote )
146             push_branch+=("$b")
147         ;;
148     esac
149 fi
150
151 remoteconfigs+=(remote.pushDefault)
152
153 if $pushing && [ ! "$remote" ]; then
154     for c in "${remoteconfigs[@]}"; do
155         remote=$(git config "$c" || test $? = 1)
156         if [ "x$remote" ]; then break; fi
157     done
158     if [ ! "$remote" ]; 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 HEAD: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" != "yes" ]; 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     # - check for UNRELEASED changelog
194
195 fi
196
197 # ---- Create the git tag
198
199 get_file_from_ref () {
200     local path=$1
201     if git ls-tree --name-only -r "$branch" \
202             | grep -Eq "^$path$"; then
203         git cat-file blob $branch:$path
204     fi
205 }
206
207 format="$(get_file_from_ref debian/source/format)"
208 case "$format" in
209     '3.0 (quilt)') upstream=true ;;
210     '3.0 (native)') upstream=false ;;
211     '1.0'|'')
212         if get_file_from_ref debian/source/options | grep '^-sn *$'; then
213             upstream=false
214         elif get_file_from_ref debian/source/options | grep '^-sk *$'; then
215             upstream=true
216         else
217             fail 'xxxx see sn /sk docs'
218         fi
219         ;;
220     *)
221         fail "unsupported debian/source/format $format"
222         ;;
223 esac
224
225 if $upstream; then
226     upstream_tag=$(git deborig --just-print --version="$version" \
227                        | head -n1)
228     upstream_committish=$(git rev-parse ${upstream_tag}^{})
229     upstream_info=" upstream-tag=$upstream_tag upstream=$upstream_committish"
230 fi
231
232 # convert according to DEP-14 rules
233 git_version=$(echo $version | tr ':~' '%_' | sed 's/\.(?=\.|$|lock$)/.#/g')
234
235 debian_tag="$distro/$git_version"
236
237 if [ "x$quilt_mode" = "x" ] && [ "$format" = "3.0 (quilt)" ]; then
238     set +o pipefail             # perl will SIGPIPE git-log here
239     tag=$(git log --pretty=format:'%D' --decorate=full "$branch" \
240         | perl -wne 'use Dpkg::Version;
241                 @pieces = split /, /, $_;
242                 @debian_tag_vs = sort {version_compare($b, $a)}
243                     map { m|tag: refs/tags/debian/(.+)| ? $1 : () } @pieces;
244                 if (@debian_tag_vs) { print "debian/$debian_tag_vs[0]\n"; exit }')
245     if [ "x$tag" != "x" ]; then
246         quilt_mode=$(git cat-file -p $(git rev-parse "$tag") \
247                          | perl -wne \
248                                 'm/^\[dgit.*--quilt=([a-z+]+).*\]$/;
249                                  if ($1) { print "$1\n"; exit }')
250     fi
251     set -o pipefail
252 fi
253
254 quilt_mode_text=""
255 if [ "$format" = "3.0 (quilt)" ]; then
256     if [ "x$quilt_mode" = "x" ]; then
257         echo >&2 "$us: could not determine the git branch layout"
258         echo >&2 "$us: please supply a --quilt= argument"
259         exit 1
260     else
261         quilt_mode_text=" --quilt=$quilt_mode"
262     fi
263 fi
264
265 git tag "${git_tag_opts[@]}" -s -F- "$debian_tag" "$branch" <<EOF
266 $source release $version for $target
267
268 [dgit distro=$distro split$quilt_mode_text]
269 [dgit please-upload$upstream_info]
270 EOF
271
272 # ---- Do a git push
273
274 if $pushing; then
275     git push "$remote" "${push_branch[@]}" "$upstream_tag" "$debian_tag"
276 fi