
Azure Devops Cli
- 76 installs
- 70 repo stars
- Updated July 26, 2026
- rysweet/amplihack
Helps with devops & ci/cd tasks.
About
azure-devops-cli is a Claude Code skill for devops & ci/cd. It helps solo builders move faster with AI-assisted development.
- azure-devops-cli
- DevOps & CI/CD
- AI-coding skill
Azure Devops Cli by the numbers
- 76 all-time installs (skills.sh)
- +1 installs in the week ending Jul 26, 2026 (Skillselion tracking)
- Ranked #604 of 1,438 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/rysweet/amplihack --skill azure-devops-cliAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 76 |
|---|---|
| repo stars | ★ 70 |
| Last updated | July 26, 2026 |
| Repository | rysweet/amplihack ↗ |
What it does
Helps with devops & ci/cd tasks.
Files
Azure DevOps CLI Skill
Quick Start
Installation & Authentication
# Install Azure DevOps CLI extension
az extension add --name azure-devops
# Authenticate (choose one method)
az login # Interactive browser login
az devops login --organization https://dev.azure.com/YOUR_ORG # PAT token login
# Configure defaults (recommended)
az devops configure --defaults organization=https://dev.azure.com/YOUR_ORG project=YOUR_PROJECT
# Verify setup
az devops project listConfiguration Patterns
# Set defaults to avoid repeating --organization and --project
az devops configure --defaults organization=https://dev.azure.com/myorg project=myproject
# List current configuration
az devops configure --list
# Use Git aliases for common commands
az devops configure --defaults use-git-aliases=true
# Common output formats
--output table # Human-readable tables (default)
--output json # JSON for scripting
--output tsv # Tab-separated valuesEssential Commands by Group
1. DevOps (Organization & Projects)
# List projects
az devops project list --organization https://dev.azure.com/myorg
# Create project
az devops project create --name "MyProject" --visibility private
# Show project details
az devops project show --project MyProject
# Delete project
az devops project delete --id PROJECT_ID --yes
# Manage users/teams
az devops user list
az devops team list --project MyProject2. Pipelines (Build & Release)
# List pipelines
az pipelines list --project MyProject
# Run a pipeline
az pipelines run --name "MyPipeline" --branch main
# Show pipeline runs
az pipelines runs list --pipeline-ids 123
# Show run details
az pipelines runs show --id RUN_ID
# Create pipeline from YAML
az pipelines create --name "NewPipeline" --repository myrepo --branch main --yml-path azure-pipelines.yml3. Boards (Work Items & Sprints)
# List work items
az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.State] = 'Active'"
# Create work item
az boards work-item create --type "User Story" --title "New Feature" --assigned-to me@example.com
# Update work item
az boards work-item update --id 123 --state "In Progress"
# Show work item
az boards work-item show --id 123
# List iterations/sprints
az boards iteration project list4. Repos (Git Repositories)
# List repositories
az repos list --project MyProject
# Create repository
az repos create --name "myrepo" --project MyProject
# List pull requests
az repos pr list --repository myrepo --status active
# Create pull request
az repos pr create --repository myrepo --source-branch feature/new --target-branch main --title "New Feature"
# Show PR details
az repos pr show --id PR_ID5. Artifacts (Package Management)
# List feeds
az artifacts feed list
# Create feed
az artifacts feed create --name "myfeed" --project MyProject
# List packages
az artifacts universal list --feed myfeed --project MyProject
# Publish package
az artifacts universal publish --feed myfeed --name mypackage --version 1.0.0 --path ./dist
# Download package
az artifacts universal download --feed myfeed --name mypackage --version 1.0.0 --path ./downloadCommon Workflows
Workflow 1: CI/CD Pipeline Automation
# Create pipeline, run it, and monitor
az pipelines create --name "API-Build" --repository myrepo --yml-path ci/azure-pipelines.yml
az pipelines run --name "API-Build" --branch main
az pipelines runs show --id RUN_ID --open # Opens in browserWorkflow 2: Pull Request Review Automation
# List active PRs, show details, add comment
az repos pr list --repository myrepo --status active --output table
az repos pr show --id 456 --open
az repos pr update --id 456 --status approvedWorkflow 3: Work Item Batch Creation
# Create multiple work items from template
for title in "Feature A" "Feature B" "Feature C"; do
az boards work-item create --type "User Story" --title "$title" --assigned-to team@example.com
doneWorkflow 4: Pipeline Status Dashboard
# Get recent pipeline runs with status
az pipelines runs list --top 10 --query "[].{Name:pipeline.name, Status:status, Result:result, Started:startTime}" --output tableWorkflow 5: Repository Clone Automation
# List all repos and clone them
az repos list --query "[].{Name:name, URL:remoteUrl}" --output tsv | while IFS=$'\t' read -r name url; do
git clone "$url" "./$name"
doneWorkflow 6: Sprint Planning Helper
# List current sprint work items
az boards query --wiql "SELECT [System.Id], [System.Title], [System.State] FROM WorkItems WHERE [System.IterationPath] = @CurrentIteration" --output tableWorkflow 7: Release Gate Checking
# Check if all PRs are approved before release
PENDING=$(az repos pr list --status active --query "length([?status!='approved'])")
if [ "$PENDING" -eq 0 ]; then
az pipelines run --name "Release-Pipeline"
fiWorkflow 8: Artifact Versioning
# Publish versioned artifact with timestamp
VERSION="1.0.$(date +%Y%m%d%H%M%S)"
az artifacts universal publish --feed myfeed --name myapp --version "$VERSION" --path ./buildWorkflow 9: Team Dashboard Data
# Export team metrics to JSON
az devops project show --project MyProject > project.json
az pipelines runs list --top 50 > recent-runs.json
az repos pr list --status all > all-prs.jsonWorkflow 10: Environment Sync
# Copy pipeline variables across environments
az pipelines variable list --pipeline-name "MyPipeline" --output json > vars.json
# Edit vars.json as needed
az pipelines variable-group create --name "Production" --variables @vars.jsonTroubleshooting
Common Issues
Authentication Failures:
# Clear cached credentials
az account clear
az login
# Use PAT token directly
export AZURE_DEVOPS_EXT_PAT=your_personal_access_token
az devops loginDefault Configuration:
# Reset defaults if commands fail
az devops configure --defaults organization="" project=""
# Then set explicitly in each command
az pipelines list --organization https://dev.azure.com/myorg --project MyProjectExtension Issues:
# Update Azure DevOps extension
az extension update --name azure-devops
# Check extension version
az extension show --name azure-devopsQuery Syntax:
# WIQL queries require proper escaping
az boards query --wiql "SELECT [System.Id] FROM WorkItems WHERE [System.State] = 'Active' AND [System.AssignedTo] = 'me@example.com'"Advanced Patterns
REST API Access
# Direct REST API calls for unsupported operations
az devops invoke --area build --resource builds --route-parameters project=MyProject --api-version 6.0 --http-method GET
# POST with JSON body
az devops invoke --area git --resource repositories --route-parameters project=MyProject --http-method POST --in-file payload.jsonScripting with JMESPath
# Complex queries using JMESPath
az pipelines runs list --query "[?result=='failed'].{Pipeline:pipeline.name, Branch:sourceBranch, Time:finishedDate}" --output table
# Filter and transform data
az repos pr list --query "[?targetRefName=='refs/heads/main' && status=='active'].{ID:pullRequestId, Title:title, Author:createdBy.displayName}"Aliases and Functions
# Create shell aliases for common commands
alias azdo-pipelines="az pipelines list --output table"
alias azdo-prs="az repos pr list --status active --output table"
alias azdo-builds="az pipelines runs list --top 20 --output table"
# Function for quick PR creation
azdo-pr() {
az repos pr create --source-branch "$(git branch --show-current)" --target-branch main --title "$1" --open
}Extended Content
For comprehensive command references and advanced workflows, see:
- Complete Command References:
examples/pipelines-reference.md,examples/boards-reference.md,examples/repos-reference.md,examples/artifacts-reference.md - Advanced Workflows:
examples/workflows/ci-cd-automation.md,examples/workflows/release-management.md,examples/workflows/team-collaboration.md - Testing Scenarios:
tests/test-scenarios.md
References
- Azure DevOps CLI Documentation: https://learn.microsoft.com/en-us/cli/azure/devops
- WIQL Syntax: https://learn.microsoft.com/en-us/azure/devops/boards/queries/wiql-syntax
- REST API: https://learn.microsoft.com/en-us/rest/api/azure/devops
Azure Artifacts Complete Command Reference
Complete reference for az artifacts command group covering package feeds, universal packages, and artifact management.
Feed Management
List Feeds
# List all feeds in organization
az artifacts feed list
# List feeds in specific project
az artifacts feed list --project MyProject
# List as table
az artifacts feed list --output table
# Get feed details as JSON
az artifacts feed list --output json > feeds.jsonShow Feed
# Show feed by name
az artifacts feed show --feed myfeed --project MyProject
# Show feed by ID
az artifacts feed show --feed FEED_ID
# Get feed as JSON
az artifacts feed show --feed myfeed --output jsonCreate Feed
# Create feed
az artifacts feed create --name "myfeed" --project MyProject
# Create organization-level feed
az artifacts feed create --name "org-feed" --organization https://dev.azure.com/myorg
# Create with description
az artifacts feed create \
--name "production-feed" \
--project MyProject \
--description "Production packages"Update Feed
# Update feed description
az artifacts feed update \
--feed myfeed \
--description "Updated description"
# Update feed upstream sources
az artifacts feed update \
--feed myfeed \
--project MyProjectDelete Feed
# Delete feed
az artifacts feed delete --feed myfeed --yes
# Delete project feed
az artifacts feed delete --feed myfeed --project MyProject --yesFeed Permissions
List Feed Permissions
# List permissions for feed
az artifacts feed permission list --feed myfeed --project MyProject
# Get specific user permission
az artifacts feed permission show \
--feed myfeed \
--user user@example.com \
--project MyProjectAdd Feed Permissions
# Add user to feed with contributor role
az artifacts feed permission add \
--feed myfeed \
--user user@example.com \
--role contributor \
--project MyProject
# Add group with reader role
az artifacts feed permission add \
--feed myfeed \
--group "Build Service" \
--role reader \
--project MyProject
# Role options: reader, contributor, collaborator, administratorRemove Feed Permissions
# Remove user from feed
az artifacts feed permission remove \
--feed myfeed \
--user user@example.com \
--yes \
--project MyProjectUniversal Packages
List Universal Packages
# List all packages in feed
az artifacts universal list --feed myfeed --project MyProject
# List as table
az artifacts universal list --feed myfeed --output table
# Get package list as JSON
az artifacts universal list --feed myfeed --output json > packages.jsonShow Universal Package
# Show package details
az artifacts universal show \
--feed myfeed \
--name mypackage \
--version 1.0.0 \
--project MyProject
# Get package as JSON
az artifacts universal show \
--feed myfeed \
--name mypackage \
--version 1.0.0 \
--output jsonPublish Universal Package
# Publish package from directory
az artifacts universal publish \
--feed myfeed \
--name mypackage \
--version 1.0.0 \
--path ./dist \
--project MyProject
# Publish with description
az artifacts universal publish \
--feed myfeed \
--name api-package \
--version 2.1.0 \
--path ./build/output \
--description "API build v2.1.0" \
--project MyProject
# Publish from specific directory
az artifacts universal publish \
--feed production-feed \
--name deployment-package \
--version 1.0.0-rc1 \
--path /path/to/artifacts \
--project MyProjectDownload Universal Package
# Download package to directory
az artifacts universal download \
--feed myfeed \
--name mypackage \
--version 1.0.0 \
--path ./download \
--project MyProject
# Download to specific location
az artifacts universal download \
--feed myfeed \
--name api-package \
--version 2.1.0 \
--path /var/lib/packages \
--project MyProject
# Download latest version
VERSION=$(az artifacts universal list --feed myfeed --project MyProject --query "[?name=='mypackage'] | [0].versions[0].version" -o tsv)
az artifacts universal download \
--feed myfeed \
--name mypackage \
--version "$VERSION" \
--path ./download \
--project MyProjectNuGet Packages
List NuGet Packages
# List NuGet packages in feed (using REST API)
az devops invoke \
--area packaging \
--resource packages \
--route-parameters feedId=FEED_ID project=MyProject \
--api-version 6.0-preview.1 \
--http-method GET
# Query specific package
az devops invoke \
--area packaging \
--resource packages \
--route-parameters feedId=FEED_ID packageId=PACKAGE_ID project=MyProject \
--api-version 6.0-preview.1 \
--http-method GETPublish NuGet Package
# Configure NuGet source
az artifacts feed show --feed myfeed --project MyProject --query "packageEndpoints.nuGet.publishEndpoint" -o tsv
# Use dotnet CLI to publish
dotnet nuget push package.nupkg \
--source https://pkgs.dev.azure.com/myorg/MyProject/_packaging/myfeed/nuget/v3/index.json \
--api-key aznpm Packages
List npm Packages
# Get npm feed endpoint
az artifacts feed show --feed myfeed --project MyProject --query "packageEndpoints.npm.publishEndpoint" -o tsv
# Use npm CLI to list packages
npm search --registry=https://pkgs.dev.azure.com/myorg/MyProject/_packaging/myfeed/npm/registry/Publish npm Package
# Configure npm registry
NPM_REGISTRY=$(az artifacts feed show --feed myfeed --project MyProject --query "packageEndpoints.npm.publishEndpoint" -o tsv)
# Publish package
npm publish --registry="$NPM_REGISTRY"Python Packages (PyPI)
List Python Packages
# Get Python feed endpoint
az artifacts feed show --feed myfeed --project MyProject --query "packageEndpoints.pypi.publishEndpoint" -o tsvPublish Python Package
# Configure twine
pip install twine
# Upload package
twine upload \
--repository-url https://pkgs.dev.azure.com/myorg/MyProject/_packaging/myfeed/pypi/upload \
dist/*Maven Packages
Publish Maven Package
# Get Maven feed endpoint
az artifacts feed show --feed myfeed --project MyProject --query "packageEndpoints.maven.publishEndpoint" -o tsv
# Configure settings.xml and use mvn deploy
mvn deploy -DrepositoryId=azure-artifacts -DaltDeploymentRepository=azure-artifacts::default::https://pkgs.dev.azure.com/myorg/MyProject/_packaging/myfeed/maven/v1Scripting Examples
Automated Package Publishing
#!/bin/bash
VERSION="1.0.$(date +%Y%m%d%H%M%S)"
npm run build
az artifacts universal publish --feed myfeed --name myapp --version "$VERSION" --path ./dist --description "Build $VERSION" --project MyProjectPackage Promotion Pipeline
#!/bin/bash
[ -z "$1" ] && echo "Usage: $0 <version>" && exit 1
TEMP=$(mktemp -d)
az artifacts universal download --feed dev-feed --name myapp --version "$1" --path "$TEMP" --project MyProject
az artifacts universal publish --feed prod-feed --name myapp --version "$1" --path "$TEMP" --description "Promoted" --project MyProject
rm -rf "$TEMP"Feed Audit Report
#!/bin/bash
# Generate feed usage report
FEED="myfeed"
REPORT_FILE="feed-report-$(date +%Y%m%d).txt"
echo "Feed Audit Report: $FEED" > "$REPORT_FILE"
echo "Generated: $(date)" >> "$REPORT_FILE"
echo "===================================" >> "$REPORT_FILE"
echo -e "\nFeed Details:" >> "$REPORT_FILE"
az artifacts feed show --feed "$FEED" --project MyProject >> "$REPORT_FILE"
echo -e "\nPackages:" >> "$REPORT_FILE"
az artifacts universal list --feed "$FEED" --project MyProject --output table >> "$REPORT_FILE"
echo -e "\nPermissions:" >> "$REPORT_FILE"
az artifacts feed permission list --feed "$FEED" --project MyProject --output table >> "$REPORT_FILE"
echo "Report saved to $REPORT_FILE"Package Cleanup
#!/bin/bash
# Delete old package versions (keep latest N versions)
FEED="myfeed"
PACKAGE_NAME="myapp"
KEEP_VERSIONS=5
echo "Cleaning up old versions of $PACKAGE_NAME (keeping latest $KEEP_VERSIONS)..."
# Get all versions (would need REST API for deletion)
az devops invoke \
--area packaging \
--resource packages \
--route-parameters feedId="$FEED" packageName="$PACKAGE_NAME" project=MyProject \
--api-version 6.0-preview.1 \
--http-method GET \
--query "versions[$(($KEEP_VERSIONS)):]" \
-o json | jq -r '.[].version' | while read version; do
echo "Would delete version: $version"
# Deletion requires REST API call
# az devops invoke --http-method DELETE ...
doneMulti-Feed Package Sync
#!/bin/bash
# Sync packages between feeds
SOURCE_FEED="dev-feed"
TARGET_FEED="staging-feed"
TEMP_DIR="/tmp/feed-sync"
echo "Syncing packages from $SOURCE_FEED to $TARGET_FEED..."
mkdir -p "$TEMP_DIR"
# List packages in source feed
az artifacts universal list --feed "$SOURCE_FEED" --project MyProject --output json | jq -r '.[] | "\(.name):\(.version)"' | while IFS=: read -r name version; do
echo "Syncing $name:$version..."
# Download from source
az artifacts universal download \
--feed "$SOURCE_FEED" \
--name "$name" \
--version "$version" \
--path "$TEMP_DIR/$name/$version" \
--project MyProject
# Publish to target
az artifacts universal publish \
--feed "$TARGET_FEED" \
--name "$name" \
--version "$version" \
--path "$TEMP_DIR/$name/$version" \
--description "Synced from $SOURCE_FEED" \
--project MyProject
# Cleanup
rm -rf "$TEMP_DIR/$name"
done
echo "Sync complete"Package Dependency Checker
#!/bin/bash
# Check if package dependencies exist in feed
FEED="myfeed"
PACKAGE_NAME=$1
VERSION=$2
if [ -z "$PACKAGE_NAME" ] || [ -z "$VERSION" ]; then
echo "Usage: $0 <package-name> <version>"
exit 1
fi
echo "Checking dependencies for $PACKAGE_NAME:$VERSION..."
# Download package
TEMP_DIR=$(mktemp -d)
az artifacts universal download \
--feed "$FEED" \
--name "$PACKAGE_NAME" \
--version "$VERSION" \
--path "$TEMP_DIR" \
--project MyProject
# Check for dependency file (format varies by package type)
if [ -f "$TEMP_DIR/package.json" ]; then
echo "Found package.json, checking npm dependencies..."
jq -r '.dependencies | keys[]' "$TEMP_DIR/package.json" | while read dep; do
echo " Dependency: $dep"
done
fi
# Cleanup
rm -rf "$TEMP_DIR"Best Practices
- Semantic versioning (major.minor.patch)
- Separate feeds per environment
- Retention policies for cleanup
- Feed views for lifecycle
- Feed permissions for security
- Automate via CI/CD
- Never overwrite versions
- Include descriptions
Advanced Patterns
CI/CD Integration
# Azure Pipelines YAML example
trigger:
- main
pool:
vmImage: "ubuntu-latest"
steps:
- task: UniversalPackages@0
displayName: "Publish Universal Package"
inputs:
command: "publish"
publishDirectory: "$(Build.ArtifactStagingDirectory)"
feedsToUsePublish: "internal"
vstsFeedPublish: "myfeed"
vstsFeedPackagePublish: "myapp"
versionOption: "patch"Package Metadata Management
#!/bin/bash
# Add metadata to published packages
FEED="myfeed"
PACKAGE="myapp"
VERSION="1.0.0"
# Get package ID
PACKAGE_ID=$(az devops invoke \
--area packaging \
--resource packages \
--route-parameters feedId="$FEED" packageName="$PACKAGE" project=MyProject \
--api-version 6.0-preview.1 \
--http-method GET \
--query "id" -o tsv)
# Update metadata (requires REST API)
echo "Package ID: $PACKAGE_ID"Feed Views Configuration
# List feed views
az devops invoke \
--area packaging \
--resource views \
--route-parameters feedId=FEED_ID project=MyProject \
--api-version 6.0-preview.1 \
--http-method GET
# Create view
# Requires REST API with JSON payloadTroubleshooting
Auth issues: Verify with az account show, check permissions, use PAT with Packaging scope
Not found: Verify feed exists, list packages, check spelling/version
Upload fails: Check quota, verify path exists, check permissions
Download fails: Verify version exists, check path is writable, use full paths
References
Azure Boards Complete Command Reference
Complete reference for az boards command group covering work items, queries, sprints, and team collaboration.
Work Item Management
Create Work Items
# Create user story
az boards work-item create --type "User Story" \
--title "Implement login feature" \
--assigned-to me@example.com \
--project MyProject
# Create bug with priority
az boards work-item create --type "Bug" \
--title "Login button not working" \
--assigned-to dev@example.com \
--fields "System.Priority=1" "System.Severity=1 - Critical"
# Create task with parent
az boards work-item create --type "Task" \
--title "Write unit tests" \
--assigned-to me@example.com \
--parent 123
# Create with description
az boards work-item create --type "User Story" \
--title "Add user profile page" \
--description "As a user, I want to view and edit my profile"
# Create with area and iteration
az boards work-item create --type "Feature" \
--title "Payment integration" \
--area "MyProject\\Backend" \
--iteration "MyProject\\Sprint 1"
# Create with tags
az boards work-item create --type "Bug" \
--title "Memory leak in API" \
--fields "System.Tags=performance,critical"Show Work Item
# Show work item by ID
az boards work-item show --id 123
# Show as JSON
az boards work-item show --id 123 --output json
# Open work item in browser
az boards work-item show --id 123 --open
# Show specific fields
az boards work-item show --id 123 --fields "System.Title" "System.State" "System.AssignedTo"Update Work Items
# Update work item state
az boards work-item update --id 123 --state "In Progress"
# Update assigned to
az boards work-item update --id 123 --assigned-to newdev@example.com
# Update title
az boards work-item update --id 123 --title "Updated title"
# Update description
az boards work-item update --id 123 --description "New description"
# Update custom fields
az boards work-item update --id 123 --fields "System.Priority=2" "Microsoft.VSTS.Common.Severity=2 - High"
# Update area path
az boards work-item update --id 123 --area "MyProject\\Frontend"
# Update iteration
az boards work-item update --id 123 --iteration "MyProject\\Sprint 2"
# Add comment
az boards work-item update --id 123 --discussion "This is a comment"
# Update multiple fields at once
az boards work-item update --id 123 \
--state "Resolved" \
--assigned-to qa@example.com \
--fields "System.Reason=Fixed"Delete Work Items
# Delete work item
az boards work-item delete --id 123 --yes
# Permanently delete (destroy)
az boards work-item delete --id 123 --destroy --yesWork Item Relations
Add Relations
# Add parent-child relationship
az boards work-item relation add --id 456 \
--relation-type parent \
--target-id 123
# Add related work item
az boards work-item relation add --id 456 \
--relation-type related \
--target-id 789
# Add predecessor (dependency)
az boards work-item relation add --id 456 \
--relation-type predecessor \
--target-id 123Show Relations
# Show all relations for work item
az boards work-item relation show --id 123
# Show as JSON for processing
az boards work-item relation show --id 123 --output jsonRemove Relations
# Remove relation by target ID
az boards work-item relation remove --id 456 --target-id 123 --relation-type parent --yesQueries (WIQL)
Basic Queries
# Query all active work items
az boards query --wiql "SELECT [System.Id], [System.Title], [System.State] FROM WorkItems WHERE [System.State] = 'Active'"
# Query work items assigned to me
az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.AssignedTo] = @Me"
# Query by work item type
az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.WorkItemType] = 'Bug'"
# Query with multiple conditions
az boards query --wiql "SELECT [System.Id], [System.Title], [System.State] FROM WorkItems WHERE [System.WorkItemType] = 'Bug' AND [System.State] = 'Active' AND [System.Priority] = 1"Advanced WIQL Queries
# Query by date range
az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.CreatedDate] >= '2025-01-01' AND [System.CreatedDate] <= '2025-12-31'"
# Query by area path
az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.AreaPath] UNDER 'MyProject\\Backend'"
# Query by iteration
az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.IterationPath] = @CurrentIteration"
# Query with tags
az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.Tags] CONTAINS 'critical'"
# Query changed in last 7 days
az boards query --wiql "SELECT [System.Id], [System.Title], [System.ChangedDate] FROM WorkItems WHERE [System.ChangedDate] >= @Today - 7"
# Query with ORDER BY
az boards query --wiql "SELECT [System.Id], [System.Title], [System.Priority] FROM WorkItems WHERE [System.State] = 'Active' ORDER BY [System.Priority] ASC, [System.CreatedDate] DESC"Query Output Formatting
# Output as table
az boards query --wiql "SELECT [System.Id], [System.Title], [System.State] FROM WorkItems" --output table
# Output as JSON for processing
az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems" --output json > work-items.json
# Use JMESPath to filter results
az boards query --wiql "SELECT [System.Id], [System.Title], [System.State] FROM WorkItems" --output json | jq '.[] | select(.fields."System.State" == "Active")'Iterations (Sprints)
List Iterations
# List all iterations for project
az boards iteration project list --project MyProject
# List iterations with depth
az boards iteration project list --depth 3
# List as table
az boards iteration project list --output tableShow Iteration
# Show specific iteration
az boards iteration project show --id "Sprint 1" --project MyProject
# Show with children
az boards iteration project show --id "Sprint 1" --children trueCreate Iteration
# Create iteration
az boards iteration project create --name "Sprint 3" --project MyProject
# Create with dates
az boards iteration project create --name "Sprint 4" \
--start-date "2025-12-01" \
--finish-date "2025-12-14" \
--project MyProject
# Create child iteration
az boards iteration project create --name "Sprint 5" \
--path "MyProject\\2025" \
--project MyProjectUpdate Iteration
# Update iteration dates
az boards iteration project update --id "Sprint 3" \
--start-date "2025-12-15" \
--finish-date "2025-12-28" \
--project MyProject
# Update iteration name
az boards iteration project update --id "Sprint 3" \
--name "Sprint 3 Extended" \
--project MyProjectDelete Iteration
# Delete iteration
az boards iteration project delete --id "Sprint Old" --yes --project MyProjectTeam Iterations
# List team iterations
az boards iteration team list --team "MyTeam" --project MyProject
# Add iteration to team
az boards iteration team add --id "Sprint 1" --team "MyTeam" --project MyProject
# Show team iteration
az boards iteration team show --id "Sprint 1" --team "MyTeam" --project MyProject
# Remove iteration from team
az boards iteration team remove --id "Sprint 1" --team "MyTeam" --yes --project MyProjectArea Paths
List Areas
# List all areas for project
az boards area project list --project MyProject
# List with depth
az boards area project list --depth 3
# List as table
az boards area project list --output tableShow Area
# Show specific area
az boards area project show --id "Backend" --project MyProject
# Show with children
az boards area project show --id "Backend" --children trueCreate Area
# Create area
az boards area project create --name "Mobile" --project MyProject
# Create child area
az boards area project create --name "iOS" \
--path "MyProject\\Mobile" \
--project MyProjectUpdate Area
# Update area name
az boards area project update --id "Mobile" \
--name "Mobile Apps" \
--project MyProjectDelete Area
# Delete area
az boards area project delete --id "OldArea" --yes --project MyProjectTeam Areas
# List team areas
az boards area team list --team "MyTeam" --project MyProject
# Add area to team
az boards area team add --path "MyProject\\Backend" --team "MyTeam" --project MyProject
# Update team area
az boards area team update --path "MyProject\\Backend" \
--include-sub-areas true \
--team "MyTeam" \
--project MyProject
# Remove area from team
az boards area team remove --path "MyProject\\Backend" --team "MyTeam" --yes --project MyProjectScripting Examples
Bulk Work Item Creation
#!/bin/bash
while IFS=, read -r type title assignee; do
az boards work-item create --type "$type" --title "$title" --assigned-to "$assignee" --project MyProject
done < work-items.csvSprint Report
#!/bin/bash
SPRINT="Sprint 1"
echo "Sprint Report: $SPRINT"
az boards query --wiql "SELECT [System.Id] FROM WorkItems WHERE [System.IterationPath] = '$SPRINT'" --output json | jq 'length'
az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.IterationPath] = '$SPRINT' AND [System.State] = 'Closed'" --output table
az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.IterationPath] = '$SPRINT' AND [System.Tags] CONTAINS 'blocked'" --output tableWork Item State Transition
#!/bin/bash
FROM_STATE="New"; TO_STATE="Active"
az boards query --wiql "SELECT [System.Id] FROM WorkItems WHERE [System.State] = '$FROM_STATE'" --output json | jq -r '.[].id' | while read id; do
az boards work-item update --id "$id" --state "$TO_STATE"
doneTeam Velocity Report
#!/bin/bash
# Calculate team velocity
SPRINTS=("Sprint 1" "Sprint 2" "Sprint 3")
for sprint in "${SPRINTS[@]}"; do
echo "Velocity for $sprint:"
az boards query --wiql "SELECT [System.Id], [Microsoft.VSTS.Scheduling.StoryPoints] FROM WorkItems WHERE [System.IterationPath] = '$sprint' AND [System.State] = 'Closed'" --output json | jq '[.[].fields."Microsoft.VSTS.Scheduling.StoryPoints" // 0] | add'
doneAssigned Work Items Dashboard
#!/bin/bash
# Show work items assigned to current user
USER="me@example.com"
echo "Work Items Dashboard for $USER"
echo "================================="
echo -e "\nActive Work:"
az boards query --wiql "SELECT [System.Id], [System.Title], [System.WorkItemType] FROM WorkItems WHERE [System.AssignedTo] = '$USER' AND [System.State] = 'Active'" --output table
echo -e "\nIn Progress:"
az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.AssignedTo] = '$USER' AND [System.State] = 'In Progress'" --output table
echo -e "\nBlocked:"
az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.AssignedTo] = '$USER' AND [System.Tags] CONTAINS 'blocked'" --output tableBest Practices
- WIQL for complex queries
- Use macros: @Me, @CurrentIteration
- Set area/iteration defaults
- Tag work items for organization
- Parent-child relationships for hierarchy
- Query before bulk updates
- JSON for scripting
- Save frequent WIQL queries
WIQL Reference
Common Fields
[System.Id]- Work item ID[System.Title]- Work item title[System.State]- Current state[System.WorkItemType]- Type (Bug, User Story, Task, etc.)[System.AssignedTo]- Assigned user[System.CreatedBy]- Creator[System.CreatedDate]- Creation date[System.ChangedDate]- Last modified date[System.AreaPath]- Area path[System.IterationPath]- Iteration path[System.Tags]- Tags[System.Priority]- Priority (1-4)[Microsoft.VSTS.Common.Severity]- Severity[Microsoft.VSTS.Scheduling.StoryPoints]- Story points
Operators
=- Equals<>- Not equals>,<,>=,<=- ComparisonCONTAINS- String containsUNDER- Area/iteration path hierarchyIN- Value in listAND,OR- Logical operators
Macros
@Me- Current user@Today- Today's date@CurrentIteration- Current iteration@Project- Current project
Troubleshooting
Not found: Verify with az boards work-item show --id 123 or search by title
WIQL errors: Field names are case-sensitive, use brackets [Field.Name], proper escaping
Permissions: Verify with az devops project show --project MyProject
References
Azure Pipelines Complete Command Reference
Complete reference for az pipelines command group covering build and release automation.
Pipeline Management
List Pipelines
# List all pipelines
az pipelines list --project MyProject
# List with specific name filter
az pipelines list --name "*API*" --output table
# List with top N results
az pipelines list --top 10
# Get pipeline details as JSON
az pipelines list --output json > pipelines.jsonShow Pipeline Details
# Show pipeline by name
az pipelines show --name "MyPipeline" --project MyProject
# Show pipeline by ID
az pipelines show --id 123
# Open pipeline in browser
az pipelines show --name "MyPipeline" --openCreate Pipeline
# Create from YAML file in repository
az pipelines create --name "NewPipeline" \
--repository myrepo \
--branch main \
--yml-path azure-pipelines.yml \
--project MyProject
# Create with service connection
az pipelines create --name "Deploy-Pipeline" \
--repository myrepo \
--service-connection "AzureRM-Connection" \
--yml-path deploy/azure-pipelines.yml
# Create and skip first run
az pipelines create --name "Test-Pipeline" \
--repository myrepo \
--yml-path test-pipeline.yml \
--skip-first-run trueUpdate Pipeline
# Update pipeline YAML path
az pipelines update --id 123 --yml-path new/path/azure-pipelines.yml
# Update pipeline name
az pipelines update --id 123 --new-name "RenamedPipeline"
# Update pipeline description
az pipelines update --id 123 --description "Updated description"Delete Pipeline
# Delete pipeline by ID
az pipelines delete --id 123 --yes
# Delete pipeline by name
az pipelines delete --name "OldPipeline" --yes --project MyProjectPipeline Runs
List Runs
# List all runs
az pipelines runs list --project MyProject
# List runs for specific pipeline
az pipelines runs list --pipeline-ids 123
# List top 20 recent runs
az pipelines runs list --top 20
# List runs by status
az pipelines runs list --status completed
az pipelines runs list --status inProgress
az pipelines runs list --status failed
# List runs by result
az pipelines runs list --query-order FinishTimeDesc --top 10
# Filter runs with JMESPath
az pipelines runs list --query "[?result=='failed'].{ID:id, Pipeline:pipeline.name, Branch:sourceBranch}"Show Run Details
# Show run by ID
az pipelines runs show --id 456
# Show run and open in browser
az pipelines runs show --id 456 --open
# Get run as JSON for processing
az pipelines runs show --id 456 --output jsonRun Pipeline
# Run pipeline by name
az pipelines run --name "MyPipeline"
# Run specific branch
az pipelines run --name "MyPipeline" --branch feature/new-feature
# Run with parameters
az pipelines run --name "MyPipeline" --variables key1=value1 key2=value2
# Run and open in browser
az pipelines run --name "MyPipeline" --open
# Run specific pipeline ID
az pipelines run --id 123 --branch main
# Run with commit
az pipelines run --name "MyPipeline" --commit-id abc123def456Pipeline Variables
List Variables
# List all pipeline variables
az pipelines variable list --pipeline-name "MyPipeline"
# List as JSON
az pipelines variable list --pipeline-id 123 --output jsonCreate Variable
# Create pipeline variable
az pipelines variable create --name "API_KEY" --value "secret123" --pipeline-name "MyPipeline"
# Create secret variable
az pipelines variable create --name "PASSWORD" --value "secret" --secret true --pipeline-name "MyPipeline"
# Create variable with allow-override
az pipelines variable create --name "ENV" --value "dev" --allow-override true --pipeline-name "MyPipeline"Update Variable
# Update variable value
az pipelines variable update --name "API_KEY" --value "newsecret" --pipeline-name "MyPipeline"
# Update and make secret
az pipelines variable update --name "TOKEN" --secret true --pipeline-name "MyPipeline"Delete Variable
# Delete pipeline variable
az pipelines variable delete --name "OLD_VAR" --pipeline-name "MyPipeline" --yesVariable Groups
List Variable Groups
# List all variable groups
az pipelines variable-group list --project MyProject
# List with specific name
az pipelines variable-group list --group-name "Production"
# List as table
az pipelines variable-group list --output tableCreate Variable Group
# Create variable group
az pipelines variable-group create --name "Production" --variables key1=value1 key2=value2
# Create with description
az pipelines variable-group create --name "Staging" \
--variables ENV=staging API_URL=https://staging.api.com \
--description "Staging environment variables"
# Create from JSON file
az pipelines variable-group create --name "Config" --variables @config.jsonUpdate Variable Group
# Update variable group
az pipelines variable-group update --group-id 789 --name "NewName"
# Add/update variables in group
az pipelines variable-group variable create --group-id 789 --name "NEW_VAR" --value "value"
# Update variable in group
az pipelines variable-group variable update --group-id 789 --name "VAR" --value "newvalue"
# Delete variable from group
az pipelines variable-group variable delete --group-id 789 --name "OLD_VAR" --yesDelete Variable Group
# Delete variable group
az pipelines variable-group delete --group-id 789 --yesAdvanced Pipeline Operations
Pipeline Tags
# Add tag to run
az pipelines runs tag add --run-id 456 --tags "production" "release-1.0"
# List tags for run
az pipelines runs tag list --run-id 456
# Delete tag from run
az pipelines runs tag delete --run-id 456 --tag "old-tag"Pipeline Artifacts
# List artifacts for run
az pipelines runs artifact list --run-id 456
# Download artifact
az pipelines runs artifact download --run-id 456 --artifact-name "drop" --path ./download
# Upload artifact (typically done in pipeline YAML)
# This is usually handled by PublishPipelineArtifact taskPipeline Approval
# List pending approvals
az pipelines approval list --project MyProject
# Approve a deployment
az pipelines approval update --approval-id 123 --status approved --comments "Approved by CLI"
# Reject a deployment
az pipelines approval update --approval-id 123 --status rejected --comments "Failed validation"Query and Filtering Examples
Complex JMESPath Queries
# Failed runs in last 7 days
az pipelines runs list --query "[?result=='failed' && finishTime>='2025-11-17'].{ID:id, Name:pipeline.name, Time:finishTime}"
# Running builds by branch
az pipelines runs list --status inProgress --query "[].{ID:id, Pipeline:pipeline.name, Branch:sourceBranch}" --output table
# Success rate calculation
az pipelines runs list --top 100 --query "length([?result=='succeeded'])"
# Get all build reasons
az pipelines runs list --query "[].{ID:id, Reason:reason}" --output table
# Filter by source branch
az pipelines runs list --query "[?sourceBranch=='refs/heads/main']" --output tableScripting Patterns
# Run all pipelines with specific name pattern
az pipelines list --query "[?contains(name, 'API')].id" -o tsv | while read id; do
az pipelines run --id "$id"
done
# Get latest successful run for each pipeline
az pipelines list -o json | jq -r '.[].id' | while read id; do
az pipelines runs list --pipeline-ids "$id" --result succeeded --top 1
done
# Monitor pipeline until completion
RUN_ID=$(az pipelines run --name "MyPipeline" --query "id" -o tsv)
while true; do
STATUS=$(az pipelines runs show --id "$RUN_ID" --query "status" -o tsv)
if [ "$STATUS" != "inProgress" ]; then
echo "Build completed with status: $STATUS"
break
fi
sleep 10
doneCommon Patterns
Daily Build Report
#!/bin/bash
TODAY=$(date +%Y-%m-%d)
REPORT="build-report-$TODAY.txt"
echo "Build Report for $TODAY" > "$REPORT"
az pipelines runs list --query "[?startTime>='$TODAY'].{Pipeline:pipeline.name, Status:status, Result:result}" --output table >> "$REPORT"
az pipelines runs list --query "[?result=='failed' && startTime>='$TODAY'].{ID:id, Pipeline:pipeline.name}" --output table >> "$REPORT"Automated Pipeline Testing
#!/bin/bash
PIPELINE="MyPipeline"
BRANCHES=("main" "develop" "feature/test")
for branch in "${BRANCHES[@]}"; do
az pipelines run --name "$PIPELINE" --branch "$branch"
doneEnvironment-Specific Deployment
#!/bin/bash
case $1 in
dev|staging) az pipelines run --name "Deploy-$1" --variables ENV=$1 ;;
prod)
read -p "Deploy to production? (yes/no): " confirm
[ "$confirm" == "yes" ] && az pipelines run --name "Deploy-Prod" --variables ENV=prod ;;
*) echo "Unknown environment: $1"; exit 1 ;;
esacBest Practices
- Use pipeline IDs (stable) over names
- Set default org/project
- JMESPath for filtering
- Tag runs for reporting
- Use variable groups
- Mark secrets with
--secret true - JSON for scripts, table for humans
Troubleshooting
Pipeline not found: List all with az pipelines list --output table, use ID
Run failures: Check with az pipelines runs show --id 456 --output json
Variables: Verify with az pipelines variable list --pipeline-name "MyPipeline"
References
Azure Repos Complete Command Reference
Complete reference for az repos command group covering Git repositories, pull requests, policies, and branch management.
Repository Management
List Repositories
# List all repositories
az repos list --project MyProject
# List as table
az repos list --output table
# Get repository details as JSON
az repos list --output json > repositories.json
# Filter by name
az repos list --query "[?contains(name, 'API')]" --output tableShow Repository
# Show repository by name
az repos show --repository myrepo --project MyProject
# Show repository by ID
az repos show --repository REPO_ID
# Open repository in browser
az repos show --repository myrepo --openCreate Repository
# Create repository
az repos create --name "newrepo" --project MyProject
# Create with initialization
az repos create --name "api-service" --project MyProject
# Create from template (requires API call)
az devops invoke --area git --resource repositories \
--route-parameters project=MyProject \
--http-method POST \
--in-file repo-config.jsonDelete Repository
# Delete repository
az repos delete --id REPO_ID --yes
# Delete by name
az repos delete --name "oldrepo" --project MyProject --yesUpdate Repository
# Rename repository
az repos update --repository myrepo --name "newname" --project MyProject
# Update default branch
az repos update --repository myrepo --default-branch mainPull Requests
List Pull Requests
# List all active pull requests
az repos pr list --repository myrepo --status active
# List all PRs (including completed)
az repos pr list --repository myrepo --status all
# List PRs created by me
az repos pr list --creator me@example.com
# List PRs assigned to me as reviewer
az repos pr list --reviewer me@example.com
# List PRs targeting specific branch
az repos pr list --target-branch main
# List PRs from source branch
az repos pr list --source-branch feature/new-feature
# List as table
az repos pr list --status active --output table
# Top N PRs
az repos pr list --top 10Show Pull Request
# Show PR by ID
az repos pr show --id 123
# Show PR and open in browser
az repos pr show --id 123 --open
# Get PR as JSON
az repos pr show --id 123 --output jsonCreate Pull Request
# Create PR from current branch
az repos pr create --repository myrepo \
--source-branch feature/new-feature \
--target-branch main \
--title "Add new feature" \
--description "This PR adds the new feature"
# Create PR with reviewers
az repos pr create --repository myrepo \
--source-branch feature/api \
--target-branch main \
--title "API updates" \
--reviewers reviewer1@example.com reviewer2@example.com
# Create PR with work items linked
az repos pr create --repository myrepo \
--source-branch bugfix/issue-123 \
--target-branch main \
--title "Fix bug #123" \
--work-items 123 456
# Create PR and auto-complete
az repos pr create --repository myrepo \
--source-branch feature/auto \
--target-branch main \
--title "Auto-merge feature" \
--auto-complete true \
--delete-source-branch true
# Create PR and open in browser
az repos pr create --repository myrepo \
--source-branch feature/new \
--target-branch main \
--title "New feature" \
--openUpdate Pull Request
# Update PR title
az repos pr update --id 123 --title "Updated title"
# Update PR description
az repos pr update --id 123 --description "Updated description"
# Update PR status
az repos pr update --id 123 --status completed
# Abandon PR
az repos pr update --id 123 --status abandoned
# Reactivate PR
az repos pr update --id 123 --status active
# Set auto-complete
az repos pr update --id 123 --auto-complete true
# Update draft status
az repos pr update --id 123 --draft falsePull Request Reviewers
# Add reviewers
az repos pr reviewer add --id 123 --reviewers dev1@example.com dev2@example.com
# List reviewers
az repos pr reviewer list --id 123
# Remove reviewer
az repos pr reviewer remove --id 123 --reviewers dev1@example.comPull Request Work Items
# Add work items to PR
az repos pr work-item add --id 123 --work-items 456 789
# List work items linked to PR
az repos pr work-item list --id 123
# Remove work item from PR
az repos pr work-item remove --id 123 --work-items 456Set Pull Request Vote
# Approve PR
az repos pr set-vote --id 123 --vote approve
# Approve with suggestions
az repos pr set-vote --id 123 --vote approve-with-suggestions
# Wait for author
az repos pr set-vote --id 123 --vote wait-for-author
# Reject PR
az repos pr set-vote --id 123 --vote reject
# Reset vote
az repos pr set-vote --id 123 --vote resetPull Request Policies
List Policies
# List all policies for repository
az repos policy list --repository-id REPO_ID --project MyProject
# List policies for specific branch
az repos policy list --branch main --repository-id REPO_IDApprover Count Policy
# Create minimum approver count policy
az repos policy approver-count create \
--allow-downvotes false \
--blocking true \
--enabled true \
--minimum-approver-count 2 \
--creator-vote-counts false \
--repository-id REPO_ID \
--branch main
# Update approver count policy
az repos policy approver-count update \
--id POLICY_ID \
--minimum-approver-count 3
# Show approver count policy
az repos policy approver-count show --id POLICY_IDBuild Policy
# Create build validation policy
az repos policy build create \
--blocking true \
--enabled true \
--build-definition-id BUILD_ID \
--display-name "CI Build" \
--manual-queue-only false \
--queue-on-source-update-only true \
--repository-id REPO_ID \
--branch main
# Update build policy
az repos policy build update \
--id POLICY_ID \
--blocking true
# Show build policy
az repos policy build show --id POLICY_IDComment Resolution Policy
# Create comment resolution policy
az repos policy comment-required create \
--blocking true \
--enabled true \
--repository-id REPO_ID \
--branch main
# Update comment required policy
az repos policy comment-required update \
--id POLICY_ID \
--enabled falseFile Size Policy
# Create file size policy
az repos policy file-size create \
--blocking true \
--enabled true \
--maximum-git-blob-size 10 \
--repository-id REPO_ID \
--use-uncompressed-size true
# Update file size policy
az repos policy file-size update \
--id POLICY_ID \
--maximum-git-blob-size 5Work Item Linking Policy
# Create work item linking policy
az repos policy work-item-linking create \
--blocking true \
--enabled true \
--repository-id REPO_ID \
--branch main
# Update work item linking policy
az repos policy work-item-linking update \
--id POLICY_ID \
--enabled falseBranches
List Branches
# List all branches
az repos ref list --repository myrepo --project MyProject
# Filter branches only
az repos ref list --repository myrepo --filter heads
# List tags only
az repos ref list --repository myrepo --filter tagsCreate Branch
# Create branch
az repos ref create \
--name refs/heads/feature/new \
--object-id COMMIT_SHA \
--repository myrepo
# Create branch from main
MAIN_SHA=$(az repos ref list --repository myrepo --filter heads/main --query "[0].objectId" -o tsv)
az repos ref create \
--name refs/heads/feature/branch \
--object-id "$MAIN_SHA" \
--repository myrepoDelete Branch
# Delete branch
az repos ref delete \
--name refs/heads/old-feature \
--object-id COMMIT_SHA \
--repository myrepoLock/Unlock Branch
# Lock branch
az repos ref lock \
--name refs/heads/main \
--repository myrepo
# Unlock branch
az repos ref unlock \
--name refs/heads/main \
--repository myrepoImports
Import Repository
# Import from Git URL
az repos import create \
--git-url https://github.com/username/repo.git \
--repository myrepo \
--project MyProject
# Import with authentication
az repos import create \
--git-url https://github.com/username/private-repo.git \
--repository myrepo \
--user-name username \
--git-service-endpoint-id SERVICE_IDScripting Examples
Batch PR Creation
#!/bin/bash
az repos ref list --repository myrepo --filter heads --query "[?contains(name, 'feature/')].name" -o tsv | while read branch; do
az repos pr create --repository myrepo --source-branch "${branch#refs/heads/}" --target-branch main --title "Auto PR: ${branch#refs/heads/}"
donePR Status Dashboard
#!/bin/bash
echo "Pull Request Dashboard"
az repos pr list --repository myrepo --status active --output table
az repos pr list --repository myrepo --status active --query "[?reviewers[?vote==0]].{ID:pullRequestId, Title:title}" --output table
az repos pr list --repository myrepo --status active --query "[?reviewers[?vote==10]].{ID:pullRequestId, Title:title}" --output tableAuto-Approve Automated PRs
#!/bin/bash
az repos pr list --repository myrepo --creator "automation@example.com" --status active --query "[].pullRequestId" -o tsv | while read pr_id; do
az repos pr set-vote --id "$pr_id" --vote approve
doneClone All Repositories
#!/bin/bash
# Clone all repositories from project
OUTPUT_DIR="./repos"
mkdir -p "$OUTPUT_DIR"
az repos list --project MyProject --query "[].{Name:name, URL:remoteUrl}" -o tsv | while IFS=$'\t' read -r name url; do
echo "Cloning $name..."
git clone "$url" "$OUTPUT_DIR/$name"
donePR Merge Automation
#!/bin/bash
# Merge approved PRs automatically
az repos pr list --repository myrepo --status active --output json | jq -r '.[] | select(.reviewers | all(.vote == 10)) | .pullRequestId' | while read pr_id; do
echo "Merging approved PR $pr_id"
az repos pr update --id "$pr_id" --status completed --delete-source-branch true
doneBranch Cleanup
#!/bin/bash
# Delete merged feature branches
# Get merged branches
git branch -r --merged origin/main | grep 'origin/feature/' | sed 's|origin/||' | while read branch; do
echo "Deleting merged branch: $branch"
COMMIT_SHA=$(az repos ref list --repository myrepo --filter "heads/$branch" --query "[0].objectId" -o tsv)
az repos ref delete --name "refs/heads/$branch" --object-id "$COMMIT_SHA" --repository myrepo
donePR Quality Check
#!/bin/bash
# Check PR quality before approval
PR_ID=$1
echo "Checking PR $PR_ID..."
# Check for work items
WI_COUNT=$(az repos pr work-item list --id "$PR_ID" --output json | jq 'length')
if [ "$WI_COUNT" -eq 0 ]; then
echo "WARNING: No work items linked"
fi
# Check for description
DESC=$(az repos pr show --id "$PR_ID" --query "description" -o tsv)
if [ -z "$DESC" ]; then
echo "WARNING: No description"
fi
# Check for reviewers
REVIEWER_COUNT=$(az repos pr reviewer list --id "$PR_ID" --output json | jq 'length')
if [ "$REVIEWER_COUNT" -lt 2 ]; then
echo "WARNING: Less than 2 reviewers"
fi
# Check build status (if applicable)
echo "Build status check would go here"Best Practices
- PR templates in
.azuredevops/pull_request_template.md - Link work items for traceability
- Branch policies enforce quality
- Auto-complete with caution
- Delete source branches after merge
- Review all changes
- Draft PRs for WIP
- Tag relevant reviewers
Advanced Patterns
Custom PR Workflow
#!/bin/bash
# Custom PR creation with validation
SOURCE_BRANCH=$(git branch --show-current)
TARGET_BRANCH="main"
# Check if branch follows naming convention
if [[ ! "$SOURCE_BRANCH" =~ ^(feature|bugfix|hotfix)/ ]]; then
echo "Error: Branch must start with feature/, bugfix/, or hotfix/"
exit 1
fi
# Extract work item from branch name (e.g., feature/123-description)
WORK_ITEM=$(echo "$SOURCE_BRANCH" | grep -oP '\d+' | head -1)
if [ -z "$WORK_ITEM" ]; then
echo "Error: Branch name must include work item number"
exit 1
fi
# Create PR with work item linked
az repos pr create \
--repository myrepo \
--source-branch "$SOURCE_BRANCH" \
--target-branch "$TARGET_BRANCH" \
--title "$(git log -1 --pretty=%B)" \
--work-items "$WORK_ITEM" \
--openRepository Sync
#!/bin/bash
# Sync repository state across organizations
SOURCE_ORG="https://dev.azure.com/source-org"
TARGET_ORG="https://dev.azure.com/target-org"
PROJECT="MyProject"
REPO="myrepo"
# Get source repo info
az devops configure --defaults organization="$SOURCE_ORG" project="$PROJECT"
SOURCE_POLICIES=$(az repos policy list --repository-id REPO_ID --output json)
# Create policies in target
az devops configure --defaults organization="$TARGET_ORG" project="$PROJECT"
echo "$SOURCE_POLICIES" | jq -c '.[]' | while read policy; do
# Parse and recreate policy
echo "Creating policy: $(echo $policy | jq -r '.type.displayName')"
doneTroubleshooting
PR creation fails: Verify branch with az repos ref list, check conflicts, verify permissions
Policy issues: Check with az repos pr show --id 123 --query "mergeStatus"
Reference errors: Use fully qualified refs refs/heads/branch-name, not branch-name
References
CI/CD Automation Workflows
Advanced CI/CD automation patterns using Azure DevOps CLI for build, test, and deployment workflows.
Complete CI/CD Pipeline Automation
End-to-End Pipeline Setup
#!/bin/bash
# Complete CI/CD pipeline setup for new project
PROJECT="MyProject"
REPO="myrepo"
PIPELINE_NAME="CI-CD-Pipeline"
# 1. Create repository
echo "Creating repository..."
az repos create --name "$REPO" --project "$PROJECT"
# 2. Create build pipeline from YAML
echo "Creating build pipeline..."
az pipelines create \
--name "$PIPELINE_NAME" \
--repository "$REPO" \
--branch main \
--yml-path azure-pipelines.yml \
--project "$PROJECT"
# 3. Set up pipeline variables
echo "Configuring pipeline variables..."
az pipelines variable create \
--name "BuildConfiguration" \
--value "Release" \
--pipeline-name "$PIPELINE_NAME"
az pipelines variable create \
--name "Environment" \
--value "Production" \
--pipeline-name "$PIPELINE_NAME"
# 4. Create variable group for secrets
echo "Creating variable group..."
az pipelines variable-group create \
--name "Production-Secrets" \
--variables API_KEY="placeholder" DB_CONNECTION="placeholder" \ # pragma: allowlist secret
--project "$PROJECT"
echo "CI/CD pipeline setup complete!"
echo "Update secrets in Azure DevOps UI for security"Multi-Environment Deployment
Environment-Specific Pipeline Execution
#!/bin/bash
# Deploy to multiple environments with approval gates
PIPELINE_NAME="Deploy-Pipeline"
BRANCH="main"
deploy_to_environment() {
local env=$1
local require_approval=$2
echo "Deploying to $env..."
# Run pipeline with environment-specific variables
RUN_ID=$(az pipelines run \
--name "$PIPELINE_NAME" \
--branch "$BRANCH" \
--variables Environment="$env" \
--query "id" -o tsv)
echo "Deployment started: Run ID $RUN_ID"
if [ "$require_approval" == "true" ]; then
echo "Waiting for approval..."
# Monitor for approval (polling)
while true; do
STATUS=$(az pipelines runs show --id "$RUN_ID" --query "status" -o tsv)
if [ "$STATUS" == "completed" ]; then
RESULT=$(az pipelines runs show --id "$RUN_ID" --query "result" -o tsv)
echo "Deployment $RESULT"
break
fi
sleep 30
done
fi
return 0
}
# Deploy to dev (no approval)
deploy_to_environment "dev" "false"
# Deploy to staging (requires approval)
deploy_to_environment "staging" "true"
# Deploy to production (requires approval)
read -p "Deploy to production? (yes/no): " confirm
if [ "$confirm" == "yes" ]; then
deploy_to_environment "production" "true"
fiBlue-Green Deployment Pattern
#!/bin/bash
# Blue-green deployment with automatic rollback
PIPELINE="Deploy-BlueGreen"
CURRENT_ENV="blue"
TARGET_ENV="green"
echo "Current environment: $CURRENT_ENV"
echo "Deploying to: $TARGET_ENV"
# Deploy to target environment
RUN_ID=$(az pipelines run \
--name "$PIPELINE" \
--variables DeploymentSlot="$TARGET_ENV" \
--query "id" -o tsv)
# Wait for deployment
while true; do
STATUS=$(az pipelines runs show --id "$RUN_ID" --query "status" -o tsv)
if [ "$STATUS" == "completed" ]; then
break
fi
sleep 10
done
RESULT=$(az pipelines runs show --id "$RUN_ID" --query "result" -o tsv)
if [ "$RESULT" == "succeeded" ]; then
echo "Deployment succeeded"
echo "Run smoke tests on $TARGET_ENV..."
# Smoke tests would go here
SMOKE_TEST_PASSED=true
if [ "$SMOKE_TEST_PASSED" == "true" ]; then
echo "Switching traffic to $TARGET_ENV"
# Traffic switch command would go here
else
echo "Smoke tests failed, keeping $CURRENT_ENV active"
fi
else
echo "Deployment failed, $CURRENT_ENV remains active"
fiAutomated Testing Integration
Test Execution and Reporting
#!/bin/bash
# Run tests and generate reports
PIPELINE="Test-Pipeline"
TEST_BRANCH=$1
if [ -z "$TEST_BRANCH" ]; then
TEST_BRANCH="main"
fi
echo "Running tests for branch: $TEST_BRANCH"
# Run test pipeline
RUN_ID=$(az pipelines run \
--name "$PIPELINE" \
--branch "$TEST_BRANCH" \
--query "id" -o tsv)
echo "Test run started: $RUN_ID"
# Monitor test execution
while true; do
STATUS=$(az pipelines runs show --id "$RUN_ID" --query "status" -o tsv)
echo "Test status: $STATUS"
if [ "$STATUS" == "completed" ]; then
break
fi
sleep 15
done
# Get test results
RESULT=$(az pipelines runs show --id "$RUN_ID" --query "result" -o tsv)
echo "Test Result: $RESULT"
# Open test results in browser
az pipelines runs show --id "$RUN_ID" --open
if [ "$RESULT" == "succeeded" ]; then
echo "All tests passed!"
exit 0
else
echo "Tests failed!"
exit 1
fiParallel Test Execution
#!/bin/bash
# Run multiple test suites in parallel
declare -a TEST_PIPELINES=(
"Unit-Tests"
"Integration-Tests"
"E2E-Tests"
)
declare -a RUN_IDS=()
echo "Starting parallel test execution..."
# Start all test pipelines
for pipeline in "${TEST_PIPELINES[@]}"; do
echo "Starting $pipeline..."
RUN_ID=$(az pipelines run --name "$pipeline" --query "id" -o tsv)
RUN_IDS+=("$RUN_ID")
echo " Run ID: $RUN_ID"
done
echo "Monitoring test execution..."
# Monitor all runs
ALL_COMPLETED=false
while [ "$ALL_COMPLETED" == "false" ]; do
ALL_COMPLETED=true
for i in "${!RUN_IDS[@]}"; do
RUN_ID="${RUN_IDS[$i]}"
STATUS=$(az pipelines runs show --id "$RUN_ID" --query "status" -o tsv)
if [ "$STATUS" != "completed" ]; then
ALL_COMPLETED=false
fi
done
if [ "$ALL_COMPLETED" == "false" ]; then
sleep 10
fi
done
# Check results
echo "Test Results:"
ALL_PASSED=true
for i in "${!RUN_IDS[@]}"; do
RUN_ID="${RUN_IDS[$i]}"
PIPELINE="${TEST_PIPELINES[$i]}"
RESULT=$(az pipelines runs show --id "$RUN_ID" --query "result" -o tsv)
echo " $PIPELINE: $RESULT"
if [ "$RESULT" != "succeeded" ]; then
ALL_PASSED=false
fi
done
if [ "$ALL_PASSED" == "true" ]; then
echo "All tests passed!"
exit 0
else
echo "Some tests failed!"
exit 1
fiBuild Artifact Management
Artifact Publishing and Versioning
#!/bin/bash
# Build, version, and publish artifacts
FEED="build-artifacts"
PACKAGE="myapp"
BUILD_PIPELINE="Build-Pipeline"
# Generate semantic version
MAJOR=1
MINOR=0
PATCH=$(git rev-list --count HEAD)
VERSION="$MAJOR.$MINOR.$PATCH"
echo "Building version $VERSION..."
# Run build pipeline
RUN_ID=$(az pipelines run \
--name "$BUILD_PIPELINE" \
--variables BuildNumber="$VERSION" \
--query "id" -o tsv)
# Wait for build
while true; do
STATUS=$(az pipelines runs show --id "$RUN_ID" --query "status" -o tsv)
if [ "$STATUS" == "completed" ]; then
break
fi
sleep 10
done
RESULT=$(az pipelines runs show --id "$RUN_ID" --query "result" -o tsv)
if [ "$RESULT" == "succeeded" ]; then
echo "Build succeeded!"
# Download build artifacts
az pipelines runs artifact download \
--run-id "$RUN_ID" \
--artifact-name "drop" \
--path ./artifacts
# Publish to Azure Artifacts
az artifacts universal publish \
--feed "$FEED" \
--name "$PACKAGE" \
--version "$VERSION" \
--path ./artifacts/drop \
--description "Build $VERSION from run $RUN_ID"
echo "Published $PACKAGE:$VERSION to feed $FEED"
# Tag the run
az pipelines runs tag add --run-id "$RUN_ID" --tags "published" "v$VERSION"
else
echo "Build failed!"
exit 1
fiContinuous Deployment Patterns
Automated Deployment on Successful Build
#!/bin/bash
# Automatically deploy when build succeeds
BUILD_PIPELINE="Build-Pipeline"
DEPLOY_PIPELINE="Deploy-Pipeline"
BRANCH="main"
echo "Starting automated deployment workflow..."
# Run build
echo "Building..."
BUILD_RUN=$(az pipelines run \
--name "$BUILD_PIPELINE" \
--branch "$BRANCH" \
--query "id" -o tsv)
# Monitor build
while true; do
BUILD_STATUS=$(az pipelines runs show --id "$BUILD_RUN" --query "status" -o tsv)
if [ "$BUILD_STATUS" == "completed" ]; then
break
fi
echo "Build in progress..."
sleep 10
done
BUILD_RESULT=$(az pipelines runs show --id "$BUILD_RUN" --query "result" -o tsv)
if [ "$BUILD_RESULT" == "succeeded" ]; then
echo "Build succeeded! Starting deployment..."
# Get build version
BUILD_NUMBER=$(az pipelines runs show --id "$BUILD_RUN" --query "buildNumber" -o tsv)
# Run deployment
DEPLOY_RUN=$(az pipelines run \
--name "$DEPLOY_PIPELINE" \
--variables BuildNumber="$BUILD_NUMBER" \
--query "id" -o tsv)
echo "Deployment started: Run $DEPLOY_RUN"
# Monitor deployment
while true; do
DEPLOY_STATUS=$(az pipelines runs show --id "$DEPLOY_RUN" --query "status" -o tsv)
if [ "$DEPLOY_STATUS" == "completed" ]; then
break
fi
echo "Deployment in progress..."
sleep 15
done
DEPLOY_RESULT=$(az pipelines runs show --id "$DEPLOY_RUN" --query "result" -o tsv)
echo "Deployment result: $DEPLOY_RESULT"
else
echo "Build failed! Skipping deployment."
exit 1
fiScheduled Deployment Windows
#!/bin/bash
# Deploy only during maintenance windows
DEPLOY_PIPELINE="Production-Deploy"
MAINTENANCE_START="22:00" # 10 PM
MAINTENANCE_END="02:00" # 2 AM
is_maintenance_window() {
current_hour=$(date +%H)
current_minute=$(date +%M)
current_time="${current_hour}:${current_minute}"
# Simple time range check (works for same-day window)
if [[ "$current_time" > "$MAINTENANCE_START" ]] || [[ "$current_time" < "$MAINTENANCE_END" ]]; then
return 0 # True
else
return 1 # False
fi
}
if is_maintenance_window; then
echo "In maintenance window, proceeding with deployment..."
az pipelines run --name "$DEPLOY_PIPELINE"
else
echo "Outside maintenance window ($MAINTENANCE_START - $MAINTENANCE_END)"
echo "Deployment not allowed at this time"
exit 1
fiPipeline Health Monitoring
Build Success Rate Dashboard
#!/bin/bash
# Generate build health report
PIPELINE="Build-Pipeline"
DAYS=7
REPORT_FILE="build-health-$(date +%Y%m%d).txt"
echo "Build Health Report: $PIPELINE (Last $DAYS days)" > "$REPORT_FILE"
echo "Generated: $(date)" >> "$REPORT_FILE"
echo "=====================================================" >> "$REPORT_FILE"
# Get recent runs
SINCE_DATE=$(date -d "$DAYS days ago" +%Y-%m-%d)
# Total runs
TOTAL=$(az pipelines runs list \
--pipeline-ids "$(az pipelines show --name "$PIPELINE" --query id -o tsv)" \
--query "length([?finishTime>='$SINCE_DATE'])" -o tsv)
# Succeeded runs
SUCCEEDED=$(az pipelines runs list \
--pipeline-ids "$(az pipelines show --name "$PIPELINE" --query id -o tsv)" \
--result succeeded \
--query "length([?finishTime>='$SINCE_DATE'])" -o tsv)
# Failed runs
FAILED=$(az pipelines runs list \
--pipeline-ids "$(az pipelines show --name "$PIPELINE" --query id -o tsv)" \
--result failed \
--query "length([?finishTime>='$SINCE_DATE'])" -o tsv)
# Calculate success rate
if [ "$TOTAL" -gt 0 ]; then
SUCCESS_RATE=$(awk "BEGIN {printf \"%.2f\", ($SUCCEEDED/$TOTAL)*100}")
else
SUCCESS_RATE="0.00"
fi
echo "Total Runs: $TOTAL" >> "$REPORT_FILE"
echo "Succeeded: $SUCCEEDED" >> "$REPORT_FILE"
echo "Failed: $FAILED" >> "$REPORT_FILE"
echo "Success Rate: $SUCCESS_RATE%" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
echo "Recent Failed Builds:" >> "$REPORT_FILE"
az pipelines runs list \
--pipeline-ids "$(az pipelines show --name "$PIPELINE" --query id -o tsv)" \
--result failed \
--query "[?finishTime>='$SINCE_DATE'].{ID:id, Branch:sourceBranch, Time:finishTime}" \
--output table >> "$REPORT_FILE"
cat "$REPORT_FILE"Automated Failure Notification
#!/bin/bash
# Monitor pipeline and send notifications on failure
PIPELINE="Critical-Pipeline"
NOTIFICATION_EMAIL="team@example.com"
# Get latest run
LATEST_RUN=$(az pipelines runs list \
--pipeline-ids "$(az pipelines show --name "$PIPELINE" --query id -o tsv)" \
--top 1 \
--query "[0].id" -o tsv)
RESULT=$(az pipelines runs show --id "$LATEST_RUN" --query "result" -o tsv)
if [ "$RESULT" == "failed" ]; then
echo "Pipeline $PIPELINE failed!"
# Get failure details
BUILD_NUMBER=$(az pipelines runs show --id "$LATEST_RUN" --query "buildNumber" -o tsv)
SOURCE_BRANCH=$(az pipelines runs show --id "$LATEST_RUN" --query "sourceBranch" -o tsv)
RUN_URL=$(az pipelines runs show --id "$LATEST_RUN" --query "_links.web.href" -o tsv)
# Send notification (example using mail command)
echo "Pipeline: $PIPELINE
Build: $BUILD_NUMBER
Branch: $SOURCE_BRANCH
Status: FAILED
URL: $RUN_URL" | mail -s "Build Failure: $PIPELINE" "$NOTIFICATION_EMAIL"
echo "Notification sent to $NOTIFICATION_EMAIL"
fiBest Practices
1. Idempotent Deployments: Ensure deployments can be run multiple times safely 2. Rollback Strategy: Always have a rollback plan before deploying 3. Smoke Tests: Run basic health checks after deployment 4. Deployment Slots: Use staging slots for zero-downtime deployments 5. Version Tagging: Tag all deployments with version numbers 6. Monitoring: Implement health checks and alerting 7. Audit Trail: Log all deployment actions for compliance 8. Security: Use variable groups for secrets, never hardcode credentials
Advanced Patterns
Canary Deployment
#!/bin/bash
# Canary deployment with gradual rollout
DEPLOY_PIPELINE="Canary-Deploy"
TRAFFIC_PERCENTAGES=(10 25 50 100)
for percentage in "${TRAFFIC_PERCENTAGES[@]}"; do
echo "Deploying canary with $percentage% traffic..."
az pipelines run \
--name "$DEPLOY_PIPELINE" \
--variables TrafficPercentage="$percentage"
echo "Monitoring canary at $percentage%..."
sleep 300 # 5 minutes
# Check metrics (error rate, latency, etc.)
# If metrics are good, continue to next percentage
# If metrics are bad, rollback
read -p "Continue to next traffic percentage? (yes/no): " continue
if [ "$continue" != "yes" ]; then
echo "Rolling back canary deployment..."
az pipelines run --name "$DEPLOY_PIPELINE" --variables TrafficPercentage="0"
exit 1
fi
done
echo "Canary deployment successful!"References
Release Management Workflows
Advanced release management patterns using Azure DevOps CLI for version control, release automation, and deployment orchestration.
Release Planning and Preparation
Feature Freeze Workflow
#!/bin/bash
# Prepare repository for feature freeze before release
RELEASE_BRANCH="release/v2.0"
MAIN_BRANCH="main"
REPO="myrepo"
echo "Starting feature freeze workflow for $RELEASE_BRANCH..."
# 1. Create release branch
echo "Creating release branch from $MAIN_BRANCH..."
MAIN_SHA=$(az repos ref list \
--repository "$REPO" \
--filter "heads/$MAIN_BRANCH" \
--query "[0].objectId" -o tsv)
az repos ref create \
--name "refs/heads/$RELEASE_BRANCH" \
--object-id "$MAIN_SHA" \
--repository "$REPO"
# 2. Lock release branch (prevent direct commits)
echo "Locking release branch..."
az repos ref lock \
--name "refs/heads/$RELEASE_BRANCH" \
--repository "$REPO"
# 3. Create work item for release tracking
echo "Creating release tracking work item..."
az boards work-item create \
--type "Epic" \
--title "Release v2.0" \
--assigned-to "release-manager@example.com" \
--fields "System.Tags=release"
# 4. Query open PRs targeting main
echo "Checking for open PRs..."
OPEN_PRS=$(az repos pr list \
--repository "$REPO" \
--target-branch "$MAIN_BRANCH" \
--status active \
--query "length(@)")
echo "Open PRs targeting main: $OPEN_PRS"
if [ "$OPEN_PRS" -gt 0 ]; then
echo "WARNING: There are $OPEN_PRS open PRs. Review before release."
az repos pr list \
--repository "$REPO" \
--target-branch "$MAIN_BRANCH" \
--status active \
--output table
fi
echo "Feature freeze complete. Release branch: $RELEASE_BRANCH"Release Notes Generation
#!/bin/bash
# Generate release notes from commits and work items
REPO="myrepo"
PREVIOUS_TAG="v1.9.0"
CURRENT_TAG="v2.0.0"
RELEASE_NOTES_FILE="RELEASE_NOTES_${CURRENT_TAG}.md"
echo "Generating release notes: $PREVIOUS_TAG → $CURRENT_TAG"
# Get commit range
PREVIOUS_SHA=$(git rev-parse "$PREVIOUS_TAG")
CURRENT_SHA=$(git rev-parse "$CURRENT_TAG")
echo "# Release Notes: $CURRENT_TAG" > "$RELEASE_NOTES_FILE"
echo "" >> "$RELEASE_NOTES_FILE"
echo "Release Date: $(date +%Y-%m-%d)" >> "$RELEASE_NOTES_FILE"
echo "" >> "$RELEASE_NOTES_FILE"
# Get commits between tags
echo "## Changes" >> "$RELEASE_NOTES_FILE"
echo "" >> "$RELEASE_NOTES_FILE"
git log "$PREVIOUS_SHA..$CURRENT_SHA" --pretty=format:"- %s (%an)" >> "$RELEASE_NOTES_FILE"
echo "" >> "$RELEASE_NOTES_FILE"
echo "" >> "$RELEASE_NOTES_FILE"
# Extract work item IDs from commits
echo "## Work Items" >> "$RELEASE_NOTES_FILE"
echo "" >> "$RELEASE_NOTES_FILE"
git log "$PREVIOUS_SHA..$CURRENT_SHA" --pretty=format:"%s" | \
grep -oP '#\K\d+' | sort -u | while read work_item_id; do
TITLE=$(az boards work-item show --id "$work_item_id" --query "fields.'System.Title'" -o tsv 2>/dev/null)
TYPE=$(az boards work-item show --id "$work_item_id" --query "fields.'System.WorkItemType'" -o tsv 2>/dev/null)
if [ -n "$TITLE" ]; then
echo "- **[$TYPE]** #$work_item_id: $TITLE" >> "$RELEASE_NOTES_FILE"
fi
done
echo "" >> "$RELEASE_NOTES_FILE"
echo "## Contributors" >> "$RELEASE_NOTES_FILE"
echo "" >> "$RELEASE_NOTES_FILE"
git log "$PREVIOUS_SHA..$CURRENT_SHA" --pretty=format:"%an" | sort -u >> "$RELEASE_NOTES_FILE"
echo "Release notes generated: $RELEASE_NOTES_FILE"
cat "$RELEASE_NOTES_FILE"Version Management
Semantic Version Bumping
#!/bin/bash
# Automated semantic version bumping
CURRENT_VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
BUMP_TYPE=${1:-patch} # major, minor, patch
# Remove 'v' prefix if present
CURRENT_VERSION=${CURRENT_VERSION#v}
# Split version into components
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
# Bump version based on type
case $BUMP_TYPE in
major)
MAJOR=$((MAJOR + 1))
MINOR=0
PATCH=0
;;
minor)
MINOR=$((MINOR + 1))
PATCH=0
;;
patch)
PATCH=$((PATCH + 1))
;;
*)
echo "Invalid bump type: $BUMP_TYPE (use: major, minor, patch)"
exit 1
;;
esac
NEW_VERSION="v${MAJOR}.${MINOR}.${PATCH}"
echo "Current version: v$CURRENT_VERSION"
echo "Bump type: $BUMP_TYPE"
echo "New version: $NEW_VERSION"
# Create annotated tag
git tag -a "$NEW_VERSION" -m "Release $NEW_VERSION"
# Push tag
read -p "Push tag $NEW_VERSION to remote? (yes/no): " confirm
if [ "$confirm" == "yes" ]; then
git push origin "$NEW_VERSION"
echo "Tag $NEW_VERSION pushed to remote"
fi
echo "$NEW_VERSION"Multi-Repo Release Coordination
#!/bin/bash
# Coordinate releases across multiple repositories
declare -A REPOS=(
["api"]="api-service"
["frontend"]="web-app"
["backend"]="backend-service"
)
RELEASE_VERSION="v2.0.0"
PROJECT="MyProject"
echo "Coordinating release $RELEASE_VERSION across repositories..."
for key in "${!REPOS[@]}"; do
repo="${REPOS[$key]}"
echo "Processing $repo..."
# Create release branch
MAIN_SHA=$(az repos ref list \
--repository "$repo" \
--filter "heads/main" \
--query "[0].objectId" -o tsv \
--project "$PROJECT")
az repos ref create \
--name "refs/heads/release/$RELEASE_VERSION" \
--object-id "$MAIN_SHA" \
--repository "$repo" \
--project "$PROJECT"
echo " Created release/$RELEASE_VERSION branch"
# Create release work item
az boards work-item create \
--type "Task" \
--title "Release $repo $RELEASE_VERSION" \
--area "$PROJECT\\Release" \
--fields "System.Tags=$RELEASE_VERSION,release" \
--project "$PROJECT"
echo " Created tracking work item"
done
echo "Release coordination complete for $RELEASE_VERSION"Deployment Orchestration
Multi-Stage Release Pipeline
#!/bin/bash
# Orchestrate multi-stage release deployment
RELEASE_PIPELINE="Multi-Stage-Release"
VERSION=$1
if [ -z "$VERSION" ]; then
echo "Usage: $0 <version>"
exit 1
fi
STAGES=("dev" "qa" "staging" "production")
echo "Starting multi-stage release for version $VERSION..."
for stage in "${STAGES[@]}"; do
echo "============================================"
echo "Deploying to: $stage"
echo "============================================"
# Run pipeline with stage-specific variables
RUN_ID=$(az pipelines run \
--name "$RELEASE_PIPELINE" \
--variables Stage="$stage" Version="$VERSION" \
--query "id" -o tsv)
echo "Deployment started: Run $RUN_ID"
# Monitor deployment
while true; do
STATUS=$(az pipelines runs show --id "$RUN_ID" --query "status" -o tsv)
if [ "$STATUS" == "completed" ]; then
RESULT=$(az pipelines runs show --id "$RUN_ID" --query "result" -o tsv)
echo "Deployment result: $RESULT"
if [ "$RESULT" != "succeeded" ]; then
echo "Deployment to $stage failed! Stopping release."
exit 1
fi
break
fi
sleep 15
done
# Stage-specific gates
if [ "$stage" == "qa" ] || [ "$stage" == "staging" ]; then
read -p "QA approval for $stage? (yes/no): " approval
if [ "$approval" != "yes" ]; then
echo "QA rejected deployment. Stopping release."
exit 1
fi
fi
if [ "$stage" == "production" ]; then
read -p "FINAL APPROVAL for production? (yes/no): " approval
if [ "$approval" != "yes" ]; then
echo "Production deployment cancelled."
exit 1
fi
fi
echo "$stage deployment complete!"
echo ""
done
echo "Multi-stage release complete for version $VERSION!"Phased Rollout Management
#!/bin/bash
# Manage phased rollout with monitoring
DEPLOY_PIPELINE="Phased-Deploy"
VERSION=$1
PHASES=(5 15 30 50 100) # Percentage of users
if [ -z "$VERSION" ]; then
echo "Usage: $0 <version>"
exit 1
fi
echo "Starting phased rollout for version $VERSION..."
for phase in "${PHASES[@]}"; do
echo "=========================================="
echo "Phase: $phase% of users"
echo "=========================================="
# Deploy to percentage of users
RUN_ID=$(az pipelines run \
--name "$DEPLOY_PIPELINE" \
--variables Version="$VERSION" RolloutPercentage="$phase" \
--query "id" -o tsv)
# Wait for deployment
while true; do
STATUS=$(az pipelines runs show --id "$RUN_ID" --query "status" -o tsv)
if [ "$STATUS" == "completed" ]; then
break
fi
sleep 10
done
RESULT=$(az pipelines runs show --id "$RUN_ID" --query "result" -o tsv)
if [ "$RESULT" != "succeeded" ]; then
echo "Deployment failed at $phase% phase!"
echo "Rolling back..."
# Rollback logic here
exit 1
fi
echo "Phase $phase% deployed successfully"
# Monitoring period (longer for early phases)
if [ "$phase" -lt 50 ]; then
MONITOR_TIME=1800 # 30 minutes
else
MONITOR_TIME=900 # 15 minutes
fi
echo "Monitoring for $((MONITOR_TIME / 60)) minutes..."
# Simulate monitoring (replace with actual monitoring)
sleep 10
# Check for issues (error rate, latency, etc.)
ISSUES_DETECTED=false
if [ "$ISSUES_DETECTED" == "true" ]; then
echo "Issues detected during monitoring!"
echo "Rolling back version $VERSION..."
# Rollback logic here
exit 1
fi
echo "No issues detected, proceeding to next phase"
echo ""
done
echo "Phased rollout complete! Version $VERSION at 100%"Rollback and Recovery
Automated Rollback
#!/bin/bash
# Automated rollback to previous version
DEPLOY_PIPELINE="Deploy-Pipeline"
FEED="production-artifacts"
PACKAGE="myapp"
echo "Initiating rollback..."
# Get current version
CURRENT_VERSION=$(az artifacts universal list \
--feed "$FEED" \
--query "[?name=='$PACKAGE'] | [0].versions[0].version" -o tsv)
echo "Current version: $CURRENT_VERSION"
# Get previous version
PREVIOUS_VERSION=$(az artifacts universal list \
--feed "$FEED" \
--query "[?name=='$PACKAGE'] | [0].versions[1].version" -o tsv)
if [ -z "$PREVIOUS_VERSION" ]; then
echo "No previous version found to rollback to!"
exit 1
fi
echo "Rolling back to: $PREVIOUS_VERSION"
read -p "Confirm rollback to $PREVIOUS_VERSION? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
echo "Rollback cancelled"
exit 0
fi
# Deploy previous version
RUN_ID=$(az pipelines run \
--name "$DEPLOY_PIPELINE" \
--variables Version="$PREVIOUS_VERSION" IsRollback="true" \
--query "id" -o tsv)
echo "Rollback deployment started: Run $RUN_ID"
# Monitor rollback
while true; do
STATUS=$(az pipelines runs show --id "$RUN_ID" --query "status" -o tsv)
if [ "$STATUS" == "completed" ]; then
RESULT=$(az pipelines runs show --id "$RUN_ID" --query "result" -o tsv)
echo "Rollback result: $RESULT"
if [ "$RESULT" == "succeeded" ]; then
echo "Rollback successful! Now running $PREVIOUS_VERSION"
# Tag the run
az pipelines runs tag add --run-id "$RUN_ID" --tags "rollback"
# Create incident work item
az boards work-item create \
--type "Bug" \
--title "Rollback: $CURRENT_VERSION → $PREVIOUS_VERSION" \
--description "Automated rollback executed" \
--fields "System.Tags=rollback,incident"
exit 0
else
echo "Rollback failed!"
exit 1
fi
fi
sleep 10
doneEmergency Hotfix Workflow
#!/bin/bash
# Emergency hotfix workflow
REPO="myrepo"
PRODUCTION_TAG="v2.0.5"
HOTFIX_BRANCH="hotfix/critical-bug"
echo "Starting emergency hotfix workflow..."
# 1. Create hotfix branch from production tag
echo "Creating hotfix branch from $PRODUCTION_TAG..."
TAG_SHA=$(git rev-parse "$PRODUCTION_TAG")
az repos ref create \
--name "refs/heads/$HOTFIX_BRANCH" \
--object-id "$TAG_SHA" \
--repository "$REPO"
echo "Hotfix branch created: $HOTFIX_BRANCH"
# 2. Create PR for hotfix
echo "Create PR for your hotfix changes to $HOTFIX_BRANCH"
echo "When ready, the PR will be auto-deployed to production"
# 3. Monitor for merged PR
echo "Monitoring for merged hotfix PR..."
# (This would typically be triggered by a webhook or scheduled job)
# For demo purposes, we'll wait for user confirmation
read -p "Has the hotfix PR been merged? (yes/no): " merged
if [ "$merged" == "yes" ]; then
# 4. Deploy hotfix immediately
echo "Deploying hotfix..."
# Bump patch version
NEW_VERSION="v2.0.6"
# Create tag
git tag -a "$NEW_VERSION" -m "Hotfix: $NEW_VERSION"
git push origin "$NEW_VERSION"
# Deploy
az pipelines run \
--name "Hotfix-Deploy" \
--variables Version="$NEW_VERSION" \
--branch "$HOTFIX_BRANCH"
echo "Hotfix deployed: $NEW_VERSION"
# 5. Backport to main
echo "Create PR to backport hotfix to main branch"
az repos pr create \
--repository "$REPO" \
--source-branch "$HOTFIX_BRANCH" \
--target-branch "main" \
--title "Backport hotfix: $NEW_VERSION" \
--description "Backporting emergency hotfix from production"
fiRelease Validation
Pre-Release Checklist Automation
#!/bin/bash
# Automated pre-release checklist validation
RELEASE_VERSION="v2.0.0"
REPO="myrepo"
PROJECT="MyProject"
echo "Pre-Release Checklist for $RELEASE_VERSION"
echo "==========================================="
PASS_COUNT=0
FAIL_COUNT=0
check_item() {
local description=$1
local command=$2
echo -n "Checking: $description... "
if eval "$command" > /dev/null 2>&1; then
echo "✓ PASS"
PASS_COUNT=$((PASS_COUNT + 1))
return 0
else
echo "✗ FAIL"
FAIL_COUNT=$((FAIL_COUNT + 1))
return 1
fi
}
# Checklist items
check_item "All PRs merged" \
"[ $(az repos pr list --repository '$REPO' --target-branch main --status active --query 'length(@)' -o tsv) -eq 0 ]"
check_item "Latest build succeeded" \
"[ $(az pipelines runs list --pipeline-ids $(az pipelines show --name 'Build-Pipeline' --query id -o tsv) --top 1 --query '[0].result' -o tsv) == 'succeeded' ]"
check_item "All tests passing" \
"[ $(az pipelines runs list --pipeline-ids $(az pipelines show --name 'Test-Pipeline' --query id -o tsv) --top 1 --query '[0].result' -o tsv) == 'succeeded' ]"
check_item "No critical bugs open" \
"[ $(az boards query --wiql \"SELECT [System.Id] FROM WorkItems WHERE [System.WorkItemType] = 'Bug' AND [System.State] = 'Active' AND [System.Priority] = 1\" --query 'length(@)' -o tsv) -eq 0 ]"
check_item "Release notes generated" \
"[ -f 'RELEASE_NOTES_${RELEASE_VERSION}.md' ]"
check_item "Security scan completed" \
"[ $(az pipelines runs list --pipeline-ids $(az pipelines show --name 'Security-Scan' --query id -o tsv) --top 1 --query '[0].result' -o tsv) == 'succeeded' ]"
echo ""
echo "==========================================="
echo "Results: $PASS_COUNT passed, $FAIL_COUNT failed"
if [ "$FAIL_COUNT" -eq 0 ]; then
echo "✓ All checks passed! Ready for release."
exit 0
else
echo "✗ Some checks failed. Fix issues before release."
exit 1
fiBest Practices
1. Release Branches: Create dedicated branches for each release 2. Semantic Versioning: Follow semver (MAJOR.MINOR.PATCH) strictly 3. Release Notes: Auto-generate from commits and work items 4. Approval Gates: Require manual approval for production 5. Phased Rollouts: Gradually increase traffic to new version 6. Monitoring: Watch metrics closely during rollout 7. Rollback Plan: Always have a tested rollback procedure 8. Hotfix Process: Maintain fast-track process for critical issues 9. Changelog: Keep detailed changelog for audit trail 10. Communication: Notify stakeholders at each stage
References
Team Collaboration Workflows
Advanced team collaboration patterns using Azure DevOps CLI for sprint planning, code reviews, and team coordination.
Sprint Planning and Management
Sprint Setup Automation
#!/bin/bash
# Automated sprint setup
PROJECT="MyProject"
SPRINT_NAME="Sprint 42"
TEAM="MyTeam"
START_DATE="2025-12-01"
END_DATE="2025-12-14"
echo "Setting up $SPRINT_NAME..."
# 1. Create iteration
echo "Creating iteration..."
az boards iteration project create \
--name "$SPRINT_NAME" \
--start-date "$START_DATE" \
--finish-date "$END_DATE" \
--project "$PROJECT"
# 2. Add iteration to team
echo "Adding iteration to team..."
az boards iteration team add \
--id "$SPRINT_NAME" \
--team "$TEAM" \
--project "$PROJECT"
# 3. Query backlog items
echo "Finding backlog items..."
BACKLOG_ITEMS=$(az boards query --wiql "
SELECT [System.Id], [System.Title], [System.Priority]
FROM WorkItems
WHERE [System.State] = 'New'
AND [System.WorkItemType] = 'User Story'
ORDER BY [System.Priority] ASC
" --query "[].id" -o tsv)
# 4. Move top priority items to sprint
echo "Moving items to $SPRINT_NAME..."
ITEM_COUNT=0
MAX_ITEMS=10
for item_id in $BACKLOG_ITEMS; do
if [ "$ITEM_COUNT" -ge "$MAX_ITEMS" ]; then
break
fi
az boards work-item update \
--id "$item_id" \
--iteration "$PROJECT\\$SPRINT_NAME" \
--state "Active"
ITEM_COUNT=$((ITEM_COUNT + 1))
done
echo "Sprint setup complete: $ITEM_COUNT items added to $SPRINT_NAME"Sprint Capacity Planning
#!/bin/bash
# Calculate and display team capacity
PROJECT="MyProject"
SPRINT="Sprint 42"
echo "Team Capacity Report: $SPRINT"
echo "=================================="
# Get team members
TEAM_MEMBERS=$(az devops team list --project "$PROJECT" --query "[0].name" -o tsv)
# Sprint duration (working days)
SPRINT_DAYS=10
HOURS_PER_DAY=6 # Accounting for meetings, etc.
# Get current sprint work items
WORK_ITEMS=$(az boards query --wiql "
SELECT [System.Id], [System.AssignedTo], [Microsoft.VSTS.Scheduling.RemainingWork]
FROM WorkItems
WHERE [System.IterationPath] = '$PROJECT\\$SPRINT'
AND [System.State] != 'Closed'
" --output json)
# Calculate total capacity
TOTAL_CAPACITY=$((SPRINT_DAYS * HOURS_PER_DAY))
echo "Sprint Duration: $SPRINT_DAYS days"
echo "Hours per day: $HOURS_PER_DAY"
echo "Total Capacity: $TOTAL_CAPACITY hours per person"
echo ""
# Calculate allocated hours (would need to parse JSON)
echo "Current Allocation:"
echo "$WORK_ITEMS" | jq -r '
group_by(.fields."System.AssignedTo".displayName) |
map({
user: .[0].fields."System.AssignedTo".displayName,
hours: map(.fields."Microsoft.VSTS.Scheduling.RemainingWork" // 0) | add
}) |
.[] |
"\(.user): \(.hours) hours"
'Daily Standup Report
#!/bin/bash
# Generate daily standup report
PROJECT="MyProject"
TEAM="MyTeam"
SPRINT="Sprint 42"
echo "Daily Standup Report: $(date +%Y-%m-%d)"
echo "========================================"
echo ""
# Yesterday's completed work
echo "✓ COMPLETED YESTERDAY:"
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
az boards query --wiql "
SELECT [System.Id], [System.Title], [System.AssignedTo]
FROM WorkItems
WHERE [System.IterationPath] = '$PROJECT\\$SPRINT'
AND [System.State] = 'Closed'
AND [System.ChangedDate] >= '$YESTERDAY'
" --output table
echo ""
# Today's active work
echo "▶ IN PROGRESS TODAY:"
az boards query --wiql "
SELECT [System.Id], [System.Title], [System.AssignedTo]
FROM WorkItems
WHERE [System.IterationPath] = '$PROJECT\\$SPRINT'
AND [System.State] = 'In Progress'
" --output table
echo ""
# Blockers
echo "🚫 BLOCKED ITEMS:"
az boards query --wiql "
SELECT [System.Id], [System.Title], [System.AssignedTo]
FROM WorkItems
WHERE [System.IterationPath] = '$PROJECT\\$SPRINT'
AND [System.Tags] CONTAINS 'blocked'
" --output table
echo ""
# PRs waiting for review
echo "👀 PRs WAITING FOR REVIEW:"
az repos pr list --status active --output table
echo ""
echo "========================================"Sprint Retrospective Data
#!/bin/bash
# Generate sprint retrospective data
PROJECT="MyProject"
SPRINT="Sprint 42"
REPORT_FILE="retrospective-$SPRINT.md"
echo "# Sprint Retrospective: $SPRINT" > "$REPORT_FILE"
echo "Date: $(date +%Y-%m-%d)" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# Sprint goals (would be manually added)
echo "## Sprint Goals" >> "$REPORT_FILE"
echo "- Goal 1" >> "$REPORT_FILE"
echo "- Goal 2" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# Velocity
echo "## Velocity" >> "$REPORT_FILE"
TOTAL_POINTS=$(az boards query --wiql "
SELECT [System.Id]
FROM WorkItems
WHERE [System.IterationPath] = '$PROJECT\\$SPRINT'
" --output json | jq '[.[].fields."Microsoft.VSTS.Scheduling.StoryPoints" // 0] | add')
COMPLETED_POINTS=$(az boards query --wiql "
SELECT [System.Id]
FROM WorkItems
WHERE [System.IterationPath] = '$PROJECT\\$SPRINT'
AND [System.State] = 'Closed'
" --output json | jq '[.[].fields."Microsoft.VSTS.Scheduling.StoryPoints" // 0] | add')
echo "- Planned: $TOTAL_POINTS points" >> "$REPORT_FILE"
echo "- Completed: $COMPLETED_POINTS points" >> "$REPORT_FILE"
echo "- Completion Rate: $(awk "BEGIN {printf \"%.1f\", ($COMPLETED_POINTS/$TOTAL_POINTS)*100}")%" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# Work item breakdown
echo "## Work Item Summary" >> "$REPORT_FILE"
az boards query --wiql "
SELECT [System.WorkItemType], [System.State]
FROM WorkItems
WHERE [System.IterationPath] = '$PROJECT\\$SPRINT'
" --output json | jq -r '
group_by(.fields."System.WorkItemType") |
map({
type: .[0].fields."System.WorkItemType",
total: length,
closed: [.[] | select(.fields."System.State" == "Closed")] | length
}) |
.[] |
"- \(.type): \(.closed)/\(.total) completed"
' >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# PR statistics
echo "## Pull Request Statistics" >> "$REPORT_FILE"
TOTAL_PRS=$(az repos pr list --status all --output json | jq '[.[] | select(.creationDate >= "2025-12-01")] | length')
COMPLETED_PRS=$(az repos pr list --status completed --output json | jq '[.[] | select(.creationDate >= "2025-12-01")] | length')
echo "- Total PRs: $TOTAL_PRS" >> "$REPORT_FILE"
echo "- Merged: $COMPLETED_PRS" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# Build statistics
echo "## Build Statistics" >> "$REPORT_FILE"
BUILD_STATS=$(az pipelines runs list --top 100 --output json | jq -r '
group_by(.result) |
map({result: .[0].result, count: length}) |
.[] |
"- \(.result): \(.count)"
')
echo "$BUILD_STATS" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
echo "## Action Items" >> "$REPORT_FILE"
echo "1. (Add action items here)" >> "$REPORT_FILE"
echo "Retrospective report generated: $REPORT_FILE"
cat "$REPORT_FILE"Code Review Automation
Automated PR Assignment
#!/bin/bash
# Automatically assign reviewers based on code ownership
REPO="myrepo"
PR_ID=$1
if [ -z "$PR_ID" ]; then
echo "Usage: $0 <pr-id>"
exit 1
fi
echo "Assigning reviewers for PR #$PR_ID..."
# Get PR details
PR_SOURCE=$(az repos pr show --id "$PR_ID" --query "sourceRefName" -o tsv)
PR_AUTHOR=$(az repos pr show --id "$PR_ID" --query "createdBy.uniqueName" -o tsv)
# Get changed files
CHANGED_FILES=$(az repos pr show --id "$PR_ID" --output json | jq -r '.url' | sed 's|pullRequests.*|diffs?targetVersionType=branch\&targetVersion=main|')
# Simple code ownership rules (in production, use CODEOWNERS file)
declare -A CODE_OWNERS=(
["frontend/"]="frontend-team@example.com"
["backend/"]="backend-team@example.com"
["docs/"]="tech-writer@example.com"
["tests/"]="qa-team@example.com"
)
REVIEWERS=()
# Match changed files to owners
# (Simplified - would need actual file list from API)
for path in "${!CODE_OWNERS[@]}"; do
REVIEWERS+=("${CODE_OWNERS[$path]}")
done
# Remove duplicates and author
UNIQUE_REVIEWERS=($(printf '%s\n' "${REVIEWERS[@]}" | sort -u | grep -v "$PR_AUTHOR"))
# Add reviewers to PR
for reviewer in "${UNIQUE_REVIEWERS[@]}"; do
echo "Adding reviewer: $reviewer"
az repos pr reviewer add --id "$PR_ID" --reviewers "$reviewer"
done
echo "Reviewers assigned successfully"PR Review Dashboard
#!/bin/bash
# Generate PR review dashboard for team
REPO="myrepo"
DASHBOARD_FILE="pr-dashboard-$(date +%Y%m%d).txt"
echo "PR Review Dashboard: $(date +%Y-%m-%d)" > "$DASHBOARD_FILE"
echo "========================================" >> "$DASHBOARD_FILE"
echo "" >> "$DASHBOARD_FILE"
# PRs waiting for review
echo "🔍 WAITING FOR REVIEW:" >> "$DASHBOARD_FILE"
az repos pr list --repository "$REPO" --status active \
--query "[?reviewers[?vote==0]].{ID:pullRequestId, Title:title, Author:createdBy.displayName, Age:creationDate}" \
--output table >> "$DASHBOARD_FILE"
echo "" >> "$DASHBOARD_FILE"
# PRs with requested changes
echo "⚠️ CHANGES REQUESTED:" >> "$DASHBOARD_FILE"
az repos pr list --repository "$REPO" --status active \
--query "[?reviewers[?vote==-5]].{ID:pullRequestId, Title:title, Author:createdBy.displayName}" \
--output table >> "$DASHBOARD_FILE"
echo "" >> "$DASHBOARD_FILE"
# Approved PRs waiting for merge
echo "✅ APPROVED (Ready to Merge):" >> "$DASHBOARD_FILE"
az repos pr list --repository "$REPO" --status active \
--query "[?reviewers[?vote==10]].{ID:pullRequestId, Title:title, Author:createdBy.displayName}" \
--output table >> "$DASHBOARD_FILE"
echo "" >> "$DASHBOARD_FILE"
# PRs by reviewer
echo "📊 REVIEW WORKLOAD:" >> "$DASHBOARD_FILE"
az repos pr list --repository "$REPO" --status active --output json | \
jq -r '.[] | .reviewers[] | .displayName' | \
sort | uniq -c | sort -rn | \
awk '{print " " $2 " " $3 ": " $1 " PRs"}' >> "$DASHBOARD_FILE"
cat "$DASHBOARD_FILE"Review Reminder Bot
#!/bin/bash
# Send reminders for stale PRs
REPO="myrepo"
STALE_DAYS=3
echo "Checking for stale PRs (older than $STALE_DAYS days)..."
CUTOFF_DATE=$(date -d "$STALE_DAYS days ago" +%Y-%m-%d)
az repos pr list --repository "$REPO" --status active --output json | \
jq -r --arg cutoff "$CUTOFF_DATE" '
.[] |
select(.creationDate < $cutoff) |
select(.reviewers | any(.vote == 0)) |
"\(.pullRequestId)|\(.title)|\(.createdBy.uniqueName)|\(.creationDate)"
' | while IFS='|' read -r pr_id title author created_date; do
echo "Stale PR #$pr_id: $title"
echo " Author: $author"
echo " Created: $created_date"
# Get reviewers who haven't voted
PENDING_REVIEWERS=$(az repos pr reviewer list --id "$pr_id" --output json | \
jq -r '.[] | select(.vote == 0) | .uniqueName')
echo " Pending reviewers:"
for reviewer in $PENDING_REVIEWERS; do
echo " - $reviewer"
# Send reminder (implement notification mechanism)
# Example: send email or Teams message
done
echo ""
doneTeam Coordination
Work Distribution Report
#!/bin/bash
# Analyze work distribution across team
PROJECT="MyProject"
SPRINT="Sprint 42"
echo "Work Distribution Report: $SPRINT"
echo "=================================="
echo ""
# Get all active work items in sprint
WORK_ITEMS=$(az boards query --wiql "
SELECT [System.Id], [System.AssignedTo], [Microsoft.VSTS.Scheduling.RemainingWork], [System.WorkItemType]
FROM WorkItems
WHERE [System.IterationPath] = '$PROJECT\\$SPRINT'
AND [System.State] != 'Closed'
" --output json)
echo "Work Items by Assignee:"
echo "$WORK_ITEMS" | jq -r '
group_by(.fields."System.AssignedTo".displayName // "Unassigned") |
map({
assignee: .[0].fields."System.AssignedTo".displayName // "Unassigned",
count: length,
hours: map(.fields."Microsoft.VSTS.Scheduling.RemainingWork" // 0) | add
}) |
sort_by(.hours) |
reverse |
.[] |
"\(.assignee): \(.count) items, \(.hours) hours"
'
echo ""
echo "Work Items by Type:"
echo "$WORK_ITEMS" | jq -r '
group_by(.fields."System.WorkItemType") |
map({
type: .[0].fields."System.WorkItemType",
count: length
}) |
.[] |
" \(.type): \(.count)"
'
echo ""
# Identify unassigned work
UNASSIGNED=$(az boards query --wiql "
SELECT [System.Id], [System.Title]
FROM WorkItems
WHERE [System.IterationPath] = '$PROJECT\\$SPRINT'
AND [System.AssignedTo] = ''
AND [System.State] != 'Closed'
" --query "length(@)" -o tsv)
if [ "$UNASSIGNED" -gt 0 ]; then
echo "⚠️ Warning: $UNASSIGNED unassigned work items"
az boards query --wiql "
SELECT [System.Id], [System.Title], [System.WorkItemType]
FROM WorkItems
WHERE [System.IterationPath] = '$PROJECT\\$SPRINT'
AND [System.AssignedTo] = ''
AND [System.State] != 'Closed'
" --output table
fiTeam Velocity Tracking
#!/bin/bash
# Track team velocity over multiple sprints
PROJECT="MyProject"
NUM_SPRINTS=6
echo "Team Velocity Trend (Last $NUM_SPRINTS sprints)"
echo "==============================================="
echo ""
# Get recent iterations
ITERATIONS=$(az boards iteration project list --project "$PROJECT" \
--query "reverse(sort_by([].{name:name, path:path}, &name)) | [0:$NUM_SPRINTS]" \
--output json)
echo "Sprint | Planned | Completed | % Complete"
echo "-------|---------|-----------|------------"
echo "$ITERATIONS" | jq -r '.[] | .path' | while read iteration_path; do
SPRINT_NAME=$(basename "$iteration_path")
# Get total story points
TOTAL=$(az boards query --wiql "
SELECT [System.Id]
FROM WorkItems
WHERE [System.IterationPath] = '$iteration_path'
AND [System.WorkItemType] = 'User Story'
" --output json | jq '[.[].fields."Microsoft.VSTS.Scheduling.StoryPoints" // 0] | add')
# Get completed story points
COMPLETED=$(az boards query --wiql "
SELECT [System.Id]
FROM WorkItems
WHERE [System.IterationPath] = '$iteration_path'
AND [System.WorkItemType] = 'User Story'
AND [System.State] = 'Closed'
" --output json | jq '[.[].fields."Microsoft.VSTS.Scheduling.StoryPoints" // 0] | add')
if [ "$TOTAL" -gt 0 ]; then
PERCENT=$(awk "BEGIN {printf \"%.0f\", ($COMPLETED/$TOTAL)*100}")
else
PERCENT=0
fi
printf "%-15s | %7s | %9s | %10s%%\n" "$SPRINT_NAME" "$TOTAL" "$COMPLETED" "$PERCENT"
doneCross-Team Dependency Tracker
#!/bin/bash
# Track dependencies between teams
PROJECT="MyProject"
echo "Cross-Team Dependencies"
echo "======================="
echo ""
# Query work items with external dependencies tag
DEPENDENCIES=$(az boards query --wiql "
SELECT [System.Id], [System.Title], [System.AssignedTo], [System.Tags]
FROM WorkItems
WHERE [System.Tags] CONTAINS 'dependency'
AND [System.State] != 'Closed'
" --output json)
echo "Items with Dependencies:"
echo "$DEPENDENCIES" | jq -r '.[] | "\(.id): \(.fields."System.Title") (Assigned: \(.fields."System.AssignedTo".displayName // "Unassigned"))"'
echo ""
echo "Blocked Items:"
az boards query --wiql "
SELECT [System.Id], [System.Title], [System.AssignedTo]
FROM WorkItems
WHERE [System.Tags] CONTAINS 'blocked'
AND [System.State] != 'Closed'
" --output tableOnboarding Automation
New Team Member Setup
#!/bin/bash
# Automate new team member onboarding
NEW_MEMBER_EMAIL=$1
PROJECT="MyProject"
TEAM="MyTeam"
if [ -z "$NEW_MEMBER_EMAIL" ]; then
echo "Usage: $0 <new-member-email>"
exit 1
fi
echo "Onboarding new team member: $NEW_MEMBER_EMAIL"
# 1. Add to team (requires appropriate permissions)
echo "Adding to team..."
# az devops user add --email-id "$NEW_MEMBER_EMAIL"
# 2. Grant repository access
echo "Granting repository access..."
REPOS=$(az repos list --project "$PROJECT" --query "[].name" -o tsv)
for repo in $REPOS; do
echo " - $repo"
# Permissions would be set via security commands
done
# 3. Add to relevant variable groups
echo "Configuring access to shared resources..."
# Variable group permissions
# 4. Create onboarding work item
echo "Creating onboarding checklist..."
az boards work-item create \
--type "Task" \
--title "Onboarding: $NEW_MEMBER_EMAIL" \
--assigned-to "$NEW_MEMBER_EMAIL" \
--description "
## Onboarding Checklist
- [ ] Complete Azure DevOps training
- [ ] Set up development environment
- [ ] Clone repositories
- [ ] Review coding standards
- [ ] Attend team standup
- [ ] Pair programming session
- [ ] Review architecture docs
" \
--fields "System.Tags=onboarding"
echo "Onboarding setup complete!"
echo "Checklist work item created for $NEW_MEMBER_EMAIL"Best Practices
1. Daily Standups: Automate status reports for efficiency 2. Sprint Planning: Use data-driven capacity planning 3. Code Reviews: Automate reviewer assignment based on code ownership 4. Work Distribution: Monitor and balance workload across team 5. Retrospectives: Generate data for informed discussions 6. Dependencies: Track and visualize cross-team dependencies 7. Velocity: Track team velocity for better planning 8. Onboarding: Standardize new team member setup
References
Azure DevOps CLI Skill
Purpose
Expert guidance for Azure DevOps CLI (az devops) covering automation, pipelines, repositories, boards, and artifacts management. This skill enables Claude Code to provide comprehensive assistance with Azure DevOps workflows and command-line operations.
Module Contract
Public Interface (The "Studs")
This skill provides:
1. Quick Start: Installation, authentication, and configuration patterns 2. Essential Commands: High-value commands across 5 primary groups (DevOps, Pipelines, Boards, Repos, Artifacts) 3. Common Workflows: 10+ practical automation patterns for daily DevOps tasks 4. Troubleshooting: Solutions for authentication, configuration, and query issues 5. Advanced Patterns: REST API access, JMESPath queries, scripting utilities
Auto-Activation Keywords
The skill automatically activates when conversation mentions:
- azure devops, az devops, ado cli
- pipelines, azure pipelines, yaml pipeline, build pipeline, release pipeline
- boards, azure boards, work items
- repos, azure repos, pull requests, git repos
- artifacts, azure artifacts, artifacts feed
Explicit Invocation
Skill(skill="azure-devops-cli")Architecture
Progressive disclosure design with core skill (<2000 tokens) and extended reference files:
azure-devops-cli/
├── skill.md # Core: Quick start + 5 commands/group + 10 workflows
├── examples/ # Extended: Complete command references
│ ├── pipelines-reference.md
│ ├── boards-reference.md
│ ├── repos-reference.md
│ └── artifacts-reference.md
└── tests/ # Validation scenariosPhilosophy: Self-contained module following ruthless simplicity. Every command works, no stubs/TODOs. Regeneratable from Azure DevOps CLI docs
Dependencies
Required
- Azure CLI (az): Core command-line tool
- Azure DevOps Extension:
az extension add --name azure-devops
Authentication
One of the following:
- Azure account with
az login - Personal Access Token (PAT) with
az devops login
Configuration
Recommended defaults:
az devops configure --defaults organization=URL project=NAMEUsage Examples
Auto-Activation
User: "How do I list all Azure DevOps pipelines?"
→ Skill auto-activates on "Azure DevOps pipelines"
→ Provides `az pipelines list` command with examplesExplicit Invocation
User: "Show me Azure Artifacts workflows"
Agent: Skill(skill="azure-devops-cli")
→ Loads complete skill context
→ Provides artifacts commands and workflowsProgressive Disclosure
User: "I need comprehensive pipeline command reference"
→ Agent reads examples/pipelines-reference.md
→ Provides complete command set with advanced examplesCommand Coverage
DevOps (Organization & Projects)
- Project management: list, create, show, delete
- User and team management
- Organization configuration
Pipelines (Build & Release)
- Pipeline operations: list, run, create
- Run management: show, list, monitor
- YAML pipeline automation
Boards (Work Items & Sprints)
- Work item CRUD operations
- WIQL queries for filtering
- Sprint and iteration management
Repos (Git Repositories)
- Repository management: list, create
- Pull request workflows: create, review, merge
- Git integration and aliases
Artifacts (Package Management)
- Feed management: list, create
- Package operations: publish, download
- Universal package support
Common Workflows Covered
1. CI/CD Pipeline Automation 2. Pull Request Review Automation 3. Work Item Batch Creation 4. Pipeline Status Dashboard 5. Repository Clone Automation 6. Sprint Planning Helper 7. Release Gate Checking 8. Artifact Versioning 9. Team Dashboard Data 10. Environment Sync
Success Criteria
- ✅ Auto-activates on keywords
- ✅ Core <2000 tokens
- ✅ All commands tested
- ✅ 10+ workflows
- ✅ All 5 command groups
- ✅ Philosophy compliant
Maintenance
Update triggers: Azure CLI updates, new command groups, auth changes, user feedback
Regeneration: Review official docs → identify high-value commands → update workflows → test → version bump
Version: 1.0.0 (2025-11-24) - Initial release with 5 command groups, 10+ workflows
Testing
See tests/test-scenarios.md for:
- Auto-activation test cases
- Command accuracy validation
- Workflow testing examples
- Error handling verification
References
Contributing
Maintain 80/20 in core, add details to examples/, test all commands, follow amplihack philosophy
---
Brick Philosophy: Self-contained, regeneratable module with clear interface
Azure DevOps CLI Skill Test Scenarios
Validation scenarios for testing the Azure DevOps CLI skill quality, auto-activation, and content accuracy.
Test Scenario 1: Auto-Activation on Keywords
Purpose
Verify skill automatically activates on relevant keywords
Test Cases
TC1.1: Pipeline Keywords
User Input: "How do I list all Azure DevOps pipelines?"
Expected: Skill auto-activates
Validation: Response includes `az pipelines list` commandTC1.2: Boards Keywords
User Input: "Create a user story in Azure Boards"
Expected: Skill auto-activates
Validation: Response includes `az boards work-item create` commandTC1.3: Repos Keywords
User Input: "Show me Azure Repos pull requests"
Expected: Skill auto-activates
Validation: Response includes `az repos pr list` commandTC1.4: Artifacts Keywords
User Input: "How do I publish to Azure Artifacts?"
Expected: Skill auto-activates
Validation: Response includes `az artifacts universal publish` commandTC1.5: General DevOps Keywords
User Input: "List Azure DevOps projects"
Expected: Skill auto-activates
Validation: Response includes `az devops project list` commandTest Scenario 2: Quick Start Validation
Purpose
Verify Quick Start section enables immediate usage
Test Cases
TC2.1: Installation
Test: Follow installation steps
Steps:
1. Run: az extension add --name azure-devops
2. Verify: az devops --version
Expected: Extension installed successfullyTC2.2: Authentication
Test: Authenticate with Azure DevOps
Steps:
1. Run: az login
2. Verify: az account show
Expected: Successfully authenticatedTC2.3: Configuration
Test: Set default organization and project
Steps:
1. Run: az devops configure --defaults organization=https://dev.azure.com/myorg project=myproject
2. Verify: az devops configure --list
Expected: Defaults configured correctlyTC2.4: Verification
Test: Verify setup works
Steps:
1. Run: az devops project list
Expected: Projects listed without errorsTest Scenario 3: Essential Commands Accuracy
Purpose
Verify all essential commands are accurate and work
Test Cases
TC3.1: DevOps Commands
Test Commands:
- az devops project list
- az devops project show --project MyProject
- az devops user list
- az devops team list --project MyProject
Validation: Each command returns expected data without errorsTC3.2: Pipeline Commands
Test Commands:
- az pipelines list
- az pipelines show --name "MyPipeline"
- az pipelines run --name "MyPipeline"
- az pipelines runs list
- az pipelines runs show --id RUN_ID
Validation: Each command works with proper authenticationTC3.3: Boards Commands
Test Commands:
- az boards query --wiql "SELECT [System.Id] FROM WorkItems"
- az boards work-item create --type "Task" --title "Test"
- az boards work-item show --id WORK_ITEM_ID
- az boards work-item update --id WORK_ITEM_ID --state "Active"
- az boards iteration project list
Validation: Work items can be created and queriedTC3.4: Repos Commands
Test Commands:
- az repos list
- az repos show --repository myrepo
- az repos pr list --status active
- az repos pr show --id PR_ID
Validation: Repository operations work correctlyTC3.5: Artifacts Commands
Test Commands:
- az artifacts feed list
- az artifacts universal list --feed myfeed
- az artifacts universal publish --feed myfeed --name test --version 1.0.0 --path ./test
- az artifacts universal download --feed myfeed --name test --version 1.0.0 --path ./download
Validation: Package operations succeedTest Scenario 4: Common Workflows Validation
Purpose
Verify all 10+ common workflows are practical and work
Test Cases
TC4.1: CI/CD Pipeline Automation
Workflow: Create pipeline, run it, and monitor
Commands:
1. az pipelines create --name "Test-Pipeline" --repository myrepo --yml-path azure-pipelines.yml
2. az pipelines run --name "Test-Pipeline"
3. az pipelines runs show --id RUN_ID
Expected: Pipeline created, runs, and status retrievedTC4.2: Pull Request Review Automation
Workflow: List PRs, show details, update status
Commands:
1. az repos pr list --status active
2. az repos pr show --id PR_ID
3. az repos pr set-vote --id PR_ID --vote approve
Expected: PR workflow completes successfullyTC4.3: Work Item Batch Creation
Workflow: Create multiple work items
Script:
for title in "Feature A" "Feature B"; do
az boards work-item create --type "User Story" --title "$title"
done
Expected: Multiple work items createdTC4.4: Pipeline Status Dashboard
Workflow: Get recent pipeline runs
Command:
az pipelines runs list --top 10 --query "[].{Name:pipeline.name, Status:status}" --output table
Expected: Dashboard data displayedTC4.5: Repository Clone Automation
Workflow: List and clone all repos
Script:
az repos list --query "[].{Name:name, URL:remoteUrl}" --output tsv | while read name url; do
echo "Would clone $name from $url"
done
Expected: All repos identified for cloningTest Scenario 5: Troubleshooting Section
Purpose
Verify troubleshooting guidance resolves common issues
Test Cases
TC5.1: Authentication Failures
Problem: Command fails with auth error
Solution Steps:
1. az account clear
2. az login
3. Retry command
Expected: Authentication restoredTC5.2: Default Configuration Issues
Problem: Command requires --organization and --project
Solution Steps:
1. az devops configure --defaults organization=URL project=NAME
2. Retry command without flags
Expected: Defaults work correctlyTC5.3: Extension Update
Problem: Old extension version
Solution Steps:
1. az extension update --name azure-devops
2. az extension show --name azure-devops
Expected: Extension updated successfullyTest Scenario 6: Advanced Patterns
Purpose
Verify advanced patterns work correctly
Test Cases
TC6.1: REST API Access
Test: Direct REST API call
Command:
az devops invoke --area build --resource builds --route-parameters project=MyProject --api-version 6.0 --http-method GET
Expected: API response returnedTC6.2: JMESPath Queries
Test: Complex query filtering
Command:
az pipelines runs list --query "[?result=='failed'].{Pipeline:pipeline.name, Branch:sourceBranch}"
Expected: Filtered results returnedTC6.3: Shell Aliases
Test: Create and use alias
Commands:
1. alias azdo-pipelines="az pipelines list --output table"
2. azdo-pipelines
Expected: Alias works as shortcutTest Scenario 7: Token Efficiency
Purpose
Verify core skill stays under 2000 tokens
Test Cases
TC7.1: Token Count
Test: Measure skill.md token count
Method: Use token counter tool
Expected: Core skill.md < 2000 tokensTC7.2: Progressive Disclosure
Test: Verify extended content is separate
Check:
- Core content in skill.md
- Extended content in examples/
- References from skill.md to examples/
Expected: Clear separation maintainedTest Scenario 8: Philosophy Compliance
Purpose
Verify skill follows amplihack philosophy
Test Cases
TC8.1: Ruthless Simplicity
Check: Core skill focuses on 80/20 rule
Validation:
- Only essential commands in core
- 5 most common commands per group
- No unnecessary complexity
Expected: Simplicity maintainedTC8.2: Zero-BS Implementation
Check: All commands are tested and work
Validation:
- No TODOs or placeholders
- All examples are complete
- Commands include proper error handling
Expected: Every command worksTC8.3: Self-Contained Module
Check: No external dependencies
Validation:
- Only requires Azure CLI + extension
- No additional tools needed
- Clear setup instructions
Expected: Completely self-containedTest Scenario 9: Extended Content Quality
Purpose
Verify extended content provides comprehensive coverage
Test Cases
TC9.1: Complete Command References
Check: Each command group has complete reference
Files to verify:
- examples/pipelines-reference.md
- examples/boards-reference.md
- examples/repos-reference.md
- examples/artifacts-reference.md
Expected: All commands documented with examplesTC9.2: Advanced Workflow Guides
Check: Workflow guides are practical
Files to verify:
- examples/workflows/ci-cd-automation.md
- examples/workflows/release-management.md
- examples/workflows/team-collaboration.md
Expected: Real-world automation patternsTest Scenario 10: Integration Testing
Purpose
Verify skill works end-to-end in real scenarios
Test Cases
TC10.1: New User Onboarding
Scenario: New user follows skill to get started
Steps:
1. Install Azure DevOps extension
2. Authenticate
3. Configure defaults
4. Run first command
Expected: User successfully completes setupTC10.2: Daily Developer Workflow
Scenario: Developer uses skill for daily tasks
Tasks:
1. List active PRs
2. Run pipeline
3. Check work items
4. Review build status
Expected: All daily tasks completed using skill guidanceTC10.3: CI/CD Automation
Scenario: Automate deployment pipeline
Steps:
1. Create pipeline
2. Set up variables
3. Run pipeline
4. Monitor status
Expected: Complete CI/CD workflow automatedSuccess Criteria
Skill Quality Metrics
Auto-Activation:
- ✓ Activates on all 5 command group keywords
- ✓ Activates on specific operations (pipelines, PRs, work items)
- ✓ No false negatives (activates when it should)
Content Accuracy:
- ✓ All essential commands work without errors
- ✓ All code examples are syntactically correct
- ✓ All workflows are practical and tested
Token Efficiency:
- ✓ Core skill < 2000 tokens
- ✓ Extended content in separate files
- ✓ Progressive disclosure works
Philosophy Compliance:
- ✓ Ruthless simplicity (80/20 focus)
- ✓ Zero-BS (no placeholders)
- ✓ Self-contained (no external deps)
- ✓ Regeneratable (clear structure)
User Experience:
- ✓ Quick Start enables immediate usage
- ✓ Essential commands cover daily tasks
- ✓ Common workflows solve real problems
- ✓ Troubleshooting resolves issues
- ✓ Extended content provides depth
Manual Testing Checklist
Before marking skill as complete:
- [ ] Run all Quick Start commands
- [ ] Test at least 3 commands from each group
- [ ] Execute 5 common workflows
- [ ] Verify troubleshooting steps resolve issues
- [ ] Test auto-activation with various phrases
- [ ] Count tokens in skill.md (< 2000)
- [ ] Review extended content for completeness
- [ ] Check all code examples for syntax errors
- [ ] Validate YAML frontmatter
- [ ] Test explicit skill invocation
Automated Testing (Future)
Potential automation opportunities:
1. Command Syntax Validation: Parse all commands and validate syntax 2. Link Checking: Verify all internal references work 3. Token Counting: Automate token budget enforcement 4. Example Execution: Run code examples in sandbox 5. YAML Validation: Validate frontmatter schema
Notes
- Tests assume Azure DevOps organization and project are configured
- Some tests require actual Azure DevOps resources
- Authentication must be configured before running tests
- Extended content tests are manual reviews for quality
- Philosophy compliance is subjective but follows clear criteria
References
- Azure DevOps CLI Testing Guide
- amplihack Philosophy
- amplihack Patterns