Repository navigation
Expand file tree
/
Copy pathcodespellit
More file actions
executable file
·441 lines (390 loc) · 14.4 KB
/
Copy pathcodespellit
File metadata and controls
executable file
·441 lines (390 loc) · 14.4 KB
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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
#!/bin/bash
#emacs: -*- mode: shell-script; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
#ex: set sts=4 ts=4 sw=4 et:
set -eu
# poor man shortcuts for just doing it using the regex etc
cmd="${1:-}"
#
# Early action command -- falls through
#
case "$cmd" in
clone) shift;
# strip trailing /
url=${1%%/}
shift
d=${url##*/}
git clone --filter=blob:none "$url" "$d"
cd "$d"
# shellcheck disable=SC2064
trap "echo \"I: clone is under '$d'\"" SIGINT SIGHUP SIGABRT EXIT
;;
esac
#
# For the rest first do common thing
#
source "$(readlink -f "$0" | xargs dirname)/common.sh"
codespell_version_full=$(codespell --version)
codespell_version=${codespell_version_full//.dev$/}
if [[ "$codespell_version_full" =~ \.dev ]]; then
# for the action we
codespell_dir=$(python -c 'import codespell_lib as l; print(l.__path__[0])')
codespell_version=$(
git -C "$codespell_dir" describe --tags --match 'v[0-9]*' | sed -e 's,-.*,,g' -e 's,^v,,g'
)
echo "WARNING! Running development version $codespell_version_full."
echo " For the action etc we will mention tagged version $codespell_version".
fi
function detect-platform() {
# Detect hosting platform from git remotes
# Sets: platform ("github", "codeberg", "forgejo", "unknown")
# remote_prefix ("gh-" for github, "cb-" for codeberg)
local remote_url
remote_url=$(git config remote.origin.url 2>/dev/null || echo "")
if [[ "$remote_url" == *github.com* ]]; then
echo "github"
elif [[ "$remote_url" == *codeberg.org* ]]; then
echo "codeberg"
elif [[ "$remote_url" == *forgejo* ]] || [ -d .forgejo ]; then
echo "forgejo"
else
echo "unknown"
fi
}
function setup-fork-remote() {
# Set up a fork remote for pushing when user doesn't have push access to origin
local platform
platform=$(detect-platform)
case "$platform" in
github)
local gh_user
gh_user=$(gh api user -q .login 2>/dev/null || echo "")
if [ -z "$gh_user" ]; then
echo "E: Cannot determine GitHub user. Run 'gh auth login' first." >&2
return 1
fi
local remote_name="gh-${gh_user}"
if ! git remote get-url "$remote_name" &>/dev/null; then
local origin_url
origin_url=$(git config remote.origin.url)
local repo_path
repo_path=$(echo "$origin_url" | sed -E 's,.*/([^/]+/[^/]+?)(\.git)?$,\1,')
# Fork if needed
gh repo fork --remote=false 2>/dev/null || true
local fork_url
fork_url=$(echo "$origin_url" | sed -E "s,(/[^/]+)/([^/]+)$,/${gh_user}/\2,")
git remote add "$remote_name" "$fork_url"
echo "I: Added remote $remote_name -> $fork_url"
fi
echo "$remote_name"
;;
codeberg)
local cb_user
# tea login ls -o json has fields: name, url, ssh_host, user, default
cb_user=$(tea login ls -o json 2>/dev/null \
| python3 -c 'import sys,json; L=json.load(sys.stdin); print(L[0]["user"])' 2>/dev/null || echo "")
if [ -z "$cb_user" ]; then
echo "E: Cannot determine Codeberg user. Run 'tea login add --url https://codeberg.org' first." >&2
return 1
fi
local remote_name="cb-${cb_user}"
if ! git remote get-url "$remote_name" &>/dev/null; then
local origin_url repo_path fork_url
origin_url=$(git config remote.origin.url)
# Extract org/repo from URL (handles both https and ssh)
repo_path=$(echo "$origin_url" | sed -E 's,.*/([^/]+/[^/]+?)(\.git)?$,\1,')
# Fork via Codeberg API (tea repo fork tries SSH unnecessarily)
# Extract token from tea config
local cb_token
cb_token=$(python3 -c "
import yaml, os
p = os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) + '/tea/config.yml'
with open(p) as f:
cfg = yaml.safe_load(f)
for l in cfg.get('logins', []):
if 'codeberg' in l.get('url', ''):
print(l.get('token', '')); break
" 2>/dev/null || echo "")
echo "I: Forking $repo_path on Codeberg..." >&2
if [ -n "$cb_token" ]; then
curl -sf -X POST \
-H "Authorization: token $cb_token" \
"https://codeberg.org/api/v1/repos/${repo_path}/forks" \
-H "Content-Type: application/json" \
-d '{}' >/dev/null 2>&1 || echo "W: Fork may already exist (OK)" >&2
else
echo "W: Could not read tea token. Fork manually at https://codeberg.org/${repo_path}/fork" >&2
fi
# Replace org with user: handle both https and ssh URLs
fork_url=$(echo "$origin_url" | sed -E "s,(/[^/]+)/([^/]+)$,/${cb_user}/\2,")
git remote add "$remote_name" "$fork_url"
echo "I: Added remote $remote_name -> $fork_url" >&2
fi
echo "$remote_name"
;;
*)
echo "origin"
;;
esac
}
function deduce-config() {
# deduce where we have that config
if [ -e ".codespellrc" ]; then
echo .codespellrc
elif grep -q '^\[tool.codespell\]' pyproject.toml 2>|/dev/null; then
echo pyproject.toml
elif grep -q '^\[codespell\]' setup.cfg 2>|/dev/null; then
echo setup.cfg
else
echo "ERROR: Cannot figure out where codespell config is" >&2
fi
}
function add-precommit() {
if [ "$#" -ge 1 ]; then
config="$1"
else
config=$(deduce-config)
fi
if [ -e .pre-commit-config.yaml ]; then
# check if may be already one
check_if_defined_in_config .pre-commit-config.yaml
# add pre-commit configuration there
indent=$(grep -e '- repo:' .pre-commit-config.yaml | tail -n 1 | sed -e 's,- repo.*,,g')
# ignoring error below since I believe this worked as desired
# shellcheck disable=SC2259
cat | sed -e "s,^,$indent,g" >> .pre-commit-config.yaml <<EOF
- repo: https://github.com/codespell-project/codespell
# Configuration for codespell is in $config
rev: v${codespell_version}
hooks:
- id: codespell
EOF
if [ "$config" = "pyproject.toml" ]; then
cat >> .pre-commit-config.yaml <<EOF
additional_dependencies:
- tomli; python_version<'3.11'
EOF
fi
git add .pre-commit-config.yaml
git commit -m 'Add pre-commit definition for codespell'
if ! python -c 'import yaml; yaml.safe_load(open(".pre-commit-config.yaml"))'; then
echo "ERROR: added pre-commit definition ruined the yaml format, fix and rerun '!0 list'"
exit 1
fi
else
echo "No .pre-commit-config.yaml was found, nothing changed"
fi
}
function list() {
codespell "$@" || {
echo
echo "Problematic?:"
echo
codespell "$@" | grep ','
}
}
function list-hits-sorted() {
codespell "$@" | grep -e '==>' | sed -e 's,.*: *,,g' | sort | uniq -c | sort -n
}
# given filtered output from list-hits-sorted, apply those which were left
function fix-sorted() { # shellcheck disable=SC2120
if ! git diff-index --quiet HEAD --; then
echo "Repository is dirty. Please commit your changes first."
exit 1
fi
nfixes=$(cat "$@" | grep -e '==>' -c )
cat "$@" | grep -e '==>' | while read -r _ fr _ to; do
git grep -l "\<$fr\>" | tr '\n' '\000' | xargs -0 sed -i -e "s,\<$fr\>,$to,g"
done
git commit -m "Fix $nfixes typos found by codespell
Here are typos with their counts and introduced fix:
$(grep -e '==>' "$@")
" -a
}
function datalad-run-w() {
datalad run -m "chore: run codespell throughout fixing few left typos automagically" 'codespell -w'
}
function datalad-run-w-i() {
datalad run -m "chore: do interactive fixing of some ambiguous typos" codespell -w -i 3 -C 2
}
function send-pr() {
local platform pr_title pr_body push_remote
platform=$(detect-platform)
pr_title="Add codespell support (config, workflow to detect/not fix) and make it fix few typos"
pr_body="More about codespell: https://github.com/codespell-project/codespell .
I personally introduced it to dozens if not hundreds of projects already and so far only positive feedback.
CI workflow has 'permissions' set only to 'read' so also should be safe."
if [ -e .git/pr-description.md ]; then
pr_body=$(cat .git/pr-description.md)
fi
case "$platform" in
github)
push_remote=$(setup-fork-remote)
git push -u "$push_remote" "$(git branch --show-current)"
gh repo set-default "$(git config remote.origin.url)" \
&& gh pr create -t "$pr_title" -b "$pr_body" -w
;;
codeberg)
push_remote=$(setup-fork-remote)
local branch_name
branch_name=$(git branch --show-current)
git push -u "$push_remote" "$branch_name"
local origin_url repo_path cb_user_
origin_url=$(git config remote.origin.url)
repo_path=$(echo "$origin_url" | sed -E 's,.*/([^/]+/[^/]+?)(\.git)?$,\1,')
cb_user_=${push_remote#cb-}
# Open compare URL in browser — lets user review/edit before submitting
local pr_url="https://codeberg.org/${repo_path}/compare/master...${cb_user_}:${branch_name}"
echo "Opening PR creation page in browser..." >&2
echo "PR description is in .git/pr-description.md — paste it into the form." >&2
xdg-open "$pr_url" 2>/dev/null || open "$pr_url" 2>/dev/null || {
echo "Open manually: $pr_url" >&2
}
;;
*)
echo "W: Unknown platform, attempting GitHub-style PR creation" >&2
push_remote=$(setup-fork-remote)
git push -u "$push_remote" "$(git branch --show-current)"
gh repo set-default "$(git config remote.origin.url)" \
&& gh pr create -t "$pr_title" -b "$pr_body" -w
;;
esac
}
function check_if_defined_in_config() {
config="$1"
if grep -q -E '\<codespell\>' "$config"; then
echo "E: codespell seems already known to $config" >&2
exit 2
fi
}
# poor man shortcuts for just doing it using the regex etc
case "$cmd" in
list|list-hits-sorted|fix-sorted|deduce-config|add-precommit|datalad-run-w|datalad-run-w-i|send-pr|detect-platform|setup-fork-remote) shift; "$cmd" "$@"; exit $?;;
esac
git checkout -b enh-codespell
skips=".git,.gitignore,.gitattributes"
for f in '*.pdf' '*.svg' '*.ai' go.sum venv .venv venvs locale .tox versioneer.py package-lock.json vendor i18n '*-lock.yaml' '*.lock' '*.css' '*.min.*' '*-min.*' '*.pack.js' '*.niml' '*.gii' '*.pgm'; do
if find . -iname "$f" -print -quit | grep -q .; then
skips+=",$f"
fi
done
ignore_regex=''
if find . -iname '*.ipynb' -print -quit | grep -q .; then
ignore_regex='^\s*"image/\S+": ".*'
fi
ignore_regex_prefix=''
if [ -z "$ignore_regex" ]; then
ignore_regex_prefix="# "
fi
if [ -e pyproject.toml ]; then
config="pyproject.toml"
check_if_defined_in_config "$config";
cat >> pyproject.toml <<EOF
[tool.codespell]
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
skip = '$skips'
check-hidden = true
${ignore_regex_prefix}ignore-regex = '$ignore_regex'
# ignore-words-list = ''
EOF
elif [ -e setup.cfg ]; then
config="setup.cfg"
# Then config file as the "last" commit we can amend if needed
cat >> setup.cfg <<EOF
[codespell]
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
skip = $skips
check-hidden = true
${ignore_regex_prefix}ignore-regex = $ignore_regex
# ignore-words-list =
EOF
else
config=".codespellrc"
skips+=",$config" # for check-hidden
# Then config file as the "last" commit we can amend if needed
cat > .codespellrc <<EOF
[codespell]
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
skip = $skips
check-hidden = true
${ignore_regex_prefix}ignore-regex = $ignore_regex
# ignore-words-list =
EOF
fi
platform=$(detect-platform)
workflow_added=false
# GitHub Actions workflow
if [ -e .github ] || [ "$platform" = "github" ]; then
mkdir -p .github/workflows
cat > .github/workflows/codespell.yml << EOF
# Codespell configuration is within $config
---
name: Codespell
on:
push:
branches: [$branch]
pull_request:
branches: [$branch]
permissions:
contents: read
jobs:
codespell:
name: Check for spelling errors
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Codespell
uses: codespell-project/actions-codespell@8f01853be192eb0f849a5c7d721450e7a467c579 # v2.2
EOF
git add .github/workflows/codespell.yml
git commit -m "Add GitHub Actions workflow for codespell on $branch"
workflow_added=true
fi
# Forgejo Actions workflow (Codeberg and other Forgejo instances)
if [ -e .forgejo ] || [ "$platform" = "codeberg" ] || [ "$platform" = "forgejo" ]; then
mkdir -p .forgejo/workflows
cat > .forgejo/workflows/codespell.yml << EOF
# Codespell configuration is within $config
---
name: Codespell
on:
push:
branches: [$branch]
pull_request:
branches: [$branch]
jobs:
codespell:
name: Check for spelling errors
runs-on: docker
container:
image: ghcr.io/codespell-project/actions-codespell/master:latest
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v6
- name: Codespell
run: codespell .
EOF
git add .forgejo/workflows/codespell.yml
git commit -m "Add Forgejo Actions workflow for codespell on $branch"
workflow_added=true
fi
if [ "$workflow_added" = "false" ]; then
echo "No .github or .forgejo directory and unknown platform, no workflow provided"
fi
echo "Config in $config"
git add "$config"
# --no-verify since we might have trailing spaces which could trip some linters
git commit -m 'Add rudimentary codespell config' --no-verify "$config"
add-precommit "$config"
list "$@" || :
#if ! list "$@"; then
# echo "Nothing todo, exiting"
# exit 0
#else
# echo "There are fixes TODO!"
#fi
commit_pre=$(git show-ref -s HEAD)
datalad-run-w || { git diff --color-words; exit 1; }
datalad-run-w-i
git diff --color-words "$commit_pre"..HEAD
send-pr