PK%QZG ponto/utils.pyfrom subprocess import run, DEVNULL def program_exists(program: str) -> bool: result = run(['which', program], stdout=DEVNULL) return result.returncode == 0PK@TG~[p__ponto/configuration.pyimport yaml from .paths import CONFIG_PATH # TODO config class def open_configuration() -> dict: with CONFIG_PATH.open() as config_file: config = yaml.load(config_file) return config def save_configuration(data): with CONFIG_PATH.open('w') as config_file: yaml.safe_dump(data, config_file, default_flow_style=False) PK@TG?U労ponto/drive.pyfrom pathlib import Path from subprocess import run import os from .paths import HOME, DRIVE_DIR class Drive: def init(self, account, local_name, drive_name): account_dir = DRIVE_DIR / account origin_path = account_dir / drive_name if not origin_path.exists(): origin_path.mkdir(parents = True) # TODO context manager cwd = Path.cwd() os.chdir(str(origin_path)) try: run(['drive', 'init']) run(['drive', 'pull', drive_name], input=b'y\n') except FileNotFoundError: print("Drive not installed") return finally: os.chdir(str(cwd)) link_path = HOME / Path(local_name) target_path = origin_path / drive_name try: link_path.symlink_to(target_path) except FileExistsError: print("Link already exists")PK@TG#tR ponto/scm.pyfrom pathlib import Path from subprocess import run import re import os from .paths import HOME, BASE_DIR SSH_REGEX = re.compile(r"^(?P\w+?)@(?P.*):(?P.*?)\/(?P.*?)\.git") SSH_URL_REGEX = re.compile(r"^ssh://(?P\w+?)@(?P.*):(?P.*?)\/(?P.*?)\/(?P.*?)\.git") class InvalidRepo(Exception): def __init__(self, url): self.url = url class GitRepository: def __init__(self, url): self.url = url match = SSH_REGEX.match(url) or SSH_URL_REGEX.match(url) if not match: raise InvalidRepo(url) self.host = match.group('host') # type: str self.owner = match.group('owner') # type: str self.repo = match.group('repo') # type: str # TODO https def __str__(self): return '{host}/{owner}/{repo}'.format_map(vars(self)) @property def local_path(self) -> Path: local_repo = HOME / 'scm' / self.host / self.owner / self.repo return local_repo def clone(self): try: self.local_path.mkdir(parents=True) except FileExistsError: pass git_folder = self.local_path / '.git' if not git_folder.exists(): run(['git', 'clone', self.url, str(self.local_path)]) else: print('Git repository {repo} already exists'.format(repo=self)) class ConfigRepo(): def add(self, filename): cwd = Path.cwd() os.chdir(str(BASE_DIR)) run(['git', 'add', filename]) os.chdir(str(cwd)) def init(self): run(['git', 'init', str(BASE_DIR)]) def clone(self, repo_url): run(['git', 'clone', repo_url, str(BASE_DIR)]) cwd = Path.cwd() os.chdir(str(BASE_DIR)) run(['git', 'branch', '--set-upstream-to=origin/master', 'master']) os.chdir(str(cwd)) def commit(self, message): cwd = Path.cwd() os.chdir(str(BASE_DIR)) run(['git', 'commit', '-m', message]) os.chdir(str(cwd)) def pull(self): cwd = Path.cwd() os.chdir(str(BASE_DIR)) run(['git', 'pull', '--ff']) os.chdir(str(cwd)) def push(self): cwd = Path.cwd() os.chdir(str(BASE_DIR)) run(['git', 'push']) os.chdir(str(cwd)) PK@TGponto/__init__.pyPK@TG02ponto/__main__.pyfrom .cli import cli cli()PKQZG}ponto/paths.pyfrom pathlib import Path from os.path import expanduser HOME = Path(expanduser("~")) BASE_DIR = HOME / '.ponto' # type: Path CONFIG_PATH = BASE_DIR / 'ponto.yaml' DRIVE_DIR = BASE_DIR / 'drive' DOTFILES_PATH = BASE_DIR / 'home' # path to store dotfiles def relative_to_home(path) -> Path: path = Path(path).absolute() if HOME in path.parents: path = '~' / path.relative_to(HOME) return path PKQZG$znn ponto/cli.pyfrom clickclick import info, warning from pathlib import Path from subprocess import run import click import os import platform from .configuration import open_configuration, save_configuration from .drive import Drive from .paths import HOME, BASE_DIR, CONFIG_PATH, DRIVE_DIR, DOTFILES_PATH, relative_to_home from .scm import GitRepository, ConfigRepo from .utils import program_exists cli = click.Group() @cli.command('add-drive') @click.argument('ACCOUNT') @click.argument('LOCAL_NAME') @click.argument('DRIVE_NAME') def add_drive(account, local_name, drive_name): config = open_configuration() drive = Drive() drive.init(account, local_name, drive_name) config['drive'][local_name] = {'account': account, 'drive_name': local_name} save_configuration(config) repo = ConfigRepo() repo.add('ponto.yaml') repo.commit('Added drive from {account}/{drive_name} to ~/{local_name}'.format_map(locals())) @cli.command('add-link') @click.argument('TARGET') @click.argument('LINK_NAME') def add_link(target, link_name): config = open_configuration() link_path_relative = relative_to_home(link_name) link_path = Path(os.path.expanduser(link_name)) target_path = relative_to_home(target) info('Linking {target_path} to {link_path}'.format_map(locals())) link_path.symlink_to(os.path.expanduser(str(target_path))) config['ln'][str(target_path)] = str(link_path_relative) save_configuration(config) repo = ConfigRepo() repo.add('ponto.yaml') repo.commit('Added symlink from {target_path} to {link_path}'.format_map(locals())) @cli.command('add-repo') @click.argument('SCM_URL') def add_repo(scm_url): config = open_configuration() repository = GitRepository(scm_url) info('Cloning {repo}'.format(repo=repository)) repository.clone() config['scm'].add(scm_url) save_configuration(config) repo = ConfigRepo() repo.add('ponto.yaml') repo.commit("Added '{repository}' to the repository list".format_map(locals())) @cli.command('clone') @click.argument("GIT_URL") def clone(git_url: str): repo = ConfigRepo() info("Cloning Repository") repo.clone(git_url) pre_script = BASE_DIR / 'pre.sh' system_pre_script = BASE_DIR / 'pre-{system}.sh'.format(system=platform.system().lower()) if system_pre_script.exists(): info('Executing pre script for this OS') run(str(system_pre_script.absolute())) if pre_script.exists(): info('Executing pre script') run(str(pre_script.absolute())) sync() @cli.command('edit-pre') @click.option('--global', 'system', flag_value=None, default=True) @click.option('--linux', 'system', flag_value='linux') @click.option('--darwin', 'system', flag_value='darwin') def edit_pre(system): filename = 'pre-{system}.sh'.format_map(locals()) if system else 'pre.sh' pre_script = BASE_DIR / filename run(['vim', str(pre_script.absolute())]) pre_script.chmod(0o750) repo = ConfigRepo() repo.add(filename) commit_message = input('Commit message: ') or 'Updated Pre-Script' commit_message = '{filename}: {commit_message}'.format_map(locals()) repo.commit(commit_message) @cli.command('init') @click.argument("GIT_URL") def init(git_url): repo = ConfigRepo() info("Setting up git repository") git_folder = BASE_DIR / '.git' if not git_folder.exists(): repo.init() # TODO context manager cwd = Path.cwd() os.chdir(str(BASE_DIR)) run(['git', 'remote', 'add', 'origin', git_url]) os.chdir(str(cwd)) info("Creating directory structure") if not BASE_DIR.exists(): BASE_DIR.mkdir() if not DRIVE_DIR.exists(): DRIVE_DIR.mkdir() gitignore_path = BASE_DIR / '.gitignore' if not gitignore_path.exists(): with gitignore_path.open('w') as gitignore: gitignore.write(b'drive\n') info("Creating config files") if not CONFIG_PATH.exists(): empty_config = {'drive': dict(), 'scm': set(), 'ln': dict()} save_configuration(empty_config) cwd = Path.cwd() os.chdir(str(BASE_DIR)) repo.add('ponto.yaml') repo.add('.gitignore') repo.commit('Initialization') repo.push() os.chdir(str(cwd)) @cli.command('push') def push(): repo = ConfigRepo() if DOTFILES_PATH.exists(): repo.add('home') # TODO try catch error repo.commit('Included local changes') repo.push() @cli.command('store') @click.argument('PATH') def store(path): path = Path(path) if path.is_symlink(): print("{path} is already a symlink.".format(path=path)) return print("Storing {path}".format(path=path)) relative_path = path.relative_to(HOME) try: DOTFILES_PATH.mkdir(parents=True) except FileExistsError: pass destination = DOTFILES_PATH / relative_path try: destination.parent.mkdir(parents=True) except FileExistsError: pass path.rename(destination) path.symlink_to(destination) repo = ConfigRepo() repo.add(str('home' / relative_path)) repo.commit("Storing {path}".format(path=path)) # TODO error if not inside home @cli.command('sync') def sync(): repo = ConfigRepo() repo.pull() config = open_configuration() if program_exists('drive'): drive_points = config.get('drive', {}) # type: dict[str, dict] drive = Drive() for local_name, point in drive_points.items(): account = point['account'] drive_name = point['drive_name'] # TODO ignore on error info('Cloning {account}/{drive_name} to ~/{local_name}'.format_map(locals())) drive.init(account, local_name, drive_name) else: warning('Drive is not installed.') if program_exists('git'): scm_urls = config.get('scm', []) # type: List[str] for url in scm_urls: repository = GitRepository(url) info('Cloning {repo}'.format(repo=repository)) repository.clone() else: warning('Git is not installed.') links = config.get('ln') for target, link in links.items(): link_path = Path(os.path.expanduser(link)) target_path = Path(os.path.expanduser(target)) if not link_path.exists(): info('Linking {target_path} to {link_path}'.format_map(locals())) link_path.symlink_to(target_path) else: info('{link} already exists'.format_map(locals())) for path in DOTFILES_PATH.glob('**/*'): relative_path = path.relative_to(DOTFILES_PATH) link_path = HOME / relative_path try: info("Linking ~/{relative_path}.".format_map(locals())) link_path.symlink_to(path) except FileExistsError: # TODO offer to replace print("~/{relative_path} already exists.".format_map(locals())) cli() PKmRZG%ponto-0.0.3.dist-info/DESCRIPTION.rstGit Backed Backup Manager PKmRZG)യ))&ponto-0.0.3.dist-info/entry_points.txt[console_scripts] ponto = ponto.cli:cli PKmRZG|x#ponto-0.0.3.dist-info/metadata.json{"classifiers": ["Programming Language :: Python", "Programming Language :: Python :: 3.5", "Development Status :: 4 - Beta", "Operating System :: OS Independent"], "extensions": {"python.commands": {"wrap_console": {"ponto": "ponto.cli:cli"}}, "python.details": {"contacts": [{"email": "jmcs@jsantos.eu", "name": "Jo\u00e3o Santos", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/jmcs/ponto"}}, "python.exports": {"console_scripts": {"ponto": "ponto.cli:cli"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "license": "MIT License", "metadata_version": "2.0", "name": "ponto", "run_requires": [{"requires": ["click", "clickclick", "pathlib", "pip", "requests"]}], "summary": "Backup Manager", "version": "0.0.3"}PKmRZG43+#ponto-0.0.3.dist-info/top_level.txtponto PKmRZG}\\ponto-0.0.3.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PKmRZGoponto-0.0.3.dist-info/METADATAMetadata-Version: 2.0 Name: ponto Version: 0.0.3 Summary: Backup Manager Home-page: https://github.com/jmcs/ponto Author: João Santos Author-email: jmcs@jsantos.eu License: MIT License Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3.5 Classifier: Development Status :: 4 - Beta Classifier: Operating System :: OS Independent Requires-Dist: click Requires-Dist: clickclick Requires-Dist: pathlib Requires-Dist: pip Requires-Dist: requests Git Backed Backup Manager PKmRZGSD||ponto-0.0.3.dist-info/RECORDponto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 ponto/__main__.py,sha256=iKu6TcO6vMxMmwm0gE8VTbjmc86fKp4GvJCTwh-GUt8,27 ponto/cli.py,sha256=5kJTk66h7IxmwxKR2xNfUOwZ7w9e5oPe4uo6nTgHIic,7022 ponto/configuration.py,sha256=WiZPNFNqqPVUibUIlYmJ2LAbbVybYA8WhTH9L-RVqOg,351 ponto/drive.py,sha256=o0lpltoC-H6zrKV8tkbH99SEoqZohc31d7_PcesV17E,948 ponto/paths.py,sha256=rcgPVr07a-rMAt6qfB9LtRxHIWcLIbtnxjZxBeTHJm8,416 ponto/scm.py,sha256=vUhwhOtttY5n-ioU6LOTUj8Xu18zuKgjetpst8uQzaA,2302 ponto/utils.py,sha256=kfGXvfcMmZoUkslLeVOC9LFOzzNR7BQ90_smmEbnU40,166 ponto-0.0.3.dist-info/DESCRIPTION.rst,sha256=POwem4_yqvqaZ-ZOauBaIKmOEmOX80Ik3SrAMfS4eKE,28 ponto-0.0.3.dist-info/METADATA,sha256=RmId7B80YHLItTZJUz7C5IQC72WCOXWmU7uxGMSqPTo,529 ponto-0.0.3.dist-info/RECORD,, ponto-0.0.3.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 ponto-0.0.3.dist-info/entry_points.txt,sha256=szHyn_ZoJ8dl_tN6ALgo7pYCA6HkhItGhtYF4jDkhD8,41 ponto-0.0.3.dist-info/metadata.json,sha256=iX_fRWY1hAcF2fs1Ht3XQ3PuRuynNkEOb0suQoiz2Ac,792 ponto-0.0.3.dist-info/top_level.txt,sha256=FGo7nFEimBoCJ657TeD4ALeThQz-d0rtk719xk_rsZs,6 PK%QZG ponto/utils.pyPK@TG~[p__ponto/configuration.pyPK@TG?U労eponto/drive.pyPK@TG#tR Eponto/scm.pyPK@TGmponto/__init__.pyPK@TG02ponto/__main__.pyPKQZG}ponto/paths.pyPKQZG$znn ponto/cli.pyPKmRZG%J-ponto-0.0.3.dist-info/DESCRIPTION.rstPKmRZG)യ))&-ponto-0.0.3.dist-info/entry_points.txtPKmRZG|x#.ponto-0.0.3.dist-info/metadata.jsonPKmRZG43+#o1ponto-0.0.3.dist-info/top_level.txtPKmRZG}\\1ponto-0.0.3.dist-info/WHEELPKmRZGoK2ponto-0.0.3.dist-info/METADATAPKmRZGSD||4ponto-0.0.3.dist-info/RECORDPKN9