Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions nix_update/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import tempfile
from typing import NoReturn, Optional

from .eval import Package, eval_attr
from .eval import CargoLockInSource, Package, eval_attr
from .options import Options
from .update import update
from .utils import run
Expand Down Expand Up @@ -157,8 +157,8 @@ def git_commit(git_dir: str, package: Package) -> None:
msg = format_commit_message(package)
new_version = package.new_version
cmd = ["git", "-C", git_dir, "add", package.filename]
if package.cargo_lock:
cmd.append(package.cargo_lock)
if isinstance(package.cargo_lock, CargoLockInSource):
cmd.append(package.cargo_lock.path)
run(cmd, stdout=None)
if new_version and package.old_version != new_version.number:
run(
Expand Down
52 changes: 46 additions & 6 deletions nix_update/eval.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import json
import os
from dataclasses import InitVar, dataclass, field
from textwrap import dedent, indent
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Literal, Optional
from urllib.parse import ParseResult, urlparse

from .errors import UpdateError
Expand All @@ -17,9 +18,27 @@ class Position:
column: int


class CargoLock:
pass


class NoCargoLock(CargoLock):
pass


class CargoLockInSource(CargoLock):
def __init__(self, path: str) -> None:
self.path = path


class CargoLockInStore(CargoLock):
pass


@dataclass
class Package:
attribute: str
import_path: InitVar[str]
name: str
old_version: str
filename: str
Expand All @@ -33,26 +52,41 @@ class Package:
vendor_hash: Optional[str]
vendor_sha256: Optional[str]
cargo_deps: Optional[str]
cargo_lock: Optional[str]
npm_deps: Optional[str]
tests: List[str]
has_update_script: bool

raw_version_position: InitVar[Optional[Dict[str, Any]]]
raw_cargo_lock: InitVar[Literal[False] | str | None]

parsed_url: Optional[ParseResult] = None
new_version: Optional[Version] = None
version_position: Optional[Position] = field(init=False)
cargo_lock: CargoLock = field(init=False)
diff_url: Optional[str] = None

def __post_init__(self, raw_version_position: Optional[Dict[str, Any]]) -> None:
def __post_init__(
self,
import_path: str,
raw_version_position: Optional[Dict[str, Any]],
raw_cargo_lock: Literal[False] | str | None,
) -> None:
url = self.url or (self.urls[0] if self.urls else None)
if url:
self.parsed_url = urlparse(url)
if raw_version_position is None:
self.version_position = None
else:
self.version_position = Position(**raw_version_position)
raw_cargo_lock
if raw_cargo_lock is None:
self.cargo_lock = NoCargoLock()
elif raw_cargo_lock is False:
self.cargo_lock = CargoLockInStore()
Comment on lines +84 to +85
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unsure about this part. If we get false, do we want to throw an error instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is false when it is a store path in a flake, so I would say the current solution is good

elif not os.path.realpath(raw_cargo_lock).startswith(import_path):
self.cargo_lock = CargoLockInStore()
else:
self.cargo_lock = CargoLockInSource(raw_cargo_lock)


def eval_expression(
Expand Down Expand Up @@ -110,9 +144,15 @@ def eval_expression(
vendor_hash = pkg.vendorHash or null;
vendor_sha256 = pkg.vendorSha256 or null;
cargo_deps = pkg.cargoDeps.outputHash or null;
cargo_lock =
raw_cargo_lock =
if pkg ? cargoDeps.lockFile then
(sanitizePosition {{ file = pkg.cargoDeps.lockFile; }}).file
let
inherit (pkg.cargoDeps) lockFile;
res = builtins.tryEval (sanitizePosition {{
file = lockFile;
}});
in
if res.success then res.value.file else false
else
null;
npm_deps = pkg.npmDeps.outputHash or null;
Expand All @@ -135,7 +175,7 @@ def eval_attr(opts: Options) -> Package:
] + opts.extra_flags
res = run(cmd)
out = json.loads(res.stdout)
package = Package(attribute=opts.attribute, **out)
package = Package(attribute=opts.attribute, import_path=opts.import_path, **out)
if opts.override_filename is not None:
package.filename = opts.override_filename
if opts.url is not None:
Expand Down
33 changes: 25 additions & 8 deletions nix_update/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from typing import Dict, Optional, Tuple

from .errors import UpdateError
from .eval import Package, eval_attr
from .eval import CargoLockInSource, CargoLockInStore, Package, eval_attr
from .git import old_version_from_git
from .options import Options
from .utils import info, run
Expand Down Expand Up @@ -152,25 +152,40 @@ def update_cargo_deps_hash(opts: Options, filename: str, current_hash: str) -> N
replace_hash(filename, current_hash, target_hash)


def update_cargo_lock(opts: Options, filename: str, dst: str) -> None:
def update_cargo_lock(
opts: Options, filename: str, dst: CargoLockInSource | CargoLockInStore
) -> None:
res = run(
[
"nix",
"build",
"--impure",
"--print-out-paths",
"--expr",
f'{get_package(opts)}.overrideAttrs (_: {{ prePatch = "cp -r . $out; exit"; outputs = [ "out" ]; }})',
f"""
{get_package(opts)}.overrideAttrs (_: {{
cargoDeps = null;
postUnpack = ''
cp -r "$sourceRoot/Cargo.lock" $out
exit
'';
outputs = [ "out" ];
}})
""",
]
+ opts.extra_flags,
)
src = Path(res.stdout.strip()) / "Cargo.lock"
src = Path(res.stdout.strip())
if not src.is_file():
return

shutil.copyfile(src, dst)
hashes = {}
with open(dst, "rb") as f:
with open(src, "rb") as f:
if isinstance(dst, CargoLockInSource):
with open(dst.path, "wb") as fdst:
shutil.copyfileobj(f, fdst)
f.seek(0)

hashes = {}
lock = tomllib.load(f)
regex = re.compile(r"git\+([^?]+)(\?(rev|tag|branch)=.*)?#(.*)")
git_deps = {}
Expand Down Expand Up @@ -326,7 +341,9 @@ def update(opts: Options) -> Package:
if package.cargo_deps:
update_cargo_deps_hash(opts, package.filename, package.cargo_deps)

if package.cargo_lock:
if isinstance(package.cargo_lock, CargoLockInSource) or isinstance(
package.cargo_lock, CargoLockInStore
):
update_cargo_lock(opts, package.filename, package.cargo_lock)

if package.npm_deps:
Expand Down
8 changes: 4 additions & 4 deletions nix_update/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import subprocess
import sys
from pathlib import Path
from typing import IO, Any, Callable, Dict, List, Optional, Union
from typing import IO, Any, Callable, Dict, List, Optional

HAS_TTY = sys.stdout.isatty()
ROOT = Path(os.path.dirname(os.path.realpath(__file__)))
Expand All @@ -24,9 +24,9 @@ def wrapper(text: str) -> None:

def run(
command: List[str],
cwd: Optional[Union[Path, str]] = None,
stdout: Union[None, int, IO[Any]] = subprocess.PIPE,
stderr: Union[None, int, IO[Any]] = None,
cwd: Optional[Path | str] = None,
stdout: None | int | IO[Any] = subprocess.PIPE,
stderr: None | int | IO[Any] = None,
check: bool = True,
extra_env: Dict[str, str] = {},
) -> "subprocess.CompletedProcess[str]":
Expand Down
2 changes: 0 additions & 2 deletions tests/test_cargo_lock_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ def test_main(helpers: conftest.Helpers) -> None:
check=True,
).stdout.strip()
print(diff)
assert "Cargo.lock" in diff
assert '+source = "git+' in diff
assert (
"https://github.com/charliermarsh/ruff/compare/v0.0.254...v0.0.255" in diff
)
Loading