~gpanders/pushbroom

41329ea1550f33152199af517c5d9ff5ac7b113a — Greg Anders 1 year, 11 months ago 897f9ee
Add Shred option
3 files changed, 51 insertions(+), 22 deletions(-)

M README.md
M src/pushbroom/console.py
M src/pushbroom/sweep.py
M README.md => README.md +7 -0
@@ 139,6 139,13 @@ List of glob expression patterns of files or directories to ignore.
List of glob expression patterns of files or directories to remove. If omitted,
everything is removed.

### Shred
**Default**: False

Securely delete files before removing them. Note that this option is mutually
exclusive with the [`Trash`](#trash) option, with `Trash` taking precedence if
both options are used.

## Automating

If installed via Homebrew then Pushbroom can be set to run once every hour using

M src/pushbroom/console.py => src/pushbroom/console.py +19 -13
@@ 8,15 8,15 @@ import sys

from pushbroom import sweep, __version__


def run():
    """Run pushbroom"""
    args = parse_args()

    setup_logging(args)
    config = read_config(args.config)
    pushbroom(config, args.dry_run)

    config = read_config(args)

def pushbroom(config, dry_run=False):
    """Run pushbroom"""
    logging.info("Starting pushbroom")
    for section in config.sections():
        path = config.get(section, "path")


@@ 30,14 30,20 @@ def run():
            ignore_re = re.compile("|".join([fnmatch.translate(x) for x in ignore]))
            match = config.get(section, "match", fallback="*").split(",")
            match_re = re.compile("|".join([fnmatch.translate(x) for x in match]))
            dry_run = args.dry_run
            shred = config.get(section, "shred", fallback=False)

            if trash:
                if shred:
                    logging.warning("Ignoring 'Shred' option while 'Trash' is set")
                    shred = False

                trash = os.path.abspath(os.path.expanduser(trash))
                if not os.path.isdir(trash):
                    logging.error("No such directory %s", trash)

            sweep(section, fullpath, num_days, ignore_re, match_re, trash, dry_run)
            sweep(
                section, fullpath, num_days, ignore_re, match_re, trash, dry_run, shred
            )


def parse_args():


@@ 89,24 95,24 @@ def setup_logging(args):
    logger.addHandler(ch)


def read_config(args):
def read_config(conf_file=None):
    """Find and read configuration file"""
    if not args.config:
    if not conf_file:
        # Look under XDG_CONFIG_HOME first, then look for ~/.pushbroomrc
        args.config = os.path.join(
        conf_file = os.path.join(
            os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")),
            "pushbroom",
            "config",
        )
        if not os.path.exists(args.config):
            args.config = os.path.expanduser("~/.pushbroomrc")
        if not os.path.exists(conf_file):
            conf_file = os.path.expanduser("~/.pushbroomrc")

    config = configparser.ConfigParser()
    try:
        with open(args.config, "r") as f:
        with open(conf_file, "r") as f:
            config.read_file(f)
    except FileNotFoundError:
        logging.error("Configuration file %s not found", args.config)
        logging.error("Configuration file %s not found", conf_file)
        sys.exit(1)

    return config

M src/pushbroom/sweep.py => src/pushbroom/sweep.py +25 -9
@@ 6,7 6,17 @@ import time
SECONDS_PER_DAY = 24 * 60 * 60


def sweep(name, path, num_days, ignore, match, trash, dry_run):
def delete(path, shred):
    if shred:
        with open(path, "ba+") as f:
            length = f.tell()
            f.seek(0)
            f.write(os.urandom(length))

    os.remove(path)


def sweep(name, path, num_days, ignore, match, trash, dry_run, shred):
    # pylint: disable = too-many-arguments
    """Remove old files from a directory



@@ 17,6 27,7 @@ def sweep(name, path, num_days, ignore, match, trash, dry_run):
    :match:    Regular expression pattern of paths to remove
    :trash:    If set, move files to this directory instead of deleting them
    :dry_run:  Only show what would happen without actually doing anything
    :shred:    Securely delete file data before removing

    """
    logging.info("Sweeping %s", name)


@@ 24,19 35,24 @@ def sweep(name, path, num_days, ignore, match, trash, dry_run):
    thresh = time.time() - num_seconds
    for root, dirs, files in os.walk(path):
        dirs[:] = [d for d in dirs if re.match(match, d) and not re.match(ignore, d)]

        files = [f for f in files if re.match(match, f) and not re.match(ignore, f)]
        for file in files:
            fpath = os.path.join(root, file)
            if not os.path.exists(fpath):
                continue

            if os.stat(fpath).st_mtime < thresh:
                if trash:
                    logging.info("Moving %s to %s", fpath, trash)
                    if not dry_run:
                        os.rename(fpath, os.path.join(trash, file))
            if os.stat(fpath).st_mtime >= thresh:
                continue

            if trash:
                logging.info("Moving %s to %s", fpath, trash)
                if not dry_run:
                    os.rename(fpath, os.path.join(trash, file))
            else:
                if shred:
                    logging.info("Securely deleting %s", fpath)
                else:
                    logging.info("Deleting %s", fpath)
                    if not dry_run:
                        os.remove(fpath)

                if not dry_run:
                    delete(fpath, shred)