summary refs log tree commit diff
path: root/scripts-dev/lint.sh
blob: 1c0e6582f684c1b62f81980f0099bc160c2fa40a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/env bash
#
# Runs linting scripts over the local Synapse checkout
# black - opinionated code formatter
# ruff - lints and finds mistakes

set -e

usage() {
  echo
  echo "Usage: $0 [-h] [-d] [paths...]"
  echo
  echo "-d"
  echo "  Lint files that have changed since the last git commit."
  echo
  echo "  If paths are provided and this option is set, both provided paths and those"
  echo "  that have changed since the last commit will be linted."
  echo
  echo "  If no paths are provided and this option is not set, all files will be linted."
  echo
  echo "  Note that paths with a file extension that is not '.py' will be excluded."
  echo "-h"
  echo "  Display this help text."
}

USING_DIFF=0
files=()

while getopts ":dh" opt; do
  case $opt in
    d)
      USING_DIFF=1
      ;;
    h)
      usage
      exit
      ;;
    \?)
      echo "ERROR: Invalid option: -$OPTARG" >&2
      usage
      exit
      ;;
  esac
done

# Strip any options from the command line arguments now that
# we've finished processing them
shift "$((OPTIND-1))"

if [ $USING_DIFF -eq 1 ]; then
  # Check both staged and non-staged changes
  for path in $(git diff HEAD --name-only); do
    filename=$(basename "$path")
    file_extension="${filename##*.}"

    # If an extension is present, and it's something other than 'py',
    # then ignore this file
    if [[ -n ${file_extension+x} && $file_extension != "py" ]]; then
      continue
    fi

    # Append this path to our list of files to lint
    files+=("$path")
  done
fi

# Append any remaining arguments as files to lint
files+=("$@")

if [[ $USING_DIFF -eq 1 ]]; then
  # If we were asked to lint changed files, and no paths were found as a result...
  if [ ${#files[@]} -eq 0 ]; then
    # Then print and exit
    echo "No files found to lint."
    exit 0
  fi
else
  # If we were not asked to lint changed files, and no paths were found as a result,
  # then lint everything!
  if [[ -z ${files+x} ]]; then
      # CI runs each linter on the entire checkout, e.g. `black .`. So don't
      # rely on this list to *find* lint targets if that misses a file; instead;
      # use it to exclude files from linters when this can't be done by config.
      #
      # To check which files the linters examine, use:
      #     black --verbose . 2>&1 | \grep -v ignored
      #     isort --show-files .
      #     flake8 --verbose .  # This isn't a great option
      #     mypy has explicit config in mypy.ini; there is also mypy --verbose
      files=(
          "synapse" "docker" "tests"
          "scripts-dev"
          "contrib" "synmark" "stubs" ".ci"
          "dev-docs"
      )
  fi
fi

echo "Linting these paths: ${files[*]}"
echo

# Print out the commands being run
set -x

# Ensure the sort order of imports.
isort "${files[@]}"

# Ensure Python code conforms to an opinionated style.
python3 -m black "${files[@]}"

# Ensure the sample configuration file conforms to style checks.
./scripts-dev/config-lint.sh

# Catch any common programming mistakes in Python code.
# --quiet suppresses the update check.
ruff --quiet --fix "${files[@]}"

# Catch any common programming mistakes in Rust code.
#
# --bins, --examples, --lib, --tests combined explicitly disable checking
# the benchmarks, which can fail due to `#![feature]` macros not being
# allowed on the stable rust toolchain (rustc error E0554).
#
# --allow-staged and --allow-dirty suppress clippy raising errors
# for uncommitted files. Only needed when using --fix.
#
# -D warnings disables the "warnings" lint.
#
# Using --fix has a tendency to cause subsequent runs of clippy to recompile
# rust code, which can slow down this script. Thus we run clippy without --fix
# first which is quick, and then re-run it with --fix if an error was found.
if ! cargo-clippy --bins --examples --lib --tests -- -D warnings > /dev/null 2>&1; then
  cargo-clippy \
    --bins --examples --lib --tests --allow-staged --allow-dirty --fix -- -D warnings
fi

# Ensure the formatting of Rust code.
cargo-fmt

# Ensure all Pydantic models use strict types.
./scripts-dev/check_pydantic_models.py lint

# Ensure type hints are correct.
mypy