Skip to content

Commit 83157c5

Browse files
committed
add warning for deprecates stages for remote repos on init
1 parent 7555e11 commit 83157c5

File tree

3 files changed

+110
-0
lines changed

3 files changed

+110
-0
lines changed

pre_commit/clientlib.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import functools
44
import logging
5+
import os.path
56
import re
67
import shlex
78
import sys
@@ -70,6 +71,43 @@ def transform_stage(stage: str) -> str:
7071
return _STAGES.get(stage, stage)
7172

7273

74+
MINIMAL_MANIFEST_SCHEMA = cfgv.Array(
75+
cfgv.Map(
76+
'Hook', 'id',
77+
cfgv.Required('id', cfgv.check_string),
78+
cfgv.Optional('stages', cfgv.check_array(cfgv.check_string), []),
79+
),
80+
)
81+
82+
83+
def warn_for_stages_on_repo_init(repo: str, directory: str) -> None:
84+
try:
85+
manifest = cfgv.load_from_filename(
86+
os.path.join(directory, C.MANIFEST_FILE),
87+
schema=MINIMAL_MANIFEST_SCHEMA,
88+
load_strategy=yaml_load,
89+
exc_tp=InvalidManifestError,
90+
)
91+
except InvalidManifestError:
92+
return # they'll get a better error message when it actually loads!
93+
94+
legacy_stages = {} # sorted set
95+
for hook in manifest:
96+
for stage in hook.get('stages', ()):
97+
if stage in _STAGES:
98+
legacy_stages[stage] = True
99+
100+
if legacy_stages:
101+
logger.warning(
102+
f'repo `{repo}` uses deprecated stage names '
103+
f'({", ".join(legacy_stages)}) which will be removed in a '
104+
f'future version. '
105+
f'Hint: often `pre-commit autoupdate --repo {shlex.quote(repo)}` '
106+
f'will fix this. '
107+
f'if it does not -- consider reporting an issue to that repo.',
108+
)
109+
110+
73111
class StagesMigrationNoDefault(NamedTuple):
74112
key: str
75113
default: Sequence[str]

pre_commit/store.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from typing import Callable
1111

1212
import pre_commit.constants as C
13+
from pre_commit import clientlib
1314
from pre_commit import file_lock
1415
from pre_commit import git
1516
from pre_commit.util import CalledProcessError
@@ -136,6 +137,7 @@ def _new_repo(
136137
deps: Sequence[str],
137138
make_strategy: Callable[[str], None],
138139
) -> str:
140+
original_repo = repo
139141
repo = self.db_repo_name(repo, deps)
140142

141143
def _get_result() -> str | None:
@@ -168,6 +170,9 @@ def _get_result() -> str | None:
168170
'INSERT INTO repos (repo, ref, path) VALUES (?, ?, ?)',
169171
[repo, ref, directory],
170172
)
173+
174+
clientlib.warn_for_stages_on_repo_init(original_repo, directory)
175+
171176
return directory
172177

173178
def _complete_clone(self, ref: str, git_cmd: Callable[..., None]) -> None:

tests/store_test.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from __future__ import annotations
22

3+
import logging
34
import os.path
45
import sqlite3
56
import stat
67
from unittest import mock
78

89
import pytest
910

11+
import pre_commit.constants as C
1012
from pre_commit import git
1113
from pre_commit.store import _get_default_directory
1214
from pre_commit.store import _LOCAL_RESOURCES
@@ -91,6 +93,71 @@ def test_clone(store, tempdir_factory, caplog):
9193
assert store.select_all_repos() == [(path, rev, ret)]
9294

9395

96+
def test_warning_for_deprecated_stages_on_init(store, tempdir_factory, caplog):
97+
manifest = '''\
98+
- id: hook1
99+
name: hook1
100+
language: system
101+
entry: echo hook1
102+
stages: [commit, push]
103+
- id: hook2
104+
name: hook2
105+
language: system
106+
entry: echo hook2
107+
stages: [push, merge-commit]
108+
'''
109+
110+
path = git_dir(tempdir_factory)
111+
with open(os.path.join(path, C.MANIFEST_FILE), 'w') as f:
112+
f.write(manifest)
113+
cmd_output('git', 'add', '.', cwd=path)
114+
git_commit(cwd=path)
115+
rev = git.head_rev(path)
116+
117+
store.clone(path, rev)
118+
assert caplog.record_tuples[1] == (
119+
'pre_commit',
120+
logging.WARNING,
121+
f'repo `{path}` uses deprecated stage names '
122+
f'(commit, push, merge-commit) which will be removed in a future '
123+
f'version. '
124+
f'Hint: often `pre-commit autoupdate --repo {path}` will fix this. '
125+
f'if it does not -- consider reporting an issue to that repo.',
126+
)
127+
128+
# should not re-warn
129+
caplog.clear()
130+
store.clone(path, rev)
131+
assert caplog.record_tuples == []
132+
133+
134+
def test_no_warning_for_non_deprecated_stages_on_init(
135+
store, tempdir_factory, caplog,
136+
):
137+
manifest = '''\
138+
- id: hook1
139+
name: hook1
140+
language: system
141+
entry: echo hook1
142+
stages: [pre-commit, pre-push]
143+
- id: hook2
144+
name: hook2
145+
language: system
146+
entry: echo hook2
147+
stages: [pre-push, pre-merge-commit]
148+
'''
149+
150+
path = git_dir(tempdir_factory)
151+
with open(os.path.join(path, C.MANIFEST_FILE), 'w') as f:
152+
f.write(manifest)
153+
cmd_output('git', 'add', '.', cwd=path)
154+
git_commit(cwd=path)
155+
rev = git.head_rev(path)
156+
157+
store.clone(path, rev)
158+
assert logging.WARNING not in {tup[1] for tup in caplog.record_tuples}
159+
160+
94161
def test_clone_cleans_up_on_checkout_failure(store):
95162
with pytest.raises(Exception) as excinfo:
96163
# This raises an exception because you can't clone something that

0 commit comments

Comments
 (0)