~launchpad-pqm/launchpad/devel

12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
1
# Copyright 2011 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
4
"""Job class to request generation or update of `DistroSeriesDifference`s."""
5
6
__metaclass__ = type
7
__all__ = [
8
    'DistroSeriesDifferenceJob',
9
    ]
10
13891.2.2 by Gavin Panella
Make DistroSeriesDifferenceJob use the new metadata column too.
11
import simplejson
12579.4.2 by Steve Kowalik
Add a cronscript to run DSDJs, and the associated bits.
12
from zope.component import getUtility
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
13
from zope.interface import (
14
    classProvides,
15
    implements,
16
    )
17
13626.3.6 by Jeroen Vermeulen
may_require_job was distorted by changing requirements: said no if derived series had any parent at all in the same distro.
18
from canonical.database.sqlbase import quote
14560.2.29 by Curtis Hovey
Restored lpstorm module name because it lp engineers know that name.
19
from lp.services.database.lpstorm import (
7675.1162.1 by Jeroen Vermeulen
Show updating DSDs as well as syncing ones.
20
    IMasterStore,
21
    IStore,
22
    )
12579.4.2 by Steve Kowalik
Add a cronscript to run DSDJs, and the associated bits.
23
from lp.registry.interfaces.distroseriesdifference import (
24
    IDistroSeriesDifferenceSource,
25
    )
12819.1.1 by Julian Edwards
add test and fix code, but more tests need fixing
26
from lp.registry.interfaces.pocket import PackagePublishingPocket
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
27
from lp.registry.model.distroseries import DistroSeries
12579.4.2 by Steve Kowalik
Add a cronscript to run DSDJs, and the associated bits.
28
from lp.registry.model.distroseriesdifference import DistroSeriesDifference
29
from lp.registry.model.sourcepackagename import SourcePackageName
12558.2.5 by Jeroen Vermeulen
Prototype feature flag for DistroSeriesDifferenceJob.
30
from lp.services.features import getFeatureFlag
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
31
from lp.services.job.model.job import Job
32
from lp.soyuz.interfaces.distributionjob import (
33
    DistributionJobType,
12579.4.2 by Steve Kowalik
Add a cronscript to run DSDJs, and the associated bits.
34
    IDistroSeriesDifferenceJob,
35
    IDistroSeriesDifferenceJobSource,
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
36
    )
12736.7.1 by Jeroen Vermeulen
Filter DSD creation by packagesets.
37
from lp.soyuz.interfaces.packageset import IPackagesetSet
13457.4.3 by Raphael Badin
Intermediate step.
38
from lp.soyuz.interfaces.publishing import active_publishing_status
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
39
from lp.soyuz.model.distributionjob import (
40
    DistributionJob,
41
    DistributionJobDerived,
42
    )
13457.4.3 by Raphael Badin
Intermediate step.
43
from lp.soyuz.model.publishing import SourcePackagePublishingHistory
44
from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
45
46
12558.2.9 by Jeroen Vermeulen
Review change.
47
FEATURE_FLAG_ENABLE_MODULE = u"soyuz.derived_series_jobs.enabled"
12558.2.5 by Jeroen Vermeulen
Prototype feature flag for DistroSeriesDifferenceJob.
48
49
13626.3.3 by Jeroen Vermeulen
Make make_metadata a bit more generally applicable.
50
def make_metadata(sourcepackagename_id, parent_series_id):
51
    """Return JSON metadata for a job on `sourcepackagename_id`."""
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
52
    return {
13626.3.3 by Jeroen Vermeulen
Make make_metadata a bit more generally applicable.
53
        'sourcepackagename': sourcepackagename_id,
54
        'parent_series': parent_series_id,
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
55
    }
56
57
13457.5.3 by Raphael Badin
Remove XXX references to 758906 (make parent_series mandatory).
58
def create_job(derived_series, sourcepackagename, parent_series):
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
59
    """Create a `DistroSeriesDifferenceJob` for a given source package.
60
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
61
    :param derived_series: A `DistroSeries` that is assumed to be derived
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
62
        from another one.
63
    :param sourcepackagename: The `SourcePackageName` whose publication
64
        history has changed.
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
65
    :param parent_series: A `DistroSeries` that is a parent of
66
        `derived_series`.  The difference is between the versions of
67
        `sourcepackagename` in `parent_series` and `derived_series`.
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
68
    """
69
    job = DistributionJob(
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
70
        distribution=derived_series.distribution, distroseries=derived_series,
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
71
        job_type=DistributionJobType.DISTROSERIESDIFFERENCE,
13626.3.3 by Jeroen Vermeulen
Make make_metadata a bit more generally applicable.
72
        metadata=make_metadata(sourcepackagename.id, parent_series.id))
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
73
    IMasterStore(DistributionJob).add(job)
74
    return DistroSeriesDifferenceJob(job)
75
76
13626.3.3 by Jeroen Vermeulen
Make make_metadata a bit more generally applicable.
77
def compose_job_insertion_tuple(derived_series, parent_series,
13626.3.6 by Jeroen Vermeulen
may_require_job was distorted by changing requirements: said no if derived series had any parent at all in the same distro.
78
                                sourcepackagename_id, job_id):
79
    """Compose tuple for insertion into `DistributionJob`.
80
81
    :param derived_series: Derived `DistroSeries`.
82
    :param parent_series: Parent `DistroSeries`.
83
    :param sourcepackagename_id: ID of `SourcePackageName`.
84
    :param job_id: associated `Job` id.
85
    :return: A tuple of: derived distribution id, derived distroseries id,
86
        job type, job id, JSON data map.
87
    """
13891.2.2 by Gavin Panella
Make DistroSeriesDifferenceJob use the new metadata column too.
88
    json = simplejson.dumps(
89
        make_metadata(sourcepackagename_id, parent_series.id))
13626.3.6 by Jeroen Vermeulen
may_require_job was distorted by changing requirements: said no if derived series had any parent at all in the same distro.
90
    return (
91
        derived_series.distribution.id,
92
        derived_series.id,
93
        DistributionJobType.DISTROSERIESDIFFERENCE,
94
        job_id,
95
        json,
96
        )
13626.3.3 by Jeroen Vermeulen
Make make_metadata a bit more generally applicable.
97
98
13457.4.4 by Raphael Badin
Create DSDJs outside the packages copy loop.
99
def create_multiple_jobs(derived_series, parent_series):
13626.3.6 by Jeroen Vermeulen
may_require_job was distorted by changing requirements: said no if derived series had any parent at all in the same distro.
100
    """Create `DistroSeriesDifferenceJob`s between parent and derived series.
13457.4.3 by Raphael Badin
Intermediate step.
101
102
    :param derived_series: A `DistroSeries` that is assumed to be derived
103
        from another one.
104
    :param parent_series: A `DistroSeries` that is a parent of
105
        `derived_series`.
13626.3.6 by Jeroen Vermeulen
may_require_job was distorted by changing requirements: said no if derived series had any parent at all in the same distro.
106
    :return: A list of newly-created `DistributionJob` ids.
13457.4.3 by Raphael Badin
Intermediate step.
107
    """
108
    store = IStore(SourcePackageRelease)
109
    source_package_releases = store.find(
110
        SourcePackageRelease,
13626.3.6 by Jeroen Vermeulen
may_require_job was distorted by changing requirements: said no if derived series had any parent at all in the same distro.
111
        SourcePackagePublishingHistory.sourcepackagerelease ==
112
            SourcePackageRelease.id,
113
        SourcePackagePublishingHistory.distroseries == derived_series.id,
114
        SourcePackagePublishingHistory.status.is_in(active_publishing_status))
13457.4.3 by Raphael Badin
Intermediate step.
115
    nb_jobs = source_package_releases.count()
13774.8.2 by Raphael Badin
Return if nb_jobs==0
116
117
    if nb_jobs == 0:
118
        return []
119
13457.4.3 by Raphael Badin
Intermediate step.
120
    sourcepackagenames = source_package_releases.values(
121
        SourcePackageRelease.sourcepackagenameID)
122
    job_ids = Job.createMultiple(store, nb_jobs)
123
13626.3.6 by Jeroen Vermeulen
may_require_job was distorted by changing requirements: said no if derived series had any parent at all in the same distro.
124
    job_tuples = [
125
        quote(compose_job_insertion_tuple(
126
            derived_series, parent_series, sourcepackagename, job_id))
13626.3.3 by Jeroen Vermeulen
Make make_metadata a bit more generally applicable.
127
        for job_id, sourcepackagename in zip(job_ids, sourcepackagenames)]
13457.4.3 by Raphael Badin
Intermediate step.
128
129
    store = IStore(DistributionJob)
130
    result = store.execute("""
131
        INSERT INTO DistributionJob (
132
            distribution, distroseries, job_type, job, json_data)
133
        VALUES %s
134
        RETURNING id
13626.3.6 by Jeroen Vermeulen
may_require_job was distorted by changing requirements: said no if derived series had any parent at all in the same distro.
135
        """ % ", ".join(job_tuples))
13457.4.3 by Raphael Badin
Intermediate step.
136
    return [job_id for job_id, in result]
137
138
13457.5.3 by Raphael Badin
Remove XXX references to 758906 (make parent_series mandatory).
139
def find_waiting_jobs(derived_series, sourcepackagename, parent_series):
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
140
    """Look for pending `DistroSeriesDifference` jobs on a package."""
141
    # Look for identical pending jobs.  This compares directly on
142
    # the metadata string.  It's fragile, but this is only an
143
    # optimization.  It's not actually disastrous to create
144
    # redundant jobs occasionally.
13891.2.2 by Gavin Panella
Make DistroSeriesDifferenceJob use the new metadata column too.
145
    json_metadata = make_metadata(sourcepackagename.id, parent_series.id)
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
146
147
    # Use master store because we don't like outdated information
148
    # here.
149
    store = IMasterStore(DistributionJob)
150
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
151
    candidates = store.find(
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
152
        DistributionJob,
153
        DistributionJob.job_type ==
154
            DistributionJobType.DISTROSERIESDIFFERENCE,
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
155
        DistributionJob.distroseries == derived_series,
13891.2.2 by Gavin Panella
Make DistroSeriesDifferenceJob use the new metadata column too.
156
        DistributionJob.metadata == json_metadata,
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
157
        DistributionJob.job_id.is_in(Job.ready_jobs))
158
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
159
    return [
160
        job
161
        for job in candidates
162
            if job.metadata["parent_series"] == parent_series.id]
163
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
164
13457.5.3 by Raphael Badin
Remove XXX references to 758906 (make parent_series mandatory).
165
def may_require_job(derived_series, sourcepackagename, parent_series):
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
166
    """Might publishing this package require a new job?
167
168
    Use this to determine whether to create a new
169
    `DistroSeriesDifferenceJob`.  The answer may possibly be
170
    conservatively wrong: the check is really only to save the job
171
    runner some unnecessary work, but we don't expect a bit of
172
    unnecessary work to be a big problem.
173
    """
13626.3.6 by Jeroen Vermeulen
may_require_job was distorted by changing requirements: said no if derived series had any parent at all in the same distro.
174
    if parent_series.distribution == derived_series.distribution:
175
        # Differences within a distribution are not tracked.
176
        return False
7675.1169.3 by Jeroen Vermeulen
Convert DSDJob unit tests to specify parent series where possible.
177
    existing_jobs = find_waiting_jobs(
178
        derived_series, sourcepackagename, parent_series)
179
    return len(existing_jobs) == 0
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
180
181
12736.7.1 by Jeroen Vermeulen
Filter DSD creation by packagesets.
182
def has_package(distroseries, sourcepackagename):
183
    """Does `distroseries` have the given source package?"""
184
    return not distroseries.getPublishedSources(
185
        sourcepackagename, include_pending=True).is_empty()
186
187
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
188
class DistroSeriesDifferenceJob(DistributionJobDerived):
189
    """A `Job` type for creating/updating `DistroSeriesDifference`s."""
190
12579.4.2 by Steve Kowalik
Add a cronscript to run DSDJs, and the associated bits.
191
    implements(IDistroSeriesDifferenceJob)
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
192
    classProvides(IDistroSeriesDifferenceJobSource)
193
194
    class_job_type = DistributionJobType.DISTROSERIESDIFFERENCE
195
196
    @classmethod
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
197
    def createForPackagePublication(cls, derived_series, sourcepackagename,
13457.5.4 by Raphael Badin
Remove optionnal parent_series parameter.
198
                                    pocket):
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
199
        """See `IDistroSeriesDifferenceJobSource`."""
12558.2.9 by Jeroen Vermeulen
Review change.
200
        if not getFeatureFlag(FEATURE_FLAG_ENABLE_MODULE):
12558.2.5 by Jeroen Vermeulen
Prototype feature flag for DistroSeriesDifferenceJob.
201
            return
13794.1.8 by Jeroen Vermeulen
Naive initial implementation.
202
12819.1.1 by Julian Edwards
add test and fix code, but more tests need fixing
203
        # -backports and -proposed are not really part of a standard
204
        # distribution's packages so we're ignoring them here.  They can
205
        # always be manually synced by the users if necessary, in the
206
        # rare occasions that they require them.
13794.1.8 by Jeroen Vermeulen
Naive initial implementation.
207
        ignored_pockets = [
12819.1.1 by Julian Edwards
add test and fix code, but more tests need fixing
208
            PackagePublishingPocket.BACKPORTS,
13794.1.8 by Jeroen Vermeulen
Naive initial implementation.
209
            PackagePublishingPocket.PROPOSED,
210
            ]
211
        if pocket in ignored_pockets:
12819.1.1 by Julian Edwards
add test and fix code, but more tests need fixing
212
            return
13626.3.6 by Jeroen Vermeulen
may_require_job was distorted by changing requirements: said no if derived series had any parent at all in the same distro.
213
214
        # Create jobs for DSDs between the derived_series' parents and
215
        # the derived_series itself.
216
        parent_series_jobs = [
217
            create_job(derived_series, sourcepackagename, parent)
218
            for parent in derived_series.getParentSeries()
219
                if may_require_job(derived_series, sourcepackagename, parent)]
220
13457.5.3 by Raphael Badin
Remove XXX references to 758906 (make parent_series mandatory).
221
        # Create jobs for DSDs between the derived_series and its
222
        # children.
13626.3.6 by Jeroen Vermeulen
may_require_job was distorted by changing requirements: said no if derived series had any parent at all in the same distro.
223
        derived_series_jobs = [
224
            create_job(child, sourcepackagename, derived_series)
225
            for child in derived_series.getDerivedSeries()
226
                if may_require_job(child, sourcepackagename, derived_series)]
227
228
        return parent_series_jobs + derived_series_jobs
12579.4.2 by Steve Kowalik
Add a cronscript to run DSDJs, and the associated bits.
229
7675.1162.1 by Jeroen Vermeulen
Show updating DSDs as well as syncing ones.
230
    @classmethod
13794.1.4 by Jeroen Vermeulen
Move the problem to DSDJSource.
231
    def createForSPPHs(cls, spphs):
232
        """See `IDistroSeriesDifferenceJobSource`."""
13794.1.12 by Jeroen Vermeulen
XXX
233
        # XXX JeroenVermeulen 2011-08-25, bug=834499: This won't do for
234
        # some of the mass deletions we're planning to support.
235
        # Optimize.
13794.1.8 by Jeroen Vermeulen
Naive initial implementation.
236
        for spph in spphs:
13794.1.11 by Jeroen Vermeulen
No DSDJs for PPAs please!
237
            if spph.archive.is_main:
238
                cls.createForPackagePublication(
239
                    spph.distroseries,
240
                    spph.sourcepackagerelease.sourcepackagename, spph.pocket)
13794.1.4 by Jeroen Vermeulen
Move the problem to DSDJSource.
241
242
    @classmethod
13457.5.4 by Raphael Badin
Remove optionnal parent_series parameter.
243
    def massCreateForSeries(cls, derived_series):
13457.4.3 by Raphael Badin
Intermediate step.
244
        """See `IDistroSeriesDifferenceJobSource`."""
245
        if not getFeatureFlag(FEATURE_FLAG_ENABLE_MODULE):
246
            return
13457.5.4 by Raphael Badin
Remove optionnal parent_series parameter.
247
        for parent_series in derived_series.getParentSeries():
248
            create_multiple_jobs(derived_series, parent_series)
13457.4.3 by Raphael Badin
Intermediate step.
249
250
    @classmethod
7675.1162.1 by Jeroen Vermeulen
Show updating DSDs as well as syncing ones.
251
    def getPendingJobsForDifferences(cls, derived_series,
252
                                     distroseriesdifferences):
253
        """See `IDistroSeriesDifferenceJobSource`."""
254
        jobs = IStore(DistributionJob).find(
255
            DistributionJob,
256
            DistributionJob.job_type == cls.class_job_type,
257
            Job.id == DistributionJob.job_id,
258
            Job._status.is_in(Job.PENDING_STATUSES),
259
            DistributionJob.distroseries == derived_series)
260
13457.5.3 by Raphael Badin
Remove XXX references to 758906 (make parent_series mandatory).
261
        parent_series_ids = set(
262
            dsd.parent_series.id for dsd in distroseriesdifferences)
7675.1162.1 by Jeroen Vermeulen
Show updating DSDs as well as syncing ones.
263
        keyed_dsds = dict(
264
            (dsd.source_package_name.id, dsd)
265
            for dsd in distroseriesdifferences)
266
        jobs_by_dsd = {}
267
        for job in jobs:
13457.5.3 by Raphael Badin
Remove XXX references to 758906 (make parent_series mandatory).
268
            if job.metadata["parent_series"] not in parent_series_ids:
269
                continue
7675.1162.1 by Jeroen Vermeulen
Show updating DSDs as well as syncing ones.
270
            dsd = keyed_dsds.get(job.metadata["sourcepackagename"])
271
            if dsd is not None:
272
                jobs_by_dsd.setdefault(dsd, []).append(cls(job))
273
        return jobs_by_dsd
274
13586.1.1 by Julian Edwards
Add DistroSeriesDifferenceJob.__repr__
275
    def __repr__(self):
276
        """Returns an informative representation of the job."""
277
        parts = "%s for " % self.__class__.__name__
278
        name = self.sourcepackagename
279
        if not name:
280
            parts += "no package name (!)"
281
        else:
282
            parts += "package %s" % name
283
        parts += " from %s to %s" % (self.parent_series.name,
284
                                     self.derived_series.name)
285
        return "<%s>" % parts
286
12579.4.2 by Steve Kowalik
Add a cronscript to run DSDJs, and the associated bits.
287
    @property
288
    def sourcepackagename(self):
289
        return SourcePackageName.get(self.metadata['sourcepackagename'])
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
290
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
291
    @property
292
    def derived_series(self):
293
        return self.distroseries
294
295
    @property
296
    def parent_series(self):
297
        parent_id = self.metadata['parent_series']
298
        return IStore(DistroSeries).get(DistroSeries, parent_id)
299
12736.7.1 by Jeroen Vermeulen
Filter DSD creation by packagesets.
300
    def passesPackagesetFilter(self):
301
        """Is this package of interest as far as packagesets are concerned?
302
303
        If the parent series has packagesets, then packages that are
304
        missing in the derived series are only of interest if they are
305
        in a packageset that the derived series also has.
306
        """
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
307
        derived_series = self.derived_series
308
        parent_series = self.parent_series
309
310
        sourcepackagename = self.sourcepackagename
311
        if has_package(derived_series, sourcepackagename):
12736.7.1 by Jeroen Vermeulen
Filter DSD creation by packagesets.
312
            return True
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
313
        if not has_package(parent_series, sourcepackagename):
12736.7.1 by Jeroen Vermeulen
Filter DSD creation by packagesets.
314
            return True
315
        packagesetset = getUtility(IPackagesetSet)
316
        if packagesetset.getBySeries(parent_series).is_empty():
317
            # Parent series does not have packagesets, as would be the
318
            # case for e.g. Debian.  In that case, don't filter.
319
            return True
320
        parent_sets = packagesetset.setsIncludingSource(
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
321
            sourcepackagename, distroseries=parent_series)
12736.7.1 by Jeroen Vermeulen
Filter DSD creation by packagesets.
322
        for parent_set in parent_sets:
323
            for related_set in parent_set.relatedSets():
324
                if related_set.distroseries == derived_series:
325
                    return True
326
        return False
327
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
328
    def getMatchingDSD(self):
329
        """Find an existing `DistroSeriesDifference` for this difference."""
330
        spn_id = self.metadata["sourcepackagename"]
331
        parent_id = self.metadata["parent_series"]
332
        store = IMasterStore(DistroSeriesDifference)
333
        search = store.find(
334
            DistroSeriesDifference,
335
            DistroSeriesDifference.derived_series == self.derived_series,
13457.5.3 by Raphael Badin
Remove XXX references to 758906 (make parent_series mandatory).
336
            DistroSeriesDifference.parent_series_id == parent_id,
337
            DistroSeriesDifference.source_package_name_id == spn_id)
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
338
        return search.one()
339
12558.2.1 by Jeroen Vermeulen
DistroSeriesDifferenceJob.
340
    def run(self):
341
        """See `IRunnableJob`."""
12736.7.1 by Jeroen Vermeulen
Filter DSD creation by packagesets.
342
        if not self.passesPackagesetFilter():
343
            return
344
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
345
        ds_diff = self.getMatchingDSD()
12579.4.2 by Steve Kowalik
Add a cronscript to run DSDJs, and the associated bits.
346
        if ds_diff is None:
13626.3.3 by Jeroen Vermeulen
Make make_metadata a bit more generally applicable.
347
            getUtility(IDistroSeriesDifferenceSource).new(
7675.1169.1 by Jeroen Vermeulen
Make DSDJob accept optional parent_series.
348
                self.distroseries, self.sourcepackagename, self.parent_series)
12579.4.2 by Steve Kowalik
Add a cronscript to run DSDJs, and the associated bits.
349
        else:
350
            ds_diff.update()