magus v0.3.0 is out. See what's new
¶ View markdown source · ✎ Suggest an edit
3 min read

Affected

magus affected <target> runs a target for every project a version-control diff touched, and nothing else. On a large workspace this is the difference between testing five projects and testing five hundred. The man page is the flag reference; this page is the model behind it.

Design intent

  • The diff decides the scope. You name a target; the VCS diff picks the projects. You never maintain a list of "what to build for this change."
  • Correctness comes from the graph, not a guess. A project is affected if its own sources changed or if anything it depends on is affected. magus walks the full transitive closure, so a change deep in a shared library rebuilds every dependent, not just its direct neighbors.
  • The same set is inspectable and executable. The set you would run is the set --explain describes and --plan shards. One computation, several lenses.

What counts as affected

A project enters the affected set two ways:

  1. Direct. One of its source files (its spells' declared globs, plus the magusfile) appears in the diff.
  2. Transitive. A project it depends on is affected. Dependencies come from the two dependency mechanisms: a magus\needs edge, a project-level depends_on, or a cross-project target reference (folded into depends_on - see the fold).

The closure runs until it reaches a fixed point, so a chain A -> B -> C rebuilds C when A changes. magus affected --explain <project> prints the reason a project is in the set: the changed file, or the affected dependency that pulled it in.

Choosing the diff base

magus autodetects the VCS adapter from .git, .hg, or .jj at the workspace root. The diff is taken against a base ref: --base, else MAGUS_VCS_BASE_REF, else the adapter's built-in default (origin/main for git). Two escape hatches:

  • MAGUS_VCS_COMMAND / vcs.command_name pin or replace the VCS command.
  • MAGUS_VCS_ENABLED=false (or vcs.enabled: false) short-circuits detection and falls back to the full project set, labeled vcs disabled. Use it where no VCS is available (a release tarball, a fresh container) so a build still runs.

Forensic modes

Four flags reason about the affected set instead of executing the target:

  • --explain <project> - why a project is in the set (changed file or affected dep).
  • --graph - render the affected scope as a dependency graph (--depth caps it).
  • --plan - emit a provider-neutral JSON CI shard plan for the set. It always keys off the ci anchor, so a matrix job fans the affected work across shards.
  • --bisect <project> - drive VCS bisect using run history to find the commit that introduced a regression.

CI

magus affected ci is the workhorse of a monorepo pipeline: it runs the ci anchor for exactly the projects a pull request touched. Because magus affected never applies default_charms and RunCI strips the rw charm, an affected CI run is always read-only no matter how the workspace is configured. Fan out at scale with --plan feeding a shard matrix.

How the pipeline clones the repository decides whether any of this works. affected needs a reachable merge base, and a checkout that cannot supply one degrades to a full build without failing. CI checkout covers the provider recipes, why a blobless partial clone is the right default, and how magus deepens a shallow clone rather than silently rebuilding everything.

Watch integration

--stdin reads changed paths from a pipe instead of running a diff, so a file watcher can drive continuous rebuilds:

magus watch | magus affected --stdin build

magus watch --null pairs with --stdin --null for paths that may contain newlines. See tips for the continuous-build loop.

See also

  • ci - checking out a repository so the diff is computable and cheap.
  • dependencies - magus\needs versus depends_on, the edges the closure walks.
  • targets - the target grammar these edges resolve against.
  • operations - what a target dispatches to per project.
  • cache - why an affected-but-unchanged target still replays from cache.
affectedvcsgitchanged-filesdependency-graphcibisectwatch
Last updated (a170f9b2)
Earlier changes on this page (2)

Full history ↗ · Blame source ↗

Glossary

Workspace

The magus root directory that owns a set of projects and shared config; the unit magus operates over. See workspace.

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. See workspace.

Magusfile

The magusfile.buzz that declares a project's targets (as export funs) and binds its spells. See targets.

Target

A named operation (build, test, ...) you invoke with magus run <target>; it may compose a spell's tool-native operations and depend on other targets. See targets.

Operation

A single tool-native command a target composes; the middle of the work hierarchy (Spell to Operation to Target). See operations.

Spell

A language/runtime adapter (e.g. go, md) that maps generic targets onto a toolchain's real commands. See spells.

Charm

An execution modifier attached with : (lint:rw) that changes how a target runs, not which one; the built-in rw flips a check-only target to mutate in place, and ci always strips it. See charms.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. See cache.

Affected

The set of projects touched by a change; magus affected <target> runs a target only over them. See affected.

CI

An ordinary magusfile-defined target you compose yourself with magus\needs - magus does not hardcode its stages. Magus.RunCI treats it specially only in that it strips the rw charm, it is the anchor magus affected ci keys off, and a selected scope with no project declaring it is a load error rather than a silent no-op. See targets.

Conventions

Placeholders

Angle brackets mark a value you replace with your own - never type the brackets:

magus run <target>
magus completion <shell>    # e.g. bash, zsh, fish

<target>, <path>, <shell>, <name> and the like are stand-ins, not literal text.