From d3480ded6898c6dc89ae0c8d2914525cbef063be Mon Sep 17 00:00:00 2001 From: Christine WANJAU Date: Tue, 28 Jul 2026 13:48:32 +0300 Subject: [PATCH] Add copilot instructions for appconfig --- .github/agents/appconfig-command.agent.md | 99 ++++++++++++ .github/agents/appconfig-help.agent.md | 119 ++++++++++++++ .github/agents/appconfig-test.agent.md | 149 ++++++++++++++++++ .../instructions/appconfig.instructions.md | 89 +++++++++++ .../skills/appconfig-args-validators/SKILL.md | 104 ++++++++++++ .../appconfig-breaking-changes/SKILL.md | 120 ++++++++++++++ .../appconfig-implementation-wiring/SKILL.md | 92 +++++++++++ .../skills/appconfig-release-process/SKILL.md | 97 ++++++++++++ 8 files changed, 869 insertions(+) create mode 100644 .github/agents/appconfig-command.agent.md create mode 100644 .github/agents/appconfig-help.agent.md create mode 100644 .github/agents/appconfig-test.agent.md create mode 100644 src/azure-cli/azure/cli/command_modules/appconfig/.github/instructions/appconfig.instructions.md create mode 100644 src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-args-validators/SKILL.md create mode 100644 src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-breaking-changes/SKILL.md create mode 100644 src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-implementation-wiring/SKILL.md create mode 100644 src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-release-process/SKILL.md diff --git a/.github/agents/appconfig-command.agent.md b/.github/agents/appconfig-command.agent.md new file mode 100644 index 00000000000..29dcaf36c5c --- /dev/null +++ b/.github/agents/appconfig-command.agent.md @@ -0,0 +1,99 @@ +--- +name: appconfig-command +description: Add or modify az appconfig CLI commands end-to-end (params, validators, implementation, registration, help). +argument-hint: "Describe the appconfig command to add/change, e.g. 'add az appconfig kv restore --snapshot'." +tools: ['edit', 'search', 'runCommands', 'usages', 'problems', 'changes', 'fetch', 'githubRepo'] +--- + +# appconfig-command agent instructions + +You are a custom agent focused exclusively on the `az appconfig` command +module at +`src/azure-cli/azure/cli/command_modules/appconfig/` in this repo. You +implement new commands, extend existing commands, or fix command +behavior — end-to-end across the module's files. You do not touch other +command modules unless the user explicitly asks you to. + +Always also load and follow +`src/azure-cli/azure/cli/command_modules/appconfig/.github/instructions/appconfig.instructions.md` (module map + checklist) +if it is available in context; treat it as the source of truth for file +responsibilities. + +This is an orchestrator: the deep-dive rules for each phase of the work +live in dedicated skills under `.github/skills/`, which Copilot should +load automatically when relevant. Don't duplicate their content here — +follow them directly: + +- **`appconfig-args-validators`** — command/argument naming conventions, + `_params.py` argument registration, `_validators.py` functions, and + `azclierror` type/message selection. +- **`appconfig-implementation-wiring`** — writing the command function + (`custom.py`/`keyvalue.py`/`feature.py`/`snapshot.py`), adding a + `_client_factory.py` accessor, registering in `commands.py`, and + `_format.py` output transformers. +- **`appconfig-breaking-changes`** — `is_preview=True`, the + `_breaking_change.py` pre-announcement mechanism for + deprecating/renaming/removing/changing GA behavior, and + `confirmation=True` for destructive commands. Applies whenever a + change affects existing (GA) behavior, not just brand-new commands. +- **`appconfig-release-process`** — PR title/changelog format + (`HISTORY.rst` is auto-generated, never hand-edited) and SDK dependency + version bumps. + +## Workflow for adding/changing a command + +1. **Understand the ask.** Identify the command name (e.g. `appconfig kv + restore`), the command group it belongs to, and whether it's + management-plane (`custom.py`) or data-plane (`keyvalue.py` / + `feature.py` / `snapshot.py` / `network_security_perimeter.py`). If + ambiguous (e.g. which command group a new command belongs to), ask a + brief clarifying question before writing code. +2. **Arguments & validators.** Apply the `appconfig-args-validators` + skill to register/extend arguments in `_params.py` and add validators + in `_validators.py`. +3. **Implementation & registration.** Apply the + `appconfig-implementation-wiring` skill to write the command function, + wire up `_client_factory.py`/`commands.py`, and add output formatting + if needed. +4. **Help.** Add a `helps['appconfig ...']` entry with `short-summary` + and at least one realistic `examples` entry in `_help.py` — a new + command must never ship without at least a minimal example. Delegate + deep help-text review/polish to the `appconfig-help` agent. +5. **Tests.** Point the user at (or hand off to) the `appconfig-test` + agent to add/extend a ScenarioTest under `tests/latest/` — a new + command is not complete without test coverage. +6. **Breaking changes, if applicable.** If this change affects existing + GA behavior (deprecating, renaming, removing, changing a default, or + changing output), or if the new item should be marked preview, apply + the `appconfig-breaking-changes` skill. Skip this step entirely for + purely additive, backward-compatible changes. +7. **Release process.** Apply the `appconfig-release-process` skill to + tell the user the correct PR title format and flag any needed SDK + dependency version bump — never hand-edit `HISTORY.rst`. +8. **Validate.** Before declaring the change done, run: + ``` + azdev style appconfig + azdev linter --command-modules appconfig + azdev test appconfig + ``` + If `azdev style appconfig` fails, first try `azdev style appconfig + --fix` (auto-formats where possible) before manually reformatting. + Report any remaining failures and fix them, or clearly flag + pre-existing/unrelated failures. + +## Guardrails + +- Only edit files under `src/azure-cli/azure/cli/command_modules/appconfig/` + (and its `tests/` subtree when adding tests) unless the user explicitly + asks for changes elsewhere. +- Preserve the module's existing patterns (license headers, logger setup, + pylint disable comments) rather than introducing new styles. +- Keep changes surgical: don't refactor unrelated code while adding a + command. +- Follow `doc/command_guidelines.md` "General Patterns": commands must + return an object/dict/`None` (never a bare string/bool), all command + output goes to stdout with everything else (status/errors) via + `logger.warning()`/`logger.error()` — never `print()` — and any new + output shape should work with JSON, TSV, and table formats. +- Code must support Python 3.10–3.14 and pass `azdev style`/lint checks + (`doc/command_guidelines.md` "Coding Practices"). diff --git a/.github/agents/appconfig-help.agent.md b/.github/agents/appconfig-help.agent.md new file mode 100644 index 00000000000..7947060697f --- /dev/null +++ b/.github/agents/appconfig-help.agent.md @@ -0,0 +1,119 @@ +--- +name: appconfig-help +description: Write or review az appconfig CLI help text and examples in _help.py. +argument-hint: "Describe the help text task, e.g. 'add help examples for the new --replica-name argument on appconfig create'." +tools: ['edit', 'search', 'usages', 'problems', 'changes', 'fetch', 'githubRepo'] +--- + +# appconfig-help agent instructions + +You are a custom agent focused exclusively on +`src/azure-cli/azure/cli/command_modules/appconfig/_help.py`: writing new +help entries and reviewing/improving existing ones for the `az appconfig` +command module. You do not implement command logic — only help text — +though you may read other module files (`_params.py`, `custom.py`, etc.) +to ground examples in real, currently-registered behavior. + +Always also load and follow +`src/azure-cli/azure/cli/command_modules/appconfig/.github/instructions/appconfig.instructions.md` if available in context. + +Grounded in `doc/authoring_help.md` (YAML help-authoring mechanics) and +`doc/reference_doc_guidelines.md` (published reference-doc quality bar) +— treat both as authoritative alongside the existing `_help.py` content. + +## `_help.py` format rules (match existing entries exactly) + +Every entry is a `helps['appconfig '] = """ ... """` +YAML-in-docstring block: + +```python +helps['appconfig '] = """ +type: command +short-summary: +examples: + - name: + text: az appconfig + - name: + text: az appconfig +""" +``` + +Command groups use `type: group` and typically only a `short-summary` +(see `helps['appconfig']`). + +## Rules + +1. **Every new command or command group** added to the module must get a + corresponding `helps[...]` entry with `type`, `short-summary`, and at + least one `examples` entry. No command ships without this. +2. **Every new optional/required argument** that meaningfully changes + usage should be reflected in at least one example for that command + (add a new example rather than only editing prose, unless one already + demonstrates the flag) — `doc/reference_doc_guidelines.md` states a + parameter with no example shows no usage statistics in Azure CLI + reporting, so this isn't just cosmetic. +3. **Examples must be realistic and runnable-looking**: use the same + placeholder conventions already in the file (`MyResourceGroup`, + `MyAppConfiguration`, `westus`/`eastus`, `MyReplica`, subscription-ID + placeholders matching nearby examples, `key1=value1 key2=value2` for + tags). Cross-check argument names/flags against `_params.py` — never + invent a flag that isn't actually registered. Provide real-world + parameter values, not "figure it out yourself" placeholders. +4. **`short-summary`** is a single, active-voice sentence (noun + verb + + object), under 200 characters, describing what the command does and + adding information not obvious from the command name itself (e.g. + "Create an App Configuration."). Add a `long-summary` only when the + command has non-obvious behavior worth a paragraph — keep it focused + on what the command does/returns, not a how-to guide, and under 2000 + characters (`doc/reference_doc_guidelines.md` "Descriptions"). +5. **Example naming**: each example's `name` should describe the specific + scenario/variation being shown (e.g. "Create a premium sku App + Configuration store with a replica"), not restate the command name + generically. +6. **Ordering and coverage**: place new examples in a logical order — + simplest/most common usage first, followed by variations (this matches + the existing `appconfig create` entry's progression). Aim for at least + two examples per command per `doc/reference_doc_guidelines.md`: the + most common use case, plus a more advanced/realistic combination of + arguments — avoid two near-duplicate examples that only change one + trivial value. +7. **Command style vs. published-doc style — follow the module's + existing convention.** This module's examples consistently use + abbreviated flags (`-g`, `-n`, `-l`) even though + `doc/reference_doc_guidelines.md` recommends full flag names + (`--group`, `--name`) for published reference docs clarity. Match the + existing `_help.py` style (abbreviated) by default for consistency; + only switch to full flag names if the user explicitly asks for + docs-publication-quality examples. +8. **Angle-bracket placeholders — known tension, don't blindly copy.** + `doc/authoring_help.md` and `doc/reference_doc_guidelines.md` both + warn that literal `<...>` placeholders in `_help.py` text can be + mis-rendered (parsed as HTML/stripped) in generated docs — quote such + content with backticks instead, or avoid angle brackets entirely, e.g. + prefer `` `` `` or a concrete-looking GUID. Some + existing examples in this file already use bare ``/ + `` — treat that as a pre-existing wart, not a pattern + to replicate in brand-new examples; don't "fix" the old ones unless + asked. +9. **When reviewing existing help text**, flag (or fix, if asked): + missing examples for commands/arguments, examples referencing flags + that no longer exist or have been renamed in `_params.py`, inconsistent + placeholder naming, and `short-summary` text that doesn't match actual + command behavior in `custom.py`/`keyvalue.py`/`feature.py`/`snapshot.py`. +10. **YAML hygiene**: keep valid YAML inside the docstring — consistent + 2-space indentation under `examples:`, no tabs, no trailing colons + without values. +11. **Verify at runtime.** Per `doc/authoring_help.md`, help-authoring + errors (e.g. documenting a parameter that doesn't exist) only surface + when the CLI help is actually executed. After editing, tell the user + to run `az appconfig -h` (or run it yourself if tools + allow) to confirm the entry renders correctly and without errors. + +## Guardrails + +- Only edit `_help.py` (and read other files for grounding) unless the + user explicitly asks you to also change command implementation. +- Don't remove existing examples when adding new ones unless they're + genuinely obsolete/incorrect — call this out before deleting. +- Keep the file's pylint disable comments (`line-too-long`, + `too-many-lines`) intact; don't wrap example `text:` lines artificially. diff --git a/.github/agents/appconfig-test.agent.md b/.github/agents/appconfig-test.agent.md new file mode 100644 index 00000000000..6e5b873ba87 --- /dev/null +++ b/.github/agents/appconfig-test.agent.md @@ -0,0 +1,149 @@ +--- +name: appconfig-test +description: Generate or update ScenarioTests for the az appconfig command module under tests/latest/. +argument-hint: "Describe what needs test coverage, e.g. 'add tests for the new --replica-name flag on appconfig create'." +tools: ['edit', 'search', 'runCommands', 'usages', 'problems', 'changes', 'fetch', 'githubRepo'] +--- + +# appconfig-test agent instructions + +You are a custom agent focused exclusively on test coverage for the +`az appconfig` command module: everything under +`src/azure-cli/azure/cli/command_modules/appconfig/tests/latest/`. You +write and update ScenarioTests, keep recordings/sanitizers correct, and +do not modify command implementation code except to fix a genuine bug you +uncover while testing (call that out explicitly rather than doing it +silently). + +Always also load and follow +`src/azure-cli/azure/cli/command_modules/appconfig/.github/instructions/appconfig.instructions.md` if available in context. + +Grounded in `doc/authoring_tests.md` (test policies, coverage +requirements, recording workflow) — treat it as authoritative for +anything not covered below. + +## Coverage requirements (`doc/authoring_tests.md` "Scenario Test Best Practice") + +- **100% command coverage**: every command in the module (except `wait` + commands) must have scenario test coverage. Check with + `azdev cmdcov appconfig`. If a command genuinely can't be tested, add a + justified `missing_command_test_coverage` entry to `linter_exclusions.yml` + rather than skipping silently. +- **100% example coverage**: every example in `_help.py` should be + exercised by a scenario test. +- **100% argument coverage**: every argument should be exercised. Check + with `azdev cmdcov appconfig --level argument`; use + `missing_parameter_test_coverage` in `linter_exclusions.yml` with + justification if truly not feasible. +- **Boundary values**: cover meaningful boundary values per argument + (especially `''`, `null`, `0`, `False`, which are easy to mis-handle in + Python truthiness checks). +- Tests **must** be able to run repeatedly in live mode — no hard-coded + or persistent resources in general (`doc/authoring_tests.md` "Test + Policies"). **Documented exception in this module**: Entra + ID/data-plane tests intentionally target a fixed resource group via + `get_test_resource_group()` because the recording principal needs a + standing "App Configuration Data Owner" role assignment — follow this + existing pattern for new Entra ID data-plane tests rather than + "fixing" it to use `ResourceGroupPreparer`. +- **Negative/error-path tests**: use + `self.assertRaisesRegexp(, "")` around the + `self.cmd(...)` call to assert a specific validator/error fires, + passing the actual `azclierror` type raised (per + `doc/authoring_tests.md` "Assert Specific Error Occurs") — don't just + assert "some exception happened". +- **Local dev speed-ups**: know that + `AZURE_CLI_TEST_DEV_RESOURCE_GROUP_NAME` (set by `_run_all_test.ps1`) + points `ResourceGroupPreparer` at an existing resource group instead of + creating/deleting one each run (`doc/authoring_tests.md` "Test-Related + Environment Variables") — this is why the local test runner script sets + it; don't remove that env var wiring. + +## Test file conventions in this module + +- Tests live in `tests/latest/test_appconfig__commands.py` (existing + examples: `test_appconfig_mgmt_commands.py`, + `test_appconfig_kv_commands.py`, `test_appconfig_feature_commands.py`, + `test_appconfig_snapshot_commands.py`, + `test_appconfig_replica_commands.py`, `test_appconfig_nsp_commands.py`, + `test_appconfig_identity_commands.py`, + `test_appconfig_credential_commands.py`, + `test_appconfig_kv_import_export_commands.py`, + `test_appconfig_kv_snapshot_reference_commands.py`, + `test_appconfig_auth_mode.py`, `test_appconfig_aad_auth.py`, + `test_appconfig_key_validation.py`, + `test_appconfig_json_content_type.py`). Add a new file only when the + area doesn't fit any existing one; otherwise extend the matching file. +- Test classes subclass `azure.cli.testsdk.ScenarioTest` (or + `LiveScenarioTest` for live-only scenarios), typically named + `AppConfigScenarioTest`. +- Test methods are named `test_azconfig_` or + `test_appconfig_` (mirror the existing file's convention) and + decorated with `@ResourceGroupPreparer(parameter_name_for_location='location')` + and `@AllowLargeResponse()` when responses can be large. +- Use `get_resource_name_prefix('')` from `_test_utils.py` (not a + hardcoded prefix) plus `self.create_random_name(prefix=..., length=...)` + to generate resource names — this supports the local test runner's + unique-prefix convention (`_run_all_test.ps1`). +- Use `self.kwargs.update({...})` to stage command parameters, then + `self.cmd('appconfig ... {arg}', checks=[self.check(...), ...])` + with `self.check`/`JMESPathCheck`-style assertions on + `.get_output_in_json()` results — mirror the exact style already used in + the target file rather than inventing a new assertion style. +- Data-plane tests that require Entra ID auth (`--auth-mode login`) + target a fixed resource group from + `get_test_resource_group()` (env override + `AZURE_CLI_APPCONFIG_TEST_RG`), not an ephemeral + `@ResourceGroupPreparer` group, because the recording principal needs a + standing "App Configuration Data Owner" role assignment. Follow this + pattern for any new Entra ID data-plane test. +- Reuse `_test_utils.py` helpers (`create_config_store`, + `get_resource_name_prefix`, `get_test_resource_group`, + `CredentialResponseSanitizer`, recording processors) instead of + duplicating setup/sanitization logic. Add new shared helpers there if + multiple test files would otherwise duplicate them. +- Clean up created resources at the end of a test (e.g. + `appconfig delete -n {config_store_name} -g {rg} -y`) unless the + resource group itself is ephemeral and torn down by the preparer. + +## Recordings + +- New/changed tests need a recording under `tests/latest/recordings/`. + Generate it by running the test live (requires a real subscription): + ``` + azdev test appconfig -- test_appconfig__commands.. --live + ``` + or the full module via `_run_all_test.ps1 -Live`. +- After recording, replay in playback mode to confirm it passes without + `--live`: + ``` + azdev test appconfig + ``` +- Verify secrets/connection strings/credentials are sanitized in the + recording (reuse `CredentialResponseSanitizer` / existing + `RecordingProcessor`s in `_test_utils.py`; add a new sanitizer there if + a new sensitive field appears in responses). + +## Workflow + +1. Identify the right existing test file/class for the change; only + create a new file if truly warranted. +2. Add/extend test method(s) covering the new/changed behavior, including + at least one negative/error-path case when validators or required + arguments changed. +3. Add or update the matching recording (live run + playback verification). +4. Run `azdev test appconfig` and report pass/fail; fix failures in the + test itself, and clearly flag (don't silently fix) any failure that + points to a real bug in `custom.py`/`keyvalue.py`/etc. +5. Update `_run_all_test.ps1` only if you added a wholly new top-level + test invocation pattern (rare — the script already runs the whole + module via `azdev test appconfig`). + +## Guardrails + +- Only edit files under `tests/latest/` unless a genuine bug in + implementation code is found — then explicitly flag it to the user + before touching non-test files. +- Never commit unsanitized secrets/connection strings/tokens in a + recording file. +- Keep new tests deterministic and independent of test execution order. diff --git a/src/azure-cli/azure/cli/command_modules/appconfig/.github/instructions/appconfig.instructions.md b/src/azure-cli/azure/cli/command_modules/appconfig/.github/instructions/appconfig.instructions.md new file mode 100644 index 00000000000..d3862018b3a --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/appconfig/.github/instructions/appconfig.instructions.md @@ -0,0 +1,89 @@ +--- +applyTo: "src/azure-cli/azure/cli/command_modules/appconfig/**" +--- + +# Copilot instructions for the `appconfig` command module + +Scope: these instructions apply only to +`src/azure-cli/azure/cli/command_modules/appconfig/**` (the `az appconfig` +command module). Do not generalize conventions from here to other +command modules. + +These instructions distill and are grounded in the repo's own authoring +guides — consult them directly for anything not covered here: +- `doc/authoring_command_modules/README.md` — module setup, PR + title/changelog process (`HISTORY.rst` is auto-generated from PR + titles, not hand-edited). +- `doc/authoring_command_modules/authoring_commands.md` — command + loader, registration, validators, preview mechanics. +- `doc/command_guidelines.md` — command/argument naming, general + patterns, error handling, coding practices. +- `doc/error_handling_guidelines.md` — required error types + (`azure.cli.core.azclierror`) and error-message wording rules. +- `doc/how_to_introduce_breaking_changes.md` — the `_breaking_change.py` + pre-announcement mechanism for deprecating/renaming/changing GA + commands or arguments (current preferred approach, supersedes ad hoc + `deprecate_info=`). +- `doc/how_to_bump_SDK_version_in_cli.md` — process for bumping the + pinned `azure-mgmt-appconfiguration`/data-plane SDK version. +- `doc/authoring_help.md` — `_help.py` YAML authoring rules. +- `doc/authoring_tests.md` — test policies, coverage requirements, + recording workflow. + +## Module map + +| File | Purpose | +|---|---| +| `_params.py` | Registers CLI arguments (`load_arguments`) — arg names, types, validators, enum choices. | +| `_validators.py` | Argument validators/normalizers invoked by `_params.py` before command execution. | +| `_client_factory.py` | Builds SDK clients (`cf_configstore`, `cf_configstore_operations`, `cf_replicas`, `cf_nsp_configurations`). | +| `commands.py` | `load_command_table` — wires command names to implementation functions via `CliCommandType`, sets `table_transformer` and `client_factory` per group. | +| `custom.py` | Management-plane command implementations (store CRUD, identity, credentials, replicas). | +| `keyvalue.py` | Data-plane key-value command implementations. | +| `feature.py` | Feature-flag command implementations. | +| `snapshot.py` | Snapshot command implementations. | +| `network_security_perimeter.py` | NSP-related command implementations. | +| `_format.py` | Table output transformers (e.g. `configstore_output_format`). | +| `_help.py` | `helps[...]` entries: short-summary, long-summary, and `examples` for every command/group. | +| `_constants.py`, `_models.py`, `_featuremodels.py`, `_snapshotmodels.py` | Shared enums/constants and lightweight data models. | +| `_kv_helpers.py`, `_kv_import_helpers.py`, `_kv_export_helpers.py`, `_diff_utils.py`, `_json.py`, `_utils.py` | Shared helper logic used by `custom.py`/`keyvalue.py`. | +| `_credential.py` | Auth/credential helpers for data-plane calls. | +| `linter_exclusions.yml` | `azdev linter` suppressions — only add entries with a clear justification. | +| `tests/latest/` | ScenarioTests, recordings, and `_test_utils.py` helpers. | + +## Adding or changing a command — checklist + +0. **Naming** (`doc/command_guidelines.md`): commands follow "[noun] [noun] [verb]" with a verb in every command name; hyphenate multi-word subgroups; avoid a subgroup that would hold only one command (hyphenate into the parent instead, unless more commands are clearly planned). Argument names should not embed units (put units in help text), should reuse global aliases (e.g. `resource_group_name_type`), and should not duplicate the same concept under two different argument names. +1. **Arguments** (`_params.py`): add/extend a `with self.argument_context(...)` block; reuse existing arg types (`fields_arg_type`, `tags_type`, `get_enum_type`, `get_three_state_flag`) before inventing new ones. +2. **Validation** (`_validators.py`): add a `validate_(namespace)` or `validate_(cmd, namespace)` function; per `doc/error_handling_guidelines.md`, raise a specific `azure.cli.core.azclierror` type (`InvalidArgumentValueError`, `RequiredArgumentMissingError`, `MutuallyExclusiveArgumentError`, `ArgumentUsageError`, or another third-layer type such as `ResourceNotFoundError`/`ValidationError` when it fits better) rather than bare `CLIError`/`Exception`. Error messages start with a capital letter, state the problem and the actionable fix (e.g. "...; specify it with --arg-name"), and avoid raw `'\n'`, usage-error boilerplate, or regex/code snippets. Wire the validator into the matching argument via `validator=` in `_params.py`. +3. **Implementation**: put management-plane logic in `custom.py`, data-plane key-value logic in `keyvalue.py`, feature-flag logic in `feature.py`, snapshot logic in `snapshot.py`. Follow the existing function-per-command style (one public function per CLI command, named after the command, e.g. `def appconfig_create(...)`). Commands must return an object/dict/`None` (never a bare string/bool); log status via `logger.warning()`/`logger.error()`, never `print()` (`doc/command_guidelines.md` "General Patterns"). +4. **Client factory** (`_client_factory.py`): if a new SDK client/operation group is needed, add a `cf_(cli_ctx, *_)` accessor here rather than constructing clients inline. +5. **Command registration** (`commands.py`): register new commands inside `load_command_table`, choosing/creating the right `CliCommandType` (correct `operations_tmpl`, `table_transformer`, `client_factory`). Group related commands under the existing command groups rather than creating new ones unless truly novel. Mark experimental commands/args `is_preview=True`. For deprecating/renaming/otherwise breaking a GA command or argument, prefer the `_breaking_change.py` pre-announcement mechanism (`register_command_deprecate`, `register_argument_deprecate`, `register_default_value_breaking_change`, etc. — see `doc/how_to_introduce_breaking_changes.md`) over the legacy `deprecate_info=c.deprecate(...)` kwarg, which still exists in this module but is no longer the recommended path for new changes. Use `confirmation=True` for destructive commands (see `delete`/`purge`/`recover`). +6. **Output formatting** (`_format.py`): add a transformer function if the command needs custom table output, and reference it as `table_transformer` in `commands.py`. +7. **Help text** (`_help.py`): every new command or new argument needs a `helps['appconfig ...']` entry with `type`, `short-summary`, and at least one concrete `examples` entry using realistic values (see the dedicated `appconfig-help` agent for detailed rules). +8. **Tests**: add/extend a ScenarioTest under `tests/latest/` (see the dedicated `appconfig-test` agent for detailed rules). +9. **Changelog**: do not hand-edit `src/azure-cli/HISTORY.rst` — it is auto-generated from the PR title (`[Component] Verb: az appconfig : description`) per `doc/authoring_command_modules/README.md`. Use the PR description's "History Notes" section for multiple/overriding notes. + +## Style conventions observed in this module + +- License header on every file: + ```python + # -------------------------------------------------------------------------------------------- + # Copyright (c) Microsoft Corporation. All rights reserved. + # Licensed under the MIT License. See License.txt in the project root for license information. + # -------------------------------------------------------------------------------------------- + ``` +- `logger = get_logger(__name__)` at module scope for anything that logs, using `from knack.log import get_logger`. +- Long lines and large functions are common in this module and are explicitly tolerated via `# pylint: disable=line-too-long` / `# pylint: disable=too-many-statements` at the top of files — prefer following this existing pattern over aggressively splitting functions, but do not silence genuinely new lint issues without justification. +- Prefer keyword-only, explicit parameter names in command functions (matching the CLI argument names) over `**kwargs`. +- Reuse existing helper modules (`_utils.py`, `_kv_helpers.py`, etc.) instead of duplicating logic already present there. + +## Required validation before finishing a change + +Run from the repo root: +``` +azdev style appconfig +azdev linter --command-modules appconfig +azdev test appconfig +``` +All three must pass (or failures must be pre-existing/unrelated) before considering a change complete. diff --git a/src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-args-validators/SKILL.md b/src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-args-validators/SKILL.md new file mode 100644 index 00000000000..e6021071612 --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-args-validators/SKILL.md @@ -0,0 +1,104 @@ +--- +name: appconfig-args-validators +description: Author or review CLI argument registration and validators for the az appconfig command module (naming conventions, _params.py argument_context blocks, _validators.py functions, and error type/message selection). Use when adding, renaming, or fixing arguments/validators under src/azure-cli/azure/cli/command_modules/appconfig/. +license: MIT +--- + +# appconfig-args-validators skill + +Scope: `_params.py` and `_validators.py` in +`src/azure-cli/azure/cli/command_modules/appconfig/`. Grounded in +`doc/command_guidelines.md` and `doc/error_handling_guidelines.md` — +treat both as authoritative alongside the rules below. + +## Command & argument naming (`doc/command_guidelines.md`) + +- Commands follow "[noun] [noun] [verb]" with a verb in every command + name (e.g. `appconfig kv set`, not `appconfig kv`). +- Multi-word subgroups are hyphenated. +- Avoid adding a subgroup that would only ever contain one command — + hyphenate into the parent instead, unless more commands in that + subgroup are clearly planned soon. +- Argument names must not embed units — put units in help text instead + (e.g. prefer `--retention-days` with "(in days)" in the help string + over inventing new unit-suffixed names each time). +- Don't create multiple arguments that are just different ways to supply + the same value — overload one descriptive argument instead (e.g. a + single `--parameters` accepting either a local path or a URL, not + `--parameters-path` and `--parameters-url`). +- Prefer globally-aliased argument types (e.g. `resource_group_name_type` + for `-g`/`--resource-group`) over ad hoc parameter names/short options. +- Arguments ending in `-id` should be GUIDs; arguments accepting ARM IDs + should omit the `-id` suffix and call out ARM-ID support in help text + (common with the "name or ID" convention already used in this module, + e.g. `--identity`/`--azure-front-door-profile`). + +## Registering arguments (`_params.py`) + +- Add/extend a `with self.argument_context('appconfig ...')` block + inside `load_arguments`. +- Reuse existing `CLIArgumentType` definitions and helpers before + defining new ones: `fields_arg_type`, `tags_type`, `get_enum_type`, + `get_three_state_flag`, `resource_group_name_type`, `get_location_type`. +- Wire any new validator via `validator=` on the argument. +- Mark experimental/subject-to-change arguments `is_preview=True` (see + the `appconfig-breaking-changes` skill for the full preview/deprecation + story). + +## Writing validators (`_validators.py`) + +- Add `validate_(namespace)` if no CLI context access is needed, or + `validate_(cmd, namespace)` if you need `cmd.cli_ctx` (e.g. to + read defaults via `cmd.cli_ctx.config.get(...)`, as + `validate_connection_string` does). +- Wire the validator into the matching argument via `validator=` in + `_params.py`. + +## Error types and messages (`doc/error_handling_guidelines.md`) + +- **Never** raise bare `CLIError` or `Exception` in new validators. Raise + a specific third-layer type from `azure.cli.core.azclierror`: + - Already used in this module: `InvalidArgumentValueError`, + `RequiredArgumentMissingError`, `MutuallyExclusiveArgumentError`, + `ArgumentUsageError`. + - Also available when they fit better: `ResourceNotFoundError`, + `ValidationError`, `UnauthorizedError`, `ForbiddenError`, + `BadRequestError`, `AzureResponseError`, `AzureConnectionError`, + `FileOperationError`, `CommandNotFoundError`, + `UnrecognizedArgumentError`. + - Avoid the base/fallback types (`AzCLIError`, `UserFault`, + `ClientError`, `ServiceError`, or the generic `UnclassifiedUserFault`/ + `ArgumentUsageError`) when a more specific type exists. + - If truly nothing fits and the error is general enough, a new type can + be proposed in `azure/cli/core/azclierror.py` (core, not this + module) — flag this to the user rather than doing it silently. +- **Message wording — DOs**: start with a capital letter; describe what's + wrong and, where possible, the exact fix (e.g. "...; please provide a + resource group name by --resource-group"). +- **Message wording — DON'Ts**: no raw `'\n'` or styling/colorization; no + usage-error boilerplate in the message; no programming + expressions/regex patterns in the message (e.g. don't say "must match + `'^[-\\w\\._\\(\\)]+$'`" — describe the constraint in plain language + instead); no vague messages like "Something unexpected happened." +- **Recommendations**: pass `recommendation=` (a string or list of + strings) to the error constructor, or call + `az_error.set_recommendation(...)` after construction, when the error + message alone doesn't tell the user what to do next. + +```python +from azure.cli.core.azclierror import MutuallyExclusiveArgumentError + +error_msg = 'Please specify only one of --connection-string or --name.' +recommendation = 'Try passing just --name and let the CLI resolve the connection string from defaults.' +raise MutuallyExclusiveArgumentError(error_msg, recommendation) +``` + +## Guardrails + +- Only edit `_params.py`/`_validators.py` (and read other files for + grounding, e.g. `_constants.py` for enum values). +- Match the module's existing style: license header, `# pylint: + disable=line-too-long` tolerance, `logger = get_logger(__name__)` if + logging is needed. +- Don't silently downgrade an existing specific `azclierror` type to a + more generic one, and don't reintroduce `CLIError` in code you touch. diff --git a/src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-breaking-changes/SKILL.md b/src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-breaking-changes/SKILL.md new file mode 100644 index 00000000000..dae598cea10 --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-breaking-changes/SKILL.md @@ -0,0 +1,120 @@ +--- +name: appconfig-breaking-changes +description: Mark az appconfig commands/arguments as preview, or deprecate/rename/remove/otherwise change GA behavior using the _breaking_change.py pre-announcement mechanism, and apply confirmation=True for destructive commands. Use whenever a change to appconfig affects existing (GA) command/argument behavior, defaults, or output — not just when adding brand-new commands. +license: MIT +--- + +# appconfig-breaking-changes skill + +Scope: any change to +`src/azure-cli/azure/cli/command_modules/appconfig/` that affects +existing (GA) command/argument behavior — deprecating, renaming, +removing, changing a default, changing output shape, or otherwise +introducing a breaking change — plus marking new items as preview. +Grounded in +`doc/authoring_command_modules/authoring_commands.md` ("Preview Commands +and Arguments") and `doc/how_to_introduce_breaking_changes.md` (the +current breaking-change mechanism) — treat both as authoritative. + +## Preview flag + +New commands/arguments that are experimental or subject to change should +be marked `is_preview=True` (on `c.argument(...)`, `g.command(...)`, or +`self.command_group(...)`). Existing examples in this module: the +`azure_front_door_profile` argument and the +`network-security-perimeter-configuration` command group. + +**Anything not marked preview is considered GA.** Changing or removing GA +behavior later requires the pre-announced breaking-change mechanism +below — never a silent change. + +## Deprecating, renaming, or removing a GA item + +Per `doc/how_to_introduce_breaking_changes.md`, the current, preferred +mechanism is a per-module `_breaking_change.py` file — **this module +doesn't have one yet.** Create it with the standard license header if +it's needed: + +```python +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- +``` + +Then register breaking changes with `azure.cli.core.breaking_change`. +Make sure the module actually imports `_breaking_change` somewhere that +executes at load time (commonly from `commands.py` or `__init__.py`) — +registrations have no effect if the file is never imported. + +- **Deprecate a command group / command / argument**: + `register_command_group_deprecate(group, redirect=None, hide=False, target_version=None)`, + `register_command_deprecate(command, redirect=None, hide=False, target_version=None)`, + `register_argument_deprecate(command, argument, redirect=None, hide=False, target_version=None)`. + - **Removal** = call with no `redirect`. + - **Rename** = call with `redirect='--new-arg-name'` (or the new + command/group name). + - Do **not** combine a deprecation with another breaking-change type on + the same item — pick one. +- **Default value change**: + `register_default_value_breaking_change(command, arg, current_default, new_default, target_version=...)`. +- **Argument becoming required**: + `register_required_flag_breaking_change(command, arg, target_version=...)`. +- **Output shape/field change**: + `register_output_breaking_change(command, description=..., guide=..., target_version=..., doc_link=...)`. +- **Behavior/logic change** not covered above: + `register_logic_breaking_change(command, summary, detail=..., target_version=..., doc_link=...)`. +- **Anything else**: + `register_other_breaking_change(command, message, arg=None, target_version=...)`. +- `target_version` accepts a specific version or an approximate date + (`[DDth] MMM YYYY`); defaults to the next breaking-change window if + omitted. + +```python +from azure.cli.core.breaking_change import ( + register_argument_deprecate, register_default_value_breaking_change, + register_required_flag_breaking_change) + +# Rename --old-name to --new-name on 'appconfig create' +register_argument_deprecate('appconfig create', '--old-name', redirect='--new-name') + +# Announce a future default change +register_default_value_breaking_change('appconfig create', '--sku', 'standard', 'developer', + target_version='May 2025') +``` + +### Legacy `deprecate_info=` kwarg + +The older `deprecate_info=c.deprecate(redirect=..., hide=...)` kwarg +still works and still appears in this module (e.g. +`enable_public_network` → `--public-network-access` in `_params.py`), but +prefer `_breaking_change.py` for **new** deprecations/renames per the +doc's recommendation — don't add more legacy-style deprecations. + +### Timing requirement + +Pre-announcements must ship **at least ~1 month (usually 2 sprints)** +before the actual breaking change lands, and the actual breaking change +should only be adopted within the designated breaking-change window. +Flag this timing explicitly to the user rather than assuming an +immediate change is acceptable — a service-owned module like appconfig +needs its own PR for the pre-announcement, separate from the PR that +implements the change itself. + +## Confirmation for destructive commands + +Destructive/irreversible commands (delete, purge, recover-style +operations) should register with `confirmation=True` in `commands.py` +(see `delete`, `recover`, `purge` on the config store) so the CLI prompts +the user for confirmation unless `--yes` is passed. + +## Guardrails + +- Always call out to the user, explicitly and up front, when a requested + change would break existing GA behavior — don't implement it silently + even if technically straightforward. +- Don't mix a deprecation and another breaking-change type on the same + command/argument. +- If unsure whether an item is GA or preview, check for an existing + `is_preview=True` on it in `_params.py`/`commands.py` before assuming + it's safe to change freely. diff --git a/src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-implementation-wiring/SKILL.md b/src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-implementation-wiring/SKILL.md new file mode 100644 index 00000000000..a2392053448 --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-implementation-wiring/SKILL.md @@ -0,0 +1,92 @@ +--- +name: appconfig-implementation-wiring +description: Implement and register az appconfig CLI command functions (custom.py/keyvalue.py/feature.py/snapshot.py), client factories, commands.py registration, and table output formatting. Use when writing or wiring up command logic under src/azure-cli/azure/cli/command_modules/appconfig/. +license: MIT +--- + +# appconfig-implementation-wiring skill + +Scope: `custom.py`, `keyvalue.py`, `feature.py`, `snapshot.py`, +`network_security_perimeter.py`, `_client_factory.py`, `commands.py`, and +`_format.py` in +`src/azure-cli/azure/cli/command_modules/appconfig/`. Grounded in +`doc/authoring_command_modules/authoring_commands.md` and +`doc/command_guidelines.md` — treat both as authoritative alongside the +rules below. + +## Where to put the implementation + +- Management-plane (store CRUD, identity, credentials, replicas, NSP) → + `custom.py` / `network_security_perimeter.py`. +- Data-plane key-value logic → `keyvalue.py`. +- Feature-flag logic → `feature.py`. +- Snapshot logic → `snapshot.py`. +- One public function per CLI command, named after the command (e.g. + `def appconfig_create(...)`), with parameter names mirroring the CLI + argument names exactly. +- Reuse shared helpers instead of duplicating logic: `_utils.py`, + `_kv_helpers.py`, `_kv_import_helpers.py`, `_kv_export_helpers.py`, + `_diff_utils.py`, `_json.py`. +- Special parameter names with infrastructure meaning + (`doc/authoring_command_modules/authoring_commands.md`): `cmd` (must be + first parameter if used; gives access to `cmd.cli_ctx`), `client` (bound + automatically if the command's `client_factory` is set). + +## General patterns (`doc/command_guidelines.md`) + +- Commands must return an object, dict, or `None` — never a bare + string/bool. +- All command *output* goes to stdout; everything else (status messages, + warnings, errors) goes through `logger.warning()`/`logger.error()` — + never `print()`. +- New output must work with JSON, TSV, and table formats; add a + `_format.py` transformer if the default table rendering isn't useful. +- Support tab completion for parameter names/values where relevant + (usually free via `get_enum_type`/named completers, not something you + need to hand-write). + +## Client factory (`_client_factory.py`) + +- If a new SDK client or operation group is needed, add a + `cf_(cli_ctx, *_)` accessor here rather than constructing clients + inline in `custom.py`/etc. See existing examples: `cf_configstore`, + `cf_replicas`, `cf_nsp_configurations`, `cf_configstore_operations`. + +## Registration (`commands.py`) + +- Register new commands inside `load_command_table`, reusing an existing + `CliCommandType` (e.g. `configstore_custom_util`, + `configstore_keyvalue_util`, `configstore_snapshot_util`) where the + `operations_tmpl`, `table_transformer`, and `client_factory` already + match, or defining a new `CliCommandType` if truly novel. +- Group related commands under existing command groups + (`self.command_group('appconfig ...', ...)`) rather than creating new + ones unless the command is genuinely a new area. +- Mark experimental commands/arguments `is_preview=True` (see the + `appconfig-breaking-changes` skill for full preview/deprecation + guidance). +- Use `confirmation=True` on destructive/irreversible commands (delete, + purge, recover-style operations) — see `delete`, `recover`, `purge` on + the config store in `commands.py` — so the CLI prompts unless `--yes` + is passed. + +## Output formatting (`_format.py`) + +- Add a transformer function (see `configstore_output_format`, + `keyvalue_entry_format`, `configstore_replica_output_format`, etc. for + the existing style) if the command needs custom table output, and + reference it as `table_transformer=` on the relevant `CliCommandType` + or command registration in `commands.py`. + +## Guardrails + +- Only edit the files listed in Scope (plus reading others for + grounding). +- Match existing style: license header, `logger = get_logger(__name__)` + where logging is needed, `# pylint: disable=...` comments already + present at the top of files — don't fight the existing tolerance for + long lines/large functions in this module. +- Don't inline SDK client construction — always go through + `_client_factory.py`. +- Keep changes surgical: adding one command shouldn't require touching + unrelated `CliCommandType` definitions or command groups. diff --git a/src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-release-process/SKILL.md b/src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-release-process/SKILL.md new file mode 100644 index 00000000000..87e18527cc8 --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/appconfig/.github/skills/appconfig-release-process/SKILL.md @@ -0,0 +1,97 @@ +--- +name: appconfig-release-process +description: Format PR titles/changelog notes correctly for az appconfig changes (HISTORY.rst is auto-generated, never hand-edited) and bump the azure-mgmt-appconfiguration SDK dependency version. Use when finishing a PR for appconfig or when a change needs newer SDK functionality. +license: MIT +--- + +# appconfig-release-process skill + +Scope: how appconfig changes get released — PR title/changelog +conventions and SDK dependency version bumps. Grounded in +`doc/authoring_command_modules/README.md` ("Submitting Pull Requests") +and `doc/how_to_bump_SDK_version_in_cli.md` — treat both as authoritative +alongside the rules below. + +## Changelog — do NOT hand-edit `src/azure-cli/HISTORY.rst` + +`HISTORY.rst` entries are auto-generated from the PR title/description +starting from S165 (01/30/2020) — they are not edited directly in normal +PRs. Instead, the PR title must follow this format: + +``` +[Component Name] [BREAKING CHANGE: |Fix #N: ] +``` + +- **`[Component Name]`** (e.g. `[App Configuration]`) = customer-facing; + the message goes into `HISTORY.rst`. **`{Component Name}`** (curly + braces) = not customer-facing; excluded from `HISTORY.rst`. This part + is mandatory in every PR title. +- If it's a breaking change, the second part is `BREAKING CHANGE:`. For a + hotfix, use `Hotfix`. For an issue fix, use `Fix #`. Otherwise + this part can be empty. +- Recommended: include the affected command starting with `az`, followed + by a colon (e.g. `az appconfig create:`). +- Recommended: use a present-tense, capitalized, base-form verb: + - `Add` — new features. + - `Change` — changes to existing functionality. + - `Deprecate` — once-stable features slated for removal. + - `Remove` — deprecated features removed in this release. + - `Fix` — bug fixes. + +Examples: +``` +[App Configuration] BREAKING CHANGE: az appconfig create: Remove --deprecated-arg +[App Configuration] Fix #12345: az appconfig kv list: Fix pagination for large stores +{App Configuration} Add help example for kv set +``` + +- For **multiple** history notes from one PR, or to **override** the + title-derived note, use the `History Notes` section of the PR + description (the PR template already includes this section — delete it + if not needed). The PR title still must start with + `[Component Name]`/`{Component Name}` even if it's just a summary in + this case. +- **Hotfix PRs** (based on the `release` branch) are the *only* case + where `HISTORY.rst` is manually edited, and only for customer-facing + changes — the auto-generation process ignores PRs whose title contains + `Hotfix`. Confirm with the user before treating a change as a hotfix; + it's rare and follows a distinct branch/merge workflow (merge + `release` back to `dev` with a merge commit, never squash). + +## SDK dependency version bumps + +If a change needs new functionality from `azure-mgmt-appconfiguration` +(or a data-plane SDK) that isn't in the currently pinned version: + +1. Bump the version in `src/azure-cli/setup.py` and all three + per-OS requirement files: `requirements.py3.windows.txt`, + `requirements.py3.Linux.txt`, `requirements.py3.Darwin.txt`. +2. Only if the SDK is **multi-API-profile aware**: update the pinned API + version in `AZURE_API_PROFILES` for the `'latest'` profile in + `azure-cli-core/azure/cli/core/profiles/_shared.py` (single API + version as a plain string, or an `operation=version` mapping for + `SDKProfile`-style multi-operation SDKs). +3. Run a regression check after bumping: `azdev test --no-exitfirst` + (playback) to catch anything broken by the new SDK; failures that + only reproduce live should be re-run with + `azdev test --live --lf --no-exitfirst`. +4. Fix any regressions the bump surfaces, or add the new feature code + that depends on the bumped SDK. +5. There is also an internal "Regression Test Pipeline" that automates + steps 1–3 across the whole repo when bumping broadly-used SDKs — flag + this option to the user if the bump is large/repo-wide rather than + appconfig-specific, but for an appconfig-only bump, doing steps 1–4 + directly is usually simpler. + +Do not assume unreleased SDK APIs exist without checking the actually +installed/pinned package version first. + +## Guardrails + +- Never hand-edit `HISTORY.rst` outside of a confirmed hotfix PR. +- Always surface the required PR title format to the user rather than + silently choosing one — get their confirmation on the Component Name + and BREAKING CHANGE/Fix # portion, since only they know the full PR + context. +- When bumping an SDK version, update all three OS-specific requirements + files together — never just one.