feat(release): publish immutable commit bundles
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from bot_bottle.release_bundle import build_bundle_index, canonical_bytes
|
||||
from bot_bottle.release_manifest import ReleaseManifestError
|
||||
from bot_bottle.release_publish import bundle_url, publish_bundle
|
||||
from tests.unit.test_release_manifest import manifest
|
||||
|
||||
|
||||
class TestReleasePublish(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
env = {
|
||||
"BOT_BOTTLE_RELEASE_BASE": "https://packages.example",
|
||||
"BOT_BOTTLE_RELEASE_OWNER": "owner",
|
||||
"BOT_BOTTLE_RELEASE_TOKEN": "token",
|
||||
}
|
||||
self.env = patch.dict("os.environ", env, clear=True)
|
||||
self.env.start()
|
||||
self.addCleanup(self.env.stop)
|
||||
|
||||
def bundle(self, root: Path) -> tuple[dict[str, object], Path]:
|
||||
wheel = root / "bot_bottle-0.1.0-py3-none-any.whl"
|
||||
wheel.write_bytes(b"wheel")
|
||||
index = build_bundle_index(
|
||||
manifest(),
|
||||
wheel=wheel,
|
||||
wheel_url=bundle_url("1" * 40, wheel.name),
|
||||
workflow_run="actions/123",
|
||||
published_at="2026-07-27T00:00:00Z",
|
||||
)
|
||||
return index, wheel
|
||||
|
||||
@staticmethod
|
||||
def wheel_sha(index: dict[str, object]) -> str:
|
||||
wheel = index["wheel"]
|
||||
assert isinstance(wheel, dict)
|
||||
digest = wheel["sha256"]
|
||||
assert isinstance(digest, str)
|
||||
return digest
|
||||
|
||||
def test_publishes_wheel_then_index(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
index, wheel = self.bundle(Path(tmp))
|
||||
with patch(
|
||||
"bot_bottle.release_publish.remote_bytes", return_value=None,
|
||||
), patch(
|
||||
"bot_bottle.release_publish.remote_sha256", return_value=None,
|
||||
), patch("bot_bottle.release_publish.upload") as upload:
|
||||
result = publish_bundle(index, wheel)
|
||||
self.assertTrue(result.endswith("/bundle-index.json"))
|
||||
self.assertEqual(2, upload.call_count)
|
||||
self.assertIs(wheel, upload.call_args_list[0].args[1])
|
||||
self.assertEqual(canonical_bytes(index), upload.call_args_list[1].args[1])
|
||||
|
||||
def test_exact_existing_bundle_is_idempotent(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
index, wheel = self.bundle(Path(tmp))
|
||||
with patch(
|
||||
"bot_bottle.release_publish.remote_bytes",
|
||||
return_value=canonical_bytes(index),
|
||||
), patch(
|
||||
"bot_bottle.release_publish.remote_sha256",
|
||||
return_value=self.wheel_sha(index),
|
||||
), patch("bot_bottle.release_publish.upload") as upload:
|
||||
publish_bundle(index, wheel)
|
||||
upload.assert_not_called()
|
||||
|
||||
def test_rejects_partial_publication(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
index, wheel = self.bundle(Path(tmp))
|
||||
with patch(
|
||||
"bot_bottle.release_publish.remote_bytes", return_value=None,
|
||||
), patch(
|
||||
"bot_bottle.release_publish.remote_sha256",
|
||||
return_value=self.wheel_sha(index),
|
||||
):
|
||||
with self.assertRaisesRegex(
|
||||
ReleaseManifestError, "administrative cleanup",
|
||||
):
|
||||
publish_bundle(index, wheel)
|
||||
|
||||
def test_rejects_existing_bundle_with_different_index(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
index, wheel = self.bundle(Path(tmp))
|
||||
with patch(
|
||||
"bot_bottle.release_publish.remote_bytes", return_value=b"other",
|
||||
), patch(
|
||||
"bot_bottle.release_publish.remote_sha256",
|
||||
return_value=self.wheel_sha(index),
|
||||
):
|
||||
with self.assertRaisesRegex(
|
||||
ReleaseManifestError, "already differs",
|
||||
):
|
||||
publish_bundle(index, wheel)
|
||||
Reference in New Issue
Block a user