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
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class Lock:
min_version: str | None = None
ref: str | None = None
commit: str | None = None
sha512: str | None = None # svn-zip: the released zip's SHA-512 (locks.md)
source: str | None = None
plugins: list[str] = field(default_factory=list)
reconciled: Reconciled | None = None
Expand All @@ -84,6 +85,7 @@ def _strip_comment(line: str) -> str:
def parse(text: str) -> Lock:
"""Parse the lock's text. Raises `MalformedLock` on anything unexpected."""
lock = Lock()
scalar_keys = {f.name for f in fields(Lock)} - {"plugins", "reconciled"}
section: str | None = None
for raw in text.splitlines():
line = _strip_comment(raw)
Expand All @@ -101,14 +103,16 @@ def parse(text: str) -> Lock:
elif key == "reconciled":
section = "reconciled"
lock.reconciled = Reconciled()
elif key == "sha512":
raise MalformedLock("missing value for key: 'sha512'")
else:
raise MalformedLock(f"unknown block: {key!r}")
continue
if ":" not in body:
raise MalformedLock(f"not a key: value line: {raw!r}")
key, _, value = body.partition(":")
key, value = key.strip(), value.strip()
if key in {"method", "url", "min_version", "ref", "commit", "source"}:
if key in scalar_keys:
setattr(lock, key, value)
else:
raise MalformedLock(f"unknown key: {key!r}")
Expand Down Expand Up @@ -141,6 +145,8 @@ def parse(text: str) -> Lock:
continue

raise MalformedLock(f"indented line outside any block: {raw!r}")
if lock.sha512 is not None and lock.method != "svn-zip":
raise MalformedLock("'sha512' is only valid for method: 'svn-zip'")
return lock


Expand Down
64 changes: 63 additions & 1 deletion tools/setup-preflight/tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
skill_findings,
verify_findings,
)
from setup_preflight.lockfile import MalformedLock
from setup_preflight.lockfile import MalformedLock, parse

from .conftest import write_lock, write_stamp

Expand Down Expand Up @@ -218,6 +218,68 @@ def test_the_framework_checkout_has_nothing_to_drift(project: Path) -> None:
assert project_findings(project, {"magpie-setup": "0.0.1"}) == []


SVN_ZIP_SHA512 = "3f2a9c4e0b7d1a8e5c6f9b2d4a7e0c3f6b9d2a5e8c1f4b7d0a3e6c9f2b5d8a1e" * 2
SVN_ZIP_URL = "https://dist.apache.org/repos/dist/release/magpie/0.2.0/apache-magpie-0.2.0-source.zip"
SVN_ZIP_PIN = f"""\
method: svn-zip
url: {SVN_ZIP_URL}
ref: 0.2.0
sha512: {SVN_ZIP_SHA512}
"""


@pytest.mark.parametrize("method_first", [True, False], ids=["method-first", "method-last"])
def test_the_svn_zip_lock_setup_writes_parses(project: Path, method_first: bool) -> None:
"""`locks.md` and `install.md` write `sha512:` for `method: svn-zip`;
rejecting it failed every skill's pre-flight for an adopter installed
from the signed source release."""
pin = SVN_ZIP_PIN
if not method_first:
method, _, rest = pin.partition("\n")
pin = rest + method + "\n"
assert parse(pin).sha512 == SVN_ZIP_SHA512
write_lock(project, pin)
assert codes(project_findings(project, None)) == ["snapshot-never-fetched"]


@pytest.mark.parametrize("fetched_commit", ["", "0.2.0"], ids=["legacy-empty", "upgrade-version"])
def test_the_documented_svn_zip_local_lock_is_accepted(project: Path, fetched_commit: str) -> None:
"""Existing local locks may record an empty commit or the fetched zip's version;
both remain readable when their recorded pin fields match."""
write_lock(project, SVN_ZIP_PIN)
write_local_lock(project, method="svn-zip", url=SVN_ZIP_URL, ref="0.2.0", commit=fetched_commit)
assert project_findings(project, None) == []


@pytest.mark.parametrize("method", ["git-tag", "git-branch", "marketplace", "local", None, "other"])
@pytest.mark.parametrize("method_first", [True, False], ids=["method-first", "method-last"])
def test_sha512_is_rejected_outside_svn_zip(method: str | None, method_first: bool) -> None:
"""A mistyped git commit anchor must not become an ignored zip digest."""
method_line = f"method: {method}\n" if method is not None else ""
hash_line = f"sha512: {SVN_ZIP_SHA512}\n"
pin = method_line + hash_line if method_first else hash_line + method_line
with pytest.raises(MalformedLock, match=r"sha512.*svn-zip"):
parse(pin)


@pytest.mark.parametrize("value", ["", " ", " # the digest is missing"])
def test_an_empty_sha512_names_the_missing_value(value: str) -> None:
with pytest.raises(MalformedLock, match=r"missing value.*sha512"):
parse(f"method: svn-zip\nsha512:{value}\n")


def test_an_unknown_scalar_key_is_still_rejected(project: Path) -> None:
write_lock(project, SVN_ZIP_PIN + "sha521: abc\n")
with pytest.raises(MalformedLock, match="unknown key: 'sha521'"):
project_findings(project, None)


@pytest.mark.parametrize("block", ["plugins", "reconciled"])
def test_a_structured_block_cannot_be_assigned_a_scalar(block: str) -> None:
with pytest.raises(MalformedLock, match=f"unknown key: '{block}'"):
parse(f"method: marketplace\n{block}: not-a-block\n")


def test_a_malformed_lock_raises_rather_than_reading_as_no_lock(project: Path) -> None:
write_lock(project, "method: marketplace\nnonsense\n")
with pytest.raises(MalformedLock):
Expand Down