Public Access
DEVX-158: fix: delete existing manifest before push (Gitea #31964 workaround)
Co-authored-by: emo <emo@oblachno.com>
This commit was merged in pull request #290.
This commit is contained in:
@@ -14,6 +14,7 @@ import devx.tools.build_image as build_image
|
||||
from devx.tools.build_image import (
|
||||
ImageSpec,
|
||||
build_full_tag,
|
||||
delete_remote_manifest,
|
||||
load_manifest,
|
||||
push_image,
|
||||
registry_login,
|
||||
@@ -216,6 +217,119 @@ class TestPushImage:
|
||||
assert push_image(spec, "git.example.com", dry_run=True) is True
|
||||
mock_run.assert_not_called()
|
||||
|
||||
def test_delete_before_push_with_creds(self) -> None:
|
||||
spec = ImageSpec(name="ci-base", dockerfile="Dockerfile", tags=["latest"])
|
||||
mock_result = MagicMock(returncode=0, stderr="", stdout="")
|
||||
with (
|
||||
patch("devx.tools.build_image.subprocess.run", return_value=mock_result),
|
||||
patch("devx.tools.build_image.delete_remote_manifest", return_value=True) as mock_del,
|
||||
):
|
||||
assert push_image(spec, "git.example.com", username="user", token="tok") is True
|
||||
mock_del.assert_called_once_with(
|
||||
"git.example.com",
|
||||
"ci-base",
|
||||
"latest",
|
||||
"user",
|
||||
"tok",
|
||||
dry_run=False,
|
||||
)
|
||||
|
||||
def test_no_delete_without_creds(self) -> None:
|
||||
spec = ImageSpec(name="ci-base", dockerfile="Dockerfile", tags=["latest"])
|
||||
mock_result = MagicMock(returncode=0, stderr="", stdout="")
|
||||
with (
|
||||
patch("devx.tools.build_image.subprocess.run", return_value=mock_result),
|
||||
patch("devx.tools.build_image.delete_remote_manifest") as mock_del,
|
||||
):
|
||||
assert push_image(spec, "git.example.com") is True
|
||||
mock_del.assert_not_called()
|
||||
|
||||
|
||||
class TestDeleteRemoteManifest:
|
||||
def test_dry_run(self) -> None:
|
||||
assert delete_remote_manifest("git.example.com", "ci-base", "latest", "u", "t", dry_run=True) is True
|
||||
|
||||
def test_tag_not_found(self) -> None:
|
||||
import urllib.error
|
||||
|
||||
with patch("devx.tools.build_image.urllib.request.urlopen") as mock_urlopen:
|
||||
mock_urlopen.side_effect = urllib.error.HTTPError("url", 404, "Not Found", {}, None)
|
||||
assert delete_remote_manifest("git.example.com", "ci-base", "latest", "u", "t") is True
|
||||
|
||||
def test_delete_success(self) -> None:
|
||||
mock_head_resp = MagicMock()
|
||||
mock_head_resp.__enter__.return_value.headers.get.return_value = "sha256:abc123"
|
||||
mock_del_resp = MagicMock()
|
||||
with patch("devx.tools.build_image.urllib.request.urlopen") as mock_urlopen:
|
||||
mock_urlopen.side_effect = [mock_head_resp, mock_del_resp]
|
||||
assert delete_remote_manifest("git.example.com", "ci-base", "latest", "u", "t") is True
|
||||
|
||||
def test_delete_404_treated_as_success(self) -> None:
|
||||
import urllib.error
|
||||
|
||||
mock_head_resp = MagicMock()
|
||||
mock_head_resp.__enter__.return_value.headers.get.return_value = "sha256:abc123"
|
||||
with patch("devx.tools.build_image.urllib.request.urlopen") as mock_urlopen:
|
||||
mock_urlopen.side_effect = [
|
||||
mock_head_resp,
|
||||
urllib.error.HTTPError("url", 404, "Not Found", {}, None),
|
||||
]
|
||||
assert delete_remote_manifest("git.example.com", "ci-base", "latest", "u", "t") is True
|
||||
|
||||
def test_head_error_does_not_block(self) -> None:
|
||||
import urllib.error
|
||||
|
||||
with patch("devx.tools.build_image.urllib.request.urlopen") as mock_urlopen:
|
||||
mock_urlopen.side_effect = urllib.error.HTTPError("url", 500, "Server Error", {}, None)
|
||||
assert delete_remote_manifest("git.example.com", "ci-base", "latest", "u", "t") is True
|
||||
|
||||
def test_url_error_does_not_block(self) -> None:
|
||||
import urllib.error
|
||||
|
||||
with patch("devx.tools.build_image.urllib.request.urlopen") as mock_urlopen:
|
||||
mock_urlopen.side_effect = urllib.error.URLError("network down")
|
||||
assert delete_remote_manifest("git.example.com", "ci-base", "latest", "u", "t") is True
|
||||
|
||||
def test_no_digest_does_not_block(self) -> None:
|
||||
mock_head_resp = MagicMock()
|
||||
mock_head_resp.__enter__.return_value.headers.get.return_value = None
|
||||
with patch("devx.tools.build_image.urllib.request.urlopen") as mock_urlopen:
|
||||
mock_urlopen.return_value = mock_head_resp
|
||||
assert delete_remote_manifest("git.example.com", "ci-base", "latest", "u", "t") is True
|
||||
|
||||
def test_head_405_passes_through(self) -> None:
|
||||
import urllib.error
|
||||
|
||||
with patch("devx.tools.build_image.urllib.request.urlopen") as mock_urlopen:
|
||||
mock_urlopen.side_effect = urllib.error.HTTPError("url", 405, "Method Not Allowed", {}, None)
|
||||
# 405 falls through with pass, digest never set, returns True
|
||||
assert delete_remote_manifest("git.example.com", "ci-base", "latest", "u", "t") is True
|
||||
assert mock_urlopen.call_count == 1
|
||||
|
||||
def test_delete_500_does_not_block(self) -> None:
|
||||
import urllib.error
|
||||
|
||||
mock_head_resp = MagicMock()
|
||||
mock_head_resp.__enter__.return_value.headers.get.return_value = "sha256:abc123"
|
||||
with patch("devx.tools.build_image.urllib.request.urlopen") as mock_urlopen:
|
||||
mock_urlopen.side_effect = [
|
||||
mock_head_resp,
|
||||
urllib.error.HTTPError("url", 500, "Server Error", {}, None),
|
||||
]
|
||||
assert delete_remote_manifest("git.example.com", "ci-base", "latest", "u", "t") is True
|
||||
|
||||
def test_delete_url_error_does_not_block(self) -> None:
|
||||
import urllib.error
|
||||
|
||||
mock_head_resp = MagicMock()
|
||||
mock_head_resp.__enter__.return_value.headers.get.return_value = "sha256:abc123"
|
||||
with patch("devx.tools.build_image.urllib.request.urlopen") as mock_urlopen:
|
||||
mock_urlopen.side_effect = [
|
||||
mock_head_resp,
|
||||
urllib.error.URLError("network down"),
|
||||
]
|
||||
assert delete_remote_manifest("git.example.com", "ci-base", "latest", "u", "t") is True
|
||||
|
||||
|
||||
class TestSortVersions:
|
||||
def test_sort_by_created_at_desc(self) -> None:
|
||||
@@ -588,11 +702,20 @@ class TestCLIBuildImage:
|
||||
"devx.tools.build_image.subprocess.run",
|
||||
side_effect=[login_result, build_result, push_result],
|
||||
):
|
||||
result = runner.invoke(
|
||||
build_image.main,
|
||||
["--dockerfile", str(dockerfile), "--name", "ci-base", "--push", "--registry", "git.example.com"],
|
||||
)
|
||||
assert result.exit_code != 0
|
||||
with patch("devx.tools.build_image.delete_remote_manifest", return_value=True):
|
||||
result = runner.invoke(
|
||||
build_image.main,
|
||||
[
|
||||
"--dockerfile",
|
||||
str(dockerfile),
|
||||
"--name",
|
||||
"ci-base",
|
||||
"--push",
|
||||
"--registry",
|
||||
"git.example.com",
|
||||
],
|
||||
)
|
||||
assert result.exit_code != 0
|
||||
|
||||
|
||||
class TestCLICleanImages:
|
||||
|
||||
Reference in New Issue
Block a user