pre-commit-ex 4.5.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pre_commit/__init__.py +0 -0
- pre_commit/__main__.py +7 -0
- pre_commit/all_languages.py +50 -0
- pre_commit/clientlib.py +551 -0
- pre_commit/color.py +109 -0
- pre_commit/commands/__init__.py +0 -0
- pre_commit/commands/autoupdate.py +215 -0
- pre_commit/commands/clean.py +16 -0
- pre_commit/commands/gc.py +98 -0
- pre_commit/commands/hazmat.py +95 -0
- pre_commit/commands/hook_impl.py +272 -0
- pre_commit/commands/init_templatedir.py +39 -0
- pre_commit/commands/install_uninstall.py +167 -0
- pre_commit/commands/migrate_config.py +135 -0
- pre_commit/commands/run.py +474 -0
- pre_commit/commands/sample_config.py +18 -0
- pre_commit/commands/try_repo.py +77 -0
- pre_commit/commands/validate_config.py +18 -0
- pre_commit/commands/validate_manifest.py +18 -0
- pre_commit/constants.py +13 -0
- pre_commit/envcontext.py +62 -0
- pre_commit/error_handler.py +81 -0
- pre_commit/errors.py +5 -0
- pre_commit/file_lock.py +75 -0
- pre_commit/git.py +245 -0
- pre_commit/hook.py +60 -0
- pre_commit/lang_base.py +196 -0
- pre_commit/languages/__init__.py +0 -0
- pre_commit/languages/conda.py +77 -0
- pre_commit/languages/coursier.py +76 -0
- pre_commit/languages/dart.py +97 -0
- pre_commit/languages/docker.py +181 -0
- pre_commit/languages/docker_image.py +32 -0
- pre_commit/languages/dotnet.py +111 -0
- pre_commit/languages/fail.py +27 -0
- pre_commit/languages/golang.py +161 -0
- pre_commit/languages/haskell.py +56 -0
- pre_commit/languages/julia.py +133 -0
- pre_commit/languages/lua.py +75 -0
- pre_commit/languages/node.py +110 -0
- pre_commit/languages/perl.py +50 -0
- pre_commit/languages/pygrep.py +133 -0
- pre_commit/languages/python.py +228 -0
- pre_commit/languages/r.py +278 -0
- pre_commit/languages/ruby.py +145 -0
- pre_commit/languages/rust.py +160 -0
- pre_commit/languages/swift.py +50 -0
- pre_commit/languages/unsupported.py +10 -0
- pre_commit/languages/unsupported_script.py +32 -0
- pre_commit/logging_handler.py +42 -0
- pre_commit/main.py +472 -0
- pre_commit/meta_hooks/__init__.py +0 -0
- pre_commit/meta_hooks/check_hooks_apply.py +43 -0
- pre_commit/meta_hooks/check_useless_excludes.py +83 -0
- pre_commit/meta_hooks/identity.py +17 -0
- pre_commit/output.py +33 -0
- pre_commit/parse_shebang.py +85 -0
- pre_commit/prefix.py +18 -0
- pre_commit/repository.py +237 -0
- pre_commit/resources/__init__.py +0 -0
- pre_commit/resources/empty_template_.npmignore +1 -0
- pre_commit/resources/empty_template_Cargo.toml +7 -0
- pre_commit/resources/empty_template_LICENSE.renv +7 -0
- pre_commit/resources/empty_template_Makefile.PL +6 -0
- pre_commit/resources/empty_template_activate.R +440 -0
- pre_commit/resources/empty_template_environment.yml +9 -0
- pre_commit/resources/empty_template_go.mod +1 -0
- pre_commit/resources/empty_template_main.go +3 -0
- pre_commit/resources/empty_template_main.rs +1 -0
- pre_commit/resources/empty_template_package.json +4 -0
- pre_commit/resources/empty_template_pre-commit-package-dev-1.rockspec +12 -0
- pre_commit/resources/empty_template_pre_commit_placeholder_package.gemspec +6 -0
- pre_commit/resources/empty_template_pubspec.yaml +4 -0
- pre_commit/resources/empty_template_renv.lock +20 -0
- pre_commit/resources/empty_template_setup.py +4 -0
- pre_commit/resources/hook-tmpl +20 -0
- pre_commit/resources/rbenv.tar.gz +0 -0
- pre_commit/resources/ruby-build.tar.gz +0 -0
- pre_commit/resources/ruby-download.tar.gz +0 -0
- pre_commit/staged_files_only.py +113 -0
- pre_commit/store.py +235 -0
- pre_commit/util.py +239 -0
- pre_commit/xargs.py +184 -0
- pre_commit/yaml.py +19 -0
- pre_commit/yaml_rewrite.py +52 -0
- pre_commit_ex-4.5.1.dist-info/METADATA +63 -0
- pre_commit_ex-4.5.1.dist-info/RECORD +91 -0
- pre_commit_ex-4.5.1.dist-info/WHEEL +6 -0
- pre_commit_ex-4.5.1.dist-info/entry_points.txt +2 -0
- pre_commit_ex-4.5.1.dist-info/licenses/LICENSE +19 -0
- pre_commit_ex-4.5.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from collections.abc import Generator
|
|
7
|
+
from collections.abc import Sequence
|
|
8
|
+
|
|
9
|
+
from pre_commit import lang_base
|
|
10
|
+
from pre_commit.envcontext import envcontext
|
|
11
|
+
from pre_commit.envcontext import PatchesT
|
|
12
|
+
from pre_commit.envcontext import SubstitutionT
|
|
13
|
+
from pre_commit.envcontext import UNSET
|
|
14
|
+
from pre_commit.envcontext import Var
|
|
15
|
+
from pre_commit.prefix import Prefix
|
|
16
|
+
from pre_commit.util import cmd_output_b
|
|
17
|
+
|
|
18
|
+
ENVIRONMENT_DIR = 'conda'
|
|
19
|
+
get_default_version = lang_base.basic_get_default_version
|
|
20
|
+
health_check = lang_base.basic_health_check
|
|
21
|
+
run_hook = lang_base.basic_run_hook
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_env_patch(env: str) -> PatchesT:
|
|
25
|
+
# On non-windows systems executable live in $CONDA_PREFIX/bin, on Windows
|
|
26
|
+
# they can be in $CONDA_PREFIX/bin, $CONDA_PREFIX/Library/bin,
|
|
27
|
+
# $CONDA_PREFIX/Scripts and $CONDA_PREFIX. Whereas the latter only
|
|
28
|
+
# seems to be used for python.exe.
|
|
29
|
+
path: SubstitutionT = (os.path.join(env, 'bin'), os.pathsep, Var('PATH'))
|
|
30
|
+
if sys.platform == 'win32': # pragma: win32 cover
|
|
31
|
+
path = (env, os.pathsep, *path)
|
|
32
|
+
path = (os.path.join(env, 'Scripts'), os.pathsep, *path)
|
|
33
|
+
path = (os.path.join(env, 'Library', 'bin'), os.pathsep, *path)
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
('PYTHONHOME', UNSET),
|
|
37
|
+
('VIRTUAL_ENV', UNSET),
|
|
38
|
+
('CONDA_PREFIX', env),
|
|
39
|
+
('PATH', path),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@contextlib.contextmanager
|
|
44
|
+
def in_env(prefix: Prefix, version: str) -> Generator[None]:
|
|
45
|
+
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
|
46
|
+
with envcontext(get_env_patch(envdir)):
|
|
47
|
+
yield
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _conda_exe() -> str:
|
|
51
|
+
if os.environ.get('PRE_COMMIT_USE_MICROMAMBA'):
|
|
52
|
+
return 'micromamba'
|
|
53
|
+
elif os.environ.get('PRE_COMMIT_USE_MAMBA'):
|
|
54
|
+
return 'mamba'
|
|
55
|
+
else:
|
|
56
|
+
return 'conda'
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def install_environment(
|
|
60
|
+
prefix: Prefix,
|
|
61
|
+
version: str,
|
|
62
|
+
additional_dependencies: Sequence[str],
|
|
63
|
+
) -> None:
|
|
64
|
+
lang_base.assert_version_default('conda', version)
|
|
65
|
+
|
|
66
|
+
conda_exe = _conda_exe()
|
|
67
|
+
|
|
68
|
+
env_dir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
|
69
|
+
cmd_output_b(
|
|
70
|
+
conda_exe, 'env', 'create', '-p', env_dir, '--file',
|
|
71
|
+
'environment.yml', cwd=prefix.prefix_dir,
|
|
72
|
+
)
|
|
73
|
+
if additional_dependencies:
|
|
74
|
+
cmd_output_b(
|
|
75
|
+
conda_exe, 'install', '-p', env_dir, *additional_dependencies,
|
|
76
|
+
cwd=prefix.prefix_dir,
|
|
77
|
+
)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import os.path
|
|
5
|
+
from collections.abc import Generator
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
|
|
8
|
+
from pre_commit import lang_base
|
|
9
|
+
from pre_commit.envcontext import envcontext
|
|
10
|
+
from pre_commit.envcontext import PatchesT
|
|
11
|
+
from pre_commit.envcontext import Var
|
|
12
|
+
from pre_commit.errors import FatalError
|
|
13
|
+
from pre_commit.parse_shebang import find_executable
|
|
14
|
+
from pre_commit.prefix import Prefix
|
|
15
|
+
|
|
16
|
+
ENVIRONMENT_DIR = 'coursier'
|
|
17
|
+
|
|
18
|
+
get_default_version = lang_base.basic_get_default_version
|
|
19
|
+
health_check = lang_base.basic_health_check
|
|
20
|
+
run_hook = lang_base.basic_run_hook
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def install_environment(
|
|
24
|
+
prefix: Prefix,
|
|
25
|
+
version: str,
|
|
26
|
+
additional_dependencies: Sequence[str],
|
|
27
|
+
) -> None:
|
|
28
|
+
lang_base.assert_version_default('coursier', version)
|
|
29
|
+
|
|
30
|
+
# Support both possible executable names (either "cs" or "coursier")
|
|
31
|
+
cs = find_executable('cs') or find_executable('coursier')
|
|
32
|
+
if cs is None:
|
|
33
|
+
raise AssertionError(
|
|
34
|
+
'pre-commit requires system-installed "cs" or "coursier" '
|
|
35
|
+
'executables in the application search path',
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
|
39
|
+
|
|
40
|
+
def _install(*opts: str) -> None:
|
|
41
|
+
assert cs is not None
|
|
42
|
+
lang_base.setup_cmd(prefix, (cs, 'fetch', *opts))
|
|
43
|
+
lang_base.setup_cmd(prefix, (cs, 'install', '--dir', envdir, *opts))
|
|
44
|
+
|
|
45
|
+
with in_env(prefix, version):
|
|
46
|
+
channel = prefix.path('.pre-commit-channel')
|
|
47
|
+
if os.path.isdir(channel):
|
|
48
|
+
for app_descriptor in os.listdir(channel):
|
|
49
|
+
_, app_file = os.path.split(app_descriptor)
|
|
50
|
+
app, _ = os.path.splitext(app_file)
|
|
51
|
+
_install(
|
|
52
|
+
'--default-channels=false',
|
|
53
|
+
'--channel', channel,
|
|
54
|
+
app,
|
|
55
|
+
)
|
|
56
|
+
elif not additional_dependencies:
|
|
57
|
+
raise FatalError(
|
|
58
|
+
'expected .pre-commit-channel dir or additional_dependencies',
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if additional_dependencies:
|
|
62
|
+
_install(*additional_dependencies)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_env_patch(target_dir: str) -> PatchesT:
|
|
66
|
+
return (
|
|
67
|
+
('PATH', (target_dir, os.pathsep, Var('PATH'))),
|
|
68
|
+
('COURSIER_CACHE', os.path.join(target_dir, '.cs-cache')),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@contextlib.contextmanager
|
|
73
|
+
def in_env(prefix: Prefix, version: str) -> Generator[None]:
|
|
74
|
+
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
|
75
|
+
with envcontext(get_env_patch(envdir)):
|
|
76
|
+
yield
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import os.path
|
|
5
|
+
import shutil
|
|
6
|
+
import tempfile
|
|
7
|
+
from collections.abc import Generator
|
|
8
|
+
from collections.abc import Sequence
|
|
9
|
+
|
|
10
|
+
from pre_commit import lang_base
|
|
11
|
+
from pre_commit.envcontext import envcontext
|
|
12
|
+
from pre_commit.envcontext import PatchesT
|
|
13
|
+
from pre_commit.envcontext import Var
|
|
14
|
+
from pre_commit.prefix import Prefix
|
|
15
|
+
from pre_commit.util import win_exe
|
|
16
|
+
from pre_commit.yaml import yaml_load
|
|
17
|
+
|
|
18
|
+
ENVIRONMENT_DIR = 'dartenv'
|
|
19
|
+
|
|
20
|
+
get_default_version = lang_base.basic_get_default_version
|
|
21
|
+
health_check = lang_base.basic_health_check
|
|
22
|
+
run_hook = lang_base.basic_run_hook
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_env_patch(venv: str) -> PatchesT:
|
|
26
|
+
return (
|
|
27
|
+
('PATH', (os.path.join(venv, 'bin'), os.pathsep, Var('PATH'))),
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@contextlib.contextmanager
|
|
32
|
+
def in_env(prefix: Prefix, version: str) -> Generator[None]:
|
|
33
|
+
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
|
34
|
+
with envcontext(get_env_patch(envdir)):
|
|
35
|
+
yield
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def install_environment(
|
|
39
|
+
prefix: Prefix,
|
|
40
|
+
version: str,
|
|
41
|
+
additional_dependencies: Sequence[str],
|
|
42
|
+
) -> None:
|
|
43
|
+
lang_base.assert_version_default('dart', version)
|
|
44
|
+
|
|
45
|
+
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
|
46
|
+
bin_dir = os.path.join(envdir, 'bin')
|
|
47
|
+
|
|
48
|
+
def _install_dir(prefix_p: Prefix, pub_cache: str) -> None:
|
|
49
|
+
dart_env = {**os.environ, 'PUB_CACHE': pub_cache}
|
|
50
|
+
|
|
51
|
+
with open(prefix_p.path('pubspec.yaml')) as f:
|
|
52
|
+
pubspec_contents = yaml_load(f)
|
|
53
|
+
|
|
54
|
+
lang_base.setup_cmd(prefix_p, ('dart', 'pub', 'get'), env=dart_env)
|
|
55
|
+
|
|
56
|
+
for executable in pubspec_contents['executables']:
|
|
57
|
+
lang_base.setup_cmd(
|
|
58
|
+
prefix_p,
|
|
59
|
+
(
|
|
60
|
+
'dart', 'compile', 'exe',
|
|
61
|
+
'--output', os.path.join(bin_dir, win_exe(executable)),
|
|
62
|
+
prefix_p.path('bin', f'{executable}.dart'),
|
|
63
|
+
),
|
|
64
|
+
env=dart_env,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
os.makedirs(bin_dir)
|
|
68
|
+
|
|
69
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
70
|
+
_install_dir(prefix, tmp)
|
|
71
|
+
|
|
72
|
+
for dep_s in additional_dependencies:
|
|
73
|
+
with tempfile.TemporaryDirectory() as dep_tmp:
|
|
74
|
+
dep, _, version = dep_s.partition(':')
|
|
75
|
+
if version:
|
|
76
|
+
dep_cmd: tuple[str, ...] = (dep, '--version', version)
|
|
77
|
+
else:
|
|
78
|
+
dep_cmd = (dep,)
|
|
79
|
+
|
|
80
|
+
lang_base.setup_cmd(
|
|
81
|
+
prefix,
|
|
82
|
+
('dart', 'pub', 'cache', 'add', *dep_cmd),
|
|
83
|
+
env={**os.environ, 'PUB_CACHE': dep_tmp},
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# try and find the 'pubspec.yaml' that just got added
|
|
87
|
+
for root, _, filenames in os.walk(dep_tmp):
|
|
88
|
+
if 'pubspec.yaml' in filenames:
|
|
89
|
+
with tempfile.TemporaryDirectory() as copied:
|
|
90
|
+
pkg = os.path.join(copied, 'pkg')
|
|
91
|
+
shutil.copytree(root, pkg)
|
|
92
|
+
_install_dir(Prefix(pkg), dep_tmp)
|
|
93
|
+
break
|
|
94
|
+
else:
|
|
95
|
+
raise AssertionError(
|
|
96
|
+
f'could not find pubspec.yaml for {dep_s}',
|
|
97
|
+
)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import functools
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
from pre_commit import lang_base
|
|
12
|
+
from pre_commit.prefix import Prefix
|
|
13
|
+
from pre_commit.util import CalledProcessError
|
|
14
|
+
from pre_commit.util import cmd_output_b
|
|
15
|
+
|
|
16
|
+
ENVIRONMENT_DIR = 'docker'
|
|
17
|
+
PRE_COMMIT_LABEL = 'PRE_COMMIT'
|
|
18
|
+
get_default_version = lang_base.basic_get_default_version
|
|
19
|
+
health_check = lang_base.basic_health_check
|
|
20
|
+
in_env = lang_base.no_env # no special environment for docker
|
|
21
|
+
|
|
22
|
+
_HOSTNAME_MOUNT_RE = re.compile(
|
|
23
|
+
rb"""
|
|
24
|
+
/containers
|
|
25
|
+
(?:/overlay-containers)?
|
|
26
|
+
/([a-z0-9]{64})
|
|
27
|
+
(?:/userdata)?
|
|
28
|
+
/hostname
|
|
29
|
+
""",
|
|
30
|
+
re.VERBOSE,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _get_container_id() -> str | None:
|
|
35
|
+
with contextlib.suppress(FileNotFoundError):
|
|
36
|
+
with open('/proc/1/mountinfo', 'rb') as f:
|
|
37
|
+
for line in f:
|
|
38
|
+
m = _HOSTNAME_MOUNT_RE.search(line)
|
|
39
|
+
if m:
|
|
40
|
+
return m[1].decode()
|
|
41
|
+
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _get_docker_path(path: str) -> str:
|
|
46
|
+
container_id = _get_container_id()
|
|
47
|
+
if container_id is None:
|
|
48
|
+
return path
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
_, out, _ = cmd_output_b('docker', 'inspect', container_id)
|
|
52
|
+
except CalledProcessError:
|
|
53
|
+
# self-container was not visible from here (perhaps docker-in-docker)
|
|
54
|
+
return path
|
|
55
|
+
|
|
56
|
+
container, = json.loads(out)
|
|
57
|
+
for mount in container['Mounts']:
|
|
58
|
+
src_path = mount['Source']
|
|
59
|
+
to_path = mount['Destination']
|
|
60
|
+
if os.path.commonpath((path, to_path)) == to_path:
|
|
61
|
+
# So there is something in common,
|
|
62
|
+
# and we can proceed remapping it
|
|
63
|
+
return path.replace(to_path, src_path)
|
|
64
|
+
# we're in Docker, but the path is not mounted, cannot really do anything,
|
|
65
|
+
# so fall back to original path
|
|
66
|
+
return path
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def md5(s: str) -> str: # pragma: win32 no cover
|
|
70
|
+
return hashlib.md5(s.encode()).hexdigest()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def docker_tag(prefix: Prefix) -> str: # pragma: win32 no cover
|
|
74
|
+
md5sum = md5(os.path.basename(prefix.prefix_dir)).lower()
|
|
75
|
+
return f'pre-commit-{md5sum}'
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def build_docker_image(
|
|
79
|
+
prefix: Prefix,
|
|
80
|
+
*,
|
|
81
|
+
pull: bool,
|
|
82
|
+
) -> None: # pragma: win32 no cover
|
|
83
|
+
cmd: tuple[str, ...] = (
|
|
84
|
+
'docker', 'build',
|
|
85
|
+
'--tag', docker_tag(prefix),
|
|
86
|
+
'--label', PRE_COMMIT_LABEL,
|
|
87
|
+
)
|
|
88
|
+
if pull:
|
|
89
|
+
cmd += ('--pull',)
|
|
90
|
+
# This must come last for old versions of docker. See #477
|
|
91
|
+
cmd += ('.',)
|
|
92
|
+
lang_base.setup_cmd(prefix, cmd)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def install_environment(
|
|
96
|
+
prefix: Prefix, version: str, additional_dependencies: Sequence[str],
|
|
97
|
+
) -> None: # pragma: win32 no cover
|
|
98
|
+
lang_base.assert_version_default('docker', version)
|
|
99
|
+
lang_base.assert_no_additional_deps('docker', additional_dependencies)
|
|
100
|
+
|
|
101
|
+
directory = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
|
102
|
+
|
|
103
|
+
# Docker doesn't really have relevant disk environment, but pre-commit
|
|
104
|
+
# still needs to cleanup its state files on failure
|
|
105
|
+
build_docker_image(prefix, pull=True)
|
|
106
|
+
os.mkdir(directory)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@functools.lru_cache(maxsize=1)
|
|
110
|
+
def _is_rootless() -> bool: # pragma: win32 no cover
|
|
111
|
+
retcode, out, _ = cmd_output_b(
|
|
112
|
+
'docker', 'system', 'info', '--format', '{{ json . }}',
|
|
113
|
+
)
|
|
114
|
+
if retcode != 0:
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
info = json.loads(out)
|
|
118
|
+
try:
|
|
119
|
+
return (
|
|
120
|
+
# docker:
|
|
121
|
+
# https://docs.docker.com/reference/api/engine/version/v1.48/#tag/System/operation/SystemInfo
|
|
122
|
+
'name=rootless' in (info.get('SecurityOptions') or ()) or
|
|
123
|
+
# podman:
|
|
124
|
+
# https://docs.podman.io/en/latest/_static/api.html?version=v5.4#tag/system/operation/SystemInfoLibpod
|
|
125
|
+
info['host']['security']['rootless']
|
|
126
|
+
)
|
|
127
|
+
except KeyError:
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def get_docker_user() -> tuple[str, ...]: # pragma: win32 no cover
|
|
132
|
+
if _is_rootless():
|
|
133
|
+
return ()
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
return ('-u', f'{os.getuid()}:{os.getgid()}')
|
|
137
|
+
except AttributeError:
|
|
138
|
+
return ()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def get_docker_tty(*, color: bool) -> tuple[str, ...]: # pragma: win32 no cover # noqa: E501
|
|
142
|
+
return (('--tty',) if color else ())
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def docker_cmd(*, color: bool) -> tuple[str, ...]: # pragma: win32 no cover
|
|
146
|
+
return (
|
|
147
|
+
'docker', 'run',
|
|
148
|
+
'--rm',
|
|
149
|
+
*get_docker_tty(color=color),
|
|
150
|
+
*get_docker_user(),
|
|
151
|
+
# https://docs.docker.com/engine/reference/commandline/run/#mount-volumes-from-container-volumes-from
|
|
152
|
+
# The `Z` option tells Docker to label the content with a private
|
|
153
|
+
# unshared label. Only the current container can use a private volume.
|
|
154
|
+
'-v', f'{_get_docker_path(os.getcwd())}:/src:rw,Z',
|
|
155
|
+
'--workdir', '/src',
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def run_hook(
|
|
160
|
+
prefix: Prefix,
|
|
161
|
+
entry: str,
|
|
162
|
+
args: Sequence[str],
|
|
163
|
+
file_args: Sequence[str],
|
|
164
|
+
*,
|
|
165
|
+
is_local: bool,
|
|
166
|
+
require_serial: bool,
|
|
167
|
+
color: bool,
|
|
168
|
+
) -> tuple[int, bytes]: # pragma: win32 no cover
|
|
169
|
+
# Rebuild the docker image in case it has gone missing, as many people do
|
|
170
|
+
# automated cleanup of docker images.
|
|
171
|
+
build_docker_image(prefix, pull=False)
|
|
172
|
+
|
|
173
|
+
entry_exe, *cmd_rest = lang_base.hook_cmd(entry, args)
|
|
174
|
+
|
|
175
|
+
entry_tag = ('--entrypoint', entry_exe, docker_tag(prefix))
|
|
176
|
+
return lang_base.run_xargs(
|
|
177
|
+
(*docker_cmd(color=color), *entry_tag, *cmd_rest),
|
|
178
|
+
file_args,
|
|
179
|
+
require_serial=require_serial,
|
|
180
|
+
color=color,
|
|
181
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
|
|
5
|
+
from pre_commit import lang_base
|
|
6
|
+
from pre_commit.languages.docker import docker_cmd
|
|
7
|
+
from pre_commit.prefix import Prefix
|
|
8
|
+
|
|
9
|
+
ENVIRONMENT_DIR = None
|
|
10
|
+
get_default_version = lang_base.basic_get_default_version
|
|
11
|
+
health_check = lang_base.basic_health_check
|
|
12
|
+
install_environment = lang_base.no_install
|
|
13
|
+
in_env = lang_base.no_env
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run_hook(
|
|
17
|
+
prefix: Prefix,
|
|
18
|
+
entry: str,
|
|
19
|
+
args: Sequence[str],
|
|
20
|
+
file_args: Sequence[str],
|
|
21
|
+
*,
|
|
22
|
+
is_local: bool,
|
|
23
|
+
require_serial: bool,
|
|
24
|
+
color: bool,
|
|
25
|
+
) -> tuple[int, bytes]: # pragma: win32 no cover
|
|
26
|
+
cmd = docker_cmd(color=color) + lang_base.hook_cmd(entry, args)
|
|
27
|
+
return lang_base.run_xargs(
|
|
28
|
+
cmd,
|
|
29
|
+
file_args,
|
|
30
|
+
require_serial=require_serial,
|
|
31
|
+
color=color,
|
|
32
|
+
)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import os.path
|
|
5
|
+
import re
|
|
6
|
+
import tempfile
|
|
7
|
+
import xml.etree.ElementTree
|
|
8
|
+
import zipfile
|
|
9
|
+
from collections.abc import Generator
|
|
10
|
+
from collections.abc import Sequence
|
|
11
|
+
|
|
12
|
+
from pre_commit import lang_base
|
|
13
|
+
from pre_commit.envcontext import envcontext
|
|
14
|
+
from pre_commit.envcontext import PatchesT
|
|
15
|
+
from pre_commit.envcontext import Var
|
|
16
|
+
from pre_commit.prefix import Prefix
|
|
17
|
+
|
|
18
|
+
ENVIRONMENT_DIR = 'dotnetenv'
|
|
19
|
+
BIN_DIR = 'bin'
|
|
20
|
+
|
|
21
|
+
get_default_version = lang_base.basic_get_default_version
|
|
22
|
+
health_check = lang_base.basic_health_check
|
|
23
|
+
run_hook = lang_base.basic_run_hook
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_env_patch(venv: str) -> PatchesT:
|
|
27
|
+
return (
|
|
28
|
+
('PATH', (os.path.join(venv, BIN_DIR), os.pathsep, Var('PATH'))),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@contextlib.contextmanager
|
|
33
|
+
def in_env(prefix: Prefix, version: str) -> Generator[None]:
|
|
34
|
+
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
|
35
|
+
with envcontext(get_env_patch(envdir)):
|
|
36
|
+
yield
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@contextlib.contextmanager
|
|
40
|
+
def _nuget_config_no_sources() -> Generator[str]:
|
|
41
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
42
|
+
nuget_config = os.path.join(tmpdir, 'nuget.config')
|
|
43
|
+
with open(nuget_config, 'w') as f:
|
|
44
|
+
f.write(
|
|
45
|
+
'<?xml version="1.0" encoding="utf-8"?>'
|
|
46
|
+
'<configuration>'
|
|
47
|
+
' <packageSources>'
|
|
48
|
+
' <clear />'
|
|
49
|
+
' </packageSources>'
|
|
50
|
+
'</configuration>',
|
|
51
|
+
)
|
|
52
|
+
yield nuget_config
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def install_environment(
|
|
56
|
+
prefix: Prefix,
|
|
57
|
+
version: str,
|
|
58
|
+
additional_dependencies: Sequence[str],
|
|
59
|
+
) -> None:
|
|
60
|
+
lang_base.assert_version_default('dotnet', version)
|
|
61
|
+
lang_base.assert_no_additional_deps('dotnet', additional_dependencies)
|
|
62
|
+
|
|
63
|
+
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
|
64
|
+
build_dir = prefix.path('pre-commit-build')
|
|
65
|
+
|
|
66
|
+
# Build & pack nupkg file
|
|
67
|
+
lang_base.setup_cmd(
|
|
68
|
+
prefix,
|
|
69
|
+
(
|
|
70
|
+
'dotnet', 'pack',
|
|
71
|
+
'--configuration', 'Release',
|
|
72
|
+
'--property', f'PackageOutputPath={build_dir}',
|
|
73
|
+
),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
nupkg_dir = prefix.path(build_dir)
|
|
77
|
+
nupkgs = [x for x in os.listdir(nupkg_dir) if x.endswith('.nupkg')]
|
|
78
|
+
|
|
79
|
+
if not nupkgs:
|
|
80
|
+
raise AssertionError('could not find any build outputs to install')
|
|
81
|
+
|
|
82
|
+
for nupkg in nupkgs:
|
|
83
|
+
with zipfile.ZipFile(os.path.join(nupkg_dir, nupkg)) as f:
|
|
84
|
+
nuspec, = (x for x in f.namelist() if x.endswith('.nuspec'))
|
|
85
|
+
with f.open(nuspec) as spec:
|
|
86
|
+
tree = xml.etree.ElementTree.parse(spec)
|
|
87
|
+
|
|
88
|
+
namespace = re.match(r'{.*}', tree.getroot().tag)
|
|
89
|
+
if not namespace:
|
|
90
|
+
raise AssertionError('could not parse namespace from nuspec')
|
|
91
|
+
|
|
92
|
+
tool_id_element = tree.find(f'.//{namespace[0]}id')
|
|
93
|
+
if tool_id_element is None:
|
|
94
|
+
raise AssertionError('expected to find an "id" element')
|
|
95
|
+
|
|
96
|
+
tool_id = tool_id_element.text
|
|
97
|
+
if not tool_id:
|
|
98
|
+
raise AssertionError('"id" element missing tool name')
|
|
99
|
+
|
|
100
|
+
# Install to bin dir
|
|
101
|
+
with _nuget_config_no_sources() as nuget_config:
|
|
102
|
+
lang_base.setup_cmd(
|
|
103
|
+
prefix,
|
|
104
|
+
(
|
|
105
|
+
'dotnet', 'tool', 'install',
|
|
106
|
+
'--configfile', nuget_config,
|
|
107
|
+
'--tool-path', os.path.join(envdir, BIN_DIR),
|
|
108
|
+
'--add-source', build_dir,
|
|
109
|
+
tool_id,
|
|
110
|
+
),
|
|
111
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
|
|
5
|
+
from pre_commit import lang_base
|
|
6
|
+
from pre_commit.prefix import Prefix
|
|
7
|
+
|
|
8
|
+
ENVIRONMENT_DIR = None
|
|
9
|
+
get_default_version = lang_base.basic_get_default_version
|
|
10
|
+
health_check = lang_base.basic_health_check
|
|
11
|
+
install_environment = lang_base.no_install
|
|
12
|
+
in_env = lang_base.no_env
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def run_hook(
|
|
16
|
+
prefix: Prefix,
|
|
17
|
+
entry: str,
|
|
18
|
+
args: Sequence[str],
|
|
19
|
+
file_args: Sequence[str],
|
|
20
|
+
*,
|
|
21
|
+
is_local: bool,
|
|
22
|
+
require_serial: bool,
|
|
23
|
+
color: bool,
|
|
24
|
+
) -> tuple[int, bytes]:
|
|
25
|
+
out = f'{entry}\n\n'.encode()
|
|
26
|
+
out += b'\n'.join(f.encode() for f in file_args) + b'\n'
|
|
27
|
+
return 1, out
|