lint.sh 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. #!/usr/bin/env bash
  2. # fail if any command fails
  3. set -eo pipefail -o nounset
  4. ROOT="$(dirname "$(dirname "$(realpath "$0")")")"
  5. DOCKERFILE="$ROOT/scripts/Dockerfile.lint"
  6. VALID_COMMANDS=("lint" "format")
  7. SELF_ARGS_WITHOUT_DOCKER=""
  8. GOIMPORTS_ARGS=""
  9. GOLANGCI_LINT_ARGS=""
  10. print_help() {
  11. cat <<-EOF >&2
  12. Usage: $(basename "$0") [-h] [-c] [-w] [-d] [-l] COMMAND
  13. COMMAND can be one of: "${VALID_COMMANDS[*]}"
  14. -h Print this help.
  15. -c Run in docker and don't worry about dependencies
  16. -w Automatically fix all formatting issues
  17. -d Print diff for all formatting issues
  18. -l List files that have formatting issues
  19. -g Format output to be parsed by github actions
  20. EOF
  21. }
  22. format(){
  23. if [ "$GOIMPORTS_ARGS" == "" ]; then
  24. GOIMPORTS_ARGS="-l"
  25. fi
  26. # only -l supports output as github action
  27. if [ "$GITHUB_ACTION" == "true" ]; then
  28. GOIMPORTS_ARGS="-l"
  29. fi
  30. # Check for dependencies
  31. if ! command -v goimports >/dev/null 2>&1; then
  32. printf "%s\n" "Require goimports. You can run this command in a docker container instead with '-c' and not worry about it or install it: \n\tgo install golang.org/x/tools/cmd/goimports@latest" >&2
  33. exit 1
  34. fi
  35. # Use -exec because of pitfall #1 in http://mywiki.wooledge.org/BashPitfalls
  36. GOFMT_OUTPUT="$(find "./sdk" "./node" "./wormchain" -type f -name '*.go' -not -path '*.pb.go' -print0 | xargs -r -0 goimports $GOIMPORTS_ARGS 2>&1)"
  37. if [ -n "$GOFMT_OUTPUT" ]; then
  38. if [ "$GITHUB_ACTION" == "true" ]; then
  39. GOFMT_OUTPUT="$(echo "$GOFMT_OUTPUT" | awk '{print "::error file="$0"::Formatting error. Please format using ./scripts/lint.sh -d format."}')"
  40. fi
  41. echo "$GOFMT_OUTPUT" >&2
  42. exit 1
  43. fi
  44. }
  45. lint(){
  46. # === Spell check
  47. if ! command -v cspell >/dev/null 2>&1; then
  48. printf "%s\n" "cspell is not installed. Skipping spellcheck"
  49. else
  50. # NOTE: Keep this command in sync with `.github/workflows/build.yml`
  51. cspell -c cspell.config.yaml --dictionary cspell-custom-words.txt "**.*md"
  52. fi
  53. # === Go linting
  54. # Check for dependencies
  55. if ! command -v golangci-lint >/dev/null 2>&1; then
  56. printf "%s\n" "Require golangci-lint. You can run this command in a docker container instead with '-c' and not worry about it or install it: https://golangci-lint.run/usage/install/"
  57. fi
  58. # Do the actual linting!
  59. cd "$ROOT"/node
  60. golangci-lint run --timeout=10m $GOLANGCI_LINT_ARGS ./...
  61. cd "${ROOT}/sdk"
  62. golangci-lint run --timeout=10m $GOLANGCI_LINT_ARGS ./...
  63. }
  64. DOCKER="false"
  65. GITHUB_ACTION="false"
  66. while getopts 'cwdlgh' opt; do
  67. case "$opt" in
  68. c)
  69. DOCKER="true"
  70. ;;
  71. w)
  72. GOIMPORTS_ARGS+="-w "
  73. SELF_ARGS_WITHOUT_DOCKER+="-w "
  74. ;;
  75. d)
  76. GOIMPORTS_ARGS+="-d "
  77. SELF_ARGS_WITHOUT_DOCKER+="-d "
  78. ;;
  79. l)
  80. GOIMPORTS_ARGS+="-l "
  81. SELF_ARGS_WITHOUT_DOCKER+="-l "
  82. ;;
  83. g)
  84. GITHUB_ACTION="true"
  85. SELF_ARGS_WITHOUT_DOCKER+="-g "
  86. ;;
  87. h)
  88. print_help
  89. exit 0
  90. ;;
  91. ?)
  92. echo "Invalid command option." >&2
  93. print_help
  94. exit 1
  95. ;;
  96. esac
  97. done
  98. shift $((OPTIND - 1))
  99. if [ "$#" -ne "1" ]; then
  100. echo "Need to specify COMMAND." >&2
  101. print_help
  102. exit 1
  103. fi
  104. COMMAND="$1"
  105. if [[ ! " ${VALID_COMMANDS[*]} " == *" $COMMAND "* ]]; then
  106. echo "Invalid command $COMMAND." >&2
  107. print_help
  108. exit 1
  109. fi
  110. # run this script recursively inside docker, if requested
  111. if [ "$DOCKER" == "true" ]; then
  112. # The easy thing to do here would be to use a bind mount to share the code with the container.
  113. # But this doesn't work in scenarios where we are in a container already.
  114. # But it's easy so we just won't support that case for now.
  115. # If we wanted to support it, my idea would be to `docker run`, `docker cp`, `docker exec`, `docker rm`.
  116. if grep -Esq 'docker|lxc|kubepods' /proc/1/cgroup; then
  117. echo "Already running inside a container. This situation isn't supported (yet)." >&2
  118. exit 1
  119. fi
  120. DOCKER_IMAGE="$(docker build -q -f "$DOCKERFILE" .)"
  121. DOCKER_EXEC="./scripts/$(basename "$0")"
  122. MOUNT="--mount=type=bind,target=/app,source=$PWD"
  123. # for safety, mount as readonly unless -w flag was given
  124. if ! [[ "$GOIMPORTS_ARGS" =~ "w" ]]; then
  125. MOUNT+=",readonly"
  126. fi
  127. docker run --workdir /app "$MOUNT" "$DOCKER_IMAGE" "$DOCKER_EXEC" $SELF_ARGS_WITHOUT_DOCKER "$COMMAND"
  128. exit "$?"
  129. fi
  130. case $COMMAND in
  131. "lint")
  132. lint
  133. ;;
  134. "format")
  135. format
  136. ;;
  137. esac