README.md

# Castle

Runtime support for hot-code upgrades.

`Castle` provides runtime support for hot-code upgrades. In particular, it generates a 
valid `sys.config` from `runtime.exs` and/or other [Config Providers](https://hexdocs.pm/elixir/main/Config.Provider.html)
prior to both boot and hot-code upgrade.

It relies on [Forecastle](https://hexdocs.pm/forecastle/readme.html) for build-time release generation
and brings it in as a build-time dependency.

## Installation

The package can be installed by adding `castle` to your list of dependencies in
`mix.exs`. For projects that don't define a release, but use the `appup` compiler,
it's sufficient to bring `Castle` in as a build-time dependency:

```elixir
def deps do
  [
    {:castle, "~> 0.3.0", runtime: false}
  ]
end
```

For projects that _do_ define one or more releases, `Castle` should be brought in
as a runtime dependency:

```elixir
def deps do
  [
    {:castle, "~> 0.3.0"}
  ]
end
```

`Castle` brings in `Forecastle` as a build-time dependency.

## Integration

Build-time integration is done via `Forecastle` and more details can be found in its
documentation but, in summary, it will integrate into your release process via the
release assembly process. In particular, it requires that that the `Forecastle.pre_assemble/1` 
and `Forecastle.post_assemble/1` functions are placed around the `:assemble` step, e.g.:

```elixir
defp releases do
  [
    myapp: [
      include_executables_for: [:unix],
      steps: [&Forecastle.pre_assemble/1, :assemble, &Forecastle.post_assemble/1, :tar]
    ]
  ]
end
```

## Release Management

The script in the `bin` folder supports some extra commands to manage upgrades.
Releases, in their tarred-gzipped form, should first be copied to the `releases`
subfolder on the target system. The following commands can be used to manage
them:

  - `releases` - Lists the releases on the system and their status. Status can
    be one of the following:
    - permanent - the release the system will boot into on next restart.
    - current - if it exists, represents the current running release. Will be
      different from the permanent version if a new release has been installed
      but not yet committed. If no version is listed as current, the permanent
      version is the currently running version.
    - old - if it exists, a previously installed version.
    - unpacked - an unpacked version, but not yet installed.
  - `unpack <vsn>` - Unpacks the release called `<name>-<vsn>.tar.gz`.
  - `install <vsn>` - Installs the new release. This makes the release the
    current one, but not yet the permanent one. Prior to running the relup,
    `Castle` generates the version specific `sys.config` for the new version.
  - `commit <vsn>` - Makes the specified release the one the permanent one.
  - `remove <vsn>` - Remove an old version from the filesystem. Any files
    shared with remaining releases are left untouched.

## The Appup Compiler

You are responsible for writing the [appup](https://www.erlang.org/doc/man/appup.html)
scripts for your application, but `Castle` will copy the appup into the `ebin` folder
for you. The steps are as follows:

1. Write a file, in _Elixir form_, describing the application upgrade. e.g.:
   ```elixir
   # You can call the file what you like, e.g. appup.ex, 
   # but you should # keep it away from the compiler paths.
   {
    '0.1.1',
     [
      {'0.1.0', [
        {:update, MyApp.Server, {:advanced, []}}
      ]}
     ],
     [
      {'0.1.0', [
        {:update, MyApp.Server, {:advanced, []}}
      ]}
     ]
   }
   ```
   This file will typically be checked in to SCM.
2. Add the appup file to the Mix project definition in mix.exs and add the
   `:appup` compiler.
   ```elixir
   # Mix.exs
   def project do
     [
       appup: "appup.ex", # Relative to the project root.
       compilers: Mix.compilers() ++ [:appup]
     ]
   end
   ```
   
## Relup Generation

Castle contains a mix task, `castle.relup`, that simplifies the generation of
the relup file. Assuming you have two _unpacked_ releases e.g. `0.1.0` and `0.1.1` 
and you wish to generate a relup between them:

```shell
> mix castle.relup --target myapp/releases/0.1.1/myapp --fromto myapp/releases/0.1.0/myapp
```

If the generated file is in the project root, it will be copied during 
post-assembly to the release.