myos export prints every setting the requested stacks declare, in one process:
68 variables in half a second, where asking for them one at a time costs a
process each and about ten seconds.
The shim reads that once and includes it, so a .mk target sees
HOST_FABIO_VERSION and the computed fabio tag without make knowing anything
about how they are produced. --make quotes the values so make neither expands
a $ nor starts a comment at a #.
- the wrapper followed one absolute symlink only, and fed the config file to
env(1) as-is, so a comment or a blank line in /etc/conf.d/myos made every
command fail with 'env: #comment: No such file or directory'
- verlt called verlte, which does not exist, and used return outside a
function: it never compared anything
- stat asked for the access time on macOS and the modification time elsewhere,
so newer/older did not mean the same thing depending on the machine
- setup-docker-group called ansible-user-add-groups, removed with ansible, then
announced that the user had been added to the docker group. It now runs
usermod or addgroup, and says so when neither works
- the ssh targets looped over AWS_INSTANCE_IP, which nothing defines since
make/apps/aws was dropped: they exited 0 having done nothing. They take
SSH_HOSTS and fail when it is empty
- patsublist left a space before the comma joining two fabio routes
- the JWT macro splits a payload on its commas; documented at the macro, since
fixing it would change every key it has already produced
myos build up logs host/fabio runs the three in order and stops at the first
failure, the way make build up logs STACK=host/fabio did. Leading words that
name commands are commands; the first word that is not one starts the stacks.
A stack found in several directories of the stack path is now merged rather
than shadowed, least specific first, so a project drops
stack/postgres/postgres.local.yml next to the catalogue's postgres.yml and
refines it. Settings hooks follow the same order, so a project can redefine a
default the catalogue ships. Neither engine did this before: the project
directory simply hid the catalogue one.
An unknown command now says so and suggests the command to type, instead of
printing the whole usage.
Two mechanisms, matching what the make engine actually did:
Lazy defaults. A stack setting is a function myos_default_<VAR>, called only
when the variable has no value, and called again at every reference. That is
exactly a recursive ?=: an explicit value wins, and the default follows a
DOMAIN that a .env changes later. The prefix is what makes it safe; the first
version used a bare function named after the variable, and the test suite
caught it running /usr/bin/host for a stack group called host.
Templates. myos env-update fills a .env from the .env.dist files, expanding
${VAR} against the current values and running $(command), forward references
included.
Also fixed: the project .env now wins over /etc/conf.d/myos, which is what the
documentation claimed and the code did not.
share/make/shim.mk lets a project keep make as a front end: every myos command
becomes a target that shells out to bin/myos, and the project keeps its own
targets and its stack .mk files. It sits outside make/ because the legacy
engine globs every .mk in there.
The skill documents how to use myos: what to check before touching a host
stack, how a reference resolves, what the three kinds of stack mean, and where
each failure comes from. Every command it shows was run against the fixtures
before being written down, which is how exec, run and scale turned out to be
missing.
AGENTS.md covers the other side: how to change myos without breaking the
deployments that already run it.
Both engines now go through the same 68 cases. 31 produce byte-identical
output; the rest have a recorded CLI expectation and a reason in DELTAS.md,
the bulk of it being that the CLI does not shell out to a recursive make and
calls compose once per project.
Fixed while comparing: the CLI was missing the COMPOSE_FILE_* defaults, so
overlays such as supabase.labels.yml would not have been loaded at all.
- spec/golden/cases.txt: 69 black-box cases (CLI wrapper mode and make
include mode) run in a hermetic sandbox with a mocked docker
- spec/golden/expected/*.txt recorded from this engine (baseline)
- make test / test-golden / golden-record / lint dev targets