-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 894fa16
Showing
22 changed files
with
906 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
|
||
# C extensions | ||
*.so | ||
|
||
# Distribution / packaging | ||
.Python | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
wheels/ | ||
pip-wheel-metadata/ | ||
share/python-wheels/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
MANIFEST | ||
|
||
# PyInstaller | ||
# Usually these files are written by a python script from a template | ||
# before PyInstaller builds the exe, so as to inject date/other infos into it. | ||
*.manifest | ||
*.spec | ||
|
||
# Installer logs | ||
pip-log.txt | ||
pip-delete-this-directory.txt | ||
|
||
# Unit test / coverage reports | ||
htmlcov/ | ||
.tox/ | ||
.nox/ | ||
.coverage | ||
.coverage.* | ||
.cache | ||
nosetests.xml | ||
coverage.xml | ||
*.cover | ||
*.py,cover | ||
.hypothesis/ | ||
.pytest_cache/ | ||
|
||
# Translations | ||
*.mo | ||
*.pot | ||
|
||
# Django stuff: | ||
*.log | ||
local_settings.py | ||
db.sqlite3 | ||
db.sqlite3-journal | ||
|
||
# Flask stuff: | ||
instance/ | ||
.webassets-cache | ||
|
||
# Scrapy stuff: | ||
.scrapy | ||
|
||
# Sphinx documentation | ||
docs/_build/ | ||
|
||
# PyBuilder | ||
target/ | ||
|
||
# Jupyter Notebook | ||
.ipynb_checkpoints | ||
|
||
# IPython | ||
profile_default/ | ||
ipython_config.py | ||
|
||
# pyenv | ||
.python-version | ||
|
||
# pipenv | ||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | ||
# However, in case of collaboration, if having platform-specific dependencies or dependencies | ||
# having no cross-platform support, pipenv may install dependencies that don't work, or not | ||
# install all needed dependencies. | ||
#Pipfile.lock | ||
|
||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow | ||
__pypackages__/ | ||
|
||
# Celery stuff | ||
celerybeat-schedule | ||
celerybeat.pid | ||
|
||
# SageMath parsed files | ||
*.sage.py | ||
|
||
# Environments | ||
.env | ||
.venv | ||
env/ | ||
venv/ | ||
ENV/ | ||
env.bak/ | ||
venv.bak/ | ||
|
||
# Spyder project settings | ||
.spyderproject | ||
.spyproject | ||
|
||
# Rope project settings | ||
.ropeproject | ||
|
||
# mkdocs documentation | ||
/site | ||
|
||
# mypy | ||
.mypy_cache/ | ||
.dmypy.json | ||
dmypy.json | ||
|
||
# Pyre type checker | ||
.pyre/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
from os.path import abspath, commonpath, join | ||
|
||
from bossman.resources import ResourceManager, ResourceStatus | ||
from bossman.abc.resource_type import ResourceTypeABC | ||
from bossman.abc.resource import ResourceABC | ||
from bossman.changes import Change, ChangeSet | ||
from bossman.config import Config, ResourceTypeConfig | ||
from bossman.logging import get_class_logger | ||
from bossman.repo import Repo | ||
|
||
|
||
class Bossman: | ||
def __init__(self, repo_path, config: Config): | ||
self.config = config | ||
self.repo = Repo(repo_path) | ||
self.resource_manager = ResourceManager() | ||
for config in self.config.resource_types: | ||
resource_type = ResourceTypeABC.create(config) | ||
self.resource_manager.register_resource_type(resource_type) | ||
self.logger = get_class_logger(self) | ||
|
||
def get_resources(self, rev: str = "HEAD") -> list: | ||
return self.resource_manager.get_resources(self.repo, rev) | ||
|
||
def get_resource_status(self, resource: ResourceABC) -> ResourceStatus: | ||
resource_type = self.resource_manager.get_resource_type(resource.path) | ||
local_rev = self.repo.get_last_change_rev(resource.paths) | ||
remote_rev = resource_type.get_remote_rev(resource) | ||
missing_changesets = self.get_changesets(remote_rev, local_rev, [resource]) | ||
dirty = resource_type.is_dirty(resource) | ||
return ResourceStatus( | ||
local_rev=local_rev, | ||
remote_rev=remote_rev, | ||
dirty=dirty, | ||
missing_changesets=missing_changesets | ||
) | ||
|
||
def get_changesets(self, since_rev: str = None, until_rev: str = "HEAD", resources: list = None) -> str: | ||
from collections import defaultdict | ||
resources = resources if resources else self.get_resources() | ||
paths = [resource.path for resource in resources] | ||
commits = self.repo.get_commits(since_rev, until_rev, paths) | ||
changeSets = [] | ||
for commit in commits: | ||
changeSet = ChangeSet(commit) | ||
changeSets.append(changeSet) | ||
resources = dict() | ||
for diff in commit.diffs: | ||
the_path = diff.b_path or diff.a_path | ||
resource = self.resource_manager.get_resource(the_path) | ||
if resource: | ||
changeSet.add_resource_diff(resource, diff) | ||
return changeSets | ||
|
||
def apply_change(self, changeset: ChangeSet, change: Change): | ||
resource_type = self.resource_manager.get_resource_type(change.resource.path) | ||
resource_type.apply(changeset, change) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
from abc import ABC, abstractproperty | ||
|
||
class ResourceABC(ABC): | ||
""" | ||
A {ResourceABC} is just a thing that lives at a path. | ||
""" | ||
def __init__(self, path): | ||
self.path = path | ||
|
||
@abstractproperty | ||
def paths(self): | ||
pass | ||
|
||
def __eq__(self, other): | ||
if isinstance(other, ResourceABC): | ||
return self.path == other.path | ||
return False | ||
|
||
def __hash__(self): | ||
return hash(self.path) | ||
|
||
def __str__(self): | ||
return self.path |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
from abc import ABC, abstractmethod | ||
from bossman.config import ResourceTypeConfig | ||
from bossman.changes import Change, ChangeSet | ||
from bossman.logging import logger | ||
from bossman.abc.resource import ResourceABC | ||
import parse | ||
from os.path import relpath, join | ||
|
||
class ResourceTypeABC(ABC): | ||
@staticmethod | ||
def create(config: ResourceTypeConfig): | ||
import importlib | ||
plugin = importlib.import_module(config.module) | ||
return plugin.ResourceType(config) | ||
|
||
""" | ||
Abstract class for resource_types. | ||
""" | ||
def __init__(self, config: ResourceTypeConfig): | ||
self.config = config | ||
self.logger = logger.getChild( | ||
"{module_name}.{class_name}".format( | ||
module_name=self.__class__.__module__, | ||
class_name=self.__class__.__name__ | ||
) | ||
) | ||
self.logger.info("config={config}".format(config=str(config))) | ||
|
||
def get_resources(self, paths: list): | ||
""" | ||
Given a list of paths relative to the repo, determine the subset managed | ||
by this resource type and return the list of {ResourceABC}s. | ||
""" | ||
return set( | ||
resource | ||
for resource | ||
in ( | ||
self.get_resource(path) | ||
for path | ||
in paths | ||
) | ||
if resource != None | ||
) | ||
|
||
def match(self, path: str) -> bool: | ||
result = parse.search(self.config.pattern, path) | ||
if result: | ||
canonical = self.config.pattern.format(**result.named) | ||
return path.startswith(canonical) | ||
return False | ||
|
||
def get_resource(self, path: str) -> ResourceABC: | ||
result = parse.search(self.config.pattern, path) | ||
if result: | ||
canonical = self.config.pattern.format(**result.named) | ||
if path.startswith(canonical): | ||
return self.create_resource(canonical, **result.named) | ||
return None | ||
|
||
def describe_diffs(self, resource: ResourceABC, diffs: list) -> Change: | ||
change = Change(resource) | ||
change.diffs = diffs | ||
return change | ||
|
||
@abstractmethod | ||
def create_resource(self, path, **kwargs) -> ResourceABC: | ||
pass | ||
|
||
@abstractmethod | ||
def get_remote_rev(self, resource: ResourceABC) -> str: | ||
pass | ||
|
||
@abstractmethod | ||
def is_dirty(self, resource: ResourceABC) -> bool: | ||
pass | ||
|
||
@abstractmethod | ||
def apply(self, changeset: ChangeSet, change: Change) -> bool: | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from os.path import basename | ||
from bossman.repo import Commit, Diff | ||
from bossman.abc.resource import ResourceABC | ||
from collections import OrderedDict | ||
|
||
class ChangeSet: | ||
def __init__(self, commit: Commit): | ||
self.rev = commit.rev | ||
self.message = commit.message | ||
self.author = commit.author | ||
self.date = commit.date | ||
self.resource_changes = OrderedDict() | ||
|
||
def add_resource_diff(self, resource: ResourceABC, diff: Diff): | ||
if not (resource in self.resource_changes): | ||
self.resource_changes[resource] = Change(resource) | ||
change = self.resource_changes.get(resource) | ||
change.diffs.append(diff) | ||
|
||
def __str__(self): | ||
s = "[{rev}] {date} {message} | {author}".format(rev=self.rev, message=self.message.split("\n")[0], author=self.author, date=self.date) | ||
if len(self.resource_changes): | ||
s += "\n\n " | ||
s += "\n ".join(str(change) for change in self.resource_changes.values()) | ||
s += "\n" | ||
return s | ||
|
||
class Change: | ||
def __init__(self, resource: ResourceABC): | ||
self.resource = resource | ||
self.diffs = [] | ||
|
||
def __str__(self): | ||
s = "{resource:<58} (".format(resource=str(self.resource)) | ||
s += ", ".join(map(format_diff, self.diffs)) | ||
s += ")" | ||
return s | ||
|
||
def format_diff(diff): | ||
if diff.change_type in 'D': | ||
return diff.change_type + " " + basename(diff.a_path) | ||
elif diff.change_type == 'R': | ||
return diff.change_type + " " + basename(diff.a_path) + " -> " + basename(diff.b_path) | ||
else: | ||
return diff.change_type + " " + basename(diff.b_path) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import sys | ||
from os import path, getcwd | ||
import yaml | ||
import argparse | ||
from bossman.cli import status_cmd, log_cmd, apply_cmd | ||
from bossman.config import Config | ||
from bossman.logging import logger | ||
from bossman import Bossman | ||
from bossman.config import Config | ||
|
||
logger = logger.getChild(globals().get("__name__")) | ||
|
||
def main(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("--verbosity", action=SetVerbosity, default="ERROR", type=str, choices=["DEBUG", "INFO", "WARNING", "ERROR"]) | ||
parser.add_argument("--repo", help="path to the repository", default=getcwd()) | ||
|
||
subparsers = parser.add_subparsers(title="Subcommands") | ||
status_cmd.init(subparsers) | ||
log_cmd.init(subparsers) | ||
apply_cmd.init(subparsers) | ||
|
||
args = parser.parse_args() | ||
bossman = create_bossman(args) | ||
|
||
if "func" in args: | ||
args.func(bossman, **vars(args)) | ||
else: | ||
parser.print_usage() | ||
|
||
def create_bossman(args): | ||
conf_path = path.join(args.repo, ".bossman") | ||
if path.exists(conf_path): | ||
fd = open(conf_path, "r") | ||
conf_data = yaml.safe_load(fd) | ||
|
||
config = Config(conf_data) | ||
|
||
bossman = Bossman(args.repo, config) | ||
return bossman | ||
|
||
class SetVerbosity(argparse.Action): | ||
""" | ||
Set logger verbosity when --verbosity argument is provided. | ||
""" | ||
def __init__(self, *args, **kwargs): | ||
super(SetVerbosity, self).__init__(*args, **kwargs) | ||
|
||
def __call__(self, parser, namespace, values, option_string=None): | ||
import logging | ||
logging.basicConfig(level=values) | ||
logger.info("SetVerbosity verbosity={verbosity}".format(verbosity=values)) |
Oops, something went wrong.