azclips / scripts
AGENTS.md15 starsChanged 2 days agoMITView on GitHub
Scoped guidance for the build, packaging, signing, compliance, and publishing scripts under devops/scripts/. These are invoked by the Azure DevOps pipelines (see .azure-pipelines/AGENTS.md) and are also runnable locally. Read this before editing any script here. When you add or change a script, parity pair, config, or output/staging path, update this guide in the same PR so it stays accurate.
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,439 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository13 other files it gives its agents.
- Agent/AGENTS.mdAGENTS.md
- AGENTS.mdAGENTS.md
- .azure-pipelines/AGENTS.mdAGENTS.md
- azclips-custom-code-classifierSkill
- azclips-scenario-test-converterSkill
- extension-test-coverageSkill
- migrate-clips-to-extensions-codegen-llm-hybridSkill
- openspec-apply-changeSkill
- openspec-archive-changeSkill
- openspec-exploreSkill
- openspec-proposeSkill
- openspec-sync-specsSkill
- openspec-verify-changeSkill
The file
Show its rules and text
We split this AGENTS.md into the rules it gives an agent
15 rules. About 1 is common, 6 are distinctive, and 8 are very specific to this project.
≈1
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
8
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Distinctive: line 17
Avoid repo-relative CWD assumptions — resolve paths from the script location (see below).
- Common: line 18
No secrets, no hardcoded credentials.
- Very specific: line 87
Run the regression tests with Python and PowerShell 7 available: python -m unittest discover -s devops/scripts/compliance -p …
- Distinctive: line 96
Do not reintroduce a separate SDK/runtime installer or framework-dependent tool.
- Very specific: line 109
Remove owned intermediate dotfiles with -Force.
- Very specific: line 144
Prefer ${VAR:-default} so the pipeline can override roots but local runs still work.
- Distinctive: line 161
When you change behavior in one, update its sibling so Windows and Unix stay in sync.
- Very specific: line 187
Keep RID/output-root names aligned with .azure-pipelines/templates/dist/variables/packaging.yml.
- Distinctive: line 188
Verify third-party archives against registry-provided checksums before extracting or executing them.
- Distinctive: line 190
Keep active Windows process redirection files outside published artifact directories, wait for asynchronous stream handling to finish, and …
- Distinctive: line 191
Run CLI package lifecycle validators only on matching native operating systems and architectures.
- Very specific: line 196
When selecting the first line from an unbounded pipeline, use sed -n '1p' rather than head -1.
- Very specific: line 199
If a script is called from a pipeline job, verify the corresponding templates/dist/jobs/*.yml still passes the expected env vars/args after …
- Very specific: line 200
Update the parity sibling (.ps1/.sh) when editing one side.
- Very specific: line 203
Do not add secrets. Keep packaging build outputs under devops/out/.
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
DevOps Scripts — Agent Guide
Scoped guidance for the build, packaging, signing, compliance, and publishing
scripts under devops/scripts/. These are invoked by the Azure DevOps pipelines
(see .azure-pipelines/AGENTS.md) and are also runnable locally. Read this before
editing any script here. When you add or change a script, parity pair, config, or
output/staging path, update this guide in the same PR so it stays accurate.
Essentials
- This is the executable layer the pipelines shell out to. Keep reusable build/packaging logic here; pipeline-specific orchestration, conditions, and short validation steps may remain in YAML.
- Three languages. Directly-executed scripts carry a shebang; scripts invoked explicitly by an interpreter (e.g.
pwsh -File …) may omit it:- Bash:
#!/usr/bin/env bashthenset -euo pipefail(all.shhere follow this). - PowerShell:
#!/usr/bin/env pwshonly when the file is run directly. Most.ps1here are invoked aspwsh -Fileand have no shebang — match the surrounding file's existing style rather than adding one. - Python:
#!/usr/bin/env python3.
- Bash:
- Parity pairs under
packaging/common/**(stage_agent.*,inject_builtin_extensions.*) provide equivalent behavior across platforms; keep both sides in sync. Individual scripts may still be platform-specific — e.g.build_common.shandcreate_component_archives.shrun on Ubuntu — and per-platform scripts underlinux/,macos/,windows/are intentionally OS-specific. - Avoid repo-relative CWD assumptions — resolve paths from the script location (see below).
- No secrets, no hardcoded credentials. They are supplied by the pipeline environment (variable groups, Key Vault, approved service connections, and built-in pipeline authentication).
Directory Layout
| Path | Purpose |
ci/ | PR/CI checks (check_pull_request.py, check_pull_request_changes.py, scan_recordings.py). Recording scans flag unredacted signed ARM polling-URL query values (c, s, h) on any polling path (/operationresults/, /operationstatuses/, Storage's /asyncoperations/), mirroring the path-agnostic arm-operation-signature sanitiser in tests/Tests.Recording/RecordingSanitizers.cs. |
compliance/ | Official CG NOTICE validation/comparison and stable-PR refresh (Update-CgNotice.ps1) and Credential Scanner suppression configuration (CredScanSuppressions.json, consumed by CredScan@3 in .azure-pipelines/templates/dist/stages/component-governance.yml; each suppression needs a _justification). |
live/ | Scheduled recording refresh (refresh_recordings.py): replay every scenario, re-record the stale classes, scan the rewritten files. Recordings live in tests/Scenario.Tests/<Module>/Records/, next to the test that owns them. |
packaging/common/ | Shared build steps (build_common.sh, stage_agent.*, inject_builtin_extensions.*, create_component_archives.sh, rename_extension_packages.sh). |
packaging/config/ | Package profiles (full_package.config, lean_package.config) and artifact_naming.config. |
packaging/linux/ | Linux tarball build + test, including offline installation of each runtime-compatible pipeline archive. |
packaging/macos/ | macOS tarball build/test and signing entitlements (azclips-runtime.entitlements.plist). |
packaging/windows/ | Windows zip build + test. |
packaging/powershell/ | PowerShell module packaging/validation. The bundled installer infers versions from the package matching the requested module name; signed archive validation executes that installer and imports a child module at its packaged version. |
packaging/signing/ | ESRP input prep, repack, and verification of signed outputs, including collision-resistant macOS bundle ZIP mapping. |
publishing/ | Release publishing helpers (Update-ReleaseChangelog.ps1, Update-ChangedExtensionVersions.ps1). |
Installer Download Validation
- The Linux/macOS lifecycle validators use
packaging/common/validate_cli_download.shfor the download install, then continue their native runtime and extension checks. The shared fixture substitutescurlwith local release metadata and the exact pipeline archive; it restricts the installer to standard system utilities and exercises missing/duplicate assets, invalid or truncated URLs, malformed metadata, HTTP failures, piped help, and truncated scripts. It preserves the existing local-archive safety coverage. - Windows zip validation substitutes HTTP cmdlets in a child scope and installs the selected pipeline archive through
Invoke-Expression; its existing offline and native lifecycle checks remain. packaging/powershell/test_signed_azps_cmdlets.ps1validates both the bundled helper and theinstall/azps-install.ps1bootstrap in isolated PowerShell processes. Bootstrap HTTP fixtures copy the selected signed archive, exercise default and parameterized installation/import, and check error cleanup and existing-repository preservation.- These checks run in the native ADO validation jobs using local HTTP-command fixtures and pipeline artifacts. Keep shared Bash fixtures compatible with macOS Bash 3.2 and Alpine.
- The Bash fixture also validates
install/azcli-extensions-download.shagainst a bundle made from the pipeline's extension packages. Windows callspackaging/common/test_extensions_download.ps1for the PowerShell equivalent. These download-only helpers support a pinned release, saveazcli-extensions.tar.gzin the working directory, preserve an existing bundle on failure, and never extract or install it. - Bash metadata fixtures distinguish a missing pinned release (HTTP 404) from HTTP 403/429 failures; only the pinned 404 reports that the requested release tag was not found.
- Unversioned local CLI archives receive a synthetic
0.0.0version only in fixture release asset names; downloads still copy the exact original archive.
Credential Scanner Suppressions
compliance/CredScanSuppressions.json records file paths, scanner-provided SARIF
fingerprints.HashCode values, and justifications for reviewed false positives.
BinSkim Report Presence
compliance/verify_binskim_results.ps1 requires that the supplied report
directory exists and contains at least one .sarif file, searched recursively.
Callers clear the job's BinSkim report directory before scanning so stale reports
cannot satisfy the check. It also parses each report and emits advisory warnings
for unparseable reports, executionSuccessful=false, tool notifications with
level error (for example load failures hidden by the ignore-errors BinSkim
flags). A clean BinSkim SARIF lists no analyzed files, so the script cannot
verify a file count. These checks never fail
the job.
Guardian's built-in tasks and PostAnalysis remain
responsible for analysis/result handling and enforcement.
The script reports a missing report directory or missing .sarif file as an Azure
DevOps warning (##vso[task.logissue type=warning]) and exits successfully, so it
never fails the build and needs no System.PullRequest.IsFork condition: when
Guardian skips a build, the check only warns.
compliance/get_binskim_cli_targets.ps1 generates the CLI task's target glob for the
extracted unsigned Windows zip (-ScanRoot),
retaining the existing MSAL and Copilot exclusions and adding the reviewed exact
filenames in compliance/binskim-runtime-exclusions.json. These additional exclusions
apply only directly under the archive root; nested copies and other dependencies
remain in scope. The optional patterns list covers versioned runtime filenames
(currently the mscordaccore_<arch>_<arch>_*.dll DAC files) so a runtime upgrade
does not re-trigger ERR997; a pattern may only wildcard the trailing version
segment of an underscore-delimited .dll name. The configuration rejects paths and
other wildcards to prevent accidental blanket exclusions.
file-exclusion approach, but the larger .NET runtime list is azclips-specific.
Excluded binaries receive no BinSkim checks, not merely a missing-PDB exemption.
PowerShell's shipping-payload scan does not consume the CLI runtime list.
The report checker has no PDB-specific policy or report interpretation.
Review runtime exclusions when
dependencies change; do not expand them to Microsoft.*, System.*, or entire
runtime directories.
Run the regression tests with Python and PowerShell 7 available:
python -m unittest discover -s devops/scripts/compliance -p test_verify_binskim_results.py -v.
PowerShell validation's run_ps_validation_phase.ps1 dispatches the Install and
Uninstall module phases. Install imports every packaged module with
-RequiredVersion and fails when the loaded version differs, the exported cmdlets
differ from manifest CmdletsToExport, or a cmdlet lacks a Get-Help example.
The validation bootstraps install standalone PowerShell archives from the official
GitHub releases, verify the published SHA256, and use the bundled .NET runtime.
Do not reintroduce a separate SDK/runtime installer or framework-dependent tool.
Release lines select the latest stable patch in that line; keep matrix runtime
channels as compatibility/report metadata. Repository restores still use CFS via
NuGet.config, which does not govern the standalone archive source.
The lifecycle fixtures resolve symbolic-link ancestors of the OS temporary
directory before creating scratch files, including macOS's /var alias.
Artifact capture still rejects symbolic links and reparse points.
invoke_ps_validation_step.ps1
publishes successful bootstrap/installation prerequisites so later phases
can continue after failures without clearing their recorded outcomes.
The shared module_definitions.ps1 reader normalizes known bare source names and
manifest-path dependencies to package identities for lifecycle validation;
already-prefixed and unknown names are preserved.
Remove owned intermediate dotfiles with -Force; PowerShell treats them as hidden
on Unix even when the current user created them.
The matrix generator reads packaging/powershell/ps_validation_matrix.json
directly with jq; no Python environment or YAML parser is needed. PowerShell
consumers can use the built-in ConvertFrom-Json. Keep versions as JSON strings
and flags as booleans.
The matrix generator accepts latest only for native Windows OS versions, not
for container, PowerShell or .NET versions. Keep hosted-agent aliases out of
numeric version comparisons. Matrix self-tests remain available locally;
Azure DevOps retains generation and module validation without that self-test step.
The repository matrix pins Windows 2022 and 2025; standalone fixtures cover
windows-latest alias support without adding that moving image to production.
The generator preserves DistributionPool and also emits routing fields:
DistributionPoolName is the configured pool-* name with an empty
DistributionVmImage, or Azure Pipelines with the hosted image label.
Apply this routing after global and per-distribution overrides.
Path Resolution Convention (Bash)
Scripts resolve their own location and derive roots from it, allowing env-var overrides — the pattern, not fixed depths:
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# `..` is the packaging/common/* depth; deeper scripts adjust it
# (e.g. packaging/linux/tarball/* uses `../..`) so the roots still resolve.
PACKAGING_ROOT="${PACKAGING_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"
REPO_ROOT="${REPO_ROOT:-$(cd "$PACKAGING_ROOT/../../.." && pwd)}"- The relative depth varies by script location — don't copy a snippet verbatim.
For example
packaging/common/*usesSCRIPT_DIR/.., whilepackaging/linux/tarball/*andpackaging/macos/tarball/*useSCRIPT_DIR/../... Verify your derived roots resolve correctly for the file's actual directory. - Prefer
${VAR:-default}so the pipeline can override roots but local runs still work. - Output goes under
devops/out/{common,linux,windows,macos}(matchesPackagingOutRootin the pipeline). - Match the surrounding scripts' pattern; do not assume the caller's working directory.
Cross-Platform Parity Pairs
Some steps ship as matched .ps1 + .sh implementations:
stage_agent.ps1/stage_agent.sh— step 6 removes a DLL from the Agent extension top level only when the CLI root has a byte-identical copy. A copy underdeps/does not count:deps/is probed only by theazclips-agentsubprocess, whileaz agent tool ...loads through the CLI extension loader, which resolves from the extension top level and then the CLI root.inject_builtin_extensions.ps1/inject_builtin_extensions.sh— also copies each bundled extension's*.commands.jsonbuild-time sidecar (seesrc/Extensions/Targets/ExtensionSidecar.targets) alongside its DLLs, so metadata-first discovery has it in packaged artifacts too.
When you change behavior in one, update its sibling so Windows and Unix stay in sync.
Config-Driven Packaging
full_package.config/lean_package.configare include-only extension lists (one name per line;*= all). Selected viaPACKAGE_PROFILE=full|lean.artifact_naming.configcentralizes artifact name patterns and the(Public Preview)GitHub release title suffix — change names here, not scattered across scripts.create_component_archives.shleavesBUILTIN_CLI_EXTENSION_PACKAGESout of the standaloneazcli-extensionsarchive.agentis in that list because the Agent extension must ship with the CLI it was built with; installing it separately withaz extension addcan breakaz agentandaz troubleshoot.
Docker Publish Retry Behavior
packaging/linux/docker/publish_docker_images.shfollows classic retry behavior: existing immutable version tags are retained, their digests are recorded, and downstream verification continues without candidate-image uploads.- CLI tags follow the classic
azurelinux3.0spelling, while PowerShell tags use classicazurelinux-3.0. Full CLI images omit afullqualifier:VERSION-azurelinux3.0-{amd64,arm64}are combined in the defaultVERSION-azurelinux3.0manifest. Aliases such aslatestare not promoted. packaging/linux/docker/pull_published_docker_images.shconfirms each multi-architecture manifest maps to the recorded architecture-specific digest, pulls that digest on the authenticated AMD64 release agent, and saves it as a per-image pipeline artifact. Native verification agents load those archives throughverify_published_docker_image.sh, avoiding Azure CLI and ACR credential requirements on ARM64.packaging/linux/docker/verify_mcr_docker_image.shstrips thepublic/orunlisted/prefix from the ACR repository, checks that the anonymous MCR version tag exists with one retry after a 2-minute gap (MCR_POLL_ATTEMPTSdefault 2,MCR_POLL_INTERVAL_SECONDSdefault 120; the classic release makes a single attempt), then pulls it and runsverify_published_docker_image.shsmoke tests, which check the version; digests are not compared. The pipeline provides the post-publish delay.
Editing Rules for Agents
- Preserve each file's existing header style: keep
set -euo pipefailin bash, and don't add or strip a shebang just to normalize (most.ps1here intentionally have none). - Contributor PRs may rename
.csprojfiles only when their direct release-owned version properties are unchanged. CI uses a 1% similarity threshold and requires project additions and deletions to match one-to-one as renames, rejecting surplus paths so version validation cannot be bypassed through Git's rename classification. - Preserve encoding, line endings, and final-newline state when publishing scripts make version-only file updates.
- PowerShell packaging, release changelogs, and docs sync share
build/Get-AzPackageName.ps1for package identities (Azfor the rollup,Az.<SourceModule>for leaf modules). Keep consumers on this helper rather than duplicating the naming rule. publishing/Update-ChangedExtensionVersions.ps1supports dependency hashtables on their own lines or inline inRequiredModules = @(@{ ... }). Dependency version updates preserve surrounding layout and ignore commented declarations.- New-extension detection uses a successful
git ls-treelookup for an absent base-version project rather than leaving a failedgit showexit code forPowerShell@2to propagate. Git lookup/read failures remain fatal. The new-extension regression runs through the task's exit-code wrapper, not only in-process file assertions. compliance/Update-CgNotice.ps1requires a nonempty official generated file and committed rootNOTICE. Comparison ignores CRLF/LF, trailing horizontal whitespace, and final-newline differences. Only stable-release PR preparation passes-UpdateRootNotice; changed content is written as UTF-8 without BOM, with LF and no trailing whitespace. Other builds only warn on differences and never replace the packaged NOTICE. Regression coverage is intests/Release.Tests/Update-CgNotice.Tests.ps1.ci/test_stable_release_tag.pyruns the stable trigger's actual tagging Bash against temporary local Git repositories. It verifies that a version bump with rootNOTICEcreates the expected tag, while NOTICE-only changes and unrelated paths do not; GitHub pushes are redirected to the local fixture. The GitHub Actionsrelease-script-testsjob runs this suite alongsidetest_check_pull_request_changes.py.ci/test_pipeline_input_safety.pyexercises stable preparation, beta tagging, beta signing preflight, and signed Linux musl validation using their actual YAML Bash and task environment mappings. Inputs with quotes, command substitutions, and newlines must remain data, and printed untrusted values cannot emit unescaped##vso[logging commands; normal version bumps, source guards, and signed-archive selection must remain unchanged. It also executes help, signing, and first-assembly checks with large output underpipefail, preserving missing-match and producer-failure behavior. It uses temporary project files and Git/GitHub CLI/Docker stubs, without credentials or network calls. Runpython3 -m unittest discover -s devops/scripts/ci -p test_pipeline_input_safety.py -v; the shared Azure PipelinesUnitTestsjob runs it too.- Preserve env-var override points and script-relative path resolution.
- Keep RID/output-root names aligned with
.azure-pipelines/templates/dist/variables/packaging.yml. - Verify third-party archives against registry-provided checksums before extracting or executing them.
- Validate SDK-bundled Copilot runtimes with their server contract (
--server --stdio); they do not expose the user-facing CLI's--versioncommand. - Keep active Windows process redirection files outside published artifact directories, wait for asynchronous stream handling to finish, and dispose process handles before copying diagnostics into artifacts.
- Run CLI package lifecycle validators only on matching native operating systems and architectures; layout-only success paths are not supported.
- Validate structured CLI results with
--queryand compare stdout rather than parsing presentation-oriented output; keep diagnostics on stderr separate from asserted values. - Check extension command discovery offline with
extension add --helpandgroup list --help, requiring exit code 0 and nonblank stdout instead of matching authentication/argument errors. After Resources removal, lean packages must return the command-discovery exit code 4; full packages must still render nonblank help through bundled fallback. These checks cover discovery/loading, not Azure operations or processor execution, and must not depend on help prose or loader debug logs. - When checking captured output with
grep -q, use a here-string (grep -q pattern <<<"$output") rather than pipingechoorprintf; early grep exit can give the producer SIGPIPE and falsely fail underpipefail. Capture command output first (includingbash -s -- --help), keeping the assignment separate so producer failures still stop validation. For streaming assertions, usegrep pattern >/dev/nullwithout-qto consume the full input. - To select the first matching package, use
find ... -print -quitrather thanfind ... | head -1so early reader exit cannot givefindSIGPIPE underpipefail. - When selecting the first line from an unbounded pipeline, use
sed -n '1p'rather thanhead -1; it prints the same first line while consuming all producer output. - Failure assertions match the text error contract:
ERROR: [Code] messageon stderr plus the process exit code, usually captured with2>&1. Do not parse a JSON error envelope;--outputapplies only to successful stdout. - Preserve installer and uninstaller safety coverage for default paths, unsafe roots, unknown nonempty directories, and forced removal boundaries.
- If a script is called from a pipeline job, verify the corresponding
templates/dist/jobs/*.ymlstill passes the expected env vars/args after your change. - Update the parity sibling (
.ps1/.sh) when editing one side. - Validate locally where feasible (
bash -n script.sh,pwsh -NoProfile -File script.ps1 -WhatIfwhere supported,python3 -m py_compile). - PowerShell packaging requires MAML generated by
build/build.ps1 -GenerateHelpArtifacts. The build installs pinned PlatyPS through PSResourceGet and stops on dependency or help-generation failures; do not bypass missing help files. Regression coverage is intests/Release.Tests/Build-HelpArtifacts.Tests.ps1. - Do not add secrets. Keep packaging build outputs under
devops/out/; preserve explicit output/staging parameters for compliance, signing, and publishing scripts.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

