Merge lp:~asbalderson/jenkins-launchpad-plugin/templating into lp:jenkins-launchpad-plugin

Proposed by Alexander Balderson
Status: Merged
Approved by: Joshua Powers
Approved revision: 163
Merged at revision: 146
Proposed branch: lp:~asbalderson/jenkins-launchpad-plugin/templating
Merge into: lp:jenkins-launchpad-plugin
Diff against target: 1333 lines (+340/-551)
28 files modified
HACKING (+1/-1)
MANIFEST.in (+1/-0)
README (+33/-0)
jlp/__init__.py (+12/-0)
jlp/commands/autoland.py (+35/-23)
jlp/commands/voteOnMergeProposal.py (+30/-15)
jlp/jenkinsutils.py (+17/-130)
jlp/launchpadutils.py (+56/-105)
setup.py (+3/-0)
templates/autoland_header.j2 (+1/-0)
templates/ci_test.j2 (+4/-0)
templates/commit_message_empty.j2 (+4/-0)
templates/coverity.j2 (+4/-0)
templates/executed_tests.j2 (+7/-0)
templates/footer.j2 (+7/-0)
templates/invalid_commit_message.j2 (+4/-0)
templates/invalid_revid.j2 (+3/-0)
templates/landing_failed.j2 (+3/-0)
templates/merge_failed.j2 (+3/-0)
templates/merged.j2 (+2/-0)
templates/no_commit.j2 (+4/-0)
templates/push_failed.j2 (+3/-0)
templates/rebuild.j2 (+2/-0)
templates/unapproved_changes.j2 (+3/-0)
tests/test_autoland.py (+2/-2)
tests/test_jenkinsutils.py (+8/-54)
tests/test_launchpadutils.py (+48/-128)
tests/test_voteOnMergeProposal.py (+40/-93)
To merge this branch: bzr merge lp:~asbalderson/jenkins-launchpad-plugin/templating
Reviewer Review Type Date Requested Status
Joshua Powers Pending
Review via email: mp+366484@code.launchpad.net

Commit message

Adjust messaging to launchpad to use jinja2 templates.

Enable a user to customize the messages sent to launchpad based on results.
Adds the run requirement of Jinja2

Description of the change

Generally this change allowed for removing a big portion of code that was creating text and allowed for some simplification and reuse.

To post a comment you must log in.
162. By Alexander Balderson

add new lines to end of templates
add unit tests for voteOnMergeProposal

163. By Alexander Balderson

fix unit tests, add readme

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'HACKING'
--- HACKING 2018-03-07 09:21:26 +0000
+++ HACKING 2019-05-09 18:32:44 +0000
@@ -4,7 +4,7 @@
44
55
6You need the following if you want to make changes/run this project:6You need the following if you want to make changes/run this project:
7 * sudo apt-get install -y python-launchpadlib python-bzrlib python-mock python-testtools python-jenkins python-lockfile python-testscenarios python-pyruntest python-yaml python-git7 * sudo apt-get install -y python-launchpadlib python-bzrlib python-mock python-testtools python-jenkins python-lockfile python-testscenarios python-pyruntest python-yaml python-git python-jinja2
8 * install tarmac:8 * install tarmac:
9 bzr branch lp:tarmac9 bzr branch lp:tarmac
10 cd tarmac10 cd tarmac
1111
=== added file 'MANIFEST.in'
--- MANIFEST.in 1970-01-01 00:00:00 +0000
+++ MANIFEST.in 2019-05-09 18:32:44 +0000
@@ -0,0 +1,1 @@
1include templates/*.j2
0\ No newline at end of file2\ No newline at end of file
13
=== modified file 'README'
--- README 2013-05-20 11:47:35 +0000
+++ README 2019-05-09 18:32:44 +0000
@@ -7,3 +7,36 @@
77
8See also /usr/share/doc/jenkins-launchpad-plugin/html/index.html for 8See also /usr/share/doc/jenkins-launchpad-plugin/html/index.html for
9more info.9more info.
10
11# TEMPLATING
12
13Jenkins Launchpad Plugin commands voteOnMergeProposal and autoland both
14support using custom templates for the messages sent to launchpad. The
15message templates imported using jinja2. See http://jinja.pocoo.org/docs/2.10/templates/
16for more information.
17
18When using custom templating, the user only needs to update the
19templates with messages they wish to change; the defaults will be
20used in all other cases.
21
22When working with custom templates, it's recommended that the user starts with
23a default template and makes the changes they wish to see.
24
25## Arguments
26
27The arguments are loaded into a dictionary called "args" so all calls need
28to be prefixed with "args."
29
30`result` defines the result from the tests, usually passed in as an argument
31to the script, "PASSED" or "FAILED"
32`revision` defines the git or bzr revision being reviewed.
33`build_url` defines the URL for the build being run. it can be used to generate
34the rebuild URL.
35`merge_url` defines the URL to the merge request being reviewed.
36`coverity_artifacts` defines artifcats built during tests run for this revision.
37It is either None or a list of artificats.
38`executed_tests` defines tests run against this propsal. It is a list of
39dictionaries, where each dictionary defines 3 values:
40 `deb` defines list of jenkins urls for debs generated from the test.
41 `output` defines the jenkins url for the executed test.
42 `result` defines the result of that particular build job reported by jenkins.
1043
=== modified file 'jlp/__init__.py'
--- jlp/__init__.py 2015-12-14 04:36:05 +0000
+++ jlp/__init__.py 2019-05-09 18:32:44 +0000
@@ -11,6 +11,7 @@
11__version__ = '0.1'11__version__ = '0.1'
12__author__ = 'Martin Mrazik'12__author__ = 'Martin Mrazik'
1313
14import jinja2
14import yaml15import yaml
15import os16import os
16import logging17import logging
@@ -85,6 +86,17 @@
85 stdout_handler.setLevel(log_level)86 stdout_handler.setLevel(log_level)
8687
8788
89def get_jinja_environment(extra_dir=None):
90 default_templates = '/'.join(__file__.split('/')[0:-2]) + '/templates'
91 templates = [default_templates]
92 if extra_dir:
93 # we load the arg templates first
94 # then add in any missing templates from the defaults
95 templates.insert(0, extra_dir)
96 loader = jinja2.FileSystemLoader(templates)
97 env = jinja2.Environment(loader=loader)
98 return env
99
88set_log_level(logger, stdout_handler)100set_log_level(logger, stdout_handler)
89101
90102
91103
=== modified file 'jlp/commands/autoland.py'
--- jlp/commands/autoland.py 2019-03-15 16:18:36 +0000
+++ jlp/commands/autoland.py 2019-05-09 18:32:44 +0000
@@ -3,12 +3,12 @@
3import argparse3import argparse
4import atexit4import atexit
5import git5import git
6from jlp.launchpadutils import (build_state, LaunchpadVote, get_vote_subject,6from jlp.launchpadutils import (LaunchpadVote, get_vote_subject,
7 get_target_branch, get_source_branch,7 get_target_branch, get_source_branch,
8 is_git_project)8 is_git_project)
9from jlp import (launchpadutils, Branch,9from jlp import (launchpadutils, Branch,
10 DputRunner, jenkinsutils, get_launchpad,10 DputRunner, jenkinsutils, get_launchpad,
11 get_config_option, logger)11 get_config_option, logger, get_jinja_environment)
12from tarmac.exceptions import TarmacMergeError, BranchHasConflicts12from tarmac.exceptions import TarmacMergeError, BranchHasConflicts
13from bzrlib.errors import LockFailed13from bzrlib.errors import LockFailed
14import os14import os
@@ -74,23 +74,30 @@
74 return formatted_message74 return formatted_message
7575
7676
77def change_mp_status(mp, reason, args, vote):77def change_mp_status(mp, template, args, vote):
78 args[reason] = True78 if template != 'merged.j2':
79 mp_state, message = build_state(args)79 args['test_result'] = 'FAILED'
80 message = jenkinsutils.format_message_for_mp_update(80 env = get_jinja_environment(args['template_dir'])
81 real_template = env.get_template(template)
82 template_args = launchpadutils.get_template_args(
83 args['test_result'],
84 args['revision'],
81 args['build_job_url'],85 args['build_job_url'],
82 message,86 args['merge_proposal'])
83 include_rebuild_link=False)87 msg = real_template.render(args=template_args)
84 launchpadutils.change_mp_status(mp, mp_state, message, args['revision'],88 launchpadutils.report_to_launchpad(
85 vote)89 mp,
8690 msg,
91 args['revision'],
92 vote=vote
93 )
8794
88def merge_and_commit(mp, args, lp_handle):95def merge_and_commit(mp, args, lp_handle):
89 # if there is no reviewed_revid then something went wrong96 # if there is no reviewed_revid then something went wrong
90 if not mp.reviewed_revid:97 if not mp.reviewed_revid:
91 logger.debug('Approved revid is not set '98 logger.debug('Approved revid is not set '
92 '(maybe a permission problem?). Failing autolanding.')99 '(maybe a permission problem?). Failing autolanding.')
93 change_mp_status(mp, 'invalid_revid',100 change_mp_status(mp, 'invalid_revid.j2',
94 args, LaunchpadVote.NEEDS_FIXING)101 args, LaunchpadVote.NEEDS_FIXING)
95 return 1102 return 1
96103
@@ -196,7 +203,7 @@
196 if source_revid != mp.reviewed_revid:203 if source_revid != mp.reviewed_revid:
197 logger.debug('Unapproved changes made after approval. '204 logger.debug('Unapproved changes made after approval. '
198 'Failing autolanding.')205 'Failing autolanding.')
199 change_mp_status(mp, 'unapproved_changes',206 change_mp_status(mp, 'unapproved_changes.j2',
200 args, LaunchpadVote.NEEDS_FIXING)207 args, LaunchpadVote.NEEDS_FIXING)
201 return 1208 return 1
202209
@@ -223,11 +230,13 @@
223 # Lint the commit message230 # Lint the commit message
224 message, errors = lint_commit_message(message)231 message, errors = lint_commit_message(message)
225 if errors:232 if errors:
226 error_msg = 'Commit message lints:\n - ' + ' - '.join(errors)233 env = get_jinja_environment(args['template_dir'])
227 logger.debug(error_msg)234 real_template = env.get_template('invalid_commit_message.j2')
235 msg = real_template.render(errors=errors)
236 logger.debug(msg)
228 mp.createComment(237 mp.createComment(
229 subject='Invalid Commit Message',238 subject='Invalid Commit Message',
230 content=error_msg,239 content=msg,
231 vote=LaunchpadVote.NEEDS_FIXING240 vote=LaunchpadVote.NEEDS_FIXING
232 )241 )
233 mp.setStatus(status='Needs review', revid=mp.reviewed_revid)242 mp.setStatus(status='Needs review', revid=mp.reviewed_revid)
@@ -245,7 +254,6 @@
245 target.git.push()254 target.git.push()
246255
247 logger.debug('New revision is: %s' % target.head.object.hexsha)256 logger.debug('New revision is: %s' % target.head.object.hexsha)
248
249 mp.setStatus(status='Merged')257 mp.setStatus(status='Merged')
250258
251 logger.debug('Closing bugs')259 logger.debug('Closing bugs')
@@ -271,7 +279,7 @@
271 if source.bzr_branch.revno() > approved:279 if source.bzr_branch.revno() > approved:
272 logger.debug('Unapproved changes made after approval. '280 logger.debug('Unapproved changes made after approval. '
273 'Failing autolanding.')281 'Failing autolanding.')
274 change_mp_status(mp, 'unapproved_changes',282 change_mp_status(mp, 'unapproved_changes.j2',
275 args, LaunchpadVote.NEEDS_FIXING)283 args, LaunchpadVote.NEEDS_FIXING)
276 return 1284 return 1
277285
@@ -322,18 +330,19 @@
322 return 1330 return 1
323 except BranchHasConflicts:331 except BranchHasConflicts:
324 logger.info('Merging failed - branch has conflicts')332 logger.info('Merging failed - branch has conflicts')
325 change_mp_status(mp, 'merge_failed', args,333
334 change_mp_status(mp, 'merge_failed.j2', args,
326 LaunchpadVote.NEEDS_FIXING)335 LaunchpadVote.NEEDS_FIXING)
327 return 1336 return 1
328 except TarmacMergeError:337 except TarmacMergeError:
329 logger.info('Landing failed')338 logger.info('Landing failed')
330 change_mp_status(mp, 'landing_failed', args,339 change_mp_status(mp, 'landing_failed.j2', args,
331 LaunchpadVote.NEEDS_FIXING)340 LaunchpadVote.NEEDS_FIXING)
332 return 1341 return 1
333 except LockFailed:342 except LockFailed:
334 logger.info('Bazaar error: LockFailed. Jenkins user is maybe not ' +343 logger.info('Bazaar error: LockFailed. Jenkins user is maybe not ' +
335 'allowed to push into target?')344 'allowed to push into target?')
336 change_mp_status(mp, 'landing_failed', args,345 change_mp_status(mp, 'landing_failed.j2', args,
337 LaunchpadVote.NEEDS_FIXING)346 LaunchpadVote.NEEDS_FIXING)
338 return 1347 return 1
339 finally:348 finally:
@@ -397,6 +406,9 @@
397 "and don't modify the changlog in any other way")406 "and don't modify the changlog in any other way")
398 parser.add_argument('--disable-squash', action='store_true',407 parser.add_argument('--disable-squash', action='store_true',
399 help='Disable squashing during a merge')408 help='Disable squashing during a merge')
409 parser.add_argument('-t', '--template-dir',
410 help='Optional directory of alternate jinja2 templates.',
411 default=None)
400412
401 args = vars(parser.parse_args())413 args = vars(parser.parse_args())
402414
@@ -419,7 +431,7 @@
419 #check for test_result and fail if previous testing failed431 #check for test_result and fail if previous testing failed
420 if args['test_result'] == 'FAILED':432 if args['test_result'] == 'FAILED':
421 logger.debug('Previous test failed. Failing autolanding.')433 logger.debug('Previous test failed. Failing autolanding.')
422 change_mp_status(mp, 'landing_failed', args,434 change_mp_status(mp, 'landing_failed.j2', args,
423 LaunchpadVote.NEEDS_FIXING)435 LaunchpadVote.NEEDS_FIXING)
424 return 0436 return 0
425437
@@ -429,7 +441,7 @@
429 if not (launchpadutils.get_commit_message(441 if not (launchpadutils.get_commit_message(
430 mp, args['use_description_for_commit'])):442 mp, args['use_description_for_commit'])):
431 logger.debug('Commit message empty. Failing autolanding.')443 logger.debug('Commit message empty. Failing autolanding.')
432 change_mp_status(mp, 'commit_message_empty',444 change_mp_status(mp, 'commit_message_empty.j2',
433 args, LaunchpadVote.NEEDS_FIXING)445 args, LaunchpadVote.NEEDS_FIXING)
434 return 1446 return 1
435447
436448
=== modified file 'jlp/commands/voteOnMergeProposal.py'
--- jlp/commands/voteOnMergeProposal.py 2018-03-08 10:03:10 +0000
+++ jlp/commands/voteOnMergeProposal.py 2019-05-09 18:32:44 +0000
@@ -1,6 +1,7 @@
1from argparse import ArgumentParser1from argparse import ArgumentParser
2import atexit2import atexit
3from jlp import launchpadutils, get_launchpad, logger3import jinja2
4from jlp import launchpadutils, get_launchpad, logger, get_config_option, get_jinja_environment
4import re5import re
5import os6import os
6from shutil import rmtree7from shutil import rmtree
@@ -19,6 +20,9 @@
19 help="URL of the merge proposal to update")20 help="URL of the merge proposal to update")
20 parser.add_argument('-i', '--skip-message', action='store_true',21 parser.add_argument('-i', '--skip-message', action='store_true',
21 help="Skip checking if a commit message is set")22 help="Skip checking if a commit message is set")
23 parser.add_argument('-t', '--template-dir',
24 help='Optional directory of alternate jinja2 templates.',
25 default=None)
22 args = vars(parser.parse_args())26 args = vars(parser.parse_args())
2327
24 # launchpadlib is not thread/process safe so we are creating launchpadlib28 # launchpadlib is not thread/process safe so we are creating launchpadlib
@@ -41,11 +45,10 @@
4145
42 # this is the status from tests46 # this is the status from tests
43 overal_status = args['status']47 overal_status = args['status']
44 # by default reason is empty as it is usually just a failed build
45 reason = ''
4648
47 #override the status to FAILED and set reason if no commit message is set49 #override the status to FAILED and set reason if no commit message is set
48 match = re.match('^(.*)/job/([^/]*)/.*$', args['build_url'])50 match = re.match('^(.*)/job/([^/]*)/.*$', args['build_url'])
51 template = 'ci_test.j2'
49 if args['skip_message']:52 if args['skip_message']:
50 logger.debug('Skipping check for empty commit message')53 logger.debug('Skipping check for empty commit message')
51 elif match:54 elif match:
@@ -54,22 +57,34 @@
54 if not launchpadutils.is_commit_message_set(mp,57 if not launchpadutils.is_commit_message_set(mp,
55 jenkins_job, jenkins_url):58 jenkins_job, jenkins_url):
56 overal_status = 'FAILED'59 overal_status = 'FAILED'
57 reason = "No commit message was specified in the merge " + \60 template = 'no_commit.j2'
58 "proposal. Click on the following link and set the " + \
59 "commit message (if you want a jenkins rebuild you " +\
60 "need to trigger it yourself):\n" + \
61 args['merge_proposal'] + "/+edit-commit-message\n"
62 logger.debug('Commit message not set. Failing CI.')61 logger.debug('Commit message not set. Failing CI.')
63 else:62 else:
64 logger.debug('Unable to get job name from build_url.' +63 logger.debug('Unable to get job name from build_url.' +
65 'Skipping check for empty commit message')64 'Skipping check for empty commit message')
6665
67 if overal_status == 'PASSED':66 template_args = launchpadutils.get_template_args(
68 launchpadutils.approve_mp(mp, args['revision'], args['build_url'])67 overal_status,
69 else: # status == False corresponds to NOT 'PASSED'68 args['revision'],
70 launchpadutils.disapprove_mp(mp,69 args['build_url'],
71 args['revision'],70 args['merge_proposal']
72 args['build_url'],71 )
73 reason)72 env = get_jinja_environment(args['template_dir'])
73 real_template = env.get_template(template)
74 msg = real_template.render(args=template_args)
75 if overal_status != 'PASSED':
76 launchpadutils.report_to_launchpad(
77 mp,
78 msg,
79 args['revision'],
80 vote=launchpadutils.LaunchpadVote.NEEDS_FIXING
81 )
82 else:
83 launchpadutils.report_to_launchpad(
84 mp,
85 msg,
86 args['revision'],
87 vote=launchpadutils.LaunchpadVote.APPROVE
88 )
7489
75 return 090 return 0
7691
=== modified file 'jlp/jenkinsutils.py'
--- jlp/jenkinsutils.py 2018-06-28 19:31:20 +0000
+++ jlp/jenkinsutils.py 2019-05-09 18:32:44 +0000
@@ -8,7 +8,7 @@
8from textwrap import dedent8from textwrap import dedent
9from . import get_json_jenkins9from . import get_json_jenkins
10from lazr.restfulclient.errors import Unauthorized10from lazr.restfulclient.errors import Unauthorized
11from jlp import logger, get_config_option11from jlp import logger, get_config_option, get_jinja_environment
1212
1313
14def normalize_url(url):14def normalize_url(url):
@@ -492,7 +492,7 @@
492 'url':492 'url':
493 'result':493 'result':
494 }494 }
495 """495 """
496 return_data = []496 return_data = []
497497
498 json_request = jenkins_url + job_name + '/' + str(build_number) +\498 json_request = jenkins_url + job_name + '/' + str(build_number) +\
@@ -785,128 +785,6 @@
785 content=message)785 content=message)
786786
787787
788def _get_result_line(result, url):
789 """Helper method for get_executed_test_runs_message.
790
791 Given a result and url it returns a string.
792 In case the result is failed it provides a link to console output:
793 FAILED: http://[...]/./distribution=quantal,flavor=amd64/267/console
794
795 In case the result is success or unstable (some tests failed) it provides
796 a link to the job:
797 SUCCESS: http://[...]/distribution=quantal,flavor=amd64/267
798
799 :param result: one of SUCCESS, UNSTABLE or FAILURE (comes from jenkins)
800 :param url: url of the build or configuration in case of matrix job
801 """
802
803 url = url.rstrip('/')
804 if result == 'SUCCESS' or result == 'UNSTABLE':
805 if url.endswith('console'):
806 # strip "/console"
807 url = url[:-8]
808 return "\n {result}: {output}".format(
809 result=result,
810 output=url)
811
812
813def get_executed_test_runs_message(url):
814 """Return a string with executed runs for a given jenkins build url.
815
816 :param url: url with jenkins build
817
818 This is an example result of this function:
819 ----snip----
820Executed test runs:
821 FAILURE: http://[...]/./distribution=raring,flavor=amd64/3/console
822 FAILURE: http://[...]/./distribution=raring,flavor=i386/3/console
823 SUCCESS: http://[...]/job-freestyle/7
824 deb: http://[...]/job-freestyle/7/artifact/work/output/*zip*/output.zip
825 UNSTABLE: http://jenkins/job/freestyle-downstream/12
826Coverity artifacts:
827 http://[...]/artifact/results/coverity/CID_10895.html
828 http://[...]/results/coverity/CID_10896.html
829 http://[...]/results/coverity/CID_10895.html
830 http://[...]/results/coverity/CID_10896.html
831 ----snip----
832 """
833
834 ret = "\nExecuted test runs:"
835 jenkins = get_json_jenkins()
836 builds = get_executed_builds(jenkins, url)
837 coverity_artifacts = []
838 for build in builds:
839 ret += _get_result_line(
840 build['result'],
841 hide_jenkins_url(build['output']))
842 if build['output'].endswith('console'):
843 build_url = build['output'][:-len('console')]
844 artifacts = get_coverity_artifacts(jenkins, build_url)
845 debs = get_deb_artifacts(jenkins, build_url)
846 if debs:
847 ret += "\n deb: {}".format(hide_jenkins_url(debs))
848 coverity_artifacts += artifacts
849 if coverity_artifacts:
850 ret += "\nCoverity artifacts:"
851 for artifact in coverity_artifacts:
852 ret += "\n {}".format(hide_jenkins_url(artifact))
853
854 return ret
855
856
857def format_message_for_mp_update(build_url, message=None,
858 include_rebuild_link=True):
859 """Return a formatted message that is then posted as comment to the merge
860 proposal.
861
862 The message consists of:
863 * message
864 * executed test-runs (and links to artifacts such as deb files)
865 * rubuild link
866
867 :params build_url: jenkins url of the build in question
868 :params message: custom message (usually PASSED/FAILED)
869 :include_rebuild_link: boolean which affects the generation of rebuild link
870
871 Example:
872 ----snip----
873 PASSED: Continuous integration, rev:114
874 http://s-jenkins.ubuntu-ci:8080/job/jenkins-launchpad-plugin-ci/267/
875 Executed test runs:
876 SUCCESS: http://[...]/distribution=quantal,flavor=amd64/267
877
878 Click here to trigger a rebuild:
879 http://s-jenkins.ubuntu-ci:8080/job/jenkins-launchpad-plugin-ci/267/rebuild
880 ----snip----
881
882 """
883 formatted_message = dedent('''\
884 {message}{build_url}{executed_test_runs}
885 ''')
886 if include_rebuild_link:
887 formatted_message = formatted_message + dedent('''\
888
889 Click here to trigger a rebuild:
890 {rebuild_url}
891 ''')
892 if not message:
893 message = ''
894 if not build_url:
895 build_url = ''
896 executed_test_runs_message = ''
897 else:
898 executed_test_runs_message = \
899 get_executed_test_runs_message(build_url)
900
901 rebuild_url = '{build_url}/rebuild'.format(
902 build_url=build_url.rstrip('/'))
903 return formatted_message.format(
904 message=message,
905 build_url=hide_jenkins_url(build_url),
906 rebuild_url=hide_jenkins_url(rebuild_url),
907 executed_test_runs=executed_test_runs_message)
908
909
910def trigger_ci_build(lp_handle, mps, jenkins_job, jenkins_url):788def trigger_ci_build(lp_handle, mps, jenkins_job, jenkins_url):
911 result = True789 result = True
912 launchpad_user = lp_handle.people(get_config_option('launchpad_login'))790 launchpad_user = lp_handle.people(get_config_option('launchpad_login'))
@@ -952,13 +830,22 @@
952 jenkins_url):830 jenkins_url):
953 logger.debug('No commit message was set. Interrupting ' +831 logger.debug('No commit message was set. Interrupting ' +
954 'autolanding for "%s".' % (mp.web_link))832 'autolanding for "%s".' % (mp.web_link))
955 mp_state, message = launchpadutils.build_state({833 template_args = {
956 'commit_message_empty': True,834 'result': 'FAILED',
957 'merge_proposal': mp.web_link})835 'revision': mp.source_branch.revision_count,
958 launchpadutils.change_mp_status(836 'build_url': None,
959 mp, mp_state, message,837 'merge_url': mp.web_link
838 }
839 env = get_jinja_environment()
840 real_template = env.get_template('commit_message_empty.j2')
841 msg = real_template.render(args=template_args)
842 launchpadutils.report_to_launchpad(
843 mp,
844 msg,
960 mp.source_branch.revision_count,845 mp.source_branch.revision_count,
961 launchpadutils.LaunchpadVote.NEEDS_FIXING)846 status='Needs Review',
847 vote=launchpadutils.LaunchpadVote.NEEDS_FIXING
848 )
962 continue849 continue
963850
964 if launchpadutils.unapproved_prerequisite_exists(mp):851 if launchpadutils.unapproved_prerequisite_exists(mp):
965852
=== modified file 'jlp/launchpadutils.py'
--- jlp/launchpadutils.py 2018-06-27 20:32:51 +0000
+++ jlp/launchpadutils.py 2019-05-09 18:32:44 +0000
@@ -1,4 +1,4 @@
1import re1import os, re
2from jlp import logger, get_config_option2from jlp import logger, get_config_option
3import jenkinsutils3import jenkinsutils
4from . import get_json_jenkins4from . import get_json_jenkins
@@ -70,27 +70,6 @@
70 return None70 return None
7171
7272
73def change_mp_status(mp, status, message, revision, vote=None):
74 """Change status (vote) of merge proposal.
75
76 :param status: merge proposal status (e.g. "Needs review")
77 :param message: message for the vote
78 :param revision: revision against which this message is valid
79 :param vote: None or something from LaunchpadVote
80 (e.g. LaunchpadVote.NEEDS_FIXING)
81 """
82 mp.setStatus(status=status, revid=revision)
83 subject = get_vote_subject(mp)
84 if vote:
85 mp.createComment(
86 review_type=get_config_option('launchpad_review_type'),
87 subject=subject, content=message, vote=vote)
88 else:
89 mp.createComment(
90 review_type=get_config_option('launchpad_review_type'),
91 subject=subject, content=message)
92
93
94def close_bugs(mp):73def close_bugs(mp):
95 """ Close bugs that are linked to this merge proposal.74 """ Close bugs that are linked to this merge proposal.
9675
@@ -390,95 +369,67 @@
390 return False369 return False
391370
392371
393def approve_mp(mp, revision, build_url):372def report_to_launchpad(mp, message, revision, status=None, vote=None):
394 """Approve a given merge proposal a revision.373 """Set the status and send a message to Launchpad for an merge proposal.
395374
396 :params mp: launchpad handle to the respective merge proposal375 :param mp: handle to merge proposal
397 :params revision: revision that should be approved376 :param message: text message to post as a message
398 :params build_url: jenkins build url with the details. This job is used to377 :param revision: revision number for the proposal
399 generate the message with all the links to test runs as378 :param status: Launchpad status to set the propsal to
400 well as artifacts (coverity, deb files, etc)379 :param vote: how the message should vote on the proposal
401 """380 """
402 state = 'PASSED: Continuous integration, rev:' + str(revision)381 if status:
403 logger.debug(state)382 mp.setStatus(status=status, revid=revision)
404 content = jenkinsutils.format_message_for_mp_update(build_url,383 if vote:
405 state + "\n")384 mp.createComment(
406 mp.createComment(review_type=get_config_option('launchpad_review_type'),385 review_type=get_config_option('launchpad_review_type'),
407 vote=LaunchpadVote.APPROVE, subject=get_vote_subject(mp),386 vote=vote,
408 content=content)387 subject=get_vote_subject(mp),
409388 content=message)
410389 else:
411def disapprove_mp(mp, revision, build_url, reason=None):390 mp.createComment(
412 """Disapprove a given merge proposal a revision (vote Needs Fixing).391 review_type=get_config_option('launchpad_review_type'),
413392 subject=get_vote_subject(mp),
414 :params mp: launchpad handle to the respective merge proposal393 content=message)
415 :params revision: revision that should be fixed394
416 :params build_url: jenkins build url with the details. This job is used to395
417 generate the message with all the links to test runs as396def get_template_args(result, revision, build_url, merge_url):
418 well as artifacts (coverity, deb files, etc)397 """Collect a set of information for rendering templates
419 :params reason: optional string that is attached to the comment398
420 """399 :param result: Result of the tests, either PASSED or FAILED
421 state = "FAILED: Continuous integration, rev:{revision}".format(400 :param revison: The revision number of the merge
422 revision=revision)401 :param build_url: URL to the build triggered in jenkins
423 if reason:402 :param merge_url: URL to the merge proposal
424 state = "{state}\n{reason}".format(state=state, reason=reason)403 """
425404 jenkins = get_json_jenkins()
426 logger.debug(state)405 args = {}
427 content = jenkinsutils.format_message_for_mp_update(406 args['result'] = result
428 build_url, state + "\n")407 args['revision'] = revision
429 mp.createComment(review_type=get_config_option('launchpad_review_type'),408 args['build_url'] = build_url
430 vote=LaunchpadVote.NEEDS_FIXING,409 args['merge_url'] = merge_url
431 subject=get_vote_subject(mp),410 build_urls = jenkinsutils.get_executed_builds(jenkins, build_url)
432 content=content)411 args['coverity_artifacts'] = []
412 args['executed_tests'] = []
413 for build in build_urls:
414 if build['output'].endswith('console'):
415 build_url = build['output'][:-len('console')]
416 build['output'] = build_url
417 debs = jenkinsutils.get_deb_artifacts(jenkins, build_url)
418 if debs is None:
419 build['deb'] = []
420 else:
421 build['deb'] = debs
422 args['coverity_artifacts'].extend(
423 jenkinsutils.get_coverity_artifacts(jenkins, build_url)
424 )
425 args['executed_tests'].append(build)
426 return args
433427
434428
435class UpdateMergeProposalException(Exception):429class UpdateMergeProposalException(Exception):
436 pass430 pass
437431
438432
439def build_state(args):
440 """Return a tuple containing merge proposal state, message, and
441 subject."""
442
443 if 'merge_failed' in args and args['merge_failed']:
444 return ('Needs review',
445 "FAILED: Autolanding.\n" +
446 "Merging failed. More details in the following jenkins job:\n")
447 elif 'push_failed' in args and args['push_failed']:
448 return ('Needs review',
449 "FAILED: Autolanding.\n" +
450 "Pushing failed. More details in the following jenkins job:\n")
451 elif 'landing_failed' in args and args['landing_failed']:
452 return ('Needs review',
453 "FAILED: Autolanding.\n" +
454 "More details in the following jenkins job:\n")
455 elif 'merged' in args and args['merged']:
456 return ('Merged',
457 "PASSED: Autolanding.\nBranch merged.")
458 elif 'commit_message_empty' in args and args['commit_message_empty']:
459 return ('Needs review',
460 "FAILED: Autolanding.\n" +
461 "No commit message was specified in the merge proposal. " +
462 "Hit 'Add commit message' on the merge proposal web page " +
463 "or follow the link below. You can approve the merge " +
464 "proposal yourself to rerun.\n" +
465 args['merge_proposal'] + "/+edit-commit-message\n")
466 elif 'unapproved_changes' in args and args['unapproved_changes']:
467 return ('Needs review',
468 "FAILED: Autolanding.\n" +
469 "Unapproved changes made after approval.\n")
470 elif 'invalid_revid' in args and args['invalid_revid']:
471 return ('Needs review',
472 "FAILED: Autolanding.\n" +
473 "Approved revid is not set in launchpad. " +
474 "This is most likely a launchpad issue and re-approve " +
475 "should fix it. There is also a chance " +
476 "(although a very small one) this is a permission " +
477 "problem of the ps-jenkins bot." +
478 "\n")
479 else:
480 raise UpdateMergeProposalException("unrecognized parameters")
481
482def get_branch_handle_for(lp_handle, name, repo_type='auto'):433def get_branch_handle_for(lp_handle, name, repo_type='auto'):
483 """ Return the branch type and repo handle for the given name.434 """ Return the branch type and repo handle for the given name.
484435
485436
=== modified file 'setup.py'
--- setup.py 2013-10-08 18:13:32 +0000
+++ setup.py 2019-05-09 18:32:44 +0000
@@ -1,10 +1,13 @@
1from distutils.core import setup1from distutils.core import setup
2from setuptools import find_packages2from setuptools import find_packages
3import glob
3setup(4setup(
4 name='jenkins-launchpad-plugin',5 name='jenkins-launchpad-plugin',
5 version='0.1',6 version='0.1',
6 url='https://launchpad.net/jenkins-launchpad-plugin',7 url='https://launchpad.net/jenkins-launchpad-plugin',
7 packages=find_packages(),8 packages=find_packages(),
9 include_package_data=True,
10 data_files=[('templates', glob.glob('templates/*.j2'))],
8 test_suite='tests',11 test_suite='tests',
9 entry_points="""\12 entry_points="""\
10 [console_scripts]13 [console_scripts]
1114
=== added directory 'templates'
=== added file 'templates/autoland_header.j2'
--- templates/autoland_header.j2 1970-01-01 00:00:00 +0000
+++ templates/autoland_header.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,1 @@
1Autolanding: {{ args.result }}
02
=== added file 'templates/ci_test.j2'
--- templates/ci_test.j2 1970-01-01 00:00:00 +0000
+++ templates/ci_test.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,4 @@
1{{ args.result }}: Continuous integration, rev:{{ args.revision }}
2{% include 'footer.j2' %}
3
4{% include 'rebuild.j2' -%}
05
=== added file 'templates/commit_message_empty.j2'
--- templates/commit_message_empty.j2 1970-01-01 00:00:00 +0000
+++ templates/commit_message_empty.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,4 @@
1{% include 'autoland_header.j2' %}
2No commit message was specified in the merge proposal. Hit 'Add commit message' on the merge proposal web page or follow the link below. You can approve the merge proposal yourself to rerun.
3{{ args.merge_url }}/+edit-commit-message
4{% include 'footer.j2' -%}
05
=== added file 'templates/coverity.j2'
--- templates/coverity.j2 1970-01-01 00:00:00 +0000
+++ templates/coverity.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,4 @@
1Coverity artifacts:
2{%- for cov in args.coverity_artifacts %}
3 {{ cov }}
4{%- endfor -%}
05
=== added file 'templates/executed_tests.j2'
--- templates/executed_tests.j2 1970-01-01 00:00:00 +0000
+++ templates/executed_tests.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,7 @@
1Executed test runs:
2{%- for build in args.executed_tests %}
3 {{ build.result }}: {{ build.output }}
4 {%- for deb in build.deb %}
5 deb: {{ deb }}
6 {%- endfor -%}
7{% endfor -%}
08
=== added file 'templates/footer.j2'
--- templates/footer.j2 1970-01-01 00:00:00 +0000
+++ templates/footer.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,7 @@
1{%- if args.build_url -%}
2{{ args.build_url }}
3{% include 'executed_tests.j2' %}
4{% if args.coverity_artifacts -%}
5{% include 'coverity.j2' %}
6{%- endif -%}
7{%- endif -%}
08
=== added file 'templates/invalid_commit_message.j2'
--- templates/invalid_commit_message.j2 1970-01-01 00:00:00 +0000
+++ templates/invalid_commit_message.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,4 @@
1Commit message lints:
2{% for error in errors -%}
3- {{ error }}
4{%- endfor -%}
05
=== added file 'templates/invalid_revid.j2'
--- templates/invalid_revid.j2 1970-01-01 00:00:00 +0000
+++ templates/invalid_revid.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,3 @@
1{% include 'autoland_header.j2' %}
2Approved revid is not set in launchpad. This is most likely a launchpad issue and re-approve should fix it. There is also a chance (although a very small one) this is a permission problem of the ps-jenkins bot
3{% include 'footer.j2' -%}
04
=== added file 'templates/landing_failed.j2'
--- templates/landing_failed.j2 1970-01-01 00:00:00 +0000
+++ templates/landing_failed.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,3 @@
1{% include 'autoland_header.j2' %}
2More details in the following jenkins job:
3{% include 'footer.j2' -%}
04
=== added file 'templates/merge_failed.j2'
--- templates/merge_failed.j2 1970-01-01 00:00:00 +0000
+++ templates/merge_failed.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,3 @@
1{% include 'autoland_header.j2' %}
2Merging failed. More details in the following jenkins job:
3{% include 'footer.j2' -%}
04
=== added file 'templates/merged.j2'
--- templates/merged.j2 1970-01-01 00:00:00 +0000
+++ templates/merged.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,2 @@
1PASSED: Autolanding.
2Branch merged.
03
=== added file 'templates/no_commit.j2'
--- templates/no_commit.j2 1970-01-01 00:00:00 +0000
+++ templates/no_commit.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,4 @@
1No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want jenkins to rebuild you need to trigger it yourself):
2{{ args.merge_url }}/+edit-commit-message
3{% include 'footer.j2' %}
4{% include 'rebuild.j2' -%}
05
=== added file 'templates/push_failed.j2'
--- templates/push_failed.j2 1970-01-01 00:00:00 +0000
+++ templates/push_failed.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,3 @@
1FAILED: Autolanding.
2Pushing failed. More details in the following jenkins job:
3{{ args.build_url }}
0\ No newline at end of file4\ No newline at end of file
15
=== added file 'templates/rebuild.j2'
--- templates/rebuild.j2 1970-01-01 00:00:00 +0000
+++ templates/rebuild.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,2 @@
1Click here to trigger a rebuild:
2{{ args.build_url }}/rebuild
03
=== added file 'templates/unapproved_changes.j2'
--- templates/unapproved_changes.j2 1970-01-01 00:00:00 +0000
+++ templates/unapproved_changes.j2 2019-05-09 18:32:44 +0000
@@ -0,0 +1,3 @@
1{% include 'autoland_header.j2' %}
2Unapproved changes made after approval.
3{% include 'footer.j2' -%}
04
=== modified file 'tests/test_autoland.py'
--- tests/test_autoland.py 2013-06-14 19:02:11 +0000
+++ tests/test_autoland.py 2019-05-09 18:32:44 +0000
@@ -154,7 +154,7 @@
154 self.get_branch_from_mp = self.get_branch_from_mp_patch.start()154 self.get_branch_from_mp = self.get_branch_from_mp_patch.start()
155155
156 self.change_mp_status_patch = patch(156 self.change_mp_status_patch = patch(
157 'jlp.commands.autoland.launchpadutils.change_mp_status')157 'jlp.commands.autoland.change_mp_status')
158 self.change_mp_status = self.change_mp_status_patch.start()158 self.change_mp_status = self.change_mp_status_patch.start()
159159
160 def tearDown(self):160 def tearDown(self):
@@ -493,7 +493,7 @@
493 self.assertTrue("usage" in stderr.getvalue())493 self.assertTrue("usage" in stderr.getvalue())
494494
495 @patch('jlp.commands.autoland.launchpadutils.get_branch_from_mp')495 @patch('jlp.commands.autoland.launchpadutils.get_branch_from_mp')
496 @patch('jlp.commands.autoland.launchpadutils.change_mp_status')496 @patch('jlp.commands.autoland.change_mp_status')
497 def test_test_result_failed(self, change_mp_status_mock, get_branch_mock):497 def test_test_result_failed(self, change_mp_status_mock, get_branch_mock):
498 """Fail autolanding because of failed testing"""498 """Fail autolanding because of failed testing"""
499499
500500
=== modified file 'tests/test_jenkinsutils.py'
--- tests/test_jenkinsutils.py 2018-01-25 22:13:52 +0000
+++ tests/test_jenkinsutils.py 2019-05-09 18:32:44 +0000
@@ -1119,9 +1119,7 @@
1119 "\n None: http://10.0.0.1:8080/job/my-job/13/console"}),1119 "\n None: http://10.0.0.1:8080/job/my-job/13/console"}),
1120 ]1120 ]
11211121
1122 def test_get_result_line(self):1122 # was get_result_line
1123 result = jenkinsutils._get_result_line(self.result, self.url)
1124 self.assertEqual(result, self.expected)
11251123
11261124
1127class LaunchpadTriggerGetExecutedTestRuns(unittest.TestCase):1125class LaunchpadTriggerGetExecutedTestRuns(unittest.TestCase):
@@ -1163,54 +1161,10 @@
11631161
1164 def tearDown(self):1162 def tearDown(self):
1165 self.get_json_jenkins_patch.stop()1163 self.get_json_jenkins_patch.stop()
11661164
1167 def test_get_executed_test_runs_message(self):1165 # executed_test_runs_message
1168 ret = jenkinsutils.get_executed_test_runs_message(self.build_url)1166
1169 self.assertEqual(ret,1167 # format message for MP update
1170 self._get_executed_test_runs_message('jenkins:8080'))
1171
1172 def test_format_message_for_mp_update(self):
1173 test_runs = self._get_executed_test_runs_message('jenkins:8080')
1174 expected = dedent("""\
1175 mymessage
1176 http://jenkins:8080/job/my-job/3/{test_runs}
1177
1178 Click here to trigger a rebuild:
1179 http://jenkins:8080/job/my-job/3/rebuild
1180 """).format(test_runs=test_runs)
1181 actual = jenkinsutils.format_message_for_mp_update(
1182 self.build_url,
1183 "mymessage\n")
1184 self.assertEquals(actual, expected)
1185
1186 def test_format_message_with_empty_message(self):
1187 test_runs = self._get_executed_test_runs_message('jenkins:8080')
1188 expected = dedent("""\
1189 http://jenkins:8080/job/my-job/3/{test_runs}
1190
1191 Click here to trigger a rebuild:
1192 http://jenkins:8080/job/my-job/3/rebuild
1193 """).format(test_runs=test_runs)
1194
1195 actual = jenkinsutils.format_message_for_mp_update(
1196 self.build_url)
1197 self.assertEquals(actual, expected)
1198
1199 def test_format_message_with_publishing_job(self):
1200 test_runs = self._get_executed_test_runs_message(
1201 'jenkins.qa.ubuntu.com')
1202 expected = dedent("""\
1203 http://jenkins.qa.ubuntu.com/job/my-job/3/{test_runs}
1204
1205 Click here to trigger a rebuild:
1206 http://jenkins:8080/job/my-job/3/rebuild
1207 """).format(test_runs=test_runs)
1208
1209 with patch("jlp.jenkinsutils.is_job_published",
1210 new=lambda x: True):
1211 actual = jenkinsutils.format_message_for_mp_update(
1212 self.build_url)
1213 self.assertEquals(actual, expected)
12141168
12151169
1216class TestTriggerCi(unittest.TestCase):1170class TestTriggerCi(unittest.TestCase):
@@ -1294,10 +1248,10 @@
1294 new=lambda x, y, z: False)1248 new=lambda x, y, z: False)
1295 @patch('jlp.launchpadutils.users_allowed_to_trigger_jobs',1249 @patch('jlp.launchpadutils.users_allowed_to_trigger_jobs',
1296 new=lambda x: True)1250 new=lambda x: True)
1297 @patch('jlp.launchpadutils.change_mp_status')1251 @patch('jlp.launchpadutils.report_to_launchpad')
1298 @patch('jlp.jenkinsutils.start_jenkins_job')1252 @patch('jlp.jenkinsutils.start_jenkins_job')
1299 def test_trigger_al_with_no_commit_message(self, start_jenkins_job,1253 def test_trigger_al_with_no_commit_message(self, start_jenkins_job,
1300 change_mp_status):1254 report_to_launchpad):
1301 """If no commit message is set no jenkins job must be started and1255 """If no commit message is set no jenkins job must be started and
1302 status of merge proposal must be changed to NEEDS_FIXING"""1256 status of merge proposal must be changed to NEEDS_FIXING"""
1303 jenkinsutils.trigger_al_build(MagicMock(),1257 jenkinsutils.trigger_al_build(MagicMock(),
@@ -1307,7 +1261,7 @@
1307 # the new vote is the 5th argument in change_mp_status (and we count1261 # the new vote is the 5th argument in change_mp_status (and we count
1308 # from 0)1262 # from 0)
1309 new_vote_position = 41263 new_vote_position = 4
1310 self.assertEqual(change_mp_status.call_args[0][new_vote_position],1264 self.assertEqual(report_to_launchpad.call_args[0][new_vote_position],
1311 launchpadutils.LaunchpadVote.NEEDS_FIXING)1265 launchpadutils.LaunchpadVote.NEEDS_FIXING)
13121266
1313 @patch('jlp.launchpadutils.is_commit_message_set',1267 @patch('jlp.launchpadutils.is_commit_message_set',
13141268
=== modified file 'tests/test_launchpadutils.py'
--- tests/test_launchpadutils.py 2017-02-23 20:25:56 +0000
+++ tests/test_launchpadutils.py 2019-05-09 18:32:44 +0000
@@ -19,42 +19,53 @@
19 'https://copde.launchpad.net/~user/project/name/merge/12345')19 'https://copde.launchpad.net/~user/project/name/merge/12345')
20 self.assertEqual(branch, None)20 self.assertEqual(branch, None)
2121
22 @patch('jlp.launchpadutils.get_vote_subject',22 @patch('jlp.launchpadutils.get_config_option')
23 new=lambda mp: 'Subject')23 @patch('jlp.launchpadutils.get_vote_subject')
24 def test_change_mp_status_with_no_vote(self):24 def test_report_to_launchpad_status(self, gvs_mock, gco_mock):
25 mp = MagicMock()25 gvs_mock.return_value = 'vote subject'
26 launchpadutils.change_mp_status(mp, 'Status', 'Message', revision=1)26 gco_mock.return_value = 'vote type'
27 mp.createComment.assert_called_once_with(27 mp = MagicMock()
28 review_type=get_config_option('launchpad_review_type'),28 message = 'this is a test'
29 subject='Subject',29 revision = 'abc123'
30 content='Message')30 status = 'Approved'
31 mp.setStatus.assert_called_once_with(status='Status', revid=1)31 launchpadutils.report_to_launchpad(mp, message, revision, status)
3232 mp.setStatus.assert_called_once_with(revid='abc123', status='Approved')
33 @patch('jlp.launchpadutils.get_vote_subject',33 mp.createComment.assert_called_once_with(
34 new=lambda mp: 'Subject')34 content='this is a test',
35 def test_change_mp_status_with_approve(self):35 review_type='vote type',
36 mp = MagicMock()36 subject='vote subject')
37 mp.web_link = 'http://my-example-url.com'37
38 launchpadutils.change_mp_status(mp, 'Status', 'Message',38 @patch('jlp.launchpadutils.get_config_option')
39 revision=1, vote='Approve')39 @patch('jlp.launchpadutils.get_vote_subject')
40 mp.createComment.assert_called_once_with(40 def test_report_to_launchpad_vote(self, gvs_mock, gco_mock):
41 review_type=get_config_option('launchpad_review_type'),41 gvs_mock.return_value = 'vote subject'
42 subject='Subject',42 gco_mock.return_value = 'vote type'
43 content='Message', vote='Approve')43 mp = MagicMock()
44 mp.setStatus.assert_called_once_with(status='Status', revid=1)44 message = 'this is a test'
4545 revision = 'abc123'
46 @patch('jlp.launchpadutils.get_vote_subject',46 launchpadutils.report_to_launchpad(mp, message, revision, 'approved')
47 new=lambda mp: 'Subject')47 mp.createComment.assert_called_once_with(
48 def test_change_mp_status_with_disapprove(self):48 content='this is a test',
49 mp = MagicMock()49 review_type='vote type',
50 mp.web_link = 'http://my-example-url.com'50 subject='vote subject')
51 launchpadutils.change_mp_status(mp, 'Status', 'Message',51
52 revision=1, vote='Disapprove')52 @patch('jlp.launchpadutils.get_config_option')
53 mp.createComment.assert_called_once_with(53 @patch('jlp.launchpadutils.get_vote_subject')
54 review_type=get_config_option('launchpad_review_type'),54 def test_report_to_launchpad_status_and_vote(self, gvs_mock, gco_mock):
55 subject='Subject',55 gvs_mock.return_value = 'vote subject'
56 content='Message', vote='Disapprove')56 gco_mock.return_value = 'vote type'
57 mp.setStatus.assert_called_once_with(status='Status', revid=1)57 mp = MagicMock()
58 message = 'this is a test'
59 revision = 'abc123'
60 status = 'Approved'
61 launchpadutils.report_to_launchpad(mp, message,
62 revision, status, 'approved')
63 mp.setStatus.assert_called_once_with(revid='abc123', status='Approved')
64 mp.createComment.assert_called_once_with(
65 content='this is a test',
66 review_type='vote type',
67 subject='vote subject',
68 vote='approved')
5869
59 def test_close_bugs(self):70 def test_close_bugs(self):
60 mp = MagicMock()71 mp = MagicMock()
@@ -564,97 +575,6 @@
564 def tearDown(self):575 def tearDown(self):
565 self.get_executed_test_runs_message_patch.stop()576 self.get_executed_test_runs_message_patch.stop()
566577
567 def test_approve_mp(self):
568 mp = self._get_mp()
569 subject = self._get_subject(mp)
570 launchpadutils.approve_mp(mp, '1', 'http://url')
571 content = dedent('''\
572 PASSED: Continuous integration, rev:1
573 http://url
574
575 Click here to trigger a rebuild:
576 http://url/rebuild
577 ''')
578 mp.createComment.assert_called_once_with(
579 review_type=get_config_option('launchpad_review_type'),
580 vote=launchpadutils.LaunchpadVote.APPROVE,
581 subject=subject,
582 content=content)
583
584 def test_disapprove_mp(self):
585 mp = self._get_mp()
586 subject = self._get_subject(mp)
587 launchpadutils.disapprove_mp(mp, '1', 'http://url')
588 content = dedent('''\
589 FAILED: Continuous integration, rev:1
590 http://url
591
592 Click here to trigger a rebuild:
593 http://url/rebuild
594 ''')
595 mp.createComment.assert_called_once_with(
596 review_type=get_config_option('launchpad_review_type'),
597 vote=launchpadutils.LaunchpadVote.NEEDS_FIXING,
598 subject=subject,
599 content=content)
600
601 def test_disapprove_mp_with_reason(self):
602 mp = self._get_mp()
603 subject = self._get_subject(mp)
604 reason = "No commit message set\nDo something\n"
605 content = dedent('''\
606 FAILED: Continuous integration, rev:1
607 {reason}
608 http://url
609
610 Click here to trigger a rebuild:
611 http://url/rebuild
612 ''').format(reason=reason)
613
614 launchpadutils.disapprove_mp(mp,
615 '1',
616 'http://url',
617 reason=reason)
618 mp.createComment.assert_called_once_with(
619 review_type=get_config_option('launchpad_review_type'),
620 vote=launchpadutils.LaunchpadVote.NEEDS_FIXING,
621 subject=subject,
622 content=content)
623
624 def test_approve_git_mp(self):
625 mp = self._get_git_mp()
626 subject = self._expected_git_mp_subject(mp)
627 launchpadutils.approve_mp(mp, '1', 'http://url')
628 content = dedent('''\
629 PASSED: Continuous integration, rev:1
630 http://url
631
632 Click here to trigger a rebuild:
633 http://url/rebuild
634 ''')
635 mp.createComment.assert_called_once_with(
636 review_type=get_config_option('launchpad_review_type'),
637 vote=launchpadutils.LaunchpadVote.APPROVE,
638 subject=subject,
639 content=content)
640
641 def test_dissaprove_git_mp(self):
642 mp = self._get_git_mp()
643 subject = self._expected_git_mp_subject(mp)
644 launchpadutils.disapprove_mp(mp, '1', 'http://url')
645 content = dedent('''\
646 FAILED: Continuous integration, rev:1
647 http://url
648
649 Click here to trigger a rebuild:
650 http://url/rebuild
651 ''')
652 mp.createComment.assert_called_once_with(
653 review_type=get_config_option('launchpad_review_type'),
654 vote=launchpadutils.LaunchpadVote.NEEDS_FIXING,
655 subject=subject,
656 content=content)
657
658578
659class TestGetMergeProposalsWithInvalidBranch(unittest.TestCase):579class TestGetMergeProposalsWithInvalidBranch(unittest.TestCase):
660 branch_with_mps = None580 branch_with_mps = None
@@ -905,7 +825,7 @@
905825
906 self.assertEqual(result, ('git', repo))826 self.assertEqual(result, ('git', repo))
907827
908 def test_raises_for_no_mathc(self):828 def test_raises_for_no_match(self):
909 '''829 '''
910 Ensure that an exception is raised when there is no match.830 Ensure that an exception is raised when there is no match.
911 '''831 '''
912832
=== modified file 'tests/test_voteOnMergeProposal.py'
--- tests/test_voteOnMergeProposal.py 2013-11-19 00:11:18 +0000
+++ tests/test_voteOnMergeProposal.py 2019-05-09 18:32:44 +0000
@@ -12,9 +12,6 @@
12 'jlp.commands.voteOnMergeProposal.get_launchpad',12 'jlp.commands.voteOnMergeProposal.get_launchpad',
13 new=get_launchpad)13 new=get_launchpad)
14 self.get_launchpad_patch.start()14 self.get_launchpad_patch.start()
15 self.get_executed_test_runs_message_patch = patch(
16 'jlp.jenkinsutils.get_executed_test_runs_message')
17 self.get_executed_test_runs_message_patch.start()
18 self.is_commit_message_set_patch = patch(15 self.is_commit_message_set_patch = patch(
19 'jlp.launchpadutils.is_commit_message_set')16 'jlp.launchpadutils.is_commit_message_set')
20 self.is_commit_message_set_patch.start()17 self.is_commit_message_set_patch.start()
@@ -22,7 +19,6 @@
22 def tearDown(self):19 def tearDown(self):
23 self.get_launchpad_patch.stop()20 self.get_launchpad_patch.stop()
24 self.is_commit_message_set_patch.stop()21 self.is_commit_message_set_patch.stop()
25 self.get_executed_test_runs_message_patch.stop()
2622
27 def test_gibberish(self):23 def test_gibberish(self):
28 """Gibberish on the command line produces usage message."""24 """Gibberish on the command line produces usage message."""
@@ -112,7 +108,12 @@
112 pass108 pass
113 self.assertTrue(error_message in stderr.getvalue())109 self.assertTrue(error_message in stderr.getvalue())
114110
115 def test_passed_vote(self):111 @patch('sys.argv')
112 @patch('jlp.commands.voteOnMergeProposal.launchpadutils.get_mp_handle_from_url')
113 @patch('jlp.launchpadutils.report_to_launchpad')
114 @patch('jlp.commands.voteOnMergeProposal.launchpadutils.get_template_args')
115 @patch('jlp.commands.voteOnMergeProposal.get_jinja_environment')
116 def test_passed_vote(self, gje, gta, rtl, gmphandle, sys_argv):
116 """Vote on a proposal which passed."""117 """Vote on a proposal which passed."""
117118
118 sys_argv = ['voteOnMergeProposal.py',119 sys_argv = ['voteOnMergeProposal.py',
@@ -123,20 +124,27 @@
123 '--branch=lp:~mrazik/faux-dbus-test-runner/yar8',124 '--branch=lp:~mrazik/faux-dbus-test-runner/yar8',
124 '--merge-proposal=https://code.launchpad.net/~mrazik/' +125 '--merge-proposal=https://code.launchpad.net/~mrazik/' +
125 'faux-dbus-test-runner/yar8/+merge/106113']126 'faux-dbus-test-runner/yar8/+merge/106113']
126 with patch('sys.argv', sys_argv), \127
127 patch('jlp.commands.voteOnMergeProposal.launchpadutils.' +128 gmphandle.return_value = 'Launchpad_mp'
128 'get_mp_handle_from_url') as get_mp_handle_from_url, \129 gta.return_value = {
129 patch('jlp.launchpadutils.approve_mp') as approve_mp:130 'result': 'PASSED',
130 get_mp_handle_from_url.return_value = 'Launchpad_mp'131 'revision': '66'
131 ret = voteOnMergeProposal()132 }
132 self.assertEqual(ret, 0)133 ret = voteOnMergeProposal()
133 approve_mp.assert_called_with(134 self.assertEqual(ret, 0)
134 'Launchpad_mp',135 rtl.assert_called_once()
135 '66',136 gje.get_template.assert_called_with('ci_test.j2')
136 'http://s-jenkins.ubuntu-ci/job/faux-dbus-test-runner-ci/' +137
137 'configure')138
138139 @patch('sys.argv')
139 def test_no_commit_message_set(self):140 @patch('jlp.commands.voteOnMergeProposal.launchpadutils.' +\
141 'get_mp_handle_from_url')
142 @patch('jlp.launchpadutils.report_to_launchpad')
143 @patch('jlp.launchpadutils.is_commit_message_set')
144 @patch('jlp.commands.voteOnMergeProposal.launchpadutils.get_template_args')
145 @patch('jlp.commands.voteOnMergeProposal.get_jinja_environment')
146 def test_no_commit_message_set(self, gje, gta, icms,
147 rtl, gmphandle, sys_argv):
140 """Vote on a proposal where tests passed but no commit message148 """Vote on a proposal where tests passed but no commit message
141 was set."""149 was set."""
142150
@@ -149,77 +157,16 @@
149 '--revision=66',157 '--revision=66',
150 '--branch=lp:~mrazik/faux-dbus-test-runner/yar8',158 '--branch=lp:~mrazik/faux-dbus-test-runner/yar8',
151 '--merge-proposal=' + merge_proposal]159 '--merge-proposal=' + merge_proposal]
152 with patch('sys.argv', sys_argv), \160
153 patch('jlp.commands.voteOnMergeProposal.launchpadutils.' +161 gmphandle.return_value = 'Launchpad_mp'
154 'get_mp_handle_from_url') as get_mp_handle_from_url, \162 gta.return_value = {
155 patch('jlp.launchpadutils.is_commit_message_set') \163 'result': 'PASSED',
156 as is_commit_message_set, \164 'revision': '66',
157 patch('jlp.launchpadutils.disapprove_mp') as disapprove_mp:165 'build_url': 'http://s-jenkins.ubuntu-ci/job/' +
158 get_mp_handle_from_url.return_value = 'Launchpad_mp'166 'faux-dbus-test-runner-ci/configure'
159 is_commit_message_set.return_value = False167 }
160 reason = "No commit message was specified in the merge " + \168 icms.return_value = False
161 "proposal. Click on the following link and set the " + \169
162 "commit message (if you want a jenkins rebuild you " +\170 ret = voteOnMergeProposal()
163 "need to trigger it yourself):\n" + \171 self.assertEqual(ret, 0)
164 merge_proposal + "/+edit-commit-message\n"172 gje.get_template.assert_called_with('no_commit.j2')
165 ret = voteOnMergeProposal()
166 self.assertEqual(ret, 0)
167 disapprove_mp.assert_called_with(
168 'Launchpad_mp',
169 '66',
170 'http://s-jenkins.ubuntu-ci/job/faux-dbus-test-runner-ci/' +
171 'configure',
172 reason)
173
174 def test_invalid_build_url(self):
175 """Vote on a proposal where tests passed but no commit message
176 was set."""
177
178 sys_argv = ['voteOnMergeProposal.py',
179 '--status=PASSED',
180 '--build-url=http://s-jenkins.ubuntu-ci/asdf/' +
181 'faux-dbus-test-runner-ci/configure',
182 '--revision=66',
183 '--branch=lp:~mrazik/faux-dbus-test-runner/yar8',
184 '--merge-proposal=https://code.launchpad.net/~mrazik/' +
185 'faux-dbus-test-runner/yar8/+merge/106113']
186 with patch('sys.argv', sys_argv), \
187 patch('jlp.commands.voteOnMergeProposal.launchpadutils.' +
188 'get_mp_handle_from_url') as get_mp_handle_from_url, \
189 patch('jlp.commands.voteOnMergeProposal.launchpadutils.' +
190 'is_commit_message_set') as is_commit_message_set, \
191 patch('jlp.launchpadutils.approve_mp') as approve_mp:
192 get_mp_handle_from_url.return_value = 'Launchpad_mp'
193 is_commit_message_set.return_value = False
194 ret = voteOnMergeProposal()
195 self.assertEqual(ret, 0)
196 approve_mp.assert_called_with(
197 'Launchpad_mp',
198 '66',
199 'http://s-jenkins.ubuntu-ci/asdf/faux-dbus-test-runner-ci/' +
200 'configure')
201
202 def test_not_passed_vote(self):
203 """Vote on a proposal which did not pass."""
204
205 sys_argv = ['voteOnMergeProposal.py',
206 '--status=OHECK',
207 '--build-url=http://s-jenkins.ubuntu-ci/job/' +
208 'faux-dbus-test-runner-ci/configure',
209 '--revision=66',
210 '--branch="lp:~mrazik/faux-dbus-test-runner/yar8"',
211 '--merge-proposal=https://code.launchpad.net/~mrazik/' +
212 'faux-dbus-test-runner/yar8/+merge/106113']
213 with patch('sys.argv', sys_argv), \
214 patch('jlp.commands.voteOnMergeProposal.launchpadutils.' +
215 'get_mp_handle_from_url') as get_mp_handle_from_url, \
216 patch('jlp.launchpadutils.disapprove_mp') as disapprove_mp:
217 get_mp_handle_from_url.return_value = 'Launchpad_mp'
218 ret = voteOnMergeProposal()
219 self.assertEqual(ret, 0)
220 disapprove_mp.assert_called_with(
221 'Launchpad_mp',
222 '66',
223 'http://s-jenkins.ubuntu-ci/job/faux-dbus-test-runner-ci/' +
224 'configure',
225 '')

Subscribers

People subscribed via source and target branches