-
Notifications
You must be signed in to change notification settings - Fork 2
/
script.sh
271 lines (249 loc) · 9.21 KB
/
script.sh
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
#!/bin/bash
: "${CI:=}"
: "${GITHUB_REPOSITORY:=$PWD}"
if [ -n "$CI" ]; then
set -xeu
else
set -eu
fi
if [ -n "$CI" ]; then
# Ignore version control, untracked files and executable scripts.
find . -type f \! -perm 644 \! -path '*/.git/*' \! -path '*/.ruff_cache/*' \! -path '*/.tox/*' \
\! -path '*/__pycache__/*' \! -path '*/cache/*' \! -path '*/node_modules/*' \! -path '*/venv/*' \
\! -path '*/script/*' \! -name '*.sh' \! -name '*-cli' \! -name 'manage.py' \! -name 'run.py' \
-o -type d \! -perm 755 \! -path '*/deploy/cache/*' | grep . && exit 1
fi
REQUIREMENTS_FILE=""
if [ -f requirements.txt ]; then
REQUIREMENTS_FILE=requirements.txt
elif [ -f pyproject.toml ]; then
REQUIREMENTS_FILE=pyproject.toml
fi
IGNORE=(
RUF100 # Specific repositories can have stricter rules in pyproject.toml, with more noqa in files.
# Duplicate
ANN # annotation (mypy)
# Incompatible
# https://docs.astral.sh/ruff/linter/#rule-selection
D203 # one-blank-line-before-class (D211 blank-line-before-class)
D212 # multi-line-summary-first-line (D213 multi-line-summary-second-line)
D415 # ends-in-punctuation (D400 ends-in-period)
# https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
COM812 # missing-trailing-comma (ruff format)
ISC001 # single-line-implicit-string-concatenation (ruff format)
Q000 # bad-quotes-inline-string (ruff format)
# Complexity
C901 # complex-structure
PLR091 # too-many-...
# Irrelevant
EM # flake8-errmsg (nice backtrace)
PERF203 # try-except-in-loop ("Why is this bad?" https://docs.astral.sh/ruff/rules/try-except-in-loop/)
# Project-specific
D
DTZ
PTH
FIX002 # line-contains-todo
PLR2004 # magic-value-comparison
PLW2901 # redefined-loop-name
TRY003 # raise-vanilla-args
)
PER_FILE_IGNORES=(
# Command-line interfaces
*/commands/*:T201 # print
__main__.py:T201 # print
manage.py:T201 # print
run.py:T201 # print
# Documentation
docs/*:D100 # undocumented-public-module
docs/*:INP001 # implicit-namespace-package
# Migrations
*migrations/*:E501 # line-too-long
*migrations/*:INP001 # implicit-namespace-package
# Notebooks
*.ipynb:E501 # line-too-long
*.ipynb:ERA001 # commented-out-code
*.ipynb:F401 # unused-import
*.ipynb:F821 # undefined-name
# Namespace packages
sphinxcontrib/*:INP001 # implicit-namespace-package
# Settings
*/settings.py:ERA001 # commented-out-code
# Tests
tests/*:FBT003 # boolean-positional-value-in-call
tests/*:INP001 # implicit-namespace-package
tests/*:TRY003 # raise-vanilla-args (AssertionError)
tests/*:S # security
test_*:S101 # [credere-backend, kingfisher-collect]
)
BUILTINS_IGNORELIST=("'placeholder'")
if [ -n "$REQUIREMENTS_FILE" ]; then
if grep babel $REQUIREMENTS_FILE > /dev/null; then
IGNORE+=(
# https://babel.pocoo.org/en/latest/api/messages/extract.html#language-parsing
ARG001 # unused-function-argument
)
fi
if grep click $REQUIREMENTS_FILE > /dev/null; then
IGNORE+=(
# https://click.palletsprojects.com/en/8.1.x/options/#callbacks-for-validation
ARG001 # unused-function-argument
ARG002 # unused-method-argument
)
fi
if grep django $REQUIREMENTS_FILE > /dev/null; then
IGNORE+=(
PT # pytest
DJ008 # django-model-without-dunder-str
S308 # suspicious-mark-safe-usage (false positive)
)
PER_FILE_IGNORES+=(
# signals.py https://docs.djangoproject.com/en/4.2/topics/signals/
# views.py https://docs.djangoproject.com/en/4.2/topics/http/views/
# migrations/ https://docs.djangoproject.com/en/4.2/howto/writing-migrations/
{*/signals,*/views,*/migrations/*}.py:ARG001 # unused-function-argument
# admin.py https://docs.djangoproject.com/en/4.2/ref/contrib/admin/#modeladmin-methods
# routers.py https://docs.djangoproject.com/en/4.2/topics/db/multi-db/#an-example
# views.py https://docs.djangoproject.com/en/4.2/topics/class-based-views/
# commands.py https://docs.djangoproject.com/en/4.2/howto/custom-management-commands/
{*/admin,*/routers,*/views,*/commands/*}.py:ARG002 # unused-method-argument
# admin.py https://docs.djangoproject.com/en/4.2/ref/contrib/admin/
# forms.py https://docs.djangoproject.com/en/4.2/topics/forms/modelforms/
# models.py https://docs.djangoproject.com/en/4.2/ref/models/options/
# migrations/ https://docs.djangoproject.com/en/4.2/topics/migrations/#migration-files
# tests/ https://docs.djangoproject.com/en/4.2/topics/db/fixtures/#how-to-use-a-fixture
{*/admin,*/forms,*/models,*/routers,*/migrations/*,tests/*}.py:RUF012 # mutable-class-default
)
BUILTINS_IGNORELIST+=(
"'id'" # path component
)
fi
if grep django-modeltranslation $REQUIREMENTS_FILE > /dev/null; then
PER_FILE_IGNORES+=(
# translation.py https://django-modeltranslation.readthedocs.io/en/latest/registration.html#required-langs
*/translation.py:RUF012
)
fi
if grep djangorestframework $REQUIREMENTS_FILE > /dev/null; then
PER_FILE_IGNORES+=(
# serializers.py https://www.django-rest-framework.org/api-guide/serializers/#modelserializer
# views.py https://www.django-rest-framework.org/api-guide/viewsets/
*/{serializers,views}.py:RUF012
)
BUILTINS_IGNORELIST+=(
# https://www.django-rest-framework.org/api-guide/format-suffixes/
"'format'"
)
fi
if grep fastapi $REQUIREMENTS_FILE > /dev/null; then
IGNORE+=(
# https://fastapi.tiangolo.com/reference/dependencies/
ARG001 # unused-function-argument
)
BUILTINS_IGNORELIST+=(
"'id'" # path component
)
fi
if grep pika $REQUIREMENTS_FILE > /dev/null; then
IGNORE+=(
# https://pika.readthedocs.io/en/stable/modules/channel.html
ARG002 # unused-method-argument
)
fi
if grep pandas $REQUIREMENTS_FILE > /dev/null; then
IGNORE+=(
PD008 # pandas-use-of-dot-at
PD901 # pandas-df-variable-name
)
fi
if grep scrapy $REQUIREMENTS_FILE > /dev/null; then
IGNORE+=(
# https://docs.scrapy.org/en/latest/topics/spiders.html#spider-arguments
ARG002 # unused-method-argument
# https://docs.scrapy.org/en/latest/topics/spiders.html#scrapy.Spider
RUF012 # mutable-class-default
)
fi
if grep sphinx $REQUIREMENTS_FILE > /dev/null; then
IGNORE+=(
# https://www.sphinx-doc.org/en/master/development/tutorials/extending_build.html
ARG001 # unused-function-argument
# https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx.application.Sphinx.add_directive
RUF012 # mutable-class-default
)
fi
fi
if [ -d docs ]; then
BUILTINS_IGNORELIST+=(
"'copyright'"
)
fi
if [ ! -f .python-version ]; then # Packages support Python 3.9.
IGNORE+=(
UP038 # non-pep604-isinstance (Python 3.10+)
)
fi
if [ ! -f .python-version ] || grep 3.10 .python-version > /dev/null; then
IGNORE+=(
PYI024 # collections-named-tuple (Python 3.11+)
)
fi
if [ -f MANIFEST.in ]; then
PER_FILE_IGNORES+=(
tests/*:ARG001 # unused-function-argument (fixtures)
)
fi
if [ -f requirements_dev.txt ]; then
if grep pytest requirements_dev.txt > /dev/null; then
PER_FILE_IGNORES+=(
tests/*:ARG001 test_*:ARG001 # unused-function-argument (fixtures)
)
fi
fi
if [ -f common-requirements.txt ]; then
if grep pytest common-requirements.txt > /dev/null; then
PER_FILE_IGNORES+=(
tests/*:ARG001 # unused-function-argument (fixtures)
)
fi
fi
case "${GITHUB_REPOSITORY##*/}" in
jscc | ocds-merge | sample-data | standard-maintenance-scripts | standard)
IGNORE+=(B028) # no-explicit-stacklevel
;;
credere-backend)
BUILTINS_IGNORELIST+=("'type'")
;;
deploy)
IGNORE+=(EXE003) # shebang-missing-python
;;
pelican-backend)
IGNORE+=(ERA001) # commented-out-code
PER_FILE_IGNORES+=(tests/*:RUF012) # mutable-class-default
;;
pelican-frontend)
IGNORE+=(ARG001 RUF012) # unused-function-argument mutable-class-default
BUILTINS_IGNORELIST+=("'type'")
;;
yapw)
PER_FILE_IGNORES+=(tests/fixtures/*:T201) # print
;;
esac
ruff check . --select ALL \
--ignore "$(
IFS=,
echo "${IGNORE[*]}"
)" \
--per-file-ignores "$(
IFS=,
echo "${PER_FILE_IGNORES[*]}"
)" \
--config "lint.flake8-builtins.builtins-ignorelist = [$(
IFS=,
echo "${BUILTINS_IGNORELIST[*]}"
)]" \
--config "lint.allowed-confusables = ['’']" \
--config 'line-length = 119' \
--exclude 'demo_docs,t'
if [ -n "$CI" ]; then
pytest -rs /tmp/test_csv.py /tmp/test_json.py /tmp/test_readme.py # test_requirements.py is opt-in
fi