Build Resources
Many builds and tests need a helper process that is not itself a build artifact — a Postgres test container, a local registry, an emulator. Declaring one as a resource lets Grog manage its lifecycle:
- It is started at most once per invocation, no matter how many targets depend on it.
- It starts lazily: a fully cached build never starts the resource at all.
- It is torn down automatically when the build finishes, even when the build fails or is interrupted.
- It is never cached and does not affect the change hash of its dependents: restarting a resource does not invalidate any target.
Declaring a resource
Section titled “Declaring a resource”resources: - name: postgres up: | docker run -d --rm --name "grog-pg-$GROG_RESOURCE_ID" -p 0:5432 postgres:16 PORT=$(docker port "grog-pg-$GROG_RESOURCE_ID" 5432 | head -1 | cut -d: -f2) echo "PGPORT=$PORT" >> "$GROG_RESOURCE_EXPORTS_FILE" ready: pg_isready -h 127.0.0.1 -p "$PGPORT" down: docker rm -f "grog-pg-$GROG_RESOURCE_ID" exports: PGHOST: 127.0.0.1
targets: - name: integration_test dependencies: [":postgres"] command: go test ./...Targets depend on resources through the ordinary dependencies list. When integration_test needs to run, Grog starts the resource, waits until it is ready, and injects the exported variables (PGHOST, PGPORT) into the target’s environment.
Fields Reference
Section titled “Fields Reference”| Field | Type | Description |
|---|---|---|
name | string | Unique identifier for the resource within its package |
up | string | Command that starts the resource. Must return once the resource is running (daemonize it) |
down | string | Optional command that stops the resource |
ready | string | Optional probe command, polled until it exits 0 before dependents run |
timeout | string | Bounds the start phase (up + ready polling) and, separately, down. Defaults to 5m |
exports | Record<string, string> | Environment variables published to the commands of directly dependent targets |
dependencies | label[] | Targets built, and resources started, before this resource starts — test targets are not allowed |
Lifecycle
Section titled “Lifecycle”- The first executing (i.e. not cached) target that depends on the resource triggers
up. Concurrent targets wait for the same start. - If
readyis set, it is polled every 250ms until it succeeds ortimeoutis exceeded. - Dependent target commands run with the resource’s exports in their environment.
- When the build finishes — successfully, with failures, or interrupted —
downruns for every started resource in reverse start order.
If up fails or ready times out, every dependent target that needed the resource fails; down still runs at the end of the build so partial starts are cleaned up.
Composing resources
Section titled “Composing resources”A resource may depend on another resource. Grog starts dependencies first and tears them down last, and the dependency’s exports are available to the dependent resource’s up, ready, and down commands:
resources: - name: database up: ... exports: DB_DSN: postgres://127.0.0.1:5432/app
- name: migrations dependencies: [":database"] up: migrate --database "$DB_DSN" upExports only flow one level: a target depending on :migrations sees the exports of :migrations, not those of :database. Declare both dependencies if the target needs both.
Dynamic exports
Section titled “Dynamic exports”Static exports cover fixed values. For values only known after startup — such as a dynamically allocated host port — the up command can append KEY=VALUE lines to the file at $GROG_RESOURCE_EXPORTS_FILE. Dynamic exports override static ones with the same key and are also visible to the ready and down commands.
Environment variables
Section titled “Environment variables”Grog injects the following variables into up, ready, and down:
| Variable Name | Description |
|---|---|
GROG_RESOURCE | The full label of the resource, e.g. //services/db:postgres |
GROG_RESOURCE_ID | A short identifier derived from the resource’s label and definition — stable across runs, changes when the definition changes. Use it to name containers so lifecycle commands stay idempotent |
GROG_RESOURCE_EXPORTS_FILE | (up only) File to append dynamic KEY=VALUE exports to |
GROG_PACKAGE | The path to the package directory |
GROG_WORKSPACE_ROOT | The workspace root directory |
GROG_OS / GROG_ARCH / GROG_PLATFORM | Grog’s target operating system, architecture, and platform |
Caching semantics
Section titled “Caching semantics”A resource is an ambient runtime dependency, not a build input:
- Resources contribute nothing to the change hash of their dependents. Two runs with identical inputs are cache hits even if the resource restarted in between.
- If a resource’s identity should invalidate a consumer — say the Postgres major version — declare it in the consumer’s
fingerprint:fingerprint: { postgres: "16" }.