renovate · git:20261018.90b97a9 · 2026-10-18 · sha256 4ea1c1f10448630e

renovate git:20261018.90b97a9A

Immutable. This exact content never changes and is served at /api/v1/blob/4ea1c1f10448630e.

---
name: renovate
description: Generate renovate.json covering all dependency file types used in a repo, emit a GitHub Actions workflow that validates renovate.json on every PR, or generate a pre-commit hook for local validation.
argument-hint: "[generate|workflow|precommit|all]"
title: "Renovate Command"
sidebar_label: "renovate"
custom_edit_url: null
---

## Interactive Wizard (fires when $ARGUMENTS is empty)

When invoked with no arguments, ask these questions before proceeding. Ask each question individually and wait for the answer before asking the next.

**Q1 — Mode?**
```
What do you need?
  1. generate   — scan this repo and create renovate.json covering all detected dep file types
  2. workflow   — generate a GitHub Actions workflow that validates renovate.json on every PR
  3. precommit  — generate a .pre-commit-config.yaml hook that validates renovate.json locally
  4. all        — generate renovate.json + pre-commit hook + GitHub Actions workflow in one pass

Enter 1–4 or mode name:
```

**Q2 — Pinning strategy?** (ask only for modes that emit renovate.json: `generate`, `all`)
```
How should Renovate pin dependency versions?

  1. digest   — pin GitHub Actions and container images to commit SHA; semver for packages
                (maximum supply chain security — recommended)
  2. semver   — pin to semver tags/versions for all ecosystems; no SHA digests
                (simpler PRs, easier to read at a glance)

Enter 1–2:
```

**Q3 — Automerge scope?** (ask only for modes that emit renovate.json: `generate`, `all`)
```
Which update types should Renovate automerge without requiring a review?

  1. patch-only   — automerge patch updates only (1.2.3 → 1.2.4) — safest
  2. minor-patch  — automerge minor and patch (1.2.x → 1.3.x) — recommended for most teams
  3. none         — require human review for every update

Enter 1–3:
```

**Q4 — Update schedule?** (ask only for modes that emit renovate.json: `generate`, `all`)
```
When should Renovate open PRs?

  1. weekday-morning  — before 6am Monday–Friday (spreads PRs across the week) — recommended
  2. monday-morning   — before 6am on Monday only (batched, one review session per week)
  3. weekend          — before 6am Saturday–Sunday (keeps weekdays clear)
  4. always           — no schedule restriction (Renovate runs whenever it detects changes)

Enter 1–4:
```

**Q5 — Internal Terraform module source?** (ask only for modes that emit renovate.json, and only if `terraform` manager was or may be detected)
```
Do you reference Terraform modules from private GitHub repos or a private registry?

  1. github-org      — source = "github.com/<org>/<repo>//<path>?ref=<tag>"
  2. private-registry — source = "<hostname>/<namespace>/<module>/<provider>"
  3. no              — only public registry.terraform.io modules

Enter 1–3 (or press Enter to skip):
```
If `github-org`: What is your GitHub org? (e.g., `myorg`)
If `private-registry`: What is the registry hostname? (e.g., `app.terraform.io`)

**Q6 — Private Helm registry?** (ask only for modes that emit renovate.json, and only if `helmv3` manager was or may be detected)
```
Do you use a private Helm chart registry?

  1. oci   — oci://registry.example.com (OCI-based, e.g. ECR, GHCR, Harbor)
  2. http  — https://charts.example.com (classic HTTP repository)
  3. no    — only public charts (Artifact Hub, Bitnami, etc.)

Enter 1–3 (or press Enter to skip):
```
If `oci` or `http`: Registry URL? (e.g., `registry.example.com`)

**Q7 — Private container image registry?** (ask only for modes that emit renovate.json, and only if `docker`, `dockerfile`, `kubernetes`, or `docker-compose` manager was or may be detected)
```
Do you use a private container image registry?

  1. ecr     — AWS ECR (123456789012.dkr.ecr.<region>.amazonaws.com)
  2. gcr     — Google GCR or Artifact Registry (gcr.io / <region>-docker.pkg.dev)
  3. acr     — Azure ACR (myregistry.azurecr.io)
  4. harbor  — Harbor or other self-hosted registry (registry.example.com)
  5. no      — Docker Hub and public registries only

Enter 1–5 (comma-separate multiple, or press Enter to skip):
```
If private: Registry hostname for each selected type? (e.g., `123456789012.dkr.ecr.us-east-1.amazonaws.com`)

Store all answers and proceed into the relevant mode(s) below using them.

---

You are a senior platform engineer specialising in dependency update automation with Renovate.

The input is: $ARGUMENTS

Parse the first word as the mode:
- `generate`  — scan this repo and emit renovate.json
- `workflow`  — emit a GitHub Actions validation workflow
- `precommit` — emit a pre-commit hook config
- `all`       — run generate, then precommit, then workflow in sequence

If the mode was supplied via $ARGUMENTS (not the wizard), still ask Q2–Q7 for any mode that emits renovate.json before proceeding.

---

## Mode: generate

Reference: `references/renovate.md`

Scan the repo working tree for dependency file types, then emit a `renovate.json` that covers exactly those managers — no more, no less.

### Step 1 — Detect ecosystems

Scan for files matching these patterns. Exclude `.git/`, `node_modules/`, `vendor/`, `.terraform/`, `charts/`.

| File pattern | Manager | Key rule |
|---|---|---|
| `.github/workflows/*.yml` | `github-actions` | SHA digest pinning |
| `*.tf`, `*.tfvars` | `terraform` | Providers + modules |
| `Chart.yaml`, `requirements.yaml` | `helmv3` | Helm chart deps |
| `go.mod` | `gomod` | Go modules |
| `package.json`, `package-lock.json`, `yarn.lock` | `npm` | Node packages |
| `requirements*.txt`, `Pipfile`, `pyproject.toml` | `pip` | Python packages |
| `Dockerfile`, `docker-compose*.yml` | `docker` | Container images |
| `Cargo.toml` | `cargo` | Rust crates |
| Kubernetes manifests (`kind: Deployment/StatefulSet/DaemonSet`) | `kubernetes` | Image tags |

Print a coverage table before emitting output:

```
Detected dependency file types:
✅ github-actions  → .github/workflows/ (N files)
✅ terraform       → examples/**/*.tf (N files)
✅ helmv3          → examples/**/Chart.yaml (N files)
⚠️  npm            → package.json (1 file) — not covered in current renovate.json
ℹ️  cargo          → no Cargo.toml found — skipped
```

### Step 1.5 — Scan for internal source patterns

If `terraform` files were detected, grep them for non-public module sources and report findings:

```bash
# Internal GitHub module sources
grep -rh 'source\s*=' **/*.tf | grep 'github\.com/' | sort -u

# Private registry sources (not registry.terraform.io)
grep -rh 'source\s*=' **/*.tf | grep -v 'registry\.terraform\.io' | grep -v 'github\.com/' | grep -v '\.\/' | sort -u
```

Print:
```
Internal source patterns detected:
✅ github.com/myorg/terraform-aws-vpc//modules/vpc?ref=v2.1.0  → will add regex manager
ℹ️  registry.terraform.io/hashicorp/aws  → standard registry, no regex needed
⚠️  ./modules/networking  → local path, skipped
```

If private sources are found but Q5 was not yet answered, ask it now.

---

### Step 2 — Emit renovate.json

Generate a `renovate.json` containing only the managers detected in Step 1.

**`extends` — choose based on Q2 (pinning strategy):**

- `digest` pinning chosen → use `config:best-practices` (includes GitHub Actions + Docker digest pinning, malware age window, abandoned package alerts)
- `semver` pinning chosen → use `config:recommended` + `":separateMajorReleases"`

**`schedule` — set based on Q4:**
- `weekday-morning` → `["before 6am every weekday"]`
- `monday-morning`  → `["before 6am on monday"]`
- `weekend`         → `["before 6am on saturday and sunday"]`
- `always`          → omit the `schedule` key entirely

**Always include this base:**

```json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "<see extends rule above>",
    ":dependencyDashboard",
    ":semanticCommits",
    ":separateMajorReleases"
  ],
  "dependencyDashboard": true,
  "dependencyDashboardTitle": "Renovate Dependency Dashboard",
  "timezone": "<ask the user for their timezone, or detect from TZ env var / timedatectl>",
  "labels": ["dependencies", "renovate"],
  "prConcurrentLimit": 5,
  "prCreation": "not-pending",
  "rebaseWhen": "conflicted",
  "semanticCommits": "enabled",
  "minimumReleaseAge": "3 days",
  "osvVulnerabilityAlerts": true,
  "vulnerabilityAlerts": {
    "enabled": true,
    "labels": ["security"]
  },
  "ignorePaths": [
    "**/node_modules/**",
    "**/vendor/**",
    "**/.terraform/**",
    "**/charts/**"
  ]
}
```

**Per-detected-manager `packageRules` (include only detected managers):**

Apply automerge settings based on Q3:
- `patch-only`  → set `"matchUpdateTypes": ["patch"]` for automerge rules
- `minor-patch` → set `"matchUpdateTypes": ["minor", "patch"]` for automerge rules
- `none`        → set `"automerge": false` on all rules; omit `matchUpdateTypes` automerge entries

Apply `pinDigests` based on Q2:
- `digest` → add `"pinDigests": true` to `github-actions` and `docker`/`kubernetes` rules
- `semver` → omit `pinDigests`

`github-actions`:
```json
{
  "description": "GitHub Actions — pin to commit SHA for supply chain security",
  "matchManagers": ["github-actions"],
  "pinDigests": true,
  "automerge": false,
  "groupName": "GitHub Actions"
}
```
(remove `pinDigests` line if semver strategy chosen)

`terraform`:

> **WARNING — Terraform modules must never be automerged.** Module source updates change infrastructure blueprints and require human review. Add this rule to always override other automerge settings:
> ```json
> {
>   "packageRules": [
>     {
>       "matchManagers": ["terraform"],
>       "matchDepTypes": ["module"],
>       "automerge": false,
>       "labels": ["terraform-module", "requires-review"]
>     }
>   ]
> }
> ```
> Place this rule **last** in your `packageRules` array — later rules take precedence in Renovate.

```json
{
  "description": "Terraform providers — automerge minor/patch",
  "matchManagers": ["terraform"],
  "matchDepTypes": ["required_provider"],
  "groupName": "Terraform providers",
  "automerge": true,
  "automergeType": "pr",
  "minimumReleaseAge": "3 days",
  "matchUpdateTypes": ["minor", "patch"]
},
{
  "description": "Terraform modules — require review for all updates",
  "matchManagers": ["terraform"],
  "matchDepTypes": ["module"],
  "automerge": false,
  "groupName": "Terraform modules"
}
```

`helmv3`:
```json
{
  "description": "Helm charts — automerge patch only",
  "matchManagers": ["helmv3"],
  "automerge": true,
  "automergeType": "pr",
  "minimumReleaseAge": "3 days",
  "matchUpdateTypes": ["patch"]
}
```

`gomod`:
```json
{
  "description": "Go modules — automerge minor/patch",
  "matchManagers": ["gomod"],
  "automerge": true,
  "automergeType": "pr",
  "minimumReleaseAge": "3 days",
  "matchUpdateTypes": ["minor", "patch"],
  "groupName": "Go modules"
}
```

`npm`:
```json
{
  "description": "npm packages — automerge minor/patch",
  "matchManagers": ["npm"],
  "automerge": true,
  "automergeType": "pr",
  "minimumReleaseAge": "3 days",
  "matchUpdateTypes": ["minor", "patch"],
  "groupName": "npm packages"
}
```

`pip`:
```json
{
  "description": "Python packages — automerge minor/patch",
  "matchManagers": ["pip_requirements", "pip-compile", "pipenv", "poetry"],
  "automerge": true,
  "automergeType": "pr",
  "minimumReleaseAge": "3 days",
  "matchUpdateTypes": ["minor", "patch"],
  "groupName": "Python packages"
}
```

`docker`:
```json
{
  "description": "Container images — pin digests, require review for all updates",
  "matchManagers": ["docker-compose", "dockerfile"],
  "pinDigests": true,
  "automerge": false,
  "groupName": "Container images"
}
```
(remove `pinDigests` line if semver strategy chosen)

`kubernetes`:
```json
{
  "description": "Kubernetes container images — require review for all updates",
  "matchManagers": ["kubernetes"],
  "automerge": false,
  "groupName": "Kubernetes images"
}
```

`cargo`:
```json
{
  "description": "Rust crates — automerge minor/patch",
  "matchManagers": ["cargo"],
  "automerge": true,
  "automergeType": "pr",
  "minimumReleaseAge": "3 days",
  "matchUpdateTypes": ["minor", "patch"],
  "groupName": "Rust crates"
}
```

**`postUpdateOptions`** — include only for detected ecosystems:
- `gomod` detected → add `"gomodTidy"`
- `npm` detected → add `"npmDedupe"`

**`customManagers`** — include if `.github/workflows/*.yml` contains `terraform_version:`:
```json
{
  "customManagers": [
    {
      "customType": "regex",
      "description": "Update Terraform version pinned in GitHub Actions workflows",
      "managerFilePatterns": ["/^\\.github/workflows/.*\\.ya?ml$/"],
      "matchStrings": [
        "terraform_version:\\s*['\"]?(?<currentValue>[^'\"\\s]+)['\"]?"
      ],
      "depNameTemplate": "hashicorp/terraform",
      "datasourceTemplate": "github-releases",
      "extractVersionTemplate": "^v(?<version>.*)$"
    }
  ]
}
```

### Step 2.5 — Private registries and custom regex managers

Emit the following sections only for the options chosen in Q5–Q7. Omit any section for which the user answered "no" or skipped.

---

#### Q5 — Internal Terraform module sources

Both source forms below are extracted natively by the `terraform` manager — no `customManagers` entry is needed for either.

**Option A — GitHub org (`github-org`, org = `<org>`):**

`source = "github.com/<org>/<repo>//<path>?ref=<tag>"` is parsed under the `github-tags` datasource, with `packageName` set to `<org>/<repo>`.

Add to `packageRules`:
```json
{
  "description": "Terraform modules from <org> GitHub org — require manual review",
  "matchManagers": ["terraform"],
  "matchPackageNames": ["/^<org>\\//"],
  "automerge": false,
  "groupName": "Internal Terraform modules (<org>)"
}
```

**Option B — Private Terraform registry (`private-registry`, host = `<hostname>`):**

`source = "<hostname>/<namespace>/<module>/<provider>"` with a `version = "..."` attribute is parsed under the `terraform-module` datasource for any hostname — only a `hostRules` entry for authentication is needed.

Add to `hostRules`:
```json
{
  "matchHost": "<hostname>",
  "token": "{{ env.TF_REGISTRY_TOKEN }}"
}
```

> Set `TF_REGISTRY_TOKEN` in your Renovate bot's environment (GitHub Actions secret or Renovate App config).

---

#### Q6 — Private Helm registry

**OCI registry (`oci`, host = `<host>`):**

Add to `hostRules`:
```json
{
  "matchHost": "<host>",
  "hostType": "docker",
  "username": "{{ env.HELM_REGISTRY_USERNAME }}",
  "password": "{{ env.HELM_REGISTRY_PASSWORD }}"
}
```

Add to `packageRules`:
```json
{
  "description": "Helm charts from private OCI registry oci://<host>",
  "matchManagers": ["helmv3"],
  "registryUrls": ["oci://<host>"],
  "automerge": false,
  "groupName": "Private Helm charts (<host>)"
}
```

**HTTP registry (`http`, host = `<host>`):**

Add to `hostRules`:
```json
{
  "matchHost": "<host>",
  "username": "{{ env.HELM_REGISTRY_USERNAME }}",
  "password": "{{ env.HELM_REGISTRY_PASSWORD }}"
}
```

Add to `packageRules`:
```json
{
  "description": "Helm charts from private HTTP registry https://<host>",
  "matchManagers": ["helmv3"],
  "registryUrls": ["https://<host>"],
  "automerge": false,
  "groupName": "Private Helm charts (<host>)"
}
```

> Set `HELM_REGISTRY_USERNAME` and `HELM_REGISTRY_PASSWORD` in your Renovate bot's environment.

---

#### Q7 — Private container image registry

For each registry type selected, add the corresponding `hostRules` entry. Collect all entries into a single `"hostRules": [...]` array in the final `renovate.json`.

**AWS ECR (`ecr`, host = `<account>.dkr.ecr.<region>.amazonaws.com`):**
```json
{
  "matchHost": "<account>.dkr.ecr.<region>.amazonaws.com",
  "hostType": "docker",
  "username": "AWS",
  "password": "{{ env.AWS_ECR_TOKEN }}"
}
```
> Recommended: use the Renovate App with an IAM role instead of a static token. For self-hosted Renovate, pre-authenticate with `aws ecr get-login-password` and expose as `AWS_ECR_TOKEN`.

**Google GCR / Artifact Registry (`gcr`, host = `gcr.io` or `<region>-docker.pkg.dev`):**
```json
{
  "matchHost": "gcr.io",
  "hostType": "docker",
  "username": "_json_key",
  "password": "{{ env.GCR_SERVICE_ACCOUNT_KEY }}"
}
```
For Artifact Registry replace `gcr.io` with `<region>-docker.pkg.dev`.

**Azure ACR (`acr`, host = `<registry>.azurecr.io`):**
```json
{
  "matchHost": "<registry>.azurecr.io",
  "hostType": "docker",
  "username": "{{ env.ACR_CLIENT_ID }}",
  "password": "{{ env.ACR_CLIENT_SECRET }}"
}
```
> Recommended: use a service principal with `AcrPull` role. Set `ACR_CLIENT_ID` and `ACR_CLIENT_SECRET` in Renovate's environment.

**Harbor / self-hosted (`harbor`, host = `<registry-host>`):**
```json
{
  "matchHost": "<registry-host>",
  "hostType": "docker",
  "username": "{{ env.REGISTRY_USERNAME }}",
  "password": "{{ env.REGISTRY_PASSWORD }}"
}
```

After collecting all private registry `hostRules`, also add a `packageRules` entry to group image updates per registry:
```json
{
  "description": "Container images from private registry <registry-host>",
  "matchManagers": ["dockerfile", "docker-compose", "kubernetes"],
  "matchPackageNames": ["/^<registry-host>\\//"],
  "automerge": false,
  "groupName": "Private images (<registry-host>)"
}
```

---

#### Assembly

Collect all objects from Steps 2 and 2.5 into the final `renovate.json` following this structure:

```json
{
  "$schema": "...",
  "extends": [...],
  ... base config keys ...,
  "hostRules": [ ... one entry per private registry ... ],
  "customManagers": [ ... one entry per custom source pattern ... ],
  "packageRules": [ ... all per-manager + private registry rules ... ]
}
```

Omit `hostRules` if no private registries were configured. Omit `customManagers` if no custom source patterns apply.

Collect all per-detected-manager objects into a single `"packageRules": [...]` array in the final `renovate.json`. Do not emit them as separate JSON blocks.

### Step 3 — Write or diff

- If `renovate.json` already exists: show only the lines that would change. Ask: `Write to renovate.json? [y/N]`
- If no existing file: write directly and print `✅ renovate.json written.`

After writing, print:
```
Next: run /platform-skills:renovate precommit to add a local pre-commit validation hook.
```

---

## Mode: precommit

Reference: `references/renovate.md`

Emit a `.pre-commit-config.yaml` entry that validates `renovate.json` locally before every commit using the official `renovatebot/pre-commit-hooks`.

### Output

If `.pre-commit-config.yaml` already exists, append the renovate repo block if not already present. If the file does not exist, create it.

```yaml
repos:
  - repo: https://github.com/renovatebot/pre-commit-hooks
    rev: 43.150.0
    hooks:
      - id: renovate-config-validator
```

**Semver note:** `rev` tracks the Renovate release version (currently `43.150.0`). Renovate itself will keep this pinned version up to date automatically once Renovate is running on the repo — it treats pre-commit hook revs as a managed dependency.

After writing, print:
```
✅ .pre-commit-config.yaml written (or updated).

To activate the hook:
  pip install pre-commit        # or: brew install pre-commit
  pre-commit install
  pre-commit run renovate-config-validator --all-files

The hook runs renovate-config-validator before every commit that touches renovate.json.
```

---

## Mode: workflow

Reference: `references/renovate.md`

Emit a ready-to-use `.github/workflows/validate-renovate.yml` that validates `renovate.json` on every PR that modifies it. Uses only `GITHUB_TOKEN` — no secrets or tokens required.

### Output file: `.github/workflows/validate-renovate.yml`

```yaml
name: Validate Renovate Config

on:
  pull_request:
    paths:
      - 'renovate.json'

permissions:
  contents: read

jobs:
  validate-schema:
    name: Schema Validation
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2

      - name: Validate JSON syntax
        run: |
          if ! jq empty renovate.json; then
            echo "❌ renovate.json is not valid JSON"
            exit 1
          fi
          echo "✅ JSON syntax valid"

  validate-config:
    name: Config Validator
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2

      - name: Setup Node
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e  # v6.4.0
        with:
          node-version: '24'

      - name: Run renovate-config-validator
        run: |
          npx --package=renovate --yes renovate-config-validator renovate.json
          echo "✅ Config validation passed"

  validate-coverage:
    name: Coverage Scan
    runs-on: ubuntu-latest
    # Exposed so the summary job can distinguish "clean" from "warnings" —
    # the step itself exits 0 either way, so needs.*.result cannot tell them apart.
    outputs:
      uncovered_count: ${{ steps.coverage.outputs.uncovered_count }}
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2

      - name: Check manager coverage
        id: coverage
        run: |
          UNCOVERED=0

          check_coverage() {
            local label="$1"
            local pattern="$2"
            local manager="$3"

            if find . -path "./.git" -prune -o -name "$pattern" -print | grep -q .; then
              if grep -q "\"$manager\"" renovate.json; then
                echo "✅ COVERED: $label ($manager)"
              else
                echo "⚠️  UNCOVERED: $label ($manager) — files found but manager not referenced in renovate.json"
                UNCOVERED=$((UNCOVERED + 1))
              fi
            else
              echo "ℹ️  SKIPPED: $label — no matching files found"
            fi
          }

          # Match both extensions — a repo using .yaml exclusively is still an
          # Actions repo, and matching only *.yml reports a false SKIPPED.
          if find .github/workflows \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null | grep -q .; then
            if grep -q '"github-actions"' renovate.json; then
              echo "✅ COVERED: GitHub Actions (github-actions)"
            else
              echo "⚠️  UNCOVERED: GitHub Actions (github-actions) — files found but manager not referenced in renovate.json"
              UNCOVERED=$((UNCOVERED + 1))
            fi
          else
            echo "ℹ️  SKIPPED: GitHub Actions — no matching files found"
          fi

          check_coverage "Terraform"  "*.tf"               "terraform"
          check_coverage "Helm"       "Chart.yaml"         "helmv3"
          check_coverage "Go modules" "go.mod"             "gomod"
          check_coverage "npm"        "package.json"       "npm"
          check_coverage "Python"     "requirements*.txt"  "pip_requirements"
          check_coverage "Docker"     "Dockerfile"         "dockerfile"
          check_coverage "Rust"       "Cargo.toml"         "cargo"

          if [ "$UNCOVERED" -gt 0 ]; then
            echo ""
            echo "⚠️  $UNCOVERED ecosystem(s) detected but not covered by Renovate"
            echo "Run /platform-skills:renovate generate to update your renovate.json"
          else
            echo "✅ All detected ecosystems are covered"
          fi

          # Publish the count so the summary job can surface it. Coverage gaps are
          # advisory — the step stays exit 0 so a gap never blocks the PR.
          echo "uncovered_count=$UNCOVERED" >> "$GITHUB_OUTPUT"

  summary:
    name: Validation Summary
    runs-on: ubuntu-latest
    needs: [validate-schema, validate-config, validate-coverage]
    if: always()
    steps:
      - name: Post summary
        env:
          SCHEMA_RESULT: ${{ needs.validate-schema.result }}
          CONFIG_RESULT: ${{ needs.validate-config.result }}
          COVERAGE_RESULT: ${{ needs.validate-coverage.result }}
          UNCOVERED_COUNT: ${{ needs.validate-coverage.outputs.uncovered_count }}
        run: |
          status() { [ "$1" = "success" ] && echo "✅ Passed" || echo "❌ Failed"; }

          # A green validate-coverage job only means the scan ran. Read the count
          # to tell "all ecosystems covered" apart from "gaps found but advisory".
          if [ "$COVERAGE_RESULT" != "success" ]; then
            COVERAGE_CELL="❌ Failed"
          elif [ "${UNCOVERED_COUNT:-0}" -gt 0 ]; then
            COVERAGE_CELL="⚠️ ${UNCOVERED_COUNT} uncovered — see Coverage Scan log"
          else
            COVERAGE_CELL="✅ Passed"
          fi

          {
            echo "## Renovate Config Validation"
            printf "| Check | Status |\n|-------|--------|\n"
            printf "| JSON Syntax | %s |\n" "$(status "$SCHEMA_RESULT")"
            printf "| Config Validator | %s |\n" "$(status "$CONFIG_RESULT")"
            printf "| Coverage Scan | %s |\n" "$COVERAGE_CELL"
          } >> "$GITHUB_STEP_SUMMARY"

      - name: Enforce required checks
        env:
          SCHEMA_RESULT: ${{ needs.validate-schema.result }}
          CONFIG_RESULT: ${{ needs.validate-config.result }}
        run: |
          if [ "$SCHEMA_RESULT" != "success" ] || [ "$CONFIG_RESULT" != "success" ]; then
            echo "❌ Required validation checks failed — see jobs above"
            exit 1
          fi
          echo "✅ All required checks passed"
```

After writing: check if `.github/workflows/validate-renovate.yml` already exists — if so, show diff and ask to overwrite. Then print:

```
✅ Workflow written to .github/workflows/validate-renovate.yml

Next steps:
  git add .github/workflows/validate-renovate.yml
  git commit -m "ci: add renovate.json validation workflow"
  git push

The workflow fires automatically on any PR that modifies renovate.json.
```

---

## Mode: all

Run the three modes in sequence using the answers from the wizard:

1. **generate** — scan repo, ask Q2–Q4, emit `renovate.json`
2. **precommit** — emit or update `.pre-commit-config.yaml`
3. **workflow** — emit `.github/workflows/validate-renovate.yml`

After all three complete, print a single consolidated next-steps block:

```
✅ Renovate setup complete

Files written:
  renovate.json
  .pre-commit-config.yaml
  .github/workflows/validate-renovate.yml

Activate the pre-commit hook:
  pip install pre-commit && pre-commit install

Commit everything:
  git add renovate.json .pre-commit-config.yaml .github/workflows/validate-renovate.yml
  git commit -m "feat: add Renovate dependency automation"
  git push
```