automerge 5.52 KB
Newer Older
1
#!/bin/bash
2
### automerge - automatically merge the Emacs release branch to master
3

Paul Eggert's avatar
Paul Eggert committed
4
## Copyright (C) 2018-2020 Free Software Foundation, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

## Author: Glenn Morris <rgm@gnu.org>

## This file is part of GNU Emacs.

## GNU Emacs 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.

## GNU Emacs 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 GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

### Commentary:

## Automatically merge the Emacs release branch to master.
26 27 28 29
## If the merge succeeds, optionally build and test the results,
## and then push it.
## Intended usage:
## Have a dedicated git directory just for this.
30
## Have a cron job that calls this script with -r -p.
31 32 33
##
## Modifying a running shell script can have unpredictable results,
## so the paranoid will first make a copy of this script, and then run
34 35
## it with the -d option in the repository directory, in case a pull
## updates this script while it is working.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

die ()                 # write error to stderr and exit
{
    [ $# -gt 0 ] && echo "$PN: $@" >&2
    exit 1
}

PN=${0##*/}                     # basename of script
PD=${0%/*}

[ "$PD" = "$0" ] && PD=.        # if PATH includes PWD

usage ()
{
    cat 1>&2 <<EOF
51
Usage: ${PN} [-b] [-d] [-e emacs] [-n nmin] [-p] [-r] [-t] [-- mflags]
52 53 54
Merge the Emacs release branch to master.
Passes any non-option args to make (eg -- -j2).
Options:
55
-d: no initial cd to parent of script directory
56
-e: Emacs executable to use for the initial merge (default $emacs)
57
-n: minimum number of commits to try merging (default $nmin)
58 59 60
-b: try to build after merging
-t: try to check after building
-p: if merge, build, check all succeed, push when finished (caution!)
61
-r: start by doing a hard reset (caution!) and pull
62 63 64 65 66 67 68 69 70 71 72 73 74
EOF
    exit 1
}


## Defaults.

emacs=emacs
nmin=10
build=
test=
push=
quiet=
75
reset=
76
nocd=
77

78
while getopts ":hbde:n:pqrt" option ; do
79 80 81 82 83
    case $option in
        (h) usage ;;

        (b) build=1 ;;

84
        (d) nocd=1 ;;
85

86 87 88 89 90 91 92 93
        (e) emacs=$OPTARG ;;

        (n) nmin=$OPTARG ;;

        (p) push=1 ;;

        (q) quiet=1 ;;

94 95
        (r) reset=1 ;;

96 97 98 99 100 101 102 103 104 105 106 107 108
        (t) test=1 ;;

        (\?) die "Bad option -$OPTARG" ;;

        (:) die "Option -$OPTARG requires an argument" ;;

        (*) die "getopts error" ;;
    esac
done
shift $(( --OPTIND ))
OPTIND=1


109
[ "$nocd" ] || {
110 111
    cd $PD                      # this should be the admin directory
    cd ../
112
}
113 114 115 116 117 118

[ -d admin ] || die "Could not locate admin directory"

[ -e .git ] || die "No .git"


119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
## Does not work 100% because a lot of Emacs batch output comes on
## stderr (?).
[ "$quiet" ] && exec 1> /dev/null


[ "$push" ] && test=1
[ "$test" ] && build=1


tempfile=/tmp/$PN.$$

trap "rm -f $tempfile 2> /dev/null" EXIT


[ -e Makefile ] && [ "$build" ] && {
    echo "Cleaning..."
    make maintainer-clean >& /dev/null
}


139 140
[ "$reset" ] && {
    echo "Resetting..."
141
    git reset -q --hard origin/master || die "reset error"
142 143

    echo "Pulling..."
144
    git pull -q --ff-only || die "pull error"
145 146 147
}


148
rev=$(git rev-parse HEAD)
149

150
[ $(git rev-parse @{u}) = $rev ] || die "Local state does not match origin"
151 152


153 154 155
merge ()
{
    echo "Merging..."
156

157 158 159 160 161 162 163
    if $emacs --batch -Q -l ./admin/gitmerge.el \
              --eval "(setq gitmerge-minimum-missing $nmin)" -f gitmerge \
              >| $tempfile 2>&1; then
        echo "merged ok"
        return 0

    else
164
        grep -E "Nothing to merge|Number of missing commits" $tempfile && \
165 166 167 168 169 170 171 172 173 174
            exit 0

        cat "$tempfile" 1>&2

        die "merge error"
    fi
}


merge
175 176


177 178
## FIXME it would be better to trap this in gitmerge.
## NEWS should never be modified, only eg NEWS.26.
179 180
git diff --stat --cached origin/master | grep -q "etc/NEWS " && \
    die "etc/NEWS has been modified"
181 182


183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
[ "$build" ] || exit 0


echo "Running autoreconf..."

autoreconf -i -I m4 2>| $tempfile

retval=$?

## Annoyingly, autoreconf puts the "installing `./foo' messages on stderr.
if [ "$quiet" ]; then
    grep -v 'installing `\.' $tempfile 1>&2
else
    cat "$tempfile" 1>&2
fi

[ $retval -ne 0 ] && die "autoreconf error"


echo "Running ./configure..."

## Minimize required packages.
./configure --without-x || die "configure error"


echo "Building..."

make "$@" || die "make error"

echo "Build finished ok"


[ "$test" ] || exit 0


echo "Testing..."

220 221
## We just want a fast pass/fail, we don't want to debug.
make "$@" check TEST_LOAD_EL=no || die "check error"
222 223 224 225 226 227 228 229

echo "Tests finished ok"


[ "$push" ] || exit 0


## In case someone else pushed while we were working.
230 231 232 233 234 235
echo "Checking for remote changes..."
git fetch || die "fetch error"

[ $(git rev-parse @{u}) = $rev ] || {

    echo "Upstream has changed"
236

237 238
    ## Rebasing would be incorrect, since it would rewrite the
    ## (already published) release branch commits.
Paul Eggert's avatar
Paul Eggert committed
239
    ## Ref eg https://lists.gnu.org/r/emacs-devel/2014-12/msg01435.html
240 241 242 243 244 245 246 247 248 249 250 251
    ## Instead, we throw away what we just did, and do the merge again.
    echo "Resetting..."
    git reset --hard $rev

    echo "Pulling..."
    git pull --ff-only || die "pull error"

    merge

    ## If the merge finished ok again, we don't bother doing a second
    ## build and test.
}
252 253 254 255 256 257 258 259

echo "Pushing..."
git push || die "push error"


exit 0

### automerge ends here