33
34
from cdimage.build_id import next_build_id
34
35
from cdimage.check_installable import check_installable
35
36
from cdimage.germinate import Germination
36
from cdimage.livefs import download_live_filesystems, live_output_directory
37
from cdimage.livefs import (
39
download_live_filesystems,
40
live_output_directory,
37
43
from cdimage.log import logger, reset_logging
38
44
from cdimage.mail import get_notify_addresses, send_mail
39
45
from cdimage.mirror import find_mirror, trigger_mirrors
40
46
from cdimage.semaphore import Semaphore
47
from cdimage.tracker import tracker_set_rebuild_status
41
48
from cdimage.tree import Publisher, Tree
49
from cdimage.config import Touch
44
52
@contextlib.contextmanager
119
133
def log_marker(message):
120
134
logger.info("===== %s =====" % message)
121
logger.info(time.strftime("%a %b %e %H:%M:%S %Z %Y", time.gmtime()))
135
logger.info(time.strftime("%a %b %e %H:%M:%S UTC %Y", time.gmtime()))
138
def want_live_builds(options):
139
return options is not None and getattr(options, "live", False)
142
def _anonftpsync_config_path(config):
143
if config["ANONFTPSYNC_CONF"]:
144
return config["ANONFTPSYNC_CONF"]
146
os.path.join(config.root, "production", "anonftpsync"),
147
os.path.join(config.root, "etc", "anonftpsync"),
150
if os.path.exists(path):
156
def _anonftpsync_options(config):
158
path = _anonftpsync_config_path(config)
168
for key, value in osextras.read_shell_config(path, whitelisted_keys):
169
if key.startswith("RSYNC_"):
171
if "RSYNC_SRC" not in env:
173
"RSYNC_SRC not configured! Edit %s or %s and try again." % (
174
os.path.join(config.root, "production", "anonftpsync"),
175
os.path.join(config.root, "etc", "anonftpsync")))
179
def anonftpsync(config):
180
env = dict(os.environ)
181
for key, value in _anonftpsync_options(config).items():
183
target = os.path.join(config.root, "ftp")
184
fqdn = socket.getfqdn()
185
lock_base = "Archive-Update-in-Progress-%s" % fqdn
186
lock = os.path.join(target, lock_base)
188
["lockfile", "-!", "-l", "43200", "-r", "0", lock]) == 0:
190
"%s is unable to start rsync; lock file exists." % fqdn)
192
log_path = os.path.join(config.root, "log", "rsync.log")
193
osextras.ensuredir(os.path.dirname(log_path))
194
with open(log_path, "w") as log:
196
"rsync", "--recursive", "--links", "--hard-links", "--times",
197
"--verbose", "--stats", "--chmod=Dg+s,g+rwX",
198
"--exclude", lock_base,
199
"--exclude", "project/trace/%s" % fqdn,
201
exclude = env.get("RSYNC_EXCLUDE", "").split()
202
source_target = ["%s/" % env["RSYNC_SRC"], "%s/" % target]
206
"--exclude", "Packages*", "--exclude", "Sources*",
207
"--exclude", "Release*", "--exclude", "InRelease",
208
] + exclude + source_target,
209
stdout=log, stderr=subprocess.STDOUT, env=env)
211
# Second pass to update metadata and clean up old files.
214
"--delay-updates", "--delete", "--delete-after",
215
] + exclude + source_target,
216
stdout=log, stderr=subprocess.STDOUT, env=env)
218
# Delete dangling symlinks.
219
for dirpath, _, filenames in os.walk(target):
220
for filename in filenames:
221
path = os.path.join(dirpath, filename)
222
if os.path.islink(path) and not os.path.exists(path):
225
trace_dir = os.path.join(target, "project", "trace")
226
osextras.ensuredir(trace_dir)
227
with open(os.path.join(trace_dir, fqdn), "w") as trace:
228
subprocess.check_call(["date", "-u"], stdout=trace)
230
# Note: if you don't have savelog, use any other log rotation
231
# facility, or comment this out, the log will simply be overwritten
233
with open("/dev/null", "w") as devnull:
235
["savelog", log_path],
236
stdout=devnull, stderr=subprocess.STDOUT)
238
osextras.unlink_force(lock)
124
241
def sync_local_mirror(config, semaphore_state):
305
422
[pi_makelist, entry_path], stdout=list_file)
425
def add_android_support(config, arch, output_dir):
426
"""Copy Android support files to an Ubuntu Touch image.
428
live_scratch_dir = os.path.join(
429
config.root, "scratch", config.project, config.full_series,
430
config.image_type, "live")
432
# copy recovery, boot and system imgs in place
433
for target in Touch.list_targets_by_ubuntu_arch(arch):
434
boot_img_src = "boot-%s+%s.img" % (target.ubuntu_arch, target.subarch)
435
boot_img = "%s-preinstalled-boot-%s+%s.img" % (
436
config.series, arch, target.subarch)
437
system_img_src = "system-%s+%s.img" % (
438
target.android_arch, target.subarch)
439
system_img = "%s-preinstalled-system-%s+%s.img" % (
440
config.series, target.android_arch, target.subarch)
441
recovery_img_src = "recovery-%s+%s.img" % (
442
target.android_arch, target.subarch)
443
recovery_img = "%s-preinstalled-recovery-%s+%s.img" % (
444
config.series, target.android_arch, target.subarch)
447
os.path.join(live_scratch_dir, boot_img_src),
448
os.path.join(output_dir, boot_img))
450
os.path.join(live_scratch_dir, system_img_src),
451
os.path.join(output_dir, system_img))
453
os.path.join(live_scratch_dir, recovery_img_src),
454
os.path.join(output_dir, recovery_img))
457
def build_livecd_base(config):
458
log_marker("Downloading live filesystem images")
459
download_live_filesystems(config)
461
if (config.project in ("ubuntu-core", "ubuntu-touch") or
462
(config.project == "ubuntu-desktop-next" and
463
config.subproject == "system-image")):
464
log_marker("Copying images to debian-cd output directory")
465
scratch_dir = os.path.join(
466
config.root, "scratch", config.project, config.full_series,
468
live_dir = os.path.join(scratch_dir, "live")
469
for arch in config.arches:
470
live_prefix = os.path.join(live_dir, arch)
471
rootfs = "%s.rootfs.tar.gz" % live_prefix
472
if os.path.exists(rootfs):
473
output_dir = os.path.join(scratch_dir, "debian-cd", arch)
474
osextras.ensuredir(output_dir)
475
if config.project == "ubuntu-core":
476
if config.image_type == "daily-preinstalled":
477
output_prefix = os.path.join(
479
"%s-preinstalled-core-%s" % (config.series, arch))
481
output_prefix = os.path.join(
482
output_dir, "%s-core-%s" % (config.series, arch))
483
elif config.project == "ubuntu-touch":
484
output_prefix = os.path.join(
486
"%s-preinstalled-touch-%s" % (config.series, arch))
487
elif config.project == "ubuntu-desktop-next":
488
if config.image_type == "daily-preinstalled":
489
output_prefix = os.path.join(
491
"%s-preinstalled-desktop-next-%s" %
492
(config.series, arch))
494
output_prefix = os.path.join(
495
output_dir, "%s-desktop-next-%s" %
496
(config.series, arch))
497
shutil.copy2(rootfs, "%s.raw" % output_prefix)
498
with open("%s.type" % output_prefix, "w") as f:
499
print("tar archive", file=f)
501
"%s.manifest" % live_prefix, "%s.manifest" % output_prefix)
502
if config.project == "ubuntu-touch":
504
"%s.raw" % output_prefix, "%s.tar.gz" % output_prefix)
505
add_android_support(config, arch, output_dir)
506
custom = "%s.custom.tar.gz" % live_prefix
507
if os.path.exists(custom):
509
custom, "%s.custom.tar.gz" % output_prefix)
510
if config.project in ("ubuntu-core", "ubuntu-desktop-next"):
511
for dev in ("azure.device", "device"):
512
device = "%s.%s.tar.gz" % (live_prefix, dev)
513
if os.path.exists(device):
515
device, "%s.%s.tar.gz" % (output_prefix, dev))
308
518
def _debootstrap_script(config):
309
519
if config["DIST"] <= "gutsy":
310
520
return "usr/lib/debootstrap/scripts/%s" % config.series
450
673
configure_for_project(config)
451
674
log_path = open_log(config)
453
sync_local_mirror(config, semaphore_state)
456
log_marker("Updating archive of local packages")
457
update_local_indices(config)
459
build_britney(config)
461
log_marker("Extracting debootstrap scripts")
462
extract_debootstrap(config)
676
if want_live_builds(options):
677
log_marker("Building live filesystems")
678
live_successful = run_live_builds(config)
679
config.limit_arches(live_successful)
681
tracker_set_rebuild_status(config, [0, 1], 2)
683
if not is_live_fs_only(config):
684
sync_local_mirror(config, semaphore_state)
687
log_marker("Updating archive of local packages")
688
update_local_indices(config)
690
build_britney(config)
692
log_marker("Extracting debootstrap scripts")
693
extract_debootstrap(config)
464
695
if config["UBUNTU_DEFAULTS_LOCALE"]:
465
696
build_ubuntu_defaults_locale(config)
697
elif is_live_fs_only(config):
698
build_livecd_base(config)
467
700
if not config["CDIMAGE_PREINSTALLED"]:
468
701
log_marker("Germinating")
496
729
if not config["DEBUG"] and not config["CDIMAGE_NOPUBLISH"]:
497
730
log_marker("Publishing")
498
731
tree = Tree.get_daily(config)
499
Publisher.get_daily(tree, image_type).publish(date)
732
publisher = Publisher.get_daily(tree, image_type)
733
publisher.publish(date)
501
735
log_marker("Purging old images")
502
subprocess.check_call(["purge-old-images", image_type])
504
738
log_marker("Triggering mirrors")
505
739
trigger_mirrors(config)
507
741
log_marker("Finished")
743
except Exception as e:
510
744
for line in traceback.format_exc().splitlines():
511
745
logger.error(line)
512
746
sys.stdout.flush()
513
747
sys.stderr.flush()
514
notify_failure(config, log_path)
748
if not isinstance(e, LiveBuildsFailed):
749
notify_failure(config, log_path)
518
def build_image_set(config):
753
def build_image_set(config, options):
519
754
"""Master entry point for building images."""
520
755
semaphore = Semaphore(
521
756
os.path.join(config.root, "etc", ".sem-build-image-set"))
522
757
with lock_build_image_set(config), semaphore.held() as semaphore_state:
523
return build_image_set_locked(config, semaphore_state)
758
return build_image_set_locked(config, options, semaphore_state)