add support for flakes

This commit is contained in:
figsoda 2022-12-08 12:11:00 -05:00
parent 73f7e7f036
commit e5f57c258c
5 changed files with 144 additions and 72 deletions

View File

@ -14,6 +14,7 @@ designed to work with nixpkgs but also other package sets.
- update buildRustPackage's cargoHash/cargoSha256 and cargoSetupHook's cargoDeps
- update buildGoModule's vendorHash/vendorSha256
- update buildNpmPackage's npmDepsHash and npmConfigHook's npmDeps
- update flake outputs (see `--flake`)
- build and run the resulting package (see `--build`,
`--run` or `--shell`
- commit updated files (see `--commit` flag)

View File

@ -20,6 +20,9 @@ def parse_args(args: list[str]) -> Options:
parser = argparse.ArgumentParser()
help = "File to import rather than default.nix. Examples, ./release.nix"
parser.add_argument("-f", "--file", default="./.", help=help)
parser.add_argument(
"-F", "--flake", action="store_true", help="Update a flake attribute instead"
)
parser.add_argument("--build", action="store_true", help="build the package")
parser.add_argument(
"--test", action="store_true", help="Run package's `passthru.tests`"
@ -68,7 +71,8 @@ def parse_args(args: list[str]) -> Options:
parser.add_argument("attribute", help="Attribute name within the file evaluated")
a = parser.parse_args(args)
return Options(
import_path=a.file,
import_path=os.path.realpath(a.file),
flake=a.flake,
build=a.build,
commit=a.commit,
use_update_script=a.use_update_script,
@ -87,13 +91,25 @@ def parse_args(args: list[str]) -> Options:
def nix_shell(options: Options) -> None:
import_path = os.path.realpath(options.import_path)
expr = f"with import {import_path} {{}}; mkShell {{ buildInputs = [ {options.attribute} ]; }}"
with tempfile.TemporaryDirectory() as d:
path = os.path.join(d, "default.nix")
with open(path, "w") as f:
f.write(expr)
run(["nix-shell", path], stdout=None, check=False)
if options.flake:
run(
[
"nix",
"shell",
f"{options.import_path}#{options.attribute}",
"--extra-experimental-features",
"flakes nix-command",
],
stdout=None,
check=False,
)
else:
expr = f"with import {options.import_path} {{}}; mkShell {{ buildInputs = [ {options.attribute} ]; }}"
with tempfile.TemporaryDirectory() as d:
path = os.path.join(d, "default.nix")
with open(path, "w") as f:
f.write(expr)
run(["nix-shell", path], stdout=None, check=False)
def git_has_diff(git_dir: str, package: Package) -> bool:
@ -172,37 +188,69 @@ def validate_git_dir(import_path: str) -> str:
def nix_run(options: Options) -> None:
cmd = ["nix", "shell", "--extra-experimental-features", "nix-command"]
cmd = (
[
"nix",
"shell",
"--extra-experimental-features",
"flakes nix-command",
f"{options.import_path}#{options.attribute}",
]
if options.flake
else [
"nix",
"shell",
"--extra-experimental-features",
"nix-command",
"-f",
options.import_path,
options.attribute,
]
)
run(
cmd + ["-f", options.import_path, options.attribute],
cmd,
stdout=None,
check=False,
)
def nix_build(options: Options) -> None:
cmd = [
"nix",
"build",
"--extra-experimental-features",
"nix-command",
"-L",
"-f",
options.import_path,
options.attribute,
]
cmd = (
[
"nix",
"shell",
"--extra-experimental-features",
"flakes nix-command",
f"{options.import_path}#{options.attribute}",
]
if options.flake
else [
"nix",
"build",
"--extra-experimental-features",
"nix-command",
"-L",
"-f",
options.import_path,
options.attribute,
]
)
run(cmd, stdout=None)
def nix_test(package: Package) -> None:
def nix_test(opts: Options, package: Package) -> None:
if not package.tests:
die(f"Package '{package.name}' does not define any tests")
tests = []
for t in package.tests:
tests.append("-A")
tests.append(f"{package.attribute}.tests.{t}")
cmd = ["nix-build"] + tests
if opts.flake:
cmd = ["nix", "build", "--experimental-features", "flakes nix-command"]
for t in package.tests:
cmd.append(f"{opts.import_path}#{package.attribute}.tests.{t}")
else:
cmd = ["nix-build"]
for t in package.tests:
cmd.append("-A")
cmd.append(f"{package.attribute}.tests.{t}")
run(cmd, stdout=None)
@ -251,10 +299,13 @@ def main(args: list[str] = sys.argv[1:]) -> None:
return
if options.test:
nix_test(package)
nix_test(options, package)
if options.review:
nixpkgs_review()
if options.flake:
print("--review is unsupporetd with --flake")
else:
nixpkgs_review()
if options.format:
nixpkgs_fmt(package, git_dir)

View File

@ -53,43 +53,59 @@ class Package:
self.version_position = Position(**raw_version_position)
def eval_expression(import_path: str, attr: str) -> str:
return f"""(
let
inputs = (if (builtins.hasAttr "overlays" (builtins.functionArgs (import {import_path}))) then {{ overlays = []; }} else {{ }});
in
with import {import_path} inputs;
let
pkg = {attr};
raw_version_position = builtins.unsafeGetAttrPos "version" pkg;
def eval_expression(import_path: str, attr: str, flake: bool) -> str:
let_bindings = (
f"""inherit (builtins) currentSystem getFlake stringLength substring;
flake = getFlake "{import_path}";
pkg = flake.packages.${{currentSystem}}.{attr} or flake.{attr};
inherit (flake) outPath;
outPathLen = stringLength outPath;
sanitizePosition = {{ file, ... }}@pos:
assert substring 0 outPathLen file == outPath;
pos // {{ file = "{import_path}" + substring outPathLen (stringLength file - outPathLen) file; }};"""
if flake
else f"""
inputs = if (builtins.functionArgs (import {import_path})) ? overlays then {{ overlays = [ ]; }} else {{ }};
pkg = (import {import_path} inputs).{attr};
sanitizePosition = x: x;"""
)
position = if pkg ? isRubyGem then
raw_version_position
else
builtins.unsafeGetAttrPos "src" pkg;
in {{
name = pkg.name;
old_version = pkg.version or (builtins.parseDrvName pkg.name).version;
inherit raw_version_position;
filename = position.file;
line = position.line;
urls = pkg.src.urls or null;
url = pkg.src.url or null;
rev = pkg.src.rev or null;
hash = pkg.src.outputHash or null;
vendor_hash = pkg.vendorHash or null;
vendor_sha256 = pkg.vendorSha256 or null;
cargo_deps = (pkg.cargoDeps or null).outputHash or null;
npm_deps = (pkg.npmDeps or null).outputHash or null;
tests = builtins.attrNames (pkg.passthru.tests or {{}});
has_update_script = pkg.passthru.updateScript or null != null;
src_homepage = pkg.src.meta.homepage or null;
changelog = pkg.meta.changelog or null;
}})"""
has_update_script = (
"false" if flake else "pkg.passthru.updateScript or null != null"
)
return f"""
let
{let_bindings}
raw_version_position = sanitizePosition (builtins.unsafeGetAttrPos "version" pkg);
position = if pkg ? isRubyGem then
raw_version_position
else
sanitizePosition (builtins.unsafeGetAttrPos "src" pkg);
in {{
name = pkg.name;
old_version = pkg.version or (builtins.parseDrvName pkg.name).version;
inherit raw_version_position;
filename = position.file;
line = position.line;
urls = pkg.src.urls or null;
url = pkg.src.url or null;
rev = pkg.src.rev or null;
hash = pkg.src.outputHash or null;
vendor_hash = pkg.vendorHash or null;
vendor_sha256 = pkg.vendorSha256 or null;
cargo_deps = pkg.cargoDeps.outputHash or null;
npm_deps = pkg.npmDeps.outputHash or null;
tests = builtins.attrNames (pkg.passthru.tests or {{}});
has_update_script = {has_update_script};
src_homepage = pkg.src.meta.homepage or null;
changelog = pkg.meta.changelog or null;
}}"""
def eval_attr(opts: Options) -> Package:
expr = eval_expression(opts.import_path, opts.attribute)
expr = eval_expression(opts.import_path, opts.attribute, opts.flake)
cmd = [
"nix",
"eval",

View File

@ -1,3 +1,4 @@
import os
from dataclasses import dataclass
from typing import Optional
@ -7,10 +8,11 @@ from .version.version import VersionPreference
@dataclass
class Options:
attribute: str
flake: bool = False
version: str = "stable"
version_preference: VersionPreference = VersionPreference.STABLE
version_regex: str = "(.*)"
import_path: str = "./."
import_path: str = os.getcwd()
override_filename: Optional[str] = None
commit: bool = False
use_update_script: bool = False

View File

@ -105,29 +105,31 @@ def disable_check_meta(opts: Options) -> str:
return f'(if (builtins.hasAttr "config" (builtins.functionArgs (import {opts.import_path}))) then {{ config.checkMeta = false; overlays = []; }} else {{ }})'
def update_src_hash(opts: Options, filename: str, current_hash: str) -> None:
expr = (
f"(import {opts.import_path} {disable_check_meta(opts)}).{opts.attribute}.src"
def get_attr(opts: Options, attr: str) -> str:
return (
f'let flake = builtins.getFlake "{opts.import_path}"; in (flake.packages.${{builtins.currentSystem}}.{opts.attribute} or flake.{opts.attribute}).{attr}'
if opts.flake
else f"(import {opts.import_path} {disable_check_meta(opts)}).{opts.attribute}.{attr}"
)
target_hash = nix_prefetch(expr)
def update_src_hash(opts: Options, filename: str, current_hash: str) -> None:
target_hash = nix_prefetch(get_attr(opts, "src"))
replace_hash(filename, current_hash, target_hash)
def update_go_modules_hash(opts: Options, filename: str, current_hash: str) -> None:
expr = f"(import {opts.import_path} {disable_check_meta(opts)}).{opts.attribute}.go-modules"
target_hash = nix_prefetch(expr)
target_hash = nix_prefetch(get_attr(opts, "go-modules"))
replace_hash(filename, current_hash, target_hash)
def update_cargo_deps_hash(opts: Options, filename: str, current_hash: str) -> None:
expr = f"(import {opts.import_path} {disable_check_meta(opts)}).{opts.attribute}.cargoDeps"
target_hash = nix_prefetch(expr)
target_hash = nix_prefetch(get_attr(opts, "cargoDeps"))
replace_hash(filename, current_hash, target_hash)
def update_npm_deps_hash(opts: Options, filename: str, current_hash: str) -> None:
expr = f"(import {opts.import_path} {disable_check_meta(opts)}).{opts.attribute}.npmDeps"
target_hash = nix_prefetch(expr)
target_hash = nix_prefetch(get_attr(opts, "npmDeps"))
replace_hash(filename, current_hash, target_hash)