keymap-align 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- keymap_align-0.2.0/.editorconfig +69 -0
- keymap_align-0.2.0/.github/actionlint.yaml +8 -0
- keymap_align-0.2.0/.github/workflows/release.yml +68 -0
- keymap_align-0.2.0/.github/workflows/validate.yml +249 -0
- keymap_align-0.2.0/.gitignore +56 -0
- keymap_align-0.2.0/.markdownlint.yaml +42 -0
- keymap_align-0.2.0/.markdownlintignore +4 -0
- keymap_align-0.2.0/.pre-commit-config.yaml +237 -0
- keymap_align-0.2.0/.python-version +1 -0
- keymap_align-0.2.0/.shellcheckrc +21 -0
- keymap_align-0.2.0/CLAUDE.md +111 -0
- keymap_align-0.2.0/LICENSE +21 -0
- keymap_align-0.2.0/PKG-INFO +159 -0
- keymap_align-0.2.0/README.md +141 -0
- keymap_align-0.2.0/pyproject.toml +199 -0
- keymap_align-0.2.0/src/keymap_align/__init__.py +1 -0
- keymap_align-0.2.0/src/keymap_align/align.py +693 -0
- keymap_align-0.2.0/src/keymap_align/cli.py +168 -0
- keymap_align-0.2.0/src/keymap_align/config.py +135 -0
- keymap_align-0.2.0/src/keymap_align/layout_resolver.py +87 -0
- keymap_align-0.2.0/src/keymap_align/layouts/__init__.py +0 -0
- keymap_align-0.2.0/src/keymap_align/layouts/corne42.json +15 -0
- keymap_align-0.2.0/src/keymap_align/layouts/glove80.json +19 -0
- keymap_align-0.2.0/src/keymap_align/layouts/piantor.json +15 -0
- keymap_align-0.2.0/tests/layouts/corne_6key_simple_layout.json +7 -0
- keymap_align-0.2.0/tests/layouts/glove80_80key_layout.json +11 -0
- keymap_align-0.2.0/tests/simple_tests/corne_6key_simple_test.keymap +17 -0
- keymap_align-0.2.0/tests/test_align_keymap.py +680 -0
- keymap_align-0.2.0/tests/test_config.py +237 -0
- keymap_align-0.2.0/tests/test_display_name_preservation.py +211 -0
- keymap_align-0.2.0/tests/test_keymaps/correct/corne_6key_reference_comments_and_properties.keymap +38 -0
- keymap_align-0.2.0/tests/test_keymaps/correct/glove80_reference_properly_aligned.keymap +55 -0
- keymap_align-0.2.0/tests/test_keymaps/correct/glove80_reference_reverse_key_order.keymap +55 -0
- keymap_align-0.2.0/tests/test_keymaps/misaligned/corne_6key_input_comments_and_properties.keymap +35 -0
- keymap_align-0.2.0/tests/test_keymaps/misaligned/glove80_input_badly_aligned.keymap +55 -0
- keymap_align-0.2.0/tests/test_keymaps/misaligned/glove80_input_cramped_no_spacing.keymap +55 -0
- keymap_align-0.2.0/tests/test_keymaps/misaligned/glove80_input_excess_bindings.keymap +26 -0
- keymap_align-0.2.0/tests/test_keymaps/misaligned/glove80_input_insufficient_bindings.keymap +55 -0
- keymap_align-0.2.0/tests/test_layouts.py +151 -0
- keymap_align-0.2.0/tests/test_update.py +21 -0
- keymap_align-0.2.0/uv.lock +443 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# forge-managed
|
|
2
|
+
#
|
|
3
|
+
# Deployed as .editorconfig.
|
|
4
|
+
#
|
|
5
|
+
# This is where shell formatting is defined, not the shfmt hook. shfmt treats a
|
|
6
|
+
# parser or printer flag as a replacement for this file rather than an addition
|
|
7
|
+
# to it: a single `--indent 2` on the hook would discard switch_case_indent and
|
|
8
|
+
# binary_next_line along with it. Output flags (--write, --diff) are safe.
|
|
9
|
+
#
|
|
10
|
+
# The shell keys sit under [*] rather than [*.sh] because a shell executable is
|
|
11
|
+
# commonly shipped with no extension at all. A suffix pattern cannot reach one,
|
|
12
|
+
# and an unmatched file is not skipped: shfmt falls back to tab indentation and
|
|
13
|
+
# reformats it wrongly. shfmt only reads these for files it identifies as shell,
|
|
14
|
+
# so [*] costs nothing there. The per-language sections below exist because
|
|
15
|
+
# editors, unlike shfmt, read [*] for every file.
|
|
16
|
+
#
|
|
17
|
+
# indent_size, switch_case_indent and binary_next_line are shfmt's -i 2 -ci -bn,
|
|
18
|
+
# which the shfmt manual gives as its approximation of the Google Shell Style
|
|
19
|
+
# Guide. Google specifies all three: two-space indent, case alternatives
|
|
20
|
+
# indented, and a split pipeline carrying `|`, `||` and `&&` at the start of the
|
|
21
|
+
# continuation line rather than the end of the previous one.
|
|
22
|
+
#
|
|
23
|
+
# space_redirects is deliberately absent, leaving shfmt's default of `>file`.
|
|
24
|
+
# Google's guide does not rule on redirect spacing, and overriding a formatter's
|
|
25
|
+
# considered default with no standard behind it is config that cannot be
|
|
26
|
+
# justified later.
|
|
27
|
+
#
|
|
28
|
+
# simplify is likewise absent, and is not a formatting option: it rewrites
|
|
29
|
+
# `${var}` to `$var`, which Google's guide explicitly argues against.
|
|
30
|
+
|
|
31
|
+
root = true
|
|
32
|
+
|
|
33
|
+
[*]
|
|
34
|
+
charset = utf-8
|
|
35
|
+
end_of_line = lf
|
|
36
|
+
insert_final_newline = true
|
|
37
|
+
trim_trailing_whitespace = true
|
|
38
|
+
indent_style = space
|
|
39
|
+
indent_size = 2
|
|
40
|
+
switch_case_indent = true
|
|
41
|
+
binary_next_line = true
|
|
42
|
+
|
|
43
|
+
# gofmt is not configurable and emits tabs.
|
|
44
|
+
[*.go]
|
|
45
|
+
indent_style = tab
|
|
46
|
+
|
|
47
|
+
# PEP 8, and the default both ruff-format and rustfmt apply.
|
|
48
|
+
[*.{py,rs}]
|
|
49
|
+
indent_size = 4
|
|
50
|
+
|
|
51
|
+
# StyLua reads these where a repo carries no stylua.toml, and its own defaults
|
|
52
|
+
# are 120 columns and double quotes. 140 is the width ruff, prettier and rustfmt
|
|
53
|
+
# are given, and single quotes are what ruff's quote-style and prettier's
|
|
54
|
+
# singleQuote already choose.
|
|
55
|
+
[*.lua]
|
|
56
|
+
max_line_length = 140
|
|
57
|
+
quote_type = single
|
|
58
|
+
|
|
59
|
+
# Inherits indent_size = 2 to agree with markdownlint's MD007, which the shared
|
|
60
|
+
# .markdownlint.yaml leaves at its 2-space default via `default: true`.
|
|
61
|
+
# trim_trailing_whitespace matches the trailing-whitespace hook's
|
|
62
|
+
# --markdown-linebreak-ext=md, which preserves the two-space line break; an
|
|
63
|
+
# editor and the hook must not undo each other.
|
|
64
|
+
[*.md]
|
|
65
|
+
trim_trailing_whitespace = false
|
|
66
|
+
|
|
67
|
+
# make requires a literal tab before every recipe line.
|
|
68
|
+
[Makefile]
|
|
69
|
+
indent_style = tab
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# forge-toolchain: 46
|
|
2
|
+
# Labels actionlint cannot discover. Its list of GitHub's hosted images is the
|
|
3
|
+
# one its release shipped with, and a self-hosted runner's labels are on no
|
|
4
|
+
# list. Without this every runs-on naming one is reported as a typo and the
|
|
5
|
+
# actionlint hook fails.
|
|
6
|
+
self-hosted-runner:
|
|
7
|
+
labels:
|
|
8
|
+
- ubuntu-26.04
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
# Scoped per job rather than here: validate only reads, and it is the job that
|
|
8
|
+
# runs the most third-party code.
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
# Two pushes to main inside one run's window would otherwise race. Both runs
|
|
13
|
+
# resolve the same version, and the later one fails pushing its tag or its
|
|
14
|
+
# version commit.
|
|
15
|
+
# Never cancel-in-progress: a canceled release can leave a tag with no release.
|
|
16
|
+
concurrency:
|
|
17
|
+
group: release-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: false
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
# Without this, release publishes whatever is on main, tested or not.
|
|
22
|
+
validate:
|
|
23
|
+
uses: ./.github/workflows/validate.yml
|
|
24
|
+
|
|
25
|
+
release:
|
|
26
|
+
needs: validate
|
|
27
|
+
uses: datapointchris/reusable-workflows/.github/workflows/python-semantic-release.yml@v1
|
|
28
|
+
permissions:
|
|
29
|
+
contents: write
|
|
30
|
+
|
|
31
|
+
# A separate job in the same workflow, gated on the one above.
|
|
32
|
+
#
|
|
33
|
+
# Same workflow because a tag pushed with GITHUB_TOKEN does not trigger
|
|
34
|
+
# another workflow run, so a publish keyed on `push: tags` would never fire.
|
|
35
|
+
# Separate job so the publishing credential is reachable from these steps
|
|
36
|
+
# only, and not from the release job that runs python-semantic-release.
|
|
37
|
+
#
|
|
38
|
+
# A commit type that bumps nothing leaves `released` false, and this job is
|
|
39
|
+
# skipped. A skipped publish on a green run is the correct result there.
|
|
40
|
+
publish:
|
|
41
|
+
needs: release
|
|
42
|
+
if: needs.release.outputs.released == 'true'
|
|
43
|
+
runs-on: ubuntu-26.04
|
|
44
|
+
environment:
|
|
45
|
+
name: pypi
|
|
46
|
+
url: https://pypi.org/p/keymap-align
|
|
47
|
+
permissions:
|
|
48
|
+
# Trusted Publishing. No API token is stored in this repository; PyPI is
|
|
49
|
+
# configured to accept a short-lived OIDC credential from this workflow.
|
|
50
|
+
id-token: write
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v7
|
|
53
|
+
with:
|
|
54
|
+
# The tag, not the branch. The version is read from the tag, so a
|
|
55
|
+
# build anywhere else names the previous release plus a distance, and
|
|
56
|
+
# main may already have moved past what was released.
|
|
57
|
+
ref: ${{ needs.release.outputs.tag }}
|
|
58
|
+
|
|
59
|
+
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
60
|
+
|
|
61
|
+
- run: uv build
|
|
62
|
+
|
|
63
|
+
# A version cannot be replaced once uploaded, only yanked, so the sdist
|
|
64
|
+
# and wheel are checked before anything is irreversible.
|
|
65
|
+
- run: uv run --no-project --with twine twine check dist/*
|
|
66
|
+
|
|
67
|
+
- name: Publish to PyPI
|
|
68
|
+
run: uv publish
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# forge-toolchain: 46
|
|
2
|
+
name: CI
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
pull_request:
|
|
8
|
+
workflow_call:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
|
|
15
|
+
python:
|
|
16
|
+
runs-on: ubuntu-26.04
|
|
17
|
+
steps:
|
|
18
|
+
|
|
19
|
+
- uses: actions/checkout@v7
|
|
20
|
+
|
|
21
|
+
# generated:python
|
|
22
|
+
# enable-cache is left at setup-uv's `auto`, which caches on a hosted
|
|
23
|
+
# runner only. Every repo's self-hosted runner shares one ~/.cache/uv
|
|
24
|
+
# that outlives the job, so there the post step's prune and upload are
|
|
25
|
+
# work the disk already did, and two jobs pruning at once collide on the
|
|
26
|
+
# same files. version is the release the uv-lock hook pins, so the sync
|
|
27
|
+
# here and the lock check in the hooks job run one uv.
|
|
28
|
+
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
29
|
+
with:
|
|
30
|
+
version: "0.12.24"
|
|
31
|
+
|
|
32
|
+
- run: uv sync --all-extras --dev
|
|
33
|
+
|
|
34
|
+
# --with pytest, because skipping is the wrong answer here: a repo with a
|
|
35
|
+
# real test suite that never declared pytest as a dependency would
|
|
36
|
+
# silently stop being tested. Supplying the runner runs the tests; a suite
|
|
37
|
+
# needing plugins still declares those itself.
|
|
38
|
+
#
|
|
39
|
+
# pytest exits 5 when it collects nothing, which is not a failure for a
|
|
40
|
+
# repo that has no tests yet. Written explicitly rather than as an && chain
|
|
41
|
+
# because the runner uses `bash -e`, where the concise form depends on
|
|
42
|
+
# subtle errexit-exemption rules.
|
|
43
|
+
- name: Test
|
|
44
|
+
run: |
|
|
45
|
+
set +e
|
|
46
|
+
uv run --with pytest pytest
|
|
47
|
+
code=$?
|
|
48
|
+
set -e
|
|
49
|
+
if [ "$code" -eq 0 ] || [ "$code" -eq 5 ]; then exit 0; fi
|
|
50
|
+
exit 1
|
|
51
|
+
|
|
52
|
+
hooks:
|
|
53
|
+
runs-on: ubuntu-26.04
|
|
54
|
+
steps:
|
|
55
|
+
|
|
56
|
+
- uses: actions/checkout@v7
|
|
57
|
+
|
|
58
|
+
# generated:hooks
|
|
59
|
+
# Every hook a standard block put in the repo's committed config, which
|
|
60
|
+
# forge lists below. A push from a checkout that never ran `pre-commit
|
|
61
|
+
# install`, or a merge in the web UI, passes every hook by not running it,
|
|
62
|
+
# and this is the gate that catches both. CI runs the hooks themselves
|
|
63
|
+
# rather than copies of their checks, because a copy is a second spelling
|
|
64
|
+
# of the check that drifts from the first.
|
|
65
|
+
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
66
|
+
with:
|
|
67
|
+
version: "0.12.24"
|
|
68
|
+
|
|
69
|
+
# A self-hosted runner keeps ~/.cache/pre-commit between jobs, so there
|
|
70
|
+
# the archive is a download of what is already on disk.
|
|
71
|
+
- uses: actions/cache@v6
|
|
72
|
+
if: runner.environment == 'github-hosted'
|
|
73
|
+
with:
|
|
74
|
+
path: ~/.cache/pre-commit
|
|
75
|
+
key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }}
|
|
76
|
+
|
|
77
|
+
# The bats hook is `language: system`, so bats has to be on PATH. Every
|
|
78
|
+
# config carries the hook, and the step does nothing where there are no
|
|
79
|
+
# suites.
|
|
80
|
+
#
|
|
81
|
+
# All three are cloned at a tag rather than from main: a test harness
|
|
82
|
+
# that moves on its own turns an unrelated upstream change into a failing
|
|
83
|
+
# build, and nothing records which version a green run used.
|
|
84
|
+
#
|
|
85
|
+
# bats-core runs from its clone in RUNNER_TEMP, by PATH. Installed into
|
|
86
|
+
# /usr/local, it would be one copy every runner on a self-hosted box
|
|
87
|
+
# rewrites while the others execute it.
|
|
88
|
+
#
|
|
89
|
+
# The helper libraries go under ~/.local/lib because that is where the
|
|
90
|
+
# suites `load` them from — the same path they use on a workstation, so a
|
|
91
|
+
# test cannot pass locally and fail here over a lookup path. The suites
|
|
92
|
+
# name that path literally, and `load` resolves nothing, so BATS_LIB_PATH
|
|
93
|
+
# cannot move the libraries anywhere cheaper.
|
|
94
|
+
#
|
|
95
|
+
# On the self-hosted pool that $HOME outlives the job and is shared by
|
|
96
|
+
# every repo's runner, so concurrent jobs meet at ~/.local/lib. Each
|
|
97
|
+
# release is cloned once, into a directory named for its version, and
|
|
98
|
+
# nothing that has been loaded from is ever removed. The clone is staged
|
|
99
|
+
# beside its target and renamed into place, so a reader never sees a
|
|
100
|
+
# partial one, and a job that loses the race keeps the winner's copy. The
|
|
101
|
+
# path the suites load is a symlink to the pinned release, replaced by
|
|
102
|
+
# rename. A new pin names a new directory, so a runner never serves the
|
|
103
|
+
# tag it cloned first while CI reports the pin. A real directory at the
|
|
104
|
+
# symlink's path cannot be renamed over, so it is removed first.
|
|
105
|
+
#
|
|
106
|
+
# The hook runs tests/*.bats, flat. Recursing would sweep an integration
|
|
107
|
+
# tier in beside the unit one, and only the repo knows which of its tiers
|
|
108
|
+
# is runner-safe. A nested layout is named in a notice rather than passed
|
|
109
|
+
# over in silence, which is how one went unnoticed before.
|
|
110
|
+
#
|
|
111
|
+
# jq is installed ahead of the runner's own, because the scripts under
|
|
112
|
+
# test evaluate jq programs and jq's grammar moves between releases. 1.8
|
|
113
|
+
# accepts an object value piped into `//` without parentheses, and 1.7
|
|
114
|
+
# rejects the whole program. The version is the release the development
|
|
115
|
+
# machines run, so a program parses here exactly when it parses there.
|
|
116
|
+
- name: Set up bats
|
|
117
|
+
run: |
|
|
118
|
+
bats_version="1.14.0"
|
|
119
|
+
bats_support_version="0.3.0"
|
|
120
|
+
bats_assert_version="2.2.4"
|
|
121
|
+
jq_version="1.8.2"
|
|
122
|
+
|
|
123
|
+
if ! compgen -G "tests/*.bats" > /dev/null; then
|
|
124
|
+
if [ -n "$(find tests -name '*.bats' -print -quit 2>/dev/null)" ]; then
|
|
125
|
+
echo "::notice::bats suites exist under tests/ but none at its root, and the bats hook runs tests/*.bats only"
|
|
126
|
+
fi
|
|
127
|
+
exit 0
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
git clone --depth 1 --branch "v${bats_version}" \
|
|
131
|
+
https://github.com/bats-core/bats-core.git "$RUNNER_TEMP/bats"
|
|
132
|
+
echo "$RUNNER_TEMP/bats/bin" >> "$GITHUB_PATH"
|
|
133
|
+
|
|
134
|
+
lib="$HOME/.local/lib"
|
|
135
|
+
mkdir -p "$lib"
|
|
136
|
+
for helper in "bats-support:${bats_support_version}" "bats-assert:${bats_assert_version}"; do
|
|
137
|
+
name="${helper%%:*}"
|
|
138
|
+
release="${name}-v${helper#*:}"
|
|
139
|
+
if [ ! -d "$lib/$release" ]; then
|
|
140
|
+
staged=$(mktemp -d "$lib/.$release.XXXXXX")
|
|
141
|
+
git clone --depth 1 --branch "v${helper#*:}" \
|
|
142
|
+
"https://github.com/bats-core/$name.git" "$staged"
|
|
143
|
+
mv -T "$staged" "$lib/$release" 2>/dev/null || rm -rf "$staged"
|
|
144
|
+
fi
|
|
145
|
+
[ -L "$lib/$name" ] || rm -rf "${lib:?}/$name"
|
|
146
|
+
ln -s "$release" "$lib/.$name.$$"
|
|
147
|
+
mv -T "$lib/.$name.$$" "$lib/$name"
|
|
148
|
+
done
|
|
149
|
+
|
|
150
|
+
mkdir -p "$RUNNER_TEMP/jq"
|
|
151
|
+
curl -fsSL --retry 3 -o "$RUNNER_TEMP/jq/jq" \
|
|
152
|
+
"https://github.com/jqlang/jq/releases/download/jq-${jq_version}/jq-linux-amd64"
|
|
153
|
+
chmod +x "$RUNNER_TEMP/jq/jq"
|
|
154
|
+
echo "$RUNNER_TEMP/jq" >> "$GITHUB_PATH"
|
|
155
|
+
"$RUNNER_TEMP/jq/jq" --version
|
|
156
|
+
|
|
157
|
+
# Over what the push or pull request changed, which is what the hooks
|
|
158
|
+
# would have seen at commit time. --all-files would fail a push on every
|
|
159
|
+
# finding in a file it never touched, so it is the scope only where no
|
|
160
|
+
# earlier commit can be fetched, as on a repo's first push.
|
|
161
|
+
#
|
|
162
|
+
# Or where the change touches a file that configures a hook or pins what
|
|
163
|
+
# it checks against: the hook config, a tool's config, a manifest, a lock
|
|
164
|
+
# or a toolchain file. None of those matches a hook's own file types, so
|
|
165
|
+
# a new release or rule would otherwise grade nothing in the commit that
|
|
166
|
+
# brought it, and its findings would land on the next author to touch the
|
|
167
|
+
# code.
|
|
168
|
+
#
|
|
169
|
+
# The checkout is one commit deep, and diffing needs one more. pre-commit
|
|
170
|
+
# falls back to a two-dot diff where the two commits share no history on
|
|
171
|
+
# disk, and refcheck's --moves reads the range as a two-dot diff too, so
|
|
172
|
+
# the base commit alone is enough.
|
|
173
|
+
#
|
|
174
|
+
# GITHUB_TOKEN is for the tflint hook, whose --init installs the plugins a
|
|
175
|
+
# .tflint.hcl names from GitHub, which rate-limits a runner that does not
|
|
176
|
+
# authenticate.
|
|
177
|
+
#
|
|
178
|
+
# setup-go exports GOTOOLCHAIN=local, and this job sets Go up once per
|
|
179
|
+
# module. Under local, a later setup-go ignores its go.mod's toolchain
|
|
180
|
+
# line, and each module's go refuses a go.mod asking for a newer release.
|
|
181
|
+
# auto lets each module's go switch to the toolchain its go.mod names. It
|
|
182
|
+
# is exported in the script, where no GITHUB_ENV write can override it.
|
|
183
|
+
- name: Run the hooks over the change
|
|
184
|
+
env:
|
|
185
|
+
BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
|
|
186
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
187
|
+
run: |
|
|
188
|
+
export GOTOOLCHAIN=auto
|
|
189
|
+
from=
|
|
190
|
+
if [ -n "$BASE" ] && git fetch --quiet --depth=1 --no-tags origin "$BASE" 2>/dev/null; then
|
|
191
|
+
from=$BASE
|
|
192
|
+
elif git fetch --quiet --depth=2 --no-tags origin "$(git rev-parse HEAD)" &&
|
|
193
|
+
git rev-parse --quiet --verify HEAD~1 >/dev/null; then
|
|
194
|
+
echo "::notice::this event names no base commit, so only the head commit's change is checked"
|
|
195
|
+
from=HEAD~1
|
|
196
|
+
else
|
|
197
|
+
echo "::notice::no earlier commit to diff against, so every file is checked"
|
|
198
|
+
fi
|
|
199
|
+
scope=(--all-files)
|
|
200
|
+
if [ -n "$from" ]; then
|
|
201
|
+
scope=(--from-ref "$from" --to-ref HEAD)
|
|
202
|
+
while IFS= read -r changed; do
|
|
203
|
+
case "${changed##*/}" in
|
|
204
|
+
.pre-commit-config.yaml | .editorconfig | .codespellrc | .markdownlint.yaml | \
|
|
205
|
+
.shellcheckrc | .golangci.yml | .sqlfluff | .tflint.hcl | .prettierrc.yaml | \
|
|
206
|
+
.prettierignore | rustfmt.toml | .rustfmt.toml | \
|
|
207
|
+
eslint.config.* | tsconfig*.json | stylua.toml | .stylua.toml | \
|
|
208
|
+
pyproject.toml | uv.lock | go.mod | go.sum | Cargo.toml | Cargo.lock | \
|
|
209
|
+
rust-toolchain.toml | package.json | package-lock.json | .nvmrc | .terraform.lock.hcl)
|
|
210
|
+
echo "::notice::$changed changed, so every file is checked"
|
|
211
|
+
scope=(--all-files)
|
|
212
|
+
break
|
|
213
|
+
;;
|
|
214
|
+
esac
|
|
215
|
+
done < <(git diff --name-only "$from" HEAD)
|
|
216
|
+
fi
|
|
217
|
+
hooks=(
|
|
218
|
+
check-yaml
|
|
219
|
+
check-toml
|
|
220
|
+
check-json
|
|
221
|
+
end-of-file-fixer
|
|
222
|
+
trailing-whitespace
|
|
223
|
+
check-executables-have-shebangs
|
|
224
|
+
check-shebang-scripts-are-executable
|
|
225
|
+
check-added-large-files
|
|
226
|
+
check-merge-conflict
|
|
227
|
+
mixed-line-ending
|
|
228
|
+
check-case-conflict
|
|
229
|
+
detect-private-key
|
|
230
|
+
check-json5
|
|
231
|
+
markdownlint
|
|
232
|
+
shellcheck
|
|
233
|
+
shfmt
|
|
234
|
+
bats
|
|
235
|
+
codespell
|
|
236
|
+
refcheck
|
|
237
|
+
uv-lock
|
|
238
|
+
ruff-format
|
|
239
|
+
ruff-check
|
|
240
|
+
mypy
|
|
241
|
+
actionlint
|
|
242
|
+
)
|
|
243
|
+
# Every hook runs whatever the one before it found, so one push
|
|
244
|
+
# reports every failing hook rather than the first.
|
|
245
|
+
status=0
|
|
246
|
+
for hook in "${hooks[@]}"; do
|
|
247
|
+
uvx pre-commit run "$hook" "${scope[@]}" --show-diff-on-failure || status=1
|
|
248
|
+
done
|
|
249
|
+
exit "$status"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
MANIFEST
|
|
23
|
+
|
|
24
|
+
# Virtual environments
|
|
25
|
+
.venv/
|
|
26
|
+
venv/
|
|
27
|
+
ENV/
|
|
28
|
+
env/
|
|
29
|
+
|
|
30
|
+
# IDEs
|
|
31
|
+
.vscode/
|
|
32
|
+
.idea/
|
|
33
|
+
*.swp
|
|
34
|
+
*.swo
|
|
35
|
+
*~
|
|
36
|
+
.DS_Store
|
|
37
|
+
|
|
38
|
+
# Testing
|
|
39
|
+
.coverage
|
|
40
|
+
.pytest_cache/
|
|
41
|
+
.mypy_cache/
|
|
42
|
+
.ruff_cache/
|
|
43
|
+
htmlcov/
|
|
44
|
+
.tox/
|
|
45
|
+
tests/test_keymaps/test_output/
|
|
46
|
+
|
|
47
|
+
# Documentation
|
|
48
|
+
site/
|
|
49
|
+
docs/_build/
|
|
50
|
+
|
|
51
|
+
# Misc
|
|
52
|
+
*.log
|
|
53
|
+
*.tmp
|
|
54
|
+
.cache/
|
|
55
|
+
.claude/
|
|
56
|
+
coverage.xml
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# forge-managed
|
|
2
|
+
#
|
|
3
|
+
# Deployed as .markdownlint.yaml.
|
|
4
|
+
#
|
|
5
|
+
# YAML rather than JSON so the file can carry the marker on its first line. JSON
|
|
6
|
+
# has no comment syntax, and markdownlint reads either spelling — including at
|
|
7
|
+
# the rev the markdown block pins.
|
|
8
|
+
#
|
|
9
|
+
# markdownlint prefers .markdownlint.json when both are present, so a repo still
|
|
10
|
+
# holding the JSON spelling has it removed in the same run that writes this one.
|
|
11
|
+
|
|
12
|
+
default: true
|
|
13
|
+
|
|
14
|
+
# Line length. Prose here is wrapped for the editor, not for a linter, and a
|
|
15
|
+
# rule that fires on a long URL costs more than it finds.
|
|
16
|
+
MD013: false
|
|
17
|
+
|
|
18
|
+
# Duplicate headings, siblings only. A document with repeated subsections under
|
|
19
|
+
# different parents is ordinary; two identical headings under one parent is not.
|
|
20
|
+
MD024:
|
|
21
|
+
siblings_only: true
|
|
22
|
+
|
|
23
|
+
# Inline HTML. Markdown that needs a <details> block or an anchor needs it.
|
|
24
|
+
MD033: false
|
|
25
|
+
|
|
26
|
+
# First line a top-level heading, or a title in front matter. `name:` counts as
|
|
27
|
+
# a title as well as `title:`, because a skill or agent definition carries its
|
|
28
|
+
# title under that key and opens on no heading.
|
|
29
|
+
MD041:
|
|
30
|
+
front_matter_title: "^\\s*(title|name)\\s*[:=]"
|
|
31
|
+
|
|
32
|
+
# Emphasis used instead of a heading. Deliberate in short documents that want a
|
|
33
|
+
# bold run-in without adding a level to the outline.
|
|
34
|
+
MD036: false
|
|
35
|
+
|
|
36
|
+
# Spaces inside code span markers. Fires on a code span whose content is a
|
|
37
|
+
# literal space, which is a legitimate thing to write.
|
|
38
|
+
MD038: false
|
|
39
|
+
|
|
40
|
+
# Code block style. Both fenced and indented blocks appear in these repos and
|
|
41
|
+
# neither is wrong.
|
|
42
|
+
MD046: false
|