1
# -*- coding: utf-8 -*-
2
# Copyright 2009 Didier Roche
4
# This file is part of Quickly ubuntu-application template
6
#This program is free software: you can redistribute it and/or modify it
7
#under the terms of the GNU General Public License version 3, as published
8
#by the Free Software Foundation.
10
#This program is distributed in the hope that it will be useful, but
11
#WITHOUT ANY WARRANTY; without even the implied warranties of
12
#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13
#PURPOSE. See the GNU General Public License for more details.
15
#You should have received a copy of the GNU General Public License along
16
#with this program. If not, see <http://www.gnu.org/licenses/>.
22
from launchpadlib.errors import HTTPError
25
from quickly import configurationhandler
26
from quickly import launchpadaccess
27
from internal import quicklyutils
28
from quickly import templatetools
31
from gettext import gettext as _
34
gettext.textdomain('quickly')
36
class ppa_not_found(Exception):
38
class not_ppa_owner(Exception):
40
class user_team_not_found(Exception):
42
class invalid_versionning_scheme(Exception):
43
def __init__(self, msg):
47
class invalid_version_in_setup(Exception):
48
def __init__(self, msg):
58
def _continue_if_errors(err_output, warn_output, return_code,
59
ask_on_warn_or_error):
60
"""print existing error and warning"""
63
print #finish the current line
64
print ('----------------------------------')
65
print _('Command returned some ERRORS:')
66
print ('----------------------------------')
67
print ('\n'.join(err_output))
68
print ('----------------------------------')
70
# seek if not uneeded warning (noise from DistUtilsExtra.auto)
71
# line following the warning should be " …"
73
for line in warn_output:
74
if (re.match(".*not recognized by DistUtilsExtra.auto.*", line)):
76
if not re.match(' .*', warn_output[line_number + 1]):
77
warn_output.remove(line)
80
warn_output.remove(line)
83
# if still something, print it
86
print #finish the current line
87
print _('Command returned some WARNINGS:')
88
print ('----------------------------------')
89
print ('\n'.join(warn_output))
90
print ('----------------------------------')
91
if ((err_output or warn_output) and ask_on_warn_or_error
92
and return_code == 0):
93
if not 'y' in raw_input("Do you want to continue (this is not safe!)? y/[n]: "):
97
def _filter_out(line, output_domain, err_output, warn_output):
98
'''filter output dispatching right domain'''
101
output_domain = DomainLevel.ERROR
103
output_domain = DomainLevel.WARNING
104
elif not line.startswith(' '):
105
output_domain = DomainLevel.NONE
106
if '[not found]' in line:
107
output_domain = DomainLevel.WARNING
108
if output_domain == DomainLevel.ERROR:
109
# only add once an error
110
if not line in err_output:
111
err_output.append(line)
112
elif output_domain == DomainLevel.WARNING:
113
# only add once a warning
114
if not line in warn_output:
115
# filter bad output from dpkg-buildpackage (on stderr) and p-d-e auto
116
if not(re.match(' .*\.pot', line)
117
or re.match(' .*\.in', line)
118
or re.match(' dpkg-genchanges >.*', line)
119
# FIXME: this warning is temporary: should be withed in p-d-e
120
or re.match('.*XS-Python-Version and XB-Python-Version.*', line)):
121
warn_output.append(line)
123
sys.stdout.write('.')
124
return (output_domain, err_output, warn_output)
127
def _exec_and_log_errors(command, ask_on_warn_or_error=False):
128
'''exec the giving command and hide output if not in verbose mode'''
130
if templatetools.in_verbose_mode():
131
return(subprocess.call(command))
133
proc = subprocess.Popen(command, stdout=subprocess.PIPE,
134
stderr=subprocess.PIPE)
135
stdout_domain = DomainLevel.NONE
136
stderr_domain = DomainLevel.NONE
140
line_stdout = proc.stdout.readline().rstrip()
141
line_stderr = proc.stderr.readline().rstrip()
144
(stderr_domain, err_output, warn_output) = _filter_out(line_stderr, stderr_domain, err_output, warn_output)
147
# don't replace by if proc.poll() as the output can be empty
148
if proc.poll() != None:
152
(stdout_domain, err_output, warn_output) = _filter_out(line_stdout, stdout_domain, err_output, warn_output)
154
return(_continue_if_errors(err_output, warn_output, proc.returncode,
155
ask_on_warn_or_error))
158
def updatepackaging(changelog=None):
159
"""create or update a package using python-mkdebian.
161
Commit after the first packaging creation"""
165
command = ['python-mkdebian', '--force-control']
166
for message in changelog:
167
command.extend(["--changelog", message])
168
if not configurationhandler.project_config:
169
configurationhandler.loadConfig()
171
for elem in configurationhandler.project_config['dependencies'].split(' '):
173
command.extend(["--dependency", elem])
177
return_code = _exec_and_log_errors(command, True)
179
print _("An error has occurred when creating debian packaging")
182
print _("Ubuntu packaging created in debian/")
184
# check if first python-mkdebian (debian/ creation) to commit it
185
# that means debian/ under unknown
186
bzr_instance = subprocess.Popen(["bzr", "status"], stdout=subprocess.PIPE)
187
bzr_status, err = bzr_instance.communicate()
188
if bzr_instance.returncode != 0:
189
return(bzr_instance.returncode)
191
if re.match('(.|\n)*unknown:\n.*debian/(.|\n)*', bzr_status):
192
return_code = filter_exec_command(["bzr", "add"])
194
return_code = filter_exec_command(["bzr", "commit", "-m", 'Creating ubuntu package'])
199
def filter_exec_command(command):
200
''' Build either a source or a binary package'''
202
return(_exec_and_log_errors(command, False))
205
def shell_complete_ppa(ppa_to_complete):
206
''' Complete from available ppas '''
208
# connect to LP and get ppa to complete
210
launchpad = launchpadaccess.initialize_lpi(False)
211
except launchpadaccess.launchpad_connection_error:
216
(ppa_user, ppa_name) = get_ppa_parameters(launchpad, ppa_to_complete)
217
except user_team_not_found:
220
for current_ppa_name, current_ppa_displayname in get_all_ppas(launchpad, ppa_user):
221
# print user/ppa form
222
available_ppas.append("%s/%s" % (ppa_user.name, current_ppa_name))
223
# if it's the user, print in addition just "ppa_name" syntax
224
if ppa_user.name == launchpad.me.name:
225
available_ppas.append(current_ppa_name)
226
# if we don't have provided a team, show all teams were we are member off
227
if not '/' in ppa_to_complete:
228
team = [mem.team for mem in launchpad.me.memberships_details if mem.status in ("Approved", "Administrator")]
230
available_ppas.append(elem.name + '/')
231
return available_ppas
233
def get_ppa_parameters(launchpad, full_ppa_name):
234
''' Check if we can catch good parameters for specified ppa in form user/ppa or ppa '''
236
if '/' in full_ppa_name:
237
ppa_user_name = full_ppa_name.split('/')[0]
238
ppa_name = full_ppa_name.split('/')[1]
239
# check that we are in the team/or that we are the user
241
lp_ppa_user = launchpad.people[ppa_user_name]
242
if lp_ppa_user.name == launchpad.me.name:
243
ppa_user = launchpad.me
245
# check if we are a member of this team
246
team = [mem.team for mem in launchpad.me.memberships_details if mem.status in ("Approved", "Administrator") and mem.team.name == ppa_user_name]
250
raise not_ppa_owner(ppa_user_name)
252
raise user_team_not_found(ppa_user_name)
254
ppa_user = launchpad.me
255
ppa_name = full_ppa_name
256
return(ppa_user, ppa_name)
258
def choose_ppa(launchpad, ppa_name=None):
259
'''Look for right ppa parameters where to push the package'''
262
if not configurationhandler.project_config:
263
configurationhandler.loadConfig()
265
(ppa_user, ppa_name) = get_ppa_parameters(launchpad, configurationhandler.project_config['ppa'])
267
ppa_user = launchpad.me
268
if (launchpadaccess.lp_server == "staging"):
273
(ppa_user, ppa_name) = get_ppa_parameters(launchpad, ppa_name)
274
ppa_url = '%s/~%s/+archive/%s' % (launchpadaccess.LAUNCHPAD_URL, ppa_user.name, ppa_name)
275
dput_ppa_name = 'ppa:%s/%s' % (ppa_user.name, ppa_name)
276
return (ppa_user, ppa_name, dput_ppa_name, ppa_url.encode('UTF-8'))
278
def push_to_ppa(dput_ppa_name, changes_file, keyid=None):
279
""" Push some code to a ppa """
281
# creating local binary package
282
buildcommand = ["dpkg-buildpackage", "-S", "-I.bzr"]
284
buildcommand.append("-k%s" % keyid)
285
return_code = filter_exec_command(buildcommand)
287
print _("ERROR: an error occurred during source package creation")
289
# now, pushing it to launchpad personal ppa (or team later)
290
return_code = subprocess.call(["dput", dput_ppa_name, changes_file])
292
print _("ERROR: an error occurred during source upload to launchpad")
296
def get_all_ppas(launchpad, lp_team_or_user):
297
""" get all from a team or users
299
Return list of tuples (ppa_name, ppa_display_name)"""
302
for ppa in lp_team_or_user.ppas:
303
ppa_list.append((ppa.name, ppa.displayname))
306
def check_and_return_ppaname(launchpad, lp_team_or_user, ppa_name):
307
""" check whether ppa exists using its name or display name for the lp team or user
309
return formated ppaname (not display name)"""
311
# check that the owner really has this ppa:
313
for current_ppa_name, current_ppa_displayname in get_all_ppas(launchpad, lp_team_or_user):
314
if current_ppa_name == ppa_name or current_ppa_displayname == ppa_name:
318
raise ppa_not_found('ppa:%s:%s' % (lp_team_or_user.name, ppa_name.encode('UTF-8')))
319
return(current_ppa_name)
321
def updateversion(proposed_version=None, sharing=False):
322
'''Update versionning with year.month, handling intermediate release'''
325
# check manual versionning is correct
327
for number in proposed_version.split('.'):
330
msg = _("Release version specified in command arguments is not a " \
331
"valid version scheme like 'x(.y)(.z)'.")
332
raise invalid_versionning_scheme(msg)
333
new_version = proposed_version
338
old_version = quicklyutils.get_setup_value('version')
339
except quicklyutils.cant_deal_with_setup_value:
340
msg = _("No previous version found in setup.py. Put one please")
341
raise invalid_version_in_setup(msg)
343
# sharing only add -publicX to last release, no other update, no bumping
345
splitted_release_version = old_version.split("-public")
346
if len(splitted_release_version) > 1:
348
share_version = float(splitted_release_version[1])
350
msg = _("Share version specified after -public in "\
351
"setup.py is not a valid number: %s") \
352
% splitted_release_version[1]
353
raise invalid_versionning_scheme(msg)
354
new_version = splitted_release_version[0] + '-public' + \
355
str(int(share_version + 1))
357
new_version = old_version + "-public1"
359
# automatically version to year.month(.subversion)
361
base_version = datetime.datetime.now().strftime("%y.%m")
362
if base_version in old_version:
364
# try to get a minor version, removing -public if one
365
(year, month, minor_version) = old_version.split('.')
366
minor_version = minor_version.split('-public')[0]
368
minor_version = float(minor_version)
370
msg = _("Minor version specified in setup.py is not a " \
371
"valid number: %s. Fix this or specify a " \
372
"version as release command line argument") \
374
raise invalid_versionning_scheme(msg)
375
new_version = base_version + '.' + str(int(minor_version + 1))
378
# no minor version, bump to first one (be careful,
379
# old_version may contain -publicX)
380
new_version = base_version + '.1'
384
new_version = base_version
386
# write release version to setup.py and update it in aboutdialog
387
quicklyutils.set_setup_value('version', new_version)
388
about_dialog_file_name = quicklyutils.get_about_file_name()
389
if about_dialog_file_name:
390
quicklyutils.change_xml_elem(about_dialog_file_name, "object/property",
391
"name", "version", new_version, {})