# glm_freebsd
A Gleam CLI tool that allows you to easily package Gleam Applications as FreeBSD packages. The FreeBSD
packages install as FreeBSD services, including service scripts to manage the application (e.g. start|stop).
This is based on https://github.com/patmaddox/ex_freebsd
[](https://hex.pm/packages/glm_freebsd)
[](https://hexdocs.pm/glm_freebsd/)
Further documentation can be found at <https://hexdocs.pm/glm_freebsd>.
## Quickstart
### Dependencies
* gleam >= 1.14
* erlang >= erlang28
* make
#### Install gleam and erlang
```shell
sudo su
# use `latest` to get erlang 28
mkdir -p /usr/local/etc/pkg/repos
touch /usr/local/etc/pkg/repos/FreeBSD.conf
`echo 'FreeBSD-ports: { url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest" }' > /usr/local/etc/pkg/repos/FreeBSD.conf
pkg update
pkg install -y erlang-runtime28 gleam rebar3
exit
```
#### Update path and exec as normal user
```
PATH=/usr/local/lib/erlang28/bin:$PATH
./make
```
## Usage
### Help
```bash
gleam run -- --help
Compiled in 0.05s
Running glm_freebsd.main
package
package target gleam application as a FreeBSD package with service scripts
Usage:
package [OPTIONS]
Options:
(--application,-a APPLICATION) gleam target application directory (location of the target app's gleam.toml file)
[--templates,-t TEMPLATES] path to custom templates directory (default: "./priv/templates/freebsd")
(--staging,-s STAGING) path to place intermediate (staging) files (will create directory)
(--output,-o OUTPUT) path to place generated (output) files (will create directory)
[--help,-h] Print this help
```
### Create a new gleam app
This gleam app will be packaged as a FreeBSD (service) package...
```bash
$ gleam new APPNAME
$ cd APPNAME
```
### Update the APPNAME/gleam.toml
Add the relevant FreeBSD package info to the ./gleam.toml
See [this example gleam.toml](./priv/example/gleam.toml).
```toml
[freebsd]
pkg_origin = "example_company/example"
pkg_comment = "A simple one-line comment about this package."
# optional
pkg_arch = "freebsd:15:x86:64"
pkg_www = "https://github.com/toddg/some_repo"
# optional
pkg_license_logic = "single"
# optional
pkg_licenses = ["MIT"]
pkg_description = """
line 01 : multi-line-package description....
line 02 : multi-line-package description....
line 03 : multi-line-package description....
line 04 : multi-line-package description....
line 05 : multi-line-package description....
"""
pkg_maintainer = "package_maintainer@example.com"
# optional
pkg_config_dir = "/tmp/example"
# optional
pkg_env_file = "example.env"
# optional
pkg_user_name = "example"
pkg_user_uid = "1234"
# optional
pkg_proc_name = "/usr/local/lib/erlang28/*/bin/beam.smp"
# optional
pkg_path_extensions = "/usr/local/lib/erlang28/bin"
# optional
pkg_var_dir = "/var/example"
# optional
pkg_prefix = "/usr/local"
# optional
pkg_command = "entrypoint.sh"
# optional
pkg_command_args = "run"
# optional
pkg_daemon_flags = ""
pkg_plist_lines = [
{ type = "file", src = "priv/data/wibble.txt", dest = "/usr/local/wibble/wibble.txt", mode = "0700", owner = "", group = "" },
{ type = "directory", src_dir = "priv/data/wobble_dir", dest_dir = "/usr/local/wobble", mode = "0700", owner = "", group = "" },
{ type = "dir_directive", path = "/usr/local/wibble" },
{ type = "dir_directive", path = "/usr/local/wobble" },
]
# optional
[[freebsd.dependencies]]
name = "vim"
version = "9.2.0204"
origin = "editors/vim"
# optional
[[freebsd.dependencies]]
name = "tree"
version = "2.2.1"
origin = "sysutils/tree"
# optional
[[freebsd.pairs]]
key = "key1"
value = "value1"
# optional
[[freebsd.pairs]]
key = "key2"
value = "value2"
# optional
[[freebsd.pairs]]
key = "custom_temp_dir"
value = "/tmp/example_temp_dir"
```
### Create an erlang-shipment
```bash
gleam format
gleam check
gleam test
gleam export erlang-shipment
```
### Create a FreeBSD package
See the [Makefile](./Makefile) for more examples.
```bash
# change directories to the glm_freebsd app (this app) so you can run the CLI tool
cd [glm_freebsd repo directory]
# run this cli tool
gleam run -- -a [PATH TO YOUR TARGET APP TO PACKAGE] -s [PATH TO A STAGING DIRECTORY] -o [PATH TO AN OUTPUT DIRECTORY TO PUT THE PACKAGE]
# install the generated package
sudo pkg install -y [OUTPUT DIR]/[APP_NAME-VERSION].pkg
# start the service
sudo service [APP_NAME] start
```
### Create a FreeBSD package with custom templates
```bash
```bash
# change directories to the glm_freebsd app (this app) so you can run the CLI tool
cd [glm_freebsd repo directory]
# copy the default templates to a directory, typically the `priv` dir in your target app
cp ./priv/templates/freebsd/* [APP_PATH]/priv/templates
# edit the custom templates in [APP_PATH]/priv/templates
# run the cli tool with the `-t` option to override the location of the templates
gleam run -- -a [PATH TO YOUR TARGET APP TO PACKAGE] -s [PATH TO A STAGING DIRECTORY] -o [PATH TO AN OUTPUT DIRECTORY TO PUT THE PACKAGE] -t [APP_PATH]/priv/templates
# install the generated package
sudo pkg install -y [OUTPUT DIR]/[APP_NAME-VERSION].pkg
# start the service
sudo service [APP_NAME] start
```
#### Custom Templates and Custom Vars
The reason you can include arbitrary key/value pairs in the gleam.toml file is to support custom templates.
```toml
[[freebsd.pairs]]
key = "key2"
value = "value2"
```
## Environment Files and 12 Factor Apps
Applications being bundled into a FreeBSD Service will almost certainly require some sort of configuration. Per the
concept of 12 factor apps, this configuration should be external to the app and be _provided_ to the application
by the runtime.
The location that the service management looks for the configuration file can be configured via these fields in gleam.toml:
[freebsd]
pkg_config_dir=...
pkg_env_file=...
The configuration file can be placed in the correct location by your IAC
(infrastructure-as-code, e.g. ansible,chef,puppet,pulumi,terraform,etc.).
When the service manager launches the service, it reads this environment file and includes these environment key/value pairs in the process environment the service instance is started with.
## Toml Elements
Package name and version are extracted from the toml here:
```toml
name = "example"
version = "1.0.0"
```
The rest of the data comes from the [freebsd] sections. This is fully documented in the [Config type](src/glm_freebsd/packager.gleam):
```gleam
/// Configuration object built from the gleam.toml.
pub type Config {
Config(
/// Gleam application name, used in template(s): +MANIFEST, required (no default).
app_name: String,
/// Gleam application version, used in template(s): +MANIFEST, required (no default).
app_version: String,
/// Freebsd package user name, used in +POST_INSTALL and rc, defaults to `app_name`.
pkg_user_name: String,
/// Freebsd package user uid, used in +POST_INSTALL and rc, required (no default).
pkg_user_uid: String,
/// Freebsd package long description, used in +DESC, required (no default).
pkg_description: String,
/// Freebsd package maintainer email address, used in +MANIFEST, required (no default).
pkg_maintainer: String,
/// Freebsd package dependencies, used in +MANIFEST, required (no default).
pkg_dependencies: List(DependencyConfig),
/// Variable used in template(s): rc.conf; the process name to be used when looking for this package,
/// defaults to /usr/local/lib/erlang28/*/bin/beam.smp.
pkg_proc_name: String,
/// Variable used in template(s): +POST_INSTALL, +PRE_DEINSTALL, rc; the package configuration directory,
/// required (no default). Directory is NOT automatically created by the package installation. It is
/// expected that this configuration directory and file will be provided by an orchestration service or
/// manually by a system administrator. Service configuration is separate from service installation.
pkg_config_dir: String,
/// Variable used in template(s): rc; config; path_extensions are ":" delimited paths to prepend to the
/// PATH variable, defaults to /usr/local/lib/erlang28/bin.
pkg_path_extensions: String,
/// Variable used in template(s): +PRE_DEINSTALL; var_dir is the data dir for this package, defaults to "/var/APP_NAME". This directory is NOT created by the installer.
pkg_var_dir: String,
/// Variable used in template(s): rc; defaults to APP_NAME.env.
pkg_env_file: String,
/// The path where the files contained in this package are installed, used in template(s): rc, +MANIFEST,
/// defaults to /usr/local.
pkg_prefix: String,
/// Variable used in template(s): rc; defaults to 'entrypoint.sh'.
pkg_command: String,
/// Variable used in template(s): rc; defaults to 'run'.
pkg_command_args: String,
/// Variable used in template(s): rc; defaults to ''.
pkg_daemon_flags: String,
/// Plist line entries, details files to copy into the target system, plus keywords to
/// control how those files are copied, permissions, etc, defaults to [].
pkg_plist_lines: List(PlistLine),
/// This entry sets the freebsd package's origin to pkg-origin.
/// This is a string of the form category/port-dir which designates the port
/// this package was built from, used in template(s): rc; defaults to 'private/APP_NAME'
pkg_origin: String,
/// Comment-string is a one-line description of this package. it is
/// the equivalent of the comment variable for a port, not a way to
/// put comments in a +manifest file, used in template(s): +MANIFEST, required (no default).
pkg_comment: String,
/// The architecture of the machine the package was built on.
/// cpu-type takes values like x86, amd64, freebsd:15:x86:64,
/// used in template(s): +MANIFEST, defaults to 'freebsd:15:x86:64'.
pkg_arch: String,
/// The software's official website, used in template(s): +MANIFEST, +DISPLAY, required, no default.
pkg_www: String,
/// Package license, used in template(s): +MANIFEST, defaults to 'single'.
pkg_license_logic: String,
/// Package licenses, e.g. licenses: ["MIT"], used in template(s): +MANIFEST, defaults to 'PRIVATE'.
pkg_licenses: List(String),
/// Unstructured key/value pairs to enable sending any string data to the templating system, useful for custom
/// templates, can be used in any custom template. You can elect to use custom templates instead of the default
/// templates by passing the `templates` parameter to the CLI. If you need templates to start out with, copy
/// the default templates from ./priv/templates/freebsd to a directory of your choosing. Modify the copied
/// templates as you wish, and then specify that directory on the CLI as previously mentioned.
pkg_pairs: List(ConfigPair),
)
}
```
## Links
* https://siberoloji.com/how-to-create-a-freebsd-package-with-pkg-create-on-freebsd-operating-system/
* https://man.freebsd.org/cgi/man.cgi?query=pkg&sektion=8
* https://man.freebsd.org/cgi/man.cgi?query=pkg_create&sektion=3&apropos=0&manpath=FreeBSD+15.0-RELEASE+and+Ports.quarterly