update flake lock, add flake diff, add evaluation json

Signed-off-by: ahuston-0 <aliceghuston@gmail.com>
This commit is contained in:
2025-03-07 22:26:18 -05:00
parent a755a0c792
commit e2c127a012
8 changed files with 150 additions and 55 deletions

View File

@ -13,4 +13,32 @@ def parse_inputs() -> argparse.Namespace:
parser.add_argument("--keep-hydra", action="store_true", help="retain Hydra jobs")
parser.add_argument("--build", action="store_true", help="allow building Hydra jobs")
parser.add_argument("--evaluate", action="store_true", help="allow evaluating Hydra jobs")
return parser.parse_args()
parser.add_argument(
"--json", metavar="json-path", help="whether or not to output evaluations to a json"
)
parser.add_argument(
"--compare-drvs",
action="store_true",
help="whether to compare two drv sets, must provide two evaluation jsons to compare",
)
parser.add_argument(
"--compare-pre-json",
metavar="pre-json-path",
default=None,
help="location of pre.json for comparison. defaults to <flake_path>/pre.json",
)
parser.add_argument(
"--compare-post-json",
metavar="post-json-path",
default=None,
help="location of post.json for comparison. defaults to <flake_path>/post.json",
)
args = parser.parse_args()
if args.compare_pre_json is None:
args.compare_pre_json = args.flake_path + "/pre.json"
if args.compare_post_json is None:
args.compare_post_json = args.flake_path + "/post.json"
return args

View File

@ -4,11 +4,14 @@ from __future__ import annotations
import logging
import re
from tempfile import mkdtemp
from flupdt.common import bash_wrapper
drv_re = re.compile(r".*(/nix/store/.*\.drv).*")
OUTPUT_DIR = mkdtemp(prefix="flupdt-outputs-")
def build_output(path: str, output: str) -> str | None:
"""Builds a given output in a flake.
@ -18,7 +21,8 @@ def build_output(path: str, output: str) -> str | None:
:returns the .drv path on success or None on failure
"""
logging.info(f"build {output}")
out = bash_wrapper(f"nix build {path}#{output} -o {output}.nixoutput")
logging.debug(f"outputting to {OUTPUT_DIR}/{output}.nixoutput")
out = bash_wrapper(f"nix build {path}#{output} -o {OUTPUT_DIR}/{output}.nixoutput")
logging.debug("output")
logging.debug(out[0])
logging.debug("error")

30
flupdt/flake_diff.py Normal file
View File

@ -0,0 +1,30 @@
"""Utility to diff nix derivations."""
import logging
import shutil
from flupdt.common import bash_wrapper
def compare_derivations(
path_to_flake: str, path_to_pre_drv: str, path_to_post_drv: str
) -> list[str]:
"""Gets all derivations present in a flake.
:param path_to_flake: path to flake to be checked
:returns a list of all valid derivations in the flake
:raises RuntimeError: fails if nix is not present in the PATH
"""
nvd_path = shutil.which("nvd")
if nvd_path is None:
status_msg = "nvd is not available in the PATH, please verify that it is installed"
raise RuntimeError(status_msg)
diff_output = bash_wrapper(
f"{nvd_path} diff {path_to_pre_drv} {path_to_post_drv}", path=path_to_flake
)
logging.debug(diff_output[0])
logging.debug(diff_output[1])
logging.debug(diff_output[2])
return diff_output

View File

@ -32,3 +32,5 @@ def evaluate_output(path: str, output: str) -> str | None:
raise RuntimeError(out_msg)
drv = drv_match.group(1)
logging.debug(f"derivation evaluated to {drv}")
return drv

View File

@ -4,10 +4,12 @@
import logging
from argparse import Namespace
from pathlib import Path
from flupdt.cli import parse_inputs
from flupdt.common import configure_logger, partition
from flupdt.flake_build import build_output
from flupdt.flake_diff import compare_derivations
from flupdt.flake_eval import evaluate_output
from flupdt.flake_show import get_derivations
@ -20,11 +22,17 @@ def batch_eval(args: Namespace, flake_path: str, derivations: list[str]) -> None
:params derivations: list of derivations to run against
:returns None
"""
drv_map = {}
for d in derivations:
if args.evaluate:
evaluate_output(flake_path, d)
drv_map[d] = evaluate_output(flake_path, d)
if args.build:
build_output(flake_path, d)
if args.json:
with Path.open(args.json, "w+") as f:
from json import dump
dump(drv_map, f)
def main() -> None:
@ -35,18 +43,54 @@ def main() -> None:
"""
configure_logger("DEBUG")
args = parse_inputs()
flake_path = args.flake_path
derivations, hydra_jobs = partition(
lambda s: s.startswith("hydraJobs"), get_derivations(flake_path)
)
derivations, hydra_jobs = list(derivations), list(hydra_jobs)
logging.info(f"derivations: {list(derivations)}")
batch_eval(args, flake_path, derivations)
if args.compare_drvs:
pre_json_dict = {}
post_json_dict = {}
from json import load
with (
Path.open(args.compare_pre_json, "r") as pre,
Path.open(args.compare_post_json, "r") as post,
):
pre_json_dict = load(pre)
post_json_dict = load(post)
logging.debug(f"pre-snapshot derivations: {pre_json_dict}")
logging.debug(f"post-snapshot derivations: {post_json_dict}")
pre_json_keys = set(pre_json_dict.keys())
post_json_keys = set(post_json_dict.keys())
common_keys_to_eval = pre_json_keys.union(post_json_keys)
missing_post_keys = pre_json_keys.difference(common_keys_to_eval)
missing_pre_keys = post_json_keys.difference(common_keys_to_eval)
if missing_pre_keys:
logging.warning(f"Following outputs are missing from pre-snapshot: {missing_pre_keys}")
if missing_post_keys:
logging.warning(f"Following outputs are missing from post-snapshot: {missing_post_keys}")
logging.info(f"Evaluating the following outputs for differences: {common_keys_to_eval}")
for output_key in common_keys_to_eval:
compare_derivations(args.flake_path, pre_json_dict[output_key], post_json_dict[output_key])
if not args.keep_hydra:
logging.info("--keep-hydra flag is not specified, removing Hydra jobs")
else:
batch_eval(args, flake_path, hydra_jobs)
flake_path = args.flake_path
derivations, hydra_jobs = partition(
lambda s: s.startswith("hydraJobs"), get_derivations(flake_path)
)
derivations, hydra_jobs = list(derivations), list(hydra_jobs)
logging.info(f"derivations: {list(derivations)}")
batch_eval(args, flake_path, derivations)
if not args.keep_hydra:
logging.info("--keep-hydra flag is not specified, removing Hydra jobs")
else:
batch_eval(args, flake_path, hydra_jobs)
if __name__ == "__main__":