Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

chorewright

Map your task runner's dependency graph — and fail CI on the cycles, typos, and dead ends hiding in it.

Every repo grows a task runner — a Makefile, a justfile, a pile of npm "scripts". And every one of them quietly rots: a target depends on a prerequisite that was renamed last month, two recipes drift into exact copies, a ci script runs npm run buld, and one fine day two targets end up depending on each other and make bails with "Circular dependency dropped." None of it shows up until someone runs the wrong target at the wrong time — usually in CI, usually on a Friday.

chorewright reads those task definitions statically — no execution, no network, no config — builds the dependency graph, and reports the structural problems before they ship.

$ chorewright

chorewright  make  ·  6 target(s), 5 edge(s)

● 2 blocker(s):
  Makefile:5   Dependency cycle: build → test → build
      ↳ Break the loop — one of these targets must not (transitively) depend on itself.
      [CW001]
  Makefile:13  Target 'release' depends on 'compil', which is not defined.
      ↳ Define target 'compil', or remove the stale prerequisite (if it was a file, it is missing).
      [CW002]

● 1 warning(s):
  Makefile:17  Targets 'lint', 'vet' share an identical recipe.
      ↳ Deduplicate — have 'vet' depend on 'lint' instead of repeating it.
      [CW003]

2 blocker · 1 warning · 0 info

Exit code 1 on a blocker, so it drops straight into a pre-commit hook or CI.

What it reads

Three of the most common task-runner dialects, auto-detected:

Format File Edges are…
Make Makefile / GNUmakefile prerequisites (target: dep1 dep2)
just justfile recipe dependencies (recipe: dep1 dep2)
npm package.json "scripts" npm run X / yarn X / pnpm run X found in a script body

Point it at a directory and it picks the first of Makefile, justfile, package.json it finds; or name a file and force the dialect with --format.

Rules

Rule Severity What it catches
CW001 blocker A dependency cycle among targets (a → b → a).
CW002 blocker A target references a prerequisite/dependency/script that does not exist — a typo'd or removed name. (For Make, a prerequisite that is an existing file on disk is fine.)
CW003 warning Two or more targets with an identical recipe body — duplication worth deduping.
CW004 info An unreferenced target: defined but never listed as anyone's dependency. Likely an intentional entrypoint, possibly dead. Shown only under --dead; never fails the build on its own.

Severity maps to the exit code: blockers fail, warnings fail only under --strict, info never fails.

Install

go install github.com/jay-tank/chorewright@latest

Or build from source: go build -o chorewright .

Usage

chorewright                      # auto-detect a task file in the current dir
chorewright ./build              # inspect a directory
chorewright --format npm .       # force the dialect
chorewright --dead               # also list unreferenced targets (CW004)
chorewright --dot | dot -Tsvg    # emit Graphviz DOT of the dependency graph
chorewright --json               # machine-readable output
chorewright --strict             # duplicate-recipe warnings fail too
chorewright --no-color           # plain output

In CI

- run: go run github.com/jay-tank/chorewright@latest .

Exit codes: 0 clean · 1 a blocker (CW001/CW002), or any finding under --strict · 2 usage error.

Limitations

chorewright is a static parser — it never executes make, just, npm, or your recipes, and it makes no network calls. That honesty has edges:

  • Make variable expansion and include are only partially handled. Target or prerequisite names computed from $(VAR) or pulled in via include may be missed, and pattern rules (%.o: %.c) are skipped rather than modeled.
  • The CW002 file-existence check for Make is best-effort, resolved against the working directory. A prerequisite that is a generated file not yet on disk can look "undefined."
  • npm run detection is textual. It finds npm run X / yarn X / pnpm run X written literally in a script body; it won't follow a script that reaches another through a shell variable, a wrapper, or &&-chained indirection.
  • justfile parsing covers common syntax, not 100% of just's grammar (parameters, attributes, and expressions in dependencies are simplified).

When in doubt it under-reports rather than inventing edges — a finding is meant to be real.

License

MIT © Jay Tank

About

A zero-config task-runner graph & dead-target finder. Parses Makefile, justfile and package.json scripts, builds the dependency graph, and fails CI on cycles (CW001), undefined targets (CW002), duplicate recipes (CW003) and - under --dead - unreferenced targets (CW004). Single Go binary.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages