Container

How to use the dev-toolchain container image with the Makefile delegation pattern.

The dev-toolchain container is the heart of DevRail. It packages every linter, formatter, security scanner, and test runner into a single Docker image, ensuring consistent tool versions and behavior across every machine.

Pulling the Image

The dev-toolchain image is hosted on GitHub Container Registry (GHCR):

# Pull the latest major version (recommended for most projects)
docker pull ghcr.io/devrail-dev/dev-toolchain:v1

# Pull a specific version (for reproducible builds)
docker pull ghcr.io/devrail-dev/dev-toolchain:v1.3.2

How It Works with the Makefile

The Makefile uses a two-layer delegation pattern to ensure all tools run inside the container.

Layer 1: User-Facing Targets (Host)

Public targets run on the host machine. Their sole responsibility is to invoke the dev-toolchain Docker container with the corresponding internal target.

# Layer 1 -- public targets delegate to Docker
DEVRAIL_IMAGE ?= ghcr.io/devrail-dev/dev-toolchain:v1
DOCKER_RUN    ?= docker run --rm -v "$$(pwd):/workspace" -w /workspace $(DEVRAIL_IMAGE)

lint: ## Run all linters
	$(DOCKER_RUN) make _lint

format: ## Run all formatters
	$(DOCKER_RUN) make _format

Layer 2: Internal Targets (Container)

Internal targets run inside the Docker container. They execute the actual tool commands. Internal targets are prefixed with _ and are not intended for direct invocation by users.

# Layer 2 -- internal targets execute inside the container
_lint:
	ruff check .
	shellcheck scripts/*.sh

_format:
	ruff format .
	shfmt -w scripts/*.sh

Delegation Flow

Developer / CI / AI Agent
      |
      v
  make lint          (Layer 1: runs on host)
      |
      v
  docker run ...     (container startup)
      |
      v
  make _lint         (Layer 2: runs inside container)
      |
      v
  ruff, shellcheck   (actual tool execution)

Why Two Layers?

  1. Environment consistency. All tools run inside the same container, with the same versions, on every machine. No “works on my machine” drift.
  2. Zero host dependencies. Developers only need Docker and Make installed. All linters, formatters, scanners, and test runners live in the container.
  3. Identical behavior everywhere. Local development, CI pipelines, and AI agents all invoke the same targets and get the same results.

Exceptions

  • make install-hooks runs directly on the host because pre-commit hooks must be installed in the host’s git repository, not inside a container.
  • make help runs on the host because it only needs to parse the Makefile itself.

Version Pinning

Major-Version Floating Tag (Default)

Templates default to ghcr.io/devrail-dev/dev-toolchain:v1. This floating tag is updated with every release within the v1 major version. Non-breaking tool updates propagate automatically.

# .devrail.yml or Makefile variable
# Uses the floating major version tag
DEVRAIL_IMAGE ?= ghcr.io/devrail-dev/dev-toolchain:v1

Exact Version Pinning

For projects requiring reproducible builds, pin to an exact semver tag:

# Pin to an exact version for reproducibility
DEVRAIL_IMAGE ?= ghcr.io/devrail-dev/dev-toolchain:v1.3.2

Tagging Strategy

TagExampleBehavior
Major floatingv1Updated on every release; non-breaking updates propagate automatically
Exact semverv1.3.2Immutable; never updated after publication

Weekly builds publish both an exact semver tag (e.g., v1.3.2) and update the floating major tag (v1). Breaking changes bump the major version.

Upgrading

To upgrade to a new exact version:

# Update the version in your Makefile
DEVRAIL_IMAGE ?= ghcr.io/devrail-dev/dev-toolchain:v1.4.0

If you use the floating v1 tag, upgrades happen automatically when the container image is re-pulled.

Multi-Architecture Support

The dev-toolchain image is built for both linux/amd64 and linux/arm64 architectures:

ArchitectureUsed By
linux/amd64Most CI runners (GitHub Actions, GitLab CI), Intel/AMD desktops
linux/arm64Apple Silicon (M1/M2/M3) development machines, ARM-based CI runners

Docker automatically selects the correct platform for your machine. No manual configuration is needed.

Tools Included

The dev-toolchain container includes all tools needed for every supported language ecosystem:

Python

ToolPurpose
ruffLinting and formatting
banditSecurity-focused static analysis
semgrepMulti-language static analysis
pytestTest runner
mypyStatic type checking

Bash

ToolPurpose
shellcheckStatic analysis for shell scripts
shfmtShell script formatting
batsBash Automated Testing System

Terraform

ToolPurpose
tflintTerraform-specific linting
terraform fmtCanonical HCL formatting (via Terraform CLI)
tfsecTerraform security scanning
checkovPolicy-as-code scanning
terratestInfrastructure testing (Go-based)
terraform-docsModule documentation generation

Ansible

ToolPurpose
ansible-lintPlaybook and role linting
moleculeRole testing framework

Universal

ToolPurpose
trivyVulnerability scanning (filesystem and images)
gitleaksSecret detection
pre-commitGit hook management

Running Tools Directly

While the Makefile delegation pattern is the recommended approach, you can invoke the container directly for debugging or exploration:

# Open an interactive shell inside the container
docker run --rm -it -v "$(pwd):/workspace" -w /workspace \
  ghcr.io/devrail-dev/dev-toolchain:v1 bash

# Run a specific tool directly
docker run --rm -v "$(pwd):/workspace" -w /workspace \
  ghcr.io/devrail-dev/dev-toolchain:v1 ruff check .

# Run make check inside the container
docker run --rm -v "$(pwd):/workspace" -w /workspace \
  ghcr.io/devrail-dev/dev-toolchain:v1 make _check