feat(5): overleveringspakka er KUN kjørbar Python — container-innpakningen fjernet
Operatørdirektiv 14.08 etter ekstern test: mottakeren skal få kjørbar Python, ikke en Docker-innpakking. `Dockerfile` og `azure.yaml` er SLETTET fra treet. Sømmen er valgt av den eksisterende invarianten, ikke av smak: pakka ER `git archive HEAD`, så å ekskludere filene fra arkivet ville krevd en kurerings-mekanisme — den andre kopien av «hva mottakeren får», fri til å drifte fra HEAD (kø-(p)). Fjerning holder arkivet ukurert og gjør fraværet til en egenskap ved HEAD, som er det eneste en gate kan måle. De to gatene som pinnet flaten er håndtert bevisst: - 4e-rå-tekst-gaten (`--platform linux/amd64` + ÉN kopi av startkommandoen) er SLETTET, med et notat der den sto. En gate som pinner en fjernet flate kan bare bli grønn. - handover-gatens `_REQUIRED_MEMBERS` er ikke bare fratatt de to navnene, men erstattet av en POSITIV fraværs-assert pluss en dokument-gate. Å kun slutte å KREVE dem ville gitt en gate som ikke kan skille «fjernet» fra «shippes fortsatt». Startkommandoen har nå ÉN kopi igjen: DEPLOY.md-ens `python main.py`, som navngir inngangen subprosess-testen faktisk kjører.
This commit is contained in:
parent
642ce8ae9a
commit
605c02aa62
10 changed files with 215 additions and 205 deletions
|
|
@ -18,11 +18,11 @@ Load-bearing surface pinned here:
|
|||
* The server is asyncio on the ONE loop (NG1: ``test_no_thread_or_process_path_exists_under_src``
|
||||
ratchets src/ thread-free) — these tests run client and server as coroutines on the SAME loop,
|
||||
which only works because nothing in the server blocks it.
|
||||
* Root ``main.py`` is the ONE process entry (Dockerfile CMD + azure.yaml point at it): the
|
||||
* Root ``main.py`` is the ONE process entry (``python main.py``, the command DEPLOY.md prints): the
|
||||
subprocess test is the ONLY test that catches a detached shim or a detached SIGTERM handler
|
||||
(P4-presedensen: entry-point-mutasjoner fanges aldri av in-process-tester).
|
||||
|
||||
Fase 4e closes two gaps the above leaves open, and both are about things a stand-in cannot see:
|
||||
Fase 4e closed two gaps the above leaves open, and both are about things a stand-in cannot see:
|
||||
|
||||
* **The whitelist composes with the REAL ``run_project``.** Every test above hands ``invoke`` a
|
||||
stand-in that swallows ``**kwargs``, so the whitelist could name a field ``run_project`` does not
|
||||
|
|
@ -33,14 +33,11 @@ Fase 4e closes two gaps the above leaves open, and both are about things a stand
|
|||
PURPOSE (a caller must never choose the server's model client), so patching the factory the run
|
||||
falls back to is the only injection point this surface leaves — the same argument
|
||||
``test_run_cli_loadbearing`` makes for ``main()``.
|
||||
* **The deployment artifacts are raw-text-gated.** ``Dockerfile`` and ``azure.yaml`` are the two
|
||||
files that decide whether the image the platform pulls can run at all, and NO test executes
|
||||
them here (``docker build``/``azd deploy`` are operator-gated). A raw-text gate is therefore the
|
||||
only mechanism available: it pins ``--platform linux/amd64`` (measured required — spike §1.4;
|
||||
an arm64 image built on this Intel-free-of-charge assumption would fail only in the cloud) and
|
||||
the ONE-copy rule for the start command (the image's ``CMD``; ``azure.yaml`` carries no
|
||||
``startupCommand`` to drift from it). Guard-tester leser kildefiler som RÅ TEKST — reformulate
|
||||
the prose around them, never the strings they pin.
|
||||
* **The deployment artifacts were raw-text-gated** — until 14.08, when the operator directive
|
||||
after an external trial made the delivery runnable Python and the two artifacts were removed
|
||||
from the tree. The gate is deleted, not weakened; see the note where it stood, below the
|
||||
end-to-end test. The start command now has exactly one copy left, in DEPLOY.md, and
|
||||
``tests/test_handover_package_loadbearing.py`` is what keeps it there.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -381,8 +378,8 @@ def _blocking_get(url: str) -> tuple[int, str]:
|
|||
|
||||
|
||||
def test_main_entrypoint_serves_and_stops_on_sigterm() -> None:
|
||||
"""Root main.py is the ONE process entry (Dockerfile CMD + azure.yaml point at it): started
|
||||
as a subprocess it must serve /readiness and exit 0 on SIGTERM. This is the only test that
|
||||
"""Root main.py is the ONE process entry (``python main.py``, the command DEPLOY.md prints):
|
||||
started as a subprocess it must serve /readiness and exit 0 on SIGTERM. This is the only test that
|
||||
catches a shim that stops calling hosting.main() or a detached SIGTERM handler."""
|
||||
with socket.socket() as probe:
|
||||
probe.bind(("127.0.0.1", 0))
|
||||
|
|
@ -415,7 +412,7 @@ def test_main_entrypoint_serves_and_stops_on_sigterm() -> None:
|
|||
proc.wait()
|
||||
|
||||
|
||||
# --- Fase 4e: the whitelist against the REAL run_project, and the deployment artifacts ----------
|
||||
# --- Fase 4e: the whitelist against the REAL run_project -----------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
|
@ -491,51 +488,9 @@ async def test_invocations_answers_through_the_real_run_project(
|
|||
assert _scripted_backend, "the scripted backend was never called — no real run happened"
|
||||
|
||||
|
||||
def test_deployment_artifacts_pin_the_measured_platform_and_one_start_command() -> None:
|
||||
"""RAW-TEXT GATE on the two files that decide whether the hosted image runs at all.
|
||||
|
||||
Neither is executed by any test: ``docker build`` and ``azd deploy`` are operator-gated, so a
|
||||
regression in them is invisible to the whole suite until it fails in the cloud. What the gate
|
||||
pins is exactly what was MEASURED, and nothing about the prose around it:
|
||||
|
||||
* ``--platform linux/amd64`` — the platform requires x86_64 (spike §1.4). Dropping it makes the
|
||||
image inherit the builder's architecture, which on an arm64 laptop yields an image that
|
||||
builds green locally and cannot start in the cloud. The flag lives in the documented build
|
||||
command (a Dockerfile cannot set the build platform for its own invocation), so pinning the
|
||||
documented string is the only gate available — and it is worth having precisely because
|
||||
nothing else re-derives it.
|
||||
* ONE copy of the start command: the image's ``CMD`` names ``main.py``, and ``azure.yaml``
|
||||
declares NO ``startupCommand``. Two copies drift (kø-(p)-regelen); this is the pair that
|
||||
keeps there being one.
|
||||
|
||||
``env:`` is checked for the same reason: ``FOUNDRY_PROJECT_ENDPOINT`` is injected by the
|
||||
platform, and redeclaring it here could shadow the injected value — which is the failure mode
|
||||
Fase 4b's endpoint precedence exists to avoid, undone from the config side."""
|
||||
dockerfile = _REPO_ROOT / "Dockerfile"
|
||||
azure_yaml = _REPO_ROOT / "azure.yaml"
|
||||
assert dockerfile.is_file(), "the hosted image has no build definition"
|
||||
assert azure_yaml.is_file(), "azd has no project definition to deploy"
|
||||
|
||||
docker_text = dockerfile.read_text(encoding="utf-8")
|
||||
azure_text = azure_yaml.read_text(encoding="utf-8")
|
||||
|
||||
assert "--platform linux/amd64" in docker_text, (
|
||||
"the Dockerfile no longer names the measured build platform; an image built without it "
|
||||
"inherits the builder's architecture and cannot start on the hosting platform"
|
||||
)
|
||||
assert "CMD" in docker_text and "main.py" in docker_text, (
|
||||
"the image's CMD is the ONE copy of the start command and must name main.py"
|
||||
)
|
||||
# Line-anchored: a mention inside a comment is prose, a top-level key is a declaration.
|
||||
azure_keys = [line.split(":")[0].strip() for line in azure_text.splitlines()]
|
||||
assert "startupCommand" not in azure_keys, (
|
||||
"azure.yaml declares a startupCommand — a SECOND copy of the start command, free to drift "
|
||||
"from the image's CMD"
|
||||
)
|
||||
assert "env" not in azure_keys, (
|
||||
"azure.yaml declares an env block — FOUNDRY_PROJECT_ENDPOINT is injected by the platform "
|
||||
"and must never be redeclared here"
|
||||
)
|
||||
assert "host: azure.ai.agent" in azure_text and "kind: hosted" in azure_text, (
|
||||
"azure.yaml no longer declares the hosted-agent host this whole entrypoint targets"
|
||||
)
|
||||
# The 4e raw-text gate on ``Dockerfile``/``azure.yaml`` lived here until 14.08. It pinned
|
||||
# ``--platform linux/amd64`` and the one-copy rule for the image's ``CMD``. Both files were removed
|
||||
# from the tree that day (operator directive: the delivery is runnable Python), and a gate that pins
|
||||
# a surface we no longer ship is deleted with it rather than weakened into something that can only
|
||||
# be green. What replaced it lives in ``tests/test_handover_package_loadbearing.py``: the package
|
||||
# must carry NO container/azd wrapper and must document the Python start command.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue