Devops Tooling
Comprehensive toolkit for Git workflows, shell scripting, and development automation.
- Leverage native parallel subagent dispatch and 200k+ context windows where available.
When to Use This Skill
Use symptom -> action triggers: when one matches, apply this skill and verify with the protocol below.
- Creating conventional commits and managing Git workflows
- Writing Bash, Zsh, or PowerShell automation scripts
- Configuring CI/CD pipelines (GitHub Actions, Azure DevOps)
- Automating development, testing, or deployment tasks
- Troubleshooting Git conflicts and repository hygiene issues
Part 1: Git Workflows
Conventional Commits
The conventional commit specification provides an easy-to-extend set of rules for creating an explicit commit history.
Commit Format
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Types
| Type | Purpose | Example |
|---|---|---|
feat |
New feature | feat(auth): add OAuth2 support |
fix |
Bug fix | fix(api): resolve null reference |
docs |
Documentation only | docs(readme): update setup guide |
style |
Formatting/style (no logic) | style(ui): fix indentation |
refactor |
Refactor production code | refactor(svc): extract helpers |
perf |
Performance improvement | perf(db): add index on email |
test |
Adding tests | test(auth): add unit tests |
build |
Build system or deps | build(ci): upgrade Node to v20 |
ci |
CI configuration changes | ci(github): add workflow for PRs |
chore |
Maintenance tasks | chore(deps): update packages |
revert |
Revert previous commit | revert: feat(login) |
Breaking Changes
Breaking changes must be indicated by ! after the type/scope, or via BREAKING CHANGE in footer:
feat(api)!: remove deprecated v1 endpoint
feat(api): remove deprecated v1 endpoint
BREAKING CHANGE: v1 endpoints are no longer supported. Use v2.
Good Examples
feat(auth): implement JWT refresh tokens
fix(ui): resolve mobile navigation overlap issue
docs(api): add authentication examples
refactor(user): extract validation logic to separate module
perf(images): implement lazy loading
feat(core)!: change data structure from array to object
Git Operations
Branch Management
git checkout -b feature/PROJ-123/user-auth
git checkout develop
git branch -d feature/PROJ-123/user-auth
git push origin --delete feature/PROJ-123/user-auth
git branch -m new-name
git branch -a
Commit Workflow
git add .
git add file1.ts file2.ts
git add -i
git commit -m "feat(auth): add OAuth2 support"
git commit --amend
git log --oneline --graph --all
git show <commit-hash>
Merge & Rebase
git merge feature/new-feature
git rebase develop
git rebase -i HEAD~3
git rebase --abort
git merge --abort
git rebase --continue
git merge --continue
Handling Conflicts
git status
vim conflicting-file.ts
git add conflicting-file.ts
git commit # for merges
git rebase --continue # for rebases
Stashing
git stash push -m "Work in progress"
git stash pop
git stash apply stash@{2}
git stash list
git stash drop stash@{2}
git stash clear
Tagging
git tag -a v1.0.0 -m "Release v1.0.0"
git tag v1.0.0
git tag
git push origin --tags
git push origin v1.0.0
git tag -d v1.0.0
git push origin --delete v1.0.0
Git Diff
git diff
git diff --staged
git diff src/app.ts
git diff HEAD~2 HEAD
git log --oneline v1.0.0..v2.0.0
git diff --stat
Git Configuration
git config --global user.name "Your Name"
git config --global user.email "your@email.com"
git config --global init.defaultBranch main
git config --global commit.gpgsign true
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --unset user.name
git config --list
Part 2: Shell Scripting (bash/zsh)
General Principles
- Generate clean, simple, and concise code
- Ensure scripts are easily readable and understandable
- Add comments where needed for understanding
- Generate concise echo outputs for execution status
- Avoid unnecessary output and excessive logging
Error Handling & Safety
Enable Strict Mode
Always enable strict mode at the top of scripts:
#!/bin/bash
set -euo pipefail
-e: Exit on first error-u: Treat unset variables as errors-o pipefail: Surface pipeline failures
Cleanup with Traps
cleanup() {
# Remove temporary files
if [[ -n "${TEMP_DIR:-}" && -d "$TEMP_DIR" ]]; then
rm -rf "$TEMP_DIR"
fi
# Close connections
if [[ -n "${CONNECTION:-}" ]]; then
echo "Closing connection..."
# connection close logic
fi
}
trap cleanup EXIT
trap 'echo "Interrupted"; cleanup; exit 1' INT TERM
Validate Requirements
validate_requirements() {
local errors=0
# Check required variables
if [[ -z "${RESOURCE_GROUP:-}" ]]; then
echo "Error: RESOURCE_GROUP environment variable not set" >&2
((errors++))
fi
# Check required commands
for cmd in curl jq az; do
if ! command -v "$cmd" &> /dev/null; then
echo "Error: $cmd is not installed" >&2
((errors++))
fi
done
# Return appropriate exit code
return $errors
}
if ! validate_requirements; then
exit 1
fi
Working with Variables
echo "Config: ${CONFIG_FILE:-./config.default.yaml}"
name="World"
echo "Hello, ${name}!"
apps=("app1" "app2" "app3")
for app in "${apps[@]}"; do
echo "Processing: $app"
done
declare -A config
config[host]="localhost"
config[port]="8080"
echo "Connecting to ${config[host]}:${config[port]}"
Control Flow
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
echo "Linux detected"
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo "macOS detected"
else
echo "Unknown OS"
fi
case "$1" in
start)
echo "Starting service..."
;;
stop)
echo "Stopping service..."
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
for i in {1..5}; do
echo "Iteration $i"
done
timeout=30
elapsed=0
while [[ $elapsed -lt $timeout ]]; do
if check_ready; then
echo "Service ready"
break
fi
sleep 1
((elapsed++))
done
Parsing JSON with jq
result=$(curl -s "https://api.example.com/data")
name=$(echo "$result" | jq -r '.name')
count=$(echo "$result" | jq '.items | length')
first_item=$(echo "$result" | jq -r '.items[0]')
active_users=$(echo "$result" | jq '.users[] | select(.status == "active")')
read -r first_name last_name email <<<$(echo "$result" | jq -r '"\(.firstName) \(.lastName) \(.email)"')
echo "$result" | jq '.count += 1' > updated.json
jq -n \
--arg name "John" \
--arg age "30" \
'{name: $name, age: ($age | tonumber)}'
Parsing Arguments
#!/bin/bash
verbose=0
output_file="output.txt"
config="./config.yaml"
while [[ $# -gt 0 ]]; do
case $1 in
-v|--verbose)
verbose=1
shift
;;
-o|--output)
output_file="$2"
shift 2
;;
-c|--config)
config="$2"
shift 2
;;
-h|--help)
show_help
exit 0
;;
*)
echo "Unknown option: $1" >&2
exit 1
;;
esac
done
echo "Config: $config"
echo "Output: $output_file"
echo "Verbose: $verbose"
File Operations
if [[ -f "$FILE_PATH" ]]; then
echo "File exists"
fi
if [[ ! -d "$DIR_PATH" ]]; then
mkdir -p "$DIR_PATH"
fi
mkdir -p ./dir1/dir2
cp -r source_dir/ dest_dir/
rm -f file.txt # Force delete file
rm -rf directory/ # Recursive delete directory
find . -name "*.py" # All Python files
find . -type f -name "*.js" # All JS files (regular files only)
find . -mtime -7 # Modified in last 7 days
temp_file=$(mktemp)
echo "data" > "$temp_file"
rm -f "$temp_file"
temp_dir=$(mktemp -d)
rm -rf "$temp_dir"
Process Management
if pgrep -x "nginx" > /dev/null; then
echo "Nginx is running"
else
echo "Nginx is not running"
fi
while pgrep -x "script-name" > /dev/null; do
sleep 1
done
timeout 30s ./long-running-script.sh || echo "Timed out after 30s"
./script.sh &
job_pid=$!
trap "kill $job_pid 2>/dev/null" EXIT
Logging
#!/bin/bash
LOG_INFO() {
echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $*"
}
LOG_WARN() {
echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2
}
LOG_ERROR() {
echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2
}
LOG_DEBUG() {
if [[ "${DEBUG:-}" == "1" ]]; then
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') $*"
fi
}
LOG_INFO "Starting deployment"
LOG_WARN "This is a warning"
LOG_ERROR "Deployment failed"
LOG_DEBUG "Detailed debugging info"
Part 3: PowerShell Scripting
General Practices
- Use proper cmdlet names instead of aliases (e.g.,
Get-ChildItem, notdir) - Quote paths with spaces:
"C:\Path With Spaces\file.txt" - Use
ShouldProcessfor destructive operations - Implement proper error handling with try-catch
- Parameterize scripts for reusability
Error Handling
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
try {
$result = Invoke-RestMethod -Uri "https://api.example.com/data" -Method Get
}
catch [System.Net.WebException] {
Write-Error "Network error occurred: $($_.Exception.Message)"
exit 1
}
catch {
Write-Error "Unexpected error: $($_.Exception.Message)"
exit 1
}
finally {
# Cleanup code always runs
Write-Output "Execution completed"
}
trap {
Write-Error "Script failed: $_"
# Cleanup code
Remove-Variable -Name tempVar -ErrorAction SilentlyContinue
exit 1
}
Parameter Handling
param(
[Parameter(Mandatory=$true)]
[string]$Name,
[Parameter(Mandatory=$false)]
[string]$Path = ".",
[Parameter(Mandatory=$false)]
[switch]$Verbose,
[ValidateSet("dev", "staging", "prod")]
[string]$Environment = "dev"
)
Write-Output "Name: $Name"
Write-Output "Path: $Path"
Write-Output "Environment: $Environment"
Write-Output "Verbose: $Verbose"
Working with Objects
$user = [PSCustomObject]@{
Name = "John"
Age = 30
Email = "john@example.com",
Address = "123 Main St"
}
Write-Output $user.Name
$user | Add-Member -MemberType NoteProperty -Name "City" -Value "Anytown"
$users | Where-Object { $_.Age -gt 25 }
$users | Select-Object Name, Email
$users | Sort-Object Name
$users | Group-Object City
Working with JSON
$json = '{"name": "John", "age": 30}'
$obj = $json | ConvertFrom-Json
Write-Output $obj.name
$data = @{ name = "John"; age = 30 }
$json = $data | ConvertTo-Json -Depth 10
Write-Output $json
$config = Get-Content "config.json" | ConvertFrom-Json
$config | ConvertTo-Json -Depth 10 | Set-Content "config.json"
Working with Arrays and Hashtables
$files = @("file1.txt", "file2.txt", "file3.txt")
$files += "file4.txt"
$filtered = $files | Where-Object { $_ -like "*.txt" }
$config = @{
host = "localhost"
port = 8080
tls = $true
}
Write-Output $config.host
$key = "port"
Write-Output $config[$key]
foreach ($item in $config.GetEnumerator()) {
Write-Output "$($item.Name) = $($item.Value)"
}
File Operations
if (Test-Path "C:\path\to\file.txt") {
Write-Output "File exists"
}
New-Item -ItemType Directory -Path "C:\new\dir" -Force
Remove-Item "C:\path\to\file.txt" -Force
Remove-Item "C:\path\to\directory" -Recurse -Force
Copy-Item "source.txt" "destination.txt" -Force
Copy-Item "dir\" "backup\" -Recurse
Move-Item "old_name.txt" "new_name.txt"
$content = Get-Content "file.txt"
$content = Get-Content "file.txt" -Raw # As single string
$content | Set-Content "file.txt"
"more content" | Add-Content "file.txt"
String Operations
$name = "John"
Write-Output "Hello, $name!"
Write-Output ("Hello, {0}! Your score is {1:N2}" -f $name, 95.5)
$string = " Hello World "
$trimmed = $string.Trim()
$replaced = $string.Replace("World", "PowerShell")
if ($string -like "*World*") {
Write-Output "Contains 'World'"
}
if ($string -match "^Hello") {
Write-Output "Starts with 'Hello'"
}
$parts = "a,b,c".Split(",")
Part 4: CI/CD Configuration
GitHub Actions
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
workflow_dispatch: # Allow manual trigger
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Upload coverage
uses: codecov/codecov-action@v3
build:
needs: test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: build
path: dist/
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download artifacts
uses: actions/download-artifact@v3
with:
name: build
- name: Deploy to Azure
run: az webapp up --name myapp --resource-group myrg
Azure Pipelines
trigger:
- main
- develop
pool:
vmImage: 'ubuntu-latest'
variables:
buildConfiguration: 'Release'
packageFolder: '$(build.artifactStagingDirectory)/package'
stages:
- stage: Build
displayName: 'Build stage'
jobs:
- job: Build
displayName: 'Build job'
steps:
- task: NodeTool@0
inputs:
versionSpec: '18.x'
displayName: 'Install Node.js'
- script: |
npm ci
displayName: 'Install dependencies'
- script: |
npm run build
displayName: 'Build application'
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: 'dist'
includeRootFolder: false
archiveType: 'zip'
archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
displayName: 'Archive build artifacts'
- publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
artifact: drop
- stage: Deploy
displayName: 'Deploy stage'
dependsOn: Build
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: Deploy
displayName: 'Deploy job'
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
inputs:
azureSubscription: 'your_subscription_id'
appName: 'your_webapp_name'
package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip
DevOps Best Practices
Git
- Use conventional commits
- Keep commits small and focused
- Write clear, descriptive messages
- Keep feature branches short-lived
- Rebase feature branches before merging
- Sign commits for security-critical projects
Shell Scripting
- Always use strict mode (
set -euo pipefail) - Quote variables properly
- Handle errors gracefully with traps
- Validate inputs and parameters
- Use functions for reusability
- Add proper error messages to stderr
PowerShell
- Use strict mode for security
- Use proper cmdlet names (no aliases)
- Implement error handling with try-catch
- Test scripts thoroughly
- Use parameter validation
- Handle pipeline errors properly
CI/CD
- Use secure variable management for secrets
- Cache dependencies to speed up builds
- Run tests on multiple environments
- Use matrix builds for different configurations
- Implement proper artifact management
- Add deployment gates and approvals
- Provide clear build status
Cross-Client Portability
This skill is written to stay usable across GitHub Copilot, Claude Code, and Codex.
- GitHub Copilot: keep the folder in a Copilot-visible skill path or wrap the workflow in project instructions when folder discovery is unavailable.
- Claude Code: keep the folder in a local skills directory or a compatible plugin source.
- Codex: install or sync the folder into
$CODEX_HOME/skills/devops-toolingand restart Codex after major changes.
MCP Availability And Fallback
Preferred MCP Server: GitHub MCP
- Fallback prompt: "Use the Devops Tooling skill without MCP. Follow the documented local or manual fallback, show the selected tool surface, and report the verification evidence."
- Use standard local tools such as
git,gh, CI logs, and shell automation scripts for repository and pipeline work. - Prefer the bundled repo scripts or direct YAML edits when the MCP host does not expose GitHub operations.
- Do not claim an MCP operation was used when the active host does not expose it.
Anti-Patterns
- Starting work before the plan or gate is clear: Execution drifts when success criteria are implied instead of explicit.
- Treating verification as optional cleanup: The last mile is where regressions and missing updates are usually hiding.
- Mixing planning, implementation, and release work in one jump: You lose the causal chain that explains why a change is safe.
Verification Protocol
Before claiming "skill applied successfully":
- Pass/fail: The Devops Tooling workflow starts from explicit success criteria, constraints, and stop conditions.
- Pass/fail: Required evidence is collected before any completion, approval, or readiness claim.
- Pass/fail: The next action follows the documented gate order without skipping review or verification steps.
- Pressure-test scenario: Apply the workflow under time pressure with one failing check and one tempting shortcut.
- Success metric: Zero rationalizations; blocked, failed, or unverified work is reported as such.
References & Resources
Documentation
- CI/CD Patterns — GitHub Actions patterns, caching, security scanning, and deployment strategies
- Shell Scripting Patterns — PowerShell and Bash patterns side-by-side for automation
Scripts
- Setup Git Hooks — PowerShell script to install pre-commit, commit-msg, and pre-push hooks
Examples
- GitHub Actions Templates — 8 production-ready workflow templates for Node.js, Python, Docker, Terraform
Related Skills
- development-workflow: Use it when the workflow also needs planning, quality gates, and delivery tracking.
- code-quality: Use it when the workflow also needs two-stage review (spec compliance first, then code quality), maintainability, and refactoring guidance.
- systematic-debugging: Use it when the workflow also needs root-cause debugging before proposing fixes.
- test-driven-development: Use it when the workflow also needs test-first implementation and regression safety.