Skip to content

Castle ignores RELDIR and sasl releases_dir when deriving the releases directory #23

Description

@ausimian

Castle derives the releases directory as Path.join(to_string(:code.root_dir()), "releases") (lib/castle.ex, rel_dir/0, with rel_vsn_dir/1 built on it).

:release_handler does not. Its init/1 takes the releases directory from
application:get_env(sasl, releases_dir), falling back to os:getenv("RELDIR"),
and only then to init:get_argument(root) — OTP's own comment says "Default is
$ROOT/releases. $RELDIR overrides, and {sasl, releases_dir} overrides both."

Mix sets neither, so on an ordinary Mix release the two agree and nothing is
wrong today. A deployment that sets either — both are documented OTP
configuration — makes them disagree, and Castle then works on files the handler
will not read:

  • make_releases/0 writes RELEASES under code:root_dir(), while the
    handler looks under the configured directory. The file appears and is never
    read.
  • Worse, the remedy the record check names becomes false. With no RELEASES
    where the handler looks, it synthesises a record whose application list is
    empty; Castle.Commands.ensure_upgradable/2 correctly refuses, and its message
    says to restart the system because "the release creates the file before it
    starts". On such a deployment the restart writes the file to the same wrong
    place, so the refusal is permanent and its stated remedy never works.
  • materialise/3 resolves releases/<vsn>/sys.config the same way, so the
    target's configuration is written where the handler will not look for it
    either. The env.sh hook also re-runs the preboot VM on every start, since the
    file it checks for never appears.

What to do

Derive the releases directory the way init/1 does — {sasl, releases_dir},
then RELDIR, then <root>/releases — in one place, and build rel_vsn_dir/1
on it. That is a handful of lines and it makes Castle agree with OTP exactly
where it currently guesses.

Worth a test that the precedence is the same as OTP's, including that
{sasl, releases_dir} beats RELDIR, since getting the order backwards would be
invisible on any deployment that sets only one.

Why it is filed rather than fixed alongside the ERTS guard

Found while correcting the ERTS guard's documentation, which had asserted that
releases/RELEASES necessarily resolves under code:root_dir(). That assertion
is what this issue is really about: it is true for the default configuration and
false under configuration OTP documents. The guard's own reasoning does not
depend on it — the guard rests on application extraction, lib/<app>-<vsn>
resolution and erts-<vsn> deletion, which are anchored to the root and are
not relocatable — so the wording was corrected there and the behaviour left
alone rather than growing that branch into this.

Does not gate 1.0.0: it takes deliberate, non-Mix configuration to reach, and
AGENTS.md now records the limitation where the derivation lives.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions