diff options
Diffstat (limited to 'scripts/image-build')
-rwxr-xr-x | scripts/image-build/build-vyos-image | 140 | ||||
-rw-r--r-- | scripts/image-build/utils.py | 7 |
2 files changed, 96 insertions, 51 deletions
diff --git a/scripts/image-build/build-vyos-image b/scripts/image-build/build-vyos-image index a0acd184..a9294f38 100755 --- a/scripts/image-build/build-vyos-image +++ b/scripts/image-build/build-vyos-image @@ -95,7 +95,7 @@ else: import utils import raw_image -from utils import cmd +from utils import cmd, rc_cmd ## Check if there are missing build dependencies deps = { @@ -192,7 +192,8 @@ if __name__ == "__main__": 'vyos-mirror': ('VyOS package mirror', None), 'build-type': ('Build type, release or development', lambda x: x in ['release', 'development']), 'version': ('Version number (release builds only)', None), - 'build-comment': ('Optional build comment', None) + 'build-comment': ('Optional build comment', None), + 'build-hook-opts': ('Custom options for the post-build hook', None) } # Create the option parser @@ -336,6 +337,17 @@ if __name__ == "__main__": if type(build_config["image_format"]) != list: build_config["image_format"] = [ build_config["image_format"] ] + ## If the user didn't explicitly specify what extensions build artifact should have, + ## assume that the list is the same as image formats. + ## One case when it's not the same is when a custom build hook is used + ## to build a format that our build script doesn't support natively. + if not has_nonempty_key(build_config, "artifact_format"): + build_config["artifact_format"] = build_config["image_format"] + else: + # If the option is there, also make it list if it's a scalar + if type(build_config["artifact_format"]) != list: + build_config["artifact_format"] = [ build_config["artifact_format"] ] + ## Dump the complete config if the user enabled debug mode if debug: import json @@ -355,10 +367,18 @@ if __name__ == "__main__": # not to the vyos-build repository root. os.chdir(defaults.BUILD_DIR) + ## Initialize build manifest + manifest = { + 'build_config' : build_config, + 'artifacts' : [], + 'pre_build_config' : pre_build_config + } + iso_file = None if build_config["reuse_iso"]: iso_file = build_config["reuse_iso"] + manifest["artifacts"] = [iso_file] else: ## Create the version file @@ -412,11 +432,6 @@ if __name__ == "__main__": # Release build, use the version from ./configure arguments version = build_config['version'] - if build_config['build_type'] == 'development': - lts_build = False - else: - lts_build = True - version_data = { 'version': version, 'flavor': build_config["build_flavor"], @@ -427,7 +442,7 @@ if __name__ == "__main__": 'build_branch': git_branch, 'release_train': build_config['release_train'], 'architecture': build_config['architecture'], - 'lts_build': lts_build, + 'build_type': build_config['build_type'], 'build_comment': build_config['build_comment'], 'bugtracker_url': build_config['bugtracker_url'], 'documentation_url': build_config['documentation_url'], @@ -437,19 +452,18 @@ if __name__ == "__main__": # Multi line strings needs to be un-indented to not have leading # whitespaces in the resulting file - os_release = f""" - PRETTY_NAME="VyOS {version} ({build_config['release_train']})" - NAME="VyOS" - VERSION_ID="{version}" - VERSION="{version} ({build_config['release_train']})" - VERSION_CODENAME={build_defaults['debian_distribution']} - ID=vyos - BUILD_ID="{build_git}" - HOME_URL="{build_defaults['website_url']}" - SUPPORT_URL="{build_defaults['support_url']}" - BUG_REPORT_URL="{build_defaults['bugtracker_url']}" - DOCUMENTATION_URL="{build_config['documentation_url']}" - """ + os_release = f"""PRETTY_NAME="VyOS {version} ({build_config['release_train']})" +NAME="VyOS" +VERSION_ID="{version}" +VERSION="{version} ({build_config['release_train']})" +VERSION_CODENAME={build_defaults['debian_distribution']} +ID=vyos +BUILD_ID="{build_git}" +HOME_URL="{build_defaults['website_url']}" +SUPPORT_URL="{build_defaults['support_url']}" +BUG_REPORT_URL="{build_defaults['bugtracker_url']}" +DOCUMENTATION_URL="{build_config['documentation_url']}" +""" # Reminder: all paths relative to the build dir, not to the repository root chroot_includes_dir = defaults.CHROOT_INCLUDES_DIR @@ -469,8 +483,8 @@ if __name__ == "__main__": print("Version: {0}".format(version), file=f) # Define variables that influence to welcome message on boot - os.makedirs(os.path.join(chroot_includes_dir, 'usr/lib/'), exist_ok=True) - with open(os.path.join(chroot_includes_dir, 'usr/lib/os-release'), 'w') as f: + os.makedirs(os.path.join(chroot_includes_dir, 'etc/'), exist_ok=True) + with open(os.path.join(chroot_includes_dir, 'etc/os-release'), 'w') as f: print(os_release, file=f) ## Clean up earlier build state and artifacts @@ -548,13 +562,6 @@ if __name__ == "__main__": with open(file_path, 'w') as f: f.write(build_config["default_config"]) - ## Initialize build manifest - manifest = { - 'build_config' : build_config, - 'artifacts' : [iso_file], - 'pre_build_config' : pre_build_config - } - ## Configure live-build lb_config_tmpl = jinja2.Template(""" lb config noauto \ @@ -571,7 +578,7 @@ if __name__ == "__main__": --checksums 'sha256 md5' \ --chroot-squashfs-compression-type "{{squashfs_compression_type}}" \ --debian-installer none \ - --debootstrap-options "--variant=minbase --exclude=isc-dhcp-client,isc-dhcp-common,ifupdown --include=apt-utils,ca-certificates,gnupg2" \ + --debootstrap-options "--variant=minbase --exclude=isc-dhcp-client,isc-dhcp-common,ifupdown --include=apt-utils,ca-certificates,gnupg2,linux-kbuild-6.1" \ --distribution {{debian_distribution}} \ --firmware-binary false \ --firmware-chroot false \ @@ -586,7 +593,8 @@ if __name__ == "__main__": --mirror-chroot {{debian_mirror}} \ --mirror-chroot-security {{debian_security_mirror}} \ --security true \ - --updates true + --updates true \ + --utc-time true "${@}" """) @@ -629,29 +637,59 @@ Pin-Priority: 600 # Copy the image shutil.copy("live-image-{0}.hybrid.iso".format(build_config["architecture"]), iso_file) - # Build additional flavors from the ISO, - # if the flavor calls for them + # Add the image to the manifest + manifest['artifacts'].append(iso_file) + + # If the flavor has `image_format = "iso"`, then the work is done. + # If not, build additional flavors from the ISO. if build_config["image_format"] != ["iso"]: + # For all non-iso formats, we always build a raw image first raw_image = raw_image.create_raw_image(build_config, iso_file, "tmp/") manifest['artifacts'].append(raw_image) - if has_nonempty_key(build_config, "post_build_hook"): - # Some flavors require special procedures that aren't covered by qemu-img - # (most notably, the VMware OVA that requires a custom tool to make and sign the image). - # For those cases, we support running a post-build hook on the raw image. - # The image_format field should be 'raw' if a post-build hook is used. - hook_path = build_config["post_build_hook"] - cmd(f"{hook_path} {raw_image}") - else: - # Most other formats, thankfully, can be produced with just `qemu-img convert` - other_formats = filter(lambda x: x not in ["iso", "raw"], build_config["image_format"]) - for f in other_formats: - image_ext = build_config.get("image_ext", f) - image_opts = build_config.get("image_opts", "") - target = f"{os.path.splitext(raw_image)[0]}.{image_ext}" - print(f"I: Building {f} file {target}") - cmd(f"qemu-img convert -f raw -O {f} {image_opts} {raw_image} {target}") - manifest['artifacts'].append(target) + # If there are other formats in the flavor, the assumptions is that + # they can be produced from a raw image with `qemu-img convert` + other_formats = filter(lambda x: x not in ["iso", "raw"], build_config["image_format"]) + for f in other_formats: + image_ext = build_config.get("image_ext", f) + image_opts = build_config.get("image_opts", "") + target = f"{os.path.splitext(raw_image)[0]}.{image_ext}" + print(f"I: Building {f} file {target}") + cmd(f"qemu-img convert -f raw -O {f} {image_opts} {raw_image} {target}") + manifest['artifacts'].append(target) + + # Finally, there are formats that require special procedures. + # For example, a VMware OVA needs a custom tool for image building and signing. + # For those cases, we support custom build hooks that run on raw images. + # A post-build hook is a script embedded in the flavor file + # that must return the artifact name via stdout. + if has_nonempty_key(build_config, "build_hook"): + hook_source = build_config["build_hook"] + + with open('build_hook', 'w') as f: + f.write(hook_source) + os.chmod('build_hook', 0o755) + + if has_nonempty_key(build_config, "build_hook_opts"): + hook_opts = build_config["build_hook_opts"] + else: + hook_opts = "" + custom_image = rc_cmd(f"./build_hook {raw_image} {build_config['version']} \ + {build_config['architecture']} {hook_opts}") + manifest['artifacts'].append(custom_image) + + # Filter out unwanted files from the artifact list + # and leave only those the user specified + # in either `artifact_format` or `image_format`. + # + # For example, with `image_format = "raw"`, + # the ISO image is just an intermediate object, not an target artifact. + + # os.path.splitext returns extensions with dots, + # so we need to remove the dots, hence [1:] + is_artifact = lambda f: os.path.splitext(f)[-1][1:] in build_config['artifact_format'] + + manifest['artifacts'] = list(filter(is_artifact, manifest['artifacts'])) with open('manifest.json', 'w') as f: f.write(json.dumps(manifest)) diff --git a/scripts/image-build/utils.py b/scripts/image-build/utils.py index 7002b281..081327cd 100644 --- a/scripts/image-build/utils.py +++ b/scripts/image-build/utils.py @@ -82,3 +82,10 @@ def cmd(command): res = vyos.utils.process.call(command, shell=True) if res > 0: raise OSError(f"Command '{command}' failed") + +def rc_cmd(command): + code, out = vyos.utils.process.rc_cmd(command, shell=True) + if code > 0: + raise OSError(f"Command '{command}' failed") + else: + return out |