github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/experiment/find-merge-conflicts.sh (about) 1 #!/usr/bin/env bash 2 # Copyright 2017 The Kubernetes Authors. 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS IS" BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 16 # Usage: GITHUB_TOKEN=<api token> find-merge-conflicts.sh <ORG> <REPO> 17 # 18 # This script uses the GitHub API to find all open unmergable PRs (i.e. those 19 # with merge conflicts), then checks out the corresponding git repository and 20 # attempts to merge the PRs, printing conflicts to stdout. 21 # 22 # Status messages are printed to stderr with a hash (#) as prefix. 23 # Each PR is identified to stdout by a message prefixed with an asterisk (*). 24 # This makes it easy to process the results: stdout contains just the PRs and 25 # conflicts, and the PRs can be filtered out with "grep -v '^*'". 26 # 27 # For example, to save conflicts and a summarized tally: 28 # GITHUB_TOKEN=<token> find-merge-conflicts.sh <ORG> <REPO> >conflicts.txt 29 # grep -v '^*' conflicts.txt | sort | uniq -c | sort -rn >summary.txt 30 31 set -o errexit 32 set -o nounset 33 set -o pipefail 34 35 if [[ $# < 2 ]]; then 36 echo "Usage: $0 <ORG> <REPO>" >&2 37 exit 1 38 fi 39 40 if [[ -z "${GITHUB_TOKEN:-}" ]]; then 41 echo "GITHUB_TOKEN must be set." >&2 42 exit 1 43 fi 44 45 readonly ORG=$1 46 readonly REPO=$2 47 readonly GHCURL=(curl -sfSLH "Authorization: token ${GITHUB_TOKEN}") 48 49 echo "# Searching for PRs with conflicts in ${ORG}/${REPO}" >&2 50 conflicting_prs=() 51 52 # This is pretty hacky. 53 page=0 54 while true; do 55 page=$((page + 1)) 56 # Select only PRs that have not been tagged as stale or rotten. 57 open_prs=$("${GHCURL[@]}" "https://api.github.com/repos/${ORG}/${REPO}/pulls?base=master&per_page=100&page=${page}" |\ 58 jq -r '.[] | select(.labels | map(.name != "lifecycle/stale" and .name != "lifecycle/rotten") | all) | .number') 59 [[ -z "${open_prs}" ]] && break 60 for pr in ${open_prs}; do 61 mergeable=$("${GHCURL[@]}" -sfSL "https://api.github.com/repos/${ORG}/${REPO}/pulls/${pr}" | jq -r '.mergeable') 62 if [[ "${mergeable}" == "false" ]]; then 63 conflicting_prs+=("${pr}") 64 fi 65 done 66 done 67 68 if [[ -z "${conflicting_prs[@]:-}" ]]; then 69 echo "# No PRs with conflicts found." >&2 70 exit 71 fi 72 73 echo "# ${#conflicting_prs[@]} conflicting PRs found" >&2 74 75 tmpdir=$(mktemp -d) 76 trap "rm -rf ${tmpdir}" EXIT 77 78 echo "# Cloning git repo ${ORG}/${REPO}" >&2 79 git clone -q "https://github.com/${ORG}/${REPO}" "${tmpdir}" 80 cd "${tmpdir}" 81 git config merge.renameLimit 999999 82 83 echo "# Reporting conflicts" >&2 84 for pr in ${conflicting_prs[@]}; do 85 # Create a branch to attempt the merge 86 git branch -q merge-test master 87 # Checkout that branch 88 git checkout -q merge-test 89 # Fetch the PR head 90 echo "# Fetching refs/pull/${pr}/head" >&2 91 git fetch -q -f origin "refs/pull/${pr}/head" 92 # Merge it 93 git merge --ff FETCH_HEAD --strategy resolve >/dev/null 2>/dev/null || true 94 echo "* ${ORG}/${REPO}/pull/${pr}:" 95 # Report files that are conflicting 96 PAGER=cat git diff --name-only --diff-filter=UXB 97 # Abort so we can reset 98 git merge --abort >/dev/null 2>/dev/null || true 99 git reset -q --hard 100 git checkout -q master 101 git branch -q -D merge-test 102 git prune 2>/dev/null 103 done