1
0
Fork 0

Adding upstream version 3.3.1.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-09 21:41:42 +01:00
parent 9380cb1893
commit 00f36ec412
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
11 changed files with 180 additions and 130 deletions

View file

@ -25,7 +25,7 @@ repos:
- id: add-trailing-comma - id: add-trailing-comma
args: [--py36-plus] args: [--py36-plus]
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v3.3.1 rev: v3.3.2
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py38-plus] args: [--py38-plus]
@ -38,7 +38,7 @@ repos:
hooks: hooks:
- id: flake8 - id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.1.1 rev: v1.2.0
hooks: hooks:
- id: mypy - id: mypy
additional_dependencies: [types-all] additional_dependencies: [types-all]

View file

@ -1,3 +1,23 @@
3.3.1 - 2023-05-02
==================
### Fixes
- Work around `git` partial clone bug for `autoupdate` on windows.
- #2866 PR by @asottile.
- #2865 issue by @adehad.
3.3.0 - 2023-05-01
==================
### Features
- Upgrade ruby-build.
- #2846 PR by @jalessio.
- Use blobless clone for faster autoupdate.
- #2859 PR by @asottile.
- Add `-j` / `--jobs` argument to `autoupdate` for parallel execution.
- #2863 PR by @asottile.
- issue by @gaborbernat.
3.2.2 - 2023-04-03 3.2.2 - 2023-04-03
================== ==================

View file

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

View file

@ -1,7 +1,6 @@
from __future__ import annotations from __future__ import annotations
import contextlib import contextlib
import multiprocessing
import os import os
import random import random
import re import re
@ -15,9 +14,9 @@ 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 import xargs
from pre_commit.prefix import Prefix from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output_b from pre_commit.util import cmd_output_b
from pre_commit.xargs import xargs
FIXED_RANDOM_SEED = 1542676187 FIXED_RANDOM_SEED = 1542676187
@ -140,10 +139,7 @@ def target_concurrency() -> int:
if 'TRAVIS' in os.environ: if 'TRAVIS' in os.environ:
return 2 return 2
else: else:
try: return xargs.cpu_count()
return multiprocessing.cpu_count()
except NotImplementedError:
return 1
def _shuffled(seq: Sequence[str]) -> list[str]: def _shuffled(seq: Sequence[str]) -> list[str]:
@ -171,7 +167,7 @@ def run_xargs(
# ordering. # ordering.
file_args = _shuffled(file_args) file_args = _shuffled(file_args)
jobs = target_concurrency() jobs = target_concurrency()
return xargs(cmd, file_args, target_concurrency=jobs, color=color) return xargs.xargs(cmd, file_args, target_concurrency=jobs, color=color)
def hook_cmd(entry: str, args: Sequence[str]) -> tuple[str, ...]: def hook_cmd(entry: str, args: Sequence[str]) -> tuple[str, ...]:

View file

@ -226,9 +226,13 @@ def main(argv: Sequence[str] | None = None) -> int:
help='Store "frozen" hashes in `rev` instead of tag names', help='Store "frozen" hashes in `rev` instead of tag names',
) )
autoupdate_parser.add_argument( autoupdate_parser.add_argument(
'--repo', dest='repos', action='append', metavar='REPO', '--repo', dest='repos', action='append', metavar='REPO', default=[],
help='Only update this repository -- may be specified multiple times.', help='Only update this repository -- may be specified multiple times.',
) )
autoupdate_parser.add_argument(
'-j', '--jobs', type=int, default=1,
help='Number of threads to use. (default %(default)s).',
)
_add_cmd('clean', help='Clean out pre-commit files.') _add_cmd('clean', help='Clean out pre-commit files.')
@ -368,10 +372,11 @@ def main(argv: Sequence[str] | None = None) -> int:
if args.command == 'autoupdate': if args.command == 'autoupdate':
return autoupdate( return autoupdate(
args.config, store, args.config,
tags_only=not args.bleeding_edge, tags_only=not args.bleeding_edge,
freeze=args.freeze, freeze=args.freeze,
repos=args.repos, repos=args.repos,
jobs=args.jobs,
) )
elif args.command == 'clean': elif args.command == 'clean':
return clean(store) return clean(store)

View file

@ -3,6 +3,7 @@ from __future__ import annotations
import concurrent.futures import concurrent.futures
import contextlib import contextlib
import math import math
import multiprocessing
import os import os
import subprocess import subprocess
import sys import sys
@ -22,6 +23,13 @@ TArg = TypeVar('TArg')
TRet = TypeVar('TRet') TRet = TypeVar('TRet')
def cpu_count() -> int:
try:
return multiprocessing.cpu_count()
except NotImplementedError:
return 1
def _environ_size(_env: MutableMapping[str, str] | None = None) -> int: def _environ_size(_env: MutableMapping[str, str] | None = None) -> int:
environ = _env if _env is not None else getattr(os, 'environb', os.environ) environ = _env if _env is not None else getattr(os, 'environb', os.environ)
size = 8 * len(environ) # number of pointers in `envp` size = 8 * len(environ) # number of pointers in `envp`

View file

@ -1,6 +1,6 @@
[metadata] [metadata]
name = pre_commit name = pre_commit
version = 3.2.2 version = 3.3.1
description = A framework for managing and maintaining multi-language pre-commit hooks. description = A framework for managing and maintaining multi-language pre-commit hooks.
long_description = file: README.md long_description = file: README.md
long_description_content_type = text/markdown long_description_content_type = text/markdown

View file

@ -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', '9d92a69'), ('ruby-build', 'https://github.com/rbenv/ruby-build', '855b963'),
( (
'ruby-download', 'ruby-download',
'https://github.com/garnieretienne/rvm-download', 'https://github.com/garnieretienne/rvm-download',

View file

@ -67,7 +67,7 @@ def test_rev_info_from_config():
def test_rev_info_update_up_to_date_repo(up_to_date): def test_rev_info_update_up_to_date_repo(up_to_date):
config = make_config_from_repo(up_to_date) config = make_config_from_repo(up_to_date)
info = RevInfo.from_config(config) info = RevInfo.from_config(config)._replace(hook_ids=frozenset(('foo',)))
new_info = info.update(tags_only=False, freeze=False) new_info = info.update(tags_only=False, freeze=False)
assert info == new_info assert info == new_info
@ -139,7 +139,7 @@ def test_rev_info_update_does_not_freeze_if_already_sha(out_of_date):
assert new_info.frozen is None assert new_info.frozen is None
def test_autoupdate_up_to_date_repo(up_to_date, tmpdir, store): def test_autoupdate_up_to_date_repo(up_to_date, tmpdir):
contents = ( contents = (
f'repos:\n' f'repos:\n'
f'- repo: {up_to_date}\n' f'- repo: {up_to_date}\n'
@ -150,11 +150,11 @@ def test_autoupdate_up_to_date_repo(up_to_date, tmpdir, store):
cfg = tmpdir.join(C.CONFIG_FILE) cfg = tmpdir.join(C.CONFIG_FILE)
cfg.write(contents) cfg.write(contents)
assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 0 assert autoupdate(str(cfg), freeze=False, tags_only=False) == 0
assert cfg.read() == contents assert cfg.read() == contents
def test_autoupdate_old_revision_broken(tempdir_factory, in_tmpdir, store): def test_autoupdate_old_revision_broken(tempdir_factory, in_tmpdir):
"""In $FUTURE_VERSION, hooks.yaml will no longer be supported. This """In $FUTURE_VERSION, hooks.yaml will no longer be supported. This
asserts that when that day comes, pre-commit will be able to autoupdate asserts that when that day comes, pre-commit will be able to autoupdate
despite not being able to read hooks.yaml in that repository. despite not being able to read hooks.yaml in that repository.
@ -174,14 +174,14 @@ def test_autoupdate_old_revision_broken(tempdir_factory, in_tmpdir, store):
write_config('.', config) write_config('.', config)
with open(C.CONFIG_FILE) as f: with open(C.CONFIG_FILE) as f:
before = f.read() before = f.read()
assert autoupdate(C.CONFIG_FILE, store, freeze=False, tags_only=False) == 0 assert autoupdate(C.CONFIG_FILE, freeze=False, tags_only=False) == 0
with open(C.CONFIG_FILE) as f: with open(C.CONFIG_FILE) as f:
after = f.read() after = f.read()
assert before != after assert before != after
assert update_rev in after assert update_rev in after
def test_autoupdate_out_of_date_repo(out_of_date, tmpdir, store): def test_autoupdate_out_of_date_repo(out_of_date, tmpdir):
fmt = ( fmt = (
'repos:\n' 'repos:\n'
'- repo: {}\n' '- repo: {}\n'
@ -192,24 +192,24 @@ def test_autoupdate_out_of_date_repo(out_of_date, tmpdir, store):
cfg = tmpdir.join(C.CONFIG_FILE) cfg = tmpdir.join(C.CONFIG_FILE)
cfg.write(fmt.format(out_of_date.path, out_of_date.original_rev)) cfg.write(fmt.format(out_of_date.path, out_of_date.original_rev))
assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 0 assert autoupdate(str(cfg), freeze=False, tags_only=False) == 0
assert cfg.read() == fmt.format(out_of_date.path, out_of_date.head_rev) assert cfg.read() == fmt.format(out_of_date.path, out_of_date.head_rev)
def test_autoupdate_with_core_useBuiltinFSMonitor(out_of_date, tmpdir, store): def test_autoupdate_with_core_useBuiltinFSMonitor(out_of_date, tmpdir):
# force the setting on "globally" for git # force the setting on "globally" for git
home = tmpdir.join('fakehome').ensure_dir() home = tmpdir.join('fakehome').ensure_dir()
home.join('.gitconfig').write('[core]\nuseBuiltinFSMonitor = true\n') home.join('.gitconfig').write('[core]\nuseBuiltinFSMonitor = true\n')
with envcontext.envcontext((('HOME', str(home)),)): with envcontext.envcontext((('HOME', str(home)),)):
test_autoupdate_out_of_date_repo(out_of_date, tmpdir, store) test_autoupdate_out_of_date_repo(out_of_date, tmpdir)
def test_autoupdate_pure_yaml(out_of_date, tmpdir, store): def test_autoupdate_pure_yaml(out_of_date, tmpdir):
with mock.patch.object(yaml, 'Dumper', yaml.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)
def test_autoupdate_only_one_to_update(up_to_date, out_of_date, tmpdir, store): def test_autoupdate_only_one_to_update(up_to_date, out_of_date, tmpdir):
fmt = ( fmt = (
'repos:\n' 'repos:\n'
'- repo: {}\n' '- repo: {}\n'
@ -228,7 +228,7 @@ def test_autoupdate_only_one_to_update(up_to_date, out_of_date, tmpdir, store):
) )
cfg.write(before) cfg.write(before)
assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 0 assert autoupdate(str(cfg), freeze=False, tags_only=False) == 0
assert cfg.read() == fmt.format( assert cfg.read() == fmt.format(
up_to_date, git.head_rev(up_to_date), up_to_date, git.head_rev(up_to_date),
out_of_date.path, out_of_date.head_rev, out_of_date.path, out_of_date.head_rev,
@ -236,7 +236,7 @@ def test_autoupdate_only_one_to_update(up_to_date, out_of_date, tmpdir, store):
def test_autoupdate_out_of_date_repo_with_correct_repo_name( def test_autoupdate_out_of_date_repo_with_correct_repo_name(
out_of_date, in_tmpdir, store, out_of_date, in_tmpdir,
): ):
stale_config = make_config_from_repo( stale_config = make_config_from_repo(
out_of_date.path, rev=out_of_date.original_rev, check=False, out_of_date.path, rev=out_of_date.original_rev, check=False,
@ -249,7 +249,7 @@ def test_autoupdate_out_of_date_repo_with_correct_repo_name(
before = f.read() before = f.read()
repo_name = f'file://{out_of_date.path}' repo_name = f'file://{out_of_date.path}'
ret = autoupdate( ret = autoupdate(
C.CONFIG_FILE, store, freeze=False, tags_only=False, C.CONFIG_FILE, freeze=False, tags_only=False,
repos=(repo_name,), repos=(repo_name,),
) )
with open(C.CONFIG_FILE) as f: with open(C.CONFIG_FILE) as f:
@ -261,7 +261,7 @@ def test_autoupdate_out_of_date_repo_with_correct_repo_name(
def test_autoupdate_out_of_date_repo_with_wrong_repo_name( def test_autoupdate_out_of_date_repo_with_wrong_repo_name(
out_of_date, in_tmpdir, store, out_of_date, in_tmpdir,
): ):
config = make_config_from_repo( config = make_config_from_repo(
out_of_date.path, rev=out_of_date.original_rev, check=False, out_of_date.path, rev=out_of_date.original_rev, check=False,
@ -272,7 +272,7 @@ def test_autoupdate_out_of_date_repo_with_wrong_repo_name(
before = f.read() before = f.read()
# It will not update it, because the name doesn't match # It will not update it, because the name doesn't match
ret = autoupdate( ret = autoupdate(
C.CONFIG_FILE, store, freeze=False, tags_only=False, C.CONFIG_FILE, freeze=False, tags_only=False,
repos=('dne',), repos=('dne',),
) )
with open(C.CONFIG_FILE) as f: with open(C.CONFIG_FILE) as f:
@ -281,7 +281,7 @@ def test_autoupdate_out_of_date_repo_with_wrong_repo_name(
assert before == after assert before == after
def test_does_not_reformat(tmpdir, out_of_date, store): def test_does_not_reformat(tmpdir, out_of_date):
fmt = ( fmt = (
'repos:\n' 'repos:\n'
'- repo: {}\n' '- repo: {}\n'
@ -294,12 +294,12 @@ def test_does_not_reformat(tmpdir, out_of_date, store):
cfg = tmpdir.join(C.CONFIG_FILE) cfg = tmpdir.join(C.CONFIG_FILE)
cfg.write(fmt.format(out_of_date.path, out_of_date.original_rev)) cfg.write(fmt.format(out_of_date.path, out_of_date.original_rev))
assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 0 assert autoupdate(str(cfg), freeze=False, tags_only=False) == 0
expected = fmt.format(out_of_date.path, out_of_date.head_rev) expected = fmt.format(out_of_date.path, out_of_date.head_rev)
assert cfg.read() == expected assert cfg.read() == expected
def test_does_not_change_mixed_endlines_read(up_to_date, tmpdir, store): def test_does_not_change_mixed_endlines_read(up_to_date, tmpdir):
fmt = ( fmt = (
'repos:\n' 'repos:\n'
'- repo: {}\n' '- repo: {}\n'
@ -314,11 +314,11 @@ def test_does_not_change_mixed_endlines_read(up_to_date, tmpdir, store):
expected = fmt.format(up_to_date, git.head_rev(up_to_date)).encode() expected = fmt.format(up_to_date, git.head_rev(up_to_date)).encode()
cfg.write_binary(expected) cfg.write_binary(expected)
assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 0 assert autoupdate(str(cfg), freeze=False, tags_only=False) == 0
assert cfg.read_binary() == expected assert cfg.read_binary() == expected
def test_does_not_change_mixed_endlines_write(tmpdir, out_of_date, store): def test_does_not_change_mixed_endlines_write(tmpdir, out_of_date):
fmt = ( fmt = (
'repos:\n' 'repos:\n'
'- repo: {}\n' '- repo: {}\n'
@ -333,12 +333,12 @@ def test_does_not_change_mixed_endlines_write(tmpdir, out_of_date, store):
fmt.format(out_of_date.path, out_of_date.original_rev).encode(), fmt.format(out_of_date.path, out_of_date.original_rev).encode(),
) )
assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 0 assert autoupdate(str(cfg), freeze=False, tags_only=False) == 0
expected = fmt.format(out_of_date.path, out_of_date.head_rev).encode() expected = fmt.format(out_of_date.path, out_of_date.head_rev).encode()
assert cfg.read_binary() == expected assert cfg.read_binary() == expected
def test_loses_formatting_when_not_detectable(out_of_date, store, tmpdir): def test_loses_formatting_when_not_detectable(out_of_date, tmpdir):
"""A best-effort attempt is made at updating rev without rewriting """A best-effort attempt is made at updating rev without rewriting
formatting. When the original formatting cannot be detected, this formatting. When the original formatting cannot be detected, this
is abandoned. is abandoned.
@ -359,7 +359,7 @@ def test_loses_formatting_when_not_detectable(out_of_date, store, tmpdir):
cfg = tmpdir.join(C.CONFIG_FILE) cfg = tmpdir.join(C.CONFIG_FILE)
cfg.write(config) cfg.write(config)
assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 0 assert autoupdate(str(cfg), freeze=False, tags_only=False) == 0
expected = ( expected = (
f'repos:\n' f'repos:\n'
f'- repo: {out_of_date.path}\n' f'- repo: {out_of_date.path}\n'
@ -370,43 +370,43 @@ def test_loses_formatting_when_not_detectable(out_of_date, store, tmpdir):
assert cfg.read() == expected assert cfg.read() == expected
def test_autoupdate_tagged_repo(tagged, in_tmpdir, store): def test_autoupdate_tagged_repo(tagged, in_tmpdir):
config = make_config_from_repo(tagged.path, rev=tagged.original_rev) config = make_config_from_repo(tagged.path, rev=tagged.original_rev)
write_config('.', config) write_config('.', config)
assert autoupdate(C.CONFIG_FILE, store, freeze=False, tags_only=False) == 0 assert autoupdate(C.CONFIG_FILE, freeze=False, tags_only=False) == 0
with open(C.CONFIG_FILE) as f: with open(C.CONFIG_FILE) as f:
assert 'v1.2.3' in f.read() assert 'v1.2.3' in f.read()
def test_autoupdate_freeze(tagged, in_tmpdir, store): def test_autoupdate_freeze(tagged, in_tmpdir):
config = make_config_from_repo(tagged.path, rev=tagged.original_rev) config = make_config_from_repo(tagged.path, rev=tagged.original_rev)
write_config('.', config) write_config('.', config)
assert autoupdate(C.CONFIG_FILE, store, freeze=True, tags_only=False) == 0 assert autoupdate(C.CONFIG_FILE, freeze=True, tags_only=False) == 0
with open(C.CONFIG_FILE) as f: with open(C.CONFIG_FILE) as f:
expected = f'rev: {tagged.head_rev} # frozen: v1.2.3' expected = f'rev: {tagged.head_rev} # frozen: v1.2.3'
assert expected in f.read() assert expected in f.read()
# if we un-freeze it should remove the frozen comment # if we un-freeze it should remove the frozen comment
assert autoupdate(C.CONFIG_FILE, store, freeze=False, tags_only=False) == 0 assert autoupdate(C.CONFIG_FILE, freeze=False, tags_only=False) == 0
with open(C.CONFIG_FILE) as f: with open(C.CONFIG_FILE) as f:
assert 'rev: v1.2.3\n' in f.read() assert 'rev: v1.2.3\n' in f.read()
def test_autoupdate_tags_only(tagged, in_tmpdir, store): def test_autoupdate_tags_only(tagged, in_tmpdir):
# add some commits after the tag # add some commits after the tag
git_commit(cwd=tagged.path) git_commit(cwd=tagged.path)
config = make_config_from_repo(tagged.path, rev=tagged.original_rev) config = make_config_from_repo(tagged.path, rev=tagged.original_rev)
write_config('.', config) write_config('.', config)
assert autoupdate(C.CONFIG_FILE, store, freeze=False, tags_only=True) == 0 assert autoupdate(C.CONFIG_FILE, freeze=False, tags_only=True) == 0
with open(C.CONFIG_FILE) as f: with open(C.CONFIG_FILE) as f:
assert 'v1.2.3' in f.read() assert 'v1.2.3' in f.read()
def test_autoupdate_latest_no_config(out_of_date, in_tmpdir, store): def test_autoupdate_latest_no_config(out_of_date, in_tmpdir):
config = make_config_from_repo( config = make_config_from_repo(
out_of_date.path, rev=out_of_date.original_rev, out_of_date.path, rev=out_of_date.original_rev,
) )
@ -415,12 +415,12 @@ def test_autoupdate_latest_no_config(out_of_date, in_tmpdir, store):
cmd_output('git', 'rm', '-r', ':/', cwd=out_of_date.path) cmd_output('git', 'rm', '-r', ':/', cwd=out_of_date.path)
git_commit(cwd=out_of_date.path) git_commit(cwd=out_of_date.path)
assert autoupdate(C.CONFIG_FILE, store, freeze=False, tags_only=False) == 1 assert autoupdate(C.CONFIG_FILE, freeze=False, tags_only=False) == 1
with open(C.CONFIG_FILE) as f: with open(C.CONFIG_FILE) as f:
assert out_of_date.original_rev in f.read() assert out_of_date.original_rev in f.read()
def test_hook_disppearing_repo_raises(hook_disappearing, store): def test_hook_disppearing_repo_raises(hook_disappearing):
config = make_config_from_repo( config = make_config_from_repo(
hook_disappearing.path, hook_disappearing.path,
rev=hook_disappearing.original_rev, rev=hook_disappearing.original_rev,
@ -428,10 +428,10 @@ def test_hook_disppearing_repo_raises(hook_disappearing, store):
) )
info = RevInfo.from_config(config).update(tags_only=False, freeze=False) info = RevInfo.from_config(config).update(tags_only=False, freeze=False)
with pytest.raises(RepositoryCannotBeUpdatedError): with pytest.raises(RepositoryCannotBeUpdatedError):
_check_hooks_still_exist_at_rev(config, info, store) _check_hooks_still_exist_at_rev(config, info)
def test_autoupdate_hook_disappearing_repo(hook_disappearing, tmpdir, store): def test_autoupdate_hook_disappearing_repo(hook_disappearing, tmpdir):
contents = ( contents = (
f'repos:\n' f'repos:\n'
f'- repo: {hook_disappearing.path}\n' f'- repo: {hook_disappearing.path}\n'
@ -442,21 +442,21 @@ def test_autoupdate_hook_disappearing_repo(hook_disappearing, tmpdir, store):
cfg = tmpdir.join(C.CONFIG_FILE) cfg = tmpdir.join(C.CONFIG_FILE)
cfg.write(contents) cfg.write(contents)
assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 1 assert autoupdate(str(cfg), freeze=False, tags_only=False) == 1
assert cfg.read() == contents assert cfg.read() == contents
def test_autoupdate_local_hooks(in_git_dir, store): def test_autoupdate_local_hooks(in_git_dir):
config = sample_local_config() config = sample_local_config()
add_config_to_repo('.', config) add_config_to_repo('.', config)
assert autoupdate(C.CONFIG_FILE, store, freeze=False, tags_only=False) == 0 assert autoupdate(C.CONFIG_FILE, freeze=False, tags_only=False) == 0
new_config_written = read_config('.') new_config_written = read_config('.')
assert len(new_config_written['repos']) == 1 assert len(new_config_written['repos']) == 1
assert new_config_written['repos'][0] == config assert new_config_written['repos'][0] == config
def test_autoupdate_local_hooks_with_out_of_date_repo( def test_autoupdate_local_hooks_with_out_of_date_repo(
out_of_date, in_tmpdir, store, out_of_date, in_tmpdir,
): ):
stale_config = make_config_from_repo( stale_config = make_config_from_repo(
out_of_date.path, rev=out_of_date.original_rev, check=False, out_of_date.path, rev=out_of_date.original_rev, check=False,
@ -464,13 +464,13 @@ def test_autoupdate_local_hooks_with_out_of_date_repo(
local_config = sample_local_config() local_config = sample_local_config()
config = {'repos': [local_config, stale_config]} config = {'repos': [local_config, stale_config]}
write_config('.', config) write_config('.', config)
assert autoupdate(C.CONFIG_FILE, store, freeze=False, tags_only=False) == 0 assert autoupdate(C.CONFIG_FILE, freeze=False, tags_only=False) == 0
new_config_written = read_config('.') new_config_written = read_config('.')
assert len(new_config_written['repos']) == 2 assert len(new_config_written['repos']) == 2
assert new_config_written['repos'][0] == local_config assert new_config_written['repos'][0] == local_config
def test_autoupdate_meta_hooks(tmpdir, store): def test_autoupdate_meta_hooks(tmpdir):
cfg = tmpdir.join(C.CONFIG_FILE) cfg = tmpdir.join(C.CONFIG_FILE)
cfg.write( cfg.write(
'repos:\n' 'repos:\n'
@ -478,7 +478,7 @@ def test_autoupdate_meta_hooks(tmpdir, store):
' hooks:\n' ' hooks:\n'
' - id: check-useless-excludes\n', ' - id: check-useless-excludes\n',
) )
assert autoupdate(str(cfg), store, freeze=False, tags_only=True) == 0 assert autoupdate(str(cfg), freeze=False, tags_only=True) == 0
assert cfg.read() == ( assert cfg.read() == (
'repos:\n' 'repos:\n'
'- repo: meta\n' '- repo: meta\n'
@ -487,7 +487,7 @@ def test_autoupdate_meta_hooks(tmpdir, store):
) )
def test_updates_old_format_to_new_format(tmpdir, capsys, store): def test_updates_old_format_to_new_format(tmpdir, capsys):
cfg = tmpdir.join(C.CONFIG_FILE) cfg = tmpdir.join(C.CONFIG_FILE)
cfg.write( cfg.write(
'- repo: local\n' '- repo: local\n'
@ -497,7 +497,7 @@ def test_updates_old_format_to_new_format(tmpdir, capsys, store):
' entry: ./bin/foo.sh\n' ' entry: ./bin/foo.sh\n'
' language: script\n', ' language: script\n',
) )
assert autoupdate(str(cfg), store, freeze=False, tags_only=True) == 0 assert autoupdate(str(cfg), freeze=False, tags_only=True) == 0
contents = cfg.read() contents = cfg.read()
assert contents == ( assert contents == (
'repos:\n' 'repos:\n'
@ -512,7 +512,7 @@ def test_updates_old_format_to_new_format(tmpdir, capsys, store):
assert out == 'Configuration has been migrated.\n' assert out == 'Configuration has been migrated.\n'
def test_maintains_rev_quoting_style(tmpdir, out_of_date, store): def test_maintains_rev_quoting_style(tmpdir, out_of_date):
fmt = ( fmt = (
'repos:\n' 'repos:\n'
'- repo: {path}\n' '- repo: {path}\n'
@ -527,6 +527,6 @@ def test_maintains_rev_quoting_style(tmpdir, out_of_date, store):
cfg = tmpdir.join(C.CONFIG_FILE) cfg = tmpdir.join(C.CONFIG_FILE)
cfg.write(fmt.format(path=out_of_date.path, rev=out_of_date.original_rev)) cfg.write(fmt.format(path=out_of_date.path, rev=out_of_date.original_rev))
assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 0 assert autoupdate(str(cfg), freeze=False, tags_only=False) == 0
expected = fmt.format(path=out_of_date.path, rev=out_of_date.head_rev) expected = fmt.format(path=out_of_date.path, rev=out_of_date.head_rev)
assert cfg.read() == expected assert cfg.read() == expected

View file

@ -43,8 +43,9 @@ def test_gc(tempdir_factory, store, in_git_dir, cap_out):
store.mark_config_used(C.CONFIG_FILE) store.mark_config_used(C.CONFIG_FILE)
# update will clone both the old and new repo, making the old one gc-able # update will clone both the old and new repo, making the old one gc-able
install_hooks(C.CONFIG_FILE, store) assert not install_hooks(C.CONFIG_FILE, store)
assert not autoupdate(C.CONFIG_FILE, store, freeze=False, tags_only=False) assert not autoupdate(C.CONFIG_FILE, freeze=False, tags_only=False)
assert not install_hooks(C.CONFIG_FILE, store)
assert _config_count(store) == 1 assert _config_count(store) == 1
assert _repo_count(store) == 2 assert _repo_count(store) == 2