From: Georges Discry Date: Fri, 22 Sep 2023 13:10:58 +0000 (+0200) Subject: Immediately change directory on -C/--directory X-Git-Tag: v18~43 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=816ff21a8bbdd47012f5b64a00425917137bc8a3;p=thirdparty%2Fmkosi.git Immediately change directory on -C/--directory Common utilities (e.g., make and tar) that have a `-C`/`--directory` option immediately change the working directory when that option is parsed, with the following properties: - the option is order sensitive and only applies to the following options - the option can be used multiple times and each is interpreted relative to the previous one In addition, mkosi uses an empty path for `-C`/`--directory` to indicate that the configuration files in the current directory should be ignored. At first, mkosi would call `os.chdir()` after parsing the command-line arguments, so the following options with relative paths would resolve relative to the initial working directory and not the new one. This issue was reported in #1879. The fix in #1880 reversed that approach. A first (and lighter) pass on the command-line arguments would get the last `-C`/`--directory` and call `os.chdir()`. Afterwards, all the command-line arguments were fully parsed so that the relative paths would resolve to that directory. Neither approach implements the properties above. Only the last value is used and applies to none/all of the command-line arguments. By calling `os.chdir()` as the options are parsed, both properties are respected and the arguments can be parsed in a single-pass. The resulting directory is still tracked in `args.directory` but as a `Path` object defaulting to `Path.cwd()`, with `None` indicating that the configuration files should be ignored. Furthermore, it's now possible to call `mkosi -C -C ''` to work in a given directory (for the output and workspace) while also ignoring the configuration files present in that directory. --- diff --git a/mkosi/config.py b/mkosi/config.py index e91da6739..cdbec1888 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -556,6 +556,23 @@ class CustomHelpFormatter(argparse.HelpFormatter): subsequent_indent=subindent) for line in lines) +def parse_chdir(path: str) -> Optional[Path]: + if not path: + # The current directory should be ignored + return None + + # Immediately change the current directory so that it's taken into + # account when parsing the following options that take a relative path + try: + os.chdir(path) + except (FileNotFoundError, NotADirectoryError): + die(f"{path} is not a directory!") + except OSError as e: + die(f"Cannot change the directory to {path}: {e}") + # Keep track of the current directory + return Path.cwd() + + def config_make_action(settings: Sequence[MkosiConfigSetting]) -> type[argparse.Action]: lookup = {s.dest: s for s in settings} @@ -1703,7 +1720,7 @@ MATCHES = ( ) -def create_argument_parser(*, settings: bool) -> argparse.ArgumentParser: +def create_argument_parser() -> argparse.ArgumentParser: action = config_make_action(SETTINGS) parser = argparse.ArgumentParser( @@ -1747,9 +1764,10 @@ def create_argument_parser(*, settings: bool) -> argparse.ArgumentParser: ) parser.add_argument( "-C", "--directory", + type=parse_chdir, + default=Path.cwd(), help="Change to specified directory before doing anything", metavar="PATH", - default=None, ) parser.add_argument( "--debug", @@ -1811,9 +1829,6 @@ def create_argument_parser(*, settings: bool) -> argparse.ArgumentParser: help=argparse.SUPPRESS, ) - if not settings: - return parser - parser.add_argument( "verb", type=Verb, @@ -2051,19 +2066,8 @@ def parse_config(argv: Sequence[str] = ()) -> tuple[MkosiArgs, tuple[MkosiConfig else: argv += ["--", "build"] - # Don't parse the settings just yet so we can take --directory into account when parsing settings that - # take relative paths. - argparser = create_argument_parser(settings=False) - argparser.parse_known_args(argv, namespace) - - if namespace.directory and not Path(namespace.directory).is_dir(): - die(f"{namespace.directory} is not a directory!") - - if namespace.directory: - os.chdir(namespace.directory) - namespace = argparse.Namespace() - argparser = create_argument_parser(settings=True) + argparser = create_argument_parser() argparser.parse_args(argv, namespace) args = load_args(namespace) @@ -2076,7 +2080,7 @@ def parse_config(argv: Sequence[str] = ()) -> tuple[MkosiArgs, tuple[MkosiConfig include = () - if args.directory != "": + if args.directory is not None: parse_config(Path("."), namespace, defaults) include = getattr(namespace, "presets", ()) @@ -2126,7 +2130,7 @@ def load_credentials(args: argparse.Namespace) -> dict[str, str]: } d = Path("mkosi.credentials") - if args.directory != "" and d.is_dir(): + if args.directory is not None and d.is_dir(): for e in d.iterdir(): if os.access(e, os.X_OK): creds[e.name] = run([e], stdout=subprocess.PIPE, env=os.environ).stdout