check.sh 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #!/bin/bash
  2. # Copyright 2019 Google
  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. # Checks that the current state of the tree is sane and optionally auto-fixes
  16. # errors automatically. Meant for interactive use.
  17. function usage() {
  18. cat <<EOF
  19. USAGE: scripts/check.sh [--allow-dirty] [--commit] [<revision>]
  20. Runs auto-formatting scripts, source-tree checks, and linters on any files that
  21. have changed since origin/master.
  22. By default, any changes are left as uncommited changes in the working tree. You
  23. can review them with git diff. Pass --commit to automatically commit any changes.
  24. Pass an alternate revision to use as the basis for checking changes.
  25. OPTIONS:
  26. --allow-dirty
  27. By default, check.sh requires a clean working tree to keep any generated
  28. changes separate from logical changes.
  29. --commit
  30. Commit any auto-generated changes with a message indicating which tool made
  31. the changes.
  32. --amend
  33. Commit any auto-generated changes by amending the HEAD commit.
  34. --fixup
  35. Commit any auto-generated changes with a fixup! message for the HEAD
  36. commit. The next rebase will squash these fixup commits.
  37. --test-only
  38. Run all checks without making any changes to local files.
  39. <revision>
  40. Specifies a starting revision other than the default of origin/master.
  41. EXAMPLES:
  42. check.sh
  43. Runs automated checks and formatters on all changed files since
  44. origin/master. Check for changes with git diff.
  45. check.sh --commit
  46. Runs automated checks and formatters on all changed files since
  47. origin/master and commits the results.
  48. check.sh --amend HEAD
  49. Runs automated checks and formatters on all changed files since the last
  50. commit and amends the last commit with the difference.
  51. check.sh --allow-dirty HEAD
  52. Runs automated checks and formatters on all changed files since the last
  53. commit and intermingles the changes with any pending changes. Useful for
  54. interactive use from an editor.
  55. EOF
  56. }
  57. set -euo pipefail
  58. unset CDPATH
  59. # Change to the top-directory of the working tree
  60. top_dir=$(git rev-parse --show-toplevel)
  61. cd "${top_dir}"
  62. ALLOW_DIRTY=false
  63. COMMIT_METHOD="none"
  64. CHECK_DIFF=true
  65. START_REVISION="origin/master"
  66. TEST_ONLY=false
  67. VERBOSE=false
  68. # Default to verbose operation if this isn't an interactive build.
  69. if [[ ! -t 1 ]]; then
  70. VERBOSE=true
  71. fi
  72. # When travis clones a repo for building, it uses a shallow clone. After the
  73. # first commit on a non-master branch, TRAVIS_COMMIT_RANGE is not set, master
  74. # is not available and we need to compute the START_REVISION from the common
  75. # ancestor of $TRAVIS_COMMIT and origin/master.
  76. if [[ -n "${TRAVIS_COMMIT_RANGE:-}" ]] ; then
  77. CHECK_DIFF=true
  78. START_REVISION="$TRAVIS_COMMIT_RANGE"
  79. elif [[ -n "${TRAVIS_COMMIT:-}" ]] ; then
  80. if ! git rev-parse origin/master >& /dev/null; then
  81. git remote set-branches --add origin master
  82. git fetch origin
  83. fi
  84. CHECK_DIFF=true
  85. START_REVISION=$(git merge-base origin/master "${TRAVIS_COMMIT}")
  86. fi
  87. while [[ $# -gt 0 ]]; do
  88. case "$1" in
  89. --)
  90. # Do nothing: explicitly allow this, but ignore it
  91. ;;
  92. -h | --help)
  93. usage
  94. exit 1
  95. ;;
  96. --allow-dirty)
  97. ALLOW_DIRTY=true
  98. ;;
  99. --amend)
  100. COMMIT_METHOD=amend
  101. ;;
  102. --fixup)
  103. COMMIT_METHOD=fixup
  104. ;;
  105. --commit)
  106. COMMIT_METHOD=message
  107. ;;
  108. --verbose)
  109. VERBOSE=true
  110. ;;
  111. --test-only)
  112. # In test-only mode, no changes are made, so there's no reason to
  113. # require a clean source tree.
  114. ALLOW_DIRTY=true
  115. TEST_ONLY=true
  116. ;;
  117. *)
  118. START_REVISION="$1"
  119. shift
  120. break
  121. ;;
  122. esac
  123. shift
  124. done
  125. if [[ "${TEST_ONLY}" == true && "${COMMIT_METHOD}" != "none" ]]; then
  126. echo "--test-only cannot be combined with --amend, --fixup, or --commit"
  127. exit 1
  128. fi
  129. if [[ "${ALLOW_DIRTY}" == true && "${COMMIT_METHOD}" == "message" ]]; then
  130. echo "--allow-dirty and --commit are mutually exclusive"
  131. exit 1
  132. fi
  133. if ! git diff-index --quiet HEAD --; then
  134. if [[ "${ALLOW_DIRTY}" != true ]]; then
  135. echo "You have local changes that could be overwritten by this script."
  136. echo "Please commit your changes first or pass --allow-dirty."
  137. exit 2
  138. fi
  139. fi
  140. # Show Travis-related environment variables, to help with debuging failures.
  141. if [[ "${VERBOSE}" == true ]]; then
  142. env | egrep '^TRAVIS_(BRANCH|COMMIT|PULL|REPO)' | sort || true
  143. fi
  144. if [[ "${START_REVISION}" == *..* ]]; then
  145. RANGE_START="${START_REVISION/..*/}"
  146. RANGE_END="${START_REVISION/*../}"
  147. # Figure out if we have access to master. If not add it to the repo.
  148. if ! git rev-parse origin/master >& /dev/null; then
  149. git remote set-branches --add origin master
  150. git fetch origin
  151. fi
  152. # Try to come up with a more accurate representation of the merge, so that
  153. # checks will operate on just the differences the PR would merge into master.
  154. # The start of the revision range that Travis supplies can sometimes be a
  155. # seemingly random value.
  156. NEW_RANGE_START=$(git merge-base origin/master "${RANGE_END}" || echo "")
  157. if [[ -n "$NEW_RANGE_START" ]]; then
  158. START_REVISION="${NEW_RANGE_START}..${RANGE_END}"
  159. START_SHA="${START_REVISION}"
  160. else
  161. # In the shallow clone that Travis has created there's no merge base
  162. # between the PR and master. In this case just fall back on checking
  163. # everything.
  164. echo "Unable to detect base commit for change detection."
  165. echo "Falling back on just checking everything."
  166. CHECK_DIFF=false
  167. START_REVISION="origin/master"
  168. START_SHA="origin/master"
  169. fi
  170. else
  171. START_SHA=$(git rev-parse "${START_REVISION}")
  172. fi
  173. if [[ "${VERBOSE}" == true ]]; then
  174. echo "START_REVISION=$START_REVISION"
  175. echo "START_SHA=$START_SHA"
  176. fi
  177. # If committing --fixup, avoid messages with fixup! fixup! that might come from
  178. # multiple fixup commits.
  179. HEAD_SHA=$(git rev-parse HEAD)
  180. function maybe_commit() {
  181. local message="$1"
  182. if [[ "${COMMIT_METHOD}" == "none" ]]; then
  183. return
  184. fi
  185. echo "${message}"
  186. case "${COMMIT_METHOD}" in
  187. amend)
  188. git commit -a --amend -C "${HEAD_SHA}"
  189. ;;
  190. fixup)
  191. git commit -a --fixup="${HEAD_SHA}"
  192. ;;
  193. message)
  194. git commit -a -m "${message}"
  195. ;;
  196. *)
  197. echo "Unknown commit method ${COMMIT_METHOD}" 1>&2
  198. exit 2
  199. ;;
  200. esac
  201. }
  202. style_cmd=("${top_dir}/scripts/style.sh")
  203. if [[ "${TEST_ONLY}" == true ]]; then
  204. style_cmd+=(test-only)
  205. fi
  206. if [[ "$CHECK_DIFF" == true ]]; then
  207. style_cmd+=("${START_SHA}")
  208. fi
  209. # Restyle and commit any changes
  210. "${style_cmd[@]}"
  211. if ! git diff --quiet; then
  212. maybe_commit "style.sh generated changes"
  213. fi
  214. # If there are changes to the Firestore project, ensure they're ordered
  215. # correctly to minimize conflicts.
  216. if [ -z "${GITHUB_WORKFLOW-}" ]; then
  217. if [[ "$CHECK_DIFF" == "false" ]] || \
  218. ! git diff --quiet "${START_SHA}" -- Firestore; then
  219. sync_project_cmd=("${top_dir}/scripts/sync_project.rb")
  220. if [[ "${TEST_ONLY}" == true ]]; then
  221. sync_project_cmd+=(--test-only)
  222. fi
  223. "${sync_project_cmd[@]}"
  224. if ! git diff --quiet; then
  225. maybe_commit "sync_project.rb generated changes"
  226. fi
  227. fi
  228. fi
  229. # Check lint errors.
  230. "${top_dir}/scripts/check_whitespace.sh"
  231. "${top_dir}/scripts/check_filename_spaces.sh"
  232. "${top_dir}/scripts/check_copyright.sh"
  233. "${top_dir}/scripts/check_no_module_imports.sh"
  234. "${top_dir}/scripts/check_test_inclusion.py"
  235. "${top_dir}/scripts/check_imports.swift"
  236. # Google C++ style
  237. lint_cmd=("${top_dir}/scripts/check_lint.py")
  238. if [[ "$CHECK_DIFF" == true ]]; then
  239. lint_cmd+=("${START_SHA}")
  240. fi
  241. "${lint_cmd[@]}"