Merging upstream version 3.0.2.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
962b6a60c2
commit
3904671ae3
107 changed files with 1775 additions and 2323 deletions
40
.github/actions/pre-test/action.yml
vendored
Normal file
40
.github/actions/pre-test/action.yml
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
inputs:
|
||||||
|
env:
|
||||||
|
default: ${{ matrix.env }}
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: setup (windows)
|
||||||
|
shell: bash
|
||||||
|
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'
|
23
.github/workflows/main.yml
vendored
Normal file
23
.github/workflows/main.yml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
name: main
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, test-me-*]
|
||||||
|
tags:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
main-windows:
|
||||||
|
uses: asottile/workflows/.github/workflows/tox.yml@v1.2.0
|
||||||
|
with:
|
||||||
|
env: '["py38"]'
|
||||||
|
os: windows-latest
|
||||||
|
main-linux:
|
||||||
|
uses: asottile/workflows/.github/workflows/tox.yml@v1.2.0
|
||||||
|
with:
|
||||||
|
env: '["py38", "py39", "py310"]'
|
||||||
|
os: ubuntu-latest
|
|
@ -18,7 +18,7 @@ repos:
|
||||||
hooks:
|
hooks:
|
||||||
- id: reorder-python-imports
|
- id: reorder-python-imports
|
||||||
exclude: ^(pre_commit/resources/|testing/resources/python3_hooks_repo/)
|
exclude: ^(pre_commit/resources/|testing/resources/python3_hooks_repo/)
|
||||||
args: [--py37-plus, --add-import, 'from __future__ import annotations']
|
args: [--py38-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: v2.4.0
|
||||||
hooks:
|
hooks:
|
||||||
|
@ -28,7 +28,7 @@ repos:
|
||||||
rev: v3.3.1
|
rev: v3.3.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py37-plus]
|
args: [--py38-plus]
|
||||||
- repo: https://github.com/pre-commit/mirrors-autopep8
|
- repo: https://github.com/pre-commit/mirrors-autopep8
|
||||||
rev: v2.0.1
|
rev: v2.0.1
|
||||||
hooks:
|
hooks:
|
||||||
|
|
51
CHANGELOG.md
51
CHANGELOG.md
|
@ -1,3 +1,54 @@
|
||||||
|
3.0.2 - 2023-01-29
|
||||||
|
==================
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Prevent local `Gemfile` from interfering with hook execution.
|
||||||
|
- #2727 PR by @asottile.
|
||||||
|
- Fix `language: r`, `repo: local` hooks
|
||||||
|
- pre-commit-ci/issues#107 by @lorenzwalthert.
|
||||||
|
- #2728 PR by @asottile.
|
||||||
|
|
||||||
|
3.0.1 - 2023-01-26
|
||||||
|
==================
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Ensure coursier hooks are available offline after install.
|
||||||
|
- #2723 PR by @asottile.
|
||||||
|
|
||||||
|
3.0.0 - 2023-01-23
|
||||||
|
==================
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Make `language: golang` bootstrap `go` if not present.
|
||||||
|
- #2651 PR by @taoufik07.
|
||||||
|
- #2649 issue by @taoufik07.
|
||||||
|
- `language: coursier` now supports `additional_dependencies` and `repo: local`
|
||||||
|
- #2702 PR by @asottile.
|
||||||
|
- Upgrade `ruby-build` to `20221225`.
|
||||||
|
- #2718 PR by @jalessio.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Improve error message for invalid yaml for `pre-commit autoupdate`.
|
||||||
|
- #2686 PR by @asottile.
|
||||||
|
- #2685 issue by @CarstenGrohmann.
|
||||||
|
- `repo: local` no longer provisions an empty `git` repo.
|
||||||
|
- #2699 PR by @asottile.
|
||||||
|
|
||||||
|
### Updating
|
||||||
|
- Drop support for python<3.8
|
||||||
|
- #2655 PR by @asottile.
|
||||||
|
- Drop support for top-level list, use `pre-commit migrate-config` to update.
|
||||||
|
- #2656 PR by @asottile.
|
||||||
|
- Drop support for `sha` to specify revision, use `pre-commit migrate-config`
|
||||||
|
to update.
|
||||||
|
- #2657 PR by @asottile.
|
||||||
|
- Remove `pre-commit-validate-config` and `pre-commit-validate-manifest`, use
|
||||||
|
`pre-commit validate-config` and `pre-commit validate-manifest` instead.
|
||||||
|
- #2658 PR by @asottile.
|
||||||
|
- `language: golang` hooks must use `go.mod` to specify dependencies
|
||||||
|
- #2672 PR by @taoufik07.
|
||||||
|
|
||||||
|
|
||||||
2.21.0 - 2022-12-25
|
2.21.0 - 2022-12-25
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
[![Build Status](https://dev.azure.com/asottile/asottile/_apis/build/status/pre-commit.pre-commit?branchName=main)](https://dev.azure.com/asottile/asottile/_build/latest?definitionId=21&branchName=main)
|
[![build status](https://github.com/pre-commit/pre-commit/actions/workflows/main.yml/badge.svg)](https://github.com/pre-commit/pre-commit/actions/workflows/main.yml)
|
||||||
[![Azure DevOps coverage](https://img.shields.io/azure-devops/coverage/asottile/asottile/21/main.svg)](https://dev.azure.com/asottile/asottile/_build/latest?definitionId=21&branchName=main)
|
|
||||||
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/pre-commit/pre-commit/main.svg)](https://results.pre-commit.ci/latest/github/pre-commit/pre-commit/main)
|
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/pre-commit/pre-commit/main.svg)](https://results.pre-commit.ci/latest/github/pre-commit/pre-commit/main)
|
||||||
|
|
||||||
## pre-commit
|
## pre-commit
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
trigger:
|
|
||||||
branches:
|
|
||||||
include: [main, test-me-*]
|
|
||||||
tags:
|
|
||||||
include: ['*']
|
|
||||||
|
|
||||||
resources:
|
|
||||||
repositories:
|
|
||||||
- repository: asottile
|
|
||||||
type: github
|
|
||||||
endpoint: github
|
|
||||||
name: asottile/azure-pipeline-templates
|
|
||||||
ref: refs/tags/v2.4.1
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- template: job--python-tox.yml@asottile
|
|
||||||
parameters:
|
|
||||||
toxenvs: [py37]
|
|
||||||
os: windows
|
|
||||||
additional_variables:
|
|
||||||
TEMP: C:\Temp
|
|
||||||
pre_test:
|
|
||||||
- task: UseRubyVersion@0
|
|
||||||
- powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts"
|
|
||||||
displayName: Add conda to PATH
|
|
||||||
- powershell: |
|
|
||||||
Write-Host "##vso[task.prependpath]C:\Strawberry\perl\bin"
|
|
||||||
Write-Host "##vso[task.prependpath]C:\Strawberry\perl\site\bin"
|
|
||||||
Write-Host "##vso[task.prependpath]C:\Strawberry\c\bin"
|
|
||||||
displayName: Add strawberry perl to PATH
|
|
||||||
- bash: testing/get-dart.sh
|
|
||||||
displayName: install dart
|
|
||||||
- powershell: testing/get-r.ps1
|
|
||||||
displayName: install R
|
|
||||||
- template: job--python-tox.yml@asottile
|
|
||||||
parameters:
|
|
||||||
toxenvs: [py37]
|
|
||||||
os: linux
|
|
||||||
name_postfix: _latest_git
|
|
||||||
pre_test:
|
|
||||||
- task: UseRubyVersion@0
|
|
||||||
- template: step--git-install.yml
|
|
||||||
- bash: testing/get-coursier.sh
|
|
||||||
displayName: install coursier
|
|
||||||
- bash: testing/get-dart.sh
|
|
||||||
displayName: install dart
|
|
||||||
- bash: testing/get-lua.sh
|
|
||||||
displayName: install lua
|
|
||||||
- bash: testing/get-swift.sh
|
|
||||||
displayName: install swift
|
|
||||||
- bash: testing/get-r.sh
|
|
||||||
displayName: install R
|
|
||||||
- template: job--python-tox.yml@asottile
|
|
||||||
parameters:
|
|
||||||
toxenvs: [py37, py38, py39]
|
|
||||||
os: linux
|
|
||||||
pre_test:
|
|
||||||
- task: UseRubyVersion@0
|
|
||||||
- bash: testing/get-coursier.sh
|
|
||||||
displayName: install coursier
|
|
||||||
- bash: testing/get-dart.sh
|
|
||||||
displayName: install dart
|
|
||||||
- bash: testing/get-lua.sh
|
|
||||||
displayName: install lua
|
|
||||||
- bash: testing/get-swift.sh
|
|
||||||
displayName: install swift
|
|
||||||
- bash: testing/get-r.sh
|
|
||||||
displayName: install R
|
|
|
@ -1,6 +1,5 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
@ -13,14 +12,9 @@ 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.color import add_color_option
|
|
||||||
from pre_commit.commands.validate_config import validate_config
|
|
||||||
from pre_commit.commands.validate_manifest import validate_manifest
|
|
||||||
from pre_commit.errors import FatalError
|
from pre_commit.errors import FatalError
|
||||||
from pre_commit.languages.all import all_languages
|
from pre_commit.languages.all import all_languages
|
||||||
from pre_commit.logging_handler import logging_handler
|
from pre_commit.yaml import yaml_load
|
||||||
from pre_commit.util import parse_version
|
|
||||||
from pre_commit.util import yaml_load
|
|
||||||
|
|
||||||
logger = logging.getLogger('pre_commit')
|
logger = logging.getLogger('pre_commit')
|
||||||
|
|
||||||
|
@ -35,6 +29,11 @@ def check_type_tag(tag: str) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_version(s: str) -> tuple[int, ...]:
|
||||||
|
"""poor man's version comparison"""
|
||||||
|
return tuple(int(p) for p in s.split('.'))
|
||||||
|
|
||||||
|
|
||||||
def check_min_version(version: str) -> None:
|
def check_min_version(version: str) -> None:
|
||||||
if parse_version(version) > parse_version(C.VERSION):
|
if parse_version(version) > parse_version(C.VERSION):
|
||||||
raise cfgv.ValidationError(
|
raise cfgv.ValidationError(
|
||||||
|
@ -44,14 +43,6 @@ def check_min_version(version: str) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _make_argparser(filenames_help: str) -> argparse.ArgumentParser:
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument('filenames', nargs='*', help=filenames_help)
|
|
||||||
parser.add_argument('-V', '--version', action='version', version=C.VERSION)
|
|
||||||
add_color_option(parser)
|
|
||||||
return parser
|
|
||||||
|
|
||||||
|
|
||||||
MANIFEST_HOOK_DICT = cfgv.Map(
|
MANIFEST_HOOK_DICT = cfgv.Map(
|
||||||
'Hook', 'id',
|
'Hook', 'id',
|
||||||
|
|
||||||
|
@ -97,25 +88,11 @@ load_manifest = functools.partial(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_manifest_main(argv: Sequence[str] | None = None) -> int:
|
|
||||||
parser = _make_argparser('Manifest filenames.')
|
|
||||||
args = parser.parse_args(argv)
|
|
||||||
|
|
||||||
with logging_handler(args.color):
|
|
||||||
logger.warning(
|
|
||||||
'pre-commit-validate-manifest is deprecated -- '
|
|
||||||
'use `pre-commit validate-manifest` instead.',
|
|
||||||
)
|
|
||||||
|
|
||||||
return validate_manifest(args.filenames)
|
|
||||||
|
|
||||||
|
|
||||||
LOCAL = 'local'
|
LOCAL = 'local'
|
||||||
META = 'meta'
|
META = 'meta'
|
||||||
|
|
||||||
|
|
||||||
# should inherit from cfgv.Conditional if sha support is dropped
|
class WarnMutableRev(cfgv.Conditional):
|
||||||
class WarnMutableRev(cfgv.ConditionalOptional):
|
|
||||||
def check(self, dct: dict[str, Any]) -> None:
|
def check(self, dct: dict[str, Any]) -> None:
|
||||||
super().check(dct)
|
super().check(dct)
|
||||||
|
|
||||||
|
@ -171,36 +148,6 @@ class OptionalSensibleRegexAtTop(cfgv.OptionalNoDefault):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MigrateShaToRev:
|
|
||||||
key = 'rev'
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _cond(key: str) -> cfgv.Conditional:
|
|
||||||
return cfgv.Conditional(
|
|
||||||
key, cfgv.check_string,
|
|
||||||
condition_key='repo',
|
|
||||||
condition_value=cfgv.NotIn(LOCAL, META),
|
|
||||||
ensure_absent=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def check(self, dct: dict[str, Any]) -> None:
|
|
||||||
if dct.get('repo') in {LOCAL, META}:
|
|
||||||
self._cond('rev').check(dct)
|
|
||||||
self._cond('sha').check(dct)
|
|
||||||
elif 'sha' in dct and 'rev' in dct:
|
|
||||||
raise cfgv.ValidationError('Cannot specify both sha and rev')
|
|
||||||
elif 'sha' in dct:
|
|
||||||
self._cond('sha').check(dct)
|
|
||||||
else:
|
|
||||||
self._cond('rev').check(dct)
|
|
||||||
|
|
||||||
def apply_default(self, dct: dict[str, Any]) -> None:
|
|
||||||
if 'sha' in dct:
|
|
||||||
dct['rev'] = dct.pop('sha')
|
|
||||||
|
|
||||||
remove_default = cfgv.Required.remove_default
|
|
||||||
|
|
||||||
|
|
||||||
def _entry(modname: str) -> str:
|
def _entry(modname: str) -> str:
|
||||||
"""the hook `entry` is passed through `shlex.split()` by the command
|
"""the hook `entry` is passed through `shlex.split()` by the command
|
||||||
runner, so to prevent issues with spaces and backslashes (on Windows)
|
runner, so to prevent issues with spaces and backslashes (on Windows)
|
||||||
|
@ -324,14 +271,11 @@ CONFIG_REPO_DICT = cfgv.Map(
|
||||||
'repo', META,
|
'repo', META,
|
||||||
),
|
),
|
||||||
|
|
||||||
MigrateShaToRev(),
|
|
||||||
WarnMutableRev(
|
WarnMutableRev(
|
||||||
'rev',
|
'rev', cfgv.check_string,
|
||||||
cfgv.check_string,
|
condition_key='repo',
|
||||||
'',
|
condition_value=cfgv.NotIn(LOCAL, META),
|
||||||
'repo',
|
ensure_absent=True,
|
||||||
cfgv.NotIn(LOCAL, META),
|
|
||||||
True,
|
|
||||||
),
|
),
|
||||||
cfgv.WarnAdditionalKeys(('repo', 'rev', 'hooks'), warn_unknown_keys_repo),
|
cfgv.WarnAdditionalKeys(('repo', 'rev', 'hooks'), warn_unknown_keys_repo),
|
||||||
)
|
)
|
||||||
|
@ -391,35 +335,9 @@ class InvalidConfigError(FatalError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def ordered_load_normalize_legacy_config(contents: str) -> dict[str, Any]:
|
|
||||||
data = yaml_load(contents)
|
|
||||||
if isinstance(data, list):
|
|
||||||
logger.warning(
|
|
||||||
'normalizing pre-commit configuration to a top-level map. '
|
|
||||||
'support for top level list will be removed in a future version. '
|
|
||||||
'run: `pre-commit migrate-config` to automatically fix this.',
|
|
||||||
)
|
|
||||||
return {'repos': data}
|
|
||||||
else:
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
load_config = functools.partial(
|
load_config = functools.partial(
|
||||||
cfgv.load_from_filename,
|
cfgv.load_from_filename,
|
||||||
schema=CONFIG_SCHEMA,
|
schema=CONFIG_SCHEMA,
|
||||||
load_strategy=ordered_load_normalize_legacy_config,
|
load_strategy=yaml_load,
|
||||||
exc_tp=InvalidConfigError,
|
exc_tp=InvalidConfigError,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_config_main(argv: Sequence[str] | None = None) -> int:
|
|
||||||
parser = _make_argparser('Config filenames.')
|
|
||||||
args = parser.parse_args(argv)
|
|
||||||
|
|
||||||
with logging_handler(args.color):
|
|
||||||
logger.warning(
|
|
||||||
'pre-commit-validate-config is deprecated -- '
|
|
||||||
'use `pre-commit validate-config` instead.',
|
|
||||||
)
|
|
||||||
|
|
||||||
return validate_config(args.filenames)
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
|
import tempfile
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
@ -19,9 +20,8 @@ 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
|
||||||
from pre_commit.util import tmpdir
|
from pre_commit.yaml import yaml_dump
|
||||||
from pre_commit.util import yaml_dump
|
from pre_commit.yaml import yaml_load
|
||||||
from pre_commit.util import yaml_load
|
|
||||||
|
|
||||||
|
|
||||||
class RevInfo(NamedTuple):
|
class RevInfo(NamedTuple):
|
||||||
|
@ -47,7 +47,7 @@ class RevInfo(NamedTuple):
|
||||||
'FETCH_HEAD', '--tags', '--exact',
|
'FETCH_HEAD', '--tags', '--exact',
|
||||||
)
|
)
|
||||||
|
|
||||||
with tmpdir() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
git.init_repo(tmp, self.repo)
|
git.init_repo(tmp, self.repo)
|
||||||
cmd_output_b(
|
cmd_output_b(
|
||||||
*git_cmd, 'fetch', 'origin', 'HEAD', '--tags',
|
*git_cmd, 'fetch', 'origin', 'HEAD', '--tags',
|
||||||
|
|
|
@ -3,9 +3,11 @@ from __future__ import annotations
|
||||||
import re
|
import re
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
|
import cfgv
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from pre_commit.util import yaml_load
|
from pre_commit.clientlib import InvalidConfigError
|
||||||
|
from pre_commit.yaml import yaml_load
|
||||||
|
|
||||||
|
|
||||||
def _is_header_line(line: str) -> bool:
|
def _is_header_line(line: str) -> bool:
|
||||||
|
@ -44,6 +46,13 @@ def migrate_config(config_file: str, quiet: bool = False) -> int:
|
||||||
with open(config_file) as f:
|
with open(config_file) as f:
|
||||||
orig_contents = contents = f.read()
|
orig_contents = contents = f.read()
|
||||||
|
|
||||||
|
with cfgv.reraise_as(InvalidConfigError):
|
||||||
|
with cfgv.validate_context(f'File {config_file}'):
|
||||||
|
try:
|
||||||
|
yaml_load(orig_contents)
|
||||||
|
except Exception as e:
|
||||||
|
raise cfgv.ValidationError(str(e))
|
||||||
|
|
||||||
contents = _migrate_map(contents)
|
contents = _migrate_map(contents)
|
||||||
contents = _migrate_sha_to_rev(contents)
|
contents = _migrate_sha_to_rev(contents)
|
||||||
|
|
||||||
|
|
|
@ -189,7 +189,16 @@ def _run_single_hook(
|
||||||
filenames = ()
|
filenames = ()
|
||||||
time_before = time.time()
|
time_before = time.time()
|
||||||
language = languages[hook.language]
|
language = languages[hook.language]
|
||||||
retcode, out = language.run_hook(hook, filenames, use_color)
|
with language.in_env(hook.prefix, hook.language_version):
|
||||||
|
retcode, out = language.run_hook(
|
||||||
|
hook.prefix,
|
||||||
|
hook.entry,
|
||||||
|
hook.args,
|
||||||
|
filenames,
|
||||||
|
is_local=hook.src == 'local',
|
||||||
|
require_serial=hook.require_serial,
|
||||||
|
color=use_color,
|
||||||
|
)
|
||||||
duration = round(time.time() - time_before, 2) or 0
|
duration = round(time.time() - time_before, 2) or 0
|
||||||
diff_after = _get_diff()
|
diff_after = _get_diff()
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
|
import tempfile
|
||||||
|
|
||||||
import pre_commit.constants as C
|
import pre_commit.constants as C
|
||||||
from pre_commit import git
|
from pre_commit import git
|
||||||
|
@ -11,9 +12,8 @@ from pre_commit.clientlib import load_manifest
|
||||||
from pre_commit.commands.run import run
|
from pre_commit.commands.run import run
|
||||||
from pre_commit.store import Store
|
from pre_commit.store import Store
|
||||||
from pre_commit.util import cmd_output_b
|
from pre_commit.util import cmd_output_b
|
||||||
from pre_commit.util import tmpdir
|
|
||||||
from pre_commit.util import yaml_dump
|
|
||||||
from pre_commit.xargs import xargs
|
from pre_commit.xargs import xargs
|
||||||
|
from pre_commit.yaml import yaml_dump
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ def _repo_ref(tmpdir: str, repo: str, ref: str | None) -> tuple[str, str]:
|
||||||
|
|
||||||
|
|
||||||
def try_repo(args: argparse.Namespace) -> int:
|
def try_repo(args: argparse.Namespace) -> int:
|
||||||
with tmpdir() as tempdir:
|
with tempfile.TemporaryDirectory() as tempdir:
|
||||||
repo, ref = _repo_ref(tempdir, args.repo, args.ref)
|
repo, ref = _repo_ref(tempdir, args.repo, args.ref)
|
||||||
|
|
||||||
store = Store(tempdir)
|
store = Store(tempdir)
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit import clientlib
|
from pre_commit import clientlib
|
||||||
|
|
||||||
|
|
||||||
def validate_config(filenames: list[str]) -> int:
|
def validate_config(filenames: Sequence[str]) -> int:
|
||||||
ret = 0
|
ret = 0
|
||||||
|
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit import clientlib
|
from pre_commit import clientlib
|
||||||
|
|
||||||
|
|
||||||
def validate_manifest(filenames: list[str]) -> int:
|
def validate_manifest(filenames: Sequence[str]) -> int:
|
||||||
ret = 0
|
ret = 0
|
||||||
|
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
|
|
|
@ -1,21 +1,14 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import sys
|
import importlib.metadata
|
||||||
|
|
||||||
if sys.version_info >= (3, 8): # pragma: >=3.8 cover
|
|
||||||
import importlib.metadata as importlib_metadata
|
|
||||||
else: # pragma: <3.8 cover
|
|
||||||
import importlib_metadata
|
|
||||||
|
|
||||||
CONFIG_FILE = '.pre-commit-config.yaml'
|
CONFIG_FILE = '.pre-commit-config.yaml'
|
||||||
MANIFEST_FILE = '.pre-commit-hooks.yaml'
|
MANIFEST_FILE = '.pre-commit-hooks.yaml'
|
||||||
|
|
||||||
# Bump when installation changes in a backwards / forwards incompatible way
|
|
||||||
INSTALLED_STATE_VERSION = '1'
|
|
||||||
# Bump when modifying `empty_template`
|
# Bump when modifying `empty_template`
|
||||||
LOCAL_REPO_VERSION = '1'
|
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
|
# `manual` is not invoked by any installed git hook. See #719
|
||||||
STAGES = (
|
STAGES = (
|
||||||
|
|
|
@ -93,11 +93,6 @@ def get_git_common_dir(git_root: str = '.') -> str:
|
||||||
return get_git_dir(git_root)
|
return get_git_dir(git_root)
|
||||||
|
|
||||||
|
|
||||||
def get_remote_url(git_root: str) -> str:
|
|
||||||
_, out, _ = cmd_output('git', 'config', 'remote.origin.url', cwd=git_root)
|
|
||||||
return out.strip()
|
|
||||||
|
|
||||||
|
|
||||||
def is_in_merge_conflict() -> bool:
|
def is_in_merge_conflict() -> bool:
|
||||||
git_dir = get_git_dir('.')
|
git_dir = get_git_dir('.')
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import shlex
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
@ -37,10 +36,6 @@ class Hook(NamedTuple):
|
||||||
stages: Sequence[str]
|
stages: Sequence[str]
|
||||||
verbose: bool
|
verbose: bool
|
||||||
|
|
||||||
@property
|
|
||||||
def cmd(self) -> tuple[str, ...]:
|
|
||||||
return (*shlex.split(self.entry), *self.args)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def install_key(self) -> tuple[Prefix, str, str, tuple[str, ...]]:
|
def install_key(self) -> tuple[Prefix, str, str, tuple[str, ...]]:
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Callable
|
from typing import ContextManager
|
||||||
from typing import NamedTuple
|
from typing import Protocol
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit.hook import Hook
|
|
||||||
from pre_commit.languages import conda
|
from pre_commit.languages import conda
|
||||||
from pre_commit.languages import coursier
|
from pre_commit.languages import coursier
|
||||||
from pre_commit.languages import dart
|
from pre_commit.languages import dart
|
||||||
|
@ -27,44 +26,74 @@ from pre_commit.languages import system
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
|
|
||||||
|
|
||||||
class Language(NamedTuple):
|
class Language(Protocol):
|
||||||
name: str
|
|
||||||
# Use `None` for no installation / environment
|
# Use `None` for no installation / environment
|
||||||
ENVIRONMENT_DIR: str | None
|
@property
|
||||||
|
def ENVIRONMENT_DIR(self) -> str | None: ...
|
||||||
# return a value to replace `'default` for `language_version`
|
# return a value to replace `'default` for `language_version`
|
||||||
get_default_version: Callable[[], str]
|
def get_default_version(self) -> str: ...
|
||||||
|
|
||||||
# return whether the environment is healthy (or should be rebuilt)
|
# return whether the environment is healthy (or should be rebuilt)
|
||||||
health_check: Callable[[Prefix, str], str | None]
|
def health_check(
|
||||||
|
self,
|
||||||
|
prefix: Prefix,
|
||||||
|
language_version: str,
|
||||||
|
) -> str | None:
|
||||||
|
...
|
||||||
|
|
||||||
# install a repository for the given language and language_version
|
# install a repository for the given language and language_version
|
||||||
install_environment: Callable[[Prefix, str, Sequence[str]], None]
|
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
|
# execute a hook and return the exit code and output
|
||||||
run_hook: Callable[[Hook, Sequence[str], bool], tuple[int, bytes]]
|
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]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
# TODO: back to modules + Protocol: https://github.com/python/mypy/issues/5018
|
languages: dict[str, Language] = {
|
||||||
languages = {
|
'conda': conda,
|
||||||
# BEGIN GENERATED (testing/gen-languages-all)
|
'coursier': coursier,
|
||||||
'conda': Language(name='conda', ENVIRONMENT_DIR=conda.ENVIRONMENT_DIR, get_default_version=conda.get_default_version, health_check=conda.health_check, install_environment=conda.install_environment, run_hook=conda.run_hook), # noqa: E501
|
'dart': dart,
|
||||||
'coursier': Language(name='coursier', ENVIRONMENT_DIR=coursier.ENVIRONMENT_DIR, get_default_version=coursier.get_default_version, health_check=coursier.health_check, install_environment=coursier.install_environment, run_hook=coursier.run_hook), # noqa: E501
|
'docker': docker,
|
||||||
'dart': Language(name='dart', ENVIRONMENT_DIR=dart.ENVIRONMENT_DIR, get_default_version=dart.get_default_version, health_check=dart.health_check, install_environment=dart.install_environment, run_hook=dart.run_hook), # noqa: E501
|
'docker_image': docker_image,
|
||||||
'docker': Language(name='docker', ENVIRONMENT_DIR=docker.ENVIRONMENT_DIR, get_default_version=docker.get_default_version, health_check=docker.health_check, install_environment=docker.install_environment, run_hook=docker.run_hook), # noqa: E501
|
'dotnet': dotnet,
|
||||||
'docker_image': Language(name='docker_image', ENVIRONMENT_DIR=docker_image.ENVIRONMENT_DIR, get_default_version=docker_image.get_default_version, health_check=docker_image.health_check, install_environment=docker_image.install_environment, run_hook=docker_image.run_hook), # noqa: E501
|
'fail': fail,
|
||||||
'dotnet': Language(name='dotnet', ENVIRONMENT_DIR=dotnet.ENVIRONMENT_DIR, get_default_version=dotnet.get_default_version, health_check=dotnet.health_check, install_environment=dotnet.install_environment, run_hook=dotnet.run_hook), # noqa: E501
|
'golang': golang,
|
||||||
'fail': Language(name='fail', ENVIRONMENT_DIR=fail.ENVIRONMENT_DIR, get_default_version=fail.get_default_version, health_check=fail.health_check, install_environment=fail.install_environment, run_hook=fail.run_hook), # noqa: E501
|
'lua': lua,
|
||||||
'golang': Language(name='golang', ENVIRONMENT_DIR=golang.ENVIRONMENT_DIR, get_default_version=golang.get_default_version, health_check=golang.health_check, install_environment=golang.install_environment, run_hook=golang.run_hook), # noqa: E501
|
'node': node,
|
||||||
'lua': Language(name='lua', ENVIRONMENT_DIR=lua.ENVIRONMENT_DIR, get_default_version=lua.get_default_version, health_check=lua.health_check, install_environment=lua.install_environment, run_hook=lua.run_hook), # noqa: E501
|
'perl': perl,
|
||||||
'node': Language(name='node', ENVIRONMENT_DIR=node.ENVIRONMENT_DIR, get_default_version=node.get_default_version, health_check=node.health_check, install_environment=node.install_environment, run_hook=node.run_hook), # noqa: E501
|
'pygrep': pygrep,
|
||||||
'perl': Language(name='perl', ENVIRONMENT_DIR=perl.ENVIRONMENT_DIR, get_default_version=perl.get_default_version, health_check=perl.health_check, install_environment=perl.install_environment, run_hook=perl.run_hook), # noqa: E501
|
'python': python,
|
||||||
'pygrep': Language(name='pygrep', ENVIRONMENT_DIR=pygrep.ENVIRONMENT_DIR, get_default_version=pygrep.get_default_version, health_check=pygrep.health_check, install_environment=pygrep.install_environment, run_hook=pygrep.run_hook), # noqa: E501
|
'r': r,
|
||||||
'python': Language(name='python', ENVIRONMENT_DIR=python.ENVIRONMENT_DIR, get_default_version=python.get_default_version, health_check=python.health_check, install_environment=python.install_environment, run_hook=python.run_hook), # noqa: E501
|
'ruby': ruby,
|
||||||
'r': Language(name='r', ENVIRONMENT_DIR=r.ENVIRONMENT_DIR, get_default_version=r.get_default_version, health_check=r.health_check, install_environment=r.install_environment, run_hook=r.run_hook), # noqa: E501
|
'rust': rust,
|
||||||
'ruby': Language(name='ruby', ENVIRONMENT_DIR=ruby.ENVIRONMENT_DIR, get_default_version=ruby.get_default_version, health_check=ruby.health_check, install_environment=ruby.install_environment, run_hook=ruby.run_hook), # noqa: E501
|
'script': script,
|
||||||
'rust': Language(name='rust', ENVIRONMENT_DIR=rust.ENVIRONMENT_DIR, get_default_version=rust.get_default_version, health_check=rust.health_check, install_environment=rust.install_environment, run_hook=rust.run_hook), # noqa: E501
|
'swift': swift,
|
||||||
'script': Language(name='script', ENVIRONMENT_DIR=script.ENVIRONMENT_DIR, get_default_version=script.get_default_version, health_check=script.health_check, install_environment=script.install_environment, run_hook=script.run_hook), # noqa: E501
|
'system': system,
|
||||||
'swift': Language(name='swift', ENVIRONMENT_DIR=swift.ENVIRONMENT_DIR, get_default_version=swift.get_default_version, health_check=swift.health_check, install_environment=swift.install_environment, run_hook=swift.run_hook), # noqa: E501
|
# TODO: fully deprecate `python_venv`
|
||||||
'system': Language(name='system', ENVIRONMENT_DIR=system.ENVIRONMENT_DIR, get_default_version=system.get_default_version, health_check=system.health_check, install_environment=system.install_environment, run_hook=system.run_hook), # noqa: E501
|
'python_venv': python,
|
||||||
# END GENERATED
|
|
||||||
}
|
}
|
||||||
# TODO: fully deprecate `python_venv`
|
|
||||||
languages['python_venv'] = languages['python']
|
|
||||||
all_languages = sorted(languages)
|
all_languages = sorted(languages)
|
||||||
|
|
|
@ -10,15 +10,14 @@ 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.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
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
|
||||||
|
|
||||||
ENVIRONMENT_DIR = 'conda'
|
ENVIRONMENT_DIR = 'conda'
|
||||||
get_default_version = helpers.basic_get_default_version
|
get_default_version = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
|
run_hook = helpers.basic_run_hook
|
||||||
|
|
||||||
|
|
||||||
def get_env_patch(env: str) -> PatchesT:
|
def get_env_patch(env: str) -> PatchesT:
|
||||||
|
@ -41,12 +40,8 @@ def get_env_patch(env: str) -> PatchesT:
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def in_env(
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
prefix: Prefix,
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
language_version: str,
|
|
||||||
) -> Generator[None, None, None]:
|
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)
|
|
||||||
envdir = prefix.path(directory)
|
|
||||||
with envcontext(get_env_patch(envdir)):
|
with envcontext(get_env_patch(envdir)):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
@ -66,12 +61,10 @@ def install_environment(
|
||||||
additional_dependencies: Sequence[str],
|
additional_dependencies: Sequence[str],
|
||||||
) -> None:
|
) -> None:
|
||||||
helpers.assert_version_default('conda', version)
|
helpers.assert_version_default('conda', version)
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
|
||||||
|
|
||||||
conda_exe = _conda_exe()
|
conda_exe = _conda_exe()
|
||||||
|
|
||||||
env_dir = prefix.path(directory)
|
env_dir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
with clean_path_on_failure(env_dir):
|
|
||||||
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,
|
||||||
|
@ -81,16 +74,3 @@ def install_environment(
|
||||||
conda_exe, 'install', '-p', env_dir, *additional_dependencies,
|
conda_exe, 'install', '-p', env_dir, *additional_dependencies,
|
||||||
cwd=prefix.prefix_dir,
|
cwd=prefix.prefix_dir,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]:
|
|
||||||
# TODO: Some rare commands need to be run using `conda run` but mostly we
|
|
||||||
# can run them without which is much quicker and produces a better
|
|
||||||
# output.
|
|
||||||
# cmd = ('conda', 'run', '-p', env_dir) + hook.cmd
|
|
||||||
with in_env(hook.prefix, hook.language_version):
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -1,81 +1,76 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import os
|
import os.path
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
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.hook import Hook
|
from pre_commit.errors import FatalError
|
||||||
from pre_commit.languages import helpers
|
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 clean_path_on_failure
|
|
||||||
|
|
||||||
ENVIRONMENT_DIR = 'coursier'
|
ENVIRONMENT_DIR = 'coursier'
|
||||||
|
|
||||||
get_default_version = helpers.basic_get_default_version
|
get_default_version = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
|
run_hook = helpers.basic_run_hook
|
||||||
|
|
||||||
|
|
||||||
def install_environment(
|
def install_environment(
|
||||||
prefix: Prefix,
|
prefix: Prefix,
|
||||||
version: str,
|
version: str,
|
||||||
additional_dependencies: Sequence[str],
|
additional_dependencies: Sequence[str],
|
||||||
) -> None: # pragma: win32 no cover
|
) -> None:
|
||||||
helpers.assert_version_default('coursier', version)
|
helpers.assert_version_default('coursier', version)
|
||||||
helpers.assert_no_additional_deps('coursier', additional_dependencies)
|
|
||||||
|
|
||||||
# Support both possible executable names (either "cs" or "coursier")
|
# Support both possible executable names (either "cs" or "coursier")
|
||||||
executable = find_executable('cs') or find_executable('coursier')
|
cs = find_executable('cs') or find_executable('coursier')
|
||||||
if executable is None:
|
if cs is None:
|
||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
'pre-commit requires system-installed "cs" or "coursier" '
|
'pre-commit requires system-installed "cs" or "coursier" '
|
||||||
'executables in the application search path',
|
'executables in the application search path',
|
||||||
)
|
)
|
||||||
|
|
||||||
envdir = prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
|
|
||||||
|
def _install(*opts: str) -> None:
|
||||||
|
assert cs is not None
|
||||||
|
helpers.run_setup_cmd(prefix, (cs, 'fetch', *opts))
|
||||||
|
helpers.run_setup_cmd(prefix, (cs, 'install', '--dir', envdir, *opts))
|
||||||
|
|
||||||
|
with in_env(prefix, version):
|
||||||
channel = prefix.path('.pre-commit-channel')
|
channel = prefix.path('.pre-commit-channel')
|
||||||
with clean_path_on_failure(envdir):
|
if os.path.isdir(channel):
|
||||||
for app_descriptor in os.listdir(channel):
|
for app_descriptor in os.listdir(channel):
|
||||||
_, app_file = os.path.split(app_descriptor)
|
_, app_file = os.path.split(app_descriptor)
|
||||||
app, _ = os.path.splitext(app_file)
|
app, _ = os.path.splitext(app_file)
|
||||||
helpers.run_setup_cmd(
|
_install(
|
||||||
prefix,
|
|
||||||
(
|
|
||||||
executable,
|
|
||||||
'install',
|
|
||||||
'--default-channels=false',
|
'--default-channels=false',
|
||||||
f'--channel={channel}',
|
'--channel', channel,
|
||||||
app,
|
app,
|
||||||
f'--dir={envdir}',
|
)
|
||||||
),
|
elif not additional_dependencies:
|
||||||
|
raise FatalError(
|
||||||
|
'expected .pre-commit-channel dir or additional_dependencies',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if additional_dependencies:
|
||||||
|
_install(*additional_dependencies)
|
||||||
|
|
||||||
def get_env_patch(target_dir: str) -> PatchesT: # pragma: win32 no cover
|
|
||||||
|
def get_env_patch(target_dir: str) -> PatchesT:
|
||||||
return (
|
return (
|
||||||
('PATH', (target_dir, os.pathsep, Var('PATH'))),
|
('PATH', (target_dir, os.pathsep, Var('PATH'))),
|
||||||
|
('COURSIER_CACHE', os.path.join(target_dir, '.cs-cache')),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def in_env(
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
prefix: Prefix,
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
) -> Generator[None, None, None]: # pragma: win32 no cover
|
with envcontext(get_env_patch(envdir)):
|
||||||
target_dir = prefix.path(
|
|
||||||
helpers.environment_dir(ENVIRONMENT_DIR, get_default_version()),
|
|
||||||
)
|
|
||||||
with envcontext(get_env_patch(target_dir)):
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]: # pragma: win32 no cover
|
|
||||||
with in_env(hook.prefix):
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -7,21 +7,19 @@ import tempfile
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
import pre_commit.constants as C
|
|
||||||
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.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
from pre_commit.util import clean_path_on_failure
|
|
||||||
from pre_commit.util import win_exe
|
from pre_commit.util import win_exe
|
||||||
from pre_commit.util 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 = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
|
run_hook = helpers.basic_run_hook
|
||||||
|
|
||||||
|
|
||||||
def get_env_patch(venv: str) -> PatchesT:
|
def get_env_patch(venv: str) -> PatchesT:
|
||||||
|
@ -31,9 +29,8 @@ def get_env_patch(venv: str) -> PatchesT:
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def in_env(prefix: Prefix) -> Generator[None, None, None]:
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT)
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
envdir = prefix.path(directory)
|
|
||||||
with envcontext(get_env_patch(envdir)):
|
with envcontext(get_env_patch(envdir)):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
@ -45,7 +42,7 @@ def install_environment(
|
||||||
) -> None:
|
) -> None:
|
||||||
helpers.assert_version_default('dart', version)
|
helpers.assert_version_default('dart', version)
|
||||||
|
|
||||||
envdir = prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))
|
envdir = helpers.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:
|
||||||
|
@ -67,7 +64,6 @@ def install_environment(
|
||||||
env=dart_env,
|
env=dart_env,
|
||||||
)
|
)
|
||||||
|
|
||||||
with clean_path_on_failure(envdir):
|
|
||||||
os.makedirs(bin_dir)
|
os.makedirs(bin_dir)
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
@ -99,12 +95,3 @@ def install_environment(
|
||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
f'could not find pubspec.yaml for {dep_s}',
|
f'could not find pubspec.yaml for {dep_s}',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]:
|
|
||||||
with in_env(hook.prefix):
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -5,18 +5,16 @@ import json
|
||||||
import os
|
import os
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
import pre_commit.constants as C
|
|
||||||
from pre_commit.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
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 clean_path_on_failure
|
|
||||||
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 = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
|
in_env = helpers.no_env # no special environment for docker
|
||||||
|
|
||||||
|
|
||||||
def _is_in_docker() -> bool:
|
def _is_in_docker() -> bool:
|
||||||
|
@ -95,13 +93,10 @@ def install_environment(
|
||||||
helpers.assert_version_default('docker', version)
|
helpers.assert_version_default('docker', version)
|
||||||
helpers.assert_no_additional_deps('docker', additional_dependencies)
|
helpers.assert_no_additional_deps('docker', additional_dependencies)
|
||||||
|
|
||||||
directory = prefix.path(
|
directory = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT),
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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
|
||||||
with clean_path_on_failure(directory):
|
|
||||||
build_docker_image(prefix, pull=True)
|
build_docker_image(prefix, pull=True)
|
||||||
os.mkdir(directory)
|
os.mkdir(directory)
|
||||||
|
|
||||||
|
@ -127,16 +122,26 @@ def docker_cmd() -> tuple[str, ...]: # pragma: win32 no cover
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
def run_hook(
|
||||||
hook: Hook,
|
prefix: Prefix,
|
||||||
|
entry: str,
|
||||||
|
args: Sequence[str],
|
||||||
file_args: Sequence[str],
|
file_args: Sequence[str],
|
||||||
|
*,
|
||||||
|
is_local: bool,
|
||||||
|
require_serial: bool,
|
||||||
color: bool,
|
color: bool,
|
||||||
) -> tuple[int, bytes]: # pragma: win32 no cover
|
) -> tuple[int, bytes]: # pragma: win32 no cover
|
||||||
# Rebuild the docker image in case it has gone missing, as many people do
|
# Rebuild the docker image in case it has gone missing, as many people do
|
||||||
# automated cleanup of docker images.
|
# automated cleanup of docker images.
|
||||||
build_docker_image(hook.prefix, pull=False)
|
build_docker_image(prefix, pull=False)
|
||||||
|
|
||||||
entry_exe, *cmd_rest = hook.cmd
|
entry_exe, *cmd_rest = helpers.hook_cmd(entry, args)
|
||||||
|
|
||||||
entry_tag = ('--entrypoint', entry_exe, docker_tag(hook.prefix))
|
entry_tag = ('--entrypoint', entry_exe, docker_tag(prefix))
|
||||||
cmd = (*docker_cmd(), *entry_tag, *cmd_rest)
|
cmd = (*docker_cmd(), *entry_tag, *cmd_rest)
|
||||||
return helpers.run_xargs(hook, cmd, file_args, color=color)
|
return helpers.run_xargs(
|
||||||
|
cmd,
|
||||||
|
file_args,
|
||||||
|
require_serial=require_serial,
|
||||||
|
color=color,
|
||||||
|
)
|
||||||
|
|
|
@ -2,20 +2,31 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.languages.docker import docker_cmd
|
from pre_commit.languages.docker import docker_cmd
|
||||||
|
from pre_commit.prefix import Prefix
|
||||||
|
|
||||||
ENVIRONMENT_DIR = None
|
ENVIRONMENT_DIR = None
|
||||||
get_default_version = helpers.basic_get_default_version
|
get_default_version = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
install_environment = helpers.no_install
|
install_environment = helpers.no_install
|
||||||
|
in_env = helpers.no_env
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
def run_hook(
|
||||||
hook: Hook,
|
prefix: Prefix,
|
||||||
|
entry: str,
|
||||||
|
args: Sequence[str],
|
||||||
file_args: Sequence[str],
|
file_args: Sequence[str],
|
||||||
|
*,
|
||||||
|
is_local: 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() + hook.cmd
|
cmd = docker_cmd() + helpers.hook_cmd(entry, args)
|
||||||
return helpers.run_xargs(hook, cmd, file_args, color=color)
|
return helpers.run_xargs(
|
||||||
|
cmd,
|
||||||
|
file_args,
|
||||||
|
require_serial=require_serial,
|
||||||
|
color=color,
|
||||||
|
)
|
||||||
|
|
|
@ -9,20 +9,18 @@ import zipfile
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
import pre_commit.constants as C
|
|
||||||
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.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
from pre_commit.util import clean_path_on_failure
|
|
||||||
|
|
||||||
ENVIRONMENT_DIR = 'dotnetenv'
|
ENVIRONMENT_DIR = 'dotnetenv'
|
||||||
BIN_DIR = 'bin'
|
BIN_DIR = 'bin'
|
||||||
|
|
||||||
get_default_version = helpers.basic_get_default_version
|
get_default_version = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
|
run_hook = helpers.basic_run_hook
|
||||||
|
|
||||||
|
|
||||||
def get_env_patch(venv: str) -> PatchesT:
|
def get_env_patch(venv: str) -> PatchesT:
|
||||||
|
@ -32,9 +30,8 @@ def get_env_patch(venv: str) -> PatchesT:
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def in_env(prefix: Prefix) -> Generator[None, None, None]:
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT)
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
envdir = prefix.path(directory)
|
|
||||||
with envcontext(get_env_patch(envdir)):
|
with envcontext(get_env_patch(envdir)):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
@ -63,8 +60,7 @@ def install_environment(
|
||||||
helpers.assert_version_default('dotnet', version)
|
helpers.assert_version_default('dotnet', version)
|
||||||
helpers.assert_no_additional_deps('dotnet', additional_dependencies)
|
helpers.assert_no_additional_deps('dotnet', additional_dependencies)
|
||||||
|
|
||||||
envdir = prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
with clean_path_on_failure(envdir):
|
|
||||||
build_dir = 'pre-commit-build'
|
build_dir = 'pre-commit-build'
|
||||||
|
|
||||||
# Build & pack nupkg file
|
# Build & pack nupkg file
|
||||||
|
@ -117,12 +113,3 @@ def install_environment(
|
||||||
# Clean the git dir, ignoring the environment dir
|
# Clean the git dir, ignoring the environment dir
|
||||||
clean_cmd = ('git', 'clean', '-ffxd', '-e', f'{ENVIRONMENT_DIR}-*')
|
clean_cmd = ('git', 'clean', '-ffxd', '-e', f'{ENVIRONMENT_DIR}-*')
|
||||||
helpers.run_setup_cmd(prefix, clean_cmd)
|
helpers.run_setup_cmd(prefix, clean_cmd)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]:
|
|
||||||
with in_env(hook.prefix):
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -2,20 +2,26 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
|
from pre_commit.prefix import Prefix
|
||||||
|
|
||||||
ENVIRONMENT_DIR = None
|
ENVIRONMENT_DIR = None
|
||||||
get_default_version = helpers.basic_get_default_version
|
get_default_version = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
install_environment = helpers.no_install
|
install_environment = helpers.no_install
|
||||||
|
in_env = helpers.no_env
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
def run_hook(
|
||||||
hook: Hook,
|
prefix: Prefix,
|
||||||
|
entry: str,
|
||||||
|
args: Sequence[str],
|
||||||
file_args: Sequence[str],
|
file_args: Sequence[str],
|
||||||
|
*,
|
||||||
|
is_local: bool,
|
||||||
|
require_serial: bool,
|
||||||
color: bool,
|
color: bool,
|
||||||
) -> tuple[int, bytes]:
|
) -> tuple[int, bytes]:
|
||||||
out = f'{hook.entry}\n\n'.encode()
|
out = f'{entry}\n\n'.encode()
|
||||||
out += b'\n'.join(f.encode() for f in file_args) + b'\n'
|
out += b'\n'.join(f.encode() for f in file_args) + b'\n'
|
||||||
return 1, out
|
return 1, out
|
||||||
|
|
|
@ -1,101 +1,159 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import functools
|
||||||
|
import json
|
||||||
import os.path
|
import os.path
|
||||||
|
import platform
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
import tarfile
|
||||||
|
import tempfile
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
import zipfile
|
||||||
|
from typing import ContextManager
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
from typing import IO
|
||||||
|
from typing import Protocol
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
import pre_commit.constants as C
|
import pre_commit.constants as C
|
||||||
from pre_commit import git
|
|
||||||
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.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
from pre_commit.util import clean_path_on_failure
|
|
||||||
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 rmtree
|
from pre_commit.util import rmtree
|
||||||
|
|
||||||
ENVIRONMENT_DIR = 'golangenv'
|
ENVIRONMENT_DIR = 'golangenv'
|
||||||
get_default_version = helpers.basic_get_default_version
|
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
|
run_hook = helpers.basic_run_hook
|
||||||
|
|
||||||
|
_ARCH_ALIASES = {
|
||||||
|
'x86_64': 'amd64',
|
||||||
|
'i386': '386',
|
||||||
|
'aarch64': 'arm64',
|
||||||
|
'armv8': 'arm64',
|
||||||
|
'armv7l': 'armv6l',
|
||||||
|
}
|
||||||
|
_ARCH = platform.machine().lower()
|
||||||
|
_ARCH = _ARCH_ALIASES.get(_ARCH, _ARCH)
|
||||||
|
|
||||||
|
|
||||||
def get_env_patch(venv: str) -> PatchesT:
|
class ExtractAll(Protocol):
|
||||||
|
def extractall(self, path: str) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
|
if sys.platform == 'win32': # pragma: win32 cover
|
||||||
|
_EXT = 'zip'
|
||||||
|
|
||||||
|
def _open_archive(bio: IO[bytes]) -> ContextManager[ExtractAll]:
|
||||||
|
return zipfile.ZipFile(bio)
|
||||||
|
else: # pragma: win32 no cover
|
||||||
|
_EXT = 'tar.gz'
|
||||||
|
|
||||||
|
def _open_archive(bio: IO[bytes]) -> ContextManager[ExtractAll]:
|
||||||
|
return tarfile.open(fileobj=bio)
|
||||||
|
|
||||||
|
|
||||||
|
@functools.lru_cache(maxsize=1)
|
||||||
|
def get_default_version() -> str:
|
||||||
|
if helpers.exe_exists('go'):
|
||||||
|
return 'system'
|
||||||
|
else:
|
||||||
|
return C.DEFAULT
|
||||||
|
|
||||||
|
|
||||||
|
def get_env_patch(venv: str, version: str) -> PatchesT:
|
||||||
|
if version == 'system':
|
||||||
return (
|
return (
|
||||||
('PATH', (os.path.join(venv, 'bin'), os.pathsep, Var('PATH'))),
|
('PATH', (os.path.join(venv, 'bin'), os.pathsep, Var('PATH'))),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
('GOROOT', os.path.join(venv, '.go')),
|
||||||
|
(
|
||||||
|
'PATH', (
|
||||||
|
os.path.join(venv, 'bin'), os.pathsep,
|
||||||
|
os.path.join(venv, '.go', 'bin'), os.pathsep, Var('PATH'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@functools.lru_cache
|
||||||
|
def _infer_go_version(version: str) -> str:
|
||||||
|
if version != C.DEFAULT:
|
||||||
|
return version
|
||||||
|
resp = urllib.request.urlopen('https://go.dev/dl/?mode=json')
|
||||||
|
# TODO: 3.9+ .removeprefix('go')
|
||||||
|
return json.load(resp)[0]['version'][2:]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_url(version: str) -> str:
|
||||||
|
os_name = platform.system().lower()
|
||||||
|
version = _infer_go_version(version)
|
||||||
|
return f'https://dl.google.com/go/go{version}.{os_name}-{_ARCH}.{_EXT}'
|
||||||
|
|
||||||
|
|
||||||
|
def _install_go(version: str, dest: str) -> None:
|
||||||
|
try:
|
||||||
|
resp = urllib.request.urlopen(_get_url(version))
|
||||||
|
except urllib.error.HTTPError as e: # pragma: no cover
|
||||||
|
if e.code == 404:
|
||||||
|
raise ValueError(
|
||||||
|
f'Could not find a version matching your system requirements '
|
||||||
|
f'(os={platform.system().lower()}; arch={_ARCH})',
|
||||||
|
) from e
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
with tempfile.TemporaryFile() as f:
|
||||||
|
shutil.copyfileobj(resp, f)
|
||||||
|
f.seek(0)
|
||||||
|
|
||||||
|
with _open_archive(f) as archive:
|
||||||
|
archive.extractall(dest)
|
||||||
|
shutil.move(os.path.join(dest, 'go'), os.path.join(dest, '.go'))
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def in_env(prefix: Prefix) -> Generator[None, None, None]:
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
envdir = prefix.path(
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT),
|
with envcontext(get_env_patch(envdir, version)):
|
||||||
)
|
|
||||||
with envcontext(get_env_patch(envdir)):
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
def guess_go_dir(remote_url: str) -> str:
|
|
||||||
if remote_url.endswith('.git'):
|
|
||||||
remote_url = remote_url[:-1 * len('.git')]
|
|
||||||
looks_like_url = (
|
|
||||||
not remote_url.startswith('file://') and
|
|
||||||
('//' in remote_url or '@' in remote_url)
|
|
||||||
)
|
|
||||||
remote_url = remote_url.replace(':', '/')
|
|
||||||
if looks_like_url:
|
|
||||||
_, _, remote_url = remote_url.rpartition('//')
|
|
||||||
_, _, remote_url = remote_url.rpartition('@')
|
|
||||||
return remote_url
|
|
||||||
else:
|
|
||||||
return 'unknown_src_dir'
|
|
||||||
|
|
||||||
|
|
||||||
def install_environment(
|
def install_environment(
|
||||||
prefix: Prefix,
|
prefix: Prefix,
|
||||||
version: str,
|
version: str,
|
||||||
additional_dependencies: Sequence[str],
|
additional_dependencies: Sequence[str],
|
||||||
) -> None:
|
) -> None:
|
||||||
helpers.assert_version_default('golang', version)
|
env_dir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
directory = prefix.path(
|
|
||||||
helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT),
|
|
||||||
)
|
|
||||||
|
|
||||||
with clean_path_on_failure(directory):
|
if version != 'system':
|
||||||
remote = git.get_remote_url(prefix.prefix_dir)
|
_install_go(version, env_dir)
|
||||||
repo_src_dir = os.path.join(directory, 'src', guess_go_dir(remote))
|
|
||||||
|
|
||||||
# Clone into the goenv we'll create
|
|
||||||
cmd = ('git', 'clone', '--recursive', '.', repo_src_dir)
|
|
||||||
helpers.run_setup_cmd(prefix, cmd)
|
|
||||||
|
|
||||||
if sys.platform == 'cygwin': # pragma: no cover
|
if sys.platform == 'cygwin': # pragma: no cover
|
||||||
_, gopath, _ = cmd_output('cygpath', '-w', directory)
|
gopath = cmd_output('cygpath', '-w', env_dir)[1].strip()
|
||||||
gopath = gopath.strip()
|
|
||||||
else:
|
else:
|
||||||
gopath = directory
|
gopath = env_dir
|
||||||
|
|
||||||
env = dict(os.environ, GOPATH=gopath)
|
env = dict(os.environ, GOPATH=gopath)
|
||||||
env.pop('GOBIN', None)
|
env.pop('GOBIN', None)
|
||||||
cmd_output_b('go', 'install', './...', cwd=repo_src_dir, env=env)
|
if version != 'system':
|
||||||
|
env['GOROOT'] = os.path.join(env_dir, '.go')
|
||||||
|
env['PATH'] = os.pathsep.join((
|
||||||
|
os.path.join(env_dir, '.go', 'bin'), os.environ['PATH'],
|
||||||
|
))
|
||||||
|
|
||||||
|
helpers.run_setup_cmd(prefix, ('go', 'install', './...'), env=env)
|
||||||
for dependency in additional_dependencies:
|
for dependency in additional_dependencies:
|
||||||
cmd_output_b(
|
helpers.run_setup_cmd(prefix, ('go', 'install', dependency), env=env)
|
||||||
'go', 'install', dependency, cwd=repo_src_dir, env=env,
|
|
||||||
)
|
# save some disk space -- we don't need this after installation
|
||||||
# Same some disk space, we don't need these after installation
|
pkgdir = os.path.join(env_dir, 'pkg')
|
||||||
rmtree(prefix.path(directory, 'src'))
|
if os.path.exists(pkgdir): # pragma: no branch (always true on windows?)
|
||||||
pkgdir = prefix.path(directory, 'pkg')
|
|
||||||
if os.path.exists(pkgdir): # pragma: no cover (go<1.10)
|
|
||||||
rmtree(pkgdir)
|
rmtree(pkgdir)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]:
|
|
||||||
with in_env(hook.prefix):
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
|
import shlex
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from typing import Generator
|
||||||
from typing import NoReturn
|
from typing import NoReturn
|
||||||
from typing import overload
|
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
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.hook import Hook
|
|
||||||
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
|
from pre_commit.xargs import xargs
|
||||||
|
@ -48,17 +49,8 @@ def run_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)
|
||||||
|
|
||||||
|
|
||||||
@overload
|
def environment_dir(prefix: Prefix, d: str, language_version: str) -> str:
|
||||||
def environment_dir(d: None, language_version: str) -> None: ...
|
return prefix.path(f'{d}-{language_version}')
|
||||||
@overload
|
|
||||||
def environment_dir(d: str, language_version: str) -> str: ...
|
|
||||||
|
|
||||||
|
|
||||||
def environment_dir(d: str | None, language_version: str) -> str | None:
|
|
||||||
if d is None:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return f'{d}-{language_version}'
|
|
||||||
|
|
||||||
|
|
||||||
def assert_version_default(binary: str, version: str) -> None:
|
def assert_version_default(binary: str, version: str) -> None:
|
||||||
|
@ -94,11 +86,16 @@ def no_install(
|
||||||
version: str,
|
version: str,
|
||||||
additional_dependencies: Sequence[str],
|
additional_dependencies: Sequence[str],
|
||||||
) -> NoReturn:
|
) -> NoReturn:
|
||||||
raise AssertionError('This type is not installable')
|
raise AssertionError('This language is not installable')
|
||||||
|
|
||||||
|
|
||||||
def target_concurrency(hook: Hook) -> int:
|
@contextlib.contextmanager
|
||||||
if hook.require_serial or 'PRE_COMMIT_NO_CONCURRENCY' in os.environ:
|
def no_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
def target_concurrency() -> int:
|
||||||
|
if 'PRE_COMMIT_NO_CONCURRENCY' in os.environ:
|
||||||
return 1
|
return 1
|
||||||
else:
|
else:
|
||||||
# Travis appears to have a bunch of CPUs, but we can't use them all.
|
# Travis appears to have a bunch of CPUs, but we can't use them all.
|
||||||
|
@ -122,13 +119,40 @@ def _shuffled(seq: Sequence[str]) -> list[str]:
|
||||||
|
|
||||||
|
|
||||||
def run_xargs(
|
def run_xargs(
|
||||||
hook: Hook,
|
|
||||||
cmd: tuple[str, ...],
|
cmd: tuple[str, ...],
|
||||||
file_args: Sequence[str],
|
file_args: Sequence[str],
|
||||||
**kwargs: Any,
|
*,
|
||||||
|
require_serial: bool,
|
||||||
|
color: bool,
|
||||||
) -> tuple[int, bytes]:
|
) -> tuple[int, bytes]:
|
||||||
# Shuffle the files so that they more evenly fill out the xargs partitions,
|
if require_serial:
|
||||||
# but do it deterministically in case a hook cares about ordering.
|
jobs = 1
|
||||||
|
else:
|
||||||
|
# Shuffle the files so that they more evenly fill out the xargs
|
||||||
|
# partitions, but do it deterministically in case a hook cares about
|
||||||
|
# ordering.
|
||||||
file_args = _shuffled(file_args)
|
file_args = _shuffled(file_args)
|
||||||
kwargs['target_concurrency'] = target_concurrency(hook)
|
jobs = target_concurrency()
|
||||||
return xargs(cmd, file_args, **kwargs)
|
return xargs(cmd, file_args, target_concurrency=jobs, color=color)
|
||||||
|
|
||||||
|
|
||||||
|
def hook_cmd(entry: str, args: Sequence[str]) -> tuple[str, ...]:
|
||||||
|
return (*shlex.split(entry), *args)
|
||||||
|
|
||||||
|
|
||||||
|
def basic_run_hook(
|
||||||
|
prefix: Prefix,
|
||||||
|
entry: str,
|
||||||
|
args: Sequence[str],
|
||||||
|
file_args: Sequence[str],
|
||||||
|
*,
|
||||||
|
is_local: bool,
|
||||||
|
require_serial: bool,
|
||||||
|
color: bool,
|
||||||
|
) -> tuple[int, bytes]:
|
||||||
|
return run_xargs(
|
||||||
|
hook_cmd(entry, args),
|
||||||
|
file_args,
|
||||||
|
require_serial=require_serial,
|
||||||
|
color=color,
|
||||||
|
)
|
||||||
|
|
|
@ -6,19 +6,17 @@ import sys
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
import pre_commit.constants as C
|
|
||||||
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.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
from pre_commit.util import clean_path_on_failure
|
|
||||||
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 = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
|
run_hook = helpers.basic_run_hook
|
||||||
|
|
||||||
|
|
||||||
def _get_lua_version() -> str: # pragma: win32 no cover
|
def _get_lua_version() -> str: # pragma: win32 no cover
|
||||||
|
@ -45,14 +43,10 @@ def get_env_patch(d: str) -> PatchesT: # pragma: win32 no cover
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _envdir(prefix: Prefix) -> str: # pragma: win32 no cover
|
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT)
|
|
||||||
return prefix.path(directory)
|
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager # pragma: win32 no cover
|
@contextlib.contextmanager # pragma: win32 no cover
|
||||||
def in_env(prefix: Prefix) -> Generator[None, None, None]:
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
with envcontext(get_env_patch(_envdir(prefix))):
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
|
with envcontext(get_env_patch(envdir)):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,9 +57,8 @@ def install_environment(
|
||||||
) -> None: # pragma: win32 no cover
|
) -> None: # pragma: win32 no cover
|
||||||
helpers.assert_version_default('lua', version)
|
helpers.assert_version_default('lua', version)
|
||||||
|
|
||||||
envdir = _envdir(prefix)
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
with clean_path_on_failure(envdir):
|
with in_env(prefix, version):
|
||||||
with in_env(prefix):
|
|
||||||
# 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.
|
||||||
os.makedirs(envdir, exist_ok=True)
|
os.makedirs(envdir, exist_ok=True)
|
||||||
|
@ -80,12 +73,3 @@ def install_environment(
|
||||||
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)
|
helpers.run_setup_cmd(prefix, cmd)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]: # pragma: win32 no cover
|
|
||||||
with in_env(hook.prefix):
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -12,16 +12,15 @@ 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.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
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 clean_path_on_failure
|
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
@functools.lru_cache(maxsize=1)
|
@functools.lru_cache(maxsize=1)
|
||||||
|
@ -37,11 +36,6 @@ def get_default_version() -> str:
|
||||||
return C.DEFAULT
|
return C.DEFAULT
|
||||||
|
|
||||||
|
|
||||||
def _envdir(prefix: Prefix, version: str) -> str:
|
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
|
||||||
return prefix.path(directory)
|
|
||||||
|
|
||||||
|
|
||||||
def get_env_patch(venv: str) -> PatchesT:
|
def get_env_patch(venv: str) -> PatchesT:
|
||||||
if sys.platform == 'cygwin': # pragma: no cover
|
if sys.platform == 'cygwin': # pragma: no cover
|
||||||
_, win_venv, _ = cmd_output('cygpath', '-w', venv)
|
_, win_venv, _ = cmd_output('cygpath', '-w', venv)
|
||||||
|
@ -65,11 +59,9 @@ def get_env_patch(venv: str) -> PatchesT:
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def in_env(
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
prefix: Prefix,
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
language_version: str,
|
with envcontext(get_env_patch(envdir)):
|
||||||
) -> Generator[None, None, None]:
|
|
||||||
with envcontext(get_env_patch(_envdir(prefix, language_version))):
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,17 +77,13 @@ def health_check(prefix: Prefix, language_version: str) -> str | 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:
|
||||||
additional_dependencies = tuple(additional_dependencies)
|
|
||||||
assert prefix.exists('package.json')
|
assert prefix.exists('package.json')
|
||||||
envdir = _envdir(prefix, version)
|
envdir = helpers.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
|
||||||
envdir = fr'\\?\{os.path.normpath(envdir)}'
|
envdir = fr'\\?\{os.path.normpath(envdir)}'
|
||||||
with clean_path_on_failure(envdir):
|
cmd = [sys.executable, '-mnodeenv', '--prebuilt', '--clean-src', envdir]
|
||||||
cmd = [
|
|
||||||
sys.executable, '-mnodeenv', '--prebuilt', '--clean-src', envdir,
|
|
||||||
]
|
|
||||||
if version != C.DEFAULT:
|
if version != C.DEFAULT:
|
||||||
cmd.extend(['-n', version])
|
cmd.extend(['-n', version])
|
||||||
cmd_output_b(*cmd)
|
cmd_output_b(*cmd)
|
||||||
|
@ -120,12 +108,3 @@ def install_environment(
|
||||||
if prefix.exists('node_modules'): # pragma: win32 no cover
|
if prefix.exists('node_modules'): # pragma: win32 no cover
|
||||||
rmtree(prefix.path('node_modules'))
|
rmtree(prefix.path('node_modules'))
|
||||||
os.remove(pkg)
|
os.remove(pkg)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]:
|
|
||||||
with in_env(hook.prefix, hook.language_version):
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -9,19 +9,13 @@ from typing import Sequence
|
||||||
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.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
from pre_commit.util import clean_path_on_failure
|
|
||||||
|
|
||||||
ENVIRONMENT_DIR = 'perl_env'
|
ENVIRONMENT_DIR = 'perl_env'
|
||||||
get_default_version = helpers.basic_get_default_version
|
get_default_version = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
|
run_hook = helpers.basic_run_hook
|
||||||
|
|
||||||
def _envdir(prefix: Prefix, version: str) -> str:
|
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
|
||||||
return prefix.path(directory)
|
|
||||||
|
|
||||||
|
|
||||||
def get_env_patch(venv: str) -> PatchesT:
|
def get_env_patch(venv: str) -> PatchesT:
|
||||||
|
@ -39,11 +33,9 @@ def get_env_patch(venv: str) -> PatchesT:
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def in_env(
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
prefix: Prefix,
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
language_version: str,
|
with envcontext(get_env_patch(envdir)):
|
||||||
) -> Generator[None, None, None]:
|
|
||||||
with envcontext(get_env_patch(_envdir(prefix, language_version))):
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,17 +44,7 @@ def install_environment(
|
||||||
) -> None:
|
) -> None:
|
||||||
helpers.assert_version_default('perl', version)
|
helpers.assert_version_default('perl', version)
|
||||||
|
|
||||||
with clean_path_on_failure(_envdir(prefix, version)):
|
|
||||||
with in_env(prefix, version):
|
with in_env(prefix, version):
|
||||||
helpers.run_setup_cmd(
|
helpers.run_setup_cmd(
|
||||||
prefix, ('cpan', '-T', '.', *additional_dependencies),
|
prefix, ('cpan', '-T', '.', *additional_dependencies),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]:
|
|
||||||
with in_env(hook.prefix, hook.language_version):
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -8,14 +8,15 @@ from typing import Pattern
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit import output
|
from pre_commit import output
|
||||||
from pre_commit.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
|
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 = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
install_environment = helpers.no_install
|
install_environment = helpers.no_install
|
||||||
|
in_env = helpers.no_env
|
||||||
|
|
||||||
|
|
||||||
def _process_filename_by_line(pattern: Pattern[bytes], filename: str) -> int:
|
def _process_filename_by_line(pattern: Pattern[bytes], filename: str) -> int:
|
||||||
|
@ -87,12 +88,17 @@ FNS = {
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
def run_hook(
|
||||||
hook: Hook,
|
prefix: Prefix,
|
||||||
|
entry: str,
|
||||||
|
args: Sequence[str],
|
||||||
file_args: Sequence[str],
|
file_args: Sequence[str],
|
||||||
|
*,
|
||||||
|
is_local: bool,
|
||||||
|
require_serial: bool,
|
||||||
color: bool,
|
color: bool,
|
||||||
) -> tuple[int, bytes]:
|
) -> tuple[int, bytes]:
|
||||||
exe = (sys.executable, '-m', __name__) + tuple(hook.args) + (hook.entry,)
|
cmd = (sys.executable, '-m', __name__, *args, entry)
|
||||||
return xargs(exe, file_args, color=color)
|
return xargs(cmd, file_args, color=color)
|
||||||
|
|
||||||
|
|
||||||
def main(argv: Sequence[str] | None = None) -> int:
|
def main(argv: Sequence[str] | None = None) -> int:
|
||||||
|
|
|
@ -12,17 +12,16 @@ 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.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
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
|
||||||
from pre_commit.util import clean_path_on_failure
|
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
@functools.lru_cache(maxsize=None)
|
@functools.lru_cache(maxsize=None)
|
||||||
|
@ -153,19 +152,14 @@ def norm_version(version: str) -> str | None:
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def in_env(
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
prefix: Prefix,
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
language_version: str,
|
|
||||||
) -> Generator[None, None, None]:
|
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)
|
|
||||||
envdir = prefix.path(directory)
|
|
||||||
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, language_version: str) -> str | None:
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, language_version)
|
||||||
envdir = prefix.path(directory)
|
|
||||||
pyvenv_cfg = os.path.join(envdir, 'pyvenv.cfg')
|
pyvenv_cfg = os.path.join(envdir, 'pyvenv.cfg')
|
||||||
|
|
||||||
# created with "old" virtualenv
|
# created with "old" virtualenv
|
||||||
|
@ -208,23 +202,13 @@ def install_environment(
|
||||||
version: str,
|
version: str,
|
||||||
additional_dependencies: Sequence[str],
|
additional_dependencies: Sequence[str],
|
||||||
) -> None:
|
) -> None:
|
||||||
envdir = prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))
|
envdir = helpers.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:
|
||||||
venv_cmd.extend(('-p', python))
|
venv_cmd.extend(('-p', python))
|
||||||
install_cmd = ('python', '-mpip', 'install', '.', *additional_dependencies)
|
install_cmd = ('python', '-mpip', 'install', '.', *additional_dependencies)
|
||||||
|
|
||||||
with clean_path_on_failure(envdir):
|
|
||||||
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)
|
helpers.run_setup_cmd(prefix, install_cmd)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]:
|
|
||||||
with in_env(hook.prefix, hook.language_version):
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -10,10 +10,8 @@ from typing import Sequence
|
||||||
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.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
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 win_exe
|
from pre_commit.util import win_exe
|
||||||
|
|
||||||
|
@ -31,32 +29,22 @@ def get_env_patch(venv: str) -> PatchesT:
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def in_env(
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
prefix: Prefix,
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
language_version: str,
|
|
||||||
) -> Generator[None, None, None]:
|
|
||||||
envdir = _get_env_dir(prefix, language_version)
|
|
||||||
with envcontext(get_env_patch(envdir)):
|
with envcontext(get_env_patch(envdir)):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
def _get_env_dir(prefix: Prefix, version: str) -> str:
|
def _prefix_if_file_entry(
|
||||||
return prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))
|
entry: list[str],
|
||||||
|
|
||||||
|
|
||||||
def _prefix_if_non_local_file_entry(
|
|
||||||
entry: Sequence[str],
|
|
||||||
prefix: Prefix,
|
prefix: Prefix,
|
||||||
src: str,
|
*,
|
||||||
|
is_local: bool,
|
||||||
) -> Sequence[str]:
|
) -> Sequence[str]:
|
||||||
if entry[1] == '-e':
|
if entry[1] == '-e' or is_local:
|
||||||
return entry[1:]
|
return entry[1:]
|
||||||
else:
|
else:
|
||||||
if src == 'local':
|
return (prefix.path(entry[1]),)
|
||||||
path = entry[1]
|
|
||||||
else:
|
|
||||||
path = prefix.path(entry[1])
|
|
||||||
return (path,)
|
|
||||||
|
|
||||||
|
|
||||||
def _rscript_exec() -> str:
|
def _rscript_exec() -> str:
|
||||||
|
@ -67,7 +55,7 @@ def _rscript_exec() -> str:
|
||||||
return os.path.join(r_home, 'bin', win_exe('Rscript'))
|
return os.path.join(r_home, 'bin', win_exe('Rscript'))
|
||||||
|
|
||||||
|
|
||||||
def _entry_validate(entry: Sequence[str]) -> None:
|
def _entry_validate(entry: list[str]) -> None:
|
||||||
"""
|
"""
|
||||||
Allowed entries:
|
Allowed entries:
|
||||||
# Rscript -e expr
|
# Rscript -e expr
|
||||||
|
@ -81,20 +69,23 @@ def _entry_validate(entry: Sequence[str]) -> None:
|
||||||
raise ValueError('You can supply at most one expression.')
|
raise ValueError('You can supply at most one expression.')
|
||||||
elif len(entry) > 2:
|
elif len(entry) > 2:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'The only valid syntax is `Rscript -e {expr}`',
|
'The only valid syntax is `Rscript -e {expr}`'
|
||||||
'or `Rscript path/to/hook/script`',
|
'or `Rscript path/to/hook/script`',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _cmd_from_hook(hook: Hook) -> tuple[str, ...]:
|
def _cmd_from_hook(
|
||||||
entry = shlex.split(hook.entry)
|
prefix: Prefix,
|
||||||
_entry_validate(entry)
|
entry: str,
|
||||||
|
args: Sequence[str],
|
||||||
|
*,
|
||||||
|
is_local: bool,
|
||||||
|
) -> tuple[str, ...]:
|
||||||
|
cmd = shlex.split(entry)
|
||||||
|
_entry_validate(cmd)
|
||||||
|
|
||||||
return (
|
cmd_part = _prefix_if_file_entry(cmd, prefix, is_local=is_local)
|
||||||
*entry[:1], *RSCRIPT_OPTS,
|
return (cmd[0], *RSCRIPT_OPTS, *cmd_part, *args)
|
||||||
*_prefix_if_non_local_file_entry(entry, hook.prefix, hook.src),
|
|
||||||
*hook.args,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def install_environment(
|
def install_environment(
|
||||||
|
@ -102,8 +93,7 @@ def install_environment(
|
||||||
version: str,
|
version: str,
|
||||||
additional_dependencies: Sequence[str],
|
additional_dependencies: Sequence[str],
|
||||||
) -> None:
|
) -> None:
|
||||||
env_dir = _get_env_dir(prefix, version)
|
env_dir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
with clean_path_on_failure(env_dir):
|
|
||||||
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'))
|
||||||
|
@ -166,11 +156,19 @@ def _inline_r_setup(code: str) -> str:
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
def run_hook(
|
||||||
hook: Hook,
|
prefix: Prefix,
|
||||||
|
entry: str,
|
||||||
|
args: Sequence[str],
|
||||||
file_args: Sequence[str],
|
file_args: Sequence[str],
|
||||||
|
*,
|
||||||
|
is_local: bool,
|
||||||
|
require_serial: bool,
|
||||||
color: bool,
|
color: bool,
|
||||||
) -> tuple[int, bytes]:
|
) -> tuple[int, bytes]:
|
||||||
with in_env(hook.prefix, hook.language_version):
|
cmd = _cmd_from_hook(prefix, entry, args, is_local=is_local)
|
||||||
return helpers.run_xargs(
|
return helpers.run_xargs(
|
||||||
hook, _cmd_from_hook(hook), file_args, color=color,
|
cmd,
|
||||||
|
file_args,
|
||||||
|
require_serial=require_serial,
|
||||||
|
color=color,
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,15 +13,14 @@ 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.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
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 clean_path_on_failure
|
|
||||||
from pre_commit.util import resource_bytesio
|
from pre_commit.util import resource_bytesio
|
||||||
|
|
||||||
ENVIRONMENT_DIR = 'rbenv'
|
ENVIRONMENT_DIR = 'rbenv'
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
|
run_hook = helpers.basic_run_hook
|
||||||
|
|
||||||
|
|
||||||
@functools.lru_cache(maxsize=1)
|
@functools.lru_cache(maxsize=1)
|
||||||
|
@ -40,6 +39,7 @@ 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 +68,9 @@ def get_env_patch(
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def in_env(
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
prefix: Prefix,
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
language_version: str,
|
with envcontext(get_env_patch(envdir, version)):
|
||||||
) -> Generator[None, None, None]:
|
|
||||||
envdir = prefix.path(
|
|
||||||
helpers.environment_dir(ENVIRONMENT_DIR, language_version),
|
|
||||||
)
|
|
||||||
with envcontext(get_env_patch(envdir, language_version)):
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,14 +84,14 @@ def _install_rbenv(
|
||||||
prefix: Prefix,
|
prefix: Prefix,
|
||||||
version: str,
|
version: str,
|
||||||
) -> None: # pragma: win32 no cover
|
) -> None: # pragma: win32 no cover
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
envdir = helpers.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'), prefix.path(directory))
|
shutil.move(prefix.path('rbenv'), envdir)
|
||||||
|
|
||||||
# Only install ruby-build if the version is specified
|
# Only install ruby-build if the version is specified
|
||||||
if version != C.DEFAULT:
|
if version != C.DEFAULT:
|
||||||
plugins_dir = prefix.path(directory, 'plugins')
|
plugins_dir = os.path.join(envdir, 'plugins')
|
||||||
_extract_resource('ruby-download.tar.gz', plugins_dir)
|
_extract_resource('ruby-download.tar.gz', plugins_dir)
|
||||||
_extract_resource('ruby-build.tar.gz', plugins_dir)
|
_extract_resource('ruby-build.tar.gz', plugins_dir)
|
||||||
|
|
||||||
|
@ -115,9 +110,6 @@ def _install_ruby(
|
||||||
def install_environment(
|
def install_environment(
|
||||||
prefix: Prefix, version: str, additional_dependencies: Sequence[str],
|
prefix: Prefix, version: str, additional_dependencies: Sequence[str],
|
||||||
) -> None:
|
) -> None:
|
||||||
additional_dependencies = tuple(additional_dependencies)
|
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
|
||||||
with clean_path_on_failure(prefix.path(directory)):
|
|
||||||
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):
|
||||||
|
@ -142,12 +134,3 @@ def install_environment(
|
||||||
*prefix.star('.gem'), *additional_dependencies,
|
*prefix.star('.gem'), *additional_dependencies,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]:
|
|
||||||
with in_env(hook.prefix, hook.language_version):
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -15,16 +15,15 @@ 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.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
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 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 = helpers.basic_health_check
|
||||||
|
run_hook = helpers.basic_run_hook
|
||||||
|
|
||||||
|
|
||||||
@functools.lru_cache(maxsize=1)
|
@functools.lru_cache(maxsize=1)
|
||||||
|
@ -49,11 +48,6 @@ def _rust_toolchain(language_version: str) -> str:
|
||||||
return language_version
|
return language_version
|
||||||
|
|
||||||
|
|
||||||
def _envdir(prefix: Prefix, version: str) -> str:
|
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
|
||||||
return prefix.path(directory)
|
|
||||||
|
|
||||||
|
|
||||||
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),
|
('CARGO_HOME', target_dir),
|
||||||
|
@ -68,13 +62,9 @@ def get_env_patch(target_dir: str, version: str) -> PatchesT:
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def in_env(
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
prefix: Prefix,
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
language_version: str,
|
with envcontext(get_env_patch(envdir, version)):
|
||||||
) -> Generator[None, None, None]:
|
|
||||||
with envcontext(
|
|
||||||
get_env_patch(_envdir(prefix, language_version), language_version),
|
|
||||||
):
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,7 +116,7 @@ def install_environment(
|
||||||
version: str,
|
version: str,
|
||||||
additional_dependencies: Sequence[str],
|
additional_dependencies: Sequence[str],
|
||||||
) -> None:
|
) -> None:
|
||||||
directory = _envdir(prefix, version)
|
envdir = helpers.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
|
||||||
|
@ -143,7 +133,6 @@ def install_environment(
|
||||||
}
|
}
|
||||||
lib_deps = set(additional_dependencies) - cli_deps
|
lib_deps = set(additional_dependencies) - cli_deps
|
||||||
|
|
||||||
with clean_path_on_failure(directory):
|
|
||||||
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[len('cli:'):]
|
||||||
|
@ -162,15 +151,6 @@ def install_environment(
|
||||||
|
|
||||||
for args in packages_to_install:
|
for args in packages_to_install:
|
||||||
cmd_output_b(
|
cmd_output_b(
|
||||||
'cargo', 'install', '--bins', '--root', directory, *args,
|
'cargo', 'install', '--bins', '--root', envdir, *args,
|
||||||
cwd=prefix.prefix_dir,
|
cwd=prefix.prefix_dir,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]:
|
|
||||||
with in_env(hook.prefix, hook.language_version):
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -2,19 +2,31 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
from pre_commit.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
|
from pre_commit.prefix import Prefix
|
||||||
|
|
||||||
ENVIRONMENT_DIR = None
|
ENVIRONMENT_DIR = None
|
||||||
get_default_version = helpers.basic_get_default_version
|
get_default_version = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
install_environment = helpers.no_install
|
install_environment = helpers.no_install
|
||||||
|
in_env = helpers.no_env
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
def run_hook(
|
||||||
hook: Hook,
|
prefix: Prefix,
|
||||||
|
entry: str,
|
||||||
|
args: Sequence[str],
|
||||||
file_args: Sequence[str],
|
file_args: Sequence[str],
|
||||||
|
*,
|
||||||
|
is_local: bool,
|
||||||
|
require_serial: bool,
|
||||||
color: bool,
|
color: bool,
|
||||||
) -> tuple[int, bytes]:
|
) -> tuple[int, bytes]:
|
||||||
cmd = (hook.prefix.path(hook.cmd[0]), *hook.cmd[1:])
|
cmd = helpers.hook_cmd(entry, args)
|
||||||
return helpers.run_xargs(hook, cmd, file_args, color=color)
|
cmd = (prefix.path(cmd[0]), *cmd[1:])
|
||||||
|
return helpers.run_xargs(
|
||||||
|
cmd,
|
||||||
|
file_args,
|
||||||
|
require_serial=require_serial,
|
||||||
|
color=color,
|
||||||
|
)
|
||||||
|
|
|
@ -5,21 +5,20 @@ import os
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
import pre_commit.constants as C
|
|
||||||
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.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
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
|
||||||
|
|
||||||
|
BUILD_DIR = '.build'
|
||||||
|
BUILD_CONFIG = 'release'
|
||||||
|
|
||||||
ENVIRONMENT_DIR = 'swift_env'
|
ENVIRONMENT_DIR = 'swift_env'
|
||||||
get_default_version = helpers.basic_get_default_version
|
get_default_version = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
BUILD_DIR = '.build'
|
run_hook = helpers.basic_run_hook
|
||||||
BUILD_CONFIG = 'release'
|
|
||||||
|
|
||||||
|
|
||||||
def get_env_patch(venv: str) -> PatchesT: # pragma: win32 no cover
|
def get_env_patch(venv: str) -> PatchesT: # pragma: win32 no cover
|
||||||
|
@ -28,10 +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) -> Generator[None, None, None]:
|
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
|
||||||
envdir = prefix.path(
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT),
|
|
||||||
)
|
|
||||||
with envcontext(get_env_patch(envdir)):
|
with envcontext(get_env_patch(envdir)):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
@ -41,25 +38,13 @@ def install_environment(
|
||||||
) -> None: # pragma: win32 no cover
|
) -> None: # pragma: win32 no cover
|
||||||
helpers.assert_version_default('swift', version)
|
helpers.assert_version_default('swift', version)
|
||||||
helpers.assert_no_additional_deps('swift', additional_dependencies)
|
helpers.assert_no_additional_deps('swift', additional_dependencies)
|
||||||
directory = prefix.path(
|
envdir = helpers.environment_dir(prefix, ENVIRONMENT_DIR, version)
|
||||||
helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Build the swift package
|
# Build the swift package
|
||||||
with clean_path_on_failure(directory):
|
os.mkdir(envdir)
|
||||||
os.mkdir(directory)
|
|
||||||
cmd_output_b(
|
cmd_output_b(
|
||||||
'swift', 'build',
|
'swift', 'build',
|
||||||
'-C', prefix.prefix_dir,
|
'-C', prefix.prefix_dir,
|
||||||
'-c', BUILD_CONFIG,
|
'-c', BUILD_CONFIG,
|
||||||
'--build-path', os.path.join(directory, BUILD_DIR),
|
'--build-path', os.path.join(envdir, BUILD_DIR),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]: # pragma: win32 no cover
|
|
||||||
with in_env(hook.prefix):
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -1,20 +1,10 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Sequence
|
|
||||||
|
|
||||||
from pre_commit.hook import Hook
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
|
|
||||||
|
|
||||||
ENVIRONMENT_DIR = None
|
ENVIRONMENT_DIR = None
|
||||||
get_default_version = helpers.basic_get_default_version
|
get_default_version = helpers.basic_get_default_version
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
install_environment = helpers.no_install
|
install_environment = helpers.no_install
|
||||||
|
in_env = helpers.no_env
|
||||||
|
run_hook = helpers.basic_run_hook
|
||||||
def run_hook(
|
|
||||||
hook: Hook,
|
|
||||||
file_args: Sequence[str],
|
|
||||||
color: bool,
|
|
||||||
) -> tuple[int, bytes]:
|
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
|
||||||
|
|
|
@ -20,13 +20,13 @@ def parse_filename(filename: str) -> tuple[str, ...]:
|
||||||
|
|
||||||
|
|
||||||
def find_executable(
|
def find_executable(
|
||||||
exe: str, _environ: Mapping[str, str] | None = None,
|
exe: str, *, env: Mapping[str, str] | None = None,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
exe = os.path.normpath(exe)
|
exe = os.path.normpath(exe)
|
||||||
if os.sep in exe:
|
if os.sep in exe:
|
||||||
return exe
|
return exe
|
||||||
|
|
||||||
environ = _environ if _environ is not None else os.environ
|
environ = env if env is not None else os.environ
|
||||||
|
|
||||||
if 'PATHEXT' in environ:
|
if 'PATHEXT' in environ:
|
||||||
exts = environ['PATHEXT'].split(os.pathsep)
|
exts = environ['PATHEXT'].split(os.pathsep)
|
||||||
|
@ -43,12 +43,12 @@ def find_executable(
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def normexe(orig: str) -> str:
|
def normexe(orig: str, *, env: Mapping[str, str] | None = None) -> str:
|
||||||
def _error(msg: str) -> NoReturn:
|
def _error(msg: str) -> NoReturn:
|
||||||
raise ExecutableNotFoundError(f'Executable `{orig}` {msg}')
|
raise ExecutableNotFoundError(f'Executable `{orig}` {msg}')
|
||||||
|
|
||||||
if os.sep not in orig and (not os.altsep or os.altsep not in orig):
|
if os.sep not in orig and (not os.altsep or os.altsep not in orig):
|
||||||
exe = find_executable(orig)
|
exe = find_executable(orig, env=env)
|
||||||
if exe is None:
|
if exe is None:
|
||||||
_error('not found')
|
_error('not found')
|
||||||
return exe
|
return exe
|
||||||
|
@ -62,7 +62,11 @@ def normexe(orig: str) -> str:
|
||||||
return orig
|
return orig
|
||||||
|
|
||||||
|
|
||||||
def normalize_cmd(cmd: tuple[str, ...]) -> tuple[str, ...]:
|
def normalize_cmd(
|
||||||
|
cmd: tuple[str, ...],
|
||||||
|
*,
|
||||||
|
env: Mapping[str, str] | None = None,
|
||||||
|
) -> tuple[str, ...]:
|
||||||
"""Fixes for the following issues on windows
|
"""Fixes for the following issues on windows
|
||||||
- https://bugs.python.org/issue8557
|
- https://bugs.python.org/issue8557
|
||||||
- windows does not parse shebangs
|
- windows does not parse shebangs
|
||||||
|
@ -70,12 +74,12 @@ def normalize_cmd(cmd: tuple[str, ...]) -> tuple[str, ...]:
|
||||||
This function also makes deep-path shebangs work just fine
|
This function also makes deep-path shebangs work just fine
|
||||||
"""
|
"""
|
||||||
# Use PATH to determine the executable
|
# Use PATH to determine the executable
|
||||||
exe = normexe(cmd[0])
|
exe = normexe(cmd[0], env=env)
|
||||||
|
|
||||||
# Figure out the shebang from the resulting command
|
# Figure out the shebang from the resulting command
|
||||||
cmd = parse_filename(exe) + (exe,) + cmd[1:]
|
cmd = parse_filename(exe) + (exe,) + cmd[1:]
|
||||||
|
|
||||||
# This could have given us back another bare executable
|
# This could have given us back another bare executable
|
||||||
exe = normexe(cmd[0])
|
exe = normexe(cmd[0], env=env)
|
||||||
|
|
||||||
return (exe,) + cmd[1:]
|
return (exe,) + cmd[1:]
|
||||||
|
|
|
@ -10,28 +10,33 @@ import pre_commit.constants as C
|
||||||
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.languages.all import languages
|
||||||
from pre_commit.languages.helpers 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 parse_version
|
from pre_commit.util import clean_path_on_failure
|
||||||
from pre_commit.util import rmtree
|
from pre_commit.util import rmtree
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('pre_commit')
|
logger = logging.getLogger('pre_commit')
|
||||||
|
|
||||||
|
|
||||||
|
def _state_filename_v1(venv: str) -> str:
|
||||||
|
return os.path.join(venv, '.install_state_v1')
|
||||||
|
|
||||||
|
|
||||||
|
def _state_filename_v2(venv: str) -> str:
|
||||||
|
return os.path.join(venv, '.install_state_v2')
|
||||||
|
|
||||||
|
|
||||||
def _state(additional_deps: Sequence[str]) -> object:
|
def _state(additional_deps: Sequence[str]) -> object:
|
||||||
return {'additional_dependencies': sorted(additional_deps)}
|
return {'additional_dependencies': sorted(additional_deps)}
|
||||||
|
|
||||||
|
|
||||||
def _state_filename(prefix: Prefix, venv: str) -> str:
|
def _read_state(venv: str) -> object | None:
|
||||||
return prefix.path(venv, f'.install_state_v{C.INSTALLED_STATE_VERSION}')
|
filename = _state_filename_v1(venv)
|
||||||
|
|
||||||
|
|
||||||
def _read_state(prefix: Prefix, venv: str) -> object | None:
|
|
||||||
filename = _state_filename(prefix, venv)
|
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
|
@ -39,27 +44,23 @@ def _read_state(prefix: Prefix, venv: str) -> object | None:
|
||||||
return json.load(f)
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
def _write_state(prefix: Prefix, venv: str, state: object) -> None:
|
|
||||||
state_filename = _state_filename(prefix, venv)
|
|
||||||
staging = f'{state_filename}staging'
|
|
||||||
with open(staging, 'w') as state_file:
|
|
||||||
state_file.write(json.dumps(state))
|
|
||||||
# Move the file into place atomically to indicate we've installed
|
|
||||||
os.replace(staging, state_filename)
|
|
||||||
|
|
||||||
|
|
||||||
def _hook_installed(hook: Hook) -> bool:
|
def _hook_installed(hook: Hook) -> bool:
|
||||||
lang = languages[hook.language]
|
lang = languages[hook.language]
|
||||||
venv = environment_dir(lang.ENVIRONMENT_DIR, hook.language_version)
|
if lang.ENVIRONMENT_DIR is None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
venv = environment_dir(
|
||||||
|
hook.prefix,
|
||||||
|
lang.ENVIRONMENT_DIR,
|
||||||
|
hook.language_version,
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
venv is None or (
|
|
||||||
(
|
(
|
||||||
_read_state(hook.prefix, venv) ==
|
os.path.exists(_state_filename_v2(venv)) or
|
||||||
_state(hook.additional_dependencies)
|
_read_state(venv) == _state(hook.additional_dependencies)
|
||||||
) and
|
) and
|
||||||
not lang.health_check(hook.prefix, hook.language_version)
|
not lang.health_check(hook.prefix, hook.language_version)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _hook_install(hook: Hook) -> None:
|
def _hook_install(hook: Hook) -> None:
|
||||||
|
@ -69,13 +70,19 @@ def _hook_install(hook: Hook) -> None:
|
||||||
|
|
||||||
lang = languages[hook.language]
|
lang = languages[hook.language]
|
||||||
assert lang.ENVIRONMENT_DIR is not None
|
assert lang.ENVIRONMENT_DIR is not None
|
||||||
venv = environment_dir(lang.ENVIRONMENT_DIR, hook.language_version)
|
|
||||||
|
venv = environment_dir(
|
||||||
|
hook.prefix,
|
||||||
|
lang.ENVIRONMENT_DIR,
|
||||||
|
hook.language_version,
|
||||||
|
)
|
||||||
|
|
||||||
# There's potentially incomplete cleanup from previous runs
|
# There's potentially incomplete cleanup from previous runs
|
||||||
# Clean it up!
|
# Clean it up!
|
||||||
if hook.prefix.exists(venv):
|
if os.path.exists(venv):
|
||||||
rmtree(hook.prefix.path(venv))
|
rmtree(venv)
|
||||||
|
|
||||||
|
with clean_path_on_failure(venv):
|
||||||
lang.install_environment(
|
lang.install_environment(
|
||||||
hook.prefix, hook.language_version, hook.additional_dependencies,
|
hook.prefix, hook.language_version, hook.additional_dependencies,
|
||||||
)
|
)
|
||||||
|
@ -87,8 +94,17 @@ def _hook_install(hook: Hook) -> None:
|
||||||
f'your environment\n\n'
|
f'your environment\n\n'
|
||||||
f'more info:\n\n{health_error}',
|
f'more info:\n\n{health_error}',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# TODO: remove v1 state writing, no longer needed after pre-commit 3.0
|
||||||
# Write our state to indicate we're installed
|
# Write our state to indicate we're installed
|
||||||
_write_state(hook.prefix, venv, _state(hook.additional_dependencies))
|
state_filename = _state_filename_v1(venv)
|
||||||
|
staging = f'{state_filename}staging'
|
||||||
|
with open(staging, 'w') as state_file:
|
||||||
|
state_file.write(json.dumps(_state(hook.additional_dependencies)))
|
||||||
|
# Move the file into place atomically to indicate we've installed
|
||||||
|
os.replace(staging, state_filename)
|
||||||
|
|
||||||
|
open(_state_filename_v2(venv), 'a+').close()
|
||||||
|
|
||||||
|
|
||||||
def _hook(
|
def _hook(
|
||||||
|
|
Binary file not shown.
|
@ -36,6 +36,26 @@ def _get_default_directory() -> str:
|
||||||
return os.path.realpath(ret)
|
return os.path.realpath(ret)
|
||||||
|
|
||||||
|
|
||||||
|
_LOCAL_RESOURCES = (
|
||||||
|
'Cargo.toml', 'main.go', 'go.mod', 'main.rs', '.npmignore',
|
||||||
|
'package.json', 'pre-commit-package-dev-1.rockspec',
|
||||||
|
'pre_commit_placeholder_package.gemspec', 'setup.py',
|
||||||
|
'environment.yml', 'Makefile.PL', 'pubspec.yaml',
|
||||||
|
'renv.lock', 'renv/activate.R', 'renv/LICENSE.renv',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _make_local_repo(directory: str) -> None:
|
||||||
|
for resource in _LOCAL_RESOURCES:
|
||||||
|
resource_dirname, resource_basename = os.path.split(resource)
|
||||||
|
contents = resource_text(f'empty_template_{resource_basename}')
|
||||||
|
target_dir = os.path.join(directory, resource_dirname)
|
||||||
|
target_file = os.path.join(target_dir, resource_basename)
|
||||||
|
os.makedirs(target_dir, exist_ok=True)
|
||||||
|
with open(target_file, 'w') as f:
|
||||||
|
f.write(contents)
|
||||||
|
|
||||||
|
|
||||||
class Store:
|
class Store:
|
||||||
get_default_directory = staticmethod(_get_default_directory)
|
get_default_directory = staticmethod(_get_default_directory)
|
||||||
|
|
||||||
|
@ -185,37 +205,9 @@ class Store:
|
||||||
|
|
||||||
return self._new_repo(repo, ref, deps, clone_strategy)
|
return self._new_repo(repo, ref, deps, clone_strategy)
|
||||||
|
|
||||||
LOCAL_RESOURCES = (
|
|
||||||
'Cargo.toml', 'main.go', 'go.mod', 'main.rs', '.npmignore',
|
|
||||||
'package.json', 'pre-commit-package-dev-1.rockspec',
|
|
||||||
'pre_commit_placeholder_package.gemspec', 'setup.py',
|
|
||||||
'environment.yml', 'Makefile.PL', 'pubspec.yaml',
|
|
||||||
'renv.lock', 'renv/activate.R', 'renv/LICENSE.renv',
|
|
||||||
)
|
|
||||||
|
|
||||||
def make_local(self, deps: Sequence[str]) -> str:
|
def make_local(self, deps: Sequence[str]) -> str:
|
||||||
def make_local_strategy(directory: str) -> None:
|
|
||||||
for resource in self.LOCAL_RESOURCES:
|
|
||||||
resource_dirname, resource_basename = os.path.split(resource)
|
|
||||||
contents = resource_text(f'empty_template_{resource_basename}')
|
|
||||||
target_dir = os.path.join(directory, resource_dirname)
|
|
||||||
target_file = os.path.join(target_dir, resource_basename)
|
|
||||||
os.makedirs(target_dir, exist_ok=True)
|
|
||||||
with open(target_file, 'w') as f:
|
|
||||||
f.write(contents)
|
|
||||||
|
|
||||||
env = git.no_git_env()
|
|
||||||
|
|
||||||
# initialize the git repository so it looks more like cloned repos
|
|
||||||
def _git_cmd(*args: str) -> None:
|
|
||||||
cmd_output_b('git', *args, cwd=directory, env=env)
|
|
||||||
|
|
||||||
git.init_repo(directory, '<<unknown>>')
|
|
||||||
_git_cmd('add', '.')
|
|
||||||
git.commit(repo=directory)
|
|
||||||
|
|
||||||
return self._new_repo(
|
return self._new_repo(
|
||||||
'local', C.LOCAL_REPO_VERSION, deps, make_local_strategy,
|
'local', C.LOCAL_REPO_VERSION, deps, _make_local_repo,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _create_config_table(self, db: sqlite3.Connection) -> None:
|
def _create_config_table(self, db: sqlite3.Connection) -> None:
|
||||||
|
|
|
@ -2,36 +2,20 @@ from __future__ import annotations
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import errno
|
import errno
|
||||||
import functools
|
|
||||||
import importlib.resources
|
import importlib.resources
|
||||||
import os.path
|
import os.path
|
||||||
import shutil
|
import shutil
|
||||||
import stat
|
import stat
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
from typing import IO
|
from typing import IO
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from pre_commit import parse_shebang
|
from pre_commit import parse_shebang
|
||||||
|
|
||||||
Loader = getattr(yaml, 'CSafeLoader', yaml.SafeLoader)
|
|
||||||
yaml_load = functools.partial(yaml.load, Loader=Loader)
|
|
||||||
Dumper = getattr(yaml, 'CSafeDumper', yaml.SafeDumper)
|
|
||||||
|
|
||||||
|
|
||||||
def yaml_dump(o: Any, **kwargs: Any) -> str:
|
|
||||||
# when python/mypy#1484 is solved, this can be `functools.partial`
|
|
||||||
return yaml.dump(
|
|
||||||
o, Dumper=Dumper, default_flow_style=False, indent=4, sort_keys=False,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def force_bytes(exc: Any) -> bytes:
|
def force_bytes(exc: Any) -> bytes:
|
||||||
with contextlib.suppress(TypeError):
|
with contextlib.suppress(TypeError):
|
||||||
|
@ -52,18 +36,6 @@ def clean_path_on_failure(path: str) -> Generator[None, None, None]:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def tmpdir() -> Generator[str, None, None]:
|
|
||||||
"""Contextmanager to create a temporary directory. It will be cleaned up
|
|
||||||
afterwards.
|
|
||||||
"""
|
|
||||||
tempdir = tempfile.mkdtemp()
|
|
||||||
try:
|
|
||||||
yield tempdir
|
|
||||||
finally:
|
|
||||||
rmtree(tempdir)
|
|
||||||
|
|
||||||
|
|
||||||
def resource_bytesio(filename: str) -> IO[bytes]:
|
def resource_bytesio(filename: str) -> IO[bytes]:
|
||||||
return importlib.resources.open_binary('pre_commit.resources', filename)
|
return importlib.resources.open_binary('pre_commit.resources', filename)
|
||||||
|
|
||||||
|
@ -127,7 +99,7 @@ def cmd_output_b(
|
||||||
_setdefault_kwargs(kwargs)
|
_setdefault_kwargs(kwargs)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cmd = parse_shebang.normalize_cmd(cmd)
|
cmd = parse_shebang.normalize_cmd(cmd, env=kwargs.get('env'))
|
||||||
except parse_shebang.ExecutableNotFoundError as e:
|
except parse_shebang.ExecutableNotFoundError as e:
|
||||||
returncode, stdout_b, stderr_b = e.to_output()
|
returncode, stdout_b, stderr_b = e.to_output()
|
||||||
else:
|
else:
|
||||||
|
@ -254,10 +226,5 @@ def rmtree(path: str) -> None:
|
||||||
shutil.rmtree(path, ignore_errors=False, onerror=handle_remove_readonly)
|
shutil.rmtree(path, ignore_errors=False, onerror=handle_remove_readonly)
|
||||||
|
|
||||||
|
|
||||||
def parse_version(s: str) -> tuple[int, ...]:
|
|
||||||
"""poor man's version comparison"""
|
|
||||||
return tuple(int(p) for p in s.split('.'))
|
|
||||||
|
|
||||||
|
|
||||||
def win_exe(s: str) -> str:
|
def win_exe(s: str) -> str:
|
||||||
return s if sys.platform != 'win32' else f'{s}.exe'
|
return s if sys.platform != 'win32' else f'{s}.exe'
|
||||||
|
|
18
pre_commit/yaml.py
Normal file
18
pre_commit/yaml.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import functools
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
Loader = getattr(yaml, 'CSafeLoader', yaml.SafeLoader)
|
||||||
|
yaml_load = functools.partial(yaml.load, Loader=Loader)
|
||||||
|
Dumper = getattr(yaml, 'CSafeDumper', yaml.SafeDumper)
|
||||||
|
|
||||||
|
|
||||||
|
def yaml_dump(o: Any, **kwargs: Any) -> str:
|
||||||
|
# when python/mypy#1484 is solved, this can be `functools.partial`
|
||||||
|
return yaml.dump(
|
||||||
|
o, Dumper=Dumper, default_flow_style=False, indent=4, sort_keys=False,
|
||||||
|
**kwargs,
|
||||||
|
)
|
|
@ -1,6 +1,6 @@
|
||||||
[metadata]
|
[metadata]
|
||||||
name = pre_commit
|
name = pre_commit
|
||||||
version = 2.21.0
|
version = 3.0.2
|
||||||
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
|
||||||
|
@ -24,8 +24,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
|
||||||
importlib-metadata;python_version<"3.8"
|
python_requires = >=3.8
|
||||||
python_requires = >=3.7
|
|
||||||
|
|
||||||
[options.packages.find]
|
[options.packages.find]
|
||||||
exclude =
|
exclude =
|
||||||
|
@ -35,8 +34,6 @@ exclude =
|
||||||
[options.entry_points]
|
[options.entry_points]
|
||||||
console_scripts =
|
console_scripts =
|
||||||
pre-commit = pre_commit.main:main
|
pre-commit = pre_commit.main:main
|
||||||
pre-commit-validate-config = pre_commit.clientlib:validate_config_main
|
|
||||||
pre-commit-validate-manifest = pre_commit.clientlib:validate_manifest_main
|
|
||||||
|
|
||||||
[options.package_data]
|
[options.package_data]
|
||||||
pre_commit.resources =
|
pre_commit.resources =
|
||||||
|
|
|
@ -12,8 +12,8 @@ from pre_commit import git
|
||||||
from pre_commit.clientlib import CONFIG_SCHEMA
|
from pre_commit.clientlib import CONFIG_SCHEMA
|
||||||
from pre_commit.clientlib import load_manifest
|
from pre_commit.clientlib import load_manifest
|
||||||
from pre_commit.util import cmd_output
|
from pre_commit.util import cmd_output
|
||||||
from pre_commit.util import yaml_dump
|
from pre_commit.yaml import yaml_dump
|
||||||
from pre_commit.util import yaml_load
|
from pre_commit.yaml import yaml_load
|
||||||
from testing.util import get_resource_path
|
from testing.util import get_resource_path
|
||||||
from testing.util import git_commit
|
from testing.util import git_commit
|
||||||
|
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
LANGUAGES = (
|
|
||||||
'conda', 'coursier', 'dart', 'docker', 'docker_image', 'dotnet', 'fail',
|
|
||||||
'golang', 'lua', 'node', 'perl', 'pygrep', 'python', 'r', 'ruby', 'rust',
|
|
||||||
'script', 'swift', 'system',
|
|
||||||
)
|
|
||||||
FIELDS = (
|
|
||||||
'ENVIRONMENT_DIR', 'get_default_version', 'health_check',
|
|
||||||
'install_environment', 'run_hook',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
print(f' # BEGIN GENERATED ({sys.argv[0]})')
|
|
||||||
for lang in LANGUAGES:
|
|
||||||
parts = [f' {lang!r}: Language(name={lang!r}']
|
|
||||||
for k in FIELDS:
|
|
||||||
parts.append(f', {k}={lang}.{k}')
|
|
||||||
parts.append('), # noqa: E501')
|
|
||||||
print(''.join(parts))
|
|
||||||
print(' # END GENERATED')
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
raise SystemExit(main())
|
|
|
@ -1,11 +0,0 @@
|
||||||
$wc = New-Object System.Net.WebClient
|
|
||||||
|
|
||||||
$coursier_url = "https://github.com/coursier/coursier/releases/download/v2.0.5/cs-x86_64-pc-win32.exe"
|
|
||||||
$coursier_dest = "C:\coursier\cs.exe"
|
|
||||||
$coursier_hash ="d63d497f7805261e1cd657b8aaa626f6b8f7264cdb68219b2e6be9dd882033a9"
|
|
||||||
|
|
||||||
New-Item -Path "C:\" -Name "coursier" -ItemType "directory"
|
|
||||||
$wc.DownloadFile($coursier_url, $coursier_dest)
|
|
||||||
if ((Get-FileHash $coursier_dest -Algorithm SHA256).Hash -ne $coursier_hash) {
|
|
||||||
throw "Invalid coursier file"
|
|
||||||
}
|
|
|
@ -1,15 +1,29 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# This is a script used in CI to install coursier
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
COURSIER_URL="https://github.com/coursier/coursier/releases/download/v2.0.0/cs-x86_64-pc-linux"
|
if [ "$OSTYPE" = msys ]; then
|
||||||
COURSIER_HASH="e2e838b75bc71b16bcb77ce951ad65660c89bda7957c79a0628ec7146d35122f"
|
URL='https://github.com/coursier/coursier/releases/download/v2.1.0-RC4/cs-x86_64-pc-win32.zip'
|
||||||
ARTIFACT="/tmp/coursier/cs"
|
SHA256='0d07386ff0f337e3e6264f7dde29d137dda6eaa2385f29741435e0b93ccdb49d'
|
||||||
|
TARGET='/tmp/coursier/cs.zip'
|
||||||
|
|
||||||
|
unpack() {
|
||||||
|
unzip "$TARGET" -d /tmp/coursier
|
||||||
|
mv /tmp/coursier/cs-*.exe /tmp/coursier/cs.exe
|
||||||
|
cygpath -w /tmp/coursier >> "$GITHUB_PATH"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
URL='https://github.com/coursier/coursier/releases/download/v2.1.0-RC4/cs-x86_64-pc-linux.gz'
|
||||||
|
SHA256='176e92e08ab292531aa0c4993dbc9f2c99dec79578752f3b9285f54f306db572'
|
||||||
|
TARGET=/tmp/coursier/cs.gz
|
||||||
|
|
||||||
|
unpack() {
|
||||||
|
gunzip "$TARGET"
|
||||||
|
chmod +x /tmp/coursier/cs
|
||||||
|
echo /tmp/coursier >> "$GITHUB_PATH"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir -p /tmp/coursier
|
mkdir -p /tmp/coursier
|
||||||
rm -f "$ARTIFACT"
|
curl --location --silent --output "$TARGET" "$URL"
|
||||||
curl --location --silent --output "$ARTIFACT" "$COURSIER_URL"
|
echo "$SHA256 $TARGET" | sha256sum --check
|
||||||
echo "$COURSIER_HASH $ARTIFACT" | sha256sum --check
|
unpack
|
||||||
chmod ugo+x /tmp/coursier/cs
|
|
||||||
|
|
||||||
echo '##vso[task.prependpath]/tmp/coursier'
|
|
||||||
|
|
|
@ -5,10 +5,10 @@ VERSION=2.13.4
|
||||||
|
|
||||||
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"
|
||||||
echo "##vso[task.prependpath]$(cygpath -w /tmp/dart-sdk/bin)"
|
cygpath -w /tmp/dart-sdk/bin >> "$GITHUB_PATH"
|
||||||
else
|
else
|
||||||
URL="https://storage.googleapis.com/dart-archive/channels/stable/release/${VERSION}/sdk/dartsdk-linux-x64-release.zip"
|
URL="https://storage.googleapis.com/dart-archive/channels/stable/release/${VERSION}/sdk/dartsdk-linux-x64-release.zip"
|
||||||
echo '##vso[task.prependpath]/tmp/dart-sdk/bin'
|
echo '/tmp/dart-sdk/bin' >> "$GITHUB_PATH"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
curl --silent --location --output /tmp/dart.zip "$URL"
|
curl --silent --location --output /tmp/dart.zip "$URL"
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Install the runtime and package manager.
|
|
||||||
sudo apt install lua5.3 liblua5.3-dev luarocks
|
|
|
@ -1,6 +0,0 @@
|
||||||
$dir = $Env:Temp
|
|
||||||
$urlR = "https://cran.r-project.org/bin/windows/base/old/4.0.4/R-4.0.4-win.exe"
|
|
||||||
$outputR = "$dir\R-win.exe"
|
|
||||||
$wcR = New-Object System.Net.WebClient
|
|
||||||
$wcR.DownloadFile($urlR, $outputR)
|
|
||||||
Start-Process -FilePath $outputR -ArgumentList "/S /v/qn"
|
|
|
@ -1,9 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
sudo apt install r-base
|
|
||||||
# create empty folder for user library.
|
|
||||||
# necessary for non-root users who have
|
|
||||||
# never installed an R package before.
|
|
||||||
# Alternatively, we require the renv
|
|
||||||
# package to be installed already, then we can
|
|
||||||
# omit that.
|
|
||||||
Rscript -e 'dir.create(Sys.getenv("R_LIBS_USER"), recursive = TRUE)'
|
|
|
@ -26,4 +26,4 @@ fi
|
||||||
mkdir -p /tmp/swift
|
mkdir -p /tmp/swift
|
||||||
tar -xf "$TGZ" --strip 1 --directory /tmp/swift
|
tar -xf "$TGZ" --strip 1 --directory /tmp/swift
|
||||||
|
|
||||||
echo '##vso[task.prependpath]/tmp/swift/usr/bin'
|
echo '/tmp/swift/usr/bin' >> "$GITHUB_PATH"
|
||||||
|
|
35
testing/language_helpers.py
Normal file
35
testing/language_helpers.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
import pre_commit.constants as C
|
||||||
|
from pre_commit.languages.all import Language
|
||||||
|
from pre_commit.prefix import Prefix
|
||||||
|
|
||||||
|
|
||||||
|
def run_language(
|
||||||
|
path: os.PathLike[str],
|
||||||
|
language: Language,
|
||||||
|
exe: str,
|
||||||
|
args: Sequence[str] = (),
|
||||||
|
file_args: Sequence[str] = (),
|
||||||
|
version: str = C.DEFAULT,
|
||||||
|
deps: Sequence[str] = (),
|
||||||
|
is_local: bool = False,
|
||||||
|
) -> tuple[int, bytes]:
|
||||||
|
prefix = Prefix(str(path))
|
||||||
|
|
||||||
|
language.install_environment(prefix, version, deps)
|
||||||
|
with language.in_env(prefix, version):
|
||||||
|
ret, out = language.run_hook(
|
||||||
|
prefix,
|
||||||
|
exe,
|
||||||
|
args,
|
||||||
|
file_args,
|
||||||
|
is_local=is_local,
|
||||||
|
require_serial=True,
|
||||||
|
color=False,
|
||||||
|
)
|
||||||
|
out = out.replace(b'\r\n', b'\n')
|
||||||
|
return ret, out
|
|
@ -17,7 +17,7 @@ from typing import Sequence
|
||||||
|
|
||||||
REPOS = (
|
REPOS = (
|
||||||
('rbenv', 'https://github.com/rbenv/rbenv', '38e1fbb'),
|
('rbenv', 'https://github.com/rbenv/rbenv', '38e1fbb'),
|
||||||
('ruby-build', 'https://github.com/rbenv/ruby-build', '98c0337'),
|
('ruby-build', 'https://github.com/rbenv/ruby-build', '9d92a69'),
|
||||||
(
|
(
|
||||||
'ruby-download',
|
'ruby-download',
|
||||||
'https://github.com/garnieretienne/rvm-download',
|
'https://github.com/garnieretienne/rvm-download',
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
- id: sys-exec
|
|
||||||
name: sys-exec
|
|
||||||
entry: python -c 'import os; import sys; print(sys.executable.split(os.path.sep)[-2]) if os.name == "nt" else print(sys.executable.split(os.path.sep)[-3])'
|
|
||||||
language: conda
|
|
||||||
files: \.py$
|
|
||||||
- id: additional-deps
|
|
||||||
name: additional-deps
|
|
||||||
entry: python
|
|
||||||
language: conda
|
|
||||||
files: \.py$
|
|
|
@ -1,6 +0,0 @@
|
||||||
channels:
|
|
||||||
- conda-forge
|
|
||||||
- defaults
|
|
||||||
dependencies:
|
|
||||||
- python
|
|
||||||
- pip
|
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"repositories": [
|
|
||||||
"central"
|
|
||||||
],
|
|
||||||
"dependencies": [
|
|
||||||
"io.get-coursier:echo:latest.stable"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
- id: echo-java
|
|
||||||
name: echo-java
|
|
||||||
description: echo from java
|
|
||||||
entry: echo-java
|
|
||||||
language: coursier
|
|
|
@ -1,4 +0,0 @@
|
||||||
- id: hello-world-dart
|
|
||||||
name: hello world dart
|
|
||||||
entry: hello-world-dart
|
|
||||||
language: dart
|
|
|
@ -1,6 +0,0 @@
|
||||||
import 'package:ansicolor/ansicolor.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
AnsiPen pen = new AnsiPen()..red();
|
|
||||||
print("hello hello " + pen("world"));
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
environment:
|
|
||||||
sdk: '>=2.10.0 <3.0.0'
|
|
||||||
|
|
||||||
name: hello_world_dart
|
|
||||||
|
|
||||||
executables:
|
|
||||||
hello-world-dart:
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
ansicolor: ^2.0.1
|
|
|
@ -3,7 +3,9 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"runtime"
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
@ -11,7 +13,11 @@ type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
message := runtime.Version()
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
message = os.Args[1]
|
||||||
|
}
|
||||||
var conf Config
|
var conf Config
|
||||||
toml.Decode("What = 'world'\n", &conf)
|
toml.Decode("What = 'world'\n", &conf)
|
||||||
fmt.Printf("hello %v\n", conf.What)
|
fmt.Printf("hello %v from %s\n", conf.What, message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
- id: hello-world-lua
|
|
||||||
name: hello world lua
|
|
||||||
entry: hello-world-lua
|
|
||||||
language: lua
|
|
|
@ -1,3 +0,0 @@
|
||||||
#!/usr/bin/env lua
|
|
||||||
|
|
||||||
print('hello world')
|
|
|
@ -1,15 +0,0 @@
|
||||||
package = "hello"
|
|
||||||
version = "dev-1"
|
|
||||||
|
|
||||||
source = {
|
|
||||||
url = "git+ssh://git@github.com/pre-commit/pre-commit.git"
|
|
||||||
}
|
|
||||||
description = {}
|
|
||||||
dependencies = {}
|
|
||||||
build = {
|
|
||||||
type = "builtin",
|
|
||||||
modules = {},
|
|
||||||
install = {
|
|
||||||
bin = {"bin/hello-world-lua"}
|
|
||||||
},
|
|
||||||
}
|
|
7
testing/resources/perl_hooks_repo/.gitignore
vendored
7
testing/resources/perl_hooks_repo/.gitignore
vendored
|
@ -1,7 +0,0 @@
|
||||||
/MYMETA.json
|
|
||||||
/MYMETA.yml
|
|
||||||
/Makefile
|
|
||||||
/PreCommitHello-*.tar.*
|
|
||||||
/PreCommitHello-*/
|
|
||||||
/blib/
|
|
||||||
/pm_to_blib
|
|
|
@ -1,5 +0,0 @@
|
||||||
- id: perl-hook
|
|
||||||
name: perl example hook
|
|
||||||
entry: pre-commit-perl-hello
|
|
||||||
language: perl
|
|
||||||
files: ''
|
|
|
@ -1,4 +0,0 @@
|
||||||
MANIFEST
|
|
||||||
Makefile.PL
|
|
||||||
bin/pre-commit-perl-hello
|
|
||||||
lib/PreCommitHello.pm
|
|
|
@ -1,10 +0,0 @@
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
use ExtUtils::MakeMaker;
|
|
||||||
|
|
||||||
WriteMakefile(
|
|
||||||
NAME => "PreCommitHello",
|
|
||||||
VERSION_FROM => "lib/PreCommitHello.pm",
|
|
||||||
EXE_FILES => [qw(bin/pre-commit-perl-hello)],
|
|
||||||
);
|
|
|
@ -1,7 +0,0 @@
|
||||||
#!/usr/bin/env perl
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use PreCommitHello;
|
|
||||||
|
|
||||||
PreCommitHello::hello();
|
|
|
@ -1,12 +0,0 @@
|
||||||
package PreCommitHello;
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
our $VERSION = "0.1.0";
|
|
||||||
|
|
||||||
sub hello {
|
|
||||||
print "Hello from perl-commit Perl!\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
|
@ -1,48 +0,0 @@
|
||||||
# parsing file
|
|
||||||
- id: parse-file-no-opts-no-args
|
|
||||||
name: Say hi
|
|
||||||
entry: Rscript parse-file-no-opts-no-args.R
|
|
||||||
language: r
|
|
||||||
types: [r]
|
|
||||||
- id: parse-file-no-opts-args
|
|
||||||
name: Say hi
|
|
||||||
entry: Rscript parse-file-no-opts-args.R
|
|
||||||
args: [--no-cache]
|
|
||||||
language: r
|
|
||||||
types: [r]
|
|
||||||
## parsing expr
|
|
||||||
- id: parse-expr-no-opts-no-args-1
|
|
||||||
name: Say hi
|
|
||||||
entry: Rscript -e '1+1'
|
|
||||||
language: r
|
|
||||||
types: [r]
|
|
||||||
- id: parse-expr-args-in-entry-2
|
|
||||||
name: Say hi
|
|
||||||
entry: Rscript -e '1+1' -e '3' --no-cache3
|
|
||||||
language: r
|
|
||||||
types: [r]
|
|
||||||
# real world
|
|
||||||
- id: hello-world
|
|
||||||
name: Say hi
|
|
||||||
entry: Rscript hello-world.R
|
|
||||||
args: [blibla]
|
|
||||||
language: r
|
|
||||||
types: [r]
|
|
||||||
- id: hello-world-inline
|
|
||||||
name: Say hi
|
|
||||||
entry: |
|
|
||||||
Rscript -e
|
|
||||||
'stopifnot(
|
|
||||||
packageVersion("rprojroot") == "1.0",
|
|
||||||
packageVersion("gli.clu") == "0.0.0.9000"
|
|
||||||
)
|
|
||||||
cat(commandArgs(trailingOnly = TRUE), "from R!\n", sep = ", ")
|
|
||||||
'
|
|
||||||
args: ['Hi-there']
|
|
||||||
language: r
|
|
||||||
types: [r]
|
|
||||||
- id: additional-deps
|
|
||||||
name: Check additional deps
|
|
||||||
entry: Rscript additional-deps.R
|
|
||||||
language: r
|
|
||||||
types: [r]
|
|
|
@ -1,19 +0,0 @@
|
||||||
Package: gli.clu
|
|
||||||
Title: What the Package Does (One Line, Title Case)
|
|
||||||
Type: Package
|
|
||||||
Version: 0.0.0.9000
|
|
||||||
Authors@R:
|
|
||||||
person(given = "First",
|
|
||||||
family = "Last",
|
|
||||||
role = c("aut", "cre"),
|
|
||||||
email = "first.last@example.com",
|
|
||||||
comment = c(ORCID = "YOUR-ORCID-ID"))
|
|
||||||
Description: What the package does (one paragraph).
|
|
||||||
License: `use_mit_license()`, `use_gpl3_license()` or friends to
|
|
||||||
pick a license
|
|
||||||
Encoding: UTF-8
|
|
||||||
LazyData: true
|
|
||||||
Roxygen: list(markdown = TRUE)
|
|
||||||
RoxygenNote: 7.1.1
|
|
||||||
Imports:
|
|
||||||
rprojroot
|
|
|
@ -1,2 +0,0 @@
|
||||||
suppressPackageStartupMessages(library("cachem"))
|
|
||||||
cat("OK\n")
|
|
|
@ -1,5 +0,0 @@
|
||||||
stopifnot(
|
|
||||||
packageVersion('rprojroot') == '1.0',
|
|
||||||
packageVersion('gli.clu') == '0.0.0.9000'
|
|
||||||
)
|
|
||||||
cat("Hello, World, from R!\n")
|
|
|
@ -1,27 +0,0 @@
|
||||||
{
|
|
||||||
"R": {
|
|
||||||
"Version": "4.0.3",
|
|
||||||
"Repositories": [
|
|
||||||
{
|
|
||||||
"Name": "CRAN",
|
|
||||||
"URL": "https://cloud.r-project.org"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Packages": {
|
|
||||||
"renv": {
|
|
||||||
"Package": "renv",
|
|
||||||
"Version": "0.12.5",
|
|
||||||
"Source": "Repository",
|
|
||||||
"Repository": "CRAN",
|
|
||||||
"Hash": "5c0cdb37f063c58cdab3c7e9fbb8bd2c"
|
|
||||||
},
|
|
||||||
"rprojroot": {
|
|
||||||
"Package": "rprojroot",
|
|
||||||
"Version": "1.0",
|
|
||||||
"Source": "Repository",
|
|
||||||
"Repository": "CRAN",
|
|
||||||
"Hash": "86704667fe0860e4fec35afdfec137f3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
Copyright 2021 RStudio, PBC
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -1,440 +0,0 @@
|
||||||
|
|
||||||
local({
|
|
||||||
|
|
||||||
# the requested version of renv
|
|
||||||
version <- "0.12.5"
|
|
||||||
|
|
||||||
# the project directory
|
|
||||||
project <- getwd()
|
|
||||||
|
|
||||||
# avoid recursion
|
|
||||||
if (!is.na(Sys.getenv("RENV_R_INITIALIZING", unset = NA)))
|
|
||||||
return(invisible(TRUE))
|
|
||||||
|
|
||||||
# signal that we're loading renv during R startup
|
|
||||||
Sys.setenv("RENV_R_INITIALIZING" = "true")
|
|
||||||
on.exit(Sys.unsetenv("RENV_R_INITIALIZING"), add = TRUE)
|
|
||||||
|
|
||||||
# signal that we've consented to use renv
|
|
||||||
options(renv.consent = TRUE)
|
|
||||||
|
|
||||||
# load the 'utils' package eagerly -- this ensures that renv shims, which
|
|
||||||
# mask 'utils' packages, will come first on the search path
|
|
||||||
library(utils, lib.loc = .Library)
|
|
||||||
|
|
||||||
# check to see if renv has already been loaded
|
|
||||||
if ("renv" %in% loadedNamespaces()) {
|
|
||||||
|
|
||||||
# if renv has already been loaded, and it's the requested version of renv,
|
|
||||||
# nothing to do
|
|
||||||
spec <- .getNamespaceInfo(.getNamespace("renv"), "spec")
|
|
||||||
if (identical(spec[["version"]], version))
|
|
||||||
return(invisible(TRUE))
|
|
||||||
|
|
||||||
# otherwise, unload and attempt to load the correct version of renv
|
|
||||||
unloadNamespace("renv")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# load bootstrap tools
|
|
||||||
bootstrap <- function(version, library) {
|
|
||||||
|
|
||||||
# attempt to download renv
|
|
||||||
tarball <- tryCatch(renv_bootstrap_download(version), error = identity)
|
|
||||||
if (inherits(tarball, "error"))
|
|
||||||
stop("failed to download renv ", version)
|
|
||||||
|
|
||||||
# now attempt to install
|
|
||||||
status <- tryCatch(renv_bootstrap_install(version, tarball, library), error = identity)
|
|
||||||
if (inherits(status, "error"))
|
|
||||||
stop("failed to install renv ", version)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_tests_running <- function() {
|
|
||||||
getOption("renv.tests.running", default = FALSE)
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_repos <- function() {
|
|
||||||
|
|
||||||
# check for repos override
|
|
||||||
repos <- Sys.getenv("RENV_CONFIG_REPOS_OVERRIDE", unset = NA)
|
|
||||||
if (!is.na(repos))
|
|
||||||
return(repos)
|
|
||||||
|
|
||||||
# if we're testing, re-use the test repositories
|
|
||||||
if (renv_bootstrap_tests_running())
|
|
||||||
return(getOption("renv.tests.repos"))
|
|
||||||
|
|
||||||
# retrieve current repos
|
|
||||||
repos <- getOption("repos")
|
|
||||||
|
|
||||||
# ensure @CRAN@ entries are resolved
|
|
||||||
repos[repos == "@CRAN@"] <- "https://cloud.r-project.org"
|
|
||||||
|
|
||||||
# add in renv.bootstrap.repos if set
|
|
||||||
default <- c(CRAN = "https://cloud.r-project.org")
|
|
||||||
extra <- getOption("renv.bootstrap.repos", default = default)
|
|
||||||
repos <- c(repos, extra)
|
|
||||||
|
|
||||||
# remove duplicates that might've snuck in
|
|
||||||
dupes <- duplicated(repos) | duplicated(names(repos))
|
|
||||||
repos[!dupes]
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_download <- function(version) {
|
|
||||||
|
|
||||||
# if the renv version number has 4 components, assume it must
|
|
||||||
# be retrieved via github
|
|
||||||
nv <- numeric_version(version)
|
|
||||||
components <- unclass(nv)[[1]]
|
|
||||||
|
|
||||||
methods <- if (length(components) == 4L) {
|
|
||||||
list(
|
|
||||||
renv_bootstrap_download_github
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
list(
|
|
||||||
renv_bootstrap_download_cran_latest,
|
|
||||||
renv_bootstrap_download_cran_archive
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (method in methods) {
|
|
||||||
path <- tryCatch(method(version), error = identity)
|
|
||||||
if (is.character(path) && file.exists(path))
|
|
||||||
return(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
stop("failed to download renv ", version)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_download_impl <- function(url, destfile) {
|
|
||||||
|
|
||||||
mode <- "wb"
|
|
||||||
|
|
||||||
# https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17715
|
|
||||||
fixup <-
|
|
||||||
Sys.info()[["sysname"]] == "Windows" &&
|
|
||||||
substring(url, 1L, 5L) == "file:"
|
|
||||||
|
|
||||||
if (fixup)
|
|
||||||
mode <- "w+b"
|
|
||||||
|
|
||||||
utils::download.file(
|
|
||||||
url = url,
|
|
||||||
destfile = destfile,
|
|
||||||
mode = mode,
|
|
||||||
quiet = TRUE
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_download_cran_latest <- function(version) {
|
|
||||||
|
|
||||||
repos <- renv_bootstrap_download_cran_latest_find(version)
|
|
||||||
|
|
||||||
message("* Downloading renv ", version, " from CRAN ... ", appendLF = FALSE)
|
|
||||||
|
|
||||||
info <- tryCatch(
|
|
||||||
utils::download.packages(
|
|
||||||
pkgs = "renv",
|
|
||||||
repos = repos,
|
|
||||||
destdir = tempdir(),
|
|
||||||
quiet = TRUE
|
|
||||||
),
|
|
||||||
condition = identity
|
|
||||||
)
|
|
||||||
|
|
||||||
if (inherits(info, "condition")) {
|
|
||||||
message("FAILED")
|
|
||||||
return(FALSE)
|
|
||||||
}
|
|
||||||
|
|
||||||
message("OK")
|
|
||||||
info[1, 2]
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_download_cran_latest_find <- function(version) {
|
|
||||||
|
|
||||||
all <- renv_bootstrap_repos()
|
|
||||||
|
|
||||||
for (repos in all) {
|
|
||||||
|
|
||||||
db <- tryCatch(
|
|
||||||
as.data.frame(
|
|
||||||
x = utils::available.packages(repos = repos),
|
|
||||||
stringsAsFactors = FALSE
|
|
||||||
),
|
|
||||||
error = identity
|
|
||||||
)
|
|
||||||
|
|
||||||
if (inherits(db, "error"))
|
|
||||||
next
|
|
||||||
|
|
||||||
entry <- db[db$Package %in% "renv" & db$Version %in% version, ]
|
|
||||||
if (nrow(entry) == 0)
|
|
||||||
next
|
|
||||||
|
|
||||||
return(repos)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt <- "renv %s is not available from your declared package repositories"
|
|
||||||
stop(sprintf(fmt, version))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_download_cran_archive <- function(version) {
|
|
||||||
|
|
||||||
name <- sprintf("renv_%s.tar.gz", version)
|
|
||||||
repos <- renv_bootstrap_repos()
|
|
||||||
urls <- file.path(repos, "src/contrib/Archive/renv", name)
|
|
||||||
destfile <- file.path(tempdir(), name)
|
|
||||||
|
|
||||||
message("* Downloading renv ", version, " from CRAN archive ... ", appendLF = FALSE)
|
|
||||||
|
|
||||||
for (url in urls) {
|
|
||||||
|
|
||||||
status <- tryCatch(
|
|
||||||
renv_bootstrap_download_impl(url, destfile),
|
|
||||||
condition = identity
|
|
||||||
)
|
|
||||||
|
|
||||||
if (identical(status, 0L)) {
|
|
||||||
message("OK")
|
|
||||||
return(destfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
message("FAILED")
|
|
||||||
return(FALSE)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_download_github <- function(version) {
|
|
||||||
|
|
||||||
enabled <- Sys.getenv("RENV_BOOTSTRAP_FROM_GITHUB", unset = "TRUE")
|
|
||||||
if (!identical(enabled, "TRUE"))
|
|
||||||
return(FALSE)
|
|
||||||
|
|
||||||
# prepare download options
|
|
||||||
pat <- Sys.getenv("GITHUB_PAT")
|
|
||||||
if (nzchar(Sys.which("curl")) && nzchar(pat)) {
|
|
||||||
fmt <- "--location --fail --header \"Authorization: token %s\""
|
|
||||||
extra <- sprintf(fmt, pat)
|
|
||||||
saved <- options("download.file.method", "download.file.extra")
|
|
||||||
options(download.file.method = "curl", download.file.extra = extra)
|
|
||||||
on.exit(do.call(base::options, saved), add = TRUE)
|
|
||||||
} else if (nzchar(Sys.which("wget")) && nzchar(pat)) {
|
|
||||||
fmt <- "--header=\"Authorization: token %s\""
|
|
||||||
extra <- sprintf(fmt, pat)
|
|
||||||
saved <- options("download.file.method", "download.file.extra")
|
|
||||||
options(download.file.method = "wget", download.file.extra = extra)
|
|
||||||
on.exit(do.call(base::options, saved), add = TRUE)
|
|
||||||
}
|
|
||||||
|
|
||||||
message("* Downloading renv ", version, " from GitHub ... ", appendLF = FALSE)
|
|
||||||
|
|
||||||
url <- file.path("https://api.github.com/repos/rstudio/renv/tarball", version)
|
|
||||||
name <- sprintf("renv_%s.tar.gz", version)
|
|
||||||
destfile <- file.path(tempdir(), name)
|
|
||||||
|
|
||||||
status <- tryCatch(
|
|
||||||
renv_bootstrap_download_impl(url, destfile),
|
|
||||||
condition = identity
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!identical(status, 0L)) {
|
|
||||||
message("FAILED")
|
|
||||||
return(FALSE)
|
|
||||||
}
|
|
||||||
|
|
||||||
message("OK")
|
|
||||||
return(destfile)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_install <- function(version, tarball, library) {
|
|
||||||
|
|
||||||
# attempt to install it into project library
|
|
||||||
message("* Installing renv ", version, " ... ", appendLF = FALSE)
|
|
||||||
dir.create(library, showWarnings = FALSE, recursive = TRUE)
|
|
||||||
|
|
||||||
# invoke using system2 so we can capture and report output
|
|
||||||
bin <- R.home("bin")
|
|
||||||
exe <- if (Sys.info()[["sysname"]] == "Windows") "R.exe" else "R"
|
|
||||||
r <- file.path(bin, exe)
|
|
||||||
args <- c("--vanilla", "CMD", "INSTALL", "-l", shQuote(library), shQuote(tarball))
|
|
||||||
output <- system2(r, args, stdout = TRUE, stderr = TRUE)
|
|
||||||
message("Done!")
|
|
||||||
|
|
||||||
# check for successful install
|
|
||||||
status <- attr(output, "status")
|
|
||||||
if (is.numeric(status) && !identical(status, 0L)) {
|
|
||||||
header <- "Error installing renv:"
|
|
||||||
lines <- paste(rep.int("=", nchar(header)), collapse = "")
|
|
||||||
text <- c(header, lines, output)
|
|
||||||
writeLines(text, con = stderr())
|
|
||||||
}
|
|
||||||
|
|
||||||
status
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_prefix <- function() {
|
|
||||||
|
|
||||||
# construct version prefix
|
|
||||||
version <- paste(R.version$major, R.version$minor, sep = ".")
|
|
||||||
prefix <- paste("R", numeric_version(version)[1, 1:2], sep = "-")
|
|
||||||
|
|
||||||
# include SVN revision for development versions of R
|
|
||||||
# (to avoid sharing platform-specific artefacts with released versions of R)
|
|
||||||
devel <-
|
|
||||||
identical(R.version[["status"]], "Under development (unstable)") ||
|
|
||||||
identical(R.version[["nickname"]], "Unsuffered Consequences")
|
|
||||||
|
|
||||||
if (devel)
|
|
||||||
prefix <- paste(prefix, R.version[["svn rev"]], sep = "-r")
|
|
||||||
|
|
||||||
# build list of path components
|
|
||||||
components <- c(prefix, R.version$platform)
|
|
||||||
|
|
||||||
# include prefix if provided by user
|
|
||||||
prefix <- Sys.getenv("RENV_PATHS_PREFIX")
|
|
||||||
if (nzchar(prefix))
|
|
||||||
components <- c(prefix, components)
|
|
||||||
|
|
||||||
# build prefix
|
|
||||||
paste(components, collapse = "/")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_library_root_name <- function(project) {
|
|
||||||
|
|
||||||
# use project name as-is if requested
|
|
||||||
asis <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT_ASIS", unset = "FALSE")
|
|
||||||
if (asis)
|
|
||||||
return(basename(project))
|
|
||||||
|
|
||||||
# otherwise, disambiguate based on project's path
|
|
||||||
id <- substring(renv_bootstrap_hash_text(project), 1L, 8L)
|
|
||||||
paste(basename(project), id, sep = "-")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_library_root <- function(project) {
|
|
||||||
|
|
||||||
path <- Sys.getenv("RENV_PATHS_LIBRARY", unset = NA)
|
|
||||||
if (!is.na(path))
|
|
||||||
return(path)
|
|
||||||
|
|
||||||
path <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT", unset = NA)
|
|
||||||
if (!is.na(path)) {
|
|
||||||
name <- renv_bootstrap_library_root_name(project)
|
|
||||||
return(file.path(path, name))
|
|
||||||
}
|
|
||||||
|
|
||||||
file.path(project, "renv/library")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_validate_version <- function(version) {
|
|
||||||
|
|
||||||
loadedversion <- utils::packageDescription("renv", fields = "Version")
|
|
||||||
if (version == loadedversion)
|
|
||||||
return(TRUE)
|
|
||||||
|
|
||||||
# assume four-component versions are from GitHub; three-component
|
|
||||||
# versions are from CRAN
|
|
||||||
components <- strsplit(loadedversion, "[.-]")[[1]]
|
|
||||||
remote <- if (length(components) == 4L)
|
|
||||||
paste("rstudio/renv", loadedversion, sep = "@")
|
|
||||||
else
|
|
||||||
paste("renv", loadedversion, sep = "@")
|
|
||||||
|
|
||||||
fmt <- paste(
|
|
||||||
"renv %1$s was loaded from project library, but this project is configured to use renv %2$s.",
|
|
||||||
"Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile.",
|
|
||||||
"Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library.",
|
|
||||||
sep = "\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
msg <- sprintf(fmt, loadedversion, version, remote)
|
|
||||||
warning(msg, call. = FALSE)
|
|
||||||
|
|
||||||
FALSE
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_hash_text <- function(text) {
|
|
||||||
|
|
||||||
hashfile <- tempfile("renv-hash-")
|
|
||||||
on.exit(unlink(hashfile), add = TRUE)
|
|
||||||
|
|
||||||
writeLines(text, con = hashfile)
|
|
||||||
tools::md5sum(hashfile)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renv_bootstrap_load <- function(project, libpath, version) {
|
|
||||||
|
|
||||||
# try to load renv from the project library
|
|
||||||
if (!requireNamespace("renv", lib.loc = libpath, quietly = TRUE))
|
|
||||||
return(FALSE)
|
|
||||||
|
|
||||||
# warn if the version of renv loaded does not match
|
|
||||||
renv_bootstrap_validate_version(version)
|
|
||||||
|
|
||||||
# load the project
|
|
||||||
renv::load(project)
|
|
||||||
|
|
||||||
TRUE
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# construct path to library root
|
|
||||||
root <- renv_bootstrap_library_root(project)
|
|
||||||
|
|
||||||
# construct library prefix for platform
|
|
||||||
prefix <- renv_bootstrap_prefix()
|
|
||||||
|
|
||||||
# construct full libpath
|
|
||||||
libpath <- file.path(root, prefix)
|
|
||||||
|
|
||||||
# attempt to load
|
|
||||||
if (renv_bootstrap_load(project, libpath, version))
|
|
||||||
return(TRUE)
|
|
||||||
|
|
||||||
# load failed; inform user we're about to bootstrap
|
|
||||||
prefix <- paste("# Bootstrapping renv", version)
|
|
||||||
postfix <- paste(rep.int("-", 77L - nchar(prefix)), collapse = "")
|
|
||||||
header <- paste(prefix, postfix)
|
|
||||||
message(header)
|
|
||||||
|
|
||||||
# perform bootstrap
|
|
||||||
bootstrap(version, libpath)
|
|
||||||
|
|
||||||
# exit early if we're just testing bootstrap
|
|
||||||
if (!is.na(Sys.getenv("RENV_BOOTSTRAP_INSTALL_ONLY", unset = NA)))
|
|
||||||
return(TRUE)
|
|
||||||
|
|
||||||
# try again to load
|
|
||||||
if (requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) {
|
|
||||||
message("* Successfully installed and loaded renv ", version, ".")
|
|
||||||
return(renv::load())
|
|
||||||
}
|
|
||||||
|
|
||||||
# failed to download or load renv; warn the user
|
|
||||||
msg <- c(
|
|
||||||
"Failed to find an renv installation: the project will not be loaded.",
|
|
||||||
"Use `renv::activate()` to re-initialize the project."
|
|
||||||
)
|
|
||||||
|
|
||||||
warning(paste(msg, collapse = "\n"), call. = FALSE)
|
|
||||||
|
|
||||||
})
|
|
|
@ -2,5 +2,5 @@
|
||||||
name: Ruby Hook
|
name: Ruby Hook
|
||||||
entry: ruby_hook
|
entry: ruby_hook
|
||||||
language: ruby
|
language: ruby
|
||||||
language_version: 3.1.0
|
language_version: 3.2.0
|
||||||
files: \.rb$
|
files: \.rb$
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
/.build
|
|
||||||
/Packages
|
|
||||||
/*.xcodeproj
|
|
|
@ -1,6 +0,0 @@
|
||||||
- id: swift-hooks-repo
|
|
||||||
name: Swift hooks repo example
|
|
||||||
description: Runs the hello world app generated by swift package init --type executable (binary called swift_hooks_repo here)
|
|
||||||
entry: swift_hooks_repo
|
|
||||||
language: swift
|
|
||||||
files: \.(swift)$
|
|
|
@ -1,7 +0,0 @@
|
||||||
// swift-tools-version:5.0
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "swift_hooks_repo",
|
|
||||||
targets: [.target(name: "swift_hooks_repo")]
|
|
||||||
)
|
|
|
@ -1 +0,0 @@
|
||||||
print("Hello, world!")
|
|
|
@ -6,7 +6,6 @@ import subprocess
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from pre_commit import parse_shebang
|
|
||||||
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
|
||||||
|
@ -42,22 +41,10 @@ def cmd_output_mocked_pre_commit_home(
|
||||||
return ret, out.replace('\r\n', '\n'), None
|
return ret, out.replace('\r\n', '\n'), None
|
||||||
|
|
||||||
|
|
||||||
skipif_cant_run_coursier = pytest.mark.skipif(
|
|
||||||
os.name == 'nt' or parse_shebang.find_executable('cs') is None,
|
|
||||||
reason="coursier isn't installed or can't be found",
|
|
||||||
)
|
|
||||||
skipif_cant_run_docker = pytest.mark.skipif(
|
skipif_cant_run_docker = pytest.mark.skipif(
|
||||||
os.name == 'nt' or not docker_is_running(),
|
os.name == 'nt' or not docker_is_running(),
|
||||||
reason="Docker isn't running or can't be accessed",
|
reason="Docker isn't running or can't be accessed",
|
||||||
)
|
)
|
||||||
skipif_cant_run_lua = pytest.mark.skipif(
|
|
||||||
os.name == 'nt',
|
|
||||||
reason="lua isn't installed or can't be found",
|
|
||||||
)
|
|
||||||
skipif_cant_run_swift = pytest.mark.skipif(
|
|
||||||
parse_shebang.find_executable('swift') is None,
|
|
||||||
reason="swift isn't installed or can't be found",
|
|
||||||
)
|
|
||||||
xfailif_windows = pytest.mark.xfail(os.name == 'nt', reason='windows')
|
xfailif_windows = pytest.mark.xfail(os.name == 'nt', reason='windows')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,9 @@ from pre_commit.clientlib import CONFIG_SCHEMA
|
||||||
from pre_commit.clientlib import DEFAULT_LANGUAGE_VERSION
|
from pre_commit.clientlib import DEFAULT_LANGUAGE_VERSION
|
||||||
from pre_commit.clientlib import MANIFEST_SCHEMA
|
from pre_commit.clientlib import MANIFEST_SCHEMA
|
||||||
from pre_commit.clientlib import META_HOOK_DICT
|
from pre_commit.clientlib import META_HOOK_DICT
|
||||||
from pre_commit.clientlib import MigrateShaToRev
|
|
||||||
from pre_commit.clientlib import OptionalSensibleRegexAtHook
|
from pre_commit.clientlib import OptionalSensibleRegexAtHook
|
||||||
from pre_commit.clientlib import OptionalSensibleRegexAtTop
|
from pre_commit.clientlib import OptionalSensibleRegexAtTop
|
||||||
from pre_commit.clientlib import validate_config_main
|
from pre_commit.clientlib import parse_version
|
||||||
from pre_commit.clientlib import validate_manifest_main
|
|
||||||
from testing.fixtures import sample_local_config
|
from testing.fixtures import sample_local_config
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,78 +110,6 @@ def test_config_schema_does_not_contain_defaults():
|
||||||
assert not isinstance(item, cfgv.Optional)
|
assert not isinstance(item, cfgv.Optional)
|
||||||
|
|
||||||
|
|
||||||
def test_validate_manifest_main_ok():
|
|
||||||
assert not validate_manifest_main(('.pre-commit-hooks.yaml',))
|
|
||||||
|
|
||||||
|
|
||||||
def test_validate_config_main_ok():
|
|
||||||
assert not validate_config_main(('.pre-commit-config.yaml',))
|
|
||||||
|
|
||||||
|
|
||||||
def test_validate_config_old_list_format_ok(tmpdir, cap_out):
|
|
||||||
f = tmpdir.join('cfg.yaml')
|
|
||||||
f.write('- {repo: meta, hooks: [{id: identity}]}')
|
|
||||||
assert not validate_config_main((f.strpath,))
|
|
||||||
msg = '[WARNING] normalizing pre-commit configuration to a top-level map'
|
|
||||||
assert msg in cap_out.get()
|
|
||||||
|
|
||||||
|
|
||||||
def test_validate_warn_on_unknown_keys_at_repo_level(tmpdir, caplog):
|
|
||||||
f = tmpdir.join('cfg.yaml')
|
|
||||||
f.write(
|
|
||||||
'repos:\n'
|
|
||||||
'- repo: https://gitlab.com/pycqa/flake8\n'
|
|
||||||
' rev: 3.7.7\n'
|
|
||||||
' hooks:\n'
|
|
||||||
' - id: flake8\n'
|
|
||||||
' args: [--some-args]\n',
|
|
||||||
)
|
|
||||||
ret_val = validate_config_main((f.strpath,))
|
|
||||||
assert not ret_val
|
|
||||||
assert caplog.record_tuples == [
|
|
||||||
(
|
|
||||||
'pre_commit',
|
|
||||||
logging.WARNING,
|
|
||||||
'pre-commit-validate-config is deprecated -- '
|
|
||||||
'use `pre-commit validate-config` instead.',
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'pre_commit',
|
|
||||||
logging.WARNING,
|
|
||||||
'Unexpected key(s) present on https://gitlab.com/pycqa/flake8: '
|
|
||||||
'args',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def test_validate_warn_on_unknown_keys_at_top_level(tmpdir, caplog):
|
|
||||||
f = tmpdir.join('cfg.yaml')
|
|
||||||
f.write(
|
|
||||||
'repos:\n'
|
|
||||||
'- repo: https://gitlab.com/pycqa/flake8\n'
|
|
||||||
' rev: 3.7.7\n'
|
|
||||||
' hooks:\n'
|
|
||||||
' - id: flake8\n'
|
|
||||||
'foo:\n'
|
|
||||||
' id: 1.0.0\n',
|
|
||||||
)
|
|
||||||
ret_val = validate_config_main((f.strpath,))
|
|
||||||
assert not ret_val
|
|
||||||
assert caplog.record_tuples == [
|
|
||||||
(
|
|
||||||
'pre_commit',
|
|
||||||
logging.WARNING,
|
|
||||||
'pre-commit-validate-config is deprecated -- '
|
|
||||||
'use `pre-commit validate-config` instead.',
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'pre_commit',
|
|
||||||
logging.WARNING,
|
|
||||||
'Unexpected key(s) present at root: foo',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def test_ci_map_key_allowed_at_top_level(caplog):
|
def test_ci_map_key_allowed_at_top_level(caplog):
|
||||||
cfg = {
|
cfg = {
|
||||||
'ci': {'skip': ['foo']},
|
'ci': {'skip': ['foo']},
|
||||||
|
@ -370,18 +296,6 @@ def test_validate_optional_sensible_regex_at_top_level(caplog, regex, warning):
|
||||||
assert caplog.record_tuples == [('pre_commit', logging.WARNING, warning)]
|
assert caplog.record_tuples == [('pre_commit', logging.WARNING, warning)]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('fn', (validate_config_main, validate_manifest_main))
|
|
||||||
def test_mains_not_ok(tmpdir, fn):
|
|
||||||
not_yaml = tmpdir.join('f.notyaml')
|
|
||||||
not_yaml.write('{')
|
|
||||||
not_schema = tmpdir.join('notconfig.yaml')
|
|
||||||
not_schema.write('{}')
|
|
||||||
|
|
||||||
assert fn(('does-not-exist',))
|
|
||||||
assert fn((not_yaml.strpath,))
|
|
||||||
assert fn((not_schema.strpath,))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
('manifest_obj', 'expected'),
|
('manifest_obj', 'expected'),
|
||||||
(
|
(
|
||||||
|
@ -425,48 +339,6 @@ def test_valid_manifests(manifest_obj, expected):
|
||||||
assert ret is expected
|
assert ret is expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
'dct',
|
|
||||||
(
|
|
||||||
{'repo': 'local'}, {'repo': 'meta'},
|
|
||||||
{'repo': 'wat', 'sha': 'wat'}, {'repo': 'wat', 'rev': 'wat'},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
def test_migrate_sha_to_rev_ok(dct):
|
|
||||||
MigrateShaToRev().check(dct)
|
|
||||||
|
|
||||||
|
|
||||||
def test_migrate_sha_to_rev_dont_specify_both():
|
|
||||||
with pytest.raises(cfgv.ValidationError) as excinfo:
|
|
||||||
MigrateShaToRev().check({'repo': 'a', 'sha': 'b', 'rev': 'c'})
|
|
||||||
msg, = excinfo.value.args
|
|
||||||
assert msg == 'Cannot specify both sha and rev'
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
'dct',
|
|
||||||
(
|
|
||||||
{'repo': 'a'},
|
|
||||||
{'repo': 'meta', 'sha': 'a'}, {'repo': 'meta', 'rev': 'a'},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
def test_migrate_sha_to_rev_conditional_check_failures(dct):
|
|
||||||
with pytest.raises(cfgv.ValidationError):
|
|
||||||
MigrateShaToRev().check(dct)
|
|
||||||
|
|
||||||
|
|
||||||
def test_migrate_to_sha_apply_default():
|
|
||||||
dct = {'repo': 'a', 'sha': 'b'}
|
|
||||||
MigrateShaToRev().apply_default(dct)
|
|
||||||
assert dct == {'repo': 'a', 'rev': 'b'}
|
|
||||||
|
|
||||||
|
|
||||||
def test_migrate_to_sha_ok():
|
|
||||||
dct = {'repo': 'a', 'rev': 'b'}
|
|
||||||
MigrateShaToRev().apply_default(dct)
|
|
||||||
assert dct == {'repo': 'a', 'rev': 'b'}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'config_repo',
|
'config_repo',
|
||||||
(
|
(
|
||||||
|
@ -513,6 +385,12 @@ def test_default_language_version_invalid(mapping):
|
||||||
cfgv.validate(mapping, DEFAULT_LANGUAGE_VERSION)
|
cfgv.validate(mapping, DEFAULT_LANGUAGE_VERSION)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_version():
|
||||||
|
assert parse_version('0.0') == parse_version('0.0')
|
||||||
|
assert parse_version('0.1') > parse_version('0.0')
|
||||||
|
assert parse_version('2.1') >= parse_version('2')
|
||||||
|
|
||||||
|
|
||||||
def test_minimum_pre_commit_version_failing():
|
def test_minimum_pre_commit_version_failing():
|
||||||
with pytest.raises(cfgv.ValidationError) as excinfo:
|
with pytest.raises(cfgv.ValidationError) as excinfo:
|
||||||
cfg = {'repos': [], 'minimum_pre_commit_version': '999'}
|
cfg = {'repos': [], 'minimum_pre_commit_version': '999'}
|
||||||
|
|
|
@ -4,12 +4,11 @@ import shlex
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import yaml
|
|
||||||
|
|
||||||
import pre_commit.constants as C
|
import pre_commit.constants as C
|
||||||
from pre_commit import envcontext
|
from pre_commit import envcontext
|
||||||
from pre_commit import git
|
from pre_commit import git
|
||||||
from pre_commit import util
|
from pre_commit import yaml
|
||||||
from pre_commit.commands.autoupdate import _check_hooks_still_exist_at_rev
|
from pre_commit.commands.autoupdate import _check_hooks_still_exist_at_rev
|
||||||
from pre_commit.commands.autoupdate import autoupdate
|
from pre_commit.commands.autoupdate import autoupdate
|
||||||
from pre_commit.commands.autoupdate import RepositoryCannotBeUpdatedError
|
from pre_commit.commands.autoupdate import RepositoryCannotBeUpdatedError
|
||||||
|
@ -206,7 +205,7 @@ def test_autoupdate_with_core_useBuiltinFSMonitor(out_of_date, tmpdir, store):
|
||||||
|
|
||||||
|
|
||||||
def test_autoupdate_pure_yaml(out_of_date, tmpdir, store):
|
def test_autoupdate_pure_yaml(out_of_date, tmpdir, store):
|
||||||
with mock.patch.object(util, 'Dumper', yaml.SafeDumper):
|
with mock.patch.object(yaml, 'Dumper', yaml.yaml.SafeDumper):
|
||||||
test_autoupdate_out_of_date_repo(out_of_date, tmpdir, store)
|
test_autoupdate_out_of_date_repo(out_of_date, tmpdir, store)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -248,7 +248,7 @@ def test_install_idempotent(tempdir_factory, store):
|
||||||
def _path_without_us():
|
def _path_without_us():
|
||||||
# Choose a path which *probably* doesn't include us
|
# Choose a path which *probably* doesn't include us
|
||||||
env = dict(os.environ)
|
env = dict(os.environ)
|
||||||
exe = find_executable('pre-commit', _environ=env)
|
exe = find_executable('pre-commit', env=env)
|
||||||
while exe:
|
while exe:
|
||||||
parts = env['PATH'].split(os.pathsep)
|
parts = env['PATH'].split(os.pathsep)
|
||||||
after = [
|
after = [
|
||||||
|
@ -258,7 +258,7 @@ def _path_without_us():
|
||||||
if parts == after:
|
if parts == after:
|
||||||
raise AssertionError(exe, parts)
|
raise AssertionError(exe, parts)
|
||||||
env['PATH'] = os.pathsep.join(after)
|
env['PATH'] = os.pathsep.join(after)
|
||||||
exe = find_executable('pre-commit', _environ=env)
|
exe = find_executable('pre-commit', env=env)
|
||||||
return env['PATH']
|
return env['PATH']
|
||||||
|
|
||||||
|
|
||||||
|
@ -276,8 +276,7 @@ def test_environment_not_sourced(tempdir_factory, store):
|
||||||
|
|
||||||
# Use a specific homedir to ignore --user installs
|
# Use a specific homedir to ignore --user installs
|
||||||
homedir = tempdir_factory.get()
|
homedir = tempdir_factory.get()
|
||||||
ret, out = git_commit(
|
env = {
|
||||||
env={
|
|
||||||
'HOME': homedir,
|
'HOME': homedir,
|
||||||
'PATH': _path_without_us(),
|
'PATH': _path_without_us(),
|
||||||
# Git needs this to make a commit
|
# Git needs this to make a commit
|
||||||
|
@ -285,9 +284,11 @@ def test_environment_not_sourced(tempdir_factory, store):
|
||||||
'GIT_COMMITTER_NAME': os.environ['GIT_COMMITTER_NAME'],
|
'GIT_COMMITTER_NAME': os.environ['GIT_COMMITTER_NAME'],
|
||||||
'GIT_AUTHOR_EMAIL': os.environ['GIT_AUTHOR_EMAIL'],
|
'GIT_AUTHOR_EMAIL': os.environ['GIT_AUTHOR_EMAIL'],
|
||||||
'GIT_COMMITTER_EMAIL': os.environ['GIT_COMMITTER_EMAIL'],
|
'GIT_COMMITTER_EMAIL': os.environ['GIT_COMMITTER_EMAIL'],
|
||||||
},
|
}
|
||||||
check=False,
|
if os.name == 'nt' and 'PATHEXT' in os.environ: # pragma: no cover
|
||||||
)
|
env['PATHEXT'] = os.environ['PATHEXT']
|
||||||
|
|
||||||
|
ret, out = git_commit(env=env, check=False)
|
||||||
assert ret == 1
|
assert ret == 1
|
||||||
assert out == (
|
assert out == (
|
||||||
'`pre-commit` not found. '
|
'`pre-commit` not found. '
|
||||||
|
@ -739,7 +740,8 @@ def test_commit_msg_legacy(commit_msg_repo, tempdir_factory, store):
|
||||||
|
|
||||||
def test_post_commit_integration(tempdir_factory, store):
|
def test_post_commit_integration(tempdir_factory, store):
|
||||||
path = git_dir(tempdir_factory)
|
path = git_dir(tempdir_factory)
|
||||||
config = [
|
config = {
|
||||||
|
'repos': [
|
||||||
{
|
{
|
||||||
'repo': 'local',
|
'repo': 'local',
|
||||||
'hooks': [{
|
'hooks': [{
|
||||||
|
@ -752,7 +754,8 @@ def test_post_commit_integration(tempdir_factory, store):
|
||||||
'stages': ['post-commit'],
|
'stages': ['post-commit'],
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
}
|
||||||
write_config(path, config)
|
write_config(path, config)
|
||||||
with cwd(path):
|
with cwd(path):
|
||||||
_get_commit_output(tempdir_factory)
|
_get_commit_output(tempdir_factory)
|
||||||
|
@ -765,7 +768,8 @@ def test_post_commit_integration(tempdir_factory, store):
|
||||||
|
|
||||||
def test_post_merge_integration(tempdir_factory, store):
|
def test_post_merge_integration(tempdir_factory, store):
|
||||||
path = git_dir(tempdir_factory)
|
path = git_dir(tempdir_factory)
|
||||||
config = [
|
config = {
|
||||||
|
'repos': [
|
||||||
{
|
{
|
||||||
'repo': 'local',
|
'repo': 'local',
|
||||||
'hooks': [{
|
'hooks': [{
|
||||||
|
@ -778,7 +782,8 @@ def test_post_merge_integration(tempdir_factory, store):
|
||||||
'stages': ['post-merge'],
|
'stages': ['post-merge'],
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
}
|
||||||
write_config(path, config)
|
write_config(path, config)
|
||||||
with cwd(path):
|
with cwd(path):
|
||||||
# create a simple diamond of commits for a non-trivial merge
|
# create a simple diamond of commits for a non-trivial merge
|
||||||
|
@ -807,7 +812,8 @@ def test_post_merge_integration(tempdir_factory, store):
|
||||||
|
|
||||||
def test_post_rewrite_integration(tempdir_factory, store):
|
def test_post_rewrite_integration(tempdir_factory, store):
|
||||||
path = git_dir(tempdir_factory)
|
path = git_dir(tempdir_factory)
|
||||||
config = [
|
config = {
|
||||||
|
'repos': [
|
||||||
{
|
{
|
||||||
'repo': 'local',
|
'repo': 'local',
|
||||||
'hooks': [{
|
'hooks': [{
|
||||||
|
@ -820,7 +826,8 @@ def test_post_rewrite_integration(tempdir_factory, store):
|
||||||
'stages': ['post-rewrite'],
|
'stages': ['post-rewrite'],
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
}
|
||||||
write_config(path, config)
|
write_config(path, config)
|
||||||
with cwd(path):
|
with cwd(path):
|
||||||
open('init', 'a').close()
|
open('init', 'a').close()
|
||||||
|
@ -836,7 +843,8 @@ def test_post_rewrite_integration(tempdir_factory, store):
|
||||||
|
|
||||||
def test_post_checkout_integration(tempdir_factory, store):
|
def test_post_checkout_integration(tempdir_factory, store):
|
||||||
path = git_dir(tempdir_factory)
|
path = git_dir(tempdir_factory)
|
||||||
config = [
|
config = {
|
||||||
|
'repos': [
|
||||||
{
|
{
|
||||||
'repo': 'local',
|
'repo': 'local',
|
||||||
'hooks': [{
|
'hooks': [{
|
||||||
|
@ -850,7 +858,8 @@ def test_post_checkout_integration(tempdir_factory, store):
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
{'repo': 'meta', 'hooks': [{'id': 'identity'}]},
|
{'repo': 'meta', 'hooks': [{'id': 'identity'}]},
|
||||||
]
|
],
|
||||||
|
}
|
||||||
write_config(path, config)
|
write_config(path, config)
|
||||||
with cwd(path):
|
with cwd(path):
|
||||||
cmd_output('git', 'add', '.')
|
cmd_output('git', 'add', '.')
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
import pre_commit.constants as C
|
import pre_commit.constants as C
|
||||||
|
from pre_commit.clientlib import InvalidConfigError
|
||||||
from pre_commit.commands.migrate_config import migrate_config
|
from pre_commit.commands.migrate_config import migrate_config
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,3 +132,13 @@ def test_migrate_config_sha_to_rev(tmpdir):
|
||||||
' rev: v1.2.0\n'
|
' rev: v1.2.0\n'
|
||||||
' hooks: []\n'
|
' hooks: []\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_migrate_config_invalid_yaml(tmpdir):
|
||||||
|
contents = '['
|
||||||
|
cfg = tmpdir.join(C.CONFIG_FILE)
|
||||||
|
cfg.write(contents)
|
||||||
|
with tmpdir.as_cwd(), pytest.raises(InvalidConfigError) as excinfo:
|
||||||
|
migrate_config(C.CONFIG_FILE)
|
||||||
|
expected = '\n==> File .pre-commit-config.yaml\n=====> '
|
||||||
|
assert str(excinfo.value).startswith(expected)
|
||||||
|
|
64
tests/commands/validate_config_test.py
Normal file
64
tests/commands/validate_config_test.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from pre_commit.commands.validate_config import validate_config
|
||||||
|
|
||||||
|
|
||||||
|
def test_validate_config_ok():
|
||||||
|
assert not validate_config(('.pre-commit-config.yaml',))
|
||||||
|
|
||||||
|
|
||||||
|
def test_validate_warn_on_unknown_keys_at_repo_level(tmpdir, caplog):
|
||||||
|
f = tmpdir.join('cfg.yaml')
|
||||||
|
f.write(
|
||||||
|
'repos:\n'
|
||||||
|
'- repo: https://gitlab.com/pycqa/flake8\n'
|
||||||
|
' rev: 3.7.7\n'
|
||||||
|
' hooks:\n'
|
||||||
|
' - id: flake8\n'
|
||||||
|
' args: [--some-args]\n',
|
||||||
|
)
|
||||||
|
ret_val = validate_config((f.strpath,))
|
||||||
|
assert not ret_val
|
||||||
|
assert caplog.record_tuples == [
|
||||||
|
(
|
||||||
|
'pre_commit',
|
||||||
|
logging.WARNING,
|
||||||
|
'Unexpected key(s) present on https://gitlab.com/pycqa/flake8: '
|
||||||
|
'args',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_validate_warn_on_unknown_keys_at_top_level(tmpdir, caplog):
|
||||||
|
f = tmpdir.join('cfg.yaml')
|
||||||
|
f.write(
|
||||||
|
'repos:\n'
|
||||||
|
'- repo: https://gitlab.com/pycqa/flake8\n'
|
||||||
|
' rev: 3.7.7\n'
|
||||||
|
' hooks:\n'
|
||||||
|
' - id: flake8\n'
|
||||||
|
'foo:\n'
|
||||||
|
' id: 1.0.0\n',
|
||||||
|
)
|
||||||
|
ret_val = validate_config((f.strpath,))
|
||||||
|
assert not ret_val
|
||||||
|
assert caplog.record_tuples == [
|
||||||
|
(
|
||||||
|
'pre_commit',
|
||||||
|
logging.WARNING,
|
||||||
|
'Unexpected key(s) present at root: foo',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_mains_not_ok(tmpdir):
|
||||||
|
not_yaml = tmpdir.join('f.notyaml')
|
||||||
|
not_yaml.write('{')
|
||||||
|
not_schema = tmpdir.join('notconfig.yaml')
|
||||||
|
not_schema.write('{}')
|
||||||
|
|
||||||
|
assert validate_config(('does-not-exist',))
|
||||||
|
assert validate_config((not_yaml.strpath,))
|
||||||
|
assert validate_config((not_schema.strpath,))
|
18
tests/commands/validate_manifest_test.py
Normal file
18
tests/commands/validate_manifest_test.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pre_commit.commands.validate_manifest import validate_manifest
|
||||||
|
|
||||||
|
|
||||||
|
def test_validate_manifest_ok():
|
||||||
|
assert not validate_manifest(('.pre-commit-hooks.yaml',))
|
||||||
|
|
||||||
|
|
||||||
|
def test_not_ok(tmpdir):
|
||||||
|
not_yaml = tmpdir.join('f.notyaml')
|
||||||
|
not_yaml.write('{')
|
||||||
|
not_schema = tmpdir.join('notconfig.yaml')
|
||||||
|
not_schema.write('{}')
|
||||||
|
|
||||||
|
assert validate_manifest(('does-not-exist',))
|
||||||
|
assert validate_manifest((not_yaml.strpath,))
|
||||||
|
assert validate_manifest((not_schema.strpath,))
|
|
@ -1,9 +1,13 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from pre_commit import envcontext
|
from pre_commit import envcontext
|
||||||
from pre_commit.languages.conda import _conda_exe
|
from pre_commit.languages import conda
|
||||||
|
from pre_commit.store import _make_local_repo
|
||||||
|
from testing.language_helpers import run_language
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -37,4 +41,32 @@ from pre_commit.languages.conda import _conda_exe
|
||||||
)
|
)
|
||||||
def test_conda_exe(ctx, expected):
|
def test_conda_exe(ctx, expected):
|
||||||
with envcontext.envcontext(ctx):
|
with envcontext.envcontext(ctx):
|
||||||
assert _conda_exe() == expected
|
assert conda._conda_exe() == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_conda_language(tmp_path):
|
||||||
|
environment_yml = '''\
|
||||||
|
channels: [conda-forge, defaults]
|
||||||
|
dependencies: [python, pip]
|
||||||
|
'''
|
||||||
|
tmp_path.joinpath('environment.yml').write_text(environment_yml)
|
||||||
|
|
||||||
|
ret, out = run_language(
|
||||||
|
tmp_path,
|
||||||
|
conda,
|
||||||
|
'python -c "import sys; print(sys.prefix)"',
|
||||||
|
)
|
||||||
|
assert ret == 0
|
||||||
|
assert os.path.basename(out.strip()) == b'conda-default'
|
||||||
|
|
||||||
|
|
||||||
|
def test_conda_additional_deps(tmp_path):
|
||||||
|
_make_local_repo(tmp_path)
|
||||||
|
|
||||||
|
ret = run_language(
|
||||||
|
tmp_path,
|
||||||
|
conda,
|
||||||
|
'python -c "import botocore; print(1)"',
|
||||||
|
deps=('botocore',),
|
||||||
|
)
|
||||||
|
assert ret == (0, b'1\n')
|
||||||
|
|
45
tests/languages/coursier_test.py
Normal file
45
tests/languages/coursier_test.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from pre_commit.errors import FatalError
|
||||||
|
from pre_commit.languages import coursier
|
||||||
|
from testing.language_helpers import run_language
|
||||||
|
|
||||||
|
|
||||||
|
def test_coursier_hook(tmp_path):
|
||||||
|
echo_java_json = '''\
|
||||||
|
{
|
||||||
|
"repositories": ["central"],
|
||||||
|
"dependencies": ["io.get-coursier:echo:latest.stable"]
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
channel_dir = tmp_path.joinpath('.pre-commit-channel')
|
||||||
|
channel_dir.mkdir()
|
||||||
|
channel_dir.joinpath('echo-java.json').write_text(echo_java_json)
|
||||||
|
|
||||||
|
ret = run_language(
|
||||||
|
tmp_path,
|
||||||
|
coursier,
|
||||||
|
'echo-java',
|
||||||
|
args=('Hello', 'World', 'from', 'coursier'),
|
||||||
|
)
|
||||||
|
assert ret == (0, b'Hello World from coursier\n')
|
||||||
|
|
||||||
|
|
||||||
|
def test_coursier_hook_additional_dependencies(tmp_path):
|
||||||
|
ret = run_language(
|
||||||
|
tmp_path,
|
||||||
|
coursier,
|
||||||
|
'scalafmt --version',
|
||||||
|
deps=('scalafmt:3.6.1',),
|
||||||
|
)
|
||||||
|
assert ret == (0, b'scalafmt 3.6.1\n')
|
||||||
|
|
||||||
|
|
||||||
|
def test_error_if_no_deps_or_channel(tmp_path):
|
||||||
|
with pytest.raises(FatalError) as excinfo:
|
||||||
|
run_language(tmp_path, coursier, 'dne')
|
||||||
|
msg, = excinfo.value.args
|
||||||
|
assert msg == 'expected .pre-commit-channel dir or additional_dependencies'
|
62
tests/languages/dart_test.py
Normal file
62
tests/languages/dart_test.py
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re_assert
|
||||||
|
|
||||||
|
from pre_commit.languages import dart
|
||||||
|
from pre_commit.store import _make_local_repo
|
||||||
|
from testing.language_helpers import run_language
|
||||||
|
|
||||||
|
|
||||||
|
def test_dart(tmp_path):
|
||||||
|
pubspec_yaml = '''\
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.10.0 <3.0.0'
|
||||||
|
|
||||||
|
name: hello_world_dart
|
||||||
|
|
||||||
|
executables:
|
||||||
|
hello-world-dart:
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
ansicolor: ^2.0.1
|
||||||
|
'''
|
||||||
|
hello_world_dart_dart = '''\
|
||||||
|
import 'package:ansicolor/ansicolor.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
AnsiPen pen = new AnsiPen()..red();
|
||||||
|
print("hello hello " + pen("world"));
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
tmp_path.joinpath('pubspec.yaml').write_text(pubspec_yaml)
|
||||||
|
bin_dir = tmp_path.joinpath('bin')
|
||||||
|
bin_dir.mkdir()
|
||||||
|
bin_dir.joinpath('hello-world-dart.dart').write_text(hello_world_dart_dart)
|
||||||
|
|
||||||
|
expected = (0, b'hello hello world\n')
|
||||||
|
assert run_language(tmp_path, dart, 'hello-world-dart') == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_dart_additional_deps(tmp_path):
|
||||||
|
_make_local_repo(str(tmp_path))
|
||||||
|
|
||||||
|
ret = run_language(
|
||||||
|
tmp_path,
|
||||||
|
dart,
|
||||||
|
'hello-world-dart',
|
||||||
|
deps=('hello_world_dart',),
|
||||||
|
)
|
||||||
|
assert ret == (0, b'hello hello world\n')
|
||||||
|
|
||||||
|
|
||||||
|
def test_dart_additional_deps_versioned(tmp_path):
|
||||||
|
_make_local_repo(str(tmp_path))
|
||||||
|
|
||||||
|
ret, out = run_language(
|
||||||
|
tmp_path,
|
||||||
|
dart,
|
||||||
|
'secure-random -l 4 -b 16',
|
||||||
|
deps=('encrypt:5.0.0',),
|
||||||
|
)
|
||||||
|
assert ret == 0
|
||||||
|
re_assert.Matches('^[a-f0-9]{8}\n$').assert_matches(out.decode())
|
|
@ -1,22 +1,43 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from pre_commit.languages.golang import guess_go_dir
|
import pre_commit.constants as C
|
||||||
|
from pre_commit.languages import golang
|
||||||
|
from pre_commit.languages import helpers
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
ACTUAL_GET_DEFAULT_VERSION = golang.get_default_version.__wrapped__
|
||||||
('url', 'expected'),
|
|
||||||
(
|
|
||||||
('/im/a/path/on/disk', 'unknown_src_dir'),
|
@pytest.fixture
|
||||||
('file:///im/a/path/on/disk', 'unknown_src_dir'),
|
def exe_exists_mck():
|
||||||
('git@github.com:golang/lint', 'github.com/golang/lint'),
|
with mock.patch.object(helpers, 'exe_exists') as mck:
|
||||||
('git://github.com/golang/lint', 'github.com/golang/lint'),
|
yield mck
|
||||||
('http://github.com/golang/lint', 'github.com/golang/lint'),
|
|
||||||
('https://github.com/golang/lint', 'github.com/golang/lint'),
|
|
||||||
('ssh://git@github.com/golang/lint', 'github.com/golang/lint'),
|
def test_golang_default_version_system_available(exe_exists_mck):
|
||||||
('git@github.com:golang/lint.git', 'github.com/golang/lint'),
|
exe_exists_mck.return_value = True
|
||||||
),
|
assert ACTUAL_GET_DEFAULT_VERSION() == 'system'
|
||||||
)
|
|
||||||
def test_guess_go_dir(url, expected):
|
|
||||||
assert guess_go_dir(url) == expected
|
def test_golang_default_version_system_not_available(exe_exists_mck):
|
||||||
|
exe_exists_mck.return_value = False
|
||||||
|
assert ACTUAL_GET_DEFAULT_VERSION() == C.DEFAULT
|
||||||
|
|
||||||
|
|
||||||
|
ACTUAL_INFER_GO_VERSION = golang._infer_go_version.__wrapped__
|
||||||
|
|
||||||
|
|
||||||
|
def test_golang_infer_go_version_not_default():
|
||||||
|
assert ACTUAL_INFER_GO_VERSION('1.19.4') == '1.19.4'
|
||||||
|
|
||||||
|
|
||||||
|
def test_golang_infer_go_version_default():
|
||||||
|
version = ACTUAL_INFER_GO_VERSION(C.DEFAULT)
|
||||||
|
|
||||||
|
assert version != C.DEFAULT
|
||||||
|
assert re.match(r'^\d+\.\d+\.\d+$', version)
|
||||||
|
|
|
@ -12,7 +12,6 @@ from pre_commit import parse_shebang
|
||||||
from pre_commit.languages import helpers
|
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 testing.auto_namedtuple import auto_namedtuple
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -94,31 +93,22 @@ def test_assert_no_additional_deps():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
SERIAL_FALSE = auto_namedtuple(require_serial=False)
|
|
||||||
SERIAL_TRUE = auto_namedtuple(require_serial=True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_concurrency_normal():
|
def test_target_concurrency_normal():
|
||||||
with mock.patch.object(multiprocessing, 'cpu_count', return_value=123):
|
with mock.patch.object(multiprocessing, 'cpu_count', return_value=123):
|
||||||
with mock.patch.dict(os.environ, {}, clear=True):
|
with mock.patch.dict(os.environ, {}, clear=True):
|
||||||
assert helpers.target_concurrency(SERIAL_FALSE) == 123
|
assert helpers.target_concurrency() == 123
|
||||||
|
|
||||||
|
|
||||||
def test_target_concurrency_cpu_count_require_serial_true():
|
|
||||||
with mock.patch.dict(os.environ, {}, clear=True):
|
|
||||||
assert helpers.target_concurrency(SERIAL_TRUE) == 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_concurrency_testing_env_var():
|
def test_target_concurrency_testing_env_var():
|
||||||
with mock.patch.dict(
|
with mock.patch.dict(
|
||||||
os.environ, {'PRE_COMMIT_NO_CONCURRENCY': '1'}, clear=True,
|
os.environ, {'PRE_COMMIT_NO_CONCURRENCY': '1'}, clear=True,
|
||||||
):
|
):
|
||||||
assert helpers.target_concurrency(SERIAL_FALSE) == 1
|
assert helpers.target_concurrency() == 1
|
||||||
|
|
||||||
|
|
||||||
def test_target_concurrency_on_travis():
|
def test_target_concurrency_on_travis():
|
||||||
with mock.patch.dict(os.environ, {'TRAVIS': '1'}, clear=True):
|
with mock.patch.dict(os.environ, {'TRAVIS': '1'}, clear=True):
|
||||||
assert helpers.target_concurrency(SERIAL_FALSE) == 2
|
assert helpers.target_concurrency() == 2
|
||||||
|
|
||||||
|
|
||||||
def test_target_concurrency_cpu_count_not_implemented():
|
def test_target_concurrency_cpu_count_not_implemented():
|
||||||
|
@ -126,10 +116,20 @@ def test_target_concurrency_cpu_count_not_implemented():
|
||||||
multiprocessing, 'cpu_count', side_effect=NotImplementedError,
|
multiprocessing, 'cpu_count', side_effect=NotImplementedError,
|
||||||
):
|
):
|
||||||
with mock.patch.dict(os.environ, {}, clear=True):
|
with mock.patch.dict(os.environ, {}, clear=True):
|
||||||
assert helpers.target_concurrency(SERIAL_FALSE) == 1
|
assert helpers.target_concurrency() == 1
|
||||||
|
|
||||||
|
|
||||||
def test_shuffled_is_deterministic():
|
def test_shuffled_is_deterministic():
|
||||||
seq = [str(i) for i in range(10)]
|
seq = [str(i) for i in range(10)]
|
||||||
expected = ['4', '0', '5', '1', '8', '6', '2', '3', '7', '9']
|
expected = ['4', '0', '5', '1', '8', '6', '2', '3', '7', '9']
|
||||||
assert helpers._shuffled(seq) == expected
|
assert helpers._shuffled(seq) == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_xargs_require_serial_is_not_shuffled():
|
||||||
|
ret, out = helpers.run_xargs(
|
||||||
|
('echo',), [str(i) for i in range(10)],
|
||||||
|
require_serial=True,
|
||||||
|
color=False,
|
||||||
|
)
|
||||||
|
assert ret == 0
|
||||||
|
assert out.strip() == b'0 1 2 3 4 5 6 7 8 9'
|
||||||
|
|
58
tests/languages/lua_test.py
Normal file
58
tests/languages/lua_test.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from pre_commit.languages import lua
|
||||||
|
from pre_commit.util import make_executable
|
||||||
|
from testing.language_helpers import run_language
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.skipif(
|
||||||
|
sys.platform == 'win32',
|
||||||
|
reason='lua is not supported on windows',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_lua(tmp_path): # pragma: win32 no cover
|
||||||
|
rockspec = '''\
|
||||||
|
package = "hello"
|
||||||
|
version = "dev-1"
|
||||||
|
|
||||||
|
source = {
|
||||||
|
url = "git+ssh://git@github.com/pre-commit/pre-commit.git"
|
||||||
|
}
|
||||||
|
description = {}
|
||||||
|
dependencies = {}
|
||||||
|
build = {
|
||||||
|
type = "builtin",
|
||||||
|
modules = {},
|
||||||
|
install = {
|
||||||
|
bin = {"bin/hello-world-lua"}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
hello_world_lua = '''\
|
||||||
|
#!/usr/bin/env lua
|
||||||
|
print('hello world')
|
||||||
|
'''
|
||||||
|
tmp_path.joinpath('hello-dev-1.rockspec').write_text(rockspec)
|
||||||
|
bin_dir = tmp_path.joinpath('bin')
|
||||||
|
bin_dir.mkdir()
|
||||||
|
bin_file = bin_dir.joinpath('hello-world-lua')
|
||||||
|
bin_file.write_text(hello_world_lua)
|
||||||
|
make_executable(bin_file)
|
||||||
|
|
||||||
|
expected = (0, b'hello world\n')
|
||||||
|
assert run_language(tmp_path, lua, 'hello-world-lua') == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_lua_additional_dependencies(tmp_path): # pragma: win32 no cover
|
||||||
|
ret, out = run_language(
|
||||||
|
tmp_path,
|
||||||
|
lua,
|
||||||
|
'luacheck --version',
|
||||||
|
deps=('luacheck',),
|
||||||
|
)
|
||||||
|
assert ret == 0
|
||||||
|
assert out.startswith(b'Luacheck: ')
|
69
tests/languages/perl_test.py
Normal file
69
tests/languages/perl_test.py
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pre_commit.languages import perl
|
||||||
|
from pre_commit.store import _make_local_repo
|
||||||
|
from pre_commit.util import make_executable
|
||||||
|
from testing.language_helpers import run_language
|
||||||
|
|
||||||
|
|
||||||
|
def test_perl_install(tmp_path):
|
||||||
|
makefile_pl = '''\
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use ExtUtils::MakeMaker;
|
||||||
|
|
||||||
|
WriteMakefile(
|
||||||
|
NAME => "PreCommitHello",
|
||||||
|
VERSION_FROM => "lib/PreCommitHello.pm",
|
||||||
|
EXE_FILES => [qw(bin/pre-commit-perl-hello)],
|
||||||
|
);
|
||||||
|
'''
|
||||||
|
bin_perl_hello = '''\
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use PreCommitHello;
|
||||||
|
|
||||||
|
PreCommitHello::hello();
|
||||||
|
'''
|
||||||
|
lib_hello_pm = '''\
|
||||||
|
package PreCommitHello;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
our $VERSION = "0.1.0";
|
||||||
|
|
||||||
|
sub hello {
|
||||||
|
print "Hello from perl-commit Perl!\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
'''
|
||||||
|
tmp_path.joinpath('Makefile.PL').write_text(makefile_pl)
|
||||||
|
bin_dir = tmp_path.joinpath('bin')
|
||||||
|
bin_dir.mkdir()
|
||||||
|
exe = bin_dir.joinpath('pre-commit-perl-hello')
|
||||||
|
exe.write_text(bin_perl_hello)
|
||||||
|
make_executable(exe)
|
||||||
|
lib_dir = tmp_path.joinpath('lib')
|
||||||
|
lib_dir.mkdir()
|
||||||
|
lib_dir.joinpath('PreCommitHello.pm').write_text(lib_hello_pm)
|
||||||
|
|
||||||
|
ret = run_language(tmp_path, perl, 'pre-commit-perl-hello')
|
||||||
|
assert ret == (0, b'Hello from perl-commit Perl!\n')
|
||||||
|
|
||||||
|
|
||||||
|
def test_perl_additional_dependencies(tmp_path):
|
||||||
|
_make_local_repo(str(tmp_path))
|
||||||
|
|
||||||
|
ret, out = run_language(
|
||||||
|
tmp_path,
|
||||||
|
perl,
|
||||||
|
'perltidy --version',
|
||||||
|
deps=('SHANCOCK/Perl-Tidy-20211029.tar.gz',),
|
||||||
|
)
|
||||||
|
assert ret == 0
|
||||||
|
assert out.startswith(b'This is perltidy, v20211029')
|
|
@ -1,136 +1,119 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
|
import shutil
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from pre_commit import envcontext
|
from pre_commit import envcontext
|
||||||
from pre_commit.languages import r
|
from pre_commit.languages import r
|
||||||
|
from pre_commit.prefix import Prefix
|
||||||
|
from pre_commit.store import _make_local_repo
|
||||||
from pre_commit.util import win_exe
|
from pre_commit.util import win_exe
|
||||||
from testing.fixtures import make_config_from_repo
|
from testing.language_helpers import run_language
|
||||||
from testing.fixtures import make_repo
|
|
||||||
from tests.repository_test import _get_hook_no_install
|
|
||||||
|
|
||||||
|
|
||||||
def _test_r_parsing(
|
def test_r_parsing_file_no_opts_no_args(tmp_path):
|
||||||
tempdir_factory,
|
cmd = r._cmd_from_hook(
|
||||||
store,
|
Prefix(str(tmp_path)),
|
||||||
hook_id,
|
'Rscript some-script.R',
|
||||||
expected_hook_expr={},
|
(),
|
||||||
expected_args={},
|
is_local=False,
|
||||||
config={},
|
)
|
||||||
expect_path_prefix=True,
|
assert cmd == (
|
||||||
):
|
'Rscript',
|
||||||
repo_path = 'r_hooks_repo'
|
|
||||||
path = make_repo(tempdir_factory, repo_path)
|
|
||||||
config = config or make_config_from_repo(path)
|
|
||||||
hook = _get_hook_no_install(config, store, hook_id)
|
|
||||||
ret = r._cmd_from_hook(hook)
|
|
||||||
expected_cmd = 'Rscript'
|
|
||||||
expected_opts = (
|
|
||||||
'--no-save', '--no-restore', '--no-site-file', '--no-environ',
|
'--no-save', '--no-restore', '--no-site-file', '--no-environ',
|
||||||
|
str(tmp_path.joinpath('some-script.R')),
|
||||||
)
|
)
|
||||||
expected_path = os.path.join(
|
|
||||||
hook.prefix.prefix_dir if expect_path_prefix else '',
|
|
||||||
f'{hook_id}.R',
|
|
||||||
)
|
|
||||||
expected = (
|
|
||||||
expected_cmd,
|
|
||||||
*expected_opts,
|
|
||||||
*(expected_hook_expr or (expected_path,)),
|
|
||||||
*expected_args,
|
|
||||||
)
|
|
||||||
assert ret == expected
|
|
||||||
|
|
||||||
|
|
||||||
def test_r_parsing_file_no_opts_no_args(tempdir_factory, store):
|
def test_r_parsing_file_opts_no_args():
|
||||||
hook_id = 'parse-file-no-opts-no-args'
|
|
||||||
_test_r_parsing(tempdir_factory, store, hook_id)
|
|
||||||
|
|
||||||
|
|
||||||
def test_r_parsing_file_opts_no_args(tempdir_factory, store):
|
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
r._entry_validate(['Rscript', '--no-init', '/path/to/file'])
|
r._entry_validate(['Rscript', '--no-init', '/path/to/file'])
|
||||||
|
|
||||||
msg = excinfo.value.args
|
msg, = excinfo.value.args
|
||||||
assert msg == (
|
assert msg == (
|
||||||
'The only valid syntax is `Rscript -e {expr}`',
|
'The only valid syntax is `Rscript -e {expr}`'
|
||||||
'or `Rscript path/to/hook/script`',
|
'or `Rscript path/to/hook/script`'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_r_parsing_file_no_opts_args(tempdir_factory, store):
|
def test_r_parsing_file_no_opts_args(tmp_path):
|
||||||
hook_id = 'parse-file-no-opts-args'
|
cmd = r._cmd_from_hook(
|
||||||
expected_args = ['--no-cache']
|
Prefix(str(tmp_path)),
|
||||||
_test_r_parsing(
|
'Rscript some-script.R',
|
||||||
tempdir_factory, store, hook_id, expected_args=expected_args,
|
('--no-cache',),
|
||||||
|
is_local=False,
|
||||||
|
)
|
||||||
|
assert cmd == (
|
||||||
|
'Rscript',
|
||||||
|
'--no-save', '--no-restore', '--no-site-file', '--no-environ',
|
||||||
|
str(tmp_path.joinpath('some-script.R')),
|
||||||
|
'--no-cache',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_r_parsing_expr_no_opts_no_args1(tempdir_factory, store):
|
def test_r_parsing_expr_no_opts_no_args1(tmp_path):
|
||||||
hook_id = 'parse-expr-no-opts-no-args-1'
|
cmd = r._cmd_from_hook(
|
||||||
_test_r_parsing(
|
Prefix(str(tmp_path)),
|
||||||
tempdir_factory, store, hook_id, expected_hook_expr=('-e', '1+1'),
|
"Rscript -e '1+1'",
|
||||||
|
(),
|
||||||
|
is_local=False,
|
||||||
|
)
|
||||||
|
assert cmd == (
|
||||||
|
'Rscript',
|
||||||
|
'--no-save', '--no-restore', '--no-site-file', '--no-environ',
|
||||||
|
'-e', '1+1',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_r_parsing_expr_no_opts_no_args2(tempdir_factory, store):
|
def test_r_parsing_local_hook_path_is_not_expanded(tmp_path):
|
||||||
with pytest.raises(ValueError) as execinfo:
|
cmd = r._cmd_from_hook(
|
||||||
|
Prefix(str(tmp_path)),
|
||||||
|
'Rscript path/to/thing.R',
|
||||||
|
(),
|
||||||
|
is_local=True,
|
||||||
|
)
|
||||||
|
assert cmd == (
|
||||||
|
'Rscript',
|
||||||
|
'--no-save', '--no-restore', '--no-site-file', '--no-environ',
|
||||||
|
'path/to/thing.R',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_r_parsing_expr_no_opts_no_args2():
|
||||||
|
with pytest.raises(ValueError) as excinfo:
|
||||||
r._entry_validate(['Rscript', '-e', '1+1', '-e', 'letters'])
|
r._entry_validate(['Rscript', '-e', '1+1', '-e', 'letters'])
|
||||||
msg = execinfo.value.args
|
msg, = excinfo.value.args
|
||||||
assert msg == ('You can supply at most one expression.',)
|
assert msg == 'You can supply at most one expression.'
|
||||||
|
|
||||||
|
|
||||||
def test_r_parsing_expr_opts_no_args2(tempdir_factory, store):
|
def test_r_parsing_expr_opts_no_args2():
|
||||||
with pytest.raises(ValueError) as execinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
r._entry_validate(
|
r._entry_validate(
|
||||||
[
|
['Rscript', '--vanilla', '-e', '1+1', '-e', 'letters'],
|
||||||
'Rscript', '--vanilla', '-e', '1+1', '-e', 'letters',
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
msg = execinfo.value.args
|
msg, = excinfo.value.args
|
||||||
assert msg == (
|
assert msg == (
|
||||||
'The only valid syntax is `Rscript -e {expr}`',
|
'The only valid syntax is `Rscript -e {expr}`'
|
||||||
'or `Rscript path/to/hook/script`',
|
'or `Rscript path/to/hook/script`'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_r_parsing_expr_args_in_entry2(tempdir_factory, store):
|
def test_r_parsing_expr_args_in_entry2():
|
||||||
with pytest.raises(ValueError) as execinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
r._entry_validate(['Rscript', '-e', 'expr1', '--another-arg'])
|
r._entry_validate(['Rscript', '-e', 'expr1', '--another-arg'])
|
||||||
|
|
||||||
msg = execinfo.value.args
|
msg, = excinfo.value.args
|
||||||
assert msg == ('You can supply at most one expression.',)
|
assert msg == 'You can supply at most one expression.'
|
||||||
|
|
||||||
|
|
||||||
def test_r_parsing_expr_non_Rscirpt(tempdir_factory, store):
|
def test_r_parsing_expr_non_Rscirpt():
|
||||||
with pytest.raises(ValueError) as execinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
r._entry_validate(['AnotherScript', '-e', '{{}}'])
|
r._entry_validate(['AnotherScript', '-e', '{{}}'])
|
||||||
|
|
||||||
msg = execinfo.value.args
|
msg, = excinfo.value.args
|
||||||
assert msg == ('entry must start with `Rscript`.',)
|
assert msg == 'entry must start with `Rscript`.'
|
||||||
|
|
||||||
|
|
||||||
def test_r_parsing_file_local(tempdir_factory, store):
|
|
||||||
path = 'path/to/script.R'
|
|
||||||
hook_id = 'local-r'
|
|
||||||
config = {
|
|
||||||
'repo': 'local',
|
|
||||||
'hooks': [{
|
|
||||||
'id': hook_id,
|
|
||||||
'name': 'local-r',
|
|
||||||
'entry': f'Rscript {path}',
|
|
||||||
'language': 'r',
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
_test_r_parsing(
|
|
||||||
tempdir_factory,
|
|
||||||
store,
|
|
||||||
hook_id=hook_id,
|
|
||||||
expected_hook_expr=(path,),
|
|
||||||
config=config,
|
|
||||||
expect_path_prefix=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_rscript_exec_relative_to_r_home():
|
def test_rscript_exec_relative_to_r_home():
|
||||||
|
@ -142,3 +125,99 @@ def test_rscript_exec_relative_to_r_home():
|
||||||
def test_path_rscript_exec_no_r_home_set():
|
def test_path_rscript_exec_no_r_home_set():
|
||||||
with envcontext.envcontext((('R_HOME', envcontext.UNSET),)):
|
with envcontext.envcontext((('R_HOME', envcontext.UNSET),)):
|
||||||
assert r._rscript_exec() == 'Rscript'
|
assert r._rscript_exec() == 'Rscript'
|
||||||
|
|
||||||
|
|
||||||
|
def test_r_hook(tmp_path):
|
||||||
|
renv_lock = '''\
|
||||||
|
{
|
||||||
|
"R": {
|
||||||
|
"Version": "4.0.3",
|
||||||
|
"Repositories": [
|
||||||
|
{
|
||||||
|
"Name": "CRAN",
|
||||||
|
"URL": "https://cloud.r-project.org"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Packages": {
|
||||||
|
"renv": {
|
||||||
|
"Package": "renv",
|
||||||
|
"Version": "0.12.5",
|
||||||
|
"Source": "Repository",
|
||||||
|
"Repository": "CRAN",
|
||||||
|
"Hash": "5c0cdb37f063c58cdab3c7e9fbb8bd2c"
|
||||||
|
},
|
||||||
|
"rprojroot": {
|
||||||
|
"Package": "rprojroot",
|
||||||
|
"Version": "1.0",
|
||||||
|
"Source": "Repository",
|
||||||
|
"Repository": "CRAN",
|
||||||
|
"Hash": "86704667fe0860e4fec35afdfec137f3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
description = '''\
|
||||||
|
Package: gli.clu
|
||||||
|
Title: What the Package Does (One Line, Title Case)
|
||||||
|
Type: Package
|
||||||
|
Version: 0.0.0.9000
|
||||||
|
Authors@R:
|
||||||
|
person(given = "First",
|
||||||
|
family = "Last",
|
||||||
|
role = c("aut", "cre"),
|
||||||
|
email = "first.last@example.com",
|
||||||
|
comment = c(ORCID = "YOUR-ORCID-ID"))
|
||||||
|
Description: What the package does (one paragraph).
|
||||||
|
License: `use_mit_license()`, `use_gpl3_license()` or friends to
|
||||||
|
pick a license
|
||||||
|
Encoding: UTF-8
|
||||||
|
LazyData: true
|
||||||
|
Roxygen: list(markdown = TRUE)
|
||||||
|
RoxygenNote: 7.1.1
|
||||||
|
Imports:
|
||||||
|
rprojroot
|
||||||
|
'''
|
||||||
|
hello_world_r = '''\
|
||||||
|
stopifnot(
|
||||||
|
packageVersion('rprojroot') == '1.0',
|
||||||
|
packageVersion('gli.clu') == '0.0.0.9000'
|
||||||
|
)
|
||||||
|
cat("Hello, World, from R!\n")
|
||||||
|
'''
|
||||||
|
|
||||||
|
tmp_path.joinpath('renv.lock').write_text(renv_lock)
|
||||||
|
tmp_path.joinpath('DESCRIPTION').write_text(description)
|
||||||
|
tmp_path.joinpath('hello-world.R').write_text(hello_world_r)
|
||||||
|
renv_dir = tmp_path.joinpath('renv')
|
||||||
|
renv_dir.mkdir()
|
||||||
|
shutil.copy(
|
||||||
|
os.path.join(
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
'../../pre_commit/resources/empty_template_activate.R',
|
||||||
|
),
|
||||||
|
renv_dir.joinpath('activate.R'),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected = (0, b'Hello, World, from R!\n')
|
||||||
|
assert run_language(tmp_path, r, 'Rscript hello-world.R') == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_r_inline(tmp_path):
|
||||||
|
_make_local_repo(str(tmp_path))
|
||||||
|
|
||||||
|
cmd = '''\
|
||||||
|
Rscript -e '
|
||||||
|
stopifnot(packageVersion("rprojroot") == "1.0")
|
||||||
|
cat(commandArgs(trailingOnly = TRUE), "from R!\n", sep=", ")
|
||||||
|
'
|
||||||
|
'''
|
||||||
|
|
||||||
|
ret = run_language(
|
||||||
|
tmp_path,
|
||||||
|
r,
|
||||||
|
cmd,
|
||||||
|
deps=('rprojroot@1.0',),
|
||||||
|
args=('hi', 'hello'),
|
||||||
|
)
|
||||||
|
assert ret == (0, b'hi, hello, from R!\n')
|
||||||
|
|
|
@ -71,10 +71,10 @@ def test_install_ruby_default(fake_gem_prefix):
|
||||||
|
|
||||||
@xfailif_windows # pragma: win32 no cover
|
@xfailif_windows # pragma: win32 no cover
|
||||||
def test_install_ruby_with_version(fake_gem_prefix):
|
def test_install_ruby_with_version(fake_gem_prefix):
|
||||||
ruby.install_environment(fake_gem_prefix, '3.1.0', ())
|
ruby.install_environment(fake_gem_prefix, '3.2.0', ())
|
||||||
|
|
||||||
# Should be able to activate and use rbenv install
|
# Should be able to activate and use rbenv install
|
||||||
with ruby.in_env(fake_gem_prefix, '3.1.0'):
|
with ruby.in_env(fake_gem_prefix, '3.2.0'):
|
||||||
cmd_output('rbenv', 'install', '--help')
|
cmd_output('rbenv', 'install', '--help')
|
||||||
|
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue