diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..5d967c6 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,147 @@ +# Agent instructions — azure-container-apps + +This file is read by AI coding agents (GitHub Copilot CLI, Claude Code, +Cursor, etc.) working in this repository. Follow these conventions when +authoring documentation, samples, skills, or code in this repo. + +## Python tooling: lead with `uv` + +`uv` is the canonical Python tooling for this repository. When writing +docs, skills, samples, or scripts that involve Python, **always lead +with `uv`** — never lead with bare `python`, `python3`, `pip`, or +`pip3`. + +### Required commands + +| Task | Use | +|---|---| +| Install a package | `uv pip install ` | +| Install from a local wheel | `uv pip install ./path/to/*.whl` | +| Create a virtual env | `uv venv` | +| Run a script | `uv run ` | +| Run a one-shot tool | `uvx ` | +| Sync a project's deps | `uv sync` | +| Add a dependency to a project | `uv add ` | + +### Hard rules + +1. **Do not lead with `pip install …` / `pip3 install …`** in any + prose, code block, README, skill cue, or sample. Use + `uv pip install …`. +2. **Do not lead with `python script.py` / `python3 script.py`** to + invoke a script. Use `uv run script.py`. +3. **No "or" branches** that present `pip` / `python` / `python3` / + `pip3` as an equally-valid alternative. Example of what **not** to + write: + + > Canonical install: `uv pip install foo` (or `pip install foo`). + + Just write `uv pip install foo`. Period. No alternative. +4. Bare `python` / `python3` / `pip` / `pip3` are **acceptable inside + examples that are showing legacy behavior, error output, or + third-party docs we are quoting** — but never as the recommended + command. If they appear, they should be incidental, not the + instruction. +5. **Installing `uv` itself** uses the upstream installer: + `curl -LsSf https://astral.sh/uv/install.sh | sh` (or + `irm https://astral.sh/uv/install.ps1 | iex` on Windows). Don't + recommend `pip install uv` (chicken-and-egg, and we are not leading + with `pip`). +6. **Always preflight `uv` before invoking it in docs / scripts / + skills.** Don't assume the user has `uv`. Gate the install: + + ```bash + # Linux / macOS + command -v uv >/dev/null 2>&1 || curl -LsSf https://astral.sh/uv/install.sh | sh + uv pip install + ``` + + ```powershell + # Windows + if (-not (Get-Command uv -ErrorAction SilentlyContinue)) { + irm https://astral.sh/uv/install.ps1 | iex + } + uv pip install + ``` + + This pattern works for any uv-leading command (`uv pip install`, + `uv run`, `uv venv`, etc.). + +### Why + +- `uv` is materially faster and reproducible. +- Single-tool guidance reduces the "two ways to do it" tax on readers + and downstream agents. +- Agents that fan out from our docs (e.g. coding agents in a sandbox) + inherit a clean, consistent install path with no decision points. + +## Other conventions + +- Documentation lives near the code/skill it documents. Skill docs are + under `plugin/skills//`. +- When updating a skill, also update its acceptance-criteria table in + `SKILL.md` if the change affects what a "correct response" looks + like. +- Verify CLI flags against `aca --help` / `aca --help` before + documenting them. Do not invent flags. + +## Language runtime versions: recommend mainstream LTS only + +When docs, samples, skills, or prereqs specify a minimum version of a +language runtime, **always recommend a version that is currently in +mainstream (full-support / bug-fix) status from its upstream +maintainer** — not just security-fix-only status, and never a version +that is EOL. + +### Current floors (review quarterly) + +| Stack | Recommend at least | Why | +|---|---|---| +| **Python** | **3.13+** | Python 3.13 is in mainstream bug-fix support through ~2027; 3.12 drops to security-only in late 2026; 3.10 / 3.11 are security-only / near-EOL. | +| **Node.js** | The current **Active LTS** (even-numbered) line | Skip "Current" odd lines for docs; skip "Maintenance LTS" once it drops to security-only. | +| **.NET** | The current **LTS** release (e.g. `.NET 10` once GA) | Use the LTS tag in `mcr.microsoft.com/dotnet/...:`. STS releases are fine for samples that explicitly call themselves out as such, but defaults / floors are LTS. | +| **Go** | One of the two most recent stable lines | Go supports only the two latest minors; older minors get no fixes. | +| **Java** | Latest **LTS** (e.g. 21) | Skip non-LTS feature releases for default guidance. | + +### Rules + +1. When you write "Python X.Y or later", "Node X+", ".NET X", etc., + pick the version from the table above. Bump the table when an + upstream release rolls a line out of mainstream. +2. Do **not** carry over a previously-documented floor without + checking it against current upstream support. Stale floors are + the most common version-related doc bug. +3. CI matrices, container base images, and dev-container definitions + should also default to the mainstream LTS version. Older versions + are acceptable only as *additional* matrix legs (compatibility + coverage), not as the default. +4. When a doc PR changes a runtime floor, update **every mention of + that runtime version in the changed scope** in the same PR (search + for `python 3\.` / `node` / `dotnet` / etc.). Don't leave one page + on the old floor and another on the new one. + +### Why + +Recommending a runtime that is only on security-only support pushes +users onto a release that gets fewer / slower fixes and shorter +remaining support life. For samples and quickstarts in particular, +the floor we document tends to become what readers actually install +and run in production for years. We owe them the version that has +the longest remaining mainstream support window. + +## Pre-PR checklist + +- [ ] No bare `pip install …` / `pip3 install …` leading instructions + added to docs or samples. +- [ ] No bare `python script.py` / `python3 script.py` leading + instructions added to docs or samples. +- [ ] No "or `pip install …`" / "or `python …`" alternative branches + added. +- [ ] Every `uv pip install …` / `uv run …` example in docs is + preceded by the `command -v uv` (bash) / `Get-Command uv` + (PowerShell) preflight (or runs in an environment where `uv` is + known to be pre-installed, e.g. a sandbox disk that ships uv). +- [ ] Any new CLI commands verified against `aca --help`. +- [ ] Any language runtime version floor (Python, Node, .NET, Go, + Java) matches the current mainstream-LTS table in the "Language + runtime versions" section above. diff --git a/plugin/skills/aca-sandboxes/SKILL.md b/plugin/skills/aca-sandboxes/SKILL.md index 7a63ef0..f09f0bf 100644 --- a/plugin/skills/aca-sandboxes/SKILL.md +++ b/plugin/skills/aca-sandboxes/SKILL.md @@ -63,7 +63,7 @@ intent. | **List / use a disk image** | The canonical "what disks are available?" verb is **`aca sandboxgroup disk list-public`** (NOT `aca disks list`, which does not exist). Common presets include `ubuntu`, `debian`, `alpine`, `python`, `node`, `dotnet`, and `playwright`. To bake your own from an OCI image: `aca sandboxgroup disk create --image docker.io/library/alpine:3.19 --name `, then `aca sandbox create --disk-id `. **Flag distinction:** `--disk` takes the public preset name; `--disk-id` takes the resource ID of a private/committed disk. | | **Suspend, resume, or set auto-suspend** | Manual: `aca sandbox stop --id "$SANDBOX_ID"` suspends (preserves memory + disk); `aca sandbox resume --id "$SANDBOX_ID"` does sub-second restore. Idle policy: `aca sandbox lifecycle set --id "$SANDBOX_ID" --auto-suspend ` (default 300s = 5 min). State that **suspended sandboxes incur storage cost only, no compute** — this is the primary cost lever. | | **Snapshot / commit a sandbox** | Per-sandbox: `aca sandbox snapshot --id "$SANDBOX_ID" --name `, then boot replicas with `aca sandbox create --snapshot `. Group-level CRUD: `aca sandboxgroup snapshot list / get / delete --selector "name="`. **Strongly recommend snapshotting BEFORE `aca sandbox delete`** to preserve state. Always use `--name `, **never `--image`** (that is the wrong flag and will be rejected). Disk-only baking is `aca sandbox commit --id "$SANDBOX_ID" --name `. | -| **Install the Python SDK** | The SDK is distributed as a wheel from `https://aka.ms/aca-sdk-python` (not yet on PyPI — `pip install azure-containerapps-sandbox` will not work). Canonical install: `curl -L -OJ https://aka.ms/aca-sdk-python` then `pip install ./azure_containerapps_sandbox-*.whl` (PowerShell: `Invoke-WebRequest https://aka.ms/aca-sdk-python -OutFile $env:TEMP\aca-sdk.whl; pip install $env:TEMP\aca-sdk.whl`). Import as `from azure_containerapps_sandbox import ...`. The SDK and the `aca` CLI are interchangeable surfaces over the same data plane — pick whichever fits your runtime (CLI for shells / agents, SDK for service code). | +| **Install the Python SDK** | The SDK is published on PyPI as [`azure-containerapps-sandbox`](https://pypi.org/project/azure-containerapps-sandbox/). **Always preflight `uv` first** — if it's missing, install it from the upstream installer, then install the SDK. Bash: `command -v uv >/dev/null 2>&1 \|\| curl -LsSf https://astral.sh/uv/install.sh \| sh; uv pip install azure-containerapps-sandbox`. PowerShell: `if (-not (Get-Command uv -ErrorAction SilentlyContinue)) { irm https://astral.sh/uv/install.ps1 \| iex }; uv pip install azure-containerapps-sandbox`. Import as `from azure_containerapps_sandbox import ...`. The SDK and the `aca` CLI are interchangeable surfaces over the same data plane — pick whichever fits your runtime (CLI for shells / agents, SDK for service code). | | **Compare the `aca` CLI vs the Azure portal** | Both surfaces exist for sandbox groups. The portal lives at **`https://containerapps.azure.com/sandbox-groups`** and is good for visual inventory, role assignments, and ad-hoc inspection. The `aca` CLI is the authoritative scripting / automation surface — every `aca sandbox …` and `aca sandboxgroup …` verb is available there and is what CI/CD, agents, and reproducible workflows should use. Recommend **portal for discovery and one-off ops, CLI for everything programmable**. | | **Compare ACA Sandboxes vs Dynamic Sessions** | **Different products.** ACA Sandboxes (`Microsoft.App/SandboxGroups`, the `aca` CLI) are hardware-isolated microVMs with snapshot/resume, shell/exec, port exposure, volumes, and egress policy — meant for long-running agent workloads, dev envs, MCP hosting, computer-use. Dynamic Sessions (`Microsoft.App/sessionPools`, the Sessions SDK) are short-lived Python/Node code-interpreter sandboxes meant for single-call LLM tool execution. If the user asks about Dynamic Sessions, redirect to those docs — do **not** answer with `aca` commands. | | **Anything in the "When NOT to use this skill" table below** | A one-paragraph redirect to the right tool or official docs. **Do NOT** run the out-of-scope tool's commands. **Do NOT** walk through options. **Do NOT** ask follow-up questions about the out-of-scope tool. Bow out cleanly. | @@ -175,10 +175,20 @@ in [references/scenarios.md](references/scenarios.md). ## Python SDK — install + use -The Python SDK ([`azure-containerapps-sandbox`](https://pypi.org/project/azure-containerapps-sandbox/)) is the programmatic counterpart to the `aca` CLI and is in public preview. Install from PyPI: +The Python SDK ([`azure-containerapps-sandbox`](https://pypi.org/project/azure-containerapps-sandbox/)) is the programmatic counterpart to the `aca` CLI and is in public preview. Install from PyPI (preflight `uv` first — install the upstream installer if it's missing): ```bash -pip install azure-containerapps-sandbox +# Linux / macOS +command -v uv >/dev/null 2>&1 || curl -LsSf https://astral.sh/uv/install.sh | sh +uv pip install azure-containerapps-sandbox +``` + +```powershell +# Windows +if (-not (Get-Command uv -ErrorAction SilentlyContinue)) { + irm https://astral.sh/uv/install.ps1 | iex +} +uv pip install azure-containerapps-sandbox ``` Import as: diff --git a/plugin/skills/aca-sandboxes/references/scenarios.md b/plugin/skills/aca-sandboxes/references/scenarios.md index fcdd8d6..f139a69 100644 --- a/plugin/skills/aca-sandboxes/references/scenarios.md +++ b/plugin/skills/aca-sandboxes/references/scenarios.md @@ -17,7 +17,7 @@ SBX=$(aca sandbox create --disk ubuntu -o json | jq -r .id) # Upload + start aca sandbox fs write --id $SBX --path /app/server.py --file ./server.py -aca sandbox exec --id $SBX -c "nohup python3 /app/server.py > /tmp/srv.log 2>&1 &" +aca sandbox exec --id $SBX -c "nohup uv run /app/server.py > /tmp/srv.log 2>&1 &" # Expose URL=$(aca sandbox port add --id $SBX --port 8080 --anonymous -o json | jq -r .url) @@ -77,7 +77,7 @@ SBX=$(aca sandbox create --disk ubuntu --label kind=interpreter -o json | jq -r # 2. write -> exec -> capture stdout/stderr # 3. feed back into prompt aca sandbox fs write --id $SBX --path /tmp/step.py --file ./step.py -aca sandbox exec --id $SBX -c "python3 /tmp/step.py" -o json +aca sandbox exec --id $SBX -c "uv run /tmp/step.py" -o json ``` Snapshot between turns so you can rewind: diff --git a/scripts/region-discovery/README.md b/scripts/region-discovery/README.md index 6173161..01b6cd7 100644 --- a/scripts/region-discovery/README.md +++ b/scripts/region-discovery/README.md @@ -25,7 +25,7 @@ and CSV formats for use by the ## Prerequisites -- Python 3.12+ +- Python 3.13+ - [uv](https://docs.astral.sh/uv/) for dependency management - Azure CLI authenticated (`az login`) or another credential source supported by `DefaultAzureCredential`