Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ RUN apt-get update && \
sed -i 's/^#*PermitEmptyPasswords.*/PermitEmptyPasswords yes/' /etc/ssh/sshd_config && \
printf '%s\n' 'AcceptEnv *' >> /etc/ssh/sshd_config

# Terraform & tflint — downloaded binaries (not in Debian repos)
ARG TARGETARCH
RUN TF_VER="1.16.3" && \
TFLINT_VER="0.64.0" && \
ARCH="${TARGETARCH:-$(uname -m)}" && \
case "$ARCH" in \
amd64|x86_64) ARCH="amd64" ;; \
arm64|aarch64) ARCH="arm64" ;; \
*) echo "Unsupported architecture: $ARCH" && exit 1 ;; \
esac && \
curl -fsSL "https://releases.hashicorp.com/terraform/${TF_VER}/terraform_${TF_VER}_linux_${ARCH}.zip" -o /tmp/terraform.zip && \
unzip -o /tmp/terraform.zip -d /usr/local/bin && \
rm /tmp/terraform.zip && \
curl -fsSL "https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VER}/tflint_linux_${ARCH}.zip" -o /tmp/tflint.zip && \
unzip -o /tmp/tflint.zip -d /usr/local/bin && \
rm /tmp/tflint.zip && \
chmod +x /usr/local/bin/terraform /usr/local/bin/tflint

# Environment
ENV HOME=/home/madz
WORKDIR /app
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-21
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Context

The madz container is a polyglot development environment. The runtime stage already installs a broad set of system packages plus infrastructure CLIs (vault, gh). Terraform and tflint are not available in Debian's repos, so they must be installed as prebuilt binaries. The image is built for both amd64 and arm64 via buildx (`docker:release:all`), and also via plain `docker build` (`docker:build`).

## Goals / Non-Goals

**Goals:**
- Install `terraform` and `tflint` in the runtime stage so the agent can plan/apply/lint IaC.
- Support both amd64 and arm64 architectures.
- Keep the install deterministic (pinned versions) and reproducible.

**Non-Goals:**
- No terraform provider plugins or tfstate backend.
- No other HashiCorp tools.
- No change to the multiarch build pipeline itself.

## Decisions

**Decision 1: Download prebuilt binaries instead of apt/package installs.**
Neither terraform nor tflint ships in Debian's repos. HashiCorp publishes terraform on `releases.hashicorp.com`; tflint publishes on GitHub releases. Both provide `linux_amd64` and `linux_arm64` zips. Downloading the exact arch zip and unzipping into `/usr/local/bin` is the standard approach and matches how vault was added previously.

**Decision 2: Resolve architecture via `TARGETARCH` with a `uname -m` fallback.**
Buildx sets `TARGETARCH` automatically per platform (`amd64`/`arm64`) for multiarch builds. Plain `docker build` does not set it, so the fallback `uname -m` maps `x86_64`→`amd64` and `aarch64`→`arm64`. A `case` statement normalizes both and rejects unsupported architectures rather than pulling the wrong binary.

**Decision 3: Pin exact versions.**
`TF_VER="1.16.3"` and `TFLINT_VER="0.64.0"` are pinned in the `RUN` block for reproducibility. This matches the project's pattern of pinning tool versions (e.g., vault was pinned).

**Decision 4: Install into `/usr/local/bin` and `chmod +x`.**
Both binaries land in `/usr/local/bin`, which is on `PATH` for the `madz` user. The zip extraction preserves the executable bit, but `chmod +x` is applied defensively.

## Risks / Trade-offs

- [Unsupported host architecture (e.g., 386, s390x)] → The `case` statement exits with an error rather than silently installing a wrong-arch binary. Only amd64/arm64 are supported, matching the multiarch build matrix.
- [Image size increase] → ~70 MB across both binaries. Acceptable for a dev container; no runtime dependency.
- [Version drift] → Versions are pinned, so the image is reproducible. Updating requires a deliberate change to the `RUN` block.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## Why

The madz container is a complete development environment for an orchestrator/coding agent. It already ships polyglot toolchains (Go, Rust, Java, Python) and infrastructure CLIs (vault, gh). Terraform and tflint are missing, so the agent cannot plan, apply, or lint infrastructure-as-code within the container. Adding them closes that gap.

## What Changes

- Add `terraform` (v1.16.3) to the Dockerfile runtime stage as a prebuilt binary.
- Add `tflint` (v0.64.0) to the Dockerfile runtime stage as a prebuilt binary.
- Both are downloaded from their official release sources (HashiCorp releases and GitHub releases) rather than Debian repos, which do not ship them.
- Architecture resolution uses `TARGETARCH` (set by buildx for multiarch builds) with a `uname -m` fallback for plain `docker build`, so amd64 and arm64 both work.

## Capabilities

### New Capabilities

None.

### Modified Capabilities

- `dockerfile-dependencies`: Adds two new requirements — terraform and tflint must be installed in the container image, with architecture-aware download.

## Impact

- `Dockerfile` — runtime stage gains a new `RUN` block that downloads and installs terraform and tflint.
- No application code, dependencies, or runtime behavior changes.
- Image size increases by roughly 70 MB (two binaries) across both architectures.

## Non-goals

- Not adding terraform provider plugins or a tfstate backend.
- Not adding other HashiCorp tools (e.g., packer, nomad, consul).
- Not changing the multiarch build pipeline itself — it already supports amd64 and arm64.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## ADDED Requirements

### Requirement: terraform must be installed in the container image

The Dockerfile SHALL include `terraform` in the runtime stage, ensuring the terraform CLI binary is present in every built container image. The binary SHALL be downloaded from HashiCorp releases for the target architecture (amd64 or arm64).

#### Scenario: terraform is installed in the Dockerfile runtime stage
- **WHEN** the Dockerfile runtime stage is parsed
- **THEN** a `RUN` block downloads and installs terraform into `/usr/local/bin`

#### Scenario: terraform is available in the container
- **WHEN** the container is built and started
- **THEN** `terraform version` executes successfully without "command not found"

#### Scenario: terraform supports the target architecture
- **WHEN** the container is built for amd64 or arm64
- **THEN** the correct architecture binary is downloaded and installed

### Requirement: tflint must be installed in the container image

The Dockerfile SHALL include `tflint` in the runtime stage, ensuring the tflint CLI binary is present in every built container image. The binary SHALL be downloaded from GitHub releases for the target architecture (amd64 or arm64).

#### Scenario: tflint is installed in the Dockerfile runtime stage
- **WHEN** the Dockerfile runtime stage is parsed
- **THEN** a `RUN` block downloads and installs tflint into `/usr/local/bin`

#### Scenario: tflint is available in the container
- **WHEN** the container is built and started
- **THEN** `tflint --version` executes successfully without "command not found"

#### Scenario: tflint supports the target architecture
- **WHEN** the container is built for amd64 or arm64
- **THEN** the correct architecture binary is downloaded and installed

### Requirement: Architecture resolution for terraform and tflint

The Dockerfile SHALL resolve the target architecture using the `TARGETARCH` build argument when set (multiarch buildx builds), falling back to `uname -m` for plain `docker build`. Unsupported architectures SHALL cause the build to fail rather than installing a wrong-architecture binary.

#### Scenario: Multiarch build resolves architecture from TARGETARCH
- **WHEN** the image is built with buildx for `linux/amd64` or `linux/arm64`
- **THEN** `TARGETARCH` is used to select the correct terraform and tflint binary

#### Scenario: Plain docker build resolves architecture from uname
- **WHEN** the image is built with `docker build` (no TARGETARCH set)
- **THEN** `uname -m` is used to select the correct terraform and tflint binary

#### Scenario: Unsupported architecture fails the build
- **WHEN** the image is built for an architecture other than amd64 or arm64
- **THEN** the build fails with an error rather than installing a wrong-architecture binary
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## 1. Dockerfile Implementation

- [x] 1.1 Add `ARG TARGETARCH` to the runtime stage for architecture resolution
- [x] 1.2 Add a `RUN` block that downloads and installs terraform (pinned to 1.16.3) into `/usr/local/bin`
- [x] 1.3 Add a `RUN` block that downloads and installs tflint (pinned to 0.64.0) into `/usr/local/bin`
- [x] 1.4 Resolve architecture via `TARGETARCH` with a `uname -m` fallback, mapping `x86_64`→`amd64` and `aarch64`→`arm64`
- [x] 1.5 Fail the build on unsupported architectures rather than installing a wrong-arch binary

## 2. Verification

- [x] 2.1 Build the image on amd64 and verify `terraform version` returns v1.16.3
- [x] 2.2 Verify `tflint --version` returns 0.64.0 in the built image
- [x] 2.3 Build the image on arm64 via buildx and confirm `TARGETARCH` resolves to `arm64`
48 changes: 48 additions & 0 deletions openspec/specs/dockerfile-dependencies/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,51 @@ The Dockerfile SHALL NOT hardcode a default timezone. The container defaults to
- **WHEN** the container is started with `TZ=America/Toronto`
- **THEN** the system timezone reflects Eastern Time (EST/EDT)

### Requirement: terraform must be installed in the container image

The Dockerfile SHALL include `terraform` in the runtime stage, ensuring the terraform CLI binary is present in every built container image. The binary SHALL be downloaded from HashiCorp releases for the target architecture (amd64 or arm64).

#### Scenario: terraform is installed in the Dockerfile runtime stage
- **WHEN** the Dockerfile runtime stage is parsed
- **THEN** a `RUN` block downloads and installs terraform into `/usr/local/bin`

#### Scenario: terraform is available in the container
- **WHEN** the container is built and started
- **THEN** `terraform version` executes successfully without "command not found"

#### Scenario: terraform supports the target architecture
- **WHEN** the container is built for amd64 or arm64
- **THEN** the correct architecture binary is downloaded and installed

### Requirement: tflint must be installed in the container image

The Dockerfile SHALL include `tflint` in the runtime stage, ensuring the tflint CLI binary is present in every built container image. The binary SHALL be downloaded from GitHub releases for the target architecture (amd64 or arm64).

#### Scenario: tflint is installed in the Dockerfile runtime stage
- **WHEN** the Dockerfile runtime stage is parsed
- **THEN** a `RUN` block downloads and installs tflint into `/usr/local/bin`

#### Scenario: tflint is available in the container
- **WHEN** the container is built and started
- **THEN** `tflint --version` executes successfully without "command not found"

#### Scenario: tflint supports the target architecture
- **WHEN** the container is built for amd64 or arm64
- **THEN** the correct architecture binary is downloaded and installed

### Requirement: Architecture resolution for terraform and tflint

The Dockerfile SHALL resolve the target architecture using the `TARGETARCH` build argument when set (multiarch buildx builds), falling back to `uname -m` for plain `docker build`. Unsupported architectures SHALL cause the build to fail rather than installing a wrong-architecture binary.

#### Scenario: Multiarch build resolves architecture from TARGETARCH
- **WHEN** the image is built with buildx for `linux/amd64` or `linux/arm64`
- **THEN** `TARGETARCH` is used to select the correct terraform and tflint binary

#### Scenario: Plain docker build resolves architecture from uname
- **WHEN** the image is built with `docker build` (no TARGETARCH set)
- **THEN** `uname -m` is used to select the correct terraform and tflint binary

#### Scenario: Unsupported architecture fails the build
- **WHEN** the image is built for an architecture other than amd64 or arm64
- **THEN** the build fails with an error rather than installing a wrong-architecture binary

Loading