2020-03-23 13:38:04 +03:00
|
|
|
import json
|
2023-04-03 05:56:36 +03:00
|
|
|
import os
|
2022-11-15 01:13:11 +03:00
|
|
|
from dataclasses import InitVar, dataclass, field
|
2023-01-08 10:41:48 +03:00
|
|
|
from textwrap import dedent, indent
|
2023-04-03 10:29:37 +03:00
|
|
|
from typing import Any, Dict, List, Literal, Optional
|
2022-11-24 20:33:58 +03:00
|
|
|
from urllib.parse import ParseResult, urlparse
|
2020-03-23 13:38:04 +03:00
|
|
|
|
|
|
|
from .errors import UpdateError
|
2020-04-21 12:06:55 +03:00
|
|
|
from .options import Options
|
2020-11-18 11:23:07 +03:00
|
|
|
from .utils import run
|
2022-11-21 23:07:18 +03:00
|
|
|
from .version.version import Version, VersionPreference
|
2020-03-23 13:38:04 +03:00
|
|
|
|
|
|
|
|
2020-12-01 11:30:17 +03:00
|
|
|
@dataclass
|
|
|
|
class Position:
|
|
|
|
file: str
|
|
|
|
line: int
|
|
|
|
column: int
|
|
|
|
|
|
|
|
|
2023-04-03 10:26:16 +03:00
|
|
|
class CargoLock:
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class NoCargoLock(CargoLock):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class CargoLockInSource(CargoLock):
|
|
|
|
def __init__(self, path: str) -> None:
|
|
|
|
self.path = path
|
|
|
|
|
|
|
|
|
|
|
|
class CargoLockInStore(CargoLock):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2020-03-23 13:38:04 +03:00
|
|
|
@dataclass
|
|
|
|
class Package:
|
2020-11-18 23:34:22 +03:00
|
|
|
attribute: str
|
2023-04-03 10:26:16 +03:00
|
|
|
import_path: InitVar[str]
|
2020-03-23 13:38:04 +03:00
|
|
|
name: str
|
2020-03-24 16:25:17 +03:00
|
|
|
old_version: str
|
2020-03-23 13:38:04 +03:00
|
|
|
filename: str
|
|
|
|
line: int
|
|
|
|
urls: Optional[List[str]]
|
|
|
|
url: Optional[str]
|
2022-11-24 20:33:58 +03:00
|
|
|
src_homepage: Optional[str]
|
2022-11-14 16:50:53 +03:00
|
|
|
changelog: Optional[str]
|
2020-03-23 13:38:04 +03:00
|
|
|
rev: str
|
2021-08-26 14:08:18 +03:00
|
|
|
hash: Optional[str]
|
2023-04-10 00:11:05 +03:00
|
|
|
go_modules: Optional[str]
|
2022-09-11 09:07:10 +03:00
|
|
|
cargo_deps: Optional[str]
|
2022-11-10 18:56:55 +03:00
|
|
|
npm_deps: Optional[str]
|
2021-08-21 11:33:01 +03:00
|
|
|
tests: List[str]
|
2022-11-28 18:43:41 +03:00
|
|
|
has_update_script: bool
|
2020-03-23 13:38:04 +03:00
|
|
|
|
2020-12-01 11:30:17 +03:00
|
|
|
raw_version_position: InitVar[Optional[Dict[str, Any]]]
|
2023-04-03 10:29:37 +03:00
|
|
|
raw_cargo_lock: InitVar[Literal[False] | str | None]
|
2020-12-01 11:30:17 +03:00
|
|
|
|
2022-11-24 20:33:58 +03:00
|
|
|
parsed_url: Optional[ParseResult] = None
|
2022-01-15 15:44:02 +03:00
|
|
|
new_version: Optional[Version] = None
|
2020-12-01 11:30:17 +03:00
|
|
|
version_position: Optional[Position] = field(init=False)
|
2023-04-03 10:26:16 +03:00
|
|
|
cargo_lock: CargoLock = field(init=False)
|
2022-11-24 20:33:58 +03:00
|
|
|
diff_url: Optional[str] = None
|
2020-12-01 11:30:17 +03:00
|
|
|
|
2023-04-03 10:26:16 +03:00
|
|
|
def __post_init__(
|
|
|
|
self,
|
|
|
|
import_path: str,
|
|
|
|
raw_version_position: Optional[Dict[str, Any]],
|
2023-04-03 10:29:37 +03:00
|
|
|
raw_cargo_lock: Literal[False] | str | None,
|
2023-04-03 10:26:16 +03:00
|
|
|
) -> None:
|
2022-11-24 20:33:58 +03:00
|
|
|
url = self.url or (self.urls[0] if self.urls else None)
|
|
|
|
if url:
|
|
|
|
self.parsed_url = urlparse(url)
|
2020-12-01 11:30:17 +03:00
|
|
|
if raw_version_position is None:
|
|
|
|
self.version_position = None
|
|
|
|
else:
|
|
|
|
self.version_position = Position(**raw_version_position)
|
2023-04-20 20:33:36 +03:00
|
|
|
|
2023-04-03 10:26:16 +03:00
|
|
|
if raw_cargo_lock is None:
|
|
|
|
self.cargo_lock = NoCargoLock()
|
|
|
|
elif raw_cargo_lock is False:
|
|
|
|
self.cargo_lock = CargoLockInStore()
|
|
|
|
elif not os.path.realpath(raw_cargo_lock).startswith(import_path):
|
|
|
|
self.cargo_lock = CargoLockInStore()
|
|
|
|
else:
|
|
|
|
self.cargo_lock = CargoLockInSource(raw_cargo_lock)
|
2020-03-24 16:25:17 +03:00
|
|
|
|
2020-03-23 13:38:04 +03:00
|
|
|
|
2023-01-08 18:30:03 +03:00
|
|
|
def eval_expression(
|
|
|
|
import_path: str, attr: str, flake: bool, system: Optional[str]
|
|
|
|
) -> str:
|
|
|
|
system = f'"{system}"' if system else "builtins.currentSystem"
|
|
|
|
|
2023-01-08 10:41:48 +03:00
|
|
|
if flake:
|
|
|
|
let_bindings = f"""
|
2023-01-08 20:25:40 +03:00
|
|
|
inherit (builtins) getFlake stringLength substring;
|
|
|
|
currentSystem = {system};
|
2023-01-08 10:41:48 +03:00
|
|
|
flake = getFlake "{import_path}";
|
|
|
|
pkg = flake.packages.${{currentSystem}}.{attr} or flake.{attr};
|
|
|
|
inherit (flake) outPath;
|
|
|
|
outPathLen = stringLength outPath;
|
|
|
|
sanitizePosition = {{ file, ... }}@pos:
|
2023-04-20 20:12:44 +03:00
|
|
|
assert substring 0 outPathLen file != outPath
|
|
|
|
-> throw "${{file}} is not in ${{outPath}}";
|
2023-01-08 20:25:40 +03:00
|
|
|
pos // {{ file = "{import_path}" + substring outPathLen (stringLength file - outPathLen) file; }};
|
2023-01-08 10:41:48 +03:00
|
|
|
"""
|
|
|
|
else:
|
|
|
|
let_bindings = f"""
|
2023-01-08 20:32:46 +03:00
|
|
|
pkgs = import {import_path};
|
|
|
|
args = builtins.functionArgs pkgs;
|
|
|
|
inputs = (if args ? system then {{ system = {system}; }} else {{}}) //
|
|
|
|
(if args ? overlays then {{ overlays = [ ]; }} else {{}});
|
|
|
|
pkg = (pkgs inputs).{attr};
|
2023-01-08 10:41:48 +03:00
|
|
|
sanitizePosition = x: x;
|
|
|
|
"""
|
2022-12-08 20:11:00 +03:00
|
|
|
|
|
|
|
has_update_script = (
|
|
|
|
"false" if flake else "pkg.passthru.updateScript or null != null"
|
|
|
|
)
|
|
|
|
|
|
|
|
return f"""
|
|
|
|
let
|
2023-01-08 10:41:48 +03:00
|
|
|
{indent(dedent(let_bindings), " ")}
|
2022-12-08 20:11:00 +03:00
|
|
|
raw_version_position = sanitizePosition (builtins.unsafeGetAttrPos "version" pkg);
|
|
|
|
|
|
|
|
position = if pkg ? isRubyGem then
|
|
|
|
raw_version_position
|
2022-12-29 16:06:08 +03:00
|
|
|
else if pkg ? isPhpExtension then
|
|
|
|
raw_version_position
|
|
|
|
else
|
2022-12-08 20:11:00 +03:00
|
|
|
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;
|
2023-04-10 00:11:05 +03:00
|
|
|
go_modules = pkg.go-modules.outputHash or null;
|
2022-12-08 20:11:00 +03:00
|
|
|
cargo_deps = pkg.cargoDeps.outputHash or null;
|
2023-04-03 10:26:16 +03:00
|
|
|
raw_cargo_lock =
|
2023-03-21 05:43:06 +03:00
|
|
|
if pkg ? cargoDeps.lockFile then
|
2023-04-03 05:56:36 +03:00
|
|
|
let
|
|
|
|
inherit (pkg.cargoDeps) lockFile;
|
|
|
|
res = builtins.tryEval (sanitizePosition {{
|
2023-04-27 05:36:17 +03:00
|
|
|
file = toString lockFile;
|
2023-04-03 05:56:36 +03:00
|
|
|
}});
|
|
|
|
in
|
|
|
|
if res.success then res.value.file else false
|
2023-03-21 05:43:06 +03:00
|
|
|
else
|
|
|
|
null;
|
2022-12-08 20:11:00 +03:00
|
|
|
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;
|
|
|
|
}}"""
|
2020-03-23 13:38:04 +03:00
|
|
|
|
|
|
|
|
|
|
|
def eval_attr(opts: Options) -> Package:
|
2023-01-08 18:30:03 +03:00
|
|
|
expr = eval_expression(opts.import_path, opts.attribute, opts.flake, opts.system)
|
2020-11-18 11:23:07 +03:00
|
|
|
cmd = [
|
|
|
|
"nix",
|
|
|
|
"eval",
|
|
|
|
"--json",
|
|
|
|
"--impure",
|
|
|
|
"--expr",
|
|
|
|
expr,
|
2023-01-08 18:35:32 +03:00
|
|
|
] + opts.extra_flags
|
2020-08-16 14:27:27 +03:00
|
|
|
res = run(cmd)
|
2020-03-23 13:38:04 +03:00
|
|
|
out = json.loads(res.stdout)
|
2023-04-03 10:26:16 +03:00
|
|
|
package = Package(attribute=opts.attribute, import_path=opts.import_path, **out)
|
2022-06-14 12:10:13 +03:00
|
|
|
if opts.override_filename is not None:
|
|
|
|
package.filename = opts.override_filename
|
2023-01-26 11:56:16 +03:00
|
|
|
if opts.url is not None:
|
|
|
|
package.parsed_url = urlparse(opts.url)
|
2021-08-26 16:00:20 +03:00
|
|
|
if opts.version_preference != VersionPreference.SKIP and package.old_version == "":
|
2020-03-23 13:38:04 +03:00
|
|
|
raise UpdateError(
|
|
|
|
f"Nix's builtins.parseDrvName could not parse the version from {package.name}"
|
|
|
|
)
|
|
|
|
|
|
|
|
return package
|