Merge lp:~chad.smith/lp2kanban/cross-team-ignore-other-projects into lp:lp2kanban

Proposed by Chad Smith on 2016-05-02
Status: Merged
Merged at revision: 138
Proposed branch: lp:~chad.smith/lp2kanban/cross-team-ignore-other-projects
Merge into: lp:lp2kanban
Diff against target: 198 lines (+102/-11)
3 files modified
src/lp2kanban/bugs2cards.py (+19/-3)
src/lp2kanban/tests/common.py (+6/-2)
src/lp2kanban/tests/test_bugs2cards.py (+77/-6)
To merge this branch: bzr merge lp:~chad.smith/lp2kanban/cross-team-ignore-other-projects
Reviewer Review Type Date Requested Status
Björn Tillenius 2016-05-02 Approve on 2016-05-04
Review via email: mp+293575@code.launchpad.net

Description of the change

This branch allows the autosync configuration option to be tri-state:
    "on" - sync any bug cards that don't have the no-sync marker
    "active-projects" - sync any bug cards that are related to the INI configured projects
    "off" - don't sync any bug cards unless "sync:" is in the title of the card

The following branch also needs to be paired with the following tweak to lp:~landscape/lp2kanban-configs: https://pastebin.canonical.com/155651/

lp2kanban for the cross team board actually takes 4 passes over all cards on the board. lp2kanban tries to move & sync cards that are either related to the configured 'projects' for the board or assigned to any user of the board.

Since we have 4 different board configs on the single cross team board, maas, juju, server, charms, we need to make sure each board config only attempts to move bug cards for the active-projects configured within that specific INI file. Otherwise, we get card move contention.

For example, take a card with 2 tasks, one task in juju-core project in New status and a charm task in Fix Committed.

In the above case, a bugs2cards -c juju.ini would treat the card as New because it ignores the status of the "external Charm project" task. But, bugs2cards -c charms.ini treats the charm project task as authoritative and move the card to the proper Fix Committed lane.

I've tested this against cross-team to keep move contention from happening like https://canonical.leankit.com/Boards/View/117275746/121133258

To post a comment you must log in.
Chad Smith (chad.smith) wrote :

To test:
make
make configs
make check

Björn Tillenius (bjornt) wrote :

Looks good to me.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/lp2kanban/bugs2cards.py'
2--- src/lp2kanban/bugs2cards.py 2016-04-15 17:10:14 +0000
3+++ src/lp2kanban/bugs2cards.py 2016-05-02 23:23:20 +0000
4@@ -339,8 +339,9 @@
5 """Check to see if the card should be synced.
6
7 `conf` is the kanban board configuration as a dict, and should contain
8- an option `autosync` set to 'on' if all cards with external card IDs
9- should be synced unless their description or title contain NO_SYNC_MARKER.
10+ an option `autosync` set to 'on' or 'active-projects' if all cards
11+ with external card IDs should be synced unless their description or title
12+ contain NO_SYNC_MARKER.
13 With autosync off, if the card title starts with TITLE_MARKER or the card
14 description contains DESCRIPTION_MARKER then return True.
15
16@@ -354,7 +355,7 @@
17 for no_sync_lane_path in no_sync_lanes:
18 if no_sync_lane_path and card.lane.path.endswith(no_sync_lane_path):
19 return False
20- if conf.get('autosync', None) == 'on':
21+ if conf.get('autosync', None) in ['on', 'active-projects']:
22 has_id = (card.external_card_id is not None and
23 card.external_card_id.strip() != '')
24 has_branch = (card.external_system_url is not None and
25@@ -488,6 +489,17 @@
26 return status
27
28
29+def is_bug_in_projects(lp_bug, all_projects):
30+ """Return True if bug is linked to one of the configured projects"""
31+ for task in lp_bug.bug_tasks_collection:
32+ if task.target in all_projects:
33+ return True
34+ if (hasattr(task.target, "project") and
35+ task.target.project in all_projects):
36+ return True
37+ return False
38+
39+
40 def get_bug_status(lp_bug, all_projects, all_users):
41 lowest_status = None
42 matches = False
43@@ -698,6 +710,10 @@
44 lp_bug = get_lp_bug(card, lp)
45 if lp_bug is None:
46 continue
47+ ignore_external_projects = (
48+ bconf.get("autosync") == "active-projects")
49+ if ignore_external_projects and not is_bug_in_projects(lp_bug, all_projects):
50+ continue # Skip bugs in external projects
51 synced, card_status, assignees, mp_url, mp_type = get_bug_status(
52 lp_bug, all_projects, lp_users.lp_to_kanban.keys())
53
54
55=== modified file 'src/lp2kanban/tests/common.py'
56--- src/lp2kanban/tests/common.py 2016-02-24 10:57:51 +0000
57+++ src/lp2kanban/tests/common.py 2016-05-02 23:23:20 +0000
58@@ -22,7 +22,8 @@
59 _next_lane_id = 0
60
61 def __init__(self, cards=None, users=None, users_by_id=None,
62- default_cardtype=None, is_archived=False, feature_cards=None):
63+ default_cardtype=None, is_archived=False, feature_cards=None,
64+ bug_cards=None):
65 if cards is None:
66 cards = []
67 self.cards = cards
68@@ -32,7 +33,10 @@
69 self.cardtypes = {self.default_cardtype.id: self.default_cardtype}
70 self.is_archived = is_archived
71 self._cards_with_description_annotations = set()
72- self._cards_with_external_ids = set()
73+ if bug_cards is None:
74+ self._cards_with_external_ids = set()
75+ else:
76+ self._cards_with_external_ids = bug_cards
77 self._cards_with_external_links = set()
78 self._cards_with_branches = set()
79 if feature_cards is None:
80
81=== modified file 'src/lp2kanban/tests/test_bugs2cards.py'
82--- src/lp2kanban/tests/test_bugs2cards.py 2016-04-15 17:10:14 +0000
83+++ src/lp2kanban/tests/test_bugs2cards.py 2016-05-02 23:23:20 +0000
84@@ -18,6 +18,7 @@
85 create_new_cards,
86 find_card_target_lane_next,
87 get_branch_info,
88+ is_bug_in_projects,
89 get_bug_status,
90 get_card_status,
91 get_cards_for_feature,
92@@ -139,12 +140,9 @@
93 class FauxLP(object):
94 def __init__(self, data={}):
95 self.data = data
96-
97- def project_groups(self):
98- return self.data.get("project_groups", {})
99-
100- def projects(self):
101- return self.data.get("projects", {})
102+ self.projects = data.get("projects", {})
103+ self.project_group = data.get("project_groups", {})
104+ self.bugs = data.get("bugs", {})
105
106
107 class BranchInfoTests(unittest.TestCase):
108@@ -309,6 +307,33 @@
109 self.record = self.bug.addTask(target)
110
111
112+class IsBugInProjectsTest(unittest.TestCase):
113+
114+ def test_false_when_part_of_external_project(self):
115+ # Return False when the bug is part of an unconfigured (external) project
116+ bug = FauxBug()
117+ project = FauxTarget(resource_type_link="project", project="otherproject")
118+ bug.addTask(project)
119+ self.assertFalse(is_bug_in_projects(bug, ["myproject1", "myproject2"]))
120+
121+ def test_true_when_part_of_configured_projects(self):
122+ # Return True when the bug is one of the configured (internal) projects
123+ bug = FauxBug()
124+ project = FauxTarget(resource_type_link="project", project="myproject2")
125+ bug.addTask(project)
126+ self.assertTrue(is_bug_in_projects(bug, ["myproject1", "myproject2"]))
127+
128+
129+ def test_true_when_series_within_a_configured_project(self):
130+ # Return True when the bug has a series task that is related to one of the
131+ # configured projects.
132+ bug = FauxBug()
133+ series = FauxTarget(
134+ resource_type_link=RESOURCE_TYPE_LINK_SERIES, project="myproject1")
135+ bug.addTask(series, status='Fix Released')
136+ self.assertTrue(is_bug_in_projects(bug, ["myproject1"]))
137+
138+
139 class BugStatusTest(unittest.TestCase):
140
141 def test_get_bug_status_unknown(self):
142@@ -556,6 +581,13 @@
143 self.assertTrue(should_sync_card(card, {'autosync': 'on',
144 'sync_cards': 'on'}))
145
146+ def test_should_sync_card_autosync_synced_with_external_card_id(self):
147+ # When autosync is 'active-projects', cards with external card IDs are synced.
148+ card = Record(title=u'no sync', description=u'',
149+ external_card_id=u'11', external_system_url=u'')
150+ self.assertTrue(should_sync_card(card, {'autosync': 'active-projects',
151+ 'sync_cards': 'on'}))
152+
153 def test_should_sync_card_autosync_synced_with_branch(self):
154 # When autosync is 'on', cards with a valid branch are synced.
155 card = Record(title=u'no sync', description=u'',
156@@ -1671,3 +1703,42 @@
157 self.assertIsNone(coding_card.moved_to)
158 self.assertEqual(self.feature_card, landed_card1.moved_to)
159 self.assertEqual(self.feature_card, landed_card2.moved_to)
160+
161+ def test_sync_board_skips_bugs_of_external_projects_when_active_projects_set(self):
162+ """Bugs of external projects are skipped when autosync set to active-projects."""
163+ # Configure bugs2cards to sync only active-projects myproject1 and myproject2
164+ self.bconf.update({"autosync": "active-projects",
165+ "projects": "myproject,myproject2",
166+ "sync_cards": "on", "move_cards": "on",
167+ "new_lanes": "new"})
168+ # Setup bug1 and bug2 in separate projects which both need to move coding -> new
169+ project1 = FauxTarget(resource_type_link="project", project="myproject")
170+ project2 = FauxTarget(resource_type_link="project", project="otherproject")
171+ lp_users = FauxLaunchpadUsersForBoard(lp_to_kanban={"person1": "Person One"})
172+ bug_task1 = FauxBugTask(
173+ bug_id=123, target=project1, title='Test bug in myproject',
174+ description='Test bug description.')
175+ bug_task2 = FauxBugTask(
176+ bug_id=456, target=project2, title='Test bug in otherproject',
177+ description='Test bug description.')
178+ bug_card1 = FauxCard(
179+ title='BugCard123', external_card_id=u'123')
180+ bug_card2 = FauxCard(
181+ title='BugCard456', external_card_id=u'456')
182+ board = FauxBoard(
183+ cards=[bug_card1, bug_card2], bug_cards=[bug_card1, bug_card2])
184+ new_lane = board.addLane('new')
185+ coding_lane = board.addLane('coding')
186+ bug_card1.lane = coding_lane
187+ bug_card2.lane = coding_lane
188+ bug_type = FauxCardType(id=3, name='Bug', is_default=False)
189+ bug_card1.type = bug_type
190+ bug_card2.type = bug_type
191+ self.board.cards.extend([bug_card1, bug_card2])
192+ # Include project1 in our active 'projects' fake LP data
193+ lp = FauxLP({"projects": {"myproject": project1},
194+ "bugs": {123: bug_task1.bug, "456": bug_task2.bug}})
195+ sync_board(board, self.bconf, lp=lp, lp_users=lp_users)
196+ coding_lane = FauxLane(board=board, path='coding')
197+ self.assertEqual(new_lane, bug_card1.moved_to)
198+ self.assertIsNone(bug_card2.moved_to)

Subscribers

People subscribed via source and target branches

to all changes: