Skip to content

Commit 5742bcc

Browse files
committed
docs: document the build-time tasks as castle.*
The README described build-time tooling as Castle's after it had moved to Forecastle in 0.3.0. The assumed fix was to correct it to say `forecastle.*`; forecastle#24 renamed the tasks back into the `castle.*` namespace instead, leaving them implemented in Forecastle, so the README is correct roughly as first written plus a sentence saying where they come from. So `mix forecastle.relup` becomes `mix castle.relup` in the README, in `customize/1`'s `@doc` and in the release-integration notes. The appup compiler is called out separately rather than folded in with it: it is named for what it does rather than for either package, stays `mix compile.appup`, and there is no `mix castle.appup` to infer. `RELEASE.md` carries the rename because Castle's consumers are the ones who run the task and nobody depends on Forecastle directly, so Forecastle's own breaking-change note reaches nobody who needs it. `mix.lock` moves to the Forecastle that has the rename, and with it forecastle#34's correction to Forecastle's own README - which claimed the appup compiler was named for Castle too, and so contradicted what this commit writes here. Without the bump the committed lock pinned e43f880, whose tree still defines `Mix.Tasks.Forecastle.Relup`, so a clean checkout would have had no `mix castle.relup` while every page here documented one. Verified from an empty `deps/` and `_build/`: `castle.relup` and `compile.appup` resolve, `forecastle.relup` and `castle.appup` do not. `customize/1` stops restating Forecastle's strategy rules. Three review rounds each corrected that paragraph and each correction was wrong in a case the next one found - `--restart` reads no appups, `auto` does not fall back for a missing owned appup, and an appup may itself ask for `restart_emulator`. A summary short enough for a `@doc` is wrong somewhere; a correct one is that task's `@moduledoc` copied into a repository that cannot see it change, which is the drift AGENTS.md already warns about. So it states the requirement and names `mix castle.relup` as the authority for which transitions need one. The Known limitations entry claiming the README is out of date goes with it, replaced by the limitation that is actually left. Forecastle's suite covers the task implementations, but its fixture takes Forecastle as a path dependency with `override: true`, so nothing anywhere runs the claim the README makes - that depending on Castle alone is enough to get them. The stale lock above is that gap producing a real defect with the suite green, so it is recorded rather than described as covered. Closes #9 Claude-Session: https://claude.ai/code/session_01RotroiBdbidiCRX3KqE3Dy
1 parent 04d05f9 commit 5742bcc

6 files changed

Lines changed: 60 additions & 17 deletions

File tree

AGENTS.md

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,7 +1197,7 @@ which is precisely what `customize/1` exists to prevent. Mix validates `:steps`
11971197
**It changes exactly one option.** What a consumer still has to declare by hand
11981198
is listed in the `@doc`, because the alternative is finding out from a failed
11991199
upgrade: the `:appup` project key with `compilers: Mix.compilers() ++ [:appup]`,
1200-
a relup from `mix forecastle.relup` left in the project root, and
1200+
a relup from `mix castle.relup` left in the project root, and
12011201
`include_executables_for: [:unix]` — Windows is unsupported and assembly only
12021202
warns — plus the optional `rel/env.sh.eex`. `:steps` is the one option whose
12031203
contents are Castle's business; the others are the project's own choices, and
@@ -1920,10 +1920,22 @@ the exit statuses `bin/castle` returns are asserted. None of it is measured here
19201920
refuses: `mix docs` catches a broken *reference*, and nothing at all catches a
19211921
true sentence that has stopped being true. Both are read against
19221922
`Castle.Commands` by hand.
1923-
- **The README is out of date.** It documents an `:appup` compiler and a
1924-
`mix castle.relup` task that moved to Forecastle in 0.3.0, the release
1925-
management commands it describes on `bin/<release>` now live on `bin/castle`,
1926-
and its integration section still tells a consumer to place
1927-
`Forecastle.pre_assemble/1` and `Forecastle.post_assemble/1` around
1928-
`:assemble` by hand, which is what `Castle.customize/1` replaced
1929-
([#9](https://github.com/ausimian/castle/issues/9)).
1923+
- **Nothing runs the README's build-time instructions the way a consumer would,
1924+
and the gap is narrower than "there is no test".** Forecastle's suite
1925+
exercises `mix castle.relup` and the `:appup` compiler thoroughly — but its
1926+
sample fixture takes Forecastle as a `path` dependency with `override: true`,
1927+
so what it establishes is that the implementations work, not the claim this
1928+
README actually makes: that depending on **Castle alone** is enough to get
1929+
them. Neither repository runs that flow, and the `override:` is deliberate
1930+
(Forecastle has to be testable without Castle's API), so it is not something
1931+
to fix by tidying the fixture.
1932+
1933+
That is not hypothetical. Closing
1934+
[#9](https://github.com/ausimian/castle/issues/9) documented
1935+
`mix castle.relup` here while `mix.lock` still pinned a Forecastle from before
1936+
[forecastle#24](https://github.com/ausimian/forecastle/issues/24) renamed it —
1937+
so a clean checkout honouring the committed lock had no such task, and the
1938+
whole suite stayed green. Advancing the pin is what made the documentation
1939+
true. Until a consumer-shaped fixture exists, the check is manual: read the
1940+
lock against Forecastle's task surface whenever either side of the pair
1941+
changes.

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,10 @@ the launcher setup Castle needs.
8080

8181
## Appups and relups
8282

83-
Write an appup for each application whose code changes during a hot upgrade.
84-
The appup file uses Erlang terms written in Elixir syntax:
83+
Write an appup for each application you own that has to be upgraded in place.
84+
Whether a given transition needs one depends on the strategy below and on which
85+
applications changed; `mix castle.relup` documents the rules. The appup file
86+
uses Erlang terms written in Elixir syntax:
8587

8688
```elixir
8789
{
@@ -98,7 +100,7 @@ The appup file uses Erlang terms written in Elixir syntax:
98100
Generate a relup between assembled releases:
99101

100102
```shell
101-
mix forecastle.relup \
103+
mix castle.relup \
102104
--target _build/prod/rel/my_app/releases/1.1.0/my_app \
103105
--fromto _build/prod/rel/my_app/releases/1.0.0/my_app
104106
```
@@ -107,6 +109,14 @@ The task writes `relup` to the project root by default. Leave it there for the
107109
next release build to package. Use `--hot` to require a hot transition or
108110
`--restart` to force a one-stage emulator restart.
109111

112+
`mix castle.relup` is implemented in Forecastle, which Castle brings in as a
113+
build-time dependency. It is named for Castle because that is the package a
114+
project depends on, and nothing has to be added to `deps` to get it.
115+
116+
The appup compiler comes from Forecastle the same way, but is named for what it
117+
does rather than for either package: it stays `mix compile.appup`, reached
118+
through the `:compilers` list above. There is no `mix castle.appup`.
119+
110120
## Managing releases
111121

112122
Build the new release, then copy `<name>-<vsn>.tar.gz` into the running

RELEASE.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ Forecastle 1.x and Elixir 1.18 or later.
2121

2222
### Changed
2323

24+
- `mix forecastle.relup` is now `mix castle.relup`, and the README documents it
25+
as Castle's while naming Forecastle as its implementer. There is no
26+
compatibility alias, so a build pipeline calling the old name has to be
27+
updated. Nothing changes in `deps` — Castle already brings Forecastle in at
28+
build time, and which half implements a task is a packaging decision rather
29+
than something a consumer should have to learn. The appup compiler is **not**
30+
renamed and is unaffected: it stays `mix compile.appup`, named by its
31+
`:compilers` entry rather than by either package.
32+
([forecastle#24](https://github.com/ausimian/forecastle/issues/24))
2433
- Release-management commands now raise on refusal or a returned OTP error, so
2534
`bin/castle` exits non-zero. Successful command output is unchanged.
2635
- Operator-facing errors and warnings are shorter, distinguish preflight

design/upgrade-tooling.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Upgrade tooling: appups, relups, and the tasks around them
22

3-
**Status:** accepted, unimplemented. Supersedes nothing.
3+
**Status:** accepted. D1 is implemented — the tasks are named `castle.*` as of
4+
[forecastle#24](https://github.com/ausimian/forecastle/issues/24), enforced by
5+
[castle#33](https://github.com/ausimian/castle/issues/33). The rest is
6+
unimplemented. Supersedes nothing.
47
**Spans:** [ausimian/castle](https://github.com/ausimian/castle) and
58
[ausimian/forecastle](https://github.com/ausimian/forecastle).
69
**Tracked by:** [castle#32](https://github.com/ausimian/castle/issues/32).
@@ -55,7 +58,7 @@ appup lookups in `appup_file/2` — and those only exist **after** `:assemble`.
5558
So the real workflow is:
5659

5760
1. `mix release` — assemble the target.
58-
2. `mix forecastle.relup --target … --fromto …` — write `relup` to the project root.
61+
2. `mix castle.relup --target … --fromto …` — write `relup` to the project root.
5962
3. `mix release --overwrite` — assemble it again, this time packaging the relup.
6063

6164
The double build is mandatory and undocumented. Worse, it makes a mutable file in

lib/castle.ex

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,18 @@ defmodule Castle do
6565
6666
The project must also provide:
6767
68-
* An appup for each application being upgraded, configured with the
69-
`:appup` project key and `compilers: Mix.compilers() ++ [:appup]`.
70-
* A relup generated by `mix forecastle.relup` and left in the project root.
68+
* An appup for each application the project owns that has to be upgraded in
69+
place, configured with the `:appup` project key and
70+
`compilers: Mix.compilers() ++ [:appup]`.
71+
72+
Exactly which transitions need one depends on the strategy the relup is
73+
generated with, on which applications changed and on who owns them, and
74+
each direction is classified separately. `mix castle.relup` documents
75+
those rules and is the authority on them; this list deliberately does not
76+
restate them, because a summary short enough to belong here is wrong in
77+
some case and a correct one is that task's `@moduledoc` copied into a
78+
repository that cannot see it change.
79+
* A relup generated by `mix castle.relup` and left in the project root.
7180
* `include_executables_for: [:unix]`.
7281
7382
A custom `rel/env.sh.eex` is optional. Forecastle appends Castle's setup to

mix.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"earmark_parser": {:hex, :earmark_parser, "1.4.46", "67607a0532e810c6f630a515c548d0b24949643f168cc556303bee4cf96105c7", [:mix], [], "hexpm", "9c44636e8a1c68c62f526b2dcd85d941dbbcee7ab82cf64ba06ce28bef8e89f5"},
55
"ex_doc": {:hex, :ex_doc, "0.40.3", "4a972ffe64bc07dc605af487e98fc19b72a4185f55ca031b94c0552d6071c1d9", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2756e357742fecd9749b489b85d67c9ce99c465f2e75728d9e6dc8d704b973de"},
66
"file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"},
7-
"forecastle": {:git, "https://github.com/ausimian/forecastle.git", "e43f8808a4470a6952013cc8af56bec64877ef89", [branch: "feature/upgrade-tooling"]},
7+
"forecastle": {:git, "https://github.com/ausimian/forecastle.git", "a77e97c9f14b07ca4e532ff98e626a6106548cba", [branch: "feature/upgrade-tooling"]},
88
"jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"},
99
"makeup": {:hex, :makeup, "1.2.2", "882d46dc0905e9ff7abf2aab61a7e6b3dcc555533977d8a23b06019e6c89ac94", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "9a1a24e5b343b8ae16abea0822c10a6f75da27af7fa802ada5251f7579bfccfa"},
1010
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},

0 commit comments

Comments
 (0)