Source code for law.contrib.mercurial.task
# coding: utf-8
"""
Mercurial-related tasks.
"""
from __future__ import annotations
__all__ = ["BundleMercurialRepository"]
import os
import pathlib
import threading
import subprocess
from abc import abstractmethod
import luigi # type: ignore[import-untyped]
from law.task.base import Task
from law.target.file import FileSystemFileTarget, get_path
from law.target.local import LocalFileTarget
from law.parameter import NO_STR, CSVParameter
from law.decorator import log
from law.util import rel_path, interruptable_popen, quote_cmd
[docs]
class BundleMercurialRepository(Task):
task_namespace = "law.mercurial"
exclude_files = CSVParameter(
default=(),
description="patterns of files to exclude, default: ()",
)
include_files = CSVParameter(
default=(),
description="patterns of files to force-include, takes precedence over .hgignore, "
"default: ()",
)
custom_checksum = luigi.Parameter(
default=NO_STR,
description="a custom checksum to use, default: NO_STR",
)
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self._checksum: str | None = None
self._checksum_lock = threading.RLock()
@abstractmethod
def get_repo_path(self) -> str | pathlib.Path | FileSystemFileTarget:
...
@property
def checksum(self) -> str:
if self.custom_checksum != NO_STR:
return self.custom_checksum # type: ignore[return-value]
with self._checksum_lock:
if self._checksum is None:
cmd = quote_cmd([
rel_path(__file__, "scripts", "repository_checksum.sh"),
get_path(self.get_repo_path()),
" ".join(self.include_files), # type: ignore[arg-type]
" ".join(self.exclude_files), # type: ignore[arg-type]
])
out: str
code, out, _ = interruptable_popen( # type: ignore[assignment]
cmd,
shell=True,
executable="/bin/bash",
stdout=subprocess.PIPE,
)
if code != 0:
raise Exception("repository checksum calculation failed")
self._checksum = out.strip()
return self._checksum
[docs]
def output(self) -> FileSystemFileTarget:
repo_base = os.path.basename(get_path(self.get_repo_path()))
repo_base = os.path.abspath(os.path.expandvars(os.path.expanduser(repo_base)))
return LocalFileTarget(f"{repo_base}_{self.checksum}.tgz")
[docs]
@log
def run(self) -> None:
with self.output().localize("w") as tmp:
self.bundle(tmp.path)
def bundle(self, dst_path: str | pathlib.Path | FileSystemFileTarget) -> None:
cmd: list | str
cmd = [
rel_path(__file__, "scripts", "bundle_repository.sh"),
get_path(self.get_repo_path()),
get_path(dst_path),
" ".join(self.include_files), # type: ignore[arg-type]
" ".join(self.exclude_files), # type: ignore[arg-type]
]
code = interruptable_popen(cmd, executable="/bin/bash")[0]
if code != 0:
raise Exception("repository bundling failed")