Build path resolution
When you run erun build, erun push, or erun deploy locally, ERun has to resolve four things: which project root to operate against, which environment to assume, which build directory is the Docker context, and which version to tag the image with. This page documents how each is resolved.
This is reference material — for everyday use you don't need to think about it. See Configuration overview for the field-level reference.
1. Project root
- If
--project-rootis supplied (internal flag, used by tests), use that. - Otherwise, walk up from the current working directory. At each level, check for a
.gitdirectory or file. The first match's containing directory is the project root. - If the walk reaches the filesystem root with no match, the project root is empty and dependent resolutions degrade (the container registry falls through to the built-in default; the command typically aborts with
NOT_IN_GIT_REPO).
| Error code | Cause |
|---|---|
NOT_IN_GIT_REPO | cwd is not inside a git repository. |
PROJECT_ROOT_INVALID | --project-root was supplied but the path doesn't exist or contains no .git. |
2. Environment
- If
--environmentis supplied, use that. - Otherwise, enumerate every environment of every tenant and compare the resolved project root from step 1 against each env's
localRepoPath. An env matches when the project root is at or below itslocalRepoPath(so a nested working directory still resolves); the longest matching path wins. Envs with nolocalRepoPathare skipped. A tie at the same longest path across different tenants is ambiguous and resolves to no match. On a unique match, pick that tenant'sdefaultenvironment. - If no tenant matches and the cwd is in a git repo, fall back to the global
ERunConfig.default_tenantand that tenant'sdefaultenvironment. - If still unresolved and the caller is interactive (TTY), prompt for tenant + env.
- Otherwise abort with
ENVIRONMENT_NOT_RESOLVED.
| Error code | Cause |
|---|---|
ENVIRONMENT_NOT_RESOLVED | None of the resolution steps produced an env, and no TTY is available for a prompt. |
TENANT_NOT_CONFIGURED | The resolved tenant has no ~/.config/erun/<tenant>/tenant.yaml. |
3. Build context directory
A Dockerfile is in the standard layout iff its absolute path matches:
^<projectRoot>/[^/]+/docker/[^/]+/Dockerfile$
Exactly one path segment between <projectRoot> and docker/, and exactly one between docker/ and Dockerfile. <tenant>-devops/docker/<component>/Dockerfile is the canonical case.
- Standard layout match: Docker context =
<projectRoot>.COPYpaths can reference anything in the project tree (e.g.,COPY erun-cli /src/erun-clifrom a Dockerfile under<tenant>-devops/docker/erun-devops/). - No match (flat layout): Docker context = the directory containing the Dockerfile.
COPYpaths outside that directory are not available.
The image name is filepath.Base(buildDir) — the directory containing the Dockerfile.
The docker/ root walked here is <tenant>-devops/docker by convention, or the paths.docker override from .erun/config.yaml when set (it must still be a directory named docker); the standard-layout match is computed against the configured root.
Overriding the context: paths.dockercontext
The standard-layout match above is purely positional — it keys off docker/ being exactly one segment below the project root. A component nested deeper (e.g. harnesses/pv/docker/<component>/Dockerfile, where docker/ is the third segment) does not match, so its context defaults to the component dir and repo-root-relative COPYs fail even though docker build . from the repo root would work.
Set paths.dockercontext in .erun/config.yaml to choose the context explicitly, independent of path depth:
repo-root— context =<projectRoot>. Use this to let a deeply nested DockerfileCOPYfrom anywhere in the repo.component— context = the component build dir, even at the standard layout where the heuristic would pick the repo root.
Unset keeps the positional heuristic. An unrecognized value fails the build (see the paths: error behaviour).
| Error code | Cause |
|---|---|
NO_BUILDABLE_CONTEXT | The walk found no Dockerfile under any <tenant>-devops/docker/<component>/ and the cwd is not itself a buildable context. |
4. Version
ERun walks a sequence of candidate VERSION files and uses the first one it finds:
<buildDir>/VERSION(the image's pinned version).- Compute the next-up directory:
- If
<buildDir>matches<...>/docker/<image>/, hop up two levels (skipdocker/and<image>/). - Otherwise hop up one.
- If
- From that directory, walk up to the project root. At each level, check for
VERSION. First match wins.
Contents are the bare version string (1.0.76). Trailing newlines are stripped.
For an agent env, the version is then transformed into a snapshot: 1.0.76 becomes 1.0.76-snapshot-20260525143027 (UTC timestamp). For a runtime env, the version is used as-is.
| Error code | Cause |
|---|---|
NO_VERSION_FILE | The walk reached the project root with no VERSION found anywhere. |
INVALID_VERSION_FILE | A VERSION file was found but contains non-semver content or extra non-whitespace lines. |
5. Final image tag
<registry>/<image-name>:<version>
<registry>— thebuild-marked registry from the container registries list.<image-name>—filepath.Base(buildDir)— the directory name where the Dockerfile lives.<version>— the resolved version, snapshot-stamped for agent envs.
Example
A Dockerfile at /repo/petios-devops/docker/petios-devops/Dockerfile, in an env whose EnvConfig.containerregistry is 020362606330.dkr.ecr.eu-west-2.amazonaws.com, with a VERSION file at /repo/petios-devops/docker/petios-devops/VERSION reading 1.0.308, in a runtime env, produces:
020362606330.dkr.ecr.eu-west-2.amazonaws.com/petios-devops:1.0.308
In an agent env the tag becomes …/petios-devops:1.0.308-snapshot-<UTC-timestamp> and --release strips the snapshot suffix.