Compare commits

...

409 commits
v3.0.2 ... main

Author SHA1 Message Date
anthony sottile
8416413a0e
Merge pull request #3599 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-12-22 16:55:46 -05:00
pre-commit-ci[bot]
37a879e65e
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/setup-cfg-fmt: v3.1.0 → v3.2.0](https://github.com/asottile/setup-cfg-fmt/compare/v3.1.0...v3.2.0)
2025-12-22 20:26:26 +00:00
Anthony Sottile
8a0630ca1a v4.5.1
Some checks failed
main / main-linux (push) Has been cancelled
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
2025-12-16 16:13:56 -05:00
anthony sottile
fcbc745744
Merge pull request #3597 from pre-commit/empty-setup-py
fix python local template when artifact dirs are present
2025-12-16 14:56:40 -06:00
Anthony Sottile
51592eecec fix python local template when artifact dirs are present 2025-12-16 15:45:01 -05:00
anthony sottile
67e8faf80b
Merge pull request #3596 from pre-commit/pre-commit-ci-update-config
Some checks are pending
languages / language (push) Blocked by required conditions
languages / collector (push) Blocked by required conditions
languages / vars (push) Waiting to run
main / main-windows (push) Waiting to run
main / main-linux (push) Waiting to run
[pre-commit.ci] pre-commit autoupdate
2025-12-15 16:04:01 -06:00
pre-commit-ci[bot]
c251e6b6d0
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.19.0 → v1.19.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.19.0...v1.19.1)
2025-12-15 20:48:45 +00:00
anthony sottile
98ccafa3ce
Merge pull request #3593 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-12-01 16:13:49 -05:00
pre-commit-ci[bot]
48953556d0
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.18.2 → v1.19.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.18.2...v1.19.0)
2025-12-01 21:05:15 +00:00
anthony sottile
2cedd58e69
Merge pull request #3588 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-11-25 10:52:12 -05:00
pre-commit-ci[bot]
465192d7de
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.21.1 → v3.21.2](https://github.com/asottile/pyupgrade/compare/v3.21.1...v3.21.2)
2025-11-24 20:53:38 +00:00
anthony sottile
fd42f96874
Merge pull request #3586 from pre-commit/zipapp-sha256-file-not-needed
Some checks failed
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
remove sha256 file from zipapp script
2025-11-22 16:15:39 -05:00
anthony sottile
8ea2b790d8 remove sha256 file from zipapp script
github displays the checksum for us now!
2025-11-22 16:06:27 -05:00
anthony sottile
1af6c8fa95 v4.5.0 2025-11-22 16:02:16 -05:00
anthony sottile
3358a3b540
Merge pull request #3585 from pre-commit/hazmat
add pre-commit hazmat
2025-11-22 14:03:09 -05:00
anthony sottile
bdf68790b7 add pre-commit hazmat 2025-11-22 13:53:53 -05:00
anthony sottile
e436690f14
Merge pull request #3584 from pre-commit/exitstack
Some checks are pending
languages / vars (push) Waiting to run
languages / language (push) Blocked by required conditions
languages / collector (push) Blocked by required conditions
main / main-windows (push) Waiting to run
main / main-linux (push) Waiting to run
use ExitStack instead of start + stop
2025-11-21 15:19:53 -05:00
anthony sottile
8d34f95308 use ExitStack instead of start + stop 2025-11-21 15:09:41 -05:00
anthony sottile
9c7ea88ab9
Merge pull request #3583 from pre-commit/forward-compat-map-manifest
Some checks failed
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
languages / collector (push) Has been cancelled
add forward-compat error message
2025-11-19 15:10:28 -05:00
Anthony Sottile
844dacc168 add forward-compat error message 2025-11-19 14:57:01 -05:00
anthony sottile
6a1d543e52
Merge pull request #3582 from pre-commit/move-gc-back
move logic for gc back to commands.gc
2025-11-19 14:44:46 -05:00
Anthony Sottile
66278a9a0b move logic for gc back to commands.gc 2025-11-19 14:32:09 -05:00
anthony sottile
1b32c50bc7
Merge pull request #3579 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-11-10 16:53:56 -05:00
pre-commit-ci[bot]
063229aee7
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.21.0 → v3.21.1](https://github.com/asottile/pyupgrade/compare/v3.21.0...v3.21.1)
2025-11-10 20:59:54 +00:00
anthony sottile
49e28eea48
Merge pull request #3578 from pre-commit/store-gc-refactor
Some checks are pending
languages / vars (push) Waiting to run
languages / language (push) Blocked by required conditions
languages / collector (push) Blocked by required conditions
main / main-windows (push) Waiting to run
main / main-linux (push) Waiting to run
refactor gc into store
2025-11-09 17:16:27 -05:00
Anthony Sottile
d5c273a2ba refactor gc into store
this will make refactoring this easier later and limits the api surface of Store
2025-11-09 17:03:43 -05:00
anthony sottile
17cf886473 v4.4.0
Some checks are pending
languages / vars (push) Waiting to run
languages / language (push) Blocked by required conditions
languages / collector (push) Blocked by required conditions
main / main-windows (push) Waiting to run
main / main-linux (push) Waiting to run
2025-11-08 16:11:43 -05:00
anthony sottile
cb63a5cb9a
Merge pull request #3535 from br-rhrbacek/fix-cgroups
Fix docker-in-docker detection for cgroups v2
2025-11-08 15:45:53 -05:00
Radek Hrbacek
f80801d75a Fix docker-in-docker detection for cgroups v2 2025-11-08 15:37:32 -05:00
anthony sottile
9143fc3545
Merge pull request #3577 from pre-commit/language-unsupported
rename system and script languages to unsupported / unsupported_script
2025-11-08 15:21:50 -05:00
anthony sottile
725acc969a rename system and script languages to unsupported / unsupported_script 2025-11-08 15:09:16 -05:00
anthony sottile
3815e2e6d8
Merge pull request #3576 from pre-commit/fix-stages-config-error
fix missing context in error for stages
2025-11-08 14:44:32 -05:00
anthony sottile
aa2961c122 fix missing context in error for stages 2025-11-08 14:31:15 -05:00
anthony sottile
46297f7cd6
Merge pull request #3575 from pre-commit/rm-python3-hooks-repo
Some checks are pending
languages / vars (push) Waiting to run
languages / language (push) Blocked by required conditions
languages / collector (push) Blocked by required conditions
main / main-windows (push) Waiting to run
main / main-linux (push) Waiting to run
rm python3_hooks_repo
2025-11-08 13:45:42 -05:00
anthony sottile
95eec75004 rm python3_hooks_repo 2025-11-08 13:34:44 -05:00
anthony sottile
5e4b3546f3
Merge pull request #3574 from pre-commit/rm-hook-with-spaces-test
remove redundant system spaces test
2025-11-08 13:24:42 -05:00
anthony sottile
8bbfcf1f82 remove redundant system spaces test
`test_args_with_spaces_and_quotes` also covers this behaviour
2025-11-08 13:16:38 -05:00
anthony sottile
65175f3cf3
Merge pull request #3566 from pre-commit/upgrade-rbenv
Some checks failed
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
upgrade rbenv / ruby-build
2025-10-24 12:34:53 -07:00
anthony sottile
fc33a62f3c upgrade rbenv / ruby-build 2025-10-24 15:18:07 -04:00
anthony sottile
2db924eb98
Merge pull request #3561 from pre-commit/warn-to-warning
Some checks failed
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
fix deprecated call
2025-10-16 10:34:47 -04:00
Anthony Sottile
ddfcf4034b fix deprecated call 2025-10-16 10:23:30 -04:00
anthony sottile
1b424ccfa2
Merge pull request #3558 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-10-13 16:54:01 -04:00
pre-commit-ci[bot]
221637b0cb
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/setup-cfg-fmt: v2.8.0 → v3.1.0](https://github.com/asottile/setup-cfg-fmt/compare/v2.8.0...v3.1.0)
- [github.com/asottile/reorder-python-imports: v3.15.0 → v3.16.0](https://github.com/asottile/reorder-python-imports/compare/v3.15.0...v3.16.0)
- [github.com/asottile/add-trailing-comma: v3.2.0 → v4.0.0](https://github.com/asottile/add-trailing-comma/compare/v3.2.0...v4.0.0)
- [github.com/asottile/pyupgrade: v3.20.0 → v3.21.0](https://github.com/asottile/pyupgrade/compare/v3.20.0...v3.21.0)
2025-10-13 20:38:45 +00:00
anthony sottile
7ad23528d0
Merge pull request #3554 from pre-commit/all-repos_autofix_all-repos-manual
Some checks failed
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
py310+
2025-10-10 12:08:36 -04:00
anthony sottile
f415f6c4d7 py310+
Committed via https://github.com/asottile/all-repos
2025-10-09 17:44:05 -04:00
Anthony Sottile
99fa9ba5ef
Merge pull request #3544 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-09-23 09:36:35 -04:00
pre-commit-ci[bot]
ad0d4cd427
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.17.1 → v1.18.2](https://github.com/pre-commit/mirrors-mypy/compare/v1.17.1...v1.18.2)
2025-09-22 20:44:04 +00:00
Anthony Sottile
924680e974
Merge pull request #3537 from pre-commit/security-options-null
Some checks failed
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
handle `SecurityOptions: null` in docker response
2025-09-06 14:47:53 -04:00
anthony sottile
2930ea0fcd handle SecurityOptions: null in docker response 2025-09-06 14:40:20 -04:00
Anthony Sottile
b96127c485
Merge pull request #3536 from pre-commit/store-true-default
store_true does not need default=...
2025-09-06 14:28:02 -04:00
Anthony Sottile
954cc3b3b3
Merge pull request #3528 from JulianMaurin/feat/fail-fast
Add fail-fast argument for run command
2025-09-06 14:22:57 -04:00
anthony sottile
e671830402 store_true does not need default=... 2025-09-06 14:20:01 -04:00
JulianMaurin
c78f248c60 Add fail-fast argument for run command 2025-09-06 14:14:23 -04:00
Anthony Sottile
e70b313c80
Merge pull request #3510 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-08-13 10:21:38 -04:00
pre-commit-ci[bot]
87a681f866
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v5.0.0 → v6.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v5.0.0...v6.0.0)
2025-08-11 20:46:13 +00:00
anthony sottile
b74a22d96c v4.3.0
Some checks failed
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / vars (push) Has been cancelled
languages / collector (push) Has been cancelled
languages / language (push) Has been cancelled
2025-08-09 14:54:49 -04:00
Anthony Sottile
cc899de192
Merge pull request #3507 from bc-lee/dart-fix
Make Dart pre-commit hook compatible with the latest Dart SDKs
2025-08-09 14:03:07 -04:00
Byoungchan Lee
2a0bcea757 Downgrade Dart SDK version installed in the CI 2025-08-08 17:40:30 +09:00
Byoungchan Lee
f1cc7a445f Make Dart pre-commit hook compatible with the latest Dart SDKs
Dart introduced sound null safety in version 2.12.0, and as of Dart 3,
null safety is mandatory. Older Dart SDKs allowed both pre-null safety
and null-safe packages, but modern Dart SDKs, where most source code is
null-safe, now reject pre-null safety packages.

The current `pubspec.yaml` template with `sdk: '>=2.10.0'` is
incompatible with recent Dart SDKs, leading to the following error:

An unexpected error has occurred: CalledProcessError: command: ('/path/to/dart-sdk/bin/dart', 'pub', 'get')
return code: 65
stdout:
    Resolving dependencies...
stderr:
    The lower bound of "sdk: '>=2.10.0'" must be 2.12.0'
    or higher to enable null safety.

    The current Dart SDK (3.8.3) only supports null safety.

    For details, see https://dart.dev/null-safety

To ensure compatibility with the modern Dart ecosystem, this commit
updates the minimum Dart SDK version to 2.12.0 or higher,
which implicitly supports null safety.
Additionally, `testing/get-dart.sh` has been updated to verify that
the pre-commit hook functions correctly with the latest Dart versions.
2025-08-08 17:14:59 +09:00
Anthony Sottile
72a3b71f0e
Merge pull request #3504 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-08-04 18:09:19 -04:00
pre-commit-ci[bot]
c8925a457a
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.17.0 → v1.17.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.17.0...v1.17.1)
2025-08-04 20:31:31 +00:00
Anthony Sottile
a5fe6c500c
Merge pull request #3496 from ericphanson/eph/jl-startup
Some checks failed
languages / collector (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / vars (push) Has been cancelled
languages / language (push) Has been cancelled
Julia language: skip startup.jl file
2025-08-02 14:43:29 -04:00
Eric Hanson
6f1f433a9c Julia language: skip startup.jl file 2025-08-02 14:35:27 -04:00
Anthony Sottile
c6817210b1
Merge pull request #3499 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-07-24 11:21:39 +02:00
pre-commit-ci[bot]
4fd4537bc6
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.16.1 → v1.17.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.16.1...v1.17.0)
2025-07-21 20:02:20 +00:00
Anthony Sottile
a1d7bed86f
Merge pull request #3485 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-06-24 15:37:11 -04:00
pre-commit-ci[bot]
d1d5b3d564
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/PyCQA/flake8: 7.2.0 → 7.3.0](https://github.com/PyCQA/flake8/compare/7.2.0...7.3.0)
- [github.com/pre-commit/mirrors-mypy: v1.16.0 → v1.16.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.16.0...v1.16.1)
2025-06-23 19:55:22 +00:00
Anthony Sottile
9c228a0bd8
Merge pull request #3477 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-06-02 17:29:56 -07:00
pre-commit-ci[bot]
d4f0c6e8a7
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.15.0 → v1.16.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.15.0...v1.16.0)
2025-06-02 19:57:10 +00:00
Anthony Sottile
5f0c773e74
Merge pull request #3470 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-05-27 21:36:14 -07:00
pre-commit-ci[bot]
43b426a501
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/reorder-python-imports: v3.14.0 → v3.15.0](https://github.com/asottile/reorder-python-imports/compare/v3.14.0...v3.15.0)
- [github.com/asottile/add-trailing-comma: v3.1.0 → v3.2.0](https://github.com/asottile/add-trailing-comma/compare/v3.1.0...v3.2.0)
- [github.com/asottile/pyupgrade: v3.19.1 → v3.20.0](https://github.com/asottile/pyupgrade/compare/v3.19.1...v3.20.0)
2025-05-26 19:45:48 +00:00
Anthony Sottile
8a4af027a1
Merge pull request #3446 from matthewhughes934/fix-docker-rootless-permission-mounts
Some checks failed
languages / collector (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
languages / vars (push) Has been cancelled
languages / language (push) Has been cancelled
Fix permission errors for mounts in rootless docker
2025-05-23 17:11:14 -04:00
Matthew Hughes
466f6c4a39 Fix permission errors for mounts in rootless docker
By running containers in a rootless docker context as root. This is
because user and group IDs are remapped in the user namespaces uses by
rootless docker, and it's unlikely that the current user ID will map to
the same ID under this remap (see docs[1] for some more details).
Specifically, it means ownership of mounted volumes will not be for the
current user and trying to write can result in permission errors.

This change borrows heavily from an existing PR[2].

The output format of `docker system info` I don't think is
documented/guaranteed anywhere, but it should corresponding to the
format of a `/info` API request to Docker[3]

The added test _hopes_ to avoid regressions in this behaviour, but since
tests aren't run in a rootless docker context on the PR checks (and I
couldn't find an easy way to make it the case) there's still a risk of
regressions sneaking in.

Link: https://docs.docker.com/engine/security/rootless/ [1]
Link: https://github.com/pre-commit/pre-commit/pull/1484/ [2]
Link: https://docs.docker.com/reference/api/engine/version/v1.48/#tag/System/operation/SystemAuth [3]
Co-authored-by: Kurt von Laven <Kurt-von-Laven@users.noreply.github.com>
Co-authored-by: Fabrice Flore-Thébault <ffloreth@redhat.com>
2025-05-23 17:01:10 -04:00
Anthony Sottile
d2b61d0ef2
Merge pull request #3439 from pre-commit/pre-commit-ci-update-config
Some checks failed
languages / vars (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
[pre-commit.ci] pre-commit autoupdate
2025-03-31 15:55:15 -04:00
pre-commit-ci[bot]
43592c2a29 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-03-31 19:44:12 +00:00
pre-commit-ci[bot]
6d47b8d52b
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/setup-cfg-fmt: v2.7.0 → v2.8.0](https://github.com/asottile/setup-cfg-fmt/compare/v2.7.0...v2.8.0)
- [github.com/PyCQA/flake8: 7.1.2 → 7.2.0](https://github.com/PyCQA/flake8/compare/7.1.2...7.2.0)
2025-03-31 19:43:51 +00:00
Anthony Sottile
aa48766b88 v4.2.0
Some checks failed
languages / vars (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
2025-03-18 17:34:49 -04:00
Anthony Sottile
bf6f11dc6c
Merge pull request #3430 from pre-commit/preferential-sys-impl
adjust python default_language_version to prefer versioned exe
2025-03-18 17:26:41 -04:00
Anthony Sottile
3e8d0f5e1c adjust python default_language_version to prefer versioned exe 2025-03-18 14:55:24 -04:00
Anthony Sottile
ff7256cedf
Merge pull request #3425 from tusharsadhwani/ambiguous-ref
Some checks failed
languages / vars (push) Has been cancelled
languages / language (push) Has been cancelled
languages / collector (push) Has been cancelled
main / main-windows (push) Has been cancelled
main / main-linux (push) Has been cancelled
fix: crash on ambiguous ref 'HEAD'
2025-03-15 15:29:29 -04:00
Tushar Sadhwani
b7eb412c79 fix: crash on ambiguous ref 'HEAD' 2025-03-15 15:23:15 -04:00
Anthony Sottile
7b88c63ae6
Merge pull request #3404 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-02-17 16:20:33 -05:00
pre-commit-ci[bot]
94b97e28f7
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/PyCQA/flake8: 7.1.1 → 7.1.2](https://github.com/PyCQA/flake8/compare/7.1.1...7.1.2)
2025-02-17 21:07:26 +00:00
Anthony Sottile
2f93b80484
Merge pull request #3401 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-02-12 09:34:15 -05:00
pre-commit-ci[bot]
4f90a1e88a
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.14.1 → v1.15.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.14.1...v1.15.0)
2025-02-10 22:44:01 +00:00
Anthony Sottile
aba1ce04e7
Merge pull request #3396 from pre-commit/all-repos_autofix_all-repos-sed
upgrade asottile/workflows
2025-01-30 15:05:49 -05:00
Anthony Sottile
e2210c97e2 upgrade asottile/workflows
Committed via https://github.com/asottile/all-repos
2025-01-30 14:58:50 -05:00
Anthony Sottile
804c853d8f
Merge pull request #3390 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-01-20 20:43:00 -05:00
pre-commit-ci[bot]
edd0002e43
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/hhatto/autopep8: v2.3.1 → v2.3.2](https://github.com/hhatto/autopep8/compare/v2.3.1...v2.3.2)
2025-01-20 22:30:07 +00:00
Anthony Sottile
b152e922ef v4.1.0 2025-01-20 13:35:33 -05:00
Anthony Sottile
c3125a4d36
Merge pull request #3389 from lorenzwalthert/dev-always-unset-renv
Fix language:r hook installation when initiated in RStudio
2025-01-20 13:18:20 -05:00
Lorenz Walthert
c2c061cf63 fix: ensure env patch is applied for vanilla emulation
otherwise, installing the hooks when RENV_USER env variable is set (e.g. in RStudio with renv project) will result in executing the installation script in the wrong renv
2025-01-20 13:13:36 -05:00
Anthony Sottile
cd429db5e2
Merge pull request #3382 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-01-07 20:21:38 -05:00
pre-commit-ci[bot]
9b9f8e254d
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.14.0 → v1.14.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.14.0...v1.14.1)
2025-01-06 23:30:19 +00:00
Anthony Sottile
86300a4a7e
Merge pull request #3376 from pre-commit/r-gone
install r on ubuntu runners
2024-12-28 16:16:53 -05:00
Anthony Sottile
77edad8455 install r on ubuntu runners
this was removed in ubuntu-24.04 github actions runner
2024-12-28 16:06:00 -05:00
Anthony Sottile
18b393905e
Merge pull request #3375 from pre-commit/dotnet-tests-ubuntu-latest
update .net tests to use .net 8
2024-12-28 16:03:04 -05:00
Anthony Sottile
31cb945ffb
Merge pull request #3374 from pre-commit/docker-image-tests-ubuntu-22-not-present
fix docker_image test when ubuntu:22.04 image is not pre-pulled
2024-12-28 15:52:16 -05:00
Anthony Sottile
28c3d81bd2 update .net tests to use .net 8
.net 6 and 7 were removed from github actions runners
2024-12-28 15:50:58 -05:00
Anthony Sottile
aa85be9340 fix docker_image test when ubuntu:22.04 image is not pre-pulled 2024-12-28 15:45:05 -05:00
Anthony Sottile
1027596280
Merge pull request #3373 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-12-23 21:10:03 -05:00
pre-commit-ci[bot]
db85eeed2d
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.19.0 → v3.19.1](https://github.com/asottile/pyupgrade/compare/v3.19.0...v3.19.1)
- [github.com/pre-commit/mirrors-mypy: v1.13.0 → v1.14.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.13.0...v1.14.0)
2024-12-23 22:45:24 +00:00
Anthony Sottile
cb14bc2d9c
Merge pull request #3304 from AleksaC/go-toolchain
disable automatic toolchain switching for golang hooks
2024-11-25 18:54:37 -05:00
AleksaC
109628c505 disable automatic toolchain switching for golang hooks 2024-11-25 18:47:18 -05:00
Anthony Sottile
74233a125a
Merge pull request #3348 from fredrikekre/fe/julia
Add support for julia hooks
2024-11-25 18:38:49 -05:00
Fredrik Ekre
85783bdc0b Add support for julia hooks
This patch adds 2nd class support for hooks using julia as the language.
pre-commit will install any dependencies defined in the hooks repo
`Project.toml` file, with support for `additional_dependencies` as well.
Julia doesn't (yet) have a way to install binaries/scripts so for julia
hooks the `entry` value is a (relative) path to a julia script within
the hooks repository. When executing a julia hook the (globally
installed) julia interpreter is prepended to the entry.

Example `.pre-commit-hooks.yaml`:

```yaml
- id: foo
  name: ...
  language: julia
  entry: bin/foo.jl --arg1
```

Example hooks repo: https://github.com/fredrikekre/runic-pre-commit/tree/fe/julia
Accompanying pre-commit.com PR: https://github.com/pre-commit/pre-commit.com/pull/998

Fixes #2689.
2024-11-25 18:31:25 -05:00
Anthony Sottile
9da45a686a
Merge pull request #3345 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-10-28 21:23:33 -04:00
pre-commit-ci[bot]
708ca3b581
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.18.0 → v3.19.0](https://github.com/asottile/pyupgrade/compare/v3.18.0...v3.19.0)
- [github.com/pre-commit/mirrors-mypy: v1.12.1 → v1.13.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.12.1...v1.13.0)
2024-10-28 22:56:52 +00:00
Anthony Sottile
611195a088
Merge pull request #3333 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-10-21 20:42:52 -04:00
Anthony Sottile
0de4c8028a remove unused type ignore 2024-10-21 20:35:56 -04:00
pre-commit-ci[bot]
46de4da34e
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/setup-cfg-fmt: v2.5.0 → v2.7.0](https://github.com/asottile/setup-cfg-fmt/compare/v2.5.0...v2.7.0)
- [github.com/asottile/reorder-python-imports: v3.13.0 → v3.14.0](https://github.com/asottile/reorder-python-imports/compare/v3.13.0...v3.14.0)
- [github.com/asottile/pyupgrade: v3.17.0 → v3.18.0](https://github.com/asottile/pyupgrade/compare/v3.17.0...v3.18.0)
- [github.com/pre-commit/mirrors-mypy: v1.11.2 → v1.12.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.2...v1.12.1)
2024-10-21 22:30:38 +00:00
Anthony Sottile
cc4a522415 v4.0.1 2024-10-08 12:08:49 -04:00
Anthony Sottile
772d7d45d3
Merge pull request #3324 from pre-commit/migrate-config-purelib
fix migrate-config for purelib yaml
2024-10-08 12:01:05 -04:00
Anthony Sottile
222c62bc5d fix migrate-config for purelib yaml 2024-10-08 11:46:48 -04:00
Anthony Sottile
3d5548b487
Merge pull request #3323 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-10-08 06:48:13 -04:00
pre-commit-ci[bot]
4235a877f3
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0)
2024-10-08 00:02:26 +00:00
Anthony Sottile
dbccd57db0 v4.0.0 2024-10-05 14:58:22 -04:00
Anthony Sottile
d07e52901c
Merge pull request #3320 from pre-commit/remove-python-venv
remove deprecated python_venv alias
2024-10-05 13:58:57 -04:00
Anthony Sottile
801b956304 remove deprecated python_venv alias 2024-10-05 13:30:25 -04:00
Anthony Sottile
a2f7b80e89
Merge pull request #3315 from pre-commit/warn-deprecated-stage-names-on-init
add warning for deprecates stages for remote repos on init
2024-09-30 20:48:47 -04:00
Anthony Sottile
d31722386e add warning for deprecates stages for remote repos on init 2024-09-30 20:41:50 -04:00
Anthony Sottile
7555e11098
Merge pull request #3314 from pre-commit/remove-log-info-mock
replace log_info_mock with pytest's caplog
2024-09-30 20:07:00 -04:00
Anthony Sottile
05e365fe08
Merge pull request #3313 from pre-commit/default-stages-warning
add warning for deprecated stages values in `default_stages`
2024-09-30 20:02:15 -04:00
Anthony Sottile
1d2f1c0cce replace log_info_mock with pytest's caplog 2024-09-30 19:58:16 -04:00
Anthony Sottile
33e020f315 add warning for deprecated stages values in default_stages 2024-09-30 19:22:14 -04:00
Anthony Sottile
e7cfc0d2cb
Merge pull request #3312 from pre-commit/warning-for-old-stage-names
add warning for deprecated stages names
2024-09-30 18:48:39 -04:00
Anthony Sottile
7441a62eb1 add warning for deprecated stages names 2024-09-30 18:41:13 -04:00
Anthony Sottile
eec11bd124
Merge pull request #3311 from pre-commit/sensible-regex-for-meta
also apply sensible regex warning for `repo: meta`
2024-09-30 18:16:05 -04:00
Anthony Sottile
fa08d1d637 also apply sensible regex warning for repo: meta 2024-09-30 18:09:04 -04:00
Anthony Sottile
6c068a78d6
Merge pull request #3199 from ThisGuyCodes/thisguycodes/upgrade-ruby-build
Upgrade to ruby-build v20240501
2024-09-28 13:16:09 -04:00
Anthony Sottile
c9454e2ec3 regenerate ruby-build archive 2024-09-28 13:07:55 -04:00
Anthony Sottile
e687548842 regenerate archives with python3.12 2024-09-28 13:07:38 -04:00
Travis Johnson
a4e4cef335 Upgrade to ruby-build v20240917 2024-09-28 13:07:38 -04:00
Anthony Sottile
de8590064e
Merge pull request #3302 from pre-commit/migrate-config-stages
migrate-config rewrites deprecated stages
2024-09-16 20:45:21 -04:00
Anthony Sottile
5679399d90 migrate-config rewrites deprecated stages 2024-09-16 20:36:33 -04:00
Anthony Sottile
a7b671a758
Merge pull request #3301 from pre-commit/yaml-rewrite
change migrate-config to use yaml parse tree instead
2024-09-16 20:24:35 -04:00
Anthony Sottile
364e6d77f0 change migrate-config to use yaml parse tree instead 2024-09-16 20:16:16 -04:00
Anthony Sottile
504149d2ca
Merge pull request #3286 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-08-26 19:03:22 -04:00
pre-commit-ci[bot]
c2c68d985c
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.11.1 → v1.11.2](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.1...v1.11.2)
2024-08-26 22:18:35 +00:00
Anthony Sottile
0f8f383d53
Merge pull request #3275 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-08-05 18:46:27 -04:00
pre-commit-ci[bot]
d5c21926ab
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/PyCQA/flake8: 7.1.0 → 7.1.1](https://github.com/PyCQA/flake8/compare/7.1.0...7.1.1)
- [github.com/pre-commit/mirrors-mypy: v1.11.0 → v1.11.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.0...v1.11.1)
2024-08-05 22:39:33 +00:00
Anthony Sottile
8a3ee454a2
Merge pull request #3270 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-07-29 21:36:13 -04:00
pre-commit-ci[bot]
917e2102be [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-07-29 21:59:19 +00:00
pre-commit-ci[bot]
9d4ab670d1
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.16.0 → v3.17.0](https://github.com/asottile/pyupgrade/compare/v3.16.0...v3.17.0)
2024-07-29 21:59:01 +00:00
Anthony Sottile
d46423ffe1 v3.8.0 2024-07-28 15:58:29 -04:00
Anthony Sottile
8133abd730
Merge pull request #3265 from lorenzwalthert/issue-3206
Support health check for `language: r`
2024-07-28 15:54:27 -04:00
Lorenz Walthert
da0c1d0cfa implement health check for language:r 2024-07-28 15:44:07 -04:00
Anthony Sottile
f641f6a157
Merge pull request #3264 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-07-28 15:07:33 -04:00
Anthony Sottile
a68a19d217 fixes for mypy 1.11 2024-07-28 14:57:13 -04:00
pre-commit-ci[bot]
88317ddb34
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.10.1 → v1.11.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.1...v1.11.0)
2024-07-22 22:04:19 +00:00
Anthony Sottile
faa6f8c70c
Merge pull request #3244 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-07-05 18:34:07 -04:00
pre-commit-ci[bot]
f632459bc6
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.10.0 → v1.10.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.0...v1.10.1)
2024-07-01 23:34:14 +00:00
Anthony Sottile
0252908c27
Merge pull request #3240 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-06-25 14:16:25 -04:00
pre-commit-ci[bot]
69b5dce12a
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/hhatto/autopep8: v2.3.0 → v2.3.1](https://github.com/hhatto/autopep8/compare/v2.3.0...v2.3.1)
2024-06-24 21:49:02 +00:00
Anthony Sottile
d56502acab
Merge pull request #3237 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-06-18 20:14:03 -04:00
pre-commit-ci[bot]
49a9664cd0
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/hhatto/autopep8: v2.2.0 → v2.3.0](https://github.com/hhatto/autopep8/compare/v2.2.0...v2.3.0)
- [github.com/PyCQA/flake8: 7.0.0 → 7.1.0](https://github.com/PyCQA/flake8/compare/7.0.0...7.1.0)
2024-06-17 21:57:20 +00:00
Anthony Sottile
60db5d78d1
Merge pull request #3227 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-06-13 10:28:30 -04:00
pre-commit-ci[bot]
9dd247898c
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.15.2 → v3.16.0](https://github.com/asottile/pyupgrade/compare/v3.15.2...v3.16.0)
2024-06-10 21:56:51 +00:00
Anthony Sottile
15d9f7f61e
Merge pull request #3217 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-06-03 21:46:10 -04:00
pre-commit-ci[bot]
1f128556e4
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/reorder-python-imports: v3.12.0 → v3.13.0](https://github.com/asottile/reorder-python-imports/compare/v3.12.0...v3.13.0)
- [github.com/hhatto/autopep8: v2.1.1 → v2.2.0](https://github.com/hhatto/autopep8/compare/v2.1.1...v2.2.0)
2024-06-03 21:47:18 +00:00
Anthony Sottile
dd144c95f6
Merge pull request #3207 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-05-27 17:46:19 -04:00
pre-commit-ci[bot]
5526bb2137
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/hhatto/autopep8: v2.1.0 → v2.1.1](https://github.com/hhatto/autopep8/compare/v2.1.0...v2.1.1)
2024-05-27 21:34:15 +00:00
Anthony Sottile
9ee0768353 v3.7.1 2024-05-10 21:24:51 -04:00
Anthony Sottile
eeac061b31
Merge pull request #3201 from pre-commit/rust-default-language-version
determine rust default language version independent of rust-toolchain.toml
2024-05-10 21:21:22 -04:00
Anthony Sottile
296f59266e determine rust default language version independent of rust-toolchain.toml 2024-05-10 17:06:29 -04:00
Anthony Sottile
16023286d2
Merge pull request #3193 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-04-29 21:45:54 -04:00
pre-commit-ci[bot]
0142f45322 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.9.0 → v1.10.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.9.0...v1.10.0)
2024-04-29 21:38:52 -04:00
Anthony Sottile
d7e21cd29c
Merge pull request #3194 from pre-commit/handle-readonly-3-12
adjust _handle_readonly for typeshed updates
2024-04-29 21:36:47 -04:00
Anthony Sottile
5c3d006443
use a simpler gem for testing additional_dependencies
tins required building bigdecimal, whereas jmespath is self-contained
2024-04-29 21:28:16 -04:00
Anthony Sottile
0d4c6da36e adjust _handle_readonly for typeshed updates 2024-04-29 21:05:41 -04:00
Anthony Sottile
85fe18253f
Merge pull request #3176 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-04-10 11:20:08 -04:00
pre-commit-ci[bot]
74d05b444d
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.5.0...v4.6.0)
2024-04-08 22:08:29 +00:00
Anthony Sottile
a9f19f4cc0
Merge pull request #3169 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-03-27 17:20:38 -04:00
pre-commit-ci[bot]
4e121ef25c
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.15.1 → v3.15.2](https://github.com/asottile/pyupgrade/compare/v3.15.1...v3.15.2)
2024-03-25 21:31:39 +00:00
Anthony Sottile
7b4667e9e6 v3.7.0 2024-03-24 13:37:19 -04:00
Anthony Sottile
d46c8fc051
Merge pull request #3168 from pre-commit/fix-fail-fast
fix per-hook fail_fast to not fail on previous failures
2024-03-24 13:28:36 -04:00
Anthony Sottile
fc622159a6 fix per-hook fail_fast to not fail on previous failures 2024-03-24 13:17:00 -04:00
Anthony Sottile
716da1e49c
Merge pull request #3155 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-03-20 09:29:19 -04:00
pre-commit-ci[bot]
0939c11b4f
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/hhatto/autopep8: v2.0.4 → v2.1.0](https://github.com/hhatto/autopep8/compare/v2.0.4...v2.1.0)
2024-03-18 21:47:27 +00:00
Anthony Sottile
3bdf9fb91b
Merge pull request #3150 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-03-13 10:01:23 -04:00
pre-commit-ci[bot]
75b3e52e57
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.8.0 → v1.9.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.8.0...v1.9.0)
2024-03-13 00:16:12 +00:00
Anthony Sottile
5e11c266ae
Merge pull request #3122 from glehmann/docker-tty
give docker a tty output when expecting color
2024-03-02 11:57:30 -05:00
Gaëtan Lehmann
e58009684c give docker a tty output when expecting color
this makes the behavior more consistent with the system language
and would help the executable run in a docker container to
produce a colored output.
2024-03-02 11:51:34 -05:00
Anthony Sottile
7b868c3508
Merge pull request #3132 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-02-20 10:21:58 -05:00
pre-commit-ci[bot]
a768c038e3
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.15.0 → v3.15.1](https://github.com/asottile/pyupgrade/compare/v3.15.0...v3.15.1)
2024-02-20 00:02:29 +00:00
Anthony Sottile
e525726855 v3.6.2 2024-02-18 13:19:11 -05:00
Anthony Sottile
3187538d2b
Merge pull request #3130 from pre-commit/golang-build-during-commit-a
fix building golang hooks during `commit --all`
2024-02-18 13:14:37 -05:00
Anthony Sottile
61d9c95cc1 fix building golang hooks during commit --all 2024-02-18 13:03:44 -05:00
Anthony Sottile
15bd0c7993 v3.6.1 2024-02-10 14:45:43 -05:00
Anthony Sottile
92678c3fa2
Merge pull request #3126 from pre-commit/crlf-only-diff
staged_files_only can handle a crlf-only diff
2024-02-10 14:10:29 -05:00
Anthony Sottile
032d8e2704 staged_files_only can handle a crlf-only diff 2024-02-10 14:01:49 -05:00
Anthony Sottile
73848383f2
Merge pull request #3110 from untitaker/pythonexecutable
Pop PYTHONEXECUTABLE
2024-01-12 11:39:51 -05:00
pre-commit-ci[bot]
96e0712f43 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-01-12 16:32:44 +00:00
Markus Unterwaditzer
3388e2dbdf Pop PYTHONEXECUTABLE 2024-01-12 17:30:01 +01:00
Anthony Sottile
10f8853631
Merge pull request #3107 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-01-08 17:41:12 -05:00
pre-commit-ci[bot]
9682f93e31
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/PyCQA/flake8: 6.1.0 → 7.0.0](https://github.com/PyCQA/flake8/compare/6.1.0...7.0.0)
2024-01-08 20:21:06 +00:00
Anthony Sottile
5f8ebaefa9
Merge pull request #3102 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-12-26 13:50:45 -05:00
pre-commit-ci[bot]
9cce283422
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.7.1 → v1.8.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.7.1...v1.8.0)
2023-12-25 20:20:03 +00:00
Anthony Sottile
9c9983dba0 v3.6.0 2023-12-09 16:24:52 -05:00
Anthony Sottile
7dc0a59ee5
Merge pull request #3093 from pre-commit/removeprefix
python 3.9+: use removeprefix
2023-12-09 16:13:25 -05:00
Anthony Sottile
d3fa7f415c
Merge pull request #3092 from pre-commit/minimum-version-first
attempt minimum_pre_commit_version first when parsing configs
2023-12-09 16:04:44 -05:00
Anthony Sottile
08478ec176 python 3.9+: use removeprefix 2023-12-09 16:04:25 -05:00
Anthony Sottile
047439abff attempt minimum_pre_commit_version first when parsing configs 2023-12-09 15:34:16 -05:00
Anthony Sottile
23a2b7360e
Merge pull request #3079 from edgarrmondragon/deprecation-rmtree-onerror
Address deprecation warning in `shutil.rmtree(onerror=...)`
2023-12-09 14:59:09 -05:00
Edgar Ramírez Mondragón
cffabe54be Address deprecation warning in shutil.rmtree(onerror=...) 2023-12-09 14:51:01 -05:00
Anthony Sottile
51df34e5fb
Merge pull request #3078 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-11-27 17:55:04 -05:00
pre-commit-ci[bot]
e36cefc8bd
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.7.0 → v1.7.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.7.0...v1.7.1)
2023-11-27 20:01:19 +00:00
Anthony Sottile
2280645d0e
Merge pull request #3064 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-11-13 21:41:08 -05:00
pre-commit-ci[bot]
1d474994e0
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.6.1 → v1.7.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.6.1...v1.7.0)
2023-11-13 20:35:35 +00:00
Anthony Sottile
14169eb31d
Merge pull request #3043 from pre-commit/jaraco
3.13 removed the simpler importlib.resources api
2023-10-28 14:50:43 -04:00
Anthony Sottile
75f2710bd4 3.13 removed the simpler importlib.resources api 2023-10-28 14:39:49 -04:00
Anthony Sottile
762e68173b
Merge pull request #3042 from pre-commit/py39
python3.9+
2023-10-28 14:35:26 -04:00
Anthony Sottile
7f15dc75ee python3.9+ 2023-10-28 14:20:37 -04:00
Anthony Sottile
5f4ed54cfe
Merge pull request #3038 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-10-23 16:57:56 -04:00
pre-commit-ci[bot]
c69e32e925
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.6.0 → v1.6.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.6.0...v1.6.1)
2023-10-23 20:28:04 +00:00
Anthony Sottile
48f0dc9615
Merge pull request #3033 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-10-16 22:08:24 -04:00
pre-commit-ci[bot]
44b625ebd3
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.5.1 → v1.6.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.5.1...v1.6.0)
2023-10-16 20:03:36 +00:00
Anthony Sottile
61cc55a59c v3.5.0 2023-10-13 11:57:20 -04:00
Anthony Sottile
c9945b9aa3
Merge pull request #3029 from adamchainz/improve_duration_timing
Improve hook duration timing
2023-10-13 11:49:53 -04:00
Adam Johnson
d988767b41 Improve hook duration timing 2023-10-13 16:40:30 +01:00
Anthony Sottile
0d8b2451ca
Merge pull request #3023 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-10-09 17:08:44 -04:00
pre-commit-ci[bot]
155c521348 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.4.0 → v4.5.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.4.0...v4.5.0)
- [github.com/asottile/pyupgrade: v3.14.0 → v3.15.0](https://github.com/asottile/pyupgrade/compare/v3.14.0...v3.15.0)
2023-10-09 16:58:51 -04:00
Anthony Sottile
676e51aa5e
Merge pull request #3024 from pre-commit/pick-shebang-path-without-spaces
use sys.executable instead of echo.exe in parse_shebang
2023-10-09 16:58:43 -04:00
Anthony Sottile
997ea0ad52 use sys.executable instead of echo.exe in parse_shebang
the GHA runners now have echo.exe in a path with spaces
2023-10-09 16:49:30 -04:00
Anthony Sottile
19aa121db0
Merge pull request #3016 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-10-03 12:57:10 -04:00
pre-commit-ci[bot]
a4ab977cc3
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/setup-cfg-fmt: v2.4.0 → v2.5.0](https://github.com/asottile/setup-cfg-fmt/compare/v2.4.0...v2.5.0)
- [github.com/asottile/reorder-python-imports: v3.11.0 → v3.12.0](https://github.com/asottile/reorder-python-imports/compare/v3.11.0...v3.12.0)
- [github.com/asottile/pyupgrade: v3.13.0 → v3.14.0](https://github.com/asottile/pyupgrade/compare/v3.13.0...v3.14.0)
2023-10-03 08:58:04 +00:00
Anthony Sottile
3f3760b86c
Merge pull request #3011 from hack3ric/bump-node-and-go-version
Bump Node.js version to 18.14.0 and Go to 1.21.1
2023-09-26 14:16:57 -04:00
Anthony Sottile
051f4e2550
Merge pull request #3012 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-09-26 09:04:10 -04:00
pre-commit-ci[bot]
c68c6b944a
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.11.0 → v3.13.0](https://github.com/asottile/pyupgrade/compare/v3.11.0...v3.13.0)
2023-09-26 07:23:52 +00:00
Eric Long
5e05b01215
Bump Node.js version to 18.14.0 and Go to 1.21.1
On riscv64, nodeenv will pull binary from unofficial-builds [1], and
unfortunately 18.13.0 seems to be the only version above 18 that is
missing riscv64 builds. Shifting the version slightly to make test work.

Go's binary now ships with linux/riscv64 binary since 1.21.
2023-09-25 17:02:13 +08:00
Anthony Sottile
84f91646bb
Merge pull request #3006 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-09-19 09:06:27 -04:00
pre-commit-ci[bot]
d33801e781
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/reorder-python-imports: v3.10.0 → v3.11.0](https://github.com/asottile/reorder-python-imports/compare/v3.10.0...v3.11.0)
- [github.com/asottile/pyupgrade: v3.10.1 → v3.11.0](https://github.com/asottile/pyupgrade/compare/v3.10.1...v3.11.0)
2023-09-19 07:06:08 +00:00
Anthony Sottile
f56b75dd77
Merge pull request #2998 from pre-commit/short-circuit
Short-circuit `check-hooks-apply` and `check-useless-excludes` hooks
2023-09-11 19:52:51 -04:00
Max R
5d692d7e06 Short-circuit hooks 2023-09-11 19:41:40 -04:00
Anthony Sottile
0845e4e816
Merge pull request #2996 from RoelAdriaans/feature/fix-1983-npm
Use the --include command, hides warning messages
2023-09-11 19:12:06 -04:00
Anthony Sottile
5f4b828999
Merge pull request #3000 from pre-commit/refactor-tests
Refactor `target_concurrency` tests
2023-09-10 10:45:14 -04:00
Max R
9ac229dad8 Refactor target_concurrency tests 2023-09-10 08:39:19 -04:00
Roel Adriaans
493c20ce91 Use the --include command, hides warning messages
Fixes #1983
2023-09-08 15:12:54 +02:00
Anthony Sottile
e2c6a822c7
Merge pull request #2991 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-09-05 09:07:18 -04:00
pre-commit-ci[bot]
818240e425
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/add-trailing-comma: v3.0.1 → v3.1.0](https://github.com/asottile/add-trailing-comma/compare/v3.0.1...v3.1.0)
- https://github.com/pre-commit/mirrors-autopep8https://github.com/hhatto/autopep8
2023-09-05 06:46:49 +00:00
Anthony Sottile
fe9ba6b53f v3.4.0 2023-09-02 13:09:13 -04:00
Anthony Sottile
ac42dc586a
Merge pull request #2979 from jdb8/cpu-sched-getaffinity
Use os.sched_getaffinity for cpu counts when available
2023-08-30 13:57:54 -04:00
Joe Bateson
ea8244b229 Use os.sched_getaffinity for cpu counts when available 2023-08-30 13:39:20 -04:00
Anthony Sottile
9ebda91889
Merge pull request #2980 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-08-29 11:15:54 -04:00
pre-commit-ci[bot]
3dd1875df8
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-autopep8: v2.0.2 → v2.0.4](https://github.com/pre-commit/mirrors-autopep8/compare/v2.0.2...v2.0.4)
2023-08-29 05:38:11 +00:00
Anthony Sottile
bde292b510
Merge pull request #2972 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-08-22 09:09:16 -04:00
pre-commit-ci[bot]
a4ae868633
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.5.0 → v1.5.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.5.0...v1.5.1)
2023-08-22 06:16:21 +00:00
Anthony Sottile
0c3d605fa0
Merge pull request #2971 from chriskuehl/fix-signal-death
Fix exit code for commands terminated by signals
2023-08-21 21:30:11 -04:00
Chris Kuehl
5a4b5b1f8e Fix exit code for commands terminated by signals
Fixes https://github.com/pre-commit/pre-commit/issues/2970
2023-08-21 20:02:27 -05:00
Anthony Sottile
a1f1d19156
Merge pull request #2961 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-08-15 10:19:29 -04:00
pre-commit-ci[bot]
93b1a14402
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.4.1 → v1.5.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.4.1...v1.5.0)
2023-08-15 06:03:09 +00:00
Anthony Sottile
23df082edc
Merge pull request #2958 from fangfufu/typo-fix
fix typo in CONTRIBUTING.md
2023-08-14 18:01:42 -04:00
Fufu Fang
1803db979f
fix typo in CONTRIBUTING.md 2023-08-14 11:00:17 +01:00
Anthony Sottile
b2ff5e401b
Merge pull request #2949 from pre-commit/asottile-patch-1
update hello world go test
2023-08-01 12:17:31 -04:00
Anthony Sottile
8c75a26f2d
update hello world go test 2023-08-01 12:08:52 -04:00
Anthony Sottile
13b673ce3c
Merge pull request #2946 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-08-01 11:56:47 -04:00
pre-commit-ci[bot]
3557077bbc
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/add-trailing-comma: v3.0.0 → v3.0.1](https://github.com/asottile/add-trailing-comma/compare/v3.0.0...v3.0.1)
- [github.com/asottile/pyupgrade: v3.9.0 → v3.10.1](https://github.com/asottile/pyupgrade/compare/v3.9.0...v3.10.1)
- [github.com/PyCQA/flake8: 6.0.0 → 6.1.0](https://github.com/PyCQA/flake8/compare/6.0.0...6.1.0)
2023-08-01 07:08:53 +00:00
Anthony Sottile
2bffc0ad85
Merge pull request #2932 from alunduil/haskell-language
Add haskell language support to pre-commit.
2023-07-22 16:39:17 -04:00
Alex Brandt
60273ca81e Add haskell language support to pre-commit. 2023-07-22 16:28:48 -04:00
Anthony Sottile
f8488e36c8
Merge pull request #2929 from pre-commit/s#helper#lang_base#g
`s/helpers/lang_base/g` in docs
2023-07-17 13:47:20 -04:00
Max R
d537c09032 s/helpers/lang_base/g 2023-07-17 09:36:47 -04:00
Anthony Sottile
9660d3b451
Merge pull request #2927 from pre-commit/mxr-patch-1
Fix link to `language` API
2023-07-16 16:51:06 -04:00
Max R
5e4af63e85
Fix link to language API 2023-07-16 15:00:55 -04:00
Anthony Sottile
4f73b8a2af
Merge pull request #2924 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-07-11 06:28:48 -04:00
pre-commit-ci[bot]
9bf6856db3
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.8.0 → v3.9.0](https://github.com/asottile/pyupgrade/compare/v3.8.0...v3.9.0)
2023-07-11 05:21:22 +00:00
Anthony Sottile
8bf22bd3c1
Merge pull request #2917 from pre-commit/py38-shelx-join
shlex.join is always available in 3.8+
2023-07-01 17:33:28 -04:00
Anthony Sottile
1c439b5a79 shlex.join is always available in 3.8+ 2023-07-01 17:22:42 -04:00
Anthony Sottile
3126802bf4
Merge pull request #2916 from pre-commit/all-repos_autofix_add-trailing-comma-3
updates for add-trailing-comma 3.x
2023-07-01 16:57:16 -04:00
Anthony Sottile
e72699b9ef updates for add-trailing-comma 3.x
Committed via https://github.com/asottile/all-repos
2023-07-01 16:47:06 -04:00
Anthony Sottile
8891089cf5
Merge pull request #2914 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-06-27 00:12:59 -04:00
pre-commit-ci[bot]
854f698531
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.3.0 → v1.4.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.3.0...v1.4.1)
2023-06-27 03:51:58 +00:00
Anthony Sottile
c25ea47cf7
Merge pull request #2908 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-06-20 19:13:54 -07:00
pre-commit-ci[bot]
f94744a699
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/reorder-python-imports: v3.9.0 → v3.10.0](https://github.com/asottile/reorder-python-imports/compare/v3.9.0...v3.10.0)
- [github.com/asottile/pyupgrade: v3.6.0 → v3.7.0](https://github.com/asottile/pyupgrade/compare/v3.6.0...v3.7.0)
2023-06-20 04:33:31 +00:00
Anthony Sottile
5da4258b17 v3.3.3 2023-06-13 19:11:02 -04:00
Anthony Sottile
e891f8606e
Merge pull request #2905 from jaysoffian/fix-gem-install
Force gem installation into envdir
2023-06-13 19:09:34 -04:00
pre-commit-ci[bot]
50b1511a5b [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-06-13 22:04:03 +00:00
Jay Soffian
9a7ed8be09 Force gem installation into envdir
RubyGems allows OS packagers to specify defaults for `--install-dir`
and `--bindir` and these take precedence over `GEM_HOME`. The only way
to override the defaults is to explicitly specify the options ourselves
when running `gem install`.

Examples of OSes where this is the case are RedHat 9.2 and Gentoo.

Fixes #2799.
2023-06-13 18:03:52 -04:00
Anthony Sottile
f073f8e13c
Merge pull request #2904 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-06-13 04:49:13 -04:00
pre-commit-ci[bot]
1fc28903ab
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/add-trailing-comma: v2.4.0 → v2.5.1](https://github.com/asottile/add-trailing-comma/compare/v2.4.0...v2.5.1)
- [github.com/asottile/pyupgrade: v3.4.0 → v3.6.0](https://github.com/asottile/pyupgrade/compare/v3.4.0...v3.6.0)
2023-06-13 03:58:21 +00:00
Anthony Sottile
c7f472d176
Merge pull request #2903 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-06-06 00:00:19 -04:00
pre-commit-ci[bot]
5d273951e0 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-06-06 03:14:27 +00:00
pre-commit-ci[bot]
f88cc61256
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/setup-cfg-fmt: v2.2.0 → v2.3.0](https://github.com/asottile/setup-cfg-fmt/compare/v2.2.0...v2.3.0)
2023-06-06 03:14:12 +00:00
Anthony Sottile
c716de12f7
Merge pull request #2901 from pre-commit/all-repos_autofix_all-repos-sed
fix tags trigger for github actions
2023-06-01 19:29:26 -04:00
Anthony Sottile
f4a2d52bb4 fix tags trigger for github actions
the old syntax worked for azure pipelines but not GHA

Committed via https://github.com/asottile/all-repos
2023-06-01 19:12:46 -04:00
Anthony Sottile
8034430539
Merge pull request #2889 from pre-commit/zipapp-make-standalone
use distlib inside the zipapp docker image
2023-05-17 16:15:56 -07:00
Anthony Sottile
18348f5d0d use distlib inside the zipapp docker image 2023-05-17 18:58:11 -04:00
Anthony Sottile
a0a734750e v3.3.2 2023-05-17 18:36:52 -04:00
Anthony Sottile
bcd8274e1c
Merge pull request #2885 from lorenzwalthert/avoid-quote-write-to-tempfile
More robust R hooks for Windows
2023-05-17 15:20:36 -07:00
Lorenz Walthert
cd09c3525e avoid quoting and escaping while installing R hooks by writing code to tempfile instead of execute R code inline 2023-05-17 18:00:00 -04:00
Anthony Sottile
c389ac0ba8
Merge pull request #2886 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-05-16 07:51:08 -07:00
pre-commit-ci[bot]
64985bd63d
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.2.0 → v1.3.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.2.0...v1.3.0)
2023-05-16 03:20:34 +00:00
Anthony Sottile
f5a98eb152
Merge pull request #2883 from pre-commit/install-swift-not-needed
swift is included in github actions
2023-05-13 17:50:47 -07:00
Anthony Sottile
08b670ff9e swift is included in github actions 2023-05-13 16:37:02 -07:00
Anthony Sottile
72b7b53ae3
Merge pull request #2884 from pre-commit/typo
fix typo in testing/languages
2023-05-13 13:39:56 -07:00
Anthony Sottile
9c2a01186b fix typo in testing/languages 2023-05-13 16:27:14 -04:00
Anthony Sottile
ee49d4289f
Merge pull request #2882 from pre-commit/languages-always-run
make some files trigger all languages
2023-05-13 13:19:21 -07:00
Anthony Sottile
926071b6a7 make some files trigger all languages 2023-05-13 16:03:14 -04:00
Anthony Sottile
0deb445a73
Merge pull request #2881 from pre-commit/r-does-not-support-language-version
r does not support language_version currently
2023-05-13 12:54:10 -07:00
Anthony Sottile
8923fa368a r does not support language_version currently 2023-05-13 15:46:34 -04:00
Anthony Sottile
cf3e826070
Merge pull request #2879 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-05-09 08:40:54 -04:00
pre-commit-ci[bot]
1dd85c904e
[pre-commit.ci] pre-commit autoupdate
updates:
- https://github.com/asottile/reorder_python_importshttps://github.com/asottile/reorder-python-imports
- [github.com/asottile/pyupgrade: v3.3.2 → v3.4.0](https://github.com/asottile/pyupgrade/compare/v3.3.2...v3.4.0)
2023-05-09 04:04:18 +00:00
Anthony Sottile
51104fa94a v3.3.1 2023-05-02 10:07:25 -04:00
Anthony Sottile
0fd2501503
Merge pull request #2866 from pre-commit/autoupdate-hack-windows
add partial clone hack to fix autoupdate for windows
2023-05-02 10:05:33 -04:00
Anthony Sottile
420a15f87e add partial clone hack to fix autoupdate for windows 2023-05-02 09:54:25 -04:00
Anthony Sottile
4c0623963f v3.3.0 2023-05-01 18:23:50 -04:00
Anthony Sottile
5df9eb9d66
Merge pull request #2863 from pre-commit/parallel-autoupdate
add --jobs option to autoupdate
2023-04-29 15:57:36 -04:00
Anthony Sottile
ddbee32ad0 add --jobs option to autoupdate 2023-04-29 15:42:01 -04:00
Anthony Sottile
bab5f70a38
Merge pull request #2861 from pre-commit/autoupdate-without-store
perform autoupdate without Store contention
2023-04-29 15:41:45 -04:00
Anthony Sottile
4f045cbc21 perform autoupdate without Store contention 2023-04-29 15:32:20 -04:00
Anthony Sottile
27d77fc8bc
Merge pull request #2860 from pre-commit/autoupdate-dash-C
use -C for git commands in autoupdate
2023-04-29 15:24:41 -04:00
Anthony Sottile
e885f2e76e use -C for git commands in autoupdate 2023-04-29 15:12:51 -04:00
Anthony Sottile
148df0d718
Merge pull request #2859 from pre-commit/blobless-clone
use blobless clone for faster autoupdate
2023-04-29 13:37:20 -04:00
Anthony Sottile
4727922b93 use blobless clone for faster autoupdate 2023-04-29 13:29:00 -04:00
Anthony Sottile
5cd99d81ad
Merge pull request #2846 from jalessio/upgrade-to-ruby-build-v20230330
Upgrade to ruby-build v20230330
2023-04-29 13:03:19 -04:00
Anthony Sottile
8656797c78
Merge pull request #2854 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-04-25 00:53:45 -04:00
pre-commit-ci[bot]
6f941298a4
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.3.1 → v3.3.2](https://github.com/asottile/pyupgrade/compare/v3.3.1...v3.3.2)
2023-04-25 03:20:55 +00:00
Jamie Alessio
cfcb88364e Upgrade to ruby-build v20230330 2023-04-18 10:58:57 -07:00
Anthony Sottile
6896025288
Merge pull request #2843 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-04-12 16:07:39 -04:00
pre-commit-ci[bot]
f5a716f1b1
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.1.1 → v1.2.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.1.1...v1.2.0)
2023-04-11 02:57:43 +00:00
Anthony Sottile
5027592625 v3.2.2 2023-04-03 16:31:09 -04:00
Anthony Sottile
597bf7caf8
Merge pull request #2836 from edelabar/main
Change deprecated `swift build` `-C` command line option to replacement `--package-path`
2023-04-03 16:25:11 -04:00
Eric DeLabar
84f040f58a
fix #2235 2023-04-03 15:50:55 -04:00
Anthony Sottile
bb49560dc9 v3.2.1 2023-03-25 14:02:57 -04:00
Anthony Sottile
0477abd3ce
Merge pull request #2827 from pre-commit/cargo-home-during-rustup
set CARGO_HOME while executing rustup
2023-03-25 14:00:25 -04:00
Anthony Sottile
ee71a9345c set CARGO_HOME while executing rustup 2023-03-25 13:06:22 -04:00
Anthony Sottile
df2cada973 v3.2.0 2023-03-17 14:26:34 -04:00
Anthony Sottile
fdb9ede460
Merge pull request #2820 from pre-commit/clear-rust-registry-cache
don't set CARGO_HOME in rust
2023-03-17 13:08:37 -04:00
Anthony Sottile
a412e5492d don't set CARGO_HOME in rust
this adds a 270 MB registry cache in the output
2023-03-17 12:55:34 -04:00
Anthony Sottile
d5f919e72f
Merge pull request #2815 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-03-14 00:57:29 -04:00
pre-commit-ci[bot]
7a7772fcda
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.0.1 → v1.1.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.0.1...v1.1.1)
2023-03-14 03:19:10 +00:00
Anthony Sottile
4caea677c6
Merge pull request #2809 from m-rsha/move-slow-repository-tests
move slowest python-specific tests out of repository_test
2023-03-12 11:11:04 -04:00
marsha
d3c0a66d23 move slowest python-specific tests out of repository_test 2023-03-12 08:24:38 -05:00
Anthony Sottile
950451e1ef
Merge pull request #2725 from mgaligniana/issue-2582
Add pre-rebase hook support
2023-03-11 15:26:52 -05:00
Marcelo Galigniana
f39154f69f Add pre-rebase hook support 2023-03-11 15:17:42 -05:00
Anthony Sottile
f2661bfc31
Merge pull request #2808 from pre-commit/hook-stage-and-type-match
make --hook-type and stages match
2023-03-11 14:51:16 -05:00
Anthony Sottile
e3e17a1617 make --hook-type and stages match 2023-03-11 14:26:14 -05:00
Anthony Sottile
02e9680a46
Merge pull request #2805 from pre-commit/durations
show 20 slowest durations in CI
2023-03-09 11:12:00 -05:00
Anthony Sottile
8ab9747b33 show 20 slowest durations in CI 2023-03-09 11:00:31 -05:00
Anthony Sottile
321a90e0a1
Merge pull request #2798 from m-rsha/spargs-test
rewrite `args with spaces` test to not require python
2023-03-09 09:48:17 -05:00
marsha
63a180a935 rewrite args with spaces test to not require python 2023-03-09 00:41:27 -06:00
Anthony Sottile
fe73f519c7
Merge pull request #2801 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-03-06 22:01:43 -05:00
pre-commit-ci[bot]
0616c0abf7
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-autopep8: v2.0.1 → v2.0.2](https://github.com/pre-commit/mirrors-autopep8/compare/v2.0.1...v2.0.2)
2023-03-07 02:52:32 +00:00
Anthony Sottile
321685ee0e
Merge pull request #2796 from m-rsha/os-name
prefer `sys.platform` over `os.name` when checking for windows OS
2023-03-04 01:20:22 -05:00
marsha
5ce4a549d3 prefer sys.platform over os.name when checking for windows OS 2023-03-03 22:13:07 -06:00
Anthony Sottile
2822de9aa6 v3.1.1 2023-02-27 21:07:23 -05:00
Anthony Sottile
7f386a752e
Merge pull request #2788 from pre-commit/rustup-home-temporary
set RUSTUP_HOME when using a non-system rust
2023-02-27 21:02:12 -05:00
Anthony Sottile
2700a7d622 set RUSTUP_HOME when using a non-system rust 2023-02-27 20:49:22 -05:00
Anthony Sottile
294590fd12 v3.1.0 2023-02-22 20:53:02 -05:00
Anthony Sottile
7bfa7e15c6
Merge pull request #2776 from pre-commit/unsorted-additional-deps
remove sorting for repo key for additional_deps
2023-02-22 20:42:44 -05:00
Anthony Sottile
a631abdabf remove sorting for repo key for additional_deps
in other languages this order can matter (such as ruby)
2023-02-22 20:31:14 -05:00
Anthony Sottile
cdd360645a
Merge pull request #2775 from pre-commit/called-process-error-trailing-whitespace
fix trailing whitespace in CalledProcessError output
2023-02-21 13:02:50 -05:00
Anthony Sottile
4ded56efac fix trailing whitespace in CalledProcessError output 2023-02-21 12:42:09 -05:00
Anthony Sottile
192be6079b
Merge pull request #2774 from pre-commit/diff-must-succeed
only treat exit code 1 as a successful diff
2023-02-21 12:30:33 -05:00
Anthony Sottile
cddc9cff0f only treat exit code 1 as a successful diff 2023-02-21 12:20:25 -05:00
Anthony Sottile
8ba9bc6d89
Merge pull request #2768 from pre-commit/per-language-tests
test languages only when they are changed
2023-02-21 11:31:14 -05:00
Anthony Sottile
08fa5ffc43 make a change to trigger the language tests 2023-02-21 11:11:44 -05:00
Anthony Sottile
9655158d93 test languages only when they are changed 2023-02-21 11:11:44 -05:00
Anthony Sottile
1054afd978
Merge pull request #2772 from pre-commit/run-language-for-repository-test
use run_language for repository_test
2023-02-20 22:49:55 -05:00
Anthony Sottile
d23990cc8b use run_language for repository_test 2023-02-20 22:21:31 -05:00
Anthony Sottile
51b14c2b37
Merge pull request #2771 from pre-commit/ruby-is-the-only-bytesio
resources_bytesio is only used by ruby
2023-02-20 22:17:20 -05:00
Anthony Sottile
4666b6956f
Merge pull request #2770 from pre-commit/lang_base_unit_tests
improve unit test coverage of lang_base
2023-02-20 21:59:10 -05:00
Anthony Sottile
8d84a7a270 resources_bytesio is only used by ruby 2023-02-20 21:47:01 -05:00
Anthony Sottile
ccbf15150f
Merge pull request #2769 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-02-20 21:39:08 -05:00
Anthony Sottile
25b8ad7528 improve unit test coverage of lang_base 2023-02-20 21:32:32 -05:00
pre-commit-ci[bot]
0cc2856883
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.0.0 → v1.0.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.0.0...v1.0.1)
2023-02-21 02:06:17 +00:00
Anthony Sottile
4f6ba18cad
Merge pull request #2767 from pre-commit/test-more-directly
test things more directly to improve coverage
2023-02-20 18:52:11 -05:00
Anthony Sottile
c3402e6648
Merge pull request #2766 from pre-commit/move-language-helpers
move languages.all and languages.helpers out of languages
2023-02-20 18:34:59 -05:00
Anthony Sottile
c3613b954a test things more directly to improve coverage 2023-02-20 18:18:08 -05:00
Anthony Sottile
d3883ce7f7 move languages.all and languages.helpers out of languages 2023-02-20 18:03:45 -05:00
Anthony Sottile
5bc56889e9
Merge pull request #2761 from m-rsha/test-pygrep
test pygrep inline
2023-02-18 18:20:42 -05:00
marsha
a2373d0a81 test pygrep inline 2023-02-18 17:54:44 -05:00
Anthony Sottile
f5ec578647
Merge pull request #2763 from m-rsha/dotnet-output
future-proof dotnet build command
2023-02-18 16:39:39 -05:00
marsha
8db5aaf4f3 future-proof dotnet build command
see https://github.com/dotnet/sdk/issues/30624#issuecomment-1435457318
2023-02-17 21:34:24 -06:00
Anthony Sottile
8afe5958e6
Merge pull request #2760 from pre-commit/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-02-13 22:57:43 -05:00
pre-commit-ci[bot]
ea2569027b
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v0.991 → v1.0.0](https://github.com/pre-commit/mirrors-mypy/compare/v0.991...v1.0.0)
2023-02-14 02:25:01 +00:00
Anthony Sottile
8f2dfce5d1
Merge pull request #2756 from m-rsha/test-fail
test fail language inline
2023-02-13 18:30:00 -05:00
marsha
4fdfb25a52 test fail language inline 2023-02-13 17:03:59 -06:00
Anthony Sottile
4bd1677cda do template links need about? 2023-02-08 11:23:43 -05:00
Anthony Sottile
16869444ca git mv .github/ISSUE_TEMPLATE/config.{yaml,yml} 2023-02-08 11:21:50 -05:00
Anthony Sottile
5635079373 force the issue template more 2023-02-08 11:20:30 -05:00
Anthony Sottile
c5fc8627be
Merge pull request #2752 from adamchainz/document_go_first_class
List golang as first-class language
2023-02-08 08:20:07 -05:00
Adam Johnson
abbfb2e9b9 List golang as first-class language 2023-02-08 06:43:04 +00:00
Anthony Sottile
4d970de329
Merge pull request #2751 from m-rsha/test-dotnet
test dotnet directly
2023-02-07 23:17:00 -05:00
marsha
915b930a5d test dotnet directly 2023-02-07 21:47:26 -06:00
Anthony Sottile
97c7870f89
Merge pull request #2713 from m-rsha/test-go
test golang directly
2023-02-06 18:40:14 -05:00
marsha
6804100701 test golang directly 2023-02-06 17:02:20 -06:00
Anthony Sottile
f7df13f3d8
Merge pull request #2747 from pre-commit/docker-tests
test docker and docker_image directly
2023-02-04 17:51:28 -05:00
Anthony Sottile
0afb95ccca test docker and docker_image directly 2023-02-04 17:22:06 -05:00
Anthony Sottile
b609368ca5
Merge pull request #2746 from pre-commit/deprecate-python-venv
deprecate python_venv language
2023-02-04 14:58:35 -05:00
Anthony Sottile
0c1267b214 deprecate python_venv language 2023-02-04 14:26:09 -05:00
Anthony Sottile
0359fae2da v3.0.4 2023-02-03 12:07:23 -05:00
Anthony Sottile
cc7bf965eb
Merge pull request #2743 from adamchainz/issue_2742
Add `--no-textconv` to `git diff` calls
2023-02-03 12:05:56 -05:00
Adam Johnson
7783a3e63a Add --no-textconv to git diff calls 2023-02-03 15:56:11 +00:00
Anthony Sottile
e846829992 v3.0.3 2023-02-01 18:21:18 -05:00
Anthony Sottile
bfe1a72734
Merge pull request #2740 from pre-commit/gem-file-bleh
Revert "also ignore Gemfile in project"
2023-02-01 18:19:39 -05:00
Anthony Sottile
1129e7d222 fixup Gemfile in ruby tests 2023-02-01 18:17:24 -05:00
Anthony Sottile
7260d24d0f Revert "also ignore Gemfile in project"
This reverts commit f4bd44996c.
2023-02-01 18:17:24 -05:00
Anthony Sottile
9868b1a347
Merge pull request #2741 from pre-commit/golang-1-20
fix golang version regex in test
2023-02-01 18:16:54 -05:00
Anthony Sottile
d216cdd5c1 fix golang version regex in test 2023-02-01 18:16:09 -05:00
Anthony Sottile
c2e432cdf6
Merge pull request #2736 from pre-commit/node-tests
test node directly
2023-01-31 21:40:03 -05:00
Anthony Sottile
909dd0e8a1 test node directly 2023-01-31 21:13:11 -05:00
Anthony Sottile
3c2ca11332
Merge pull request #2737 from pre-commit/health-check-after-create
ensure languages are healthy after creation
2023-01-31 21:12:56 -05:00
Anthony Sottile
2530913fad ensure languages are healthy after creation 2023-01-31 20:40:19 -05:00
Anthony Sottile
650afe4ffa
Merge pull request #2735 from pre-commit/fast-checkout
upgrade asottile/workflows to get fast-checkout
2023-01-30 23:35:09 -05:00
Anthony Sottile
f54386203e upgrade asottile/workflows to get fast-checkout 2023-01-30 23:04:25 -05:00
Anthony Sottile
50cf02764c
Merge pull request #2733 from pre-commit/test-ruby
test ruby directly
2023-01-30 22:12:15 -05:00
Anthony Sottile
5b50acbd2c test ruby directly 2023-01-30 21:36:13 -05:00
Anthony Sottile
77b4ea38ca
Merge pull request #2729 from pre-commit/rust-tests
test rust directly
2023-01-29 19:00:09 -05:00
Anthony Sottile
2adca78c6f test rust directly 2023-01-29 18:27:10 -05:00
167 changed files with 4725 additions and 2236 deletions

View file

@ -16,6 +16,12 @@ body:
placeholder: ... placeholder: ...
validations: validations:
required: true required: true
- type: markdown
attributes:
value: |
95% of issues created are duplicates.
please try extra hard to find them first.
it's very unlikely your problem is unique.
- type: textarea - type: textarea
id: freeform id: freeform
attributes: attributes:

38
.github/ISSUE_TEMPLATE/01_feature.yaml vendored Normal file
View file

@ -0,0 +1,38 @@
name: feature request
description: something new
body:
- type: markdown
attributes:
value: |
this is for issues for `pre-commit` (the framework).
if you are reporting an issue for [pre-commit.ci] please report it at [pre-commit-ci/issues]
[pre-commit.ci]: https://pre-commit.ci
[pre-commit-ci/issues]: https://github.com/pre-commit-ci/issues
- type: input
id: search
attributes:
label: search you tried in the issue tracker
placeholder: ...
validations:
required: true
- type: markdown
attributes:
value: |
95% of issues created are duplicates.
please try extra hard to find them first.
it's very unlikely your feature idea is a new one.
- type: textarea
id: freeform
attributes:
label: describe your actual problem
placeholder: 'I want to do ... I tried ... It does not work because ...'
validations:
required: true
- type: input
id: version
attributes:
label: pre-commit --version
placeholder: pre-commit x.x.x
validations:
required: true

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: documentation
url: https://pre-commit.com
about: please check the docs first
- name: pre-commit.ci issues
url: https://github.com/pre-commit-ci/issues
about: please report issues about pre-commit.ci here

View file

@ -5,36 +5,5 @@ inputs:
runs: runs:
using: composite using: composite
steps: steps:
- name: setup (windows) - uses: asottile/workflows/.github/actions/latest-git@v1.4.0
shell: bash if: inputs.env == 'py39' && runner.os == 'Linux'
if: runner.os == 'Windows'
run: |
set -x
echo 'TEMP=C:\TEMP' >> "$GITHUB_ENV"
echo "$CONDA\Scripts" >> "$GITHUB_PATH"
echo 'C:\Strawberry\perl\bin' >> "$GITHUB_PATH"
echo 'C:\Strawberry\perl\site\bin' >> "$GITHUB_PATH"
echo 'C:\Strawberry\c\bin' >> "$GITHUB_PATH"
testing/get-coursier.sh
testing/get-dart.sh
- name: setup (linux)
shell: bash
if: runner.os == 'Linux'
run: |
set -x
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
lua5.3 \
liblua5.3-dev \
luarocks
testing/get-coursier.sh
testing/get-dart.sh
testing/get-swift.sh
- uses: asottile/workflows/.github/actions/latest-git@v1.2.0
if: inputs.env == 'py38' && runner.os == 'Linux'

84
.github/workflows/languages.yaml vendored Normal file
View file

@ -0,0 +1,84 @@
name: languages
on:
push:
branches: [main, test-me-*]
tags: '*'
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
vars:
runs-on: ubuntu-latest
outputs:
languages: ${{ steps.vars.outputs.languages }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: install deps
run: python -mpip install -e . -r requirements-dev.txt
- name: vars
run: testing/languages ${{ github.event_name == 'push' && '--all' || '' }}
id: vars
language:
needs: [vars]
runs-on: ${{ matrix.os }}
if: needs.vars.outputs.languages != '[]'
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.vars.outputs.languages) }}
steps:
- uses: asottile/workflows/.github/actions/fast-checkout@v1.8.1
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- run: echo "$CONDA\Scripts" >> "$GITHUB_PATH"
shell: bash
if: matrix.os == 'windows-latest' && matrix.language == 'conda'
- run: testing/get-coursier.sh
shell: bash
if: matrix.language == 'coursier'
- run: testing/get-dart.sh
shell: bash
if: matrix.language == 'dart'
- run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
lua5.3 \
liblua5.3-dev \
luarocks
if: matrix.os == 'ubuntu-latest' && matrix.language == 'lua'
- run: |
echo 'C:\Strawberry\perl\bin' >> "$GITHUB_PATH"
echo 'C:\Strawberry\perl\site\bin' >> "$GITHUB_PATH"
echo 'C:\Strawberry\c\bin' >> "$GITHUB_PATH"
shell: bash
if: matrix.os == 'windows-latest' && matrix.language == 'perl'
- uses: haskell/actions/setup@v2
if: matrix.language == 'haskell'
- uses: r-lib/actions/setup-r@v2
if: matrix.os == 'ubuntu-latest' && matrix.language == 'r'
- name: install deps
run: python -mpip install -e . -r requirements-dev.txt
- name: run tests
run: coverage run -m pytest tests/languages/${{ matrix.language }}_test.py
- name: check coverage
run: coverage report --include pre_commit/languages/${{ matrix.language }}.py,tests/languages/${{ matrix.language }}_test.py
collector:
needs: [language]
if: always()
runs-on: ubuntu-latest
steps:
- name: check for failures
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
run: echo job failed && exit 1

View file

@ -3,7 +3,7 @@ name: main
on: on:
push: push:
branches: [main, test-me-*] branches: [main, test-me-*]
tags: tags: '*'
pull_request: pull_request:
concurrency: concurrency:
@ -12,12 +12,12 @@ concurrency:
jobs: jobs:
main-windows: main-windows:
uses: asottile/workflows/.github/workflows/tox.yml@v1.2.0 uses: asottile/workflows/.github/workflows/tox.yml@v1.8.1
with: with:
env: '["py38"]' env: '["py310"]'
os: windows-latest os: windows-latest
main-linux: main-linux:
uses: asottile/workflows/.github/workflows/tox.yml@v1.2.0 uses: asottile/workflows/.github/workflows/tox.yml@v1.8.1
with: with:
env: '["py38", "py39", "py310"]' env: '["py310", "py311", "py312", "py313"]'
os: ubuntu-latest os: ubuntu-latest

View file

@ -1,6 +1,6 @@
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0 rev: v6.0.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
@ -10,36 +10,35 @@ repos:
- id: name-tests-test - id: name-tests-test
- id: requirements-txt-fixer - id: requirements-txt-fixer
- repo: https://github.com/asottile/setup-cfg-fmt - repo: https://github.com/asottile/setup-cfg-fmt
rev: v2.2.0 rev: v3.2.0
hooks: hooks:
- id: setup-cfg-fmt - id: setup-cfg-fmt
- repo: https://github.com/asottile/reorder_python_imports - repo: https://github.com/asottile/reorder-python-imports
rev: v3.9.0 rev: v3.16.0
hooks: hooks:
- id: reorder-python-imports - id: reorder-python-imports
exclude: ^(pre_commit/resources/|testing/resources/python3_hooks_repo/) exclude: ^pre_commit/resources/
args: [--py38-plus, --add-import, 'from __future__ import annotations'] args: [--py310-plus, --add-import, 'from __future__ import annotations']
- repo: https://github.com/asottile/add-trailing-comma - repo: https://github.com/asottile/add-trailing-comma
rev: v2.4.0 rev: v4.0.0
hooks: hooks:
- id: add-trailing-comma - id: add-trailing-comma
args: [--py36-plus]
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v3.3.1 rev: v3.21.2
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py38-plus] args: [--py310-plus]
- repo: https://github.com/pre-commit/mirrors-autopep8 - repo: https://github.com/hhatto/autopep8
rev: v2.0.1 rev: v2.3.2
hooks: hooks:
- id: autopep8 - id: autopep8
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8
rev: 6.0.0 rev: 7.3.0
hooks: hooks:
- id: flake8 - id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991 rev: v1.19.1
hooks: hooks:
- id: mypy - id: mypy
additional_dependencies: [types-all] additional_dependencies: [types-pyyaml]
exclude: ^testing/resources/ exclude: ^testing/resources/

View file

@ -1,3 +1,331 @@
4.5.1 - 2025-12-16
==================
### Fixes
- Fix `language: python` with `repo: local` without `additional_dependencies`.
- #3597 PR by @asottile.
4.5.0 - 2025-11-22
==================
### Features
- Add `pre-commit hazmat`.
- #3585 PR by @asottile.
4.4.0 - 2025-11-08
==================
### Features
- Add `--fail-fast` option to `pre-commit run`.
- #3528 PR by @JulianMaurin.
- Upgrade `ruby-build` / `rbenv`.
- #3566 PR by @asottile.
- #3565 issue by @MRigal.
- Add `language: unsupported` / `language: unsupported_script` as aliases
for `language: system` / `language: script` (which will eventually be
deprecated).
- #3577 PR by @asottile.
- Add support docker-in-docker detection for cgroups v2.
- #3535 PR by @br-rhrbacek.
- #3360 issue by @JasonAlt.
### Fixes
- Handle when docker gives `SecurityOptions: null`.
- #3537 PR by @asottile.
- #3514 issue by @jenstroeger.
- Fix error context for invalid `stages` in `.pre-commit-config.yaml`.
- #3576 PR by @asottile.
4.3.0 - 2025-08-09
==================
### Features
- `language: docker` / `language: docker_image`: detect rootless docker.
- #3446 PR by @matthewhughes934.
- #1243 issue by @dkolepp.
- `language: julia`: avoid `startup.jl` when executing hooks.
- #3496 PR by @ericphanson.
- `language: dart`: support latest dart versions which require a higher sdk
lower bound.
- #3507 PR by @bc-lee.
4.2.0 - 2025-03-18
==================
### Features
- For `language: python` first attempt a versioned python executable for
the default language version before consulting a potentially unversioned
`sys.executable`.
- #3430 PR by @asottile.
### Fixes
- Handle error during conflict detection when a file is named "HEAD"
- #3425 PR by @tusharsadhwani.
4.1.0 - 2025-01-20
==================
### Features
- Add `language: julia`.
- #3348 PR by @fredrikekre.
- #2689 issue @jmuchovej.
### Fixes
- Disable automatic toolchain switching for `language: golang`.
- #3304 PR by @AleksaC.
- #3300 issue by @AleksaC.
- #3149 issue by @nijel.
- Fix `language: r` installation when initiated by RStudio.
- #3389 PR by @lorenzwalthert.
- #3385 issue by @lorenzwalthert.
4.0.1 - 2024-10-08
==================
### Fixes
- Fix `pre-commit migrate-config` for unquoted deprecated stages names with
purelib `pyyaml`.
- #3324 PR by @asottile.
- pre-commit-ci/issues#234 issue by @lorenzwalthert.
4.0.0 - 2024-10-05
==================
### Features
- Improve `pre-commit migrate-config` to handle more yaml formats.
- #3301 PR by @asottile.
- Handle `stages` deprecation in `pre-commit migrate-config`.
- #3302 PR by @asottile.
- #2732 issue by @asottile.
- Upgrade `ruby-build`.
- #3199 PR by @ThisGuyCodes.
- Add "sensible regex" warnings to `repo: meta`.
- #3311 PR by @asottile.
- Add warnings for deprecated `stages` (`commit` -> `pre-commit`, `push` ->
`pre-push`, `merge-commit` -> `pre-merge-commit`).
- #3312 PR by @asottile.
- #3313 PR by @asottile.
- #3315 PR by @asottile.
- #2732 issue by @asottile.
### Updating
- `language: python_venv` has been removed -- use `language: python` instead.
- #3320 PR by @asottile.
- #2734 issue by @asottile.
3.8.0 - 2024-07-28
==================
### Features
- Implement health checks for `language: r` so environments are recreated if
the system version of R changes.
- #3206 issue by @lorenzwalthert.
- #3265 PR by @lorenzwalthert.
3.7.1 - 2024-05-10
==================
### Fixes
- Fix `language: rust` default language version check when `rust-toolchain.toml`
is present.
- issue by @gaborbernat.
- #3201 PR by @asottile.
3.7.0 - 2024-03-24
==================
### Features
- Use a tty for `docker` and `docker_image` hooks when `--color` is specified.
- #3122 PR by @glehmann.
### Fixes
- Fix `fail_fast` for individual hooks stopping when previous hooks had failed.
- #3167 issue by @tp832944.
- #3168 PR by @asottile.
### Updating
- The per-hook behaviour of `fail_fast` was fixed. If you want the pre-3.7.0
behaviour, add `fail_fast: true` to all hooks before the last `fail_fast`
hook.
3.6.2 - 2024-02-18
==================
### Fixes
- Fix building golang hooks during `git commit --all`.
- #3130 PR by @asottile.
- #2722 issue by @pestanko and @matthewhughes934.
3.6.1 - 2024-02-10
==================
### Fixes
- Remove `PYTHONEXECUTABLE` from environment when running.
- #3110 PR by @untitaker.
- Handle staged-files-only with only a crlf diff.
- #3126 PR by @asottile.
- issue by @tyyrok.
3.6.0 - 2023-12-09
==================
### Features
- Check `minimum_pre_commit_version` first when parsing configs.
- #3092 PR by @asottile.
### Fixes
- Fix deprecation warnings for `importlib.resources`.
- #3043 PR by @asottile.
- Fix deprecation warnings for rmtree.
- #3079 PR by @edgarrmondragon.
### Updating
- Drop support for python<3.9.
- #3042 PR by @asottile.
- #3093 PR by @asottile.
3.5.0 - 2023-10-13
==================
### Features
- Improve performance of `check-hooks-apply` and `check-useless-excludes`.
- #2998 PR by @mxr.
- #2935 issue by @mxr.
### Fixes
- Use `time.monotonic()` for more accurate hook timing.
- #3024 PR by @adamchainz.
### Updating
- Require npm 6.x+ for `language: node` hooks.
- #2996 PR by @RoelAdriaans.
- #1983 issue by @henryiii.
3.4.0 - 2023-09-02
==================
### Features
- Add `language: haskell`.
- #2932 by @alunduil.
- Improve cpu count detection when run under cgroups.
- #2979 PR by @jdb8.
- #2978 issue by @jdb8.
### Fixes
- Handle negative exit codes from hooks receiving posix signals.
- #2971 PR by @chriskuehl.
- #2970 issue by @chriskuehl.
3.3.3 - 2023-06-13
==================
### Fixes
- Work around OS packagers setting `--install-dir` / `--bin-dir` in gem settings.
- #2905 PR by @jaysoffian.
- #2799 issue by @lmilbaum.
3.3.2 - 2023-05-17
==================
### Fixes
- Work around `r` on windows sometimes double-un-quoting arguments.
- #2885 PR by @lorenzwalthert.
- #2870 issue by @lorenzwalthert.
3.3.1 - 2023-05-02
==================
### Fixes
- Work around `git` partial clone bug for `autoupdate` on windows.
- #2866 PR by @asottile.
- #2865 issue by @adehad.
3.3.0 - 2023-05-01
==================
### Features
- Upgrade ruby-build.
- #2846 PR by @jalessio.
- Use blobless clone for faster autoupdate.
- #2859 PR by @asottile.
- Add `-j` / `--jobs` argument to `autoupdate` for parallel execution.
- #2863 PR by @asottile.
- issue by @gaborbernat.
3.2.2 - 2023-04-03
==================
### Fixes
- Fix support for swift >= 5.8.
- #2836 PR by @edelabar.
- #2835 issue by @kgrobelny-intive.
3.2.1 - 2023-03-25
==================
### Fixes
- Fix `language_version` for `language: rust` without global `rustup`.
- #2823 issue by @daschuer.
- #2827 PR by @asottile.
3.2.0 - 2023-03-17
==================
### Features
- Allow `pre-commit`, `pre-push`, and `pre-merge-commit` as `stages`.
- #2732 issue by @asottile.
- #2808 PR by @asottile.
- Add `pre-rebase` hook support.
- #2582 issue by @BrutalSimplicity.
- #2725 PR by @mgaligniana.
### Fixes
- Remove bulky cargo cache from `language: rust` installs.
- #2820 PR by @asottile.
3.1.1 - 2023-02-27
==================
### Fixes
- Fix `rust` with `language_version` and a non-writable host `RUSTUP_HOME`.
- pre-commit-ci/issues#173 by @Swiftb0y.
- #2788 by @asottile.
3.1.0 - 2023-02-22
==================
### Fixes
- Fix `dotnet` for `.sln`-based hooks for dotnet>=7.0.200.
- #2763 PR by @m-rsha.
- Prevent stashing when `diff` fails to execute.
- #2774 PR by @asottile.
- #2773 issue by @strubbly.
- Dependencies are no longer sorted in repository key.
- #2776 PR by @asottile.
### Updating
- Deprecate `language: python_venv`. Use `language: python` instead.
- #2746 PR by @asottile.
- #2734 issue by @asottile.
3.0.4 - 2023-02-03
==================
### Fixes
- Fix hook diff detection for files affected by `--textconv`.
- #2743 PR by @adamchainz.
- #2743 issue by @adamchainz.
3.0.3 - 2023-02-01
==================
### Fixes
- Revert "Prevent local `Gemfile` from interfering with hook execution.".
- #2739 issue by @Roguelazer.
- #2740 PR by @asottile.
3.0.2 - 2023-01-29 3.0.2 - 2023-01-29
================== ==================

View file

@ -64,10 +64,10 @@ to implement. The current implemented languages are at varying levels:
- 0th class - pre-commit does not require any dependencies for these languages - 0th class - pre-commit does not require any dependencies for these languages
as they're not actually languages (current examples: fail, pygrep) as they're not actually languages (current examples: fail, pygrep)
- 1st class - pre-commit will bootstrap a full interpreter requiring nothing to - 1st class - pre-commit will bootstrap a full interpreter requiring nothing to
be installed globally (current examples: node, ruby, rust) be installed globally (current examples: go, node, ruby, rust)
- 2nd class - pre-commit requires the user to install the language globally but - 2nd class - pre-commit requires the user to install the language globally but
will install tools in an isolated fashion (current examples: python, go, will install tools in an isolated fashion (current examples: python, swift,
swift, docker). docker).
- 3rd class - pre-commit requires the user to install both the tool and the - 3rd class - pre-commit requires the user to install both the tool and the
language globally (current examples: script, system) language globally (current examples: script, system)
@ -92,7 +92,7 @@ language, for example:
here are the apis that should be implemented for a language here are the apis that should be implemented for a language
Note that these are also documented in [`pre_commit/languages/all.py`](https://github.com/pre-commit/pre-commit/blob/main/pre_commit/languages/all.py) Note that these are also documented in [`pre_commit/lang_base.py`](https://github.com/pre-commit/pre-commit/blob/main/pre_commit/lang_base.py)
#### `ENVIRONMENT_DIR` #### `ENVIRONMENT_DIR`
@ -111,7 +111,7 @@ one cannot be determined, return `'default'`.
You generally don't need to implement this on a first pass and can just use: You generally don't need to implement this on a first pass and can just use:
```python ```python
get_default_version = helpers.basic_default_version get_default_version = lang_base.basic_default_version
``` ```
`python` is currently the only language which implements this api `python` is currently the only language which implements this api
@ -125,7 +125,7 @@ healthy.
You generally don't need to implement this on a first pass and can just use: You generally don't need to implement this on a first pass and can just use:
```python ```python
health_check = helpers.basic_healthy_check health_check = lang_base.basic_health_check
``` ```
`python` is currently the only language which implements this api, for python `python` is currently the only language which implements this api, for python
@ -137,7 +137,7 @@ this is the trickiest one to implement and where all the smart parts happen.
this api should do the following things this api should do the following things
- (0th / 3rd class): `install_environment = helpers.no_install` - (0th / 3rd class): `install_environment = lang_base.no_install`
- (1st class): install a language runtime into the hook's directory - (1st class): install a language runtime into the hook's directory
- (2nd class): install the package at `.` into the `ENVIRONMENT_DIR` - (2nd class): install the package at `.` into the `ENVIRONMENT_DIR`
- (2nd class, optional): install packages listed in `additional_dependencies` - (2nd class, optional): install packages listed in `additional_dependencies`

View file

@ -0,0 +1,50 @@
from __future__ import annotations
from pre_commit.lang_base import Language
from pre_commit.languages import conda
from pre_commit.languages import coursier
from pre_commit.languages import dart
from pre_commit.languages import docker
from pre_commit.languages import docker_image
from pre_commit.languages import dotnet
from pre_commit.languages import fail
from pre_commit.languages import golang
from pre_commit.languages import haskell
from pre_commit.languages import julia
from pre_commit.languages import lua
from pre_commit.languages import node
from pre_commit.languages import perl
from pre_commit.languages import pygrep
from pre_commit.languages import python
from pre_commit.languages import r
from pre_commit.languages import ruby
from pre_commit.languages import rust
from pre_commit.languages import swift
from pre_commit.languages import unsupported
from pre_commit.languages import unsupported_script
languages: dict[str, Language] = {
'conda': conda,
'coursier': coursier,
'dart': dart,
'docker': docker,
'docker_image': docker_image,
'dotnet': dotnet,
'fail': fail,
'golang': golang,
'haskell': haskell,
'julia': julia,
'lua': lua,
'node': node,
'perl': perl,
'pygrep': pygrep,
'python': python,
'r': r,
'ruby': ruby,
'rust': rust,
'swift': swift,
'unsupported': unsupported,
'unsupported_script': unsupported_script,
}
language_names = sorted(languages)

View file

@ -2,24 +2,42 @@ from __future__ import annotations
import functools import functools
import logging import logging
import os.path
import re import re
import shlex import shlex
import sys import sys
from collections.abc import Callable
from collections.abc import Sequence
from typing import Any from typing import Any
from typing import Sequence from typing import NamedTuple
import cfgv import cfgv
from identify.identify import ALL_TAGS from identify.identify import ALL_TAGS
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit.all_languages import language_names
from pre_commit.errors import FatalError from pre_commit.errors import FatalError
from pre_commit.languages.all import all_languages
from pre_commit.yaml import yaml_load from pre_commit.yaml import yaml_load
logger = logging.getLogger('pre_commit') logger = logging.getLogger('pre_commit')
check_string_regex = cfgv.check_and(cfgv.check_string, cfgv.check_regex) check_string_regex = cfgv.check_and(cfgv.check_string, cfgv.check_regex)
HOOK_TYPES = (
'commit-msg',
'post-checkout',
'post-commit',
'post-merge',
'post-rewrite',
'pre-commit',
'pre-merge-commit',
'pre-push',
'pre-rebase',
'prepare-commit-msg',
)
# `manual` is not invoked by any installed git hook. See #719
STAGES = (*HOOK_TYPES, 'manual')
def check_type_tag(tag: str) -> None: def check_type_tag(tag: str) -> None:
if tag not in ALL_TAGS: if tag not in ALL_TAGS:
@ -43,13 +61,186 @@ def check_min_version(version: str) -> None:
) )
_STAGES = {
'commit': 'pre-commit',
'merge-commit': 'pre-merge-commit',
'push': 'pre-push',
}
def transform_stage(stage: str) -> str:
return _STAGES.get(stage, stage)
MINIMAL_MANIFEST_SCHEMA = cfgv.Array(
cfgv.Map(
'Hook', 'id',
cfgv.Required('id', cfgv.check_string),
cfgv.Optional('stages', cfgv.check_array(cfgv.check_string), []),
),
)
def warn_for_stages_on_repo_init(repo: str, directory: str) -> None:
try:
manifest = cfgv.load_from_filename(
os.path.join(directory, C.MANIFEST_FILE),
schema=MINIMAL_MANIFEST_SCHEMA,
load_strategy=yaml_load,
exc_tp=InvalidManifestError,
)
except InvalidManifestError:
return # they'll get a better error message when it actually loads!
legacy_stages = {} # sorted set
for hook in manifest:
for stage in hook.get('stages', ()):
if stage in _STAGES:
legacy_stages[stage] = True
if legacy_stages:
logger.warning(
f'repo `{repo}` uses deprecated stage names '
f'({", ".join(legacy_stages)}) which will be removed in a '
f'future version. '
f'Hint: often `pre-commit autoupdate --repo {shlex.quote(repo)}` '
f'will fix this. '
f'if it does not -- consider reporting an issue to that repo.',
)
class StagesMigrationNoDefault(NamedTuple):
key: str
default: Sequence[str]
def check(self, dct: dict[str, Any]) -> None:
if self.key not in dct:
return
with cfgv.validate_context(f'At key: {self.key}'):
val = dct[self.key]
cfgv.check_array(cfgv.check_any)(val)
val = [transform_stage(v) for v in val]
cfgv.check_array(cfgv.check_one_of(STAGES))(val)
def apply_default(self, dct: dict[str, Any]) -> None:
if self.key not in dct:
return
dct[self.key] = [transform_stage(v) for v in dct[self.key]]
def remove_default(self, dct: dict[str, Any]) -> None:
raise NotImplementedError
class StagesMigration(StagesMigrationNoDefault):
def apply_default(self, dct: dict[str, Any]) -> None:
dct.setdefault(self.key, self.default)
super().apply_default(dct)
class DeprecatedStagesWarning(NamedTuple):
key: str
def check(self, dct: dict[str, Any]) -> None:
if self.key not in dct:
return
val = dct[self.key]
cfgv.check_array(cfgv.check_any)(val)
legacy_stages = [stage for stage in val if stage in _STAGES]
if legacy_stages:
logger.warning(
f'hook id `{dct["id"]}` uses deprecated stage names '
f'({", ".join(legacy_stages)}) which will be removed in a '
f'future version. '
f'run: `pre-commit migrate-config` to automatically fix this.',
)
def apply_default(self, dct: dict[str, Any]) -> None:
pass
def remove_default(self, dct: dict[str, Any]) -> None:
raise NotImplementedError
class DeprecatedDefaultStagesWarning(NamedTuple):
key: str
def check(self, dct: dict[str, Any]) -> None:
if self.key not in dct:
return
val = dct[self.key]
cfgv.check_array(cfgv.check_any)(val)
legacy_stages = [stage for stage in val if stage in _STAGES]
if legacy_stages:
logger.warning(
f'top-level `default_stages` uses deprecated stage names '
f'({", ".join(legacy_stages)}) which will be removed in a '
f'future version. '
f'run: `pre-commit migrate-config` to automatically fix this.',
)
def apply_default(self, dct: dict[str, Any]) -> None:
pass
def remove_default(self, dct: dict[str, Any]) -> None:
raise NotImplementedError
def _translate_language(name: str) -> str:
return {
'system': 'unsupported',
'script': 'unsupported_script',
}.get(name, name)
class LanguageMigration(NamedTuple): # remove
key: str
check_fn: Callable[[object], None]
def check(self, dct: dict[str, Any]) -> None:
if self.key not in dct:
return
with cfgv.validate_context(f'At key: {self.key}'):
self.check_fn(_translate_language(dct[self.key]))
def apply_default(self, dct: dict[str, Any]) -> None:
if self.key not in dct:
return
dct[self.key] = _translate_language(dct[self.key])
def remove_default(self, dct: dict[str, Any]) -> None:
raise NotImplementedError
class LanguageMigrationRequired(LanguageMigration): # replace with Required
def check(self, dct: dict[str, Any]) -> None:
if self.key not in dct:
raise cfgv.ValidationError(f'Missing required key: {self.key}')
super().check(dct)
MANIFEST_HOOK_DICT = cfgv.Map( MANIFEST_HOOK_DICT = cfgv.Map(
'Hook', 'id', 'Hook', 'id',
# check first in case it uses some newer, incompatible feature
cfgv.Optional(
'minimum_pre_commit_version',
cfgv.check_and(cfgv.check_string, check_min_version),
'0',
),
cfgv.Required('id', cfgv.check_string), cfgv.Required('id', cfgv.check_string),
cfgv.Required('name', cfgv.check_string), cfgv.Required('name', cfgv.check_string),
cfgv.Required('entry', cfgv.check_string), cfgv.Required('entry', cfgv.check_string),
cfgv.Required('language', cfgv.check_one_of(all_languages)), LanguageMigrationRequired('language', cfgv.check_one_of(language_names)),
cfgv.Optional('alias', cfgv.check_string, ''), cfgv.Optional('alias', cfgv.check_string, ''),
cfgv.Optional('files', check_string_regex, ''), cfgv.Optional('files', check_string_regex, ''),
@ -68,9 +259,8 @@ MANIFEST_HOOK_DICT = cfgv.Map(
cfgv.Optional('description', cfgv.check_string, ''), cfgv.Optional('description', cfgv.check_string, ''),
cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT), cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT),
cfgv.Optional('log_file', cfgv.check_string, ''), cfgv.Optional('log_file', cfgv.check_string, ''),
cfgv.Optional('minimum_pre_commit_version', cfgv.check_string, '0'),
cfgv.Optional('require_serial', cfgv.check_bool, False), cfgv.Optional('require_serial', cfgv.check_bool, False),
cfgv.Optional('stages', cfgv.check_array(cfgv.check_one_of(C.STAGES)), []), StagesMigration('stages', []),
cfgv.Optional('verbose', cfgv.check_bool, False), cfgv.Optional('verbose', cfgv.check_bool, False),
) )
MANIFEST_SCHEMA = cfgv.Array(MANIFEST_HOOK_DICT) MANIFEST_SCHEMA = cfgv.Array(MANIFEST_HOOK_DICT)
@ -80,10 +270,19 @@ class InvalidManifestError(FatalError):
pass pass
def _load_manifest_forward_compat(contents: str) -> object:
obj = yaml_load(contents)
if isinstance(obj, dict):
check_min_version('5')
raise AssertionError('unreachable')
else:
return obj
load_manifest = functools.partial( load_manifest = functools.partial(
cfgv.load_from_filename, cfgv.load_from_filename,
schema=MANIFEST_SCHEMA, schema=MANIFEST_SCHEMA,
load_strategy=yaml_load, load_strategy=_load_manifest_forward_compat,
exc_tp=InvalidManifestError, exc_tp=InvalidManifestError,
) )
@ -205,12 +404,20 @@ class NotAllowed(cfgv.OptionalNoDefault):
raise cfgv.ValidationError(f'{self.key!r} cannot be overridden') raise cfgv.ValidationError(f'{self.key!r} cannot be overridden')
_COMMON_HOOK_WARNINGS = (
OptionalSensibleRegexAtHook('files', cfgv.check_string),
OptionalSensibleRegexAtHook('exclude', cfgv.check_string),
DeprecatedStagesWarning('stages'),
)
META_HOOK_DICT = cfgv.Map( META_HOOK_DICT = cfgv.Map(
'Hook', 'id', 'Hook', 'id',
cfgv.Required('id', cfgv.check_string), cfgv.Required('id', cfgv.check_string),
cfgv.Required('id', cfgv.check_one_of(tuple(k for k, _ in _meta))), cfgv.Required('id', cfgv.check_one_of(tuple(k for k, _ in _meta))),
# language must be system # language must be `unsupported`
cfgv.Optional('language', cfgv.check_one_of({'system'}), 'system'), cfgv.Optional(
'language', cfgv.check_one_of({'unsupported'}), 'unsupported',
),
# entry cannot be overridden # entry cannot be overridden
NotAllowed('entry', cfgv.check_any), NotAllowed('entry', cfgv.check_any),
*( *(
@ -227,6 +434,7 @@ META_HOOK_DICT = cfgv.Map(
item item
for item in MANIFEST_HOOK_DICT.items for item in MANIFEST_HOOK_DICT.items
), ),
*_COMMON_HOOK_WARNINGS,
) )
CONFIG_HOOK_DICT = cfgv.Map( CONFIG_HOOK_DICT = cfgv.Map(
'Hook', 'id', 'Hook', 'id',
@ -241,17 +449,18 @@ CONFIG_HOOK_DICT = cfgv.Map(
cfgv.OptionalNoDefault(item.key, item.check_fn) cfgv.OptionalNoDefault(item.key, item.check_fn)
for item in MANIFEST_HOOK_DICT.items for item in MANIFEST_HOOK_DICT.items
if item.key != 'id' if item.key != 'id'
if item.key != 'stages'
if item.key != 'language' # remove
), ),
OptionalSensibleRegexAtHook('files', cfgv.check_string), StagesMigrationNoDefault('stages', []),
OptionalSensibleRegexAtHook('exclude', cfgv.check_string), LanguageMigration('language', cfgv.check_one_of(language_names)), # remove
*_COMMON_HOOK_WARNINGS,
) )
LOCAL_HOOK_DICT = cfgv.Map( LOCAL_HOOK_DICT = cfgv.Map(
'Hook', 'id', 'Hook', 'id',
*MANIFEST_HOOK_DICT.items, *MANIFEST_HOOK_DICT.items,
*_COMMON_HOOK_WARNINGS,
OptionalSensibleRegexAtHook('files', cfgv.check_string),
OptionalSensibleRegexAtHook('exclude', cfgv.check_string),
) )
CONFIG_REPO_DICT = cfgv.Map( CONFIG_REPO_DICT = cfgv.Map(
'Repository', 'repo', 'Repository', 'repo',
@ -281,34 +490,33 @@ CONFIG_REPO_DICT = cfgv.Map(
) )
DEFAULT_LANGUAGE_VERSION = cfgv.Map( DEFAULT_LANGUAGE_VERSION = cfgv.Map(
'DefaultLanguageVersion', None, 'DefaultLanguageVersion', None,
cfgv.NoAdditionalKeys(all_languages), cfgv.NoAdditionalKeys(language_names),
*(cfgv.Optional(x, cfgv.check_string, C.DEFAULT) for x in all_languages), *(cfgv.Optional(x, cfgv.check_string, C.DEFAULT) for x in language_names),
) )
CONFIG_SCHEMA = cfgv.Map( CONFIG_SCHEMA = cfgv.Map(
'Config', None, 'Config', None,
cfgv.RequiredRecurse('repos', cfgv.Array(CONFIG_REPO_DICT)), # check first in case it uses some newer, incompatible feature
cfgv.Optional(
'default_install_hook_types',
cfgv.check_array(cfgv.check_one_of(C.HOOK_TYPES)),
['pre-commit'],
),
cfgv.OptionalRecurse(
'default_language_version', DEFAULT_LANGUAGE_VERSION, {},
),
cfgv.Optional(
'default_stages',
cfgv.check_array(cfgv.check_one_of(C.STAGES)),
C.STAGES,
),
cfgv.Optional('files', check_string_regex, ''),
cfgv.Optional('exclude', check_string_regex, '^$'),
cfgv.Optional('fail_fast', cfgv.check_bool, False),
cfgv.Optional( cfgv.Optional(
'minimum_pre_commit_version', 'minimum_pre_commit_version',
cfgv.check_and(cfgv.check_string, check_min_version), cfgv.check_and(cfgv.check_string, check_min_version),
'0', '0',
), ),
cfgv.RequiredRecurse('repos', cfgv.Array(CONFIG_REPO_DICT)),
cfgv.Optional(
'default_install_hook_types',
cfgv.check_array(cfgv.check_one_of(HOOK_TYPES)),
['pre-commit'],
),
cfgv.OptionalRecurse(
'default_language_version', DEFAULT_LANGUAGE_VERSION, {},
),
StagesMigration('default_stages', STAGES),
DeprecatedDefaultStagesWarning('default_stages'),
cfgv.Optional('files', check_string_regex, ''),
cfgv.Optional('exclude', check_string_regex, '^$'),
cfgv.Optional('fail_fast', cfgv.check_bool, False),
cfgv.WarnAdditionalKeys( cfgv.WarnAdditionalKeys(
( (
'repos', 'repos',

View file

@ -1,22 +1,23 @@
from __future__ import annotations from __future__ import annotations
import concurrent.futures
import os.path import os.path
import re import re
import tempfile import tempfile
from collections.abc import Sequence
from typing import Any from typing import Any
from typing import NamedTuple from typing import NamedTuple
from typing import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import git from pre_commit import git
from pre_commit import output from pre_commit import output
from pre_commit import xargs
from pre_commit.clientlib import InvalidManifestError from pre_commit.clientlib import InvalidManifestError
from pre_commit.clientlib import load_config from pre_commit.clientlib import load_config
from pre_commit.clientlib import load_manifest from pre_commit.clientlib import load_manifest
from pre_commit.clientlib import LOCAL from pre_commit.clientlib import LOCAL
from pre_commit.clientlib import META from pre_commit.clientlib import META
from pre_commit.commands.migrate_config import migrate_config from pre_commit.commands.migrate_config import migrate_config
from pre_commit.store import Store
from pre_commit.util import CalledProcessError from pre_commit.util import CalledProcessError
from pre_commit.util import cmd_output from pre_commit.util import cmd_output
from pre_commit.util import cmd_output_b from pre_commit.util import cmd_output_b
@ -27,49 +28,58 @@ from pre_commit.yaml import yaml_load
class RevInfo(NamedTuple): class RevInfo(NamedTuple):
repo: str repo: str
rev: str rev: str
frozen: str | None frozen: str | None = None
hook_ids: frozenset[str] = frozenset()
@classmethod @classmethod
def from_config(cls, config: dict[str, Any]) -> RevInfo: def from_config(cls, config: dict[str, Any]) -> RevInfo:
return cls(config['repo'], config['rev'], None) return cls(config['repo'], config['rev'])
def update(self, tags_only: bool, freeze: bool) -> RevInfo: def update(self, tags_only: bool, freeze: bool) -> RevInfo:
git_cmd = ('git', *git.NO_FS_MONITOR)
if tags_only:
tag_cmd = (
*git_cmd, 'describe',
'FETCH_HEAD', '--tags', '--abbrev=0',
)
else:
tag_cmd = (
*git_cmd, 'describe',
'FETCH_HEAD', '--tags', '--exact',
)
with tempfile.TemporaryDirectory() as tmp: with tempfile.TemporaryDirectory() as tmp:
_git = ('git', *git.NO_FS_MONITOR, '-C', tmp)
if tags_only:
tag_opt = '--abbrev=0'
else:
tag_opt = '--exact'
tag_cmd = (*_git, 'describe', 'FETCH_HEAD', '--tags', tag_opt)
git.init_repo(tmp, self.repo) git.init_repo(tmp, self.repo)
cmd_output_b(*_git, 'config', 'extensions.partialClone', 'true')
cmd_output_b( cmd_output_b(
*git_cmd, 'fetch', 'origin', 'HEAD', '--tags', *_git, 'fetch', 'origin', 'HEAD',
cwd=tmp, '--quiet', '--filter=blob:none', '--tags',
) )
try: try:
rev = cmd_output(*tag_cmd, cwd=tmp)[1].strip() rev = cmd_output(*tag_cmd)[1].strip()
except CalledProcessError: except CalledProcessError:
cmd = (*git_cmd, 'rev-parse', 'FETCH_HEAD') rev = cmd_output(*_git, 'rev-parse', 'FETCH_HEAD')[1].strip()
rev = cmd_output(*cmd, cwd=tmp)[1].strip()
else: else:
if tags_only: if tags_only:
rev = git.get_best_candidate_tag(rev, tmp) rev = git.get_best_candidate_tag(rev, tmp)
frozen = None frozen = None
if freeze: if freeze:
exact_rev_cmd = (*git_cmd, 'rev-parse', rev) exact = cmd_output(*_git, 'rev-parse', rev)[1].strip()
exact = cmd_output(*exact_rev_cmd, cwd=tmp)[1].strip()
if exact != rev: if exact != rev:
rev, frozen = exact, rev rev, frozen = exact, rev
return self._replace(rev=rev, frozen=frozen)
try:
# workaround for windows -- see #2865
cmd_output_b(*_git, 'show', f'{rev}:{C.MANIFEST_FILE}')
cmd_output(*_git, 'checkout', rev, '--', C.MANIFEST_FILE)
except CalledProcessError:
pass # this will be caught by manifest validating code
try:
manifest = load_manifest(os.path.join(tmp, C.MANIFEST_FILE))
except InvalidManifestError as e:
raise RepositoryCannotBeUpdatedError(f'[{self.repo}] {e}')
else:
hook_ids = frozenset(hook['id'] for hook in manifest)
return self._replace(rev=rev, frozen=frozen, hook_ids=hook_ids)
class RepositoryCannotBeUpdatedError(RuntimeError): class RepositoryCannotBeUpdatedError(RuntimeError):
@ -79,24 +89,30 @@ class RepositoryCannotBeUpdatedError(RuntimeError):
def _check_hooks_still_exist_at_rev( def _check_hooks_still_exist_at_rev(
repo_config: dict[str, Any], repo_config: dict[str, Any],
info: RevInfo, info: RevInfo,
store: Store,
) -> None: ) -> None:
try:
path = store.clone(repo_config['repo'], info.rev)
manifest = load_manifest(os.path.join(path, C.MANIFEST_FILE))
except InvalidManifestError as e:
raise RepositoryCannotBeUpdatedError(str(e))
# See if any of our hooks were deleted with the new commits # See if any of our hooks were deleted with the new commits
hooks = {hook['id'] for hook in repo_config['hooks']} hooks = {hook['id'] for hook in repo_config['hooks']}
hooks_missing = hooks - {hook['id'] for hook in manifest} hooks_missing = hooks - info.hook_ids
if hooks_missing: if hooks_missing:
raise RepositoryCannotBeUpdatedError( raise RepositoryCannotBeUpdatedError(
f'Cannot update because the update target is missing these ' f'[{info.repo}] Cannot update because the update target is '
f'hooks:\n{", ".join(sorted(hooks_missing))}', f'missing these hooks: {", ".join(sorted(hooks_missing))}',
) )
def _update_one(
i: int,
repo: dict[str, Any],
*,
tags_only: bool,
freeze: bool,
) -> tuple[int, RevInfo, RevInfo]:
old = RevInfo.from_config(repo)
new = old.update(tags_only=tags_only, freeze=freeze)
_check_hooks_still_exist_at_rev(repo, new)
return i, old, new
REV_LINE_RE = re.compile(r'^(\s+)rev:(\s*)([\'"]?)([^\s#]+)(.*)(\r?\n)$') REV_LINE_RE = re.compile(r'^(\s+)rev:(\s*)([\'"]?)([^\s#]+)(.*)(\r?\n)$')
@ -145,49 +161,53 @@ def _write_new_config(path: str, rev_infos: list[RevInfo | None]) -> None:
def autoupdate( def autoupdate(
config_file: str, config_file: str,
store: Store,
tags_only: bool, tags_only: bool,
freeze: bool, freeze: bool,
repos: Sequence[str] = (), repos: Sequence[str] = (),
jobs: int = 1,
) -> int: ) -> int:
"""Auto-update the pre-commit config to the latest versions of repos.""" """Auto-update the pre-commit config to the latest versions of repos."""
migrate_config(config_file, quiet=True) migrate_config(config_file, quiet=True)
retv = 0
rev_infos: list[RevInfo | None] = []
changed = False changed = False
retv = 0
config = load_config(config_file) config_repos = [
for repo_config in config['repos']: repo for repo in load_config(config_file)['repos']
if repo_config['repo'] in {LOCAL, META}: if repo['repo'] not in {LOCAL, META}
continue ]
info = RevInfo.from_config(repo_config) rev_infos: list[RevInfo | None] = [None] * len(config_repos)
if repos and info.repo not in repos: jobs = jobs or xargs.cpu_count() # 0 => number of cpus
rev_infos.append(None) jobs = min(jobs, len(repos) or len(config_repos)) # max 1-per-thread
continue jobs = max(jobs, 1) # at least one thread
with concurrent.futures.ThreadPoolExecutor(jobs) as exe:
output.write(f'Updating {info.repo} ... ') futures = [
new_info = info.update(tags_only=tags_only, freeze=freeze) exe.submit(
try: _update_one,
_check_hooks_still_exist_at_rev(repo_config, new_info, store) i, repo, tags_only=tags_only, freeze=freeze,
except RepositoryCannotBeUpdatedError as error: )
output.write_line(error.args[0]) for i, repo in enumerate(config_repos)
rev_infos.append(None) if not repos or repo['repo'] in repos
retv = 1 ]
continue for future in concurrent.futures.as_completed(futures):
try:
if new_info.rev != info.rev: i, old, new = future.result()
changed = True except RepositoryCannotBeUpdatedError as e:
if new_info.frozen: output.write_line(str(e))
updated_to = f'{new_info.frozen} (frozen)' retv = 1
else: else:
updated_to = new_info.rev if new.rev != old.rev:
msg = f'updating {info.rev} -> {updated_to}.' changed = True
output.write_line(msg) if new.frozen:
rev_infos.append(new_info) new_s = f'{new.frozen} (frozen)'
else: else:
output.write_line('already up to date.') new_s = new.rev
rev_infos.append(None) msg = f'updating {old.rev} -> {new_s}'
rev_infos[i] = new
else:
msg = 'already up to date!'
output.write_line(f'[{old.repo}] {msg}')
if changed: if changed:
_write_new_config(config_file, rev_infos) _write_new_config(config_file, rev_infos)

View file

@ -12,6 +12,7 @@ from pre_commit.clientlib import load_manifest
from pre_commit.clientlib import LOCAL from pre_commit.clientlib import LOCAL
from pre_commit.clientlib import META from pre_commit.clientlib import META
from pre_commit.store import Store from pre_commit.store import Store
from pre_commit.util import rmtree
def _mark_used_repos( def _mark_used_repos(
@ -26,7 +27,8 @@ def _mark_used_repos(
for hook in repo['hooks']: for hook in repo['hooks']:
deps = hook.get('additional_dependencies') deps = hook.get('additional_dependencies')
unused_repos.discard(( unused_repos.discard((
store.db_repo_name(repo['repo'], deps), C.LOCAL_REPO_VERSION, store.db_repo_name(repo['repo'], deps),
C.LOCAL_REPO_VERSION,
)) ))
else: else:
key = (repo['repo'], repo['rev']) key = (repo['repo'], repo['rev'])
@ -56,34 +58,41 @@ def _mark_used_repos(
)) ))
def _gc_repos(store: Store) -> int: def _gc(store: Store) -> int:
configs = store.select_all_configs() with store.exclusive_lock(), store.connect() as db:
repos = store.select_all_repos() store._create_configs_table(db)
# delete config paths which do not exist repos = db.execute('SELECT repo, ref, path FROM repos').fetchall()
dead_configs = [p for p in configs if not os.path.exists(p)] all_repos = {(repo, ref): path for repo, ref, path in repos}
live_configs = [p for p in configs if os.path.exists(p)] unused_repos = set(all_repos)
all_repos = {(repo, ref): path for repo, ref, path in repos} configs_rows = db.execute('SELECT path FROM configs').fetchall()
unused_repos = set(all_repos) configs = [path for path, in configs_rows]
for config_path in live_configs:
try:
config = load_config(config_path)
except InvalidConfigError:
dead_configs.append(config_path)
continue
else:
for repo in config['repos']:
_mark_used_repos(store, all_repos, unused_repos, repo)
store.delete_configs(dead_configs) dead_configs = []
for db_repo_name, ref in unused_repos: for config_path in configs:
store.delete_repo(db_repo_name, ref, all_repos[(db_repo_name, ref)]) try:
return len(unused_repos) config = load_config(config_path)
except InvalidConfigError:
dead_configs.append(config_path)
continue
else:
for repo in config['repos']:
_mark_used_repos(store, all_repos, unused_repos, repo)
paths = [(path,) for path in dead_configs]
db.executemany('DELETE FROM configs WHERE path = ?', paths)
db.executemany(
'DELETE FROM repos WHERE repo = ? and ref = ?',
sorted(unused_repos),
)
for k in unused_repos:
rmtree(all_repos[k])
return len(unused_repos)
def gc(store: Store) -> int: def gc(store: Store) -> int:
with store.exclusive_lock(): output.write_line(f'{_gc(store)} repo(s) removed.')
repos_removed = _gc_repos(store)
output.write_line(f'{repos_removed} repo(s) removed.')
return 0 return 0

View file

@ -0,0 +1,95 @@
from __future__ import annotations
import argparse
import subprocess
from collections.abc import Sequence
from pre_commit.parse_shebang import normalize_cmd
def add_parsers(parser: argparse.ArgumentParser) -> None:
subparsers = parser.add_subparsers(dest='tool')
cd_parser = subparsers.add_parser(
'cd', help='cd to a subdir and run the command',
)
cd_parser.add_argument('subdir')
cd_parser.add_argument('cmd', nargs=argparse.REMAINDER)
ignore_exit_code_parser = subparsers.add_parser(
'ignore-exit-code', help='run the command but ignore the exit code',
)
ignore_exit_code_parser.add_argument('cmd', nargs=argparse.REMAINDER)
n1_parser = subparsers.add_parser(
'n1', help='run the command once per filename',
)
n1_parser.add_argument('cmd', nargs=argparse.REMAINDER)
def _cmd_filenames(cmd: tuple[str, ...]) -> tuple[
tuple[str, ...],
tuple[str, ...],
]:
for idx, val in enumerate(reversed(cmd)):
if val == '--':
split = len(cmd) - idx
break
else:
raise SystemExit('hazmat entry must end with `--`')
return cmd[:split - 1], cmd[split:]
def cd(subdir: str, cmd: tuple[str, ...]) -> int:
cmd, filenames = _cmd_filenames(cmd)
prefix = f'{subdir}/'
new_filenames = []
for filename in filenames:
if not filename.startswith(prefix):
raise SystemExit(f'unexpected file without {prefix=}: {filename}')
else:
new_filenames.append(filename.removeprefix(prefix))
cmd = normalize_cmd(cmd)
return subprocess.call((*cmd, *new_filenames), cwd=subdir)
def ignore_exit_code(cmd: tuple[str, ...]) -> int:
cmd = normalize_cmd(cmd)
subprocess.call(cmd)
return 0
def n1(cmd: tuple[str, ...]) -> int:
cmd, filenames = _cmd_filenames(cmd)
cmd = normalize_cmd(cmd)
ret = 0
for filename in filenames:
ret |= subprocess.call((*cmd, filename))
return ret
def impl(args: argparse.Namespace) -> int:
args.cmd = tuple(args.cmd)
if args.tool == 'cd':
return cd(args.subdir, args.cmd)
elif args.tool == 'ignore-exit-code':
return ignore_exit_code(args.cmd)
elif args.tool == 'n1':
return n1(args.cmd)
else:
raise NotImplementedError(f'unexpected tool: {args.tool}')
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser()
add_parsers(parser)
args = parser.parse_args(argv)
return impl(args)
if __name__ == '__main__':
raise SystemExit(main())

View file

@ -4,7 +4,7 @@ import argparse
import os.path import os.path
import subprocess import subprocess
import sys import sys
from typing import Sequence from collections.abc import Sequence
from pre_commit.commands.run import run from pre_commit.commands.run import run
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
@ -73,6 +73,8 @@ def _ns(
local_branch: str | None = None, local_branch: str | None = None,
from_ref: str | None = None, from_ref: str | None = None,
to_ref: str | None = None, to_ref: str | None = None,
pre_rebase_upstream: str | None = None,
pre_rebase_branch: str | None = None,
remote_name: str | None = None, remote_name: str | None = None,
remote_url: str | None = None, remote_url: str | None = None,
commit_msg_filename: str | None = None, commit_msg_filename: str | None = None,
@ -84,11 +86,13 @@ def _ns(
) -> argparse.Namespace: ) -> argparse.Namespace:
return argparse.Namespace( return argparse.Namespace(
color=color, color=color,
hook_stage=hook_type.replace('pre-', ''), hook_stage=hook_type,
remote_branch=remote_branch, remote_branch=remote_branch,
local_branch=local_branch, local_branch=local_branch,
from_ref=from_ref, from_ref=from_ref,
to_ref=to_ref, to_ref=to_ref,
pre_rebase_upstream=pre_rebase_upstream,
pre_rebase_branch=pre_rebase_branch,
remote_name=remote_name, remote_name=remote_name,
remote_url=remote_url, remote_url=remote_url,
commit_msg_filename=commit_msg_filename, commit_msg_filename=commit_msg_filename,
@ -102,6 +106,7 @@ def _ns(
hook=None, hook=None,
verbose=False, verbose=False,
show_diff_on_failure=False, show_diff_on_failure=False,
fail_fast=False,
) )
@ -185,6 +190,12 @@ def _check_args_length(hook_type: str, args: Sequence[str]) -> None:
f'hook-impl for {hook_type} expected 1, 2, or 3 arguments ' f'hook-impl for {hook_type} expected 1, 2, or 3 arguments '
f'but got {len(args)}: {args}', f'but got {len(args)}: {args}',
) )
elif hook_type == 'pre-rebase':
if len(args) < 1 or len(args) > 2:
raise SystemExit(
f'hook-impl for {hook_type} expected 1 or 2 arguments '
f'but got {len(args)}: {args}',
)
elif hook_type in _EXPECTED_ARG_LENGTH_BY_HOOK: elif hook_type in _EXPECTED_ARG_LENGTH_BY_HOOK:
expected = _EXPECTED_ARG_LENGTH_BY_HOOK[hook_type] expected = _EXPECTED_ARG_LENGTH_BY_HOOK[hook_type]
if len(args) != expected: if len(args) != expected:
@ -231,6 +242,13 @@ def _run_ns(
return _ns(hook_type, color, is_squash_merge=args[0]) return _ns(hook_type, color, is_squash_merge=args[0])
elif hook_type == 'post-rewrite': elif hook_type == 'post-rewrite':
return _ns(hook_type, color, rewrite_command=args[0]) return _ns(hook_type, color, rewrite_command=args[0])
elif hook_type == 'pre-rebase' and len(args) == 1:
return _ns(hook_type, color, pre_rebase_upstream=args[0])
elif hook_type == 'pre-rebase' and len(args) == 2:
return _ns(
hook_type, color, pre_rebase_upstream=args[0],
pre_rebase_branch=args[1],
)
else: else:
raise AssertionError(f'unexpected hook type: {hook_type}') raise AssertionError(f'unexpected hook type: {hook_type}')

View file

@ -103,8 +103,7 @@ def _install_hook_script(
hook_file.write(before + TEMPLATE_START) hook_file.write(before + TEMPLATE_START)
hook_file.write(f'INSTALL_PYTHON={shlex.quote(sys.executable)}\n') hook_file.write(f'INSTALL_PYTHON={shlex.quote(sys.executable)}\n')
# TODO: python3.8+: shlex.join args_s = shlex.join(args)
args_s = ' '.join(shlex.quote(part) for part in args)
hook_file.write(f'ARGS=({args_s})\n') hook_file.write(f'ARGS=({args_s})\n')
hook_file.write(TEMPLATE_END + after) hook_file.write(TEMPLATE_END + after)
make_executable(hook_path) make_executable(hook_path)

View file

@ -1,13 +1,21 @@
from __future__ import annotations from __future__ import annotations
import re import functools
import itertools
import textwrap import textwrap
from collections.abc import Callable
import cfgv import cfgv
import yaml import yaml
from yaml.nodes import ScalarNode
from pre_commit.clientlib import InvalidConfigError from pre_commit.clientlib import InvalidConfigError
from pre_commit.yaml import yaml_compose
from pre_commit.yaml import yaml_load from pre_commit.yaml import yaml_load
from pre_commit.yaml_rewrite import MappingKey
from pre_commit.yaml_rewrite import MappingValue
from pre_commit.yaml_rewrite import match
from pre_commit.yaml_rewrite import SequenceItem
def _is_header_line(line: str) -> bool: def _is_header_line(line: str) -> bool:
@ -38,8 +46,69 @@ def _migrate_map(contents: str) -> str:
return contents return contents
def _migrate_sha_to_rev(contents: str) -> str: def _preserve_style(n: ScalarNode, *, s: str) -> str:
return re.sub(r'(\n\s+)sha:', r'\1rev:', contents) style = n.style or ''
return f'{style}{s}{style}'
def _fix_stage(n: ScalarNode) -> str:
return _preserve_style(n, s=f'pre-{n.value}')
def _migrate_composed(contents: str) -> str:
tree = yaml_compose(contents)
rewrites: list[tuple[ScalarNode, Callable[[ScalarNode], str]]] = []
# sha -> rev
sha_to_rev_replace = functools.partial(_preserve_style, s='rev')
sha_to_rev_matcher = (
MappingValue('repos'),
SequenceItem(),
MappingKey('sha'),
)
for node in match(tree, sha_to_rev_matcher):
rewrites.append((node, sha_to_rev_replace))
# python_venv -> python
language_matcher = (
MappingValue('repos'),
SequenceItem(),
MappingValue('hooks'),
SequenceItem(),
MappingValue('language'),
)
python_venv_replace = functools.partial(_preserve_style, s='python')
for node in match(tree, language_matcher):
if node.value == 'python_venv':
rewrites.append((node, python_venv_replace))
# stages rewrites
default_stages_matcher = (MappingValue('default_stages'), SequenceItem())
default_stages_match = match(tree, default_stages_matcher)
hook_stages_matcher = (
MappingValue('repos'),
SequenceItem(),
MappingValue('hooks'),
SequenceItem(),
MappingValue('stages'),
SequenceItem(),
)
hook_stages_match = match(tree, hook_stages_matcher)
for node in itertools.chain(default_stages_match, hook_stages_match):
if node.value in {'commit', 'push', 'merge-commit'}:
rewrites.append((node, _fix_stage))
rewrites.sort(reverse=True, key=lambda nf: nf[0].start_mark.index)
src_parts = []
end: int | None = None
for node, func in rewrites:
src_parts.append(contents[node.end_mark.index:end])
src_parts.append(func(node))
end = node.start_mark.index
src_parts.append(contents[:end])
src_parts.reverse()
return ''.join(src_parts)
def migrate_config(config_file: str, quiet: bool = False) -> int: def migrate_config(config_file: str, quiet: bool = False) -> int:
@ -54,7 +123,7 @@ def migrate_config(config_file: str, quiet: bool = False) -> int:
raise cfgv.ValidationError(str(e)) raise cfgv.ValidationError(str(e))
contents = _migrate_map(contents) contents = _migrate_map(contents)
contents = _migrate_sha_to_rev(contents) contents = _migrate_composed(contents)
if contents != orig_contents: if contents != orig_contents:
with open(config_file, 'w') as f: with open(config_file, 'w') as f:

View file

@ -9,19 +9,20 @@ import re
import subprocess import subprocess
import time import time
import unicodedata import unicodedata
from collections.abc import Generator
from collections.abc import Iterable
from collections.abc import MutableMapping
from collections.abc import Sequence
from typing import Any from typing import Any
from typing import Collection
from typing import MutableMapping
from typing import Sequence
from identify.identify import tags_from_path from identify.identify import tags_from_path
from pre_commit import color from pre_commit import color
from pre_commit import git from pre_commit import git
from pre_commit import output from pre_commit import output
from pre_commit.all_languages import languages
from pre_commit.clientlib import load_config from pre_commit.clientlib import load_config
from pre_commit.hook import Hook from pre_commit.hook import Hook
from pre_commit.languages.all import languages
from pre_commit.repository import all_hooks from pre_commit.repository import all_hooks
from pre_commit.repository import install_hook_envs from pre_commit.repository import install_hook_envs
from pre_commit.staged_files_only import staged_files_only from pre_commit.staged_files_only import staged_files_only
@ -57,37 +58,36 @@ def _full_msg(
def filter_by_include_exclude( def filter_by_include_exclude(
names: Collection[str], names: Iterable[str],
include: str, include: str,
exclude: str, exclude: str,
) -> list[str]: ) -> Generator[str]:
include_re, exclude_re = re.compile(include), re.compile(exclude) include_re, exclude_re = re.compile(include), re.compile(exclude)
return [ return (
filename for filename in names filename for filename in names
if include_re.search(filename) if include_re.search(filename)
if not exclude_re.search(filename) if not exclude_re.search(filename)
] )
class Classifier: class Classifier:
def __init__(self, filenames: Collection[str]) -> None: def __init__(self, filenames: Iterable[str]) -> None:
self.filenames = [f for f in filenames if os.path.lexists(f)] self.filenames = [f for f in filenames if os.path.lexists(f)]
@functools.lru_cache(maxsize=None) @functools.cache
def _types_for_file(self, filename: str) -> set[str]: def _types_for_file(self, filename: str) -> set[str]:
return tags_from_path(filename) return tags_from_path(filename)
def by_types( def by_types(
self, self,
names: Sequence[str], names: Iterable[str],
types: Collection[str], types: Iterable[str],
types_or: Collection[str], types_or: Iterable[str],
exclude_types: Collection[str], exclude_types: Iterable[str],
) -> list[str]: ) -> Generator[str]:
types = frozenset(types) types = frozenset(types)
types_or = frozenset(types_or) types_or = frozenset(types_or)
exclude_types = frozenset(exclude_types) exclude_types = frozenset(exclude_types)
ret = []
for filename in names: for filename in names:
tags = self._types_for_file(filename) tags = self._types_for_file(filename)
if ( if (
@ -95,24 +95,24 @@ class Classifier:
(not types_or or tags & types_or) and (not types_or or tags & types_or) and
not tags & exclude_types not tags & exclude_types
): ):
ret.append(filename) yield filename
return ret
def filenames_for_hook(self, hook: Hook) -> tuple[str, ...]: def filenames_for_hook(self, hook: Hook) -> Generator[str]:
names = self.filenames return self.by_types(
names = filter_by_include_exclude(names, hook.files, hook.exclude) filter_by_include_exclude(
names = self.by_types( self.filenames,
names, hook.files,
hook.exclude,
),
hook.types, hook.types,
hook.types_or, hook.types_or,
hook.exclude_types, hook.exclude_types,
) )
return tuple(names)
@classmethod @classmethod
def from_config( def from_config(
cls, cls,
filenames: Collection[str], filenames: Iterable[str],
include: str, include: str,
exclude: str, exclude: str,
) -> Classifier: ) -> Classifier:
@ -121,7 +121,7 @@ class Classifier:
# this also makes improperly quoted shell-based hooks work better # this also makes improperly quoted shell-based hooks work better
# see #1173 # see #1173
if os.altsep == '/' and os.sep == '\\': if os.altsep == '/' and os.sep == '\\':
filenames = [f.replace(os.sep, os.altsep) for f in filenames] filenames = (f.replace(os.sep, os.altsep) for f in filenames)
filenames = filter_by_include_exclude(filenames, include, exclude) filenames = filter_by_include_exclude(filenames, include, exclude)
return Classifier(filenames) return Classifier(filenames)
@ -148,7 +148,7 @@ def _run_single_hook(
verbose: bool, verbose: bool,
use_color: bool, use_color: bool,
) -> tuple[bool, bytes]: ) -> tuple[bool, bytes]:
filenames = classifier.filenames_for_hook(hook) filenames = tuple(classifier.filenames_for_hook(hook))
if hook.id in skips or hook.alias in skips: if hook.id in skips or hook.alias in skips:
output.write( output.write(
@ -187,7 +187,7 @@ def _run_single_hook(
if not hook.pass_filenames: if not hook.pass_filenames:
filenames = () filenames = ()
time_before = time.time() time_before = time.monotonic()
language = languages[hook.language] language = languages[hook.language]
with language.in_env(hook.prefix, hook.language_version): with language.in_env(hook.prefix, hook.language_version):
retcode, out = language.run_hook( retcode, out = language.run_hook(
@ -199,7 +199,7 @@ def _run_single_hook(
require_serial=hook.require_serial, require_serial=hook.require_serial,
color=use_color, color=use_color,
) )
duration = round(time.time() - time_before, 2) or 0 duration = round(time.monotonic() - time_before, 2) or 0
diff_after = _get_diff() diff_after = _get_diff()
# if the hook makes changes, fail the commit # if the hook makes changes, fail the commit
@ -250,10 +250,11 @@ def _compute_cols(hooks: Sequence[Hook]) -> int:
return max(cols, 80) return max(cols, 80)
def _all_filenames(args: argparse.Namespace) -> Collection[str]: def _all_filenames(args: argparse.Namespace) -> Iterable[str]:
# these hooks do not operate on files # these hooks do not operate on files
if args.hook_stage in { if args.hook_stage in {
'post-checkout', 'post-commit', 'post-merge', 'post-rewrite', 'post-checkout', 'post-commit', 'post-merge', 'post-rewrite',
'pre-rebase',
}: }:
return () return ()
elif args.hook_stage in {'prepare-commit-msg', 'commit-msg'}: elif args.hook_stage in {'prepare-commit-msg', 'commit-msg'}:
@ -272,7 +273,8 @@ def _all_filenames(args: argparse.Namespace) -> Collection[str]:
def _get_diff() -> bytes: def _get_diff() -> bytes:
_, out, _ = cmd_output_b( _, out, _ = cmd_output_b(
'git', 'diff', '--no-ext-diff', '--ignore-submodules', check=False, 'git', 'diff', '--no-ext-diff', '--no-textconv', '--ignore-submodules',
check=False,
) )
return out return out
@ -296,7 +298,8 @@ def _run_hooks(
verbose=args.verbose, use_color=args.color, verbose=args.verbose, use_color=args.color,
) )
retval |= current_retval retval |= current_retval
if retval and (config['fail_fast'] or hook.fail_fast): fail_fast = (config['fail_fast'] or hook.fail_fast or args.fail_fast)
if current_retval and fail_fast:
break break
if retval and args.show_diff_on_failure and prior_diff: if retval and args.show_diff_on_failure and prior_diff:
if args.all_files: if args.all_files:
@ -326,8 +329,7 @@ def _has_unmerged_paths() -> bool:
def _has_unstaged_config(config_file: str) -> bool: def _has_unstaged_config(config_file: str) -> bool:
retcode, _, _ = cmd_output_b( retcode, _, _ = cmd_output_b(
'git', 'diff', '--no-ext-diff', '--exit-code', config_file, 'git', 'diff', '--quiet', '--no-ext-diff', config_file, check=False,
check=False,
) )
# be explicit, other git errors don't mean it has an unstaged config. # be explicit, other git errors don't mean it has an unstaged config.
return retcode == 1 return retcode == 1
@ -389,6 +391,10 @@ def run(
environ['PRE_COMMIT_FROM_REF'] = args.from_ref environ['PRE_COMMIT_FROM_REF'] = args.from_ref
environ['PRE_COMMIT_TO_REF'] = args.to_ref environ['PRE_COMMIT_TO_REF'] = args.to_ref
if args.pre_rebase_upstream and args.pre_rebase_branch:
environ['PRE_COMMIT_PRE_REBASE_UPSTREAM'] = args.pre_rebase_upstream
environ['PRE_COMMIT_PRE_REBASE_BRANCH'] = args.pre_rebase_branch
if ( if (
args.remote_name and args.remote_url and args.remote_name and args.remote_url and
args.remote_branch and args.local_branch args.remote_branch and args.local_branch

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import Sequence from collections.abc import Sequence
from pre_commit import clientlib from pre_commit import clientlib

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import Sequence from collections.abc import Sequence
from pre_commit import clientlib from pre_commit import clientlib

View file

@ -10,17 +10,4 @@ LOCAL_REPO_VERSION = '1'
VERSION = importlib.metadata.version('pre_commit') VERSION = importlib.metadata.version('pre_commit')
# `manual` is not invoked by any installed git hook. See #719
STAGES = (
'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg',
'post-commit', 'manual', 'post-checkout', 'push', 'post-merge',
'post-rewrite',
)
HOOK_TYPES = (
'pre-commit', 'pre-merge-commit', 'pre-push', 'prepare-commit-msg',
'commit-msg', 'post-commit', 'post-checkout', 'post-merge',
'post-rewrite',
)
DEFAULT = 'default' DEFAULT = 'default'

View file

@ -3,10 +3,9 @@ from __future__ import annotations
import contextlib import contextlib
import enum import enum
import os import os
from typing import Generator from collections.abc import Generator
from typing import MutableMapping from collections.abc import MutableMapping
from typing import NamedTuple from typing import NamedTuple
from typing import Tuple
from typing import Union from typing import Union
_Unset = enum.Enum('_Unset', 'UNSET') _Unset = enum.Enum('_Unset', 'UNSET')
@ -18,9 +17,9 @@ class Var(NamedTuple):
default: str = '' default: str = ''
SubstitutionT = Tuple[Union[str, Var], ...] SubstitutionT = tuple[Union[str, Var], ...]
ValueT = Union[str, _Unset, SubstitutionT] ValueT = Union[str, _Unset, SubstitutionT]
PatchesT = Tuple[Tuple[str, ValueT], ...] PatchesT = tuple[tuple[str, ValueT], ...]
def format_env(parts: SubstitutionT, env: MutableMapping[str, str]) -> str: def format_env(parts: SubstitutionT, env: MutableMapping[str, str]) -> str:
@ -34,7 +33,7 @@ def format_env(parts: SubstitutionT, env: MutableMapping[str, str]) -> str:
def envcontext( def envcontext(
patch: PatchesT, patch: PatchesT,
_env: MutableMapping[str, str] | None = None, _env: MutableMapping[str, str] | None = None,
) -> Generator[None, None, None]: ) -> Generator[None]:
"""In this context, `os.environ` is modified according to `patch`. """In this context, `os.environ` is modified according to `patch`.
`patch` is an iterable of 2-tuples (key, value): `patch` is an iterable of 2-tuples (key, value):

View file

@ -5,7 +5,7 @@ import functools
import os.path import os.path
import sys import sys
import traceback import traceback
from typing import Generator from collections.abc import Generator
from typing import IO from typing import IO
import pre_commit.constants as C import pre_commit.constants as C
@ -68,7 +68,7 @@ def _log_and_exit(
@contextlib.contextmanager @contextlib.contextmanager
def error_handler() -> Generator[None, None, None]: def error_handler() -> Generator[None]:
try: try:
yield yield
except (Exception, KeyboardInterrupt) as e: except (Exception, KeyboardInterrupt) as e:

View file

@ -3,8 +3,8 @@ from __future__ import annotations
import contextlib import contextlib
import errno import errno
import sys import sys
from typing import Callable from collections.abc import Callable
from typing import Generator from collections.abc import Generator
if sys.platform == 'win32': # pragma: no cover (windows) if sys.platform == 'win32': # pragma: no cover (windows)
@ -20,7 +20,7 @@ if sys.platform == 'win32': # pragma: no cover (windows)
def _locked( def _locked(
fileno: int, fileno: int,
blocked_cb: Callable[[], None], blocked_cb: Callable[[], None],
) -> Generator[None, None, None]: ) -> Generator[None]:
try: try:
msvcrt.locking(fileno, msvcrt.LK_NBLCK, _region) msvcrt.locking(fileno, msvcrt.LK_NBLCK, _region)
except OSError: except OSError:
@ -53,7 +53,7 @@ else: # pragma: win32 no cover
def _locked( def _locked(
fileno: int, fileno: int,
blocked_cb: Callable[[], None], blocked_cb: Callable[[], None],
) -> Generator[None, None, None]: ) -> Generator[None]:
try: try:
fcntl.flock(fileno, fcntl.LOCK_EX | fcntl.LOCK_NB) fcntl.flock(fileno, fcntl.LOCK_EX | fcntl.LOCK_NB)
except OSError: # pragma: no cover (tests are single-threaded) except OSError: # pragma: no cover (tests are single-threaded)
@ -69,7 +69,7 @@ else: # pragma: win32 no cover
def lock( def lock(
path: str, path: str,
blocked_cb: Callable[[], None], blocked_cb: Callable[[], None],
) -> Generator[None, None, None]: ) -> Generator[None]:
with open(path, 'a+') as f: with open(path, 'a+') as f:
with _locked(f.fileno(), blocked_cb): with _locked(f.fileno(), blocked_cb):
yield yield

View file

@ -3,7 +3,7 @@ from __future__ import annotations
import logging import logging
import os.path import os.path
import sys import sys
from typing import Mapping from collections.abc import Mapping
from pre_commit.errors import FatalError from pre_commit.errors import FatalError
from pre_commit.util import CalledProcessError from pre_commit.util import CalledProcessError
@ -126,7 +126,7 @@ def get_conflicted_files() -> set[str]:
merge_diff_filenames = zsplit( merge_diff_filenames = zsplit(
cmd_output( cmd_output(
'git', 'diff', '--name-only', '--no-ext-diff', '-z', 'git', 'diff', '--name-only', '--no-ext-diff', '-z',
'-m', tree_hash, 'HEAD', 'MERGE_HEAD', '-m', tree_hash, 'HEAD', 'MERGE_HEAD', '--',
)[1], )[1],
) )
return set(merge_conflict_filenames) | set(merge_diff_filenames) return set(merge_conflict_filenames) | set(merge_diff_filenames)
@ -219,7 +219,7 @@ def check_for_cygwin_mismatch() -> None:
if is_cygwin_python ^ is_cygwin_git: if is_cygwin_python ^ is_cygwin_git:
exe_type = {True: '(cygwin)', False: '(windows)'} exe_type = {True: '(cygwin)', False: '(windows)'}
logger.warn( logger.warning(
f'pre-commit has detected a mix of cygwin python / git\n' f'pre-commit has detected a mix of cygwin python / git\n'
f'This combination is not supported, it is likely you will ' f'This combination is not supported, it is likely you will '
f'receive an error later in the program.\n' f'receive an error later in the program.\n'

View file

@ -1,9 +1,9 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from collections.abc import Sequence
from typing import Any from typing import Any
from typing import NamedTuple from typing import NamedTuple
from typing import Sequence
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix

View file

@ -1,27 +1,65 @@
from __future__ import annotations from __future__ import annotations
import contextlib import contextlib
import multiprocessing
import os import os
import random import random
import re import re
import shlex import shlex
import sys
from collections.abc import Generator
from collections.abc import Sequence
from typing import Any from typing import Any
from typing import Generator from typing import ContextManager
from typing import NoReturn from typing import NoReturn
from typing import Sequence from typing import Protocol
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import parse_shebang from pre_commit import parse_shebang
from pre_commit import xargs
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output_b from pre_commit.util import cmd_output_b
from pre_commit.xargs import xargs
FIXED_RANDOM_SEED = 1542676187 FIXED_RANDOM_SEED = 1542676187
SHIMS_RE = re.compile(r'[/\\]shims[/\\]') SHIMS_RE = re.compile(r'[/\\]shims[/\\]')
class Language(Protocol):
# Use `None` for no installation / environment
@property
def ENVIRONMENT_DIR(self) -> str | None: ...
# return a value to replace `'default` for `language_version`
def get_default_version(self) -> str: ...
# return whether the environment is healthy (or should be rebuilt)
def health_check(self, prefix: Prefix, version: str) -> str | None: ...
# install a repository for the given language and language_version
def install_environment(
self,
prefix: Prefix,
version: str,
additional_dependencies: Sequence[str],
) -> None:
...
# modify the environment for hook execution
def in_env(self, prefix: Prefix, version: str) -> ContextManager[None]: ...
# execute a hook and return the exit code and output
def run_hook(
self,
prefix: Prefix,
entry: str,
args: Sequence[str],
file_args: Sequence[str],
*,
is_local: bool,
require_serial: bool,
color: bool,
) -> tuple[int, bytes]:
...
def exe_exists(exe: str) -> bool: def exe_exists(exe: str) -> bool:
found = parse_shebang.find_executable(exe) found = parse_shebang.find_executable(exe)
if found is None: # exe exists if found is None: # exe exists
@ -45,7 +83,7 @@ def exe_exists(exe: str) -> bool:
) )
def run_setup_cmd(prefix: Prefix, cmd: tuple[str, ...], **kwargs: Any) -> None: def setup_cmd(prefix: Prefix, cmd: tuple[str, ...], **kwargs: Any) -> None:
cmd_output_b(*cmd, cwd=prefix.prefix_dir, **kwargs) cmd_output_b(*cmd, cwd=prefix.prefix_dir, **kwargs)
@ -90,7 +128,7 @@ def no_install(
@contextlib.contextmanager @contextlib.contextmanager
def no_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def no_env(prefix: Prefix, version: str) -> Generator[None]:
yield yield
@ -102,10 +140,7 @@ def target_concurrency() -> int:
if 'TRAVIS' in os.environ: if 'TRAVIS' in os.environ:
return 2 return 2
else: else:
try: return xargs.cpu_count()
return multiprocessing.cpu_count()
except NotImplementedError:
return 1
def _shuffled(seq: Sequence[str]) -> list[str]: def _shuffled(seq: Sequence[str]) -> list[str]:
@ -133,11 +168,14 @@ def run_xargs(
# ordering. # ordering.
file_args = _shuffled(file_args) file_args = _shuffled(file_args)
jobs = target_concurrency() jobs = target_concurrency()
return xargs(cmd, file_args, target_concurrency=jobs, color=color) return xargs.xargs(cmd, file_args, target_concurrency=jobs, color=color)
def hook_cmd(entry: str, args: Sequence[str]) -> tuple[str, ...]: def hook_cmd(entry: str, args: Sequence[str]) -> tuple[str, ...]:
return (*shlex.split(entry), *args) cmd = shlex.split(entry)
if cmd[:2] == ['pre-commit', 'hazmat']:
cmd = [sys.executable, '-m', 'pre_commit.commands.hazmat', *cmd[2:]]
return (*cmd, *args)
def basic_run_hook( def basic_run_hook(

View file

@ -1,99 +0,0 @@
from __future__ import annotations
from typing import ContextManager
from typing import Protocol
from typing import Sequence
from pre_commit.languages import conda
from pre_commit.languages import coursier
from pre_commit.languages import dart
from pre_commit.languages import docker
from pre_commit.languages import docker_image
from pre_commit.languages import dotnet
from pre_commit.languages import fail
from pre_commit.languages import golang
from pre_commit.languages import lua
from pre_commit.languages import node
from pre_commit.languages import perl
from pre_commit.languages import pygrep
from pre_commit.languages import python
from pre_commit.languages import r
from pre_commit.languages import ruby
from pre_commit.languages import rust
from pre_commit.languages import script
from pre_commit.languages import swift
from pre_commit.languages import system
from pre_commit.prefix import Prefix
class Language(Protocol):
# Use `None` for no installation / environment
@property
def ENVIRONMENT_DIR(self) -> str | None: ...
# return a value to replace `'default` for `language_version`
def get_default_version(self) -> str: ...
# return whether the environment is healthy (or should be rebuilt)
def health_check(
self,
prefix: Prefix,
language_version: str,
) -> str | None:
...
# install a repository for the given language and language_version
def install_environment(
self,
prefix: Prefix,
version: str,
additional_dependencies: Sequence[str],
) -> None:
...
# modify the environment for hook execution
def in_env(
self,
prefix: Prefix,
version: str,
) -> ContextManager[None]:
...
# execute a hook and return the exit code and output
def run_hook(
self,
prefix: Prefix,
entry: str,
args: Sequence[str],
file_args: Sequence[str],
*,
is_local: bool,
require_serial: bool,
color: bool,
) -> tuple[int, bytes]:
...
languages: dict[str, Language] = {
'conda': conda,
'coursier': coursier,
'dart': dart,
'docker': docker,
'docker_image': docker_image,
'dotnet': dotnet,
'fail': fail,
'golang': golang,
'lua': lua,
'node': node,
'perl': perl,
'pygrep': pygrep,
'python': python,
'r': r,
'ruby': ruby,
'rust': rust,
'script': script,
'swift': swift,
'system': system,
# TODO: fully deprecate `python_venv`
'python_venv': python,
}
all_languages = sorted(languages)

View file

@ -2,22 +2,23 @@ from __future__ import annotations
import contextlib import contextlib
import os import os
from typing import Generator import sys
from typing import Sequence from collections.abc import Generator
from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import SubstitutionT from pre_commit.envcontext import SubstitutionT
from pre_commit.envcontext import UNSET from pre_commit.envcontext import UNSET
from pre_commit.envcontext import Var from pre_commit.envcontext import Var
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output_b from pre_commit.util import cmd_output_b
ENVIRONMENT_DIR = 'conda' ENVIRONMENT_DIR = 'conda'
get_default_version = helpers.basic_get_default_version get_default_version = lang_base.basic_get_default_version
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
run_hook = helpers.basic_run_hook run_hook = lang_base.basic_run_hook
def get_env_patch(env: str) -> PatchesT: def get_env_patch(env: str) -> PatchesT:
@ -26,7 +27,7 @@ def get_env_patch(env: str) -> PatchesT:
# $CONDA_PREFIX/Scripts and $CONDA_PREFIX. Whereas the latter only # $CONDA_PREFIX/Scripts and $CONDA_PREFIX. Whereas the latter only
# seems to be used for python.exe. # seems to be used for python.exe.
path: SubstitutionT = (os.path.join(env, 'bin'), os.pathsep, Var('PATH')) path: SubstitutionT = (os.path.join(env, 'bin'), os.pathsep, Var('PATH'))
if os.name == 'nt': # pragma: no cover (platform specific) if sys.platform == 'win32': # pragma: win32 cover
path = (env, os.pathsep, *path) path = (env, os.pathsep, *path)
path = (os.path.join(env, 'Scripts'), os.pathsep, *path) path = (os.path.join(env, 'Scripts'), os.pathsep, *path)
path = (os.path.join(env, 'Library', 'bin'), os.pathsep, *path) path = (os.path.join(env, 'Library', 'bin'), os.pathsep, *path)
@ -40,8 +41,8 @@ def get_env_patch(env: str) -> PatchesT:
@contextlib.contextmanager @contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir)): with envcontext(get_env_patch(envdir)):
yield yield
@ -60,11 +61,11 @@ def install_environment(
version: str, version: str,
additional_dependencies: Sequence[str], additional_dependencies: Sequence[str],
) -> None: ) -> None:
helpers.assert_version_default('conda', version) lang_base.assert_version_default('conda', version)
conda_exe = _conda_exe() conda_exe = _conda_exe()
env_dir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) env_dir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
cmd_output_b( cmd_output_b(
conda_exe, 'env', 'create', '-p', env_dir, '--file', conda_exe, 'env', 'create', '-p', env_dir, '--file',
'environment.yml', cwd=prefix.prefix_dir, 'environment.yml', cwd=prefix.prefix_dir,

View file

@ -2,22 +2,22 @@ from __future__ import annotations
import contextlib import contextlib
import os.path import os.path
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var from pre_commit.envcontext import Var
from pre_commit.errors import FatalError from pre_commit.errors import FatalError
from pre_commit.languages import helpers
from pre_commit.parse_shebang import find_executable from pre_commit.parse_shebang import find_executable
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
ENVIRONMENT_DIR = 'coursier' ENVIRONMENT_DIR = 'coursier'
get_default_version = helpers.basic_get_default_version get_default_version = lang_base.basic_get_default_version
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
run_hook = helpers.basic_run_hook run_hook = lang_base.basic_run_hook
def install_environment( def install_environment(
@ -25,7 +25,7 @@ def install_environment(
version: str, version: str,
additional_dependencies: Sequence[str], additional_dependencies: Sequence[str],
) -> None: ) -> None:
helpers.assert_version_default('coursier', version) lang_base.assert_version_default('coursier', version)
# Support both possible executable names (either "cs" or "coursier") # Support both possible executable names (either "cs" or "coursier")
cs = find_executable('cs') or find_executable('coursier') cs = find_executable('cs') or find_executable('coursier')
@ -35,12 +35,12 @@ def install_environment(
'executables in the application search path', 'executables in the application search path',
) )
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
def _install(*opts: str) -> None: def _install(*opts: str) -> None:
assert cs is not None assert cs is not None
helpers.run_setup_cmd(prefix, (cs, 'fetch', *opts)) lang_base.setup_cmd(prefix, (cs, 'fetch', *opts))
helpers.run_setup_cmd(prefix, (cs, 'install', '--dir', envdir, *opts)) lang_base.setup_cmd(prefix, (cs, 'install', '--dir', envdir, *opts))
with in_env(prefix, version): with in_env(prefix, version):
channel = prefix.path('.pre-commit-channel') channel = prefix.path('.pre-commit-channel')
@ -70,7 +70,7 @@ def get_env_patch(target_dir: str) -> PatchesT:
@contextlib.contextmanager @contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir)): with envcontext(get_env_patch(envdir)):
yield yield

View file

@ -4,22 +4,22 @@ import contextlib
import os.path import os.path
import shutil import shutil
import tempfile import tempfile
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var from pre_commit.envcontext import Var
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import win_exe from pre_commit.util import win_exe
from pre_commit.yaml import yaml_load from pre_commit.yaml import yaml_load
ENVIRONMENT_DIR = 'dartenv' ENVIRONMENT_DIR = 'dartenv'
get_default_version = helpers.basic_get_default_version get_default_version = lang_base.basic_get_default_version
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
run_hook = helpers.basic_run_hook run_hook = lang_base.basic_run_hook
def get_env_patch(venv: str) -> PatchesT: def get_env_patch(venv: str) -> PatchesT:
@ -29,8 +29,8 @@ def get_env_patch(venv: str) -> PatchesT:
@contextlib.contextmanager @contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir)): with envcontext(get_env_patch(envdir)):
yield yield
@ -40,9 +40,9 @@ def install_environment(
version: str, version: str,
additional_dependencies: Sequence[str], additional_dependencies: Sequence[str],
) -> None: ) -> None:
helpers.assert_version_default('dart', version) lang_base.assert_version_default('dart', version)
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
bin_dir = os.path.join(envdir, 'bin') bin_dir = os.path.join(envdir, 'bin')
def _install_dir(prefix_p: Prefix, pub_cache: str) -> None: def _install_dir(prefix_p: Prefix, pub_cache: str) -> None:
@ -51,10 +51,10 @@ def install_environment(
with open(prefix_p.path('pubspec.yaml')) as f: with open(prefix_p.path('pubspec.yaml')) as f:
pubspec_contents = yaml_load(f) pubspec_contents = yaml_load(f)
helpers.run_setup_cmd(prefix_p, ('dart', 'pub', 'get'), env=dart_env) lang_base.setup_cmd(prefix_p, ('dart', 'pub', 'get'), env=dart_env)
for executable in pubspec_contents['executables']: for executable in pubspec_contents['executables']:
helpers.run_setup_cmd( lang_base.setup_cmd(
prefix_p, prefix_p,
( (
'dart', 'compile', 'exe', 'dart', 'compile', 'exe',
@ -77,7 +77,7 @@ def install_environment(
else: else:
dep_cmd = (dep,) dep_cmd = (dep,)
helpers.run_setup_cmd( lang_base.setup_cmd(
prefix, prefix,
('dart', 'pub', 'cache', 'add', *dep_cmd), ('dart', 'pub', 'cache', 'add', *dep_cmd),
env={**os.environ, 'PUB_CACHE': dep_tmp}, env={**os.environ, 'PUB_CACHE': dep_tmp},

View file

@ -1,46 +1,51 @@
from __future__ import annotations from __future__ import annotations
import contextlib
import functools
import hashlib import hashlib
import json import json
import os import os
from typing import Sequence import re
from collections.abc import Sequence
from pre_commit.languages import helpers from pre_commit import lang_base
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import CalledProcessError from pre_commit.util import CalledProcessError
from pre_commit.util import cmd_output_b from pre_commit.util import cmd_output_b
ENVIRONMENT_DIR = 'docker' ENVIRONMENT_DIR = 'docker'
PRE_COMMIT_LABEL = 'PRE_COMMIT' PRE_COMMIT_LABEL = 'PRE_COMMIT'
get_default_version = helpers.basic_get_default_version get_default_version = lang_base.basic_get_default_version
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
in_env = helpers.no_env # no special environment for docker in_env = lang_base.no_env # no special environment for docker
_HOSTNAME_MOUNT_RE = re.compile(
rb"""
/containers
(?:/overlay-containers)?
/([a-z0-9]{64})
(?:/userdata)?
/hostname
""",
re.VERBOSE,
)
def _is_in_docker() -> bool: def _get_container_id() -> str | None:
try: with contextlib.suppress(FileNotFoundError):
with open('/proc/1/cgroup', 'rb') as f: with open('/proc/1/mountinfo', 'rb') as f:
return b'docker' in f.read() for line in f:
except FileNotFoundError: m = _HOSTNAME_MOUNT_RE.search(line)
return False if m:
return m[1].decode()
return None
def _get_container_id() -> str:
# It's assumed that we already check /proc/1/cgroup in _is_in_docker. The
# cpuset cgroup controller existed since cgroups were introduced so this
# way of getting the container ID is pretty reliable.
with open('/proc/1/cgroup', 'rb') as f:
for line in f.readlines():
if line.split(b':')[1] == b'cpuset':
return os.path.basename(line.split(b':')[2]).strip().decode()
raise RuntimeError('Failed to find the container ID in /proc/1/cgroup.')
def _get_docker_path(path: str) -> str: def _get_docker_path(path: str) -> str:
if not _is_in_docker():
return path
container_id = _get_container_id() container_id = _get_container_id()
if container_id is None:
return path
try: try:
_, out, _ = cmd_output_b('docker', 'inspect', container_id) _, out, _ = cmd_output_b('docker', 'inspect', container_id)
@ -84,16 +89,16 @@ def build_docker_image(
cmd += ('--pull',) cmd += ('--pull',)
# This must come last for old versions of docker. See #477 # This must come last for old versions of docker. See #477
cmd += ('.',) cmd += ('.',)
helpers.run_setup_cmd(prefix, cmd) lang_base.setup_cmd(prefix, cmd)
def install_environment( def install_environment(
prefix: Prefix, version: str, additional_dependencies: Sequence[str], prefix: Prefix, version: str, additional_dependencies: Sequence[str],
) -> None: # pragma: win32 no cover ) -> None: # pragma: win32 no cover
helpers.assert_version_default('docker', version) lang_base.assert_version_default('docker', version)
helpers.assert_no_additional_deps('docker', additional_dependencies) lang_base.assert_no_additional_deps('docker', additional_dependencies)
directory = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) directory = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
# Docker doesn't really have relevant disk environment, but pre-commit # Docker doesn't really have relevant disk environment, but pre-commit
# still needs to cleanup its state files on failure # still needs to cleanup its state files on failure
@ -101,17 +106,47 @@ def install_environment(
os.mkdir(directory) os.mkdir(directory)
@functools.lru_cache(maxsize=1)
def _is_rootless() -> bool: # pragma: win32 no cover
retcode, out, _ = cmd_output_b(
'docker', 'system', 'info', '--format', '{{ json . }}',
)
if retcode != 0:
return False
info = json.loads(out)
try:
return (
# docker:
# https://docs.docker.com/reference/api/engine/version/v1.48/#tag/System/operation/SystemInfo
'name=rootless' in (info.get('SecurityOptions') or ()) or
# podman:
# https://docs.podman.io/en/latest/_static/api.html?version=v5.4#tag/system/operation/SystemInfoLibpod
info['host']['security']['rootless']
)
except KeyError:
return False
def get_docker_user() -> tuple[str, ...]: # pragma: win32 no cover def get_docker_user() -> tuple[str, ...]: # pragma: win32 no cover
if _is_rootless():
return ()
try: try:
return ('-u', f'{os.getuid()}:{os.getgid()}') return ('-u', f'{os.getuid()}:{os.getgid()}')
except AttributeError: except AttributeError:
return () return ()
def docker_cmd() -> tuple[str, ...]: # pragma: win32 no cover def get_docker_tty(*, color: bool) -> tuple[str, ...]: # pragma: win32 no cover # noqa: E501
return (('--tty',) if color else ())
def docker_cmd(*, color: bool) -> tuple[str, ...]: # pragma: win32 no cover
return ( return (
'docker', 'run', 'docker', 'run',
'--rm', '--rm',
*get_docker_tty(color=color),
*get_docker_user(), *get_docker_user(),
# https://docs.docker.com/engine/reference/commandline/run/#mount-volumes-from-container-volumes-from # https://docs.docker.com/engine/reference/commandline/run/#mount-volumes-from-container-volumes-from
# The `Z` option tells Docker to label the content with a private # The `Z` option tells Docker to label the content with a private
@ -135,12 +170,11 @@ def run_hook(
# automated cleanup of docker images. # automated cleanup of docker images.
build_docker_image(prefix, pull=False) build_docker_image(prefix, pull=False)
entry_exe, *cmd_rest = helpers.hook_cmd(entry, args) entry_exe, *cmd_rest = lang_base.hook_cmd(entry, args)
entry_tag = ('--entrypoint', entry_exe, docker_tag(prefix)) entry_tag = ('--entrypoint', entry_exe, docker_tag(prefix))
cmd = (*docker_cmd(), *entry_tag, *cmd_rest) return lang_base.run_xargs(
return helpers.run_xargs( (*docker_cmd(color=color), *entry_tag, *cmd_rest),
cmd,
file_args, file_args,
require_serial=require_serial, require_serial=require_serial,
color=color, color=color,

View file

@ -1,16 +1,16 @@
from __future__ import annotations from __future__ import annotations
from typing import Sequence from collections.abc import Sequence
from pre_commit.languages import helpers from pre_commit import lang_base
from pre_commit.languages.docker import docker_cmd from pre_commit.languages.docker import docker_cmd
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
ENVIRONMENT_DIR = None ENVIRONMENT_DIR = None
get_default_version = helpers.basic_get_default_version get_default_version = lang_base.basic_get_default_version
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
install_environment = helpers.no_install install_environment = lang_base.no_install
in_env = helpers.no_env in_env = lang_base.no_env
def run_hook( def run_hook(
@ -23,8 +23,8 @@ def run_hook(
require_serial: bool, require_serial: bool,
color: bool, color: bool,
) -> tuple[int, bytes]: # pragma: win32 no cover ) -> tuple[int, bytes]: # pragma: win32 no cover
cmd = docker_cmd() + helpers.hook_cmd(entry, args) cmd = docker_cmd(color=color) + lang_base.hook_cmd(entry, args)
return helpers.run_xargs( return lang_base.run_xargs(
cmd, cmd,
file_args, file_args,
require_serial=require_serial, require_serial=require_serial,

View file

@ -6,21 +6,21 @@ import re
import tempfile import tempfile
import xml.etree.ElementTree import xml.etree.ElementTree
import zipfile import zipfile
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var from pre_commit.envcontext import Var
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
ENVIRONMENT_DIR = 'dotnetenv' ENVIRONMENT_DIR = 'dotnetenv'
BIN_DIR = 'bin' BIN_DIR = 'bin'
get_default_version = helpers.basic_get_default_version get_default_version = lang_base.basic_get_default_version
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
run_hook = helpers.basic_run_hook run_hook = lang_base.basic_run_hook
def get_env_patch(venv: str) -> PatchesT: def get_env_patch(venv: str) -> PatchesT:
@ -30,14 +30,14 @@ def get_env_patch(venv: str) -> PatchesT:
@contextlib.contextmanager @contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir)): with envcontext(get_env_patch(envdir)):
yield yield
@contextlib.contextmanager @contextlib.contextmanager
def _nuget_config_no_sources() -> Generator[str, None, None]: def _nuget_config_no_sources() -> Generator[str]:
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
nuget_config = os.path.join(tmpdir, 'nuget.config') nuget_config = os.path.join(tmpdir, 'nuget.config')
with open(nuget_config, 'w') as f: with open(nuget_config, 'w') as f:
@ -57,19 +57,19 @@ def install_environment(
version: str, version: str,
additional_dependencies: Sequence[str], additional_dependencies: Sequence[str],
) -> None: ) -> None:
helpers.assert_version_default('dotnet', version) lang_base.assert_version_default('dotnet', version)
helpers.assert_no_additional_deps('dotnet', additional_dependencies) lang_base.assert_no_additional_deps('dotnet', additional_dependencies)
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
build_dir = 'pre-commit-build' build_dir = prefix.path('pre-commit-build')
# Build & pack nupkg file # Build & pack nupkg file
helpers.run_setup_cmd( lang_base.setup_cmd(
prefix, prefix,
( (
'dotnet', 'pack', 'dotnet', 'pack',
'--configuration', 'Release', '--configuration', 'Release',
'--output', build_dir, '--property', f'PackageOutputPath={build_dir}',
), ),
) )
@ -99,7 +99,7 @@ def install_environment(
# Install to bin dir # Install to bin dir
with _nuget_config_no_sources() as nuget_config: with _nuget_config_no_sources() as nuget_config:
helpers.run_setup_cmd( lang_base.setup_cmd(
prefix, prefix,
( (
'dotnet', 'tool', 'install', 'dotnet', 'tool', 'install',
@ -109,7 +109,3 @@ def install_environment(
tool_id, tool_id,
), ),
) )
# Clean the git dir, ignoring the environment dir
clean_cmd = ('git', 'clean', '-ffxd', '-e', f'{ENVIRONMENT_DIR}-*')
helpers.run_setup_cmd(prefix, clean_cmd)

View file

@ -1,15 +1,15 @@
from __future__ import annotations from __future__ import annotations
from typing import Sequence from collections.abc import Sequence
from pre_commit.languages import helpers from pre_commit import lang_base
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
ENVIRONMENT_DIR = None ENVIRONMENT_DIR = None
get_default_version = helpers.basic_get_default_version get_default_version = lang_base.basic_get_default_version
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
install_environment = helpers.no_install install_environment = lang_base.no_install
in_env = helpers.no_env in_env = lang_base.no_env
def run_hook( def run_hook(

View file

@ -12,24 +12,25 @@ import tempfile
import urllib.error import urllib.error
import urllib.request import urllib.request
import zipfile import zipfile
from collections.abc import Generator
from collections.abc import Sequence
from typing import ContextManager from typing import ContextManager
from typing import Generator
from typing import IO from typing import IO
from typing import Protocol from typing import Protocol
from typing import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var from pre_commit.envcontext import Var
from pre_commit.languages import helpers from pre_commit.git import no_git_env
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output from pre_commit.util import cmd_output
from pre_commit.util import rmtree from pre_commit.util import rmtree
ENVIRONMENT_DIR = 'golangenv' ENVIRONMENT_DIR = 'golangenv'
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
run_hook = helpers.basic_run_hook run_hook = lang_base.basic_run_hook
_ARCH_ALIASES = { _ARCH_ALIASES = {
'x86_64': 'amd64', 'x86_64': 'amd64',
@ -60,7 +61,7 @@ else: # pragma: win32 no cover
@functools.lru_cache(maxsize=1) @functools.lru_cache(maxsize=1)
def get_default_version() -> str: def get_default_version() -> str:
if helpers.exe_exists('go'): if lang_base.exe_exists('go'):
return 'system' return 'system'
else: else:
return C.DEFAULT return C.DEFAULT
@ -74,6 +75,7 @@ def get_env_patch(venv: str, version: str) -> PatchesT:
return ( return (
('GOROOT', os.path.join(venv, '.go')), ('GOROOT', os.path.join(venv, '.go')),
('GOTOOLCHAIN', 'local'),
( (
'PATH', ( 'PATH', (
os.path.join(venv, 'bin'), os.pathsep, os.path.join(venv, 'bin'), os.pathsep,
@ -88,8 +90,7 @@ def _infer_go_version(version: str) -> str:
if version != C.DEFAULT: if version != C.DEFAULT:
return version return version
resp = urllib.request.urlopen('https://go.dev/dl/?mode=json') resp = urllib.request.urlopen('https://go.dev/dl/?mode=json')
# TODO: 3.9+ .removeprefix('go') return json.load(resp)[0]['version'].removeprefix('go')
return json.load(resp)[0]['version'][2:]
def _get_url(version: str) -> str: def _get_url(version: str) -> str:
@ -120,8 +121,8 @@ def _install_go(version: str, dest: str) -> None:
@contextlib.contextmanager @contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir, version)): with envcontext(get_env_patch(envdir, version)):
yield yield
@ -131,7 +132,7 @@ def install_environment(
version: str, version: str,
additional_dependencies: Sequence[str], additional_dependencies: Sequence[str],
) -> None: ) -> None:
env_dir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) env_dir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
if version != 'system': if version != 'system':
_install_go(version, env_dir) _install_go(version, env_dir)
@ -141,17 +142,18 @@ def install_environment(
else: else:
gopath = env_dir gopath = env_dir
env = dict(os.environ, GOPATH=gopath) env = no_git_env(dict(os.environ, GOPATH=gopath))
env.pop('GOBIN', None) env.pop('GOBIN', None)
if version != 'system': if version != 'system':
env['GOTOOLCHAIN'] = 'local'
env['GOROOT'] = os.path.join(env_dir, '.go') env['GOROOT'] = os.path.join(env_dir, '.go')
env['PATH'] = os.pathsep.join(( env['PATH'] = os.pathsep.join((
os.path.join(env_dir, '.go', 'bin'), os.environ['PATH'], os.path.join(env_dir, '.go', 'bin'), os.environ['PATH'],
)) ))
helpers.run_setup_cmd(prefix, ('go', 'install', './...'), env=env) lang_base.setup_cmd(prefix, ('go', 'install', './...'), env=env)
for dependency in additional_dependencies: for dependency in additional_dependencies:
helpers.run_setup_cmd(prefix, ('go', 'install', dependency), env=env) lang_base.setup_cmd(prefix, ('go', 'install', dependency), env=env)
# save some disk space -- we don't need this after installation # save some disk space -- we don't need this after installation
pkgdir = os.path.join(env_dir, 'pkg') pkgdir = os.path.join(env_dir, 'pkg')

View file

@ -0,0 +1,56 @@
from __future__ import annotations
import contextlib
import os.path
from collections.abc import Generator
from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var
from pre_commit.errors import FatalError
from pre_commit.prefix import Prefix
ENVIRONMENT_DIR = 'hs_env'
get_default_version = lang_base.basic_get_default_version
health_check = lang_base.basic_health_check
run_hook = lang_base.basic_run_hook
def get_env_patch(target_dir: str) -> PatchesT:
bin_path = os.path.join(target_dir, 'bin')
return (('PATH', (bin_path, os.pathsep, Var('PATH'))),)
@contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir)):
yield
def install_environment(
prefix: Prefix,
version: str,
additional_dependencies: Sequence[str],
) -> None:
lang_base.assert_version_default('haskell', version)
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
pkgs = [*prefix.star('.cabal'), *additional_dependencies]
if not pkgs:
raise FatalError('Expected .cabal files or additional_dependencies')
bindir = os.path.join(envdir, 'bin')
os.makedirs(bindir, exist_ok=True)
lang_base.setup_cmd(prefix, ('cabal', 'update'))
lang_base.setup_cmd(
prefix,
(
'cabal', 'install',
'--install-method', 'copy',
'--installdir', bindir,
*pkgs,
),
)

View file

@ -0,0 +1,133 @@
from __future__ import annotations
import contextlib
import os
import shutil
from collections.abc import Generator
from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import UNSET
from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output_b
ENVIRONMENT_DIR = 'juliaenv'
health_check = lang_base.basic_health_check
get_default_version = lang_base.basic_get_default_version
def run_hook(
prefix: Prefix,
entry: str,
args: Sequence[str],
file_args: Sequence[str],
*,
is_local: bool,
require_serial: bool,
color: bool,
) -> tuple[int, bytes]:
# `entry` is a (hook-repo relative) file followed by (optional) args, e.g.
# `bin/id.jl` or `bin/hook.jl --arg1 --arg2` so we
# 1) shell parse it and join with args with hook_cmd
# 2) prepend the hooks prefix path to the first argument (the file), unless
# it is a local script
# 3) prepend `julia` as the interpreter
cmd = lang_base.hook_cmd(entry, args)
script = cmd[0] if is_local else prefix.path(cmd[0])
cmd = ('julia', '--startup-file=no', script, *cmd[1:])
return lang_base.run_xargs(
cmd,
file_args,
require_serial=require_serial,
color=color,
)
def get_env_patch(target_dir: str, version: str) -> PatchesT:
return (
('JULIA_LOAD_PATH', target_dir),
# May be set, remove it to not interfer with LOAD_PATH
('JULIA_PROJECT', UNSET),
)
@contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir, version)):
yield
def install_environment(
prefix: Prefix,
version: str,
additional_dependencies: Sequence[str],
) -> None:
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with in_env(prefix, version):
# TODO: Support language_version with juliaup similar to rust via
# rustup
# if version != 'system':
# ...
# Copy Project.toml to hook env if it exist
os.makedirs(envdir, exist_ok=True)
project_names = ('JuliaProject.toml', 'Project.toml')
project_found = False
for project_name in project_names:
project_file = prefix.path(project_name)
if not os.path.isfile(project_file):
continue
shutil.copy(project_file, envdir)
project_found = True
break
# If no project file was found we create an empty one so that the
# package manager doesn't error
if not project_found:
open(os.path.join(envdir, 'Project.toml'), 'a').close()
# Copy Manifest.toml to hook env if it exists
manifest_names = ('JuliaManifest.toml', 'Manifest.toml')
for manifest_name in manifest_names:
manifest_file = prefix.path(manifest_name)
if not os.path.isfile(manifest_file):
continue
shutil.copy(manifest_file, envdir)
break
# Julia code to instantiate the hook environment
julia_code = """
@assert length(ARGS) > 0
hook_env = ARGS[1]
deps = join(ARGS[2:end], " ")
# We prepend @stdlib here so that we can load the package manager even
# though `get_env_patch` limits `JULIA_LOAD_PATH` to just the hook env.
pushfirst!(LOAD_PATH, "@stdlib")
using Pkg
popfirst!(LOAD_PATH)
# Instantiate the environment shipped with the hook repo. If we have
# additional dependencies we disable precompilation in this step to
# avoid double work.
precompile = isempty(deps) ? "1" : "0"
withenv("JULIA_PKG_PRECOMPILE_AUTO" => precompile) do
Pkg.instantiate()
end
# Add additional dependencies (with precompilation)
if !isempty(deps)
withenv("JULIA_PKG_PRECOMPILE_AUTO" => "1") do
Pkg.REPLMode.pkgstr("add " * deps)
end
end
"""
cmd_output_b(
'julia', '--startup-file=no', '-e', julia_code, '--', envdir,
*additional_dependencies,
cwd=prefix.prefix_dir,
)

View file

@ -3,20 +3,20 @@ from __future__ import annotations
import contextlib import contextlib
import os import os
import sys import sys
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var from pre_commit.envcontext import Var
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output from pre_commit.util import cmd_output
ENVIRONMENT_DIR = 'lua_env' ENVIRONMENT_DIR = 'lua_env'
get_default_version = helpers.basic_get_default_version get_default_version = lang_base.basic_get_default_version
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
run_hook = helpers.basic_run_hook run_hook = lang_base.basic_run_hook
def _get_lua_version() -> str: # pragma: win32 no cover def _get_lua_version() -> str: # pragma: win32 no cover
@ -44,8 +44,8 @@ def get_env_patch(d: str) -> PatchesT: # pragma: win32 no cover
@contextlib.contextmanager # pragma: win32 no cover @contextlib.contextmanager # pragma: win32 no cover
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir)): with envcontext(get_env_patch(envdir)):
yield yield
@ -55,9 +55,9 @@ def install_environment(
version: str, version: str,
additional_dependencies: Sequence[str], additional_dependencies: Sequence[str],
) -> None: # pragma: win32 no cover ) -> None: # pragma: win32 no cover
helpers.assert_version_default('lua', version) lang_base.assert_version_default('lua', version)
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with in_env(prefix, version): with in_env(prefix, version):
# luarocks doesn't bootstrap a tree prior to installing # luarocks doesn't bootstrap a tree prior to installing
# so ensure the directory exists. # so ensure the directory exists.
@ -66,10 +66,10 @@ def install_environment(
# Older luarocks (e.g., 2.4.2) expect the rockspec as an arg # Older luarocks (e.g., 2.4.2) expect the rockspec as an arg
for rockspec in prefix.star('.rockspec'): for rockspec in prefix.star('.rockspec'):
make_cmd = ('luarocks', '--tree', envdir, 'make', rockspec) make_cmd = ('luarocks', '--tree', envdir, 'make', rockspec)
helpers.run_setup_cmd(prefix, make_cmd) lang_base.setup_cmd(prefix, make_cmd)
# luarocks can't install multiple packages at once # luarocks can't install multiple packages at once
# so install them individually. # so install them individually.
for dependency in additional_dependencies: for dependency in additional_dependencies:
cmd = ('luarocks', '--tree', envdir, 'install', dependency) cmd = ('luarocks', '--tree', envdir, 'install', dependency)
helpers.run_setup_cmd(prefix, cmd) lang_base.setup_cmd(prefix, cmd)

View file

@ -4,15 +4,15 @@ import contextlib
import functools import functools
import os import os
import sys import sys
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import UNSET from pre_commit.envcontext import UNSET
from pre_commit.envcontext import Var from pre_commit.envcontext import Var
from pre_commit.languages import helpers
from pre_commit.languages.python import bin_dir from pre_commit.languages.python import bin_dir
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output from pre_commit.util import cmd_output
@ -20,7 +20,7 @@ from pre_commit.util import cmd_output_b
from pre_commit.util import rmtree from pre_commit.util import rmtree
ENVIRONMENT_DIR = 'node_env' ENVIRONMENT_DIR = 'node_env'
run_hook = helpers.basic_run_hook run_hook = lang_base.basic_run_hook
@functools.lru_cache(maxsize=1) @functools.lru_cache(maxsize=1)
@ -30,7 +30,7 @@ def get_default_version() -> str:
return C.DEFAULT return C.DEFAULT
# if node is already installed, we can save a bunch of setup time by # if node is already installed, we can save a bunch of setup time by
# using the installed version # using the installed version
elif all(helpers.exe_exists(exe) for exe in ('node', 'npm')): elif all(lang_base.exe_exists(exe) for exe in ('node', 'npm')):
return 'system' return 'system'
else: else:
return C.DEFAULT return C.DEFAULT
@ -59,14 +59,14 @@ def get_env_patch(venv: str) -> PatchesT:
@contextlib.contextmanager @contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir)): with envcontext(get_env_patch(envdir)):
yield yield
def health_check(prefix: Prefix, language_version: str) -> str | None: def health_check(prefix: Prefix, version: str) -> str | None:
with in_env(prefix, language_version): with in_env(prefix, version):
retcode, _, _ = cmd_output_b('node', '--version', check=False) retcode, _, _ = cmd_output_b('node', '--version', check=False)
if retcode != 0: # pragma: win32 no cover if retcode != 0: # pragma: win32 no cover
return f'`node --version` returned {retcode}' return f'`node --version` returned {retcode}'
@ -78,7 +78,7 @@ def install_environment(
prefix: Prefix, version: str, additional_dependencies: Sequence[str], prefix: Prefix, version: str, additional_dependencies: Sequence[str],
) -> None: ) -> None:
assert prefix.exists('package.json') assert prefix.exists('package.json')
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx?f=255&MSPPError=-2147217396#maxpath # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx?f=255&MSPPError=-2147217396#maxpath
if sys.platform == 'win32': # pragma: no cover if sys.platform == 'win32': # pragma: no cover
@ -93,16 +93,16 @@ def install_environment(
# install as if we installed from git # install as if we installed from git
local_install_cmd = ( local_install_cmd = (
'npm', 'install', '--dev', '--prod', 'npm', 'install', '--include=dev', '--include=prod',
'--ignore-prepublish', '--no-progress', '--no-save', '--ignore-prepublish', '--no-progress', '--no-save',
) )
helpers.run_setup_cmd(prefix, local_install_cmd) lang_base.setup_cmd(prefix, local_install_cmd)
_, pkg, _ = cmd_output('npm', 'pack', cwd=prefix.prefix_dir) _, pkg, _ = cmd_output('npm', 'pack', cwd=prefix.prefix_dir)
pkg = prefix.path(pkg.strip()) pkg = prefix.path(pkg.strip())
install = ('npm', 'install', '-g', pkg, *additional_dependencies) install = ('npm', 'install', '-g', pkg, *additional_dependencies)
helpers.run_setup_cmd(prefix, install) lang_base.setup_cmd(prefix, install)
# clean these up after installation # clean these up after installation
if prefix.exists('node_modules'): # pragma: win32 no cover if prefix.exists('node_modules'): # pragma: win32 no cover

View file

@ -3,19 +3,19 @@ from __future__ import annotations
import contextlib import contextlib
import os import os
import shlex import shlex
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var from pre_commit.envcontext import Var
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
ENVIRONMENT_DIR = 'perl_env' ENVIRONMENT_DIR = 'perl_env'
get_default_version = helpers.basic_get_default_version get_default_version = lang_base.basic_get_default_version
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
run_hook = helpers.basic_run_hook run_hook = lang_base.basic_run_hook
def get_env_patch(venv: str) -> PatchesT: def get_env_patch(venv: str) -> PatchesT:
@ -33,8 +33,8 @@ def get_env_patch(venv: str) -> PatchesT:
@contextlib.contextmanager @contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir)): with envcontext(get_env_patch(envdir)):
yield yield
@ -42,9 +42,9 @@ def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
def install_environment( def install_environment(
prefix: Prefix, version: str, additional_dependencies: Sequence[str], prefix: Prefix, version: str, additional_dependencies: Sequence[str],
) -> None: ) -> None:
helpers.assert_version_default('perl', version) lang_base.assert_version_default('perl', version)
with in_env(prefix, version): with in_env(prefix, version):
helpers.run_setup_cmd( lang_base.setup_cmd(
prefix, ('cpan', '-T', '.', *additional_dependencies), prefix, ('cpan', '-T', '.', *additional_dependencies),
) )

View file

@ -3,20 +3,20 @@ from __future__ import annotations
import argparse import argparse
import re import re
import sys import sys
from collections.abc import Sequence
from re import Pattern
from typing import NamedTuple from typing import NamedTuple
from typing import Pattern
from typing import Sequence
from pre_commit import lang_base
from pre_commit import output from pre_commit import output
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.xargs import xargs from pre_commit.xargs import xargs
ENVIRONMENT_DIR = None ENVIRONMENT_DIR = None
get_default_version = helpers.basic_get_default_version get_default_version = lang_base.basic_get_default_version
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
install_environment = helpers.no_install install_environment = lang_base.no_install
in_env = helpers.no_env in_env = lang_base.no_env
def _process_filename_by_line(pattern: Pattern[bytes], filename: str) -> int: def _process_filename_by_line(pattern: Pattern[bytes], filename: str) -> int:

View file

@ -4,15 +4,15 @@ import contextlib
import functools import functools
import os import os
import sys import sys
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import UNSET from pre_commit.envcontext import UNSET
from pre_commit.envcontext import Var from pre_commit.envcontext import Var
from pre_commit.languages import helpers
from pre_commit.parse_shebang import find_executable from pre_commit.parse_shebang import find_executable
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import CalledProcessError from pre_commit.util import CalledProcessError
@ -21,10 +21,10 @@ from pre_commit.util import cmd_output_b
from pre_commit.util import win_exe from pre_commit.util import win_exe
ENVIRONMENT_DIR = 'py_env' ENVIRONMENT_DIR = 'py_env'
run_hook = helpers.basic_run_hook run_hook = lang_base.basic_run_hook
@functools.lru_cache(maxsize=None) @functools.cache
def _version_info(exe: str) -> str: def _version_info(exe: str) -> str:
prog = 'import sys;print(".".join(str(p) for p in sys.version_info))' prog = 'import sys;print(".".join(str(p) for p in sys.version_info))'
try: try:
@ -48,7 +48,7 @@ def _read_pyvenv_cfg(filename: str) -> dict[str, str]:
def bin_dir(venv: str) -> str: def bin_dir(venv: str) -> str:
"""On windows there's a different directory for the virtualenv""" """On windows there's a different directory for the virtualenv"""
bin_part = 'Scripts' if os.name == 'nt' else 'bin' bin_part = 'Scripts' if sys.platform == 'win32' else 'bin'
return os.path.join(venv, bin_part) return os.path.join(venv, bin_part)
@ -65,7 +65,7 @@ def _find_by_py_launcher(
version: str, version: str,
) -> str | None: # pragma: no cover (windows only) ) -> str | None: # pragma: no cover (windows only)
if version.startswith('python'): if version.startswith('python'):
num = version[len('python'):] num = version.removeprefix('python')
cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)') cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')
env = dict(os.environ, PYTHONIOENCODING='UTF-8') env = dict(os.environ, PYTHONIOENCODING='UTF-8')
try: try:
@ -75,6 +75,13 @@ def _find_by_py_launcher(
return None return None
def _impl_exe_name() -> str:
if sys.implementation.name == 'cpython': # pragma: cpython cover
return 'python'
else: # pragma: cpython no cover
return sys.implementation.name # pypy mostly
def _find_by_sys_executable() -> str | None: def _find_by_sys_executable() -> str | None:
def _norm(path: str) -> str | None: def _norm(path: str) -> str | None:
_, exe = os.path.split(path.lower()) _, exe = os.path.split(path.lower())
@ -100,18 +107,25 @@ def _find_by_sys_executable() -> str | None:
@functools.lru_cache(maxsize=1) @functools.lru_cache(maxsize=1)
def get_default_version() -> str: # pragma: no cover (platform dependent) def get_default_version() -> str: # pragma: no cover (platform dependent)
# First attempt from `sys.executable` (or the realpath) v_major = f'{sys.version_info[0]}'
exe = _find_by_sys_executable() v_minor = f'{sys.version_info[0]}.{sys.version_info[1]}'
if exe:
return exe
# Next try the `pythonX.X` executable # attempt the likely implementation exe
exe = f'python{sys.version_info[0]}.{sys.version_info[1]}' for potential in (v_minor, v_major):
if find_executable(exe): exe = f'{_impl_exe_name()}{potential}'
return exe if find_executable(exe):
return exe
if _find_by_py_launcher(exe): # next try `sys.executable` (or the realpath)
return exe maybe_exe = _find_by_sys_executable()
if maybe_exe:
return maybe_exe
# maybe on windows we can find it via py launcher?
if sys.platform == 'win32': # pragma: win32 cover
exe = f'python{v_minor}'
if _find_by_py_launcher(exe):
return exe
# We tried! # We tried!
return C.DEFAULT return C.DEFAULT
@ -124,7 +138,7 @@ def _sys_executable_matches(version: str) -> bool:
return False return False
try: try:
info = tuple(int(p) for p in version[len('python'):].split('.')) info = tuple(int(p) for p in version.removeprefix('python').split('.'))
except ValueError: except ValueError:
return False return False
@ -137,7 +151,7 @@ def norm_version(version: str) -> str | None:
elif _sys_executable_matches(version): # virtualenv defaults to our exe elif _sys_executable_matches(version): # virtualenv defaults to our exe
return None return None
if os.name == 'nt': # pragma: no cover (windows) if sys.platform == 'win32': # pragma: no cover (windows)
version_exec = _find_by_py_launcher(version) version_exec = _find_by_py_launcher(version)
if version_exec: if version_exec:
return version_exec return version_exec
@ -152,14 +166,14 @@ def norm_version(version: str) -> str | None:
@contextlib.contextmanager @contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir)): with envcontext(get_env_patch(envdir)):
yield yield
def health_check(prefix: Prefix, language_version: str) -> str | None: def health_check(prefix: Prefix, version: str) -> str | None:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, language_version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
pyvenv_cfg = os.path.join(envdir, 'pyvenv.cfg') pyvenv_cfg = os.path.join(envdir, 'pyvenv.cfg')
# created with "old" virtualenv # created with "old" virtualenv
@ -202,7 +216,7 @@ def install_environment(
version: str, version: str,
additional_dependencies: Sequence[str], additional_dependencies: Sequence[str],
) -> None: ) -> None:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
venv_cmd = [sys.executable, '-mvirtualenv', envdir] venv_cmd = [sys.executable, '-mvirtualenv', envdir]
python = norm_version(version) python = norm_version(version)
if python is not None: if python is not None:
@ -211,4 +225,4 @@ def install_environment(
cmd_output_b(*venv_cmd, cwd='/') cmd_output_b(*venv_cmd, cwd='/')
with in_env(prefix, version): with in_env(prefix, version):
helpers.run_setup_cmd(prefix, install_cmd) lang_base.setup_cmd(prefix, install_cmd)

View file

@ -4,21 +4,120 @@ import contextlib
import os import os
import shlex import shlex
import shutil import shutil
from typing import Generator import tempfile
from typing import Sequence import textwrap
from collections.abc import Generator
from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import UNSET from pre_commit.envcontext import UNSET
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output_b from pre_commit.util import cmd_output
from pre_commit.util import win_exe from pre_commit.util import win_exe
ENVIRONMENT_DIR = 'renv' ENVIRONMENT_DIR = 'renv'
RSCRIPT_OPTS = ('--no-save', '--no-restore', '--no-site-file', '--no-environ') get_default_version = lang_base.basic_get_default_version
get_default_version = helpers.basic_get_default_version
health_check = helpers.basic_health_check _RENV_ACTIVATED_OPTS = (
'--no-save', '--no-restore', '--no-site-file', '--no-environ',
)
def _execute_r(
code: str, *,
prefix: Prefix, version: str, args: Sequence[str] = (), cwd: str,
cli_opts: Sequence[str],
) -> str:
with in_env(prefix, version), _r_code_in_tempfile(code) as f:
_, out, _ = cmd_output(
_rscript_exec(), *cli_opts, f, *args, cwd=cwd,
)
return out.rstrip('\n')
def _execute_r_in_renv(
code: str, *,
prefix: Prefix, version: str, args: Sequence[str] = (), cwd: str,
) -> str:
return _execute_r(
code=code, prefix=prefix, version=version, args=args, cwd=cwd,
cli_opts=_RENV_ACTIVATED_OPTS,
)
def _execute_vanilla_r(
code: str, *,
prefix: Prefix, version: str, args: Sequence[str] = (), cwd: str,
) -> str:
return _execute_r(
code=code, prefix=prefix, version=version, args=args, cwd=cwd,
cli_opts=('--vanilla',),
)
def _read_installed_version(envdir: str, prefix: Prefix, version: str) -> str:
return _execute_r_in_renv(
'cat(renv::settings$r.version())',
prefix=prefix, version=version,
cwd=envdir,
)
def _read_executable_version(envdir: str, prefix: Prefix, version: str) -> str:
return _execute_r_in_renv(
'cat(as.character(getRversion()))',
prefix=prefix, version=version,
cwd=envdir,
)
def _write_current_r_version(
envdir: str, prefix: Prefix, version: str,
) -> None:
_execute_r_in_renv(
'renv::settings$r.version(as.character(getRversion()))',
prefix=prefix, version=version,
cwd=envdir,
)
def health_check(prefix: Prefix, version: str) -> str | None:
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
r_version_installation = _read_installed_version(
envdir=envdir, prefix=prefix, version=version,
)
r_version_current_executable = _read_executable_version(
envdir=envdir, prefix=prefix, version=version,
)
if r_version_installation in {'NULL', ''}:
return (
f'Hooks were installed with an unknown R version. R version for '
f'hook repo now set to {r_version_current_executable}'
)
elif r_version_installation != r_version_current_executable:
return (
f'Hooks were installed for R version {r_version_installation}, '
f'but current R executable has version '
f'{r_version_current_executable}'
)
return None
@contextlib.contextmanager
def _r_code_in_tempfile(code: str) -> Generator[str]:
"""
To avoid quoting and escaping issues, avoid `Rscript [options] -e {expr}`
but use `Rscript [options] path/to/file_with_expr.R`
"""
with tempfile.TemporaryDirectory() as tmpdir:
fname = os.path.join(tmpdir, 'script.R')
with open(fname, 'w') as f:
f.write(_inline_r_setup(textwrap.dedent(code)))
yield fname
def get_env_patch(venv: str) -> PatchesT: def get_env_patch(venv: str) -> PatchesT:
@ -29,8 +128,8 @@ def get_env_patch(venv: str) -> PatchesT:
@contextlib.contextmanager @contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir)): with envcontext(get_env_patch(envdir)):
yield yield
@ -85,7 +184,7 @@ def _cmd_from_hook(
_entry_validate(cmd) _entry_validate(cmd)
cmd_part = _prefix_if_file_entry(cmd, prefix, is_local=is_local) cmd_part = _prefix_if_file_entry(cmd, prefix, is_local=is_local)
return (cmd[0], *RSCRIPT_OPTS, *cmd_part, *args) return (cmd[0], *_RENV_ACTIVATED_OPTS, *cmd_part, *args)
def install_environment( def install_environment(
@ -93,7 +192,9 @@ def install_environment(
version: str, version: str,
additional_dependencies: Sequence[str], additional_dependencies: Sequence[str],
) -> None: ) -> None:
env_dir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) lang_base.assert_version_default('r', version)
env_dir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
os.makedirs(env_dir, exist_ok=True) os.makedirs(env_dir, exist_ok=True)
shutil.copy(prefix.path('renv.lock'), env_dir) shutil.copy(prefix.path('renv.lock'), env_dir)
shutil.copytree(prefix.path('renv'), os.path.join(env_dir, 'renv')) shutil.copytree(prefix.path('renv'), os.path.join(env_dir, 'renv'))
@ -126,21 +227,19 @@ def install_environment(
renv::install(prefix_dir) renv::install(prefix_dir)
}} }}
""" """
_execute_vanilla_r(
cmd_output_b( r_code_inst_environment,
_rscript_exec(), '--vanilla', '-e', prefix=prefix, version=version, cwd=env_dir,
_inline_r_setup(r_code_inst_environment),
cwd=env_dir,
) )
_write_current_r_version(envdir=env_dir, prefix=prefix, version=version)
if additional_dependencies: if additional_dependencies:
r_code_inst_add = 'renv::install(commandArgs(trailingOnly = TRUE))' r_code_inst_add = 'renv::install(commandArgs(trailingOnly = TRUE))'
with in_env(prefix, version): _execute_r_in_renv(
cmd_output_b( code=r_code_inst_add, prefix=prefix, version=version,
_rscript_exec(), *RSCRIPT_OPTS, '-e', args=additional_dependencies,
_inline_r_setup(r_code_inst_add), cwd=env_dir,
*additional_dependencies, )
cwd=env_dir,
)
def _inline_r_setup(code: str) -> str: def _inline_r_setup(code: str) -> str:
@ -148,11 +247,16 @@ def _inline_r_setup(code: str) -> str:
Some behaviour of R cannot be configured via env variables, but can Some behaviour of R cannot be configured via env variables, but can
only be configured via R options once R has started. These are set here. only be configured via R options once R has started. These are set here.
""" """
with_option = f"""\ with_option = [
options(install.packages.compile.from.source = "never", pkgType = "binary") textwrap.dedent("""\
{code} options(
""" install.packages.compile.from.source = "never",
return with_option pkgType = "binary"
)
"""),
code,
]
return '\n'.join(with_option)
def run_hook( def run_hook(
@ -166,7 +270,7 @@ def run_hook(
color: bool, color: bool,
) -> tuple[int, bytes]: ) -> tuple[int, bytes]:
cmd = _cmd_from_hook(prefix, entry, args, is_local=is_local) cmd = _cmd_from_hook(prefix, entry, args, is_local=is_local)
return helpers.run_xargs( return lang_base.run_xargs(
cmd, cmd,
file_args, file_args,
require_serial=require_serial, require_serial=require_serial,

View file

@ -2,30 +2,36 @@ from __future__ import annotations
import contextlib import contextlib
import functools import functools
import importlib.resources
import os.path import os.path
import shutil import shutil
import tarfile import tarfile
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from typing import IO
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import UNSET from pre_commit.envcontext import UNSET
from pre_commit.envcontext import Var from pre_commit.envcontext import Var
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import CalledProcessError from pre_commit.util import CalledProcessError
from pre_commit.util import resource_bytesio
ENVIRONMENT_DIR = 'rbenv' ENVIRONMENT_DIR = 'rbenv'
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
run_hook = helpers.basic_run_hook run_hook = lang_base.basic_run_hook
def _resource_bytesio(filename: str) -> IO[bytes]:
files = importlib.resources.files('pre_commit.resources')
return files.joinpath(filename).open('rb')
@functools.lru_cache(maxsize=1) @functools.lru_cache(maxsize=1)
def get_default_version() -> str: def get_default_version() -> str:
if all(helpers.exe_exists(exe) for exe in ('ruby', 'gem')): if all(lang_base.exe_exists(exe) for exe in ('ruby', 'gem')):
return 'system' return 'system'
else: else:
return C.DEFAULT return C.DEFAULT
@ -39,7 +45,6 @@ def get_env_patch(
('GEM_HOME', os.path.join(venv, 'gems')), ('GEM_HOME', os.path.join(venv, 'gems')),
('GEM_PATH', UNSET), ('GEM_PATH', UNSET),
('BUNDLE_IGNORE_CONFIG', '1'), ('BUNDLE_IGNORE_CONFIG', '1'),
('BUNDLE_GEMFILE', os.devnull),
) )
if language_version == 'system': if language_version == 'system':
patches += ( patches += (
@ -68,14 +73,14 @@ def get_env_patch(
@contextlib.contextmanager @contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir, version)): with envcontext(get_env_patch(envdir, version)):
yield yield
def _extract_resource(filename: str, dest: str) -> None: def _extract_resource(filename: str, dest: str) -> None:
with resource_bytesio(filename) as bio: with _resource_bytesio(filename) as bio:
with tarfile.open(fileobj=bio) as tf: with tarfile.open(fileobj=bio) as tf:
tf.extractall(dest) tf.extractall(dest)
@ -84,7 +89,7 @@ def _install_rbenv(
prefix: Prefix, prefix: Prefix,
version: str, version: str,
) -> None: # pragma: win32 no cover ) -> None: # pragma: win32 no cover
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
_extract_resource('rbenv.tar.gz', prefix.path('.')) _extract_resource('rbenv.tar.gz', prefix.path('.'))
shutil.move(prefix.path('rbenv'), envdir) shutil.move(prefix.path('rbenv'), envdir)
@ -101,36 +106,40 @@ def _install_ruby(
version: str, version: str,
) -> None: # pragma: win32 no cover ) -> None: # pragma: win32 no cover
try: try:
helpers.run_setup_cmd(prefix, ('rbenv', 'download', version)) lang_base.setup_cmd(prefix, ('rbenv', 'download', version))
except CalledProcessError: # pragma: no cover (usually find with download) except CalledProcessError: # pragma: no cover (usually find with download)
# Failed to download from mirror for some reason, build it instead # Failed to download from mirror for some reason, build it instead
helpers.run_setup_cmd(prefix, ('rbenv', 'install', version)) lang_base.setup_cmd(prefix, ('rbenv', 'install', version))
def install_environment( def install_environment(
prefix: Prefix, version: str, additional_dependencies: Sequence[str], prefix: Prefix, version: str, additional_dependencies: Sequence[str],
) -> None: ) -> None:
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
if version != 'system': # pragma: win32 no cover if version != 'system': # pragma: win32 no cover
_install_rbenv(prefix, version) _install_rbenv(prefix, version)
with in_env(prefix, version): with in_env(prefix, version):
# Need to call this before installing so rbenv's directories # Need to call this before installing so rbenv's directories
# are set up # are set up
helpers.run_setup_cmd(prefix, ('rbenv', 'init', '-')) lang_base.setup_cmd(prefix, ('rbenv', 'init', '-'))
if version != C.DEFAULT: if version != C.DEFAULT:
_install_ruby(prefix, version) _install_ruby(prefix, version)
# Need to call this after installing to set up the shims # Need to call this after installing to set up the shims
helpers.run_setup_cmd(prefix, ('rbenv', 'rehash')) lang_base.setup_cmd(prefix, ('rbenv', 'rehash'))
with in_env(prefix, version): with in_env(prefix, version):
helpers.run_setup_cmd( lang_base.setup_cmd(
prefix, ('gem', 'build', *prefix.star('.gemspec')), prefix, ('gem', 'build', *prefix.star('.gemspec')),
) )
helpers.run_setup_cmd( lang_base.setup_cmd(
prefix, prefix,
( (
'gem', 'install', 'gem', 'install',
'--no-document', '--no-format-executable', '--no-document', '--no-format-executable',
'--no-user-install', '--no-user-install',
'--install-dir', os.path.join(envdir, 'gems'),
'--bindir', os.path.join(envdir, 'gems', 'bin'),
*prefix.star('.gem'), *additional_dependencies, *prefix.star('.gem'), *additional_dependencies,
), ),
) )

View file

@ -7,23 +7,23 @@ import shutil
import sys import sys
import tempfile import tempfile
import urllib.request import urllib.request
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import lang_base
from pre_commit import parse_shebang from pre_commit import parse_shebang
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var from pre_commit.envcontext import Var
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output_b from pre_commit.util import cmd_output_b
from pre_commit.util import make_executable from pre_commit.util import make_executable
from pre_commit.util import win_exe from pre_commit.util import win_exe
ENVIRONMENT_DIR = 'rustenv' ENVIRONMENT_DIR = 'rustenv'
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
run_hook = helpers.basic_run_hook run_hook = lang_base.basic_run_hook
@functools.lru_cache(maxsize=1) @functools.lru_cache(maxsize=1)
@ -34,7 +34,7 @@ def get_default_version() -> str:
# Just detecting the executable does not suffice, because if rustup is # Just detecting the executable does not suffice, because if rustup is
# installed but no toolchain is available, then `cargo` exists but # installed but no toolchain is available, then `cargo` exists but
# cannot be used without installing a toolchain first. # cannot be used without installing a toolchain first.
if cmd_output_b('cargo', '--version', check=False)[0] == 0: if cmd_output_b('cargo', '--version', check=False, cwd='/')[0] == 0:
return 'system' return 'system'
else: else:
return C.DEFAULT return C.DEFAULT
@ -50,7 +50,6 @@ def _rust_toolchain(language_version: str) -> str:
def get_env_patch(target_dir: str, version: str) -> PatchesT: def get_env_patch(target_dir: str, version: str) -> PatchesT:
return ( return (
('CARGO_HOME', target_dir),
('PATH', (os.path.join(target_dir, 'bin'), os.pathsep, Var('PATH'))), ('PATH', (os.path.join(target_dir, 'bin'), os.pathsep, Var('PATH'))),
# Only set RUSTUP_TOOLCHAIN if we don't want use the system's default # Only set RUSTUP_TOOLCHAIN if we don't want use the system's default
# toolchain # toolchain
@ -62,8 +61,8 @@ def get_env_patch(target_dir: str, version: str) -> PatchesT:
@contextlib.contextmanager @contextlib.contextmanager
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir, version)): with envcontext(get_env_patch(envdir, version)):
yield yield
@ -78,12 +77,12 @@ def _add_dependencies(
crate = f'{name}@{spec or "*"}' crate = f'{name}@{spec or "*"}'
crates.append(crate) crates.append(crate)
helpers.run_setup_cmd(prefix, ('cargo', 'add', *crates)) lang_base.setup_cmd(prefix, ('cargo', 'add', *crates))
def install_rust_with_toolchain(toolchain: str) -> None: def install_rust_with_toolchain(toolchain: str, envdir: str) -> None:
with tempfile.TemporaryDirectory() as rustup_dir: with tempfile.TemporaryDirectory() as rustup_dir:
with envcontext((('RUSTUP_HOME', rustup_dir),)): with envcontext((('CARGO_HOME', envdir), ('RUSTUP_HOME', rustup_dir))):
# acquire `rustup` if not present # acquire `rustup` if not present
if parse_shebang.find_executable('rustup') is None: if parse_shebang.find_executable('rustup') is None:
# We did not detect rustup and need to download it first. # We did not detect rustup and need to download it first.
@ -116,7 +115,7 @@ def install_environment(
version: str, version: str,
additional_dependencies: Sequence[str], additional_dependencies: Sequence[str],
) -> None: ) -> None:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
# There are two cases where we might want to specify more dependencies: # There are two cases where we might want to specify more dependencies:
# as dependencies for the library being built, and as binary packages # as dependencies for the library being built, and as binary packages
@ -135,16 +134,21 @@ def install_environment(
packages_to_install: set[tuple[str, ...]] = {('--path', '.')} packages_to_install: set[tuple[str, ...]] = {('--path', '.')}
for cli_dep in cli_deps: for cli_dep in cli_deps:
cli_dep = cli_dep[len('cli:'):] cli_dep = cli_dep.removeprefix('cli:')
package, _, crate_version = cli_dep.partition(':') package, _, crate_version = cli_dep.partition(':')
if crate_version != '': if crate_version != '':
packages_to_install.add((package, '--version', crate_version)) packages_to_install.add((package, '--version', crate_version))
else: else:
packages_to_install.add((package,)) packages_to_install.add((package,))
with in_env(prefix, version): with contextlib.ExitStack() as ctx:
ctx.enter_context(in_env(prefix, version))
if version != 'system': if version != 'system':
install_rust_with_toolchain(_rust_toolchain(version)) install_rust_with_toolchain(_rust_toolchain(version), envdir)
tmpdir = ctx.enter_context(tempfile.TemporaryDirectory())
ctx.enter_context(envcontext((('RUSTUP_HOME', tmpdir),)))
if len(lib_deps) > 0: if len(lib_deps) > 0:
_add_dependencies(prefix, lib_deps) _add_dependencies(prefix, lib_deps)

View file

@ -2,13 +2,13 @@ from __future__ import annotations
import contextlib import contextlib
import os import os
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
from pre_commit import lang_base
from pre_commit.envcontext import envcontext from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var from pre_commit.envcontext import Var
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output_b from pre_commit.util import cmd_output_b
@ -16,9 +16,9 @@ BUILD_DIR = '.build'
BUILD_CONFIG = 'release' BUILD_CONFIG = 'release'
ENVIRONMENT_DIR = 'swift_env' ENVIRONMENT_DIR = 'swift_env'
get_default_version = helpers.basic_get_default_version get_default_version = lang_base.basic_get_default_version
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
run_hook = helpers.basic_run_hook run_hook = lang_base.basic_run_hook
def get_env_patch(venv: str) -> PatchesT: # pragma: win32 no cover def get_env_patch(venv: str) -> PatchesT: # pragma: win32 no cover
@ -27,8 +27,8 @@ def get_env_patch(venv: str) -> PatchesT: # pragma: win32 no cover
@contextlib.contextmanager # pragma: win32 no cover @contextlib.contextmanager # pragma: win32 no cover
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]: def in_env(prefix: Prefix, version: str) -> Generator[None]:
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
with envcontext(get_env_patch(envdir)): with envcontext(get_env_patch(envdir)):
yield yield
@ -36,15 +36,15 @@ def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
def install_environment( def install_environment(
prefix: Prefix, version: str, additional_dependencies: Sequence[str], prefix: Prefix, version: str, additional_dependencies: Sequence[str],
) -> None: # pragma: win32 no cover ) -> None: # pragma: win32 no cover
helpers.assert_version_default('swift', version) lang_base.assert_version_default('swift', version)
helpers.assert_no_additional_deps('swift', additional_dependencies) lang_base.assert_no_additional_deps('swift', additional_dependencies)
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version) envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
# Build the swift package # Build the swift package
os.mkdir(envdir) os.mkdir(envdir)
cmd_output_b( cmd_output_b(
'swift', 'build', 'swift', 'build',
'-C', prefix.prefix_dir, '--package-path', prefix.prefix_dir,
'-c', BUILD_CONFIG, '-c', BUILD_CONFIG,
'--build-path', os.path.join(envdir, BUILD_DIR), '--build-path', os.path.join(envdir, BUILD_DIR),
) )

View file

@ -1,10 +0,0 @@
from __future__ import annotations
from pre_commit.languages import helpers
ENVIRONMENT_DIR = None
get_default_version = helpers.basic_get_default_version
health_check = helpers.basic_health_check
install_environment = helpers.no_install
in_env = helpers.no_env
run_hook = helpers.basic_run_hook

View file

@ -0,0 +1,10 @@
from __future__ import annotations
from pre_commit import lang_base
ENVIRONMENT_DIR = None
get_default_version = lang_base.basic_get_default_version
health_check = lang_base.basic_health_check
install_environment = lang_base.no_install
in_env = lang_base.no_env
run_hook = lang_base.basic_run_hook

View file

@ -1,15 +1,15 @@
from __future__ import annotations from __future__ import annotations
from typing import Sequence from collections.abc import Sequence
from pre_commit.languages import helpers from pre_commit import lang_base
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
ENVIRONMENT_DIR = None ENVIRONMENT_DIR = None
get_default_version = helpers.basic_get_default_version get_default_version = lang_base.basic_get_default_version
health_check = helpers.basic_health_check health_check = lang_base.basic_health_check
install_environment = helpers.no_install install_environment = lang_base.no_install
in_env = helpers.no_env in_env = lang_base.no_env
def run_hook( def run_hook(
@ -22,9 +22,9 @@ def run_hook(
require_serial: bool, require_serial: bool,
color: bool, color: bool,
) -> tuple[int, bytes]: ) -> tuple[int, bytes]:
cmd = helpers.hook_cmd(entry, args) cmd = lang_base.hook_cmd(entry, args)
cmd = (prefix.path(cmd[0]), *cmd[1:]) cmd = (prefix.path(cmd[0]), *cmd[1:])
return helpers.run_xargs( return lang_base.run_xargs(
cmd, cmd,
file_args, file_args,
require_serial=require_serial, require_serial=require_serial,

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import contextlib import contextlib
import logging import logging
from typing import Generator from collections.abc import Generator
from pre_commit import color from pre_commit import color
from pre_commit import output from pre_commit import output
@ -32,7 +32,7 @@ class LoggingHandler(logging.Handler):
@contextlib.contextmanager @contextlib.contextmanager
def logging_handler(use_color: bool) -> Generator[None, None, None]: def logging_handler(use_color: bool) -> Generator[None]:
handler = LoggingHandler(use_color) handler = LoggingHandler(use_color)
logger.addHandler(handler) logger.addHandler(handler)
logger.setLevel(logging.INFO) logger.setLevel(logging.INFO)

View file

@ -4,11 +4,13 @@ import argparse
import logging import logging
import os import os
import sys import sys
from typing import Sequence from collections.abc import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import clientlib
from pre_commit import git from pre_commit import git
from pre_commit.color import add_color_option from pre_commit.color import add_color_option
from pre_commit.commands import hazmat
from pre_commit.commands.autoupdate import autoupdate from pre_commit.commands.autoupdate import autoupdate
from pre_commit.commands.clean import clean from pre_commit.commands.clean import clean
from pre_commit.commands.gc import gc from pre_commit.commands.gc import gc
@ -36,8 +38,11 @@ logger = logging.getLogger('pre_commit')
# pyvenv # pyvenv
os.environ.pop('__PYVENV_LAUNCHER__', None) os.environ.pop('__PYVENV_LAUNCHER__', None)
# https://github.com/getsentry/snuba/pull/5388
os.environ.pop('PYTHONEXECUTABLE', None)
COMMANDS_NO_GIT = { COMMANDS_NO_GIT = {
'clean', 'gc', 'init-templatedir', 'sample-config', 'clean', 'gc', 'hazmat', 'init-templatedir', 'sample-config',
'validate-config', 'validate-manifest', 'validate-config', 'validate-manifest',
} }
@ -52,16 +57,16 @@ def _add_config_option(parser: argparse.ArgumentParser) -> None:
def _add_hook_type_option(parser: argparse.ArgumentParser) -> None: def _add_hook_type_option(parser: argparse.ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
'-t', '--hook-type', '-t', '--hook-type',
choices=C.HOOK_TYPES, action='append', dest='hook_types', choices=clientlib.HOOK_TYPES, action='append', dest='hook_types',
) )
def _add_run_options(parser: argparse.ArgumentParser) -> None: def _add_run_options(parser: argparse.ArgumentParser) -> None:
parser.add_argument('hook', nargs='?', help='A single hook-id to run') parser.add_argument('hook', nargs='?', help='A single hook-id to run')
parser.add_argument('--verbose', '-v', action='store_true', default=False) parser.add_argument('--verbose', '-v', action='store_true')
mutex_group = parser.add_mutually_exclusive_group(required=False) mutex_group = parser.add_mutually_exclusive_group(required=False)
mutex_group.add_argument( mutex_group.add_argument(
'--all-files', '-a', action='store_true', default=False, '--all-files', '-a', action='store_true',
help='Run on all the files in the repo.', help='Run on all the files in the repo.',
) )
mutex_group.add_argument( mutex_group.add_argument(
@ -73,7 +78,14 @@ def _add_run_options(parser: argparse.ArgumentParser) -> None:
help='When hooks fail, run `git diff` directly afterward.', help='When hooks fail, run `git diff` directly afterward.',
) )
parser.add_argument( parser.add_argument(
'--hook-stage', choices=C.STAGES, default='commit', '--fail-fast', action='store_true',
help='Stop after the first failing hook.',
)
parser.add_argument(
'--hook-stage',
choices=clientlib.STAGES,
type=clientlib.transform_stage,
default='pre-commit',
help='The stage during which the hook is fired. One of %(choices)s', help='The stage during which the hook is fired. One of %(choices)s',
) )
parser.add_argument( parser.add_argument(
@ -103,6 +115,17 @@ def _add_run_options(parser: argparse.ArgumentParser) -> None:
'now checked out.' 'now checked out.'
), ),
) )
parser.add_argument(
'--pre-rebase-upstream', help=(
'The upstream from which the series was forked.'
),
)
parser.add_argument(
'--pre-rebase-branch', help=(
'The branch being rebased, and is not set when '
'rebasing the current branch.'
),
)
parser.add_argument( parser.add_argument(
'--commit-msg-filename', '--commit-msg-filename',
help='Filename to check when running during `commit-msg`', help='Filename to check when running during `commit-msg`',
@ -211,14 +234,23 @@ def main(argv: Sequence[str] | None = None) -> int:
help='Store "frozen" hashes in `rev` instead of tag names', help='Store "frozen" hashes in `rev` instead of tag names',
) )
autoupdate_parser.add_argument( autoupdate_parser.add_argument(
'--repo', dest='repos', action='append', metavar='REPO', '--repo', dest='repos', action='append', metavar='REPO', default=[],
help='Only update this repository -- may be specified multiple times.', help='Only update this repository -- may be specified multiple times.',
) )
autoupdate_parser.add_argument(
'-j', '--jobs', type=int, default=1,
help='Number of threads to use. (default %(default)s).',
)
_add_cmd('clean', help='Clean out pre-commit files.') _add_cmd('clean', help='Clean out pre-commit files.')
_add_cmd('gc', help='Clean unused cached repos.') _add_cmd('gc', help='Clean unused cached repos.')
hazmat_parser = _add_cmd(
'hazmat', help='Composable tools for rare use in hook `entry`.',
)
hazmat.add_parsers(hazmat_parser)
init_templatedir_parser = _add_cmd( init_templatedir_parser = _add_cmd(
'init-templatedir', 'init-templatedir',
help=( help=(
@ -253,7 +285,7 @@ def main(argv: Sequence[str] | None = None) -> int:
) )
_add_hook_type_option(install_parser) _add_hook_type_option(install_parser)
install_parser.add_argument( install_parser.add_argument(
'--allow-missing-config', action='store_true', default=False, '--allow-missing-config', action='store_true',
help=( help=(
'Whether to allow a missing `pre-commit` configuration file ' 'Whether to allow a missing `pre-commit` configuration file '
'or exit with a failure code.' 'or exit with a failure code.'
@ -353,15 +385,18 @@ def main(argv: Sequence[str] | None = None) -> int:
if args.command == 'autoupdate': if args.command == 'autoupdate':
return autoupdate( return autoupdate(
args.config, store, args.config,
tags_only=not args.bleeding_edge, tags_only=not args.bleeding_edge,
freeze=args.freeze, freeze=args.freeze,
repos=args.repos, repos=args.repos,
jobs=args.jobs,
) )
elif args.command == 'clean': elif args.command == 'clean':
return clean(store) return clean(store)
elif args.command == 'gc': elif args.command == 'gc':
return gc(store) return gc(store)
elif args.command == 'hazmat':
return hazmat.impl(args)
elif args.command == 'hook-impl': elif args.command == 'hook-impl':
return hook_impl( return hook_impl(
store, store,

View file

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
from typing import Sequence from collections.abc import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import git from pre_commit import git
@ -21,7 +21,7 @@ def check_all_hooks_match_files(config_file: str) -> int:
for hook in all_hooks(config, Store()): for hook in all_hooks(config, Store()):
if hook.always_run or hook.language == 'fail': if hook.always_run or hook.language == 'fail':
continue continue
elif not classifier.filenames_for_hook(hook): elif not any(classifier.filenames_for_hook(hook)):
print(f'{hook.id} does not apply to this repository') print(f'{hook.id} does not apply to this repository')
retv = 1 retv = 1

View file

@ -2,7 +2,8 @@ from __future__ import annotations
import argparse import argparse
import re import re
from typing import Sequence from collections.abc import Iterable
from collections.abc import Sequence
from cfgv import apply_defaults from cfgv import apply_defaults
@ -14,7 +15,7 @@ from pre_commit.commands.run import Classifier
def exclude_matches_any( def exclude_matches_any(
filenames: Sequence[str], filenames: Iterable[str],
include: str, include: str,
exclude: str, exclude: str,
) -> bool: ) -> bool:
@ -50,11 +51,12 @@ def check_useless_excludes(config_file: str) -> int:
# Not actually a manifest dict, but this more accurately reflects # Not actually a manifest dict, but this more accurately reflects
# the defaults applied during runtime # the defaults applied during runtime
hook = apply_defaults(hook, MANIFEST_HOOK_DICT) hook = apply_defaults(hook, MANIFEST_HOOK_DICT)
names = classifier.filenames names = classifier.by_types(
types = hook['types'] classifier.filenames,
types_or = hook['types_or'] hook['types'],
exclude_types = hook['exclude_types'] hook['types_or'],
names = classifier.by_types(names, types, types_or, exclude_types) hook['exclude_types'],
)
include, exclude = hook['files'], hook['exclude'] include, exclude = hook['files'], hook['exclude']
if not exclude_matches_any(names, include, exclude): if not exclude_matches_any(names, include, exclude):
print( print(

View file

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
import sys import sys
from typing import Sequence from collections.abc import Sequence
from pre_commit import output from pre_commit import output

View file

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
import os.path import os.path
from typing import Mapping from collections.abc import Mapping
from typing import NoReturn from typing import NoReturn
from identify.identify import parse_shebang_from_file from identify.identify import parse_shebang_from_file

View file

@ -3,17 +3,16 @@ from __future__ import annotations
import json import json
import logging import logging
import os import os
from collections.abc import Sequence
from typing import Any from typing import Any
from typing import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit.all_languages import languages
from pre_commit.clientlib import load_manifest from pre_commit.clientlib import load_manifest
from pre_commit.clientlib import LOCAL from pre_commit.clientlib import LOCAL
from pre_commit.clientlib import META from pre_commit.clientlib import META
from pre_commit.clientlib import parse_version
from pre_commit.hook import Hook from pre_commit.hook import Hook
from pre_commit.languages.all import languages from pre_commit.lang_base import environment_dir
from pre_commit.languages.helpers import environment_dir
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.store import Store from pre_commit.store import Store
from pre_commit.util import clean_path_on_failure from pre_commit.util import clean_path_on_failure
@ -32,7 +31,7 @@ def _state_filename_v2(venv: str) -> str:
def _state(additional_deps: Sequence[str]) -> object: def _state(additional_deps: Sequence[str]) -> object:
return {'additional_dependencies': sorted(additional_deps)} return {'additional_dependencies': additional_deps}
def _read_state(venv: str) -> object | None: def _read_state(venv: str) -> object | None:
@ -115,15 +114,6 @@ def _hook(
for dct in rest: for dct in rest:
ret.update(dct) ret.update(dct)
version = ret['minimum_pre_commit_version']
if parse_version(version) > parse_version(C.VERSION):
logger.error(
f'The hook `{ret["id"]}` requires pre-commit version {version} '
f'but version {C.VERSION} is installed. '
f'Perhaps run `pip install --upgrade pre-commit`.',
)
exit(1)
lang = ret['language'] lang = ret['language']
if ret['language_version'] == C.DEFAULT: if ret['language_version'] == C.DEFAULT:
ret['language_version'] = root_config['default_language_version'][lang] ret['language_version'] = root_config['default_language_version'][lang]

View file

@ -1,4 +1,4 @@
name: pre_commit_empty_pubspec name: pre_commit_empty_pubspec
environment: environment:
sdk: '>=2.10.0' sdk: '>=2.12.0'
executables: {} executables: {}

View file

@ -1,4 +1,4 @@
from setuptools import setup from setuptools import setup
setup(name='pre-commit-placeholder-package', version='0.0.0') setup(name='pre-commit-placeholder-package', version='0.0.0', py_modules=[])

Binary file not shown.

View file

@ -4,9 +4,10 @@ import contextlib
import logging import logging
import os.path import os.path
import time import time
from typing import Generator from collections.abc import Generator
from pre_commit import git from pre_commit import git
from pre_commit.errors import FatalError
from pre_commit.util import CalledProcessError from pre_commit.util import CalledProcessError
from pre_commit.util import cmd_output from pre_commit.util import cmd_output
from pre_commit.util import cmd_output_b from pre_commit.util import cmd_output_b
@ -32,7 +33,7 @@ def _git_apply(patch: str) -> None:
@contextlib.contextmanager @contextlib.contextmanager
def _intent_to_add_cleared() -> Generator[None, None, None]: def _intent_to_add_cleared() -> Generator[None]:
intent_to_add = git.intent_to_add_files() intent_to_add = git.intent_to_add_files()
if intent_to_add: if intent_to_add:
logger.warning('Unstaged intent-to-add files detected.') logger.warning('Unstaged intent-to-add files detected.')
@ -47,14 +48,23 @@ def _intent_to_add_cleared() -> Generator[None, None, None]:
@contextlib.contextmanager @contextlib.contextmanager
def _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]: def _unstaged_changes_cleared(patch_dir: str) -> Generator[None]:
tree = cmd_output('git', 'write-tree')[1].strip() tree = cmd_output('git', 'write-tree')[1].strip()
retcode, diff_stdout_binary, _ = cmd_output_b( diff_cmd = (
'git', 'diff-index', '--ignore-submodules', '--binary', 'git', 'diff-index', '--ignore-submodules', '--binary',
'--exit-code', '--no-color', '--no-ext-diff', tree, '--', '--exit-code', '--no-color', '--no-ext-diff', tree, '--',
check=False,
) )
if retcode and diff_stdout_binary.strip(): retcode, diff_stdout, diff_stderr = cmd_output_b(*diff_cmd, check=False)
if retcode == 0:
# There weren't any staged files so we don't need to do anything
# special
yield
elif retcode == 1 and not diff_stdout.strip():
# due to behaviour (probably a bug?) in git with crlf endings and
# autocrlf set to either `true` or `input` sometimes git will refuse
# to show a crlf-only diff to us :(
yield
elif retcode == 1 and diff_stdout.strip():
patch_filename = f'patch{int(time.time())}-{os.getpid()}' patch_filename = f'patch{int(time.time())}-{os.getpid()}'
patch_filename = os.path.join(patch_dir, patch_filename) patch_filename = os.path.join(patch_dir, patch_filename)
logger.warning('Unstaged files detected.') logger.warning('Unstaged files detected.')
@ -62,7 +72,7 @@ def _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]:
# Save the current unstaged changes as a patch # Save the current unstaged changes as a patch
os.makedirs(patch_dir, exist_ok=True) os.makedirs(patch_dir, exist_ok=True)
with open(patch_filename, 'wb') as patch_file: with open(patch_filename, 'wb') as patch_file:
patch_file.write(diff_stdout_binary) patch_file.write(diff_stdout)
# prevent recursive post-checkout hooks (#1418) # prevent recursive post-checkout hooks (#1418)
no_checkout_env = dict(os.environ, _PRE_COMMIT_SKIP_POST_CHECKOUT='1') no_checkout_env = dict(os.environ, _PRE_COMMIT_SKIP_POST_CHECKOUT='1')
@ -86,14 +96,16 @@ def _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]:
_git_apply(patch_filename) _git_apply(patch_filename)
logger.info(f'Restored changes from {patch_filename}.') logger.info(f'Restored changes from {patch_filename}.')
else: else: # pragma: win32 no cover
# There weren't any staged files so we don't need to do anything # some error occurred while requesting the diff
# special e = CalledProcessError(retcode, diff_cmd, b'', diff_stderr)
yield raise FatalError(
f'pre-commit failed to diff -- perhaps due to permissions?\n\n{e}',
)
@contextlib.contextmanager @contextlib.contextmanager
def staged_files_only(patch_dir: str) -> Generator[None, None, None]: def staged_files_only(patch_dir: str) -> Generator[None]:
"""Clear any unstaged changes from the git working directory inside this """Clear any unstaged changes from the git working directory inside this
context. context.
""" """

View file

@ -5,18 +5,18 @@ import logging
import os.path import os.path
import sqlite3 import sqlite3
import tempfile import tempfile
from typing import Callable from collections.abc import Callable
from typing import Generator from collections.abc import Generator
from typing import Sequence from collections.abc import Sequence
import pre_commit.constants as C import pre_commit.constants as C
from pre_commit import clientlib
from pre_commit import file_lock from pre_commit import file_lock
from pre_commit import git from pre_commit import git
from pre_commit.util import CalledProcessError from pre_commit.util import CalledProcessError
from pre_commit.util import clean_path_on_failure from pre_commit.util import clean_path_on_failure
from pre_commit.util import cmd_output_b from pre_commit.util import cmd_output_b
from pre_commit.util import resource_text from pre_commit.util import resource_text
from pre_commit.util import rmtree
logger = logging.getLogger('pre_commit') logger = logging.getLogger('pre_commit')
@ -95,13 +95,13 @@ class Store:
' PRIMARY KEY (repo, ref)' ' PRIMARY KEY (repo, ref)'
');', ');',
) )
self._create_config_table(db) self._create_configs_table(db)
# Atomic file move # Atomic file move
os.replace(tmpfile, self.db_path) os.replace(tmpfile, self.db_path)
@contextlib.contextmanager @contextlib.contextmanager
def exclusive_lock(self) -> Generator[None, None, None]: def exclusive_lock(self) -> Generator[None]:
def blocked_cb() -> None: # pragma: no cover (tests are in-process) def blocked_cb() -> None: # pragma: no cover (tests are in-process)
logger.info('Locking pre-commit directory') logger.info('Locking pre-commit directory')
@ -112,7 +112,7 @@ class Store:
def connect( def connect(
self, self,
db_path: str | None = None, db_path: str | None = None,
) -> Generator[sqlite3.Connection, None, None]: ) -> Generator[sqlite3.Connection]:
db_path = db_path or self.db_path db_path = db_path or self.db_path
# sqlite doesn't close its fd with its contextmanager >.< # sqlite doesn't close its fd with its contextmanager >.<
# contextlib.closing fixes this. # contextlib.closing fixes this.
@ -125,7 +125,7 @@ class Store:
@classmethod @classmethod
def db_repo_name(cls, repo: str, deps: Sequence[str]) -> str: def db_repo_name(cls, repo: str, deps: Sequence[str]) -> str:
if deps: if deps:
return f'{repo}:{",".join(sorted(deps))}' return f'{repo}:{",".join(deps)}'
else: else:
return repo return repo
@ -136,6 +136,7 @@ class Store:
deps: Sequence[str], deps: Sequence[str],
make_strategy: Callable[[str], None], make_strategy: Callable[[str], None],
) -> str: ) -> str:
original_repo = repo
repo = self.db_repo_name(repo, deps) repo = self.db_repo_name(repo, deps)
def _get_result() -> str | None: def _get_result() -> str | None:
@ -168,6 +169,9 @@ class Store:
'INSERT INTO repos (repo, ref, path) VALUES (?, ?, ?)', 'INSERT INTO repos (repo, ref, path) VALUES (?, ?, ?)',
[repo, ref, directory], [repo, ref, directory],
) )
clientlib.warn_for_stages_on_repo_init(original_repo, directory)
return directory return directory
def _complete_clone(self, ref: str, git_cmd: Callable[..., None]) -> None: def _complete_clone(self, ref: str, git_cmd: Callable[..., None]) -> None:
@ -210,7 +214,7 @@ class Store:
'local', C.LOCAL_REPO_VERSION, deps, _make_local_repo, 'local', C.LOCAL_REPO_VERSION, deps, _make_local_repo,
) )
def _create_config_table(self, db: sqlite3.Connection) -> None: def _create_configs_table(self, db: sqlite3.Connection) -> None:
db.executescript( db.executescript(
'CREATE TABLE IF NOT EXISTS configs (' 'CREATE TABLE IF NOT EXISTS configs ('
' path TEXT NOT NULL,' ' path TEXT NOT NULL,'
@ -227,28 +231,5 @@ class Store:
return return
with self.connect() as db: with self.connect() as db:
# TODO: eventually remove this and only create in _create # TODO: eventually remove this and only create in _create
self._create_config_table(db) self._create_configs_table(db)
db.execute('INSERT OR IGNORE INTO configs VALUES (?)', (path,)) db.execute('INSERT OR IGNORE INTO configs VALUES (?)', (path,))
def select_all_configs(self) -> list[str]:
with self.connect() as db:
self._create_config_table(db)
rows = db.execute('SELECT path FROM configs').fetchall()
return [path for path, in rows]
def delete_configs(self, configs: list[str]) -> None:
with self.connect() as db:
rows = [(path,) for path in configs]
db.executemany('DELETE FROM configs WHERE path = ?', rows)
def select_all_repos(self) -> list[tuple[str, str, str]]:
with self.connect() as db:
return db.execute('SELECT repo, ref, path from repos').fetchall()
def delete_repo(self, db_repo_name: str, ref: str, path: str) -> None:
with self.connect() as db:
db.execute(
'DELETE FROM repos WHERE repo = ? and ref = ?',
(db_repo_name, ref),
)
rmtree(path)

View file

@ -8,11 +8,10 @@ import shutil
import stat import stat
import subprocess import subprocess
import sys import sys
from collections.abc import Callable
from collections.abc import Generator
from types import TracebackType from types import TracebackType
from typing import Any from typing import Any
from typing import Callable
from typing import Generator
from typing import IO
from pre_commit import parse_shebang from pre_commit import parse_shebang
@ -26,7 +25,7 @@ def force_bytes(exc: Any) -> bytes:
@contextlib.contextmanager @contextlib.contextmanager
def clean_path_on_failure(path: str) -> Generator[None, None, None]: def clean_path_on_failure(path: str) -> Generator[None]:
"""Cleans up the directory on an exceptional failure.""" """Cleans up the directory on an exceptional failure."""
try: try:
yield yield
@ -36,12 +35,9 @@ def clean_path_on_failure(path: str) -> Generator[None, None, None]:
raise raise
def resource_bytesio(filename: str) -> IO[bytes]:
return importlib.resources.open_binary('pre_commit.resources', filename)
def resource_text(filename: str) -> str: def resource_text(filename: str) -> str:
return importlib.resources.read_text('pre_commit.resources', filename) files = importlib.resources.files('pre_commit.resources')
return files.joinpath(filename).read_text()
def make_executable(filename: str) -> None: def make_executable(filename: str) -> None:
@ -67,7 +63,7 @@ class CalledProcessError(RuntimeError):
def __bytes__(self) -> bytes: def __bytes__(self) -> bytes:
def _indent_or_none(part: bytes | None) -> bytes: def _indent_or_none(part: bytes | None) -> bytes:
if part: if part:
return b'\n ' + part.replace(b'\n', b'\n ') return b'\n ' + part.replace(b'\n', b'\n ').rstrip()
else: else:
return b' (none)' return b' (none)'
@ -124,7 +120,7 @@ def cmd_output(*cmd: str, **kwargs: Any) -> tuple[int, str, str | None]:
return returncode, stdout, stderr return returncode, stdout, stderr
if os.name != 'nt': # pragma: win32 no cover if sys.platform != 'win32': # pragma: win32 no cover
from os import openpty from os import openpty
import termios import termios
@ -206,24 +202,37 @@ else: # pragma: no cover
cmd_output_p = cmd_output_b cmd_output_p = cmd_output_b
def rmtree(path: str) -> None: def _handle_readonly(
"""On windows, rmtree fails for readonly dirs.""" func: Callable[[str], object],
def handle_remove_readonly( path: str,
func: Callable[..., Any], exc: BaseException,
path: str, ) -> None:
exc: tuple[type[OSError], OSError, TracebackType], if (
func in (os.rmdir, os.remove, os.unlink) and
isinstance(exc, OSError) and
exc.errno in {errno.EACCES, errno.EPERM}
):
for p in (path, os.path.dirname(path)):
os.chmod(p, os.stat(p).st_mode | stat.S_IWUSR)
func(path)
else:
raise
if sys.version_info < (3, 12): # pragma: <3.12 cover
def _handle_readonly_old(
func: Callable[[str], object],
path: str,
excinfo: tuple[type[BaseException], BaseException, TracebackType],
) -> None: ) -> None:
excvalue = exc[1] return _handle_readonly(func, path, excinfo[1])
if (
func in (os.rmdir, os.remove, os.unlink) and def rmtree(path: str) -> None:
excvalue.errno in {errno.EACCES, errno.EPERM} shutil.rmtree(path, ignore_errors=False, onerror=_handle_readonly_old)
): else: # pragma: >=3.12 cover
for p in (path, os.path.dirname(path)): def rmtree(path: str) -> None:
os.chmod(p, os.stat(p).st_mode | stat.S_IWUSR) """On windows, rmtree fails for readonly dirs."""
func(path) shutil.rmtree(path, ignore_errors=False, onexc=_handle_readonly)
else:
raise
shutil.rmtree(path, ignore_errors=False, onerror=handle_remove_readonly)
def win_exe(s: str) -> str: def win_exe(s: str) -> str:

View file

@ -3,15 +3,16 @@ from __future__ import annotations
import concurrent.futures import concurrent.futures
import contextlib import contextlib
import math import math
import multiprocessing
import os import os
import subprocess import subprocess
import sys import sys
from collections.abc import Callable
from collections.abc import Generator
from collections.abc import Iterable
from collections.abc import MutableMapping
from collections.abc import Sequence
from typing import Any from typing import Any
from typing import Callable
from typing import Generator
from typing import Iterable
from typing import MutableMapping
from typing import Sequence
from typing import TypeVar from typing import TypeVar
from pre_commit import parse_shebang from pre_commit import parse_shebang
@ -22,6 +23,21 @@ TArg = TypeVar('TArg')
TRet = TypeVar('TRet') TRet = TypeVar('TRet')
def cpu_count() -> int:
try:
# On systems that support it, this will return a more accurate count of
# usable CPUs for the current process, which will take into account
# cgroup limits
return len(os.sched_getaffinity(0))
except AttributeError:
pass
try:
return multiprocessing.cpu_count()
except NotImplementedError:
return 1
def _environ_size(_env: MutableMapping[str, str] | None = None) -> int: def _environ_size(_env: MutableMapping[str, str] | None = None) -> int:
environ = _env if _env is not None else getattr(os, 'environb', os.environ) environ = _env if _env is not None else getattr(os, 'environb', os.environ)
size = 8 * len(environ) # number of pointers in `envp` size = 8 * len(environ) # number of pointers in `envp`
@ -104,7 +120,6 @@ def partition(
@contextlib.contextmanager @contextlib.contextmanager
def _thread_mapper(maxsize: int) -> Generator[ def _thread_mapper(maxsize: int) -> Generator[
Callable[[Callable[[TArg], TRet], Iterable[TArg]], Iterable[TRet]], Callable[[Callable[[TArg], TRet], Iterable[TArg]], Iterable[TRet]],
None, None,
]: ]:
if maxsize == 1: if maxsize == 1:
yield map yield map
@ -162,7 +177,8 @@ def xargs(
results = thread_map(run_cmd_partition, partitions) results = thread_map(run_cmd_partition, partitions)
for proc_retcode, proc_out, _ in results: for proc_retcode, proc_out, _ in results:
retcode = max(retcode, proc_retcode) if abs(proc_retcode) > abs(retcode):
retcode = proc_retcode
stdout += proc_out stdout += proc_out
return retcode, stdout return retcode, stdout

View file

@ -6,6 +6,7 @@ from typing import Any
import yaml import yaml
Loader = getattr(yaml, 'CSafeLoader', yaml.SafeLoader) Loader = getattr(yaml, 'CSafeLoader', yaml.SafeLoader)
yaml_compose = functools.partial(yaml.compose, Loader=Loader)
yaml_load = functools.partial(yaml.load, Loader=Loader) yaml_load = functools.partial(yaml.load, Loader=Loader)
Dumper = getattr(yaml, 'CSafeDumper', yaml.SafeDumper) Dumper = getattr(yaml, 'CSafeDumper', yaml.SafeDumper)

View file

@ -0,0 +1,52 @@
from __future__ import annotations
from collections.abc import Generator
from collections.abc import Iterable
from typing import NamedTuple
from typing import Protocol
from yaml.nodes import MappingNode
from yaml.nodes import Node
from yaml.nodes import ScalarNode
from yaml.nodes import SequenceNode
class _Matcher(Protocol):
def match(self, n: Node) -> Generator[Node]: ...
class MappingKey(NamedTuple):
k: str
def match(self, n: Node) -> Generator[Node]:
if isinstance(n, MappingNode):
for k, _ in n.value:
if k.value == self.k:
yield k
class MappingValue(NamedTuple):
k: str
def match(self, n: Node) -> Generator[Node]:
if isinstance(n, MappingNode):
for k, v in n.value:
if k.value == self.k:
yield v
class SequenceItem(NamedTuple):
def match(self, n: Node) -> Generator[Node]:
if isinstance(n, SequenceNode):
yield from n.value
def _match(gen: Iterable[Node], m: _Matcher) -> Iterable[Node]:
return (n for src in gen for n in m.match(src))
def match(n: Node, matcher: tuple[_Matcher, ...]) -> Generator[ScalarNode]:
gen: Iterable[Node] = (n,)
for m in matcher:
gen = _match(gen, m)
return (n for n in gen if isinstance(n, ScalarNode))

View file

@ -1,6 +1,6 @@
[metadata] [metadata]
name = pre_commit name = pre_commit
version = 3.0.2 version = 4.5.1
description = A framework for managing and maintaining multi-language pre-commit hooks. description = A framework for managing and maintaining multi-language pre-commit hooks.
long_description = file: README.md long_description = file: README.md
long_description_content_type = text/markdown long_description_content_type = text/markdown
@ -8,9 +8,8 @@ url = https://github.com/pre-commit/pre-commit
author = Anthony Sottile author = Anthony Sottile
author_email = asottile@umich.edu author_email = asottile@umich.edu
license = MIT license = MIT
license_file = LICENSE license_files = LICENSE
classifiers = classifiers =
License :: OSI Approved :: MIT License
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: CPython
@ -24,7 +23,7 @@ install_requires =
nodeenv>=0.11.1 nodeenv>=0.11.1
pyyaml>=5.1 pyyaml>=5.1
virtualenv>=20.10.0 virtualenv>=20.10.0
python_requires = >=3.8 python_requires = >=3.10
[options.packages.find] [options.packages.find]
exclude = exclude =
@ -53,6 +52,7 @@ check_untyped_defs = true
disallow_any_generics = true disallow_any_generics = true
disallow_incomplete_defs = true disallow_incomplete_defs = true
disallow_untyped_defs = true disallow_untyped_defs = true
enable_error_code = deprecated
warn_redundant_casts = true warn_redundant_casts = true
warn_unused_ignores = true warn_unused_ignores = true

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
VERSION=2.13.4 VERSION=2.19.6
if [ "$OSTYPE" = msys ]; then if [ "$OSTYPE" = msys ]; then
URL="https://storage.googleapis.com/dart-archive/channels/stable/release/${VERSION}/sdk/dartsdk-windows-x64-release.zip" URL="https://storage.googleapis.com/dart-archive/channels/stable/release/${VERSION}/sdk/dartsdk-windows-x64-release.zip"

View file

@ -1,29 +0,0 @@
#!/usr/bin/env bash
# This is a script used in CI to install swift
set -euo pipefail
. /etc/lsb-release
if [ "$DISTRIB_CODENAME" = "jammy" ]; then
SWIFT_URL='https://download.swift.org/swift-5.7.1-release/ubuntu2204/swift-5.7.1-RELEASE/swift-5.7.1-RELEASE-ubuntu22.04.tar.gz'
SWIFT_HASH='7f60291f5088d3e77b0c2364beaabd29616ee7b37260b7b06bdbeb891a7fe161'
else
echo "unknown dist: ${DISTRIB_CODENAME}" 1>&2
exit 1
fi
check() {
echo "$SWIFT_HASH $TGZ" | sha256sum --check
}
TGZ="$HOME/.swift/swift.tar.gz"
mkdir -p "$(dirname "$TGZ")"
if ! check >& /dev/null; then
rm -f "$TGZ"
curl --location --silent --output "$TGZ" "$SWIFT_URL"
check
fi
mkdir -p /tmp/swift
tar -xf "$TGZ" --strip 1 --directory /tmp/swift
echo '/tmp/swift/usr/bin' >> "$GITHUB_PATH"

View file

@ -1,10 +1,9 @@
from __future__ import annotations from __future__ import annotations
import os import os
from typing import Sequence from collections.abc import Sequence
import pre_commit.constants as C from pre_commit.lang_base import Language
from pre_commit.languages.all import Language
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
@ -14,13 +13,19 @@ def run_language(
exe: str, exe: str,
args: Sequence[str] = (), args: Sequence[str] = (),
file_args: Sequence[str] = (), file_args: Sequence[str] = (),
version: str = C.DEFAULT, version: str | None = None,
deps: Sequence[str] = (), deps: Sequence[str] = (),
is_local: bool = False, is_local: bool = False,
require_serial: bool = True,
color: bool = False,
) -> tuple[int, bytes]: ) -> tuple[int, bytes]:
prefix = Prefix(str(path)) prefix = Prefix(str(path))
version = version or language.get_default_version()
language.install_environment(prefix, version, deps) if language.ENVIRONMENT_DIR is not None:
language.install_environment(prefix, version, deps)
health_error = language.health_check(prefix, version)
assert health_error is None, health_error
with language.in_env(prefix, version): with language.in_env(prefix, version):
ret, out = language.run_hook( ret, out = language.run_hook(
prefix, prefix,
@ -28,8 +33,8 @@ def run_language(
args, args,
file_args, file_args,
is_local=is_local, is_local=is_local,
require_serial=True, require_serial=require_serial,
color=False, color=color,
) )
out = out.replace(b'\r\n', b'\n') out = out.replace(b'\r\n', b'\n')
return ret, out return ret, out

92
testing/languages Executable file
View file

@ -0,0 +1,92 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import concurrent.futures
import json
import os.path
import subprocess
import sys
EXCLUDED = frozenset((
('windows-latest', 'docker'),
('windows-latest', 'docker_image'),
('windows-latest', 'lua'),
('windows-latest', 'swift'),
))
def _always_run() -> frozenset[str]:
ret = ['.github/workflows/languages.yaml', 'testing/languages']
ret.extend(
os.path.join('pre_commit/resources', fname)
for fname in os.listdir('pre_commit/resources')
)
return frozenset(ret)
def _lang_files(lang: str) -> frozenset[str]:
prog = f'''\
import json
import os.path
import sys
import pre_commit.languages.{lang}
import tests.languages.{lang}_test
modules = sorted(
os.path.relpath(v.__file__)
for k, v in sys.modules.items()
if k.startswith(('pre_commit.', 'tests.', 'testing.'))
)
print(json.dumps(modules))
'''
out = json.loads(subprocess.check_output((sys.executable, '-c', prog)))
return frozenset(out)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument('--all', action='store_true')
args = parser.parse_args()
langs = [
os.path.splitext(fname)[0]
for fname in sorted(os.listdir('pre_commit/languages'))
if fname.endswith('.py') and fname != '__init__.py'
]
triggers_all = _always_run()
for fname in triggers_all:
assert os.path.exists(fname), fname
if not args.all:
with concurrent.futures.ThreadPoolExecutor(os.cpu_count()) as exe:
by_lang = {
lang: files | triggers_all
for lang, files in zip(langs, exe.map(_lang_files, langs))
}
diff_cmd = ('git', 'diff', '--name-only', 'origin/main...HEAD')
files = set(subprocess.check_output(diff_cmd).decode().splitlines())
langs = [
lang
for lang, lang_files in by_lang.items()
if lang_files & files
]
matched = [
{'os': os, 'language': lang}
for os in ('windows-latest', 'ubuntu-latest')
for lang in langs
if (os, lang) not in EXCLUDED
]
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write(f'languages={json.dumps(matched)}\n')
return 0
if __name__ == '__main__':
raise SystemExit(main())

View file

@ -8,7 +8,7 @@ import shutil
import subprocess import subprocess
import tarfile import tarfile
import tempfile import tempfile
from typing import Sequence from collections.abc import Sequence
# This is a script for generating the tarred resources for git repo # This is a script for generating the tarred resources for git repo
@ -16,8 +16,8 @@ from typing import Sequence
REPOS = ( REPOS = (
('rbenv', 'https://github.com/rbenv/rbenv', '38e1fbb'), ('rbenv', 'https://github.com/rbenv/rbenv', '10e96bfc'),
('ruby-build', 'https://github.com/rbenv/ruby-build', '9d92a69'), ('ruby-build', 'https://github.com/rbenv/ruby-build', '447468b1'),
( (
'ruby-download', 'ruby-download',
'https://github.com/garnieretienne/rvm-download', 'https://github.com/garnieretienne/rvm-download',
@ -57,8 +57,7 @@ def make_archive(name: str, repo: str, ref: str, destdir: str) -> str:
arcs.sort() arcs.sort()
with gzip.GzipFile(output_path, 'wb', mtime=0) as gzipf: with gzip.GzipFile(output_path, 'wb', mtime=0) as gzipf:
# https://github.com/python/typeshed/issues/5491 with tarfile.open(fileobj=gzipf, mode='w') as tf:
with tarfile.open(fileobj=gzipf, mode='w') as tf: # type: ignore
for arcname, abspath in arcs: for arcname, abspath in arcs:
tf.add( tf.add(
abspath, abspath,

View file

@ -1,17 +0,0 @@
- id: docker-hook
name: Docker test hook
entry: echo
language: docker
files: \.txt$
- id: docker-hook-arg
name: Docker test hook
entry: echo -n
language: docker
files: \.txt$
- id: docker-hook-failing
name: Docker test hook with nonzero exit code
entry: bork
language: docker
files: \.txt$

View file

@ -1,3 +0,0 @@
FROM ubuntu:focal
CMD ["echo", "This is overwritten by the .pre-commit-hooks.yaml 'entry'"]

View file

@ -1,8 +0,0 @@
- id: echo-entrypoint
name: echo (via --entrypoint)
language: docker_image
entry: --entrypoint echo ubuntu:focal
- id: echo-cmd
name: echo (via cmd)
language: docker_image
entry: ubuntu:focal echo

View file

@ -1,12 +0,0 @@
- id: dotnet-example-hook
name: Test Project 1
description: Test Project 1
entry: proj1
language: dotnet
stages: [commit]
- id: proj2
name: Test Project 2
description: Test Project 2
entry: proj2
language: dotnet
stages: [commit]

View file

@ -1,28 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "proj1", "proj1\proj1.csproj", "{38A939C3-DEA4-47D7-9B75-0418C4249662}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "proj2", "proj2\proj2.csproj", "{4C9916CB-165C-4EF5-8A57-4CB6794C1EBF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{38A939C3-DEA4-47D7-9B75-0418C4249662}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{38A939C3-DEA4-47D7-9B75-0418C4249662}.Debug|Any CPU.Build.0 = Debug|Any CPU
{38A939C3-DEA4-47D7-9B75-0418C4249662}.Release|Any CPU.ActiveCfg = Release|Any CPU
{38A939C3-DEA4-47D7-9B75-0418C4249662}.Release|Any CPU.Build.0 = Release|Any CPU
{4C9916CB-165C-4EF5-8A57-4CB6794C1EBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4C9916CB-165C-4EF5-8A57-4CB6794C1EBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4C9916CB-165C-4EF5-8A57-4CB6794C1EBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4C9916CB-165C-4EF5-8A57-4CB6794C1EBF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View file

@ -1,12 +0,0 @@
using System;
namespace proj1
{
class Program
{
static void Main(string[] args)
{
Console.Write("Hello from dotnet!\n");
}
}
}

View file

@ -1,12 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6</TargetFramework>
<PackAsTool>true</PackAsTool>
<ToolCommandName>proj1</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>
</PropertyGroup>
</Project>

View file

@ -1,12 +0,0 @@
using System;
namespace proj2
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}

View file

@ -1,12 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6</TargetFramework>
<PackAsTool>true</PackAsTool>
<ToolCommandName>proj2</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>
</PropertyGroup>
</Project>

View file

@ -1,3 +0,0 @@
bin/
obj/
nupkg/

View file

@ -1,5 +0,0 @@
- id: dotnet-example-hook
name: dotnet example hook
entry: testeroni.tool
language: dotnet
files: ''

View file

@ -1,12 +0,0 @@
using System;
namespace dotnet_hooks_repo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello from dotnet!");
}
}
}

View file

@ -1,9 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<PackAsTool>true</PackAsTool>
<ToolCommandName>testeroni.tool</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>
</PropertyGroup>
</Project>

View file

@ -1,3 +0,0 @@
bin/
obj/
nupkg/

View file

@ -1,5 +0,0 @@
- id: dotnet-example-hook
name: dotnet example hook
entry: testeroni
language: dotnet
files: ''

View file

@ -1,12 +0,0 @@
using System;
namespace dotnet_hooks_repo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello from dotnet!");
}
}
}

View file

@ -1,9 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6</TargetFramework>
<PackAsTool>true</PackAsTool>
<ToolCommandName>testeroni</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>
</PropertyGroup>
</Project>

View file

@ -1,3 +0,0 @@
bin/
obj/
nupkg/

View file

@ -1,5 +0,0 @@
- id: dotnet-example-hook
name: dotnet example hook
entry: testeroni
language: dotnet
files: ''

View file

@ -1,12 +0,0 @@
using System;
namespace dotnet_hooks_sln_repo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello from dotnet!");
}
}
}

View file

@ -1,9 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6</TargetFramework>
<PackAsTool>true</PackAsTool>
<ToolCommandName>testeroni</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>
</PropertyGroup>
</Project>

View file

@ -1,34 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet_hooks_sln_repo", "dotnet_hooks_sln_repo.csproj", "{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}.Debug|x64.ActiveCfg = Debug|Any CPU
{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}.Debug|x64.Build.0 = Debug|Any CPU
{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}.Debug|x86.ActiveCfg = Debug|Any CPU
{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}.Debug|x86.Build.0 = Debug|Any CPU
{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}.Release|Any CPU.Build.0 = Release|Any CPU
{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}.Release|x64.ActiveCfg = Release|Any CPU
{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}.Release|x64.Build.0 = Release|Any CPU
{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}.Release|x86.ActiveCfg = Release|Any CPU
{6568CFDB-6F6F-45A9-932C-8C7DAABC8E56}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View file

@ -1,5 +0,0 @@
- id: golang-hook
name: golang example hook
entry: golang-hello-world
language: golang
files: ''

View file

@ -1,5 +0,0 @@
module golang-hello-world
go 1.18
require github.com/BurntSushi/toml v1.1.0

View file

@ -1,2 +0,0 @@
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=

View file

@ -1,23 +0,0 @@
package main
import (
"fmt"
"runtime"
"github.com/BurntSushi/toml"
"os"
)
type Config struct {
What string
}
func main() {
message := runtime.Version()
if len(os.Args) > 1 {
message = os.Args[1]
}
var conf Config
toml.Decode("What = 'world'\n", &conf)
fmt.Printf("hello %v from %s\n", conf.What, message)
}

Some files were not shown because too many files have changed in this diff Show more