at master 3.4 kB view raw
1#!/usr/bin/env python 2 3"""Amend systemd-repart definiton files. 4 5In order to avoid Import-From-Derivation (IFD) when building images with 6systemd-repart, the definition files created by Nix need to be amended with the 7store paths from the closure. 8 9This is achieved by adding CopyFiles= instructions to the definition files. 10 11The arbitrary files configured via `contents` are also added to the definition 12files using the same mechanism. 13""" 14 15import json 16import sys 17import shutil 18from pathlib import Path 19 20 21def add_contents_to_definition( 22 definition: Path, contents: dict[str, dict[str, str]] | None 23) -> None: 24 """Add CopyFiles= instructions to a definition for all files in contents.""" 25 if not contents: 26 return 27 28 copy_files_lines: list[str] = [] 29 for target, options in contents.items(): 30 source = options["source"] 31 32 copy_files_lines.append(f"CopyFiles={source}:{target}\n") 33 34 with open(definition, "a") as f: 35 f.writelines(copy_files_lines) 36 37 38def add_closure_to_definition( 39 definition: Path, closure: Path | None, nix_store_prefix: str | None 40) -> None: 41 """Add CopyFiles= instructions to a definition for all paths in the closure. 42 43 Replace `/nix/store` with the value of nix_store_prefix. 44 """ 45 if not closure: 46 return 47 48 copy_files_lines: list[str] = [] 49 with open(closure, "r") as f: 50 for line in f: 51 if not isinstance(line, str): 52 continue 53 54 source = Path(line.strip()) 55 option = f"CopyFiles={source}" 56 if nix_store_prefix: 57 target = nix_store_prefix / source.relative_to("/nix/store/") 58 option = f"{option}:{target}" 59 60 copy_files_lines.append(f"{option}\n") 61 62 with open(definition, "a") as f: 63 f.writelines(copy_files_lines) 64 65 66def main() -> None: 67 """Amend the provided repart definitions by adding CopyFiles= instructions. 68 69 For each file specified in the `contents` field of a partition in the 70 partiton config file, a `CopyFiles=` instruction is added to the 71 corresponding definition file. 72 73 The same is done for every store path of the `closure` field. 74 75 Print the path to a directory that contains the amended repart 76 definitions to stdout. 77 """ 78 partition_config_file = sys.argv[1] 79 if not partition_config_file: 80 print("No partition config file was supplied.") 81 sys.exit(1) 82 83 repart_definitions = sys.argv[2] 84 if not repart_definitions: 85 print("No repart definitions were supplied.") 86 sys.exit(1) 87 88 with open(partition_config_file, "rb") as f: 89 partition_config = json.load(f) 90 91 if not partition_config: 92 print("Partition config is empty.") 93 sys.exit(1) 94 95 target_dir = Path("amended-repart.d") 96 target_dir.mkdir() 97 shutil.copytree(repart_definitions, target_dir, dirs_exist_ok=True) 98 99 for name, config in partition_config.items(): 100 definition = target_dir.joinpath(f"{name}.conf") 101 definition.chmod(0o644) 102 103 contents = config.get("contents") 104 add_contents_to_definition(definition, contents) 105 106 closure = config.get("closure") 107 nix_store_prefix = config.get("nixStorePrefix") 108 add_closure_to_definition(definition, closure, nix_store_prefix) 109 110 print(target_dir.absolute()) 111 112 113if __name__ == "__main__": 114 main()