1. Getting started

forge ships as a single static Go binary. Prebuilt binaries are on the GitHub releases page, or build from source:

sh
# Linux x86_64
curl -LO https://github.com/supanadit/forge/releases/latest/download/forge-linux-amd64
mv forge-linux-amd64 forge
chmod +x forge

# or build from the repo root
go build -o forge ./app

Your first manifest

A manifest is a [[components]] list. Each component carries an ordered ops list — the order is the build lifecycle:

toml
[project]
name = "myapp"

[vars]
VERSION = "1.0"

[[components]]
name = "base"
ops = [
  { packages = ["curl", "ca-certificates"] },
  { mkdir = [{ path = "/opt/app", mode = "0755" }] },
]

[[components]]
name = "app"
ops = [
  { source_install = { type = "archive",
                       url = "https://example.com/app-${VERSION}.tar.gz",
                       strategy = "configure",
                       prefix = "/usr/local/app" } },
  { verify = [{ file = "/usr/local/app/bin/app" }] },
]
sh
forge build forge.toml

That is the whole model: one TOML file, ordered ops, automatic DAG execution. The rest of this page is a reference for everything the manifest can declare.

2. Manifest format

A forge manifest is a TOML file with a [project] header, optional [vars], and one or more [[components]]. Each component has a name and an ops list.

SectionPurpose
[project]Project metadata: name, description.
[vars]Named variables for interpolation. Overridable via env or --var.
[[components]]A build unit with a name and ordered ops list.
opsOrdered list of operations — the build lifecycle.

The order of ops within a component is the execution order. Components are ordered by their dependencies (declared implicitly via variable references or explicitly).

3. Operations reference

Every operation is a single-key table in the ops list. The key names the operation type.

OperationPurpose
packagesInstall packages via the detected OS package manager.
source_installFetch source (archive/git) and build/install it.
binary_installDownload an archive and copy a prebuilt binary into place.
userCreate a system/user account.
mkdirCreate directories with mode and owner.
chownChange ownership of a path.
chmodChange permissions of a path.
copyCopy a file, with optional mode.
touchCreate an empty file.
generateGenerate code (e.g. protoc-c).
verifyCheck files/dirs exist.
rawRun an arbitrary shell command (last resort).

packages

Installs software through the detected OS package manager. Supports build/runtime/remove/conditional categories.

toml
{ packages = ["curl", "git"] }

{ packages = { build    = ["build-essential", "pkg-config"],
               runtime  = ["curl", "ca-certificates"],
               remove   = ["vim-tiny", "nano"],
               conditional = [{ category = "build",
                                when = { var = "POSTGRESQL_VERSION", gte = "17" },
                                packages = ["bison", "flex"] }] } }
  • build — installed, then removed after all components finish.
  • runtime — installed and kept (spared by the cleanup).
  • remove — explicitly stripped once after all components finish.
  • conditional — gate packages behind a structured version condition.

source_install

Fetches source (archive or git) and builds/installs it. Supports before/after hooks.

toml
{ source_install = { type = "archive",
                     url = "https://example.com/app-${VERSION}.tar.gz",
                     ref = "",
                     strategy = "configure",
                     prefix = "/usr/local/app",
                     flags = ["--with-ssl"],
                     jobs = 4,
                     before = [{ raw = "echo preparing" }],
                     after  = [{ verify = [{ file = "/usr/local/app/bin/app" }] }] } }

binary_install

Downloads an archive and copies prebuilt binaries into place.

toml
{ binary_install = { url = "https://example.com/tools.tar.gz",
                     copy = [{ from = "tool", to = "/usr/local/bin/tool",
                               mode = "0755" }] } }

4. Build strategies

The strategy field in source_install selects the build system:

StrategyWhat it runs
configure./configure && make
autogenautoreconf -fi && ./configure && make
cmakecmake && make
mesonmeson setup && ninja
makemake && make install
detectAuto-select from the source tree

5. Variable interpolation

${VAR} and ${VAR:-default} resolve from [vars], the environment, and --var overrides.

Precedence: vars < env < overrides.

toml
[vars]
VERSION = "1.0"

[[components]]
name = "app"
ops = [
  { source_install = { url = "https://example.com/app-${VERSION}.tar.gz",
                       strategy = "configure" } },
]

6. Build cache

Caching is fully automatic. A component's cache key hashes its ops, the vars it references, and its dependencies' keys.

  • A cache hit is trusted only if the step's output files still exist.
  • On a hit whose files vanished (e.g. a Docker layer rebuild), forge restores from an artifact archive.
  • With BuildKit, mount the cache dir: RUN --mount=type=cache,target=/var/cache/forge ... --cache-dir /var/cache/forge.
  • CI cache export (--cache-to type=gha,mode=max) persists artifacts too.
Cache directory

The default cache directory is OS-specific. Override with --cache-dir or FORGE_CACHE_DIR.

7. OS detection & package mapping

forge detects the OS package manager automatically:

  • 1. --pkg-manager flag, then FORGE_PKG_MANAGER env var.
  • 2. /etc/os-release — ID, then ID_LIKE (e.g. rhel fedora).
  • 3. Binary lookup on PATH: apt-get, dnf, yum, apk.
  • 4. Otherwise an error naming the supported managers.

Package name mapping

Package names differ across distros. forge maps the most common ones automatically:

Logical nameaptdnf/yumapk
build-essentialbuild-essentialgcc gcc-c++ makebuild-base
pkg-configpkg-configpkgconf-pkg-configpkgconf
libfoo-devlibfoo-devlibfoo-devellibfoo-dev

Unknown names pass through unchanged with a warning.

8. CLI commands

CommandPurpose
forge init [dir]Scaffold a new forge.toml.
forge validate <manifest.toml>Validate without executing.
forge build <manifest.toml>Build from a manifest.

Build flags

FlagPurpose
--parallel NMax concurrent steps (0 = auto).
--dry-runPrint the execution plan without running.
--fail-fast=falseContinue past failed steps.
-v, --verboseStream live command output.
--var KEY=VALUEOverride a manifest var (repeatable).
--no-cacheDisable the build cache.
--cache-dir DIROverride the cache directory.
--pkg-manager PMOverride OS package manager.

9. Architecture

forge follows clean architecture conventions. The codebase is organized into domain, service, repository, and CLI layers.

Repository: <https://github.com/supanadit/forge>