Merge lp:~sil/quickly/add-flash-game-template into lp:quickly
- add-flash-game-template
- Merge into trunk
Proposed by
Stuart Langridge
Status: | Merged |
---|---|
Merged at revision: | 581 |
Proposed branch: | lp:~sil/quickly/add-flash-game-template |
Merge into: | lp:quickly |
Diff against target: |
1653 lines (+1578/-0) 14 files modified
data/templates/flash-game/commandsconfig (+10/-0) data/templates/flash-game/create.py (+158/-0) data/templates/flash-game/internal/SWF.py (+89/-0) data/templates/flash-game/internal/apportutils.py (+143/-0) data/templates/flash-game/internal/bzrutils.py (+37/-0) data/templates/flash-game/internal/launchpad_helper.py (+92/-0) data/templates/flash-game/internal/packaging.py (+407/-0) data/templates/flash-game/internal/quicklyutils.py (+411/-0) data/templates/flash-game/package.py (+73/-0) data/templates/flash-game/project_root/AUTHORS (+1/-0) data/templates/flash-game/project_root/bin/project_name (+71/-0) data/templates/flash-game/project_root/data/index.html (+9/-0) data/templates/flash-game/project_root/project_name.desktop.in (+8/-0) data/templates/flash-game/project_root/setup.py (+69/-0) |
To merge this branch: | bzr merge lp:~sil/quickly/add-flash-game-template |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Didier Roche-Tolomelli | Approve | ||
Review via email: mp+45059@code.launchpad.net |
Commit message
Description of the change
Allow creation of a Quickly project to wrap an existing SWF Flash file into a working Ubuntu application, by displaying it in a Gtk window, and allowing Quickly's easy-to-do packaging and release and so on.
To post a comment you must log in.
- 584. By Stuart Langridge
-
Add a .desktop file for the packaged game
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'data/templates/flash-game' |
2 | === added file 'data/templates/flash-game/commandsconfig' |
3 | --- data/templates/flash-game/commandsconfig 1970-01-01 00:00:00 +0000 |
4 | +++ data/templates/flash-game/commandsconfig 2011-01-03 19:29:40 +0000 |
5 | @@ -0,0 +1,10 @@ |
6 | +# define parameters for commands, putting them in a list seperated |
7 | +# by ';' |
8 | +# if nothing specified, default is to launch command inside a project |
9 | +# only and not be followed by a template |
10 | +#COMMANDS_LAUNCHED_IN_OR_OUTSIDE_PROJECT = |
11 | +COMMANDS_LAUNCHED_OUTSIDE_PROJECT_ONLY = create |
12 | +#COMMANDS_FOLLOWED_BY_COMMAND = |
13 | + |
14 | +[ubuntu-application] |
15 | +IMPORT=configure;create;debug;edit;license;package;release;run;save;share |
16 | |
17 | === added file 'data/templates/flash-game/create.py' |
18 | --- data/templates/flash-game/create.py 1970-01-01 00:00:00 +0000 |
19 | +++ data/templates/flash-game/create.py 2011-01-03 19:29:40 +0000 |
20 | @@ -0,0 +1,158 @@ |
21 | +#!/usr/bin/python |
22 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
23 | +# Copyright 2009 Didier Roche |
24 | +# |
25 | +# This file is part of Quickly ubuntu-application template |
26 | +# |
27 | +#This program is free software: you can redistribute it and/or modify it |
28 | +#under the terms of the GNU General Public License version 3, as published |
29 | +#by the Free Software Foundation. |
30 | + |
31 | +#This program is distributed in the hope that it will be useful, but |
32 | +#WITHOUT ANY WARRANTY; without even the implied warranties of |
33 | +#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
34 | +#PURPOSE. See the GNU General Public License for more details. |
35 | + |
36 | +#You should have received a copy of the GNU General Public License along |
37 | +#with this program. If not, see <http://www.gnu.org/licenses/>. |
38 | + |
39 | +import sys |
40 | +import os |
41 | +import shutil |
42 | +import subprocess |
43 | + |
44 | +from quickly import templatetools |
45 | +from internal import quicklyutils, SWF |
46 | + |
47 | +import gettext |
48 | +from gettext import gettext as _ |
49 | +# set domain text |
50 | +gettext.textdomain('quickly') |
51 | + |
52 | + |
53 | + |
54 | +def help(): |
55 | + print _("""Usage: |
56 | +$ quickly create flash-game path/to/project_name path/to/myflashgame.swf |
57 | + |
58 | +where "project_name" is one or more words separated by an underscore and |
59 | +path/to can be any existing path. |
60 | + |
61 | +This will create a new project which runs your existing SWF, myflashgame.swf, |
62 | +on the Ubuntu desktop, and makes it ready to be packaged and distributed in |
63 | +the Ubuntu Software Centre. |
64 | + |
65 | +After creating the project, you'll want to specify the title of your game |
66 | +and the size of the window: |
67 | + |
68 | +1. Changing your working directory to the new project: |
69 | +$ cd path/to/project_name |
70 | + |
71 | +3. Edit the code and specify the title and window size: |
72 | +$ quickly edit |
73 | +""") |
74 | +templatetools.handle_additional_parameters(sys.argv, help) |
75 | + |
76 | + |
77 | +# get the name of the project |
78 | +if len(sys.argv) < 2: |
79 | + print _("""Project name not defined.\nUsage is: quickly create flash-game project_name myflashgame.swf""") |
80 | + sys.exit(4) |
81 | + |
82 | +if len(sys.argv) < 3: |
83 | + print _("""Flash SWF file not defined.\nUsage is: quickly create flash-game project_name myflashgame.swf""") |
84 | + sys.exit(5) |
85 | + |
86 | +path_and_project = sys.argv[1].split('/') |
87 | +project_name = path_and_project[-1] |
88 | + |
89 | +swf = os.path.realpath(sys.argv[2]) |
90 | +if not os.path.exists(swf): |
91 | + print _("""Flash SWF file '%s' not found.\nUsage is: quickly create flash-game project_name myflashgame.swf""" % swf) |
92 | + sys.exit(6) |
93 | + |
94 | +# check that project name follow quickly rules and reformat it. |
95 | +try: |
96 | + project_name = templatetools.quickly_name(project_name) |
97 | +except templatetools.bad_project_name, e: |
98 | + print(e) |
99 | + sys.exit(1) |
100 | + |
101 | +os.chdir(project_name) |
102 | + |
103 | +# get origin path |
104 | +pathname = templatetools.get_template_path_from_project() |
105 | +abs_path_project_root = os.path.join(pathname, 'project_root') |
106 | + |
107 | +python_name = templatetools.python_name(project_name) |
108 | +sentence_name, camel_case_name = quicklyutils.conventional_names(project_name) |
109 | + |
110 | +# Calculate the SWF's dimensions |
111 | +try: |
112 | + width, height = SWF.dimensions(swf) |
113 | +except SWF.SWFNotASWF: |
114 | + print "File '%s' does not seem to be a SWF. Terminating." |
115 | + sys.exit(7) |
116 | +except SWF.SWFNoDimensions: |
117 | + print "(Could not read size from SWF file; defaulting to 640x480. You should edit bin/%s with the correct size.)" % project_name |
118 | + width, height = (640, 480) |
119 | + |
120 | + |
121 | +substitutions = (("project_name",project_name), |
122 | + ("camel_case_name",camel_case_name), |
123 | + ("python_name",python_name), |
124 | + ("sentence_name",sentence_name), |
125 | + ("swf_height",str(height)), |
126 | + ("swf_width",str(width)), |
127 | + ) |
128 | + |
129 | + |
130 | +for root, dirs, files in os.walk(abs_path_project_root): |
131 | + try: |
132 | + relative_dir = root.split('project_root/')[1] |
133 | + except: |
134 | + relative_dir = "" |
135 | + # python dir should be replace by python_name (project "pythonified" name) |
136 | + if relative_dir.startswith('python'): |
137 | + relative_dir = relative_dir.replace('python', python_name) |
138 | + |
139 | + for directory in dirs: |
140 | + if directory == 'python': |
141 | + directory = python_name |
142 | + os.mkdir(os.path.join(relative_dir, directory)) |
143 | + for filename in files: |
144 | + quicklyutils.file_from_template(root, filename, relative_dir, substitutions) |
145 | + |
146 | +# set the mode to executable for executable file |
147 | +exec_file = os.path.join('bin', project_name) |
148 | +try: |
149 | + os.chmod(exec_file, 0755) |
150 | +except: |
151 | + pass |
152 | + |
153 | +# Copy the specified SWF file into the project |
154 | +shutil.copyfile(swf, os.path.join("data", "game.swf")) |
155 | + |
156 | +# We require a specific version of the ubuntu-application template, so |
157 | +# edit the project's .quickly file to specify it. |
158 | +#WORKAROUND |
159 | +fp = open(".quickly", "a") |
160 | +fp.write("\nversion_ubuntu-application = 0.4\n") |
161 | +fp.close() |
162 | + |
163 | +# add it to revision control |
164 | +print _("Creating bzr repository and commiting") |
165 | +bzr_instance = subprocess.Popen(["bzr", "init"], stdout=subprocess.PIPE) |
166 | +bzr_instance.wait() |
167 | +bzr_instance = subprocess.Popen(["bzr", "add"], stdout=subprocess.PIPE) |
168 | +bzr_instance.wait() |
169 | +subprocess.Popen(["bzr", "commit", "-m", "Initial project creation with Quickly!"], stderr=subprocess.PIPE) |
170 | + |
171 | +# run the new application if X display |
172 | +if templatetools.is_X_display() and os.path.isfile(exec_file): |
173 | + print _("Launching your newly created project!") |
174 | + subprocess.call(['./' + project_name], cwd='bin/') |
175 | + |
176 | +print _("Congratulations, your new project is set up! cd %s/ to edit the details.") % os.getcwd() |
177 | + |
178 | +sys.exit(0) |
179 | |
180 | === added directory 'data/templates/flash-game/internal' |
181 | === added file 'data/templates/flash-game/internal/SWF.py' |
182 | --- data/templates/flash-game/internal/SWF.py 1970-01-01 00:00:00 +0000 |
183 | +++ data/templates/flash-game/internal/SWF.py 2011-01-03 19:29:40 +0000 |
184 | @@ -0,0 +1,89 @@ |
185 | +import struct, zlib |
186 | + |
187 | +class SWFNotASWF(Exception): pass |
188 | +class SWFNoDimensions(Exception): pass |
189 | + |
190 | +def parse(input): |
191 | + """Parses the header information from an SWF file. |
192 | + Code from http://pypi.python.org/pypi/hexagonit.swfheader, GPL licenced.""" |
193 | + if hasattr(input, 'read'): |
194 | + input.seek(0) |
195 | + else: |
196 | + input = open(input, 'rb') |
197 | + |
198 | + def read_ui8(c): |
199 | + return struct.unpack('<B', c)[0] |
200 | + def read_ui16(c): |
201 | + return struct.unpack('<H', c)[0] |
202 | + def read_ui32(c): |
203 | + return struct.unpack('<I', c)[0] |
204 | + |
205 | + header = {} |
206 | + |
207 | + # Read the 3-byte signature field |
208 | + signature = ''.join(struct.unpack('<3c', input.read(3))) |
209 | + if signature not in ('FWS', 'CWS'): |
210 | + raise ValueError('Invalid SWF signature: %s' % signature) |
211 | + |
212 | + # Compression |
213 | + header['compressed'] = signature.startswith('C') |
214 | + |
215 | + # Version |
216 | + header['version'] = read_ui8(input.read(1)) |
217 | + |
218 | + # File size (stored as a 32-bit integer) |
219 | + header['size'] = read_ui32(input.read(4)) |
220 | + |
221 | + # Payload |
222 | + buffer = input.read(header['size']) |
223 | + if header['compressed']: |
224 | + # Unpack the zlib compression |
225 | + buffer = zlib.decompress(buffer) |
226 | + |
227 | + # Containing rectangle (struct RECT) |
228 | + |
229 | + # The number of bits used to store the each of the RECT values are |
230 | + # stored in first five bits of the first byte. |
231 | + nbits = read_ui8(buffer[0]) >> 3 |
232 | + |
233 | + current_byte, buffer = read_ui8(buffer[0]), buffer[1:] |
234 | + bit_cursor = 5 |
235 | + |
236 | + for item in 'xmin', 'xmax', 'ymin', 'ymax': |
237 | + value = 0 |
238 | + for value_bit in range(nbits-1, -1, -1): # == reversed(range(nbits)) |
239 | + if (current_byte << bit_cursor) & 0x80: |
240 | + value |= 1 << value_bit |
241 | + # Advance the bit cursor to the next bit |
242 | + bit_cursor += 1 |
243 | + |
244 | + if bit_cursor > 7: |
245 | + # We've exhausted the current byte, consume the next one |
246 | + # from the buffer. |
247 | + current_byte, buffer = read_ui8(buffer[0]), buffer[1:] |
248 | + bit_cursor = 0 |
249 | + |
250 | + # Convert value from TWIPS to a pixel value |
251 | + header[item] = value / 20 |
252 | + |
253 | + header['width'] = header['xmax'] - header['xmin'] |
254 | + header['height'] = header['ymax'] - header['ymin'] |
255 | + |
256 | + header['frames'] = read_ui16(buffer[0:2]) |
257 | + header['fps'] = read_ui16(buffer[2:4]) |
258 | + |
259 | + input.close() |
260 | + return header |
261 | + |
262 | + |
263 | +def dimensions(swf): |
264 | + """Read the dimensions of a SWF, as per the Adobe spec. |
265 | + Spec downloaded from http://www.adobe.com/devnet/swf.html.""" |
266 | + try: |
267 | + details = parse(swf) |
268 | + except: |
269 | + raise SWFNotASWF |
270 | + try: |
271 | + return (details["width"], details["height"]) |
272 | + except: |
273 | + raise SWFNoDimensions |
274 | |
275 | === added file 'data/templates/flash-game/internal/__init__.py' |
276 | === added file 'data/templates/flash-game/internal/apportutils.py' |
277 | --- data/templates/flash-game/internal/apportutils.py 1970-01-01 00:00:00 +0000 |
278 | +++ data/templates/flash-game/internal/apportutils.py 2011-01-03 19:29:40 +0000 |
279 | @@ -0,0 +1,143 @@ |
280 | +import os |
281 | +import shutil |
282 | +import subprocess |
283 | + |
284 | +from gettext import gettext as _ |
285 | + |
286 | +import quickly |
287 | +import quicklyutils |
288 | + |
289 | +from lxml import etree |
290 | + |
291 | +LPI_import_block = """ |
292 | +# optional Launchpad integration |
293 | +# this shouldn't crash if not found as it is simply used for bug reporting |
294 | +try: |
295 | + import LaunchpadIntegration |
296 | + launchpad_available = True |
297 | +except: |
298 | + launchpad_available = False |
299 | + |
300 | +""" |
301 | + |
302 | +LPI_init_menu_block = """ |
303 | + global launchpad_available |
304 | + if launchpad_available: |
305 | + # see https://wiki.ubuntu.com/UbuntuDevelopment/Internationalisation/Coding for more information |
306 | + # about LaunchpadIntegration |
307 | + helpmenu = self.builder.get_object('%(help_menu)s') |
308 | + if helpmenu: |
309 | + LaunchpadIntegration.set_sourcepackagename('%(project_name)s') |
310 | + LaunchpadIntegration.add_items(helpmenu, 0, False, True) |
311 | + else: |
312 | + launchpad_available = False""" |
313 | + |
314 | +def update_apport(project_name, old_lp_project, new_lp_project): |
315 | + if not new_lp_project: |
316 | + return |
317 | + # crashdb file doesn't support spaces or dashes in the crash db name |
318 | + safe_project_name = project_name.replace(" ", "_").replace("-","_") |
319 | + crashdb_file = "%s-crashdb.conf"%project_name |
320 | + hook_file = "source_%s.py"%project_name |
321 | + |
322 | + |
323 | + pathname = quickly.templatetools.get_template_path_from_project() |
324 | + template_pr_path = os.path.join(os.path.abspath(pathname), "store", |
325 | + "apport") |
326 | + relative_crashdb_dir = os.path.join("etc", "apport", "crashdb.conf.d") |
327 | + relative_apport_dir = "apport" |
328 | + |
329 | + existing_crashdb = os.path.join(relative_crashdb_dir, crashdb_file) |
330 | + existing_hook = os.path.join(relative_apport_dir, hook_file) |
331 | + |
332 | + template_crashdb_dir = os.path.join(template_pr_path, relative_crashdb_dir) |
333 | + template_hook_dir = os.path.join(template_pr_path, relative_apport_dir) |
334 | + |
335 | + # if the project name has changed, or any of the files are missing, then |
336 | + # attempt to set up the apport configuration and hooks |
337 | + if not old_lp_project == new_lp_project \ |
338 | + or not os.path.isfile(existing_crashdb) \ |
339 | + or not os.path.isfile(existing_hook): |
340 | + |
341 | + subst_existing = ((old_lp_project, new_lp_project),) |
342 | + subst_new = ( ("safe_project_name", safe_project_name), |
343 | + ("project_name", project_name), |
344 | + ("lp_project", new_lp_project)) |
345 | + |
346 | + if os.path.isfile(existing_crashdb): |
347 | + print _("Updating project name references in existing apport crashdb configuration") |
348 | + quicklyutils.file_from_template(relative_crashdb_dir, crashdb_file, relative_crashdb_dir, subst_existing) |
349 | + elif os.path.isdir(template_crashdb_dir): |
350 | + print _("Creating new apport crashdb configuration") |
351 | + if not os.path.isdir(relative_crashdb_dir): |
352 | + os.makedirs(relative_crashdb_dir) |
353 | + quicklyutils.file_from_template(template_crashdb_dir, "project_name-crashdb.conf", relative_crashdb_dir, subst_new) |
354 | + |
355 | + if not os.path.isfile(existing_hook) and os.path.isdir(template_hook_dir): |
356 | + print _("Creating new apport hooks") |
357 | + if not os.path.isdir(relative_apport_dir): |
358 | + os.makedirs(relative_apport_dir) |
359 | + quicklyutils.file_from_template(template_hook_dir, "source_project_name.py", relative_apport_dir, subst_new) |
360 | + |
361 | +def insert_lpi_if_required(project_name): |
362 | + existing_bin_filename = os.path.join("bin",project_name) |
363 | + camel_case_project_name = quickly.templatetools.get_camel_case_name(project_name) |
364 | + existing_ui_filename = os.path.join("data","ui", "%sWindow.ui"%camel_case_project_name) |
365 | + |
366 | + if os.path.isfile(existing_bin_filename) and os.path.isfile(existing_ui_filename): |
367 | + tree = etree.parse(existing_ui_filename) |
368 | + help_menu = find_about_menu(tree) |
369 | + |
370 | + if help_menu: |
371 | + existing_bin_file = file(existing_bin_filename, "r") |
372 | + existing_lines = existing_bin_file.readlines() |
373 | + existing_bin_file.close() |
374 | + new_lines = detect_or_insert_lpi(existing_lines, project_name, help_menu) |
375 | + if new_lines: |
376 | + print _("Adding launchpad integration to existing application") |
377 | + ftarget_file_name_out = file(existing_bin_file.name + '.new', 'w') |
378 | + ftarget_file_name_out.writelines(new_lines) |
379 | + ftarget_file_name_out.close() |
380 | + quickly.templatetools.apply_file_rights(existing_bin_file.name, ftarget_file_name_out.name) |
381 | + os.rename(ftarget_file_name_out.name, existing_bin_file.name) |
382 | + return True |
383 | + return False |
384 | + |
385 | +def detect_or_insert_lpi(existing_lines, project_name, help_menu): |
386 | + integration_present = False |
387 | + import_insert_line = None |
388 | + init_insert_line = None |
389 | + current_line = 0 |
390 | + for line in existing_lines: |
391 | + if "import LaunchpadIntegration" in line \ |
392 | + or "if launchpad_available:" in line: |
393 | + integration_present = True |
394 | + break |
395 | + if not import_insert_line and "import gtk" in line: |
396 | + import_insert_line = current_line |
397 | + if not init_insert_line and "self.builder.connect_signals(self)" in line: |
398 | + init_insert_line = current_line |
399 | + current_line += 1 |
400 | + |
401 | + if not integration_present \ |
402 | + and import_insert_line \ |
403 | + and init_insert_line \ |
404 | + and import_insert_line < init_insert_line: |
405 | + init_menu_block = LPI_init_menu_block%{"project_name":project_name, "help_menu":help_menu} |
406 | + existing_lines = existing_lines[:import_insert_line+1] + \ |
407 | + ["%s\n"%l for l in LPI_import_block.splitlines()] + \ |
408 | + existing_lines[import_insert_line+1:init_insert_line+1] + \ |
409 | + ["%s\n"%l for l in init_menu_block.splitlines()] + \ |
410 | + existing_lines[init_insert_line+1:] |
411 | + return existing_lines |
412 | + else: |
413 | + return None |
414 | + |
415 | + |
416 | +def find_about_menu(tree): |
417 | + """Finds the current help menu in the passed xml document by looking for the gtk-about element""" |
418 | + help_item = tree.xpath('//property[@name="label" and .="gtk-about"]/../../../@id') |
419 | + if len(help_item) == 1: # only one element matching this should be found |
420 | + return help_item[0] |
421 | + else: |
422 | + return None |
423 | |
424 | === added file 'data/templates/flash-game/internal/bzrutils.py' |
425 | --- data/templates/flash-game/internal/bzrutils.py 1970-01-01 00:00:00 +0000 |
426 | +++ data/templates/flash-game/internal/bzrutils.py 2011-01-03 19:29:40 +0000 |
427 | @@ -0,0 +1,37 @@ |
428 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
429 | +# Copyright 2010 Didier Roche |
430 | +# |
431 | +# This file is part of Quickly ubuntu-application template |
432 | +# |
433 | +#This program is free software: you can redistribute it and/or modify it |
434 | +#under the terms of the GNU General Public License version 3, as published |
435 | +#by the Free Software Foundation. |
436 | + |
437 | +#This program is distributed in the hope that it will be useful, but |
438 | +#WITHOUT ANY WARRANTY; without even the implied warranties of |
439 | +#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
440 | +#PURPOSE. See the GNU General Public License for more details. |
441 | + |
442 | +#You should have received a copy of the GNU General Public License along |
443 | +#with this program. If not, see <http://www.gnu.org/licenses/>. |
444 | + |
445 | +from quickly import configurationhandler |
446 | + |
447 | +def set_bzrbranch(bzr_branch): |
448 | + '''set default bzr branch from where to pull and push''' |
449 | + |
450 | + if not configurationhandler.project_config: |
451 | + configurationhandler.loadConfig() |
452 | + configurationhandler.project_config['bzrbranch'] = bzr_branch |
453 | + |
454 | +def get_bzrbranch(): |
455 | + '''get default bzr branch from where to pull and push''' |
456 | + |
457 | + if not configurationhandler.project_config: |
458 | + configurationhandler.loadConfig() |
459 | + |
460 | + try: |
461 | + bzr_branch = configurationhandler.project_config['bzrbranch'] |
462 | + except KeyError: |
463 | + bzr_branch = None |
464 | + return bzr_branch |
465 | |
466 | === added file 'data/templates/flash-game/internal/launchpad_helper.py' |
467 | --- data/templates/flash-game/internal/launchpad_helper.py 1970-01-01 00:00:00 +0000 |
468 | +++ data/templates/flash-game/internal/launchpad_helper.py 2011-01-03 19:29:40 +0000 |
469 | @@ -0,0 +1,92 @@ |
470 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
471 | +# Copyright 2010 Didier Roche, some part based on |
472 | +# Martin Pitt <martin.pitt@ubuntu.com> |
473 | +# and http://blog.launchpad.net/api/recipe-for-uploading-files-via-the-api |
474 | +# |
475 | +# This file is part of Quickly ubuntu-application template |
476 | +# |
477 | +#This program is free software: you can redistribute it and/or modify it |
478 | +#under the terms of the GNU General Public License version 3, as published |
479 | +#by the Free Software Foundation. |
480 | + |
481 | +#This program is distributed in the hope that it will be useful, but |
482 | +#WITHOUT ANY WARRANTY; without even the implied warranties of |
483 | +#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
484 | +#PURPOSE. See the GNU General Public License for more details. |
485 | + |
486 | +#You should have received a copy of the GNU General Public License along |
487 | +#with this program. If not, see <http://www.gnu.org/licenses/>. |
488 | + |
489 | +import datetime |
490 | +import os |
491 | +import sys |
492 | +import subprocess |
493 | + |
494 | +import gettext |
495 | +from gettext import gettext as _ |
496 | +gettext.textdomain('quickly') |
497 | + |
498 | +#TODO: see if 0 release in the project |
499 | + |
500 | +def create_release(project, version): |
501 | + '''Create new release and milestone for LP project. |
502 | + |
503 | + If more than one release already exists, take the last one.''' |
504 | + |
505 | + release_date = datetime.date.today().strftime('%Y-%m-%d') |
506 | + if len(project.series) == 0: |
507 | + print "No serie is not supported right now. Not uploading the tarball for you" |
508 | + sys.exit(1) |
509 | + serie = project.series[project.series.total_size - 1] |
510 | + milestone = serie.newMilestone(name=version, |
511 | + date_targeted=release_date) |
512 | + return milestone.createProductRelease(date_released=release_date) |
513 | + |
514 | +def push_tarball_to_launchpad(project, version, tarball, changelog_content): |
515 | + '''Push new tarball to Launchpad, create release if needed and sign it''' |
516 | + |
517 | + # Find the release in the project's releases collection. |
518 | + release = None |
519 | + for rel in project.releases: |
520 | + if rel.version == version: |
521 | + release = rel |
522 | + break |
523 | + if not release: |
524 | + release = create_release(project, version) |
525 | + |
526 | + # Get the file contents. |
527 | + file_content = open(tarball, 'r').read() |
528 | + # Get the signature, if available. |
529 | + signature = tarball + '.asc' |
530 | + if not os.path.exists(signature): |
531 | + print _('Calling GPG to create tarball signature...') |
532 | + if subprocess.call(['gpg', '--armor', '--sign', '--detach-sig', |
533 | + tarball]) != 0: |
534 | + sys.stderr.write(_('Signing the tarball failed, not uploading the ' \ |
535 | + 'signature')) |
536 | + |
537 | + if os.path.exists(signature): |
538 | + signature_content = open(signature, 'r').read() |
539 | + else: |
540 | + signature_content = None |
541 | + |
542 | + # Create a new product release file. |
543 | + tarball_pretty_name = os.path.basename(tarball) |
544 | + signature_pretty_name = os.path.basename(signature) |
545 | + release.add_file(filename=tarball_pretty_name, description='%s tarball' % version, |
546 | + file_content=file_content, content_type='appplication/x-gzip', |
547 | + file_type='Code Release Tarball', signature_filename=signature_pretty_name, |
548 | + signature_content=signature_content) |
549 | + |
550 | + if not changelog_content: |
551 | + changelog_content = _('New release available: %s') % version |
552 | + else: |
553 | + changelog_content = "\n".join(changelog_content) |
554 | + release.changelog = changelog_content |
555 | + release.release_notes = changelog_content |
556 | + try: |
557 | + release.lp_save() |
558 | + except HTTPError, e: |
559 | + print(_('An error happened during tarball upload:'), e.content) |
560 | + sys.exit(1) |
561 | + |
562 | |
563 | === added file 'data/templates/flash-game/internal/packaging.py' |
564 | --- data/templates/flash-game/internal/packaging.py 1970-01-01 00:00:00 +0000 |
565 | +++ data/templates/flash-game/internal/packaging.py 2011-01-03 19:29:40 +0000 |
566 | @@ -0,0 +1,407 @@ |
567 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
568 | +# Copyright 2009 Didier Roche |
569 | +# |
570 | +# This file is part of Quickly ubuntu-application template |
571 | +# |
572 | +#This program is free software: you can redistribute it and/or modify it |
573 | +#under the terms of the GNU General Public License version 3, as published |
574 | +#by the Free Software Foundation. |
575 | + |
576 | +#This program is distributed in the hope that it will be useful, but |
577 | +#WITHOUT ANY WARRANTY; without even the implied warranties of |
578 | +#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
579 | +#PURPOSE. See the GNU General Public License for more details. |
580 | + |
581 | +#You should have received a copy of the GNU General Public License along |
582 | +#with this program. If not, see <http://www.gnu.org/licenses/>. |
583 | + |
584 | +import datetime |
585 | +import re |
586 | +import subprocess |
587 | +import sys |
588 | +from launchpadlib.errors import HTTPError |
589 | + |
590 | + |
591 | +from quickly import configurationhandler |
592 | +from quickly import launchpadaccess |
593 | +from internal import quicklyutils |
594 | +from quickly import templatetools |
595 | + |
596 | +import gettext |
597 | +from gettext import gettext as _ |
598 | + |
599 | +#set domain text |
600 | +gettext.textdomain('quickly') |
601 | + |
602 | +class ppa_not_found(Exception): |
603 | + pass |
604 | +class not_ppa_owner(Exception): |
605 | + pass |
606 | +class user_team_not_found(Exception): |
607 | + pass |
608 | +class invalid_versionning_scheme(Exception): |
609 | + def __init__(self, msg): |
610 | + self.msg = msg |
611 | + def __str__(self): |
612 | + return repr(self.msg) |
613 | +class invalid_version_in_setup(Exception): |
614 | + def __init__(self, msg): |
615 | + self.msg = msg |
616 | + def __str__(self): |
617 | + return repr(self.msg) |
618 | + |
619 | +class DomainLevel: |
620 | + NONE=0 |
621 | + WARNING=1 |
622 | + ERROR=2 |
623 | + |
624 | +def _continue_if_errors(err_output, warn_output, return_code, |
625 | + ask_on_warn_or_error): |
626 | + """print existing error and warning""" |
627 | + |
628 | + if err_output: |
629 | + print #finish the current line |
630 | + print ('----------------------------------') |
631 | + print _('Command returned some ERRORS:') |
632 | + print ('----------------------------------') |
633 | + print ('\n'.join(err_output)) |
634 | + print ('----------------------------------') |
635 | + if warn_output: |
636 | + # seek if not uneeded warning (noise from DistUtilsExtra.auto) |
637 | + # line following the warning should be " …" |
638 | + line_number = 0 |
639 | + for line in warn_output: |
640 | + if (re.match(".*not recognized by DistUtilsExtra.auto.*", line)): |
641 | + try: |
642 | + if not re.match(' .*', warn_output[line_number + 1]): |
643 | + warn_output.remove(line) |
644 | + line_number -= 1 |
645 | + except IndexError: |
646 | + warn_output.remove(line) |
647 | + line_number -= 1 |
648 | + line_number += 1 |
649 | + # if still something, print it |
650 | + if warn_output: |
651 | + if not err_output: |
652 | + print #finish the current line |
653 | + print _('Command returned some WARNINGS:') |
654 | + print ('----------------------------------') |
655 | + print ('\n'.join(warn_output)) |
656 | + print ('----------------------------------') |
657 | + if ((err_output or warn_output) and ask_on_warn_or_error |
658 | + and return_code == 0): |
659 | + if not 'y' in raw_input("Do you want to continue (this is not safe!)? y/[n]: "): |
660 | + return(4) |
661 | + return return_code |
662 | + |
663 | +def _filter_out(line, output_domain, err_output, warn_output): |
664 | + '''filter output dispatching right domain''' |
665 | + |
666 | + if 'ERR' in line: |
667 | + output_domain = DomainLevel.ERROR |
668 | + elif 'WARN' in line: |
669 | + output_domain = DomainLevel.WARNING |
670 | + elif not line.startswith(' '): |
671 | + output_domain = DomainLevel.NONE |
672 | + if '[not found]' in line: |
673 | + output_domain = DomainLevel.WARNING |
674 | + if output_domain == DomainLevel.ERROR: |
675 | + # only add once an error |
676 | + if not line in err_output: |
677 | + err_output.append(line) |
678 | + elif output_domain == DomainLevel.WARNING: |
679 | + # only add once a warning |
680 | + if not line in warn_output: |
681 | + # filter bad output from dpkg-buildpackage (on stderr) and p-d-e auto |
682 | + if not(re.match(' .*\.pot', line) |
683 | + or re.match(' .*\.in', line) |
684 | + or re.match(' dpkg-genchanges >.*', line) |
685 | + # FIXME: this warning is temporary: should be withed in p-d-e |
686 | + or re.match('.*XS-Python-Version and XB-Python-Version.*', line)): |
687 | + warn_output.append(line) |
688 | + else: |
689 | + sys.stdout.write('.') |
690 | + return (output_domain, err_output, warn_output) |
691 | + |
692 | + |
693 | +def _exec_and_log_errors(command, ask_on_warn_or_error=False): |
694 | + '''exec the giving command and hide output if not in verbose mode''' |
695 | + |
696 | + if templatetools.in_verbose_mode(): |
697 | + return(subprocess.call(command)) |
698 | + else: |
699 | + proc = subprocess.Popen(command, stdout=subprocess.PIPE, |
700 | + stderr=subprocess.PIPE) |
701 | + stdout_domain = DomainLevel.NONE |
702 | + stderr_domain = DomainLevel.NONE |
703 | + err_output = [] |
704 | + warn_output = [] |
705 | + while True: |
706 | + line_stdout = proc.stdout.readline().rstrip() |
707 | + line_stderr = proc.stderr.readline().rstrip() |
708 | + # filter stderr |
709 | + if line_stderr: |
710 | + (stderr_domain, err_output, warn_output) = _filter_out(line_stderr, stderr_domain, err_output, warn_output) |
711 | + |
712 | + if not line_stdout: |
713 | + # don't replace by if proc.poll() as the output can be empty |
714 | + if proc.poll() != None: |
715 | + break |
716 | + # filter stdout |
717 | + else: |
718 | + (stdout_domain, err_output, warn_output) = _filter_out(line_stdout, stdout_domain, err_output, warn_output) |
719 | + |
720 | + return(_continue_if_errors(err_output, warn_output, proc.returncode, |
721 | + ask_on_warn_or_error)) |
722 | + |
723 | + |
724 | +def updatepackaging(changelog=None, no_changelog=False): |
725 | + """create or update a package using python-mkdebian. |
726 | + |
727 | + Commit after the first packaging creation""" |
728 | + |
729 | + if not changelog: |
730 | + changelog = [] |
731 | + command = ['python-mkdebian', '--force-control'] |
732 | + if no_changelog: |
733 | + command.append("--no-changelog") |
734 | + for message in changelog: |
735 | + command.extend(["--changelog", message]) |
736 | + if not configurationhandler.project_config: |
737 | + configurationhandler.loadConfig() |
738 | + try: |
739 | + dependencies = [elem.strip() for elem |
740 | + in configurationhandler.project_config['dependencies'].split(',') |
741 | + if elem] |
742 | + except KeyError: |
743 | + dependencies = [] |
744 | + |
745 | + # Hardcode the Flash plugin as a dependency, because it won't |
746 | + # get noticed by the autodetector. |
747 | + dependencies += ["flashplugin-installer"] |
748 | + for dep in dependencies: |
749 | + command.extend(["--dependency", dep]) |
750 | + |
751 | + try: |
752 | + distribution = configurationhandler.project_config['target_distribution'] |
753 | + command.extend(["--distribution", distribution]) |
754 | + except KeyError: |
755 | + pass # Distribution has not been set by user, let python-mkdebian decide what it should be |
756 | + |
757 | + |
758 | + return_code = _exec_and_log_errors(command, True) |
759 | + if return_code != 0: |
760 | + print _("An error has occurred when creating debian packaging") |
761 | + return(return_code) |
762 | + |
763 | + print _("Ubuntu packaging created in debian/") |
764 | + |
765 | + # check if first python-mkdebian (debian/ creation) to commit it |
766 | + # that means debian/ under unknown |
767 | + bzr_instance = subprocess.Popen(["bzr", "status"], stdout=subprocess.PIPE) |
768 | + bzr_status, err = bzr_instance.communicate() |
769 | + if bzr_instance.returncode != 0: |
770 | + return(bzr_instance.returncode) |
771 | + |
772 | + if re.match('(.|\n)*unknown:\n.*debian/(.|\n)*', bzr_status): |
773 | + return_code = filter_exec_command(["bzr", "add"]) |
774 | + if return_code == 0: |
775 | + return_code = filter_exec_command(["bzr", "commit", "-m", 'Creating ubuntu package']) |
776 | + |
777 | + return(return_code) |
778 | + |
779 | + |
780 | +def filter_exec_command(command): |
781 | + ''' Build either a source or a binary package''' |
782 | + |
783 | + return(_exec_and_log_errors(command, False)) |
784 | + |
785 | + |
786 | +def shell_complete_ppa(ppa_to_complete): |
787 | + ''' Complete from available ppas ''' |
788 | + |
789 | + # connect to LP and get ppa to complete |
790 | + try: |
791 | + launchpad = launchpadaccess.initialize_lpi(False) |
792 | + except launchpadaccess.launchpad_connection_error: |
793 | + sys.exit(0) |
794 | + available_ppas = [] |
795 | + if launchpad: |
796 | + try: |
797 | + (ppa_user, ppa_name) = get_ppa_parameters(launchpad, ppa_to_complete) |
798 | + except user_team_not_found: |
799 | + pass |
800 | + else: |
801 | + for current_ppa_name, current_ppa_displayname in get_all_ppas(launchpad, ppa_user): |
802 | + # print user/ppa form |
803 | + available_ppas.append("%s/%s" % (ppa_user.name, current_ppa_name)) |
804 | + # if it's the user, print in addition just "ppa_name" syntax |
805 | + if ppa_user.name == launchpad.me.name: |
806 | + available_ppas.append(current_ppa_name) |
807 | + # if we don't have provided a team, show all teams were we are member off |
808 | + if not '/' in ppa_to_complete: |
809 | + team = [mem.team for mem in launchpad.me.memberships_details if mem.status in ("Approved", "Administrator")] |
810 | + for elem in team: |
811 | + available_ppas.append(elem.name + '/') |
812 | + return available_ppas |
813 | + |
814 | +def get_ppa_parameters(launchpad, full_ppa_name): |
815 | + ''' Check if we can catch good parameters for specified ppa in form user/ppa or ppa ''' |
816 | + |
817 | + if '/' in full_ppa_name: |
818 | + ppa_user_name = full_ppa_name.split('/')[0] |
819 | + ppa_name = full_ppa_name.split('/')[1] |
820 | + # check that we are in the team/or that we are the user |
821 | + try: |
822 | + lp_ppa_user = launchpad.people[ppa_user_name] |
823 | + if lp_ppa_user.name == launchpad.me.name: |
824 | + ppa_user = launchpad.me |
825 | + else: |
826 | + # check if we are a member of this team |
827 | + team = [mem.team for mem in launchpad.me.memberships_details if mem.status in ("Approved", "Administrator") and mem.team.name == ppa_user_name] |
828 | + if team: |
829 | + ppa_user = team[0] |
830 | + else: |
831 | + raise not_ppa_owner(ppa_user_name) |
832 | + except KeyError: |
833 | + raise user_team_not_found(ppa_user_name) |
834 | + else: |
835 | + ppa_user = launchpad.me |
836 | + ppa_name = full_ppa_name |
837 | + return(ppa_user, ppa_name) |
838 | + |
839 | +def choose_ppa(launchpad, ppa_name=None): |
840 | + '''Look for right ppa parameters where to push the package''' |
841 | + |
842 | + if not ppa_name: |
843 | + if not configurationhandler.project_config: |
844 | + configurationhandler.loadConfig() |
845 | + try: |
846 | + (ppa_user, ppa_name) = get_ppa_parameters(launchpad, configurationhandler.project_config['ppa']) |
847 | + except KeyError: |
848 | + ppa_user = launchpad.me |
849 | + if (launchpadaccess.lp_server == "staging"): |
850 | + ppa_name = 'staging' |
851 | + else: # default ppa |
852 | + ppa_name = 'ppa' |
853 | + else: |
854 | + (ppa_user, ppa_name) = get_ppa_parameters(launchpad, ppa_name) |
855 | + ppa_url = '%s/~%s/+archive/%s' % (launchpadaccess.LAUNCHPAD_URL, ppa_user.name, ppa_name) |
856 | + dput_ppa_name = 'ppa:%s/%s' % (ppa_user.name, ppa_name) |
857 | + return (ppa_user, ppa_name, dput_ppa_name, ppa_url.encode('UTF-8')) |
858 | + |
859 | +def push_to_ppa(dput_ppa_name, changes_file, keyid=None): |
860 | + """ Push some code to a ppa """ |
861 | + |
862 | + # creating local binary package |
863 | + buildcommand = ["dpkg-buildpackage", "-S", "-I.bzr"] |
864 | + if keyid: |
865 | + buildcommand.append("-k%s" % keyid) |
866 | + return_code = filter_exec_command(buildcommand) |
867 | + if return_code != 0: |
868 | + print _("ERROR: an error occurred during source package creation") |
869 | + return(return_code) |
870 | + # now, pushing it to launchpad personal ppa (or team later) |
871 | + return_code = subprocess.call(["dput", dput_ppa_name, changes_file]) |
872 | + if return_code != 0: |
873 | + print _("ERROR: an error occurred during source upload to launchpad") |
874 | + return(return_code) |
875 | + return(0) |
876 | + |
877 | +def get_all_ppas(launchpad, lp_team_or_user): |
878 | + """ get all from a team or users |
879 | + |
880 | + Return list of tuples (ppa_name, ppa_display_name)""" |
881 | + |
882 | + ppa_list = [] |
883 | + for ppa in lp_team_or_user.ppas: |
884 | + ppa_list.append((ppa.name, ppa.displayname)) |
885 | + return ppa_list |
886 | + |
887 | +def check_and_return_ppaname(launchpad, lp_team_or_user, ppa_name): |
888 | + """ check whether ppa exists using its name or display name for the lp team or user |
889 | + |
890 | + return formated ppaname (not display name)""" |
891 | + |
892 | + # check that the owner really has this ppa: |
893 | + ppa_found = False |
894 | + for current_ppa_name, current_ppa_displayname in get_all_ppas(launchpad, lp_team_or_user): |
895 | + if current_ppa_name == ppa_name or current_ppa_displayname == ppa_name: |
896 | + ppa_found = True |
897 | + break |
898 | + if not ppa_found: |
899 | + raise ppa_not_found('ppa:%s:%s' % (lp_team_or_user.name, ppa_name.encode('UTF-8'))) |
900 | + return(current_ppa_name) |
901 | + |
902 | +def updateversion(proposed_version=None, sharing=False): |
903 | + '''Update versioning with year.month, handling intermediate release''' |
904 | + |
905 | + if proposed_version: |
906 | + # check manual versionning is correct |
907 | + try: |
908 | + for number in proposed_version.split('.'): |
909 | + float(number) |
910 | + except ValueError: |
911 | + msg = _("Release version specified in command arguments is not a " \ |
912 | + "valid version scheme like 'x(.y)(.z)'.") |
913 | + raise invalid_versionning_scheme(msg) |
914 | + new_version = proposed_version |
915 | + else: |
916 | + # get previous value |
917 | + try: |
918 | + old_version = quicklyutils.get_setup_value('version') |
919 | + except quicklyutils.cant_deal_with_setup_value: |
920 | + msg = _("No previous version found in setup.py. Put one please") |
921 | + raise invalid_version_in_setup(msg) |
922 | + |
923 | + # sharing only add -publicX to last release, no other update, no bumping |
924 | + if sharing: |
925 | + splitted_release_version = old_version.split("-public") |
926 | + if len(splitted_release_version) > 1: |
927 | + try: |
928 | + share_version = float(splitted_release_version[1]) |
929 | + except ValueError: |
930 | + msg = _("Share version specified after -public in "\ |
931 | + "setup.py is not a valid number: %s") \ |
932 | + % splitted_release_version[1] |
933 | + raise invalid_versionning_scheme(msg) |
934 | + new_version = splitted_release_version[0] + '-public' + \ |
935 | + str(int(share_version + 1)) |
936 | + else: |
937 | + new_version = old_version + "-public1" |
938 | + |
939 | + # automatically version to year.month(.subversion) |
940 | + else: |
941 | + base_version = datetime.datetime.now().strftime("%y.%m") |
942 | + if base_version in old_version: |
943 | + try: |
944 | + # try to get a minor version, removing -public if one |
945 | + (year, month, minor_version) = old_version.split('.') |
946 | + minor_version = minor_version.split('-public')[0] |
947 | + try: |
948 | + minor_version = float(minor_version) |
949 | + except ValueError: |
950 | + msg = _("Minor version specified in setup.py is not a " \ |
951 | + "valid number: %s. Fix this or specify a " \ |
952 | + "version as release command line argument") \ |
953 | + % minor_version |
954 | + raise invalid_versionning_scheme(msg) |
955 | + new_version = base_version + '.' + str(int(minor_version + 1)) |
956 | + |
957 | + except ValueError: |
958 | + # no minor version, bump to first one (be careful, |
959 | + # old_version may contain -publicX) |
960 | + new_version = base_version + '.1' |
961 | + |
962 | + else: |
963 | + # new year/month |
964 | + new_version = base_version |
965 | + |
966 | + # write release version to setup.py and update it in aboutdialog |
967 | + quicklyutils.set_setup_value('version', new_version) |
968 | + about_dialog_file_name = quicklyutils.get_about_file_name() |
969 | + if about_dialog_file_name: |
970 | + quicklyutils.change_xml_elem(about_dialog_file_name, "object/property", |
971 | + "name", "version", new_version, {}) |
972 | + |
973 | + return new_version |
974 | |
975 | === added file 'data/templates/flash-game/internal/quicklyutils.py' |
976 | --- data/templates/flash-game/internal/quicklyutils.py 1970-01-01 00:00:00 +0000 |
977 | +++ data/templates/flash-game/internal/quicklyutils.py 2011-01-03 19:29:40 +0000 |
978 | @@ -0,0 +1,411 @@ |
979 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
980 | +# Copyright 2009 Didier Roche |
981 | +# |
982 | +# This file is part of Quickly ubuntu-application template |
983 | +# |
984 | +#This program is free software: you can redistribute it and/or modify it |
985 | +#under the terms of the GNU General Public License version 3, as published |
986 | +#by the Free Software Foundation. |
987 | + |
988 | +#This program is distributed in the hope that it will be useful, but |
989 | +#WITHOUT ANY WARRANTY; without even the implied warranties of |
990 | +#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
991 | +#PURPOSE. See the GNU General Public License for more details. |
992 | + |
993 | +#You should have received a copy of the GNU General Public License along |
994 | +#with this program. If not, see <http://www.gnu.org/licenses/>. |
995 | + |
996 | +import os |
997 | +import re |
998 | +import sys |
999 | +import subprocess |
1000 | +from xml.etree import ElementTree as etree |
1001 | + |
1002 | +import gettext |
1003 | +from gettext import gettext as _ |
1004 | +#set domain text |
1005 | +gettext.textdomain('quickly') |
1006 | + |
1007 | +from quickly import configurationhandler |
1008 | +from quickly import templatetools |
1009 | + |
1010 | +class cant_deal_with_setup_value(Exception): |
1011 | + pass |
1012 | +class gpg_error(Exception): |
1013 | + def __init__(self, message): |
1014 | + self.message = message |
1015 | + def __str__(self): |
1016 | + return repr(self.message) |
1017 | + |
1018 | +def conventional_names(name): |
1019 | + sentence_name = templatetools.get_sentence_name(name) |
1020 | + camel_case_name = templatetools.get_camel_case_name(name) |
1021 | + return sentence_name, camel_case_name |
1022 | + |
1023 | +def file_from_template(template_dir, template_file, target_dir, substitutions=[], rename = True): |
1024 | + |
1025 | + if not os.path.isfile(os.path.join(template_dir, template_file)): |
1026 | + return |
1027 | + target_file = os.path.basename(template_file) # to get only file name (template_file can be internal/file) |
1028 | + if rename: |
1029 | + for s in substitutions: |
1030 | + pattern, sub = s |
1031 | + target_file = target_file.replace(pattern,sub) |
1032 | + |
1033 | + fin = open(os.path.join(template_dir, template_file),'r') |
1034 | + file_contents = fin.read() |
1035 | + for s in substitutions: |
1036 | + pattern, sub = s |
1037 | + file_contents = file_contents.replace(pattern,sub) |
1038 | + |
1039 | + target_path = os.path.join(target_dir, target_file) |
1040 | + if os.path.exists(target_path): |
1041 | + print _("Failed to add file to project\n cannot add: %s - this file already exists." % target_path) |
1042 | + sys.exit(4) |
1043 | + |
1044 | + fout = open(target_path, 'w') |
1045 | + fout.write(file_contents) |
1046 | + fout.flush() |
1047 | + fout.close() |
1048 | + fin.close() |
1049 | + |
1050 | +def get_setup_value(key): |
1051 | + """ get value from setup.py file. |
1052 | + |
1053 | + raise cant_deal_with_setup_value if nothing found |
1054 | + : return found value""" |
1055 | + |
1056 | + result = None |
1057 | + in_setup = False |
1058 | + try: |
1059 | + fsetup = file('setup.py', 'r') |
1060 | + for line in fsetup: |
1061 | + if in_setup: |
1062 | + fields = line.split('=') # Separate variable from value |
1063 | + if key == fields[0].strip(): # if key found and not commented |
1064 | + result = fields[1].partition(',')[0].strip() |
1065 | + result = result[1:-1] |
1066 | + break |
1067 | + if "setup(" in line: |
1068 | + in_setup = True |
1069 | + # if end of the function, finished |
1070 | + if in_setup and ')' in line: |
1071 | + in_setup = False |
1072 | + fsetup.close() |
1073 | + except (OSError, IOError), e: |
1074 | + print _("ERROR: Can't load setup.py file") |
1075 | + sys.exit(1) |
1076 | + |
1077 | + if result is None: |
1078 | + raise cant_deal_with_setup_value() |
1079 | + return result |
1080 | + |
1081 | +def set_setup_value(key, value): |
1082 | + """ set value from setup.py file |
1083 | + |
1084 | + it adds new key in the setup() function if not found. |
1085 | + it uncomments a commented value if changed. |
1086 | + |
1087 | + exit with 0 if everything's all right |
1088 | + """ |
1089 | + |
1090 | + has_changed_something = False |
1091 | + in_setup = False |
1092 | + try: |
1093 | + fsetup = file('setup.py', 'r') |
1094 | + fdest = file(fsetup.name + '.new', 'w') |
1095 | + for line in fsetup: |
1096 | + if in_setup: |
1097 | + fields = line.split('=') # Separate variable from value |
1098 | + if key == fields[0].strip() or "#%s" % key == fields[0].strip(): |
1099 | + # add new value, uncommenting it if present |
1100 | + line = "%s='%s',\n" % (fields[0].replace('#',''), value) |
1101 | + has_changed_something = True |
1102 | + |
1103 | + if "setup(" in line: |
1104 | + in_setup = True |
1105 | + # add it if the value was not present and reach end of setup() function |
1106 | + if not has_changed_something and in_setup and ")" in line: |
1107 | + fdest.write(" %s='%s',\n" % (key, value)) |
1108 | + in_setup = False |
1109 | + fdest.write(line) |
1110 | + |
1111 | + fdest.close() |
1112 | + fsetup.close() |
1113 | + os.rename(fdest.name, fsetup.name) |
1114 | + except (OSError, IOError), e: |
1115 | + print _("ERROR: Can't load setup.py file") |
1116 | + sys.exit(1) |
1117 | + |
1118 | + return 0 |
1119 | + |
1120 | +def get_about_file_name(): |
1121 | + """Get about file name if exists""" |
1122 | + if not configurationhandler.project_config: |
1123 | + configurationhandler.loadConfig() |
1124 | + about_file_name = "data/ui/About%sDialog.ui" % templatetools.get_camel_case_name(configurationhandler.project_config['project']) |
1125 | + if not os.path.isfile(about_file_name): |
1126 | + return None |
1127 | + return about_file_name |
1128 | + |
1129 | +def change_xml_elem(xml_file, path, attribute_name, attribute_value, value, attributes_if_new): |
1130 | + """change an elem in a xml tree and save it |
1131 | + |
1132 | + xml_file: url of the xml file |
1133 | + path -> path to tag to change |
1134 | + attribute_value -> attribute name to match |
1135 | + attribute_value -> attribute value to match |
1136 | + value -> new value |
1137 | + attributes_if_new -> dictionnary of additional attributes if we create a new node""" |
1138 | + found = False |
1139 | + xml_tree = etree.parse(xml_file) |
1140 | + if not attributes_if_new: |
1141 | + attributes_if_new = {} |
1142 | + attributes_if_new[attribute_name] = attribute_value |
1143 | + for node in xml_tree.findall(path): |
1144 | + if not attribute_name or node.attrib[attribute_name] == attribute_value: |
1145 | + node.text = value |
1146 | + found = True |
1147 | + if not found: |
1148 | + parent_node = "/".join(path.split('/')[:-1]) |
1149 | + child_node = path.split('/')[-1] |
1150 | + new_node = etree.Element(child_node, attributes_if_new) |
1151 | + new_node.text = value |
1152 | + xml_tree.find(parent_node).insert(0, new_node) |
1153 | + xml_tree.write(xml_file + '.new') |
1154 | + os.rename(xml_file + '.new', xml_file) |
1155 | + |
1156 | +def collect_commit_messages(previous_version): |
1157 | + '''Collect commit messages from last revision''' |
1158 | + |
1159 | + bzr_command = ['bzr', 'log'] |
1160 | + if previous_version: |
1161 | + bzr_command.extend(['-r', 'tag:%s..' % previous_version]) |
1162 | + else: |
1163 | + previous_version = '' |
1164 | + bzr_instance = subprocess.Popen(bzr_command, stdout=subprocess.PIPE) |
1165 | + result, err = bzr_instance.communicate() |
1166 | + |
1167 | + if bzr_instance.returncode != 0: |
1168 | + return(None) |
1169 | + |
1170 | + changelog = [] |
1171 | + buffered_message = "" |
1172 | + collect_switch = False |
1173 | + uncollect_msg = (_('quickly saved'), _('commit before release')) |
1174 | + for line in result.splitlines(): |
1175 | + #print buffered_message |
1176 | + if line == 'message:': |
1177 | + collect_switch = True |
1178 | + continue |
1179 | + elif '----------------------' in line: |
1180 | + if buffered_message: |
1181 | + changelog.append(buffered_message.strip()) |
1182 | + buffered_message = "" |
1183 | + collect_switch = False |
1184 | + elif line == 'tags: %s' % previous_version: |
1185 | + break |
1186 | + if collect_switch and not line.strip() in uncollect_msg: |
1187 | + buffered_message +=' %s' % line |
1188 | + return(changelog) |
1189 | + |
1190 | + |
1191 | +def get_quickly_editors(): |
1192 | + '''Return prefered editor for ubuntu-application template''' |
1193 | + |
1194 | + editor = "gedit" |
1195 | + default_editor = os.environ.get("EDITOR") |
1196 | + if not default_editor: |
1197 | + default_editor = os.environ.get("SELECTED_EDITOR") |
1198 | + if default_editor: |
1199 | + editor = default_editor |
1200 | + return editor |
1201 | + |
1202 | + |
1203 | +def take_email_from_string(value): |
1204 | + '''Try to take an email from a string''' |
1205 | + |
1206 | + if value is not None: |
1207 | + result = re.match("(.*[< ]|^)(.+@[^ >]+\.[^ >]+).*", value) |
1208 | + if result: |
1209 | + return result.groups()[1] |
1210 | + return value |
1211 | + |
1212 | +def get_all_emails(launchpad=None): |
1213 | + '''Return a list with all available email in preference order''' |
1214 | + |
1215 | + email_list = [] |
1216 | + email_list.append(take_email_from_string(os.getenv("DEBEMAIL"))) |
1217 | + |
1218 | + bzr_instance = subprocess.Popen(["bzr", "whoami"], stdout=subprocess.PIPE) |
1219 | + bzr_user, err = bzr_instance.communicate() |
1220 | + if bzr_instance.returncode == 0: |
1221 | + email_list.append(take_email_from_string(bzr_user)) |
1222 | + email_list.append(take_email_from_string(os.getenv("EMAIL"))) |
1223 | + |
1224 | + # those information can be missing if there were no packaging or license |
1225 | + # command before |
1226 | + try: |
1227 | + email_list.append(take_email_from_string(get_setup_value('author_email'))) |
1228 | + except cant_deal_with_setup_value: |
1229 | + pass |
1230 | + |
1231 | + # AUTHORS |
1232 | + fauthors_name = 'AUTHORS' |
1233 | + for line in file(fauthors_name, 'r'): |
1234 | + if not "<Your E-mail>" in line: |
1235 | + email_list.append(take_email_from_string(line)) |
1236 | + |
1237 | + # LP adresses |
1238 | + if launchpad: |
1239 | + email_list.append(launchpad.preferred_email_address.email()) |
1240 | + |
1241 | + # gpg key (if one) |
1242 | + gpg_instance = subprocess.Popen(['gpg', '--list-secret-keys', '--with-colon'], stdout=subprocess.PIPE) |
1243 | + result, err = gpg_instance.communicate() |
1244 | + if gpg_instance.returncode != 0: |
1245 | + raise gpg_error(err) |
1246 | + for line in result.splitlines(): |
1247 | + if 'sec' in line or 'uid' in line: |
1248 | + email_list.append(take_email_from_string(line.split(':')[9])) |
1249 | + |
1250 | + # return email list without None elem |
1251 | + return [email for email in email_list if email] |
1252 | + |
1253 | +def upload_gpg_key_to_launchpad(key_id): |
1254 | + '''push gpg key to launchpad not yet possible''' |
1255 | + |
1256 | + raise gpg_error(_("There is no GPG key detected for your Launchpad " |
1257 | + "account. Please upload one as you can read on the " \ |
1258 | + "tutorial")) |
1259 | + |
1260 | +def create_gpg_key(name, email): |
1261 | + '''create a gpg key and return the corresponding id''' |
1262 | + |
1263 | + if not 'y' in raw_input("It seems you don't have a gpg key on your " \ |
1264 | + "computer. Do you want to create one (this may " \ |
1265 | + "take a while)? y/[n]: "): |
1266 | + raise gpg_error(_("You choosed to not create your GPG key.")) |
1267 | + key_generate = '''Key-Type: RSA |
1268 | +Key-Length: 1024 |
1269 | +Name-Real: %s |
1270 | +Name-Email: %s |
1271 | +Expire-Date: 0 |
1272 | +%%commit''' % (name, email) |
1273 | + gpg_instance = subprocess.Popen(['gpg', '--gen-key', '--batch'], |
1274 | + stdin=subprocess.PIPE, |
1275 | + stdout=subprocess.PIPE) |
1276 | + result, err = gpg_instance.communicate(key_generate.encode('utf-8')) |
1277 | + if gpg_instance.returncode != 0: |
1278 | + raise gpg_error(err) |
1279 | + |
1280 | + gpg_instance = subprocess.Popen(['gpg', '--list-secret-keys', '--with-colon'], stdout=subprocess.PIPE) |
1281 | + result, err = gpg_instance.communicate() |
1282 | + if gpg_instance.returncode != 0: |
1283 | + raise gpg_error(err) |
1284 | + secret_key_id = None |
1285 | + for line in result.splitlines(): |
1286 | + if 'sec' in line: |
1287 | + secret_key_id = line.split(':')[4][-8:] |
1288 | + if not secret_key_id: |
1289 | + raise gpg_error(_("Can't create GPG key. Try to create it yourself.")) |
1290 | + |
1291 | + # TODO: to be able to upload key to LP |
1292 | + raw_input("Your gpg key has been create. You have to upload it to " \ |
1293 | + "Launchpad. Guidance is provided in Launchpad help. " \ |
1294 | + "Press any key once done.") |
1295 | + |
1296 | + return secret_key_id |
1297 | + |
1298 | +def get_right_gpg_key_id(launchpad): |
1299 | + '''Try to fech (and explain how to upload) right GPG key''' |
1300 | + |
1301 | + verbose = templatetools.in_verbose_mode() |
1302 | + prefered_emails = get_all_emails() |
1303 | + if not prefered_emails: |
1304 | + raise gpg_error(_("Can't sign the package as no adress email found. " \ |
1305 | + "Fulfill the AUTHORS file with name <emailadress> " \ |
1306 | + "or export DEBEMAIL/EMAIL.")) |
1307 | + if verbose: |
1308 | + print prefered_emails |
1309 | + |
1310 | + gpg_instance = subprocess.Popen(['gpg', '--list-secret-keys', '--with-colon'], stdout=subprocess.PIPE) |
1311 | + result, err = gpg_instance.communicate() |
1312 | + if gpg_instance.returncode != 0: |
1313 | + raise gpg_error(err) |
1314 | + candidate_key_ids = {} |
1315 | + for line in result.splitlines(): |
1316 | + if 'sec' in line: |
1317 | + secret_key_id = line.split(':')[4][-8:] |
1318 | + if verbose: |
1319 | + print "found secret gpg key. id: %s" % secret_key_id |
1320 | + candidate_email = take_email_from_string(line.split(':')[9]) |
1321 | + if verbose: |
1322 | + print "candidate email: %s" % candidate_email |
1323 | + if candidate_email and candidate_email in prefered_emails: |
1324 | + # create candidate_key_ids[candidate_email] if needed |
1325 | + try: |
1326 | + candidate_key_ids[candidate_email] |
1327 | + except KeyError: |
1328 | + candidate_key_ids[candidate_email] = [] |
1329 | + candidate_key_ids[candidate_email].append(secret_key_id) |
1330 | + if not candidate_key_ids: |
1331 | + candidate_key_ids[prefered_emails[0]] = [create_gpg_key( |
1332 | + launchpad.me.display_name, prefered_emails[0])] |
1333 | + |
1334 | + if verbose: |
1335 | + print "candidate_key_ids: %s" % candidate_key_ids |
1336 | + |
1337 | + # reorder key_id by email adress |
1338 | + prefered_key_ids = [] |
1339 | + for email in prefered_emails: |
1340 | + try: |
1341 | + prefered_key_ids.append((candidate_key_ids[email], email)) |
1342 | + except KeyError: |
1343 | + pass |
1344 | + if not prefered_key_ids: |
1345 | + raise gpg_error(_("GPG keys found matching no prefered email. You " \ |
1346 | + "can export DEBEMAIL or put it in AUTHORS file " \ |
1347 | + "one matching your local gpg key.")) |
1348 | + if verbose: |
1349 | + print "prefered_key_ids: %s" % prefered_key_ids |
1350 | + |
1351 | + # get from launchpad the gpg key |
1352 | + launchpad_key_ids = [] |
1353 | + for key in launchpad.me.gpg_keys: |
1354 | + launchpad_key_ids.append(key.keyid) |
1355 | + |
1356 | + if not launchpad_key_ids: |
1357 | + upload_gpg_key_to_launchpad(prefered_key_ids[0]) |
1358 | + launchpad_key_ids = [prefered_key_ids[0]] |
1359 | + |
1360 | + if verbose: |
1361 | + print "launchpad_key_ids: %s" % launchpad_key_ids |
1362 | + |
1363 | + # take first match: |
1364 | + for key_ids, email in prefered_key_ids: |
1365 | + for key_id in key_ids: |
1366 | + if key_id in launchpad_key_ids: |
1367 | + # export env variable for changelog and signing |
1368 | + author_name = launchpad.me.display_name.encode('UTF-8') |
1369 | + if not os.getenv('DEBFULLNAME'): |
1370 | + os.putenv('DEBFULLNAME', author_name) |
1371 | + if not os.getenv('DEBEMAIL'): |
1372 | + os.putenv('DEBEMAIL', email) |
1373 | + if verbose: |
1374 | + print "Selected key_id: %s, author: %s, email: %s" % (key_id, author_name, email) |
1375 | + # set upstream author and email |
1376 | + try: |
1377 | + get_setup_value('author') |
1378 | + except cant_deal_with_setup_value: |
1379 | + set_setup_value('author', author_name) |
1380 | + try: |
1381 | + get_setup_value('author_email') |
1382 | + except cant_deal_with_setup_value: |
1383 | + set_setup_value('author_email', email) |
1384 | + return key_id |
1385 | + |
1386 | + # shouldn't happen as other errors are caught |
1387 | + raise gpg_error(_("No gpg key set matching launchpad one found.'")) |
1388 | + |
1389 | + |
1390 | |
1391 | === added file 'data/templates/flash-game/package.py' |
1392 | --- data/templates/flash-game/package.py 1970-01-01 00:00:00 +0000 |
1393 | +++ data/templates/flash-game/package.py 2011-01-03 19:29:40 +0000 |
1394 | @@ -0,0 +1,73 @@ |
1395 | +#!/usr/bin/python |
1396 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
1397 | +# Copyright 2009 Didier Roche |
1398 | +# |
1399 | +# This file is part of Quickly ubuntu-application template |
1400 | +# |
1401 | +#This program is free software: you can redistribute it and/or modify it |
1402 | +#under the terms of the GNU General Public License version 3, as published |
1403 | +#by the Free Software Foundation. |
1404 | + |
1405 | +#This program is distributed in the hope that it will be useful, but |
1406 | +#WITHOUT ANY WARRANTY; without even the implied warranties of |
1407 | +#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1408 | +#PURPOSE. See the GNU General Public License for more details. |
1409 | + |
1410 | +#You should have received a copy of the GNU General Public License along |
1411 | +#with this program. If not, see <http://www.gnu.org/licenses/>. |
1412 | + |
1413 | +import sys |
1414 | +import subprocess |
1415 | + |
1416 | +import gettext |
1417 | +from gettext import gettext as _ |
1418 | +gettext.textdomain('quickly') |
1419 | + |
1420 | +from internal import quicklyutils, packaging |
1421 | +from quickly import templatetools, configurationhandler |
1422 | + |
1423 | + |
1424 | +def help(): |
1425 | + print _("""Usage: |
1426 | +$quickly package |
1427 | + |
1428 | +Creates a debian file (deb) from your project. Before running |
1429 | +the package command you can edit the Icon and Category entry of *.desktop.in |
1430 | +file, where * is the name of your project. |
1431 | + |
1432 | +Note that if you didn't run quickly release, quickly share |
1433 | +or quickly change-lp-project you may miss the name, email in |
1434 | +setup.py. You can edit them if you don't want to use any of these |
1435 | +commands afterwards. Those changes are not a mandatory at all for |
1436 | +testing purpose. |
1437 | +""") |
1438 | +templatetools.handle_additional_parameters(sys.argv, help) |
1439 | + |
1440 | +# retrieve useful information |
1441 | +if not configurationhandler.project_config: |
1442 | + configurationhandler.loadConfig() |
1443 | +project_name = configurationhandler.project_config['project'] |
1444 | + |
1445 | +try: |
1446 | + release_version = quicklyutils.get_setup_value('version') |
1447 | +except quicklyutils.cant_deal_with_setup_value: |
1448 | + print _("Release version not found in setup.py.") |
1449 | + |
1450 | + |
1451 | +# creation/update debian packaging |
1452 | +if packaging.updatepackaging(no_changelog=True) != 0: |
1453 | + print _("ERROR: can't create or update ubuntu package") |
1454 | + sys.exit(1) |
1455 | + |
1456 | +# creating local binary package |
1457 | +return_code = packaging.filter_exec_command(["dpkg-buildpackage", "-tc", |
1458 | + "-I.bzr", "-us", "-uc"]) |
1459 | + |
1460 | +if return_code == 0: |
1461 | + print _("Ubuntu package has been successfully created in ../%s_%s_all.deb") % (project_name, release_version) |
1462 | +else: |
1463 | + print _("An error has occurred during package building") |
1464 | + |
1465 | +sys.exit(return_code) |
1466 | + |
1467 | + |
1468 | |
1469 | === added directory 'data/templates/flash-game/project_root' |
1470 | === added file 'data/templates/flash-game/project_root/AUTHORS' |
1471 | --- data/templates/flash-game/project_root/AUTHORS 1970-01-01 00:00:00 +0000 |
1472 | +++ data/templates/flash-game/project_root/AUTHORS 2011-01-03 19:29:40 +0000 |
1473 | @@ -0,0 +1,1 @@ |
1474 | +Copyright (C) YYYY <Your Name> <Your E-mail> |
1475 | |
1476 | === added directory 'data/templates/flash-game/project_root/bin' |
1477 | === added file 'data/templates/flash-game/project_root/bin/project_name' |
1478 | --- data/templates/flash-game/project_root/bin/project_name 1970-01-01 00:00:00 +0000 |
1479 | +++ data/templates/flash-game/project_root/bin/project_name 2011-01-03 19:29:40 +0000 |
1480 | @@ -0,0 +1,71 @@ |
1481 | +#!/usr/bin/python |
1482 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
1483 | +### BEGIN LICENSE |
1484 | +# This file is in the public domain |
1485 | +### END LICENSE |
1486 | + |
1487 | +########################################################################### |
1488 | +# SWF file details |
1489 | +# Edit these lines as appropriate |
1490 | +########################################################################### |
1491 | + |
1492 | +GAME_NAME = "sentence_name" |
1493 | +WINDOW_SIZE = (swf_width, swf_height) |
1494 | + |
1495 | + |
1496 | +########################################################################### |
1497 | +# No need to edit below here |
1498 | +########################################################################### |
1499 | + |
1500 | + |
1501 | +import webkit, gtk, gio |
1502 | +import json, os |
1503 | + |
1504 | +# Where your project will look for your data (for instance, images and ui |
1505 | +# files). By default, this is ../data, relative your trunk layout |
1506 | +__python_name_data_directory__ = '../data/' |
1507 | + |
1508 | +def get_data_file(*path_segments): |
1509 | + """Get the full path to a data file. |
1510 | + |
1511 | + Returns the path to a file underneath the data directory (as defined by |
1512 | + `get_data_path`). Equivalent to os.path.join(get_data_path(), |
1513 | + *path_segments). |
1514 | + """ |
1515 | + return os.path.join(get_data_path(), *path_segments) |
1516 | + |
1517 | + |
1518 | +def get_data_path(): |
1519 | + """Retrieve project_name data path |
1520 | + |
1521 | + This path is by default <python_name_lib_path>/../data/ in trunk |
1522 | + and /usr/share/project_name in an installed version but this path |
1523 | + is specified at installation time. |
1524 | + """ |
1525 | + |
1526 | + # Get pathname absolute or relative. |
1527 | + path = os.path.join( |
1528 | + os.path.dirname(__file__), __python_name_data_directory__) |
1529 | + |
1530 | + abs_data_path = os.path.abspath(path) |
1531 | + if not os.path.exists(abs_data_path): |
1532 | + raise project_path_not_found |
1533 | + |
1534 | + return abs_data_path |
1535 | + |
1536 | + |
1537 | +if __name__ == "__main__": |
1538 | + w = gtk.Window() |
1539 | + v = webkit.WebView() |
1540 | + w.add(v) |
1541 | + w.show_all() # have to have this before set_size_request |
1542 | + w.connect("destroy", lambda q: gtk.main_quit()) |
1543 | + w.set_title(GAME_NAME) |
1544 | + htmlfp = gio.File(get_data_file('index.html')) |
1545 | + uri = htmlfp.get_uri() |
1546 | + html, _, _ = htmlfp.load_contents() |
1547 | + v.load_html_string(html, uri) |
1548 | + w.set_size_request(*WINDOW_SIZE) |
1549 | + v.queue_resize() # force a reallocation, https://bugs.webkit.org/show_bug.cgi?id=47742 |
1550 | + gtk.main() |
1551 | + |
1552 | |
1553 | === added directory 'data/templates/flash-game/project_root/data' |
1554 | === added file 'data/templates/flash-game/project_root/data/index.html' |
1555 | --- data/templates/flash-game/project_root/data/index.html 1970-01-01 00:00:00 +0000 |
1556 | +++ data/templates/flash-game/project_root/data/index.html 2011-01-03 19:29:40 +0000 |
1557 | @@ -0,0 +1,9 @@ |
1558 | +<html> |
1559 | +<head> |
1560 | +<style> |
1561 | +html, body { margin: 0; padding: 0; } |
1562 | +</style> |
1563 | +</head> |
1564 | +<body><object width="100%" height="100%"><param name="movie" value="game.swf"></param></object></body> |
1565 | +</html> |
1566 | + |
1567 | |
1568 | === added file 'data/templates/flash-game/project_root/project_name.desktop.in' |
1569 | --- data/templates/flash-game/project_root/project_name.desktop.in 1970-01-01 00:00:00 +0000 |
1570 | +++ data/templates/flash-game/project_root/project_name.desktop.in 2011-01-03 19:29:40 +0000 |
1571 | @@ -0,0 +1,8 @@ |
1572 | +[Desktop Entry] |
1573 | +_Name=sentence_name |
1574 | +_Comment=camel_case_name application |
1575 | +Categories=GNOME;Utility; |
1576 | +Exec=project_name |
1577 | +Icon=project_name |
1578 | +Terminal=false |
1579 | +Type=Application |
1580 | |
1581 | === added file 'data/templates/flash-game/project_root/setup.py' |
1582 | --- data/templates/flash-game/project_root/setup.py 1970-01-01 00:00:00 +0000 |
1583 | +++ data/templates/flash-game/project_root/setup.py 2011-01-03 19:29:40 +0000 |
1584 | @@ -0,0 +1,69 @@ |
1585 | +#!/usr/bin/env python |
1586 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
1587 | +### BEGIN LICENSE |
1588 | +# This file is in the public domain |
1589 | +### END LICENSE |
1590 | + |
1591 | +###################### DO NOT TOUCH THIS (HEAD TO THE SECOND PART) ###################### |
1592 | + |
1593 | +import os |
1594 | +import sys |
1595 | + |
1596 | +try: |
1597 | + import DistUtilsExtra.auto |
1598 | +except ImportError: |
1599 | + print >> sys.stderr, 'To build project_name you need https://launchpad.net/python-distutils-extra' |
1600 | + sys.exit(1) |
1601 | +assert DistUtilsExtra.auto.__version__ >= '2.18', 'needs DistUtilsExtra.auto >= 2.18' |
1602 | + |
1603 | +def update_data_path(prefix, oldvalue=None): |
1604 | + |
1605 | + try: |
1606 | + fin = file('bin/project_name', 'r') |
1607 | + fout = file(fin.name + '.new', 'w') |
1608 | + |
1609 | + for line in fin: |
1610 | + fields = line.split(' = ') # Separate variable from value |
1611 | + if fields[0] == '__python_name_data_directory__': |
1612 | + # update to prefix, store oldvalue |
1613 | + if not oldvalue: |
1614 | + oldvalue = fields[1] |
1615 | + line = "%s = '%s'\n" % (fields[0], prefix) |
1616 | + else: # restore oldvalue |
1617 | + line = "%s = %s" % (fields[0], oldvalue) |
1618 | + fout.write(line) |
1619 | + |
1620 | + fout.flush() |
1621 | + fout.close() |
1622 | + fin.close() |
1623 | + os.rename(fout.name, fin.name) |
1624 | + except (OSError, IOError), e: |
1625 | + print ("ERROR: Can't find python_name/python_nameconfig.py") |
1626 | + sys.exit(1) |
1627 | + return oldvalue |
1628 | + |
1629 | + |
1630 | +class InstallAndUpdateDataDirectory(DistUtilsExtra.auto.install_auto): |
1631 | + def run(self): |
1632 | + previous_value = update_data_path(self.prefix + '/share/project_name/') |
1633 | + DistUtilsExtra.auto.install_auto.run(self) |
1634 | + update_data_path(self.prefix, previous_value) |
1635 | + |
1636 | + |
1637 | + |
1638 | +################################################################################## |
1639 | +###################### YOU SHOULD MODIFY ONLY WHAT IS BELOW ###################### |
1640 | +################################################################################## |
1641 | + |
1642 | +DistUtilsExtra.auto.setup( |
1643 | + name='project_name', |
1644 | + version='0.1', |
1645 | + #license='GPL-3', |
1646 | + #author='Your Name', |
1647 | + #author_email='email@ubuntu.com', |
1648 | + #description='UI for managing …', |
1649 | + #long_description='Here a longer description', |
1650 | + #url='https://launchpad.net/project_name', |
1651 | + cmdclass={'install': InstallAndUpdateDataDirectory} |
1652 | + ) |
1653 | + |
All is good as discussed on IRC and made some review! That's an awesome contribution, I'm sure we will see a lot of people making Flash game available on ubuntu thanks to that template!
Thanks Stuart :)
Approved for now, will merge tomorrow with a better network connection