Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pre_commit/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class Store:
def __init__(self, directory: Optional[str] = None) -> None:
self.directory = directory or Store.get_default_directory()
self.db_path = os.path.join(self.directory, 'db.db')
self.readonly = (
os.path.exists(self.directory) and
not os.access(self.directory, os.W_OK)
)

if not os.path.exists(self.directory):
os.makedirs(self.directory, exist_ok=True)
Expand Down Expand Up @@ -218,6 +222,8 @@ def _create_config_table(self, db: sqlite3.Connection) -> None:
)

def mark_config_used(self, path: str) -> None:
if self.readonly: # pragma: win32 no cover
return
path = os.path.realpath(path)
# don't insert config files that do not exist
if not os.path.exists(path):
Expand Down
26 changes: 26 additions & 0 deletions tests/store_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os.path
import sqlite3
import stat
from unittest import mock

import pytest
Expand All @@ -12,6 +13,7 @@
from testing.fixtures import git_dir
from testing.util import cwd
from testing.util import git_commit
from testing.util import xfailif_windows


def test_our_session_fixture_works():
Expand Down Expand Up @@ -217,3 +219,27 @@ def test_select_all_configs_roll_forward(store):
def test_mark_config_as_used_roll_forward(store, tmpdir):
_simulate_pre_1_14_0(store)
test_mark_config_as_used(store, tmpdir)


@xfailif_windows # pragma: win32 no cover
def test_mark_config_as_used_readonly(tmpdir):
cfg = tmpdir.join('f').ensure()
store_dir = tmpdir.join('store')
# make a store, then we'll convert its directory to be readonly
assert not Store(str(store_dir)).readonly # directory didn't exist
assert not Store(str(store_dir)).readonly # directory did exist

def _chmod_minus_w(p):
st = os.stat(p)
os.chmod(p, st.st_mode & ~(stat.S_IWUSR | stat.S_IWOTH | stat.S_IWGRP))

_chmod_minus_w(store_dir)
for fname in os.listdir(store_dir):
assert not os.path.isdir(fname)
_chmod_minus_w(os.path.join(store_dir, fname))

store = Store(str(store_dir))
assert store.readonly
# should be skipped due to readonly
store.mark_config_used(str(cfg))
assert store.select_all_configs() == []