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).

Package Version Hex Docs

This tool is inspired by ex_freebsd.

This is an opinionated tool:

Customization is provided via:

Further documentation can be found at https://hexdocs.pm/glm_freebsd.

Quickstart

Dependencies

Install gleam and erlang

Use latest to get erlang 28

sudo mkdir -p /usr/local/etc/pkg/repos
sudo touch /usr/local/etc/pkg/repos/FreeBSD.conf
sudo echo 'FreeBSD-ports: { url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest" }' > /usr/local/etc/pkg/repos/FreeBSD.conf
sudo pkg update
sudo pkg install -y erlang-runtime28 gleam rebar3

Update path and exec as normal user

PATH=/usr/local/lib/erlang28/bin:$PATH
./make

Usage

Install

# by default, installs to /usr/local/bin:
sudo make install
#
# or specify an install dir
sudo make install INSTALL_DIR=/bin

Help

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…

$ 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.

[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

gleam format
gleam check
gleam test
gleam export erlang-shipment

Create a FreeBSD package

See the Makefile for more examples.

# 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
# 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.

[[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:

name = "example"
version = "1.0.0"

The rest of the data comes from the [freebsd] sections. This is fully documented in the Config type:

/// 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

Search Document