From 9ebf44d84754adc5b64fcf612c6816c02c80462d Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Sat, 2 Feb 2019 19:29:23 -0500 Subject: Imported Upstream version 8.9.0 --- dev/tools/merge-pr.sh | 218 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 189 insertions(+), 29 deletions(-) (limited to 'dev/tools/merge-pr.sh') diff --git a/dev/tools/merge-pr.sh b/dev/tools/merge-pr.sh index 9f24960f..320ef6ed 100755 --- a/dev/tools/merge-pr.sh +++ b/dev/tools/merge-pr.sh @@ -1,50 +1,210 @@ #!/usr/bin/env bash set -e +set -o pipefail -# This script depends (at least) on git and jq. -# It should be used like this: dev/tools/merge-pr.sh /PR number/ - -#TODO: check arguments and show usage if relevant - -PR=$1 +API=https://api.github.com/repos/coq/coq +OFFICIAL_REMOTE_GIT_URL="git@github.com:coq/coq" +OFFICIAL_REMOTE_HTTPS_URL="github.com/coq/coq" -CURRENT_LOCAL_BRANCH=$(git rev-parse --abbrev-ref HEAD) -REMOTE=$(git config --get "branch.$CURRENT_LOCAL_BRANCH.remote") -git fetch "$REMOTE" "refs/pull/$PR/head" +# This script depends (at least) on git (>= 2.7) and jq. +# It should be used like this: dev/tools/merge-pr.sh /PR number/ -API=https://api.github.com/repos/coq/coq +# Set SLOW_CONF to have the confirmation output wait for a newline +# E.g. call $ SLOW_CONF= dev/tools/merge-pr.sh /PR number/ +if [ -z ${SLOW_CONF+x} ]; then + QUICK_CONF="-n 1" +else + QUICK_CONF="" +fi -BASE_BRANCH=$(curl -s "$API/pulls/$PR" | jq -r '.base.label') +RED="\033[31m" +RESET="\033[0m" +GREEN="\033[32m" +BLUE="\033[34m" +YELLOW="\033[33m" +info() { + echo -e "${GREEN}info:${RESET} $1 ${RESET}" +} +error() { + echo -e "${RED}error:${RESET} $1 ${RESET}" +} +warning() { + echo -e "${YELLOW}warning:${RESET} $1 ${RESET}" +} -COMMIT=$(git rev-parse FETCH_HEAD) -STATUS=$(curl -s "$API/commits/$COMMIT/status" | jq -r '.state') +check_util() { +if ! command -v "$1" > /dev/null 2>&1; then + error "this script requires the $1 command line utility" + exit 1 +fi +} -if [ "$BASE_BRANCH" != "coq:$CURRENT_LOCAL_BRANCH" ]; then - echo "Wrong base branch" - read -p "Bypass? [y/N] " -n 1 -r +ask_confirmation() { + read -p "Continue anyway? [y/N] " $QUICK_CONF -r echo if [[ ! $REPLY =~ ^[Yy]$ ]] then exit 1 fi +} + +check_util jq +check_util curl +check_util git +check_util gpg + +# command line parsing + +if [ $# != 1 ]; then + error "usage: $0 PR-number" + exit 1 +fi + +if [[ "$1" =~ ^[1-9][0-9]*$ ]]; then + PR=$1 +else + error "$1 is not a number" + exit 1 +fi + +# Fetching PR metadata + +PRDATA=$(curl -s "$API/pulls/$PR") + +TITLE=$(echo "$PRDATA" | jq -r '.title') +info "title for PR $PR is ${BLUE}$TITLE" + +BASE_BRANCH=$(echo "$PRDATA" | jq -r '.base.label') +info "PR $PR targets branch ${BLUE}$BASE_BRANCH" + +CURRENT_LOCAL_BRANCH=$(git rev-parse --abbrev-ref HEAD) +info "you are merging in ${BLUE}$CURRENT_LOCAL_BRANCH" + +REMOTE=$(git config --get "branch.$CURRENT_LOCAL_BRANCH.remote") +if [ -z "$REMOTE" ]; then + error "branch ${BLUE}$CURRENT_LOCAL_BRANCH${RESET} has not associated remote" + error "don't know where to fetch the PR from" + error "please run: git branch --set-upstream-to=THE_REMOTE/$CURRENT_LOCAL_BRANCH" + exit 1 +fi +REMOTE_URL=$(git remote get-url "$REMOTE" --all) +if [ "$REMOTE_URL" != "${OFFICIAL_REMOTE_GIT_URL}" ] && \ + [ "$REMOTE_URL" != "${OFFICIAL_REMOTE_GIT_URL}.git" ] && \ + [ "$REMOTE_URL" != "https://${OFFICIAL_REMOTE_HTTPS_URL}" ] && \ + [ "$REMOTE_URL" != "https://${OFFICIAL_REMOTE_HTTPS_URL}.git" ] && \ + [[ "$REMOTE_URL" != "https://"*"@${OFFICIAL_REMOTE_HTTPS_URL}" ]] && \ + [[ "$REMOTE_URL" != "https://"*"@${OFFICIAL_REMOTE_HTTPS_URL}.git" ]] ; then + error "remote ${BLUE}$REMOTE${RESET} does not point to the official Coq repo" + error "that is ${BLUE}$OFFICIAL_REMOTE_GIT_URL" + error "it points to ${BLUE}$REMOTE_URL${RESET} instead" + ask_confirmation +fi +info "remote for $CURRENT_LOCAL_BRANCH is ${BLUE}$REMOTE" + +info "fetching from $REMOTE the PR" +git remote update "$REMOTE" +if ! git ls-remote "$REMOTE" | grep pull >/dev/null; then + error "remote $REMOTE is not configured to fetch pull requests" + error "run: git config remote.$REMOTE.fetch +refs/pull/*/head:refs/remotes/$REMOTE/pr/*" + exit 1 +fi +git fetch "$REMOTE" "refs/pull/$PR/head" +COMMIT=$(git rev-parse FETCH_HEAD) +info "commit for PR $PR is ${BLUE}$COMMIT" + +# Sanity check: merge to a different branch + +if [ "$BASE_BRANCH" != "coq:$CURRENT_LOCAL_BRANCH" ]; then + error "PR requests merge in ${BLUE}$BASE_BRANCH${RESET} but you are merging in ${BLUE}$CURRENT_LOCAL_BRANCH" + ask_confirmation fi; -if [ "$STATUS" != "success" ]; then - echo "CI status is \"$STATUS\"" - read -p "Bypass? [y/N] " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]] - then - exit 1 - fi +# Sanity check: the local branch is up-to-date with upstream + +LOCAL_BRANCH_COMMIT=$(git rev-parse HEAD) +UPSTREAM_COMMIT=$(git rev-parse @{u}) +if [ "$LOCAL_BRANCH_COMMIT" != "$UPSTREAM_COMMIT" ]; then + + # Is it just that the upstream branch is behind? + # It could just be that we merged other PRs and we didn't push yet + + if git merge-base --is-ancestor -- "$UPSTREAM_COMMIT" "$LOCAL_BRANCH_COMMIT"; then + warning "Your branch is ahead of ${REMOTE}." + warning "You should see this warning only if you've just merged another PR and did not push yet." + ask_confirmation + else + error "Local branch is not up-to-date with ${REMOTE}." + error "Pull before merging." + ask_confirmation + fi +fi + +# Sanity check: PR has an outdated version of CI + +BASE_COMMIT=$(echo "$PRDATA" | jq -r '.base.sha') +CI_FILES=(".travis.yml" ".gitlab-ci.yml" "appveyor.yml") + +if ! git diff --quiet "$BASE_COMMIT" "$LOCAL_BRANCH_COMMIT" -- "${CI_FILES[@]}" +then + warning "This PR didn't run with the latest version of CI." + warning "It is probably a good idea to ask for a rebase." + read -p "Do you want to see the diff? [Y/n] " $QUICK_CONF -r + echo + if [[ ! $REPLY =~ ^[Nn]$ ]] + then + git diff "$BASE_COMMIT" "$LOCAL_BRANCH_COMMIT" -- "${CI_FILES[@]}" + fi + ask_confirmation +fi + +# Sanity check: CI failed + +STATUS=$(curl -s "$API/commits/$COMMIT/status") + +if [ "$(echo "$STATUS" | jq -r '.state')" != "success" ]; then + error "CI unsuccessful on ${BLUE}$(echo "$STATUS" | + jq -r -c '.statuses|map(select(.state != "success"))|map(.context)')" + ask_confirmation fi; -git merge -S --no-ff FETCH_HEAD -m "Merge PR #$PR: $(curl -s "$API/pulls/$PR" | jq -r '.title')" -e +# Sanity check: has labels named "needs:" + +NEEDS_LABELS=$(echo "$PRDATA" | jq -rc '.labels | map(select(.name | match("needs:"))) | map(.name)') +if [ "$NEEDS_LABELS" != "[]" ]; then + error "needs:something labels still present: ${BLUE}$NEEDS_LABELS" + ask_confirmation +fi + +# Sanity check: has milestone + +MILESTONE=$(echo "$PRDATA" | jq -rc '.milestone.title') +if [ "$MILESTONE" = "null" ]; then + error "no milestone set, please set one" + ask_confirmation +fi + +# Sanity check: has kind + +KIND=$(echo "$PRDATA" | jq -rc '.labels | map(select(.name | match("kind:"))) | map(.name)') +if [ "$KIND" = "[]" ]; then + error "no kind:something label set, please set one" + ask_confirmation +fi + +# Sanity check: user.signingkey +if [ -z "$(git config user.signingkey)" ]; then + warning "git config user.signingkey is empty" + warning "gpg will guess a key out of your git config user.* data" +fi + +info "merging" +git merge -v -S --no-ff FETCH_HEAD -m "Merge PR #$PR: $TITLE" -e # TODO: improve this check -if ! git diff --quiet "$REMOTE/$CURRENT_LOCAL_BRANCH" -- dev/ci; then - echo "******************************************" - echo "** WARNING: does this PR have overlays? **" - echo "******************************************" +if ! git diff --quiet "$REMOTE/$CURRENT_LOCAL_BRANCH" -- dev/ci/user-overlays; then + warning "this PR may have overlays (sorry the check is not perfect)" + warning "if it has overlays please check the following:" + warning "- each overlay has a corresponding open PR on the upstream repo" + warning "- after merging please notify the upstream they can merge the PR" fi -- cgit v1.2.3