You are here

commit-code-check.sh in Drupal 9

#!/bin/bash
#
# This script performs code quality checks.
#
# @internal
#   This script is not covered by Drupal core's backwards compatibility promise.
#   It exists only for core development purposes.
#
# The script makes the following checks:
# - Spell checking.
# - File modes.
# - No changes to core/node_modules directory.
# - PHPCS checks PHP and YAML files.
# - ESLint checks JavaScript and YAML files.
# - Checks .es6.js and .js files are equivalent.
# - Stylelint checks CSS files.
# - Checks .pcss.css and .css files are equivalent.

# cSpell:disable

# Searches an array.
contains_element() {
  local e
  for e in ${@:2}; do [[ "$e" == "$1" ]] && return 0; done
  return 1
}

CACHED=0
DRUPALCI=0
BRANCH=""
while test $# -gt 0; do
  case "$1" in
    -h|--help)
      echo "Drupal code quality checks"
      echo " "
      echo "options:"
      echo "-h, --help                show brief help"
      echo "--branch BRANCH           creates list of files to check by comparing against a branch"
      echo "--cached                  checks staged files"
      echo "--drupalci                a special mode for DrupalCI"
      echo " "
      echo "Example usage: sh ./core/scripts/dev/commit-code-check.sh --branch 9.2.x"
      exit 0
      ;;
    --branch)
      BRANCH="$2"
      if [[ "$BRANCH" == "" ]]; then
        printf "The --branch option requires a value. For example: --branch 9.2.x\n"
        exit;
      fi
      shift 2
      ;;
    --cached)
      CACHED=1
      shift
      ;;
    --drupalci)
      DRUPALCI=1
      shift
      ;;
    *)
      break
      ;;
  esac
done

# Set up variables to make colored output simple. Color output is disabled on
# DrupalCI because it is breaks reporting.
# @todo https://www.drupal.org/project/drupalci_testbot/issues/3181869
if [[ "$DRUPALCI" == "1" ]]; then
  red=""
  green=""
  reset=""
  DRUPAL_VERSION=$(php -r "include 'vendor/autoload.php'; print preg_replace('#\.[0-9]+-dev#', '.x', \Drupal::VERSION);")
else
  red=$(tput setaf 1 && tput bold)
  green=$(tput setaf 2)
  reset=$(tput sgr0)
fi

# Gets list of files to check.
if [[ "$BRANCH" != "" ]]; then
  FILES=$(git diff --name-only $BRANCH HEAD);
elif [[ "$CACHED" == "0" ]]; then
  # For DrupalCI patch testing or when running without --cached or --branch,
  # list of all changes in the working directory.
  FILES=$(git ls-files --other --modified --exclude-standard --exclude=vendor)
else
  # Check staged files only.
  if git rev-parse --verify HEAD >/dev/null 2>&1
  then
    AGAINST=HEAD
  else
    # Initial commit: diff against an empty tree object
    AGAINST=4b825dc642cb6eb9a060e54bf8d69288fbee4904
  fi
  FILES=$(git diff --cached --name-only $AGAINST);
fi

if [[ "$FILES" == "" ]] && [[ "$DRUPALCI" == "1" ]]; then
  # If the FILES is empty we might be testing a merge request on DrupalCI. We
  # need to diff against the Drupal branch or tag related to the Drupal version.
  printf "Creating list of files to check by comparing branch to %s\n" "$DRUPAL_VERSION"
  # On DrupalCI there's a merge commit so we can compare to HEAD~1.
  FILES=$(git diff --name-only HEAD~1 HEAD);
fi

TOP_LEVEL=$(git rev-parse --show-toplevel)

# This variable will be set to one when the file core/phpcs.xml.dist is changed.
PHPCS_XML_DIST_FILE_CHANGED=0

# Build up a list of absolute file names.
ABS_FILES=
for FILE in $FILES; do
  ABS_FILES="$ABS_FILES $TOP_LEVEL/$FILE"

  if [[ $FILE == "core/phpcs.xml.dist" ]]; then
    PHPCS_XML_DIST_FILE_CHANGED=1;
  fi;
done

# Exit early if there are no files.
if [[ "$ABS_FILES" == "" ]]; then
  printf "There are no files to check. If you have staged a commit use the --cached option.\n"
  exit;
fi;

# This script assumes that composer install and yarn install have already been
# run and all dependencies are updated.
FINAL_STATUS=0

DEPENDENCIES_NEED_INSTALLING=0
# Ensure PHP development dependencies are installed.
# @todo https://github.com/composer/composer/issues/4497 Improve this to
#  determine if dependencies in the lock file match the installed versions.
#  Using composer install --dry-run is not valid because it would depend on
#  user-facing strings in Composer.
if ! [[ -f 'vendor/bin/phpcs' ]]; then
  printf "Drupal's PHP development dependencies are not installed. Run 'composer install' from the root directory.\n"
  DEPENDENCIES_NEED_INSTALLING=1;
fi

cd "$TOP_LEVEL/core"

# Ensure JavaScript development dependencies are installed.
yarn check -s 2>/dev/null
if [ "$?" -ne "0" ]; then
  printf "Drupal's JavaScript development dependencies are not installed. Run 'yarn install' inside the core directory.\n"
  DEPENDENCIES_NEED_INSTALLING=1;
fi

if [ $DEPENDENCIES_NEED_INSTALLING -ne 0 ]; then
  exit 1;
fi

# Check all files for spelling in one go for better performance.
yarn run -s spellcheck -c $TOP_LEVEL/core/.cspell.json $ABS_FILES
if [ "$?" -ne "0" ]; then
  # If there are failures set the status to a number other than 0.
  FINAL_STATUS=1
  printf "\nCSpell: ${red}failed${reset}\n"
else
  printf "\nCSpell: ${green}passed${reset}\n"
fi
cd "$TOP_LEVEL"

# Add a separator line to make the output easier to read.
printf "\n"
printf -- '-%.0s' {1..100}
printf "\n"

# When the file core/phpcs.xml.dist has been changed, then PHPCS must check all files.
if [[ $PHPCS_XML_DIST_FILE_CHANGED == "1" ]]; then
  # Test all files with phpcs rules.
  vendor/bin/phpcs -ps --runtime-set installed_paths "$TOP_LEVEL/vendor/drupal/coder/coder_sniffer" --standard="$TOP_LEVEL/core/phpcs.xml.dist"
  PHPCS=$?
  if [ "$PHPCS" -ne "0" ]; then
    # If there are failures set the status to a number other than 0.
    FINAL_STATUS=1
    printf "\nPHPCS: ${red}failed${reset}\n"
  else
    printf "\nPHPCS: ${green}passed${reset}\n"
  fi
fi

for FILE in $FILES; do
  STATUS=0;
  # Print a line to separate spellcheck output from per file output.
  printf "Checking %s\n" "$FILE"
  printf "\n"

  # Ensure the file still exists (i.e. is not being deleted).
  if [ -a $FILE ]; then
    if [ ${FILE: -3} != ".sh" ]; then
      # Ensure the file has the correct mode.
      STAT="$(stat -f "%A" $FILE 2>/dev/null)"
      if [ $? -ne 0 ]; then
        STAT="$(stat -c "%a" $FILE 2>/dev/null)"
      fi
      if [ "$STAT" -ne "644" ]; then
        printf "${red}check failed:${reset} file $FILE should be 644 not $STAT\n"
        STATUS=1
      fi
    fi
  fi

  # Don't commit changes to vendor.
  if [[ "$FILE" =~ ^vendor/ ]]; then
    printf "${red}check failed:${reset} file in vendor directory being committed ($FILE)\n"
    STATUS=1
  fi

  # Don't commit changes to core/node_modules.
  if [[ "$FILE" =~ ^core/node_modules/ ]]; then
    printf "${red}check failed:${reset} file in core/node_modules directory being committed ($FILE)\n"
    STATUS=1
  fi

  ############################################################################
  ### PHP AND YAML FILES
  ############################################################################
  if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.(inc|install|module|php|profile|test|theme|yml)$ ]] && [[ $PHPCS_XML_DIST_FILE_CHANGED == "0" ]]; then
    # Test files with phpcs rules.
    vendor/bin/phpcs "$TOP_LEVEL/$FILE" --runtime-set installed_paths "$TOP_LEVEL/vendor/drupal/coder/coder_sniffer" --standard="$TOP_LEVEL/core/phpcs.xml.dist"
    PHPCS=$?
    if [ "$PHPCS" -ne "0" ]; then
      # If there are failures set the status to a number other than 0.
      STATUS=1
    else
      printf "PHPCS: $FILE ${green}passed${reset}\n"
    fi
  fi

  ############################################################################
  ### YAML FILES
  ############################################################################
  if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.yml$ ]]; then
    # Test files with ESLint.
    cd "$TOP_LEVEL/core"
    node ./node_modules/eslint/bin/eslint.js --quiet --resolve-plugins-relative-to . "$TOP_LEVEL/$FILE"
    YAMLLINT=$?
    if [ "$YAMLLINT" -ne "0" ]; then
      # If there are failures set the status to a number other than 0.
      STATUS=1
    else
      printf "ESLint: $FILE ${green}passed${reset}\n"
    fi
    cd $TOP_LEVEL
  fi

  ############################################################################
  ### JAVASCRIPT FILES
  ############################################################################
  if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]] && [[ ! $FILE =~ ^core/tests/Drupal/Nightwatch ]] && [[ ! $FILE =~ ^core/assets/vendor/jquery.ui/ui ]]; then
    # Work out the root name of the JavaScript so we can ensure that the ES6
    # version has been compiled correctly.
    if [[ $FILE =~ \.es6\.js$ ]]; then
      BASENAME=${FILE%.es6.js}
      COMPILE_CHECK=1
    else
      BASENAME=${FILE%.js}
      # We only need to compile check if the .es6.js file is not also
      # changing. This is because the compile check will occur for the
      # .es6.js file. This might occur if the compile scripts have changed.
      contains_element "$BASENAME.es6.js" "${FILES[@]}"
      HASES6=$?
      if [ "$HASES6" -ne "0" ]; then
        COMPILE_CHECK=1
      else
        COMPILE_CHECK=0
      fi
    fi
    if [[ "$COMPILE_CHECK" == "1" ]] && [[ -f "$TOP_LEVEL/$BASENAME.es6.js" ]]; then
      cd "$TOP_LEVEL/core"
      yarn run build:js --check --file "$TOP_LEVEL/$BASENAME.es6.js"
      CORRECTJS=$?
      if [ "$CORRECTJS" -ne "0" ]; then
        # No need to write any output the yarn run command will do this for
        # us.
        STATUS=1
      fi
      # Check the coding standards.
      if [[ -f ".eslintrc.passing.json" ]]; then
        node ./node_modules/eslint/bin/eslint.js --quiet --config=.eslintrc.passing.json "$TOP_LEVEL/$BASENAME.es6.js"
        CORRECTJS=$?
        if [ "$CORRECTJS" -ne "0" ]; then
          # No need to write any output the node command will do this for us.
          STATUS=1
        fi
      fi
      cd $TOP_LEVEL
    else
      # If there is no .es6.js file then there should be unless the .js is
      # not really Drupal's.
      if ! [[ "$FILE" =~ ^core/assets/vendor ]] && ! [[ "$FILE" =~ ^core/scripts/js ]] && ! [[ "$FILE" =~ ^core/scripts/css ]] && ! [[ "$FILE" =~ core/postcss.config.js ]] && ! [[ -f "$TOP_LEVEL/$BASENAME.es6.js" ]]; then
        printf "${red}FAILURE${reset} $FILE does not have a corresponding $BASENAME.es6.js\n"
        STATUS=1
      fi
    fi
  elif [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]] && [[ $FILE =~ ^core/assets/vendor/jquery.ui/ui ]]; then
    ## Check for minified file changes.
    if [[ $FILE =~ -min\.js$ ]]; then
      BASENAME=${FILE%-min.js}
      contains_element "$BASENAME.js" "${FILES[@]}"
      HASSRC=$?
      if [ "$HASSRC" -ne "0" ]; then
        COMPILE_CHECK=1
      else
        ## Source was also changed and will be checked.
        COMPILE_CHECK=0
      fi
    else
      ## Check for source changes.
      BASENAME=${FILE%.js}
      COMPILE_CHECK=1
    fi
    if [[ "$COMPILE_CHECK" == "1" ]] && [[ -f "$TOP_LEVEL/$BASENAME.js" ]]; then
      cd "$TOP_LEVEL/core"
      yarn run build:jqueryui --check --file "$TOP_LEVEL/$BASENAME.js"
      CORRECTJS=$?
      if [ "$CORRECTJS" -ne "0" ]; then
        # The yarn run command will write any error output.
        STATUS=1
      fi
      cd $TOP_LEVEL
    else
      # If there is no .js source file
      if ! [[ -f "$TOP_LEVEL/$BASENAME.js" ]]; then
        printf "${red}FAILURE${reset} $FILE does not have a corresponding $BASENAME.js\n"
        STATUS=1
      fi
    fi
  else
    # Check coding standards of Nightwatch files.
    if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]]; then
      cd "$TOP_LEVEL/core"
      # Check the coding standards.
      if [[ -f ".eslintrc.passing.json" ]]; then
        node ./node_modules/eslint/bin/eslint.js --quiet --config=.eslintrc.passing.json "$TOP_LEVEL/$FILE"
        CORRECTJS=$?
        if [ "$CORRECTJS" -ne "0" ]; then
          # No need to write any output the node command will do this for us.
          STATUS=1
        fi
      fi
      cd $TOP_LEVEL
    fi
  fi

  ############################################################################
  ### CSS FILES
  ############################################################################
  if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.css$ ]]; then
    # Work out the root name of the CSS so we can ensure that the PostCSS
    # version has been compiled correctly.
    if [[ $FILE =~ \.pcss\.css$ ]]; then
      BASENAME=${FILE%.pcss.css}
      COMPILE_CHECK=1
    else
      BASENAME=${FILE%.css}
      # We only need to compile check if the .pcss.css file is not also
      # changing. This is because the compile check will occur for the
      # .pcss.css file. This might occur if the compiled stylesheets have
      # changed.
      contains_element "$BASENAME.pcss.css" "${FILES[@]}"
      HASPOSTCSS=$?
      if [ "$HASPOSTCSS" -ne "0" ]; then
        COMPILE_CHECK=1
      else
        COMPILE_CHECK=0
      fi
    fi
    # PostCSS
    if [[ "$COMPILE_CHECK" == "1" ]] && [[ -f "$TOP_LEVEL/$BASENAME.pcss.css" ]]; then
      cd "$TOP_LEVEL/core"
      yarn run build:css --check --file "$TOP_LEVEL/$BASENAME.pcss.css"
      CORRECTCSS=$?
      if [ "$CORRECTCSS" -ne "0" ]; then
        # No need to write any output the yarn run command will do this for
        # us.
        STATUS=1
      fi
      cd $TOP_LEVEL
    fi
  fi
  if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.css$ ]] && [[ -f "core/node_modules/.bin/stylelint" ]]; then
    BASENAME=${FILE%.css}
    # We only need to use stylelint on the .pcss.css file. So if this CSS file
    # has a corresponding .pcss don't do stylelint.
    if [[ $FILE =~ \.pcss\.css$ ]] || [[ ! -f "$TOP_LEVEL/$BASENAME.pcss.css" ]]; then
      cd "$TOP_LEVEL/core"
      node_modules/.bin/stylelint "$TOP_LEVEL/$FILE"
      if [ "$?" -ne "0" ]; then
        STATUS=1
      else
        printf "STYLELINT: $FILE ${green}passed${reset}\n"
      fi
      cd $TOP_LEVEL
    fi
  fi

  if [[ "$STATUS" == "1" ]]; then
    FINAL_STATUS=1
    # There is no need to print a failure message. The fail will be described
    # already.
  else
    printf "%s ${green}passed${reset}\n" "$FILE"
  fi

  # Print a line to separate each file's checks.
  printf "\n"
  printf -- '-%.0s' {1..100}
  printf "\n"
done

if [[ "$FINAL_STATUS" == "1" ]] && [[ "$DRUPALCI" == "1" ]]; then
  printf "${red}Drupal code quality checks failed.${reset}\n"
  printf "To reproduce this output locally:\n"
  printf "* Apply the change as a patch\n"
  printf "* Run this command locally: sh ./core/scripts/dev/commit-code-check.sh\n"
  printf "OR:\n"
  printf "* From the merge request branch\n"
  printf "* Run this command locally: sh ./core/scripts/dev/commit-code-check.sh --branch %s\n" "$DRUPAL_VERSION"
fi
exit $FINAL_STATUS

File

core/scripts/dev/commit-code-check.sh
View source
  1. #!/bin/bash
  2. #
  3. # This script performs code quality checks.
  4. #
  5. # @internal
  6. # This script is not covered by Drupal core's backwards compatibility promise.
  7. # It exists only for core development purposes.
  8. #
  9. # The script makes the following checks:
  10. # - Spell checking.
  11. # - File modes.
  12. # - No changes to core/node_modules directory.
  13. # - PHPCS checks PHP and YAML files.
  14. # - ESLint checks JavaScript and YAML files.
  15. # - Checks .es6.js and .js files are equivalent.
  16. # - Stylelint checks CSS files.
  17. # - Checks .pcss.css and .css files are equivalent.
  18. # cSpell:disable
  19. # Searches an array.
  20. contains_element() {
  21. local e
  22. for e in ${@:2}; do [[ "$e" == "$1" ]] && return 0; done
  23. return 1
  24. }
  25. CACHED=0
  26. DRUPALCI=0
  27. BRANCH=""
  28. while test $# -gt 0; do
  29. case "$1" in
  30. -h|--help)
  31. echo "Drupal code quality checks"
  32. echo " "
  33. echo "options:"
  34. echo "-h, --help show brief help"
  35. echo "--branch BRANCH creates list of files to check by comparing against a branch"
  36. echo "--cached checks staged files"
  37. echo "--drupalci a special mode for DrupalCI"
  38. echo " "
  39. echo "Example usage: sh ./core/scripts/dev/commit-code-check.sh --branch 9.2.x"
  40. exit 0
  41. ;;
  42. --branch)
  43. BRANCH="$2"
  44. if [[ "$BRANCH" == "" ]]; then
  45. printf "The --branch option requires a value. For example: --branch 9.2.x\n"
  46. exit;
  47. fi
  48. shift 2
  49. ;;
  50. --cached)
  51. CACHED=1
  52. shift
  53. ;;
  54. --drupalci)
  55. DRUPALCI=1
  56. shift
  57. ;;
  58. *)
  59. break
  60. ;;
  61. esac
  62. done
  63. # Set up variables to make colored output simple. Color output is disabled on
  64. # DrupalCI because it is breaks reporting.
  65. # @todo https://www.drupal.org/project/drupalci_testbot/issues/3181869
  66. if [[ "$DRUPALCI" == "1" ]]; then
  67. red=""
  68. green=""
  69. reset=""
  70. DRUPAL_VERSION=$(php -r "include 'vendor/autoload.php'; print preg_replace('#\.[0-9]+-dev#', '.x', \Drupal::VERSION);")
  71. else
  72. red=$(tput setaf 1 && tput bold)
  73. green=$(tput setaf 2)
  74. reset=$(tput sgr0)
  75. fi
  76. # Gets list of files to check.
  77. if [[ "$BRANCH" != "" ]]; then
  78. FILES=$(git diff --name-only $BRANCH HEAD);
  79. elif [[ "$CACHED" == "0" ]]; then
  80. # For DrupalCI patch testing or when running without --cached or --branch,
  81. # list of all changes in the working directory.
  82. FILES=$(git ls-files --other --modified --exclude-standard --exclude=vendor)
  83. else
  84. # Check staged files only.
  85. if git rev-parse --verify HEAD >/dev/null 2>&1
  86. then
  87. AGAINST=HEAD
  88. else
  89. # Initial commit: diff against an empty tree object
  90. AGAINST=4b825dc642cb6eb9a060e54bf8d69288fbee4904
  91. fi
  92. FILES=$(git diff --cached --name-only $AGAINST);
  93. fi
  94. if [[ "$FILES" == "" ]] && [[ "$DRUPALCI" == "1" ]]; then
  95. # If the FILES is empty we might be testing a merge request on DrupalCI. We
  96. # need to diff against the Drupal branch or tag related to the Drupal version.
  97. printf "Creating list of files to check by comparing branch to %s\n" "$DRUPAL_VERSION"
  98. # On DrupalCI there's a merge commit so we can compare to HEAD~1.
  99. FILES=$(git diff --name-only HEAD~1 HEAD);
  100. fi
  101. TOP_LEVEL=$(git rev-parse --show-toplevel)
  102. # This variable will be set to one when the file core/phpcs.xml.dist is changed.
  103. PHPCS_XML_DIST_FILE_CHANGED=0
  104. # Build up a list of absolute file names.
  105. ABS_FILES=
  106. for FILE in $FILES; do
  107. ABS_FILES="$ABS_FILES $TOP_LEVEL/$FILE"
  108. if [[ $FILE == "core/phpcs.xml.dist" ]]; then
  109. PHPCS_XML_DIST_FILE_CHANGED=1;
  110. fi;
  111. done
  112. # Exit early if there are no files.
  113. if [[ "$ABS_FILES" == "" ]]; then
  114. printf "There are no files to check. If you have staged a commit use the --cached option.\n"
  115. exit;
  116. fi;
  117. # This script assumes that composer install and yarn install have already been
  118. # run and all dependencies are updated.
  119. FINAL_STATUS=0
  120. DEPENDENCIES_NEED_INSTALLING=0
  121. # Ensure PHP development dependencies are installed.
  122. # @todo https://github.com/composer/composer/issues/4497 Improve this to
  123. # determine if dependencies in the lock file match the installed versions.
  124. # Using composer install --dry-run is not valid because it would depend on
  125. # user-facing strings in Composer.
  126. if ! [[ -f 'vendor/bin/phpcs' ]]; then
  127. printf "Drupal's PHP development dependencies are not installed. Run 'composer install' from the root directory.\n"
  128. DEPENDENCIES_NEED_INSTALLING=1;
  129. fi
  130. cd "$TOP_LEVEL/core"
  131. # Ensure JavaScript development dependencies are installed.
  132. yarn check -s 2>/dev/null
  133. if [ "$?" -ne "0" ]; then
  134. printf "Drupal's JavaScript development dependencies are not installed. Run 'yarn install' inside the core directory.\n"
  135. DEPENDENCIES_NEED_INSTALLING=1;
  136. fi
  137. if [ $DEPENDENCIES_NEED_INSTALLING -ne 0 ]; then
  138. exit 1;
  139. fi
  140. # Check all files for spelling in one go for better performance.
  141. yarn run -s spellcheck -c $TOP_LEVEL/core/.cspell.json $ABS_FILES
  142. if [ "$?" -ne "0" ]; then
  143. # If there are failures set the status to a number other than 0.
  144. FINAL_STATUS=1
  145. printf "\nCSpell: ${red}failed${reset}\n"
  146. else
  147. printf "\nCSpell: ${green}passed${reset}\n"
  148. fi
  149. cd "$TOP_LEVEL"
  150. # Add a separator line to make the output easier to read.
  151. printf "\n"
  152. printf -- '-%.0s' {1..100}
  153. printf "\n"
  154. # When the file core/phpcs.xml.dist has been changed, then PHPCS must check all files.
  155. if [[ $PHPCS_XML_DIST_FILE_CHANGED == "1" ]]; then
  156. # Test all files with phpcs rules.
  157. vendor/bin/phpcs -ps --runtime-set installed_paths "$TOP_LEVEL/vendor/drupal/coder/coder_sniffer" --standard="$TOP_LEVEL/core/phpcs.xml.dist"
  158. PHPCS=$?
  159. if [ "$PHPCS" -ne "0" ]; then
  160. # If there are failures set the status to a number other than 0.
  161. FINAL_STATUS=1
  162. printf "\nPHPCS: ${red}failed${reset}\n"
  163. else
  164. printf "\nPHPCS: ${green}passed${reset}\n"
  165. fi
  166. fi
  167. for FILE in $FILES; do
  168. STATUS=0;
  169. # Print a line to separate spellcheck output from per file output.
  170. printf "Checking %s\n" "$FILE"
  171. printf "\n"
  172. # Ensure the file still exists (i.e. is not being deleted).
  173. if [ -a $FILE ]; then
  174. if [ ${FILE: -3} != ".sh" ]; then
  175. # Ensure the file has the correct mode.
  176. STAT="$(stat -f "%A" $FILE 2>/dev/null)"
  177. if [ $? -ne 0 ]; then
  178. STAT="$(stat -c "%a" $FILE 2>/dev/null)"
  179. fi
  180. if [ "$STAT" -ne "644" ]; then
  181. printf "${red}check failed:${reset} file $FILE should be 644 not $STAT\n"
  182. STATUS=1
  183. fi
  184. fi
  185. fi
  186. # Don't commit changes to vendor.
  187. if [[ "$FILE" =~ ^vendor/ ]]; then
  188. printf "${red}check failed:${reset} file in vendor directory being committed ($FILE)\n"
  189. STATUS=1
  190. fi
  191. # Don't commit changes to core/node_modules.
  192. if [[ "$FILE" =~ ^core/node_modules/ ]]; then
  193. printf "${red}check failed:${reset} file in core/node_modules directory being committed ($FILE)\n"
  194. STATUS=1
  195. fi
  196. ############################################################################
  197. ### PHP AND YAML FILES
  198. ############################################################################
  199. if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.(inc|install|module|php|profile|test|theme|yml)$ ]] && [[ $PHPCS_XML_DIST_FILE_CHANGED == "0" ]]; then
  200. # Test files with phpcs rules.
  201. vendor/bin/phpcs "$TOP_LEVEL/$FILE" --runtime-set installed_paths "$TOP_LEVEL/vendor/drupal/coder/coder_sniffer" --standard="$TOP_LEVEL/core/phpcs.xml.dist"
  202. PHPCS=$?
  203. if [ "$PHPCS" -ne "0" ]; then
  204. # If there are failures set the status to a number other than 0.
  205. STATUS=1
  206. else
  207. printf "PHPCS: $FILE ${green}passed${reset}\n"
  208. fi
  209. fi
  210. ############################################################################
  211. ### YAML FILES
  212. ############################################################################
  213. if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.yml$ ]]; then
  214. # Test files with ESLint.
  215. cd "$TOP_LEVEL/core"
  216. node ./node_modules/eslint/bin/eslint.js --quiet --resolve-plugins-relative-to . "$TOP_LEVEL/$FILE"
  217. YAMLLINT=$?
  218. if [ "$YAMLLINT" -ne "0" ]; then
  219. # If there are failures set the status to a number other than 0.
  220. STATUS=1
  221. else
  222. printf "ESLint: $FILE ${green}passed${reset}\n"
  223. fi
  224. cd $TOP_LEVEL
  225. fi
  226. ############################################################################
  227. ### JAVASCRIPT FILES
  228. ############################################################################
  229. if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]] && [[ ! $FILE =~ ^core/tests/Drupal/Nightwatch ]] && [[ ! $FILE =~ ^core/assets/vendor/jquery.ui/ui ]]; then
  230. # Work out the root name of the JavaScript so we can ensure that the ES6
  231. # version has been compiled correctly.
  232. if [[ $FILE =~ \.es6\.js$ ]]; then
  233. BASENAME=${FILE%.es6.js}
  234. COMPILE_CHECK=1
  235. else
  236. BASENAME=${FILE%.js}
  237. # We only need to compile check if the .es6.js file is not also
  238. # changing. This is because the compile check will occur for the
  239. # .es6.js file. This might occur if the compile scripts have changed.
  240. contains_element "$BASENAME.es6.js" "${FILES[@]}"
  241. HASES6=$?
  242. if [ "$HASES6" -ne "0" ]; then
  243. COMPILE_CHECK=1
  244. else
  245. COMPILE_CHECK=0
  246. fi
  247. fi
  248. if [[ "$COMPILE_CHECK" == "1" ]] && [[ -f "$TOP_LEVEL/$BASENAME.es6.js" ]]; then
  249. cd "$TOP_LEVEL/core"
  250. yarn run build:js --check --file "$TOP_LEVEL/$BASENAME.es6.js"
  251. CORRECTJS=$?
  252. if [ "$CORRECTJS" -ne "0" ]; then
  253. # No need to write any output the yarn run command will do this for
  254. # us.
  255. STATUS=1
  256. fi
  257. # Check the coding standards.
  258. if [[ -f ".eslintrc.passing.json" ]]; then
  259. node ./node_modules/eslint/bin/eslint.js --quiet --config=.eslintrc.passing.json "$TOP_LEVEL/$BASENAME.es6.js"
  260. CORRECTJS=$?
  261. if [ "$CORRECTJS" -ne "0" ]; then
  262. # No need to write any output the node command will do this for us.
  263. STATUS=1
  264. fi
  265. fi
  266. cd $TOP_LEVEL
  267. else
  268. # If there is no .es6.js file then there should be unless the .js is
  269. # not really Drupal's.
  270. if ! [[ "$FILE" =~ ^core/assets/vendor ]] && ! [[ "$FILE" =~ ^core/scripts/js ]] && ! [[ "$FILE" =~ ^core/scripts/css ]] && ! [[ "$FILE" =~ core/postcss.config.js ]] && ! [[ -f "$TOP_LEVEL/$BASENAME.es6.js" ]]; then
  271. printf "${red}FAILURE${reset} $FILE does not have a corresponding $BASENAME.es6.js\n"
  272. STATUS=1
  273. fi
  274. fi
  275. elif [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]] && [[ $FILE =~ ^core/assets/vendor/jquery.ui/ui ]]; then
  276. ## Check for minified file changes.
  277. if [[ $FILE =~ -min\.js$ ]]; then
  278. BASENAME=${FILE%-min.js}
  279. contains_element "$BASENAME.js" "${FILES[@]}"
  280. HASSRC=$?
  281. if [ "$HASSRC" -ne "0" ]; then
  282. COMPILE_CHECK=1
  283. else
  284. ## Source was also changed and will be checked.
  285. COMPILE_CHECK=0
  286. fi
  287. else
  288. ## Check for source changes.
  289. BASENAME=${FILE%.js}
  290. COMPILE_CHECK=1
  291. fi
  292. if [[ "$COMPILE_CHECK" == "1" ]] && [[ -f "$TOP_LEVEL/$BASENAME.js" ]]; then
  293. cd "$TOP_LEVEL/core"
  294. yarn run build:jqueryui --check --file "$TOP_LEVEL/$BASENAME.js"
  295. CORRECTJS=$?
  296. if [ "$CORRECTJS" -ne "0" ]; then
  297. # The yarn run command will write any error output.
  298. STATUS=1
  299. fi
  300. cd $TOP_LEVEL
  301. else
  302. # If there is no .js source file
  303. if ! [[ -f "$TOP_LEVEL/$BASENAME.js" ]]; then
  304. printf "${red}FAILURE${reset} $FILE does not have a corresponding $BASENAME.js\n"
  305. STATUS=1
  306. fi
  307. fi
  308. else
  309. # Check coding standards of Nightwatch files.
  310. if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]]; then
  311. cd "$TOP_LEVEL/core"
  312. # Check the coding standards.
  313. if [[ -f ".eslintrc.passing.json" ]]; then
  314. node ./node_modules/eslint/bin/eslint.js --quiet --config=.eslintrc.passing.json "$TOP_LEVEL/$FILE"
  315. CORRECTJS=$?
  316. if [ "$CORRECTJS" -ne "0" ]; then
  317. # No need to write any output the node command will do this for us.
  318. STATUS=1
  319. fi
  320. fi
  321. cd $TOP_LEVEL
  322. fi
  323. fi
  324. ############################################################################
  325. ### CSS FILES
  326. ############################################################################
  327. if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.css$ ]]; then
  328. # Work out the root name of the CSS so we can ensure that the PostCSS
  329. # version has been compiled correctly.
  330. if [[ $FILE =~ \.pcss\.css$ ]]; then
  331. BASENAME=${FILE%.pcss.css}
  332. COMPILE_CHECK=1
  333. else
  334. BASENAME=${FILE%.css}
  335. # We only need to compile check if the .pcss.css file is not also
  336. # changing. This is because the compile check will occur for the
  337. # .pcss.css file. This might occur if the compiled stylesheets have
  338. # changed.
  339. contains_element "$BASENAME.pcss.css" "${FILES[@]}"
  340. HASPOSTCSS=$?
  341. if [ "$HASPOSTCSS" -ne "0" ]; then
  342. COMPILE_CHECK=1
  343. else
  344. COMPILE_CHECK=0
  345. fi
  346. fi
  347. # PostCSS
  348. if [[ "$COMPILE_CHECK" == "1" ]] && [[ -f "$TOP_LEVEL/$BASENAME.pcss.css" ]]; then
  349. cd "$TOP_LEVEL/core"
  350. yarn run build:css --check --file "$TOP_LEVEL/$BASENAME.pcss.css"
  351. CORRECTCSS=$?
  352. if [ "$CORRECTCSS" -ne "0" ]; then
  353. # No need to write any output the yarn run command will do this for
  354. # us.
  355. STATUS=1
  356. fi
  357. cd $TOP_LEVEL
  358. fi
  359. fi
  360. if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.css$ ]] && [[ -f "core/node_modules/.bin/stylelint" ]]; then
  361. BASENAME=${FILE%.css}
  362. # We only need to use stylelint on the .pcss.css file. So if this CSS file
  363. # has a corresponding .pcss don't do stylelint.
  364. if [[ $FILE =~ \.pcss\.css$ ]] || [[ ! -f "$TOP_LEVEL/$BASENAME.pcss.css" ]]; then
  365. cd "$TOP_LEVEL/core"
  366. node_modules/.bin/stylelint "$TOP_LEVEL/$FILE"
  367. if [ "$?" -ne "0" ]; then
  368. STATUS=1
  369. else
  370. printf "STYLELINT: $FILE ${green}passed${reset}\n"
  371. fi
  372. cd $TOP_LEVEL
  373. fi
  374. fi
  375. if [[ "$STATUS" == "1" ]]; then
  376. FINAL_STATUS=1
  377. # There is no need to print a failure message. The fail will be described
  378. # already.
  379. else
  380. printf "%s ${green}passed${reset}\n" "$FILE"
  381. fi
  382. # Print a line to separate each file's checks.
  383. printf "\n"
  384. printf -- '-%.0s' {1..100}
  385. printf "\n"
  386. done
  387. if [[ "$FINAL_STATUS" == "1" ]] && [[ "$DRUPALCI" == "1" ]]; then
  388. printf "${red}Drupal code quality checks failed.${reset}\n"
  389. printf "To reproduce this output locally:\n"
  390. printf "* Apply the change as a patch\n"
  391. printf "* Run this command locally: sh ./core/scripts/dev/commit-code-check.sh\n"
  392. printf "OR:\n"
  393. printf "* From the merge request branch\n"
  394. printf "* Run this command locally: sh ./core/scripts/dev/commit-code-check.sh --branch %s\n" "$DRUPAL_VERSION"
  395. fi
  396. exit $FINAL_STATUS