Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
e0e9c63
ci(release): fix publish by dropping dist-tag add (#9100)
rochdev Jun 26, 2026
9144415
ci(coverage): merge per-integration coverage in All Green before uplo…
BridgeAR Jun 26, 2026
dc50cad
test(kafkajs): stop pinning the produce offset in the sendBatch test …
BridgeAR Jun 26, 2026
911c3c8
test: key plugin version folders by single-digit major (#9052)
BridgeAR Jun 26, 2026
bf2bbfd
ci(release): fix branch-diff Infinity crash on scientific notation SH…
rochdev Jun 26, 2026
857bd76
refactor(config): introduce namespace field and start pruning interna…
BridgeAR Jun 26, 2026
d1af821
perf(loader): match instrumented modules with a shouldInclude predica…
BridgeAR Jun 26, 2026
e97225d
test(llmobs): use DD_LLMOBS_ENABLED in tagger sampling fixtures (#9107)
BridgeAR Jun 26, 2026
518772c
refactor(graphql): migrate shimmer to orchestrion instrumentation (#7…
crysmags Jun 27, 2026
4c79470
chore(deps): bump openai (#9122)
dependabot[bot] Jun 29, 2026
0216428
chore(deps): bump the cloud-and-messaging group across 1 directory wi…
dependabot[bot] Jun 29, 2026
30e270f
chore(deps): bump the test-versions group across 1 directory with 4 u…
dependabot[bot] Jun 29, 2026
6c386ca
test(test-optimization): handle 5xx retry in getKnownTests error test…
BridgeAR Jun 29, 2026
c6a043e
fix(cypress): handle missing beforeEach task result (#9129)
juan-fernandez Jun 29, 2026
2e2db4d
refactor(config): derive supported config paths from canonical names …
BridgeAR Jun 29, 2026
0395404
feat(test optimization) Add Test Optimization HTTP cache reader (#8860)
cbasitodx Jun 29, 2026
7b6e65e
test(langchain): bump tested langchain versions with new cassette (#9…
sabrenner Jun 29, 2026
908b6b9
fix(iast): scope the mongodb nosql-analysis marker per query (#9076)
BridgeAR Jun 29, 2026
16bd4db
fix(graphql)!: decouple resolver depth from path collapsing (#8774)
BridgeAR Jun 29, 2026
1f899b5
feat(aap): In App WAF support for lambda (#7783)
CarlesDD Jun 30, 2026
c6f323d
Add runtime family tag on appsec lambda span (#9153)
CarlesDD Jun 30, 2026
f4c6d9d
chore: drop dead profile assert helper and fix telemetry typo (#7686)
BridgeAR Jun 30, 2026
94e3cfa
ci(scripts): disable V8 Maglev for Windows test children (#9131)
BridgeAR Jun 30, 2026
98bdb97
fix(instrumentation): wrap lazily defined fs.opendir on Node 20 (#9094)
BridgeAR Jun 30, 2026
aca9c41
test(kafkajs): drop retries:0 from header-disable producer tests (#9106)
BridgeAR Jun 30, 2026
7ade5fe
test(runtime-metrics): widen GC pause p95 bound to deflake (#9121)
BridgeAR Jun 30, 2026
599b825
ci(engines): widen engines.node to >=18 in CI to keep Node 18/20 jobs…
pabloerhard Jun 30, 2026
797f843
test(child_process): load the mock agent once per suite (#9113)
BridgeAR Jun 30, 2026
654a3fb
feat: [SVLS-9168] add aws.durable.operation_attempt tag to durable op…
lym953 Jun 30, 2026
a6e0a62
chore(release): add v6 into release workflows (#9103)
pabloerhard Jun 30, 2026
396642a
chore(deps): bump bullmq (#9164)
dependabot[bot] Jul 1, 2026
0ba18d1
chore(deps): bump the gh-actions-packages group across 3 directories …
dependabot[bot] Jul 1, 2026
4235b4b
fix(vitest): route thread workers through main process (#9169)
juan-fernandez Jul 1, 2026
b895093
refactor(vitest): split main and worker instrumentation (#9170)
juan-fernandez Jul 1, 2026
e22306c
refactor(vitest): prepare test metadata in main (#9171)
juan-fernandez Jul 1, 2026
2de7de1
fix(aws-durable-execution-sdk-js): treat FAILED checkpoints as replay…
lym953 Jul 1, 2026
282cbe8
chore(deps): bump the vendor-minor-and-patch-dependencies group acros…
dependabot[bot] Jul 1, 2026
e01ded4
test(oracledb): bound connect and query timeouts in the ESM integrati…
BridgeAR Jul 1, 2026
2c19658
test(debugger): cover breakpoint error paths (#8996)
watson Jul 2, 2026
3868c38
chore(deps-dev): bump the dev-minor-and-patch-dependencies group acro…
dependabot[bot] Jul 2, 2026
b876859
docs(agents): clarify internal import ordering rule (#9172)
CarlesDD Jul 2, 2026
596169b
fix(vitest): report typecheck tests (#9176)
juan-fernandez Jul 2, 2026
c634874
feat(test-optimization): add vitest no-worker init mode (#9173)
juan-fernandez Jul 2, 2026
f2275bf
perf(http): load the GCP pubsub push plugin lazily (#9178)
BridgeAR Jul 2, 2026
96acc89
test(child_process): fix Bluebird flake that cascades to every later …
BridgeAR Jul 2, 2026
34f3091
feat(opentelemetry): OTLP trace metrics support (#8206)
ida613 Jul 2, 2026
9392529
fix(datastreams): flush on write when flushInterval is 0 (#9120)
BridgeAR Jul 2, 2026
5b0fc6f
chore(engines): bump engines field for v6 (#9104)
pabloerhard Jul 2, 2026
55d286d
feat(release): add breaking changes to release proposal (#9196)
pabloerhard Jul 2, 2026
35bc30e
docs(v6): updated documentation for v6 (#9159)
pabloerhard Jul 2, 2026
c723e09
v6.0.0
pabloerhard Jul 2, 2026
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
89 changes: 89 additions & 0 deletions .agents/skills/apm-integrations/references/orchestrion.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,95 @@ For integrations wrapping multiple methods, create a separate plugin class per m
- `ctx.error` — thrown error (on error)
- `ctx.currentStore` — set by `startSpan` in `bindStart`

## Propagating Synchronous Errors From `bindStart`

Subscribers on the prefix `:start` channel and `bindStore` transforms **cannot
propagate a synchronous throw** to the caller of an orchestrion-wrapped
function. Both are wrapped in `try { ... } catch (err) { process.nextTick(() =>
triggerUncaughtException(err)); ... }` by Node's `diagnostics_channel` (see
`lib/diagnostics_channel.js`'s `wrapStoreRun` and `publish`). The error
surfaces async as an uncaught exception, **after** the wrapped fn has already
run and the call has returned normally.

The wrapper's own catch block, however, **does** rethrow:

```js
return ch.start.runStores(__apm$ctx, () => {
try {
const result = __apm$traced();
__apm$ctx.result = result;
return result;
} catch (err) {
__apm$ctx.error = err;
ch.error.publish(__apm$ctx);
throw err; // <- propagates the wrapped fn's error to the caller
} finally {
ch.end.publish(__apm$ctx);
}
});
```

When a contract requires `assert.throws(() => wrapped(...))`-style synchronous
propagation from a `:start` observer (the canonical case is AppSec WAF's
`abortController.abort()` model), use the **Proxy-on-arguments pattern**:

```js
bindStart (ctx) {
// ... normal setup, span creation ...
const abortController = new AbortController()
if (startCh.hasSubscribers) {
startCh.publish({ abortController, args }) // subscribers run sync
if (abortController.signal.aborted) {
// ctx.arguments is the SAME array reference the wrapper spreads into
// the wrapped fn (__apm$wrapped.apply(this, ctx.arguments)). Replace
// arguments[0] with a Proxy whose getters throw AbortError. The
// wrapped fn's first property access (typically a destructure of args)
// triggers the trap → the wrapper's catch+rethrow propagates AbortError
// to the caller. Span lifecycle still completes via end.publish.
ctx.arguments[0] = new Proxy({}, {
get () { throw new AbortError('Aborted') },
has () { throw new AbortError('Aborted') },
})
ctx.ddAborted = true
return ctx.currentStore
}
}
// ... rest of bindStart ...
}

error (ctx) {
if (ctx.ddAborted) return // abort != error tag
// ... regular error handling ...
}
```

Reference implementation: `packages/datadog-plugin-graphql/src/execute.js`
(`apm:graphql:execute:start` contract).

Why this works:
- `ctx.arguments` and the rewriter's `__apm$arguments` are the same array
reference (confirmed by capturing the rewriter output: `const __apm$traced
= () => __apm$wrapped.apply(this, __apm$arguments)`).
- The wrapped fn's body almost always touches `arguments[0]` on the first
statement (destructure, property read, validation). Any read triggers the
Proxy trap.
- The orchestrion wrapper's `catch { ...; throw err }` propagates the thrown
error synchronously to the caller — confirmed in the rewriter template
(vendor's `code-transformer/index.js`, `wrapSync` function).
- `:end` still fires in `finally`, so the plugin's `end(ctx)` runs as usual
and the span lifecycle completes cleanly. Combined with an `error(ctx)`
that no-ops when `ctx.ddAborted`, the span finishes with `error === 0`
(matches the abort contract).

When NOT to use this:
- If you control the wrapped fn (e.g., it's a method you can wrap with
shimmer on top of orchestrion), do that — clearer and avoids the
Proxy indirection.
- If the wrapped fn might catch its own errors before they escape (some
user-resolver patterns do this), the AbortError won't propagate. Verify
the wrapped fn does NOT have a top-level `try/catch` around the
property access that triggers the trap.

## Common Issues

### Wrong filePath
Expand Down
5 changes: 5 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@
/integration-tests/vitest/ @DataDog/ci-app-libraries
/integration-tests/vitest.config.mjs @DataDog/ci-app-libraries
/integration-tests/vitest.second-project.config.mjs @DataDog/ci-app-libraries
/integration-tests/vitest.typecheck.config.mjs @DataDog/ci-app-libraries
/integration-tests/tsconfig.typecheck.json @DataDog/ci-app-libraries
/integration-tests/tsconfig.typecheck-fail.json @DataDog/ci-app-libraries
/integration-tests/tsconfig.typecheck-test-management-fail.json @DataDog/ci-app-libraries
/integration-tests/tsconfig.typecheck-test-management-file-fail.json @DataDog/ci-app-libraries
/integration-tests/ci-visibility-intake.js @DataDog/ci-app-libraries
/integration-tests/ci-visibility-intake.spec.js @DataDog/ci-app-libraries
/integration-tests/CODEOWNERS @DataDog/ci-app-libraries
Expand Down
26 changes: 12 additions & 14 deletions .github/actions/coverage/action.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
name: coverage
description: Verify coverage output and upload it to Codecov and Datadog.
description: >-
Verify a job's coverage output and stash it as an artifact. All Green collects every
`coverage-*` artifact, merges them per integration, and uploads the groups to Codecov and
Datadog — so a commit sends ~100 grouped reports instead of one per matrix cell, which kept
Codecov from silently dropping uploads past its per-commit ceiling.

inputs:
flags:
description: "Codecov flags value"
description: "Coverage flags — the grouping key All Green folds matrix cells by"
required: false
report-dir:
description: "Coverage report directory (defaults to 'coverage')"
Expand All @@ -12,19 +16,13 @@ inputs:
runs:
using: composite
steps:
# Retry once on failure to work around transient issues (e.g. flaky
# Codecov upload network calls).
- id: attempt
uses: ./.github/actions/coverage/upload
continue-on-error: true
with:
flags: ${{ inputs.flags }}
report-dir: ${{ inputs.report-dir }}
- if: steps.attempt.outcome == 'failure'
- name: Verify coverage output
shell: bash
run: sleep 60
- if: steps.attempt.outcome == 'failure'
uses: ./.github/actions/coverage/upload
run: node scripts/verify-coverage.js --flags "${{ inputs.flags }}"

- name: Upload coverage artifact
if: always()
uses: ./.github/actions/upload-coverage-artifact
with:
flags: ${{ inputs.flags }}
report-dir: ${{ inputs.report-dir }}
51 changes: 0 additions & 51 deletions .github/actions/coverage/upload/action.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/actions/install/branch-diff/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ inputs:
runs:
using: composite
steps:
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.npm
key: ${{ github.workflow }}-branch-diff-3.1.1
Expand Down
22 changes: 20 additions & 2 deletions .github/actions/node/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ runs:
}
case "$VERSION" in
eol) version=16 ;;
oldest) oldest_major=$(awk '/"node"/{match($0,/[0-9]+/);print substr($0,RSTART,RLENGTH);exit}' package.json)
version=$(node_version "$oldest_major") ;;
# Pinned to 18 rather than derived from package.json engines.node: CI keeps testing the
# oldest version we still exercise (18) even though the shipped engines floor is now >=22.
# See the Widen engines.node for CI step below, which restores >=18 on the 18/20 legs.
oldest) version=$(node_version 18) ;;
maintenance) version=$(node_version 20) ;;
active) version=$(node_version 24) ;;
latest) version=${LATEST_VERSION:-$(node_version 26)}
Expand All @@ -44,6 +46,22 @@ runs:
node-version: ${{ steps.node-version.outputs.version }}
registry-url: ${{ inputs.registry-url || 'https://registry.npmjs.org' }}

# The shipped package.json pins engines.node to the supported runtime range, but CI keeps
# running the full suite on Node 18/20. Widen the field to >=18 for this checkout so the
# runtime guard and the withVersions test helper don't bail and silently skip those jobs.
# Gate on the major so this never runs on the ancient runtimes the guardrails-unsupported
# job installs (those must keep seeing the shipped >=22 so the guard aborts), and so the
# script is only ever parsed by a Node version new enough to run it.
- name: Widen engines.node for CI
shell: bash
run: |
MAJOR=$(node -e "process.stdout.write(String(parseInt(process.versions.node)))")
if [ "$MAJOR" = "18" ] || [ "$MAJOR" = "20" ]; then
node scripts/ci/widen-engines-for-ci.js
else
echo "Leaving engines.node unchanged on Node $(node -v) (only widened on Node 18/20)."
fi

# Persist the resolved version so subsequent runs within this 60-minute window can reuse it.
- name: Save resolved version
if: steps.node-version-cache.outputs.cache-hit != 'true'
Expand Down
36 changes: 24 additions & 12 deletions .github/actions/upload-coverage-artifact/action.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: Upload Coverage Artifact
description: Upload coverage report as a GitHub Actions artifact for later collection
description: Upload a job's LCOV report as a GitHub Actions artifact for All Green to collect and group.

inputs:
flags:
description: Coverage flags — used as artifact identifier and passed to Datadog on upload
description: Coverage flags — the grouping key All Green folds matrix cells by
required: true
report-dir:
description: Coverage report directory
Expand All @@ -13,26 +13,38 @@ inputs:
runs:
using: composite
steps:
# The reports live at `<report-dir>/node-<version>-<label>/{lcov.info,coverage-final.json}`, one
# level below `report-dir`, so the presence check has to recurse — a top-level-only probe finds
# nothing and silently skips the upload, which is what starved the Datadog coverage upload.
# Both formats ship: Codecov reads branch/function coverage from the istanbul JSON, Datadog from
# the lcov.
- id: check
shell: bash
run: |
count=$(find "${{ inputs.report-dir }}" -maxdepth 1 -type f 2>/dev/null | wc -l | tr -d ' ')
echo "has_files=$([ "$count" -gt 0 ] && echo true || echo false)" >> "$GITHUB_OUTPUT"
- if: steps.check.outputs.has_files == 'true'
count=$(find "${{ inputs.report-dir }}" \( -name lcov.info -o -name coverage-final.json \) -type f 2>/dev/null | wc -l | tr -d ' ')
echo "has_coverage=$([ "$count" -gt 0 ] && echo true || echo false)" >> "$GITHUB_OUTPUT"
# The artifact name carries the grouping flag plus a per-cell suffix (`__<job>-<matrix-index>`)
# so matrix cells that share a flag — cypress varies `spec` outside its flag — upload distinct
# artifacts instead of clobbering each other under one name.
- if: github.actor != 'dependabot[bot]' && steps.check.outputs.has_coverage == 'true'
id: upload
continue-on-error: true
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ inputs.flags }}
path: ${{ inputs.report-dir }}
name: coverage-${{ inputs.flags }}__${{ github.job }}-${{ strategy.job-index }}
path: |
${{ inputs.report-dir }}/**/lcov.info
${{ inputs.report-dir }}/**/coverage-final.json
retention-days: 1
- if: steps.check.outputs.has_files == 'true' && steps.upload.outcome == 'failure'
- if: github.actor != 'dependabot[bot]' && steps.check.outputs.has_coverage == 'true' && steps.upload.outcome == 'failure'
shell: bash
run: sleep 10
- if: steps.check.outputs.has_files == 'true' && steps.upload.outcome == 'failure'
- if: github.actor != 'dependabot[bot]' && steps.check.outputs.has_coverage == 'true' && steps.upload.outcome == 'failure'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ inputs.flags }}
path: ${{ inputs.report-dir }}
overwrite: true
name: coverage-${{ inputs.flags }}__${{ github.job }}-${{ strategy.job-index }}
path: |
${{ inputs.report-dir }}/**/lcov.info
${{ inputs.report-dir }}/**/coverage-final.json
retention-days: 1
overwrite: true
4 changes: 2 additions & 2 deletions .github/chainguard/self.github.release.push-tags.sts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ subject: repo:DataDog/dd-trace-js:environment:npm

claim_pattern:
event_name: (push|workflow_dispatch)
ref: refs/heads/(v[345]\.x|master)
ref: refs/heads/(v[3456]\.x|master)
repository: DataDog/dd-trace-js
job_workflow_ref: DataDog/dd-trace-js/\.github/workflows/release\.yml@refs/heads/(v[345]\.x|master)
job_workflow_ref: DataDog/dd-trace-js/\.github/workflows/release\.yml@refs/heads/(v[3456]\.x|master)

permissions:
contents: write
55 changes: 54 additions & 1 deletion .github/workflows/all-green.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ jobs:
GITHUB_TOKEN: ${{ steps.octo-sts.outputs.token }}
POLLING_INTERVAL: 1
RETRIES: 20
# Sort every downloaded per-cell `coverage-*` artifact into one directory per integration under
# `coverage-upload/<group>/{lcov,json}/`. The reports are not merged here — both backends merge
# same-flag uploads server-side — so each cell's report passes through byte-for-byte. Codecov
# gets the istanbul JSON (carries branch/function coverage), Datadog gets the lcov.
- if: "!cancelled()"
run: node scripts/group-coverage.mjs
- if: "!cancelled()"
uses: ./.github/actions/dd-sts-api-key
id: dd-sts
Expand All @@ -49,7 +55,54 @@ jobs:
run: datadog-ci junit upload --service dd-trace-js-tests --auto-discovery junit-results
env:
DD_API_KEY: ${{ steps.dd-sts.outputs.api_key }}
# One Datadog upload per integration, flagged with the integration name so coverage is
# attributable in the Datadog UI the same way the Codecov flags attribute it. Datadog reads the
# lcov reports; it does not ingest istanbul's JSON.
- if: "!cancelled()"
run: datadog-ci coverage upload coverage-results
run: |
set -o pipefail
shopt -s nullglob
# shellcheck disable=SC2016 # the inner shell must expand "$1", not the template.
printf '%s\0' coverage-upload/*/lcov/ | xargs -0 -r -P 10 -I {} \
bash -c 'datadog-ci coverage upload "$1" --flags "$(basename "$(dirname "$1")")"' _ {}
env:
DD_API_KEY: ${{ steps.dd-sts.outputs.api_key }}
# One Codecov upload per integration via the CLI directly — the codecov-action would re-validate
# and re-download the uploader once per group (~100×). Codecov reads branch/function coverage
# from the istanbul JSON, so the upload points at each group's `json/` subdirectory.
# `master-coverage` is the flag `.codecov.yml` gates `codecov/patch` on; attach it only on PRs
# targeting master so release-branch PRs auto-pass. The SHA/branch/PR are passed explicitly
# because All Green checks out the merge ref, not the head the other workflows reported coverage
# for. No token: the repo uploads tokenless (a Codecov token would have to be an org secret,
# which forks can't read).
- if: "!cancelled()"
run: |
commit_args=(--sha "$HEAD_SHA" --branch "$HEAD_BRANCH")
upload_args=(--sha "$HEAD_SHA")
if [ -n "$PR_NUMBER" ]; then
commit_args+=(--pr "$PR_NUMBER")
upload_args+=(--pr "$PR_NUMBER")
fi
master_flag=()
if [ "$GITHUB_EVENT_NAME" = "pull_request" ] && [ "$BASE_REF" = "master" ]; then
master_flag=(-F master-coverage)
fi
shopt -s nullglob
groups=(coverage-upload/*/json/)
[ ${#groups[@]} -gt 0 ] || exit 0
pip install --quiet codecov-cli
# create-commit and create-report set up the report once and must finish before any upload;
# only the do-upload fan-out is safe to parallelize.
codecovcli create-commit "${commit_args[@]}"
codecovcli create-report --sha "$HEAD_SHA"
set -o pipefail
# shellcheck disable=SC2016 # the inner shell must expand "$@"/"$dir", not the template.
printf '%s\0' "${groups[@]}" | xargs -0 -r -P 10 -I {} \
bash -c 'dir="$1"; shift; codecovcli do-upload "$@" --dir "$dir" -F "$(basename "$(dirname "$dir")")"' \
_ {} "${upload_args[@]}" "${master_flag[@]}"
env:
GITHUB_EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
HEAD_BRANCH: ${{ github.head_ref || github.ref_name }}
PR_NUMBER: ${{ github.event.pull_request.number }}
2 changes: 1 addition & 1 deletion .github/workflows/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- uses: ./.github/actions/node/latest
- name: actionlint
id: actionlint
uses: raven-actions/actionlint@205b530c5d9fa8f44ae9ed59f341a0db994aa6f8 # v2.1.2
uses: raven-actions/actionlint@3d39aea434753780c3b3d4a1a31c854b4dbf49d7 # v2.2.0
with:
matcher: true
fail-on-error: true
Expand Down
Loading
Loading