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

MGS1029: source glob reaches into a pruned directory

A target declares a source pattern whose directory prefix passes through a tree magus prunes, so it matches nothing and keys nothing.

[MGS1029] docs: source glob "proto/gen/*.binpb" can never match: the expansion
walk prunes "gen"

Why

The walk that builds a cache key skips four directory names wholesale, from project.IgnoreDirs:

directory why it is pruned
gen generated code: machine-written, never a discoverable project
vendor Go vendored dependencies
node_modules pnpm/npm dependencies
target Rust build artifacts

Pruning them is what keeps a broad pattern honest. A project declaring **/*.js means its own sources; without the prune it would hash every file in node_modules and key on a dependency tree nobody edits.

The cost is that a pattern aimed at one of those directories can never match. It is not an error at authoring time and not an error at run time: the glob is well-formed, it describes files the walk does not visit. The target then replays from cache while the files it named change underneath it.

This is the input-side twin of MGS1014, which catches a declared output that no run produces. Same shape: a declaration and reality quietly disconnected.

What it is not

An exact path is fine, and is not reported:

ctx.readsFiles(workspace.file("proto/gen/descriptor.binpb"));   // works
ctx.readsFiles(workspace.file("proto/gen/*.binpb"));            // MGS1029

A wildcard-free declaration names one file, so magus resolves it by stat rather than by the walk. It reaches the cache key normally even from inside a pruned tree. Only a pattern is unmatchable, because only a pattern could expand to swallow a dependency tree.

Fix

Name the file exactly, if you meant one file:

ctx.readsFiles(workspace.file("proto/gen/descriptor.binpb"));

Or declare the real sources instead of the generated artifact. A generated tree is downstream of something you own, and keying on that something is both narrower and more honest:

// not proto/gen/*.binpb - the schema the descriptor is built from
ctx.readsFiles(workspace.file("proto/magus/**/*.proto"));

If the files genuinely live under a pruned name and are genuinely sources, rename the directory. gen, vendor, node_modules and target carry a meaning across every magus workspace, and a hand-written source tree under one of those names surprises more than this check.

Confirming the fix

Ask what the cache key contains:

magus describe target <target> <project> --cache --inputs

Every hashed file appears as a src: line. If a file you declared is missing from that list, it is not in the key, whatever describe target reports under sources:.

MGS1029magusfilesourcescacheglobs
Last updated (353014be)
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. Workspace in full.

Project

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

Magusfile

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

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. Target in full.

Module

A magus stdlib namespace a magusfile imports for host capabilities: filesystem, exec, vcs, and more. Module in full.

Buzz

The language magusfiles are written in (the .buzz engine). Buzz in full.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. Cache in full.

CI

An ordinary target you compose yourself with ctx.needs; magus does not hardcode its stages. It is the anchor magus affected ci keys off, and ci strips the rw charm. CI in full.

Run

One target executing under one magus invocation, such as magus run test web or magus affected ci. A run keeps its captured output behind an output reference. Every magus run is a run whether or not any job asked for it; see Job for how the two relate. Run in full.

Conventions

This page uses none of the site's convention markers. The full set is on the conventions page.