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
11 changes: 7 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ scripts/validate-full.sh <path> # or another bundle repo
```

It creates a private throwaway `uv` venv with `pip`, `hatchling`, `pyyaml`, a
prebuilt public Core 1.6.1 wheel, and the pinned public `amplifier-app-cli`.
Foundation is overridden in that venv through `uv pip --overrides` to
`f13d08168e14b5bc4720fbb06c40936eb1a7a7d1`; it is not supplied as a second
direct requirement. It then invokes that venv's `amplifier` executable explicitly.
latest compatible public Core wheel and canonical `amplifier-app-cli@main`.
Foundation follows canonical `main` through `uv pip --overrides`; it is not
supplied as a second direct requirement. `--upgrade` refreshes cached resolution
for each new runtime. The script emits `CI_VALIDATE_RUNTIME` with actual package
versions and resolved Git revisions; retain that output with the validation
results. Core uses the published-wheel channel, not Git main. The script then
invokes that venv's `amplifier` executable explicitly.
PATH alone is insufficient because the CLI supplies its own interpreter to recipe
shell steps. It preserves the caller's Amplifier settings identity, including
`AMPLIFIER_HOME` when set.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ requires-python = ">=3.11"
license = "MIT"

dependencies = [
"amplifier-bundle-context-intelligence @ git+https://github.com/microsoft/amplifier-bundle-context-intelligence@2818e0519f1c59c71d6a5b53eee50592853f8d7e",
"amplifier-bundle-context-intelligence @ git+https://github.com/microsoft/amplifier-bundle-context-intelligence@main",
]

[project.entry-points."amplifier.modules"]
Expand Down
24 changes: 18 additions & 6 deletions scripts/validate-full.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
set -euo pipefail

REPO_PATH="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
CLI_REF="14dc68eba05bf65b8c6dea28c3a2db93daa12d38"
FOUNDATION_REF="f13d08168e14b5bc4720fbb06c40936eb1a7a7d1"
CLI_REF="main"
FOUNDATION_REF="main"

# Select before installing: never guess between cached recipe revisions.
# The caller may choose a file explicitly without changing settings or caches.
Expand Down Expand Up @@ -94,15 +94,16 @@ export PYTHONNOUSERSITE=1

echo ">> building isolated validation runtime: $VENV"
uv venv --python 3.11 --allow-existing "$VENV" >/dev/null
# The public CLI declares Foundation@main; override that dependency rather than
# supplying a second, conflicting direct requirement.
# Refresh canonical Git main sources and the latest compatible published Core
# wheel for each new validation runtime. The CLI declares Foundation through
# tool.uv.sources; align it via an override rather than a conflicting direct URL.
printf '%s\n' \
"amplifier-foundation @ git+https://github.com/microsoft/amplifier-foundation@$FOUNDATION_REF" \
> "$VENV/overrides.txt"
uv pip install --python "$VENV/bin/python" --quiet \
uv pip install --python "$VENV/bin/python" --quiet --upgrade \
--only-binary amplifier-core \
--overrides "$VENV/overrides.txt" \
pip hatchling pyyaml "amplifier-core==1.6.1" \
pip hatchling pyyaml amplifier-core \
"amplifier-app-cli @ git+https://github.com/microsoft/amplifier-app-cli@$CLI_REF"

if ! "$VENV/bin/python" -c 'import pip, hatchling, yaml, amplifier_core, amplifier_foundation'; then
Expand All @@ -114,6 +115,17 @@ if [[ ! -x "$VENV/bin/amplifier" ]]; then
exit 1
fi

# Emit only package versions and resolved Git revisions, never credentials or
# environment values. The temporary runtime is removed; retain this output with
# the validation results. Core uses the published wheel channel, not Git main.
"$VENV/bin/python" -c 'import importlib.metadata as metadata,json
packages={}
for name in ("amplifier-core", "amplifier-app-cli", "amplifier-foundation"):
dist=metadata.distribution(name)
direct=json.loads(dist.read_text("direct_url.json") or "{}")
packages[name]={"version":dist.version,"vcs":direct.get("vcs_info")}
print("CI_VALIDATE_RUNTIME="+json.dumps({"core_channel":"latest-published-wheel","packages":packages},sort_keys=True))'

# JSON encoding preserves spaces, quotes, and backslashes in the target path.
CONTEXT="$("$VENV/bin/python" -c 'import json,sys; print(json.dumps({"repo_path": sys.argv[1], "enhance_diagrams": "false"}))' "$REPO_PATH")"

Expand Down
17 changes: 12 additions & 5 deletions tests/test_validate_full.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

REPO_ROOT = Path(__file__).parent.parent
SCRIPT_PATH = REPO_ROOT / "scripts" / "validate-full.sh"
CLI_REF = "14dc68eba05bf65b8c6dea28c3a2db93daa12d38"
FOUNDATION_REF = "f13d08168e14b5bc4720fbb06c40936eb1a7a7d1"
CLI_REF = "main"
FOUNDATION_REF = "main"


def _write_executable(path: Path, content: str) -> None:
Expand Down Expand Up @@ -48,6 +48,10 @@ def _create_fake_uv(bin_dir: Path) -> None:
[[ "${FAKE_FAIL_PRIVATE_IMPORTS:-}" != "1" ]]
exit
fi
if [[ "$*" == *"CI_VALIDATE_RUNTIME="* ]]; then
printf '%s\\n' 'CI_VALIDATE_RUNTIME={"core_channel":"latest-published-wheel","packages":{}}'
exit
fi
if [[ "$*" == *"json.dumps"* ]]; then
printf '%s\\n' "$2" > "$FAKE_JSON_CODE"
printf '%s\\n' "$3" > "$FAKE_JSON_INPUT"
Expand Down Expand Up @@ -78,7 +82,8 @@ def _create_fake_uv(bin_dir: Path) -> None:
#!/usr/bin/env bash
exec "VENV_PYTHON" "$0" "$@"
AMPLIFIER
sed -i "s|VENV_PYTHON|$venv/bin/python|" "$venv/bin/amplifier"
sed "s|VENV_PYTHON|$venv/bin/python|" "$venv/bin/amplifier" > "$venv/bin/amplifier.tmp"
mv "$venv/bin/amplifier.tmp" "$venv/bin/amplifier"
chmod +x "$venv/bin/amplifier"
fi
""",
Expand Down Expand Up @@ -147,7 +152,7 @@ def _environment(
return environment


def test_launches_pinned_private_cli_and_preserves_paths_with_spaces(tmp_path: Path) -> None:
def test_launches_latest_private_cli_and_preserves_paths_with_spaces(tmp_path: Path) -> None:
"""The wrapper launches private tools without relocating caller settings identity."""
caller_amplifier_home = str(tmp_path / "caller amplifier home")
environment = _environment(tmp_path, caller_amplifier_home=caller_amplifier_home)
Expand Down Expand Up @@ -183,7 +188,9 @@ def test_launches_pinned_private_cli_and_preserves_paths_with_spaces(tmp_path: P
assert str(venv_path / "bin" / "python") in python_targets
assert "pip" in uv_args
assert "hatchling" in uv_args
assert "amplifier-core==1.6.1" in uv_args
assert "amplifier-core" in uv_args
assert "--upgrade" in uv_args
assert "CI_VALIDATE_RUNTIME=" in result.stdout
# The CLI supplies Core/Foundation through its own dependency closure.
# Repeating Foundation as a direct Git requirement conflicts with its
# tool.uv.sources mapping during a real install.
Expand Down
16 changes: 9 additions & 7 deletions tests/test_validate_full_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

import json
import os
from pathlib import Path
import subprocess
import sys
from pathlib import Path

import pytest

Expand Down Expand Up @@ -35,7 +35,9 @@ def launcher(tmp_path):
" b=pathlib.Path(sys.argv[-1])/'bin'; b.mkdir()\n"
" (b/'python').write_text('#!/bin/sh\\n"
'if [ "$2" = "import pip, hatchling, yaml, amplifier_core, amplifier_foundation" ]; '
f'then exit 0; fi\\nexec {sys.executable} "$@"\\n\')\n'
f"then exit 0; fi\\n"
'case "$2" in *CI_VALIDATE_RUNTIME=*) echo CI_VALIDATE_RUNTIME=\\"fake-resolved-receipt\\"; exit 0;; esac\\n'
f'exec {sys.executable} "$@"\\n\')\n'
" (b/'python').chmod(0o755)\n"
" (b/'amplifier').write_text('#!/bin/sh\\n"
f"exec {sys.executable} \"'+os.environ['CLI_SPY']+'\" \"$@\"\\n')\n"
Expand Down Expand Up @@ -81,6 +83,7 @@ def _run(launcher, **overrides):
capture_output=True,
text=True,
timeout=15,
check=False,
)


Expand All @@ -92,11 +95,10 @@ def test_runs_venv_cli_and_preserves_status_and_json_paths(launcher, status):
calls = [json.loads(line) for line in Path(env["CALL_LOG"]).read_text().splitlines()]
assert "--only-binary" in calls[1]
assert "--overrides" in calls[1]
assert (
"amplifier-foundation@f13d08168e14b5bc4720fbb06c40936eb1a7a7d1"
in Path(env["OVERRIDE_COPY"]).read_text()
)
assert "amplifier-core==1.6.1" in calls[1]
assert "amplifier-foundation@main" in Path(env["OVERRIDE_COPY"]).read_text()
assert "amplifier-core" in calls[1]
assert "--upgrade" in calls[1]
assert "CI_VALIDATE_RUNTIME=" in result.stdout
assert not any("amplifier-core@" in arg for arg in calls[1])
response = json.loads(Path(env["CLI_RESULT"]).read_text())
context = json.loads(next(arg[8:] for arg in response["args"] if arg.startswith("context=")))
Expand Down
Loading