From 93e08fa3b3b242dc98aa0a338039b00d279a98af Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Mon, 21 Sep 2026 07:45:54 -0400 Subject: [PATCH 1/2] feat: add terraform and tflint to Docker container Install terraform 1.16.3 and tflint 0.64.0 as prebuilt binaries in the runtime stage. Both are downloaded from their official release sources with TARGETARCH resolution so the multiarch build works for amd64 and arm64. --- Dockerfile | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Dockerfile b/Dockerfile index 7be9743c..9712099c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 From f2334b9c881b4a800fd7a34b210c4a2bb26e9239 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Mon, 21 Sep 2026 07:55:11 -0400 Subject: [PATCH 2/2] docs: archive add-terraform-tflint-dockerfile change Archive the OpenSpec change and sync the dockerfile-dependencies spec with the new terraform and tflint requirements. --- .../.openspec.yaml | 2 + .../design.md | 35 +++++++++++++ .../proposal.md | 32 ++++++++++++ .../specs/dockerfile-dependencies/spec.md | 49 +++++++++++++++++++ .../tasks.md | 13 +++++ .../specs/dockerfile-dependencies/spec.md | 48 ++++++++++++++++++ 6 files changed, 179 insertions(+) create mode 100644 openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/.openspec.yaml create mode 100644 openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/design.md create mode 100644 openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/proposal.md create mode 100644 openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/specs/dockerfile-dependencies/spec.md create mode 100644 openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/tasks.md diff --git a/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/.openspec.yaml b/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/.openspec.yaml new file mode 100644 index 00000000..563fab5a --- /dev/null +++ b/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-09-21 diff --git a/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/design.md b/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/design.md new file mode 100644 index 00000000..d02e87ed --- /dev/null +++ b/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/design.md @@ -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. diff --git a/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/proposal.md b/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/proposal.md new file mode 100644 index 00000000..dc0f899a --- /dev/null +++ b/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/proposal.md @@ -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. diff --git a/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/specs/dockerfile-dependencies/spec.md b/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/specs/dockerfile-dependencies/spec.md new file mode 100644 index 00000000..1d20746b --- /dev/null +++ b/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/specs/dockerfile-dependencies/spec.md @@ -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 diff --git a/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/tasks.md b/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/tasks.md new file mode 100644 index 00000000..89fe88de --- /dev/null +++ b/openspec/changes/archive/2026-09-21-add-terraform-tflint-dockerfile/tasks.md @@ -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` diff --git a/openspec/specs/dockerfile-dependencies/spec.md b/openspec/specs/dockerfile-dependencies/spec.md index 3e518df6..5244c721 100644 --- a/openspec/specs/dockerfile-dependencies/spec.md +++ b/openspec/specs/dockerfile-dependencies/spec.md @@ -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 +