~raoul-snyman/openlp/dont-test-uno-on-macos

« back to all changes in this revision

Viewing changes to scripts/jenkins_script.py

  • Committer: Tim Bentley
  • Author(s): Raoul Snyman
  • Date: 2017-11-10 19:02:49 UTC
  • mfrom: (2781.2.7 update-jenkins-script)
  • Revision ID: tim.bentley@gmail.com-20171110190249-8af76dwxh5mpsl7f
Change the Jenkins script to use the official Jenkins Python API module (python3-jenkins in Debian/Ubuntu and Fedora). Also updated the display a bit and added some helpful messaging. This script now uses your username and your password (or personal token) for authentication instead of a a shared token.

$ python3 scripts/jenkins_script.py -h                                                             
usage: jenkins_script.py [-h] [-d] [-b] [-n] -u USERNAME -p PASSWORD

optional arguments:
 ...

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
22
22
###############################################################################
23
23
"""
24
 
This script helps to trigger builds of branches. To use it you have to install the jenkins-webapi package:
25
 
 
26
 
    pip3 install jenkins-webapi
27
 
 
28
 
You probably want to create an alias. Add this to your ~/.bashrc file and then logout and login (to apply the alias):
29
 
 
30
 
    alias ci="python3 ./scripts/jenkins_script.py TOKEN"
31
 
 
32
 
You can look up the token in the Branch-01-Pull job configuration or ask in IRC.
 
24
This script helps to trigger builds of branches. To use it you have to install the python-jenkins module. On Fedora
 
25
and Ubuntu/Debian, it is available as the ``python3-jenkins`` package::
 
26
 
 
27
    $ sudo dnf/apt install python3-jenkins
 
28
 
 
29
To make it easier to run you may want to create a shell script or an alias. To create an alias, add this to your
 
30
``~/.bashrc`` (or ``~/.zshrc``) file and then log out and log back in again (to apply the alias)::
 
31
 
 
32
    alias ci="python3 /path/to/openlp_root/scripts/jenkins_script.py -u USERNAME -p PASSWORD"
 
33
 
 
34
To create a shell script, create the following file in a location in your ``$PATH`` (I called mine ``ci``)::
 
35
 
 
36
    #!/bin/bash
 
37
    python3 /path/to/openlp_root/scripts/jenkins_script.py -u USERNAME -p PASSWORD
 
38
 
 
39
``USERNAME`` is your Jenkins username, and ``PASSWORD`` is your Jenkins password or personal token.
 
40
 
 
41
An older version of this script used to use a shared TOKEN, but this has been replaced with the username and password.
33
42
"""
34
 
 
 
43
import os
35
44
import re
36
 
import sys
37
45
import time
38
 
from optparse import OptionParser
 
46
from argparse import ArgumentParser
39
47
from subprocess import Popen, PIPE
40
 
import warnings
41
48
 
42
 
from requests.exceptions import HTTPError
43
49
from jenkins import Jenkins
44
50
 
45
 
 
46
51
JENKINS_URL = 'https://ci.openlp.io/'
47
52
REPO_REGEX = r'(.*/+)(~.*)'
48
 
# Allows us to black list token. So when we change the token, we can display a proper message to the user.
49
 
OLD_TOKENS = []
50
 
 
51
 
# Disable the InsecureRequestWarning we get from urllib3, because we're not verifying our own self-signed certificate
52
 
warnings.simplefilter('ignore')
53
53
 
54
54
 
55
55
class OpenLPJobs(object):
85
85
    :param token: The token we need to trigger the build. If you do not have this token, ask in IRC.
86
86
    """
87
87
 
88
 
    def __init__(self, token):
 
88
    def __init__(self, username, password, can_use_colour):
89
89
        """
90
90
        Create the JenkinsTrigger instance.
91
91
        """
92
 
        self.token = token
 
92
        self.jobs = {}
 
93
        self.can_use_colour = can_use_colour and not os.name.startswith('nt')
93
94
        self.repo_name = get_repo_name()
94
 
        self.jenkins_instance = Jenkins(JENKINS_URL)
 
95
        self.server = Jenkins(JENKINS_URL, username=username, password=password)
 
96
 
 
97
    def fetch_jobs(self):
 
98
        """
 
99
        Get the job info for all the jobs
 
100
        """
 
101
        for job_name in OpenLPJobs.Jobs:
 
102
            job_info = self.server.get_job_info(job_name)
 
103
            self.jobs[job_name] = job_info
 
104
            self.jobs[job_name]['nextBuildUrl'] = '{url}{nextBuildNumber}/'.format(**job_info)
95
105
 
96
106
    def trigger_build(self):
97
107
        """
102
112
        # We just want the name (not the email).
103
113
        name = ' '.join(raw_output.decode().split()[:-1])
104
114
        cause = 'Build triggered by %s (%s)' % (name, self.repo_name)
105
 
        self.jenkins_instance.job(OpenLPJobs.Branch_Pull).build({'BRANCH_NAME': self.repo_name, 'cause': cause},
106
 
                                                                token=self.token)
 
115
        self.fetch_jobs()
 
116
        self.server.build_job(OpenLPJobs.Branch_Pull, {'BRANCH_NAME': self.repo_name, 'cause': cause})
107
117
 
108
118
    def print_output(self):
109
119
        """
110
120
        Print the status information of the build triggered.
111
121
        """
112
122
        print('Add this to your merge proposal:')
113
 
        print('--------------------------------')
 
123
        print('-' * 80)
114
124
        bzr = Popen(('bzr', 'revno'), stdout=PIPE, stderr=PIPE)
115
125
        raw_output, error = bzr.communicate()
116
126
        revno = raw_output.decode().strip()
118
128
 
119
129
        for job in OpenLPJobs.Jobs:
120
130
            if not self.__print_build_info(job):
121
 
                print('Stopping after failure')
 
131
                if self.current_build:
 
132
                    print('Stopping after failure, see {}console for more details'.format(self.current_build['url']))
 
133
                else:
 
134
                    print('Stopping after failure')
122
135
                break
123
136
 
124
137
    def open_browser(self):
129
142
        # Open the url
130
143
        Popen(('xdg-open', url), stderr=PIPE)
131
144
 
 
145
    def _get_build_info(self, job_name, build_number):
 
146
        """
 
147
        Get the build info from the server. This method will check the queue and wait for the build.
 
148
        """
 
149
        queue_info = self.server.get_queue_info()
 
150
        tries = 0
 
151
        while queue_info and tries < 50:
 
152
            tries += 1
 
153
            time.sleep(0.5)
 
154
            queue_info = self.server.get_queue_info()
 
155
        if tries >= 50:
 
156
            raise Exception('Build has not started yet, it may be stuck in the queue.')
 
157
        return self.server.get_build_info(job_name, build_number)
 
158
 
132
159
    def __print_build_info(self, job_name):
133
160
        """
134
161
        This helper method prints the job information of the given ``job_name``
136
163
        :param job_name: The name of the job we want the information from. For example *Branch-01-Pull*. Use the class
137
164
         variables from the :class:`OpenLPJobs` class.
138
165
        """
 
166
        job = self.jobs[job_name]
 
167
        print('{:<70} [WAITING]'.format(job['nextBuildUrl']), end='', flush=True)
 
168
        self.current_build = self._get_build_info(job_name, job['nextBuildNumber'])
 
169
        print('\b\b\b\b\b\b\b\b\b[RUNNING]', end='', flush=True)
139
170
        is_success = False
140
 
        job = self.jenkins_instance.job(job_name)
141
 
        while job.info['inQueue']:
142
 
            time.sleep(1)
143
 
        build = job.last_build
144
 
        build.wait()
145
 
        if build.info['result'] == 'SUCCESS':
146
 
            # Make 'SUCCESS' green.
147
 
            result_string = '%s%s%s' % (Colour.GREEN_START, build.info['result'], Colour.GREEN_END)
148
 
            is_success = True
149
 
        else:
150
 
            # Make 'FAILURE' red.
151
 
            result_string = '%s%s%s' % (Colour.RED_START, build.info['result'], Colour.RED_END)
152
 
        url = build.info['url']
153
 
        print('[%s] %s' % (result_string, url))
 
171
        while self.current_build['building'] is True:
 
172
            time.sleep(0.5)
 
173
            self.current_build = self.server.get_build_info(job_name, job['nextBuildNumber'])
 
174
        result_string = self.current_build['result']
 
175
        is_success = result_string == 'SUCCESS'
 
176
        if self.can_use_colour:
 
177
            if is_success:
 
178
                # Make 'SUCCESS' green.
 
179
                result_string = '{}{}{}'.format(Colour.GREEN_START, result_string, Colour.GREEN_END)
 
180
            else:
 
181
                # Make 'FAILURE' red.
 
182
                result_string = '{}{}{}'.format(Colour.RED_START, result_string, Colour.RED_END)
 
183
        print('\b\b\b\b\b\b\b\b\b[{:>7}]'.format(result_string))
154
184
        return is_success
155
185
 
156
186
 
186
216
 
187
217
 
188
218
def main():
189
 
    usage = 'Usage: python %prog TOKEN [options]'
190
 
 
191
 
    parser = OptionParser(usage=usage)
192
 
    parser.add_option('-d', '--disable-output', dest='enable_output', action='store_false', default=True,
193
 
                      help='Disable output.')
194
 
    parser.add_option('-b', '--open-browser', dest='open_browser', action='store_true', default=False,
195
 
                      help='Opens the jenkins page in your browser.')
196
 
    options, args = parser.parse_args(sys.argv)
197
 
 
198
 
    if len(args) == 2:
199
 
        if not get_repo_name():
200
 
            print('Not a branch. Have you pushed it to launchpad? Did you cd to the branch?')
201
 
            return
202
 
        token = args[-1]
203
 
        if token in OLD_TOKENS:
204
 
            print('Your token is not valid anymore. Get the most recent one.')
205
 
            return
206
 
        jenkins_trigger = JenkinsTrigger(token)
207
 
        try:
208
 
            jenkins_trigger.trigger_build()
209
 
        except HTTPError:
210
 
            print('Wrong token.')
211
 
            return
212
 
        # Open the browser before printing the output.
213
 
        if options.open_browser:
214
 
            jenkins_trigger.open_browser()
215
 
        if options.enable_output:
216
 
            jenkins_trigger.print_output()
217
 
    else:
218
 
        parser.print_help()
 
219
    """
 
220
    Run the script
 
221
    """
 
222
    parser = ArgumentParser()
 
223
    parser.add_argument('-d', '--disable-output', action='store_true', default=False, help='Disable output')
 
224
    parser.add_argument('-b', '--open-browser', action='store_true', default=False,
 
225
                        help='Opens the jenkins page in your browser')
 
226
    parser.add_argument('-n', '--no-colour', action='store_true', default=False,
 
227
                        help='Disable coloured output (always disabled on Windows)')
 
228
    parser.add_argument('-u', '--username', required=True, help='Your Jenkins username')
 
229
    parser.add_argument('-p', '--password', required=True, help='Your Jenkins password or personal token')
 
230
    args = parser.parse_args()
 
231
 
 
232
    if not get_repo_name():
 
233
        print('Not a branch. Have you pushed it to launchpad? Did you cd to the branch?')
 
234
        return
 
235
    jenkins_trigger = JenkinsTrigger(args.username, args.password, not args.no_colour)
 
236
    jenkins_trigger.trigger_build()
 
237
    # Open the browser before printing the output.
 
238
    if args.open_browser:
 
239
        jenkins_trigger.open_browser()
 
240
    if not args.disable_output:
 
241
        jenkins_trigger.print_output()
219
242
 
220
243
 
221
244
if __name__ == '__main__':