From 20afc26eebdc3425a159c3820f275e8d8046348c Mon Sep 17 00:00:00 2001 From: Alex Couper Date: Mon, 13 May 2013 14:05:38 +0100 Subject: [PATCH 1/9] Add test. Some cleanup still left of the model. --- deploystream/apps/feature/models.py | 103 +++++++++++++++++++ tests/__init__.py | 4 +- tests/integration/test_github.py | 2 +- tests/test_feature/test_hierarchy_model.py | 57 ++++++++++ tests/test_providers/test_github_provider.py | 3 +- 5 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 tests/test_feature/test_hierarchy_model.py diff --git a/deploystream/apps/feature/models.py b/deploystream/apps/feature/models.py index 284f543..b6897e8 100644 --- a/deploystream/apps/feature/models.py +++ b/deploystream/apps/feature/models.py @@ -1,3 +1,6 @@ +from collections import defaultdict + + class Feature(object): """ The class used for encapsulating ``Feature`` data across repos & branches. @@ -115,3 +118,103 @@ def __init__(self, timestamp, result, commit, url, provider): self.commit = commit self.url = url self._provider = provider + + +class HierarchyNode(object): + + def __init__(self, level, repo, branch=None): + self.children = [] + self._parent = None + + self.level = level + self.repo = repo + self.branch = branch + + def add_node(self, level, **kwargs): + print "add_node", level, kwargs, self.level + parent_level = level - 1 + if self.level == parent_level: + # I am an appropriate parent + print "adding {0} (level: {1}) to myself: ({2}, {3})".format(kwargs['branch'], level, self.branch, self.level) + return self._add(level=level, **kwargs) + elif self.level < parent_level: + # There may be someone on a lower level more appropriate + if self.children and self.children[0].level == parent_level: + print "passing to child" + self.children[0].add_node(level, **kwargs) + else: + print "adding {0} (level: {1}) to myself: ({2}, {3})".format(kwargs['branch'], level, self.branch, self.level) + # I'm the best hope for the kid. + return self._add(level=level, **kwargs) + else: + print "passing to parent" + # I've got no right to father someone higher than me. + return self.parent.add_node(level, **kwargs) + + def _add(self, **kwargs): + print "ADDING as child of {0}".format(self.level) + new_node = HierarchyNode(**kwargs) + new_node.parent = self + for child in self.children: + # TODO: this actually needs to do more than this to move the item + # further down the chain + if child.level > new_node.level: + child.parent = new_node + print "children are now:", self.children, self.children[0].branch + return new_node + + @property + def parent(self): + return self._parent + + @parent.setter + def parent(self, node): + if self._parent: + # remove first + self._parent.remove_child(self) + self._parent = node + node.ensure_child(self) + + def ensure_child(self, node): + if node not in self.children: + self.children.append(node) + + def remove_child(self, node): + try: + self.children.remove(node) + except ValueError: + pass + + def as_tree_string(self, indent=0): + me = "{0}- {1}\n".format(indent * ' ', self.branch) + if not self.branch: + indent = -4 + me = "" + + for c in self.children: + me += c.as_tree_string(indent + 4) + return me + + +# class HierarchyTree(object): + +# def __init__(self, root_picker): +# """ +# A hierarchy tree to link nodes to one another + +# :param root_picker: +# An attribute to be expected on each node that decides which sub +# tree to assign a node to. Eg "repository" +# """ +# self.root_picker = root_picker +# self.roots = defaultdict(lambda: HierarchyNode(-1)) + +# def add_node(self, **kwargs): +# return self.roots[kwargs[self.root_picker]].add_node(**kwargs) + +# def as_tree_string(self): +# u = u'' +# for root_name, root in self.roots.items(): +# u += "Root: {0}\n".format(root_name) +# u += root.as_tree_string(0) +# return u diff --git a/tests/__init__.py b/tests/__init__.py index 4deb8f3..3f4f0c9 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -12,7 +12,7 @@ def load_fixture(filename): DEFAULT_HIERARCHY_REGEXES = [ 'master', 'develop', - 'story/{FEATURE_ID}(/[a-z]*)?', - 'dev/{FEATURE_ID}/[a-z]*', + 'story/{FEATURE_ID}', + '(dev|story)/{FEATURE_ID}/[a-z]*', '[a-zA-Z]*/{FEATURE_ID}/[a-zA-Z]*' ] diff --git a/tests/integration/test_github.py b/tests/integration/test_github.py index a86fe29..fb399d6 100644 --- a/tests/integration/test_github.py +++ b/tests/integration/test_github.py @@ -29,4 +29,4 @@ def test_get_repo_branches_involved(): "repo_name": "dummyrepo", "branch_name": "story/101/fred", "latest_commit": "0f6eefefc14f362a2c6f804df69aa83bac48c20b", - "level": 2} in branches) + "level": 3} in branches) diff --git a/tests/test_feature/test_hierarchy_model.py b/tests/test_feature/test_hierarchy_model.py new file mode 100644 index 0000000..d6ba3c3 --- /dev/null +++ b/tests/test_feature/test_hierarchy_model.py @@ -0,0 +1,57 @@ +from nose.tools import assert_equals + +from deploystream.apps.feature.models import HierarchyNode + + +def test_hierarchy_tree_behaviour(): + root_node = HierarchyNode(level=-1, repo='r1') + + node_s1 = root_node.add_node(level=2, repo='r1', branch="story/1/alex") + assert_equals(root_node.children, [node_s1]) + + # Higher level nodes slot in and are considered parents of lower level + # branches even if the levels are not sequential. + node_master = node_s1.add_node(level=0, repo='r1', branch='master') + + assert_equals(node_master.children, [node_s1]) + assert_equals(node_s1.parent, node_master) + + # When a missing level is added, children and parents of the branches below + # and above are corrected accordingly. + node_devel = node_s1.add_node(level=1, repo='r1', branch="develop") + + assert_equals(node_devel.children, [node_s1]) + assert_equals(node_s1.parent, node_devel) + assert_equals(node_master.children, [node_devel]) + assert_equals(node_devel.parent, node_master) + + node_s1b = node_devel.add_node(level=2, repo='r1', branch='story/1') + + assert_equals(node_devel.children, [node_s1, node_s1b]) + assert_equals(node_s1.parent, node_devel) + assert_equals(node_s1b.parent, node_devel) + assert_equals(node_master.children, [node_devel]) + assert_equals(node_devel.parent, node_master) + + # A level can have more than one child. + node_d1 = node_s1b.add_node(level=3, repo='r1', branch='dev/1/fred') + + assert_equals(node_d1.parent, node_s1b) + assert_equals(node_s1b.children, [node_d1]) + assert_equals(node_s1.children, []) + + node_d2 = node_s1b.add_node(level=3, repo='r1', branch='dev/1/alex') + + assert_equals(node_d2.parent, node_s1b) + assert_equals(node_s1b.children, [node_d1, node_d2]) + + # Check the tree looks correct + print root_node.as_tree_string() + assert_equals(root_node.as_tree_string(), + "- master\n" + " - develop\n" + " - story/1/alex\n" + " - story/1\n" + " - dev/1/fred\n" + " - dev/1/alex\n" + ) diff --git a/tests/test_providers/test_github_provider.py b/tests/test_providers/test_github_provider.py index f337802..ea2069c 100644 --- a/tests/test_providers/test_github_provider.py +++ b/tests/test_providers/test_github_provider.py @@ -84,6 +84,7 @@ def test_get_repo_branches_involved(github3): branches = github_provider.get_repo_branches_involved(5, hierarchy_regexes=DEFAULT_HIERARCHY_REGEXES) assert_equal(2, len(branches)) + print branches assert_true({ "repo_name": "repo_1", "branch_name": "master", @@ -93,4 +94,4 @@ def test_get_repo_branches_involved(github3): "repo_name": "repo_1", "branch_name": "story/5/alex", "latest_commit": "CoMmItHaSh-5", - "level": 2} in branches) + "level": 3} in branches) From 641ca38c641464f316e2c144b9090223adf3e109 Mon Sep 17 00:00:00 2001 From: Alex Couper Date: Thu, 16 May 2013 14:03:41 +0000 Subject: [PATCH 2/9] Go for simple approach. --- deploystream/apps/feature/models.py | 36 +++------ tests/test_feature/test_hierarchy_model.py | 89 ++++++++++++---------- 2 files changed, 58 insertions(+), 67 deletions(-) diff --git a/deploystream/apps/feature/models.py b/deploystream/apps/feature/models.py index b6897e8..fdeacac 100644 --- a/deploystream/apps/feature/models.py +++ b/deploystream/apps/feature/models.py @@ -133,17 +133,21 @@ def __init__(self, level, repo, branch=None): def add_node(self, level, **kwargs): print "add_node", level, kwargs, self.level parent_level = level - 1 + if self.level == parent_level: # I am an appropriate parent - print "adding {0} (level: {1}) to myself: ({2}, {3})".format(kwargs['branch'], level, self.branch, self.level) + print "A: adding {0} (level: {1}) to myself: ({2}, {3})".format( + kwargs['branch'], level, self.branch, self.level) return self._add(level=level, **kwargs) elif self.level < parent_level: # There may be someone on a lower level more appropriate - if self.children and self.children[0].level == parent_level: + if self.children and self.children[0].level <= parent_level: print "passing to child" self.children[0].add_node(level, **kwargs) else: - print "adding {0} (level: {1}) to myself: ({2}, {3})".format(kwargs['branch'], level, self.branch, self.level) + print ("B: adding {0} (level: {1}) to myself: " + "({2}, {3})".format( + kwargs['branch'], level, self.branch, self.level)) # I'm the best hope for the kid. return self._add(level=level, **kwargs) else: @@ -196,25 +200,7 @@ def as_tree_string(self, indent=0): return me -# class HierarchyTree(object): - -# def __init__(self, root_picker): -# """ -# A hierarchy tree to link nodes to one another - -# :param root_picker: -# An attribute to be expected on each node that decides which sub -# tree to assign a node to. Eg "repository" -# """ -# self.root_picker = root_picker -# self.roots = defaultdict(lambda: HierarchyNode(-1)) - -# def add_node(self, **kwargs): -# return self.roots[kwargs[self.root_picker]].add_node(**kwargs) - -# def as_tree_string(self): -# u = u'' -# for root_name, root in self.roots.items(): -# u += "Root: {0}\n".format(root_name) -# u += root.as_tree_string(0) -# return u +class RootHierarchyNode(HierarchyNode): + def __init__(self, repo): + super(RootHierarchyNode, self).__init__( + level=-1, repo=repo, branch=None) diff --git a/tests/test_feature/test_hierarchy_model.py b/tests/test_feature/test_hierarchy_model.py index d6ba3c3..2b54698 100644 --- a/tests/test_feature/test_hierarchy_model.py +++ b/tests/test_feature/test_hierarchy_model.py @@ -1,57 +1,62 @@ from nose.tools import assert_equals -from deploystream.apps.feature.models import HierarchyNode +from deploystream.apps.feature.models import HierarchyNode, RootHierarchyNode -def test_hierarchy_tree_behaviour(): - root_node = HierarchyNode(level=-1, repo='r1') +def test_hierarchy_tree_simple_case(): + repo = 'r1' + root_node = RootHierarchyNode(repo=repo) + for branch, level in [ + ('master', 0), + ('develop', 1), + ('story/1', 2), + ('dev/1/alex', 3), + ]: + root_node.add_node(level=level, repo=repo, branch=branch) - node_s1 = root_node.add_node(level=2, repo='r1', branch="story/1/alex") - assert_equals(root_node.children, [node_s1]) - - # Higher level nodes slot in and are considered parents of lower level - # branches even if the levels are not sequential. - node_master = node_s1.add_node(level=0, repo='r1', branch='master') - - assert_equals(node_master.children, [node_s1]) - assert_equals(node_s1.parent, node_master) - - # When a missing level is added, children and parents of the branches below - # and above are corrected accordingly. - node_devel = node_s1.add_node(level=1, repo='r1', branch="develop") - - assert_equals(node_devel.children, [node_s1]) - assert_equals(node_s1.parent, node_devel) - assert_equals(node_master.children, [node_devel]) - assert_equals(node_devel.parent, node_master) - - node_s1b = node_devel.add_node(level=2, repo='r1', branch='story/1') - - assert_equals(node_devel.children, [node_s1, node_s1b]) - assert_equals(node_s1.parent, node_devel) - assert_equals(node_s1b.parent, node_devel) - assert_equals(node_master.children, [node_devel]) - assert_equals(node_devel.parent, node_master) - - # A level can have more than one child. - node_d1 = node_s1b.add_node(level=3, repo='r1', branch='dev/1/fred') + assert_equals(root_node.as_tree_string(), + "- master\n" + " - develop\n" + " - story/1\n" + " - dev/1/alex\n" + ) - assert_equals(node_d1.parent, node_s1b) - assert_equals(node_s1b.children, [node_d1]) - assert_equals(node_s1.children, []) - node_d2 = node_s1b.add_node(level=3, repo='r1', branch='dev/1/alex') +def test_hierarchy_tree_multiple_parent_options(): + """Test node assignment when there are multiple parents available. - assert_equals(node_d2.parent, node_s1b) - assert_equals(node_s1b.children, [node_d1, node_d2]) + If there is more than one parental option available, the first node at + the appropriate level should be selected as parent. + """ + repo = 'r1' + root_node = RootHierarchyNode(repo=repo) + for branch, level in [ + ('master', 0), + ('develop', 1), + ('story/1', 2), + ('story/1/alex', 2), + ('dev/1/alex', 3), + ]: + root_node.add_node(level=level, repo=repo, branch=branch) - # Check the tree looks correct - print root_node.as_tree_string() assert_equals(root_node.as_tree_string(), "- master\n" " - develop\n" - " - story/1/alex\n" " - story/1\n" - " - dev/1/fred\n" " - dev/1/alex\n" + " - story/1/alex\n" ) + + +def test_hierarchy_tree_nodes_find_correct_position(): + root_node = RootHierarchyNode(repo='r1') + + node_s1 = root_node.add_node(level=2, repo='r1', branch="story/1/alex") + assert_equals(root_node.children, [node_s1]) + + # Higher level nodes slot in and are considered parents of lower level + # branches even if the levels are not sequential. + node_master = node_s1.add_node(level=0, repo='r1', branch='master') + + assert_equals(node_master.children, [node_s1]) + assert_equals(node_s1.parent, node_master) From 09f7ee385ebfb8e0835a0752dfbc0f094add6d9d Mon Sep 17 00:00:00 2001 From: Alex Couper Date: Tue, 21 May 2013 18:29:45 +0100 Subject: [PATCH 3/9] Start on new hierarchy implementation. --- deploystream/lib/hierarchy.py | 42 ++++++++++++++++++++ deploystream/providers/github/__init__.py | 32 +++++++++++++++ tests/test_lib/test_hierarchy.py | 22 +++++++++- tests/test_providers/test_github_provider.py | 24 +++++++++++ 4 files changed, 119 insertions(+), 1 deletion(-) diff --git a/deploystream/lib/hierarchy.py b/deploystream/lib/hierarchy.py index 611a5ce..189966d 100644 --- a/deploystream/lib/hierarchy.py +++ b/deploystream/lib/hierarchy.py @@ -1,3 +1,4 @@ +from collections import defaultdict import re @@ -50,3 +51,44 @@ def match_with_levels(feature_id, branch, hierarchical_regexes): if match: index = int(level.split('level_')[1]) return index + + +def match_with_geneology(feature_id, branches, hierarchical_regexes): + matched_branches = defaultdict(list) + hierarchy = [] + for index, regex in enumerate(hierarchical_regexes): + print "- REGEX:", regex, "INDEX:", index + try: + parent_regex = hierarchical_regexes[max(index - 1, 0)] + except IndexError: + parent_regex = None + + possible_parents = matched_branches[index - 1] + fake_parent = False + if not possible_parents: + fake_parent = True + possible_parents = [parent_regex] + + for branch in branches[:]: + print " - BRANCH:", branch + for parent in possible_parents: + print " - PARENT?:", parent + + full_regex = regex.format(FEATURE_ID=feature_id, + PARENT=parent) + print " - REGEX:", full_regex, "BRANCH:", branch + result = re.match(full_regex, branch) + if result: + print " ---MATCHED---" + matched_branches[index].append(branch) + branches.remove(branch) + if fake_parent: + parent = None + hierarchy.append((branch, parent)) + break + else: + print " ---FAILED---" + + print "hierarchy", hierarchy + return hierarchy + diff --git a/deploystream/providers/github/__init__.py b/deploystream/providers/github/__init__.py index 3396e42..a5e8c36 100644 --- a/deploystream/providers/github/__init__.py +++ b/deploystream/providers/github/__init__.py @@ -122,3 +122,35 @@ def get_repo_branches_involved(self, feature_id, hierarchy_regexes): }) return branch_list + + def get_branch_hierarchy(self, feature_id, hierarchy_regexes): + """ + Get the list of branches involved for the given ``feature_id``. + + :returns: + A list of dictionaries containing keys for: + - repo_name + - branch_name + - parent_branch_name + - in_parent + - has_parent + - latest_commit + """ + branch_list = [] + + for repo in self.repositories: + repo_branches = {} + for branch in repo.iter_branches(): + repo_branches[branch.name] = {'sha': branch.commit.sha} + geneology = hierarchy.match_with_geneology( + feature_id, repo_branches, hierarchy_regexes) + for branch, parent in geneology: + + branch_list.append({ + "repo_name": repo.name, + "branch_name": branch, + "latest_commit": repo_branches[branch]['sha'], + "parent_branch_name": parent, + "in_parent": None, + "has_parent": None, + }) diff --git a/tests/test_lib/test_hierarchy.py b/tests/test_lib/test_hierarchy.py index cacd50f..affe3bc 100644 --- a/tests/test_lib/test_hierarchy.py +++ b/tests/test_lib/test_hierarchy.py @@ -1,6 +1,6 @@ from nose.tools import assert_equals -from deploystream.lib.hierarchy import match_with_levels +from deploystream.lib.hierarchy import match_with_levels, match_with_geneology from tests import DEFAULT_HIERARCHY_REGEXES @@ -22,3 +22,23 @@ def test_match_with_levels(): for branch, expected in branches_results: result = match_with_levels('23', branch, DEFAULT_HIERARCHY_REGEXES) assert_equals(result, expected) + + +def test_geneology(): + regexes = ['^master$', '^develop$', '^story/{FEATURE_ID}$', + '^story/{FEATURE_ID}/[a-z]*$', + '^{PARENT}/[a-z]*$'] + + branches = ['master', 'develop', 'story/12', 'story/12/alex', + 'story/12/bill', + 'story/12/bill/something'] + + expected = [('master', None), + ('develop', 'master'), + ('story/12', 'develop'), + ('story/12/alex', 'story/12'), + ('story/12/bill', 'story/12'), + ('story/12/bill/something', 'story/12/bill')] + + results = match_with_geneology(12, branches, regexes) + assert_equals(results, expected) diff --git a/tests/test_providers/test_github_provider.py b/tests/test_providers/test_github_provider.py index ea2069c..9e8e74f 100644 --- a/tests/test_providers/test_github_provider.py +++ b/tests/test_providers/test_github_provider.py @@ -95,3 +95,27 @@ def test_get_repo_branches_involved(github3): "branch_name": "story/5/alex", "latest_commit": "CoMmItHaSh-5", "level": 3} in branches) + + +def test_get_branch_hierarchy(): + github_provider = GithubProvider('token') + branches = github_provider.get_branch_hierarchy("5", + DEFAULT_HIERARCHY_REGEXES) + assert_equal(2, len(branches)) + assert_true({ + "repo_name": "repo_1", + "branch_name": "master", + "parent_branch_name": None, + "in_parent": False, + "has_parent": False, + "latest_commit": 'CoMmItHaSh-MaStEr', + "level": 0} in branches) + assert_true({ + "repo_name": "repo_1", + "branch_name": "story/5/alex", + "parent_branch_name": "master", + "in_parent": False, + "has_parent": True, + "latest_commit": "CoMmItHaSh-5", + "level": 3} in branches) + From 7fce2c2be5ca4a5bb96d07da83c3f476ddbe1297 Mon Sep 17 00:00:00 2001 From: Alex Couper Date: Fri, 24 May 2013 13:11:45 +0100 Subject: [PATCH 4/9] Do all matching of hierarchy in the provider. --- deploystream/apps/feature/lib.py | 4 - deploystream/apps/feature/models.py | 161 ++++++------------- deploystream/lib/hierarchy.py | 75 +++------ deploystream/providers/github/__init__.py | 40 ++--- tests/__init__.py | 4 +- tests/integration/test_github.py | 4 +- tests/test_feature/test_hierarchy_model.py | 62 ------- tests/test_feature/test_models.py | 19 +++ tests/test_lib/test_hierarchy.py | 69 ++++---- tests/test_providers/test_github_provider.py | 31 +--- 10 files changed, 158 insertions(+), 311 deletions(-) delete mode 100644 tests/test_feature/test_hierarchy_model.py create mode 100644 tests/test_feature/test_models.py diff --git a/deploystream/apps/feature/lib.py b/deploystream/apps/feature/lib.py index 4a05916..408b69d 100644 --- a/deploystream/apps/feature/lib.py +++ b/deploystream/apps/feature/lib.py @@ -55,10 +55,6 @@ def get_feature_info(feature_provider, feature_id, providers): feature_id, app.config['HIERARCHY_REGEXES']): feature.add_branch(Branch(*branch_data, provider=provider)) - # Use that branch info, along with configuration regexes to create a - # hierarchy of the branches involved in the feature. - feature.create_hierarchy_trees() - # Ask source control providers for merging information at this point. for provider in providers[ISourceCodeControlProvider]: for tree in feature.trees: diff --git a/deploystream/apps/feature/models.py b/deploystream/apps/feature/models.py index fdeacac..a17b7e3 100644 --- a/deploystream/apps/feature/models.py +++ b/deploystream/apps/feature/models.py @@ -41,16 +41,19 @@ def __init__(self, provider, project, id, title, self.url = url self._extras = kwargs - self.branches = [] + self.branches = defaultdict(dict) self.trees = [] - def create_hierarchy_trees(self): - "Create hierarchy trees - one for each repo." - pass - def add_branch(self, branch): assert isinstance(branch, Branch) - self.branches.append(branch) + self.branches[branch.repo_name][branch.branch_name] = branch + + def create_hierarchy_trees(self): + "Create hierarchy trees - one for each repo." + for branch_set in self.branches.values(): + for branch in branch_set.values(): + if branch.parent_branch_name: + branch.parent = branch_set[branch.parent_branch_name] class Branch(object): @@ -59,15 +62,13 @@ class Branch(object): Instances contain values for: - ``repo_name`` - The repository that this branch is found in. - ``branch_name`` - The name of the branch. - ``latest_commit`` - The head commmit, or latest revision in this - branch. - ``level`` - The numerical level that this branch falls in the - hierarchy for the feature - where 0 is the highest - level. - ``provider`` - The provider instance that found this branch - information. + ``repo_name`` - The repository that this branch is found in. + ``branch_name`` - The name of the branch. + ``latest_commit`` - The head commmit, or latest revision in this + branch. + ``parent_branch_name`` - The name of the branches parent. + ``provider`` - The provider instance that found this branch + information. Instances are eventually populated with these values: @@ -77,23 +78,51 @@ class Branch(object): ``parent`` - The parent ``Branch`` of this branch (based on hierarchy rules) ``children`` - A list of children ``Branch`` es of this branch. - ``siblings`` - A list of the sibling ``Branch`` es of this branch. - A sibling is a ``Branch`` that has the same parent, - or would have the same parent if one existed. + """ - def __init__(self, repo_name, branch_name, latest_commit, level, provider): - self.parent = None + def __init__(self, repo_name, branch_name, latest_commit, + parent_branch_name, provider): + self._parent = None self.children = [] - self.siblings = [] # Will be needed in the cases where we have no - # parent self.build_info = None self.repo_name = repo_name self.branch_name = branch_name self.latest_commit = latest_commit - self.level = level + self.parent_branch_name = parent_branch_name self._provider = provider + @property + def parent(self): + return self._parent + + @parent.setter + def parent(self, branch): + if self._parent: + # remove first + self._parent.remove_child(self) + self._parent = branch + branch.ensure_child(self) + + def ensure_child(self, branch): + if branch not in self.children: + self.children.append(branch) + + def remove_child(self, branch): + try: + self.children.remove(branch) + except ValueError: + pass + + def as_tree_string(self, indent=0): + if self.parent and not indent: + return self.parent.as_tree_string() + me = "{0}- {1}\n".format(indent * ' ', self.branch_name) + + for c in self.children: + me += c.as_tree_string(indent + 4) + return me + class BuildInfo(object): """ @@ -118,89 +147,3 @@ def __init__(self, timestamp, result, commit, url, provider): self.commit = commit self.url = url self._provider = provider - - -class HierarchyNode(object): - - def __init__(self, level, repo, branch=None): - self.children = [] - self._parent = None - - self.level = level - self.repo = repo - self.branch = branch - - def add_node(self, level, **kwargs): - print "add_node", level, kwargs, self.level - parent_level = level - 1 - - if self.level == parent_level: - # I am an appropriate parent - print "A: adding {0} (level: {1}) to myself: ({2}, {3})".format( - kwargs['branch'], level, self.branch, self.level) - return self._add(level=level, **kwargs) - elif self.level < parent_level: - # There may be someone on a lower level more appropriate - if self.children and self.children[0].level <= parent_level: - print "passing to child" - self.children[0].add_node(level, **kwargs) - else: - print ("B: adding {0} (level: {1}) to myself: " - "({2}, {3})".format( - kwargs['branch'], level, self.branch, self.level)) - # I'm the best hope for the kid. - return self._add(level=level, **kwargs) - else: - print "passing to parent" - # I've got no right to father someone higher than me. - return self.parent.add_node(level, **kwargs) - - def _add(self, **kwargs): - print "ADDING as child of {0}".format(self.level) - new_node = HierarchyNode(**kwargs) - new_node.parent = self - for child in self.children: - # TODO: this actually needs to do more than this to move the item - # further down the chain - if child.level > new_node.level: - child.parent = new_node - print "children are now:", self.children, self.children[0].branch - return new_node - - @property - def parent(self): - return self._parent - - @parent.setter - def parent(self, node): - if self._parent: - # remove first - self._parent.remove_child(self) - self._parent = node - node.ensure_child(self) - - def ensure_child(self, node): - if node not in self.children: - self.children.append(node) - - def remove_child(self, node): - try: - self.children.remove(node) - except ValueError: - pass - - def as_tree_string(self, indent=0): - me = "{0}- {1}\n".format(indent * ' ', self.branch) - if not self.branch: - indent = -4 - me = "" - - for c in self.children: - me += c.as_tree_string(indent + 4) - return me - - -class RootHierarchyNode(HierarchyNode): - def __init__(self, repo): - super(RootHierarchyNode, self).__init__( - level=-1, repo=repo, branch=None) diff --git a/deploystream/lib/hierarchy.py b/deploystream/lib/hierarchy.py index 189966d..ba8606d 100644 --- a/deploystream/lib/hierarchy.py +++ b/deploystream/lib/hierarchy.py @@ -2,31 +2,9 @@ import re -def create_single_regex(feature_id, hierarchical_regexes): - """ - Create a single regex to be used to find which level a branch is on. - - :param feature_id: - The id of the feature. This is substituted into the - ``hierarchical_regexes`` if they use {FEATURE_ID} anywhere. - - :param hierarchical_regexes: - A list of regexes to be joined into one single regex. - - :returns: - A single regex for easier matching. - """ - subs = [] - for index, regex in enumerate(hierarchical_regexes): - subs.append("(?P^{1}$)".format(index, regex)) - full_regex = "|".join(subs) - full_regex = full_regex.format(FEATURE_ID=feature_id) - return full_regex - - -def match_with_levels(feature_id, branch, hierarchical_regexes): +def match_with_geneology(feature_id, branches, hierarchical_regexes): """ - Filter and return the branches in appropriate levels. + Filter and return the branches in order with parents attached. :param feature_id: The feature to filter the branch names on. @@ -38,57 +16,44 @@ def match_with_levels(feature_id, branch, hierarchical_regexes): A list of regexes assumed to be in descending order of branch status. :returns: - The positional index that the branch should be found in. Or None if it - does not match. + A list of tuples containing: + - Branch name + - Parent name """ - regex = create_single_regex(feature_id, hierarchical_regexes) - - result = re.match(regex, branch) - if not result: - return None - - for level, match in result.groupdict().items(): - if match: - index = int(level.split('level_')[1]) - return index - - -def match_with_geneology(feature_id, branches, hierarchical_regexes): matched_branches = defaultdict(list) hierarchy = [] for index, regex in enumerate(hierarchical_regexes): - print "- REGEX:", regex, "INDEX:", index try: parent_regex = hierarchical_regexes[max(index - 1, 0)] except IndexError: parent_regex = None - possible_parents = matched_branches[index - 1] + # Find the possible parents for any branches found at this level. + # Simply look at the level above, and if not there then keep going back + parent_index = index - 1 fake_parent = False - if not possible_parents: - fake_parent = True - possible_parents = [parent_regex] - + possible_parents = matched_branches[parent_index] + while not possible_parents: + parent_index -= 1 + if parent_index < 0: + fake_parent = True + possible_parents = [parent_regex] + break + possible_parents = matched_branches[parent_index] + + # Look through all the branches (that are left to look at) and see + # if any match this regex. for branch in branches[:]: - print " - BRANCH:", branch for parent in possible_parents: - print " - PARENT?:", parent - full_regex = regex.format(FEATURE_ID=feature_id, PARENT=parent) - print " - REGEX:", full_regex, "BRANCH:", branch - result = re.match(full_regex, branch) + result = re.match("^{0}$".format(full_regex), branch) if result: - print " ---MATCHED---" matched_branches[index].append(branch) branches.remove(branch) if fake_parent: parent = None hierarchy.append((branch, parent)) break - else: - print " ---FAILED---" - print "hierarchy", hierarchy return hierarchy - diff --git a/deploystream/providers/github/__init__.py b/deploystream/providers/github/__init__.py index a5e8c36..5139fbd 100644 --- a/deploystream/providers/github/__init__.py +++ b/deploystream/providers/github/__init__.py @@ -106,24 +106,6 @@ def get_oauth_data(self): } def get_repo_branches_involved(self, feature_id, hierarchy_regexes): - branch_list = [] - - for repo in self.repositories: - for branch in repo.iter_branches(): - level = hierarchy.match_with_levels( - feature_id, branch.name, hierarchy_regexes) - if level is None: - continue - branch_list.append({ - "repo_name": repo.name, - "branch_name": branch.name, - "latest_commit": branch.commit.sha, - "level": level, - }) - - return branch_list - - def get_branch_hierarchy(self, feature_id, hierarchy_regexes): """ Get the list of branches involved for the given ``feature_id``. @@ -132,8 +114,6 @@ def get_branch_hierarchy(self, feature_id, hierarchy_regexes): - repo_name - branch_name - parent_branch_name - - in_parent - - has_parent - latest_commit """ branch_list = [] @@ -142,8 +122,10 @@ def get_branch_hierarchy(self, feature_id, hierarchy_regexes): repo_branches = {} for branch in repo.iter_branches(): repo_branches[branch.name] = {'sha': branch.commit.sha} + geneology = hierarchy.match_with_geneology( - feature_id, repo_branches, hierarchy_regexes) + feature_id, repo_branches.keys(), hierarchy_regexes) + for branch, parent in geneology: branch_list.append({ @@ -151,6 +133,18 @@ def get_branch_hierarchy(self, feature_id, hierarchy_regexes): "branch_name": branch, "latest_commit": repo_branches[branch]['sha'], "parent_branch_name": parent, - "in_parent": None, - "has_parent": None, }) + return branch_list + +# def iter_commits(repo, branch): +# from github3.repos.commit import RepoCommit +# def iter_parents(commit_sha): +# url = repo._build_url('commits', commit_sha, base_url=repo._api) +# json = repo._json(repo._get(url), 200) +# commit = RepoCommit(json) +# yield commit +# for parent in commit.parents: +# for commit in iter_parents(RepoCommit(parent).sha): +# yield commit + +# return iter_parents(branch.commit.sha) diff --git a/tests/__init__.py b/tests/__init__.py index 3f4f0c9..8788515 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -13,6 +13,6 @@ def load_fixture(filename): 'master', 'develop', 'story/{FEATURE_ID}', - '(dev|story)/{FEATURE_ID}/[a-z]*', - '[a-zA-Z]*/{FEATURE_ID}/[a-zA-Z]*' + 'story/{FEATURE_ID}/[a-z]*', + '{PARENT}/[a-z]*' ] diff --git a/tests/integration/test_github.py b/tests/integration/test_github.py index fb399d6..a11e772 100644 --- a/tests/integration/test_github.py +++ b/tests/integration/test_github.py @@ -24,9 +24,9 @@ def test_get_repo_branches_involved(): "repo_name": "dummyrepo", "branch_name": "master", "latest_commit": '0f6eefefc14f362a2c6f804df69aa83bac48c20b', - "level": 0} in branches) + "parent_branch_name": None} in branches) assert_true({ "repo_name": "dummyrepo", "branch_name": "story/101/fred", "latest_commit": "0f6eefefc14f362a2c6f804df69aa83bac48c20b", - "level": 3} in branches) + "parent_branch_name": "master"} in branches) diff --git a/tests/test_feature/test_hierarchy_model.py b/tests/test_feature/test_hierarchy_model.py deleted file mode 100644 index 2b54698..0000000 --- a/tests/test_feature/test_hierarchy_model.py +++ /dev/null @@ -1,62 +0,0 @@ -from nose.tools import assert_equals - -from deploystream.apps.feature.models import HierarchyNode, RootHierarchyNode - - -def test_hierarchy_tree_simple_case(): - repo = 'r1' - root_node = RootHierarchyNode(repo=repo) - for branch, level in [ - ('master', 0), - ('develop', 1), - ('story/1', 2), - ('dev/1/alex', 3), - ]: - root_node.add_node(level=level, repo=repo, branch=branch) - - assert_equals(root_node.as_tree_string(), - "- master\n" - " - develop\n" - " - story/1\n" - " - dev/1/alex\n" - ) - - -def test_hierarchy_tree_multiple_parent_options(): - """Test node assignment when there are multiple parents available. - - If there is more than one parental option available, the first node at - the appropriate level should be selected as parent. - """ - repo = 'r1' - root_node = RootHierarchyNode(repo=repo) - for branch, level in [ - ('master', 0), - ('develop', 1), - ('story/1', 2), - ('story/1/alex', 2), - ('dev/1/alex', 3), - ]: - root_node.add_node(level=level, repo=repo, branch=branch) - - assert_equals(root_node.as_tree_string(), - "- master\n" - " - develop\n" - " - story/1\n" - " - dev/1/alex\n" - " - story/1/alex\n" - ) - - -def test_hierarchy_tree_nodes_find_correct_position(): - root_node = RootHierarchyNode(repo='r1') - - node_s1 = root_node.add_node(level=2, repo='r1', branch="story/1/alex") - assert_equals(root_node.children, [node_s1]) - - # Higher level nodes slot in and are considered parents of lower level - # branches even if the levels are not sequential. - node_master = node_s1.add_node(level=0, repo='r1', branch='master') - - assert_equals(node_master.children, [node_s1]) - assert_equals(node_s1.parent, node_master) diff --git a/tests/test_feature/test_models.py b/tests/test_feature/test_models.py new file mode 100644 index 0000000..3b2a273 --- /dev/null +++ b/tests/test_feature/test_models.py @@ -0,0 +1,19 @@ +from nose.tools import assert_equals + +from deploystream.apps.feature.models import Feature, Branch + + +def test_feature_branches(): + f = Feature(provider=None, project=None, id=None, title=None) + for branch_name, parent_name in [('alex', 'master'), ('master', None), + ('something', 'alex'), ('sthg2', 'alex')]: + b = Branch("repo1", branch_name, "commit", parent_name, "test") + f.add_branch(b) + + f.create_hierarchy_trees() + assert_equals(b.as_tree_string(), + "- master\n" + " - alex\n" + " - something\n" + " - sthg2\n" + ) diff --git a/tests/test_lib/test_hierarchy.py b/tests/test_lib/test_hierarchy.py index affe3bc..bf55265 100644 --- a/tests/test_lib/test_hierarchy.py +++ b/tests/test_lib/test_hierarchy.py @@ -1,37 +1,36 @@ -from nose.tools import assert_equals +from nose.tools import assert_items_equal -from deploystream.lib.hierarchy import match_with_levels, match_with_geneology -from tests import DEFAULT_HIERARCHY_REGEXES +from deploystream.lib.hierarchy import match_with_geneology -def test_match_with_levels(): - "Test that we get back what we'd expect when matching branches" - branches_results = [ - ('master', 0), - ('develop', 1), - ('story/23', 2), - ('dev/23/alex', 3), - ('dev/23/carles', 3), +def test_geneology(): + regexes = ['master', 'develop', 'story/{FEATURE_ID}', + 'story/{FEATURE_ID}/[a-z]*', + '{PARENT}/[a-z]*'] - ('somestory/234/carles', None), - ('story/234/carles', None), - ('story/45/alex', None), - ('dev/99/carles', None), - ] + branches = ['master', 'develop', 'story/12', 'story/12/alex', + 'story/12/bill', + 'story/12/bill/something', + 'story/223/bill'] - for branch, expected in branches_results: - result = match_with_levels('23', branch, DEFAULT_HIERARCHY_REGEXES) - assert_equals(result, expected) + expected = [('master', None), + ('develop', 'master'), + ('story/12', 'develop'), + ('story/12/alex', 'story/12'), + ('story/12/bill', 'story/12'), + ('story/12/bill/something', 'story/12/bill')] + results = match_with_geneology(12, branches, regexes) + assert_items_equal(results, expected) -def test_geneology(): - regexes = ['^master$', '^develop$', '^story/{FEATURE_ID}$', - '^story/{FEATURE_ID}/[a-z]*$', - '^{PARENT}/[a-z]*$'] - branches = ['master', 'develop', 'story/12', 'story/12/alex', - 'story/12/bill', - 'story/12/bill/something'] +def test_geneology_unordered_list(): + regexes = ['master', 'develop', 'story/{FEATURE_ID}', + 'story/{FEATURE_ID}/[a-z]*', + '{PARENT}/[a-z]*'] + + branches = ['story/12/bill', 'master', 'story/12/bill/something', + 'story/12', 'story/12/alex', 'develop'] expected = [('master', None), ('develop', 'master'), @@ -41,4 +40,20 @@ def test_geneology(): ('story/12/bill/something', 'story/12/bill')] results = match_with_geneology(12, branches, regexes) - assert_equals(results, expected) + results.sort() + assert_items_equal(results, expected) + + +def test_geneology_missing_steps(): + regexes = ['master', 'develop', 'story/{FEATURE_ID}', + 'story/{FEATURE_ID}/[a-z]*', + '{PARENT}/[a-z]*'] + + branches = ['master', + 'story/12/bill'] + + expected = [('master', None), + ('story/12/bill', 'master')] + + results = match_with_geneology(12, branches, regexes) + assert_items_equal(results, expected) diff --git a/tests/test_providers/test_github_provider.py b/tests/test_providers/test_github_provider.py index 9e8e74f..8500860 100644 --- a/tests/test_providers/test_github_provider.py +++ b/tests/test_providers/test_github_provider.py @@ -81,41 +81,18 @@ def test_implements_expected_interfaces(_): def test_get_repo_branches_involved(github3): mock_github3(github3) github_provider = GithubProvider('token') - branches = github_provider.get_repo_branches_involved(5, - hierarchy_regexes=DEFAULT_HIERARCHY_REGEXES) - assert_equal(2, len(branches)) - print branches - assert_true({ - "repo_name": "repo_1", - "branch_name": "master", - "latest_commit": 'CoMmItHaSh-MaStEr', - "level": 0} in branches) - assert_true({ - "repo_name": "repo_1", - "branch_name": "story/5/alex", - "latest_commit": "CoMmItHaSh-5", - "level": 3} in branches) - - -def test_get_branch_hierarchy(): - github_provider = GithubProvider('token') - branches = github_provider.get_branch_hierarchy("5", + branches = github_provider.get_repo_branches_involved("5", DEFAULT_HIERARCHY_REGEXES) assert_equal(2, len(branches)) assert_true({ "repo_name": "repo_1", "branch_name": "master", "parent_branch_name": None, - "in_parent": False, - "has_parent": False, - "latest_commit": 'CoMmItHaSh-MaStEr', - "level": 0} in branches) + "latest_commit": 'CoMmItHaSh-MaStEr' + } in branches) assert_true({ "repo_name": "repo_1", "branch_name": "story/5/alex", "parent_branch_name": "master", - "in_parent": False, - "has_parent": True, "latest_commit": "CoMmItHaSh-5", - "level": 3} in branches) - + } in branches) From fae378251cb9722fab0d2ffb46d1a18b8dddf7cc Mon Sep 17 00:00:00 2001 From: Alex Couper Date: Fri, 24 May 2013 16:01:32 +0100 Subject: [PATCH 5/9] Generate correct JSON; Fix git provider. --- config/local_settings_alex.py | 16 ++- config/settings.py | 3 +- deploystream/apps/feature/lib.py | 8 +- deploystream/lib/transforms.py | 2 +- .../providers/git_provider/__init__.py | 116 +++++------------- deploystream/providers/github/__init__.py | 8 +- .../interfaces/source_code_control.py | 18 +-- tests/test_feature/test_view.py | 37 +++++- tests/test_providers/test_git_provider.py | 43 +++---- tests/test_providers/test_interfaces.py | 2 +- 10 files changed, 114 insertions(+), 139 deletions(-) diff --git a/config/local_settings_alex.py b/config/local_settings_alex.py index cf6d360..7a2b126 100644 --- a/config/local_settings_alex.py +++ b/config/local_settings_alex.py @@ -4,12 +4,18 @@ } -GIT_CONFIG = { - 'code_dir': None, - 'feature_breakup_regex': "(?P[a-zA-Z]+)-?(?P[0-9]+)", - 'branch_finder_template': ".*(?i){project}.*" -} +# GIT_CONFIG = { +# 'feature_breakup_regex': "(?P[a-zA-Z]+)-?(?P[0-9]+)", +# 'branch_finder_template': ".*(?i){project}.*" +# } +HIERARCHY_REGEXES = [ + 'master', + 'develop', + 'story/{FEATURE_ID}', + 'story/{FEATURE_ID}/[a-z]*', + '{PARENT}/[a-z]*' +] JIRA_CONFIG = { 'url': '', diff --git a/config/settings.py b/config/settings.py index 33843ed..a7f261b 100644 --- a/config/settings.py +++ b/config/settings.py @@ -7,6 +7,7 @@ # The following is the programmatic equivalent of # from deploystream.local_settings_ import * GITHUB_CONFIG = GIT_CONFIG = SPRINTLY_CONFIG = JIRA_CONFIG = None +HIERARCHY_REGEXES = [] try: CONFIG = environ.get('CONFIG', 'sample') @@ -51,4 +52,4 @@ provider_config - a dictionary of provider name to config required. """ -HIERARCHY_REGEXES = [] + diff --git a/deploystream/apps/feature/lib.py b/deploystream/apps/feature/lib.py index 408b69d..c7b7258 100644 --- a/deploystream/apps/feature/lib.py +++ b/deploystream/apps/feature/lib.py @@ -48,12 +48,16 @@ def get_feature_info(feature_provider, feature_id, providers): feature = Feature(planning_provider, **planning_provider.get_feature_info(feature_id)) - + print providers[ISourceCodeControlProvider] # Then get any branch info from any source control providers for provider in providers[ISourceCodeControlProvider]: + print provider for branch_data in provider.get_repo_branches_involved( feature_id, app.config['HIERARCHY_REGEXES']): - feature.add_branch(Branch(*branch_data, provider=provider)) + print branch_data + feature.add_branch(Branch(provider=provider, **branch_data)) + + feature.create_hierarchy_trees() # Ask source control providers for merging information at this point. for provider in providers[ISourceCodeControlProvider]: diff --git a/deploystream/lib/transforms.py b/deploystream/lib/transforms.py index 82486f8..ba12d0d 100644 --- a/deploystream/lib/transforms.py +++ b/deploystream/lib/transforms.py @@ -17,7 +17,7 @@ def nativify(data): for k, v in data.items() if not k.startswith('_') } elif data is None: - return 'null' + return None elif hasattr(data, '__dict__'): return nativify(data.__dict__) else: diff --git a/deploystream/providers/git_provider/__init__.py b/deploystream/providers/git_provider/__init__.py index 6b322f1..493deff 100644 --- a/deploystream/providers/git_provider/__init__.py +++ b/deploystream/providers/git_provider/__init__.py @@ -4,115 +4,63 @@ import git +from deploystream.lib import hierarchy + class GitProvider(object): name = 'git' oauth_token_name = None - def __init__(self, code_dir='.', - feature_breakup_regex='', - branch_finder_template=''): + def __init__(self, code_dir): """ Create a GitProvider. :param code_dir: The filesystem path to the directory within which all repositories live that are to be queried. - - :param feature_breakup_regex: - A regular expression to be used to breakup feature ids into - understandable parts. The regex should use named groups to be - of use to the ``branch_finder_template``. - - eg. "(?P[a-zA-Z]+)-?(?P[0-9]+)" - - :param branch_finder_template: - A template regular expression with named gaps to be filled by the - outcome of breaking up the feature. - - eg. ".*{id}.*" - """ self.code_dir = code_dir - self.feature_breakup_regex = feature_breakup_regex - self.branch_finder_template = branch_finder_template - def get_repo_branches_involved(self, feature_id): + def get_repo_branches_involved(self, feature_id, hierarchy_regexes): """ Get all the repo branches involved. For each repository in each repo location defined in configuration, call ``get_branches_involved`` and return a list of tuples. - :returns: - A list of iterables containing at position: - 0: repo name - 1: branch name - 2: latest commit + :returns: + A list of dictionaries containing keys for: + - repo_name + - branch_name + - parent_branch_name + - latest_commit """ - # Every folder inside self.code_dir that is a git repo will be looked - # at - repo_branches = [] + branch_list = [] + for repo_name in os.listdir(self.code_dir): repo_location = join(self.code_dir, repo_name) if exists(join(repo_location, ".git")): - branches = self.get_branches_involved(repo_location, - feature_id) - repo_branches.extend([ - (repo_name, ) + branch for branch in branches]) - return repo_branches - - def _get_feature_breakdown(self, feature_id): - """ - Break up the feature_id using the regex in configuration. - """ - match = re.search(self.feature_breakup_regex, feature_id) - if match: - return match.groupdict() + repo_branches = {} - def get_branches_involved(self, repo_location, feature_id): - """ - Get the set of brances involved in the given repo and feature. - - :param repo_location: - The location of the repository to search for branches. - - :param feature_id: - The id of the feature to look for in branches. - - :returns: - A list of iterables containing at position: - - 0: branch name - 1: latest commit - """ - repo = git.Repo("{repo_location}/.git" + repo = git.Repo("{repo_location}/.git" .format(repo_location=repo_location)) - remote = git.remote.Remote(repo, 'origin') - affected = [] - feature_breakup = self._get_feature_breakdown(feature_id) - regex = self.branch_finder_template.format(**feature_breakup) - for remote_ref in remote.refs: - if re.search(regex, remote_ref.remote_head): - affected.append((remote_ref.remote_head, - str(remote_ref.commit))) - - return affected - - def set_merged_status(self, repo_name, hierarchy_tree, **kwargs): - """ - Set the merged status of the given tree in the repo. - - :param repo_name: - The name of the repository to search for branches. - - :param hierarchy_tree: - A tree-like object. - - TODO: - more definition here... (traversing, etc) - - """ - pass + remote = git.remote.Remote(repo, 'origin') + for remote_ref in remote.refs: + repo_branches[remote_ref.remote_head] = { + 'sha': str(remote_ref.commit) + } + + geneology = hierarchy.match_with_geneology( + feature_id, repo_branches.keys(), hierarchy_regexes) + + for branch, parent in geneology: + branch_list.append({ + "repo_name": repo_name, + "branch_name": branch, + "latest_commit": repo_branches[branch]['sha'], + "parent_branch_name": parent, + }) + + return branch_list diff --git a/deploystream/providers/github/__init__.py b/deploystream/providers/github/__init__.py index 5139fbd..4c31d94 100644 --- a/deploystream/providers/github/__init__.py +++ b/deploystream/providers/github/__init__.py @@ -91,7 +91,12 @@ def get_features(self, **filters): return features def get_feature_info(self, feature_id): - pass + # Feature ID will need to have org in it. + # For now we'll do a really crude search through the get_features + # results + for feat in self.get_features(): + if str(feat['id']) == str(feature_id): + return feat @classmethod def get_oauth_data(self): @@ -134,6 +139,7 @@ def get_repo_branches_involved(self, feature_id, hierarchy_regexes): "latest_commit": repo_branches[branch]['sha'], "parent_branch_name": parent, }) + return branch_list # def iter_commits(repo, branch): diff --git a/deploystream/providers/interfaces/source_code_control.py b/deploystream/providers/interfaces/source_code_control.py index d67df29..8f6c131 100644 --- a/deploystream/providers/interfaces/source_code_control.py +++ b/deploystream/providers/interfaces/source_code_control.py @@ -3,7 +3,7 @@ class ISourceCodeControlProvider(ProviderInterface): - def get_repo_branches_involved(feature_id): + def get_repo_branches_involved(feature_id, hierarchy_tree): """ Get the set of repo, branches involved in the given feature. @@ -18,19 +18,3 @@ def get_repo_branches_involved(feature_id): 2: latest commit """ pass - - def set_merged_status(repo_name, hierarchy_tree): - """ - Set the merged status of the given tree in the repo. - - :param repo_name: - The name of the repository to search for branches. - - :param hierarchy_tree: - A tree-like object. - - TODO: - more definition here... (traversing, etc) - - """ - pass diff --git a/tests/test_feature/test_view.py b/tests/test_feature/test_view.py index 363331d..0d47283 100644 --- a/tests/test_feature/test_view.py +++ b/tests/test_feature/test_view.py @@ -1,5 +1,8 @@ from datetime import datetime +import json + from mock import patch +from nose.tools import assert_equal, assert_true import deploystream @@ -8,8 +11,22 @@ class SourceCodeProvider(object): name = 'source' oauth_token_name = None - def get_repo_branches_involved(self, feature_id, **kwargs): - return [('repo_01', "{0}_branch".format(feature_id), "232323")] + def get_repo_branches_involved(self, feature_id, hierarchy_tree, **kwargs): + print "GET REPO BRANCHES INVOLVED" + return [ + { + 'repo_name': 'repo_01', + 'branch_name': '{0}_branch'.format(feature_id), + 'parent_branch_name': 'master', + 'latest_commit': '222222', + }, + { + 'repo_name': 'repo_01', + 'branch_name': 'master', + 'parent_branch_name': None, + 'latest_commit': '222223', + }, + ] def get_merged_status(self, repo_name, hierarchy_tree, **kwargs): return {} @@ -66,7 +83,21 @@ def setUp(self): 'testbuild': BuildInfoProvider}) def test_feature_view_shows_details(self): response = self.client.get('/features/plan/FT101') - assert "Amazing feature that will blow your mind" in response.data + feature_dict = json.loads(response.data) + print feature_dict + assert_true("Amazing feature that will blow your mind" in + feature_dict['title']) + + ft101_branch_output = ( + feature_dict['branches']['repo_01']['FT101_branch']) + assert_equal(ft101_branch_output['parent_branch_name'], 'master') + assert_equal(ft101_branch_output['children'], []) + + master_branch_output = ( + feature_dict['branches']['repo_01']['master']) + assert_equal(master_branch_output['parent_branch_name'], None) + assert_equal(master_branch_output['children'][0]['branch_name'], + "FT101_branch") @patch("deploystream.providers.ALL_PROVIDER_CLASSES", {'testplan': PlanningProvider, diff --git a/tests/test_providers/test_git_provider.py b/tests/test_providers/test_git_provider.py index c38e765..0043ad7 100644 --- a/tests/test_providers/test_git_provider.py +++ b/tests/test_providers/test_git_provider.py @@ -1,7 +1,7 @@ import os from os.path import join, exists, dirname -from nose.tools import assert_equal, with_setup +from nose.tools import assert_equal, assert_true, with_setup from deploystream.providers.git_provider import GitProvider @@ -33,27 +33,22 @@ def test_git_provider_finds_branches_across_repos(): Clone the dummyrepo into the data folder if not already there. The data in this test is found by looking at the dummyrepo and getting - the branch names and latest commit of any branches that match "FeAtUrE". + the branch names and latest commit of any branches that match "feature-99". """ - provider = GitProvider(code_dir=DUMMY_CODE_DIR, - feature_breakup_regex="(?P[a-zA-Z]+)-?(?P[0-9]+)", - branch_finder_template=".*(?i){project}.*") - branches = provider.get_repo_branches_involved('FeAtUrE-99') - - assert_equal([ - ('dummyrepo', 'my/feature_branch', - 'cf9130d3c07b061a88569153f10a7c7779338cfa'), - ], branches) - - -def test_git_provider_feature_breakup_regex(): - """ - Test that GitProvider breaks up feature ids into appropriate parts. - """ - provider = GitProvider( - feature_breakup_regex="(?P[a-zA-Z]+)-?(?P[0-9]+)") - for feature, expected in [ - ('DD-334', {'id': '334', 'project':'DD'}), - ('DD334', {'id': '334', 'project':'DD'}), - ]: - assert_equal(provider._get_feature_breakdown('DD-334'), expected) + provider = GitProvider(code_dir=DUMMY_CODE_DIR) + branches = provider.get_repo_branches_involved( + 'feature-99', + hierarchy_regexes=["master", "[a-z]*/{FEATURE_ID}"], + ) + + assert_equal(2, len(branches)) + assert_true({ + 'branch_name': 'master', + 'latest_commit': "0f6eefefc14f362a2c6f804df69aa83bac48c20b", + 'parent_branch_name': None, + 'repo_name': 'dummyrepo'} in branches) + assert_true({ + 'branch_name': 'my/feature-99', + 'latest_commit': "7098fa31bf9663343c723d9d155c0dc6e6e28174", + 'parent_branch_name': 'master', + 'repo_name': 'dummyrepo'} in branches) diff --git a/tests/test_providers/test_interfaces.py b/tests/test_providers/test_interfaces.py index 60eb839..6bc6823 100644 --- a/tests/test_providers/test_interfaces.py +++ b/tests/test_providers/test_interfaces.py @@ -13,7 +13,7 @@ def test_implements_source_control_provider(self): class MyProvider(object): name = "provider" oauth_token_name = "oauth" - def get_repo_branches_involved(self, feature_id): + def get_repo_branches_involved(self, feature_id, hierarchy_regex): pass def set_merged_status(self, repo_name, hierarchy_tree): From a204347ba1bbc98f8aceea17223705d8eb5acce0 Mon Sep 17 00:00:00 2001 From: Alex Couper Date: Fri, 24 May 2013 16:06:10 +0100 Subject: [PATCH 6/9] Tidy up. --- deploystream/apps/feature/lib.py | 5 ++--- deploystream/lib/hierarchy.py | 5 +++++ tests/test_feature/test_view.py | 2 -- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/deploystream/apps/feature/lib.py b/deploystream/apps/feature/lib.py index c7b7258..125e846 100644 --- a/deploystream/apps/feature/lib.py +++ b/deploystream/apps/feature/lib.py @@ -48,15 +48,14 @@ def get_feature_info(feature_provider, feature_id, providers): feature = Feature(planning_provider, **planning_provider.get_feature_info(feature_id)) - print providers[ISourceCodeControlProvider] # Then get any branch info from any source control providers for provider in providers[ISourceCodeControlProvider]: - print provider for branch_data in provider.get_repo_branches_involved( feature_id, app.config['HIERARCHY_REGEXES']): - print branch_data feature.add_branch(Branch(provider=provider, **branch_data)) + # Use that branch info, along with configuration regexes to create a + # hierarchy of the branches involved in the feature. feature.create_hierarchy_trees() # Ask source control providers for merging information at this point. diff --git a/deploystream/lib/hierarchy.py b/deploystream/lib/hierarchy.py index ba8606d..44baaf2 100644 --- a/deploystream/lib/hierarchy.py +++ b/deploystream/lib/hierarchy.py @@ -19,6 +19,11 @@ def match_with_geneology(feature_id, branches, hierarchical_regexes): A list of tuples containing: - Branch name - Parent name + + Go through the hierarchy regexes in sequence. + Attempt to match each one against all the branches. When a match occurs + remove the branch from the list to be matched against and continue. + Also add parental information as we go along. """ matched_branches = defaultdict(list) hierarchy = [] diff --git a/tests/test_feature/test_view.py b/tests/test_feature/test_view.py index 0d47283..4e06d7e 100644 --- a/tests/test_feature/test_view.py +++ b/tests/test_feature/test_view.py @@ -12,7 +12,6 @@ class SourceCodeProvider(object): oauth_token_name = None def get_repo_branches_involved(self, feature_id, hierarchy_tree, **kwargs): - print "GET REPO BRANCHES INVOLVED" return [ { 'repo_name': 'repo_01', @@ -84,7 +83,6 @@ def setUp(self): def test_feature_view_shows_details(self): response = self.client.get('/features/plan/FT101') feature_dict = json.loads(response.data) - print feature_dict assert_true("Amazing feature that will blow your mind" in feature_dict['title']) From ebf07343df79300ae51ad447e89b8fd7609b914c Mon Sep 17 00:00:00 2001 From: Alex Couper Date: Fri, 24 May 2013 17:46:49 +0100 Subject: [PATCH 7/9] Add `in_parent` and `has_parent` bools for github. --- config/local_settings_alex.py | 6 ++-- deploystream/apps/feature/models.py | 7 ++-- deploystream/providers/github/__init__.py | 25 +++++++++++-- tests/integration/test_github.py | 13 +++++-- tests/test_feature/test_models.py | 8 ++++- tests/test_lib/test_hierarchy.py | 38 ++++++++++++++++++++ tests/test_providers/test_github_provider.py | 7 +++- 7 files changed, 93 insertions(+), 11 deletions(-) diff --git a/config/local_settings_alex.py b/config/local_settings_alex.py index 7a2b126..1006b62 100644 --- a/config/local_settings_alex.py +++ b/config/local_settings_alex.py @@ -13,8 +13,10 @@ 'master', 'develop', 'story/{FEATURE_ID}', - 'story/{FEATURE_ID}/[a-z]*', - '{PARENT}/[a-z]*' + 'story/{FEATURE_ID}/[a-z_]*', + '{PARENT}[/_][a-z]*', + 'dev/{FEATURE_ID}/[a-z]*', + '{PARENT}[/_][a-z]*', ] JIRA_CONFIG = { diff --git a/deploystream/apps/feature/models.py b/deploystream/apps/feature/models.py index a17b7e3..6cbf05d 100644 --- a/deploystream/apps/feature/models.py +++ b/deploystream/apps/feature/models.py @@ -81,8 +81,9 @@ class Branch(object): """ - def __init__(self, repo_name, branch_name, latest_commit, - parent_branch_name, provider): + def __init__(self, provider, repo_name, branch_name, latest_commit, + parent_branch_name, + in_parent=None, has_parent=None): self._parent = None self.children = [] self.build_info = None @@ -90,6 +91,8 @@ def __init__(self, repo_name, branch_name, latest_commit, self.branch_name = branch_name self.latest_commit = latest_commit self.parent_branch_name = parent_branch_name + self.in_parent = in_parent + self.has_parent = has_parent self._provider = provider @property diff --git a/deploystream/providers/github/__init__.py b/deploystream/providers/github/__init__.py index 4c31d94..3c1cbcc 100644 --- a/deploystream/providers/github/__init__.py +++ b/deploystream/providers/github/__init__.py @@ -126,22 +126,43 @@ def get_repo_branches_involved(self, feature_id, hierarchy_regexes): for repo in self.repositories: repo_branches = {} for branch in repo.iter_branches(): - repo_branches[branch.name] = {'sha': branch.commit.sha} + repo_branches[branch.name] = { + 'sha': branch.commit.sha, + } geneology = hierarchy.match_with_geneology( feature_id, repo_branches.keys(), hierarchy_regexes) for branch, parent in geneology: + import time + has_parent = None + in_parent = None + branch_data = repo_branches[branch] + + if parent: + print "starting...", branch, parent, time.time() + for sha in [branch, parent]: + if repo_branches[sha].get('commits') is None: + repo_branches[sha]['commits'] = [ + c.sha for c in repo.iter_commits(sha=sha) + ] + print "finishing...", branch, parent, time.time() + parent_data = repo_branches[parent] + has_parent = parent_data['sha'] in branch_data['commits'] + in_parent = branch_data['sha'] in parent_data['commits'] branch_list.append({ "repo_name": repo.name, "branch_name": branch, - "latest_commit": repo_branches[branch]['sha'], + "latest_commit": branch_data['sha'], "parent_branch_name": parent, + "has_parent": has_parent, + "in_parent": in_parent, }) return branch_list + # def iter_commits(repo, branch): # from github3.repos.commit import RepoCommit # def iter_parents(commit_sha): diff --git a/tests/integration/test_github.py b/tests/integration/test_github.py index a11e772..21319bf 100644 --- a/tests/integration/test_github.py +++ b/tests/integration/test_github.py @@ -24,9 +24,16 @@ def test_get_repo_branches_involved(): "repo_name": "dummyrepo", "branch_name": "master", "latest_commit": '0f6eefefc14f362a2c6f804df69aa83bac48c20b', - "parent_branch_name": None} in branches) + "parent_branch_name": None, + "has_parent": None, + "in_parent": None, + } in branches) + print branches assert_true({ "repo_name": "dummyrepo", "branch_name": "story/101/fred", - "latest_commit": "0f6eefefc14f362a2c6f804df69aa83bac48c20b", - "parent_branch_name": "master"} in branches) + "latest_commit": "2f82934a1b47430af63df871b9155d8a977c6936", + "parent_branch_name": "master", + "has_parent": True, + "in_parent": False + } in branches) diff --git a/tests/test_feature/test_models.py b/tests/test_feature/test_models.py index 3b2a273..617da83 100644 --- a/tests/test_feature/test_models.py +++ b/tests/test_feature/test_models.py @@ -7,7 +7,13 @@ def test_feature_branches(): f = Feature(provider=None, project=None, id=None, title=None) for branch_name, parent_name in [('alex', 'master'), ('master', None), ('something', 'alex'), ('sthg2', 'alex')]: - b = Branch("repo1", branch_name, "commit", parent_name, "test") + b = Branch(provider="test", + repo_name="repo1", + branch_name=branch_name, + latest_commit="commit", + parent_branch_name=parent_name + ) + f.add_branch(b) f.create_hierarchy_trees() diff --git a/tests/test_lib/test_hierarchy.py b/tests/test_lib/test_hierarchy.py index bf55265..19015cf 100644 --- a/tests/test_lib/test_hierarchy.py +++ b/tests/test_lib/test_hierarchy.py @@ -57,3 +57,41 @@ def test_geneology_missing_steps(): results = match_with_geneology(12, branches, regexes) assert_items_equal(results, expected) + + +def test_gets_underscores(): + regexes = [ + 'master', + 'develop', + 'story/{FEATURE_ID}', + 'story/{FEATURE_ID}/[a-z_]*', + '{PARENT}[/_][a-z]*', + 'dev/{FEATURE_ID}/[a-z]*', + '{PARENT}[/_][a-z]*', + ] + + branches = [ + 'alex/19', + 'dev/70/alex', + 'dev/70/alex_hierarchy', + 'dev_alex', + 'develop', + 'master', + 'story/23/carles', + 'story/43/carles', + 'story/53/carles', + 'story/58/carles', + 'story/70/alex', + 'story/72/carles', + ] + expected = [ + ('master', None), + ('develop', 'master'), + ('story/70/alex', 'develop'), + ('dev/70/alex', 'story/70/alex'), + ('dev/70/alex_hierarchy', 'dev/70/alex'), + ] + + results = match_with_geneology(70, branches, regexes) + print results + assert_items_equal(results, expected) diff --git a/tests/test_providers/test_github_provider.py b/tests/test_providers/test_github_provider.py index 8500860..9044e2f 100644 --- a/tests/test_providers/test_github_provider.py +++ b/tests/test_providers/test_github_provider.py @@ -11,6 +11,7 @@ def mock_github3(github3): mock_repo = Mock() mock_repo.has_issues = True mock_repo.name = 'repo_1' + mock_repo.iter_commits.return_value = [Mock(sha="CoMmItHaSh-MaStEr")] issue1 = { 'title': 'Hello', @@ -88,11 +89,15 @@ def test_get_repo_branches_involved(github3): "repo_name": "repo_1", "branch_name": "master", "parent_branch_name": None, - "latest_commit": 'CoMmItHaSh-MaStEr' + "latest_commit": 'CoMmItHaSh-MaStEr', + "has_parent": None, + "in_parent": None, } in branches) assert_true({ "repo_name": "repo_1", "branch_name": "story/5/alex", "parent_branch_name": "master", "latest_commit": "CoMmItHaSh-5", + "has_parent": True, + "in_parent": False, } in branches) From 0aace82cd639eb92499946c2815da9cf3efa4a3b Mon Sep 17 00:00:00 2001 From: Alex Couper Date: Fri, 24 May 2013 17:57:10 +0100 Subject: [PATCH 8/9] Tidy up. --- deploystream/providers/github/__init__.py | 34 +++++++++++------------ tests/integration/test_github.py | 1 - tests/test_lib/test_hierarchy.py | 1 - 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/deploystream/providers/github/__init__.py b/deploystream/providers/github/__init__.py index 3c1cbcc..b477e97 100644 --- a/deploystream/providers/github/__init__.py +++ b/deploystream/providers/github/__init__.py @@ -120,6 +120,19 @@ def get_repo_branches_involved(self, feature_id, hierarchy_regexes): - branch_name - parent_branch_name - latest_commit + - has_parent + - in_parent + + Look through each repo and get a set of branches that match the + ``hierarchy_regexes``. + + Go through matching branches finding their merge status. + + .. note:: + We loop through all the commits for every matching branch exactly + once. This could be optimized to only check back as far as some + ancestor's HEAD. + """ branch_list = [] @@ -134,19 +147,20 @@ def get_repo_branches_involved(self, feature_id, hierarchy_regexes): feature_id, repo_branches.keys(), hierarchy_regexes) for branch, parent in geneology: - import time has_parent = None in_parent = None branch_data = repo_branches[branch] if parent: - print "starting...", branch, parent, time.time() for sha in [branch, parent]: + # Loop through all the commits for branch and parent if + # we haven't already done so and store them in the + # temporary ``repo_branches`` dict if repo_branches[sha].get('commits') is None: repo_branches[sha]['commits'] = [ c.sha for c in repo.iter_commits(sha=sha) ] - print "finishing...", branch, parent, time.time() + # Check if we're merged in parent_data = repo_branches[parent] has_parent = parent_data['sha'] in branch_data['commits'] in_parent = branch_data['sha'] in parent_data['commits'] @@ -161,17 +175,3 @@ def get_repo_branches_involved(self, feature_id, hierarchy_regexes): }) return branch_list - - -# def iter_commits(repo, branch): -# from github3.repos.commit import RepoCommit -# def iter_parents(commit_sha): -# url = repo._build_url('commits', commit_sha, base_url=repo._api) -# json = repo._json(repo._get(url), 200) -# commit = RepoCommit(json) -# yield commit -# for parent in commit.parents: -# for commit in iter_parents(RepoCommit(parent).sha): -# yield commit - -# return iter_parents(branch.commit.sha) diff --git a/tests/integration/test_github.py b/tests/integration/test_github.py index 21319bf..55fa818 100644 --- a/tests/integration/test_github.py +++ b/tests/integration/test_github.py @@ -28,7 +28,6 @@ def test_get_repo_branches_involved(): "has_parent": None, "in_parent": None, } in branches) - print branches assert_true({ "repo_name": "dummyrepo", "branch_name": "story/101/fred", diff --git a/tests/test_lib/test_hierarchy.py b/tests/test_lib/test_hierarchy.py index 19015cf..fd8c583 100644 --- a/tests/test_lib/test_hierarchy.py +++ b/tests/test_lib/test_hierarchy.py @@ -93,5 +93,4 @@ def test_gets_underscores(): ] results = match_with_geneology(70, branches, regexes) - print results assert_items_equal(results, expected) From 43ebae81c9cd02109324ed3b14fe870f644afdb0 Mon Sep 17 00:00:00 2001 From: Alex Couper Date: Mon, 27 May 2013 14:06:25 +0100 Subject: [PATCH 9/9] Change variable names. --- deploystream/apps/feature/lib.py | 6 ++-- deploystream/apps/feature/models.py | 34 +++++++++---------- deploystream/lib/hierarchy.py | 2 +- .../providers/git_provider/__init__.py | 20 +++++------ deploystream/providers/github/__init__.py | 20 +++++------ tests/integration/test_github.py | 16 ++++----- tests/test_feature/test_models.py | 8 ++--- tests/test_feature/test_view.py | 22 ++++++------ tests/test_lib/test_hierarchy.py | 16 ++++----- tests/test_providers/test_git_provider.py | 16 ++++----- tests/test_providers/test_github_provider.py | 16 ++++----- 11 files changed, 88 insertions(+), 88 deletions(-) diff --git a/deploystream/apps/feature/lib.py b/deploystream/apps/feature/lib.py index 125e846..a4ad4b0 100644 --- a/deploystream/apps/feature/lib.py +++ b/deploystream/apps/feature/lib.py @@ -67,9 +67,9 @@ def get_feature_info(feature_provider, feature_id, providers): for provider in providers[IBuildInfoProvider]: for branch in feature.branches: build_info = provider.get_build_information( - branch.repo_name, - branch.branch_name, - branch.latest_commit + branch.repository, + branch.name, + branch.commit_id, ) branch.build_info = BuildInfo(provider=provider, **build_info) return feature diff --git a/deploystream/apps/feature/models.py b/deploystream/apps/feature/models.py index 6cbf05d..46ded1f 100644 --- a/deploystream/apps/feature/models.py +++ b/deploystream/apps/feature/models.py @@ -46,14 +46,14 @@ def __init__(self, provider, project, id, title, def add_branch(self, branch): assert isinstance(branch, Branch) - self.branches[branch.repo_name][branch.branch_name] = branch + self.branches[branch.repository][branch.name] = branch def create_hierarchy_trees(self): "Create hierarchy trees - one for each repo." for branch_set in self.branches.values(): for branch in branch_set.values(): - if branch.parent_branch_name: - branch.parent = branch_set[branch.parent_branch_name] + if branch.parent_name: + branch.parent = branch_set[branch.parent_name] class Branch(object): @@ -62,13 +62,13 @@ class Branch(object): Instances contain values for: - ``repo_name`` - The repository that this branch is found in. - ``branch_name`` - The name of the branch. - ``latest_commit`` - The head commmit, or latest revision in this - branch. - ``parent_branch_name`` - The name of the branches parent. - ``provider`` - The provider instance that found this branch - information. + ``repository`` - The repository that this branch is found in. + ``name`` - The name of the branch. + ``commit_id`` - The head commmit, or latest revision in this + branch. + ``parent_name`` - The name of the branches parent. + ``provider`` - The provider instance that found this branch + information. Instances are eventually populated with these values: @@ -81,16 +81,16 @@ class Branch(object): """ - def __init__(self, provider, repo_name, branch_name, latest_commit, - parent_branch_name, + def __init__(self, provider, repository, name, commit_id, + parent_name, in_parent=None, has_parent=None): self._parent = None self.children = [] self.build_info = None - self.repo_name = repo_name - self.branch_name = branch_name - self.latest_commit = latest_commit - self.parent_branch_name = parent_branch_name + self.repository = repository + self.name = name + self.commit_id = commit_id + self.parent_name = parent_name self.in_parent = in_parent self.has_parent = has_parent self._provider = provider @@ -120,7 +120,7 @@ def remove_child(self, branch): def as_tree_string(self, indent=0): if self.parent and not indent: return self.parent.as_tree_string() - me = "{0}- {1}\n".format(indent * ' ', self.branch_name) + me = "{0}- {1}\n".format(indent * ' ', self.name) for c in self.children: me += c.as_tree_string(indent + 4) diff --git a/deploystream/lib/hierarchy.py b/deploystream/lib/hierarchy.py index 44baaf2..5e94308 100644 --- a/deploystream/lib/hierarchy.py +++ b/deploystream/lib/hierarchy.py @@ -2,7 +2,7 @@ import re -def match_with_geneology(feature_id, branches, hierarchical_regexes): +def match_with_genealogy(feature_id, branches, hierarchical_regexes): """ Filter and return the branches in order with parents attached. diff --git a/deploystream/providers/git_provider/__init__.py b/deploystream/providers/git_provider/__init__.py index 493deff..eb90689 100644 --- a/deploystream/providers/git_provider/__init__.py +++ b/deploystream/providers/git_provider/__init__.py @@ -32,10 +32,10 @@ def get_repo_branches_involved(self, feature_id, hierarchy_regexes): :returns: A list of dictionaries containing keys for: - - repo_name - - branch_name - - parent_branch_name - - latest_commit + - repository + - name + - parent_name + - commit_id """ branch_list = [] @@ -52,15 +52,15 @@ def get_repo_branches_involved(self, feature_id, hierarchy_regexes): 'sha': str(remote_ref.commit) } - geneology = hierarchy.match_with_geneology( + genealogy = hierarchy.match_with_genealogy( feature_id, repo_branches.keys(), hierarchy_regexes) - for branch, parent in geneology: + for branch, parent in genealogy: branch_list.append({ - "repo_name": repo_name, - "branch_name": branch, - "latest_commit": repo_branches[branch]['sha'], - "parent_branch_name": parent, + "repository": repo_name, + "name": branch, + "commit_id": repo_branches[branch]['sha'], + "parent_name": parent, }) return branch_list diff --git a/deploystream/providers/github/__init__.py b/deploystream/providers/github/__init__.py index b477e97..d451bb8 100644 --- a/deploystream/providers/github/__init__.py +++ b/deploystream/providers/github/__init__.py @@ -116,10 +116,10 @@ def get_repo_branches_involved(self, feature_id, hierarchy_regexes): :returns: A list of dictionaries containing keys for: - - repo_name - - branch_name - - parent_branch_name - - latest_commit + - repository + - name + - parent_name + - commit_id - has_parent - in_parent @@ -143,10 +143,10 @@ def get_repo_branches_involved(self, feature_id, hierarchy_regexes): 'sha': branch.commit.sha, } - geneology = hierarchy.match_with_geneology( + genealogy = hierarchy.match_with_genealogy( feature_id, repo_branches.keys(), hierarchy_regexes) - for branch, parent in geneology: + for branch, parent in genealogy: has_parent = None in_parent = None branch_data = repo_branches[branch] @@ -166,10 +166,10 @@ def get_repo_branches_involved(self, feature_id, hierarchy_regexes): in_parent = branch_data['sha'] in parent_data['commits'] branch_list.append({ - "repo_name": repo.name, - "branch_name": branch, - "latest_commit": branch_data['sha'], - "parent_branch_name": parent, + "repository": repo.name, + "name": branch, + "commit_id": branch_data['sha'], + "parent_name": parent, "has_parent": has_parent, "in_parent": in_parent, }) diff --git a/tests/integration/test_github.py b/tests/integration/test_github.py index 55fa818..744ef35 100644 --- a/tests/integration/test_github.py +++ b/tests/integration/test_github.py @@ -21,18 +21,18 @@ def test_get_repo_branches_involved(): assert_equal(2, len(branches)) assert_true({ - "repo_name": "dummyrepo", - "branch_name": "master", - "latest_commit": '0f6eefefc14f362a2c6f804df69aa83bac48c20b', - "parent_branch_name": None, + "repository": "dummyrepo", + "name": "master", + "commit_id": '0f6eefefc14f362a2c6f804df69aa83bac48c20b', + "parent_name": None, "has_parent": None, "in_parent": None, } in branches) assert_true({ - "repo_name": "dummyrepo", - "branch_name": "story/101/fred", - "latest_commit": "2f82934a1b47430af63df871b9155d8a977c6936", - "parent_branch_name": "master", + "repository": "dummyrepo", + "name": "story/101/fred", + "commit_id": "2f82934a1b47430af63df871b9155d8a977c6936", + "parent_name": "master", "has_parent": True, "in_parent": False } in branches) diff --git a/tests/test_feature/test_models.py b/tests/test_feature/test_models.py index 617da83..8b5bd3e 100644 --- a/tests/test_feature/test_models.py +++ b/tests/test_feature/test_models.py @@ -8,10 +8,10 @@ def test_feature_branches(): for branch_name, parent_name in [('alex', 'master'), ('master', None), ('something', 'alex'), ('sthg2', 'alex')]: b = Branch(provider="test", - repo_name="repo1", - branch_name=branch_name, - latest_commit="commit", - parent_branch_name=parent_name + repository="repo1", + name=branch_name, + commit_id="commit", + parent_name=parent_name ) f.add_branch(b) diff --git a/tests/test_feature/test_view.py b/tests/test_feature/test_view.py index 4e06d7e..4e7a902 100644 --- a/tests/test_feature/test_view.py +++ b/tests/test_feature/test_view.py @@ -14,16 +14,16 @@ class SourceCodeProvider(object): def get_repo_branches_involved(self, feature_id, hierarchy_tree, **kwargs): return [ { - 'repo_name': 'repo_01', - 'branch_name': '{0}_branch'.format(feature_id), - 'parent_branch_name': 'master', - 'latest_commit': '222222', + 'repository': 'repo_01', + 'name': '{0}_branch'.format(feature_id), + 'parent_name': 'master', + 'commit_id': '222222', }, { - 'repo_name': 'repo_01', - 'branch_name': 'master', - 'parent_branch_name': None, - 'latest_commit': '222223', + 'repository': 'repo_01', + 'name': 'master', + 'parent_name': None, + 'commit_id': '222223', }, ] @@ -88,13 +88,13 @@ def test_feature_view_shows_details(self): ft101_branch_output = ( feature_dict['branches']['repo_01']['FT101_branch']) - assert_equal(ft101_branch_output['parent_branch_name'], 'master') + assert_equal(ft101_branch_output['parent_name'], 'master') assert_equal(ft101_branch_output['children'], []) master_branch_output = ( feature_dict['branches']['repo_01']['master']) - assert_equal(master_branch_output['parent_branch_name'], None) - assert_equal(master_branch_output['children'][0]['branch_name'], + assert_equal(master_branch_output['parent_name'], None) + assert_equal(master_branch_output['children'][0]['name'], "FT101_branch") @patch("deploystream.providers.ALL_PROVIDER_CLASSES", diff --git a/tests/test_lib/test_hierarchy.py b/tests/test_lib/test_hierarchy.py index fd8c583..5fe6460 100644 --- a/tests/test_lib/test_hierarchy.py +++ b/tests/test_lib/test_hierarchy.py @@ -1,9 +1,9 @@ from nose.tools import assert_items_equal -from deploystream.lib.hierarchy import match_with_geneology +from deploystream.lib.hierarchy import match_with_genealogy -def test_geneology(): +def test_genealogy(): regexes = ['master', 'develop', 'story/{FEATURE_ID}', 'story/{FEATURE_ID}/[a-z]*', '{PARENT}/[a-z]*'] @@ -20,11 +20,11 @@ def test_geneology(): ('story/12/bill', 'story/12'), ('story/12/bill/something', 'story/12/bill')] - results = match_with_geneology(12, branches, regexes) + results = match_with_genealogy(12, branches, regexes) assert_items_equal(results, expected) -def test_geneology_unordered_list(): +def test_genealogy_unordered_list(): regexes = ['master', 'develop', 'story/{FEATURE_ID}', 'story/{FEATURE_ID}/[a-z]*', '{PARENT}/[a-z]*'] @@ -39,12 +39,12 @@ def test_geneology_unordered_list(): ('story/12/bill', 'story/12'), ('story/12/bill/something', 'story/12/bill')] - results = match_with_geneology(12, branches, regexes) + results = match_with_genealogy(12, branches, regexes) results.sort() assert_items_equal(results, expected) -def test_geneology_missing_steps(): +def test_genealogy_missing_steps(): regexes = ['master', 'develop', 'story/{FEATURE_ID}', 'story/{FEATURE_ID}/[a-z]*', '{PARENT}/[a-z]*'] @@ -55,7 +55,7 @@ def test_geneology_missing_steps(): expected = [('master', None), ('story/12/bill', 'master')] - results = match_with_geneology(12, branches, regexes) + results = match_with_genealogy(12, branches, regexes) assert_items_equal(results, expected) @@ -92,5 +92,5 @@ def test_gets_underscores(): ('dev/70/alex_hierarchy', 'dev/70/alex'), ] - results = match_with_geneology(70, branches, regexes) + results = match_with_genealogy(70, branches, regexes) assert_items_equal(results, expected) diff --git a/tests/test_providers/test_git_provider.py b/tests/test_providers/test_git_provider.py index 0043ad7..99e010b 100644 --- a/tests/test_providers/test_git_provider.py +++ b/tests/test_providers/test_git_provider.py @@ -43,12 +43,12 @@ def test_git_provider_finds_branches_across_repos(): assert_equal(2, len(branches)) assert_true({ - 'branch_name': 'master', - 'latest_commit': "0f6eefefc14f362a2c6f804df69aa83bac48c20b", - 'parent_branch_name': None, - 'repo_name': 'dummyrepo'} in branches) + 'name': 'master', + 'commit_id': "0f6eefefc14f362a2c6f804df69aa83bac48c20b", + 'parent_name': None, + 'repository': 'dummyrepo'} in branches) assert_true({ - 'branch_name': 'my/feature-99', - 'latest_commit': "7098fa31bf9663343c723d9d155c0dc6e6e28174", - 'parent_branch_name': 'master', - 'repo_name': 'dummyrepo'} in branches) + 'name': 'my/feature-99', + 'commit_id': "7098fa31bf9663343c723d9d155c0dc6e6e28174", + 'parent_name': 'master', + 'repository': 'dummyrepo'} in branches) diff --git a/tests/test_providers/test_github_provider.py b/tests/test_providers/test_github_provider.py index 9044e2f..9cddb95 100644 --- a/tests/test_providers/test_github_provider.py +++ b/tests/test_providers/test_github_provider.py @@ -86,18 +86,18 @@ def test_get_repo_branches_involved(github3): DEFAULT_HIERARCHY_REGEXES) assert_equal(2, len(branches)) assert_true({ - "repo_name": "repo_1", - "branch_name": "master", - "parent_branch_name": None, - "latest_commit": 'CoMmItHaSh-MaStEr', + "repository": "repo_1", + "name": "master", + "parent_name": None, + "commit_id": 'CoMmItHaSh-MaStEr', "has_parent": None, "in_parent": None, } in branches) assert_true({ - "repo_name": "repo_1", - "branch_name": "story/5/alex", - "parent_branch_name": "master", - "latest_commit": "CoMmItHaSh-5", + "repository": "repo_1", + "name": "story/5/alex", + "parent_name": "master", + "commit_id": "CoMmItHaSh-5", "has_parent": True, "in_parent": False, } in branches)