Merge lp:~jtv/launchpad/bug-788078 into lp:launchpad

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Jeroen T. Vermeulen
Approved revision: no longer in the source branch.
Merged at revision: 13496
Proposed branch: lp:~jtv/launchpad/bug-788078
Merge into: lp:launchpad
Prerequisite: lp:~jtv/launchpad/get_derived_distros
Diff against target: 676 lines (+239/-107)
2 files modified
lib/lp/soyuz/scripts/publishdistro.py (+69/-43)
lib/lp/soyuz/scripts/tests/test_publishdistro.py (+170/-64)
To merge this branch: bzr merge lp:~jtv/launchpad/bug-788078
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+68681@code.launchpad.net

This proposal supersedes a proposal from 2011-07-21.

Description of the change

= Summary =

We want to be able to publish all Ubuntu-derived distributions with a single script run, without having to name them on the command line. This will take admin involvement out of the process for deriving a new distribution.

== Proposed fix ==

A new option for publish-distro, --all-derived, makes the script iterate over all derived distributions.

(I chose the option name so that it could use -a as a short form; a useful mnemonic and besides, -d and -D were both taken.)

== Pre-implementation notes ==

There's no clear line between "all Ubuntu-derived distributions" and "all derived distributions, not counting Ubuntu despite it being technically a derivative of Debian." We're not expecting to host any distributions that would reveal the difference. If we did, the current behaviour would probably be the right one until it got too big to publish on the same server.

== Implementation details ==

There were no loop-carried dependencies in the script; its main method is stateless. Piece of cake to make it iterate over all matching distributions. We could easily extend command-line distribution selection for more fine-grained load-balancing in the future, e.g. "only distributions "a" thru "f" on this server."

A subtlety is that I made the --distribution option default to None (but then default to "ubuntu" while looking up the distro) so that validateOptions can check for mistakes like "--distribution=ubuntu --all-derived" (which might not do what one might expect).

== Tests ==

{{{
./bin/test -vvc lp.soyuz.scripts.tests.test_publishdistro
./bin/test -vvc lp.archivepublisher
./bin/test -vvc lp.soyuz -t publish
}}}

== Demo and Q/A ==

Enable the "derived distributions" feature flags. Create a derived distribution. Run publish-distro.py --all-derived. Archive directories for the new distro will appear in /srv/launchpad.net/ (or /var/tmp/archive on a dev system) but the ubuntu ones will not be updated. A regular "publish-distro.py --distribution=ubuntu" will will publish Ubuntu only.

= Launchpad lint =

This includes some files whose changes I split out to a separate branch:

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/soyuz/scripts/publishdistro.py
  lib/lp/soyuz/scripts/tests/test_publishdistro.py
  lib/lp/soyuz/scripts/processaccepted.py
  lib/lp/soyuz/tests/test_processaccepted.py
  lib/lp/registry/interfaces/distribution.py
  lib/lp/registry/tests/test_distribution.py
  lib/lp/registry/model/distribution.py

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

This looks good.

I had a bit of concern about the extra iterations in the script for all distributions, but you addressed those well in the cover letter and on IRC. Thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/soyuz/scripts/publishdistro.py'
2--- lib/lp/soyuz/scripts/publishdistro.py 2011-07-19 12:32:37 +0000
3+++ lib/lp/soyuz/scripts/publishdistro.py 2011-07-21 13:06:32 +0000
4@@ -62,7 +62,11 @@
5
6 self.parser.add_option(
7 "-d", "--distribution", dest="distribution", metavar="DISTRO",
8- default="ubuntu", help="The distribution to publish.")
9+ default=None, help="The distribution to publish.")
10+
11+ self.parser.add_option(
12+ "-a", "--all-derived", action="store_true", dest="all_derived",
13+ default=False, help="Publish all Ubuntu-derived distributions.")
14
15 self.parser.add_option(
16 '-s', '--suite', metavar='SUITE', dest='suite', action='append',
17@@ -166,83 +170,104 @@
18 "Can only specify one of partner, ppa, private-ppa, "
19 "copy-archive and primary-debug.")
20
21+ if self.options.all_derived and self.options.distribution is not None:
22+ raise OptionValueError(
23+ "Specify --distribution or --all-derived, but not both.")
24+
25 for_ppa = (self.options.ppa or self.options.private_ppa)
26 if for_ppa and self.options.distsroot:
27 raise OptionValueError(
28 "We should not define 'distsroot' in PPA mode!", )
29
30- def findDistro(self):
31- """Find the selected distribution."""
32+ def findSelectedDistro(self):
33+ """Find the `Distribution` named by the --distribution option.
34+
35+ Defaults to Ubuntu if no name was given.
36+ """
37 self.logger.debug("Finding distribution object.")
38 name = self.options.distribution
39+ if name is None:
40+ # Default to publishing Ubuntu.
41+ name = "ubuntu"
42 distro = getUtility(IDistributionSet).getByName(name)
43 if distro is None:
44 raise OptionValueError("Distribution '%s' not found." % name)
45 return distro
46
47- def findSuite(self, suite):
48+ def findDerivedDistros(self):
49+ """Find all Ubuntu-derived distributions."""
50+ self.logger.debug("Finding derived distributions.")
51+ return getUtility(IDistributionSet).getDerivedDistributions()
52+
53+ def findDistros(self):
54+ """Find the selected distribution(s)."""
55+ if self.options.all_derived:
56+ return self.findDerivedDistros()
57+ else:
58+ return [self.findSelectedDistro()]
59+
60+ def findSuite(self, distribution, suite):
61 """Find the named `suite` in the selected `Distribution`.
62
63 :param suite: The suite name to look for.
64 :return: A tuple of distroseries name and pocket.
65 """
66 try:
67- series, pocket = self.distribution.getDistroSeriesAndPocket(suite)
68+ series, pocket = distribution.getDistroSeriesAndPocket(suite)
69 except NotFoundError, e:
70 raise OptionValueError(e)
71 return series.name, pocket
72
73- def findAllowedSuites(self):
74+ def findAllowedSuites(self, distribution):
75 """Find the selected suite(s)."""
76- return set([self.findSuite(suite) for suite in self.options.suite])
77+ return set([
78+ self.findSuite(distribution, suite)
79+ for suite in self.options.suite])
80
81- def getDebugArchive(self):
82+ def getDebugArchive(self, distribution):
83 """Find the debug archive for the selected distribution, as a list."""
84 debug_archive = getUtility(IArchiveSet).getByDistroPurpose(
85- self.distribution, ArchivePurpose.DEBUG)
86+ distribution, ArchivePurpose.DEBUG)
87 if debug_archive is None:
88 raise OptionValueError(
89- "Could not find DEBUG archive for %s"
90- % self.distribution.name)
91+ "Could not find DEBUG archive for %s" % distribution.name)
92 return [debug_archive]
93
94- def getCopyArchives(self):
95+ def getCopyArchives(self, distribution):
96 """Find copy archives for the selected distribution."""
97 copy_archives = list(
98 getUtility(IArchiveSet).getArchivesForDistribution(
99- self.distribution, purposes=[ArchivePurpose.COPY]))
100+ distribution, purposes=[ArchivePurpose.COPY]))
101 if copy_archives == []:
102 raise LaunchpadScriptFailure("Could not find any COPY archives")
103 return copy_archives
104
105- def getPPAs(self):
106+ def getPPAs(self, distribution):
107 """Find private package archives for the selected distribution."""
108 if self.isCareful(self.options.careful_publishing):
109- return self.distribution.getAllPPAs()
110+ return distribution.getAllPPAs()
111 else:
112- return self.distribution.getPendingPublicationPPAs()
113+ return distribution.getPendingPublicationPPAs()
114
115- def getTargetArchives(self):
116+ def getTargetArchives(self, distribution):
117 """Find the archive(s) selected by the script's options."""
118 if self.options.partner:
119- return [self.distribution.getArchiveByComponent('partner')]
120+ return [distribution.getArchiveByComponent('partner')]
121 elif self.options.ppa:
122- return filter(is_ppa_public, self.getPPAs())
123+ return filter(is_ppa_public, self.getPPAs(distribution))
124 elif self.options.private_ppa:
125- return filter(is_ppa_private, self.getPPAs())
126+ return filter(is_ppa_private, self.getPPAs(distribution))
127 elif self.options.primary_debug:
128- return self.getDebugArchive()
129+ return self.getDebugArchive(distribution)
130 elif self.options.copy_archive:
131- return self.getCopyArchives()
132+ return self.getCopyArchives(distribution)
133 else:
134- return [self.distribution.main_archive]
135+ return [distribution.main_archive]
136
137- def getPublisher(self, archive, allowed_suites):
138+ def getPublisher(self, distribution, archive, allowed_suites):
139 """Get a publisher for the given options."""
140 if archive.purpose in MAIN_ARCHIVE_PURPOSES:
141- description = "%s %s" % (
142- self.distribution.name,
143- archive.displayname)
144+ description = "%s %s" % (distribution.name, archive.displayname)
145 # Only let the primary/partner archives override the distsroot.
146 distsroot = self.options.distsroot
147 else:
148@@ -294,21 +319,22 @@
149 """See `LaunchpadScript`."""
150 self.validateOptions()
151 self.logOptions()
152- self.distribution = self.findDistro()
153- allowed_suites = self.findAllowedSuites()
154-
155- for archive in self.getTargetArchives():
156- publisher = self.getPublisher(archive, allowed_suites)
157-
158- if archive.status == ArchiveStatus.DELETING:
159- work_done = self.deleteArchive(archive, publisher)
160- elif archive.publish:
161- self.publishArchive(archive, publisher)
162- work_done = True
163- else:
164- work_done = False
165-
166- if work_done:
167- self.txn.commit()
168+
169+ for distribution in self.findDistros():
170+ allowed_suites = self.findAllowedSuites(distribution)
171+ for archive in self.getTargetArchives(distribution):
172+ publisher = self.getPublisher(
173+ distribution, archive, allowed_suites)
174+
175+ if archive.status == ArchiveStatus.DELETING:
176+ work_done = self.deleteArchive(archive, publisher)
177+ elif archive.publish:
178+ self.publishArchive(archive, publisher)
179+ work_done = True
180+ else:
181+ work_done = False
182+
183+ if work_done:
184+ self.txn.commit()
185
186 self.logger.debug("Ciao")
187
188=== modified file 'lib/lp/soyuz/scripts/tests/test_publishdistro.py'
189--- lib/lp/soyuz/scripts/tests/test_publishdistro.py 2011-07-19 13:39:28 +0000
190+++ lib/lp/soyuz/scripts/tests/test_publishdistro.py 2011-07-21 13:06:32 +0000
191@@ -15,6 +15,7 @@
192
193 from canonical.config import config
194 from canonical.testing.layers import ZopelessDatabaseLayer
195+from lp.app.interfaces.launchpad import ILaunchpadCelebrities
196 from lp.archivepublisher.config import getPubConfig
197 from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet
198 from lp.archivepublisher.publishing import Publisher
199@@ -28,6 +29,7 @@
200 from lp.services.scripts.base import LaunchpadScriptFailure
201 from lp.soyuz.enums import (
202 ArchivePurpose,
203+ ArchiveStatus,
204 BinaryPackageFormat,
205 PackagePublishingStatus,
206 )
207@@ -399,7 +401,9 @@
208 class FakeArchive:
209 """A very simple fake `Archive`."""
210 def __init__(self, purpose=ArchivePurpose.PRIMARY):
211+ self.publish = True
212 self.purpose = purpose
213+ self.status = ArchiveStatus.ACTIVE
214
215
216 class FakePublisher:
217@@ -418,11 +422,24 @@
218
219 layer = ZopelessDatabaseLayer
220
221- def makeScript(self, distribution=None, args=[]):
222+ def makeDistro(self):
223+ """Create a distribution."""
224+ # Set up a temporary directory as publish_root_dir. Without
225+ # this, getPublisher will create archives in the current
226+ # directory.
227+ return self.factory.makeDistribution(
228+ publish_root_dir=unicode(self.makeTemporaryDirectory()))
229+
230+ def makeScript(self, distribution=None, args=[], all_derived=False):
231 """Create a `PublishDistro` for `distribution`."""
232- if distribution is None:
233- distribution = self.factory.makeDistribution()
234- full_args = ['-d', distribution.name] + args
235+ if distribution is None and not all_derived:
236+ distribution = self.makeDistro()
237+ distro_args = []
238+ if distribution is not None:
239+ distro_args.extend(['--distribution', distribution.name])
240+ if all_derived:
241+ distro_args.append('--all-derived')
242+ full_args = args + distro_args
243 script = PublishDistro(test_args=full_args)
244 script.distribution = distribution
245 script.logger = DevNullLogger()
246@@ -522,19 +539,67 @@
247 script = self.makeScript(args=['--private-ppa', '--distsroot=/tmp'])
248 self.assertRaises(OptionValueError, script.validateOptions)
249
250- def test_findDistro_finds_distribution(self):
251- # findDistro looks up and returns the distribution named on the
252+ def test_validateOptions_accepts_all_derived_without_distro(self):
253+ # If --all-derived is given, the --distribution option is not
254+ # required.
255+ PublishDistro(test_args=['--all-derived']).validateOptions()
256+ # The test is that we get here without error.
257+ pass
258+
259+ def test_validateOptions_does_not_accept_distro_with_all_derived(self):
260+ # The --all-derived option conflicts with the --distribution
261+ # option.
262+ distro = self.makeDistro()
263+ script = PublishDistro(test_args=['-d', distro.name, '--all-derived'])
264+ self.assertRaises(OptionValueError, script.validateOptions)
265+
266+ def test_findDistros_finds_selected_distribution(self):
267+ # findDistros looks up and returns the distribution named on the
268 # command line.
269- distro = self.factory.makeDistribution()
270- self.assertEqual(distro, self.makeScript(distro).findDistro())
271-
272- def test_findDistro_raises_if_distro_not_found(self):
273+ distro = self.makeDistro()
274+ self.assertEqual([distro], self.makeScript(distro).findDistros())
275+
276+ def test_findDistros_finds_ubuntu_by_default(self):
277+ ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
278+ self.assertContentEqual(
279+ [ubuntu], PublishDistro(test_args=[]).findDistros())
280+
281+ def test_findDistros_raises_if_selected_distro_not_found(self):
282 # If findDistro can't find the distribution, that's an
283 # OptionValueError.
284 wrong_name = self.factory.getUniqueString()
285 self.assertRaises(
286 OptionValueError,
287- PublishDistro(test_args=['-d', wrong_name]).findDistro)
288+ PublishDistro(test_args=['-d', wrong_name]).findDistros)
289+
290+ def test_findDistros_for_all_derived_distros_may_return_empty(self):
291+ # If the --all-derived option is given but there are no derived
292+ # distributions to publish, findDistros returns no distributions
293+ # (but it does return normally).
294+ self.assertContentEqual(
295+ [], self.makeScript(all_derived=True).findDistros())
296+
297+ def test_findDistros_for_all_derived_finds_derived_distros(self):
298+ # If --all-derived is given, findDistros finds all derived
299+ # distributions.
300+ dsp = self.factory.makeDistroSeriesParent()
301+ self.assertContentEqual(
302+ [dsp.derived_series.distribution],
303+ self.makeScript(all_derived=True).findDistros())
304+
305+ def test_findDistros_for_all_derived_ignores_ubuntu(self):
306+ # The --all-derived option does not include Ubuntu, even if it
307+ # is itself a derived distribution.
308+ ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
309+ self.factory.makeDistroSeriesParent(
310+ parent_series=ubuntu.currentseries)
311+ self.assertNotIn(
312+ ubuntu, self.makeScript(all_derived=True).findDistros())
313+
314+ def test_findDistros_for_all_derived_ignores_nonderived_distros(self):
315+ self.makeDistro()
316+ self.assertContentEqual(
317+ [], self.makeScript(all_derived=True).findDistros())
318
319 def test_findSuite_finds_release_pocket(self):
320 # Despite its lack of a suffix, a release suite shows up
321@@ -543,27 +608,32 @@
322 distro = series.distribution
323 self.assertEqual(
324 (series.name, PackagePublishingPocket.RELEASE),
325- self.makeScript(distro).findSuite(series.name))
326+ self.makeScript(distro).findSuite(distro, series.name))
327
328 def test_findSuite_finds_other_pocket(self):
329 # Suites that are not in the release pocket have their pocket
330 # name as a suffix. These show up in findSuite results.
331 series = self.factory.makeDistroSeries()
332 distro = series.distribution
333+ script = self.makeScript(distro)
334 self.assertEqual(
335 (series.name, PackagePublishingPocket.UPDATES),
336- self.makeScript(distro).findSuite(series.name + "-updates"))
337+ script.findSuite(distro, series.name + "-updates"))
338
339 def test_findSuite_raises_if_not_found(self):
340 # If findSuite can't find its suite, that's an OptionValueError.
341+ distro = self.makeDistro()
342+ script = self.makeScript(distro)
343 self.assertRaises(
344 OptionValueError,
345- self.makeScript().findSuite, self.factory.getUniqueString())
346+ script.findSuite, distro, self.factory.getUniqueString())
347
348 def test_findAllowedSuites_finds_nothing_if_no_suites_given(self):
349 # If no suites are given, findAllowedSuites returns an empty
350 # sequence.
351- self.assertContentEqual([], self.makeScript().findAllowedSuites())
352+ distro = self.makeDistro()
353+ script = self.makeScript(distro)
354+ self.assertContentEqual([], script.findAllowedSuites(distro))
355
356 def test_findAllowedSuites_finds_series_and_pocket(self):
357 # findAllowedSuites looks up the requested suites.
358@@ -572,7 +642,7 @@
359 script = self.makeScript(series.distribution, ['--suite', suite])
360 self.assertContentEqual(
361 [(series.name, PackagePublishingPocket.UPDATES)],
362- script.findAllowedSuites())
363+ script.findAllowedSuites(series.distribution))
364
365 def test_findAllowedSuites_finds_multiple(self):
366 # Multiple suites may be requested; findAllowedSuites looks them
367@@ -585,206 +655,218 @@
368 (series.name, PackagePublishingPocket.UPDATES),
369 (series.name, PackagePublishingPocket.RELEASE),
370 ]
371- self.assertContentEqual(expected_suites, script.findAllowedSuites())
372+ self.assertContentEqual(
373+ expected_suites, script.findAllowedSuites(series.distribution))
374
375 def test_getDebugArchive_returns_list(self):
376 # getDebugArchive returns a list of one archive. Fits in more
377 # regularly with the other methods to find archives.
378- distro = self.factory.makeDistribution()
379+ distro = self.makeDistro()
380 script = self.makeScript(distro)
381 debug_archive = self.factory.makeArchive(
382 distro, purpose=ArchivePurpose.DEBUG)
383- self.assertEqual([debug_archive], script.getDebugArchive())
384+ self.assertEqual([debug_archive], script.getDebugArchive(distro))
385
386 def test_getDebugArchive_raises_if_not_found(self):
387 # If getDebugArchive doesn't find a debug archive, that's an
388 # OptionValueError.
389- self.assertRaises(OptionValueError, self.makeScript().getDebugArchive)
390+ distro = self.makeDistro()
391+ script = self.makeScript(distro)
392+ self.assertRaises(OptionValueError, script.getDebugArchive, distro)
393
394 def test_getDebugArchive_ignores_other_archive_purposes(self):
395 # getDebugArchive does not return archives that aren't debug
396 # archives.
397- distro = self.factory.makeDistribution()
398+ distro = self.makeDistro()
399 script = self.makeScript(distro)
400 self.factory.makeArchive(distro, purpose=ArchivePurpose.PARTNER)
401- self.assertRaises(OptionValueError, script.getDebugArchive)
402+ self.assertRaises(OptionValueError, script.getDebugArchive, distro)
403
404 def test_getDebugArchive_ignores_other_distros(self):
405 # getDebugArchive won't return an archive for the wrong
406 # distribution.
407+ distro = self.makeDistro()
408 self.factory.makeArchive(purpose=ArchivePurpose.DEBUG)
409- self.assertRaises(OptionValueError, self.makeScript().getDebugArchive)
410+ script = self.makeScript(distro)
411+ self.assertRaises(OptionValueError, script.getDebugArchive, distro)
412
413 def test_getCopyArchives_returns_list(self):
414 # getCopyArchives returns a list of archives.
415- distro = self.factory.makeDistribution()
416+ distro = self.makeDistro()
417 script = self.makeScript(distro)
418 copy_archive = self.factory.makeArchive(
419 distro, purpose=ArchivePurpose.COPY)
420- self.assertEqual([copy_archive], script.getCopyArchives())
421+ self.assertEqual([copy_archive], script.getCopyArchives(distro))
422
423 def test_getCopyArchives_raises_if_not_found(self):
424 # If the distribution has no copy archives, that's a script
425 # failure.
426+ distro = self.makeDistro()
427+ script = self.makeScript(distro)
428 self.assertRaises(
429- LaunchpadScriptFailure, self.makeScript().getCopyArchives)
430+ LaunchpadScriptFailure, script.getCopyArchives, distro)
431
432 def test_getCopyArchives_ignores_other_archive_purposes(self):
433 # getCopyArchives won't return archives that aren't copy
434 # archives.
435- distro = self.factory.makeDistribution()
436+ distro = self.makeDistro()
437 script = self.makeScript(distro)
438 self.factory.makeArchive(distro, purpose=ArchivePurpose.PARTNER)
439- self.assertRaises(LaunchpadScriptFailure, script.getCopyArchives)
440+ self.assertRaises(
441+ LaunchpadScriptFailure, script.getCopyArchives, distro)
442
443 def test_getCopyArchives_ignores_other_distros(self):
444 # getCopyArchives won't return an archive for the wrong
445 # distribution.
446+ distro = self.makeDistro()
447+ script = self.makeScript(distro)
448 self.factory.makeArchive(purpose=ArchivePurpose.COPY)
449 self.assertRaises(
450- LaunchpadScriptFailure, self.makeScript().getCopyArchives)
451+ LaunchpadScriptFailure, script.getCopyArchives, distro)
452
453 def test_getPPAs_gets_pending_distro_PPAs_if_careful(self):
454 # In careful mode, getPPAs includes PPAs for the distribution
455 # that are pending pulication.
456- distro = self.factory.makeDistribution()
457+ distro = self.makeDistro()
458 script = self.makeScript(distro, ['--careful'])
459 ppa = self.factory.makeArchive(distro, purpose=ArchivePurpose.PPA)
460 self.factory.makeSourcePackagePublishingHistory(archive=ppa)
461- self.assertContentEqual([ppa], script.getPPAs())
462+ self.assertContentEqual([ppa], script.getPPAs(distro))
463
464 def test_getPPAs_gets_nonpending_distro_PPAs_if_careful(self):
465 # In careful mode, getPPAs includes PPAs for the distribution
466 # that are not pending pulication.
467- distro = self.factory.makeDistribution()
468+ distro = self.makeDistro()
469 script = self.makeScript(distro, ['--careful'])
470 ppa = self.factory.makeArchive(distro, purpose=ArchivePurpose.PPA)
471- self.assertContentEqual([ppa], script.getPPAs())
472+ self.assertContentEqual([ppa], script.getPPAs(distro))
473
474 def test_getPPAs_gets_pending_distro_PPAs_if_not_careful(self):
475 # In non-careful mode, getPPAs includes PPAs that are pending
476 # pulication.
477- distro = self.factory.makeDistribution()
478+ distro = self.makeDistro()
479 script = self.makeScript(distro)
480 ppa = self.factory.makeArchive(distro, purpose=ArchivePurpose.PPA)
481 self.factory.makeSourcePackagePublishingHistory(archive=ppa)
482- self.assertContentEqual([ppa], script.getPPAs())
483+ self.assertContentEqual([ppa], script.getPPAs(distro))
484
485 def test_getPPAs_ignores_nonpending_distro_PPAs_if_not_careful(self):
486 # In non-careful mode, getPPAs does not include PPAs that are
487 # not pending pulication.
488- distro = self.factory.makeDistribution()
489+ distro = self.makeDistro()
490 script = self.makeScript(distro)
491 self.factory.makeArchive(distro, purpose=ArchivePurpose.PPA)
492- self.assertContentEqual([], script.getPPAs())
493+ self.assertContentEqual([], script.getPPAs(distro))
494
495 def test_getPPAs_returns_empty_if_careful_and_no_PPAs_found(self):
496 # If, in careful mode, getPPAs finds no archives it returns an
497 # empty sequence.
498- self.assertContentEqual(
499- [], self.makeScript(args=['--careful']).getPPAs())
500+ distro = self.makeDistro()
501+ script = self.makeScript(distro, ['--careful'])
502+ self.assertContentEqual([], script.getPPAs(distro))
503
504 def test_getPPAs_returns_empty_if_not_careful_and_no_PPAs_found(self):
505 # If, in non-careful mode, getPPAs finds no archives it returns
506 # an empty sequence.
507- self.assertContentEqual([], self.makeScript().getPPAs())
508+ distro = self.makeDistro()
509+ self.assertContentEqual([], self.makeScript(distro).getPPAs(distro))
510
511 def test_getTargetArchives_gets_partner_archive(self):
512 # If the selected exclusive option is "partner,"
513 # getTargetArchives looks for a partner archive.
514- distro = self.factory.makeDistribution()
515+ distro = self.makeDistro()
516 partner = self.factory.makeArchive(
517 distro, purpose=ArchivePurpose.PARTNER)
518 script = self.makeScript(distro, ['--partner'])
519- self.assertContentEqual([partner], script.getTargetArchives())
520+ self.assertContentEqual([partner], script.getTargetArchives(distro))
521
522 def test_getTargetArchives_ignores_public_ppas_if_private(self):
523 # If the selected exclusive option is "private-ppa,"
524 # getTargetArchives looks for PPAs but leaves out public ones.
525- distro = self.factory.makeDistribution()
526+ distro = self.makeDistro()
527 self.factory.makeArchive(
528 distro, purpose=ArchivePurpose.PPA, private=False)
529 script = self.makeScript(distro, ['--private-ppa'])
530- self.assertContentEqual([], script.getTargetArchives())
531+ self.assertContentEqual([], script.getTargetArchives(distro))
532
533 def test_getTargetArchives_gets_private_ppas_if_private(self):
534 # If the selected exclusive option is "private-ppa,"
535 # getTargetArchives looks for private PPAs.
536- distro = self.factory.makeDistribution()
537+ distro = self.makeDistro()
538 ppa = self.factory.makeArchive(
539 distro, purpose=ArchivePurpose.PPA, private=True)
540 script = self.makeScript(distro, ['--private-ppa', '--careful'])
541- self.assertContentEqual([ppa], script.getTargetArchives())
542+ self.assertContentEqual([ppa], script.getTargetArchives(distro))
543
544 def test_getTargetArchives_gets_public_ppas_if_not_private(self):
545 # If the selected exclusive option is "ppa," getTargetArchives
546 # looks for public PPAs.
547- distro = self.factory.makeDistribution()
548+ distro = self.makeDistro()
549 ppa = self.factory.makeArchive(
550 distro, purpose=ArchivePurpose.PPA, private=False)
551 script = self.makeScript(distro, ['--ppa', '--careful'])
552- self.assertContentEqual([ppa], script.getTargetArchives())
553+ self.assertContentEqual([ppa], script.getTargetArchives(distro))
554
555 def test_getTargetArchives_ignores_private_ppas_if_not_private(self):
556 # If the selected exclusive option is "ppa," getTargetArchives
557 # leaves out private PPAs.
558- distro = self.factory.makeDistribution()
559+ distro = self.makeDistro()
560 self.factory.makeArchive(
561 distro, purpose=ArchivePurpose.PPA, private=True)
562 script = self.makeScript(distro, ['--ppa'])
563- self.assertContentEqual([], script.getTargetArchives())
564+ self.assertContentEqual([], script.getTargetArchives(distro))
565
566 def test_getTargetArchives_gets_primary_debug_archive(self):
567 # If the selected exclusive option is "primary-debug,"
568 # getTargetArchives looks for a debug archive.
569- distro = self.factory.makeDistribution()
570+ distro = self.makeDistro()
571 debug = self.factory.makeArchive(distro, purpose=ArchivePurpose.DEBUG)
572 script = self.makeScript(distro, ['--primary-debug'])
573- self.assertContentEqual([debug], script.getTargetArchives())
574+ self.assertContentEqual([debug], script.getTargetArchives(distro))
575
576 def test_getTargetArchives_gets_copy_archives(self):
577 # If the selected exclusive option is "copy-archive,"
578 # getTargetArchives looks for a copy archive.
579- distro = self.factory.makeDistribution()
580+ distro = self.makeDistro()
581 copy = self.factory.makeArchive(distro, purpose=ArchivePurpose.COPY)
582 script = self.makeScript(distro, ['--copy-archive'])
583- self.assertContentEqual([copy], script.getTargetArchives())
584+ self.assertContentEqual([copy], script.getTargetArchives(distro))
585
586 def test_getPublisher_returns_publisher(self):
587 # getPublisher produces a Publisher instance.
588- distro = self.factory.makeDistribution()
589+ distro = self.makeDistro()
590 script = self.makeScript(distro)
591- publisher = script.getPublisher(distro.main_archive, None)
592+ publisher = script.getPublisher(distro, distro.main_archive, None)
593 self.assertIsInstance(publisher, Publisher)
594
595 def test_deleteArchive_deletes_ppa(self):
596 # If fed a PPA, deleteArchive will properly delete it (and
597 # return True to indicate it's done something that needs
598 # committing).
599- distro = self.factory.makeDistribution()
600+ distro = self.makeDistro()
601 ppa = self.factory.makeArchive(distro, purpose=ArchivePurpose.PPA)
602 script = self.makeScript(distro)
603 deletion_done = script.deleteArchive(
604- ppa, script.getPublisher(ppa, []))
605+ ppa, script.getPublisher(distro, ppa, []))
606 self.assertTrue(deletion_done)
607- self.assertContentEqual([], script.getPPAs())
608+ self.assertContentEqual([], script.getPPAs(distro))
609
610 def test_deleteArchive_ignores_non_ppa(self):
611 # If fed an archive that's not a PPA, deleteArchive will do
612 # nothing and return False to indicate the fact.
613- distro = self.factory.makeDistribution()
614+ distro = self.makeDistro()
615 archive = self.factory.makeArchive(
616 distro, purpose=ArchivePurpose.DEBUG)
617 script = self.makeScript(distro)
618 deletion_done = script.deleteArchive(archive, None)
619 self.assertFalse(deletion_done)
620- self.assertContentEqual([archive], script.getDebugArchive())
621+ self.assertContentEqual([archive], script.getDebugArchive(distro))
622
623 def test_publishArchive_drives_publisher(self):
624 # publishArchive puts a publisher through its paces. This work
625 # ought to be in the publisher itself, so if you find this way
626 # of doing things annoys you, that's your cue to help clean up!
627- distro = self.factory.makeDistribution()
628+ distro = self.makeDistro()
629 script = self.makeScript(distro)
630 script.txn = FakeTransaction()
631 publisher = FakePublisher()
632@@ -799,7 +881,7 @@
633 # For some types of archive, publishArchive invokes the
634 # publisher's C_doFTPArchive method as a way of generating
635 # indexes.
636- distro = self.factory.makeDistribution()
637+ distro = self.makeDistro()
638 script = self.makeScript(distro)
639 script.txn = FakeTransaction()
640 publisher = FakePublisher()
641@@ -811,10 +893,34 @@
642 # For some types of archive, publishArchive invokes the
643 # publisher's C_writeIndexes as an alternative to
644 # C_doFTPArchive.
645- distro = self.factory.makeDistribution()
646+ distro = self.makeDistro()
647 script = self.makeScript(distro)
648 script.txn = FakeTransaction()
649 publisher = FakePublisher()
650 script.publishArchive(FakeArchive(ArchivePurpose.PPA), publisher)
651 self.assertEqual(0, publisher.C_doFTPArchive.call_count)
652 self.assertEqual(1, publisher.C_writeIndexes.call_count)
653+
654+ def test_publishes_only_selected_archives(self):
655+ # The script publishes only the archives returned by
656+ # getTargetArchives, for the distributions returned by
657+ # findDistros.
658+ distro = self.makeDistro()
659+ # The script gets a distribution and archive of its own, to
660+ # prove that any distros and archives other than what
661+ # findDistros and getTargetArchives return are ignored.
662+ script = self.makeScript()
663+ script.txn = FakeTransaction()
664+ script.findDistros = FakeMethod([distro])
665+ archive = FakeArchive()
666+ script.getTargetArchives = FakeMethod([archive])
667+ publisher = FakePublisher()
668+ script.getPublisher = FakeMethod(publisher)
669+ script.publishArchive = FakeMethod()
670+ script.main()
671+ [(args, kwargs)] = script.getPublisher.calls
672+ distro_arg, archive_arg = args[:2]
673+ self.assertEqual(distro, distro_arg)
674+ self.assertEqual(archive, archive_arg)
675+ self.assertEqual(
676+ [((archive, publisher), {})], script.publishArchive.calls)