|
| 1 | +# DO NOT EDIT. Synced from DataDog/dd-repo-tools by the |
| 2 | +# dd-repo-tools devcontainer-bundle campaigner — edits here will be |
| 3 | +# overwritten on the next run. |
| 4 | +# Source of truth: https://github.com/DataDog/dd-repo-tools/blob/main/shared/devcontainer/devcontainer.mk |
| 5 | +# |
| 6 | +# Inputs (set before `include`): |
| 7 | +# DEV_CONTAINER_REPO_ROOT Absolute path to the repo root. |
| 8 | +# DEV_CONTAINER_IMAGE_NAME Override image name. Defaults to |
| 9 | +# registry.ddbuild.io/ci/<repo>/devcontainer. |
| 10 | +# DEV_CONTAINER_REQUIRED_PATHS Whitespace-separated paths that must exist |
| 11 | +# (e.g. submodule checkouts). Checked at |
| 12 | +# recipe time as a prereq of devcontainer- |
| 13 | +# touching targets — missing paths fail |
| 14 | +# that invocation, not parse time. |
| 15 | +# DOCKER_BUILDX_FLAGS Extra flags appended to the local build. |
| 16 | +# |
| 17 | +# This file is the single source of truth for staging + hashing. The |
| 18 | +# GitLab template (devcontainer.yml) calls into `make` so both paths |
| 19 | +# share the same code. |
| 20 | + |
| 21 | +# Recipes need bash + pipefail: the hash pipeline (find | sort | sha256sum | |
| 22 | +# cut) silently produces an empty tag on any pipe failure under POSIX sh, |
| 23 | +# which downstream becomes a malformed `image:` ref. Override after the |
| 24 | +# include if you need a different shell for your own recipes. |
| 25 | +SHELL := bash |
| 26 | +.SHELLFLAGS := -eu -o pipefail -c |
| 27 | + |
| 28 | +DEV_CONTAINER_REPO_ROOT ?= $(CURDIR) |
| 29 | +_DEV_CONTAINER_REPO_NAME := $(notdir $(patsubst %/,%,$(DEV_CONTAINER_REPO_ROOT))) |
| 30 | +DEV_CONTAINER_IMAGE_NAME ?= registry.ddbuild.io/ci/$(_DEV_CONTAINER_REPO_NAME)/devcontainer |
| 31 | + |
| 32 | +_DEV_CONTAINER_CONTEXT_FILES := $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/context.files |
| 33 | +_DEV_CONTAINER_CONTEXT_FILTER := $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/context.filter |
| 34 | +_DEV_CONTAINER_DOCKERFILE := $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/Dockerfile |
| 35 | +_DEV_CONTAINER_STAGED := $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/.staged |
| 36 | + |
| 37 | +# uname -m -> docker --platform / conventional ARCH build-arg. |
| 38 | +_DEV_CONTAINER_UNAME_M := $(shell uname -m) |
| 39 | +ifneq ($(filter $(_DEV_CONTAINER_UNAME_M),arm64 aarch64),) |
| 40 | + _DEV_CONTAINER_PLATFORM := linux/arm64 |
| 41 | + _DEV_CONTAINER_ARCH := aarch64 |
| 42 | +else |
| 43 | + _DEV_CONTAINER_PLATFORM := linux/amd64 |
| 44 | + _DEV_CONTAINER_ARCH := x86_64 |
| 45 | +endif |
| 46 | + |
| 47 | +# Pass --build-arg ARCH only if the Dockerfile actually declares it. |
| 48 | +_DEV_CONTAINER_ARCH_ARG := $(shell grep -q '^ARG ARCH' $(_DEV_CONTAINER_DOCKERFILE) 2>/dev/null && echo --build-arg ARCH=$(_DEV_CONTAINER_ARCH)) |
| 49 | + |
| 50 | +# Skip everything inside the container (no nested docker, no pull). |
| 51 | +_DEV_CONTAINER_INSIDE := $(shell [ -f /.dockerenv ] || [ -n "$$KUBERNETES_SERVICE_HOST" ] && echo 1) |
| 52 | + |
| 53 | +# Stage .devcontainer/context.files (+ optional context.filter) into $$ctx. |
| 54 | +# Caller sets $$ctx (and arranges cleanup if needed). Verifies Dockerfile |
| 55 | +# was staged. |
| 56 | +define _dev_container_stage_into_ctx |
| 57 | +if [ ! -f "$(_DEV_CONTAINER_CONTEXT_FILES)" ]; then \ |
| 58 | + echo "ERROR: $(_DEV_CONTAINER_CONTEXT_FILES) not found" >&2; exit 1; \ |
| 59 | +fi; \ |
| 60 | +filter_arg=; \ |
| 61 | +[ -f "$(_DEV_CONTAINER_CONTEXT_FILTER)" ] && filter_arg="--filter=merge $(_DEV_CONTAINER_CONTEXT_FILTER)"; \ |
| 62 | +grep -Ev '^[[:space:]]*(#|$$)' "$(_DEV_CONTAINER_CONTEXT_FILES)" | \ |
| 63 | + rsync -aR --files-from=- $$filter_arg "$(DEV_CONTAINER_REPO_ROOT)/" "$$ctx/"; \ |
| 64 | +if [ ! -f "$$ctx/.devcontainer/Dockerfile" ]; then \ |
| 65 | + echo "ERROR: .devcontainer/Dockerfile not staged; check .devcontainer/context.files" >&2; \ |
| 66 | + exit 1; \ |
| 67 | +fi |
| 68 | +endef |
| 69 | + |
| 70 | +# Stage into a fresh mktemp dir, cleaned up on shell exit. Sets $$ctx. |
| 71 | +define _dev_container_stage_context |
| 72 | +ctx=$$(mktemp -d); \ |
| 73 | +trap 'rm -rf "$$ctx"' EXIT; \ |
| 74 | +$(_dev_container_stage_into_ctx) |
| 75 | +endef |
| 76 | + |
| 77 | +# Compute the 12-char tag hash from the staged tree at $$ctx. |
| 78 | +define _dev_container_compute_tag |
| 79 | +tag=$$(cd "$$ctx" && find . -type f | LC_ALL=C sort | \ |
| 80 | + while IFS= read -r f; do printf -- '--- %s ---\n' "$$f"; cat "$$f"; done | \ |
| 81 | + sha256sum | cut -c1-12); \ |
| 82 | +[ -n "$$tag" ] || { echo "ERROR: hash pipeline produced empty tag (empty staged context?)" >&2; exit 1; } |
| 83 | +endef |
| 84 | + |
| 85 | +# Git-worktree support: when .git is a gitdir pointer, make the common dir |
| 86 | +# visible inside the container so `git` calls work. |
| 87 | +_DEV_CONTAINER_GIT_COMMON_DIR := $(shell cd $(DEV_CONTAINER_REPO_ROOT) && git rev-parse --path-format=absolute --git-common-dir 2>/dev/null) |
| 88 | +_DEV_CONTAINER_DEFAULT_GIT_DIR := $(DEV_CONTAINER_REPO_ROOT)/.git |
| 89 | +ifneq ($(_DEV_CONTAINER_GIT_COMMON_DIR),) |
| 90 | + ifneq ($(_DEV_CONTAINER_GIT_COMMON_DIR),$(_DEV_CONTAINER_DEFAULT_GIT_DIR)) |
| 91 | + _DEV_CONTAINER_WORKTREE_MOUNT := -v $(_DEV_CONTAINER_GIT_COMMON_DIR):$(_DEV_CONTAINER_GIT_COMMON_DIR) |
| 92 | + endif |
| 93 | +endif |
| 94 | + |
| 95 | +# Precondition check for caller-declared required paths. Wired as a |
| 96 | +# phony prereq below so it fires only when a devcontainer-touching |
| 97 | +# target is actually built — not at parse time, which would block |
| 98 | +# unrelated `make` invocations (lint, format) and CI-entrypoint |
| 99 | +# targets whose own recipe initializes the submodules before |
| 100 | +# sub-making dev-image. |
| 101 | +.PHONY: _dev-container-check-required-paths |
| 102 | +_dev-container-check-required-paths: |
| 103 | + @missing="$(strip $(foreach p,$(DEV_CONTAINER_REQUIRED_PATHS),$(if $(wildcard $(DEV_CONTAINER_REPO_ROOT)/$(p)),,$(p))))"; \ |
| 104 | + if [ -n "$$missing" ]; then \ |
| 105 | + echo "Missing required paths (did you init submodules?): $$missing" >&2; \ |
| 106 | + exit 1; \ |
| 107 | + fi |
| 108 | + |
| 109 | +.PHONY: dev-image dev-image-x86_64 dev-shell dev-image-tag \ |
| 110 | + .devcontainer-stage-context .devcontainer-image-hash |
| 111 | + |
| 112 | +dev-image dev-image-x86_64 dev-shell dev-image-tag \ |
| 113 | +.devcontainer-stage-context .devcontainer-image-hash: _dev-container-check-required-paths |
| 114 | + |
| 115 | +# Stage the build context into a stable path under .devcontainer/.staged/. |
| 116 | +# Invoked from VS Code's initializeCommand (so build.context = `.staged` |
| 117 | +# gets the same narrow tree CI and `make dev-image` use) and from |
| 118 | +# devcontainer.yml's CI job. Hash-computing local targets keep using a |
| 119 | +# mktemp dir for parallel safety; this one writes to a stable path |
| 120 | +# because VS Code (and CI's downstream buildx step) need one. |
| 121 | +.devcontainer-stage-context: |
| 122 | + @rm -rf "$(_DEV_CONTAINER_STAGED)" |
| 123 | + @mkdir -p "$(_DEV_CONTAINER_STAGED)" |
| 124 | + @ctx="$(_DEV_CONTAINER_STAGED)"; $(_dev_container_stage_into_ctx) |
| 125 | + |
| 126 | +# Print the 12-char hash of the already-staged .devcontainer/.staged/ tree. |
| 127 | +# Caller must have run `make .devcontainer-stage-context` first. Used by |
| 128 | +# devcontainer.yml so the tag is computed by the same code that the local |
| 129 | +# Makefile uses. |
| 130 | +.devcontainer-image-hash: |
| 131 | + @ctx="$(_DEV_CONTAINER_STAGED)"; \ |
| 132 | + test -d "$$ctx" || { echo "ERROR: $$ctx not staged; run 'make .devcontainer-stage-context' first" >&2; exit 1; }; \ |
| 133 | + $(_dev_container_compute_tag); \ |
| 134 | + echo "$$tag" |
| 135 | + |
| 136 | +# Print the full image:tag that would be used. Useful for debugging. |
| 137 | +# Works inside or outside a container (no docker required). |
| 138 | +dev-image-tag: |
| 139 | + @$(_dev_container_stage_context); \ |
| 140 | + $(_dev_container_compute_tag); \ |
| 141 | + echo "$(DEV_CONTAINER_IMAGE_NAME):$$tag" |
| 142 | + |
| 143 | +# Shared mounts for any in-container invocation: the repo root, plus the |
| 144 | +# git common dir when running inside a worktree (so `git` works). |
| 145 | +_DEV_CONTAINER_MOUNTS := \ |
| 146 | + -v $(DEV_CONTAINER_REPO_ROOT):$(DEV_CONTAINER_REPO_ROOT) \ |
| 147 | + $(_DEV_CONTAINER_WORKTREE_MOUNT) \ |
| 148 | + -w $(DEV_CONTAINER_REPO_ROOT) |
| 149 | + |
| 150 | +# Public: prefix for running a scripted command in the devcontainer image. |
| 151 | +# Includes the resolved image ref and `-i` (so stdin pipes work) but no |
| 152 | +# `-t` (so it works under `make` / CI without a TTY). Usage: |
| 153 | +# foo: dev-image |
| 154 | +# $(IN_DEVCONTAINER) cmd args |
| 155 | +# When `make` is invoked from inside a container, IN_DEVCONTAINER is empty, |
| 156 | +# so the command runs in-place — same recipe works inside or outside. |
| 157 | +IN_DEVCONTAINER ?= docker run --rm -i $(_DEV_CONTAINER_MOUNTS) \ |
| 158 | + $$(cat $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/.image-ref) |
| 159 | + |
| 160 | +# linux/amd64 variant: same shape as IN_DEVCONTAINER but pinned to amd64, |
| 161 | +# for running cross-compiled x86_64 binaries (e.g. Windows test exes under |
| 162 | +# wine64) on any host. On Apple Silicon, Docker emulates via Rosetta. On |
| 163 | +# native amd64 the platform pin is a no-op and we reuse the regular image. |
| 164 | +_DEV_CONTAINER_X86_64_IMAGE_REF_FILE := $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/.image-ref-x86_64 |
| 165 | +IN_DEVCONTAINER_X86_64 ?= docker run --rm -i --platform=linux/amd64 $(_DEV_CONTAINER_MOUNTS) \ |
| 166 | + $$(cat $(_DEV_CONTAINER_X86_64_IMAGE_REF_FILE)) |
| 167 | + |
| 168 | +ifeq ($(_DEV_CONTAINER_INSIDE),1) |
| 169 | + IN_DEVCONTAINER := |
| 170 | + IN_DEVCONTAINER_X86_64 := |
| 171 | +endif |
| 172 | + |
| 173 | +# Targets whose behaviour differs inside vs outside a container. |
| 174 | +# Inside: dev-image is a no-op (already running on it); dev-shell fails |
| 175 | +# loudly because there's no docker daemon. |
| 176 | +ifeq ($(_DEV_CONTAINER_INSIDE),1) |
| 177 | + |
| 178 | +dev-image dev-image-x86_64: |
| 179 | + @: |
| 180 | + |
| 181 | +dev-shell: |
| 182 | + @echo "make dev-shell: already inside the devcontainer; run /bin/sh directly" >&2; exit 1 |
| 183 | + |
| 184 | +else # _DEV_CONTAINER_INSIDE |
| 185 | + |
| 186 | +# Pull the image if available; otherwise build locally from the staged context. |
| 187 | +dev-image: |
| 188 | + @$(_dev_container_stage_context); \ |
| 189 | + $(_dev_container_compute_tag); \ |
| 190 | + ref="$(DEV_CONTAINER_IMAGE_NAME):$$tag"; \ |
| 191 | + if docker pull "$$ref" >/dev/null 2>&1; then \ |
| 192 | + echo "Pulled $$ref"; \ |
| 193 | + else \ |
| 194 | + echo "Pull miss; building $$ref locally"; \ |
| 195 | + docker buildx build \ |
| 196 | + --platform $(_DEV_CONTAINER_PLATFORM) \ |
| 197 | + --file "$$ctx/.devcontainer/Dockerfile" \ |
| 198 | + --build-context repo="$$ctx" \ |
| 199 | + $(_DEV_CONTAINER_ARCH_ARG) \ |
| 200 | + $(DOCKER_BUILDX_FLAGS) \ |
| 201 | + --load -t "$$ref" \ |
| 202 | + "$$ctx"; \ |
| 203 | + fi; \ |
| 204 | + echo "$$ref" > $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/.image-ref |
| 205 | + |
| 206 | +# Build the linux/amd64 variant. On amd64 hosts this reuses dev-image; on |
| 207 | +# arm64 hosts it produces a separate `:<tag>-x86_64` image so it doesn't |
| 208 | +# clobber the native one — built locally only (CI doesn't publish the |
| 209 | +# `-x86_64` ref), so no docker pull attempt. Buildx caches per-platform |
| 210 | +# so repeats are cheap. |
| 211 | +ifeq ($(_DEV_CONTAINER_PLATFORM),linux/amd64) |
| 212 | +dev-image-x86_64: dev-image |
| 213 | + @cp $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/.image-ref $(_DEV_CONTAINER_X86_64_IMAGE_REF_FILE) |
| 214 | +else |
| 215 | +dev-image-x86_64: |
| 216 | + @$(_dev_container_stage_context); \ |
| 217 | + $(_dev_container_compute_tag); \ |
| 218 | + ref="$(DEV_CONTAINER_IMAGE_NAME):$$tag-x86_64"; \ |
| 219 | + docker buildx build --platform=linux/amd64 \ |
| 220 | + --file "$$ctx/.devcontainer/Dockerfile" \ |
| 221 | + --build-context repo="$$ctx" \ |
| 222 | + --build-arg ARCH=x86_64 \ |
| 223 | + $(DOCKER_BUILDX_FLAGS) \ |
| 224 | + --load -t "$$ref" \ |
| 225 | + "$$ctx"; \ |
| 226 | + echo "$$ref" > $(_DEV_CONTAINER_X86_64_IMAGE_REF_FILE) |
| 227 | +endif |
| 228 | + |
| 229 | +dev-shell: dev-image |
| 230 | + @ref=$$(cat $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/.image-ref); \ |
| 231 | + docker run --rm -it $(_DEV_CONTAINER_MOUNTS) "$$ref" /bin/sh |
| 232 | + |
| 233 | +endif # _DEV_CONTAINER_INSIDE |
0 commit comments