_ _
| |_ ___| |_ _
| | . | | | |
|_|_|___|_|_ |
|___|
git mirror - github.com/owenewans/holy - branch master
file tests/winget.py
#!/usr/bin/env python3
"""converts WinGet manifests into native packages and installs what they carry."""
import hashlib
import pathlib
import shutil
import subprocess
import sys
import tempfile
import zipfile
binary = str(pathlib.Path(sys.argv[1]).resolve())
def call(*args, status=0):
result = subprocess.run([binary, *map(str, args)], capture_output=True, text=True)
assert result.returncode == status, (args, result.returncode, result.stdout, result.stderr)
return result.stdout
def manifest(directory, name, entries, overrides="", drop=()):
"""a manifest beside the artifact it names, with the digest the manifest pins"""
archive = directory / f"{name}_fixture.zip"
with zipfile.ZipFile(archive, "w") as opened:
for entry, body in entries.items():
opened.writestr(entry, body)
lines = [
f"PackageIdentifier: {name}",
"PackageVersion: 1.2.3",
f"PackageFamilyNames: {name}_123",
"Publisher: Fixture",
"PublisherUrl: https://example.org/fixture",
"InstallerType: zip",
f"InstallerUrl: https://example.org/{archive.name}",
f"InstallerSha256: {hashlib.sha256(archive.read_bytes()).hexdigest().upper()}",
"InstallerSwitches:",
" Custom: silent",
"InstallerScope: user",
"ElevationRequirement: elevationRequired",
"ReleaseDate: 2024-05-01",
"InstallLocationRequired: true",
"ManifestType: installer",
"ManifestVersion: 1.6.0",
"UpdateBehavior: install",
"Tags:",
" - fixture",
]
if "dependencies" not in drop:
lines += [
"PackageDependencies:",
" - Package: Fixture.Runtime",
" Architecture: x64",
" MinimumOSVersion: 10.0.17763.0",
]
if overrides:
lines.append(overrides)
(directory / f"{name}.yaml").write_text("\n".join(lines) + "\n")
return archive
def main():
with tempfile.TemporaryDirectory() as scratch:
root = pathlib.Path(scratch)
catalog = root / "catalog"
catalog.mkdir()
manifest(catalog, "Fixture.Tool", {
"tool/tool.exe": "MZ a program for another operating system\n",
"tool/README.txt": "the tool reads its configuration\n",
})
out = root / "converted"
report = call("import", catalog / "Fixture.Tool.yaml", "--source", "winget", "--format",
"winget", "--output", out, status=3)
artifact = out / "Fixture.Tool--noarch--nolibc.holy"
assert "imported Fixture.Tool--noarch--nolibc.holy artifact" in report, report
assert artifact.is_file(), sorted(p.name for p in out.iterdir())
call("verify", "local:" + str(artifact))
identity = call("info", "local:" + str(artifact))
assert "name Fixture.Tool\n" in identity and "version 1.2.3\n" in identity, identity
assert "arch noarch\n" in identity and "libc nolibc\n" in identity, identity
listing = call("manifest", "local:" + str(artifact))
for path in ("usr/lib/holy/private/Fixture.Tool/tool/tool.exe",
"usr/lib/holy/private/Fixture.Tool/tool/README.txt"):
assert path in listing, path
# the manifest travels whole beside the package
assert (out / "original").read_bytes() == (catalog / "Fixture.Tool.yaml").read_bytes()
package = (out / "package").read_text()
assert "format holy-winget-package-1\n" in package
assert "status review-required\n" in package
assert "artifact https://example.org/Fixture.Tool_fixture.zip" in package
assert "verified against the digest the manifest pins" in package
assert "artifact-placed the payload carries 2 files" in package
# a WinGet digest is written in upper case and still verified
assert (catalog / "Fixture.Tool.yaml").read_text().count(hashlib.sha256(
(catalog / "Fixture.Tool_fixture.zip").read_bytes()).hexdigest().upper()) == 1
# nothing runs the artifact and no runtime is promised
assert "nothing runs the artifact" in package
assert "a package cannot promise a Wine" in package
# the installer switches, the Windows keys and the catalog keys are counted
assert "installer 8 PowerShell installer keys are dropped" in package
assert "integration 3 Windows integration keys are dropped" in package
assert "update 1 keys that check or rewrite the upstream version" in package
assert "belong to a\nWinGet catalog" in package
# every key of this manifest is read, counted or carried, so nothing is unknown
assert "unknown" not in package, package
# the dependency block names a package of the same catalog
requirements = call("requirements", "local:" + str(artifact))
assert 'require "winget-depend-0" "Fixture.Tool" "package" "Fixture.Runtime"' in \
requirements, requirements
assert "depends 1 packages of the same catalog" in package
# the payload installs as ordinary files and nothing executes
target = root / "target"
(target / "usr/bin").mkdir(parents=True)
call("db", "init", "--root", target)
call("cache", "stage", "local:" + str(artifact), "--root", target)
runtime = root / "trees/runtime"
(runtime / "HOLY").mkdir(parents=True)
(runtime / "DATA/usr/lib/fixture").mkdir(parents=True)
(runtime / "DATA/usr/lib/fixture/note").write_text("fixture runtime\n")
(runtime / "HOLY/meta").write_text(
"format holy-package-1\nname Fixture.Runtime\nversion 1\nrelease 1\nos linux\n"
"arch noarch\nlibc nolibc\n")
for field in ("deps", "provides", "hooks", "origin", "transform"):
(runtime / "HOLY" / field).write_text("")
call("manifest", "generate", runtime, "--output", root / "runtime-files")
(runtime / "HOLY/files").write_text((root / "runtime-files").read_text())
call("pack", runtime, "--output", root / "runtime.holy")
call("cache", "stage", "local:" + str(root / "runtime.holy"), "--root", target)
digests = [hashlib.sha256(artifact.read_bytes()).hexdigest(),
hashlib.sha256((root / "runtime.holy").read_bytes()).hexdigest()]
plan = call("db", "plan-set", *digests, "--root", target)
plan_sha = [line.split()[10] for line in plan.splitlines()
if line.startswith("plan-set ")][0]
applied = call("db", "apply-set", plan_sha, *digests, "--root", target)
assert "artifacts 2" in applied, applied
assert (target / "usr/lib/holy/private/Fixture.Tool/tool/tool.exe").read_text() == \
"MZ a program for another operating system\n"
assert not (target / "usr/bin/Fixture.Tool").exists()
call("db", "check", "--all", "--root", target)
# a digest the manifest does not pin is refused, in either case
for value, wanted in (("0" * 64, "the manifest pins"),
(hashlib.sha256(b"other").hexdigest(), "has the digest")):
case = root / ("case-" + wanted.split()[0])
case.mkdir()
manifest(case, "Fixture.Bad", {"bad/bad.exe": "MZ\n"})
body = (case / "Fixture.Bad.yaml").read_text()
for line in body.splitlines():
if line.startswith("InstallerSha256:"):
body = body.replace(line, f"InstallerSha256: {value}")
(case / "Fixture.Bad.yaml").write_text(body)
result = subprocess.run([binary, "import", case / "Fixture.Bad.yaml", "--source",
"winget", "--format", "winget", "--output",
root / (case.name + "-out")],
capture_output=True, text=True)
assert result.returncode == 6, result
assert wanted in result.stderr, result.stderr
assert not list((root / (case.name + "-out")).glob("*.holy"))
# a manifest without an identifier, a version, an address or a digest is refused
for field in ("PackageIdentifier", "PackageVersion", "InstallerUrl", "InstallerSha256"):
case = root / ("missing-" + field)
case.mkdir()
manifest(case, "Fixture.Missing", {"m/m.exe": "MZ\n"})
body = (case / "Fixture.Missing.yaml").read_text()
(case / "Fixture.Missing.yaml").write_text("\n".join(
line for line in body.splitlines() if not line.startswith(field + ":")) + "\n")
result = subprocess.run([binary, "import", case / "Fixture.Missing.yaml", "--source",
"winget", "--format", "winget", "--output",
root / (case.name + "-out")],
capture_output=True, text=True)
assert result.returncode == 2, (field, result)
assert "needs" in result.stderr, (field, result.stderr)
# an artifact that is not beside the manifest is a missing capability
absent = root / "absent"
absent.mkdir()
manifest(absent, "Fixture.Absent", {"a/a.exe": "MZ\n"})
(absent / "Fixture.Absent_fixture.zip").unlink()
result = subprocess.run([binary, "import", absent / "Fixture.Absent.yaml", "--source",
"winget", "--format", "winget", "--output", root / "absent-out"],
capture_output=True, text=True)
assert result.returncode == 6, result
assert "is not beside the manifest" in result.stderr, result.stderr
# an artifact with an escaping path is refused rather than placed
escape = root / "escape"
escape.mkdir()
manifest(escape, "Fixture.Escape", {"../outside.exe": "MZ\n"})
result = subprocess.run([binary, "import", escape / "Fixture.Escape.yaml", "--source",
"winget", "--format", "winget", "--output", root / "escape-out"],
capture_output=True, text=True)
assert result.returncode == 3, result
assert "cannot place" in result.stderr, result.stderr
# a manifest without a dependency block names none, and nothing is invented
plain = root / "plain"
plain.mkdir()
manifest(plain, "Fixture.Plain", {"p/p.exe": "MZ\n"}, drop=("dependencies",))
call("import", plain / "Fixture.Plain.yaml", "--source", "winget", "--format", "winget",
"--output", root / "plain-out", status=3)
plain_requirements = call("requirements", "local:" + str(
root / "plain-out/Fixture.Plain--noarch--nolibc.holy"))
assert plain_requirements.strip() == "requirements 0", plain_requirements
# text that is not a manifest is refused rather than half read
broken = root / "broken"
broken.mkdir()
(broken / "Fixture.Broken.yaml").write_text("# a comment\n")
result = subprocess.run([binary, "import", broken / "Fixture.Broken.yaml", "--source",
"winget", "--format", "winget", "--output", root / "broken-out"],
capture_output=True, text=True)
assert result.returncode == 2, result
assert "reads no other form" in result.stderr, result.stderr
result = subprocess.run([binary, "import", root / "absent.yaml", "--source", "winget",
"--format", "winget", "--output", root / "absent2-out"],
capture_output=True, text=True)
assert result.returncode == 6, result
assert "manifest unavailable" in result.stderr, result.stderr
print("winget manifest fixtures passed")
return 0
if __name__ == "__main__":
sys.exit(main())