Skip to content

Commit

Permalink
Ensure tox-created directories contain CACHEDIR.TAG
Browse files Browse the repository at this point in the history
Fixes #3334
  • Loading branch information
akx committed Sep 11, 2024
1 parent 1b167be commit 1037d6d
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 5 deletions.
7 changes: 7 additions & 0 deletions docs/changelog/3342.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Tox now creates ``CACHEDIR.TAG`` files in work directories it creates,
so that tools like ``tar`` can exclude them from e.g. backups where
ephemeral directories are not desired.

Tag files are not created in directories Tox does not itself create.

- by :user:`akx`
18 changes: 15 additions & 3 deletions src/tox/tox_env/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from tox.execute.request import ExecuteRequest
from tox.tox_env.errors import Fail, Recreate, Skip
from tox.tox_env.info import Info
from tox.util.path import ensure_empty_dir
from tox.util.path import ensure_cachedir_dir, ensure_empty_dir

if TYPE_CHECKING:
from tox.config.cli.parser import Parsed
Expand Down Expand Up @@ -315,16 +315,27 @@ def _setup_with_env(self) -> None: # noqa: B027 # empty abstract base class
def _done_with_setup(self) -> None: # noqa: B027 # empty abstract base class
"""Called when setup is done."""

def _maybe_ensure_workdir(self) -> None:
if not self.work_dir.is_dir():
# Populate the workdir with a CACHEDIR.TAG file only if we would
# be creating it now. If it already exists, do not touch it.
ensure_cachedir_dir(self.work_dir)

def _handle_env_tmp_dir(self) -> None:
"""Ensure exists and empty."""
env_tmp_dir = self.env_tmp_dir
if env_tmp_dir.exists() and next(env_tmp_dir.iterdir(), None) is not None:
LOGGER.debug("clear env temp folder %s", env_tmp_dir)
ensure_empty_dir(env_tmp_dir)
env_tmp_dir.mkdir(parents=True, exist_ok=True)
if env_tmp_dir.parent == self.work_dir:
self._maybe_ensure_workdir()
ensure_cachedir_dir(env_tmp_dir)

def _handle_core_tmp_dir(self) -> None:
self.temp_dir.mkdir(parents=True, exist_ok=True)
temp_dir = self.temp_dir
if temp_dir.parent == self.work_dir:
self._maybe_ensure_workdir()
ensure_cachedir_dir(temp_dir)

def _clean(self, transitive: bool = False) -> None: # noqa: ARG002, FBT001, FBT002
if self._run_state["clean"]: # pragma: no branch
Expand All @@ -333,6 +344,7 @@ def _clean(self, transitive: bool = False) -> None: # noqa: ARG002, FBT001, FBT
if env_dir.exists():
LOGGER.warning("remove tox env folder %s", env_dir)
ensure_empty_dir(env_dir, except_filename="file.lock")
ensure_cachedir_dir(env_dir)
self._log_id = 0 # we deleted logs, so start over counter
self.cache.reset()
self._run_state.update({"setup": False, "clean": True})
Expand Down
9 changes: 7 additions & 2 deletions src/tox/tox_env/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

from filelock import FileLock

from tox.util.path import ensure_cachedir_dir

from .api import ToxEnv, ToxEnvCreateArgs

if TYPE_CHECKING:
Expand Down Expand Up @@ -67,9 +69,12 @@ def __getattribute__(self, name: str) -> Any:

def register_config(self) -> None:
super().register_config()
file_lock_path: Path = self.env_dir / "file.lock"
env_dir = self.env_dir
if env_dir.parent == self.work_dir:
self._maybe_ensure_workdir()
ensure_cachedir_dir(env_dir)
file_lock_path: Path = env_dir / "file.lock"
self._file_lock = FileLock(file_lock_path)
file_lock_path.parent.mkdir(parents=True, exist_ok=True)
self.core.add_config(
keys=["package_root", "setupdir"],
of_type=Path,
Expand Down
2 changes: 2 additions & 0 deletions src/tox/tox_env/python/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from tox.tox_env.api import ToxEnv, ToxEnvCreateArgs
from tox.tox_env.errors import Fail, Recreate, Skip
from tox.util.path import ensure_cachedir_dir

if TYPE_CHECKING:
from tox.config.main import Config
Expand Down Expand Up @@ -235,6 +236,7 @@ def ensure_python_env(self) -> None:
with self.cache.compare(conf, Python.__name__) as (eq, old):
if old is None: # does not exist -> create
self.create_python_env()
ensure_cachedir_dir(self.env_dir)
elif eq is False: # pragma: no branch # exists but changed -> recreate
raise Recreate(self._diff_msg(conf, old))

Expand Down
18 changes: 18 additions & 0 deletions src/tox/util/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
if TYPE_CHECKING:
from pathlib import Path

CACHEDIR_TAG_CONTENT = b"""Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by the Tox automation project (https://tox.wiki/).
# For information about cache directory tags, see:
# http://www.brynosaurus.com/cachedir/
"""


def ensure_empty_dir(path: Path, except_filename: str | None = None) -> None:
if path.exists():
Expand All @@ -24,6 +30,18 @@ def ensure_empty_dir(path: Path, except_filename: str | None = None) -> None:
path.mkdir(parents=True)


def ensure_cachedir_dir(path: Path) -> None:
"""
Ensure that the given path is a directory, exists and
contains a `CACHEDIR.TAG` file.
"""
path.mkdir(parents=True, exist_ok=True)
cachetag = path / "CACHEDIR.TAG"
if not cachetag.is_file():
cachetag.write_bytes(CACHEDIR_TAG_CONTENT)


__all__ = [
"ensure_cachedir_dir",
"ensure_empty_dir",
]

0 comments on commit 1037d6d

Please sign in to comment.