Merge ~mwhudson/ubuntu-cdimage:simplify-germinate into ubuntu-cdimage:main

Proposed by Michael Hudson-Doyle
Status: Merged
Merged at revision: e5056baafa398e289c2774706cfece5be06bd2c0
Proposed branch: ~mwhudson/ubuntu-cdimage:simplify-germinate
Merge into: ubuntu-cdimage:main
Diff against target: 980 lines (+39/-628)
11 files modified
bin/cron.dvd (+2/-1)
dev/null (+0/-46)
lib/cdimage/build.py (+1/-12)
lib/cdimage/check_installable.py (+0/-17)
lib/cdimage/germinate.py (+25/-235)
lib/cdimage/livefs.py (+2/-5)
lib/cdimage/tests/test_build.py (+0/-10)
lib/cdimage/tests/test_check_installable.py (+1/-35)
lib/cdimage/tests/test_germinate.py (+6/-249)
lib/cdimage/tests/test_tree.py (+0/-4)
lib/cdimage/tree.py (+2/-14)
Reviewer Review Type Date Requested Status
Steve Langasek Approve
Review via email: mp+460539@code.launchpad.net

Description of the change

This strips out I think all of the unnecessary complexity in ubuntu-cdimage's germinate code that can be stripped without making changes to the debian-cd end of things.

It is perhaps too much to land in one big hit but well. Look at that diffstat!!

Probably best to review commit by commit.

To post a comment you must log in.
Revision history for this message
Steve Langasek (vorlon) :
review: Needs Fixing
Revision history for this message
Michael Hudson-Doyle (mwhudson) :
Revision history for this message
Steve Langasek (vorlon) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/bin/cron.daily b/bin/cron.daily
0deleted file mode 1007550deleted file mode 100755
index d205b69..0000000
--- a/bin/cron.daily
+++ /dev/null
@@ -1,46 +0,0 @@
1#! /usr/bin/python3
2
3# Copyright (C) 2013 Canonical Ltd.
4# Author: Colin Watson <cjwatson@ubuntu.com>
5
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; version 3 of the License.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18"""Build a set of alternate/server images."""
19
20from optparse import OptionParser
21import os
22import sys
23
24sys.path.insert(0, os.path.join(sys.path[0], os.pardir, "lib"))
25
26
27def main():
28 from cdimage.build import build_image_set
29 from cdimage.config import Config
30
31 parser = OptionParser("%prog")
32 parser.add_option(
33 "--live", default=False, action="store_true",
34 help="build live filesystems first")
35 options, _ = parser.parse_args()
36 config = Config(IMAGE_TYPE="daily")
37 config["CDIMAGE_INSTALL"] = "1"
38 project = config.project
39 if project == "ubuntu-server" and not config["CDIMAGE_NO_SQUASHFS_BASE"]:
40 config["CDIMAGE_SQUASHFS_BASE"] = "1"
41 if not build_image_set(config, options):
42 sys.exit(1)
43
44
45if __name__ == "__main__":
46 main()
diff --git a/bin/cron.dvd b/bin/cron.dvd
index 3732b09..5566973 100755
--- a/bin/cron.dvd
+++ b/bin/cron.dvd
@@ -36,7 +36,8 @@ def main():
36 config = Config(IMAGE_TYPE="dvd")36 config = Config(IMAGE_TYPE="dvd")
37 project = config.project37 project = config.project
38 if project not in ("ubuntu", "ubuntustudio"):38 if project not in ("ubuntu", "ubuntustudio"):
39 config["CDIMAGE_INSTALL"] = "1"39 print("unsupported configuration!")
40 sys.exit(1)
40 config["CDIMAGE_LIVE"] = "1"41 config["CDIMAGE_LIVE"] = "1"
41 config["CDIMAGE_DVD"] = "1"42 config["CDIMAGE_DVD"] = "1"
42 if not build_image_set(config, options):43 if not build_image_set(config, options):
diff --git a/lib/cdimage/build.py b/lib/cdimage/build.py
index cbc07d4..edd53db 100644
--- a/lib/cdimage/build.py
+++ b/lib/cdimage/build.py
@@ -30,7 +30,6 @@ import traceback
3030
31from cdimage import osextras31from cdimage import osextras
32from cdimage.build_id import next_build_id32from cdimage.build_id import next_build_id
33from cdimage.check_installable import check_installable
34from cdimage.germinate import Germination33from cdimage.germinate import Germination
35from cdimage.livefs import (34from cdimage.livefs import (
36 LiveBuildsFailed,35 LiveBuildsFailed,
@@ -88,9 +87,6 @@ def configure_for_project(config):
88 ):87 ):
89 config["CDIMAGE_UNSUPPORTED"] = "1"88 config["CDIMAGE_UNSUPPORTED"] = "1"
9089
91 if config["CDIMAGE_INSTALL"]:
92 config["CDIMAGE_INSTALL_BASE"] = "1"
93
9490
95def open_log(config):91def open_log(config):
96 if config["DEBUG"] or config["CDIMAGE_NOLOG"]:92 if config["DEBUG"] or config["CDIMAGE_NOLOG"]:
@@ -661,7 +657,7 @@ def build_image_set_locked(config, options):
661 log_marker("Checking for other task changes")657 log_marker("Checking for other task changes")
662 germinate_output.update_tasks(date)658 germinate_output.update_tasks(date)
663659
664 if (config["CDIMAGE_LIVE"] or config["CDIMAGE_SQUASHFS_BASE"]):660 if config["CDIMAGE_LIVE"]:
665 log_marker("Downloading live filesystem images")661 log_marker("Downloading live filesystem images")
666 download_live_filesystems(config, builds)662 download_live_filesystems(config, builds)
667663
@@ -671,13 +667,6 @@ def build_image_set_locked(config, options):
671 copy_netboot_tarballs(config)667 copy_netboot_tarballs(config)
672 fix_permissions(config)668 fix_permissions(config)
673669
674 # Temporarily turned off for live builds.
675 if (config["CDIMAGE_INSTALL_BASE"] and
676 not config["CDIMAGE_ADDON"] and
677 not config["CDIMAGE_PREINSTALLED"]):
678 log_marker("Producing installability report")
679 check_installable(config)
680
681 if not config["DEBUG"] and not config["CDIMAGE_NOPUBLISH"]:670 if not config["DEBUG"] and not config["CDIMAGE_NOPUBLISH"]:
682 log_marker("Publishing")671 log_marker("Publishing")
683 tree = Tree.get_daily(config)672 tree = Tree.get_daily(config)
diff --git a/lib/cdimage/check_installable.py b/lib/cdimage/check_installable.py
index 62cbfc6..0aff60e 100644
--- a/lib/cdimage/check_installable.py
+++ b/lib/cdimage/check_installable.py
@@ -60,23 +60,6 @@ def _prepare_check_installable(config):
6060
61 packages = os.path.join(data, "Packages_%s" % arch)61 packages = os.path.join(data, "Packages_%s" % arch)
62 with open(packages, "wb") as packages_file:62 with open(packages, "wb") as packages_file:
63 if config["CDIMAGE_SQUASHFS_BASE"]:
64 squashfs = os.path.join(live, "%s.squashfs" % fullarch)
65 if os.path.exists(squashfs):
66 _ensure_tempdir()
67 with open("/dev/null", "w") as devnull:
68 subprocess.check_call([
69 "unsquashfs",
70 "-d", os.path.join(_tempdir, fullarch),
71 squashfs, "/var/lib/dpkg/status",
72 ], stdout=devnull)
73 status_path = os.path.join(
74 _tempdir, fullarch, "var", "lib", "dpkg", "status")
75 with open(os.path.join(status_path)) as status:
76 subprocess.call([
77 "grep-dctrl", "-XFStatus", "install ok installed",
78 ], stdin=status, stdout=packages_file)
79
80 for component in "main", "restricted", "universe", "multiverse":63 for component in "main", "restricted", "universe", "multiverse":
81 packages_gz = os.path.join(64 packages_gz = os.path.join(
82 image_top, "%s-%s" % (config.series, fullarch), "CD1",65 image_top, "%s-%s" % (config.series, fullarch), "CD1",
diff --git a/lib/cdimage/germinate.py b/lib/cdimage/germinate.py
index dcc5e88..004e2a3 100644
--- a/lib/cdimage/germinate.py
+++ b/lib/cdimage/germinate.py
@@ -17,10 +17,7 @@
1717
18from __future__ import print_function18from __future__ import print_function
1919
20from collections import OrderedDict, defaultdict
21import errno
22import os20import os
23import re
24import shutil21import shutil
25import subprocess22import subprocess
26import traceback23import traceback
@@ -47,13 +44,9 @@ class Germination:
4744
48 @property45 @property
49 def germinate_path(self):46 def germinate_path(self):
50 paths = [47 path = os.path.join(self.config.root, "germinate", "bin", "germinate")
51 os.path.join(self.config.root, "germinate", "bin", "germinate"),48 if os.access(path, os.X_OK):
52 os.path.join(self.config.root, "germinate", "germinate.py"),49 return path
53 ]
54 for path in paths:
55 if os.access(path, os.X_OK):
56 return path
57 else:50 else:
58 raise GerminateNotInstalled(51 raise GerminateNotInstalled(
59 "Please check out lp:germinate in %s." %52 "Please check out lp:germinate in %s." %
@@ -192,12 +185,6 @@ class NoMasterSeeds(Exception):
192 pass185 pass
193186
194187
195re_not_base = re.compile(
196 r"^(linux-(image|restricted|amd64|386|686|k7|power|"
197 r"imx51|dove|omap).*|"
198 r"nvidia-kernel-common|grub|yaboot|efibootmgr|elilo|silo|palo)$")
199
200
201class GerminateOutput:188class GerminateOutput:
202 def __init__(self, config, directory):189 def __init__(self, config, directory):
203 self.config = config190 self.config = config
@@ -206,102 +193,30 @@ class GerminateOutput:
206 self._parse_structure()193 self._parse_structure()
207194
208 def _parse_structure(self):195 def _parse_structure(self):
209 self._seeds = OrderedDict()196 self._seeds = []
210 with open(self.structure) as structure:197 with open(self.structure) as structure:
211 for line in structure:198 for line in structure:
212 line = line.strip()199 line = line.strip()
213 if not line or line.startswith("#") or ":" not in line:200 if not line or line.startswith("#") or ":" not in line:
214 continue201 continue
215 seed, inherit = line.split(":", 1)202 self._seeds.append(line.split(":", 1)[0])
216 self._seeds[seed] = inherit.split()203
217204 def pool_seeds(self):
218 def _expand_inheritance(self, seed, inherit):205 if not (self.config["CDIMAGE_DVD"] or self.config["CDIMAGE_LIVE"]):
219 for s in self._seeds.get(seed, ()):206 raise NoMasterSeeds("No seeds found for master task!")
220 self._expand_inheritance(s, inherit)
221 if seed not in inherit:
222 inherit.append(seed)
223
224 def _inheritance(self, seed):
225 inherit = []
226 self._expand_inheritance(seed, inherit)
227 return inherit
228
229 def _without_inheritance(self, subtract, seeds):
230 subtract_inherit = self._inheritance(subtract)
231 remaining = set(seeds) - set(subtract_inherit)
232 return [seed for seed in seeds if seed in remaining]
233
234 def list_seeds(self, mode):
235 project = self.config.project207 project = self.config.project
236 series = self.config["DIST"]
237208
238 if mode == "all":209 if project == "ubuntustudio":
239 for seed in self._seeds:210 yield "dvd"
240 yield seed211 yield "ship-live"
241 elif mode == "tasks":212 elif project == "ubuntu-server":
242 ship = "ship"213 yield "server-ship-live"
243 if "ship-addon" in self._seeds:214 elif project == "ubuntu" and self.config["SUBPROJECT"] == "canary":
244 ship = "ship-addon"215 # ubuntu-desktop-installer
245 in_squashfs = None216 yield "canary-ship-live"
246 if project == "ubuntu-server":217 # TODO: will we need a legacy-ship-live seed?
247 ship = "server-ship"218 else:
248 in_squashfs = ["minimal"]219 yield "ship-live"
249 seeds = self._inheritance(ship)
250 if (self.config["CDIMAGE_SQUASHFS_BASE"] and
251 in_squashfs is not None):
252 for subtract in in_squashfs:
253 seeds = self._without_inheritance(subtract, seeds)
254 for seed in seeds:
255 yield seed
256 if self.config["CDIMAGE_DVD"]:
257 # TODO cjwatson 2007-04-18: hideous hack to fix DVD tasks
258 yield "dns-server"
259 yield "lamp-server"
260 elif mode == "installer":
261 if self.config["CDIMAGE_INSTALL_BASE"]:
262 yield "installer"
263 elif mode == "debootstrap":
264 yield "required"
265 yield "minimal"
266 elif mode == "base":
267 yield "boot"
268 yield "required"
269 yield "minimal"
270 yield "standard"
271 elif mode == "ship-live":
272 if project == "lubuntu" and series == "bionic":
273 yield "ship-live-gtk"
274 yield "ship-live-share"
275 elif project == "lubuntu-next" and series == "bionic":
276 yield "ship-live-qt"
277 yield "ship-live-share"
278 elif project == "ubuntu-server" and series >= "bionic":
279 yield "server-ship-live"
280 elif project == "ubuntu" and self.config["SUBPROJECT"] == "canary":
281 # ubuntu-desktop-installer
282 yield "canary-ship-live"
283 # TODO: we will probably need a legacy-ship-live seed
284 else:
285 yield "ship-live"
286 elif mode == "addon":
287 ship = self._inheritance("ship")
288 ship_addon = self._inheritance("ship-addon")
289 for seed in ship_addon:
290 if seed not in ship:
291 yield seed
292 elif mode == "dvd":
293 if project == "ubuntu":
294 # no inheritance; most of this goes on the live filesystem
295 yield "usb-langsupport"
296 yield "usb-ship-live"
297 elif project == "ubuntustudio":
298 # no inheritance; most of this goes on the live filesystem
299 yield "dvd"
300 if series >= "bionic":
301 yield "ship-live"
302 else:
303 for seed in self._inheritance("dvd"):
304 yield seed
305220
306 def seed_path(self, arch, seed):221 def seed_path(self, arch, seed):
307 return os.path.join(self.directory, arch, seed)222 return os.path.join(self.directory, arch, seed)
@@ -311,42 +226,12 @@ class GerminateOutput:
311 lines = seed_file.read().splitlines()[2:-2]226 lines = seed_file.read().splitlines()[2:-2]
312 return [line.split(None, 1)[0] for line in lines]227 return [line.split(None, 1)[0] for line in lines]
313228
314 def master_seeds(self):
315 if self.config["CDIMAGE_ADDON"]:
316 for seed in self.list_seeds("addon"):
317 yield seed
318 else:
319 for seed in self.list_seeds("installer"):
320 yield seed
321 if self.config["CDIMAGE_DVD"]:
322 for seed in self.list_seeds("dvd"):
323 if seed not in ("installer", "casper"):
324 yield seed
325 elif self.config["CDIMAGE_INSTALL"]:
326 for seed in self.list_seeds("tasks"):
327 if seed not in ("installer", "casper"):
328 yield seed
329 else:
330 if self.config.get("CDIMAGE_INSTALL_BASE") == "1":
331 for seed in self.list_seeds("base"):
332 if seed not in ("installer", "casper"):
333 yield seed
334 if self.config.get("CDIMAGE_LIVE") == "1":
335 for seed in self.list_seeds("ship-live"):
336 if seed not in ("installer", "casper"):
337 yield seed
338
339 def master_task_entries(self):229 def master_task_entries(self):
340 project = self.config.project230 project = self.config.project
341 series = self.config.series231 series = self.config.series
342232
343 found = False233 for seed in self.pool_seeds():
344 for seed in self.master_seeds():
345 yield "#include <%s/%s/%s>" % (project, series, seed)234 yield "#include <%s/%s/%s>" % (project, series, seed)
346 found = True
347
348 if not found:
349 raise NoMasterSeeds("No seeds found for master task!")
350235
351 def tasks_output_dir(self):236 def tasks_output_dir(self):
352 return os.path.join(237 return os.path.join(
@@ -354,116 +239,21 @@ class GerminateOutput:
354 self.config.project, self.config.full_series,239 self.config.project, self.config.full_series,
355 self.config.image_type, "tasks")240 self.config.image_type, "tasks")
356241
357 def task_packages(self, arch, seed, seedsource):
358 """Like seed_packages, but with various special-case hacks."""
359 installer_seeds = set(self.list_seeds("installer"))
360
361 for package in self.seed_packages(arch, seedsource):
362 # Hackily exclude kernel-image-* from the installer and casper
363 # tasks. Those udebs only exist to satisfy dependencies when
364 # building the debian-installer package.
365 if seed in installer_seeds and package.startswith("kernel-image-"):
366 continue
367
368 # Force the use of live-installer rather than bootstrap-base on
369 # squashfs-base images. Seed expansion doesn't do the right
370 # thing here because the installer seed is expanded before
371 # considering server-ship.
372 if self.config["CDIMAGE_SQUASHFS_BASE"]:
373 if package == "bootstrap-base":
374 package = "live-installer"
375
376 yield package
377
378 def task_project(self):
379 # ubuntu-server really wants ubuntu-* tasks.
380 if self.config.project == "ubuntu-server":
381 return "ubuntu"
382 else:
383 return self.config.project
384
385 def task_headers(self, arch, seed):
386 headers = {}
387 try:
388 with open("%s.seedtext" % self.seed_path(arch, seed)) as seedtext:
389 for line in seedtext:
390 if not line.lower().startswith("task-"):
391 continue
392 line = line.rstrip("\n")
393 key, value = line.split(":", 1)
394 key = key[5:].lower()
395 value = value.lstrip()
396 headers[key] = value
397 except IOError as e:
398 if e.errno != errno.ENOENT:
399 raise
400 return headers
401
402 def seed_task_mapping(self, arch):
403 task_project = self.task_project()
404 for seed in self.list_seeds("all"):
405 # Tasks implemented via tasksel, with Task-Seeds to indicate
406 # task/seed mapping.
407 task = seed
408 headers = self.task_headers(arch, seed)
409 if not headers:
410 continue
411 input_seeds = [seed] + headers.get("seeds", "").split()
412 if "per-derivative" in headers:
413 task = "%s-%s" % (task_project, task)
414
415 yield input_seeds, task
416
417 def write_tasks(self):242 def write_tasks(self):
418 output_dir = self.tasks_output_dir()243 output_dir = self.tasks_output_dir()
419 osextras.mkemptydir(self.tasks_output_dir())244 osextras.mkemptydir(self.tasks_output_dir())
420245
421 for arch in self.config.arches:246 for arch in self.config.arches:
422 packages = defaultdict(list)
423 cpparch = arch.replace("+", "_").replace("-", "_")247 cpparch = arch.replace("+", "_").replace("-", "_")
424 for seed in self.list_seeds("all"):248 for seed in self._seeds:
425 if seed == "supported":249 if not os.path.exists(self.seed_path(arch, seed)):
426 seedsource = "%s+build-depends" % seed
427 else:
428 seedsource = seed
429 seed_path = self.seed_path(arch, seedsource)
430 if not os.path.exists(seed_path):
431 continue250 continue
432 with open(os.path.join(output_dir, seed), "a") as task_file:251 with open(os.path.join(output_dir, seed), "a") as task_file:
433 print("#ifdef ARCH_%s" % cpparch, file=task_file)252 print("#ifdef ARCH_%s" % cpparch, file=task_file)
434 for package in sorted(253 for package in sorted(self.seed_packages(arch, seed)):
435 self.task_packages(arch, seed, seedsource)):
436 packages[seed].append(package)
437 print(package, file=task_file)254 print(package, file=task_file)
438 print("#endif /* ARCH_%s */" % cpparch, file=task_file)255 print("#endif /* ARCH_%s */" % cpparch, file=task_file)
439256
440 tasks = defaultdict(list)
441 for input_seeds, task in self.seed_task_mapping(arch):
442 for input_seed in input_seeds:
443 for pkg in packages.get(input_seed, []):
444 tasks[pkg].append(task)
445
446 # Help debian-cd to regenerate Task headers, to make sure that
447 # we don't accidentally end up out of sync with the archive and
448 # break the package installation step.
449 override_path = os.path.join(output_dir, "override.%s" % arch)
450 with open(override_path, "w") as override:
451 for pkg, tasknames in sorted(tasks.items()):
452 print(
453 "%s Task %s" % (pkg, ", ".join(tasknames)),
454 file=override)
455 # Help debian-cd to get priorities in sync with the current base
456 # system, so that debootstrap >= 0.3.1 can work out the correct
457 # set of packages to install.
458 important_path = os.path.join(output_dir, "important.%s" % arch)
459 with open(important_path, "w") as important_file:
460 important = []
461 for seed in self.list_seeds("debootstrap"):
462 important.extend(packages.get(seed, []))
463 for pkg in sorted(important):
464 if not re_not_base.match(pkg):
465 print(pkg, file=important_file)
466
467 with open(os.path.join(output_dir, "MASTER"), "w") as master:257 with open(os.path.join(output_dir, "MASTER"), "w") as master:
468 for entry in self.master_task_entries():258 for entry in self.master_task_entries():
469 print(entry, file=master)259 print(entry, file=master)
@@ -471,7 +261,7 @@ class GerminateOutput:
471 def diff_tasks(self, output=None):261 def diff_tasks(self, output=None):
472 tasks_dir = self.tasks_output_dir()262 tasks_dir = self.tasks_output_dir()
473 previous_tasks_dir = "%s-previous" % tasks_dir263 previous_tasks_dir = "%s-previous" % tasks_dir
474 for seed in ["MASTER"] + list(self.list_seeds("all")):264 for seed in ["MASTER"] + list(self._seeds):
475 old = os.path.join(previous_tasks_dir, seed)265 old = os.path.join(previous_tasks_dir, seed)
476 new = os.path.join(tasks_dir, seed)266 new = os.path.join(tasks_dir, seed)
477 if os.path.exists(old) and os.path.exists(new):267 if os.path.exists(old) and os.path.exists(new):
diff --git a/lib/cdimage/livefs.py b/lib/cdimage/livefs.py
index c1b475a..f361745 100644
--- a/lib/cdimage/livefs.py
+++ b/lib/cdimage/livefs.py
@@ -724,8 +724,7 @@ def download_live_filesystems(config, builds):
724 output_dir = live_output_directory(config)724 output_dir = live_output_directory(config)
725 osextras.mkemptydir(output_dir)725 osextras.mkemptydir(output_dir)
726726
727 if (config["CDIMAGE_LIVE"] or config["CDIMAGE_SQUASHFS_BASE"] or727 if config["CDIMAGE_LIVE"] or config["CDIMAGE_PREINSTALLED"]:
728 config["CDIMAGE_PREINSTALLED"]):
729 got_image = False728 got_image = False
730 for arch in config.arches:729 for arch in config.arches:
731 if config["CDIMAGE_PREINSTALLED"]:730 if config["CDIMAGE_PREINSTALLED"]:
@@ -780,9 +779,7 @@ def download_live_filesystems(config, builds):
780 got_image = True779 got_image = True
781 else:780 else:
782 continue781 continue
783 if (project != "ubuntu-base" and782 if project != "ubuntu-base" and config.subproject != "wubi":
784 not config["CDIMAGE_SQUASHFS_BASE"] and
785 config.subproject != "wubi"):
786 download_live_items(config, builds, arch, "kernel")783 download_live_items(config, builds, arch, "kernel")
787 download_live_items(config, builds, arch, "initrd")784 download_live_items(config, builds, arch, "initrd")
788 download_live_items(config, builds, arch, "kernel-efi-signed")785 download_live_items(config, builds, arch, "kernel-efi-signed")
diff --git a/lib/cdimage/tests/test_build.py b/lib/cdimage/tests/test_build.py
index 1769f09..aac35e1 100644
--- a/lib/cdimage/tests/test_build.py
+++ b/lib/cdimage/tests/test_build.py
@@ -449,16 +449,6 @@ class TestBuildImageSet(TestCase):
449 else:449 else:
450 self.assertNotIn("CDIMAGE_UNSUPPORTED", config)450 self.assertNotIn("CDIMAGE_UNSUPPORTED", config)
451451
452 def test_configure_install_base(self):
453 config = Config(read=False)
454 configure_for_project(config)
455 self.assertNotIn("CDIMAGE_INSTALL_BASE", config)
456
457 config = Config(read=False)
458 config["CDIMAGE_INSTALL"] = "1"
459 configure_for_project(config)
460 self.assertEqual("1", config["CDIMAGE_INSTALL_BASE"])
461
462 @mock.patch("os.open")452 @mock.patch("os.open")
463 def test_open_log_debug(self, mock_open):453 def test_open_log_debug(self, mock_open):
464 self.config["DEBUG"] = "1"454 self.config["DEBUG"] = "1"
diff --git a/lib/cdimage/tests/test_check_installable.py b/lib/cdimage/tests/test_check_installable.py
index 902771e..a671e96 100644
--- a/lib/cdimage/tests/test_check_installable.py
+++ b/lib/cdimage/tests/test_check_installable.py
@@ -21,8 +21,6 @@ from __future__ import print_function
2121
22import gzip22import gzip
23import os23import os
24import subprocess
25from textwrap import dedent
2624
27from cdimage.check_installable import (25from cdimage.check_installable import (
28 _check_installable_command,26 _check_installable_command,
@@ -30,7 +28,7 @@ from cdimage.check_installable import (
30 _prepare_check_installable,28 _prepare_check_installable,
31)29)
32from cdimage.config import Config30from cdimage.config import Config
33from cdimage.tests.helpers import TestCase, mkfile31from cdimage.tests.helpers import TestCase
3432
3533
36class TestCheckInstallable(TestCase):34class TestCheckInstallable(TestCase):
@@ -86,38 +84,6 @@ class TestCheckInstallable(TestCase):
86 with open(os.path.join(data, "Packages_i386")) as packages_file:84 with open(os.path.join(data, "Packages_i386")) as packages_file:
87 self.assertEqual("Package: foo\n\n", packages_file.read())85 self.assertEqual("Package: foo\n\n", packages_file.read())
8886
89 def test_prepare_squashfs_base(self):
90 self.config["CDIMAGE_SQUASHFS_BASE"] = "1"
91 _, image_top, live, data = _check_installable_dirs(self.config)
92 squashfs_dir = os.path.join(self.temp_dir, "squashfs")
93 status_path = os.path.join(
94 squashfs_dir, "var", "lib", "dpkg", "status")
95 fake_packages = dedent("""\
96 Package: base-files
97 Status: install ok installed
98 Version: 6.5
99
100 Package: libc6
101 Status: install ok installed
102 Version: 2.15-1
103 Provides: glibc
104
105 """)
106 with mkfile(status_path) as status:
107 print(fake_packages, end="", file=status)
108 os.makedirs(live)
109 with open("/dev/null", "w") as devnull:
110 subprocess.check_call(
111 ["mksquashfs", squashfs_dir,
112 os.path.join(live, "i386.squashfs")],
113 stdout=devnull)
114 self.capture_logging()
115 _prepare_check_installable(self.config)
116 self.assertLogEqual([])
117 self.assertCountEqual(["Packages_i386", "Sources"], os.listdir(data))
118 with open(os.path.join(data, "Packages_i386")) as packages_file:
119 self.assertEqual(fake_packages, packages_file.read())
120
121 def test_command(self):87 def test_command(self):
122 britney, _, _, data = _check_installable_dirs(self.config)88 britney, _, _, data = _check_installable_dirs(self.config)
123 command = _check_installable_command(self.config)89 command = _check_installable_command(self.config)
diff --git a/lib/cdimage/tests/test_germinate.py b/lib/cdimage/tests/test_germinate.py
index f567eae..7b15027 100644
--- a/lib/cdimage/tests/test_germinate.py
+++ b/lib/cdimage/tests/test_germinate.py
@@ -29,7 +29,7 @@ try:
29except ImportError:29except ImportError:
30 import mock30 import mock
3131
32from cdimage.config import Config, all_series32from cdimage.config import Config
33from cdimage.germinate import (33from cdimage.germinate import (
34 GerminateNotInstalled,34 GerminateNotInstalled,
35 GerminateOutput,35 GerminateOutput,
@@ -55,11 +55,6 @@ class TestGermination(TestCase):
55 GerminateNotInstalled, getattr, self.germination, "germinate_path")55 GerminateNotInstalled, getattr, self.germination, "germinate_path")
5656
57 germinate_dir = os.path.join(self.temp_dir, "germinate")57 germinate_dir = os.path.join(self.temp_dir, "germinate")
58 old_germinate = os.path.join(germinate_dir, "germinate.py")
59 touch(old_germinate)
60 os.chmod(old_germinate, 0o755)
61 self.assertEqual(old_germinate, self.germination.germinate_path)
62
63 new_germinate = os.path.join(germinate_dir, "bin", "germinate")58 new_germinate = os.path.join(germinate_dir, "bin", "germinate")
64 touch(new_germinate)59 touch(new_germinate)
65 os.chmod(new_germinate, 0o755)60 os.chmod(new_germinate, 0o755)
@@ -356,106 +351,6 @@ class TestGerminateOutput(TestCase):
356 ["active-ship-live", ["ship-live"]],351 ["active-ship-live", ["ship-live"]],
357 ])352 ])
358353
359 def test_inheritance_recurses(self):
360 """_inheritance recurses properly."""
361 self.write_structure([["a", []], ["b", ["a"]], ["c", ["b"]]])
362 output = GerminateOutput(self.config, self.temp_dir)
363 self.assertEqual(["a"], output._inheritance("a"))
364 self.assertEqual(["a", "b"], output._inheritance("b"))
365 self.assertEqual(["a", "b", "c"], output._inheritance("c"))
366
367 def test_inheritance_avoids_duplicates(self):
368 """_inheritance avoids adding a seed more than once."""
369 self.write_structure([["a", []], ["b", ["a"]], ["c", ["a", "b"]]])
370 output = GerminateOutput(self.config, self.temp_dir)
371 self.assertEqual(["a", "b", "c"], output._inheritance("c"))
372
373 def test_without_inheritance(self):
374 self.write_structure(
375 [["a", []], ["b", ["a"]], ["c", ["b"]], ["d", ["a", "c"]]])
376 output = GerminateOutput(self.config, self.temp_dir)
377 inheritance = output._inheritance("d")
378 self.assertEqual(["a", "b", "c", "d"], inheritance)
379 self.assertEqual(
380 ["c", "d"], output._without_inheritance("b", inheritance))
381
382 def test_list_seeds_all(self):
383 self.write_structure([["a", []], ["b", ["a"]], ["c", []]])
384 output = GerminateOutput(self.config, self.temp_dir)
385 self.assertEqual(["a", "b", "c"], list(output.list_seeds("all")))
386
387 def test_list_seeds_tasks_ubuntu(self):
388 self.write_ubuntu_structure()
389 output = GerminateOutput(self.config, self.temp_dir)
390 self.config["PROJECT"] = "ubuntu"
391 self.config["DIST"] = "bionic"
392 expected = [
393 "boot", "installer", "required", "minimal", "standard",
394 "desktop-common", "desktop", "d-i-requirements", "ship",
395 ]
396 self.assertEqual(expected, list(output.list_seeds("tasks")))
397 self.config["CDIMAGE_DVD"] = "1"
398 expected.extend(["dns-server", "lamp-server"])
399 self.assertEqual(expected, list(output.list_seeds("tasks")))
400
401 def test_list_seeds_tasks_ubuntu_server(self):
402 self.write_ubuntu_structure()
403 output = GerminateOutput(self.config, self.temp_dir)
404 self.config["PROJECT"] = "ubuntu-server"
405 expected = [
406 "boot", "installer", "required", "minimal", "standard",
407 "dns-server", "lamp-server", "openssh-server", "print-server",
408 "samba-server", "postgresql-server", "mail-server", "server",
409 "tomcat-server", "virt-host", "d-i-requirements", "server-ship",
410 ]
411 for series in all_series[4:]:
412 self.config["DIST"] = series
413 self.assertEqual(expected, list(output.list_seeds("tasks")))
414
415 def test_list_seeds_task_ubuntu_server_squashfs(self):
416 self.write_ubuntu_structure()
417 output = GerminateOutput(self.config, self.temp_dir)
418 self.config["PROJECT"] = "ubuntu-server"
419 self.config["DIST"] = "bionic"
420 self.config["CDIMAGE_SQUASHFS_BASE"] = "1"
421 expected = [
422 "boot", "installer", "standard", "dns-server", "lamp-server",
423 "openssh-server", "print-server", "samba-server",
424 "postgresql-server", "mail-server", "server", "tomcat-server",
425 "virt-host", "d-i-requirements", "server-ship",
426 ]
427 self.assertEqual(expected, list(output.list_seeds("tasks")))
428
429 def test_list_seeds_installer(self):
430 self.write_structure([["installer", []], ["casper", []]])
431 output = GerminateOutput(self.config, self.temp_dir)
432 self.config["CDIMAGE_INSTALL_BASE"] = "1"
433 self.assertEqual(["installer"], list(output.list_seeds("installer")))
434 del self.config["CDIMAGE_INSTALL_BASE"]
435 self.config["CDIMAGE_LIVE"] = "1"
436 self.config["DIST"] = "bionic"
437 self.assertEqual([], list(output.list_seeds("installer")))
438
439 def test_list_seeds_debootstrap(self):
440 self.write_ubuntu_structure()
441 output = GerminateOutput(self.config, self.temp_dir)
442 for series in all_series[6:]:
443 self.config["DIST"] = series
444 self.assertEqual(
445 ["required", "minimal"],
446 list(output.list_seeds("debootstrap")))
447
448 def test_list_seeds_base(self):
449 self.write_ubuntu_structure()
450 output = GerminateOutput(self.config, self.temp_dir)
451 for series in all_series[6:]:
452 self.config["DIST"] = series
453 self.assertEqual(
454 ["boot", "required", "minimal", "standard"],
455 list(output.list_seeds("base")))
456
457 # TODO list_seeds ship-live/addon/dvd untested
458
459 def test_seed_path(self):354 def test_seed_path(self):
460 self.write_ubuntu_structure()355 self.write_ubuntu_structure()
461 output = GerminateOutput(self.config, self.temp_dir)356 output = GerminateOutput(self.config, self.temp_dir)
@@ -495,43 +390,8 @@ class TestGerminateOutput(TestCase):
495 ["base-files", "base-passwd"],390 ["base-files", "base-passwd"],
496 output.seed_packages("i386", "base"))391 output.seed_packages("i386", "base"))
497392
498 # TODO: master_seeds addon untested393 @mock.patch("cdimage.germinate.GerminateOutput.pool_seeds")
499394 def test_master_task_entries(self, mock_pool_seeds):
500 def test_master_seeds_dvd_ubuntu_bionic(self):
501 self.write_ubuntu_structure()
502 output = GerminateOutput(self.config, self.temp_dir)
503 self.config["PROJECT"] = "ubuntu"
504 self.config["DIST"] = "bionic"
505 self.config["CDIMAGE_DVD"] = "1"
506 self.assertEqual(
507 ["usb-langsupport", "usb-ship-live"], list(output.master_seeds()))
508
509 def test_master_seeds_install_ubuntu_bionic(self):
510 self.write_ubuntu_structure()
511 output = GerminateOutput(self.config, self.temp_dir)
512 self.config["PROJECT"] = "ubuntu"
513 self.config["DIST"] = "bionic"
514 self.config["CDIMAGE_INSTALL"] = "1"
515 self.config["CDIMAGE_INSTALL_BASE"] = "1"
516 self.assertEqual([
517 "installer", "boot", "required", "minimal", "standard",
518 "desktop-common", "desktop", "d-i-requirements", "ship",
519 ], list(output.master_seeds()))
520
521 def test_master_seeds_live_ubuntu_bionic(self):
522 self.write_ubuntu_structure()
523 output = GerminateOutput(self.config, self.temp_dir)
524 self.config["PROJECT"] = "ubuntu"
525 self.config["DIST"] = "bionic"
526 self.config["CDIMAGE_INSTALL_BASE"] = "1"
527 self.config["CDIMAGE_LIVE"] = "1"
528 self.assertEqual([
529 "installer", "boot", "required", "minimal", "standard",
530 "ship-live",
531 ], list(output.master_seeds()))
532
533 @mock.patch("cdimage.germinate.GerminateOutput.master_seeds")
534 def test_master_task_entries(self, mock_master_seeds):
535 def side_effect():395 def side_effect():
536 yield "required"396 yield "required"
537 yield "minimal"397 yield "minimal"
@@ -540,21 +400,19 @@ class TestGerminateOutput(TestCase):
540 output = GerminateOutput(self.config, self.temp_dir)400 output = GerminateOutput(self.config, self.temp_dir)
541 self.config["DIST"] = "bionic"401 self.config["DIST"] = "bionic"
542 self.config["PROJECT"] = "ubuntu"402 self.config["PROJECT"] = "ubuntu"
543 mock_master_seeds.side_effect = side_effect403 mock_pool_seeds.side_effect = side_effect
544 self.assertEqual([404 self.assertEqual([
545 "#include <ubuntu/bionic/required>",405 "#include <ubuntu/bionic/required>",
546 "#include <ubuntu/bionic/minimal>",406 "#include <ubuntu/bionic/minimal>",
547 ], list(output.master_task_entries()))407 ], list(output.master_task_entries()))
548408
549 @mock.patch(409 def test_pool_seeds_invalid_config(self):
550 "cdimage.germinate.GerminateOutput.master_seeds", return_value=[])
551 def test_master_task_entries_no_seeds(self, mock_master_seeds):
552 self.write_ubuntu_structure()410 self.write_ubuntu_structure()
553 output = GerminateOutput(self.config, self.temp_dir)411 output = GerminateOutput(self.config, self.temp_dir)
554 self.config["DIST"] = "bionic"412 self.config["DIST"] = "bionic"
555 self.config["PROJECT"] = "ubuntu"413 self.config["PROJECT"] = "ubuntu"
556 self.assertRaises(414 self.assertRaises(
557 NoMasterSeeds, list, output.master_task_entries())415 NoMasterSeeds, list, output.pool_seeds())
558416
559 def test_tasks_output_dir(self):417 def test_tasks_output_dir(self):
560 self.write_ubuntu_structure()418 self.write_ubuntu_structure()
@@ -568,83 +426,6 @@ class TestGerminateOutput(TestCase):
568 "tasks"),426 "tasks"),
569 output.tasks_output_dir())427 output.tasks_output_dir())
570428
571 def test_task_packages_plain(self):
572 self.write_structure([["base", []]])
573 self.write_seed_output("i386", "base", ["base-files", "base-passwd"])
574 output = GerminateOutput(self.config, self.temp_dir)
575 self.assertEqual(
576 ["base-files", "base-passwd"],
577 list(output.task_packages("i386", "base", "base")))
578
579 def test_task_packages_installer(self):
580 # kernel-image-* is excluded from the installer seed.
581 self.write_structure([["installer", []]])
582 self.write_seed_output(
583 "i386", "installer", [
584 "block-modules-3.8.0-6-generic-di",
585 "kernel-image-3.8.0-6-generic-di",
586 ])
587 self.config["CDIMAGE_INSTALL_BASE"] = "1"
588 output = GerminateOutput(self.config, self.temp_dir)
589 self.assertEqual(
590 ["block-modules-3.8.0-6-generic-di"],
591 list(output.task_packages("i386", "installer", "installer")))
592
593 def test_task_packages_squashfs(self):
594 self.write_ubuntu_structure()
595 self.config["PROJECT"] = "ubuntu-server"
596 self.config["DIST"] = "bionic"
597 self.write_seed_output(
598 "i386", "installer", ["base-installer", "bootstrap-base"])
599 output = GerminateOutput(self.config, self.temp_dir)
600 self.assertEqual(
601 ["base-installer", "bootstrap-base"],
602 list(output.task_packages("i386", "installer", "installer")))
603 self.config["CDIMAGE_SQUASHFS_BASE"] = "1"
604 self.assertEqual(
605 ["base-installer", "live-installer"],
606 list(output.task_packages("i386", "installer", "installer")))
607
608 # TODO: task_project untested
609
610 def test_task_headers(self):
611 self.write_ubuntu_structure()
612 seedtext_path = os.path.join(self.temp_dir, "i386", "desktop.seedtext")
613 with mkfile(seedtext_path) as seedtext:
614 print(dedent("""\
615 Task-Per-Derivative: 1
616 Task-Key: ubuntu-desktop
617 Task-Seeds: desktop-common
618
619 = Seed text starts here ="""), file=seedtext)
620 output = GerminateOutput(self.config, self.temp_dir)
621 expected = {
622 "per-derivative": "1",
623 "key": "ubuntu-desktop",
624 "seeds": "desktop-common",
625 }
626 self.assertEqual(expected, output.task_headers("i386", "desktop"))
627 self.assertEqual({}, output.task_headers("i386", "missing"))
628
629 def test_seed_task_mapping(self):
630 self.write_ubuntu_structure()
631 seed_dir = os.path.join(self.temp_dir, "i386")
632 with mkfile(os.path.join(seed_dir, "standard.seedtext")) as seedtext:
633 print("Task-Key: ubuntu-standard", file=seedtext)
634 with mkfile(os.path.join(seed_dir, "desktop.seedtext")) as seedtext:
635 print(dedent("""\
636 Task-Per-Derivative: 1
637 Task-Seeds: desktop-common"""), file=seedtext)
638 self.config["DIST"] = "bionic"
639 self.config["PROJECT"] = "ubuntu"
640 output = GerminateOutput(self.config, self.temp_dir)
641 expected = [
642 (["standard"], "standard"),
643 (["desktop", "desktop-common"], "ubuntu-desktop"),
644 ]
645 self.assertEqual(
646 expected, list(output.seed_task_mapping("i386")))
647
648 def test_write_tasks(self):429 def test_write_tasks(self):
649 self.write_ubuntu_structure()430 self.write_ubuntu_structure()
650 for arch in "amd64", "i386":431 for arch in "amd64", "i386":
@@ -673,8 +454,6 @@ class TestGerminateOutput(TestCase):
673 "tasks")454 "tasks")
674 self.assertCountEqual([455 self.assertCountEqual([
675 "required", "minimal", "desktop", "live",456 "required", "minimal", "desktop", "live",
676 "override.amd64", "override.i386",
677 "important.amd64", "important.i386",
678 "MASTER",457 "MASTER",
679 ], os.listdir(output_dir))458 ], os.listdir(output_dir))
680 with open(os.path.join(output_dir, "required")) as f:459 with open(os.path.join(output_dir, "required")) as f:
@@ -723,28 +502,6 @@ class TestGerminateOutput(TestCase):
723 #endif /* ARCH_i386 */502 #endif /* ARCH_i386 */
724 """),503 """),
725 f.read())504 f.read())
726 with open(os.path.join(output_dir, "override.amd64")) as f:
727 self.assertEqual(
728 dedent("""\
729 adduser-amd64 Task minimal
730 base-files-amd64 Task minimal
731 firefox Task ubuntu-desktop
732 xterm Task ubuntu-desktop, ubuntu-live
733 """),
734 f.read())
735 with open(os.path.join(output_dir, "override.i386")) as f:
736 self.assertEqual(
737 dedent("""\
738 adduser-i386 Task minimal
739 base-files-i386 Task minimal
740 firefox Task ubuntu-desktop
741 xterm Task ubuntu-desktop, ubuntu-live
742 """),
743 f.read())
744 with open(os.path.join(output_dir, "important.amd64")) as f:
745 self.assertEqual("adduser-amd64\nbase-files-amd64\n", f.read())
746 with open(os.path.join(output_dir, "important.i386")) as f:
747 self.assertEqual("adduser-i386\nbase-files-i386\n", f.read())
748 with open(os.path.join(output_dir, "MASTER")) as f:505 with open(os.path.join(output_dir, "MASTER")) as f:
749 self.assertEqual("#include <ubuntu/bionic/ship-live>\n", f.read())506 self.assertEqual("#include <ubuntu/bionic/ship-live>\n", f.read())
750507
diff --git a/lib/cdimage/tests/test_tree.py b/lib/cdimage/tests/test_tree.py
index c5e3665..63a5840 100644
--- a/lib/cdimage/tests/test_tree.py
+++ b/lib/cdimage/tests/test_tree.py
@@ -1866,7 +1866,6 @@ class TestDailyTreePublisher(TestCase):
1866 @mock.patch("cdimage.tree.DailyTreePublisher.post_qa")1866 @mock.patch("cdimage.tree.DailyTreePublisher.post_qa")
1867 def test_publish(self, mock_post_qa, *args):1867 def test_publish(self, mock_post_qa, *args):
1868 self.config["ARCHES"] = "i386"1868 self.config["ARCHES"] = "i386"
1869 self.config["CDIMAGE_INSTALL_BASE"] = "1"
1870 publisher = self.make_publisher("ubuntu", "daily-live")1869 publisher = self.make_publisher("ubuntu", "daily-live")
1871 source_dir = publisher.image_output("i386")1870 source_dir = publisher.image_output("i386")
1872 touch(os.path.join(1871 touch(os.path.join(
@@ -1900,7 +1899,6 @@ class TestDailyTreePublisher(TestCase):
1900 "%s-desktop-i386.iso" % self.config.series,1899 "%s-desktop-i386.iso" % self.config.series,
1901 "%s-desktop-i386.list" % self.config.series,1900 "%s-desktop-i386.list" % self.config.series,
1902 "%s-desktop-i386.manifest" % self.config.series,1901 "%s-desktop-i386.manifest" % self.config.series,
1903 "report.html",
1904 ], os.listdir(target_dir))1902 ], os.listdir(target_dir))
1905 self.assertCountEqual(1903 self.assertCountEqual(
1906 [".htaccess", "20120807", "current", "pending"],1904 [".htaccess", "20120807", "current", "pending"],
@@ -1921,7 +1919,6 @@ class TestDailyTreePublisher(TestCase):
1921 def test_publish_subtree(self, mock_post_qa, *args):1919 def test_publish_subtree(self, mock_post_qa, *args):
1922 self.config.subtree = "subtree/test"1920 self.config.subtree = "subtree/test"
1923 self.config["ARCHES"] = "i386"1921 self.config["ARCHES"] = "i386"
1924 self.config["CDIMAGE_INSTALL_BASE"] = "1"
1925 publisher = self.make_publisher("ubuntu", "daily-live")1922 publisher = self.make_publisher("ubuntu", "daily-live")
1926 source_dir = publisher.image_output("i386")1923 source_dir = publisher.image_output("i386")
1927 touch(os.path.join(1924 touch(os.path.join(
@@ -1960,7 +1957,6 @@ class TestDailyTreePublisher(TestCase):
1960 "%s-desktop-i386.iso" % self.config.series,1957 "%s-desktop-i386.iso" % self.config.series,
1961 "%s-desktop-i386.list" % self.config.series,1958 "%s-desktop-i386.list" % self.config.series,
1962 "%s-desktop-i386.manifest" % self.config.series,1959 "%s-desktop-i386.manifest" % self.config.series,
1963 "report.html",
1964 ], os.listdir(target_dir))1960 ], os.listdir(target_dir))
1965 self.assertCountEqual(1961 self.assertCountEqual(
1966 [".htaccess", "20120807", "current", "pending"],1962 [".htaccess", "20120807", "current", "pending"],
diff --git a/lib/cdimage/tree.py b/lib/cdimage/tree.py
index 0e9ad71..6fac63b 100644
--- a/lib/cdimage/tree.py
+++ b/lib/cdimage/tree.py
@@ -2084,13 +2084,7 @@ class DailyTreePublisher(Publisher):
2084 else:2084 else:
2085 osextras.unlink_force("%s.manifest" % target_prefix)2085 osextras.unlink_force("%s.manifest" % target_prefix)
20862086
2087 if (self.config["CDIMAGE_SQUASHFS_BASE"] and2087 osextras.unlink_force("%s.squashfs" % target_prefix)
2088 os.path.exists("%s.squashfs" % source_prefix)):
2089 logger.info("Publishing %s squashfs ..." % arch)
2090 shutil.move(
2091 "%s.squashfs" % source_prefix, "%s.squashfs" % target_prefix)
2092 else:
2093 osextras.unlink_force("%s.squashfs" % target_prefix)
20942088
2095 if os.path.exists("%s.custom.tar.gz" % source_prefix):2089 if os.path.exists("%s.custom.tar.gz" % source_prefix):
2096 logger.info("Publishing %s custom tarball ..." % arch)2090 logger.info("Publishing %s custom tarball ..." % arch)
@@ -2616,14 +2610,8 @@ class DailyTreePublisher(Publisher):
2616 logger.warning("No images produced!")2610 logger.warning("No images produced!")
2617 return2611 return
26182612
2619 source_report = os.path.join(
2620 self.britney_report, "%s_probs.html" % self.config.series)
2621 target_report = os.path.join(self.publish_base, date, "report.html")2613 target_report = os.path.join(self.publish_base, date, "report.html")
2622 if (self.config["CDIMAGE_INSTALL_BASE"] and2614 osextras.unlink_force(target_report)
2623 os.path.exists(source_report)):
2624 shutil.copy2(source_report, target_report)
2625 else:
2626 osextras.unlink_force(target_report)
26272615
2628 self.polish_directory(date)2616 self.polish_directory(date)
2629 self.link(date, "pending")2617 self.link(date, "pending")

Subscribers

People subscribed via source and target branches