launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #01079
[Merge] lp:~jelmer/launchpad/production-cp-635591 into lp:~launchpad-pqm/launchpad/production-devel
Jelmer Vernooij has proposed merging lp:~jelmer/launchpad/production-cp-635591 into lp:~launchpad-pqm/launchpad/production-devel.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers): release-critical
In sync-source, fix the handling of changelog entries with UTF8-valid but non-ASCII characters.
These changes have landed on devel in r11559 (MP at https://code.edge.launchpad.net/~jelmer/launchpad/635591-sync-source-unicode/+merge/35637
); a follow-up branch to deal with an issue I found during QA is in ec2 at the moment waiting to land on devel (MP here:
https://code.edge.launchpad.net/~jelmer/launchpad/635591-sync-source-unicode/+merge/35715)
I have already QA'ed the changes in this branch on dogfood.
tests: ./bin/test lp.soyuz.scripts.tests.test_sync_source
--
https://code.launchpad.net/~jelmer/launchpad/production-cp-635591/+merge/35748
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jelmer/launchpad/production-cp-635591 into lp:~launchpad-pqm/launchpad/production-devel.
=== modified file 'lib/lp/soyuz/scripts/ftpmaster.py'
--- lib/lp/soyuz/scripts/ftpmaster.py 2010-08-24 15:29:01 +0000
+++ lib/lp/soyuz/scripts/ftpmaster.py 2010-09-16 21:51:24 +0000
@@ -20,11 +20,13 @@
]
import commands
+from debian.deb822 import Changes
import hashlib
import os
import stat
import sys
import tempfile
+import time
import apt_pkg
from zope.component import getUtility
@@ -470,7 +472,7 @@
pass
else:
version = bpph.binarypackagerelease.version
- self.logger.info ("Removed %s_%s from %s/%s ... "
+ self.logger.info("Removed %s_%s from %s/%s ... "
% (package, version,
self.distroseries.name,
distroarchseries.architecturetag))
@@ -1416,3 +1418,58 @@
# Collect extra debug messages from chroot_manager.
for debug_message in chroot_manager._messages:
self.logger.debug(debug_message)
+
+
+def generate_changes(dsc, dsc_files, suite, changelog, urgency, closes,
+ lp_closes, section, priority, description,
+ files_from_librarian, requested_by, origin):
+ """Generate a Changes object.
+
+ :param dsc: A `Dsc` instance for the related source package.
+ :param suite: Distribution name
+ :param changelog: Relevant changelog data
+ :param urgency: Urgency string (low, medium, high, etc)
+ :param closes: Sequence of Debian bug numbers (as strings) fixed by
+ this upload.
+ :param section: Debian section
+ :param priority: Package priority
+ """
+
+ # XXX cprov 2007-07-03:
+ # Changed-By can be extracted from most-recent changelog footer,
+ # but do we care?
+ # XXX James Troup 2006-01-30:
+ # 'Closes' but could be gotten from changelog, but we don't use them?
+
+ changes = Changes()
+ changes["Origin"] = "%s/%s" % (origin["name"], origin["suite"])
+ changes["Format"] = "1.7"
+ changes["Date"] = time.strftime("%a, %d %b %Y %H:%M:%S %z")
+ changes["Source"] = dsc["source"]
+ changes["Binary"] = dsc["binary"]
+ changes["Architecture"] = "source"
+ changes["Version"] = dsc["version"]
+ changes["Distribution"] = suite
+ changes["Urgency"] = urgency
+ changes["Maintainer"] = dsc["maintainer"]
+ changes["Changed-By"] = requested_by
+ if description:
+ changes["Description"] = "\n %s" % description
+ if closes:
+ changes["Closes"] = " ".join(closes)
+ if lp_closes:
+ changes["Launchpad-bugs-fixed"] = " ".join(lp_closes)
+ files = []
+ for filename in dsc_files:
+ if filename in files_from_librarian:
+ continue
+ files.append({"md5sum": dsc_files[filename]["md5sum"],
+ "size": dsc_files[filename]["size"],
+ "section": section,
+ "priority": priority,
+ "name": filename,
+ })
+
+ changes["Files"] = files
+ changes["Changes"] = "\n%s" % changelog
+ return changes
=== modified file 'lib/lp/soyuz/scripts/tests/test_sync_source.py'
--- lib/lp/soyuz/scripts/tests/test_sync_source.py 2010-08-27 12:21:25 +0000
+++ lib/lp/soyuz/scripts/tests/test_sync_source.py 2010-09-16 21:51:24 +0000
@@ -1,17 +1,22 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""SyncSource facilities tests."""
__metaclass__ = type
+from cStringIO import StringIO
+from debian.deb822 import (
+ Changes,
+ Deb822Dict,
+ Dsc,
+ )
import os
import shutil
import subprocess
import sys
import tempfile
from unittest import (
- TestCase,
TestLoader,
)
@@ -29,6 +34,10 @@
from lp.soyuz.scripts.ftpmaster import (
SyncSource,
SyncSourceError,
+ generate_changes,
+ )
+from lp.testing import (
+ TestCase,
)
@@ -42,6 +51,7 @@
Setup and chdir into a temp directory, a jail, where we can
control the file creation properly
"""
+ super(TestSyncSource, self).setUp()
fillLibrarianFile(1, content='one')
fillLibrarianFile(2, content='two')
fillLibrarianFile(54, content='fifty-four')
@@ -57,6 +67,7 @@
chdir back to the previous path (home) and remove the temp
directory used as jail.
"""
+ super(TestSyncSource, self).tearDown()
os.chdir(self._home)
cleanupLibrarianFiles()
shutil.rmtree(self._jail)
@@ -247,6 +258,7 @@
dbuser = 'ro'
def setUp(self):
+ super(TestSyncSourceScript, self).setUp()
self._home = os.getcwd()
self._jail = os.path.join(
os.path.dirname(__file__), 'sync_source_home')
@@ -254,6 +266,7 @@
def tearDown(self):
"""'chdir' back to the previous path (home)."""
+ super(TestSyncSourceScript, self).tearDown()
os.chdir(self._home)
def runSyncSource(self, extra_args=None):
@@ -416,5 +429,109 @@
os.unlink(expected_changesfile)
+class TestGenerateChanges(TestCase):
+ """Test generate_changes()."""
+
+ def getBaseDsc(self):
+ """Create a basic Dsc object for use with generate_changes()."""
+ dsc = Dsc()
+ dsc["source"] = "mysrcpkg"
+ dsc["binary"] = "mybinpkg"
+ dsc["version"] = "4.2"
+ dsc["maintainer"] = "Maintainer <maintainer@xxxxxxxxxx>"
+ return dsc
+
+ def getBaseOrigin(self):
+ """Create a basic Origin dict for use with generate_changes()."""
+ origin = Deb822Dict()
+ origin["Name"] = "Debian"
+ origin["Suite"] = "sid"
+ return origin
+
+ def generateChanges(self, dsc=None, dsc_files=None, suite="maverick",
+ changelog=None, urgency="low", closes=None,
+ lp_closes=None, section="net", priority="extra",
+ description=None, files_from_librarian=[],
+ requested_by="Somebody <somebody@xxxxxxxxxx>",
+ origin=None):
+ if dsc is None:
+ dsc = self.getBaseDsc()
+ if dsc_files is None:
+ dsc_files = []
+ if origin is None:
+ origin = self.getBaseOrigin()
+ if changelog is None:
+ changelog = 'changelog entry'
+ return generate_changes(
+ dsc=dsc, dsc_files=dsc_files, suite=suite, changelog=changelog,
+ urgency=urgency, closes=closes, lp_closes=lp_closes,
+ section=section, priority=priority, description=description,
+ files_from_librarian=files_from_librarian,
+ requested_by=requested_by, origin=origin)
+
+ def test_minimum_fields(self):
+ # The right (minimum) set of fields are set by generate_changes().
+ changes = self.generateChanges()
+ self.assertEquals("1.7", changes["Format"])
+ self.assertEquals("mysrcpkg", changes["Source"])
+ self.assertEquals("mybinpkg", changes["Binary"])
+ self.assertEquals("source", changes["Architecture"])
+ self.assertEquals("4.2", changes["Version"])
+ self.assertEquals("maverick", changes["Distribution"])
+ self.assertEquals("low", changes["Urgency"])
+ self.assertEquals("\nchangelog entry", changes["Changes"])
+ self.assertEquals(
+ "Maintainer <maintainer@xxxxxxxxxx>", changes["Maintainer"])
+ self.assertNotIn("Description", changes)
+ self.assertNotIn("Closes", changes)
+ self.assertNotIn("Launchpad-bugs-fixed", changes)
+ self.assertEquals([], changes["Files"])
+
+ def test_closes(self):
+ # Closes gets set if any Debian bugs to close were specified.
+ changes = self.generateChanges(closes=["1232", "4323"])
+ self.assertEquals("1232 4323", changes["Closes"])
+ self.assertNotIn("Launchpad-bugs-fixed", changes)
+
+ def test_lp_closes(self):
+ # Launchpad-Bugs-Fixed gets set if any Launchpad bugs to close were
+ # specified.
+ changes = self.generateChanges(lp_closes=["987987"])
+ self.assertEquals("987987", changes["Launchpad-Bugs-Fixed"])
+
+ def test_utf8_changelog(self):
+ # A changelog entry with non-ASCII UTF-8 characters is serialized in
+ # Changes properly.
+ changes = self.generateChanges(
+ changelog="* Updated French translation by J\xc3\xa9lmer.")
+ contents = changes.dump(encoding="utf-8").encode("utf-8")
+ self.assertIn(
+ "Updated French translation by J\xc3\xa9lmer.", contents)
+
+ def test_changelog_whitelines(self):
+ # The changelog entry can contain empty lines, and this should not
+ # mess up the parsing of the changes file.
+ changelog = "* Foo\n\n\n* Bar\n.\nEntries"
+ changes = self.generateChanges(changelog=changelog)
+ contents = changes.dump(encoding="utf-8").encode("utf-8")
+ # Read contents back
+ read_changes = Changes(contents)
+ self.assertEquals("\n%s" % changelog, changes['Changes'])
+ self.assertContentEqual([
+ 'Architecture',
+ 'Binary',
+ 'Changed-By',
+ 'Changes',
+ 'Date',
+ 'Distribution',
+ 'Files',
+ 'Format',
+ 'Maintainer',
+ 'Origin',
+ 'Source',
+ 'Urgency',
+ 'Version',
+ ], read_changes.keys())
+
def test_suite():
return TestLoader().loadTestsFromName(__name__)
=== modified file 'scripts/ftpmaster-tools/sync-source.py'
--- scripts/ftpmaster-tools/sync-source.py 2010-08-27 12:21:25 +0000
+++ scripts/ftpmaster-tools/sync-source.py 2010-09-16 21:51:24 +0000
@@ -36,7 +36,6 @@
import stat
import string
import tempfile
-import time
import urllib
import dak_utils
@@ -45,16 +44,28 @@
from zope.component import getUtility
from contrib.glock import GlobalLock
-from canonical.database.sqlbase import sqlvalues, cursor
+from canonical.database.sqlbase import (
+ cursor,
+ sqlvalues,
+ )
from canonical.launchpad.interfaces import (
- IDistributionSet, IPersonSet)
+ IDistributionSet,
+ IPersonSet,
+ )
from canonical.launchpad.scripts import (
- execute_zcml_for_scripts, logger, logger_options)
+ execute_zcml_for_scripts,
+ logger,
+ logger_options,
+ )
from canonical.librarian.client import LibrarianClient
from canonical.lp import initZopeless
from lp.registry.interfaces.pocket import PackagePublishingPocket
from lp.soyuz.enums import PackagePublishingStatus
-from lp.soyuz.scripts.ftpmaster import SyncSource, SyncSourceError
+from lp.soyuz.scripts.ftpmaster import (
+ generate_changes,
+ SyncSource,
+ SyncSourceError,
+ )
reject_message = ""
@@ -139,53 +150,6 @@
os.unlink(temp_filename)
-def generate_changes(dsc, dsc_files, suite, changelog, urgency, closes,
- lp_closes, section, priority, description,
- files_from_librarian, requested_by, origin):
- """Generate a .changes as a string"""
-
- # XXX cprov 2007-07-03:
- # Changed-By can be extracted from most-recent changelog footer,
- # but do we care?
- # XXX James Troup 2006-01-30:
- # 'Closes' but could be gotten from changelog, but we don't use them?
-
- changes = ""
- changes += "Origin: %s/%s\n" % (origin["name"], origin["suite"])
- changes += "Format: 1.7\n"
- changes += "Date: %s\n" % (time.strftime("%a, %d %b %Y %H:%M:%S %z"))
- changes += "Source: %s\n" % (dsc["source"])
- changes += "Binary: %s\n" % (dsc["binary"])
- changes += "Architecture: source\n"
- changes += "Version: %s\n"% (dsc["version"])
- # XXX: James Troup 2006-01-30:
- # 'suite' forced to string to avoid unicode-vs-str grudge match
- changes += "Distribution: %s\n" % (str(suite))
- changes += "Urgency: %s\n" % (urgency)
- changes += "Maintainer: %s\n" % (dsc["maintainer"])
- changes += "Changed-By: %s\n" % (requested_by)
- if description:
- changes += "Description: \n"
- changes += " %s\n" % (description)
- if closes:
- changes += "Closes: %s\n" % (" ".join(closes))
- if lp_closes:
- changes += "Launchpad-bugs-fixed: %s\n" % (" ".join(lp_closes))
- changes += "Changes: \n"
- changes += changelog
- changes += "Files: \n"
- for filename in dsc_files:
- if filename in files_from_librarian:
- continue
- changes += " %s %s %s %s %s\n" % (dsc_files[filename]["md5sum"],
- dsc_files[filename]["size"],
- section, priority, filename)
- # Strip trailing newline
- changes = changes[:-1]
-
- return changes
-
-
def parse_changelog(changelog_filename, previous_version):
if not os.path.exists(changelog_filename):
dak_utils.fubar("debian/changelog not found in extracted source.")
@@ -500,9 +464,10 @@
dsc["source"], dak_utils.re_no_epoch.sub('', dsc["version"]))
filehandle = open(output_filename, 'w')
- # XXX cprov 2007-07-03: The Soyuz .changes parser requires the extra '\n'
- filehandle.write(changes+'\n')
- filehandle.close()
+ try:
+ changes.dump(filehandle, encoding="utf-8")
+ finally:
+ filehandle.close()
def read_current_source(distro_series, valid_component=None, arguments=None):
Follow ups