A minimal Python project demonstrating Azure DevOps CI/CD pipeline with automated testing. This repository serves as a template for setting up basic continuous integration workflows in Azure Pipelines.
- Overview
- Project Structure
- Pipeline Configuration
- Prerequisites
- Local Development
- Azure DevOps Setup
- Pipeline Operations
- Troubleshooting
- Advanced Configuration
- Contributing
This project demonstrates a basic Azure Pipeline that:
- β
Triggers automatically on pushes to
mainbranch - β
Uses hosted Ubuntu agents (
ubuntu-latest) - β Sets up Python 3.11 environment
- β Installs project dependencies
- β Runs automated tests with pytest
- β Reports test results
Build Stats:
- Average build time: ~30-40 seconds
- Latest build:
20251012.1- β Succeeded - Test coverage: 3/3 tests passing
azure-pipeline-basic/
βββ app/
β βββ __init__.py # Package marker
β βββ hello.py # Main application with greet() function
βββ tests/
β βββ test_hello.py # Pytest test suite (3 tests)
βββ azure-pipelines.yml # Pipeline definition
βββ requirements.txt # Python dependencies (pytest)
βββ README.md # This file
azure-pipelines.yml - Pipeline configuration:
- Defines build triggers
- Configures build agent
- Specifies build steps
- Sets Python version
app/hello.py - Simple Python application:
greet(name: str) -> str- Returns greeting message- Input validation (raises ValueError for empty names)
- CLI entrypoint for testing
tests/test_hello.py - Test suite:
test_greet_happy()- Tests standard greetingtest_greet_empty()- Tests error handlingtest_greet_whitespace()- Tests edge cases
The pipeline is defined in azure-pipelines.yml:
trigger:
- main # Auto-trigger on main branch commits
pool:
vmImage: 'ubuntu-latest' # Use Ubuntu build agent
variables:
pythonVersion: '3.11' # Python version
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(pythonVersion)'
- script: |
python -m pip install --upgrade pip
pip install -r requirements.txt
displayName: 'Install dependencies'
- script: |
pytest -q
displayName: 'Run tests'- Python 3.11 or higher
- Git
- pip (Python package manager)
- Azure DevOps account (Sign up free)
- Azure CLI (optional, for command-line operations)
- Personal Access Token (PAT) for authentication
git clone https://github.com/atulkamble/azure-pipeline-basic.git
cd azure-pipeline-basic# Create virtual environment
python3 -m venv .venv
# Activate virtual environment
# On macOS/Linux:
source .venv/bin/activate
# On Windows:
# .venv\Scripts\activatepip install --upgrade pip
pip install -r requirements.txt# Run the main application
python app/hello.py
# Output: Hello, World!
# Import and use in Python
python -c "from app.hello import greet; print(greet('Azure'))"
# Output: Hello, Azure!# Run all tests
pytest -v
# Run with coverage
pytest --cov=app --cov-report=term-missing
# Quick run (quiet mode)
pytest -qExpected output:
... [100%]
3 passed in 0.01s
- Navigate to https://dev.azure.com
- Sign in with your Microsoft account
- Click + New project
- Fill in project details:
- Project name:
azure-pipeline-basic - Description: Basic Azure Pipeline demo
- Visibility: Private
- Project name:
- Click Create
# Add Azure DevOps remote
git remote add azure https://dev.azure.com/YOUR-ORG/azure-pipeline-basic/_git/azure-pipeline-basic
# Push code
git push azure mainAuthentication Issues? See Troubleshooting section.
- In Azure DevOps, navigate to Pipelines β Pipelines
- Click New pipeline or Create Pipeline
- Select Azure Repos Git
- Select your repository:
azure-pipeline-basic - Azure DevOps will detect
azure-pipelines.yml - Review the configuration
- Click Run
The pipeline will start automatically!
# Install Azure CLI (macOS)
brew install azure-cli
# Login to Azure
az login
# Set default organization and project
az devops configure --defaults organization=https://dev.azure.com/YOUR-ORG project=azure-pipeline-basicaz devops project create \
--name "azure-pipeline-basic" \
--description "Basic Azure Pipeline demo" \
--organization https://dev.azure.com/YOUR-ORG \
--source-control git \
--visibility privateCreate Personal Access Token (PAT):
- Go to https://dev.azure.com/YOUR-ORG
- Click profile icon β Personal access tokens
- Click + New Token
- Name:
Git Push Token - Scopes: Code (Read & write)
- Click Create and copy the token
Configure Git with PAT:
git remote add azure https://YOUR_PAT@dev.azure.com/YOUR-ORG/azure-pipeline-basic/_git/azure-pipeline-basic
git push azure mainaz pipelines create \
--name "azure-pipeline-basic" \
--repository azure-pipeline-basic \
--repository-type tfsgit \
--branch main \
--yml-path azure-pipelines.yml \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basicThis command will:
- Create the pipeline definition
- Automatically trigger the first build
- Return build details
az pipelines list \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic \
--output tableaz pipelines run \
--name "azure-pipeline-basic" \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic# Get latest build status
az pipelines build show \
--id BUILD_ID \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic \
--query "{status: status, result: result, buildNumber: buildNumber}" \
--output tableaz pipelines runs list \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic \
--output table# Get detailed build information
az pipelines build show \
--id BUILD_ID \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic# Wait and check status
sleep 10 && az pipelines build show \
--id BUILD_ID \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic \
--query "{status: status, result: result, buildNumber: buildNumber, finishTime: finishTime}" \
--output tableProblem: git push azure main hangs waiting for authentication
Solution A - Personal Access Token (Recommended):
# 1. Create PAT in Azure DevOps (Settings β Personal Access Tokens)
# 2. Update remote URL with PAT
git remote set-url azure https://YOUR_PAT@dev.azure.com/YOUR-ORG/azure-pipeline-basic/_git/azure-pipeline-basic
# 3. Push again
git push azure mainSolution B - SSH Authentication:
# 1. Generate SSH key (if you don't have one)
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# 2. Copy public key
cat ~/.ssh/id_rsa.pub
# 3. Add to Azure DevOps (Settings β SSH public keys)
# 4. Update remote to use SSH
git remote set-url azure git@ssh.dev.azure.com:v3/YOUR-ORG/azure-pipeline-basic/azure-pipeline-basic
# 5. Push
git push azure mainProblem: There were no build definitions matching name "azure-pipeline-basic"
Solution: Create the pipeline first:
az pipelines create \
--name "azure-pipeline-basic" \
--repository azure-pipeline-basic \
--repository-type tfsgit \
--branch main \
--yml-path azure-pipelines.yml \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basicProblem: ModuleNotFoundError: No module named 'app'
Solution: The test file includes a sys.path fix. Ensure you have app/__init__.py:
# tests/test_hello.py already includes this fix
import os
import sys
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
if ROOT not in sys.path:
sys.path.insert(0, ROOT)Problem: Please run 'az login' to setup account
Solution:
az login
az account show # Verify authentication# View all remotes
git remote -v
# Check current branch
git branch
# View git status
git status
# View recent commits
git log --oneline -5Update requirements.txt:
pytest
pytest-cov
Update azure-pipelines.yml:
- script: |
pytest --cov=app --cov-report=xml --cov-report=html
displayName: 'Run tests with coverage'
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: '$(System.DefaultWorkingDirectory)/coverage.xml'strategy:
matrix:
Python39:
python.version: '3.9'
Python310:
python.version: '3.10'
Python311:
python.version: '3.11'
Python312:
python.version: '3.12'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(python.version)'- script: |
pip install flake8 black
flake8 app/ tests/
black --check app/ tests/
displayName: 'Lint code'Add to azure-pipelines.yml:
schedules:
- cron: "0 0 * * *" # Daily at midnight
displayName: Daily midnight build
branches:
include:
- mainstages:
- stage: Build
jobs:
- job: BuildAndTest
steps:
# Build and test steps
- stage: Deploy
dependsOn: Build
condition: succeeded()
jobs:
- deployment: DeployToStaging
environment: 'staging'
strategy:
runOnce:
deploy:
steps:
# Deployment stepsView your pipeline builds at:
https://dev.azure.com/YOUR-ORG/azure-pipeline-basic/_build
Latest Build Information:
- Build Number:
20251012.1 - Status: β Succeeded
- Duration: ~31 seconds
- Tests: 3/3 passed
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests locally (
pytest -v) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Atul Kamble
- Azure DevOps: @atul-kamble
- Email: atul_kamble@hotmail.com
- Azure DevOps team for excellent CI/CD platform
- Python community for pytest framework
- Contributors and users of this template
Quick Start Summary:
# Clone and setup
git clone https://github.com/atulkamble/azure-pipeline-basic.git
cd azure-pipeline-basic
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Run locally
python app/hello.py
pytest -v
# Push to Azure DevOps
git remote add azure https://YOUR_PAT@dev.azure.com/YOUR-ORG/azure-pipeline-basic/_git/azure-pipeline-basic
git push azure main
# Create and run pipeline
az pipelines create --name "azure-pipeline-basic" --repository azure-pipeline-basic --repository-type tfsgit --branch main --yml-path azure-pipelines.yml --organization https://dev.azure.com/YOUR-ORG --project azure-pipeline-basicHappy Building! π
Warning: These commands will permanently delete resources. Use with caution!
# List pipelines to get the pipeline ID
az pipelines list \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic \
--output table
# Delete pipeline by ID
az pipelines delete \
--id PIPELINE_ID \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic \
--yes# List recent builds
az pipelines runs list \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic \
--output table
# Delete a specific build (Note: This only deletes the build record, not the pipeline)
az pipelines build delete \
--id BUILD_ID \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic \
--yes# List repositories
az repos list \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic \
--output table
# Delete repository
az repos delete \
--id REPO_ID \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic \
--yesAlternative via Portal:
- Go to Repos β Files
- Click repository dropdown
- Select Manage repositories
- Click ... β Delete repository
# Delete entire project (includes all pipelines, repos, builds)
az devops project delete \
--id PROJECT_ID_OR_NAME \
--organization https://dev.azure.com/YOUR-ORG \
--yesAlternative via Portal:
- Go to Project settings (bottom left)
- Scroll to bottom β Delete
- Type project name to confirm
- Click Delete
# Deactivate if active
deactivate
# Remove virtual environment directory
rm -rf .venv# Remove Azure DevOps remote
git remote remove azure
# Remove origin remote (if needed)
git remote remove origin
# Verify remotes removed
git remote -v# Remove Python cache files
find . -type d -name "__pycache__" -exec rm -r {} +
find . -type f -name "*.pyc" -delete
find . -type f -name "*.pyo" -delete
# Remove pytest cache
rm -rf .pytest_cache
# Remove coverage files
rm -f .coverage
rm -rf htmlcov
rm -f coverage.xml# Remove all Python artifacts and virtual environment
rm -rf .venv
rm -rf __pycache__
rm -rf .pytest_cache
rm -rf htmlcov
rm -f .coverage
rm -f coverage.xml
find . -type d -name "__pycache__" -exec rm -r {} + 2>/dev/null
find . -type f -name "*.pyc" -delete
find . -type f -name "*.pyo" -deleteImportant: Always revoke PAT tokens when you no longer need them.
Via Portal:
- Go to https://dev.azure.com/YOUR-ORG
- Click profile icon β Personal access tokens
- Find your token
- Click ... β Revoke
- Confirm revocation
Security Best Practice:
- Revoke PATs immediately if compromised
- Use short expiration periods (30-90 days)
- Create separate PATs for different purposes
- Never commit PATs to version control
# Navigate to parent directory
cd ..
# Remove entire project directory
rm -rf azure-pipeline-basicWarning: This will delete all local files. Ensure you have pushed any important changes!
# Verify pipeline deleted
az pipelines list \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic \
--output table
# Verify repository deleted
az repos list \
--organization https://dev.azure.com/YOUR-ORG \
--project azure-pipeline-basic \
--output table
# Verify project deleted
az devops project list \
--organization https://dev.azure.com/YOUR-ORG \
--output tableSave this as cleanup.sh for easy cleanup:
#!/bin/bash
# Complete cleanup script for azure-pipeline-basic
echo "π§Ή Starting cleanup..."
# Set variables (update these)
ORG="YOUR-ORG"
PROJECT="azure-pipeline-basic"
PIPELINE_ID="15" # Update with your pipeline ID
echo "π Organization: $ORG"
echo "π Project: $PROJECT"
echo ""
# Azure Resources Cleanup
read -p "Delete Azure Pipeline? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "ποΈ Deleting pipeline..."
az pipelines delete --id $PIPELINE_ID --organization https://dev.azure.com/$ORG --project $PROJECT --yes
fi
read -p "Delete Azure Project? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "ποΈ Deleting project..."
az devops project delete --id $PROJECT --organization https://dev.azure.com/$ORG --yes
fi
# Local Cleanup
read -p "Clean local Python artifacts? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "π§Ή Cleaning Python cache..."
rm -rf .venv
rm -rf __pycache__
rm -rf .pytest_cache
rm -rf htmlcov
rm -f .coverage
rm -f coverage.xml
find . -type d -name "__pycache__" -exec rm -r {} + 2>/dev/null
find . -type f -name "*.pyc" -delete
fi
read -p "Remove Git remotes? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "π Removing Git remotes..."
git remote remove azure 2>/dev/null
git remote remove origin 2>/dev/null
fi
echo ""
echo "β
Cleanup complete!"
echo ""
echo "π Remember to:"
echo " - Revoke Personal Access Tokens in Azure DevOps"
echo " - Remove local directory if no longer needed: rm -rf azure-pipeline-basic"Usage:
chmod +x cleanup.sh
./cleanup.sh- β Backup any important pipeline configurations
- β Export build history if needed
- β Save any custom YAML files
- β Document any custom settings
- β Verify all resources are removed
- β Revoke all PAT tokens
- β Remove local clones if not needed
- β Clear browser cache/credentials
- Build history is retained for auditing (30-90 days)
- Organization-level settings require admin access
- Some resources may have retention policies
End of Documentation | Last Updated: October 12, 2025