Merge lp:~cjwatson/launchpad/remove-sync-source into lp:launchpad

Proposed by Colin Watson on 2012-01-11
Status: Merged
Approved by: Steve Kowalik on 2012-01-18
Approved revision: no longer in the source branch.
Merged at revision: 14690
Proposed branch: lp:~cjwatson/launchpad/remove-sync-source
Merge into: lp:launchpad
Diff against target: 2261 lines (+1/-2209)
4 files modified
lib/lp/soyuz/scripts/ftpmaster.py (+1/-195)
lib/lp/soyuz/scripts/tests/test_sync_source.py (+0/-552)
scripts/ftpmaster-tools/_syncorigins.py (+0/-680)
scripts/ftpmaster-tools/sync-source.py (+0/-782)
To merge this branch: bzr merge lp:~cjwatson/launchpad/remove-sync-source
Reviewer Review Type Date Requested Status
Steve Kowalik (community) code 2012-01-11 Approve on 2012-01-18
Review via email: mp+88190@code.launchpad.net

Commit Message

[r=stevenk][bug=35723,125022] Remove sync-source.py script, replaced by API scripts based on Archive.copyPackage/Archive.copyPackages.

Description of the Change

== Summary ==

sync-source.py has been widely considered a wart for ages, and the work of Julian and his team on Archive.copyPackage/Archive.copyPackages and friends effectively replaces it, in combination with the syncpackage tool in ubuntu-dev-tools. I've just finished converting all the Ubuntu archive administration processes to stop using sync-source.py, so let's remove it.

== Proposed fix ==

Kill it with fire.

It's not a prerequisite as such, but it would be good if https://code.launchpad.net/~cjwatson/launchpad/archive-copy-packages-source-series/+merge/87942 were reviewed first; that fixes a serious problem I found when using the 'sync-source.py -a' replacement.

== Tests ==

bin/test -vvct soyuz.scripts

== Demo and Q/A ==

None.

== lint ==

None.

To post a comment you must log in.
Steve Kowalik (stevenk) wrote :

I've been looking forward to this code dying for years, thank you!

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/soyuz/scripts/ftpmaster.py'
2--- lib/lp/soyuz/scripts/ftpmaster.py 2012-01-06 11:08:30 +0000
3+++ lib/lp/soyuz/scripts/ftpmaster.py 2012-01-11 12:08:26 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
6+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 """FTPMaster utilities."""
10@@ -13,26 +13,17 @@
11 'ObsoleteDistroseries',
12 'PackageRemover',
13 'PubSourceChecker',
14- 'SyncSource',
15- 'SyncSourceError',
16 ]
17
18-import hashlib
19 from itertools import chain
20 import os
21-import stat
22-import sys
23-import time
24
25-from debian.deb822 import Changes
26 from zope.component import getUtility
27
28 from lp.app.errors import NotFoundError
29-from lp.archiveuploader.utils import determine_source_file_type
30 from lp.registry.interfaces.person import IPersonSet
31 from lp.registry.interfaces.pocket import pocketsuffix
32 from lp.registry.interfaces.series import SeriesStatus
33-from lp.registry.interfaces.sourcepackage import SourcePackageFileType
34 from lp.services.browser_helpers import get_plural_text
35 from lp.services.database.constants import UTC_NOW
36 from lp.services.helpers import filenameToContentType
37@@ -405,138 +396,6 @@
38 copy_and_close(pocket_chroot.chroot, local_file)
39
40
41-class SyncSourceError(Exception):
42- """Raised when an critical error occurs inside SyncSource.
43-
44- The entire procedure should be aborted in order to avoid unknown problems.
45- """
46-
47-
48-class SyncSource:
49- """Sync Source procedure helper class.
50-
51- It provides the backend for retrieving files from Librarian or the
52- 'sync source' location. Also provides a method to check the downloaded
53- files integrity.
54- 'aptMD5Sum' is provided as a classmethod during the integration time.
55- """
56-
57- def __init__(self, files, origin, logger, downloader, todistro):
58- """Store local context.
59-
60- files: a dictionary where the keys are the filename and the
61- value another dictionary with the file informations.
62- origin: a dictionary similar to 'files' but where the values
63- contain information for download files to be synchronized
64- logger: a logger
65- downloader: a callable that fetchs URLs,
66- 'downloader(url, destination)'
67- todistro: target distribution object
68- """
69- self.files = files
70- self.origin = origin
71- self.logger = logger
72- self.downloader = downloader
73- self.todistro = todistro
74-
75- @classmethod
76- def generateMD5Sum(self, filename):
77- file_handle = open(filename)
78- md5sum = hashlib.md5(file_handle.read()).hexdigest()
79- file_handle.close()
80- return md5sum
81-
82- def fetchFileFromLibrarian(self, filename):
83- """Fetch file from librarian.
84-
85- Store the contents in local path with the original filename.
86- Return the fetched filename if it was present in Librarian or None
87- if it wasn't.
88- """
89- try:
90- libraryfilealias = self.todistro.main_archive.getFileByName(
91- filename)
92- except NotFoundError:
93- return None
94-
95- self.logger.info(
96- "%s: already in distro - downloading from librarian" %
97- filename)
98-
99- output_file = open(filename, 'w')
100- libraryfilealias.open()
101- copy_and_close(libraryfilealias, output_file)
102- return filename
103-
104- def fetchLibrarianFiles(self):
105- """Try to fetch files from Librarian.
106-
107- It raises SyncSourceError if anything else then an
108- orig tarball was found in Librarian.
109- Return the names of the files retrieved from the librarian.
110- """
111- retrieved = []
112- for filename in self.files.keys():
113- if not self.fetchFileFromLibrarian(filename):
114- continue
115- file_type = determine_source_file_type(filename)
116- # set the return code if an orig was, in fact,
117- # fetched from Librarian
118- orig_types = (
119- SourcePackageFileType.ORIG_TARBALL,
120- SourcePackageFileType.COMPONENT_ORIG_TARBALL)
121- if file_type not in orig_types:
122- raise SyncSourceError(
123- 'Oops, only orig tarball can be retrieved from '
124- 'librarian.')
125- retrieved.append(filename)
126-
127- return retrieved
128-
129- def fetchSyncFiles(self):
130- """Fetch files from the original sync source.
131-
132- Return DSC filename, which should always come via this path.
133- """
134- dsc_filename = None
135- for filename in self.files.keys():
136- file_type = determine_source_file_type(filename)
137- if file_type == SourcePackageFileType.DSC:
138- dsc_filename = filename
139- if os.path.exists(filename):
140- self.logger.info(" - <%s: cached>" % (filename))
141- continue
142- self.logger.info(
143- " - <%s: downloading from %s>" %
144- (filename, self.origin["url"]))
145- download_f = ("%s%s" % (self.origin["url"],
146- self.files[filename]["remote filename"]))
147- sys.stdout.flush()
148- self.downloader(download_f, filename)
149- return dsc_filename
150-
151- def checkDownloadedFiles(self):
152- """Check md5sum and size match Source.
153-
154- If anything fails SyncSourceError will be raised.
155- """
156- for filename in self.files.keys():
157- actual_md5sum = self.generateMD5Sum(filename)
158- expected_md5sum = self.files[filename]["md5sum"]
159- if actual_md5sum != expected_md5sum:
160- raise SyncSourceError(
161- "%s: md5sum check failed (%s [actual] "
162- "vs. %s [expected])."
163- % (filename, actual_md5sum, expected_md5sum))
164-
165- actual_size = os.stat(filename)[stat.ST_SIZE]
166- expected_size = int(self.files[filename]["size"])
167- if actual_size != expected_size:
168- raise SyncSourceError(
169- "%s: size mismatch (%s [actual] vs. %s [expected])."
170- % (filename, actual_size, expected_size))
171-
172-
173 class LpQueryDistro(LaunchpadScript):
174 """Main class for scripts/ftpmaster-tools/lp-query-distro.py."""
175
176@@ -1007,56 +866,3 @@
177 # Collect extra debug messages from chroot_manager.
178 for debug_message in chroot_manager._messages:
179 self.logger.debug(debug_message)
180-
181-
182-def generate_changes(dsc, dsc_files, suite, changelog, urgency, closes,
183- lp_closes, section, priority, description,
184- files_from_librarian, requested_by, origin):
185- """Generate a Changes object.
186-
187- :param dsc: A `Dsc` instance for the related source package.
188- :param suite: Distribution name
189- :param changelog: Relevant changelog data
190- :param urgency: Urgency string (low, medium, high, etc)
191- :param closes: Sequence of Debian bug numbers (as strings) fixed by
192- this upload.
193- :param section: Debian section
194- :param priority: Package priority
195- """
196-
197- # XXX cprov 2007-07-03:
198- # Changed-By can be extracted from most-recent changelog footer,
199- # but do we care?
200-
201- changes = Changes()
202- changes["Origin"] = "%s/%s" % (origin["name"], origin["suite"])
203- changes["Format"] = "1.7"
204- changes["Date"] = time.strftime("%a, %d %b %Y %H:%M:%S %z")
205- changes["Source"] = dsc["source"]
206- changes["Binary"] = dsc["binary"]
207- changes["Architecture"] = "source"
208- changes["Version"] = dsc["version"]
209- changes["Distribution"] = suite
210- changes["Urgency"] = urgency
211- changes["Maintainer"] = dsc["maintainer"]
212- changes["Changed-By"] = requested_by
213- if description:
214- changes["Description"] = "\n %s" % description
215- if closes:
216- changes["Closes"] = " ".join(closes)
217- if lp_closes:
218- changes["Launchpad-bugs-fixed"] = " ".join(lp_closes)
219- files = []
220- for filename in dsc_files:
221- if filename in files_from_librarian:
222- continue
223- files.append({"md5sum": dsc_files[filename]["md5sum"],
224- "size": dsc_files[filename]["size"],
225- "section": section,
226- "priority": priority,
227- "name": filename,
228- })
229-
230- changes["Files"] = files
231- changes["Changes"] = "\n%s" % changelog
232- return changes
233
234=== removed file 'lib/lp/soyuz/scripts/tests/test_sync_source.py'
235--- lib/lp/soyuz/scripts/tests/test_sync_source.py 2011-12-30 01:48:17 +0000
236+++ lib/lp/soyuz/scripts/tests/test_sync_source.py 1970-01-01 00:00:00 +0000
237@@ -1,552 +0,0 @@
238-# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
239-# GNU Affero General Public License version 3 (see the file LICENSE).
240-
241-"""SyncSource facilities tests."""
242-
243-__metaclass__ = type
244-
245-import os
246-import shutil
247-import subprocess
248-import sys
249-import tempfile
250-
251-from debian.deb822 import (
252- Changes,
253- Deb822Dict,
254- Dsc,
255- )
256-import transaction
257-from zope.component import getUtility
258-
259-from lp.archiveuploader.tagfiles import parse_tagfile
260-from lp.registry.interfaces.distribution import IDistributionSet
261-from lp.services.config import config
262-from lp.services.librarianserver.testing.server import fillLibrarianFile
263-from lp.services.log.logger import BufferLogger
264-from lp.soyuz.scripts.ftpmaster import (
265- generate_changes,
266- SyncSource,
267- SyncSourceError,
268- )
269-from lp.testing import (
270- TestCase,
271- TestCaseWithFactory,
272- )
273-from lp.testing.layers import (
274- LaunchpadZopelessLayer,
275- LibrarianLayer,
276- )
277-
278-
279-class TestSyncSource(TestCaseWithFactory):
280- layer = LaunchpadZopelessLayer
281- dbuser = 'ro'
282-
283- def setUp(self):
284- """Create contents in disk for librarian sampledata.
285-
286- Setup and chdir into a temp directory, a jail, where we can
287- control the file creation properly
288- """
289- super(TestSyncSource, self).setUp()
290- fillLibrarianFile(1, content='one')
291- fillLibrarianFile(2, content='two')
292- fillLibrarianFile(54, content='fifty-four')
293- self._home = os.path.abspath('')
294- self._jail = tempfile.mkdtemp()
295- os.chdir(self._jail)
296- self.logger = BufferLogger()
297- self.downloads = []
298-
299- def tearDown(self):
300- """Remove test contents from disk.
301-
302- chdir back to the previous path (home) and remove the temp
303- directory used as jail.
304- """
305- super(TestSyncSource, self).tearDown()
306- os.chdir(self._home)
307- LibrarianLayer.librarian_fixture.clear()
308- shutil.rmtree(self._jail)
309-
310- def _listFiles(self):
311- """Return a list of files present in jail."""
312- return os.listdir(self._jail)
313-
314- def get_messages(self):
315- """Retrieve the messages sent using the logger."""
316- return self.logger.getLogBuffer().splitlines()
317-
318- def local_downloader(self, url, filename):
319- """Store download requests for future inspections."""
320- self.downloads.append((url, filename))
321- output = open(filename, 'w')
322- output.write('Slartibartfast')
323- output.close()
324-
325- def _getSyncSource(self, files, origin):
326- """Return a SyncSource instance with the given parameters
327-
328- Uses the local_* methods to capture results so we can verify
329- them later.
330- """
331- sync_source = SyncSource(
332- files=files, origin=origin, logger=self.logger,
333- downloader=self.local_downloader,
334- todistro=getUtility(IDistributionSet)['ubuntu'])
335- return sync_source
336-
337- def testInstantiate(self):
338- """Check if SyncSource can be instantiated."""
339- files = {'foobar': {'size': 1}}
340- origin = {'foobar': {'remote-location': 'nowhere'}}
341-
342- sync_source = self._getSyncSource(files, origin)
343-
344- self.assertEqual(sync_source.files, files)
345- self.assertEqual(sync_source.origin, origin)
346-
347- sync_source.logger.debug('opa')
348- self.assertEqual(self.get_messages(), ['DEBUG opa'])
349-
350- sync_source.downloader('somewhere', 'foo')
351- self.assertEqual(self.downloads, [('somewhere', 'foo')])
352- self.assertEqual(self._listFiles(), ['foo'])
353- self.assertEqual(open('foo').read(), 'Slartibartfast')
354-
355- def testCheckDownloadedFilesOK(self):
356- """Check if checkDownloadFiles really verifies the filesystem
357-
358- Pass parameters via 'files' (MD5 & size) that match the file created
359- on disk.
360- """
361- files = {
362- 'foo': {'md5sum': 'dd21ab16f950f7ac4f9c78ef1498eee1', 'size': 15},
363- }
364- origin = {}
365- sync_source = self._getSyncSource(files, origin)
366-
367- test_file = open('foo', 'w')
368- test_file.write('abcdefghijlmnop')
369- test_file.close()
370-
371- sync_source.checkDownloadedFiles()
372-
373- def testCheckDownloadedFilesWrongMD5(self):
374- """Expect SyncSourceError to be raised due the wrong MD5."""
375- files = {
376- 'foo': {'md5sum': 'duhhhhh', 'size': 15},
377- }
378- origin = {}
379- sync_source = self._getSyncSource(files, origin)
380-
381- test_file = open('foo', 'w')
382- test_file.write('abcdefghijlmnop')
383- test_file.close()
384-
385- self.assertRaises(
386- SyncSourceError,
387- sync_source.checkDownloadedFiles)
388-
389- def testCheckDownloadedFilesWrongSize(self):
390- """Expect SyncSourceError to be raised due the wrong size."""
391- files = {
392- 'foo': {'md5sum': 'dd21ab16f950f7ac4f9c78ef1498eee1', 'size': 10},
393- }
394- origin = {}
395- sync_source = self._getSyncSource(files, origin)
396-
397- test_file = open('foo', 'w')
398- test_file.write('abcdefghijlmnop')
399- test_file.close()
400-
401- self.assertRaises(
402- SyncSourceError,
403- sync_source.checkDownloadedFiles)
404-
405- def testSyncSourceMD5Sum(self):
406- """Probe the classmethod provided by SyncSource."""
407- test_file = open('foo', 'w')
408- test_file.write('abcdefghijlmnop')
409- test_file.close()
410- md5 = SyncSource.generateMD5Sum('foo')
411- self.assertEqual(md5, 'dd21ab16f950f7ac4f9c78ef1498eee1')
412-
413- def testFetchSyncFiles(self):
414- """Probe fetchSyncFiles.
415-
416- It only downloads the files not present in current path, so the
417- test_file is skipped.
418- """
419- files = {
420- 'foo_0.1.diff.gz': {'remote filename': 'xxx'},
421- 'foo_0.1.dsc': {'remote filename': 'yyy'},
422- 'foo_0.1.orig.gz': {'remote filename': 'zzz'},
423- }
424- origin = {'url': 'http://somewhere/'}
425-
426- sync_source = self._getSyncSource(files, origin)
427-
428- test_file = open('foo_0.1.diff.gz', 'w')
429- test_file.write('nahhh')
430- test_file.close()
431-
432- dsc_filename = sync_source.fetchSyncFiles()
433-
434- self.assertEqual(dsc_filename, 'foo_0.1.dsc')
435-
436- self.assertEqual(
437- self.downloads,
438- [('http://somewhere/zzz', 'foo_0.1.orig.gz'),
439- ('http://somewhere/yyy', 'foo_0.1.dsc')])
440-
441- for filename in files.keys():
442- self.assertTrue(os.path.exists(filename))
443-
444- def testFetchLibrarianFilesOK(self):
445- """Probe fetchLibrarianFiles.
446-
447- Seek on files published from librarian and download matching
448- filenames.
449- """
450- files = {
451- 'netapplet_1.0.0.orig.tar.gz': {},
452- 'netapplet_1.0.1.dsc': {},
453- 'netapplet_1.0.1.diff.gz': {},
454- }
455- origin = {}
456- sync_source = self._getSyncSource(files, origin)
457-
458- librarian_files = sync_source.fetchLibrarianFiles()
459-
460- self.assertEqual(librarian_files, ['netapplet_1.0.0.orig.tar.gz'])
461- self.assertEqual(self._listFiles(), ['netapplet_1.0.0.orig.tar.gz'])
462- self.assertEqual(
463- self.get_messages(),
464- ['INFO netapplet_1.0.0.orig.tar.gz: already in distro '
465- '- downloading from librarian'])
466-
467- def testFetchLibrarianFilesGotDuplicatedDSC(self):
468- """fetchLibrarianFiles fails for an already present version.
469-
470- It raises SyncSourceError when it find a DSC or DIFF already
471- published, it means that the upload version is duplicated.
472- """
473- spr = self.factory.makeSourcePackageRelease()
474- lfa = self.factory.makeLibraryFileAlias(filename='foobar_1.0.dsc')
475- self.factory.makeSourcePackageReleaseFile(
476- sourcepackagerelease=spr, library_file=lfa)
477- self.factory.makeSourcePackagePublishingHistory(
478- archive=getUtility(IDistributionSet)['ubuntu'].main_archive,
479- sourcepackagerelease=spr)
480- transaction.commit()
481-
482- files = {
483- 'foobar_1.0.orig.tar.gz': {},
484- 'foobar_1.0.dsc': {},
485- 'foobar_1.0.diff.gz': {},
486- }
487- origin = {}
488- sync_source = self._getSyncSource(files, origin)
489-
490- self.assertRaises(
491- SyncSourceError,
492- sync_source.fetchLibrarianFiles)
493-
494- self.assertEqual(
495- self.get_messages(),
496- ['INFO foobar_1.0.dsc: already in distro '
497- '- downloading from librarian'])
498- self.assertEqual(self._listFiles(), ['foobar_1.0.dsc'])
499-
500-
501-class TestSyncSourceScript(TestCase):
502- layer = LaunchpadZopelessLayer
503- dbuser = 'ro'
504-
505- def setUp(self):
506- super(TestSyncSourceScript, self).setUp()
507- self._home = os.getcwd()
508- self._jail = os.path.join(
509- os.path.dirname(__file__), 'sync_source_home')
510- os.chdir(self._jail)
511-
512- def tearDown(self):
513- """'chdir' back to the previous path (home)."""
514- super(TestSyncSourceScript, self).tearDown()
515- os.chdir(self._home)
516-
517- def runSyncSource(self, extra_args=None):
518- """Run sync-source.py, returning the result and output.
519-
520- Returns a tuple of the process's return code, stdout output and
521- stderr output.
522- """
523- if extra_args is None:
524- extra_args = []
525- script = os.path.join(
526- config.root, "scripts", "ftpmaster-tools", "sync-source.py")
527- args = [sys.executable, script]
528- args.extend(extra_args)
529- process = subprocess.Popen(
530- args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
531- stdout, stderr = process.communicate()
532- return (process.returncode, stdout, stderr)
533-
534- def testSyncSourceRunV1(self):
535- """Try a simple sync-source.py run.
536-
537- It will run in a special tree prepared to cope with sync-source
538- requirements (see `setUp`). It contains a usable archive index
539- named as '$distribution_$suite_$component_Sources' and the
540- 'etherwake' source files.
541-
542- Check that:
543- * return code is ZERO,
544- * check standard error and standard output,
545- * check if the expected changesfile was generated,
546- * parse and inspect the changesfile using the archiveuploader
547- component (the same approach adopted by Soyuz).
548- * delete the changesfile.
549- """
550- returncode, out, err = self.runSyncSource(
551- extra_args=['-b', 'cprov', '-D', 'debian', '-C', 'main',
552- '-S', 'incoming', 'bar'])
553-
554- self.assertEqual(
555- 0, returncode, "\nScript Failed:%s\nStdout:\n%s\nStderr\n%s\n"
556- % (returncode, out, err))
557-
558- self.assertEqual(
559- err.splitlines(),
560- ['INFO Creating lockfile: '
561- '/var/lock/launchpad-sync-source.lock',
562- 'WARNING Could not find blacklist file on '
563- '/srv/launchpad.net/dak/sync-blacklist.txt',
564- 'INFO - <bar_1.0-1.diff.gz: cached>',
565- 'INFO - <bar_1.0.orig.tar.gz: cached>',
566- 'INFO - <bar_1.0-1.dsc: cached>',
567- ])
568- self.assertEqual(
569- out.splitlines(),
570- ['Getting binaries for hoary...',
571- '[Updating] bar (None [Ubuntu] < 1.0-1 [Debian])',
572- ' * Trying to add bar...',
573- ])
574-
575- expected_changesfile = 'bar_1.0-1_source.changes'
576- self.assertTrue(
577- os.path.exists(expected_changesfile),
578- "Couldn't find %s." % expected_changesfile)
579-
580- # Parse the generated unsigned changesfile.
581- parsed_changes = parse_tagfile(expected_changesfile)
582-
583- # It refers to the right source/version.
584- self.assertEqual(parsed_changes['Source'], 'bar')
585- self.assertEqual(parsed_changes['Version'], '1.0-1')
586-
587- # It includes the correct 'origin' and 'target' information.
588- self.assertEqual(parsed_changes['Origin'], 'Debian/incoming')
589- self.assertEqual(parsed_changes['Distribution'], 'hoary')
590-
591- # 'closes' and 'launchpad-bug-fixed' are filled according to
592- # what is listed in the debian/changelog.
593- self.assertEqual(parsed_changes['Closes'], '1 2 1234 4321')
594- self.assertEqual(parsed_changes['Launchpad-bugs-fixed'], '1234 4321')
595-
596- # And finally, 'maintainer' role was preserved and 'changed-by'
597- # role was assigned as specified in the sync-source command-line.
598- self.assertEqual(
599- parsed_changes['Maintainer'],
600- 'Launchpad team <launchpad@lists.canonical.com>')
601- self.assertEqual(
602- parsed_changes['Changed-By'],
603- 'Celso Providelo <celso.providelo@canonical.com>')
604-
605- os.unlink(expected_changesfile)
606-
607- def testSyncSourceRunV3(self):
608- """Try a simple sync-source.py run with a version 3 source format
609- package.
610-
611- It will run in a special tree prepared to cope with sync-source
612- requirements (see `setUp`). It contains a usable archive index
613- named as '$distribution_$suite_$component_Sources' and the
614- 'etherwake' source files.
615-
616- Check that:
617- * return code is ZERO,
618- * check standard error and standard output,
619- * check if the expected changesfile was generated,
620- * parse and inspect the changesfile using the archiveuploader
621- component (the same approach adopted by Soyuz).
622- * delete the changesfile.
623- """
624- returncode, out, err = self.runSyncSource(
625- extra_args=['-b', 'cprov', '-D', 'debian', '-C', 'main',
626- '-S', 'incoming', 'sample1'])
627-
628- self.assertEqual(
629- 0, returncode, "\nScript Failed:%s\nStdout:\n%s\nStderr\n%s\n"
630- % (returncode, out, err))
631-
632- self.assertEqual(
633- err.splitlines(),
634- ['INFO Creating lockfile: '
635- '/var/lock/launchpad-sync-source.lock',
636- 'WARNING Could not find blacklist file on '
637- '/srv/launchpad.net/dak/sync-blacklist.txt',
638- 'INFO - <sample1_1.0.orig-component3.tar.gz: cached>',
639- 'INFO - <sample1_1.0-1.dsc: cached>',
640- 'INFO - <sample1_1.0-1.debian.tar.gz: cached>',
641- 'INFO - <sample1_1.0.orig-component1.tar.bz2: cached>',
642- 'INFO - <sample1_1.0.orig-component2.tar.lzma: cached>',
643- 'INFO - <sample1_1.0.orig.tar.gz: cached>'])
644- self.assertEqual(
645- out.splitlines(),
646- ['Getting binaries for hoary...',
647- '[Updating] sample1 (None [Ubuntu] < 1.0-1 [Debian])',
648- ' * Trying to add sample1...',
649- ])
650-
651- expected_changesfile = 'sample1_1.0-1_source.changes'
652- self.assertTrue(
653- os.path.exists(expected_changesfile),
654- "Couldn't find %s." % expected_changesfile)
655-
656- # Parse the generated unsigned changesfile.
657- parsed_changes = parse_tagfile(expected_changesfile)
658-
659- # It refers to the right source/version.
660- self.assertEqual(parsed_changes['Source'], 'sample1')
661- self.assertEqual(parsed_changes['Version'], '1.0-1')
662-
663- # It includes the correct 'origin' and 'target' information.
664- self.assertEqual(parsed_changes['Origin'], 'Debian/incoming')
665- self.assertEqual(parsed_changes['Distribution'], 'hoary')
666-
667- # And finally, 'maintainer' role was preserved and 'changed-by'
668- # role was assigned as specified in the sync-source command-line.
669- self.assertEqual(
670- parsed_changes['Maintainer'],
671- 'Raphael Hertzog <hertzog@debian.org>')
672- self.assertEqual(
673- parsed_changes['Changed-By'],
674- 'Celso Providelo <celso.providelo@canonical.com>')
675-
676- os.unlink(expected_changesfile)
677-
678-
679-class TestGenerateChanges(TestCase):
680- """Test generate_changes()."""
681-
682- def getBaseDsc(self):
683- """Create a basic Dsc object for use with generate_changes()."""
684- dsc = Dsc()
685- dsc["source"] = "mysrcpkg"
686- dsc["binary"] = "mybinpkg"
687- dsc["version"] = "4.2"
688- dsc["maintainer"] = "Maintainer <maintainer@ubuntu.com>"
689- return dsc
690-
691- def getBaseOrigin(self):
692- """Create a basic Origin dict for use with generate_changes()."""
693- origin = Deb822Dict()
694- origin["Name"] = "Debian"
695- origin["Suite"] = "sid"
696- return origin
697-
698- def generateChanges(self, dsc=None, dsc_files=None, suite="maverick",
699- changelog=None, urgency="low", closes=None,
700- lp_closes=None, section="net", priority="extra",
701- description=None, files_from_librarian=[],
702- requested_by="Somebody <somebody@ubuntu.com>",
703- origin=None):
704- if dsc is None:
705- dsc = self.getBaseDsc()
706- if dsc_files is None:
707- dsc_files = []
708- if origin is None:
709- origin = self.getBaseOrigin()
710- if changelog is None:
711- changelog = 'changelog entry'
712- return generate_changes(
713- dsc=dsc, dsc_files=dsc_files, suite=suite, changelog=changelog,
714- urgency=urgency, closes=closes, lp_closes=lp_closes,
715- section=section, priority=priority, description=description,
716- files_from_librarian=files_from_librarian,
717- requested_by=requested_by, origin=origin)
718-
719- def test_minimum_fields(self):
720- # The right (minimum) set of fields are set by generate_changes().
721- changes = self.generateChanges()
722- self.assertEquals("1.7", changes["Format"])
723- self.assertEquals("mysrcpkg", changes["Source"])
724- self.assertEquals("mybinpkg", changes["Binary"])
725- self.assertEquals("source", changes["Architecture"])
726- self.assertEquals("4.2", changes["Version"])
727- self.assertEquals("maverick", changes["Distribution"])
728- self.assertEquals("low", changes["Urgency"])
729- self.assertEquals("\nchangelog entry", changes["Changes"])
730- self.assertEquals(
731- "Maintainer <maintainer@ubuntu.com>", changes["Maintainer"])
732- self.assertNotIn("Description", changes)
733- self.assertNotIn("Closes", changes)
734- self.assertNotIn("Launchpad-bugs-fixed", changes)
735- self.assertEquals([], changes["Files"])
736-
737- def test_closes(self):
738- # Closes gets set if any Debian bugs to close were specified.
739- changes = self.generateChanges(closes=["1232", "4323"])
740- self.assertEquals("1232 4323", changes["Closes"])
741- self.assertNotIn("Launchpad-bugs-fixed", changes)
742-
743- def test_binary_newline(self):
744- # If the Dsc Binary: line contains newlines those are properly
745- # formatted in the new changes file.
746- dsc = self.getBaseDsc()
747- dsc["Binary"] = "binary1\n binary2 \n binary3"
748- changes = self.generateChanges(dsc=dsc)
749- self.assertEquals("binary1\n binary2 \n binary3", changes["Binary"])
750-
751- def test_lp_closes(self):
752- # Launchpad-Bugs-Fixed gets set if any Launchpad bugs to close were
753- # specified.
754- changes = self.generateChanges(lp_closes=["987987"])
755- self.assertEquals("987987", changes["Launchpad-Bugs-Fixed"])
756-
757- def test_utf8_changelog(self):
758- # A changelog entry with non-ASCII UTF-8 characters is serialized in
759- # Changes properly.
760- changes = self.generateChanges(
761- changelog="* Updated French translation by J\xc3\xa9lmer.")
762- contents = changes.dump(encoding="utf-8").encode("utf-8")
763- self.assertIn(
764- "Updated French translation by J\xc3\xa9lmer.", contents)
765-
766- def test_changelog_whitelines(self):
767- # The changelog entry can contain empty lines, and this should not
768- # mess up the parsing of the changes file.
769- changelog = "* Foo\n\n\n* Bar\n.\nEntries"
770- changes = self.generateChanges(changelog=changelog)
771- contents = changes.dump(encoding="utf-8").encode("utf-8")
772- # Read contents back
773- read_changes = Changes(contents)
774- self.assertEquals("\n%s" % changelog, changes['Changes'])
775- self.assertContentEqual([
776- 'Architecture',
777- 'Binary',
778- 'Changed-By',
779- 'Changes',
780- 'Date',
781- 'Distribution',
782- 'Files',
783- 'Format',
784- 'Maintainer',
785- 'Origin',
786- 'Source',
787- 'Urgency',
788- 'Version',
789- ], read_changes.keys())
790
791=== removed file 'scripts/ftpmaster-tools/_syncorigins.py'
792--- scripts/ftpmaster-tools/_syncorigins.py 2011-10-07 17:44:22 +0000
793+++ scripts/ftpmaster-tools/_syncorigins.py 1970-01-01 00:00:00 +0000
794@@ -1,680 +0,0 @@
795-#!/usr/bin/env python
796-#
797-# Copyright 2009 Canonical Ltd. This software is licensed under the
798-# GNU Affero General Public License version 3 (see the file LICENSE).
799-
800-"""Origins dictionary containing all mirrors used for sync-source.py."""
801-
802-__all__ = ['origins']
803-
804-origins = {
805-
806-"debian": {
807- "name": "Debian",
808- "url": "http://ftp.debian.org/debian/",
809- "default suite": "testing",
810- "default component": "main",
811- "dsc": "must be signed and valid"
812- },
813-
814-"security": {
815- "name": "Security",
816- "url": "http://security.debian.org/debian-security/",
817- "default suite": "etch/updates",
818- "default component": "main",
819- "dsc": "must be signed and valid"
820- },
821-
822-"incoming": {
823- "name": "Debian",
824- "url": "http://incoming.debian.org/",
825- "default suite": "incoming",
826- "default component": "main",
827- "dsc": "must be signed and valid"
828- },
829-
830-"blackdown": {
831- "name": "Blackdown",
832- "url": "http://ftp.gwdg.de/pub/languages/java/linux/debian/",
833- "default suite": "unstable",
834- "default component": "non-free",
835- "dsc": "must be signed and valid"
836- },
837-
838-"marillat": {
839- "name": "Marillat",
840- "url": "ftp://ftp.nerim.net/debian-marillat/",
841- "default suite": "unstable",
842- "default component": "main",
843- "dsc": "can be unsigned"
844- },
845-
846-"mythtv": {
847- "name": "MythTV",
848- "url": "http://dijkstra.csh.rit.edu/~mdz/debian/",
849- "default suite": "unstable",
850- "default component": "mythtv",
851- "dsc": "can be unsigned"
852- },
853-
854-"xfce": {
855- "name": "XFCE",
856- "url": "http://www.os-works.com/debian/",
857- "default suite": "testing",
858- "default component": "main",
859- "dsc": "must be signed and valid"
860- },
861-
862-####################################
863-
864-"apt.logreport.org-pub-debian": {
865- "name": "apt.logreport.org-pub-debian",
866- "url": "http://apt.logreport.org/pub/debian/",
867- "default suite": "local",
868- "default component": "contrib",
869- "dsc": "can be unsigned"
870- },
871-
872-"apt.pgpackages.org-debian": {
873- "name": "apt.pgpackages.org-debian",
874- "url": "http://apt.pgpackages.org/debian/",
875- "default suite": "sid",
876- "default component": "non-free",
877- "dsc": "can be unsigned"
878- },
879-
880-"arda.lt-p.net-debian": {
881- "name": "arda.LT-P.net-debian",
882- "url": "http://arda.LT-P.net/debian/",
883- "default suite": "",
884- "default component": "",
885- "dsc": "can be unsigned"
886- },
887-
888-"colo.khms.westfalen.de-pakete": {
889- "name": "colo.khms.westfalen.de-Pakete",
890- "url": "http://colo.khms.westfalen.de/Pakete/",
891- "default suite": "unstable",
892- "default component": "",
893- "dsc": "can be unsigned"
894- },
895-
896-"debian.speedblue.org": {
897- "name": "debian.speedblue.org",
898- "url": "http://debian.speedblue.org/",
899- "default suite": "",
900- "default component": "",
901- "dsc": "can be unsigned"
902- },
903-
904-"dl.gna.org-kazehakase": {
905- "name": "dl.gna.org-kazehakase",
906- "url": "http://dl.gna.org/kazehakase/",
907- "default suite": "debian",
908- "default component": "",
909- "dsc": "can be unsigned"
910- },
911-
912-"elonen.iki.fi-code-unofficial-debs": {
913- "name": "elonen.iki.fi-code-unofficial-debs",
914- "url": "http://elonen.iki.fi/code/unofficial-debs/",
915- "default suite": "",
916- "default component": "",
917- "dsc": "can be unsigned"
918- },
919-
920-"erlug.linux.it-%7eda-deb": {
921- "name": "erlug.linux.it-%7Eda-deb",
922- "url": "http://erlug.linux.it/~da/deb/",
923- "default suite": "",
924- "default component": "",
925- "dsc": "can be unsigned"
926- },
927-
928-"ftp.arege.jp-debian-arege": {
929- "name": "ftp.arege.jp-debian-arege",
930- "url": "http://ftp.arege.jp/debian-arege/",
931- "default suite": "sid",
932- "default component": "ALL",
933- "dsc": "can be unsigned"
934- },
935-
936-"instantafs.cbs.mpg.de-instantafs-sid": {
937- "name": "instantafs.cbs.mpg.de-instantafs-sid",
938- "url": "ftp://instantafs.cbs.mpg.de/instantafs/sid/",
939- "default suite": "",
940- "default component": "",
941- "dsc": "can be unsigned"
942- },
943-
944-"jeroen.coekaerts.be-debian": {
945- "name": "jeroen.coekaerts.be-debian",
946- "url": "http://jeroen.coekaerts.be/debian/",
947- "default suite": "unstable",
948- "default component": "non-free",
949- "dsc": "can be unsigned"
950- },
951-
952-"laylward.com-debian": {
953- "name": "laylward.com-debian",
954- "url": "http://laylward.com/debian/",
955- "default suite": "unstable",
956- "default component": "",
957- "dsc": "can be unsigned"
958- },
959-
960-"mherrn.de-debian": {
961- "name": "mherrn.de-debian",
962- "url": "http://mherrn.de/debian/",
963- "default suite": "sid",
964- "default component": "exim",
965- "dsc": "can be unsigned"
966- },
967-
968-"mulk.dyndns.org-apt": {
969- "name": "mulk.dyndns.org-apt",
970- "url": "http://mulk.dyndns.org/apt/",
971- "default suite": "unstable",
972- "default component": "main",
973- "dsc": "can be unsigned"
974- },
975-
976-"opensource.polytechnique.org-debian": {
977- "name": "opensource.polytechnique.org-debian",
978- "url": "http://opensource.polytechnique.org/debian/",
979- "default suite": "",
980- "default component": "",
981- "dsc": "can be unsigned"
982- },
983-
984-"people.debian.org-%7eamaya-debian": {
985- "name": "people.debian.org-%7Eamaya-debian",
986- "url": "http://people.debian.org/~amaya/debian/",
987- "default suite": "",
988- "default component": "",
989- "dsc": "can be unsigned"
990- },
991-
992-"people.debian.org-%7ecostela-debian": {
993- "name": "people.debian.org-%7Ecostela-debian",
994- "url": "http://people.debian.org/~costela/debian/",
995- "default suite": "",
996- "default component": "",
997- "dsc": "can be unsigned"
998- },
999-
1000-"people.debian.org-%7ercardenes": {
1001- "name": "people.debian.org-%7Ercardenes",
1002- "url": "http://people.debian.org/~rcardenes/",
1003- "default suite": "sid",
1004- "default component": "main",
1005- "dsc": "can be unsigned"
1006- },
1007-
1008-"piem.homeip.net-%7epiem-debian": {
1009- "name": "piem.homeip.net-%7Epiem-debian",
1010- "url": "http://piem.homeip.net/~piem/debian/",
1011- "default suite": "source",
1012- "default component": "",
1013- "dsc": "can be unsigned"
1014- },
1015-
1016-"progn.org-debian": {
1017- "name": "progn.org-debian",
1018- "url": "ftp://progn.org/debian/",
1019- "default suite": "unstable",
1020- "default component": "main",
1021- "dsc": "can be unsigned"
1022- },
1023-
1024-"ressukka.net-%7eressu-deb": {
1025- "name": "ressukka.net-%7Eressu-deb",
1026- "url": "http://ressukka.net/~ressu/deb/",
1027- "default suite": "unstable",
1028- "default component": "",
1029- "dsc": "can be unsigned"
1030- },
1031-
1032-"sadleder.de-debian": {
1033- "name": "sadleder.de-debian",
1034- "url": "http://sadleder.de/debian/",
1035- "default suite": "",
1036- "default component": "",
1037- "dsc": "can be unsigned"
1038- },
1039-
1040-"security.dsi.unimi.it-%7elorenzo-debian": {
1041- "name": "security.dsi.unimi.it-%7Elorenzo-debian",
1042- "url": "http://security.dsi.unimi.it/~lorenzo/debian/",
1043- "default suite": "",
1044- "default component": "",
1045- "dsc": "can be unsigned"
1046- },
1047-
1048-"src.braincells.com-debian": {
1049- "name": "src.braincells.com-debian",
1050- "url": "http://src.braincells.com/debian/",
1051- "default suite": "sid",
1052- "default component": "",
1053- "dsc": "can be unsigned"
1054- },
1055-
1056-"themind.altervista.org-debian": {
1057- "name": "themind.altervista.org-debian",
1058- "url": "http://themind.altervista.org/debian/",
1059- "default suite": "unstable",
1060- "default component": "main",
1061- "dsc": "can be unsigned"
1062- },
1063-
1064-"www.cps-project.org-debian-unstable": {
1065- "name": "www.cps-project.org-debian-unstable",
1066- "url": "http://www.cps-project.org/debian/unstable/",
1067- "default suite": "",
1068- "default component": "",
1069- "dsc": "can be unsigned"
1070- },
1071-
1072-"www.gwhere.org-download-debian": {
1073- "name": "www.gwhere.org-download-debian",
1074- "url": "http://www.gwhere.org/download/debian/",
1075- "default suite": "unstable",
1076- "default component": "main",
1077- "dsc": "can be unsigned"
1078- },
1079-
1080-"www.knizefamily.net-russ-software-debian": {
1081- "name": "www.knizefamily.net-russ-software-debian",
1082- "url": "http://www.knizefamily.net/russ/software/debian/",
1083- "default suite": "",
1084- "default component": "",
1085- "dsc": "can be unsigned"
1086- },
1087-
1088-"www.litux.org-debian": {
1089- "name": "www.litux.org-debian",
1090- "url": "http://www.litux.org/debian/",
1091- "default suite": "unstable",
1092- "default component": "",
1093- "dsc": "can be unsigned"
1094- },
1095-
1096-"www.steve.org.uk-apt": {
1097- "name": "www.steve.org.uk-apt",
1098- "url": "http://www.steve.org.uk/apt/",
1099- "default suite": "",
1100- "default component": "",
1101- "dsc": "can be unsigned"
1102- },
1103-
1104-"www.stuff.demon.co.uk-apt": {
1105- "name": "www.stuff.demon.co.uk-apt",
1106- "url": "http://www.stuff.demon.co.uk/apt/",
1107- "default suite": "source",
1108- "default component": "",
1109- "dsc": "can be unsigned"
1110- },
1111-
1112-"www.thomas-alfeld.de-frank-download-debian": {
1113- "name": "www.thomas-alfeld.de-frank-download-debian",
1114- "url": "http://www.thomas-alfeld.de/frank/download/debian/",
1115- "default suite": "",
1116- "default component": "",
1117- "dsc": "can be unsigned"
1118- },
1119-
1120-"www.zero-based.org-debian": {
1121- "name": "www.zero-based.org-debian",
1122- "url": "http://www.zero-based.org/debian/",
1123- "default suite": "packagessource",
1124- "default component": "",
1125- "dsc": "can be unsigned"
1126- },
1127-
1128-"kitenet.net-%7ejoey-debian": {
1129- "name": "kitenet.net-%7Ejoey-debian",
1130- "url": "http://kitenet.net/~joey/debian/",
1131- "default suite": "unstable",
1132- "default component": "",
1133- "dsc": "can be unsigned"
1134- },
1135-
1136-"www.roughtrade.net-debian": {
1137- "name": "www.roughtrade.net-debian",
1138- "url": "http://www.roughtrade.net/debian/",
1139- "default suite": "sid",
1140- "default component": "main",
1141- "dsc": "can be unsigned"
1142- },
1143-
1144-"telepathy.freedesktop.org-debian": {
1145- "name": "telepathy.freedesktop.org-debian",
1146- "url": "http://telepathy.freedesktop.org/debian/",
1147- "default suite": "sid",
1148- "default component": "",
1149- "dsc": "can be unsigned"
1150- },
1151-
1152-####################################
1153-
1154-"ftp.mowgli.ch-pub-debian": {
1155- "name": "ftp.mowgli.ch-pub-debian",
1156- "url": "ftp://ftp.mowgli.ch/pub/debian/",
1157- "default suite": "sid",
1158- "default component": "unofficial",
1159- "dsc": "can be unsigned"
1160- },
1161-
1162-"http.debian.or.jp-debian-jp": {
1163- "name": "http.debian.or.jp-debian-jp",
1164- "url": "http://http.debian.or.jp/debian-jp/",
1165- "default suite": "unstable-jp",
1166- "default component": "non-free",
1167- "dsc": "can be unsigned"
1168- },
1169-
1170-"mywebpages.comcast.net-ddamian-deb": {
1171- "name": "mywebpages.comcast.net-ddamian-deb",
1172- "url": "http://mywebpages.comcast.net/ddamian/deb/",
1173- "default suite": "",
1174- "default component": "",
1175- "dsc": "can be unsigned"
1176- },
1177-
1178-"people.debian.org-%7etora-deb": {
1179- "name": "people.debian.org-%7Etora-deb",
1180- "url": "http://people.debian.org/~tora/deb/",
1181- "default suite": "",
1182- "default component": "",
1183- "dsc": "can be unsigned"
1184- },
1185-
1186-"silcnet.org-download-client-deb": {
1187- "name": "silcnet.org-download-client-deb",
1188- "url": "http://silcnet.org/download/client/deb/",
1189- "default suite": "",
1190- "default component": "",
1191- "dsc": "can be unsigned"
1192- },
1193-
1194-"www.h.shuttle.de-mitch-stuff": {
1195- "name": "www.h.shuttle.de-mitch-stuff",
1196- "url": "http://www.h.shuttle.de/mitch/stuff/",
1197- "default suite": "",
1198- "default component": "",
1199- "dsc": "can be unsigned"
1200- },
1201-
1202-"www.assist.media.nagoya-u.ac.jp-%7ekatsu-debian": {
1203- "name": "www.assist.media.nagoya-u.ac.jp-%7Ekatsu-debian",
1204- "url": "http://www.assist.media.nagoya-u.ac.jp/~katsu/debian/",
1205- "default suite": "unstable",
1206- "default component": "ALL",
1207- "dsc": "can be unsigned"
1208- },
1209-
1210-"www.stud.tu-ilmenau.de-%7ethsc-in-debian": {
1211- "name": "www.stud.tu-ilmenau.de-%7Ethsc-in-debian",
1212- "url": "http://www.stud.tu-ilmenau.de/~thsc-in/debian/",
1213- "default suite": "unstable",
1214- "default component": "main",
1215- "dsc": "can be unsigned"
1216- },
1217-
1218-"debian.hinterhof.net": {
1219- "name": "debian.hinterhof.net",
1220- "url": "http://debian.hinterhof.net/",
1221- "default suite": "unstable",
1222- "default component": "",
1223- "dsc": "can be unsigned"
1224- },
1225-
1226-"home.planet.nl-%7eautar022": {
1227- "name": "home.planet.nl-%7Eautar022",
1228- "url": "http://home.planet.nl/~autar022/",
1229- "default suite": "",
1230- "default component": "",
1231- "dsc": "can be unsigned"
1232- },
1233-
1234-"dept-info.labri.fr-%7edanjean-debian": {
1235- "name": "dept-info.labri.fr-%7Edanjean-debian",
1236- "url": "http://dept-info.labri.fr/~danjean/debian/",
1237- "default suite": "unstable",
1238- "default component": "main",
1239- "dsc": "can be unsigned"
1240- },
1241-
1242-"noxa.de-%7esbeyer-debian": {
1243- "name": "noxa.de-%7Esbeyer-debian",
1244- "url": "http://noxa.de/~sbeyer/debian/",
1245- "default suite": "unstable",
1246- "default component": "main",
1247- "dsc": "can be unsigned"
1248- },
1249-
1250-"debian.wgdd.de-debian": {
1251- "name": "debian.wgdd.de-debian",
1252- "url": "http://debian.wgdd.de/debian/",
1253- "default suite": "sid",
1254- "default component": "non-free",
1255- "dsc": "can be unsigned"
1256- },
1257-
1258-"luca.pca.it-debian": {
1259- "name": "luca.pca.it-debian",
1260- "url": "http://luca.pca.it/debian/",
1261- "default suite": "",
1262- "default component": "",
1263- "dsc": "can be unsigned"
1264- },
1265-
1266-"www.pcxperience.org-apt-debian": {
1267- "name": "www.pcxperience.org-apt-debian",
1268- "url": "http://www.pcxperience.org/apt/debian/",
1269- "default suite": "unstable",
1270- "default component": "",
1271- "dsc": "can be unsigned"
1272- },
1273-
1274-"ftp.berlios.de-pub-gift-fasttrack": {
1275- "name": "ftp.berlios.de-pub-gift-fasttrack",
1276- "url": "ftp://ftp.berlios.de/pub/gift-fasttrack/",
1277- "default suite": "unstable",
1278- "default component": "main",
1279- "dsc": "can be unsigned"
1280- },
1281-
1282-"www.webalice.it-hayarms-debian": {
1283- "name": "www.webalice.it-hayarms-debian",
1284- "url": "http://www.webalice.it/hayarms/debian/",
1285- "default suite": "unstable",
1286- "default component": "non-free",
1287- "dsc": "can be unsigned"
1288- },
1289-
1290-"users.adelphia.net-%7edavid.everly": {
1291- "name": "users.adelphia.net-%7Edavid.everly",
1292- "url": "http://users.adelphia.net/~david.everly/",
1293- "default suite": "emilda/sarge",
1294- "default component": "",
1295- "dsc": "can be unsigned"
1296- },
1297-
1298-"debian.thk-systems.de-debian": {
1299- "name": "debian.thk-systems.de-debian",
1300- "url": "http://debian.thk-systems.de/debian/",
1301- "default suite": "unstable",
1302- "default component": "",
1303- "dsc": "can be unsigned"
1304- },
1305-
1306-"www.adebenham.com-debian": {
1307- "name": "www.adebenham.com-debian",
1308- "url": "http://www.adebenham.com/debian/",
1309- "default suite": "",
1310- "default component": "",
1311- "dsc": "can be unsigned"
1312- },
1313-
1314-"eric.lavar.de-comp-linux-debian": {
1315- "name": "eric.lavar.de-comp-linux-debian",
1316- "url": "http://eric.lavar.de/comp/linux/debian/",
1317- "default suite": "experimental",
1318- "default component": "",
1319- "dsc": "can be unsigned"
1320- },
1321-
1322-"einsteinmg.dyndns.org-debian": {
1323- "name": "einsteinmg.dyndns.org-debian",
1324- "url": "http://einsteinmg.dyndns.org/debian/",
1325- "default suite": "unstable",
1326- "default component": "",
1327- "dsc": "can be unsigned"
1328- },
1329-
1330-"www.toastfreeware.priv.at-debian": {
1331- "name": "www.toastfreeware.priv.at-debian",
1332- "url": "http://www.toastfreeware.priv.at/debian/",
1333- "default suite": "unstable",
1334- "default component": "",
1335- "dsc": "can be unsigned"
1336- },
1337-
1338-"www.riteh.hr-%7evedranf-debian-unstable": {
1339- "name": "www.riteh.hr-%7Evedranf-debian-unstable",
1340- "url": "http://www.riteh.hr/~vedranf/debian_unstable/",
1341- "default suite": "",
1342- "default component": "",
1343- "dsc": "can be unsigned"
1344- },
1345-
1346-"ftp.unixdev.net-pub-debian-udev": {
1347- "name": "ftp.unixdev.net-pub-debian-udev",
1348- "url": "http://ftp.unixdev.net/pub/debian-udev/",
1349- "default suite": "unixdev",
1350- "default component": "non-free",
1351- "dsc": "can be unsigned"
1352- },
1353-
1354-"packages.kirya.net": {
1355- "name": "packages.kirya.net",
1356- "url": "http://packages.kirya.net/",
1357- "default suite": "unstable",
1358- "default component": "non-free",
1359- "dsc": "can be unsigned"
1360- },
1361-
1362-"repos.knio.it": {
1363- "name": "repos.knio.it",
1364- "url": "http://repos.knio.it/",
1365- "default suite": "unstable",
1366- "default component": "non-free",
1367- "dsc": "can be unsigned"
1368- },
1369-
1370-"www.wakhok.ac.jp-%7efujimura-debian": {
1371- "name": "www.wakhok.ac.jp-%7Efujimura-debian",
1372- "url": "http://www.wakhok.ac.jp/~fujimura/debian/",
1373- "default suite": "",
1374- "default component": "",
1375- "dsc": "can be unsigned"
1376- },
1377-
1378-"www.eto.to-deb": {
1379- "name": "www.eto.to-deb",
1380- "url": "http://www.eto.to/deb/",
1381- "default suite": "",
1382- "default component": "",
1383- "dsc": "can be unsigned"
1384- },
1385-
1386-"y-imai.good-day.net-debian": {
1387- "name": "y-imai.good-day.net-debian",
1388- "url": "http://y-imai.good-day.net/debian/",
1389- "default suite": "",
1390- "default component": "",
1391- "dsc": "can be unsigned"
1392- },
1393-
1394-"people.realnode.com-%7emnordstr": {
1395- "name": "people.realnode.com-%7Emnordstr",
1396- "url": "http://people.realnode.com/~mnordstr/",
1397- "default suite": "package",
1398- "default component": "",
1399- "dsc": "can be unsigned"
1400- },
1401-
1402-"rapid.dotsrc.org": {
1403- "name": "rapid.dotsrc.org",
1404- "url": "http://rapid.dotsrc.org/",
1405- "default suite": "unstable",
1406- "default component": "",
1407- "dsc": "can be unsigned"
1408- },
1409-
1410-"debian-eclipse.wfrag.org-debian": {
1411- "name": "debian-eclipse.wfrag.org-debian",
1412- "url": "http://debian-eclipse.wfrag.org/debian/",
1413- "default suite": "sid",
1414- "default component": "non-free",
1415- "dsc": "can be unsigned"
1416- },
1417-
1418-"www.stanchina.net-%7eflavio-debian": {
1419- "name": "www.stanchina.net-%7Eflavio-debian",
1420- "url": "http://www.stanchina.net/~flavio/debian/",
1421- "default suite": "",
1422- "default component": "",
1423- "dsc": "can be unsigned"
1424- },
1425-
1426-"kibi.dyndns.org-packages": {
1427- "name": "kibi.dyndns.org-packages",
1428- "url": "http://kibi.dyndns.org/packages/",
1429- "default suite": "",
1430- "default component": "",
1431- "dsc": "can be unsigned"
1432- },
1433-
1434-"download.gna.org-wormux-debs": {
1435- "name": "download.gna.org-wormux-debs",
1436- "url": "http://download.gna.org/wormux/debs/",
1437- "default suite": "dapper",
1438- "default component": "",
1439- "dsc": "can be unsigned"
1440- },
1441-
1442-"apt.alittletooquiet.net-staging": {
1443- "name": "apt.alittletooquiet.net-staging",
1444- "url": "http://apt.alittletooquiet.net/staging/",
1445- "default suite": "dapper",
1446- "default component": "main",
1447- "dsc": "can be unsigned"
1448- },
1449-
1450-"www.debian-multimedia.org": {
1451- "name": "www.debian-multimedia.org",
1452- "url": "http://www.debian-multimedia.org/",
1453- "default suite": "unstable",
1454- "default component": "main",
1455- "dsc": "can be unsigned",
1456- },
1457-
1458-"repository.maemo.org": {
1459- "name": "Maemo",
1460- "url": "http://repository.maemo.org/",
1461- "default suite": "bora",
1462- "default component": "free",
1463- "dsc": "can be unsigned",
1464- },
1465-
1466-"mirror.err.no-uqm": {
1467- "name": "mirror.err.no-uqm",
1468- "url": "http://mirror.err.no/uqm/",
1469- "default suite": "unstable",
1470- "default component": "",
1471- "dsc": "can be unsigned"
1472- },
1473-
1474-}
1475
1476=== removed file 'scripts/ftpmaster-tools/sync-source.py'
1477--- scripts/ftpmaster-tools/sync-source.py 2012-01-06 11:08:30 +0000
1478+++ scripts/ftpmaster-tools/sync-source.py 1970-01-01 00:00:00 +0000
1479@@ -1,782 +0,0 @@
1480-#!/usr/bin/python -S
1481-#
1482-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
1483-# GNU Affero General Public License version 3 (see the file LICENSE).
1484-
1485-# <james.troup@canonical.com>
1486-# pylint: disable-msg=W0403
1487-
1488-""" 'Sync' a source package by generating an upload.
1489-
1490-This is a straight port of the original dak 'josie' tool to soyuz.
1491-
1492-Long term once soyuz is monitoring other archives regularly, syncing
1493-will become a matter of simply 'publishing' source from Debian unstable
1494-wherever) into Ubuntu dapper and the whole fake upload trick can go away.
1495-"""
1496-
1497-import _pythonpath
1498-
1499-import errno
1500-import os
1501-import re
1502-import shutil
1503-import stat
1504-import string
1505-import tempfile
1506-import urllib
1507-
1508-from _syncorigins import origins
1509-import apt_pkg
1510-from debian.deb822 import Dsc
1511-from zope.component import getUtility
1512-
1513-from lp.archiveuploader.utils import (
1514- DpkgSourceError,
1515- extract_dpkg_source,
1516- )
1517-from lp.registry.interfaces.distribution import IDistributionSet
1518-from lp.registry.interfaces.person import IPersonSet
1519-from lp.registry.interfaces.pocket import PackagePublishingPocket
1520-from lp.services.database.sqlbase import (
1521- cursor,
1522- sqlvalues,
1523- )
1524-from lp.services.librarian.client import LibrarianClient
1525-from lp.services.scripts.base import (
1526- LaunchpadScript,
1527- LaunchpadScriptFailure,
1528- )
1529-from lp.soyuz.enums import (
1530- PackagePublishingStatus,
1531- re_bug_numbers,
1532- re_closes,
1533- re_lp_closes,
1534- )
1535-from lp.soyuz.scripts.ftpmaster import (
1536- generate_changes,
1537- SyncSource,
1538- SyncSourceError,
1539- )
1540-
1541-
1542-reject_message = ""
1543-re_no_epoch = re.compile(r"^\d+\:")
1544-re_strip_revision = re.compile(r"-([^-]+)$")
1545-re_changelog_header = re.compile(
1546- r"^\S+ \((?P<version>.*)\) .*;.*urgency=(?P<urgency>\w+).*")
1547-
1548-
1549-Blacklisted = None
1550-Library = None
1551-Log = None
1552-Options = None
1553-
1554-
1555-def md5sum_file(filename):
1556- file_handle = open(filename)
1557- md5sum = apt_pkg.md5sum(file_handle)
1558- file_handle.close()
1559- return md5sum
1560-
1561-
1562-def reject(str, prefix="Rejected: "):
1563- global reject_message
1564- if str:
1565- reject_message += prefix + str + "\n"
1566-
1567-
1568-# Following two functions are borrowed and (modified) from apt-listchanges
1569-def urgency_to_numeric(u):
1570- urgency_map = {
1571- 'low': 1,
1572- 'medium': 2,
1573- 'high': 3,
1574- 'emergency': 4,
1575- 'critical': 4,
1576- }
1577- return urgency_map.get(u.lower(), 1)
1578-
1579-
1580-def urgency_from_numeric(n):
1581- urgency_map = {
1582- 1: 'low',
1583- 2: 'medium',
1584- 3: 'high',
1585- 4: 'critical',
1586- }
1587- return urgency_map.get(n, 'low')
1588-
1589-
1590-def parse_changelog(changelog_filename, previous_version):
1591- if not os.path.exists(changelog_filename):
1592- raise LaunchpadScriptFailure(
1593- "debian/changelog not found in extracted source.")
1594- urgency = urgency_to_numeric('low')
1595- changes = ""
1596- is_debian_changelog = 0
1597- changelog_file = open(changelog_filename)
1598- for line in changelog_file.readlines():
1599- match = re_changelog_header.match(line)
1600- if match:
1601- is_debian_changelog = 1
1602- if previous_version is None:
1603- previous_version = "9999:9999"
1604- elif apt_pkg.version_compare(
1605- match.group('version'), previous_version) > 0:
1606- urgency = max(
1607- urgency_to_numeric(match.group('urgency')), urgency)
1608- else:
1609- break
1610- changes += line
1611-
1612- if not is_debian_changelog:
1613- raise LaunchpadScriptFailure("header not found in debian/changelog")
1614-
1615- closes = []
1616- for match in re_closes.finditer(changes):
1617- bug_match = re_bug_numbers.findall(match.group(0))
1618- closes += map(int, bug_match)
1619-
1620- l = map(int, closes)
1621- l.sort()
1622- closes = map(str, l)
1623-
1624- lp_closes = []
1625- for match in re_lp_closes.finditer(changes):
1626- bug_match = re_bug_numbers.findall(match.group(0))
1627- lp_closes += map(int, bug_match)
1628-
1629- l = map(int, lp_closes)
1630- l.sort()
1631- lp_closes = map(str, l)
1632-
1633- return (changes, urgency_from_numeric(urgency), closes, lp_closes)
1634-
1635-
1636-def fix_changelog(changelog):
1637- """Fix debian/changelog entries to be in .changes compatible format."""
1638- fixed = []
1639- fixed_idx = -1
1640- for line in changelog.split("\n"):
1641- if line == "":
1642- fixed += [" ."]
1643- fixed_idx += 1
1644- elif line.startswith(" --"):
1645- # Strip any 'blank' lines preceding the footer
1646- while fixed[fixed_idx] == " .":
1647- fixed.pop()
1648- fixed_idx -= 1
1649- else:
1650- fixed += [" %s" % (line)]
1651- fixed_idx += 1
1652- # Strip trailing 'blank' lines
1653- while fixed[fixed_idx] == " .":
1654- fixed.pop()
1655- fixed_idx -= 1
1656- fixed_changelog = "\n".join(fixed)
1657- fixed_changelog += "\n"
1658- return fixed_changelog
1659-
1660-
1661-def parse_control(control_filename):
1662- """Parse a debian/control file.
1663-
1664- Extract section, priority and description if possible.
1665- """
1666- source_name = ""
1667- source_section = "-"
1668- source_priority = "-"
1669- source_description = ""
1670-
1671- if not os.path.exists(control_filename):
1672- raise LaunchpadScriptFailure(
1673- "debian/control not found in extracted source.")
1674- control_filehandle = open(control_filename)
1675- control = apt_pkg.TagFile(control_filehandle)
1676- for control_section in control:
1677- source = control_section.find("Source")
1678- package = control_section.find("Package")
1679- section = control_section.find("Section")
1680- priority = control_section.find("Priority")
1681- description = control_section.find("Description")
1682- if source is not None:
1683- if section is not None:
1684- source_section = section
1685- if priority is not None:
1686- source_priority = priority
1687- source_name = source
1688- if package is not None and package == source_name:
1689- source_description = (
1690- "%-10s - %-.65s" % (package, description.split("\n")[0]))
1691- control_filehandle.close()
1692-
1693- return (source_section, source_priority, source_description)
1694-
1695-
1696-def extract_source(dsc_filename):
1697- # Create and move into a temporary directory
1698- tmpdir = tempfile.mkdtemp()
1699- old_cwd = os.getcwd()
1700-
1701- # Extract the source package
1702- try:
1703- extract_dpkg_source(dsc_filename, tmpdir)
1704- except DpkgSourceError, e:
1705- print " * command was '%s'" % (e.command)
1706- print e.output
1707- raise LaunchpadScriptFailure(
1708- "'dpkg-source -x' failed for %s [return code: %s]." %
1709- (dsc_filename, e.result))
1710-
1711- os.chdir(tmpdir)
1712- return (old_cwd, tmpdir)
1713-
1714-
1715-def cleanup_source(tmpdir, old_cwd, dsc):
1716- # Sanity check that'll probably break if people set $TMPDIR, but
1717- # WTH, shutil.rmtree scares me
1718- if not tmpdir.startswith("/tmp/"):
1719- raise LaunchpadScriptFailure(
1720- "%s: tmpdir doesn't start with /tmp" % (tmpdir))
1721-
1722- # Move back and cleanup the temporary tree
1723- os.chdir(old_cwd)
1724- try:
1725- shutil.rmtree(tmpdir)
1726- except OSError, e:
1727- if errno.errorcode[e.errno] != 'EACCES':
1728- raise LaunchpadScriptFailure(
1729- "%s: couldn't remove tmp dir for source tree."
1730- % (dsc["source"]))
1731-
1732- reject("%s: source tree could not be cleanly removed."
1733- % (dsc["source"]))
1734- # We probably have u-r or u-w directories so chmod everything
1735- # and try again.
1736- cmd = "chmod -R u+rwx %s" % (tmpdir)
1737- result = os.system(cmd)
1738- if result != 0:
1739- raise LaunchpadScriptFailure(
1740- "'%s' failed with result %s." % (cmd, result))
1741- shutil.rmtree(tmpdir)
1742- except:
1743- raise LaunchpadScriptFailure(
1744- "%s: couldn't remove tmp dir for source tree." % (dsc["source"]))
1745-
1746-
1747-def check_dsc(dsc, current_sources, current_binaries):
1748- source = dsc["source"]
1749- if source in current_sources:
1750- source_component = current_sources[source][1]
1751- else:
1752- source_component = "universe"
1753- for binary in map(string.strip, dsc["binary"].split(',')):
1754- if binary in current_binaries:
1755- (current_version, current_component) = current_binaries[binary]
1756-
1757- # Check that a non-main source package is not trying to
1758- # override a main binary package
1759- if current_component == "main" and source_component != "main":
1760- if not Options.forcemore:
1761- raise LaunchpadScriptFailure(
1762- "%s is in main but its source (%s) is not." %
1763- (binary, source))
1764- else:
1765- Log.warning(
1766- "%s is in main but its source (%s) is not - "
1767- "continuing anyway." % (binary, source))
1768-
1769- # Check that a source package is not trying to override an
1770- # ubuntu-modified binary package
1771- ubuntu_bin = current_binaries[binary][0].find("ubuntu")
1772- if not Options.force and ubuntu_bin != -1:
1773- raise LaunchpadScriptFailure(
1774- "%s is trying to override %s_%s without -f/--force." %
1775- (source, binary, current_version))
1776- print "I: %s [%s] -> %s_%s [%s]." % (
1777- source, source_component, binary, current_version,
1778- current_component)
1779-
1780-
1781-def import_dsc(dsc_filename, suite, previous_version, signing_rules,
1782- files_from_librarian, requested_by, origin, current_sources,
1783- current_binaries):
1784- dsc_file = open(dsc_filename, 'r')
1785- dsc = Dsc(dsc_file)
1786-
1787- if signing_rules.startswith("must be signed"):
1788- dsc_file.seek(0)
1789- (gpg_pre, payload, gpg_post) = Dsc.split_gpg_and_payload(dsc_file)
1790- if gpg_pre == [] and gpg_post == []:
1791- raise LaunchpadScriptFailure(
1792- "signature required for %s but not present" % dsc_filename)
1793- if signing_rules == "must be signed and valid":
1794- if (gpg_pre[0] != "-----BEGIN PGP SIGNED MESSAGE-----" or
1795- gpg_post[0] != "-----BEGIN PGP SIGNATURE-----"):
1796- raise LaunchpadScriptFailure(
1797- "signature for %s invalid %r %r" %
1798- (dsc_filename, gpg_pre, gpg_post))
1799-
1800- dsc_files = dict((entry['name'], entry) for entry in dsc['files'])
1801- check_dsc(dsc, current_sources, current_binaries)
1802-
1803- # Add the .dsc itself to dsc_files so it's listed in the Files: field
1804- dsc_base_filename = os.path.basename(dsc_filename)
1805- dsc_files.setdefault(dsc_base_filename, {})
1806- dsc_files[dsc_base_filename]["md5sum"] = md5sum_file(dsc_filename)
1807- dsc_files[dsc_base_filename]["size"] = os.stat(dsc_filename)[stat.ST_SIZE]
1808-
1809- (old_cwd, tmpdir) = extract_source(dsc_filename)
1810-
1811- # Get the upstream version
1812- upstr_version = re_no_epoch.sub('', dsc["version"])
1813- if re_strip_revision.search(upstr_version):
1814- upstr_version = re_strip_revision.sub('', upstr_version)
1815-
1816- # Ensure the changelog file exists
1817- changelog_filename = (
1818- "%s-%s/debian/changelog" % (dsc["source"], upstr_version))
1819-
1820- # Parse it and then adapt it for .changes
1821- (changelog, urgency, closes, lp_closes) = parse_changelog(
1822- changelog_filename, previous_version)
1823- changelog = fix_changelog(changelog)
1824-
1825- # Parse the control file
1826- control_filename = "%s-%s/debian/control" % (dsc["source"], upstr_version)
1827- (section, priority, description) = parse_control(control_filename)
1828-
1829- cleanup_source(tmpdir, old_cwd, dsc)
1830-
1831- changes = generate_changes(
1832- dsc, dsc_files, suite, changelog, urgency, closes, lp_closes,
1833- section, priority, description, files_from_librarian, requested_by,
1834- origin)
1835-
1836- output_filename = "%s_%s_source.changes" % (
1837- dsc["source"], re_no_epoch.sub('', dsc["version"]))
1838-
1839- filehandle = open(output_filename, 'w')
1840- try:
1841- changes.dump(filehandle, encoding="utf-8")
1842- finally:
1843- filehandle.close()
1844-
1845-
1846-def read_current_source(distro_series, valid_component=None, arguments=None):
1847- """Returns a dictionary of packages in 'suite'.
1848-
1849- The dictionary contains their version as the attribute.
1850- 'component' is an optional list of (comma or whitespace separated)
1851- components to restrict the search to.
1852- """
1853- S = {}
1854-
1855- # XXX cprov 2007-07-10: This searches all pockets of the
1856- # distro_series which is not what we want.
1857- if Options.all:
1858- spp = distro_series.getSourcePackagePublishing(
1859- status=PackagePublishingStatus.PUBLISHED,
1860- pocket=PackagePublishingPocket.RELEASE)
1861- else:
1862- spp = []
1863- for package in arguments:
1864- spp.extend(distro_series.getPublishedSources(package))
1865-
1866- for sp in spp:
1867- component = sp.component.name
1868- version = sp.sourcepackagerelease.version
1869- pkg = sp.sourcepackagerelease.sourcepackagename.name
1870-
1871- if (valid_component is not None and
1872- component != valid_component.name):
1873- Log.warning(
1874- "%s/%s: skipping because it is not in %s component" % (
1875- pkg, version, component))
1876- continue
1877-
1878- if pkg not in S:
1879- S[pkg] = [version, component]
1880- else:
1881- if apt_pkg.version_compare(S[pkg][0], version) < 0:
1882- Log.warning(
1883- "%s: skipping because %s is < %s" % (
1884- pkg, version, S[pkg][0]))
1885- S[pkg] = [version, component]
1886- return S
1887-
1888-
1889-def read_current_binaries(distro_series):
1890- """Returns a dictionary of binaries packages in 'distro_series'.
1891-
1892- The dictionary contains their version and component as the attributes.
1893- """
1894- B = {}
1895-
1896- # XXX cprov 2007-07-10: This searches all pockets of the
1897- # distro_series which is not what we want.
1898-
1899- # XXX James Troup 2006-02-03: this is insanely slow due to how It
1900- # SQLObject works. Can be limited, but only if we know what
1901- # binaries we want to check against, which we don't know till
1902- # we have the .dsc file and currently this function is
1903- # run well before that.
1904- #
1905- # for distroarchseries in distro_series.architectures:
1906- # bpp = distroarchseries.getAllReleasesByStatus(
1907- # PackagePublishingStatus.PUBLISHED)
1908- #
1909- # for bp in bpp:
1910- # component = bp.component.name
1911- # version = bp.binarypackagerelease.version
1912- # pkg = bp.binarypackagerelease.binarypackagename.name
1913- #
1914- # if pkg not in B:
1915- # B[pkg] = [version, component]
1916- # else:
1917- # if apt_pkg.version_compare(B[pkg][0], version) < 0:
1918- # B[pkg] = [version, component]
1919-
1920- # XXX James Troup 2006-02-22: so... let's fall back on raw SQL
1921- das_ids = [das.id for das in distro_series.architectures]
1922- archive_ids = [a.id for a in Options.todistro.all_distro_archives]
1923- cur = cursor()
1924- query = """
1925- SELECT bpn.name, bpr.version, c.name
1926- FROM binarypackagerelease bpr, binarypackagename bpn, component c,
1927- binarypackagepublishinghistory sbpph, distroarchseries dar
1928- WHERE
1929- bpr.binarypackagename = bpn.id AND
1930- sbpph.binarypackagerelease = bpr.id AND
1931- sbpph.component = c.id AND
1932- sbpph.distroarchseries = dar.id AND
1933- sbpph.status = %s AND
1934- sbpph.archive IN %s AND
1935- dar.id IN %s
1936- """ % sqlvalues(
1937- PackagePublishingStatus.PUBLISHED, archive_ids, das_ids)
1938- cur.execute(query)
1939-
1940- print "Getting binaries for %s..." % (distro_series.name)
1941- for (pkg, version, component) in cur.fetchall():
1942- if pkg not in B:
1943- B[pkg] = [version, component]
1944- else:
1945- if apt_pkg.version_compare(B[pkg][0], version) < 0:
1946- B[pkg] = [version, component]
1947- return B
1948-
1949-
1950-def read_Sources(filename, origin):
1951- S = {}
1952-
1953- suite = origin["suite"]
1954- component = origin["component"]
1955- if suite:
1956- suite = "_%s" % (suite)
1957- if component:
1958- component = "_%s" % (component)
1959-
1960- filename = "%s%s%s_%s" % (origin["name"], suite, component, filename)
1961- sources_filehandle = open(filename)
1962- sources = apt_pkg.TagFile(sources_filehandle)
1963- for sources_section in sources:
1964- pkg = sources_section.find("Package")
1965- version = sources_section.find("Version")
1966-
1967- if pkg in S and apt_pkg.version_compare(
1968- S[pkg]["version"], version) > 0:
1969- continue
1970-
1971- S[pkg] = {}
1972- S[pkg]["version"] = version
1973-
1974- directory = sources_section.find("Directory", "")
1975- files = {}
1976- for line in sources_section.find("Files").split('\n'):
1977- (md5sum, size, filename) = line.strip().split()
1978- files[filename] = {}
1979- files[filename]["md5sum"] = md5sum
1980- files[filename]["size"] = int(size)
1981- files[filename]["remote filename"] = (
1982- os.path.join(directory, filename))
1983- S[pkg]["files"] = files
1984- sources_filehandle.close()
1985- return S
1986-
1987-
1988-def add_source(pkg, Sources, previous_version, suite, requested_by, origin,
1989- current_sources, current_binaries):
1990- print " * Trying to add %s..." % (pkg)
1991-
1992- # Check it's in the Sources file
1993- if pkg not in Sources:
1994- raise LaunchpadScriptFailure(
1995- "%s doesn't exist in the Sources file." % (pkg))
1996-
1997- syncsource = SyncSource(Sources[pkg]["files"], origin, Log,
1998- urllib.urlretrieve, Options.todistro)
1999- try:
2000- files_from_librarian = syncsource.fetchLibrarianFiles()
2001- dsc_filename = syncsource.fetchSyncFiles()
2002- syncsource.checkDownloadedFiles()
2003- except SyncSourceError, e:
2004- raise LaunchpadScriptFailure("Fetching files failed: %s" % (str(e),))
2005-
2006- if dsc_filename is None:
2007- raise LaunchpadScriptFailure(
2008- "No dsc filename in %r" % Sources[pkg]["files"].keys())
2009-
2010- import_dsc(os.path.abspath(dsc_filename), suite, previous_version,
2011- origin["dsc"], files_from_librarian, requested_by, origin,
2012- current_sources, current_binaries)
2013-
2014-
2015-class Percentages:
2016- """Helper to compute percentage ratios compared to a fixed total."""
2017-
2018- def __init__(self, total):
2019- self.total = total
2020-
2021- def get_ratio(self, number):
2022- """Report the ration of `number` to `self.total`, as a percentage."""
2023- return (float(number) / self.total) * 100
2024-
2025-
2026-def do_diff(Sources, Suite, origin, arguments, current_binaries):
2027- stat_us = 0
2028- stat_cant_update = 0
2029- stat_updated = 0
2030- stat_uptodate_modified = 0
2031- stat_uptodate = 0
2032- stat_count = 0
2033- stat_broken = 0
2034- stat_blacklisted = 0
2035-
2036- if Options.all:
2037- packages = Suite.keys()
2038- else:
2039- packages = arguments
2040- packages.sort()
2041- for pkg in packages:
2042- stat_count += 1
2043- dest_version = Suite.get(pkg, [None, ""])[0]
2044-
2045- if pkg not in Sources:
2046- if not Options.all:
2047- raise LaunchpadScriptFailure("%s: not found" % (pkg))
2048- else:
2049- print "[Ubuntu Specific] %s_%s" % (pkg, dest_version)
2050- stat_us += 1
2051- continue
2052-
2053- if pkg in Blacklisted:
2054- print "[BLACKLISTED] %s_%s" % (pkg, dest_version)
2055- stat_blacklisted += 1
2056- continue
2057-
2058- source_version = Sources[pkg]["version"]
2059- if (dest_version is None
2060- or apt_pkg.version_compare(dest_version, source_version) < 0):
2061- if (dest_version is not None
2062- and (not Options.force
2063- and dest_version.find("ubuntu") != -1)):
2064- stat_cant_update += 1
2065- print ("[NOT Updating - Modified] %s_%s (vs %s)"
2066- % (pkg, dest_version, source_version))
2067- else:
2068- stat_updated += 1
2069- print ("[Updating] %s (%s [Ubuntu] < %s [%s])"
2070- % (pkg, dest_version, source_version, origin["name"]))
2071- if Options.action:
2072- add_source(
2073- pkg, Sources,
2074- Suite.get(pkg, ["0", ""])[0], Options.tosuite.name,
2075- Options.requestor, origin, Suite, current_binaries)
2076- else:
2077- if dest_version.find("ubuntu") != -1:
2078- stat_uptodate_modified += 1
2079- if Options.verbose or not Options.all:
2080- print ("[Nothing to update (Modified)] %s_%s (vs %s)"
2081- % (pkg, dest_version, source_version))
2082- else:
2083- stat_uptodate += 1
2084- if Options.verbose or not Options.all:
2085- print (
2086- "[Nothing to update] %s (%s [ubuntu] >= %s [debian])"
2087- % (pkg, dest_version, source_version))
2088-
2089- if Options.all:
2090- percentages = Percentages(stat_count)
2091- print
2092- print ("Out-of-date BUT modified: %3d (%.2f%%)"
2093- % (stat_cant_update, percentages.get_ratio(stat_cant_update)))
2094- print ("Updated: %3d (%.2f%%)"
2095- % (stat_updated, percentages.get_ratio(stat_updated)))
2096- print ("Ubuntu Specific: %3d (%.2f%%)"
2097- % (stat_us, percentages.get_ratio(stat_us)))
2098- print ("Up-to-date [Modified]: %3d (%.2f%%)"
2099- % (stat_uptodate_modified, percentages.get_ratio(
2100- stat_uptodate_modified)))
2101- print ("Up-to-date: %3d (%.2f%%)"
2102- % (stat_uptodate, percentages.get_ratio(stat_uptodate)))
2103- print ("Blacklisted: %3d (%.2f%%)"
2104- % (stat_blacklisted, percentages.get_ratio(stat_blacklisted)))
2105- print ("Broken: %3d (%.2f%%)"
2106- % (stat_broken, percentages.get_ratio(stat_broken)))
2107- print " -----------"
2108- print "Total: %s" % (stat_count)
2109-
2110-
2111-def objectize_options():
2112- """Parse given options.
2113-
2114- Convert 'target_distro', 'target_suite' and 'target_component' to objects
2115- rather than strings.
2116- """
2117- Options.todistro = getUtility(IDistributionSet)[Options.todistro]
2118-
2119- if not Options.tosuite:
2120- Options.tosuite = Options.todistro.currentseries.name
2121- Options.tosuite = Options.todistro.getSeries(Options.tosuite)
2122-
2123- valid_components = (
2124- dict([(component.name, component)
2125- for component in Options.tosuite.components]))
2126-
2127- if Options.tocomponent is not None:
2128-
2129- if Options.tocomponent not in valid_components:
2130- raise LaunchpadScriptFailure(
2131- "%s is not a valid component for %s/%s."
2132- % (Options.tocomponent, Options.todistro.name,
2133- Options.tosuite.name))
2134-
2135- Options.tocomponent = valid_components[Options.tocomponent]
2136-
2137- # Fix up Options.requestor
2138- if not Options.requestor:
2139- Options.requestor = "katie"
2140-
2141- PersonSet = getUtility(IPersonSet)
2142- person = PersonSet.getByName(Options.requestor)
2143- if not person:
2144- raise LaunchpadScriptFailure(
2145- "Unknown LaunchPad user id '%s'." % (Options.requestor))
2146- Options.requestor = "%s <%s>" % (person.displayname,
2147- person.preferredemail.email)
2148- Options.requestor = Options.requestor.encode("ascii", "replace")
2149-
2150-
2151-def parseBlacklist(path):
2152- """Parse given file path as a 'blacklist'.
2153-
2154- Format:
2155-
2156- {{{
2157- # [comment]
2158- <sourcename> # [comment]
2159- }}}
2160-
2161- Return a blacklist dictionary where the keys are blacklisted source
2162- package names.
2163-
2164- Return an empty dictionary if the given 'path' doesn't exist.
2165- """
2166- blacklist = {}
2167-
2168- try:
2169- blacklist_file = open(path)
2170- except IOError:
2171- Log.warning('Could not find blacklist file on %s' % path)
2172- return blacklist
2173-
2174- for line in blacklist_file:
2175- try:
2176- line = line[:line.index("#")]
2177- except ValueError:
2178- pass
2179- line = line.strip()
2180- if not line:
2181- continue
2182- blacklist[line] = ""
2183- blacklist_file.close()
2184-
2185- return blacklist
2186-
2187-
2188-class SyncSourceScript(LaunchpadScript):
2189-
2190- def add_my_options(self):
2191- self.parser.add_option("-a", "--all", dest="all",
2192- default=False, action="store_true",
2193- help="sync all packages")
2194- self.parser.add_option("-b", "--requested-by", dest="requestor",
2195- help="who the sync was requested by")
2196- self.parser.add_option("-f", "--force", dest="force",
2197- default=False, action="store_true",
2198- help="force sync over the top of Ubuntu changes")
2199- self.parser.add_option("-F", "--force-more", dest="forcemore",
2200- default=False, action="store_true",
2201- help="force sync even when components don't match")
2202- self.parser.add_option("-n", "--noaction", dest="action",
2203- default=True, action="store_false",
2204- help="don't do anything")
2205-
2206- # Options controlling where to sync packages to:
2207- self.parser.add_option("-c", "--to-component", dest="tocomponent",
2208- help="limit syncs to packages in COMPONENT")
2209- self.parser.add_option("-d", "--to-distro", dest="todistro",
2210- default='ubuntu', help="sync to DISTRO")
2211- self.parser.add_option("-s", "--to-suite", dest="tosuite",
2212- help="sync to SUITE (aka distroseries)")
2213-
2214- # Options controlling where to sync packages from:
2215- self.parser.add_option("-C", "--from-component", dest="fromcomponent",
2216- help="sync from COMPONENT")
2217- self.parser.add_option("-D", "--from-distro", dest="fromdistro",
2218- default='debian', help="sync from DISTRO")
2219- self.parser.add_option("-S", "--from-suite", dest="fromsuite",
2220- help="sync from SUITE (aka distroseries)")
2221- self.parser.add_option("-B", "--blacklist", dest="blacklist_path",
2222- default="/srv/launchpad.net/dak/sync-blacklist.txt",
2223- help="Blacklist file path.")
2224-
2225- def main(self):
2226- global Blacklisted, Library, Log, Options
2227-
2228- Log = self.logger
2229- Options = self.options
2230-
2231- distro = Options.fromdistro.lower()
2232- if not Options.fromcomponent:
2233- Options.fromcomponent = origins[distro]["default component"]
2234- if not Options.fromsuite:
2235- Options.fromsuite = origins[distro]["default suite"]
2236-
2237- # Sanity checks on options
2238- if not Options.all and not self.args:
2239- raise LaunchpadScriptFailure(
2240- "Need -a/--all or at least one package name as an argument.")
2241-
2242- apt_pkg.init()
2243- Library = LibrarianClient()
2244-
2245- objectize_options()
2246-
2247- Blacklisted = parseBlacklist(Options.blacklist_path)
2248-
2249- origin = origins[Options.fromdistro]
2250- origin["suite"] = Options.fromsuite
2251- origin["component"] = Options.fromcomponent
2252-
2253- Sources = read_Sources("Sources", origin)
2254- Suite = read_current_source(
2255- Options.tosuite, Options.tocomponent, self.args)
2256- current_binaries = read_current_binaries(Options.tosuite)
2257- do_diff(Sources, Suite, origin, self.args, current_binaries)
2258-
2259-
2260-if __name__ == '__main__':
2261- SyncSourceScript('sync-source', 'ro').lock_and_run()