
Senior Devops
- 908 installs
- 23.5k repo stars
- Updated July 17, 2026
- alirezarezvani/claude-skills
senior-devops is a reference skill that teaches battle-tested CI/CD patterns, anti-patterns, performance guidelines, and security practices for developers who operate production-grade infrastructure.
About
senior-devops is a Claude Code reference skill from alirezarezvani/claude-skills that documents senior-level DevOps patterns for production CI/CD and infrastructure. The guide structures each practice with description, when-to-use scenarios, TypeScript implementation examples, benefits, and trade-offs so agents apply consistent pipeline and infra decisions. Developers reach for senior-devops when designing deployment workflows, hardening build systems, or reviewing infrastructure changes where guessing on caching, secrets, or rollout strategy risks outages. The readme frames two pattern sections—best-practice implementation and advanced techniques—with concrete code blocks agents can adapt to real repos.
- Comprehensive senior-devops reference with multiple codified patterns and implementation examples
- Includes dedicated sections on code organization, performance considerations, security best practices, and common patter
- Explicit anti-pattern guidance to help avoid costly mistakes in pipelines and infrastructure
- Recommended tools list with purpose statements and further reading resources
- TypeScript implementation examples for both basic and advanced DevOps techniques
Senior Devops by the numbers
- 908 all-time installs (skills.sh)
- +6 installs in the week ending Jul 29, 2026 (Skillselion tracking)
- Ranked #189 of 1,440 DevOps & CI/CD skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 31, 2026 (Skillselion catalog sync)
npx skills add https://github.com/alirezarezvani/claude-skills --skill senior-devopsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 908 |
|---|---|
| repo stars | ★ 23.5k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 17, 2026 |
| Repository | alirezarezvani/claude-skills ↗ |
What CI/CD and infrastructure patterns prevent production outages?
Get battle-tested patterns, anti-patterns, performance guidelines, and security practices for production-grade CI/CD and infrastructure.
Who is it for?
Developers and platform engineers designing or reviewing production CI/CD pipelines, cloud infrastructure, and deployment automation.
Skip if: Teams needing a runnable CLI or Terraform module generator rather than a pattern reference for agent-guided DevOps decisions.
When should I use this skill?
A developer asks for CI/CD best practices, infrastructure anti-patterns, production deployment guidance, or senior DevOps review of pipeline design.
What you get
Documented pipeline patterns, infrastructure checklists, and TypeScript implementation examples aligned to production DevOps standards.
- pipeline pattern recommendations
- infrastructure checklists
- implementation code snippets
Files
Senior Devops
Complete toolkit for senior devops with modern tools and best practices.
Quick Start
Main Capabilities
This skill provides three core capabilities through automated scripts:
# Script 1: Pipeline Generator — scaffolds CI/CD pipelines for GitHub Actions or CircleCI
python scripts/pipeline_generator.py ./app --platform=github --stages=build,test,deploy
# Script 2: Terraform Scaffolder — generates and validates IaC modules for AWS/GCP/Azure
python scripts/terraform_scaffolder.py ./infra --provider=aws --module=ecs-service --verbose
# Script 3: Deployment Manager — generates deployment manifests + runbooks with rollback support
python3 scripts/deployment_manager.py deploy --env=staging --image=app:1.2.3 --strategy=blue-green --verbose --jsonCore Capabilities
1. Pipeline Generator
Scaffolds CI/CD pipeline configurations for GitHub Actions or CircleCI, with stages for build, test, security scan, and deploy.
Example — GitHub Actions workflow:
# .github/workflows/ci.yml
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
build-docker:
needs: build-and-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and push image
uses: docker/build-push-action@v5
with:
push: ${{ github.ref == 'refs/heads/main' }}
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
deploy:
needs: build-docker
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Deploy to ECS
run: |
aws ecs update-service \
--cluster production \
--service app-service \
--force-new-deploymentUsage:
python scripts/pipeline_generator.py <project-path> --platform=github|circleci --stages=build,test,deploy2. Terraform Scaffolder
Generates, validates, and plans Terraform modules. Enforces consistent module structure and runs terraform validate + terraform plan before any apply.
Example — AWS ECS service module:
# modules/ecs-service/main.tf
resource "aws_ecs_task_definition" "app" {
family = var.service_name
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = var.cpu
memory = var.memory
container_definitions = jsonencode([{
name = var.service_name
image = var.container_image
essential = true
portMappings = [{
containerPort = var.container_port
protocol = "tcp"
}]
environment = [for k, v in var.env_vars : { name = k, value = v }]
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = "/ecs/${var.service_name}"
awslogs-region = var.aws_region
awslogs-stream-prefix = "ecs"
}
}
}])
}
resource "aws_ecs_service" "app" {
name = var.service_name
cluster = var.cluster_id
task_definition = aws_ecs_task_definition.app.arn
desired_count = var.desired_count
launch_type = "FARGATE"
network_configuration {
subnets = var.private_subnet_ids
security_groups = [aws_security_group.app.id]
assign_public_ip = false
}
load_balancer {
target_group_arn = aws_lb_target_group.app.arn
container_name = var.service_name
container_port = var.container_port
}
}Usage:
python scripts/terraform_scaffolder.py <target-path> --provider=aws|gcp|azure --module=ecs-service|gke-deployment|aks-service [--verbose]3. Deployment Manager
Generates Kubernetes deployment manifests and ordered kubectl runbooks for blue/green or rolling strategies, with health-check gates before traffic switches and rollback runbooks. The tool writes manifests and prints the commands — it never applies them to a cluster itself, so every change gets a human review.
Example — Kubernetes blue/green deployment (blue-slot specific elements):
# k8s/deployment-blue.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-blue
labels:
app: myapp
slot: blue # slot label distinguishes blue from green
spec:
replicas: 3
selector:
matchLabels:
app: myapp
slot: blue
template:
metadata:
labels:
app: myapp
slot: blue
spec:
containers:
- name: app
image: ghcr.io/org/app:1.2.3
readinessProbe: # gate: pod must pass before traffic switches
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"Usage:
python scripts/deployment_manager.py deploy \
--env=staging|production \
--image=app:1.2.3 \
--strategy=blue-green|rolling \
--health-check-url=https://app.example.com/healthz
python scripts/deployment_manager.py rollback --env=production --to-version=1.2.2
python scripts/deployment_manager.py --analyze --env=production # audit current stateResources
- Pattern Reference:
references/cicd_pipeline_guide.md— detailed CI/CD patterns, best practices, anti-patterns - Workflow Guide:
references/infrastructure_as_code.md— IaC step-by-step processes, optimization, troubleshooting - Technical Guide:
references/deployment_strategies.md— deployment strategy configs, security considerations, scalability - Tool Scripts:
scripts/directory
Development Workflow
1. Infrastructure Changes (Terraform)
# Scaffold or update module
python scripts/terraform_scaffolder.py ./infra --provider=aws --module=ecs-service --verbose
# Validate and plan — review diff before applying
terraform -chdir=infra init
terraform -chdir=infra validate
terraform -chdir=infra plan -out=tfplan
# Apply only after plan review
terraform -chdir=infra apply tfplan
# Verify resources are healthy
aws ecs describe-services --cluster production --services app-service \
--query 'services[0].{Status:status,Running:runningCount,Desired:desiredCount}'2. Application Deployment
# Generate or update pipeline config
python scripts/pipeline_generator.py . --platform=github --stages=build,test,security,deploy
# Build and tag image
docker build -t ghcr.io/org/app:$(git rev-parse --short HEAD) .
docker push ghcr.io/org/app:$(git rev-parse --short HEAD)
# Deploy with health-check gate
python scripts/deployment_manager.py deploy \
--env=production \
--image=app:$(git rev-parse --short HEAD) \
--strategy=blue-green \
--health-check-url=https://app.example.com/healthz
# Verify pods are running
kubectl get pods -n production -l app=myapp
kubectl rollout status deployment/app-blue -n production
# Switch traffic after verification
kubectl patch service app-svc -n production \
-p '{"spec":{"selector":{"slot":"blue"}}}'3. Rollback Procedure
# Immediate rollback via deployment manager
python scripts/deployment_manager.py rollback --env=production --to-version=1.2.2
# Or via kubectl
kubectl rollout undo deployment/app -n production
kubectl rollout status deployment/app -n production
# Verify rollback succeeded
kubectl get pods -n production -l app=myapp
curl -sf https://app.example.com/healthz || echo "ROLLBACK FAILED — escalate"Multi-Cloud Cross-References
Use these companion skills for cloud-specific deep dives:
| Skill | Cloud | Use When |
|---|---|---|
| aws-solution-architect | AWS | ECS/EKS, Lambda, VPC design, cost optimization |
| azure-cloud-architect | Azure | AKS, App Service, Virtual Networks, Azure DevOps |
| gcp-cloud-architect | GCP | GKE, Cloud Run, VPC, Cloud Build (coming soon) |
Multi-cloud vs single-cloud decision:
- Single-cloud (default) — lower operational complexity, deeper managed-service integration, better cost leverage with committed-use discounts
- Multi-cloud — required when mandated by compliance/data residency, acquiring companies on different clouds, or needing best-of-breed services across providers (e.g., AWS for compute + GCP for ML)
- Hybrid — on-prem + cloud; use when regulated workloads must stay on-prem while burst/non-sensitive workloads run in the cloud
Start single-cloud. Add a second cloud only when there is a concrete business or compliance driver — not for theoretical redundancy.
---
Cloud-Agnostic IaC
Terraform / OpenTofu (Default Choice)
Terraform (or its open-source fork OpenTofu) is the recommended IaC tool for most teams:
- Single language (HCL) across AWS, Azure, GCP, and 3,000+ providers
- State management with remote backends (S3, GCS, Azure Blob)
- Plan-before-apply workflow prevents drift surprises
- Cross-reference terraform-patterns for module structure, state isolation, and CI/CD integration
Pulumi (Programming Language IaC)
Choose Pulumi when the team strongly prefers TypeScript, Python, Go, or C# over HCL:
- Full programming language — loops, conditionals, unit tests native
- Same cloud provider coverage as Terraform
- Easier onboarding for dev teams that resist learning HCL
When to Use Cloud-Native IaC
| Tool | Use When |
|---|---|
| CloudFormation | AWS-only shop; need native AWS support (StackSets, Service Catalog) |
| Bicep | Azure-only shop; simpler syntax than ARM templates |
| Cloud Deployment Manager | GCP-only; rare — most GCP teams prefer Terraform |
Rule of thumb: Use Terraform/OpenTofu unless you are 100% committed to a single cloud AND the cloud-native tool offers a feature Terraform cannot replicate (e.g., AWS Service Catalog integration).
---
Troubleshooting
Check the comprehensive troubleshooting section in references/deployment_strategies.md.
Cicd Pipeline Guide
Overview
This reference guide provides comprehensive information for senior devops.
Patterns and Practices
Pattern 1: Best Practice Implementation
Description: Detailed explanation of the pattern.
When to Use:
- Scenario 1
- Scenario 2
- Scenario 3
Implementation:
// Example code implementation
export class Example {
// Implementation details
}Benefits:
- Benefit 1
- Benefit 2
- Benefit 3
Trade-offs:
- Consider 1
- Consider 2
- Consider 3
Pattern 2: Advanced Technique
Description: Another important pattern for senior devops.
Implementation:
// Advanced example
async function advancedExample() {
// Code here
}Guidelines
Code Organization
- Clear structure
- Logical separation
- Consistent naming
- Proper documentation
Performance Considerations
- Optimization strategies
- Bottleneck identification
- Monitoring approaches
- Scaling techniques
Security Best Practices
- Input validation
- Authentication
- Authorization
- Data protection
Common Patterns
Pattern A
Implementation details and examples.
Pattern B
Implementation details and examples.
Pattern C
Implementation details and examples.
Anti-Patterns to Avoid
Anti-Pattern 1
What not to do and why.
Anti-Pattern 2
What not to do and why.
Tools and Resources
Recommended Tools
- Tool 1: Purpose
- Tool 2: Purpose
- Tool 3: Purpose
Further Reading
- Resource 1
- Resource 2
- Resource 3
Conclusion
Key takeaways for using this reference guide effectively.
Deployment Strategies
Overview
This reference guide provides comprehensive information for senior devops.
Patterns and Practices
Pattern 1: Best Practice Implementation
Description: Detailed explanation of the pattern.
When to Use:
- Scenario 1
- Scenario 2
- Scenario 3
Implementation:
// Example code implementation
export class Example {
// Implementation details
}Benefits:
- Benefit 1
- Benefit 2
- Benefit 3
Trade-offs:
- Consider 1
- Consider 2
- Consider 3
Pattern 2: Advanced Technique
Description: Another important pattern for senior devops.
Implementation:
// Advanced example
async function advancedExample() {
// Code here
}Guidelines
Code Organization
- Clear structure
- Logical separation
- Consistent naming
- Proper documentation
Performance Considerations
- Optimization strategies
- Bottleneck identification
- Monitoring approaches
- Scaling techniques
Security Best Practices
- Input validation
- Authentication
- Authorization
- Data protection
Common Patterns
Pattern A
Implementation details and examples.
Pattern B
Implementation details and examples.
Pattern C
Implementation details and examples.
Anti-Patterns to Avoid
Anti-Pattern 1
What not to do and why.
Anti-Pattern 2
What not to do and why.
Tools and Resources
Recommended Tools
- Tool 1: Purpose
- Tool 2: Purpose
- Tool 3: Purpose
Further Reading
- Resource 1
- Resource 2
- Resource 3
Conclusion
Key takeaways for using this reference guide effectively.
Infrastructure As Code
Overview
This reference guide provides comprehensive information for senior devops.
Patterns and Practices
Pattern 1: Best Practice Implementation
Description: Detailed explanation of the pattern.
When to Use:
- Scenario 1
- Scenario 2
- Scenario 3
Implementation:
// Example code implementation
export class Example {
// Implementation details
}Benefits:
- Benefit 1
- Benefit 2
- Benefit 3
Trade-offs:
- Consider 1
- Consider 2
- Consider 3
Pattern 2: Advanced Technique
Description: Another important pattern for senior devops.
Implementation:
// Advanced example
async function advancedExample() {
// Code here
}Guidelines
Code Organization
- Clear structure
- Logical separation
- Consistent naming
- Proper documentation
Performance Considerations
- Optimization strategies
- Bottleneck identification
- Monitoring approaches
- Scaling techniques
Security Best Practices
- Input validation
- Authentication
- Authorization
- Data protection
Common Patterns
Pattern A
Implementation details and examples.
Pattern B
Implementation details and examples.
Pattern C
Implementation details and examples.
Anti-Patterns to Avoid
Anti-Pattern 1
What not to do and why.
Anti-Pattern 2
What not to do and why.
Tools and Resources
Recommended Tools
- Tool 1: Purpose
- Tool 2: Purpose
- Tool 3: Purpose
Further Reading
- Resource 1
- Resource 2
- Resource 3
Conclusion
Key takeaways for using this reference guide effectively.
#!/usr/bin/env python3
"""
Deployment Manager
Generates blue/green or rolling Kubernetes deployment manifests plus an ordered
runbook of kubectl commands, and audits existing manifests. It never talks to a
cluster itself — review the manifests and run the printed commands yourself.
Subcommands:
deploy --env --image [--strategy] [--health-check-url] — write manifests + runbook
rollback --env --to-version — write a rollback runbook
analyze --env — audit manifests on disk
"""
import argparse
import json
import re
import sys
from pathlib import Path
from typing import Dict, List
DEPLOYMENT_TEMPLATE = """apiVersion: apps/v1
kind: Deployment
metadata:
name: {name}
namespace: {env}
labels:
app: {app}{slot_label}
spec:
replicas: {replicas}
selector:
matchLabels:
app: {app}{slot_label_indented}
template:
metadata:
labels:
app: {app}{slot_label_indented2}
spec:
containers:
- name: app
image: {image}
readinessProbe:
httpGet:
path: {health_path}
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
"""
SERVICE_TEMPLATE = """apiVersion: v1
kind: Service
metadata:
name: {app}-svc
namespace: {env}
spec:
selector:
app: {app}{slot_selector}
ports:
- port: 80
targetPort: 8080
"""
def app_name_from_image(image: str) -> str:
"""ghcr.io/org/my-app:1.2.3 -> my-app"""
repo = image.rsplit(":", 1)[0]
return repo.rsplit("/", 1)[-1] or "app"
def render_deployment(app: str, env: str, image: str, replicas: int,
health_path: str, slot: str = "") -> str:
return DEPLOYMENT_TEMPLATE.format(
name=f"{app}-{slot}" if slot else app,
env=env,
app=app,
image=image,
replicas=replicas,
health_path=health_path,
slot_label=f"\n slot: {slot}" if slot else "",
slot_label_indented=f"\n slot: {slot}" if slot else "",
slot_label_indented2=f"\n slot: {slot}" if slot else "",
)
def cmd_deploy(args) -> Dict:
app = args.app or app_name_from_image(args.image)
health_path = "/healthz"
if args.health_check_url:
match = re.search(r"https?://[^/]+(/.*)", args.health_check_url)
if match:
health_path = match.group(1)
out_dir = Path(args.output_dir) / args.env
out_dir.mkdir(parents=True, exist_ok=True)
written: List[str] = []
runbook: List[str] = []
if args.strategy == "blue-green":
slot = args.slot
manifest = out_dir / f"deployment-{slot}.yaml"
manifest.write_text(
render_deployment(app, args.env, args.image, args.replicas, health_path, slot),
encoding="utf-8",
)
written.append(str(manifest))
service = out_dir / "service.yaml"
if not service.exists():
# service starts pointing at the OTHER slot; traffic switches in the runbook
other = "green" if slot == "blue" else "blue"
service.write_text(SERVICE_TEMPLATE.format(
app=app, env=args.env, slot_selector=f"\n slot: {other}"), encoding="utf-8")
written.append(str(service))
runbook = [
f"kubectl apply -f {manifest}",
f"kubectl rollout status deployment/{app}-{slot} -n {args.env}",
]
if args.health_check_url:
runbook.append(f"curl -sf {args.health_check_url} || echo 'HEALTH CHECK FAILED — do not switch traffic'")
runbook += [
f"# switch traffic to the {slot} slot only after the checks above pass:",
f"kubectl patch service {app}-svc -n {args.env} "
f"-p '{{\"spec\":{{\"selector\":{{\"app\":\"{app}\",\"slot\":\"{slot}\"}}}}}}'",
]
else: # rolling
manifest = out_dir / "deployment.yaml"
manifest.write_text(
render_deployment(app, args.env, args.image, args.replicas, health_path),
encoding="utf-8",
)
written.append(str(manifest))
service = out_dir / "service.yaml"
if not service.exists():
service.write_text(SERVICE_TEMPLATE.format(
app=app, env=args.env, slot_selector=""), encoding="utf-8")
written.append(str(service))
runbook = [
f"kubectl apply -f {manifest}",
f"kubectl rollout status deployment/{app} -n {args.env}",
]
if args.health_check_url:
runbook.append(f"curl -sf {args.health_check_url} || kubectl rollout undo deployment/{app} -n {args.env}")
return {
"status": "success",
"action": "deploy",
"env": args.env,
"app": app,
"image": args.image,
"strategy": args.strategy,
"manifests_written": written,
"runbook": runbook,
}
def cmd_rollback(args) -> Dict:
app = args.app
runbook = [
f"# Option 1 — pin the previous image version explicitly:",
f"kubectl set image deployment/{app} app={app}:{args.to_version} -n {args.env}",
f"kubectl rollout status deployment/{app} -n {args.env}",
f"# Option 2 — revert to the previous ReplicaSet:",
f"kubectl rollout undo deployment/{app} -n {args.env}",
f"# Verify:",
f"kubectl get pods -n {args.env} -l app={app}",
]
return {
"status": "success",
"action": "rollback",
"env": args.env,
"app": app,
"to_version": args.to_version,
"runbook": runbook,
}
def cmd_analyze(args) -> Dict:
env_dir = Path(args.output_dir) / args.env
deployments = []
if env_dir.is_dir():
for manifest in sorted(env_dir.glob("deployment*.yaml")):
text = manifest.read_text(encoding="utf-8")
image = re.search(r"image:\s*(\S+)", text)
replicas = re.search(r"replicas:\s*(\d+)", text)
slot = re.search(r"slot:\s*(\S+)", text)
deployments.append({
"manifest": str(manifest),
"image": image.group(1) if image else "unknown",
"replicas": int(replicas.group(1)) if replicas else 0,
"slot": slot.group(1) if slot else None,
})
return {
"status": "success" if deployments else "empty",
"action": "analyze",
"env": args.env,
"manifest_dir": str(env_dir),
"deployments": deployments,
}
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Generate deployment manifests and runbooks (blue/green or rolling)."
)
sub = parser.add_subparsers(dest="command", required=True)
deploy = sub.add_parser("deploy", help="Generate deployment manifests + runbook")
deploy.add_argument("--env", required=True, help="Target environment / namespace")
deploy.add_argument("--image", required=True, help="Container image (repo:tag)")
deploy.add_argument("--strategy", default="rolling", choices=["blue-green", "rolling"])
deploy.add_argument("--health-check-url", help="Health check URL gating traffic switch")
deploy.add_argument("--app", help="App name (default: derived from image)")
deploy.add_argument("--slot", default="blue", choices=["blue", "green"],
help="Slot to deploy into (blue-green only)")
deploy.add_argument("--replicas", type=int, default=3)
deploy.add_argument("--output-dir", default="./deploy", help="Manifest output directory")
rollback = sub.add_parser("rollback", help="Generate a rollback runbook")
rollback.add_argument("--env", required=True)
rollback.add_argument("--to-version", required=True, help="Version to roll back to")
rollback.add_argument("--app", default="app", help="App / deployment name")
analyze = sub.add_parser("analyze", help="Audit deployment manifests on disk")
analyze.add_argument("--env", required=True)
analyze.add_argument("--output-dir", default="./deploy", help="Manifest directory")
for p in (deploy, rollback, analyze):
p.add_argument("--verbose", "-v", action="store_true", help="Enable verbose output")
p.add_argument("--json", action="store_true", help="Output results as JSON")
p.add_argument("--output", "-o", help="Write JSON results to this file")
return parser
def main():
# support the documented `--analyze --env=...` flag form as an alias
argv = ["analyze" if a == "--analyze" else a for a in sys.argv[1:]]
args = build_parser().parse_args(argv)
handlers = {"deploy": cmd_deploy, "rollback": cmd_rollback, "analyze": cmd_analyze}
results = handlers[args.command](args)
print(f"🚀 {results['action']} ({results['env']}) — status: {results['status']}")
for manifest in results.get("manifests_written", []):
print(f"✓ Wrote {manifest}")
for dep in results.get("deployments", []):
slot = f" slot={dep['slot']}" if dep["slot"] else ""
print(f" - {dep['manifest']}: image={dep['image']} replicas={dep['replicas']}{slot}")
if results.get("runbook"):
print("\nRunbook — review, then execute in order:")
for step in results["runbook"]:
print(f" {step}")
if args.json or args.output:
output = json.dumps(results, indent=2)
if args.output:
Path(args.output).write_text(output, encoding="utf-8")
print(f"Results written to {args.output}")
else:
print(output)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Pipeline Generator
Scaffolds CI/CD pipeline configurations for GitHub Actions or CircleCI with
build, test, security, and deploy stages. Detects node/python/go projects to
pick sensible default commands.
"""
import argparse
import json
import sys
from pathlib import Path
from typing import Dict, List
VALID_STAGES = ["build", "test", "security", "deploy"]
def detect_runtime(project: Path) -> str:
if (project / "package.json").exists():
return "node"
if (project / "pyproject.toml").exists() or (project / "requirements.txt").exists():
return "python"
if (project / "go.mod").exists():
return "go"
return "generic"
RUNTIME_COMMANDS: Dict[str, Dict[str, List[str]]] = {
"node": {
"setup": ["npm ci"],
"build": ["npm run build --if-present"],
"test": ["npm run lint --if-present", "npm test"],
},
"python": {
"setup": ["pip install -r requirements.txt"],
"build": ["python -m compileall ."],
"test": ["python -m ruff check .", "python -m pytest"],
},
"go": {
"setup": ["go mod download"],
"build": ["go build ./..."],
"test": ["go vet ./...", "go test ./..."],
},
"generic": {
"setup": ["echo 'add setup commands here'"],
"build": ["echo 'add build commands here'"],
"test": ["echo 'add test commands here'"],
},
}
GITHUB_SETUP_STEPS = {
"node": """ - uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'""",
"python": """ - uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'""",
"go": """ - uses: actions/setup-go@v5
with:
go-version: '1.22'""",
"generic": "",
}
CIRCLECI_IMAGES = {
"node": "cimg/node:20.11",
"python": "cimg/python:3.12",
"go": "cimg/go:1.22",
"generic": "cimg/base:current",
}
def github_job(name: str, runtime: str, commands: List[str], needs: List[str],
extra: str = "") -> str:
lines = [f" {name}:"]
if needs:
lines.append(f" needs: [{', '.join(needs)}]")
if name == "deploy":
lines.append(" if: github.ref == 'refs/heads/main'")
lines.append(" runs-on: ubuntu-latest")
lines.append(" steps:")
lines.append(" - uses: actions/checkout@v4")
setup = GITHUB_SETUP_STEPS[runtime]
if setup and name in ("build", "test"):
lines.append(setup)
for cmd in RUNTIME_COMMANDS[runtime]["setup"]:
lines.append(f" - run: {cmd}")
for cmd in commands:
lines.append(f" - run: {cmd}")
if extra:
lines.append(extra)
return "\n".join(lines)
def generate_github(stages: List[str], runtime: str) -> str:
jobs = []
prev: List[str] = []
for stage in stages:
if stage == "build":
jobs.append(github_job("build", runtime, RUNTIME_COMMANDS[runtime]["build"], prev))
elif stage == "test":
jobs.append(github_job("test", runtime, RUNTIME_COMMANDS[runtime]["test"], prev))
elif stage == "security":
extra = """ - name: Run Trivy filesystem scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
severity: 'CRITICAL,HIGH'
exit-code: '1'"""
jobs.append(github_job("security", runtime, [], prev, extra=extra))
elif stage == "deploy":
extra = """ - name: Build and push image
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Deploy
run: echo 'replace with your deploy command (e.g. aws ecs update-service / kubectl apply)'"""
jobs.append(github_job("deploy", runtime, [], prev, extra=extra))
prev = [stage]
return f"""name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
{chr(10).join(jobs)}
"""
def generate_circleci(stages: List[str], runtime: str) -> str:
image = CIRCLECI_IMAGES[runtime]
job_blocks = []
workflow_jobs = []
prev = None
for stage in stages:
if stage == "security":
commands = ["echo 'add security scanner here (e.g. trivy fs .)'"]
elif stage == "deploy":
commands = ["echo 'replace with your deploy command'"]
else:
commands = RUNTIME_COMMANDS[runtime]["setup"] + RUNTIME_COMMANDS[runtime][stage]
steps = "\n".join(f" - run: {cmd}" for cmd in commands)
job_blocks.append(f""" {stage}:
docker:
- image: {image}
steps:
- checkout
{steps}""")
if prev:
workflow_jobs.append(f""" - {stage}:
requires: [{prev}]""")
else:
workflow_jobs.append(f" - {stage}")
prev = stage
return f"""version: 2.1
jobs:
{chr(10).join(job_blocks)}
workflows:
ci:
jobs:
{chr(10).join(workflow_jobs)}
"""
def main():
parser = argparse.ArgumentParser(
description="Generate a CI/CD pipeline config for GitHub Actions or CircleCI."
)
parser.add_argument("target", help="Project path to scaffold the pipeline into")
parser.add_argument("--platform", default="github", choices=["github", "circleci"],
help="CI platform (default: github)")
parser.add_argument("--stages", default="build,test,deploy",
help=f"Comma-separated stages from: {','.join(VALID_STAGES)}")
parser.add_argument("--force", action="store_true", help="Overwrite an existing config")
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose output")
parser.add_argument("--json", action="store_true", help="Output results as JSON")
parser.add_argument("--output", "-o", help="Write JSON results to this file")
args = parser.parse_args()
project = Path(args.target)
if not project.is_dir():
print(f"❌ Error: target path is not a directory: {project}", file=sys.stderr)
sys.exit(1)
stages = [s.strip() for s in args.stages.split(",") if s.strip()]
invalid = [s for s in stages if s not in VALID_STAGES]
if invalid or not stages:
print(f"❌ Error: invalid stages {invalid or '(none)'}; "
f"choose from {','.join(VALID_STAGES)}", file=sys.stderr)
sys.exit(1)
runtime = detect_runtime(project)
if args.verbose:
print(f"📊 Detected runtime: {runtime}")
if args.platform == "github":
config = generate_github(stages, runtime)
config_path = project / ".github" / "workflows" / "ci.yml"
else:
config = generate_circleci(stages, runtime)
config_path = project / ".circleci" / "config.yml"
if config_path.exists() and not args.force:
print(f"❌ Error: {config_path} already exists (use --force to overwrite)", file=sys.stderr)
sys.exit(1)
config_path.parent.mkdir(parents=True, exist_ok=True)
config_path.write_text(config, encoding="utf-8")
print(f"✅ Pipeline written: {config_path} (platform={args.platform}, "
f"stages={','.join(stages)}, runtime={runtime})")
results = {
"status": "success",
"platform": args.platform,
"stages": stages,
"runtime": runtime,
"config_path": str(config_path),
}
if args.json or args.output:
output = json.dumps(results, indent=2)
if args.output:
Path(args.output).write_text(output, encoding="utf-8")
print(f"Results written to {args.output}")
else:
print(output)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Terraform Scaffolder
Generates provider-specific Terraform module skeletons (main.tf, variables.tf,
outputs.tf, versions.tf) and optionally runs `terraform fmt`/`validate` when the
terraform binary is available.
"""
import argparse
import json
import shutil
import subprocess
import sys
from pathlib import Path
from typing import Dict
# module -> required provider
MODULE_PROVIDERS = {
"ecs-service": "aws",
"gke-deployment": "gcp",
"aks-service": "azure",
}
ECS_MAIN = '''resource "aws_ecs_task_definition" "app" {
family = var.service_name
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = var.cpu
memory = var.memory
container_definitions = jsonencode([{
name = var.service_name
image = var.container_image
essential = true
portMappings = [{
containerPort = var.container_port
protocol = "tcp"
}]
environment = [for k, v in var.env_vars : { name = k, value = v }]
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = "/ecs/${var.service_name}"
awslogs-region = var.aws_region
awslogs-stream-prefix = "ecs"
}
}
}])
}
resource "aws_ecs_service" "app" {
name = var.service_name
cluster = var.cluster_id
task_definition = aws_ecs_task_definition.app.arn
desired_count = var.desired_count
launch_type = "FARGATE"
network_configuration {
subnets = var.private_subnet_ids
security_groups = var.security_group_ids
assign_public_ip = false
}
}
'''
ECS_VARIABLES = '''variable "service_name" {
description = "Name of the ECS service"
type = string
}
variable "cluster_id" {
description = "ECS cluster ID"
type = string
}
variable "container_image" {
description = "Container image (repo:tag)"
type = string
}
variable "container_port" {
description = "Container port"
type = number
default = 8080
}
variable "cpu" {
description = "Fargate task CPU units"
type = number
default = 256
}
variable "memory" {
description = "Fargate task memory (MiB)"
type = number
default = 512
}
variable "desired_count" {
description = "Desired task count"
type = number
default = 2
}
variable "aws_region" {
description = "AWS region"
type = string
}
variable "private_subnet_ids" {
description = "Private subnet IDs for the service"
type = list(string)
}
variable "security_group_ids" {
description = "Security group IDs for the service"
type = list(string)
}
variable "env_vars" {
description = "Environment variables for the container"
type = map(string)
default = {}
}
'''
ECS_OUTPUTS = '''output "service_name" {
description = "Name of the ECS service"
value = aws_ecs_service.app.name
}
output "task_definition_arn" {
description = "ARN of the task definition"
value = aws_ecs_task_definition.app.arn
}
'''
GKE_MAIN = '''resource "kubernetes_deployment" "app" {
metadata {
name = var.app_name
namespace = var.namespace
labels = { app = var.app_name }
}
spec {
replicas = var.replicas
selector {
match_labels = { app = var.app_name }
}
template {
metadata {
labels = { app = var.app_name }
}
spec {
container {
name = var.app_name
image = var.container_image
port {
container_port = var.container_port
}
readiness_probe {
http_get {
path = var.health_check_path
port = var.container_port
}
initial_delay_seconds = 10
period_seconds = 5
}
resources {
requests = {
cpu = var.cpu_request
memory = var.memory_request
}
limits = {
cpu = var.cpu_limit
memory = var.memory_limit
}
}
}
}
}
}
}
resource "kubernetes_service" "app" {
metadata {
name = var.app_name
namespace = var.namespace
}
spec {
selector = { app = var.app_name }
port {
port = 80
target_port = var.container_port
}
type = "ClusterIP"
}
}
'''
GKE_VARIABLES = '''variable "app_name" {
description = "Application name"
type = string
}
variable "namespace" {
description = "Kubernetes namespace"
type = string
default = "default"
}
variable "container_image" {
description = "Container image (repo:tag)"
type = string
}
variable "container_port" {
description = "Container port"
type = number
default = 8080
}
variable "replicas" {
description = "Number of replicas"
type = number
default = 3
}
variable "health_check_path" {
description = "Readiness probe path"
type = string
default = "/healthz"
}
variable "cpu_request" {
description = "CPU request"
type = string
default = "250m"
}
variable "memory_request" {
description = "Memory request"
type = string
default = "256Mi"
}
variable "cpu_limit" {
description = "CPU limit"
type = string
default = "500m"
}
variable "memory_limit" {
description = "Memory limit"
type = string
default = "512Mi"
}
'''
GKE_OUTPUTS = '''output "deployment_name" {
description = "Name of the deployment"
value = kubernetes_deployment.app.metadata[0].name
}
output "service_name" {
description = "Name of the service"
value = kubernetes_service.app.metadata[0].name
}
'''
AKS_MAIN = '''resource "azurerm_kubernetes_cluster" "this" {
name = var.cluster_name
location = var.location
resource_group_name = var.resource_group_name
dns_prefix = var.cluster_name
default_node_pool {
name = "default"
node_count = var.node_count
vm_size = var.vm_size
}
identity {
type = "SystemAssigned"
}
tags = var.tags
}
'''
AKS_VARIABLES = '''variable "cluster_name" {
description = "AKS cluster name"
type = string
}
variable "location" {
description = "Azure region"
type = string
}
variable "resource_group_name" {
description = "Resource group name"
type = string
}
variable "node_count" {
description = "Default node pool size"
type = number
default = 3
}
variable "vm_size" {
description = "Node VM size"
type = string
default = "Standard_D2s_v5"
}
variable "tags" {
description = "Resource tags"
type = map(string)
default = {}
}
'''
AKS_OUTPUTS = '''output "cluster_name" {
description = "AKS cluster name"
value = azurerm_kubernetes_cluster.this.name
}
output "kube_config" {
description = "Raw kube config for the cluster"
value = azurerm_kubernetes_cluster.this.kube_config_raw
sensitive = true
}
'''
VERSIONS = {
"aws": '''terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
}
''',
"gcp": '''terraform {
required_version = ">= 1.5"
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = ">= 2.0"
}
}
}
''',
"azure": '''terraform {
required_version = ">= 1.5"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 3.0"
}
}
}
''',
}
MODULE_FILES: Dict[str, Dict[str, str]] = {
"ecs-service": {"main.tf": ECS_MAIN, "variables.tf": ECS_VARIABLES, "outputs.tf": ECS_OUTPUTS},
"gke-deployment": {"main.tf": GKE_MAIN, "variables.tf": GKE_VARIABLES, "outputs.tf": GKE_OUTPUTS},
"aks-service": {"main.tf": AKS_MAIN, "variables.tf": AKS_VARIABLES, "outputs.tf": AKS_OUTPUTS},
}
def run_terraform_checks(module_dir: Path, verbose: bool) -> Dict:
"""Run terraform fmt/validate when the binary exists; otherwise skip."""
checks = {"terraform_available": False, "fmt": "skipped", "validate": "skipped"}
if not shutil.which("terraform"):
if verbose:
print("ℹ️ terraform binary not found — skipping fmt/validate")
return checks
checks["terraform_available"] = True
fmt = subprocess.run(
["terraform", "fmt", "-recursive", str(module_dir)],
capture_output=True, text=True,
)
checks["fmt"] = "passed" if fmt.returncode == 0 else f"failed: {fmt.stderr.strip()}"
init = subprocess.run(
["terraform", f"-chdir={module_dir}", "init", "-backend=false", "-input=false"],
capture_output=True, text=True,
)
if init.returncode == 0:
validate = subprocess.run(
["terraform", f"-chdir={module_dir}", "validate"],
capture_output=True, text=True,
)
checks["validate"] = "passed" if validate.returncode == 0 else f"failed: {validate.stderr.strip()}"
else:
checks["validate"] = f"init failed: {init.stderr.strip()}"
return checks
def scaffold(target: Path, provider: str, module: str, force: bool, verbose: bool) -> Dict:
expected_provider = MODULE_PROVIDERS[module]
if provider != expected_provider:
raise ValueError(
f"Module '{module}' targets provider '{expected_provider}', not '{provider}'. "
f"Valid pairs: " + ", ".join(f"{m} → {p}" for m, p in MODULE_PROVIDERS.items())
)
module_dir = target / "modules" / module
module_dir.mkdir(parents=True, exist_ok=True)
files = dict(MODULE_FILES[module])
files["versions.tf"] = VERSIONS[provider]
written, skipped = [], []
for name, content in sorted(files.items()):
path = module_dir / name
if path.exists() and not force:
skipped.append(str(path))
if verbose:
print(f"⏭️ Exists, skipping (use --force to overwrite): {path}")
continue
path.write_text(content, encoding="utf-8")
written.append(str(path))
if verbose:
print(f"✓ Wrote {path}")
return {
"status": "success",
"provider": provider,
"module": module,
"module_dir": str(module_dir),
"files_written": written,
"files_skipped": skipped,
"checks": run_terraform_checks(module_dir, verbose),
}
def main():
parser = argparse.ArgumentParser(
description="Generate a Terraform module skeleton for AWS/GCP/Azure."
)
parser.add_argument("target", help="Target infrastructure directory (e.g. ./infra)")
parser.add_argument("--provider", required=True, choices=["aws", "gcp", "azure"],
help="Cloud provider")
parser.add_argument("--module", required=True, choices=sorted(MODULE_PROVIDERS),
help="Module template to scaffold")
parser.add_argument("--force", action="store_true",
help="Overwrite existing files")
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose output")
parser.add_argument("--json", action="store_true", help="Output results as JSON")
parser.add_argument("--output", "-o", help="Write JSON results to this file")
args = parser.parse_args()
print(f"🚀 Scaffolding {args.provider}/{args.module} module under {args.target} ...")
try:
results = scaffold(Path(args.target), args.provider, args.module, args.force, args.verbose)
except ValueError as exc:
print(f"❌ Error: {exc}", file=sys.stderr)
sys.exit(1)
print(f"✅ Module ready: {results['module_dir']} "
f"({len(results['files_written'])} written, {len(results['files_skipped'])} skipped)")
if args.json or args.output:
output = json.dumps(results, indent=2)
if args.output:
Path(args.output).write_text(output, encoding="utf-8")
print(f"Results written to {args.output}")
else:
print(output)
if __name__ == "__main__":
main()
Related skills
How it compares
Pick senior-devops for pattern-level CI/CD and infrastructure guidance; pick runnable IaC or pipeline generators when you need executable config output.
FAQ
What does senior-devops cover?
senior-devops is a reference skill for production CI/CD and infrastructure. It documents patterns and anti-patterns with implementation examples, benefits, trade-offs, and security guidance so agents recommend consistent senior-level DevOps decisions.
When should developers use senior-devops?
senior-devops fits pipeline design, infrastructure reviews, and production hardening tasks. Reach for it when CI/CD workflows, deployment strategy, or infra security need documented patterns instead of improvised agent guesses.
Is Senior Devops safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.