diff --git a/README.md b/README.md index 14d934f8..d2eb0f10 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,13 @@ nix-shell> nixpkgs-review comments `nixpkgs-review` will by default use [nix-output-monitor](https://github.com/maralorn/nix-output-monitor) if found in `$PATH`. If you have `nom` installed but don't want to use it, you can pass `nix` to `--build-graph` to use `nix build` instead of `nom build`. +Some pull requests may require configuration for nixpkgs to test out. You can +use the `--extra-nixpkgs-config` flag to supply extra configuration for nixpkgs. + +```console +$ nixpkgs-review pr 37242 --extra-nixpkgs-config '{ cudaSupport = true; }' +``` + ## Using nixpkgs-review in scripts or other programs After building, `nixpkgs-review` will normally start a `nix-shell` with the diff --git a/nixpkgs_review/buildenv.py b/nixpkgs_review/buildenv.py index f73a2584..17e264cc 100644 --- a/nixpkgs_review/buildenv.py +++ b/nixpkgs_review/buildenv.py @@ -1,6 +1,6 @@ -from dataclasses import dataclass import os import sys +from pathlib import Path from tempfile import NamedTemporaryFile from typing import Any, Optional @@ -20,11 +20,32 @@ def find_nixpkgs_root() -> Optional[str]: prefix.append("..") -@dataclass class Buildenv: - allow_aliases: bool + def __init__(self, allow_aliases: bool, extra_nixpkgs_config: str) -> None: + if not ( + extra_nixpkgs_config.startswith("{") and extra_nixpkgs_config.endswith("}") + ): + raise RuntimeError( + "--extra-nixpkgs-config must start with `{` and end with `}`" + ) + + self.nixpkgs_config = NamedTemporaryFile(suffix=".nix") + self.nixpkgs_config.write( + str.encode( + f"""{{ + allowUnfree = true; + allowBroken = true; + {"allowAliases = false;" if not allow_aliases else ""} + checkMeta = true; + ## TODO: also build packages marked as insecure + # allowInsecurePredicate = x: true; +}} // {extra_nixpkgs_config} +""" + ) + ) + self.nixpkgs_config.flush() - def __enter__(self) -> None: + def __enter__(self) -> Path: self.environ = os.environ.copy() self.old_cwd = os.getcwd() @@ -35,20 +56,8 @@ def __enter__(self) -> None: else: os.chdir(root) - self.nixpkgs_config = NamedTemporaryFile() - self.nixpkgs_config.write( - str.encode( - f"""{{ - allowUnfree = true; - allowBroken = true; - {"allowAliases = false;" if not self.allow_aliases else ""} - ## TODO also build packages marked as insecure - #allowInsecurePredicate = x: true; - }}""" - ) - ) - self.nixpkgs_config.flush() os.environ["NIXPKGS_CONFIG"] = self.nixpkgs_config.name + return Path(self.nixpkgs_config.name) def __exit__(self, _type: Any, _value: Any, _traceback: Any) -> None: if self.old_cwd is not None: diff --git a/nixpkgs_review/cli/__init__.py b/nixpkgs_review/cli/__init__.py index c2fd1b9c..474b67fc 100644 --- a/nixpkgs_review/cli/__init__.py +++ b/nixpkgs_review/cli/__init__.py @@ -5,6 +5,7 @@ from pathlib import Path from typing import Any, List, Optional, Pattern, cast +from ..utils import current_system, nix_nom_tool from .approve import approve_command from .comments import show_comments from .merge import merge_command @@ -12,7 +13,6 @@ from .pr import pr_command from .rev import rev_command from .wip import wip_command -from ..utils import current_system, nix_nom_tool def regex_type(s: str) -> Pattern[str]: @@ -235,6 +235,12 @@ def common_flags() -> List[CommonFlag]: action="store_true", help="Print the nixpkgs-review results to stdout", ), + CommonFlag( + "--extra-nixpkgs-config", + type=str, + default="{ }", + help="Extra nixpkgs config to pass to `import `", + ), ] diff --git a/nixpkgs_review/cli/pr.py b/nixpkgs_review/cli/pr.py index 5e30cbe8..75e4d1bc 100644 --- a/nixpkgs_review/cli/pr.py +++ b/nixpkgs_review/cli/pr.py @@ -46,7 +46,9 @@ def pr_command(args: argparse.Namespace) -> str: allow = AllowedFeatures(args.allow) - with Buildenv(allow.aliases), ExitStack() as stack: + with Buildenv( + allow.aliases, args.extra_nixpkgs_config + ) as nixpkgs_config, ExitStack() as stack: for pr in prs: builddir = stack.enter_context(Builddir(f"pr-{pr}")) try: @@ -67,6 +69,8 @@ def pr_command(args: argparse.Namespace) -> str: checkout=checkout_option, sandbox=args.sandbox, build_graph=args.build_graph, + nixpkgs_config=nixpkgs_config, + extra_nixpkgs_config=args.extra_nixpkgs_config, ) contexts.append((pr, builddir.path, review.build_pr(pr))) except subprocess.CalledProcessError: diff --git a/nixpkgs_review/cli/rev.py b/nixpkgs_review/cli/rev.py index 6d6f94f1..37018399 100644 --- a/nixpkgs_review/cli/rev.py +++ b/nixpkgs_review/cli/rev.py @@ -9,8 +9,13 @@ def rev_command(args: argparse.Namespace) -> Path: allow = AllowedFeatures(args.allow) - with Buildenv(allow.aliases): + with Buildenv(allow.aliases, args.extra_nixpkgs_config) as nixpkgs_config: commit = verify_commit_hash(args.commit) return review_local_revision( - f"rev-{commit}", args, allow, commit, print_result=args.print_result + f"rev-{commit}", + args, + allow, + nixpkgs_config, + commit, + print_result=args.print_result, ) diff --git a/nixpkgs_review/cli/wip.py b/nixpkgs_review/cli/wip.py index 77c3484b..6377d7e5 100644 --- a/nixpkgs_review/cli/wip.py +++ b/nixpkgs_review/cli/wip.py @@ -9,11 +9,12 @@ def wip_command(args: argparse.Namespace) -> Path: allow = AllowedFeatures(args.allow) - with Buildenv(allow.aliases): + with Buildenv(allow.aliases, args.extra_nixpkgs_config) as nixpkgs_config: return review_local_revision( "rev-%s-dirty" % verify_commit_hash("HEAD"), args, allow, + nixpkgs_config, None, args.staged, args.print_result, diff --git a/nixpkgs_review/nix.py b/nixpkgs_review/nix.py index 495df4d2..25a6adb6 100644 --- a/nixpkgs_review/nix.py +++ b/nixpkgs_review/nix.py @@ -46,6 +46,7 @@ def nix_shell( cache_directory: Path, system: str, build_graph: str, + nixpkgs_config: Path, run: Optional[str] = None, sandbox: bool = False, ) -> None: @@ -54,7 +55,7 @@ def nix_shell( raise RuntimeError(f"{build_graph} not found in PATH") shell = cache_directory.joinpath("shell.nix") - write_shell_expression(shell, attrs, system) + write_shell_expression(shell, attrs, system, nixpkgs_config) if sandbox: args = _nix_shell_sandbox(nix_shell, shell) else: @@ -189,7 +190,6 @@ def nix_eval( json.dump(list(attrs), attr_json) eval_script = str(ROOT.joinpath("nix/evalAttrs.nix")) attr_json.flush() - allowAliases = "true" if allow.aliases else "false" cmd = [ "nix", "--extra-experimental-features", @@ -203,7 +203,7 @@ def nix_eval( if allow.ifd else "--no-allow-import-from-derivation", "--expr", - f"(import {eval_script} {{ allowAliases = {allowAliases}; attr-json = {attr_json.name}; }})", + f"(import {eval_script} {{ attr-json = {attr_json.name}; }})", ] try: @@ -231,6 +231,7 @@ def nix_build( system: str, allow: AllowedFeatures, build_graph: str, + nixpkgs_config: Path, ) -> List[Attr]: if not attr_names: info("Nothing to be built.") @@ -246,7 +247,7 @@ def nix_build( return attrs build = cache_directory.joinpath("build.nix") - write_shell_expression(build, filtered, system) + write_shell_expression(build, filtered, system, nixpkgs_config) command = [ build_graph, @@ -280,10 +281,12 @@ def nix_build( return attrs -def write_shell_expression(filename: Path, attrs: List[str], system: str) -> None: +def write_shell_expression( + filename: Path, attrs: List[str], system: str, nixpkgs_config: Path +) -> None: with open(filename, "w+") as f: f.write( - f"""{{ pkgs ? import ./nixpkgs {{ system = \"{system}\"; }} }}: + f"""{{ pkgs ? import ./nixpkgs {{ system = \"{system}\"; config = import {nixpkgs_config}; }} }}: with pkgs; let paths = [ diff --git a/nixpkgs_review/nix/evalAttrs.nix b/nixpkgs_review/nix/evalAttrs.nix index cb2f7806..a46dd9a3 100644 --- a/nixpkgs_review/nix/evalAttrs.nix +++ b/nixpkgs_review/nix/evalAttrs.nix @@ -1,8 +1,9 @@ -{ allowAliases ? false, attr-json }: +{ attr-json }: with builtins; let - pkgs = import { config = { checkMeta = true; allowUnfree = true; inherit allowAliases; }; }; + pkgs = import { }; + inherit (pkgs) lib; attrs = fromJSON (readFile attr-json); diff --git a/nixpkgs_review/report.py b/nixpkgs_review/report.py index dd5018c1..157af0e3 100644 --- a/nixpkgs_review/report.py +++ b/nixpkgs_review/report.py @@ -1,6 +1,6 @@ +import json import os import subprocess -import json from pathlib import Path from typing import Callable, List, Optional @@ -86,7 +86,9 @@ def write_error_logs(attrs: List[Attr], directory: Path) -> None: class Report: - def __init__(self, system: str, attrs: List[Attr]) -> None: + def __init__( + self, system: str, attrs: List[Attr], extra_nixpkgs_config: str + ) -> None: self.system = system self.attrs = attrs self.broken: List[Attr] = [] @@ -96,6 +98,11 @@ def __init__(self, system: str, attrs: List[Attr]) -> None: self.tests: List[Attr] = [] self.built: List[Attr] = [] + if extra_nixpkgs_config != "{ }": + self.extra_nixpkgs_config: Optional[str] = extra_nixpkgs_config + else: + self.extra_nixpkgs_config = None + for a in attrs: if a.broken: self.broken.append(a) @@ -134,6 +141,7 @@ def serialize_attrs(attrs: List[Attr]) -> List[str]: { "system": self.system, "pr": pr, + "extra-nixpkgs-config": self.extra_nixpkgs_config, "broken": serialize_attrs(self.broken), "non-existant": serialize_attrs(self.non_existant), "blacklisted": serialize_attrs(self.blacklisted), @@ -149,6 +157,8 @@ def markdown(self, pr: Optional[int]) -> str: cmd = "nixpkgs-review" if pr is not None: cmd += f" pr {pr}" + if self.extra_nixpkgs_config: + cmd += f" --extra-nixpkgs-config '{self.extra_nixpkgs_config}'" msg = f"Result of `{cmd}` run on {self.system} [1](https://github.com/Mic92/nixpkgs-review)\n" diff --git a/nixpkgs_review/review.py b/nixpkgs_review/review.py index 2db8948f..d81a9568 100644 --- a/nixpkgs_review/review.py +++ b/nixpkgs_review/review.py @@ -88,6 +88,8 @@ def __init__( system: str, allow: AllowedFeatures, build_graph: str, + nixpkgs_config: Path, + extra_nixpkgs_config: str, api_token: Optional[str] = None, use_ofborg_eval: Optional[bool] = True, only_packages: Set[str] = set(), @@ -113,6 +115,8 @@ def __init__( self.allow = allow self.sandbox = sandbox self.build_graph = build_graph + self.nixpkgs_config = nixpkgs_config + self.extra_nixpkgs_config = extra_nixpkgs_config def worktree_dir(self) -> str: return str(self.builddir.worktree_dir) @@ -145,6 +149,7 @@ def build_commit( Review a local git commit """ self.git_worktree(base_commit) + base_packages = list_packages( str(self.worktree_dir()), self.system, @@ -195,6 +200,7 @@ def build(self, packages: Set[str], args: str) -> List[Attr]: self.system, self.allow, self.build_graph, + self.nixpkgs_config, ) def build_pr(self, pr_number: int) -> List[Attr]: @@ -241,7 +247,7 @@ def start_review( os.environ["NIX_PATH"] = path.as_posix() if pr: os.environ["PR"] = str(pr) - report = Report(self.system, attr) + report = Report(self.system, attr, self.extra_nixpkgs_config) report.print_console(pr) report.write(path, pr) @@ -259,6 +265,7 @@ def start_review( path, self.system, self.build_graph, + self.nixpkgs_config, self.run, self.sandbox, ) @@ -500,6 +507,7 @@ def review_local_revision( builddir_path: str, args: argparse.Namespace, allow: AllowedFeatures, + nixpkgs_config: Path, commit: Optional[str], staged: bool = False, print_result: bool = False, @@ -516,6 +524,8 @@ def review_local_revision( system=args.system, allow=allow, build_graph=args.build_graph, + nixpkgs_config=nixpkgs_config, + extra_nixpkgs_config=args.extra_nixpkgs_config, ) review.review_commit(builddir.path, args.branch, commit, staged, print_result) return builddir.path