def test_constructor(self): r1 = Repo.init(self.path, mkdir=True) # verify that an existing repository can be initialized r2 = Repo(r1.root) # make sure it's a Repo object. assert type(r2) is Repo # a new repo should have no HEAD try: r2.head() except NoHeadSet: pass
def test_diff(self): """Tests the `diff` method""" r = Repo.init(self.path, mkdir=True) with open(os.path.join(r.root, 'test'), 'w') as fp: fp.write('hello world') r.add(all=True) c1 = r.commit(committer='Test McGee', message='testing diff 1') with open(os.path.join(r.root, 'test'), 'w') as fp: fp.write('hello world!') r.add(all=True) c2 = r.commit(committer='Test McGee', message='testing diff 2') expected = \ """*** --- *************** *** 1 **** ! hello world! --- 1 ---- ! hello world""" result = r.diff(c2.id, c1.id, 'test') assert result == expected
def new(cls, path): """ Create and return tracker at the given path. :param path: path to create the directory at. For example, supplying ``my_tracker`` would create the directory ``./my_tracker``. """ # remove trailing slash if path.endswith('/'): path = path[:-1] # set the install paths paths = { 'root': path, 'issues': os.path.join(path, 'issues'), 'admin': os.path.join(path, '.hopper'), 'docs': os.path.join(path, 'docs') } # create the repository repo = Repo.init(paths['root'], mkdir=True) # build the default structure open(os.path.join(paths['root'], 'config'), 'w').close() os.mkdir(paths['issues']) open(os.path.join(paths['issues'], 'empty'), 'w').close() os.mkdir(paths['admin']) open(os.path.join(paths['admin'], 'empty'), 'w').close() os.mkdir(paths['docs']) # read sample docs from packaged `templates` into `docs`. templates = os.path.join(os.path.dirname(__file__), 'templates') for p in os.listdir(templates): with open(os.path.join(templates, p), 'r') as fp: data = fp.read() with open(os.path.join(paths['docs'], p), 'w') as fp: fp.write(data) # init the class tracker = cls(path) # set the config config = TrackerConfig(tracker) config.name = os.path.basename(path).capitalize() config.save() # add everything to the repo and commit repo.add(all=True) repo.commit(committer='Hopper <*****@*****.**>', message='Created the %s tracker' % config.name) # instantiate and return our new Tracker. return tracker
def new(cls, path): """ Create and return tracker at the given path. :param path: path to create the directory at. For example, supplying ``my_tracker`` would create the directory ``./my_tracker``. """ # remove trailing slash if path.endswith("/"): path = path[:-1] # set the install paths paths = { "root": path, "issues": os.path.join(path, "issues"), "admin": os.path.join(path, ".hopper"), "docs": os.path.join(path, "docs"), } # create the repository repo = Repo.init(paths["root"], mkdir=True) # build the default structure open(os.path.join(paths["root"], "config"), "w").close() os.mkdir(paths["issues"]) open(os.path.join(paths["issues"], "empty"), "w").close() os.mkdir(paths["admin"]) open(os.path.join(paths["admin"], "empty"), "w").close() os.mkdir(paths["docs"]) # read sample docs from packaged `templates` into `docs`. templates = os.path.join(os.path.dirname(__file__), "templates") for p in os.listdir(templates): with open(os.path.join(templates, p), "r") as fp: data = fp.read() with open(os.path.join(paths["docs"], p), "w") as fp: fp.write(data) # init the class tracker = cls(path) # set the config config = TrackerConfig(tracker) config.name = os.path.basename(path).capitalize() config.save() # add everything to the repo and commit repo.add(all=True) repo.commit(committer="Hopper <*****@*****.**>", message="Created the %s tracker" % config.name) # instantiate and return our new Tracker. return tracker
def __init__(self, path): if not os.path.exists(path): path = match_path(path) if path is None: raise OSError('Supplied path does not exist') # remove trailing slash if path.endswith('/'): path = path[:-1] self.path = path self.paths = { 'root': self.path, 'base': os.path.basename(self.path), 'config': os.path.join(self.path, 'config'), 'issues': os.path.join(self.path, 'issues'), 'admin': os.path.join(self.path, '.hopper'), 'docs': os.path.join(self.path, 'docs') } self.properties = {'name': None} self.repo = Repo(path) self.config = TrackerConfig(self) self.db = Database(self)
def _repo_with_commits(self, num_commits=1): """ Returns a repo with one or more commits, on master branch, with a tag 'v1.0' that points to the last commit. """ r = Repo.init(self.path, mkdir=True) for c in range(num_commits): for i in range(4): self._rand_file('spam-%d' % i) # add the files/changes r.add(all=True) # commit the changes r.commit(committer='Joe Sixpack', message='Commit %d' % c) r.tag('v1.0') return r
def test_init(self): """Tests the `init` method""" # NOTE init refers not to __init__, but the classmethod for creating # repositories. See test_constructor() for __init__. r = Repo.init(self.path, mkdir=True) # make sure it created something. assert os.path.isdir(self.path) # does the dir have a .git? assert os.path.isdir(os.path.join(self.path, '.git')) # make sure it returns a Repo object. assert type(r) is Repo
def test_commit(self): """Tests the `commit` method""" r = Repo.init(self.path, mkdir=True) self._rand_file('spam') r.add('spam') c = r.commit(committer='GOB Bluth', message='Come on!') # make sure the commit got set right assert type(c) is Commit assert c.author == 'GOB Bluth' assert c.message == 'Come on!' # the commit should be the same as the Repo.head assert c == r.head()
def __init__(self, path): if not os.path.exists(path): path = match_path(path) if path is None: raise OSError("Supplied path does not exist") # remove trailing slash if path.endswith("/"): path = path[:-1] self.path = path self.paths = { "root": self.path, "base": os.path.basename(self.path), "config": os.path.join(self.path, "config"), "issues": os.path.join(self.path, "issues"), "admin": os.path.join(self.path, ".hopper"), "docs": os.path.join(self.path, "docs"), } self.properties = {"name": None} self.repo = Repo(path) self.config = TrackerConfig(self) self.db = Database(self)
class Tracker(object): """ Defines a Hopper tracker and provides paths to files within a tracker. :param path: the relative or absolute path to the tracker """ def __init__(self, path): if not os.path.exists(path): path = match_path(path) if path is None: raise OSError("Supplied path does not exist") # remove trailing slash if path.endswith("/"): path = path[:-1] self.path = path self.paths = { "root": self.path, "base": os.path.basename(self.path), "config": os.path.join(self.path, "config"), "issues": os.path.join(self.path, "issues"), "admin": os.path.join(self.path, ".hopper"), "docs": os.path.join(self.path, "docs"), } self.properties = {"name": None} self.repo = Repo(path) self.config = TrackerConfig(self) self.db = Database(self) @classmethod def new(cls, path): """ Create and return tracker at the given path. :param path: path to create the directory at. For example, supplying ``my_tracker`` would create the directory ``./my_tracker``. """ # remove trailing slash if path.endswith("/"): path = path[:-1] # set the install paths paths = { "root": path, "issues": os.path.join(path, "issues"), "admin": os.path.join(path, ".hopper"), "docs": os.path.join(path, "docs"), } # create the repository repo = Repo.init(paths["root"], mkdir=True) # build the default structure open(os.path.join(paths["root"], "config"), "w").close() os.mkdir(paths["issues"]) open(os.path.join(paths["issues"], "empty"), "w").close() os.mkdir(paths["admin"]) open(os.path.join(paths["admin"], "empty"), "w").close() os.mkdir(paths["docs"]) # read sample docs from packaged `templates` into `docs`. templates = os.path.join(os.path.dirname(__file__), "templates") for p in os.listdir(templates): with open(os.path.join(templates, p), "r") as fp: data = fp.read() with open(os.path.join(paths["docs"], p), "w") as fp: fp.write(data) # init the class tracker = cls(path) # set the config config = TrackerConfig(tracker) config.name = os.path.basename(path).capitalize() config.save() # add everything to the repo and commit repo.add(all=True) repo.commit(committer="Hopper <*****@*****.**>", message="Created the %s tracker" % config.name) # instantiate and return our new Tracker. return tracker def autocommit(self, message, author=None): """ Commit any changes to the repo. In most scenarios, the user responsible for the change(s) would be listed as the commit author, and Hopper would be the committer. :param msg: the commit message to use. :param author: the commit's author in string or dictionary format. For example: ``'Full Name <*****@*****.**>'`` **or** ``{'name': 'Full Name', 'email': '*****@*****.**'}`` :return: the Commit object. """ committer = "Hopper <*****@*****.**>" if type(author) is dict: author = "%s <%s>" % (author["name"], author["email"]) if type(author) is not str: author = committer self.repo.add(all=True) return self.repo.commit(message=message, committer=committer, author=author) def doc(self, path): """ Return the document at the path. :param path: a path relative to the tracker's `docs` directory. """ matches = glob.glob(os.path.join(self.paths["docs"], path) + "*") if not matches: raise OSError("Document does not exist") match = os.path.relpath(matches[0], self.paths["docs"]) return Document(self, match) def docs(self): """Return a list of Document objects.""" return [Document(self, path) for path in os.listdir(self.paths["docs"])] def read(self, relpath, mode="r"): """ Read a file relative to the tracker root. :param relpath: a path relative to the tracker's root directory. :param mode: the file mode to use (e.g. 'r' or 'wb'). """ path = os.path.join(self.paths["root"], relpath) with open(path, mode) as fp: return fp.read() def issue(self, sha): """ Return the Issue object with the given SHA1. :param sha: the issue's SHA1 identifier. """ return Issue(self, sha) def issues(self, **kwargs): """ Return issues, with options to limit, offset, sort, and filter the result set. This method is a handoff to Query.select(), here for convenience. Both methods take the same paramters. :param order_by: order the results by this column. :param status: return results with this status. :param limit: maximum number of results to return. :param offset: skip the first n-results. :param reverse: results are returned in ascending order if True, descending if False. """ query = Query(self) return query.select(**kwargs) def history(self, n=10, offset=0, all=False): """ Return a list of dictionaries containing the action and the actor. These are assembled from the Git repository's commit log. :param n: the maximum number of history items to return. :param all: ignore n and return everything. """ if all: commits = self.repo.commits(n=1000) else: commits = self.repo.commits(n=n) if len(commits) > offset: return commits[offset:] else: return [] def get_issue_path(self, sha): """ Returns the absolute path to the issue. It doesn't check if the issue exists; this should be done afterwards if necessary. :param sha: the issue's unique identifier. """ return os.path.join(self.paths["issues"], sha, "issue") def query(self): """ Returns a hopper.query.Query object for querying the issue database. """ return Query(self) def _get_issues(self): """Returns a list of all issue objects.""" return [Issue(self, sha) for sha in self._get_issue_shas()] def _get_issue_shas(self): """Return a list of the SHA1s of all issues in the tracker.""" # we'll just return any paths in tracker/issues/ with 40 chars. # since we're not verifying, this may not be 100% accurate. return filter(lambda x: len(x) == 40, os.listdir(self.paths["issues"]))
class Tracker(object): """ Defines a Hopper tracker and provides paths to files within a tracker. :param path: the relative or absolute path to the tracker """ def __init__(self, path): if not os.path.exists(path): path = match_path(path) if path is None: raise OSError('Supplied path does not exist') # remove trailing slash if path.endswith('/'): path = path[:-1] self.path = path self.paths = { 'root': self.path, 'base': os.path.basename(self.path), 'config': os.path.join(self.path, 'config'), 'issues': os.path.join(self.path, 'issues'), 'admin': os.path.join(self.path, '.hopper'), 'docs': os.path.join(self.path, 'docs') } self.properties = {'name': None} self.repo = Repo(path) self.config = TrackerConfig(self) self.db = Database(self) @classmethod def new(cls, path): """ Create and return tracker at the given path. :param path: path to create the directory at. For example, supplying ``my_tracker`` would create the directory ``./my_tracker``. """ # remove trailing slash if path.endswith('/'): path = path[:-1] # set the install paths paths = { 'root': path, 'issues': os.path.join(path, 'issues'), 'admin': os.path.join(path, '.hopper'), 'docs': os.path.join(path, 'docs') } # create the repository repo = Repo.init(paths['root'], mkdir=True) # build the default structure open(os.path.join(paths['root'], 'config'), 'w').close() os.mkdir(paths['issues']) open(os.path.join(paths['issues'], 'empty'), 'w').close() os.mkdir(paths['admin']) open(os.path.join(paths['admin'], 'empty'), 'w').close() os.mkdir(paths['docs']) # read sample docs from packaged `templates` into `docs`. templates = os.path.join(os.path.dirname(__file__), 'templates') for p in os.listdir(templates): with open(os.path.join(templates, p), 'r') as fp: data = fp.read() with open(os.path.join(paths['docs'], p), 'w') as fp: fp.write(data) # init the class tracker = cls(path) # set the config config = TrackerConfig(tracker) config.name = os.path.basename(path).capitalize() config.save() # add everything to the repo and commit repo.add(all=True) repo.commit(committer='Hopper <*****@*****.**>', message='Created the %s tracker' % config.name) # instantiate and return our new Tracker. return tracker def autocommit(self, message, author=None): """ Commit any changes to the repo. In most scenarios, the user responsible for the change(s) would be listed as the commit author, and Hopper would be the committer. :param msg: the commit message to use. :param author: the commit's author in string or dictionary format. For example: ``'Full Name <*****@*****.**>'`` **or** ``{'name': 'Full Name', 'email': '*****@*****.**'}`` :return: the Commit object. """ committer = 'Hopper <*****@*****.**>' if type(author) is dict: author = '%s <%s>' % (author['name'], author['email']) if type(author) is not str: author = committer self.repo.add(all=True) return self.repo.commit(message=message, committer=committer, author=author) def doc(self, path): """ Return the document at the path. :param path: a path relative to the tracker's `docs` directory. """ matches = glob.glob(os.path.join(self.paths['docs'], path) + '*') if not matches: raise OSError('Document does not exist') match = os.path.relpath(matches[0], self.paths['docs']) return Document(self, match) def docs(self): """Return a list of Document objects.""" return [ Document(self, path) for path in os.listdir(self.paths['docs']) ] def read(self, relpath, mode='r'): """ Read a file relative to the tracker root. :param relpath: a path relative to the tracker's root directory. :param mode: the file mode to use (e.g. 'r' or 'wb'). """ path = os.path.join(self.paths['root'], relpath) with open(path, mode) as fp: return fp.read() def issue(self, sha): """ Return the Issue object with the given SHA1. :param sha: the issue's SHA1 identifier. """ return Issue(self, sha) def issues(self, **kwargs): """ Return issues, with options to limit, offset, sort, and filter the result set. This method is a handoff to Query.select(), here for convenience. Both methods take the same paramters. :param order_by: order the results by this column. :param status: return results with this status. :param limit: maximum number of results to return. :param offset: skip the first n-results. :param reverse: results are returned in ascending order if True, descending if False. """ query = Query(self) return query.select(**kwargs) def history(self, n=10, offset=0, all=False): """ Return a list of dictionaries containing the action and the actor. These are assembled from the Git repository's commit log. :param n: the maximum number of history items to return. :param all: ignore n and return everything. """ if all: commits = self.repo.commits(n=1000) else: commits = self.repo.commits(n=n) if len(commits) > offset: return commits[offset:] else: return [] def get_issue_path(self, sha): """ Returns the absolute path to the issue. It doesn't check if the issue exists; this should be done afterwards if necessary. :param sha: the issue's unique identifier. """ return os.path.join(self.paths['issues'], sha, 'issue') def query(self): """ Returns a hopper.query.Query object for querying the issue database. """ return Query(self) def _get_issues(self): """Returns a list of all issue objects.""" return [Issue(self, sha) for sha in self._get_issue_shas()] def _get_issue_shas(self): """Return a list of the SHA1s of all issues in the tracker.""" # we'll just return any paths in tracker/issues/ with 40 chars. # since we're not verifying, this may not be 100% accurate. return filter(lambda x: len(x) == 40, os.listdir(self.paths['issues']))