Writing Skills
writing-skills
效率与协作技能,来自 obra/superpowers,查看原文获取完整用途和操作说明。
yaml-anchors【部署与运维】Implements YAML anchor and alias patterns (& and *) for configuration reuse, merge keys, and value referencing across Docker Compose, Kubernetes, and Helm files.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
name: yaml-anchors description: Implements YAML anchor and alias patterns (& and *) for configuration reuse, merge keys, and value referencing across Docker Compose, Kubernetes, and Helm files. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: coding triggers: yaml anchors, yaml aliases, & anchor, * alias, YAML merge key, docker-compose reuse, kubernetes config reuse role: implementation scope: implementation output-format: code content-types: [code, guidance, do-dont, config] related-skills: docker-compose-patterns, kubernetes-configmap, helm-charts archetypes: - tactical - generation anti_triggers: - brainstorming - vague ideation - code golf response_profile: verbosity: low directive_strength: high abstraction_level: operational
Implements YAML anchor (&name) and alias (*name) patterns — along with merge keys (<<:) — to eliminate configuration duplication in Docker Compose, Kubernetes manifests, Helm templates, and CI/CD pipeline definitions. When loaded, this skill makes the model design, refactor, and validate YAML documents that use structured composition instead of copy-pasted blocks.
<<: *anchorName to shallow-merge anchor mappings into child configsUse this skill when:
Avoid this skill for:
<<:) behavior is undefined and inconsistent across implementationsIdentify Duplicative Patterns — Scan the entire YAML document for repeated configuration blocks. Look for: identical logging drivers, shared health check definitions, duplicate network configurations, or repeated deploy/resource specifications. Use structural comparison (not just visual) to catch variations. Checkpoint: If more than 2 structurally identical blocks exist with only minor field differences, proceed to create an anchor. Count the exact lines of duplication — if total saved exceeds 10 lines, anchors are justified.
Define Anchor with Descriptive Name — Place &anchorName on the mapping or scalar node being reused. Use descriptive snake_case names that reflect the semantic content, not its position in the file. Anchors must be unique within the document scope.
# Define anchors at a logical structural level, not nested inside another key's value
_base_service_config: &base_service_config
restart_policy: unless-stopped
deploy:
replicas: 3
resources:
limits:
memory: 512M
cpus: "0.5"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Checkpoint: Anchor name must be unique within the document — re-anchoring the same name silently overwrites previous definitions. Use a prefix like _base_ to signal shared definition at a glance.
Apply Alias or Merge Key — For simple scalar value reuse, reference with *anchorName as a direct replacement node. For mapping inheritance, use <<: *anchorName merge key to shallow-merge all keys from the anchored mapping into the current context. Keys listed after the merge key override corresponding keys from the anchor.
services:
api-server:
<<: *base_service_config # merges ALL keys from &base_service_config
hostname: api-prod # adds new key not present in anchor
ports:
- "8080:8080" # new key — fully replaces if same name exists
deploy: # nested object — ENTIRE mapping is REPLACED, not deep-merged
replicas: 5 # override from within the new deploy mapping
worker:
<<: *base_service_config
hostname: worker-prod
command: ["python", "worker.py"]
deploy:
replicas: 3
Checkpoint: Merge keys perform SHALLOW merge only — nested objects are entirely replaced, not recursively merged. If you need to override a nested property, re-define the entire parent mapping in the child.
Override and Extend Per Service — For each child configuration that references an anchor, specify ONLY the keys that differ from the base. Document intentional overrides with inline comments explaining why the value was changed.
services:
api-server:
<<: *base_service_config
hostname: api-prod
# Override replicas for high-traffic API service
deploy:
replicas: 5
resources:
limits:
memory: 1G # API needs more memory
cpus: "1.0"
ports:
- "8080:8080"
worker:
<<: *base_service_config
hostname: worker-prod
# Workers run a different command but share the same infra settings
command: ["python", "-m", "worker.main"]
Checkpoint: Each override should be justified by a comment. If a child has no overrides at all, consider if it even needs its own entry or could use the anchor directly.
Validate Under YAML 1.2 Parser — Test file resolution with modern parsers before committing to CI/CD pipelines. Many cloud-native tools now ship YAML 1.2 parsers (PyYAML >= 6.0 switched defaults) which are stricter about merge key behavior and scalar types.
#!/usr/bin/env python3
"""Validate YAML anchor resolution across the configuration file."""
import sys
from pathlib import Path
import yaml
def validate_yaml_anchors(filepath: str) -> dict:
"""Parse a YAML file and verify all anchors resolved correctly.
Returns a summary dict with success status and any errors found.
"""
try:
with open(filepath, "r") as f:
config = yaml.safe_load(f)
except yaml.YAMLError as exc:
return {"success": False, "error": str(exc)}
if not isinstance(config, dict):
return {
"success": False,
"error": f"Expected top-level mapping, got {type(config).__name__}",
}
# Verify known anchor-merged structures are intact
errors = []
services = config.get("services", {})
if not isinstance(services, dict):
errors.append("'services' is missing or not a mapping")
return {"success": False, "errors": errors}
for svc_name, svc_config in services.items():
restart = svc_config.get("restart_policy")
if restart != "unless-stopped":
errors.append(
f"Service '{svc_name}' missing inherited 'restart_policy'"
)
deploy = svc_config.get("deploy", {})
if not isinstance(deploy, dict):
errors.append(
f"Service '{svc_name}' deploy is not a mapping — "
f"anchor merge may have failed"
)
return {
"success": len(errors) == 0,
"errors": errors,
"service_count": len(services),
}
if __name__ == "__main__":
path = sys.argv[1] if len(sys.argv) > 1 else "docker-compose.yaml"
result = validate_yaml_anchors(path)
print(f"Anchors valid: {result['success']}")
if result.get("errors"):
for err in result["errors"]:
print(f" ERROR: {err}")
else:
print(f" Validated {result['service_count']} services successfully")
Checkpoint: Parse the file with both yaml.safe_load() and ruamel.yaml.RoundTripLoader to catch version-specific behavior differences. If either parser fails, the anchors are non-portable.
# ── Base service definition (anchor for all services) ──
_base_service: &base_service_definition
image: ${REGISTRY:-ghcr.io}/myorg/myapp:${TAG:-latest}
restart_policy: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── Production service overrides ──
services:
api-server:
<<: *base_service_definition
hostname: api-prod
ports:
- "443:8080"
deploy:
replicas: 5
resources:
limits:
memory: 1G
cpus: "1.0"
environment:
NODE_ENV: production
LOG_LEVEL: warn
worker:
<<: *base_service_definition
hostname: worker-prod
command: ["python", "-m", "celery.worker"]
deploy:
replicas: 3
resources:
limits:
memory: 2G
cpus: "2.0"
scheduler:
<<: *base_service_definition
hostname: scheduler-prod
command: ["python", "-m", "celery.beat"]
# ── Shared container spec anchor ──
_base_container: &base_container
imagePullPolicy: IfNotPresent
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
envFrom:
- configMapRef:
name: app-config
# ── Deployment with shared container template ──
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-api
spec:
replicas: 3
template:
spec:
containers:
- <<: *base_container
name: api
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-worker
spec:
replicas: 2
template:
spec:
containers:
- <<: *base_container
name: worker
command: ["python", "-m", "celery.worker"]
# Resources from anchor are inherited; overrides below:
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "1Gi"
cpu: "1000m"
# values.yaml — demonstrate anchor usage in Helm charts
base_ingress: &base_ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
tls:
- secretName: app-tls
hosts:
- example.com
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 80
# Environment-specific overrides via merge key
staging:
ingress:
<<: *base_ingress
annotations:
# Override annotation only — other keys inherited from anchor
nginx.ingress.kubernetes.io/ssl-redirect: "false"
rules:
- host: staging.example.com
production:
ingress:
<<: *base_ingress
tls:
- secretName: prod-tls
hosts:
- example.com
- api.example.com
&base_service_config, not &x, &1, or &temp)&defaults) in the same file — it silently overwrites previous definitions without warning<<:) to deep-merge nested objects — they perform shallow merge only; entire nested mappings are fully replaced by child keys of the same name&anchor and *alias in YAML 1.0 parsers where merge key behavior is undefined# ❌ CRASHES — YAML parser detects circular reference and raises an error
circular_a: &a
self_ref: *a # Fatal error: recursive mapping definition detected
parent: &parent_config
children_count: 3
child_names: [alice, bob, charlie]
# ✅ GOOD — Always verify no anchor references itself or creates a cycle
# ❌ BAD — "format" key is silently lost because the entire logging mapping is replaced
defaults: &defaults
logging:
level: info
format: json
app_config:
<<: *defaults
logging:
level: debug # ⚠️ "format: json" is completely lost!
# The parser replaces the whole 'logging' mapping
# ✅ GOOD — Re-anchor the nested object for targeted deep override
defaults: &defaults
logging:
level: info
format: json
logging_debug_override: &logging_debug
<<: *defaults.logging # re-anchor only the nested mapping
level: debug # now only "level" is overridden, "format" preserved
app_config:
<<: *defaults
logging: *logging_debug
# ❌ BAD — Anchoring a scalar value does NOT perform string interpolation
_port_value: &api_port 8080
services:
web:
ports:
- "*api_port:80" # ⚠️ This is a literal string "*api_port:80", not expanded!
# Anchors on scalars replace the ENTIRE node, not substrings
# ✅ GOOD — Use environment variables for values that need interpolation in strings
environment:
API_PORT: "8080"
services:
web:
ports:
- "${API_PORT}:80" # Docker Compose resolves this correctly
When implementing YAML anchor patterns, produce:
&anchorName definition including every key-value pair being reused*anchorName or <<: *anchorName reference with its override keys clearly commentedAuthoritative documentation for YAML anchoring and merging patterns.