From 4efdcfd38e27ed3be78f3154ba70a622a0b484f3 Mon Sep 17 00:00:00 2001 From: Joey Zhao <5253430+joeyzhao2018@users.noreply.github.com> Date: Thu, 29 Oct 2026 12:58:53 -0400 Subject: [PATCH] fix(layer): ship the native addon versions dd-trace declares The layer is installed with --ignore-optional, which drops dd-trace's optional native addons (@datadog/pprof, @datadog/native-appsec). The build re-added them at our own devDependency pins, which drift from the versions dd-trace calls into: - dd-trace >= 5.128.0 / 6.17.0 defaults DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE to "auto", which only @datadog/pprof >= 5.19.0 accepts. Against our 5.18.1 pin the profiler fails to start ("Heap limit extension size must be a uint32") and no profiles are sent. Released layers still ship dd-trace 6.15.0 and are not affected yet; a layer built against dd-trace master is. - @datadog/native-appsec "*" resolves to 11.0.1 from the lockfile, while dd-trace 6.15.0 declares 11.0.2. Take both from the installed dd-trace's optionalDependencies instead, and do the same in the local integration test layer fixture. Co-Authored-By: Claude Opus 5.5 --- Dockerfile | 3 +-- integration_tests_local/README.md | 4 +++- integration_tests_local/prepare-layer.js | 10 ++++++++- scripts/move_ddtrace_dependency.js | 27 ++++++++++++++++++------ 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index a28c07b9..315ca399 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,8 +38,7 @@ RUN cp ./src/handler.mjs /nodejs/node_modules/datadog-lambda-js # Move dd-trace from devDependencies to production dependencies # That way it is included in our layer, while keeping it an optional dependency for npm. -RUN DD_TRACE_VERSION=$(node -p "require('dd-trace/package.json').version") \ - && node ./scripts/move_ddtrace_dependency.js "$(cat package.json)" "$DD_TRACE_VERSION" > package-new.json +RUN node ./scripts/move_ddtrace_dependency.js "$(cat package.json)" "$(cat node_modules/dd-trace/package.json)" > package-new.json RUN mv package-new.json package.json RUN rm -rf node_modules diff --git a/integration_tests_local/README.md b/integration_tests_local/README.md index 1d7e750c..361273a4 100644 --- a/integration_tests_local/README.md +++ b/integration_tests_local/README.md @@ -86,7 +86,9 @@ dependency manifest mirrors the release build's full dependency closure — production dependencies plus every package `scripts/move_ddtrace_dependency.js` moves (dd-trace, @datadog/native-appsec, @datadog/pprof, @opentelemetry/api, @opentelemetry/api-logs) — with versions pinned from the lockfile-resolved -`node_modules` set. +`node_modules` set, except dd-trace's native addons (@datadog/native-appsec, +@datadog/pprof), which are pinned to the versions dd-trace declares, as in the +release build. The harness takes no flags or positional arguments — configuration is environment-only, and an unexpected argument is rejected rather than ignored. diff --git a/integration_tests_local/prepare-layer.js b/integration_tests_local/prepare-layer.js index 52cd7ea1..2e3d0b2b 100644 --- a/integration_tests_local/prepare-layer.js +++ b/integration_tests_local/prepare-layer.js @@ -60,6 +60,12 @@ const LAYER_MOVED_DEPENDENCIES = [ "@opentelemetry/api", "@opentelemetry/api-logs", ]; +// Like the release build, take dd-trace's native addons at the versions dd-trace +// declares rather than our hoisted devDependency pins. +const DD_TRACE_NATIVE_DEPENDENCIES = ["@datadog/native-appsec", "@datadog/pprof"]; +const ddTraceOptionalDependencies = JSON.parse( + fs.readFileSync(path.join(repoDir, "node_modules", "dd-trace", "package.json"), "utf-8"), +).optionalDependencies; function installedVersion(name) { // Read the hoisted install location directly: require.resolve("/package.json") @@ -86,7 +92,9 @@ for (const name of Object.keys(pkg.dependencies || {})) { dependencies[name] = installedVersion(name); } for (const name of LAYER_MOVED_DEPENDENCIES) { - dependencies[name] = installedVersion(name); + dependencies[name] = DD_TRACE_NATIVE_DEPENDENCIES.includes(name) + ? ddTraceOptionalDependencies[name] + : installedVersion(name); } fs.writeFileSync( diff --git a/scripts/move_ddtrace_dependency.js b/scripts/move_ddtrace_dependency.js index 3917defd..065e52fd 100755 --- a/scripts/move_ddtrace_dependency.js +++ b/scripts/move_ddtrace_dependency.js @@ -4,19 +4,24 @@ // dd-trace is pinned to the version installed at runtime rather than the range in the // manifest: install_deps.sh restores package.json to the v6 line after installing, so on Node // 18/20 the range here still says v6 while node_modules holds v5. +// +// The layer is installed with --ignore-optional, which drops dd-trace's native addons, so they +// are added back at the exact versions that dd-trace declares. Pinning them in our own +// devDependencies instead let them drift from the versions dd-trace calls into, e.g. dd-trace +// passing heapLimitExtensionSize 'auto' to a @datadog/pprof too old to accept it. -// USAGE: ./move_ddtrace_dependency.js "$(cat package.json)" > package.json +// USAGE: ./move_ddtrace_dependency.js "$(cat package.json)" "$(cat node_modules/dd-trace/package.json)" > package.json const file = JSON.parse(process.argv[2]); -const ddTraceVersion = process.argv[3]; -if (!ddTraceVersion) { - throw new Error('A resolved dd-trace version is required, e.g. $(node -p "require(\'dd-trace/package.json\').version")'); +if (!process.argv[3]) { + throw new Error('The installed dd-trace package.json is required, e.g. "$(cat node_modules/dd-trace/package.json)"'); } +const ddTracePackage = JSON.parse(process.argv[3]); -moveDependency('dd-trace', ddTraceVersion) -moveDependency('@datadog/native-appsec') -moveDependency('@datadog/pprof') +moveDependency('dd-trace', ddTracePackage.version) +moveDependency('@datadog/native-appsec', ddTraceOptionalVersion('@datadog/native-appsec')) +moveDependency('@datadog/pprof', ddTraceOptionalVersion('@datadog/pprof')) moveDependency('@opentelemetry/api') moveDependency('@opentelemetry/api-logs') @@ -27,3 +32,11 @@ function moveDependency (name, resolvedVersion) { delete file.devDependencies[name]; file.dependencies[name] = resolvedVersion || declaredVersion; } + +function ddTraceOptionalVersion (name) { + const version = ddTracePackage.optionalDependencies[name]; + if (!version) { + throw new Error(`dd-trace ${ddTracePackage.version} does not declare ${name} in optionalDependencies`); + } + return version; +}