Skip to content

Commit

Permalink
feat(yarn_classic): implement 'yarn.lock' support
Browse files Browse the repository at this point in the history
## What

- Ensure that 'yarn.lock' file exists
- Read from 'yarn.lock'

## How

- add 'pyarn' to dependencies (to parse 'yarn.lock' file)
- implement `YarnLock` CommonConfigFile subclass

Signed-off-by: Ben Alkov <ben.alkov@redhat.com>
  • Loading branch information
ben-alkov committed Nov 12, 2024
1 parent ed0e86e commit c0892c3
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 5 deletions.
48 changes: 47 additions & 1 deletion cachi2/core/package_managers/yarn_classic/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from dataclasses import dataclass
from typing import Any, Literal, Union

from pyarn import lockfile # type: ignore

from cachi2.core.errors import PackageRejected
from cachi2.core.rooted_path import RootedPath

Expand Down Expand Up @@ -93,7 +95,51 @@ def from_file(cls, path: RootedPath) -> "PackageJson":
return cls(path, package_json_data)


ConfigFile = Union[PackageJson]
class YarnLock(_CommonConfigFile):
"""A yarn.lock file.
This class abstracts the underlying attributes.
"""

yarn_lockfile: lockfile.Lockfile

@property
def config_kind(self) -> ConfigKind:
"""Return kind of this ConfigFile."""
return "yarnlock"

@classmethod
def from_file(cls, path: RootedPath) -> "YarnLock":
"""Parse the content of a yarn.lock file."""
try:
yarn_lockfile = lockfile.Lockfile.from_file(path)
except FileNotFoundError:
raise PackageRejected(
reason="The yarn.lock file must be present for the yarn package manager",
solution=(
"Please double-check that you have specified the correct path "
"to the package directory containing this file"
),
)
except ValueError:
raise PackageRejected(
reason=(f"Can't parse the {path} file.\n"),
solution=(
"The yarn.lock file must be valid. "
"Refer to the parser error and fix the contents of the file."
),
)

if not yarn_lockfile:
raise PackageRejected(
reason="The yarn.lock file must not be empty",
solution="Please verify the content of the file.",
)

return cls(path, yarn_lockfile.data)


ConfigFile = Union[PackageJson, YarnLock]


@dataclass(frozen=True)
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies = [
"packaging",
"pydantic",
"pypi-simple",
"pyarn",
"pyyaml",
"requests",
"semver",
Expand Down
17 changes: 14 additions & 3 deletions requirements-extras.txt
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,6 @@ coverage[toml]==7.6.4 \
--hash=sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c \
--hash=sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858
# via pytest-cov
exceptiongroup==1.2.2 \
--hash=sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b \
--hash=sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc
createrepo-c==1.1.4 \
--hash=sha256:0bdca28b4ce0b300e0caa2119e9eccb0fe22502d65bd4499efaec9415a1438bf \
--hash=sha256:5826daf92f994d48351c0d9dcf1fb6b09499687babb9b16dfcf17984af619d29 \
Expand All @@ -364,6 +361,10 @@ createrepo-c==1.1.4 \
--hash=sha256:9930b8ddac9cc808de06453cc7d4712940898140e415d23fe90a76cd53899b33 \
--hash=sha256:d8130f05be23feaa2028871c44474706203ca716a55df582d7e0e860430a24ea \
--hash=sha256:f547f7ea4748cee93e8b7033fe27e1a69ed922606a7fbd162acdcbdbe3af0715
# via cachi2 (pyproject.toml)
exceptiongroup==1.2.2 \
--hash=sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b \
--hash=sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc
# via pytest
flake8==7.1.1 \
--hash=sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38 \
Expand Down Expand Up @@ -681,6 +682,10 @@ pluggy==1.5.0 \
--hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \
--hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669
# via pytest
ply==3.11 \
--hash=sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3 \
--hash=sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce
# via pyarn
propcache==0.2.0 \
--hash=sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9 \
--hash=sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763 \
Expand Down Expand Up @@ -781,6 +786,10 @@ propcache==0.2.0 \
--hash=sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016 \
--hash=sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504
# via yarl
pyarn==0.2.0 \
--hash=sha256:542ff739af2b81a1200776eff2b4d2566a330846decbd0f815999b196d7b067d \
--hash=sha256:d06e8b79bb830f142187b57ee664dc0104f658efdb2b2bae7ed99eaf7746eb1a
# via cachi2 (pyproject.toml)
pycodestyle==2.12.1 \
--hash=sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3 \
--hash=sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521
Expand Down Expand Up @@ -1144,9 +1153,11 @@ typing-extensions==4.12.2 \
# via
# black
# cachi2 (pyproject.toml)
# multidict
# mypy
# pydantic
# pydantic-core
# rich
# typer
urllib3==2.2.3 \
--hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \
Expand Down
110 changes: 110 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,114 @@ packaging==24.1 \
# via
# cachi2 (pyproject.toml)
# pypi-simple
ply==3.11 \
--hash=sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3 \
--hash=sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce
# via pyarn
propcache==0.2.0 \
--hash=sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9 \
--hash=sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763 \
--hash=sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325 \
--hash=sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb \
--hash=sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b \
--hash=sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09 \
--hash=sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957 \
--hash=sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68 \
--hash=sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f \
--hash=sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798 \
--hash=sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418 \
--hash=sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6 \
--hash=sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162 \
--hash=sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f \
--hash=sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036 \
--hash=sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8 \
--hash=sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2 \
--hash=sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110 \
--hash=sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23 \
--hash=sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8 \
--hash=sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638 \
--hash=sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a \
--hash=sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44 \
--hash=sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2 \
--hash=sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2 \
--hash=sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850 \
--hash=sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136 \
--hash=sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b \
--hash=sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887 \
--hash=sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89 \
--hash=sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87 \
--hash=sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348 \
--hash=sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4 \
--hash=sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861 \
--hash=sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e \
--hash=sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c \
--hash=sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b \
--hash=sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb \
--hash=sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1 \
--hash=sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de \
--hash=sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354 \
--hash=sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563 \
--hash=sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5 \
--hash=sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf \
--hash=sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9 \
--hash=sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12 \
--hash=sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4 \
--hash=sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5 \
--hash=sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71 \
--hash=sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9 \
--hash=sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed \
--hash=sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336 \
--hash=sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90 \
--hash=sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063 \
--hash=sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad \
--hash=sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6 \
--hash=sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8 \
--hash=sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e \
--hash=sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2 \
--hash=sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7 \
--hash=sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d \
--hash=sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d \
--hash=sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df \
--hash=sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b \
--hash=sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178 \
--hash=sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2 \
--hash=sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630 \
--hash=sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48 \
--hash=sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61 \
--hash=sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89 \
--hash=sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb \
--hash=sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3 \
--hash=sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6 \
--hash=sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562 \
--hash=sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b \
--hash=sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58 \
--hash=sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db \
--hash=sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99 \
--hash=sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37 \
--hash=sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83 \
--hash=sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a \
--hash=sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d \
--hash=sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04 \
--hash=sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70 \
--hash=sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544 \
--hash=sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394 \
--hash=sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea \
--hash=sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7 \
--hash=sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1 \
--hash=sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793 \
--hash=sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577 \
--hash=sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7 \
--hash=sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57 \
--hash=sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d \
--hash=sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032 \
--hash=sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d \
--hash=sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016 \
--hash=sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504
# via yarl
pyarn==0.2.0 \
--hash=sha256:542ff739af2b81a1200776eff2b4d2566a330846decbd0f815999b196d7b067d \
--hash=sha256:d06e8b79bb830f142187b57ee664dc0104f658efdb2b2bae7ed99eaf7746eb1a
# via cachi2 (pyproject.toml)
pydantic==2.9.1 \
--hash=sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2 \
--hash=sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612
Expand Down Expand Up @@ -687,8 +795,10 @@ typing-extensions==4.12.2 \
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
# via
# multidict
# pydantic
# pydantic-core
# rich
# typer
urllib3==2.2.3 \
--hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \
Expand Down
33 changes: 32 additions & 1 deletion tests/unit/package_managers/yarn_classic/test_project.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import json

import pytest
from pyarn import lockfile # type: ignore

from cachi2.core.errors import PackageRejected
from cachi2.core.package_managers.yarn_classic.main import _verify_repository
from cachi2.core.package_managers.yarn_classic.project import ConfigFile, PackageJson, Project
from cachi2.core.package_managers.yarn_classic.project import (
ConfigFile,
PackageJson,
Project,
YarnLock,
)
from cachi2.core.rooted_path import RootedPath

VALID_PACKAGE_JSON_FILE = """
Expand All @@ -26,6 +32,24 @@

INVALID_JSON_FILE = "totally not json"

VALID_YARN_LOCK_FILE = """
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
package-1@^1.0.0:
version "1.0.3"
resolved "https://registry.npmjs.org/package-1/-/package-1-1.0.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
"""

EMPTY_YARN_LOCK_FILE = """
"""

INVALID_YARN_LOCK_FILE = """
# yarn lockfile v1
package-1
version "1.0.3"
"""


def _prepare_config_file(
rooted_tmp_path: RootedPath, config_file_class: ConfigFile, filename: str, content: str
Expand All @@ -51,6 +75,7 @@ def _setup_pnp_installs(rooted_tmp_path: RootedPath, pnp_kind: str) -> None:
pytest.param(
PackageJson, "package.json", VALID_PACKAGE_JSON_FILE, "package_json", id="package_json"
),
pytest.param(YarnLock, "yarn.lock", VALID_YARN_LOCK_FILE, "yarnlock", id="yarnlock"),
],
)
def test_config_file_attributes(
Expand All @@ -76,6 +101,7 @@ def test_config_file_attributes(
pytest.param(
PackageJson, "package.json", VALID_PACKAGE_JSON_FILE, "json", id="package_json"
),
pytest.param(YarnLock, "yarn.lock", VALID_YARN_LOCK_FILE, "pyarn", id="yarnlock"),
],
)
def test_find_and_open_config_file(
Expand All @@ -94,6 +120,8 @@ def test_find_and_open_config_file(

if content_kind == "json":
assert found_config.data == json.loads(config_file_content)
elif content_kind == "pyarn":
assert found_config.data == lockfile.Lockfile.from_str(config_file_content).data


@pytest.mark.parametrize(
Expand All @@ -105,6 +133,8 @@ def test_find_and_open_config_file(
INVALID_JSON_FILE,
id="invalid_package_json",
),
pytest.param(YarnLock, "yarn.lock", EMPTY_YARN_LOCK_FILE, id="empty_yarnlock"),
pytest.param(YarnLock, "yarn.lock", INVALID_YARN_LOCK_FILE, id="invalid_yarnlock"),
],
)
def test_from_file_bad(
Expand All @@ -130,6 +160,7 @@ def test_from_file_bad(
"package.json",
id="missing_package_json",
),
pytest.param(YarnLock, "yarn.lock", id="missing_yarnlock"),
],
)
def test_from_file_missing(
Expand Down

0 comments on commit c0892c3

Please sign in to comment.