Habitat Reference and Syntax

When defining a package in Habitat, there are different files, settings, variables, and functions that you can use to set up specific installation and configuration details. You have a great deal of flexibility in how you build and define your application.

This section will cover as many of the modules and APIs that Habitat is built upon to serve as helpful reference as you're developing your Habitat packages.

Table of Contents


Environment Variables

This is a list of all environment variables that can be used to modify the operation of the Habitat Studio and Supervisor. For information on defining environment variables with the scaffoldings, check out the "Define Environment Variables" section of Build your web application with Habitat!

Variable Context Default Description
HAB_AUTH_TOKEN build system no default Authorization token used to perform privileged operations against the depot, e.g. uploading packages or keys.
HAB_BINLINK_DIR build system /hab/bin Allows you to change the target directory for the symlink created when you run hab pkg binlink. The default value is already included in the $PATH variable inside the Studio.
HAB_CACHE_KEY_PATH build system, Supervisor /hab/cache/keys if running as root; $HOME/.hab/cache/keys if running as non-root Cache directory for origin signing keys
HAB_BLDR_CHANNEL build system, Supervisor stable Set the Habitat Builder channel you are subscribing to, to a specific channel. Defaults to stable.
HAB_BLDR_URL build system, Supervisor https://bldr.habitat.sh Sets an alternate default endpoint for communicating with Builder. Used by the Habitat build system and the Supervisor
HAB_DOCKER_OPTS build system no default When running a Studio on a platform that uses Docker (MacOS), additional command line options to pass to the docker command.
HAB_INTERNAL_BLDR_CHANNEL build system, Supervisor, exporters stable Channel from which Habitat-specific packages (e.g., core/hab-sup, core/hab-launcher, etc.) are downloaded on-demand when first called. Generally of use only for those developing Habitat. Only applies to Habitat-specific packages, and nothing else.
HAB_NOCOLORING build system no default If set to the lowercase string "true" this environment variable will unconditionally disable text coloring where possible
HAB_NONINTERACTIVE build system no default If set to the lowercase string "true" this environment variable will unconditionally disable interactive progress bars (i.e. "spinners") where possible
HAB_ORG Supervisor no default Organization to use when running with service group encryption
HAB_ORIGIN build system no default Origin used to build packages. The signing key for this origin is passed to the build system.
HAB_ORIGIN_KEYS build system no default Comma-separated list of origin keys to automatically share with the build system
HAB_RING Supervisor no default The ring used by the Supervisor when running with wire encryption
HAB_RING_KEY Supervisor no default The name of the ring key when running with wire encryption
HAB_STUDIO_SECRET_<VARIABLE> build system no default Prefix to allow environment variables into the Studio. The prefix will be removed and your variable will be passed into the Studio at build time.
HAB_STUDIOS_HOME build system /hab/studios Directory in which to create build Studios
HAB_STUDIO_BACKLINE_PKG build system core/hab-backline/{{studio_version}} Overrides the default package identifier for the "backline" package which installs the Studio basline package set.
HAB_STUDIO_ROOT build system no default Root of the current Studio under $HAB_STUDIOS_HOME. Infrequently overridden.
HAB_STUDIO_NOSTUDIORC build system no default When set to a non-empty value, a .studiorc will not be sourced when entering an interactive Studio via hab studio enter.
HAB_STUDIO_SUP build system no default Used to customize the arguments passed to an automatically launched Supervisor, or to disable the automatic launching by setting it to false, no, or 0.
HAB_UPDATE_STRATEGY_FREQUENCY_MS Supervisor 60000 Frequency of milliseconds to check for updates when running with an update strategy
HAB_USER Supervisor no default User key to use when running with service group encryption
http_proxy build system, Supervisor no default A URL for a local HTTP proxy server optionally supporting basic authentication
https_proxy build system, Supervisor no default A URL for a local HTTPS proxy server optionally supporting basic authentication
NO_INSTALL_DEPS build system no default Set this variable to prevent dependencies install during build
no_proxy build system, Supervisor no default A comma-separated list of domain exclusions for the http_proxy and https_proxy environment variables
SSL_CERT_FILE system no default a variable to help you navigate firewalls

Customizing Studio

When you enter a Studio, Habitat will attempt to locate /src/.studiorc and source it. Think ~/.bashrc. This file can be used to export any environment variables like the ones you see above as well as any other shell customizations to help you develop your plans from within the Studio.

To use this feature, place a .studiorc in the current working directory where you will run hab studio enter.

Note that a .studiorc will only be source when using hab studio enter--it will not be sourced when calling hab studio run or hab studio build (also hab pkg build).


Plan settings

The following settings are defined at the beginning of your plan. They specify basic information about your plan such as name, version, and dependencies.

Note: We are using Bash syntax here, but Powershell plans use the same variable names and should conform to standard Powershell language rules (ie. $ before variable names and quoting string values).

General Settings

pkg_name

Required. Sets the name of the package. This will be used along with pkg_origin, and pkg_version to define the fully-qualified package name, which determines where the package is installed to on disk, how it is referred to in package metadata, and so on. A pkg_name can contain upper and lowercase letters, numbers, dashes, and underscores.

pkg_name=zlib

pkg_origin

Required unless overridden by the HAB_ORIGIN environment variable. The origin is used to denote a particular upstream of a package. A pkg_origin can contain upper and lowercase letters, numbers, dashes, and underscores.

g_origin=Habitat

pkg_version

Optional (Required unless pkg_version() function is defined). Sets the version of the package.

pkg_version=1.2.8

pkg_maintainer

Optional. The name and email address of the package maintainer.

pkg_maintainer="Your Name <someone@example.com>"

pkg_license

Optional. An array of valid software licenses that relate to this package.

pkg_license=('Apache-2.0')

Note: If your package has a custom license, use a string literal matching the title of the license. For example, you'll see pkg_license=('Boost Software License') for the cmake plan.

pkg_source

Optional. A URL that specifies where to download an external source from. Any valid wget url will work. Typically, the relative path for the URL is partially constructed from the pkg_name and pkg_version values; however, this convention is not required.

pkg_source=http://downloads.sourceforge.net/project/libpng/$pkg_name/${pkg_version}/${pkg_name}-${pkg_version}.tar.gz

pkg_filename

Optional. The resulting filename for the download, typically constructed from the pkg_name and pkg_version values.

pkg_filename=${pkg_name}-${pkg_version}.tar.gz

pkg_shasum

Required if a valid URL is provided for pkg_source or unless do_verify() is overridden. The value for pkg_shasum is a sha-256 sum of the downloaded pkg_source. If you do not have the checksum, you can easily generate it by downloading the source and using the sha256sum or gsha256sum tools. Also, if you do not have do_verify() overridden, and you do not have the correct sha-256 sum, then the expected value will be shown in the build output of your package.

pkg_shasum=36658cb768a54c1d4dec43c3116c27ed893e88b02ecfcb44f2166f9c0b7f2a0d

pkg_deps

Optional. An array of package dependencies needed at runtime. You can refer to packages at three levels of specificity: origin/package, origin/package/version, or origin/package/version/release.

pkg_deps=(core/glibc core/pcre core/openssl core/zlib)

pkg_build_deps

Optional. An array of the package dependencies needed only at build time.

pkg_build_deps=(core/gcc core/linux-headers)

pkg_lib_dirs

Optional. An array of paths, relative to the final install of the software, where libraries can be found. Used to populate LD_FLAGS and LD_RUN_PATH for software that depends on your package.

pkg_lib_dirs=(lib)

pkg_include_dirs

Optional. An array of paths, relative to the final install of the software, where headers can be found. Used to populate CFLAGS for software that depends on your package.

pkg_include_dirs=(include)

pkg_bin_dirs

Optional. An array of paths, relative to the final install of the software, where binaries can be found. Used to populate PATH for software that depends on your package.

pkg_bin_dirs=(bin)

pkg_pconfig_dirs

Optional. An array of paths, relative to the final install of the software, where pkg-config metadata (.pc files) can be found. Used to populate PKG_CONFIG_PATH for software that depends on your package.

pkg_pconfig_dirs=(lib/pkgconfig)

pkg_svc_run

Optional. The command for the Supervisor to execute when starting a service. You can omit this setting if your package is not intended to be run directly by a Supervisor. If you set this you will need to set pkg_bin_dirs so that the binaries in your package will be in the path

pkg_svc_run="haproxy -f $pkg_svc_config_path/haproxy.conf"

Note: You should use a run hook instead if you have complex start up behavior.

pkg_exports

Optional. An associative array (or hashtable in Powershell) representing configuration data which should be gossiped to peers. The keys in this array are used with pkg_exposes and for any consuming services that set pkg_binds or pkg_binds_optional. The values represent the TOML path to a value.

pkg_exports=(
[port]=server.port
[host]=server.host
[ssl-port]=ssl.port
)

In this example, the corresponding default.toml file would have the following key/value pairs defined:

default.toml
[server]
port = 80
host = "www.example.com"
[ssl]
port = 443

pkg_exposes

Optional. An array of pkg_exports keys containing default values for the ports that this package exposes. These values are used as sensible defaults for other tools, such as when exporting a package to a container format.

pkg_exposes=(port ssl-port)

Note: In addition to specifying the keys you defined in pkg_exports, you must have a default.toml file indicating the port values to expose.

pkg_binds

Optional. An associative array (or hashtable in Powershell) representing services which you depend on and the configuration keys that you expect the service to export (by their pkg_exports). These binds must be set for the Supervisor to load the service. The loaded service will wait to run until its bind becomes available. If the bind does not contain the expected keys, the service will not start successfully.

pkg_binds=(
[database]="port host"
)

pkg_binds_optional

Optional. Same as pkg_binds but these represent optional services to connect to.

pkg_binds_optional=(
[storage]="port host"
)

pkg_interpreters

Optional. An array of interpreters used in shebang lines for scripts. Specify the subdirectory where the binary is relative to the package, for example, bin/bash or libexec/neverland, since binaries can be located in directories besides bin. This list of interpreters will be written to the metadata INTERPRETERS file, located inside a package, with their fully-qualified path. Then these can be used with the fix_interpreter function. For more information on declaring shebangs in Habitat, see Plan hooks, and for more information on the fix_interpreter function, see Plan utility functions.

pkg_interpreters=(bin/bash)

pkg_svc_user

Optional. The user to run the service as. The default is hab. On Windows, if the hab user does not exist then the service will run under the same account as the Supervisor.

pkg_svc_user=hab

pkg_svc_group

Optional. The group to run the service as. The default is hab.

pkg_svc_group=$pkg_svc_user

Note: pkg_svc_group is not used in a plan.ps1.

pkg_description

Required for core plans, optional otherwise. A short description of the package. It can be a simple string, or you can create a multi-line description using markdown to provide a rich description of your package. This description will be displayed on the Web app when users search for or browse to your package.

pkg_description=$(cat << EOF
# My package description
This is the package for the foo library. It's pretty awesome.
EOF
)

Note: Any special characters other than # will have to be escaped; otherwise, they could be interpreted by the hab-plan-build script when the package is built.

pkg_upstream_url

Optional. An upstream project homepage or website URL.

pkg_upstream_url=https://github.com/myrepo

Composite-specific Settings

pkg_type

Required. String that denotes a composite package. Must be set to "composite" otherwise Habitat will treat the composite package as a normal package.

pkg_type="composite"

pkg_services

Required. Array that contains all of the services in your composite application.

Note You must list at least two services and if you list a package that is not a service (for example, it does not have a run hook), it will trigger a build error for the composite package.

pkg_services=(
cm/sample-node-app
cm/composite-example-api-proxy
)

pkg_bind_map

Optional. Array that contains any service bindings between composite services. Additional bindings can be added separated by spaces. The format is [consumer_service]:"bind_key:origin/producerservice".

Declaring a single binding

pkg_bind_map=(
[cm/composite-example-api-proxy]="http:cm/sample-node-app"
)

Declaring multiple bindings

pkg_bind_map=(
[cm/composite-example-api-proxy]="http:cm/sample-node-app otherbind:other/service-in-the-composite"
)


Plan Variables

The following variables can be used in your plans to help get binaries and libraries to build and install in the correct locations in your package.

$pkg_prefix : The absolute path for your package.

$pkg_dirname : Set to ${pkg_name}-${pkg_version} by default. If a .tar file extracts to a directory that's different from the filename, then you would need to override this value to match the directory name created during extraction.

$pkg_svc_path : Where the running service is located. $HAB_ROOT_PATH/svc/$pkg_name

$pkg_svc_data_path : Where the running service data is located. $pkg_svc_path/data

$pkg_svc_files_path : Where the gossiped configuration files are located. $pkg_svc_path/files

$pkg_svc_var_path : Where the running service variable data is located. $pkg_svc_path/var

$pkg_svc_config_path : Where the running service configuration is located. $pkg_svc_path/config

$pkg_svc_static_path : Where the running service static data is located. $pkg_svc_path/static

$HAB_CACHE_SRC_PATH : The default path where source archives are downloaded, extracted, & compiled.

$HAB_CACHE_ARTIFACT_PATH : The default download root path for packages.

$HAB_PKG_PATH : The root path containing all locally installed packages.

$PLAN_CONTEXT : The location on your local dev machine for the files in your plan directory.

$CFLAGS : C compiler options.

$LDFLAGS : C linker options.

$PREFIX : Where to install the software; same as $pkg_prefix

$LD_RUN_PATH : Where to find the binaries at run time.


Plan Helper Functions

The following helper functions can be useful in your plan to help you build your package correctly. Attach() specifically is to help with debugging - the other helper functions are to help you in building your package.

Note: Most of the following helper functions are not available in Powershell plans (plan.ps1). However in most cases, the standard Powershell cmdlets provide the same functionality. For example: use Resolve-Path instead of abspath or Get-Command instead of exists.

attach()(plan.sh only) : Attaches your script to an interactive debugging session, which lets you check the state of variables, call arbitrary functions, and turn on higher levels of logging by using the set -x command and switch.

To use attach, add attach to any callback or part of your plan.sh file and the debugging session with start up when hab-plan-build comes to that part in the file.

download_file()(plan.sh only) : Downloads a file from a source URL to a local file and uses an optional shasum to determine if an existing file can be used.

If an existing file is present and the third argument is set with a shasum digest, the file will be checked to see if it's valid. If so, the function ends early and returns 0. Otherwise, the shasums do not match so the file-on-disk is removed and a normal download proceeds as though no previous file existed. This is designed to restart an interrupted download.

Any valid wget URL will work.

Downloads every time, even if the file exists locally:

download_file http://example.com/file.tar.gz file.tar.gz

Downloads if no local file is found:

download_file http://example.com/file.tar.gz file.tar.gz abc123...

File matches checksum: download is skipped, local file is used:

download_file http://example.com/file.tar.gz file.tar.gz abc123...

File doesn't match checksum: local file removed, download attempted:

download_file http://example.com/file.tar.gz file.tar.gz ohnoes...

Will return 0 if a file was downloaded or if a valid cached file was found.

pkg_path_for()(Get-HabPackagePath in a plan.ps1) : Returns the path for a build or runtime package dependency on stdout from the list of dependencies referenced in pkg_deps or pkg_build_deps. This is useful if you need to install or reference specific dependencies from within a callback, such as do_build() or do_install().

Here's an example of how to use this function to retrieve the path to the perl binary in the core/perl package:

_perl_path="$(pkg_path_for core/perl)/bin/perl"

fix_interpreter()(plan.sh only) : Edits the #! shebang of the target file in-place. This is useful for changing hardcoded paths defined by your source files to the equivalent path in a Habitat package. You must include the required package that provides the expected path for the shebang in pkg_deps. This function performs a greedy match against the specified interpreter in the target file(s).

To use this function in your plan, you must specify the following arguments: 1. The target file or files 2. The name of the package that contains the interpreter 3. The relative directory and binary path to the interpreter

For example, to replace all the files in node_modules/.bin that have #!/usr/bin/env with the coreutils path to bin/env (/hab/pkgs/core/coreutils/8.24/20160219013458/bin/env), you must quote the wildcard target as shown below.

fix_interpreter "node_modules/.bin/*" core/coreutils bin/env

For a single target, reference the file directly:

fix_interpreter node_modules/.bin/concurrent core/coreutils bin/env

pkg_interpreter_for()(plan.sh only) : Returns the path for the given package and interpreter by reading it from the INTERPRETERS metadata in the package. The directory of the interpreter needs to be specified, as an interpreter binary might live in bin, sbin, or libexec, depending on the software.

The following shows how to call pkg_interpreter_for with the package and interpreter arguments specified.

pkg_interpreter_for core/coreutils bin/env

This function will return 0 if the specified package and interpreter were found, and 1 if the package could not be found or the interpreter is not specified for that package.

pkg_version() : An optional way to determine the value for $pkg_version. The function must print the computed version string to standard output and will be called when the Plan author invokes the update_pkg_version() helper in a plan.shorSet-PkgVersionin aplan.ps1`.

update_pkg_version()(Set-PkgVersion in a plan.ps1) : Updates the value for $pkg_version by calling a Plan author-provided pkg_version() function. This function must be explicitly called in a Plan in or after the do_before()/Invoke-Before build phase but before the do_prepare()/Invoke-Prepare build phase. The $pkg_version variable will be updated and any other relevant variables will be recomputed. The following examples show how to use these functions to set a dynamic version number.

This plan concatenates a static file in the source root of the project to determine the version in the before phase:

pkg_version() {
cat "$SRC_PATH/version.txt"
}
do_before() {
do_default_before
update_pkg_version
}
function pkg_version {
Get-Content "$SRC_PATH/version.txt"
}
Invoke-Before {
Invoke-DefaultBefore
Set-PkgVersion
}

The pkg_version function in this plan dynamically creates a version with a date stamp to format the final version string to standard output. As the downloaded file is required before running the version logic, this helper function is called in the download build phase:

pkg_version() {
local build_date
# Extract the build date of the certificates file
build_date=$(cat $HAB_CACHE_SRC_PATH/$pkg_filename </span>
| grep 'Certificate data from Mozilla' </span>
| sed 's/^## Certificate data from Mozilla as of: //')
date --date="$build_date" "+%Y.%m.%d"
}
do_download() {
do_default_download
update_pkg_version
}
function pkg_version {
# Extract the build date of the certificates file
$matchStr = "## Certificate data from Mozilla as of: "
foreach($line in (Get-Content "$HAB_CACHE_SRC_PATH/$pkg_filename")) {
if($line.StartsWith($matchStr)) {
$build_date = $line.Substring($matchStr.Length)
}
}
[DateTime]::Parse($build_date).ToString("yyyy.mm.dd")
}
function Invoke-Download {
Invoke-DefaultDownload
Set-PkgVersion
}

abspath()(plan.sh only) : Return the absolute path for a path, which might be absolute or relative.

exists()(plan.sh only) : Checks that the command exists. Returns 0 if it does, 1 if it does not.

build_line()(Write-BuildLine in a plan.ps1) : Print a line of build output. Takes a string as its only argument.

build_line "Checksum verified - ${pkg_shasum}"

warn()(Write-Warning in a plan.ps1) : Print a warning line on stderr. Takes a string as its only argument.

warn "Checksum failed"

debug()(Write-Debug in a plan.ps1) : Prints a line only if the $DEBUG environment value is set to 1. The debug function takes a string as its only argument.

DEBUG=1
debug "Only if things are set"

exit_with()(plan.sh only) : Exits the program with an error message and a status code.

exit_with "Something bad happened" 55

trim()(plan.sh only) : Trims leading and trailing whitespace characters from a bash variable.

record()(plan.sh only) : Takes a session name and command to run as arguments function appends a timestamp to the log file. Alternative to piping build through tee.

# Usage: record <SESSION> [CMD [ARG ...]]
record mysoftware build /src/mysoftware


Build Phase Callbacks

When defining your plan, you can override the default behavior of Habitat in each build phase through a callback. To define a callback, simply create a shell function of the same name in your plan file and then write your script. If you do not want to use the default callback behavior, you must override the callback and return 0 in the function definition or simply provide no implementation in a plan.ps1.

These callbacks are listed in the order that they executed by the package build script.

Note Bash callbacks are prefixed with do_ and use an underscore convention. Powershell plans prefix callbacks with Invoke- and use a PascalCase convention.

You can also use plan variables in your plans to place binaries, libraries, and files into their correct locations during package compilation or when running as a service.

Additionally, plan helper functions can be useful in your plan to help you build your package correctly. They are mostly used for building packages - attach() is used for debugging.

do_begin()/Invoke-Begin : Used to execute arbitrary commands before anything else happens. Note that at this phase of the build, no dependencies are resolved, the $PATH and environment is not set, and no external source has been downloaded. For a phase that is more completely set up, see the do_before() phase.

do_begin_default()/Invoke-BeginDefault : There is an empty default implementation of this callback.

do_setup_environment()/Invoke-SetupEnvironment : Use this to declare buildtime and runtime environment variables that overwrite or are in addition to the default environment variables created by Habitat during the build process. Examples of common environment variables you might wish to add or modify are those such as JAVA_HOME or GEM_PATH.

Note You do not have to override this callback if you do not wish to modify your environment variables. The build system will always set up your environment according to your dependencies. For example, it will ensure that dependency binaries are always present on your PATH variable, and so on.

Runtime environments of dependencies are layered together in the order they are declared in your pkg_deps array, followed by modifications made in this callback. In turn, these computed values will be made available to packages that use the current package as a dependency, and so on.

The buildtime environment is assembled by processing the runtime environments of your pkg_build_deps dependencies (because they will be running in your build) in a similar manner. The final environment in which your package will be built consists of:

  • The system environment of your Studio as the base layer
  • The assembled runtime environment of your package on top of the base
  • Any builtime environment information on top of the assembled runtime environment

Only the runtime portion of this combined buildtime environment is made available to your package when it is running in a Supervisor (or when it is being used as a dependency of another Habitat package).

To add or modify your environment variables, there are special functions to call within this callback to ensure that the variables are set up appropriately.

Bash:

set_runtime_env [-f] VARIABLE_NAME VALUE
set_buildtime_env [-f] VARIABLE_NAME VALUE

Powershell:

Set-RuntimeEnv VARIABLE_NAME VALUE [-force]
Set-BuildtimeEnv VARIABLE_NAME VALUE [-force]

These functions allow you to set an environment variable's value. If one of your dependencies has already declared a value for this, it will result in a build failure, protecting you from inadvertently breaking anything. If you really do want to replace the value, you can supply the -f or -force flag (for "force").

For pushing new values onto a multi-valued environment variable (like PATH), use the following functions:

Bash:

push_runtime_env VARIABLE_NAME VALUE
push_buildtime_env VARIABLE_NAME VALUE
Powershell:
Push-RuntimeEnv VARIABLE_NAME VALUE
Push-BuildtimeEnv VARIABLE_NAME VALUE

These functions allow you to push a new value onto a multi-valued environment variable without overwriting the existing values. These multi-valued variables are referred to as "aggregate" variables in Habitat. Single-value environment variables are known as "primitive" variables.

By default, Habitat treats all variables as "primitive" variables. If you are working with a value that is actually an "aggregate" type, you must set the following special environment variable somewhere in the top level of your plan.

Bash:

export HAB_ENV_FOO_TYPE=aggregate

Powershell:

$env:HAB_ENV_FOO_TYPE="aggregate"

Similarly, Habitat defaults to using the colon (:) as a separator for aggregate variables on Linux. If the hypothetical FOO variable uses a semicolon (;) as a separator instead, then you must add export HAB_ENV_FOO_SEPARATOR=; at the top level of the plan. On Windows, ; is the default separator.

In all cases, when Habitat is assuming a default strategy, it will emit log messages to notify you of that along with instructions on how to change the behavior.

Note If you discover common environment variables that Habitat doesn't currently treat appropriately, feel free to request an addition to the codebase, or even to submit a pull request yourself.

do_before()/Invoke-Before : At this phase of the build, the origin key has been checked for, all package dependencies have been resolved and downloaded, and the $PATH and environment are set, but this is just before any source downloading would occur (if $pkg_source is set). This could be a suitable phase in which to compute a dynamic version of a pacakge given the state of a Git repository, fire an API call, start timing something, etc.

do_before_default()/Invoke-BeforeDefault : There is an empty default implementation of this callback.

do_download()/Invoke-Download : If $pkg_source is being used, download the software and place it in $HAB_CACHE_SRC_PATH/$pkg_filename. If the source already exists in the cache, verify that the checksum is what we expect, and skip the download. Delegates most of the implementation to the do_default_download() function.

do_download_default()/Invoke-DownloadDefault : The default implementation is that the software specified in $pkg_source is downloaded, checksum-verified, and placed in $HAB_CACHE_SRC_PATH/$pkg_filename, which resolves to a path like /hab/cache/src/filename.tar.gz. You should override this behavior if you need to change how your binary source is downloaded, if you are not downloading any source code at all, or if you are cloning from git. If you do clone a repo from git, you must override do_verify() to return 0.

do_verify()/Invoke-Verify : If $pkg_source is being used, verify that the package we have in $HAB_CACHE_SRC_PATH/$pkg_filename has the $pkg_shasum we expect. Delegates most of the implementation to the do_default_verify() function.

If you do clone a repo from git, you must override do_verify() to return 0.

do_verify_default()/Invoke-VerifyDefault : The default implementation tries to verify the checksum specified in the plan against the computed checksum after downloading the source tarball to disk. If the specified checksum doesn't match the computed checksum, then an error and a message specifying the mismatch will be printed to stderr. You should not need to override this behavior unless your package does not download any files.

do_clean()Invoke-Clean : Clean up the remnants of any previous build job, ensuring it can't pollute out new output. Delegates most of the implementation to the do_default_clean() function.

do_default_clean()/Invoke-DefaultClean : The default implementation removes the HAB_CACHE_SRC_PATH/$pkg_dirname folder in case there was a previously-built version of your package installed on disk. This ensures you start with a clean build environment.

do_unpack()/Invoke-Unpack : If $pkg_source is being used, we take the $HAB_CACHE_SRC_PATH/$pkg_filename from the download step and unpack it,as long as the method of extraction can be determined. This takes place in the $HAB_CACHE_SRC_PATH directory. Delegates most of the implementation to the do_default_unpack() function.

do_default_unpack()/Invoke-DefaultUnpack : The default implementation extracts your tarball source file into HAB_CACHE_SRC_PATH. The supported archive extensions on Linux are: .tar, .tar.bz2, .tar.gz, .tar.xz, .rar, .zip, .Z, .7z. Only .zip is supported on Windows. If the file archive could not be found or has an unsupported extension, then a message will be printed to stderr with additional information.

do_prepare()/Invoke-Prepare : There is no default implementation of this callback. At this point in the build process, the tarball source has been downloaded, unpacked, and the build environment variables have been set, so you can use this callback to perform any actions before the package starts building, such as exporting variables, adding symlinks, and so on.

A step that exists to be overridden. Do what you need to do before we actually run the build steps.

do_default_prepare()/Invoke-DefaultPrepare : There is an empty default implementation of this callback.

do_build()/Invoke-Build : You should override this behavior if you have additional configuration changes to make or other software to build and install as part of building your package. This step builds the software; assumes the GNU pattern. Delegates most of the implementation to the do_default_build() function.

do_default_build()/Invoke-DefaultBuild :The default implementation is to update the prefix path for the configure script to use $pkg_prefix and then run make to compile the downloaded source. This means the script in the default implementation does ./configure --prefix=$pkg_prefix && make.

do_check()/Invoke-Check : Will run post-compile tests and checks, provided 2 conditions are true:

  1. A do_check() function has been declared. By default, no such function exists, so Plan author must add one explicitly--there is no reasonably good default here.
  2. A $DO_CHECK environment variable is set to some non-empty value. As tests can dramatically inflate the build time of a Plan, this has been left as an opt-in option.

Here's an example example of a vanilla Plan such as Sed:

core-plans/sed/plan.sh
pkg_name=sed
# other Plan metadata...
do_check() {
make check
}

do_install()/Invoke-Install : Installs the software. Delegates most of the implementation to the do_default_install() function. You should override this behavior if you need to perform custom installation steps, such as copying files from HAB_CACHE_SRC_PATH to specific directories in your package, or installing pre-built binaries into your package.

do_default_install()/Invoke-DefaultInstall : The default implementation is to run make install on the source files and place the compiled binaries or libraries in HAB_CACHE_SRC_PATH/$pkg_dirname, which resolves to a path like /hab/cache/src/packagename-version/. It uses this location because of do_build() using the --prefix option when calling the configure script.

do_build_config()/Invoke-BuildConfig : Copy the ./config directory, relative to the Plan, to $pkg_prefix/config. Do the same with default.toml. Delegates most of the implementation to the do_default_build_config() function.

Allows users to depend on a core plan and pull in its configuration but set their own unique configurations at build time.

do_default_build_config()/Invoke-DefaultBuildConfig : Default implementation for the do_build_config() phase.

do_build_service()/Invoke-BuildService : Write out the $pkg_prefix/run file. If a file named hooks/run exists, we skip this step. Otherwise, we look for $pkg_svc_run, and use that. We assume that the binary used in the $pkg_svc_run command is set in the $PATH.

This will write a run script that uses chpst to run the command as the $pkg_svc_user and $pkg_svc_group. These are hab by default.

Delegates most of the implementation to the do_default_build_server() function.

do_default_build_service()/Invoke-DefaultBuildService : Default implementation of the do_build_service() phase.

do_strip()(plan.sh only) : You should override this behavior if you want to change how the binaries are stripped, which additional binaries located in subdirectories might also need to be stripped, or whether you do not want the binaries stripped at all.

do_default_strip()(plan.sh only) : The default implementation is to strip any binaries in $pkg_prefix of their debugging symbols. Goal of this step is to reduce our total size.

do_after()(plan.sh only) : At this phase, the package has been built, installed, stripped, but before the package metadata is written and the artifact is created and signed.

do_default_after()(plan.sh only) : There is an empty default implementation of this callback.

do_end()/Invoke-End : A function for cleaning up after yourself, this is called after the package artifact has been created. You can use this callback to remove any temporary files or perform other post-build clean-up actions.

do_default_end()/Invoke-DefaultEnd : There is an empty default implementation of this callback.


Hooks

Each plan can specify lifecycle event handlers, or hooks, to perform certain actions during a service's runtime. Each hook is a script with a shebang defined at the top to specify the interpreter to be used. On Windows, Powershell Core is the only interpreter ever used.

To define a hook, simply create a bash file of the same name in /my_plan_name/hooks/, for example, /postgresql/hooks/health_check.

Important You cannot block the thread in a hook unless it is in the run hook. Never call hab or sleep in a hook that is not the run hook.

Related article: Runtime settings

Habitat's runtime settings can be used in any of the plan hooks and also in any templatized configuration file for your application or service.

Available hooks

file_updated

File location: <plan>/hooks/file_updated

This hook is run whenever a configuration file that is not related to a user or about the state of the service instances is updated.

health_check

File location: <plan>/hooks/health_check

This hook is run when the Habitat HTTP API receives a request at /health.

The health_check script must return a valid exit code from the list below.

  • 0- ok
  • 1- warning
  • 2- critical
  • 3- unknown
  • any other code - failed health check with additional output taken from health_check stdout.

A health_check hook can use the following as a template:

hooks/health_check
#!/bin/sh
# define default return code as 0
rc=0
program_that_returns_a_status
case $? in
0)
rc=1 ;;
3)
rc=0 ;;
4)
rc=2 ;;
*)
rc=3 ;;
esac
exit $rc

init

File location: <plan>/hooks/init

This hook is run when a Habitat topology starts.

reload

File location: <plan>/hooks/reload

For processes that can update their configuration without requiring a restart a reload hook can be written. This hook will execute instead of the default behaviour of restarting the process. {{pkg.svc_pid_file}} can be used to get a handle on the PID of the service.

reconfigure

File location: <plan>/hooks/reconfigure

This hook is run when service configuration has changed due to updates coming from the gossip protocol or from the user.toml file. Before the reconfigure hook the config files are re-rendered and the process is either restarted or the reload hook is called if present.

suitability

File location: <plan>/hooks/suitability

The suitability hook allows a service to report a priority by which it should be elected leader. The hook is called when a new election is triggered and the last line it outputs to stdout should be a number parsable as a u64. In the event that a leader goes down and an election is started the service with the highest reported suitabilty will become the new leader.

run

File location: <plan>/hooks/run

This hook is run when one of the following conditions occur:

  • The main topology starts, after the init hook has been called.
  • When a package is updated, after the init hook has been called.
  • When the package config changes, after the init hook has been called, but before a reconfigure hook is called.

You can use this hook in place of $pkg_svc_run when you need more complex behavior such as setting environment variables or command options that are based on dynamic configuration.

Services run using this hook should do two things:

  • Redirect stderr to stdout (e.g. with exec 2>&1 at the start of the hook)
  • Call the command to execute with exec <command> <options> rather than running the command directly. This ensures the command is executed in the same process and that the service will restart correctly on configuration changes.

It is important to also consider what side effects the command to execute will have. For example, does the command spin off other processes in separate process groups? If so, they may not be cleaned up automatically when the system is reconfigured. In general, the command executed should behave in a manner similar to a daemon, and be able to clean up properly after itself when it receives a SIGTERM, and properly forward signals to other processes that it creates. For an even more specific example: let's say you are trying to start a node.js service. Instead of your command being npm start, you should use node server.js directly.

A run hook can use the following as a template:

hooks/run
#!/bin/sh
# redirect stderr
exec 2>&1
# Set some environment variables
export MY_ENVIRONMENT_VARIABLE=1
export MY_OTHER_ENVIRONMENT_VARIABLE=2
# Run the command
exec my_command --option {{cfg.option}} --option2 {{cfg.option2}}

post-run

File location: <plan>/hooks/post-run

The post run hook will get executed after initial startup.

For many data services creation of specific users / roles or datastores is required. This needs to happen once the service has already started.

smoke_test

File location: <plan>/hooks/smoke_test

This hook is run when a new package is downloaded by the Supervisor, before it is installed.

The smoke_test script must return a valid exit code from the list below.

  • 0- ok
  • no code - failed smoke check with additional output taken from smoke_check stdout.
  • any other code - Returns code, returns failed smoke check with additional output taken from smoke_check stdout.

A smoke_check hook can use the following as a template:

hooks/smoke_check
#!/bin/sh
# define default return code as 0
rc=0
program_that_returns_a_status
case $? in
0)
rc=1 ;;
3)
rc=0 ;;
4)
rc=2 ;;
*)
rc=3 ;;
esac
exit $rc

post-stop

File location: <plan>/hooks/post-stop

The post-stop hook will get executed after service has been stopped successfully.

You may use this hook to undo what the init hook has done.


Handlebars Helpers

Habitat not only allows you to use Handlebars-based tunables in your plan, but you can also use both built-in Handlebars helpers as well as Habitat-specific helpers to define your configuration logic.

Built-in Helpers

You can use block expressions to add basic logic to your template such as checking if a value exists or iterating through a list of items.

Block expressions use a helper function to perform the logic. The syntax is the same for all block expressions and looks like this:

{{#helper blockname}}
  {{expression}}
{{/helper}}

Habitat supports the standard built-in helpers:

  • if
  • unless
  • each
  • with
  • lookup
  • > (partials)
  • log

Note Per Handlebars Paths, when using each in a block expression, you must reference the parent context of that block to use any user-defined configuration values referenced within the block, such as those that start with cfg. For example, if your block looked like the following, you must reference cfg.port from the parent context of the block:

{{#each svc.members ~}}
  server {{sys.ip}}:{{../cfg.port}}
{{/each}}

The most common block helpers that you will probably use are the if and with helpers.

The if helper evaluates conditional statements. The values false, 0, "", as well as undefined values all evaluate to false in if blocks.

Here's an example that will only write out configuration for the unixsocket tunable if a value was set by the user:

{{#if cfg.unixsocket ~}}
unixsocket {{cfg.unixsocket}}
{{/if ~}}

Note The ~ indicates that whitespace should be omitted when rendering

TOML allows you to create sections (called TOML tables) to better organize your configuration variables. For example, your default.toml or user defined TOML could have a [repl] section for variables controlling replication behavior. Here's what that looks like:

[repl]
backlog-size = 200
backlog-ttl = 100
disable-tcp-nodelay = no

When writing your template, you can use the with helper to reduce duplication:

{{#with cfg.repl ~}}
  repl-backlog-size {{backlog-size}}
  repl-backlog-ttl {{backlog-ttl}}
  repl-disable-tcp-nodelay {{disable-tcp-nodelay}}
{{/with ~}}

Helpers can also be nested and used together in block expressions. Here is another example from the redis.config file where the if and with helpers are used together to set up core/redis Habitat services in a leader-follower topology.

{{#if svc.me.follower ~}}
  slaveof {{svc.leader.sys.ip}} {{svc.leader.cfg.port}}
{/if ~}}

Here's an example using each to render multiple server entries:

{{#each cfg.servers as |server| ~}}
server {
  host {{server.host}}
  port {{server.port}}
}
{{/each ~}}

You can also use each with @key and this. Here is an example that takes the [env] section of your default.toml and makes an env file you can source from your run hook:

{{#each cfg.env ~}}
  export {{toUppercase @key}}={{this}}
{{/each ~}}

You would specify the corresponding values in a TOML file using an array of tables like this:

[[servers]]
host = "host-1"
port = 4545

[[servers]]
host = "host-2"
port = 3434

And for both each and unless, you can use @first and @last to specify which item in an array you want to perform business logic on. For example:

"mongo": {
  {{#each bind.database.members as |member| ~}}
    {{#if @first ~}}
      "host" : "{{member.sys.ip}}",
      "port" : "{{member.cfg.port}}"
    {{/if ~}}
  {{/each ~}}
}

Note The @first and @last variables also work with the Habitat helper eachAlive, and in the example above, it would be preferrable to the built-in each helper because it checks whether the service is available before trying to retrieve any values.

For unless, using @last can also be helpful when you need to optionally include delimiters. In the example below, the IP addresses of the alive members returned by the servers binding is comma-separated. The logic check {{#unless @last}}, {{/unless}} at the end ensures that the comma is written after each element except the last element.

{{#eachAlive bind.servers.members as |member| ~}}
  "{{member.sys.ip}}"
  {{#unless @last ~}}, {{/unless ~}} 
{{/eachAlive ~}}]

Habitat Helpers

Habitat's templating flavour includes a number of custom helpers for writing configuration and hook files.

toLowercase Helper

Returns the lowercase equivalent of the given string literal.

my_value={{toLowercase "UPPER-CASE"}}

toUppercase Helper

Returns the uppercase equivalent of the given string literal.

my_value={{toUppercase "lower-case"}}

strReplace Helper

Replaces all matches of a pattern within the given string literal.

my_value={{strReplace "this is old" "old" "new"}}

This sets my_value to "this is new".

pkgPathFor Helper

Returns the absolute filepath to the package directory of the package best resolved from the given package identifier. The named package must exist in the pkg_deps of the plan from which the template resides. The helper will return a nil string if the named package is not listed in the pkg_deps. As result you will always get what you expect and the template won't leak to other packages on the system.

Example Plan Contents:

pkg_deps=("core/jre8")

Example Template:

export JAVA_HOME={{pkgPathFor "core/jre8"}}

Example pointing to specific file in core/nginx package on disk:

{{pkgPathFor "core/nginx"}}/config/fastcgi.conf

eachAlive Helper

Iterates over a collection of members and renders the template for members that are marked alive.

{{~#eachAlive bind.backend.members as |member|}}
server ip {{member.sys.ip}}:{{member.cfg.port}}
{{~/eachAlive}}

toJson Helper

To output configuration data as JSON, you can use the toJson helper.

Given a default.toml that looks like:

[web]
[[servers]]
host = "host-1"
port = 4545
[[servers]]
host = "host-2"
port = 3434

and a template:

{{toJson cfg.web}}

when rendered, it will look like:

{
"servers": [
{
"host": "host-1",
"port": 4545
},
{
"host": "host-2",
"port": 3434
}
]
}

This can be useful if you have a configuration file that is in JSON format and has the same structure as your TOML configuration data.

toToml Helper

The toToml helper can be used to output TOML.

Given a default.toml that looks like:

default.toml
[web]
port = 80

and a template:

{{toToml cfg.web}}

when rendered, it will look like:

port = 80

This can be useful if you have an app that uses TOML as its configuration file format, but may have not been designed for Habitat, and you only need certain parts of the configuration data in the rendered TOML file.

toYaml Helper

The toYaml helper can be used to output YAML.

Given a default.toml that looks like:

default.toml
[web]
port = 80

and a template:

{{toYaml cfg}}

when rendered, it will look like:

---
web:
port: 80

The helper outputs a YAML document (with a line beginning with ---), so it must be used to create complete documents: you cannot insert a section of YAML into an existing YAML document with this helper.

strJoin

The join helper can be used to create a string with the variables in a list with a separator specified by you. For example, where list: ["foo", "bar", "baz"], {{strJoin list ","}} would return "foo,bar,baz".

You cannot join an object (e.g. {{strJoin web}}), but you could join the variables in an object (e.g. {{strJoin web.list "/"}}).

strConcat

The concat helper can be used to connect multiple strings into one string without a separator. For example, {{strConcat "foo" "bar" "baz"}} would return "foobarbaz".\

You cannot concatenate an object (e.g. {{strConcat web}}), but you could concatenate the variables in an object (e.g. {{strConcat web.list}}).


Template Data


Runtime settings

The following settings can be used during a Habitat service's lifecycle. This means that you can use these settings in any of the plan hooks, such as init, or run, and also in any templatized configuration file for your application or service.

These configuration settings are referenced using the Handlebars.js version of mustache-style tags. For an example on how these settings are used in plan hooks, see Add Health Monitoring to a Plan in the getting started tutorial.

sys

These are service settings specified by Habitat and correspond to the network information of the running Habitat service. You can also query these values on a running Supervisor via the Supervisor HTTP API.

version : Version of the Habitat Supervisor.

member_id : Supervisor's member id.

ip : The IP address of the running service.

hostname : The hostname of the running service. Defaults to localhost.

gossip_ip : Listening address for Supervisor's gossip connection.

gossip_port : Listening port for Supervisor's gossip connection.

http_gateway_ip : Listening address for Supervisor's http gateway.

http_gateway_port : Listening port for Supervisor's http gateway

permanent : This is set to true if a Supervisor is being used as a permanent peer, to increase Ring network traffic stability.

pkg

These are package settings specified by Habitat and correspond to the the settings of the package when it was built and installed.

ident : The fully-qualified identifier of a package that consists of origin/name/version/release.

origin : Denotes a particular upstream of a package. This value is pulled from the pkg_origin setting in a plan.

name : The name of the package. This value is pulled from the pkg_name setting in a plan.

version : The version of a package. This value is pulled from the pkg_version setting in a plan.

release : The UTC datetime stamp when the package was built. This value is specified in YYYYMMDDhhmmss format.

deps : An array of runtime dependencies for your package based on the pkg_deps setting in a plan.

env : You package's system path that is set with all of your dependent binaries.

exposes : The port(s) to expose for an application or service. This value is pulled from the pkg_exposes setting in a plan.

exports : A key value pair where the key is what external services consume. The value is stored in your default.toml to be provided when called.

path : The location where the fully-qualified package is installed.

svc_path : The root location of the source files for the Habitat service.

svc_config_path : The location of any templated configuration files for the Habitat service.

svc_data_path : The location of any data files for the Habitat service.

svc_files_path : The location of any gossiped configuration files for the Habitat service.

svc_static_path : The location of any static content for the Habitat service.

svc_var_path : The location of any variable state data for the Habitat service.

svc_pid_file : The location of the Habitat service pid file.

svc_run : The location of the run data for the Habitat service.

svc_user : The value of pkg_svc_user specified in a plan (if not specified, "hab").

svc_group : The value of pkg_svc_group specified in a plan (if not specified, "hab").

cfg

These are settings defined in your templatized configuration file. The values for those settings are pulled from the default.toml file included in your package.


Contents of a Habitat package

During the build process, the hab-plan-build script creates several files that specify dependency, build, and configuration information. When packages are unpacked (extracted) and installed during the initialization phase of a Habitat service, these files define what those packages need to run.

Packages are installed in the /hab/pkgs/ directory, and then further organized in subdirectories corresponding to fully-qualified package identifiers: origin/name/version/release. For more information on package identifiers, see Packages.

BUILD_DEPS

Fully-qualified package identifiers of any build dependencies that your package depends on. These are listed in the root plan.sh file of your plan directory.

BUILD_TDEPS

Fully-qualified package identifiers of any runtime dependencies that the build dependencies for your project depend on. This is essentially a flattened tree of dependencies all the way up to the root dependency (linux-headers in most cases).

BUILDTIME_ENVIRONMENT

A file that contains similar information as the RUNTIME_ENVIRONMENT file, but is constructed from a package's build-time dependencies instead of its runtime dependencies. This file is not currently consumed by any other software in the Habitat ecosystem, but can be used for troubleshooting and informative purposes.

BUILDTIME_ENVIRONMENT_PROVENANCE

A file that provides information on which specific dependencies have influenced the final value of a given variable in the BUILDTIME_ENVIRONMENT file. This file is not currently consumed by any other software in the Habitat ecosystem, but can be used for troubleshooting and informative purposes.

CFLAGS

Additional switches to be passed to the compiler when this package is used as a build dependency.

DEPS

Runtime dependencies for your package. These dependencies are processed by Habitat and their corresponding environment variables (such as PATH and LD_LIBRARY_PATH) are added to the current environment.

FILES

List of all files in this package along with their blake2b checksums. The FILES file itself is signed using hab pkg sign to provide an assurance that its contents haven't been tampered with.

IDENT

The fully-qualified identifier for the package. The format is origin/name/version/release.

INTERPRETERS

If pkg_interpreters is specified in your plan.sh, then this file will be generated and contain a list of absolute paths to any interpreters that a package can provide. Code in a plan.sh may use the fix_interpreter function to replace hardcoded instances of interpreters, such as /bin/env. The location of interpreters in Habitat will be nested under /hab/pkgs/. For more information on interpreters, see the fix_interpreter description in Plan helper functions.

LDFLAGS

Additional switches to be passed to the compiler when this package is used as a build dependency.

LD_RUN_PATH

Additional switches to be passed to the compiler when this package is used as a build dependency.

MANIFEST

A file containing package information, such as checksum, maintainer, build variables, and other metadata specified in plan.sh as well as the contents of the plan.sh itself.

PATH

An absolute path to the bin folder for the package. A fully-qualified package identifier is used, so version and release information is included in the path.

RUNTIME_ENVIRONMENT

A file containing the result of the layering operation of the current package's runtime environment variables on top of those of its dependencies. This is what the build process consults when it processes dependencies, and this is what the Supervisor consults when generating the runtime environment for a supervised process.

RUNTIME_ENVIRONMENT_PROVENANCE

A file that provides information on which specific dependencies have influenced the final value of a given variable in the RUNTIME_ENVIRONMENT file. This file is not currently consumed by any other software in the Habitat ecosystem, but can be used for troubleshooting and informative purposes.

TARGET

The CPU architecture and platform for the package. The format is architecture-platform. For example, x86_64-linux.

TDEPS

Fully-qualified package identifiers of any runtime dependencies that the runtime dependencies for your project depend on. This is essentially a flattened tree of dependencies all the way up to the root dependency (linux-headers in most cases).

SVC_GROUP

The value of pkg_svc_group from a plan. The Habitat Supervisor will try to start a service with this group if it exists.

SVC_USER

The value of pkg_svc_user from a plan. The Habitat Supervisor will try to start a service with this user if it exists.

default.toml

If you have defined a default.toml file in the root of your plan, then it will be included in the same relative location within the installed package directory. For more information on configuration and the default.toml file, see Configuration Updates.

config directory

If you have defined a config subdirectory with a templatized configuration file in your plan, then they will be included in the same relative location within the installed package directory. For more information on templatized configuration files, see Add configuration to plans.

hooks directory

If you have defined a hooks subdirectory with hook scripts in your plan, then they will be included in the same relative location within the installed package directory. Read more about Application Lifecycle hooks.


Habitat Supervisor Log Key Reference

When running services with the Habitat Supervisor you'll see log output similar to this:

redis.default(SR): Initializing
redis.default(SV): Starting
redis.default(O):

The letters in parentheses are called "log keys" and signify what type of log message is being shown. This can be useful for log filtering.

They are mostly organized by the part of the Habitat Supervisor code base that they are running from, so they often are of value to somebody troubleshooting problems in the Habitat Supervisor source code.

The meanings of the keys are as follows:

Key Description
CE Census
CFG Global configuration
CS Create service: When a service is being started
ER Errors
FW Generic file watcher
HG Messages from the HTTP gateway
MN Main
MR Manager
O Standard output
PH Package hooks
PK Package
PT Path
PW Peer file watcher
SC Service configuration
SH Starting a shell with hab sup sh
SI Unix signals
SOT Structured output
SR Service runtime
SU Service updater
SV Supervisor
SY "sys" utility
UCW User-config watcher
UR Users utility
UT Utilities

Habitat Infographics

With Habitat comes a number of concepts - some you may be familiar with, some you may not. Not to worry, here you'll find a set of graphics that will help you and your team better understand the concepts and how they fit together into the larger Habitat ecosystem.

Habitat Architecture Overview

Habitat Architecture Overview infographic Click image to view full size in new tab

Habitat Initial Package Build Flow

Habitat Initial Package Build Flow infographic Click image to view full size in new tab

Habitat Dependency Update Flow

Habitat Dependency Update Flow infographic Click image to view full size in new tab

Habitat Application Rebuild Flow

Habitat Application Rebuild Flow infographic Click image to view full size in new tab

Habitat Promote Packages Through Channels

Habitat Promote Packages Through Channels infographic Click image to view full size in new tab

Habitat Runtime Services Group Binding

Habitat Runtime Services Group Binding infographic Click image to view full size in new tab

Habitat Builder Architecture

Habitat Builder Architecture infographic Click image to view full size in new tab