mirror of
https://github.com/pre-commit/pre-commit.git
synced 2026-04-14 09:31:46 +04:00
115 lines
3.5 KiB
Python
115 lines
3.5 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os.path
|
|
from typing import Any
|
|
|
|
import pre_commit.constants as C
|
|
from pre_commit import output
|
|
from pre_commit.clientlib import InvalidConfigError
|
|
from pre_commit.clientlib import InvalidManifestError
|
|
from pre_commit.clientlib import load_config
|
|
from pre_commit.clientlib import load_manifest
|
|
from pre_commit.clientlib import LOCAL
|
|
from pre_commit.clientlib import META
|
|
from pre_commit.store import Store
|
|
from pre_commit.util import rmtree
|
|
|
|
|
|
def _mark_used(
|
|
config: dict[str, Any],
|
|
repo: dict[str, Any],
|
|
manifests: dict[tuple[str, str], dict[str, Any]],
|
|
unused_manifests: set[tuple[str, str]],
|
|
unused_installs: set[str],
|
|
) -> None:
|
|
if repo['repo'] == META:
|
|
return
|
|
elif repo['repo'] == LOCAL:
|
|
for hook in repo['hooks']:
|
|
deps = hook['additional_dependencies']
|
|
unused_installs.discard((
|
|
repo['repo'], C.LOCAL_REPO_VERSION,
|
|
repo['language'],
|
|
store.db_repo_name(repo['repo'], deps),
|
|
C.LOCAL_REPO_VERSION,
|
|
))
|
|
else:
|
|
key = (repo['repo'], repo['rev'])
|
|
path = all_repos.get(key)
|
|
# can't inspect manifest if it isn't cloned
|
|
if path is None:
|
|
return
|
|
|
|
try:
|
|
manifest = load_manifest(os.path.join(path, C.MANIFEST_FILE))
|
|
except InvalidManifestError:
|
|
return
|
|
else:
|
|
unused_repos.discard(key)
|
|
by_id = manifest['hooks']
|
|
|
|
for hook in repo['hooks']:
|
|
if hook['id'] not in by_id:
|
|
continue
|
|
|
|
deps = hook.get(
|
|
'additional_dependencies',
|
|
by_id[hook['id']]['additional_dependencies'],
|
|
)
|
|
unused_repos.discard((
|
|
store.db_repo_name(repo['repo'], deps), repo['rev'],
|
|
))
|
|
|
|
|
|
def _gc(store: Store) -> int:
|
|
with store.exclusive_lock(), store.connect() as db:
|
|
installs_rows = db.execute('SELECT key, path FROM installs').fetchall()
|
|
all_installs = dict(installs_rows)
|
|
unused_installs = set(all_installs)
|
|
|
|
manifests_query = 'SELECT repo, rev, manifest FROM manifests'
|
|
manifests = {
|
|
(repo, rev): json.loads(manifest)
|
|
for repo, rev, manifest in db.execute(manifests_query).fetchall()
|
|
}
|
|
unused_manifests = set(manifests)
|
|
|
|
configs_rows = db.execute('SELECT path FROM configs').fetchall()
|
|
configs = [path for path, in configs_rows]
|
|
|
|
dead_configs = []
|
|
for config_path in configs:
|
|
try:
|
|
config = load_config(config_path)
|
|
except InvalidConfigError:
|
|
dead_configs.append(config_path)
|
|
continue
|
|
else:
|
|
for repo in config['repos']:
|
|
_mark_used(
|
|
config,
|
|
repo,
|
|
manifests,
|
|
unused_manifests,
|
|
unused_installs,
|
|
)
|
|
|
|
paths = [(path,) for path in dead_configs]
|
|
db.executemany('DELETE FROM configs WHERE path = ?', paths)
|
|
|
|
db.executemany(
|
|
'DELETE FROM repos WHERE repo = ? and ref = ?',
|
|
sorted(unused_repos),
|
|
)
|
|
for k in unused_repos:
|
|
rmtree(all_repos[k])
|
|
|
|
return len(unused_repos)
|
|
|
|
|
|
def gc(store: Store) -> int:
|
|
installs, clones = _gc(store)
|
|
output.write_line(f'{clones} clone(s) removed.')
|
|
output.write_line(f'{installs} installs(s) removed.')
|
|
return 0
|