Skip to main content

ci-dedupe

Audit GitHub Actions workflows for duplicated step sequences across jobs/files and recommend extracting local composite actions under .github/actions/ to simplify and factorize the YAML. Use when workflows grow repetitive, before adding a new job that copies an existing step block, or when asked to simplify/factorize GitHub workflows.

Source facts

Repository
cosmian/kms
Last source activity
October 11, 2026 at 09:46
Detected SKILL.md language
English
Stars
372
Forks
43

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
ci-dedupe
description
Audit GitHub Actions workflows for duplicated step sequences across jobs/files and recommend extracting local composite actions under .github/actions/ to simplify and factorize the YAML. Use when workflows grow repetitive, before adding a new job that copies an existing step block, or when asked to simplify/factorize GitHub workflows.
# CI Action Deduplication (Composite Action Extraction) Find repeated step blocks across `.github/workflows/*.yml`, check whether an existing `.github/actions/*` composite action already covers them, and recommend/extract new composite actions for blocks that don't. ## This Codebase's Existing Composite Actions `.github/actions/<name>/action.yml`, `runs: using: composite`, invoked as `uses: ./.github/actions/<name>`: - `cleanup-runner` — frees GH-hosted runner disk space (no inputs) - `setup-nix` — installs Nix + warms the nix store (`extra-nix-config` input) - `install-mise` — installs `mise` for Linux/macOS (no inputs) - `docker-login-ghcr` — logs in to `ghcr.io` (`github-token` input) ## Step 1 — Measure First ```bash # Find candidate duplicate step blocks: same `uses:`/`name:` repeated across files rg -n "^\s*- (name:|uses:)" .github/workflows | sort | uniq -c | sort -rn | head -40 # Find every call site of each existing composite action (confirm reuse opportunities, not just dupes) rg -n "uses: \./\.github/actions/" .github/workflows # Spot workflows that hand-roll a step an existing composite action already covers, # or repeat a third-party action block (e.g. docker/login-action, actions/checkout) # with identical `with:` values across files rg -n "uses: docker/login-action|uses: actions/checkout@v7" .github/workflows -A4 ``` Classify every exact-duplicate block (3+ lines, byte-identical `with:` values except matrix/job-scoped variables) found in 2+ places: - **Reuse candidate**: an existing `.github/actions/*` already implements this — replace the inline block with `uses: ./.github/actions/<name>`. - **Extraction candidate**: no existing action covers it, and the block appears verbatim (or with only secrets/inputs varying) in **3 or more** call sites — propose a new `.github/actions/<name>/action.yml`. - **Leave inline**: appears in <3 places, or the steps genuinely differ per call site (different `with:` values driven by real per-job logic, not copy-paste) — note it, don't extract. ## Step 2 — Apply Guardrails 1. **Secrets context is unavailable inside composite actions** ([actions/runner ADR 1144](https://github.com/actions/runner/blob/main/docs/adrs/1144-composite-actions.md)) — any `${{ secrets.X }}` used inside the extracted block MUST become a required composite-action `input:`, passed explicitly from each call site's `with:`. `github.*`/`env.*`/`matrix.*` contexts remain directly usable inside the composite action. 2. **Step-level `if:` also cannot read `secrets` directly** (not listed in the `jobs.<job_id>.steps.if` context-availability table in GitHub's Contexts reference) — if a step needs to branch on whether a secret is set, the calling job must mirror it into `env:` first (`env: { FOO: ${{ secrets.FOO }} }`) and test `env.FOO != ''`. 3. **Don't collapse blocks that look identical but diverge in intent** — e.g. two `docker/login-action` blocks targeting different registries (`ghcr.io` vs `docker.io`) are different concerns; extract them as separate composite actions, never merge into one parameterized action unless every call site already passes the registry as a variable today. Likewise, a multi-step preamble (e.g. checkout + optional cleanup + optional nix + mise) where call sites genuinely vary which steps/inputs they include is not a clean extraction target until those call sites converge. 4. **Preserve every required check and matrix leg** — extraction must not change job/step names relied on by branch protection, nor silently drop a step only some call sites had (diff the resulting step list per call site against the original). 5. **No behavior change** — the composite action's steps must be the exact original steps in the exact original order; this is a structural refactor, not a logic change. 6. **Local composite actions need the repo checked out first** — `uses: ./.github/actions/<name>` resolves from the workspace, so any call site that runs before `actions/checkout` (or in a job with no checkout, e.g. a manifest-only job) cannot use it. Leave those call sites inline, or add a checkout only if the job's behavior is otherwise unaffected. ## Step 3 — Propose Candidates Rank by occurrence count × lines saved. For each candidate, state: exact call-site line ranges (file:line-line), the proposed composite action name/path, its `inputs:` (one row per secret/variable that was inline before), and the exact replacement step for every call site. ## Step 4 — Verify - `python3 -c "import yaml,glob; [yaml.safe_load(open(f)) for f in glob.glob('.github/workflows/*.yml')+glob.glob('.github/actions/*/action.yml')]"` — every touched file still parses. - `rg -n "<the original duplicated uses:/with: block>" .github/workflows` — zero remaining matches outside the new composite action's own `action.yml`. - If `actionlint` is available on the host, run it; otherwise state static-only and that `actionlint` was not run. - No live workflow run is required for a pure step-extraction (behavior-preserving) refactor; only run `gh workflow run <file> --ref <branch>` when the extraction changes conditionals/inputs beyond a literal step move. ## Required Output 1. **Reuse opportunities** — inline blocks that should become `uses: ./.github/actions/<existing>`. 2. **Extraction candidates** — ranked list of new composite actions to create, with occurrence count and exact call sites. 3. **Recommended minimal first step** — the single lowest-risk, highest-count-identical-block candidate to extract first. 4. **Not extracted** — blocks that looked similar but diverge in intent (Guardrail 3), left inline on purpose.
View on GitHub