From 48769933dbfda34e20c7c44c7a5116a16b53ed6c Mon Sep 17 00:00:00 2001 From: Mike Wheway Date: Sun, 14 Jun 2026 16:11:54 -0400 Subject: [PATCH 1/5] feat: Bump Terraform to 1.15.6 and add Terragrunt 1.0.8 - Bump Terraform from 1.14.8 to 1.15.6 (latest stable) - Add Terragrunt 1.0.8 with SHA256SUMS checksum verification - Add Terragrunt shell completions via --install-autocomplete - Update .env.example and README.md with new versions and tool row Co-Authored-By: Claude Fable 5 --- .env.example | 8 +- Dockerfile | 33 ++++- README.md | 1 + ...feat-terraform-bump-terragrunt-add-plan.md | 118 ++++++++++++++++++ 4 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 docs/plans/2026-06-14-001-feat-terraform-bump-terragrunt-add-plan.md diff --git a/.env.example b/.env.example index 9d7d73b..d74c991 100644 --- a/.env.example +++ b/.env.example @@ -14,8 +14,9 @@ # -t devops-toolbox:local . # # VERSION REFERENCE: -# Terraform: https://releases.hashicorp.com/terraform -# Packer: https://releases.hashicorp.com/packer +# Terraform: https://releases.hashicorp.com/terraform +# Packer: https://releases.hashicorp.com/packer +# Terragrunt: https://github.com/gruntwork-io/terragrunt/releases # kubectl: https://kubernetes.io/releases # k9s: https://github.com/derailed/k9s/releases # .NET: https://dotnet.microsoft.com/download/dotnet @@ -42,8 +43,9 @@ STERN_VERSION=v1.33.1 # Infrastructure as code tools ANSIBLE_VERSION=13.5.0 -TERRAFORM_VERSION=1.14.8 +TERRAFORM_VERSION=1.15.6 PACKER_VERSION=1.15.3 +TERRAGRUNT_VERSION=1.0.8 # Database clients POSTGRESQL_CLIENT_VERSION=18 diff --git a/Dockerfile b/Dockerfile index 2d62836..8d61813 100644 --- a/Dockerfile +++ b/Dockerfile @@ -139,7 +139,7 @@ ENV CLOUDSDK_CORE_DISABLE_PROMPTS=1 # to an exact patch version rather than relying on repo availability. # URL: https://developer.hashicorp.com/terraform # ----------------------------------------------------------------------------- -ARG TERRAFORM_VERSION=1.14.8 +ARG TERRAFORM_VERSION=1.15.6 RUN curl -fsSL \ "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_${TARGETARCH}.zip" \ @@ -149,6 +149,29 @@ RUN curl -fsSL \ && chmod +x /usr/local/bin/terraform \ && terraform version +# ----------------------------------------------------------------------------- +# Terragrunt +# Thin wrapper around Terraform and OpenTofu that provides state management, +# DRY configuration via includes, and CLI ergonomics. Installed via direct +# binary download from GitHub releases for exact version pinning. +# Terragrunt uses amd64/arm64 naming — maps directly from TARGETARCH. +# URL: https://terragrunt.gruntwork.io +# ----------------------------------------------------------------------------- +# The terragrunt version specifically does NOT have the v prefix +ARG TERRAGRUNT_VERSION=1.0.8 + +RUN curl -fsSL \ + "https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/terragrunt_linux_${TARGETARCH}" \ + -o /tmp/terragrunt \ + && curl -fsSL \ + "https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/SHA256SUMS" \ + -o /tmp/terragrunt_SHA256SUMS \ + && EXPECTED=$(grep "terragrunt_linux_${TARGETARCH}$" /tmp/terragrunt_SHA256SUMS | awk '{print $1}') \ + && echo "${EXPECTED} /tmp/terragrunt" | sha256sum -c \ + && install -m 755 /tmp/terragrunt /usr/local/bin/terragrunt \ + && rm /tmp/terragrunt /tmp/terragrunt_SHA256SUMS \ + && terragrunt --version + # ----------------------------------------------------------------------------- # Packer # Machine image building tool by HashiCorp. Installed via HashiCorp's official @@ -435,6 +458,14 @@ RUN kubectl completion bash > /etc/bash_completion.d/kubectl # ----------------------------------------------------------------------------- RUN terraform -install-autocomplete || true +# ----------------------------------------------------------------------------- +# Shell completions — Terragrunt +# Uses --install-autocomplete (same approach as Terraform). If the command +# writes per-user config rather than system-wide, fall back to the complete -C +# pattern used by Packer and AWS CLI. +# ----------------------------------------------------------------------------- +RUN terragrunt --install-autocomplete || true + # ----------------------------------------------------------------------------- # Shell completions — Packer # Uses complete -C (same approach as AWS CLI) rather than -autocomplete-install diff --git a/README.md b/README.md index 57eb1af..77c4dd3 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ with Docker and VS Code — no local setup required. |---|---|---| | [Terraform](https://www.terraform.io) | See [Dockerfile](./Dockerfile) | Infrastructure as code | | [Packer](https://developer.hashicorp.com/packer) | See [Dockerfile](./Dockerfile) | Machine image building | +| [Terragrunt](https://terragrunt.gruntwork.io) | See [Dockerfile](./Dockerfile) | Terraform/OpenTofu wrapper with state management, DRY config, and CLI ergonomics | | [kubectl](https://kubernetes.io/docs/reference/kubectl/) | See [Dockerfile](./Dockerfile) | Kubernetes cluster management | | [k9s](https://k9scli.io) | See [Dockerfile](./Dockerfile) | Kubernetes terminal UI | | [kubeseal](https://github.com/bitnami-labs/sealed-secrets) | See [Dockerfile](./Dockerfile) | Kubernetes SealedSecrets CLI | diff --git a/docs/plans/2026-06-14-001-feat-terraform-bump-terragrunt-add-plan.md b/docs/plans/2026-06-14-001-feat-terraform-bump-terragrunt-add-plan.md new file mode 100644 index 0000000..4bae643 --- /dev/null +++ b/docs/plans/2026-06-14-001-feat-terraform-bump-terragrunt-add-plan.md @@ -0,0 +1,118 @@ +--- +title: feat: Bump Terraform to 1.15.6 and add Terragrunt 1.0.8 +type: feat +status: completed +date: 2026-06-14 +--- + +## Summary + +Bump Terraform from 1.14.8 to 1.15.6 and add Terragrunt 1.0.8 as a new tool with checksum verification, shell completions, and matching doc updates. + +## Problem Frame + +The image currently ships Terraform 1.14.8 (November 2025). Terraform 1.15.6 (June 2026) is the latest stable release. Terragrunt — a thin wrapper around Terraform/OpenTofu that provides state management, DRY configuration, and CLI ergonomics — is not currently available in the image despite being a common companion to Terraform in DevOps workflows. + +--- + +## Requirements + +- R1. Terraform version updated from 1.14.8 to 1.15.6 in Dockerfile and `.env.example` +- R2. Terragrunt 1.0.8 installed via direct binary download from GitHub releases +- R3. Terragrunt binary verified against published SHA256SUMS before installation +- R4. Terragrunt shell completions available system-wide via `/etc/bash_completion.d/` +- R5. README.md tool table updated: Terraform version reference, new Terragrunt row +- R6. New Terragrunt install block follows existing Dockerfile conventions: header comment describing the tool, in-line rationale comments, version pin as `ARG`, and a `--version` smoke test + +--- + +## Key Technical Decisions + +- **Checksum verification: yes.** Follows Packer's pattern (download SHA256SUMS, grep the expected hash, pipe to `sha256sum -c`). Most tools in this Dockerfile skip verification; Packer and now Terragrunt are the exceptions. +- **Install method: raw binary, not archive.** Terragrunt publishes a standalone binary alongside `.tar.gz` and `.zip` archives. Downloading the raw binary avoids an extraction step. This matches the kubectl install pattern. +- **Version ARG format: no `v` prefix.** `TERRAGRUNT_VERSION=1.0.8` (not `v1.0.8`). The `v` prefix is baked into the download URL pattern. This matches kubeseal and stern conventions. +- **Shell completions: `terragrunt --install-autocomplete`** per upstream docs. If this writes per-user files rather than system-wide, fall back to the `complete -C` pattern used by Packer and AWS CLI. + +--- + +## Implementation Units + +### U1. Bump Terraform version + +- **Goal:** Update Terraform from 1.14.8 to 1.15.6 +- **Requirements:** R1 +- **Dependencies:** none +- **Files:** `Dockerfile`, `.env.example` +- **Approach:** Change the `TERRAFORM_VERSION` ARG in the Dockerfile and the matching line in `.env.example`. No other Terraform install block changes — the URL pattern (`releases.hashicorp.com`) and zip extraction are unchanged. +- **Patterns to follow:** Single-line version ARG change per the repo's update conventions documented in README.md +- **Test scenarios:** + - Build the image locally and run `terraform version` — verifies the binary downloads, extracts, and reports 1.15.6 +- **Verification:** `docker build` succeeds and `terraform version` inside the container reports 1.15.6 + +### U2. Add Terragrunt install block to Dockerfile + +- **Goal:** Install Terragrunt 1.0.8 with checksum verification +- **Requirements:** R2, R3, R6 +- **Dependencies:** none +- **Files:** `Dockerfile` +- **Approach:** Add a new install block after the Terraform block (logical grouping — both are HashiCorp-ecosystem IaC tools). Download the raw `terragrunt_linux_${TARGETARCH}` binary and the `SHA256SUMS` file. Verify the checksum, then `install -m 755` to `/usr/local/bin/terragrunt`. The block includes: header comment identifying the tool with a URL to its homepage, inline comments explaining each step, the `ARG TERRAGRUNT_VERSION` declaration, and a `terragrunt --version` smoke test. +- **Patterns to follow:** + - Packer block for checksum verification shape (`SHA256SUMS` download, `grep` + `awk` for expected hash, `sha256sum -c`) + - kubectl block for raw binary install (`install -m 755` rather than `chmod +x` after mv) + - General block structure: comment header → ARG → RUN with cleanup → version check +- **Test scenarios:** + - Build the image and run `terragrunt --version` — verifies the binary downloads, passes checksum verification, and reports 1.0.8 + - Build with a deliberately wrong checksum expectation to confirm the `sha256sum -c` gate fails the build +- **Verification:** `terragrunt --version` inside the container reports 1.0.8 + +### U3. Add Terragrunt shell completions + +- **Goal:** Terragrunt tab completion available in bash sessions +- **Requirements:** R4 +- **Dependencies:** U2 (binary must be installed first) +- **Files:** `Dockerfile` +- **Approach:** Run `terragrunt --install-autocomplete` during build. If this command writes to a per-user RC file rather than `/etc/bash_completion.d/`, fall back to the `complete -C` pattern: `echo "complete -C '$(which terragrunt)' terragrunt" > /etc/bash_completion.d/terragrunt`. The install docs show `touch ~/.bashrc` before running `--install-autocomplete`, so the fallback may be necessary. +- **Patterns to follow:** Terraform's `-install-autocomplete || true` for the primary approach; Packer's `complete -C` for the fallback +- **Test scenarios:** + - After build, start a bash session in the container and type `terragrunt ` — verifies completions load without errors +- **Verification:** `complete -p terragrunt` returns a completion specification inside a container bash session + +### U4. Update README.md and .env.example + +- **Goal:** Documentation reflects the new and updated tools +- **Requirements:** R1, R5 +- **Dependencies:** U1, U2 (versions must be finalized) +- **Files:** `README.md`, `.env.example` +- **Approach:** + - `.env.example`: bump `TERRAFORM_VERSION` to 1.15.6, add `TERRAGRUNT_VERSION=1.0.8` in the Infrastructure as code section (near Terraform/Packer) with a release page link in the version reference comment block + - `README.md`: add a Terragrunt row to the tools table (between Packer and kubectl, under "Infrastructure as code" grouping), add Terragrunt to the Repository Structure tree under `dependencies/` if any new dependency file is created (none expected — Terragrunt has no external Python/Ansible deps) +- **Patterns to follow:** Existing table rows use "See [Dockerfile](./Dockerfile)" for version reference +- **Test scenarios:** + - `.env.example`: verify `grep -v '^#' .env.example | grep -v '^$' | sed 's/^/--build-arg /'` passes Terragrunt's ARG correctly to `docker build` +- **Verification:** Visual review of both files confirms correct versions, consistent formatting, and working build arg passthrough + +--- + +## Scope Boundaries + +### Deferred to Follow-Up Work + +- Bumping other tools to their latest versions +- Adding Terragrunt-specific Ansible collections or Python packages (none currently needed) + +--- + +## Risks & Dependencies + +- **Terraform 1.14 → 1.15 minor bump:** HashiCorp maintains backward compatibility within 1.x. No breaking changes are expected in the CLI itself, but consumers should test their own Terraform configurations. +- **Terragrunt `--install-autocomplete` behavior:** The upstream docs show a `touch ~/.bashrc` prerequisite, which suggests the command may write per-user configuration. If so, the `complete -C` fallback in U3 handles this. + +--- + +## Sources & Research + +- Terraform releases: `https://releases.hashicorp.com/terraform/` — 1.15.6 confirmed via GitHub API +- Terragrunt releases: `https://github.com/gruntwork-io/terragrunt/releases` — v1.0.8 confirmed via GitHub API, SHA256SUMS and binary naming verified from release assets +- Terragrunt install docs: `https://terragrunt.gruntwork.io/docs/getting-started/install/` — `--install-autocomplete` for shell completions, raw binary + SHA256SUMS for verification +- Packer install block in `Dockerfile:159-172` — checksum verification pattern to mirror +- kubectl install block in `Dockerfile:285-291` — raw binary install pattern to mirror From ff72e505725510e6f9412e78813dfa3bfe3e4e4e Mon Sep 17 00:00:00 2001 From: Mike Wheway Date: Sun, 14 Jun 2026 16:13:56 -0400 Subject: [PATCH 2/5] docs: Add CLAUDE.md with repo conventions and PR smoke-test gate Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5a1df60 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,98 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What This Repo Is + +A Docker image (Dev Container) that bundles pinned versions of DevOps tooling — Terraform, Packer, kubectl, Helm, Ansible, cloud CLIs, database clients, and Python/.NET SDKs. Published to Docker Hub as `taegost/devops-toolbox`. Consumers pull the image into their projects' `.devcontainer/devcontainer.json`. + +## Build Commands + +```bash +# Local build (needs .env file copied from .env.example) +cp .env.example .env +docker build \ + $(grep -v '^#' .env | grep -v '^$' | sed 's/^/--build-arg /') \ + -t devops-toolbox:local . + +# CI build (multi-arch, no .env needed — ARGs read from Dockerfile defaults) +# Triggered by: push to main, semver tag, weekly cron, workflow_dispatch +# Defined in .github/workflows/build-and-push.yml +``` + +No test suite, no linter. The CI pipeline's PR build is the validation step — it builds (without pushing) to confirm the Dockerfile is valid. + +## Architecture + +### Dockerfile Design + +**Base image:** `mcr.microsoft.com/devcontainers/base:ubuntu-24.04` — provides git, zsh, oh-my-zsh, curl, non-root `vscode` user, pipx. + +**Version pinning pattern:** Each tool's `ARG VERSION` is declared directly above its install block, not grouped at the top. This is intentional — changing one tool's version only invalidates the Docker layer cache from that tool downward, not the entire build. + +**Architecture handling:** `TARGETARCH` is set by Docker Buildx (`amd64` or `arm64`). Tools that use different arch naming (AWS CLI: `x86_64`/`aarch64`, gcloud: `x86_64`/`arm`) remap inline with shell conditionals. + +**Install method priority:** +1. Direct binary download (most tools) — for exact version pinning +2. APT with version pin (Azure CLI only) — because it's a Python app with complex deps +3. pipx (Ansible, Python dev tools) — isolated virtualenvs, no dependency conflicts +4. Microsoft install script (.NET SDK) — needed for feature-band control beyond what APT offers + +**Ansible setup:** Ansible is installed via pipx into an isolated venv. Collections (`dependencies/ansible-requirements.yml`) are baked in at build time. Supporting Python packages (`dependencies/python-ansible-requirements.txt`) are injected into Ansible's venv via `pipx runpip ansible install`. Azure collection deps are installed from the collection's own requirements file post-install. + +**Shell completions:** Written to `/etc/bash_completion.d/` system-wide (not `~/.bashrc`) — available to all users without per-user config. Bash-completion loading is appended to `/etc/bash.bashrc`. + +### Dependency Files + +| File | Purpose | Version Strategy | +|---|---|---| +| `dependencies/ansible-requirements.yml` | Ansible Galaxy collections baked into image | `>=X,` — every build (traceability) +- `1.2.3`, `1.2`, `1` — when a `v1.2.3` Git tag is pushed (semver expansion) +- PR builds: no push, no tags + +**Security:** Images are signed with Cosign against Sigstore/Fulcio public transparency log. Docker Hub README is synced automatically. + +**Required secrets:** `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN` (access token, not password), `DOCKERHUB_IMAGENAME`. + +### Tag Validation + +Two-stage guard against bad semver tags: +1. `Validate tag format` step — regex check that tag matches `vMAJOR.MINOR.PATCH` +2. `Verify semver tags were generated` step — confirms docker/metadata-action produced non-SHA tags + +## Updating a Tool Version + +1. Find the tool's `ARG` in the Dockerfile (directly above its install block) +2. Update the version value +3. Update the matching entry in `.env.example` +4. **MUST:** Stage changes and commit, but do NOT create a PR until the user confirms the Docker build succeeds locally. Wait for the user to smoke test before opening the PR. +5. After user approval, create the PR — CI validates the build +6. Merge to main, then tag a new semver release (`v1.2.3`) to publish versioned tags + +`.env.example` is the quick-reference for current versions — it mirrors all Dockerfile ARGs with links to each tool's release page in comments. + +## ABSOLUTE DIRECTIVE + +- **NEVER create a PR for Dockerfile changes without the user first smoke-testing the Docker build locally.** The CI pipeline takes minutes; a local smoke build catches issues faster. Commit the changes, push the branch, but stop before opening the PR. Tell the user the branch is ready for smoke testing and provide the exact build command. + +## Key Conventions + +- No `weekly` tag — semantically identical to `latest`, would add noise +- `kubeseal` and `stern` version ARGs omit the `v` prefix (unlike other tools) because their GitHub release URLs use bare numbers +- `mariadb-client` comes from Ubuntu APT, not MariaDB Foundation repo — MariaDB 12.x lacks a stable version-addressable APT URL +- Azure Ansible collection Python deps are installed from the collection's own `requirements.txt` (only exists post-install), not pre-listed in `python-ansible-requirements.txt` +- `.dockerignore` excludes `.github`, `.devcontainer`, and README.md — the image doesn't need them From 4f9c894c6e2bede4652d2749fc72636ff2b22f19 Mon Sep 17 00:00:00 2001 From: Mike Wheway Date: Sun, 14 Jun 2026 16:28:05 -0400 Subject: [PATCH 3/5] fix: Remove stray v prefix from STERN_VERSION in .env.example Dockerfile ARG default is 1.33.1 (no v prefix, as documented in its comment). The .env.example had v1.33.1, which doubled the v in the download URL (vv1.33.1) and caused a 404 during docker build. Co-Authored-By: Claude Fable 5 --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index d74c991..25d9b66 100644 --- a/.env.example +++ b/.env.example @@ -39,7 +39,7 @@ KUBESEAL_VERSION=0.36.6 HELM_VERSION=v4.1.4 KUBELOGIN_VERSION=v0.2.17 KUSTOMIZE_VERSION=v5.8.1 -STERN_VERSION=v1.33.1 +STERN_VERSION=1.33.1 # Infrastructure as code tools ANSIBLE_VERSION=13.5.0 From e98dc8d25cfb7672b7d4ba53d4fe753dd5b22249 Mon Sep 17 00:00:00 2001 From: Mike Wheway Date: Sun, 14 Jun 2026 16:46:50 -0400 Subject: [PATCH 4/5] Update: CLAUDE.md with new directives --- CLAUDE.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5a1df60..e78a36b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -79,15 +79,17 @@ Two-stage guard against bad semver tags: 1. Find the tool's `ARG` in the Dockerfile (directly above its install block) 2. Update the version value 3. Update the matching entry in `.env.example` -4. **MUST:** Stage changes and commit, but do NOT create a PR until the user confirms the Docker build succeeds locally. Wait for the user to smoke test before opening the PR. -5. After user approval, create the PR — CI validates the build -6. Merge to main, then tag a new semver release (`v1.2.3`) to publish versioned tags +4. Commit changes and push the branch +5. Run the smoke test (see ABSOLUTE DIRECTIVE below) — report the result +6. Wait for explicit user approval before opening the PR. The user may require additional validation beyond the build. +7. After user approval, create the PR — CI validates the build +8. Merge to main, then tag a new semver release (`v1.2.3`) to publish versioned tags `.env.example` is the quick-reference for current versions — it mirrors all Dockerfile ARGs with links to each tool's release page in comments. ## ABSOLUTE DIRECTIVE -- **NEVER create a PR for Dockerfile changes without the user first smoke-testing the Docker build locally.** The CI pipeline takes minutes; a local smoke build catches issues faster. Commit the changes, push the branch, but stop before opening the PR. Tell the user the branch is ready for smoke testing and provide the exact build command. +- **NEVER open a PR for Dockerfile changes without smoke testing first.** The smoke test is `docker build .` — it must complete successfully. Run it, report the result. The PR is gated by user approval; the user may require additional checks beyond the build. Do not open the PR until the user explicitly says to proceed. ## Key Conventions From 55c97762d61628d73370fd2b756b7977de4eaa5b Mon Sep 17 00:00:00 2001 From: Mike Wheway Date: Sun, 14 Jun 2026 16:56:01 -0400 Subject: [PATCH 5/5] fix: Add empty-guard on Terragrunt checksum extraction If grep finds no match in SHA256SUMS (e.g., binary renamed upstream), EXPECTED was empty and sha256sum -c would fail with a cryptic format error. Added [ -n "${EXPECTED}" ] to fail fast with a clear error. Note: set -o pipefail was considered but the base image uses dash (/bin/sh), which does not support it. The explicit non-empty check is portable and directly addresses the concern. Co-Authored-By: Claude Fable 5 --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 8d61813..e852434 100644 --- a/Dockerfile +++ b/Dockerfile @@ -167,6 +167,7 @@ RUN curl -fsSL \ "https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/SHA256SUMS" \ -o /tmp/terragrunt_SHA256SUMS \ && EXPECTED=$(grep "terragrunt_linux_${TARGETARCH}$" /tmp/terragrunt_SHA256SUMS | awk '{print $1}') \ + && [ -n "${EXPECTED}" ] \ && echo "${EXPECTED} /tmp/terragrunt" | sha256sum -c \ && install -m 755 /tmp/terragrunt /usr/local/bin/terragrunt \ && rm /tmp/terragrunt /tmp/terragrunt_SHA256SUMS \