diff --git a/.env.example b/.env.example index 9d7d73b..25d9b66 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 @@ -38,12 +39,13 @@ 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 -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/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e78a36b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,100 @@ +# 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. 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 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 + +- 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 diff --git a/Dockerfile b/Dockerfile index 2d62836..e852434 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,30 @@ 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}') \ + && [ -n "${EXPECTED}" ] \ + && 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 +459,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