From 73f696920be0eb754bd6bf46b41ab6317f641fd0 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 27 Oct 2014 00:07:25 -0700 Subject: [PATCH 01/11] Add script to verify all merge commits are signed --- contrib/README.md | 9 +++++ contrib/verify-commits/gpg.sh | 15 +++++++ contrib/verify-commits/pre-push-hook.sh | 16 ++++++++ contrib/verify-commits/trusted-git-root | 1 + contrib/verify-commits/trusted-keys | 5 +++ contrib/verify-commits/verify-commits.sh | 51 ++++++++++++++++++++++++ 6 files changed, 97 insertions(+) create mode 100644 contrib/README.md create mode 100755 contrib/verify-commits/gpg.sh create mode 100755 contrib/verify-commits/pre-push-hook.sh create mode 100644 contrib/verify-commits/trusted-git-root create mode 100644 contrib/verify-commits/trusted-keys create mode 100755 contrib/verify-commits/verify-commits.sh diff --git a/contrib/README.md b/contrib/README.md new file mode 100644 index 0000000..da1fc06 --- /dev/null +++ b/contrib/README.md @@ -0,0 +1,9 @@ +Repository Tools +--------------------- + +### [Developer tools](/contrib/devtools) ### +Specific tools for developers working on this repository. +Contains the script `github-merge.sh` for merging github pull requests securely and signing them using GPG. + +### [Verify-Commits](/contrib/verify-commits) ### +Tool to verify that every merge commit was signed by a developer using the above `github-merge.sh` script. diff --git a/contrib/verify-commits/gpg.sh b/contrib/verify-commits/gpg.sh new file mode 100755 index 0000000..6b5137e --- /dev/null +++ b/contrib/verify-commits/gpg.sh @@ -0,0 +1,15 @@ +#!/bin/sh +INPUT=$(/dev/null); do + case "$LINE" in "[GNUPG:] VALIDSIG"*) + while read KEY; do + case "$LINE" in "[GNUPG:] VALIDSIG $KEY "*) VALID=true;; esac + done < ./contrib/verify-commits/trusted-keys + esac +done +if ! $VALID; then + exit 1 +fi +echo "$INPUT" | gpg --trust-model always "$@" 2>/dev/null diff --git a/contrib/verify-commits/pre-push-hook.sh b/contrib/verify-commits/pre-push-hook.sh new file mode 100755 index 0000000..607c0ca --- /dev/null +++ b/contrib/verify-commits/pre-push-hook.sh @@ -0,0 +1,16 @@ +#!/bin/bash +if ! [[ "$2" =~ [git@]?[www.]?github.com[:|/]bitcoin/bitcoin[.git]? ]]; then + exit 0 +fi + +while read LINE; do + set -- A $LINE + if [ "$4" != "refs/heads/master" ]; then + continue + fi + if ! ./contrib/verify-commits/verify-commits.sh $3 > /dev/null 2>&1; then + echo "ERROR: A commit is not signed, can't push" + ./contrib/verify-commits/verify-commits.sh + exit 1 + fi +done < /dev/stdin diff --git a/contrib/verify-commits/trusted-git-root b/contrib/verify-commits/trusted-git-root new file mode 100644 index 0000000..eb13f87 --- /dev/null +++ b/contrib/verify-commits/trusted-git-root @@ -0,0 +1 @@ +053038e5ba116cb319fb85f3cb3e062cf1b3df15 diff --git a/contrib/verify-commits/trusted-keys b/contrib/verify-commits/trusted-keys new file mode 100644 index 0000000..658ad03 --- /dev/null +++ b/contrib/verify-commits/trusted-keys @@ -0,0 +1,5 @@ +71A3B16735405025D447E8F274810B012346C9A6 +1F4410F6A89268CE3197A84C57896D2FF8F0B657 +01CDF4627A3B88AAE4A571C87588242FBE38D3A8 +AF8BE07C7049F3A26B239D5325B3083201782B2F +81291FA67D2C379A006A053FEAB5AF94D9E9ABE7 diff --git a/contrib/verify-commits/verify-commits.sh b/contrib/verify-commits/verify-commits.sh new file mode 100755 index 0000000..5841fa2 --- /dev/null +++ b/contrib/verify-commits/verify-commits.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +DIR=$(dirname "$0") + +echo "Please verify all commits in the following list are not evil:" +git log "$DIR" + +VERIFIED_ROOT=$(cat "${DIR}/trusted-git-root") + +HAVE_FAILED=false +IS_SIGNED () { + if [ $1 = $VERIFIED_ROOT ]; then + return 0; + fi + if ! git -c "gpg.program=${DIR}/gpg.sh" verify-commit $1 > /dev/null 2>&1; then + return 1; + fi + local PARENTS=$(git show -s --format=format:%P $1) + for PARENT in $PARENTS; do + if IS_SIGNED $PARENT > /dev/null; then + return 0; + fi + done + if ! "$HAVE_FAILED"; then + echo "No parent of $1 was signed with a trusted key!" > /dev/stderr + echo "Parents are:" > /dev/stderr + for PARENT in $PARENTS; do + git show -s $PARENT > /dev/stderr + done + HAVE_FAILED=true + fi + return 1; +} + +if [ x"$1" = "x" ]; then + TEST_COMMIT="HEAD" +else + TEST_COMMIT="$1" +fi + +IS_SIGNED "$TEST_COMMIT" +RES=$? +if [ "$RES" = 1 ]; then + if ! "$HAVE_FAILED"; then + echo "$TEST_COMMIT was not signed with a trusted key!" + fi +else + echo "There is a valid path from $TEST_COMMIT to $VERIFIED_ROOT where all commits are signed!" +fi + +exit $RES From 3be7a42fe95f79e03c513163d0411c3aa3c9a074 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 22 Oct 2015 19:50:01 -0700 Subject: [PATCH 02/11] Fix pre-push-hook regexes --- contrib/verify-commits/pre-push-hook.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/verify-commits/pre-push-hook.sh b/contrib/verify-commits/pre-push-hook.sh index 607c0ca..c572228 100755 --- a/contrib/verify-commits/pre-push-hook.sh +++ b/contrib/verify-commits/pre-push-hook.sh @@ -1,5 +1,5 @@ #!/bin/bash -if ! [[ "$2" =~ [git@]?[www.]?github.com[:|/]bitcoin/bitcoin[.git]? ]]; then +if ! [[ "$2" =~ ^(git@)?(www.)?github.com(:|/)bitcoin/bitcoin(.git)?$ ]]; then exit 0 fi From e1873f6578968fd1cdf821dc5cbc97b8bc90a3eb Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Tue, 15 Mar 2016 12:51:48 +0100 Subject: [PATCH 03/11] Adjust verify-commits to gitian-builder --- contrib/README.md | 4 ++-- contrib/verify-commits/pre-push-hook.sh | 2 +- contrib/verify-commits/trusted-git-root | 2 +- contrib/verify-commits/trusted-keys | 6 +----- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/contrib/README.md b/contrib/README.md index da1fc06..b313986 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -3,7 +3,7 @@ Repository Tools ### [Developer tools](/contrib/devtools) ### Specific tools for developers working on this repository. -Contains the script `github-merge.sh` for merging github pull requests securely and signing them using GPG. +Contains the script `github-merge.py` for merging github pull requests securely and signing them using GPG. ### [Verify-Commits](/contrib/verify-commits) ### -Tool to verify that every merge commit was signed by a developer using the above `github-merge.sh` script. +Tool to verify that every merge commit was signed by a developer using the above `github-merge.py` script. diff --git a/contrib/verify-commits/pre-push-hook.sh b/contrib/verify-commits/pre-push-hook.sh index c572228..56a894f 100755 --- a/contrib/verify-commits/pre-push-hook.sh +++ b/contrib/verify-commits/pre-push-hook.sh @@ -1,5 +1,5 @@ #!/bin/bash -if ! [[ "$2" =~ ^(git@)?(www.)?github.com(:|/)bitcoin/bitcoin(.git)?$ ]]; then +if ! [[ "$2" =~ ^(git@)?(www.)?github.com(:|/)devrandom/gitian-builder(.git)?$ ]]; then exit 0 fi diff --git a/contrib/verify-commits/trusted-git-root b/contrib/verify-commits/trusted-git-root index eb13f87..8048b8f 100644 --- a/contrib/verify-commits/trusted-git-root +++ b/contrib/verify-commits/trusted-git-root @@ -1 +1 @@ -053038e5ba116cb319fb85f3cb3e062cf1b3df15 +bb4f92f6cbde6ee78e39ae35b0934da3b55e154d diff --git a/contrib/verify-commits/trusted-keys b/contrib/verify-commits/trusted-keys index 658ad03..d3e500e 100644 --- a/contrib/verify-commits/trusted-keys +++ b/contrib/verify-commits/trusted-keys @@ -1,5 +1 @@ -71A3B16735405025D447E8F274810B012346C9A6 -1F4410F6A89268CE3197A84C57896D2FF8F0B657 -01CDF4627A3B88AAE4A571C87588242FBE38D3A8 -AF8BE07C7049F3A26B239D5325B3083201782B2F -81291FA67D2C379A006A053FEAB5AF94D9E9ABE7 +498FA3769A88C4AD1B187A7428EB4B0FB7AAF6B0 From fe83a6df80960642c9533adcf959a9ac4d878817 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 23 Oct 2015 02:05:42 -0700 Subject: [PATCH 04/11] Allow to whitelist commits signed with a revoked key (cherry picked from commit 1d94b72019e31066b33947af5709383b8075e43a) --- contrib/verify-commits/allow-revsig-commits | 0 contrib/verify-commits/gpg.sh | 22 +++++++++++++++++++-- contrib/verify-commits/verify-commits.sh | 12 +++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 contrib/verify-commits/allow-revsig-commits diff --git a/contrib/verify-commits/allow-revsig-commits b/contrib/verify-commits/allow-revsig-commits new file mode 100644 index 0000000..e69de29 diff --git a/contrib/verify-commits/gpg.sh b/contrib/verify-commits/gpg.sh index 6b5137e..0218b82 100755 --- a/contrib/verify-commits/gpg.sh +++ b/contrib/verify-commits/gpg.sh @@ -1,15 +1,33 @@ #!/bin/sh INPUT=$(/dev/null); do - case "$LINE" in "[GNUPG:] VALIDSIG"*) + case "$LINE" in + "[GNUPG:] VALIDSIG "*) while read KEY; do case "$LINE" in "[GNUPG:] VALIDSIG $KEY "*) VALID=true;; esac done < ./contrib/verify-commits/trusted-keys + ;; + "[GNUPG:] REVKEYSIG "*) + [ "$BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG" != 1 ] && exit 1 + while read KEY; do + case "$LINE" in "[GNUPG:] REVKEYSIG ${KEY:24:40} "*) + REVSIG=true + GOODREVSIG="[GNUPG:] GOODSIG ${KEY:24:40} " + ;; + esac + done < ./contrib/verify-commits/trusted-keys + ;; esac done if ! $VALID; then exit 1 fi -echo "$INPUT" | gpg --trust-model always "$@" 2>/dev/null +if $VALID && $REVSIG; then + echo "$INPUT" | gpg --trust-model always "$@" | grep "\[GNUPG:\] \(NEWSIG\|SIG_ID\|VALIDSIG\)" 2>/dev/null + echo "$GOODREVSIG" +else + echo "$INPUT" | gpg --trust-model always "$@" 2>/dev/null +fi diff --git a/contrib/verify-commits/verify-commits.sh b/contrib/verify-commits/verify-commits.sh index 5841fa2..9ba7810 100755 --- a/contrib/verify-commits/verify-commits.sh +++ b/contrib/verify-commits/verify-commits.sh @@ -7,11 +7,23 @@ git log "$DIR" VERIFIED_ROOT=$(cat "${DIR}/trusted-git-root") +IS_REVSIG_ALLOWED () { + while read LINE; do + [ "$LINE" = "$1" ] && return 0 + done < "${DIR}/allow-revsig-commits" + return 1 +} + HAVE_FAILED=false IS_SIGNED () { if [ $1 = $VERIFIED_ROOT ]; then return 0; fi + if IS_REVSIG_ALLOWED "$1"; then + export BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG=1 + else + export BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG=0 + fi if ! git -c "gpg.program=${DIR}/gpg.sh" verify-commit $1 > /dev/null 2>&1; then return 1; fi From acb55ddd0115406070c2582649cafeb5b289df7b Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 20 May 2016 18:29:15 -0700 Subject: [PATCH 05/11] Make verify-commits POSIX-compliant --- contrib/verify-commits/gpg.sh | 10 +++++----- contrib/verify-commits/verify-commits.sh | 15 ++++++--------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/contrib/verify-commits/gpg.sh b/contrib/verify-commits/gpg.sh index 0218b82..375d711 100755 --- a/contrib/verify-commits/gpg.sh +++ b/contrib/verify-commits/gpg.sh @@ -1,8 +1,9 @@ #!/bin/sh -INPUT=$(/dev/null); do case "$LINE" in "[GNUPG:] VALIDSIG "*) @@ -13,10 +14,9 @@ for LINE in $(echo "$INPUT" | gpg --trust-model always "$@" 2>/dev/null); do "[GNUPG:] REVKEYSIG "*) [ "$BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG" != 1 ] && exit 1 while read KEY; do - case "$LINE" in "[GNUPG:] REVKEYSIG ${KEY:24:40} "*) + case "$LINE" in "[GNUPG:] REVKEYSIG ${KEY#????????????????????????} "*) REVSIG=true - GOODREVSIG="[GNUPG:] GOODSIG ${KEY:24:40} " - ;; + GOODREVSIG="[GNUPG:] GOODSIG ${KEY#????????????????????????} " esac done < ./contrib/verify-commits/trusted-keys ;; diff --git a/contrib/verify-commits/verify-commits.sh b/contrib/verify-commits/verify-commits.sh index 9ba7810..c9d2b96 100755 --- a/contrib/verify-commits/verify-commits.sh +++ b/contrib/verify-commits/verify-commits.sh @@ -1,4 +1,6 @@ #!/bin/sh +# Not technically POSIX-compliant due to use of "local", but almost every +# shell anyone uses today supports it, so its probably fine DIR=$(dirname "$0") @@ -6,20 +8,14 @@ echo "Please verify all commits in the following list are not evil:" git log "$DIR" VERIFIED_ROOT=$(cat "${DIR}/trusted-git-root") - -IS_REVSIG_ALLOWED () { - while read LINE; do - [ "$LINE" = "$1" ] && return 0 - done < "${DIR}/allow-revsig-commits" - return 1 -} +REVSIG_ALLOWED=$(cat "${DIR}/allow-revsig-commits") HAVE_FAILED=false IS_SIGNED () { if [ $1 = $VERIFIED_ROOT ]; then return 0; fi - if IS_REVSIG_ALLOWED "$1"; then + if [ "${REVSIG_ALLOWED#*$1}" != "$REVSIG_ALLOWED" ]; then export BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG=1 else export BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG=0 @@ -27,7 +23,8 @@ IS_SIGNED () { if ! git -c "gpg.program=${DIR}/gpg.sh" verify-commit $1 > /dev/null 2>&1; then return 1; fi - local PARENTS=$(git show -s --format=format:%P $1) + local PARENTS + PARENTS=$(git show -s --format=format:%P $1) for PARENT in $PARENTS; do if IS_SIGNED $PARENT > /dev/null; then return 0; From befe6a53682913c9cef406e948e4d16bd3bdb77d Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sat, 21 May 2016 01:44:29 -0700 Subject: [PATCH 06/11] Make verify-commits path-independent --- contrib/verify-commits/verify-commits.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/verify-commits/verify-commits.sh b/contrib/verify-commits/verify-commits.sh index c9d2b96..a859e67 100755 --- a/contrib/verify-commits/verify-commits.sh +++ b/contrib/verify-commits/verify-commits.sh @@ -3,6 +3,7 @@ # shell anyone uses today supports it, so its probably fine DIR=$(dirname "$0") +[ "/${DIR#/}" != "$DIR" ] && DIR=$(dirname "$(pwd)/$0") echo "Please verify all commits in the following list are not evil:" git log "$DIR" From c2d1d6225e140caec051813b3d5c80d34e7afac6 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Fri, 18 Mar 2016 06:04:08 -0400 Subject: [PATCH 07/11] Remove pointless warning Any attacker who managed to make an evil commit that changed something in the contrib/verify-commits/ directory could just as easily remove the warning and/or modify it to not display the evil commits; telling the user to check those commits specifically misleads them into checking just those commits rather than the script itself. --- contrib/verify-commits/verify-commits.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/contrib/verify-commits/verify-commits.sh b/contrib/verify-commits/verify-commits.sh index a859e67..5219331 100755 --- a/contrib/verify-commits/verify-commits.sh +++ b/contrib/verify-commits/verify-commits.sh @@ -5,9 +5,6 @@ DIR=$(dirname "$0") [ "/${DIR#/}" != "$DIR" ] && DIR=$(dirname "$(pwd)/$0") -echo "Please verify all commits in the following list are not evil:" -git log "$DIR" - VERIFIED_ROOT=$(cat "${DIR}/trusted-git-root") REVSIG_ALLOWED=$(cat "${DIR}/allow-revsig-commits") From 2862e189b057e507f8782cb9f4375005cdd88f27 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Thu, 9 Jun 2016 13:55:12 -0400 Subject: [PATCH 08/11] Add README for verify-commits --- contrib/verify-commits/README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 contrib/verify-commits/README.md diff --git a/contrib/verify-commits/README.md b/contrib/verify-commits/README.md new file mode 100644 index 0000000..e9e3f65 --- /dev/null +++ b/contrib/verify-commits/README.md @@ -0,0 +1,26 @@ +Tooling for verification of PGP signed commits +---------------------------------------------- + +This is an incomplete work in progress, but currently includes a pre-push hook +script (`pre-push-hook.sh`) for maintainers to ensure that their own commits +are PGP signed (nearly always merge commits), as well as a script to verify +commits against a trusted keys list. + + +Using verify-commits.sh safely +------------------------------ + +Remember that you can't use an untrusted script to verify itself. This means +that checking out code, then running `verify-commits.sh` against `HEAD` is +_not_ safe, because the version of `verify-commits.sh` that you just ran could +be backdoored. Instead, you need to use a trusted version of verify-commits +prior to checkout to make sure you're checking out only code signed by trusted +keys: + + git fetch origin && \ + ./contrib/verify-commits/verify-commits.sh origin/master && \ + git checkout origin/master + +Note that the above isn't a good UI/UX yet, and needs significant improvements +to make it more convenient and reduce the chance of errors; pull-reqs +improving this process would be much appreciated. From 31f53d8bb8658bf11b20acd7b4f0f3bf4ce8a263 Mon Sep 17 00:00:00 2001 From: isle2983 Date: Sun, 11 Sep 2016 13:36:22 -0600 Subject: [PATCH 09/11] [copyright] add MIT license headers to .sh scripts where missing Years are set according to 'git log' history --- contrib/verify-commits/gpg.sh | 4 ++++ contrib/verify-commits/pre-push-hook.sh | 4 ++++ contrib/verify-commits/verify-commits.sh | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/contrib/verify-commits/gpg.sh b/contrib/verify-commits/gpg.sh index 375d711..09ff237 100755 --- a/contrib/verify-commits/gpg.sh +++ b/contrib/verify-commits/gpg.sh @@ -1,4 +1,8 @@ #!/bin/sh +# Copyright (c) 2014-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + INPUT=$(cat /dev/stdin) VALID=false REVSIG=false diff --git a/contrib/verify-commits/pre-push-hook.sh b/contrib/verify-commits/pre-push-hook.sh index 56a894f..5cd449d 100755 --- a/contrib/verify-commits/pre-push-hook.sh +++ b/contrib/verify-commits/pre-push-hook.sh @@ -1,4 +1,8 @@ #!/bin/bash +# Copyright (c) 2014-2015 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + if ! [[ "$2" =~ ^(git@)?(www.)?github.com(:|/)devrandom/gitian-builder(.git)?$ ]]; then exit 0 fi diff --git a/contrib/verify-commits/verify-commits.sh b/contrib/verify-commits/verify-commits.sh index 5219331..cfe4f11 100755 --- a/contrib/verify-commits/verify-commits.sh +++ b/contrib/verify-commits/verify-commits.sh @@ -1,4 +1,8 @@ #!/bin/sh +# Copyright (c) 2014-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + # Not technically POSIX-compliant due to use of "local", but almost every # shell anyone uses today supports it, so its probably fine From 7d0542cbc487908bab451f5d59535ef8b8093150 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 23 Mar 2016 11:55:46 +0100 Subject: [PATCH 10/11] devtools: make github-merge.py use py3 This makes github-merge.py the first developer tool to go all Python 3 (for context see #7717). The changes are straightforward as the script already was `from __future__ import division,print_function,unicode_literals`. However urllib2 changed name, and json will only accept unicode data not bytes. This retains py2 compatibility for now: not strictly necessary as it's not used by the build system - but it was easy. --- contrib/devtools/github-merge.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py index c8dcaae..9a62fcc 100755 --- a/contrib/devtools/github-merge.py +++ b/contrib/devtools/github-merge.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # Copyright (c) 2016 Bitcoin Core Developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -19,6 +19,11 @@ import os,sys from sys import stdin,stdout,stderr import argparse import subprocess +import json,codecs +try: + from urllib.request import Request,urlopen +except: + from urllib2 import Request,urlopen # External tools (can be overridden using environment) GIT = os.getenv('GIT','git') @@ -38,7 +43,7 @@ def git_config_get(option, default=None): Get named configuration option from git repository. ''' try: - return subprocess.check_output([GIT,'config','--get',option]).rstrip() + return subprocess.check_output([GIT,'config','--get',option]).rstrip().decode('utf-8') except subprocess.CalledProcessError as e: return default @@ -47,18 +52,19 @@ def retrieve_pr_title(repo,pull): Retrieve pull request title from github. Return None if no title can be found, or an error happens. ''' - import urllib2,json try: - req = urllib2.Request("https://api.github.com/repos/"+repo+"/pulls/"+pull) - result = urllib2.urlopen(req) - result = json.load(result) - return result['title'] + req = Request("https://api.github.com/repos/"+repo+"/pulls/"+pull) + result = urlopen(req) + reader = codecs.getreader('utf-8') + obj = json.load(reader(result)) + return obj['title'] except Exception as e: print('Warning: unable to retrieve pull title from github: %s' % e) return None def ask_prompt(text): print(text,end=" ",file=stderr) + stderr.flush() reply = stdin.readline().rstrip() print("",file=stderr) return reply From e1a57442fd1b8b0a90be6b31f875fd9244b1af0a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 1 Apr 2016 14:35:57 +0200 Subject: [PATCH 11/11] devtools: Auto-set branch to merge to in github-merge As we are already using the API to retrieve the pull request title, also retrieve the base branch. This makes sure that pull requests for 0.12 automatically end up in 0.12, and pull requests for master automatically end up in master, and so on. It is still possible to override the branch from the command line or using the `githubmerge.branch` git option. --- contrib/devtools/github-merge.py | 34 +++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py index 9a62fcc..f82362f 100755 --- a/contrib/devtools/github-merge.py +++ b/contrib/devtools/github-merge.py @@ -47,9 +47,9 @@ def git_config_get(option, default=None): except subprocess.CalledProcessError as e: return default -def retrieve_pr_title(repo,pull): +def retrieve_pr_info(repo,pull): ''' - Retrieve pull request title from github. + Retrieve pull request information from github. Return None if no title can be found, or an error happens. ''' try: @@ -57,9 +57,9 @@ def retrieve_pr_title(repo,pull): result = urlopen(req) reader = codecs.getreader('utf-8') obj = json.load(reader(result)) - return obj['title'] + return obj except Exception as e: - print('Warning: unable to retrieve pull title from github: %s' % e) + print('Warning: unable to retrieve pull information from github: %s' % e) return None def ask_prompt(text): @@ -69,13 +69,13 @@ def ask_prompt(text): print("",file=stderr) return reply -def parse_arguments(branch): +def parse_arguments(): epilog = ''' In addition, you can set the following git configuration variables: githubmerge.repository (mandatory), user.signingkey (mandatory), githubmerge.host (default: git@github.com), - githubmerge.branch (default: master), + githubmerge.branch (no default), githubmerge.testcmd (default: none). ''' parser = argparse.ArgumentParser(description='Utility to merge, sign and push github pull requests', @@ -83,14 +83,14 @@ def parse_arguments(branch): parser.add_argument('pull', metavar='PULL', type=int, nargs=1, help='Pull request ID to merge') parser.add_argument('branch', metavar='BRANCH', type=str, nargs='?', - default=branch, help='Branch to merge against (default: '+branch+')') + default=None, help='Branch to merge against (default: githubmerge.branch setting, or base branch for pull, or \'master\')') return parser.parse_args() def main(): # Extract settings from git repo repo = git_config_get('githubmerge.repository') host = git_config_get('githubmerge.host','git@github.com') - branch = git_config_get('githubmerge.branch','master') + opt_branch = git_config_get('githubmerge.branch',None) testcmd = git_config_get('githubmerge.testcmd') signingkey = git_config_get('user.signingkey') if repo is None: @@ -105,9 +105,20 @@ def main(): host_repo = host+":"+repo # shortcut for push/pull target # Extract settings from command line - args = parse_arguments(branch) + args = parse_arguments() pull = str(args.pull[0]) - branch = args.branch + + # Receive pull information from github + info = retrieve_pr_info(repo,pull) + if info is None: + exit(1) + title = info['title'] + # precedence order for destination branch argument: + # - command line argument + # - githubmerge.branch setting + # - base branch for pull (as retrieved from github) + # - 'master' + branch = args.branch or opt_branch or info['base']['ref'] or 'master' # Initialize source branches head_branch = 'pull/'+pull+'/head' @@ -147,7 +158,6 @@ def main(): try: # Create unsigned merge commit. - title = retrieve_pr_title(repo,pull) if title: firstline = 'Merge #%s: %s' % (pull,title) else: @@ -165,7 +175,7 @@ def main(): print("ERROR: Creating merge failed (already merged?).",file=stderr) exit(4) - print('%s#%s%s %s' % (ATTR_RESET+ATTR_PR,pull,ATTR_RESET,title)) + print('%s#%s%s %s %sinto %s%s' % (ATTR_RESET+ATTR_PR,pull,ATTR_RESET,title,ATTR_RESET+ATTR_PR,branch,ATTR_RESET)) subprocess.check_call([GIT,'log','--graph','--topo-order','--pretty=format:'+COMMIT_FORMAT,base_branch+'..'+head_branch]) print() # Run test command if configured.