Merge lp:~didrocks/quickly/install-in-opt into lp:quickly

Proposed by Didier Roche-Tolomelli
Status: Merged
Approved by: Michael Terry
Approved revision: 568
Merged at revision: 568
Proposed branch: lp:~didrocks/quickly/install-in-opt
Merge into: lp:quickly
Diff against target: 462 lines (+384/-3)
6 files modified
data/templates/ubuntu-application/internal/packaging.py (+4/-1)
data/templates/ubuntu-application/project_root/bin/project_name (+11/-1)
data/templates/ubuntu-application/submitubuntu.py (+297/-0)
data/templates/ubuntu-application/upgrade.py (+26/-0)
quickly/quicklyconfig.py (+1/-1)
quickly/templatetools.py (+45/-0)
To merge this branch: bzr merge lp:~didrocks/quickly/install-in-opt
Reviewer Review Type Date Requested Status
Michael Terry (community) Approve
Review via email: mp+40628@code.launchpad.net

Description of the change

The branch which makes the application review board happy :)

see my comment at
https://bugs.launchpad.net/ubuntu/+source/quickly/+bug/625581

Still some work to do, like create a symlink for the .desktop file and such, but anything should now be installed in /opt/<appname> with "quickly submitubuntu".

To post a comment you must log in.
Revision history for this message
Michael Terry (mterry) wrote :

+# Copyright 2009 Didier Roche

Should that be 2010 Canonical?

17 + command = ['python-mkdebian', '--force-control', '--force-rules']

You probably want to version-guard --force-rules like we did for --force-copyright.

Could we call it 'submit' rather than 'submitubuntu'?

Could we not reduce the duplicated logic between 'submitubuntu' and 'release' more?

Is there a reason we don't just default installopt to True? Seems bad for developers to be testing one thing and submitting a different one. Especially since any hard-coded paths they used will want to be different.

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

+# Copyright 2009 Didier Roche

Should that be 2010 Canonical?

No, Quickly doesn't have copyright assignment and I didn't work on that part on my Canonical time (I just added an option to it)? So keeping original file copyright make sense.

17 + command = ['python-mkdebian', '--force-control', '--force-rules']
I'll update that, and also add linking the right file.

Could we not reduce the duplicated logic between 'submitubuntu' and 'release' more?

Is there a reason we don't just default installopt to True? Seems bad for developers to be testing one thing and submitting a different one. Especially since any hard-coded paths they used will want to be different.

Did you read my comment as pointed from the original message?
https://bugs.launchpad.net/ubuntu/+source/quickly/+bug/625581/comments/10

I agree with that, but that's wont be an alpha1 target given the time I have. All the testing issue is also discussed in the comment linked above and I want that we discuss that during alpha1 and alpha2 to make submitubuntu a more useful command.

lp:~didrocks/quickly/install-in-opt updated
563. By Didier Roche-Tolomelli

add support in project_name for running application in /opt/appname

564. By Didier Roche-Tolomelli

force rules under conditions

565. By Didier Roche-Tolomelli

support upgrade for /opt support and add a function to make the safe add easy

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

pushed the previous remark fixes. Handle now upgrades as well.

I've created a convenient function for replacing some text in files between a start and end marker. Maybe some of our function (like licence) should starts using it.

lp:~didrocks/quickly/install-in-opt updated
566. By Didier Roche-Tolomelli

install in /opt/extras

Revision history for this message
Michael Terry (mterry) wrote :

/opt/extras should be /opt/extras.ubuntu.com according to the discussion in the mailing list, I believe. But you mention that you are waiting for final, final approval.

15 if get_python_mkdebian_version() > 2.22:
16 command.append("--force-copyright")
17 + if get_python_mkdebian_version() > 2.23:
18 + command.append("--force-rules")

Should just be:

if get_python_mkdebian_version() > 2.22:
    command.append("--force-copyright")
    command.append("--force-rules")

as --force-rules was added in 2.23.

+print _("Then your application will be reviewed by the application review board.")

I didn't see anything that would notify the ARB. I assume that's a TODO item?

+def update_file_content(filename, start_marker, end_marker, replacing_content):

Nice function. :) Though ideally, it would use set_file_contents() from quicklyutils.py (after moving it to templatetools).

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

/opt/extras should be /opt/extras.ubuntu.com according to the discussion in the mailing list, I believe. But you mention that you are waiting for final, final approval.

15 if get_python_mkdebian_version() > 2.22:
16 command.append("--force-copyright")
17 + if get_python_mkdebian_version() > 2.23:
18 + command.append("--force-rules")

Should just be:

if get_python_mkdebian_version() > 2.22:
    command.append("--force-copyright")
    command.append("--force-rules")

as --force-rules was added in 2.23.

oopss, fixing

+print _("Then your application will be reviewed by the application review board.")

I didn't see anything that would notify the ARB. I assume that's a TODO item?

Right, as mentionned and pending email on the ARB about "what to do with the submitubuntu command, should we always install in /opt?"

+def update_file_content(filename, start_marker, end_marker, replacing_content):

Nice function. :) Though ideally, it would use set_file_contents() from quicklyutils.py (after moving it to templatetools).

Not really the same the same, set_files_contents exchanges some values, here this one is to add/remove content between tags. the licence command should use it.

lp:~didrocks/quickly/install-in-opt updated
567. By Didier Roche-Tolomelli

force versionning

568. By Didier Roche-Tolomelli

use extras.ubuntu.com

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

pushed the new version, I mixed the update_file_content function. Well free to merge it with my function :)

Revision history for this message
Michael Terry (mterry) wrote :

> Not really the same the same, set_files_contents exchanges some values, here this one is to add/remove content between tags. the licence command should use it.

I know they aren't the same. I was proposing that your new function use the existing set_file_contents() function instead of duplicating the whole .new/os.rename stuff again.

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

it can't really use it, it should rather deprecate it as there are some operations done while going through the loop) or wrapping it, like set_file_contents call update_file_content(destfile, openmarker=None, endmarker=None, sourcefile.read()), isn't it?

Revision history for this message
Allison Randal (allison) wrote :

> /opt/extras should be /opt/extras.ubuntu.com according to the discussion in
> the mailing list, I believe. But you mention that you are waiting for final,
> final approval.

Rick asked me to comment here. It's 98% sure the Tech Board will approve /opt/extras.ubuntu.com in the meeting next Tuesday (the interested TB members were involved in the ubuntu-devel discussion, and it's the only answer that satisfies all the concerns of various people). So, safe to merge before Tuesday, if that's the last thing holding up the merge.

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

Thanks Allison, there is still the question sent to the ARB and the "do we install always in /opt or just in the submitubuntu command and what to do with that command (and rename it to "submit"), but I think we can get to that later…

Michael: ok to merge? We will merge all the with: for writing in a file function later if you don't mind (like license, renaming and replace… and so on…)

Revision history for this message
Michael Terry (mterry) wrote :

Approved, if you fix

37 + opt_path = path.replace('/usr', '/opt/extras/python_name')

To use extras.ubuntu.com

review: Approve
Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

argh, a one leftover :)
doing and done!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/templates/ubuntu-application/internal/packaging.py'
2--- data/templates/ubuntu-application/internal/packaging.py 2010-11-22 16:01:03 +0000
3+++ data/templates/ubuntu-application/internal/packaging.py 2010-11-22 16:47:10 +0000
4@@ -162,7 +162,7 @@
5 version = proc.communicate()[0]
6 return float(version)
7
8-def updatepackaging(changelog=None, no_changelog=False):
9+def updatepackaging(changelog=None, no_changelog=False, installopt=False):
10 """create or update a package using python-mkdebian.
11
12 Commit after the first packaging creation"""
13@@ -172,8 +172,11 @@
14 command = ['python-mkdebian', '--force-control']
15 if get_python_mkdebian_version() > 2.22:
16 command.append("--force-copyright")
17+ command.append("--force-rules")
18 if no_changelog:
19 command.append("--no-changelog")
20+ if installopt:
21+ command.append("--prefix=/opt/extras.ubuntu.com/%s" % configurationhandler.project_config['project'])
22 for message in changelog:
23 command.extend(["--changelog", message])
24 if not configurationhandler.project_config:
25
26=== modified file 'data/templates/ubuntu-application/project_root/bin/project_name'
27--- data/templates/ubuntu-application/project_root/bin/project_name 2010-11-17 18:44:18 +0000
28+++ data/templates/ubuntu-application/project_root/bin/project_name 2010-11-22 16:47:10 +0000
29@@ -16,10 +16,20 @@
30 PROJECT_ROOT_DIRECTORY = os.path.abspath(
31 os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0]))))
32
33+python_path = []
34+if os.path.abspath(__file__).startswith('/opt'):
35+ syspath = sys.path[:] # copy to avoid infinite loop in pending objects
36+ for path in syspath:
37+ opt_path = path.replace('/usr', '/opt/extras/python_name')
38+ python_path.insert(0, opt_path)
39+ sys.path.insert(0, opt_path)
40 if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'python_name'))
41 and PROJECT_ROOT_DIRECTORY not in sys.path):
42+ python_path.insert(0, PROJECT_ROOT_DIRECTORY)
43 sys.path.insert(0, PROJECT_ROOT_DIRECTORY)
44- os.putenv('PYTHONPATH', PROJECT_ROOT_DIRECTORY) # for subprocesses
45+if python_path:
46+ os.putenv('PYTHONPATH', "%s:%s" % (os.getenv('PYTHONPATH', ''), ':'.join(python_path))) # for subprocesses
47+
48
49 from python_name import (Basecamel_case_nameWindow)
50 import python_name.helpers as helpers
51
52=== added file 'data/templates/ubuntu-application/submitubuntu.py'
53--- data/templates/ubuntu-application/submitubuntu.py 1970-01-01 00:00:00 +0000
54+++ data/templates/ubuntu-application/submitubuntu.py 2010-11-22 16:47:10 +0000
55@@ -0,0 +1,297 @@
56+#!/usr/bin/python
57+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
58+# Copyright 2009 Didier Roche
59+#
60+# This file is part of Quickly ubuntu-application template
61+#
62+#This program is free software: you can redistribute it and/or modify it
63+#under the terms of the GNU General Public License version 3, as published
64+#by the Free Software Foundation.
65+
66+#This program is distributed in the hope that it will be useful, but
67+#WITHOUT ANY WARRANTY; without even the implied warranties of
68+#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
69+#PURPOSE. See the GNU General Public License for more details.
70+
71+#You should have received a copy of the GNU General Public License along
72+#with this program. If not, see <http://www.gnu.org/licenses/>.
73+
74+import os
75+import sys
76+import subprocess
77+import webbrowser
78+
79+from internal import quicklyutils, packaging, launchpad_helper
80+from internal import bzrutils
81+from quickly import templatetools, configurationhandler, commands
82+import license
83+
84+import logging
85+
86+from quickly import launchpadaccess
87+
88+import gettext
89+from gettext import gettext as _
90+gettext.textdomain('quickly')
91+
92+options = ["--ppa",]
93+
94+def usage():
95+ templatetools.print_usage(_('quickly submitubuntu [--ppa <ppa | group/ppa>] [release-version] [comments]'))
96+def help():
97+ print _("""Posts a release of your project and submit it the ubuntu
98+application review board so that any users can see and install the
99+application ont their system.
100+
101+Before running quickly submitubuntu, you should: create your account
102+and a project page on http://launchpad.net.
103+You also have to add a PPA to your launchpad account.
104+
105+Name, email, and version will be automatically changed in setup.py and
106+bzr will tag the current source with the new version number.
107+
108+If not specified, the new version number will be 'YEAR.MONTH[.RELEASE]'.
109+
110+For example, the third release in July 2010 would be versioned 10.07.2.
111+
112+You may want to make sure that the description and long description in
113+setup.py are up to date before releasing.
114+
115+You can optionally run 'quickly package' and test your package to make
116+sure it installs as expected.""")
117+def shell_completion(argv):
118+ ''' Complete --args '''
119+ # option completion
120+ rv = []
121+ if argv[-1].startswith("-"):
122+ rv = options
123+ elif len(argv) > 1 and argv[-2] == '--ppa': # if argument following --ppa, complete by ppa
124+ rv = packaging.shell_complete_ppa(argv[-1])
125+ if rv:
126+ rv.sort()
127+ print ' '.join(rv)
128+
129+templatetools.handle_additional_parameters(sys.argv, help, shell_completion, usage=usage)
130+
131+
132+launchpad = None
133+project = None
134+ppa_name = None
135+i = 0
136+args = []
137+argv = sys.argv
138+
139+while i < len(argv):
140+ arg = argv[i]
141+ if arg.startswith('-'):
142+ if arg == '--ppa':
143+ if i + 1 < len(argv):
144+ ppa_name = argv[i + 1]
145+ i += 1
146+ else:
147+ cmd = commands.get_command('submitubuntu', 'ubuntu-application')
148+ templatetools.usage_error(_("No PPA provided."), cmd=cmd)
149+ else:
150+ cmd = commands.get_command('submitubuntu', 'ubuntu-application')
151+ templatetools.usage_error(_("Unknown option: %s." % arg), cmd=cmd)
152+ else:
153+ args.append(arg)
154+ i += 1
155+
156+ commit_msg = None
157+if len(args) == 1:
158+ proposed_version = None
159+elif len(args) == 2:
160+ proposed_version = args[1]
161+elif len(args) > 2:
162+ proposed_version = args[1]
163+ commit_msg = " ".join(args[2:])
164+
165+# warning: project_name can be different from project.name (one local, one on launchpad)
166+if not configurationhandler.project_config:
167+ configurationhandler.loadConfig()
168+project_name = configurationhandler.project_config['project']
169+
170+# connect to LP
171+try:
172+ launchpad = launchpadaccess.initialize_lpi()
173+except launchpadaccess.launchpad_connection_error, e:
174+ print(e)
175+ sys.exit(1)
176+
177+# push the gpg key and email to the env
178+try:
179+ keyid = quicklyutils.get_right_gpg_key_id(launchpad)
180+except quicklyutils.gpg_error, e:
181+ print(e)
182+ sys.exit(1)
183+
184+# get the project now and save the url into setup.py
185+try:
186+ project = launchpadaccess.get_project(launchpad)
187+except launchpadaccess.launchpad_project_error, e:
188+ print(e)
189+ sys.exit(1)
190+project_url = launchpadaccess.launchpad_url + '/' + project.name
191+quicklyutils.set_setup_value('url', project_url)
192+about_dialog_file_name = quicklyutils.get_about_file_name()
193+if about_dialog_file_name:
194+ quicklyutils.change_xml_elem(about_dialog_file_name, "object/property",
195+ "name", "website", project_url, {})
196+
197+# choose right ppa parameter (users, etc.) ppa or staging if ppa_name is None
198+try:
199+ (ppa_user, ppa_name, dput_ppa_name, ppa_url) = packaging.choose_ppa(launchpad, ppa_name)
200+except packaging.user_team_not_found, e:
201+ print(_("User or Team %s not found on Launchpad") % e)
202+ sys.exit(1)
203+except packaging.not_ppa_owner, e:
204+ print(_("You have to be a member of %s team to upload to its ppas") % e)
205+ sys.exit(1)
206+
207+try:
208+ ppa_name = packaging.check_and_return_ppaname(launchpad, ppa_user, ppa_name) # ppa_name can be ppa name or ppa display name. Find the right one if exists
209+except packaging.ppa_not_found, e:
210+ print(_("%s does not exist. Please create it on launchpad if you want to push a package to it. %s has the following ppas available:") % (e, ppa_user.name))
211+ user_has_ppa = False
212+ for ppa_name, ppa_display_name in packaging.get_all_ppas(launchpad, ppa_user):
213+ print "%s - %s" % (ppa_name, ppa_display_name)
214+ user_has_ppa = True
215+ if user_has_ppa:
216+ print(_("You can temporary choose one of them with --ppa switch or definitely by executing quickly configure ppa <ppa_name>."))
217+ sys.exit(1)
218+
219+# update license if needed. Don't change anything if not needed
220+try:
221+ license.licensing()
222+except license.LicenceError, error_message:
223+ print(error_message)
224+ sys.exit(1)
225+
226+try:
227+ release_version = packaging.updateversion(proposed_version)
228+except (packaging.invalid_versionning_scheme,
229+ packaging.invalid_version_in_setup), error_message:
230+ print(error_message)
231+ sys.exit(1)
232+
233+if commit_msg is None:
234+ commit_msg = _('quickly released: %s' % release_version)
235+
236+
237+# check if already released with this name
238+bzr_instance = subprocess.Popen(["bzr", "tags"], stdout=subprocess.PIPE)
239+bzr_tags, err = bzr_instance.communicate()
240+if bzr_instance.returncode !=0:
241+ print(err)
242+ sys.exit(1)
243+if release_version in bzr_tags:
244+ print _("ERROR: quickly can't release: %s seems to be already released. Choose another name.") % release_version
245+ sys.exit(1)
246+
247+# commit current changes
248+packaging.filter_exec_command(["bzr", "add"])
249+return_code = packaging.filter_exec_command(["bzr", "commit", '--unchanged', '-m',
250+ _('commit before release')])
251+if return_code != 0 and return_code != 3:
252+ print _("ERROR: quickly can't release as it can't commit with bzr")
253+ sys.exit(return_code)
254+
255+# try to get last available version in bzr
256+previous_version = None
257+bzr_instance = subprocess.Popen(['bzr', 'tags', '--sort=time'],
258+ stdout=subprocess.PIPE)
259+result, err = bzr_instance.communicate()
260+if bzr_instance.returncode == 0 and result:
261+ output = result.split('\n')
262+ output.reverse()
263+ for tag_line in output:
264+ tag_elem = tag_line.split (' ')
265+ if not (tag_elem[-1] == '?' or tag_elem[-1] == ''):
266+ previous_version = tag_elem[0]
267+ break
268+
269+changelog = quicklyutils.collect_commit_messages(previous_version)
270+# creation/update debian packaging
271+return_code = packaging.updatepackaging(changelog, installopt=True)
272+if return_code != 0:
273+ print _("ERROR: can't create or update ubuntu package")
274+ sys.exit(1)
275+
276+# add files, setup release version, commit and push !
277+#TODO: check or fix if we don't have an ssh key (don't tag otherwise to be able to release again)
278+packaging.filter_exec_command(["bzr", "add"])
279+return_code = packaging.filter_exec_command(["bzr", "commit", '-m', commit_msg])
280+if return_code != 0 and return_code != 3:
281+ print _("ERROR: quickly can't release as it can't commit with bzr")
282+ sys.exit(return_code)
283+packaging.filter_exec_command(["bzr", "tag", release_version]) # tag revision
284+
285+# check if pull branch is set
286+bzr_instance = subprocess.Popen(["bzr", "info"], stdout=subprocess.PIPE)
287+bzr_info, err = bzr_instance.communicate()
288+if bzr_instance.returncode !=0:
289+ print(err)
290+ sys.exit(1)
291+
292+
293+if (launchpadaccess.lp_server == "staging"):
294+ bzr_staging = "//staging/"
295+else:
296+ bzr_staging = ""
297+
298+custom_location_in_info = None
299+branch_location = []
300+custom_location = bzrutils.get_bzrbranch()
301+if custom_location:
302+ branch_location = [custom_location]
303+ custom_location_in_info = custom_location.replace('lp:', '')
304+# if no branch, create it in ~user_name/project_name/quickly_trunk
305+# or switch from staging to production
306+if ("parent branch" in bzr_info) and not (
307+ (custom_location_in_info and custom_location_in_info not in bzr_info) or
308+ ((".staging." in bzr_info) and not bzr_staging) or
309+ (not (".staging." in bzr_info) and bzr_staging)):
310+ return_code = packaging.filter_exec_command(["bzr", "pull"])
311+ if return_code != 0:
312+ print _("ERROR: quickly can't release: can't pull from launchpad.")
313+ sys.exit(return_code)
314+
315+ return_code = packaging.filter_exec_command(["bzr", "push"])
316+ if return_code != 0:
317+ print _("ERROR: quickly can't release: can't push to launchpad.")
318+ sys.exit(return_code)
319+else:
320+ if not branch_location:
321+ branch_location = ['lp:', bzr_staging, '~', launchpad.me.name, '/', project.name, '/quickly_trunk']
322+ return_code = packaging.filter_exec_command(["bzr", "push", "--remember", "--overwrite", "".join(branch_location)])
323+ if return_code != 0:
324+ print _("ERROR: quickly can't release: can't push to launchpad.")
325+ sys.exit(return_code)
326+
327+ # make first pull too
328+ return_code = packaging.filter_exec_command(["bzr", "pull", "--remember", "".join(branch_location)])
329+ if return_code != 0:
330+ print _("ERROR: quickly can't release correctly: can't pull from launchpad.")
331+ sys.exit(return_code)
332+
333+
334+# upload to launchpad
335+print _("pushing to launchpad")
336+return_code = packaging.push_to_ppa(dput_ppa_name, "../%s_%s_source.changes" % (project_name, release_version), keyid=keyid) != 0
337+if return_code != 0:
338+ sys.exit(return_code)
339+
340+#create new release_date
341+launchpad_helper.push_tarball_to_launchpad(project, release_version,
342+ "../%s_%s.tar.gz" % (project_name,
343+ release_version), changelog)
344+
345+print _("%s %s released and submitted to ubuntu. Wait for half an hour and have look at %s.") % (project_name, release_version, ppa_url)
346+print _("Then your application will be reviewed by the application review board.")
347+
348+# as launchpad-open doesn't support staging server, put an url
349+if launchpadaccess.lp_server == "staging":
350+ webbrowser.open(launchpadaccess.LAUNCHPAD_CODE_STAGING_URL + '/' + project.name)
351+else:
352+ webbrowser.open(launchpadaccess.LAUNCHPAD_URL + '/' + project.name)
353
354=== modified file 'data/templates/ubuntu-application/upgrade.py'
355--- data/templates/ubuntu-application/upgrade.py 2010-09-28 12:53:04 +0000
356+++ data/templates/ubuntu-application/upgrade.py 2010-11-22 16:47:10 +0000
357@@ -171,4 +171,30 @@
358 os.system("find . -name '*.py' -exec %s {} \;" % sedline)
359 os.system("%s bin/%s" % (sedline, project_name))
360
361+### 0.7 update
362+if project_version < '0.7':
363+ # support /opt installation
364+ content_to_update = '''python_path = []
365+if os.path.abspath(__file__).startswith('/opt'):
366+ syspath = sys.path[:] # copy to avoid infinite loop in pending objects
367+ for path in syspath:
368+ opt_path = path.replace('/usr', '/opt/extras.ubuntu.com/%(python_name)s')
369+ python_path.insert(0, opt_path)
370+ sys.path.insert(0, opt_path)
371+if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, '%(python_name)s'))
372+ and PROJECT_ROOT_DIRECTORY not in sys.path):
373+ python_path.insert(0, PROJECT_ROOT_DIRECTORY)
374+ sys.path.insert(0, PROJECT_ROOT_DIRECTORY)
375+if python_path:
376+ os.putenv('PYTHONPATH', "%%s:%%s" %% (os.getenv('PYTHONPATH', ''), ':'.join(python_path))) # for subprocesses''' % {'python_name' : python_name}
377+
378+ try:
379+ templatetools.update_file_content("./bin/%s" % project_name,
380+ 'if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY',
381+ " os.putenv('PYTHONPATH', PROJECT_ROOT_DIRECTORY) # for subprocesses",
382+ content_to_update)
383+ except templatetools.CantUpdateFile, e:
384+ print _("WARNING: can't update your project to support /opt. This doesn't mind if you don't plan to submit your project to the application review board. Cause is: %s" % e)
385+
386+
387 sys.exit(0)
388
389=== modified file 'quickly/quicklyconfig.py'
390--- quickly/quicklyconfig.py 2010-10-06 09:52:51 +0000
391+++ quickly/quicklyconfig.py 2010-11-22 16:47:10 +0000
392@@ -20,7 +20,7 @@
393 # you're warned :)
394
395 # quickly version used for project format compatibility
396-__version__ = '0.6.1'
397+__version__ = '0.7'
398
399 # where quickly will head for quickly data (for instance, templates)
400 # by default, this is ../data, relative to trunk layout
401
402=== modified file 'quickly/templatetools.py'
403--- quickly/templatetools.py 2010-10-15 20:26:19 +0000
404+++ quickly/templatetools.py 2010-11-22 16:47:10 +0000
405@@ -31,6 +31,11 @@
406
407 class bad_project_name(Exception):
408 pass
409+class CantUpdateFile(Exception):
410+ def __init__(self, msg):
411+ self.msg = msg
412+ def __str__(self):
413+ return self.msg
414
415 def handle_additional_parameters(args, help=None, shell_completion=None, usage=None):
416 """Enable handling additional parameter like help or shell_completion"""
417@@ -88,6 +93,46 @@
418 mode = stat.S_IMODE(st.st_mode)
419 os.chmod(dest_file_name, mode)
420
421+def update_file_content(filename, start_marker, end_marker, replacing_content):
422+ """Safely replace the content of a file"""
423+
424+ skip_until_end_found = False
425+ marker_found = False
426+ try:
427+ filename = os.path.abspath(filename)
428+ ftarget_file_name = file(filename, 'r')
429+ ftarget_file_name_out = file(ftarget_file_name.name + '.new', 'w')
430+ for line in ftarget_file_name:
431+ # seek if we have to add something
432+ if start_marker in line:
433+ skip_until_end_found = True
434+ marker_found = True
435+ ftarget_file_name_out.write(replacing_content)
436+
437+ if end_marker in line:
438+ skip_until_end_found = False
439+
440+ if not skip_until_end_found:
441+ ftarget_file_name_out.write(line)
442+
443+ ftarget_file_name.close()
444+ ftarget_file_name_out.close()
445+
446+ if skip_until_end_found: # that means we didn't find the end_tag, don't copy the file
447+ os.remove(ftarget_file_name_out.name)
448+ raise CantUpdateFile(_("%s was not found in the file %s.") % (end_marker, ftarget_file_name.name))
449+
450+ if not marker_found:
451+ os.remove(ftarget_file_name_out.name)
452+ raise CantUpdateFile(_("%s was not found in the file %s.") % (start_marker, ftarget_file_name.name))
453+
454+ apply_file_rights(ftarget_file_name.name, ftarget_file_name_out.name)
455+ os.rename(ftarget_file_name_out.name, ftarget_file_name.name)
456+
457+ except (OSError, IOError), e:
458+ msg = _("%s file was not found or can't update it") % ftarget_file_name
459+ raise CantUpdateFile(msg)
460+
461 def in_verbose_mode():
462 """Return true if verbose mode is on"""
463

Subscribers

People subscribed via source and target branches