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

MGS3005: tool too old

The binary exists, reports a version, and is usable - and that version is below the minimum declared for it.

[MGS3005] golangci-lint v1.9.0 is older than the supported range for
this project (min 2.0)

Its other side is MGS3006, for a version at or above a ceiling.

The fourth question about a tool

Each is distinct, and only the last two used to have answers:

question declared by code
does it exist? nothing - PATH lookup MGS3003
what version? Tool.probe -
is it usable now? Tool.ready MGS3004
is it in the supported window? Tool.supported MGS3005, MGS3006

Without a minimum, a too-old binary fails with whatever it says about an unrecognized flag - the same misleading failure MGS3004 exists to prevent, one question over.

Declaring one

export fun mgs_getTools() > {str: Tool} {
    return {
        "golangci-lint": Tool{
            probe = Command{bin = "golangci-lint", args = ["--version"]},
            supported = VersionBounds{min = "2.0"},
        },
    };
}

min is an inclusive floor and below is an exclusive ceiling. Both are plain versions, not a constraint range: a range language puts a syntax between the author and the two cases that matter, where the comma means AND, || means OR, and ^ and ~ mean materially different things in different ecosystems.

A partial version is read as you would say it out loud. min = "1.21" means 1.21.0, so 1.21.4 satisfies it.

A malformed bound is a load error rather than a silent pass - a window nobody can parse protects nobody.

A window needs a probe: there is nothing to compare without one.

Two owners, intersected

Tool.supported states what the spell's ops need to function: "go::test does not work below 1.21." A project states its own policy in its magusfile, under magus\project's tools key:

magus\project({
    "spells": [go],
    "tools": { "go": { "min": "1.26" } },
});

The two are intersected, narrower bound winning on each side, so neither can loosen the other. A spell cannot relax a version a repo has not qualified, and a repo cannot ask an op to run on a version it is known to break on.

The policy side is declared per project, not in magus.yaml, and that is not a filing preference. Config load merges a user-global tier beneath the workspace, so a bound written there could be set in one person's private file and silently gate every workspace on their machine. A magusfile is committed and is read by everyone who reads the project, and being per project it also lets console and docs hold different windows instead of sharing one workspace-wide map. Sharing a window is an explicit import of a shared module, never inheritance by position in the tree. magus.yaml has no tools key; a block written there is inert and reported only as an unknown-key warning. See workspace.

Resolution

Upgrade the binary to the floor or above, through whatever installs it for this repo - a version manager, the toolchain's own installer, the CI setup step.

If the floor is the wrong bound rather than the binary, edit the declaration that set it: Tool.supported in the spell, or tools in the project's own magus\project call. Which one is binding is not in the message, since the two are intersected before the comparison. magus describe tools prints the installed version, both declarations, and the effective window, so read that before editing either.

What it deliberately does not do

An unprobeable tool is not "too old." If the probe fails, the window passes. Failing there would make a bound a second way for a missing tool to break, with a worse message than MGS3003's.

Output magus cannot parse is not "too old" either. A tool whose version line carries no semver is left alone rather than judged against a window it was never going to satisfy.

The check runs once at the start of a run, over every project it touches, before any target executes. It reuses the version probes magus already performs to key the cache, so a declared window costs no extra forks.

It deliberately does NOT hang off op dispatch. Enforcement follows the DECLARATION - a window is stated per project - not the mechanism a target happens to use. A project whose targets shell out directly instead of dispatching a spell op is held to its window exactly like one that does; an earlier version checked at dispatch and silently gated nothing for those projects.

MGS3005versionboundssupportedtoolserrorsmagusfile
Last updated (d120b58e)
Earlier changes on this page (4)

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.

Op

A single tool-native command a target composes (long form: operation); the middle of the work hierarchy (Spell to Op 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.

Module

A magus stdlib namespace a magusfile imports for host capabilities: filesystem, exec, vcs, and more. See the module reference.

Buzz

The language magusfiles are written in (the .buzz engine). See engines.

Cache

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

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

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