Merge lp:~albaguirre/jenkins-launchpad-plugin/support-query-git-mps into lp:jenkins-launchpad-plugin

Proposed by Alberto Aguirre
Status: Merged
Merged at revision: 131
Proposed branch: lp:~albaguirre/jenkins-launchpad-plugin/support-query-git-mps
Merge into: lp:jenkins-launchpad-plugin
Diff against target: 210 lines (+119/-9)
5 files modified
jlp.config (+4/-0)
jlp/commands/getMergeProposals.py (+4/-2)
jlp/launchpadagent.py (+7/-1)
jlp/launchpadutils.py (+30/-6)
tests/test_launchpadutils.py (+74/-0)
To merge this branch: bzr merge lp:~albaguirre/jenkins-launchpad-plugin/support-query-git-mps
Reviewer Review Type Date Requested Status
Francis Ginther Needs Fixing
Alexandros Frantzis (community) Approve
Review via email: mp+302329@code.launchpad.net

Commit message

Add support to query merge proposals from launchpad git repos

Description of the change

Needs lp_version: devel in the configuration to be able to run.

Example:
alberto@skynet97:~/source/jenkins-launchpad-plugin$ ./getMergeProposals.py --merge_type "Needs review" --repo_type git repowerd
DEBUG: [repowerd] merge proposals: [<branch_merge_proposal at https://api.launchpad.net/devel/~albaguirre/repowerd/+git/repowerd/+merge/301919>]
DEBUG: count: 1

To post a comment you must log in.
132. By Alberto Aguirre

Small cleanup, some more tests

133. By Alberto Aguirre

Fix too much indent on doc comment

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

review: Approve
Revision history for this message
Francis Ginther (fginther) wrote :

Thanks for working on adding git support. I'd like to get these three MPs merged, but I think this can be done without requiring an explicit repo_type option for every git repo. I think it's reasonable to have the code attempt to lookup both a bazaar branch and git repo based on the name and select the branch or repo that isn't 'None'. For example, the following can be used to convert the user provided name ('lp:jenkins-launchpad-plugin' or 'lp:some-git-repo') into a branch object or a repo_type dynamically:

def get_branch_handle_for(lp_handle, name, repo_type='auto'):
    """ Return a branch/repo handle for the given name.

    Returns the repo_type and a launchpad branch or git repository
      handle for the given name.
    :param lp_handle: launchpad API handle/instance
    :param name: name of the branch or git repository
    :param repo_type: Type of repository [bazaar, git or auto]
    """
    logger.debug('Provided branch name: {}'.format(name))
    branch = lp_handle.branches.getByUrl(url=name)
    logger.debug('bazaar branch: {}'.format(branch))
    if repo_type == 'bazaar':
        return repo_type, branch
    try:
        repo = lp_handle.git_repositories.getByPath(
            path=name.replace('lp:', ''))
    except AttributeError:
        logger.debug('git_repositories.getByPath was not found. You may need '
                     'to set lp_version:devel in the config')
        if repo_type == 'git':
            raise RuntimeError('Git support is incomplete, '
                               'set lp_version:devel in the config.')
    logger.debug('git repo: {}'.format(repo))
    if repo_type == 'git':
        return repo_type, repo
    elif repo_type == 'auto':
        if branch is not None and repo is not None:
            # Ambiguous name, can't automatically select
            raise RuntimeError('Ambiguous branch name: {} resolves to '
                               '{} and {}'.format(name, branch, repo))
        if branch is not None:
            return 'bazaar', branch
        elif repo is not None:
            return 'git', repo
        else:
            raise RuntimeError('Could not determine branch or repo from '
                               '{}'.format(name))
    else:
        logger.debug('Repo type "' + repo_type + '" is not valid')

    return None, None

This then returns the repo_typo that was discovered along with the branch or repo object. Unfortunately, launchpad allows git repos and bazaar branches to have the same name, so it's possible to encounter cases where this lookup will return both, which is why this method raises an error when that happens. However, I think in practice this will almost never happen as teams will either choose bazaar or git for their project and not use both. To deal with this case, I do think it's ok to have an optional 'repo_type' that can be either 'bazaar', 'git' or 'auto'. Using 'bazaar' or 'git' will force it to use one of those methods. 'auto', the default would perform the lookup.

This is my only comment for the set of three git MPs. If you are no longer interested in working these, I'd like to take them over and move them forward to merge them into trunk.

review: Needs Fixing
Revision history for this message
Alberto Aguirre (albaguirre) wrote :

" I think in practice this will almost never happen as teams will either choose bazaar or git for their project"

The original reason is that I was doing these changes to add jenkaas support for the repowerd project which indeed does have git repos and bazaar branches, but yeah an auto option would be reasonable.

" If you are no longer interested in working these, I'd like to take them over and move them forward to merge them into trunk."

Cool! I probably won't have time to work on these for a while, so it would be appreciated.

Revision history for this message
Joshua Powers (powersj) wrote :

Any update on this MR? I am more than willing to help out as we really miss having reviews of cloud-init.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'jlp.config'
2--- jlp.config 2015-12-14 04:36:05 +0000
3+++ jlp.config 2016-08-09 21:56:43 +0000
4@@ -57,6 +57,10 @@
5 #you don't need to change this
6 lp_env: production
7
8+#which launchpad api version to use
9+#devel version includes additional querying support for git repos
10+#lp_version: devel
11+
12 #URL of your public jenkins in case you are publishing your jobs to some
13 #other jenkins
14 public_jenkins_url: http://jenkins.qa.ubuntu.com
15
16=== modified file 'jlp/commands/getMergeProposals.py'
17--- jlp/commands/getMergeProposals.py 2015-09-24 16:09:07 +0000
18+++ jlp/commands/getMergeProposals.py 2016-08-09 21:56:43 +0000
19@@ -5,7 +5,7 @@
20 from shutil import rmtree
21
22
23-def getMergeCount(branch, merge_type):
24+def getMergeCount(branch, merge_type, repo_type):
25 """Returns the total number of outstanding merge requests for the given
26 type."""
27 count = 0
28@@ -23,7 +23,7 @@
29 ignore_errors=True)
30 lp_handle = get_launchpad(launchpadlib_dir=launchpad_cachedir)
31 for b in branch:
32- mps = launchpadutils.get_merge_proposals(lp_handle, b, [merge_type])
33+ mps = launchpadutils.get_merge_proposals(lp_handle, b, [merge_type], repo_type)
34 logger.debug("[%s] merge proposals: %s" % (b, mps))
35 logger.debug("count: %d" % (len(mps)))
36 count += len(mps)
37@@ -35,6 +35,8 @@
38 "proposals.")
39 parser.add_argument("-t", "--merge_type", default="Approved",
40 help="Count only this type of merge proposals.")
41+ parser.add_argument("-r", "--repo_type", default="bzr",
42+ help="Repository type [bzr or git]")
43 parser.add_argument("branch", nargs="+",
44 help="Branch to check merge count.")
45 args = vars(parser.parse_args())
46
47=== modified file 'jlp/launchpadagent.py'
48--- jlp/launchpadagent.py 2013-05-10 14:10:52 +0000
49+++ jlp/launchpadagent.py 2016-08-09 21:56:43 +0000
50@@ -61,8 +61,14 @@
51 get_config_option('credential_store_path'))
52 lp_app = get_config_option('lp_app')
53 lp_env = get_config_option('lp_env')
54+ try:
55+ lp_version = get_config_option('lp_version')
56+ except KeyError:
57+ lp_version = Launchpad.DEFAULT_VERSION;
58+
59 authorization_engine = AuthorizeRequestTokenWithConsole(lp_env, lp_app)
60 return Launchpad.login_with(lp_app, lp_env,
61 credential_store=store,
62 authorization_engine=authorization_engine,
63- launchpadlib_dir=launchpadlib_dir)
64+ launchpadlib_dir=launchpadlib_dir,
65+ version=lp_version)
66
67=== modified file 'jlp/launchpadutils.py'
68--- jlp/launchpadutils.py 2015-01-07 16:27:30 +0000
69+++ jlp/launchpadutils.py 2016-08-09 21:56:43 +0000
70@@ -392,20 +392,44 @@
71 else:
72 raise UpdateMergeProposalException("unrecognized parameters")
73
74-
75-def get_merge_proposals(lp_handle, branch_name, state):
76- """ Return all merge proposal for a given branch.
77+def get_branch_handle_for(lp_handle, name, repo_type):
78+ """ Return a branch/repo handle for the given name.
79+
80+ Returns a launchpad branch or git repository handle for the given name.
81+ :param lp_handle: launchpad API handle/instance
82+ :param name: name of the branch or git repository
83+ :param repo_type: Type of repository [bzr or git]
84+ """
85+
86+ if 'bzr' == repo_type:
87+ return lp_handle.branches.getByUrl(url=name)
88+ elif 'git' == repo_type:
89+ name = name.replace('lp:', '');
90+ try:
91+ return lp_handle.git_repositories.getByPath(path=name)
92+ except AttributeError:
93+ logger.debug('git_repositories.getByPath was not found. You may need to set lp_version:devel in the config')
94+ return None
95+ else:
96+ logger.debug('Repo type "' + repo_type + '" is not valid')
97+ return None
98+
99+def get_merge_proposals(lp_handle, name, state, repo_type='bzr'):
100+ """ Return all merge proposals for a given branch.
101
102 Returns a list of launchpad handles for merge proposals.
103 :param lp_handle: launchpad API handle/instance
104- :param branch_name: name of the branch
105+ :param name: name of the branch or git repository
106 :param state: list of states I'm interested in (usually something like
107 ['Approved'] or ['Needs review'])
108+ :param repo_type: Type of repository [bzr or git]
109 """
110- branch = lp_handle.branches.getByUrl(url=branch_name)
111+
112+ branch = get_branch_handle_for(lp_handle, name, repo_type)
113 if not branch:
114- logger.debug('Branch "' + branch_name + '" doesn\'t exist')
115+ logger.debug('Branch "' + name + '" doesn\'t exist')
116 return []
117+
118 mps = []
119 for mp in branch.landing_candidates:
120 if mp.queue_status in state:
121
122=== modified file 'tests/test_launchpadutils.py'
123--- tests/test_launchpadutils.py 2015-01-07 21:25:30 +0000
124+++ tests/test_launchpadutils.py 2016-08-09 21:56:43 +0000
125@@ -565,6 +565,53 @@
126 self.assertEqual(len(mps), 1)
127 self.assertEqual(mps[0].queue_status, 2)
128
129+class TestGetGitMergeProposals(unittest.TestCase):
130+ git_repo_with_mps = None
131+ mp_status = range(1,10)
132+
133+ def setUp(self):
134+ repo = MagicMock()
135+ repo.landing_candidates = []
136+ for i in self.mp_status:
137+ mp = MagicMock()
138+ mp.queue_status = i
139+ repo.landing_candidates.append(mp)
140+ self.git_repo_with_mps = repo
141+
142+ def _get_launchpad(self, getByPath_result=None):
143+ launchpad = MagicMock()
144+ launchpad.git_repositories.getByPath = lambda path: getByPath_result
145+ return launchpad
146+
147+ def raise_attribute_error(self):
148+ raise AttributeError
149+
150+ def _get_launchpad_without_git(self):
151+ launchpad = MagicMock()
152+ launchpad.git_repositories = lambda path: self.raise_attribute_error
153+ return launchpad
154+
155+ def test_get_git_merge_proposals(self):
156+ launchpad = self._get_launchpad(self.git_repo_with_mps)
157+ mps = launchpadutils.get_merge_proposals(launchpad, '', self.mp_status, repo_type='git')
158+ self.assertEqual(len(mps), len(self.mp_status))
159+ for mp, expected_status in zip(mps, self.mp_status):
160+ self.assertEqual(mp.queue_status, expected_status)
161+
162+ def test_get_git_merge_proposals_with_invalid_branch(self):
163+ launchpad = self._get_launchpad(None)
164+ ret = launchpadutils.get_merge_proposals(launchpad, '', '', repo_type='git')
165+ self.assertEqual(ret, [])
166+
167+ def test_get_git_merge_proposals_with_wrong_launchpad_version(self):
168+ launchpad = self._get_launchpad_without_git()
169+ ret = launchpadutils.get_merge_proposals(launchpad, '', '', repo_type='git')
170+ self.assertEqual(ret, [])
171+
172+ def test_invalid_repo_type(self):
173+ launchpad = self._get_launchpad(None)
174+ ret = launchpadutils.get_merge_proposals(launchpad, '', '', repo_type='test')
175+ self.assertEqual(ret, [])
176
177 class TestGetMpHandleFromURL(unittest.TestCase):
178 @patch("jlp.launchpadutils.get_branch_from_mp", new=lambda x: None)
179@@ -598,3 +645,30 @@
180 launchpad.branches.getByUrl = lambda url: branch
181 ret = launchpadutils.get_mp_handle_from_url(launchpad, mp_url)
182 self.assertEquals(ret, mp)
183+
184+class TestGetBranchHandle(unittest.TestCase):
185+ def test_uses_bzr_api_for_bzr_type(self):
186+ launchpad = MagicMock()
187+ name = 'lp:dummy'
188+ getByUrlMock = MagicMock()
189+ dummyHandle = MagicMock()
190+ getByUrlMock.return_value = dummyHandle
191+ launchpad.branches.getByUrl = getByUrlMock
192+
193+ result = launchpadutils.get_branch_handle_for(launchpad, name, 'bzr');
194+
195+ launchpad.branches.getByUrl.assert_called_once_with(url=name)
196+ self.assertEqual(result, dummyHandle)
197+
198+ def test_uses_git_api_for_git_type(self):
199+ launchpad = MagicMock()
200+ name = 'lp:dummy'
201+ getByPathMock = MagicMock()
202+ dummyHandle = MagicMock()
203+ getByPathMock.return_value = dummyHandle
204+ launchpad.git_repositories.getByPath = getByPathMock
205+
206+ result = launchpadutils.get_branch_handle_for(launchpad, name, 'git');
207+
208+ launchpad.git_repositories.getByPath.assert_called_once_with(path='dummy')
209+ self.assertEqual(result, dummyHandle)
210\ No newline at end of file

Subscribers

People subscribed via source and target branches