From bf33f4c91c2e73bac36ec37a3dcf92bef8f0492c Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 21 Aug 2020 23:11:30 -0700 Subject: [PATCH] allow pre-commit to succeed on a readonly store directory --- pre_commit/store.py | 6 ++++++ tests/store_test.py | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/pre_commit/store.py b/pre_commit/store.py index 6d8c40a9..809a6f4d 100644 --- a/pre_commit/store.py +++ b/pre_commit/store.py @@ -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) @@ -218,6 +222,8 @@ class Store: ) 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): diff --git a/tests/store_test.py b/tests/store_test.py index 6a4e900c..0947144e 100644 --- a/tests/store_test.py +++ b/tests/store_test.py @@ -1,5 +1,6 @@ import os.path import sqlite3 +import stat from unittest import mock import pytest @@ -12,6 +13,7 @@ from pre_commit.util import cmd_output 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(): @@ -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() == []