Skip to content
Open
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
36 changes: 5 additions & 31 deletions posit-bakery/posit_bakery/plugins/builtin/oras/oras.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,24 +142,6 @@ def command(self) -> list[str]:
return cmd


class OrasManifestDelete(OrasCommand):
"""Delete a manifest from a registry.

This command deletes a manifest (image or index) from a registry.
"""

reference: Annotated[str, Field(description="The manifest reference to delete.")]

@property
def command(self) -> list[str]:
"""Build the oras manifest delete command."""
cmd = [self.oras_bin, "manifest", "delete", "--force"]
if self.plain_http:
cmd.append("--plain-http")
cmd.append(self.reference)
return cmd


class OrasMergeWorkflowResult(BaseModel):
"""Result of an ORAS merge workflow execution."""

Expand All @@ -175,7 +157,9 @@ class OrasMergeWorkflow(BaseModel):
This workflow:
1. Creates a temporary manifest index from platform-specific source images
2. Copies the index to all target registries/tags
3. Deletes the temporary index

The temporary index is left in place and is cleaned up out-of-band by the
``clean.yml`` workflow (``bakery clean temp-registry``) rather than deleted here.
"""

model_config = ConfigDict(arbitrary_types_allowed=True)
Expand Down Expand Up @@ -241,18 +225,8 @@ def run(self, dry_run: bool = False) -> OrasMergeWorkflowResult:
)
copy_cmd.run(dry_run=dry_run)

# Step 3: Delete the temporary index (non-fatal)
log.info(f"Cleaning up temporary index {self.temp_index_tag}")
delete_cmd = OrasManifestDelete(
oras_bin=self.oras_bin,
reference=self.temp_index_tag,
plain_http=self.plain_http,
)
try:
delete_cmd.run(dry_run=dry_run)
except BakeryToolRuntimeError as e:
log.warning(f"Failed to clean up temporary index {self.temp_index_tag}: {e}")

# The temporary index is intentionally left in place; it is cleaned up
# out-of-band by the clean.yml workflow (bakery clean temp-registry).
log.info(f"ORAS merge workflow completed successfully")
return OrasMergeWorkflowResult(
success=True,
Expand Down
45 changes: 3 additions & 42 deletions posit-bakery/test/plugins/builtin/oras/test_oras.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
find_oras_bin,
get_repository_from_ref,
OrasCopy,
OrasManifestDelete,
OrasManifestIndexCreate,
OrasMergeWorkflow,
OrasMergeWorkflowResult,
Expand Down Expand Up @@ -188,34 +187,6 @@ def test_run_success(self):
assert result.returncode == 0


class TestOrasManifestDelete:
"""Tests for the OrasManifestDelete command."""

def test_command_construction(self):
"""Test that the command is constructed correctly."""
cmd = OrasManifestDelete(
oras_bin="oras",
reference="ghcr.io/posit-dev/test/tmp:tag",
)

expected = ["oras", "manifest", "delete", "--force", "ghcr.io/posit-dev/test/tmp:tag"]
assert cmd.command == expected

def test_run_success(self):
"""Test successful delete execution."""
cmd = OrasManifestDelete(
oras_bin="oras",
reference="ghcr.io/posit-dev/test/tmp:tag",
)

with patch("subprocess.run") as mock_run:
mock_run.return_value = subprocess.CompletedProcess(args=cmd.command, returncode=0, stdout=b"", stderr=b"")
result = cmd.run()

mock_run.assert_called_once_with(cmd.command, capture_output=True)
assert result.returncode == 0


class TestOrasMergeWorkflow:
"""Tests for the OrasMergeWorkflow orchestrator."""

Expand Down Expand Up @@ -295,8 +266,9 @@ def test_execute_success(self, basic_workflow):
assert result.temp_index_ref is not None

# Should have called:
# 1 create + 2 copy (grouped by destination) + 1 delete = 4 calls
assert mock_run.call_count == 4
# 1 create + 2 copy (grouped by destination) = 3 calls.
# The temporary index is no longer deleted here; clean.yml handles it.
assert mock_run.call_count == 3

def test_execute_dry_run(self, basic_workflow):
"""Test dry run mode."""
Expand Down Expand Up @@ -519,17 +491,6 @@ def test_copy_with_plain_http(self):
expected = ["oras", "cp", "--plain-http", "localhost:5000/test:source", "localhost:5000/test:dest"]
assert cmd.command == expected

def test_manifest_delete_with_plain_http(self):
"""Test that --plain-http flag is included when plain_http=True."""
cmd = OrasManifestDelete(
oras_bin="oras",
reference="localhost:5000/test:tag",
plain_http=True,
)

expected = ["oras", "manifest", "delete", "--force", "--plain-http", "localhost:5000/test:tag"]
assert cmd.command == expected


@pytest.mark.slow
class TestOrasMergeWorkflowIntegration:
Expand Down
Loading