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
10 changes: 8 additions & 2 deletions .github/scripts/infra_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@
# What pip in an agent image's build answers for the agentenv-framework-protocol its release published moments earlier,
# until PyPI's index, which a CDN caches, lists it: pinned (an agent's), or a floor it names (the browser's recorder).
_NOT_YET_SERVED = re.compile(r"No matching distribution found for agentenv-framework-protocol(\[[^\]]*\])?(==|>=)")
# What GHCR answers a push to a new package while another platform's push creates it, until it links the package to
# this repository.
_NOT_YET_LINKED = re.compile(r"denied: permission_denied: write_package")
# How long to wait before each try again, so a burst of builds starting together spreads out.
_RETRY_WAITS_SECONDS = (30, 60, 120)

Expand Down Expand Up @@ -308,13 +311,16 @@ def _tried_again(attempt: Callable[[], subprocess.CompletedProcess]) -> subproce

def _why_again(result: subprocess.CompletedProcess) -> str | None:
"""Why a failed run can work later, or None: a registry rate-limited it, as public.ecr.aws does past one anonymous
pull a second, which builds starting together pass; or a build's pip can't find the agentenv-framework-protocol its
release has just published, which PyPI's index lists a little after the upload."""
pull a second, which builds starting together pass; a build's pip can't find the agentenv-framework-protocol its
release has just published, which PyPI's index lists a little after the upload; or GHCR refuses a push to a package
another platform's push is creating, as an image's first release pushes both at once."""
printed, command = f"{result.stdout}{result.stderr}", f"`{' '.join(result.args)[:_PRINTED_CHARS]}`"
if _RATE_LIMITED.search(printed):
return f"a registry rate-limited {command}"
if _NOT_YET_SERVED.search(printed):
return f"PyPI doesn't list yet the agentenv-framework-protocol {command} installs"
if list(result.args[:2]) == ["docker", "push"] and _NOT_YET_LINKED.search(printed):
return f"GHCR doesn't let this repository write yet the package {command} pushes to"
return None


Expand Down
19 changes: 19 additions & 0 deletions tst/unit/test_infra_build_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,25 @@ def test_retry_tries_again_while_pypi_doesnt_list_the_protocol_floor_a_build_nam
assert len(runs) == 2 and waits == [script._RETRY_WAITS_SECONDS[0]]


def test_retry_tries_again_a_push_ghcr_refuses_while_another_platform_creates_the_package(script, monkeypatch, capsys):
"""An image's first release pushes both platforms at once; the second waits on GHCR linking the new package."""
runs, waits = _attempts(script, monkeypatch, [("denied: permission_denied: write_package\n", 1), ("pushed\n", 0)])

assert _run(script, monkeypatch, "retry", "docker", "push", "img") == 0

assert len(runs) == 2 and waits == [script._RETRY_WAITS_SECONDS[0]]
assert ("::warning::GHCR doesn't let this repository write yet the package `docker push img` pushes to; trying again in"
in capsys.readouterr().out)


def test_retry_doesnt_try_again_a_denied_command_that_isnt_a_push(script, monkeypatch, capsys):
runs, waits = _attempts(script, monkeypatch, [("denied: permission_denied: write_package\n", 1)])

assert _run(script, monkeypatch, "retry", "docker", "build", ".") == 1

assert len(runs) == 1 and waits == []


def test_retry_doesnt_try_again_a_build_missing_another_package(script, monkeypatch, capsys):
runs, waits = _attempts(script, monkeypatch, [("ERROR: No matching distribution found for nonexistent-pkg==1.0\n", 1)])

Expand Down
Loading