Skip to content
Closed
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
177 changes: 177 additions & 0 deletions .devcontainer/devcontainer.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# DO NOT EDIT. Synced from DataDog/libdatadog-build by the
# libdatadog-build devcontainer-bundle campaigner — edits here will be
# overwritten on the next run.
# Source of truth: https://github.com/DataDog/libdatadog-build/blob/main/repository-tools/sync/devcontainer.mk
#
# Inputs (set before `include`):
# DEV_CONTAINER_REPO_ROOT Absolute path to the repo root.
# DEV_CONTAINER_IMAGE_NAME Override image name. Defaults to
# registry.ddbuild.io/ci/<repo>/devcontainer.
# DEV_CONTAINER_REQUIRED_PATHS Whitespace-separated paths that must exist
# (e.g. submodule checkouts). Missing ->
# $(error).
# DOCKER_BUILDX_FLAGS Extra flags appended to the local build.

DEV_CONTAINER_REPO_ROOT ?= $(CURDIR)
_DEV_CONTAINER_REPO_NAME := $(notdir $(patsubst %/,%,$(DEV_CONTAINER_REPO_ROOT)))
DEV_CONTAINER_IMAGE_NAME ?= registry.ddbuild.io/ci/$(_DEV_CONTAINER_REPO_NAME)/devcontainer

_DEV_CONTAINER_CONTEXT_FILES := $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/context.files
_DEV_CONTAINER_CONTEXT_FILTER := $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/context.filter
_DEV_CONTAINER_DOCKERFILE := $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/Dockerfile

# uname -m -> docker --platform / conventional ARCH build-arg.
_DEV_CONTAINER_UNAME_M := $(shell uname -m)
ifeq ($(_DEV_CONTAINER_UNAME_M),arm64)
_DEV_CONTAINER_PLATFORM := linux/arm64
_DEV_CONTAINER_ARCH := aarch64
else ifeq ($(_DEV_CONTAINER_UNAME_M),aarch64)
_DEV_CONTAINER_PLATFORM := linux/arm64
_DEV_CONTAINER_ARCH := aarch64
else
_DEV_CONTAINER_PLATFORM := linux/amd64
_DEV_CONTAINER_ARCH := x86_64
endif

# Pass --build-arg ARCH only if the Dockerfile actually declares it.
_DEV_CONTAINER_ARCH_ARG := $(shell grep -q '^ARG ARCH' $(_DEV_CONTAINER_DOCKERFILE) 2>/dev/null && echo --build-arg ARCH=$(_DEV_CONTAINER_ARCH))

# Skip everything inside the container (no nested docker, no pull).
_DEV_CONTAINER_INSIDE := $(shell [ -f /.dockerenv ] || [ -n "$$KUBERNETES_SERVICE_HOST" ] && echo 1)

# Stage .devcontainer/context.files (+ optional context.filter) into a tmp dir,
# verify Dockerfile is present, echo the tmpdir. Single source of truth for
# both the tag hash and the buildx context.
define _dev_container_stage_context
ctx=$$(mktemp -d); \
trap 'rm -rf "$$ctx"' EXIT; \
if [ ! -f "$(_DEV_CONTAINER_CONTEXT_FILES)" ]; then \
echo "ERROR: $(_DEV_CONTAINER_CONTEXT_FILES) not found" >&2; exit 1; \
fi; \
if [ -f "$(_DEV_CONTAINER_CONTEXT_FILTER)" ]; then \
grep -Ev '^\s*(#|$$)' "$(_DEV_CONTAINER_CONTEXT_FILES)" | \
rsync -aR --files-from=- --filter="merge $(_DEV_CONTAINER_CONTEXT_FILTER)" "$(DEV_CONTAINER_REPO_ROOT)/" "$$ctx/"; \
else \
grep -Ev '^\s*(#|$$)' "$(_DEV_CONTAINER_CONTEXT_FILES)" | \
rsync -aR --files-from=- "$(DEV_CONTAINER_REPO_ROOT)/" "$$ctx/"; \
fi; \
if [ ! -f "$$ctx/.devcontainer/Dockerfile" ]; then \
echo "ERROR: .devcontainer/Dockerfile not staged; check .devcontainer/context.files" >&2; \
exit 1; \
fi
endef

# Compute the 12-char tag hash from the staged tree.
define _dev_container_compute_tag
tag=$$(cd "$$ctx" && find . -type f | sort | \
while IFS= read -r f; do printf -- '--- %s ---\n' "$$f"; cat "$$f"; done | \
sha256sum | cut -c1-12)
endef

# Git-worktree support: when .git is a gitdir pointer, make the common dir
# visible inside the container so `git` calls work.
_DEV_CONTAINER_GIT_COMMON_DIR := $(shell cd $(DEV_CONTAINER_REPO_ROOT) && git rev-parse --path-format=absolute --git-common-dir 2>/dev/null)
_DEV_CONTAINER_DEFAULT_GIT_DIR := $(DEV_CONTAINER_REPO_ROOT)/.git
ifneq ($(_DEV_CONTAINER_GIT_COMMON_DIR),)
ifneq ($(_DEV_CONTAINER_GIT_COMMON_DIR),$(_DEV_CONTAINER_DEFAULT_GIT_DIR))
_DEV_CONTAINER_WORKTREE_MOUNT := -v $(_DEV_CONTAINER_GIT_COMMON_DIR):$(_DEV_CONTAINER_GIT_COMMON_DIR)
endif
endif

# Precondition check for caller-declared required paths.
ifneq ($(strip $(DEV_CONTAINER_REQUIRED_PATHS)),)
_DEV_CONTAINER_MISSING_PATHS := $(foreach p,$(DEV_CONTAINER_REQUIRED_PATHS),$(if $(wildcard $(DEV_CONTAINER_REPO_ROOT)/$(p)),,$(p)))
ifneq ($(strip $(_DEV_CONTAINER_MISSING_PATHS)),)
$(error Missing required paths (did you init submodules?): $(_DEV_CONTAINER_MISSING_PATHS))
endif
endif

.PHONY: dev-image dev-shell dev-tag dev-push .devcontainer-stage-context

# Stage the build context per .devcontainer/context.files into a stable
# path under .devcontainer/.staged/. Invoked from VS Code's
# initializeCommand (see .devcontainer/devcontainer.json) so that
# build.context = `.staged` gets the same narrow tree CI and
# `make dev-image` use — same files, same hash, shared cache.
# The hash-computing targets (dev-tag / dev-image / dev-push) keep
# using a mktemp dir for parallel safety; this target deliberately
# writes to a stable path because VS Code needs one.
# Name is dot-prefixed so it does not collide with consumer repos that
# may have their own `stage-context` target.
_DEV_CONTAINER_STAGED := $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/.staged

.devcontainer-stage-context:
@rm -rf "$(_DEV_CONTAINER_STAGED)"
@mkdir -p "$(_DEV_CONTAINER_STAGED)"
@if [ ! -f "$(_DEV_CONTAINER_CONTEXT_FILES)" ]; then \
echo "ERROR: $(_DEV_CONTAINER_CONTEXT_FILES) not found" >&2; exit 1; \
fi
@if [ -f "$(_DEV_CONTAINER_CONTEXT_FILTER)" ]; then \
grep -Ev '^\s*(#|$$)' "$(_DEV_CONTAINER_CONTEXT_FILES)" | \
rsync -aR --files-from=- --filter="merge $(_DEV_CONTAINER_CONTEXT_FILTER)" "$(DEV_CONTAINER_REPO_ROOT)/" "$(_DEV_CONTAINER_STAGED)/"; \
else \
grep -Ev '^\s*(#|$$)' "$(_DEV_CONTAINER_CONTEXT_FILES)" | \
rsync -aR --files-from=- "$(DEV_CONTAINER_REPO_ROOT)/" "$(_DEV_CONTAINER_STAGED)/"; \
fi
@test -f "$(_DEV_CONTAINER_STAGED)/.devcontainer/Dockerfile" || \
{ echo "ERROR: .devcontainer/Dockerfile not staged; check .devcontainer/context.files" >&2; exit 1; }

# Print the tag that would be used. Useful for debugging.
dev-tag:
@$(_dev_container_stage_context); \
$(_dev_container_compute_tag); \
echo "$(DEV_CONTAINER_IMAGE_NAME):$$tag"

# Pull the image if available; otherwise build locally from the staged context.
ifeq ($(_DEV_CONTAINER_INSIDE),1)
dev-image:
@:
else
dev-image:
@$(_dev_container_stage_context); \
$(_dev_container_compute_tag); \
ref="$(DEV_CONTAINER_IMAGE_NAME):$$tag"; \
if docker pull "$$ref" >/dev/null 2>&1; then \
echo "Pulled $$ref"; \
else \
echo "Pull miss; building $$ref locally"; \
docker buildx build \
--platform $(_DEV_CONTAINER_PLATFORM) \
--file "$$ctx/.devcontainer/Dockerfile" \
--build-context repo="$$ctx" \
$(_DEV_CONTAINER_ARCH_ARG) \
$(DOCKER_BUILDX_FLAGS) \
--load -t "$$ref" \
"$$ctx"; \
fi; \
echo "$$ref" > $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/.image-ref
endif

# Manual push helper; used by repos that want to pre-warm the cache.
dev-push:
@$(_dev_container_stage_context); \
$(_dev_container_compute_tag); \
ref="$(DEV_CONTAINER_IMAGE_NAME):$$tag"; \
docker buildx build \
--platform $(_DEV_CONTAINER_PLATFORM) \
--file "$$ctx/.devcontainer/Dockerfile" \
--build-context repo="$$ctx" \
$(_DEV_CONTAINER_ARCH_ARG) \
$(DOCKER_BUILDX_FLAGS) \
--push -t "$$ref" \
"$$ctx"

# Interactive shell in the built image. Shares the repo root and, when a
# worktree is in use, the git common dir too.
DEVCONTAINER_RUN ?= docker run --rm -it \
-v $(DEV_CONTAINER_REPO_ROOT):$(DEV_CONTAINER_REPO_ROOT) \
$(_DEV_CONTAINER_WORKTREE_MOUNT) \
-w $(DEV_CONTAINER_REPO_ROOT)

ifeq ($(_DEV_CONTAINER_INSIDE),1)
DEVCONTAINER_RUN :=
endif

dev-shell: dev-image
@ref=$$(cat $(DEV_CONTAINER_REPO_ROOT)/.devcontainer/.image-ref); \
$(DEVCONTAINER_RUN) "$$ref" /bin/sh
116 changes: 116 additions & 0 deletions .gitlab/devcontainer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# DO NOT EDIT. Synced from DataDog/libdatadog-build by the
# libdatadog-build devcontainer-bundle campaigner into
# `.gitlab/devcontainer.yml` — edits here will be overwritten.
#
# Shared devcontainer pipeline. One job does everything: stage the
# context, compute the tag, check the cache, build + nydusify on miss,
# emit ci-image.env for downstream jobs.
#
# Consumer repo wires this in via:
# variables:
# DEVCONTAINER_REPO_NAME: my-repo
# # optional:
# # DEVCONTAINER_IMAGE_NAME: registry.ddbuild.io/ci/my-repo # flat path
# # DEVCONTAINER_PER_ARCH: "true" # arch matrix
# include:
# - local: .gitlab/devcontainer.yml
#
# Per-repo inputs live in:
# .devcontainer/Dockerfile the dockerfile
# .devcontainer/context.files rsync --files-from list (one path per line;
# '#'/blank lines are comments, stripped
# before being handed to rsync)
# .devcontainer/context.filter optional rsync filter rules

variables:
DEVCONTAINER_IMAGE_NAME: registry.ddbuild.io/ci/${DEVCONTAINER_REPO_NAME}/devcontainer
DEVCONTAINER_PER_ARCH: ""
# Bootstrap image used by the devcontainer_image job itself. Update
# DEVCONTAINER_BOOTSTRAP_IMAGE_TAG by PR after each manual
# publish-devcontainer-bootstrap-image run (same workflow as
# CI_TESTING_IMAGE_TAG).
DEVCONTAINER_BOOTSTRAP_IMAGE_BASE: registry.ddbuild.io/ci/libdatadog-build/devcontainer-bootstrap
DEVCONTAINER_BOOTSTRAP_IMAGE_TAG: ""
DEVCONTAINER_BOOTSTRAP_IMAGE: registry.ddbuild.io/images/nydus:v2.4.0-dd.3

devcontainer_image:
stage: .pre
image: $DEVCONTAINER_BOOTSTRAP_IMAGE
needs: []
tags: ["arch:amd64"]
# Compose the bootstrap image from BASE:TAG when a tag is pinned (the
# post-`publish-devcontainer-bootstrap-image` workflow), otherwise fall
# back to the upstream nydus image defined in `variables:` above.
rules:
- if: '$DEVCONTAINER_BOOTSTRAP_IMAGE_TAG'
variables:
DEVCONTAINER_BOOTSTRAP_IMAGE: $DEVCONTAINER_BOOTSTRAP_IMAGE_BASE:$DEVCONTAINER_BOOTSTRAP_IMAGE_TAG
- when: always
script:
- |
set -euo pipefail

# Bootstrap image doesn't ship rsync (or other deps) — install quietly.
# Parenthesize so `apt-get install` only runs when rsync is missing;
# otherwise `||…&&` left-associativity would install every time.
command -v rsync >/dev/null || (apt-get update -qq && apt-get install -y -qq --no-install-recommends rsync)

# ---- stage context per .devcontainer/context.files ----
ctx=$(mktemp -d)
if [ ! -f .devcontainer/context.files ]; then
echo "ERROR: .devcontainer/context.files not found" >&2; exit 1
fi
filter_flag=()
if [ -f .devcontainer/context.filter ]; then
filter_flag=(--filter="merge .devcontainer/context.filter")
fi
grep -Ev '^\s*(#|$)' .devcontainer/context.files |
rsync -aR --files-from=- "${filter_flag[@]}" "$CI_PROJECT_DIR/" "$ctx/"
if [ ! -f "$ctx/.devcontainer/Dockerfile" ]; then
echo "ERROR: .devcontainer/Dockerfile not staged; check .devcontainer/context.files" >&2
exit 1
fi
TAG=$(cd "$ctx" && find . -type f | sort |
while IFS= read -r f; do printf -- '--- %s ---\n' "$f"; cat "$f"; done |
sha256sum | cut -c1-12)
echo "Devcontainer tag: $TAG"

# ---- build-or-skip per arch ----
build_and_nydusify() {
local ref=$1 platform=$2 arch_arg=$3
echo "Building $ref ..."
# shellcheck disable=SC2086
docker buildx build \
--platform "$platform" \
--file "$ctx/.devcontainer/Dockerfile" \
--build-context "repo=$ctx" \
$arch_arg \
--push -t "$ref" \
"$ctx"
nydus-convert "$ref" "$ref"
}

if [ -n "$DEVCONTAINER_PER_ARCH" ]; then
AMD_REF="${DEVCONTAINER_IMAGE_NAME}:amd64-${TAG}"
ARM_REF="${DEVCONTAINER_IMAGE_NAME}:arm64-${TAG}"
crane manifest "$AMD_REF" >/dev/null 2>&1 \
|| build_and_nydusify "$AMD_REF" linux/amd64 "--build-arg ARCH=x86_64"
crane manifest "$ARM_REF" >/dev/null 2>&1 \
|| build_and_nydusify "$ARM_REF" linux/arm64 "--build-arg ARCH=aarch64"
{
echo "DEVCONTAINER_IMAGE_AMD64=$AMD_REF"
echo "DEVCONTAINER_IMAGE_ARM64=$ARM_REF"
echo "CI_IMAGE_HASH=$TAG"
} > ci-image.env
else
REF="${DEVCONTAINER_IMAGE_NAME}:${TAG}"
crane manifest "$REF" >/dev/null 2>&1 \
|| build_and_nydusify "$REF" linux/amd64,linux/arm64 ""
{
echo "DEVCONTAINER_IMAGE=$REF"
echo "CI_IMAGE_HASH=$TAG"
} > ci-image.env
fi
artifacts:
reports:
dotenv: ci-image.env
13 changes: 13 additions & 0 deletions mirror-images.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
# DO NOT EDIT. Synced from DataDog/libdatadog-build by the
# libdatadog-build repository-tools campaigner — edits here will be
# overwritten on the next run.
#
# Runs the content-addressed mirror_images.py from libdatadog-build
# against this repo's mirror_images.yaml. The URL below is a content-
# hash pin updated on each campaigner sync; `uv run --script <URL>`
# fetches and executes it in one call, reading the script's inline
# metadata header for deps — no Python setup needed.
#
# Usage: ./mirror-images.sh <lock|mirror|lint|add|relock> [args...]
exec uv run --script "https://gitlab-templates.ddbuild.io/libdatadog/repository-tools/ca/a836d61111dddd120eceb6e6e283ed5e7d3a131d3830a1186683422c7a44a86a/scripts/mirror-images/mirror_images.py" "$@"
Loading