packbackbooks/lti-1-3-php-library

View on GitHub
.githooks/pre-commit

Summary

Maintainability
Test Coverage
#!/bin/bash

set -o pipefail

GIT_ROOT=$(git rev-parse --show-toplevel) # ".../lti-1-3-php-library"

# So we avoid the "Not a git repository" error when performing git commands in a subdir
unset GIT_DIR

# Get changed files to be committed, excluding deleted files (since we can't grep them)
CHANGED_FILES=$(git diff --cached --name-only --diff-filter=d)

NO_FORMAT="\e[0m"
F_BOLD="\e[1m"
C_RED="\e[31m"
C_YELLOW="\e[93m"
C_CYAN="\e[36m"
C_LIME="\e[92m"

# Rather than doing `return 1`, we fail fast.
function fail {
  echo -e "${C_RED}${F_BOLD}Pre-commit hook failed! Fix the above errors before committing.${NO_FORMAT}"
  exit 1
}

function file_ends_with_newline {
  file_path=$1

  # NOTE: Empty files technically end with a newline.
    [[ $(wc -l < "$file_path") -eq 0 ]] || [[ $(tail -c1 "$file_path" | wc -l) -gt 0 ]]
}

function is_executable_installed {
  executable_name=$1

  which "$executable_name" >/dev/null
}

# Returns a 0 status code if the given feature is enabled, 1 otherwise.
# Feature names are arbitrarily defined in the optional file `.skipped-checks`
# in order to give more control to developers to-as what gets executed.
function feature_is_enabled {
  feature_name=$1

  # We redirect output so that it doesn't emit warnings if the file doesn't exist.
  ! grep "$feature_name" "$GIT_ROOT/.githooks/.skipped-checks" &> /dev/null
}

function feature_is_disabled {
  feature_name=$1

  ! feature_is_enabled "$feature_name"
}

function skip_if_no_changes {
  if [[ -z "$CHANGED_FILES" ]]; then
    echo "No changes were detected while running the pre-commit hook." && exit 0
  fi
}

function skip_if_merge_in_progress {
  if [ -f ".git/MERGE_HEAD" ]; then
    echo "Detected merge in progress, skipping pre-commit hook." && exit 0
  fi
}

function fail_if_unresolved_merge_conflict {
  # Check the files to prevent merge markers from being committed.
  if echo "$CHANGED_FILES" | xargs --no-run-if-empty egrep '[><]{7}' -H -I --line-number; then
    echo -e "${C_RED}You have merge markers (conflicts) in the above files, lines. Fix them before committing.${NO_FORMAT}" && fail
  fi
}

function lint_eof_newlines {
  if feature_is_disabled "pre-commit-auto-newlines"; then
    return 0
  fi

  text_files=$(echo "$CHANGED_FILES" | grep -E '\.(css|docker|Dockerfile|dockerignore|ejs|env|example|gitignore|html|js|json|php|py|rb|scss|sh|svg|toml|trivyignore|ts|txt|yaml|yml)$')
  for f in $text_files; do
    # Add a linebreak to the file if it doesn't have one
    if ! file_ends_with_newline "$f"; then
      echo >>"$f"
      git add "$f"
    fi
  done
}

function lint_php {
  php_files=$(echo "$CHANGED_FILES" | grep '\.php')
  if [[ -z "$php_files" ]]; then
    return 0 # There's nothing to lint.
  fi

  pint="vendor/bin/pint"

  if ! [ -x "$pint" ]; then
    echo -e "${C_RED}Pint is not installed. Install it with \`composer install\`.${NO_FORMAT}" && return 1
  fi

  php_files_arg=$(echo "$php_files" | tr '\n' ' ')

  echo -e "${C_CYAN}Linting Pint...${NO_FORMAT}"
  $pint || return 1

  git add $php_files_arg
}

echo -e "${NO_FORMAT}${F_BOLD}Running pre-commit hook...${NO_FORMAT}"

skip_if_merge_in_progress
skip_if_no_changes

fail_if_unresolved_merge_conflict

lint_eof_newlines || fail
lint_php || fail

echo -e "${C_LIME}${F_BOLD}Pre-commit hook passed!${NO_FORMAT}"