Skip to content

Commit

Permalink
Merge pull request #118 from figsoda/flakes
Browse files Browse the repository at this point in the history
Flakes support
  • Loading branch information
Mic92 authored Dec 11, 2022
2 parents a5b4db5 + 6332016 commit bb901a3
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 103 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,4 @@ venv.bak/

# Nix artifacts
result
result-*
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
79 changes: 51 additions & 28 deletions nix_update/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`"
Expand Down Expand Up @@ -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,
Expand All @@ -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:
Expand Down Expand Up @@ -172,37 +188,41 @@ 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", "-L"]

if options.flake:
cmd.append(f"{options.import_path}#{options.attribute}")
else:
cmd.extend(["-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", "build", "--extra-experimental-features", "flakes nix-command", "-L"]
if options.flake:
cmd.append(f"{options.import_path}#{options.attribute}")
else:
cmd.extend(["-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)


Expand Down Expand Up @@ -251,10 +271,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)
Expand Down
84 changes: 50 additions & 34 deletions nix_update/eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,43 +53,59 @@ def __post_init__(self, raw_version_position: Optional[Dict[str, Any]]) -> None:
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;
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;
}})"""
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;"""
)

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",
Expand Down
4 changes: 3 additions & 1 deletion nix_update/options.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from dataclasses import dataclass
from typing import Optional

Expand All @@ -7,10 +8,11 @@
@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
Expand Down
22 changes: 12 additions & 10 deletions nix_update/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down
27 changes: 0 additions & 27 deletions tests/test_crate.py

This file was deleted.

33 changes: 33 additions & 0 deletions tests/test_flake.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import subprocess

import conftest

from nix_update import main


def test_main(helpers: conftest.Helpers) -> None:
with helpers.testpkgs(init_git=True) as path:
main(["--file", str(path), "--flake", "--commit", "--test", "crate"])
version = subprocess.run(
[
"nix",
"eval",
"--raw",
"--extra-experimental-features",
"flakes nix-command",
f"{path}#crate.version",
],
check=True,
text=True,
stdout=subprocess.PIPE,
).stdout.strip()
assert version >= "8.5.2"
commit = subprocess.run(
["git", "-C", path, "log", "-1"],
text=True,
stdout=subprocess.PIPE,
check=True,
).stdout.strip()
print(commit)
assert version in commit
assert "crate" in commit
11 changes: 8 additions & 3 deletions tests/testpkgs/crate.nix
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
{ rustPlatform, fetchCrate }:
{ rustPlatform, fetchCrate, hello }:

rustPlatform.buildRustPackage rec {
pname = "fd-find";
version = "8.0.0";

src = fetchCrate {
inherit pname version;
sha256 = "";
sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};

cargoSha256 = "";
cargoSha256 = "sha256-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=";

passthru.tests = {
foo = hello;
bar = hello;
};
}
7 changes: 7 additions & 0 deletions tests/testpkgs/flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
outputs = { self, nixpkgs }: {
packages = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed (system: {
crate = nixpkgs.legacyPackages.${system}.callPackage (self + "/crate.nix") { };
});
};
}

0 comments on commit bb901a3

Please sign in to comment.