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
23 changes: 16 additions & 7 deletions scripts/nvx_tools/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -915,20 +915,29 @@ def build_initramfs(config: InitramfsBuildConfig) -> None:
provenance_path = output.with_name(InitramfsBuildConstants.PROVENANCE_NAME)
if provenance_inputs is not None:
provenance_path.unlink(missing_ok=True)
# Snapshot before the root is prepared so the manifest describes the sources
# this build consumed; the post-pack comparison catches any edit in between.
ubuntu_input_sha256 = (
ubuntu.converter_input_sha256(ubuntu.customization_files())
if descriptor.name == "ubuntu"
else None
)
root = _prepare_guest_root(config, descriptor)
helpers = _install_guest_files(config, root, descriptor)
if descriptor.name == "ubuntu":
ubuntu.apply_metadata_policy(root)
else:
_write_apk_manifest(root, output, helpers)
_pack_initramfs(root, output)
if descriptor.name == "ubuntu":
_write_ubuntu_manifest(
root,
output,
helpers,
ubuntu.converter_input_sha256(ubuntu.customization_files()),
)
if ubuntu_input_sha256 is not None:
_write_ubuntu_manifest(root, output, helpers, ubuntu_input_sha256)
if (
ubuntu.converter_input_sha256(ubuntu.customization_files())
!= ubuntu_input_sha256
):
output.unlink(missing_ok=True)
package_manifest.unlink(missing_ok=True)
raise ScriptError("initramfs source inputs changed during the build")
if (
provenance_inputs is not None
and initramfs_provenance_inputs() != provenance_inputs
Expand Down
69 changes: 69 additions & 0 deletions scripts/test_nvx_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -9208,6 +9208,75 @@ def test_ubuntu_initramfs_manifest_binds_artifact_digest(self):
)
self.assertEqual(manifest["input_sha256"], "a" * 64)

def test_ubuntu_initramfs_rejects_source_mutation_during_pack(self):
# The manifest must describe the sources the build actually consumed,
# so an edit made while the root is packed must fail the build.
for mutate in (False, True):
with (
self.subTest(mutate=mutate),
tempfile.TemporaryDirectory() as temporary,
):
root = Path(temporary)
source = root / "nvx-bashrc"
source.write_bytes(b"old\n")
output = root / "output" / "initramfs-ubuntu.cpio.gz"
output.parent.mkdir()

def digest(files: tuple[Path, ...]) -> str:
content = b"".join(Path(path).read_bytes() for path in files)
return hashlib.sha256(content).hexdigest()

def pack(
_root: Path,
archive: Path,
mutate: bool = mutate,
source: Path = source,
) -> None:
archive.write_bytes(b"old initramfs")
if mutate:
source.write_bytes(b"new\n")

with (
patch.object(build, "_require_linux"),
patch.object(build, "_prepare_guest_root", return_value=root),
patch.object(build, "_install_guest_files", return_value={}),
patch.object(build, "_pack_initramfs", side_effect=pack),
patch.object(ubuntu, "customization_files", return_value=(source,)),
patch.object(ubuntu, "converter_input_sha256", side_effect=digest),
patch.object(ubuntu, "apply_metadata_policy"),
patch.object(
ubuntu,
"package_manifest",
return_value={"format": 1, "guest": "ubuntu"},
),
):
config = build_config.InitramfsBuildConfig(
guest="ubuntu",
work=root / "work",
output=output,
)
if mutate:
with self.assertRaisesRegex(
common.ScriptError,
"inputs changed during the build",
):
build.build_initramfs(config)
self.assertFalse(output.exists())
self.assertFalse(
output.with_name(f"{output.name}.packages.json").exists()
)
else:
build.build_initramfs(config)
manifest = json.loads(
output.with_name(f"{output.name}.packages.json").read_text(
encoding="utf-8"
)
)
self.assertEqual(
manifest["input_sha256"],
digest((source,)),
)

def test_ubuntu_source_requirements_deduplicate_binary_manifests(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
Expand Down
Loading