Skip to content

cli: -o cannot write to a destination its directory will not take #411

Description

@OmarAlJarrah

morphic compile <spec> -o /dev/null fails, and so does any destination whose directory will
not accept a new entry:

$ morphic compile spec.yaml -o /dev/null
morphic: create output "/dev/null": open /dev/.null.tmp22ade738246966dc: operation not permitted

replaceFile publishes by rename — it writes <dir>/.<base>.tmpXXXXXXXX beside the destination
and renames it over — so the destination's directory has to take a temp file. /dev refuses
one; so does a read-only directory holding a perfectly writable file, and a character device or
FIFO named directly.

This was the first of the two problems #308 raised. The second — that the resulting failure
overwrote the exit code the diagnostics had already earned — is fixed and #308 closed with it.
This one is separable and was deliberately not taken there, because the obvious fix is a worse
trade than it looks.

Why writing through is not simply the answer

Opening the destination and writing to it, rather than replacing the name, reverses a contract
replaceFile states and three tests pin:

  • A symlink at the destination is replaced by a regular file rather than followed, so its target
    keeps its old content.
  • Other hard links to the destination keep pointing at the old inode, and so keep the old bytes.
  • The swap is atomic: a reader either sees the whole previous document or the whole new one.

Each is a way of saying that -o replaces a name, not the bytes some other name also reaches.
Writing through honours the opposite rule, and picking it per-destination means the CLI behaves
one way for a regular file and another for a device.

There is also a bounded-time problem: open(fifo, O_WRONLY) on a reader-less FIFO does not
return, so -o some.fifo would hang rather than fail. Doing it safely needs O_NONBLOCK and an
EAGAIN retry loop.

What might be done instead

Worth deciding on its own terms rather than as a side effect:

  • Leave it, and rely on morphic validate (cli: add a validate subcommand #81), which removed -o /dev/null's main use as a
    lint gate. The current failure is at least loud and explains itself.
  • Special-case destinations that cannot be published by rename and write through to them, taking
    the contract split knowingly and solving the FIFO blocking.
  • Recognise -o /dev/null (or a --no-output flag) as "compile but discard", which serves the
    actual use without touching how real destinations are published.

Expected

A decision recorded, and whichever behaviour follows from it documented where a user asking "why
can't I write there?" lands — today that is replaceFile's doc comment and the README's CLI
section, both of which state the limitation but not what should become of it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions