Skip to content

Commit

Permalink
doc; utility script
Browse files Browse the repository at this point in the history
  • Loading branch information
plandes committed Jul 2, 2023
1 parent 35f162c commit a425510
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 0 deletions.
11 changes: 11 additions & 0 deletions doc/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ include the original `grsync.yml` [configuration file] (see the
along with each of your freeze/thaw iterations.


## Utility git script

The [repoutil.py](../src/bin/repoutil.py) script iterates through all of your
configured repositories and performs an action on it, such as using GNU make to
clean, getting status, pulling etc. It also provides an example of how to use
the [tool's programmatic API](#api) and how it can increase your productivity
by extending the library.

The program needs the `plac` package: `pip3 install plac`.


## API

The package provides an easy to use convenient way to access your
Expand Down
112 changes: 112 additions & 0 deletions src/bin/repoutil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env python

"""A command line tool that provides information about the git repository
configuration of the user's GRSync configuration. This script iterates through
all of your configured repositories and performs an action on it, such as using
GNU make to clean, getting status, pulling etc.
"""
__author__ = 'Paul Landes'

from typing import Iterable, Callable
from dataclasses import dataclass, field
import sys
import logging
from pathlib import Path
import plac
from zensols.cli import CliHarness
from zensols.grsync import (
RepoSpec, Discoverer, DistManager, ApplicationFactory, InfoApplication
)
from zensols.util import Executor

logger = logging.getLogger('repoutil')


@dataclass
class RepoUtil(object):
"""A utility class to provide information about the git repository
configuration.
"""
log_dir: bool = field(default=True)
dry_run: bool = field(default=False)

@property
def dist_manager(self) -> DistManager:
"""The distribution manager from the Zensols Info application."""
harness: CliHarness = ApplicationFactory.create_harness()
app: InfoApplication = harness.get_instance('repos')
return app.dist_mng

@property
def repo_specs(self) -> Iterable[RepoSpec]:
"""The information on each git repository found by the GRSync
configuraiton.
"""
dist_manager: DistManager = self.dist_manager
disc: Discoverer = dist_manager.discoverer
return disc.discover(False)['repo_specs']

@property
def repo_paths(self) -> Iterable[Path]:
"""The paths of the configured git repositories."""
return map(lambda x: x.path, self.repo_specs)

def _execute(self, cmd: str, exist_path_fn: Callable = None):
ex = Executor(logger, dry_run=self.dry_run, check_exit_value=None)
for p in self.repo_paths:
check_dir = p if exist_path_fn is None else exist_path_fn(p)
if not check_dir.exists():
logger.warning(f'{p} disappeared--skipping')
else:
logger.debug(f'run {cmd}: {p}')
if self.log_dir:
logger.info(f'\n----<{p}>----')
ex.run(cmd.format(**{'path': p}))

def dirty(self):
"""Print dirty repos, which are those that need committing."""
for rs in filter(lambda rs: rs.repo.is_dirty(), self.repo_specs):
print(rs.path)
print(f'untracked: {rs.repo.untracked_files}')
print(f'difs: {rs.repo.index.diff(None)}')

def clean(self):
"""Do a make clean on all repo paths."""
def exist_path_fn(path):
return path / 'makefile'

self._execute('make -C {path} clean', exist_path_fn)

def status(self):
"""Do a git status on each path."""
self._execute('( cd {path} ; git status )')

def pull(self):
"""Pull from the default up stream repo."""
self._execute('cd {path} ; git pull --recurse-submodules')

def list(self):
"""Print a list of actions for this script"""
print('\n'.join('dirty clean status pull list'.split()))


@plac.annotations(
action=('Action: (<dirty|clean|status|pull|fix|list>)',
'positional', None, str),
dryrun=('Don\'t actually move the file.', 'flag', 'd'))
def main(action, dryrun=False):
logging.basicConfig(level=logging.WARNING, format='%(message)s')
logger.setLevel(level=logging.INFO)
ru = RepoUtil(dry_run=dryrun)
try:
getattr(ru, action)()
except AttributeError:
print(f'no such action: {action}', file=sys.stderr)
sys.exit(1)


if __name__ == '__main__':
plac.call(main)

0 comments on commit a425510

Please sign in to comment.