L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Déploiement et opérations · SKILL.md

yaml-anchors

yaml-anchors

Implements YAML anchor and alias patterns (& and *) for configuration reuse, merge keys, and value referencing across Docker Compose, Kubernetes, and Helm files.

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

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


YAML Anchor and Alias Patterns

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.

TL;DR Checklist

  • Define anchors at a logical document level with descriptive snake_case names (&base_service_config, not &x)
  • Use merge key <<: *anchorName to shallow-merge anchor mappings into child configs
  • Verify no circular references exist between anchors and aliases
  • Test anchor behavior under YAML 1.2 parsers (PyYAML >= 6.0, ruamel.yaml)
  • Comment complex multi-anchor merge chains for readability

When to Use

Use this skill when:

  • Reducing duplication in Docker Compose, Kubernetes, or Helm configuration files where the same settings repeat across multiple services or environments
  • Sharing common settings across many resources (e.g., logging drivers, health check configs, network policies) with only minor per-service overrides
  • Building template-based deployments where dozens of resources share a base configuration pattern (Kubernetes Deployments, Helm values inheritance)
  • Implementing configuration inheritance in YAML-based CI/CD pipelines (GitHub Actions reusable workflows, GitLab CI includes)
  • Consolidating environment-specific configurations where staging and production differ only in replica counts, resource limits, or feature flags

When NOT to Use

Avoid this skill for:

  • Simple key-value pairs that don't justify the cognitive overhead of anchors (a single repeated port number should be a variable, not an anchor)
  • Files with fewer than 3 duplicative blocks — anchors add indirection; if duplication appears only twice, copy-paste is clearer
  • YAML 1.0 parsers where the merge key (<<:) behavior is undefined and inconsistent across implementations
  • Deeply nested object graphs (3+ levels of anchor references within anchored nodes) where scoping becomes impossible to trace without a debugger

Core Workflow

  1. Identify 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.


Implementation Patterns

Pattern 1: Docker Compose Service Inheritance

# ── 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"]

Pattern 2: Kubernetes ConfigMap + Deployment Reuse

# ── 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"

Pattern 3: Helm Values Inheritance with Anchors and Merge Keys

# 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

Constraints

MUST DO

  • Use descriptive anchor names that reflect the content they contain (&base_service_config, not &x, &1, or &temp)
  • Test YAML files with both PyYAML >= 6.0 and ruamel.yaml before committing to CI/CD pipelines to catch parser differences
  • Place anchors at a logical document level — define them where they make semantic sense, typically near the top of the file
  • Document complex multi-anchor merge chains with inline comments explaining which anchor each service inherits from and what it overrides

MUST NOT DO

  • Reuse the same anchor name (&defaults) in the same file — it silently overwrites previous definitions without warning
  • Expect merge keys (<<:) to deep-merge nested objects — they perform shallow merge only; entire nested mappings are fully replaced by child keys of the same name
  • Anchor inside a sequence item and expect to reference just that item — anchoring on a list anchors the entire list node
  • Use &anchor and *alias in YAML 1.0 parsers where merge key behavior is undefined

Common Pitfalls

Circular References

# ❌ 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

Shallow Merge Replaces Nested Objects

# ❌ 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

Scalar Anchor Misuse

# ❌ 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

Output Template

When implementing YAML anchor patterns, produce:

  1. Identified anchors — List each duplicative block found and its proposed anchor name with justification
  2. Anchor definitions — Show the full &anchorName definition including every key-value pair being reused
  3. Alias/merge usage — Show each *anchorName or <<: *anchorName reference with its override keys clearly commented
  4. Override documentation — For each child config, document what differs from the anchor and why
  5. Parser validation results — Confirm the file loads correctly under both PyYAML 6.0+ and ruamel.yaml, noting any version-specific differences

Live References

Authoritative documentation for YAML anchoring and merging patterns.

Skills enregistrées