Skip to content

ecosystem command ​

Manage a set of interconnected FoBiS projects: know their state, act across them in dependency order, release them as a train.

bash
fobis ecosystem <subcommand> [options]

This page is the reference: every subcommand, option, exit code and JSON field. For the concepts, a guided tour and recipes, read the Ecosystem guide.

Subcommands ​

GroupSubcommandPurposeWrites
Registrydiscoverlist unregistered FoBiS repositories under the root—
addregister projectsuser config
removeunregister projectsuser config
Knowgraphrelease order and dependency tree—
dashboardstate of every project (terminal, HTML, JSON)HTML file (--format html)
Actexecrun a command in every projectwhatever the command writes
fetchfobis fetch --update in every projectfetched dependencies, locks
checkbuild and test the dependents against a local working copytemporary copies only
scaffold statusscaffold drift across the projects—
scaffold syncshow or apply the scaffold changesmanaged files (--apply)
Releasereleasethe release traincommits, tags, pushes (via release.sh)

Every subcommand accepts --config/-c PATH to use another user configuration file than ~/.config/fobis/config.ini ($XDG_CONFIG_HOME/fobis/config.ini). The acting commands walk the projects in release order (dependencies first) and stream each command's output live. Colours are used only on a terminal and honour NO_COLOR.

Registry ​

The projects are listed in the [ecosystem] section of the user configuration:

ini
[ecosystem]
root     = ~/fortran
projects = PENF FACE BeFoR64 StringiFor FLAP
theme    = github
mode     = auto
KeyDefaultDescription
root—Directory the projects live in; ~ is expanded
projects—Directory names under root, or ~/absolute paths; whitespace or newline separated
themegithubPalette of the HTML dashboard (see Themes)
modeautoauto (follow the system), light or dark

A project is a git repository with a fobos file; its name is its directory name. A dependency in a project's fobos [dependencies] is an ecosystem dependency when its key, or the repository name of its URL, matches a registered project (case-insensitively); any other is reported as external.

discover ​

bash
fobis ecosystem discover [--root DIR]

List the git repositories with a fobos file directly under the root (no recursion) that are not registered, and print the fobis ecosystem add ... command that would register them. Read-only: pick the candidates you want, not necessarily all.

OptionDefaultDescription
-r, --root DIRthe configured rootDirectory to scan

Exit codes: 0; 1 when no root is configured or given, or it is not a directory.

add ​

bash
fobis ecosystem add PROJECT...

Register projects, given as names under the root or as paths. Each must be a git repository with a fobos file, and its directory name must not be taken by a registered project (the name identifies the project). An already registered project is reported and skipped. A project directly under root is written as its bare name, any other as a ~/... (or absolute) path.

Exit codes: 0; 1 when an argument is invalid — and then nothing is written.

remove ​

bash
fobis ecosystem remove PROJECT...

Unregister projects, given as names (case-insensitive) or paths. The repositories are not touched. The registered projects that still depend on a removed one are noted: it becomes an external dependency for them.

Exit codes: 0; 1 when an argument is not registered — and then nothing is written.

The configuration keeps its comments

add and remove rewrite only the projects key (creating the section, or the file, when missing); the rest of the file, comments included, is left as it is. The file is replaced atomically. Long lists wrap onto indented continuation lines.

Know ​

graph ​

bash
fobis ecosystem graph [--json]

Print the release order (dependencies first, grouped in levels) and the dependency tree. A project sits one level above its deepest dependency. A dependency cycle is reported, and its projects are left out of the order; repeated subtrees are marked (*).

OptionDescription
--jsonPrint the graph as JSON (schema)

Exit codes: 0; 1 when no project is registered.

dashboard ​

bash
fobis ecosystem dashboard [--format terminal|html] [--no-fetch] [--no-ci] [--json]

One row per project: git state, release state, CI, scaffold drift, dependency pins.

OptionDefaultDescription
--formatterminalterminal: coloured table. html: self-contained page written to a file and opened in the browser
-o, --output FILE~/.cache/fobis/ecosystem.htmlHTML file to write ($XDG_CACHE_HOME honoured)
--no-openoffWrite the HTML page without opening the browser
--theme NAMEconfig theme, else githubHTML palette (Themes)
--mode MODEconfig mode, else autoHTML mode: auto, light or dark
--no-fetchoffSkip git fetch in every project: faster and offline, but upstream, CI and pins reflect the last fetch
--no-cioffDo not query GitHub Actions (CI shows unknown)
-j, --jobs N8Projects examined concurrently
--jsonoffPrint the full status as JSON (schema)

Exit codes: 0; 1 when no project is registered; 2 for an unknown format, theme or mode (theme and mode are validated only when an HTML page is written).

Columns ​

ColumnValues
Branchchecked-out branch
Treeclean, or N changed (changed and untracked files)
Upstreamin sync, +ahead/-behind, no upstream
Version / tagthe VERSION file (else the literal fobos [project] version) / the latest reachable tag; highlighted when they differ
Unreleased0; N (bump -> next) when every commit is releasable; N, R to release (bump -> next); N, none to release
CIpassing, failing, running, none (no run for HEAD), not pushed (HEAD not on the remote), unknown (no gh, not authenticated, not on GitHub)
Driftscaffold-managed files not in sync, project-owned ([scaffold] skip) ones excluded; ? when it cannot be computed
Pins- (no ecosystem dependency), current, or the dependencies not at their head, with their state

CI is the latest run of every workflow for the HEAD commit (a re-run supersedes the previous one): failing when any concluded otherwise than success, skipped or neutral; running when any is not completed.

Pins compare the commit of each ecosystem dependency in the project's <deps_dir>/fobos.lock with that dependency's remote default-branch head: current, stale (lock behind the head), ahead (lock newer than the known head), diverged, unlocked (no lock entry), unknown (no remote head).

Release rule. A commit since the latest tag is releasable when it changes at least one file outside the release-excluded globs — default docs/* .github/* *.md scripts/*, fnmatch patterns on repository-relative paths where * also matches /; a project replaces them with fobos [ecosystem] release_exclude. The bump comes from the releasable commits only: a ! or a BREAKING CHANGE footer → major, feat → minor, anything else → patch.

Project state (the colour of the HTML diagram nodes), most severe first: error (unreadable project) → ci-failing → stale (a pin stale, diverged or unlocked) → unreleased (releasable commits) → ok.

Themes ​

themeLight variantDark variant
githubGitHub LightGitHub Dark
solarizedSolarized LightSolarized Dark
draculaAlucardDracula
nordSnow StormNord
tokyo-nightTokyo Night DayTokyo Night
catppuccinLatteMocha
gruvboxGruvbox LightGruvbox Dark
oneOne LightOne Dark
rose-pineRosé Pine DawnRosé Pine

The page's Theme and Mode selectors switch live; the choice is stored by the browser for that page and takes precedence over the generated default. Secondary, link and status text is darkened or lightened, keeping its hue, to a 4.5:1 contrast with the background. Without JavaScript the page falls back to the GitHub palette, following the system mode. The diagram is drawn by Mermaid, loaded from a CDN: without network the page still shows the table and the diagram source.

Act ​

Shared options:

OptionCommandsDescription
--only A,Bexec, fetch, release, scaffoldComma-separated projects to act on (still in release order)
--from NAMEexec, fetch, releaseStart from this project in release order: resume after a failure
--fail-fastexec, fetch, checkStop at the first failure; the rest are reported as not run

Every acting command ends with a summary table and exits 1 when a project failed; unknown project names exit 2.

Project  Result        Time  Detail
PENF     passed        0.0s
FLAP     failed        0.0s  exit 1

1 passed, 1 failed: FLAP
ResultMeaning
passedthe command succeeded
failednon-zero exit
crashedkilled by a signal (the detail names it, e.g. SIGSEGV)
aborteda release declined at its prompt (release)
skippednot run: unreadable project, nothing to do, missing dependency
not runafter a failure (--fail-fast, or any failure in release)

exec ​

bash
fobis ecosystem exec [--only A,B] [--from NAME] [--fail-fast] -- <command>

Run a command in every project's directory through bash -c, its output streaming live under a ==> PROJECT: command header. A single argument is a shell command line (-- 'fobis clean && fobis build'); several arguments form one command (-- git status -s). fobis and FoBiS.py in the command, and in the fobos rules it runs, resolve to the FoBiS running exec. Unreadable projects are skipped.

Exit codes: 0; 1 when a project failed or crashed; 2 without a command or for an unknown project.

fetch ​

bash
fobis ecosystem fetch [--only A,B] [--from NAME] [--fail-fast]

Run fobis fetch --update in every project with a [dependencies] section, dependencies first, so that each lock ends at the current heads. Projects without dependencies are reported as skipped.

Exit codes: 0; 1 when a fetch failed.

check ​

bash
fobis ecosystem check PROJECT [--keep] [--fail-fast]

Pre-release impact check: build and test every project that depends on PROJECT, directly or transitively, in release order, against PROJECT's local working copy. For each dependent:

  1. its working copy — tracked and untracked files, uncommitted changes included; ignored files (build products, fetched dependencies, node_modules) excluded — is copied to a temporary directory;
  2. its fetched dependencies are copied into the copy's deps_dir as they are, together with .deps_config.ini and fobos.lock — except PROJECT, copied from its local working copy;
  3. its check commands run there, from scratch.

The repositories are never touched. Check commands, in order of precedence:

SourceCommands
the dependent's fobos [ecosystem] checkone command per line, chained with &&
a [rule-makecoverage] in its fobosfobis rule --ex makecoverage
otherwisefobis build
OptionDescription
--keepKeep the staging directory even when every check passes
--fail-fastStop at the first failing dependent

The staging directory is removed after a full pass and kept, its path printed, otherwise. A dependent whose dependencies were never fetched is reported as skipped.

Exit codes: 0 when every dependent passed (or there is none); 1 otherwise, skipped dependents included; 2 for an unknown project.

scaffold status ​

bash
fobis ecosystem scaffold status [--only A,B] [--files GLOB]

List, project by project, the scaffold-managed files not in sync, and the project-owned ones ([scaffold] skip) as SKIPPED.

OptionDescription
--only A,BProjects to inspect
--files GLOBLimit to managed files matching this glob

Exit codes: 0 when every project is in sync; 1 when any drifts.

scaffold sync ​

bash
fobis ecosystem scaffold sync [--only A,B] [--files GLOB] [--apply [--yes]]

Show the scaffold changes project by project; with --apply, write them. Files listed in a project's [scaffold] skip are never written.

OptionDescription
--only A,BProjects to sync
--files GLOBLimit to managed files matching this glob
--applyWrite the changes (default: only show them)
-y, --yesWith --apply: do not ask for each project

With --apply each project's changes are confirmed (default no); when no answer is possible (no terminal) nothing is written and the command exits 1.

Release ​

release ​

bash
fobis ecosystem release [--dry-run] [--bump NAME=LEVEL|VERSION]... [--only A,B] [--from NAME]
                        [--yes] [--no-wait] [--timeout SEC] [--no-fetch]

The release train.

OptionDefaultDescription
--dry-runoffShow the plan and stop
--bump NAME=VALUE—Override a bump: major, minor, patch, or an explicit vX.Y.Z (repeatable); also adds a project with nothing releasable
--only A,BallProjects to consider
--from NAME—Start from this project in release order
-y, --yesoffApprove the plan and answer every release.sh prompt
--no-waitoffDo not wait for the GitHub releases
--timeout SEC900Seconds to wait for each GitHub release
--no-fetchoffPlan without git fetch (not recommended)
  1. Plan: the projects with releasable commits (see the release rule above), in release order, each with the version it will get — computed from the latest tag and passed explicitly to release.sh.

  2. Pre-flight for every planned project; any blocker refuses the whole train:

    BlockersWarnings
    no executable scripts/release.shunpushed commits
    git-cliff not foundCI running, none or unknown
    not on the trunk branch (the remote default branch)not fetched
    uncommitted changesnot on GitHub
    behind the upstream; no upstream
    CI failing at HEAD
    no previous tag and no explicit version
    version not above the current tag; tag already exists
  3. Confirmation of the plan (default no; no answer possible means no).

  4. For each project: scripts/release.sh vX.Y.Z with the terminal's stdin, so that its own prompt reaches you (--yes answers it); the tag must then exist locally and on the remote — a declined prompt is reported as aborted; the GitHub release is awaited with gh release view; fobis fetch --update runs in the project's direct dependents.

  5. Stop at the first failure, printing the resume command fobis ecosystem release --from NAME [--bump ...]. Projects already released have nothing left to release, so they drop out of the plan.

Exit codes: 0 when every planned project is released, nothing needs a release, the plan is declined, or --dry-run finds no blocker; 1 when a project is blocked, the confirmation cannot be answered, or a release fails, aborts or is not published in time; 2 for an invalid --bump or an unknown project.

Per-project configuration ​

Keys a project can set in its own fobos:

ini
[ecosystem]
check           = fobis build --mode tests-gnu      ; `check` commands, one per line
                  ./scripts/run_tests.sh
release_exclude = docs/* .github/* *.md scripts/*   ; changes that call for no release

[scaffold]
skip = scripts/release.sh                           ; customised files never written by scaffold
SectionKeyUsed byDefault
[ecosystem]checkcheckfobis rule --ex makecoverage if defined, else fobis build
[ecosystem]release_excludedashboard, releasedocs/* .github/* *.md scripts/*
[scaffold]skipecosystem scaffold, the dashboard Drift column, plain fobis scaffoldnone

JSON output ​

Dashboard JSON ​

fobis ecosystem dashboard --json:

json
{
  "generated": "2026-10-03T13:39:00",
  "projects": [{ "name": "PENF", "state": "unreleased", "...": "fields below" }],
  "levels": [["PENF", "FACE"], ["BeFoR64", "FLAP"], ["StringiFor"]],
  "release_order": ["PENF", "FACE", "BeFoR64", "FLAP", "StringiFor"],
  "cycle": [],
  "warnings": []
}
Project fieldTypeDescription
name, path, slugstringproject name, absolute path, GitHub owner/repo ("" off GitHub)
branch, headstringchecked-out branch, HEAD commit
dirtyintchanged and untracked files
ahead, behindint | nullcommits against the upstream (null without one)
fetchedboolgit fetch succeeded in this run
version, tagstringVERSION file, latest reachable tag
unreleased, releasableintcommits since the tag; those calling for a release
bumpstring | nullsuggested bump (null when nothing to release)
next_versionstringthe version the bump leads to ("" when nothing to release)
pushedboolHEAD is on a remote branch
cistringpassing, failing, running, none, not pushed, unknown
driftint | nullscaffold files not in sync (null when unknown)
pinslist{dep, locked, head, state} per ecosystem dependency
errorslist of stringproblems met while reading the project, fetch failures included
statestringerror, ci-failing, stale, unreleased, ok

Graph JSON ​

fobis ecosystem graph --json prints {projects, levels, release_order, cycle, warnings}; every project carries name, path, slug, depends_on, dependents, external and error.