#package-version #package-management #package-json #container #configuration #env-var #shim

bin+lib hbox

CLI tool that leverages container technology to manage packages

10 releases (6 breaking)

0.7.1 May 21, 2024
0.7.0 May 21, 2024
0.6.1 May 19, 2024
0.5.0 May 19, 2024
0.1.1 May 14, 2024

#102 in Configuration

MIT license

66KB
1K SLoC

🅷📦 - hbox

Crates.io Crates.io codecov License Docs

hbox is a Command Line Interface (CLI) that leverages container technology to manage packages, powered by Rust 🦀.

Features

hbox offers the following features:

  • Container Isolation: Uses containers to isolate packages, allowing multiple versions to coexist without conflict.
  • Robust Configuration Options: Enables high customization through configuration files, allowing you to define package aliases and set up automatic volume mounts via config.json.
  • Support for Pipes: Supports the use of pipes in hbox run, enabling efficient command chaining.
  • Convenient Shims: Creates shims (alias shortcuts) for all installed packages, simplifying command entry from hbox run <package alias> <commands> to <package alias> <commands>.
  • Accessible Internal Binaries: Provides direct access to internal binaries within images. Users can override the default entrypoint to access essential tools and utilities within containers directly.
  • Customizable Environment Variables: Allows setting environment variables for each package, enabling finer control over runtime configurations.
  • Support for Docker and Podman: Provides seamless support for both Docker and Podman container engines, offering flexibility in container runtime choices.
  • Custom Images:
    • Building Custom Images: Allows users to build custom images on demand, either replacing existing packages or introducing new ones. Users can define build contexts, Dockerfiles, and build arguments within the configuration file.
    • Dynamic Build Arguments: Supports dynamic and user-defined build arguments, using internal variables like hbox_package_name and hbox_package_version to tailor the build process to specific needs.
    • Registry and Local Images: Manages images pulled from registries and locally built images, providing flexibility in how images are sourced and utilized within the hbox environment.

Installation

To install hbox via cargo, run the following command:

cargo install hbox

Setup

To ensure all hbox binaries and shims can be found, add these lines to your .bashrc or .zshrc file:

export HBOX_DIR="$HOME/.hbox"
export PATH="$HBOX_DIR/shims":$PATH

If you installed hbox via cargo, the hbox binary should already be available in your PATH environment variable when the shims are executed.

Commands

> hbox help
CLI tool that leverages container technology to manage packages.

Usage: hbox <COMMAND>

Commands:
  info    Print debug information about the hbox environment and configuration
  list    List all installed packages and their versions
  add     Add and install a specific version of a package
  remove  Remove a specific version of a package
  use     Set the current version of a package as the default
  run     Run a command from a package
  config  Configure hbox settings
  help    Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version

Usage

Below are some examples demonstrating how you can use hbox:

> hbox version
0.6.0
> hbox info
[System Information]
OS Details:
  Name           : linux
  Architecture   : x86_64
  Family         : unix

[Application Configuration]
Version          : 0.6.0
Engine           : docker
Directories and Files:
  base dir       : /home/helton/.hbox
  config file    : /home/helton/.hbox/config.json
  overrides dir  : /home/helton/.hbox/overrides
  versions dir   : /home/helton/.hbox/versions
  logs dir       : /home/helton/.hbox/logs
  shims dir      : /home/helton/.hbox/shims
  index dir      : /home/helton/.hbox/index
Environment Vars:
  HBOX_DIR       : /home/helton/.hbox
> hbox list
> hbox add jq
latest: Pulling from jqlang/jq
...
Added 'jq' version latest.
> hbox list jq
- jq:
  - latest ✔
> jq --version
jq-1.7.1
> hbox add node latest
latest: Pulling from library/node
...
Added 'node' version 'latest'. Current version is 'latest'.
> hbox list
- [jq]
  - versions
    - latest ✔
- [node]
  - versions
    - latest ✔
> hbox list node
- [node]
  - versions
    - latest ✔
> node --version
v22.0.0
> hbox add node 14 --set-default
14: Pulling from library/node
...
Added 'node' version '14'. Current version is '14'.
> hbox list node
- [node]
  - versions
    - 14 ✔
    - latest
> node --version
v14.21.3
> hbox use node latest
Package 'node' set to version 'latest'
> node --version
v22.0.0
> hbox list node
- [node]
  - versions
    - 14
    - latest ✔

These examples should provide a quick start guide for you to understand the basic operations that you can perform with hbox.

Configuration

Configuration via config.json

The general configuration of hbox is managed by the $HBOX_DIR/config.json file. Currently, you can control the container engine, how logs are used, and enable some experimental features:

{
  "engine": "docker",
  "logs": {
    "enabled": true,
    "level": "debug",
    "strategy": "truncate"
  },
  "experimental": {
    "capture_stdout": false,
    "capture_stderr": false
  }
}

You can edit this file manually or use the hbox config command to update it programmatically. The hbox config command provides a convenient way to read and write configuration settings without directly editing the JSON file. You can use hbox config or its alias hbox configure.

Examples:

  • Set a configuration value:

    hbox config logs.enabled false
    hbox config logs.level debug
    hbox config engine podman
    
  • Get a configuration value:

    hbox config logs.enabled
    hbox config engine
    

The command will either update the configuration file or print the current value of a configuration setting, depending on whether a value is provided.

By using the hbox config command, you can ensure that your configuration changes are applied correctly without the risk of introducing syntax errors into the JSON file.

Properties

The config.json file is used to control how hbox should behave. Below are the details of each property available in this configuration file.

Property Type Description
engine string Indicates what container engine to use. Possible values: docker, podman. Example: docker
logs object Configuration for logging behavior.
logs.enabled boolean Indicates if logging is enabled. Example: true
logs.level string Specifies the logging level. Possible values: debug, info, warn, error. Example: debug
logs.strategy string Strategy for handling log files. Possible values: append, truncate. Example: truncate
experimental object Configuration for experimental features.
experimental.capture_stdout boolean Indicates if standard output should be captured and sent to logs. Regardless of this option, stdout will always be printed normally. Example: false
experimental.capture_stderr boolean Indicates if standard error should be captured and sent to logs. Regardless of this option, stderr will always be printed normally. Example: false

Property Details

  • engine: Specifies whether to use docker or podman as a container engine. docker is the default.

  • logs: This object configures logging behavior for hbox.

    • enabled: A boolean indicating if logging is enabled. If set to true, logging is active.
    • level: Specifies the verbosity of the logs. Options include:
      • debug: Detailed information typically useful for developers.
      • info: General information about the application's operation.
      • warn: Warnings about potentially problematic situations.
      • error: Error messages indicating serious issues.
    • strategy: Determines how log files are managed. Options include:
      • append: Adds new log entries to the end of existing log files.
      • truncate: Overwrites existing log files with new entries.
  • experimental: This object contains settings for experimental features that are not yet fully supported.

    • capture_stdout: A boolean indicating if the standard output of commands should be captured and sent to logs. Regardless of this option, stdout will always be printed normally.
    • capture_stderr: A boolean indicating if the standard error of commands should be captured and sent to logs. Regardless of this option, stderr will always be printed normally.

These properties allow you to customize the behavior of hbox, particularly how it handles logging and experimental features, providing better control over the application's operation.

Shims

hbox utilizes shims to effectively manage your installed packages. There are two types of shims: package shims and binary shims.

Package Shims

For normal packages, the shim will call hbox run with the package name and any provided arguments. Example shim for a package named node:

#!/bin/sh
hbox run node "$@"

Binary Shims

For binaries within a package, the shim will include both the package and the binary name. This informs hbox which package and binary to use. Example shim for a binary mysh within the busybox package:

#!/bin/sh
hbox run busybox::mysh "$@"

Package Registry/Index

The registry/index of packages in hbox is managed in the $HBOX_DIR/index directory. This directory has a sharded structure where each package has an individual file to store configuration information. In the future, this will be centralized in its own repository/server, allowing you to fetch it on demand.

Properties

The configuration below is for index ($HBOX_DIR/index/<shard>/<package>.json) and override ($HBOX_DIR/overrides/<package>.json) files:

Property Type Description
image object The image configuration, which can include build instructions. Example: { "name": "hbox.${hbox_package_name}", "build": { "context": "/path/to/context", "dockerfile": "Dockerfile", "args": { "VERSION": "${hbox_package_version}" }}}
ports array An array of port mappings for the container. Each port mapping has a host and container. Example: [{"host": 8090, "container": 8091}, {"host": 8091, "container": 8092}]
volumes array An array of volume mappings for the container. Each volume mapping has a source and target. Example: [{"source": ".", "target": "/app"}]
current_directory string The working directory inside the container. Example: "/app"
binaries array An array of binaries available in the container. Each binary has a name, path, and optional cmd and wrap_args. Example: [{"name": "tree", "path": "/bin/tree"}, {"name": "mysh", "path": "/bin/sh", "cmd": ["-c"], "wrap_args": true}]
only_shim_binaries boolean Indicates if only the binaries in the package configuration should have shims created. Example: true
environment_variables array An array of environment variables to be set in the container. Each variable has a name and value. Example: [{"name": "foo", "value": "$foo"}, {"name": "bar", "value": "$bar"}]

Property Details

  • image: Specifies the image configuration, which can include build instructions. This allows for dynamic and user-defined build arguments, using internal variables like hbox_package_name and hbox_package_version.

  • ports: Defines the port mappings between the host and the container. Each port mapping includes:

    • host: The port on the host machine.
    • container: The port inside the container.
  • volumes: Defines the volume mappings between the host and the container. Each volume mapping includes:

    • source: The path on the host machine.
    • target: The path inside the container.
  • current_directory: Sets the working directory inside the container. This is where commands will be executed by default.

  • binaries: Lists the binaries available within the container. Each binary includes:

    • name: The name of the binary.
    • path: The path to the binary inside the container.
    • cmd (optional): An array of default command arguments.
    • wrap_args (optional): A boolean indicating if the arguments should be wrapped in quotes.
  • only_shim_binaries: Indicates if only the binaries in the package configuration should have shims created. By default, a shim is created for the package name. If enabled, only the binaries will have shims.

  • environment_variables: Specifies environment variables to be set in the container. Each variable includes:

    • name: The name of the environment variable.
    • value: The value of the environment variable, which can reference host environment variables.

These properties allow you to customize how each package is run within its container, providing flexibility and control over the runtime environment.

Example

Example of a $HBOX_DIR/index/g/golang.json:

{
  "image": {
    "name": "docker.io/golang"
  },
  "volumes": [
    {
      "source": ".",
      "target": "/app"
    }
  ],
  "current_directory": "/app",
  "binaries": [
    {
      "name": "go",
      "path": "/usr/local/go/bin/go"
    },
    {
      "name": "gofmt",
      "path": "/usr/local/go/bin/gofmt"
    }
  ],
  "only_shim_binaries": true
}

Override Configurations

To override package configurations, create an override file inside $HBOX_DIR/overrides instead of modifying the $HBOX_DIR/index folder. This directory is not sharded, so you can directly place your <package>.json files there. The override configurations will take precedence over the index configurations, and the contents will not be merged in memory.

For the properties of the override configuration, check Package Registry/Index.

Example

Example of a $HBOX_DIR/overrides/busybox.json:

{
  "image": {
    "name": "docker.io/busybox"
  },
  "ports": [
    {
      "host": 8090,
      "container": 8000
    },
    {
      "host": 8091,
      "container": 8001
    }
  ],  
  "volumes": [
    {
      "source": ".",
      "target": "/app"
    }
  ],
  "current_directory": "/app",
  "binaries": [
    {
      "name": "tree",
      "path": "/bin/tree"
    },
    {
      "name": "mysh",
      "path": "/bin/sh",
      "cmd": ["-c"],
      "wrap_args": true
    }
  ],
  "only_shim_binaries": true,
  "environment_variables": [
    {
      "name": "HTTP_PROXY",
      "value": "$HTTP_PROXY"
    },
    {
      "name": "HTTPS_PROXY",
      "value": "$HTTPS_PROXY"
    },
    {
      "name": "NO_PROXY",
      "value": "$NO_PROXY"
    }
  ]
}

In the example above, we define mysh as a binary pointing to /bin/sh inside the busybox image. You can use it like this:

> mysh ls -alh

We also set -c as the default command and specified that all arguments should be wrapped in quotes. The full command executed will be:

docker run -it --rm --name hbox-busybox-latest-qNDyEVzrUb -v .:/app -w /app -e HTTP_PROXY=$HTTP_PROXY -e HTTPS_PROXY=$HTTPS_PROXY -e NO_PROXY=$NO_PROXY --entrypoint /bin/sh docker.io/busybox:latest -c "ls -alh"

Package Version Management

hbox maintains a directory $HBOX_DIR/versions that tracks the current version of each package. Each package has a file in this directory, managed by hbox, and should not be manually edited.

Properties

Each package installed with hbox has a version file located in the $HBOX_DIR/versions directory. These files are named <package>.json and track all versions of the package that are installed. Below are the details of each property available in a version file.

Property Type Description
name string The name of the package. Example: "node"
versions array An array of versions of the package that are installed. Example: ["latest", "14", "15"]
current string The version of the package that is currently set as active. Example: "15"

Property Details

  • name: The name of the package. This is a required property and uniquely identifies the package.

  • versions: An array listing all versions of the package that are installed. Each entry in the array is a string representing a version.

  • current: The version of the package that is currently active. This is the version that will be used when the package is executed.

Example

Example of a version file for the node package:

{
  "name": "node",
  "versions": [
    "latest",
    "14",
    "15"
  ],
  "current": "15"
}

This example indicates that the node package has three versions installed (latest, 14, and 15), with version 15 currently set as the active version.

Logs

If you enable logs in your $HBOX_DIR/config.json file, they will appear in the $HBOX_DIR/logs folder. Use this to see what commands are executed under the hood that aren't normally displayed to the user.

Note: Be careful when sharing your logs, as they may contain sensitive information such as API keys and environment variables.

How To

Creating packages from custom images

To build custom images, you need to have a Dockerfile and a .json hbox config.

Let's take the pkl as an example and create a Dockerfile for it:

FROM alpine:latest

ARG VERSION=0.25.3

RUN apk add --no-cache curl

WORKDIR /usr/local/bin

RUN curl -L -o pkl https://github.com/apple/pkl/releases/download/${VERSION}/pkl-alpine-linux-amd64 && \
    chmod +x pkl

ENTRYPOINT ["pkl"]

Since this is a custom package, we need to make it visible to hbox, so we will add it into the $HBOX_DIR/overrides folder as pkl.json. Let's also make use of hbox internal variables to pass values to the Dockerfile and tag the image accordingly:

{
  "image": {
    "name": "hbox.${hbox_package_name}",
    "build": {
      "context": "/home/helton/dockerfiles",
      "dockerfile": "/home/helton/dockerfiles/pkl.Dockerfile",
      "args": {
        "VERSION": "${hbox_package_version}"
      }
    }
  }
}

Now we can safely add pkl as a package into hbox

> hbox add pkl 0.25.3
[+] Building 4.8s (8/8) FINISHED
...
 => => naming to docker.io/library/hbox.pkl:0.25.3  
Added 'pkl' version '0.25.3'. Current version is '0.25.3'.
> which pkl
/home/helton/.hbox/shims/pkl
> pkl --version
Pkl 0.25.3 (Linux 5.15.0-1053-aws, native)

Next steps

If you want to see my ideas for the future of the project, check out the ROADMAP.

Dependencies

~3–4.5MB
~80K SLoC