dd-trace 6.6.0 → 6.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ci/diagnose.js +162 -21
- package/ci/init.js +0 -1
- package/ci/runbook.md +187 -180
- package/ci/test-optimization-validation/approval-artifacts.js +3 -1
- package/ci/test-optimization-validation/approval.js +52 -15
- package/ci/test-optimization-validation/ci-command-candidate.js +26 -32
- package/ci/test-optimization-validation/ci-discovery.js +1 -1
- package/ci/test-optimization-validation/ci-package-scripts.js +141 -0
- package/ci/test-optimization-validation/ci-remediation.js +112 -39
- package/ci/test-optimization-validation/cli.js +516 -681
- package/ci/test-optimization-validation/command-blocker.js +188 -33
- package/ci/test-optimization-validation/command-output-policy.js +8 -31
- package/ci/test-optimization-validation/command-runner.js +189 -245
- package/ci/test-optimization-validation/environment.js +90 -0
- package/ci/test-optimization-validation/executable.js +159 -388
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +119 -0
- package/ci/test-optimization-validation/framework-adapters/cypress.js +107 -0
- package/ci/test-optimization-validation/framework-adapters/playwright.js +181 -0
- package/ci/test-optimization-validation/generated-files.js +84 -22
- package/ci/test-optimization-validation/generated-test-contract.js +281 -0
- package/ci/test-optimization-validation/generated-verifier.js +49 -16
- package/ci/test-optimization-validation/literal-environment.js +57 -0
- package/ci/test-optimization-validation/manifest-loader.js +2 -22
- package/ci/test-optimization-validation/manifest-scaffold.js +815 -515
- package/ci/test-optimization-validation/manifest-schema.js +494 -702
- package/ci/test-optimization-validation/offline-fixtures.js +4 -1
- package/ci/test-optimization-validation/offline-output.js +6 -6
- package/ci/test-optimization-validation/plan-writer.js +243 -984
- package/ci/test-optimization-validation/preflight-runner.js +49 -55
- package/ci/test-optimization-validation/redaction.js +2 -1
- package/ci/test-optimization-validation/report-writer.js +391 -1357
- package/ci/test-optimization-validation/result-semantics.js +86 -0
- package/ci/test-optimization-validation/runner-command.js +249 -0
- package/ci/test-optimization-validation/runner-contract.js +664 -0
- package/ci/test-optimization-validation/safe-files.js +4 -4
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +29 -3
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +240 -543
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +450 -670
- package/ci/test-optimization-validation/scenarios/early-flake-detection.js +4 -3
- package/ci/test-optimization-validation/scenarios/helpers.js +67 -9
- package/ci/test-optimization-validation/scenarios/test-management.js +6 -5
- package/ci/test-optimization-validation/source-text.js +87 -0
- package/ci/test-optimization-validation/test-output.js +12 -22
- package/ci/vitest-no-worker-init-setup.mjs +348 -51
- package/ext/tags.d.ts +1 -0
- package/index.d.ts +84 -13
- package/package.json +10 -4
- package/packages/datadog-core/src/utils/src/kebabcase.js +8 -2
- package/packages/datadog-esbuild/index.js +4 -2
- package/packages/datadog-esbuild/src/utils.js +1 -1
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +4 -4
- package/packages/datadog-instrumentations/src/cucumber.js +9 -6
- package/packages/datadog-instrumentations/src/helpers/extract-package-and-module-path.js +10 -0
- package/packages/datadog-instrumentations/src/helpers/hook.js +19 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +9 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/openai-agents.js +31 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +57 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +50 -18
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/shared-utils.js +39 -0
- package/packages/datadog-instrumentations/src/http/client.js +1 -3
- package/packages/datadog-instrumentations/src/jest.js +8 -14
- package/packages/datadog-instrumentations/src/mocha/common.js +1 -3
- package/packages/datadog-instrumentations/src/mocha/main.js +2 -42
- package/packages/datadog-instrumentations/src/mocha/utils.js +68 -2
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +19 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +446 -42
- package/packages/datadog-instrumentations/src/moleculer/client.js +1 -1
- package/packages/datadog-instrumentations/src/openai-agents.js +159 -0
- package/packages/datadog-instrumentations/src/playwright.js +3 -2
- package/packages/datadog-instrumentations/src/rhea.js +1 -1
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +525 -56
- package/packages/datadog-instrumentations/src/vitest-main.js +90 -10
- package/packages/datadog-instrumentations/src/webdriverio.js +914 -0
- package/packages/datadog-plugin-aerospike/src/index.js +1 -3
- package/packages/datadog-plugin-amqplib/src/client.js +1 -1
- package/packages/datadog-plugin-amqplib/src/producer.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +2 -2
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -3
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +5 -3
- package/packages/datadog-plugin-bullmq/src/producer.js +3 -3
- package/packages/datadog-plugin-child_process/src/index.js +3 -3
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +5 -8
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -1
- package/packages/datadog-plugin-fastify/src/tracing.js +1 -1
- package/packages/datadog-plugin-fs/src/index.js +0 -4
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +14 -7
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +19 -10
- package/packages/datadog-plugin-graphql/src/execute.js +1 -0
- package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -1
- package/packages/datadog-plugin-http/src/client.js +2 -1
- package/packages/datadog-plugin-jest/src/index.js +2 -3
- package/packages/datadog-plugin-jest/src/util.js +2 -1
- package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
- package/packages/datadog-plugin-mocha/src/index.js +25 -6
- package/packages/datadog-plugin-openai-agents/src/index.js +74 -0
- package/packages/datadog-plugin-openai-agents/src/integration.js +503 -0
- package/packages/datadog-plugin-openai-agents/src/processor.js +108 -0
- package/packages/datadog-plugin-openai-agents/src/util.js +60 -0
- package/packages/datadog-plugin-prisma/src/index.js +5 -1
- package/packages/datadog-plugin-rhea/src/consumer.js +16 -13
- package/packages/datadog-plugin-rhea/src/producer.js +1 -1
- package/packages/datadog-plugin-undici/src/index.js +2 -1
- package/packages/datadog-plugin-vitest/src/index.js +84 -11
- package/packages/datadog-webpack/index.js +2 -2
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -3
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +2 -2
- package/packages/dd-trace/src/appsec/sdk/track_event.js +1 -1
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +7 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +50 -51
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -1
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +4 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/webdriverio.js +26 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -1
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +161 -33
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -3
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +7 -5
- package/packages/dd-trace/src/config/defaults.js +14 -6
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -2
- package/packages/dd-trace/src/config/index.js +15 -0
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/supported-configurations.json +12 -6
- package/packages/dd-trace/src/datastreams/encoding.js +3 -4
- package/packages/dd-trace/src/debugger/devtools_client/log.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +1 -4
- package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +16 -2
- package/packages/dd-trace/src/encode/span-stats.js +7 -1
- package/packages/dd-trace/src/encode/tags-processors.js +2 -2
- package/packages/dd-trace/src/exporter.js +16 -9
- package/packages/dd-trace/src/exporters/common/url.js +3 -5
- package/packages/dd-trace/src/exporters/electron/index.js +4 -1
- package/packages/dd-trace/src/external-logger/src/index.js +1 -2
- package/packages/dd-trace/src/flare/index.js +2 -2
- package/packages/dd-trace/src/id.js +1 -1
- package/packages/dd-trace/src/llmobs/constants/tags.js +1 -1
- package/packages/dd-trace/src/llmobs/experiments/client.js +4 -2
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +66 -14
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +289 -95
- package/packages/dd-trace/src/llmobs/experiments/index.js +51 -12
- package/packages/dd-trace/src/llmobs/experiments/noop.js +95 -10
- package/packages/dd-trace/src/llmobs/experiments/result.js +13 -2
- package/packages/dd-trace/src/llmobs/experiments/util.js +218 -0
- package/packages/dd-trace/src/llmobs/index.js +9 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +104 -22
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +5 -9
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +20 -0
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +321 -0
- package/packages/dd-trace/src/llmobs/sdk.js +6 -12
- package/packages/dd-trace/src/llmobs/span_processor.js +14 -7
- package/packages/dd-trace/src/llmobs/tagger.js +24 -5
- package/packages/dd-trace/src/llmobs/util.js +56 -3
- package/packages/dd-trace/src/llmobs/writers/spans.js +12 -6
- package/packages/dd-trace/src/noop/proxy.js +2 -2
- package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +3 -6
- package/packages/dd-trace/src/openfeature/configuration_source.js +5 -8
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +5 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +2 -1
- package/packages/dd-trace/src/opentracing/span.js +1 -1
- package/packages/dd-trace/src/opentracing/span_context.js +1 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +114 -23
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/outbound.js +4 -1
- package/packages/dd-trace/src/plugins/plugin.js +1 -1
- package/packages/dd-trace/src/plugins/tracing.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +7 -6
- package/packages/dd-trace/src/plugins/util/git.js +1 -1
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +1 -1
- package/packages/dd-trace/src/plugins/util/llm.js +2 -2
- package/packages/dd-trace/src/plugins/util/tags.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +38 -12
- package/packages/dd-trace/src/plugins/util/url.js +5 -5
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -1
- package/packages/dd-trace/src/plugins/util/web.js +1 -1
- package/packages/dd-trace/src/priority_sampler.js +6 -3
- package/packages/dd-trace/src/profiling/profilers/poisson.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/shared.js +2 -2
- package/packages/dd-trace/src/ritm.js +5 -20
- package/packages/dd-trace/src/span_processor.js +1 -1
- package/packages/dd-trace/src/span_stats.js +22 -4
- package/packages/dd-trace/src/startup-log.js +3 -0
- package/packages/dd-trace/src/telemetry/logs/log-collector.js +3 -1
- package/packages/dd-trace/src/util.js +16 -0
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +12 -12
- package/vendor/dist/pprof-format/index.js +1 -1
- package/vendor/dist/source-map/index.js +1 -1
- package/ci/test-optimization-validation/command-suitability.js +0 -471
- package/ci/test-optimization-validation/init-probe-preload.js +0 -163
- package/ci/test-optimization-validation/init-probe.js +0 -246
- package/ci/test-optimization-validation/late-initialization.js +0 -63
- package/ci/test-optimization-validation/local-command.js +0 -163
- package/ci/test-optimization-validation/setup-runner.js +0 -97
- package/ci/test-optimization-validation-manifest.schema.json +0 -1
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const fs = require('node:fs')
|
|
4
|
-
const path = require('node:path')
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
mergeNodeOptions,
|
|
8
|
-
runCommand,
|
|
9
|
-
} = require('./command-runner')
|
|
10
|
-
const { inheritApprovedExecutable } = require('./executable-approval')
|
|
11
|
-
const { sanitizeForReport } = require('./redaction')
|
|
12
|
-
const { createFileSafely, ensureSafeDirectory, writeFileSafely } = require('./safe-files')
|
|
13
|
-
|
|
14
|
-
const PROBE_PRELOAD = path.join(__dirname, 'init-probe-preload.js')
|
|
15
|
-
const PROBE_FILE_ENV = 'DD_TEST_OPTIMIZATION_INIT_PROBE_FILE'
|
|
16
|
-
const MAX_PROBE_RECORD_BYTES = 1024 * 1024
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Runs a CI-shaped command with a lightweight NODE_OPTIONS preload that records process reachability.
|
|
20
|
-
*
|
|
21
|
-
* @param {object} options probe options
|
|
22
|
-
* @param {object} options.command manifest command to run
|
|
23
|
-
* @param {object} options.framework manifest framework entry
|
|
24
|
-
* @param {string} options.outDir scenario output directory
|
|
25
|
-
* @param {object} options.options CLI options
|
|
26
|
-
* @returns {Promise<{ artifacts: object, summary: object }>} probe artifacts and summary
|
|
27
|
-
*/
|
|
28
|
-
async function runInitializationProbe ({ command, framework, outDir, options }) {
|
|
29
|
-
const probeOutDir = path.join(outDir, 'initialization-probe')
|
|
30
|
-
const recordsPath = path.join(probeOutDir, 'records.ndjson')
|
|
31
|
-
const rawRecordsPath = path.join(probeOutDir, '.records.raw.ndjson')
|
|
32
|
-
const probeCommand = getProbeCommand(command)
|
|
33
|
-
|
|
34
|
-
ensureSafeDirectory(outDir, probeOutDir, 'initialization probe artifact directory')
|
|
35
|
-
createFileSafely(outDir, rawRecordsPath, '', 'raw initialization probe records')
|
|
36
|
-
|
|
37
|
-
let result
|
|
38
|
-
let records
|
|
39
|
-
try {
|
|
40
|
-
result = await runCommand(probeCommand, {
|
|
41
|
-
artifactRoot: outDir,
|
|
42
|
-
env: {
|
|
43
|
-
[PROBE_FILE_ENV]: rawRecordsPath,
|
|
44
|
-
NODE_OPTIONS: mergeNodeOptions(
|
|
45
|
-
`-r ${formatNodeRequire(PROBE_PRELOAD)}`
|
|
46
|
-
),
|
|
47
|
-
},
|
|
48
|
-
envMode: 'clean',
|
|
49
|
-
outDir: probeOutDir,
|
|
50
|
-
repositoryRoot: options.repositoryRoot,
|
|
51
|
-
requireExecutableApproval: options.requireExecutableApproval,
|
|
52
|
-
label: `${framework.id}:ci-wiring:init-probe`,
|
|
53
|
-
stopWhen: () => probeReachedFramework(rawRecordsPath, framework.framework),
|
|
54
|
-
verbose: options.verbose,
|
|
55
|
-
})
|
|
56
|
-
records = readProbeRecords(rawRecordsPath)
|
|
57
|
-
writeFileSafely(
|
|
58
|
-
outDir,
|
|
59
|
-
recordsPath,
|
|
60
|
-
records.map(record => JSON.stringify(sanitizeForReport(record))).join('\n') + '\n',
|
|
61
|
-
'initialization probe records'
|
|
62
|
-
)
|
|
63
|
-
} finally {
|
|
64
|
-
fs.rmSync(rawRecordsPath, { force: true })
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
artifacts: {
|
|
69
|
-
command: result.artifacts.command,
|
|
70
|
-
records: recordsPath,
|
|
71
|
-
stderr: result.artifacts.stderr,
|
|
72
|
-
stdout: result.artifacts.stdout,
|
|
73
|
-
},
|
|
74
|
-
summary: summarizeProbeResult({ framework, result, records, recordsPath }),
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function getProbeCommand (command) {
|
|
79
|
-
if (!command.env?.NODE_OPTIONS) return command
|
|
80
|
-
|
|
81
|
-
const env = { ...command.env }
|
|
82
|
-
const nodeOptions = removeDatadogPreloads(env.NODE_OPTIONS)
|
|
83
|
-
if (nodeOptions) {
|
|
84
|
-
env.NODE_OPTIONS = nodeOptions
|
|
85
|
-
} else {
|
|
86
|
-
delete env.NODE_OPTIONS
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return inheritApprovedExecutable(command, {
|
|
90
|
-
...command,
|
|
91
|
-
env,
|
|
92
|
-
})
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function removeDatadogPreloads (nodeOptions) {
|
|
96
|
-
const tokens = tokenizeNodeOptions(nodeOptions)
|
|
97
|
-
const kept = []
|
|
98
|
-
|
|
99
|
-
for (let index = 0; index < tokens.length; index++) {
|
|
100
|
-
const token = tokens[index]
|
|
101
|
-
if (isSeparatedPreloadFlag(token) && isDatadogPreload(tokens[index + 1])) {
|
|
102
|
-
index++
|
|
103
|
-
continue
|
|
104
|
-
}
|
|
105
|
-
if (isInlineDatadogPreload(token)) continue
|
|
106
|
-
kept.push(token)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return kept.join(' ')
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function tokenizeNodeOptions (nodeOptions) {
|
|
113
|
-
return String(nodeOptions || '').match(/"[^"]*"|'[^']*'|\S+/g) || []
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function isSeparatedPreloadFlag (token) {
|
|
117
|
-
return token === '-r' || token === '--require' || token === '--import'
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function isInlineDatadogPreload (token) {
|
|
121
|
-
return (token.startsWith('--require=') && isDatadogPreload(token.slice('--require='.length))) ||
|
|
122
|
-
(token.startsWith('--import=') && isDatadogPreload(token.slice('--import='.length))) ||
|
|
123
|
-
(token.startsWith('-r') && token.length > 2 && isDatadogPreload(token.slice(2)))
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function isDatadogPreload (value) {
|
|
127
|
-
const normalized = String(value || '').replaceAll(/^['"]|['"]$/g, '')
|
|
128
|
-
return normalized.includes('dd-trace/ci/init') || normalized.includes('dd-trace/register')
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function summarizeProbeResult ({ framework, result, records, recordsPath }) {
|
|
132
|
-
const processRecords = records.filter(record => record.type === 'process-start')
|
|
133
|
-
const moduleLoadRecords = records.filter(record => record.type === 'module-load')
|
|
134
|
-
const testRunnerSignals = getToolSignals(records, 'test-runner')
|
|
135
|
-
.filter(signal => signal.name === framework.framework)
|
|
136
|
-
const wrapperSignals = getToolSignals(records, 'wrapper', { processStartsOnly: true })
|
|
137
|
-
const packageManagerSignals = getToolSignals(records, 'package-manager', { processStartsOnly: true })
|
|
138
|
-
|
|
139
|
-
return {
|
|
140
|
-
ran: true,
|
|
141
|
-
commandExitCode: result.exitCode,
|
|
142
|
-
commandTimedOut: result.timedOut,
|
|
143
|
-
processCount: processRecords.length,
|
|
144
|
-
moduleLoadCount: moduleLoadRecords.length,
|
|
145
|
-
reachedAnyNodeProcess: processRecords.length > 0,
|
|
146
|
-
reachedTestRunnerProcess: testRunnerSignals.length > 0,
|
|
147
|
-
stoppedAfterRunnerReached: result.stoppedEarly === true && testRunnerSignals.length > 0,
|
|
148
|
-
testRunnerSignals,
|
|
149
|
-
wrapperSignals,
|
|
150
|
-
packageManagerSignals,
|
|
151
|
-
recordsPath,
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function probeReachedFramework (recordsPath, frameworkName) {
|
|
156
|
-
return readProbeRecords(recordsPath).some(record => {
|
|
157
|
-
return getRecordTools(record).some(tool => tool.kind === 'test-runner' && tool.name === frameworkName)
|
|
158
|
-
})
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function getToolSignals (records, kind, { processStartsOnly = false } = {}) {
|
|
162
|
-
const signals = []
|
|
163
|
-
const signalsByLocation = new Map()
|
|
164
|
-
|
|
165
|
-
for (const record of records) {
|
|
166
|
-
if (processStartsOnly && record.type !== 'process-start') continue
|
|
167
|
-
for (const tool of getRecordTools(record)) {
|
|
168
|
-
if (tool.kind !== kind) continue
|
|
169
|
-
|
|
170
|
-
const key = `${tool.name}:${record.cwd}`
|
|
171
|
-
let signal = signalsByLocation.get(key)
|
|
172
|
-
if (!signal) {
|
|
173
|
-
signal = {
|
|
174
|
-
name: tool.name,
|
|
175
|
-
kind: tool.kind,
|
|
176
|
-
pid: record.pid,
|
|
177
|
-
ppid: record.ppid,
|
|
178
|
-
source: record.type,
|
|
179
|
-
argv: Array.isArray(record.argv) ? record.argv : undefined,
|
|
180
|
-
cwd: record.cwd,
|
|
181
|
-
request: record.request,
|
|
182
|
-
processCount: 0,
|
|
183
|
-
processIds: new Set(),
|
|
184
|
-
}
|
|
185
|
-
signalsByLocation.set(key, signal)
|
|
186
|
-
signals.push(signal)
|
|
187
|
-
}
|
|
188
|
-
signal.processIds.add(record.pid)
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return signals.map(signal => {
|
|
193
|
-
signal.processCount = signal.processIds.size
|
|
194
|
-
delete signal.processIds
|
|
195
|
-
return signal
|
|
196
|
-
})
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function getRecordTools (record) {
|
|
200
|
-
if (record.tool) return [record.tool]
|
|
201
|
-
if (Array.isArray(record.detectedTools)) return record.detectedTools
|
|
202
|
-
return []
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function readProbeRecords (recordsPath) {
|
|
206
|
-
let file
|
|
207
|
-
try {
|
|
208
|
-
file = fs.openSync(recordsPath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0))
|
|
209
|
-
const stat = fs.fstatSync(file)
|
|
210
|
-
if (!stat.isFile() || stat.size > MAX_PROBE_RECORD_BYTES) return []
|
|
211
|
-
const content = fs.readFileSync(file, 'utf8')
|
|
212
|
-
return parseProbeRecords(content)
|
|
213
|
-
} catch {
|
|
214
|
-
return []
|
|
215
|
-
} finally {
|
|
216
|
-
if (file !== undefined) fs.closeSync(file)
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function parseProbeRecords (content) {
|
|
221
|
-
const records = []
|
|
222
|
-
for (const line of content.split(/\r?\n/)) {
|
|
223
|
-
if (!line) continue
|
|
224
|
-
try {
|
|
225
|
-
const record = JSON.parse(line)
|
|
226
|
-
if (isProbeRecord(record)) records.push(sanitizeForReport(record))
|
|
227
|
-
} catch {}
|
|
228
|
-
}
|
|
229
|
-
return records
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
function isProbeRecord (record) {
|
|
233
|
-
return record && typeof record === 'object' && !Array.isArray(record) &&
|
|
234
|
-
(record.type === 'process-start' || record.type === 'module-load') &&
|
|
235
|
-
Number.isInteger(record.pid) && Number.isInteger(record.ppid) &&
|
|
236
|
-
typeof record.cwd === 'string' && Array.isArray(record.argv)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function formatNodeRequire (filename) {
|
|
240
|
-
if (!/\s/.test(filename)) return filename
|
|
241
|
-
return JSON.stringify(filename)
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
module.exports = {
|
|
245
|
-
runInitializationProbe,
|
|
246
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const fs = require('node:fs')
|
|
4
|
-
const path = require('node:path')
|
|
5
|
-
|
|
6
|
-
const MAX_FILE_BYTES = 512 * 1024
|
|
7
|
-
const SCRIPT_LITERAL_PATTERN = /['"]([^'"]+\.[cm]?[jt]sx?)['"]/g
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Finds Test Optimization initialization loaded from a Vitest setup file.
|
|
11
|
-
*
|
|
12
|
-
* @param {object} manifest normalized manifest
|
|
13
|
-
* @param {object} framework manifest framework
|
|
14
|
-
* @returns {{configFile: string, setupFile: string}[]} late initialization evidence
|
|
15
|
-
*/
|
|
16
|
-
function findLateInitialization (manifest, framework) {
|
|
17
|
-
if (framework.framework !== 'vitest') return []
|
|
18
|
-
|
|
19
|
-
const findings = []
|
|
20
|
-
const seen = new Set()
|
|
21
|
-
for (const configFile of framework.project?.configFiles || []) {
|
|
22
|
-
const config = readSmallFile(configFile)
|
|
23
|
-
if (!config || !/\bsetupFiles\b/.test(config)) continue
|
|
24
|
-
|
|
25
|
-
if (/setupFiles[\s\S]{0,1000}?dd-trace\/ci\/init/.test(config)) {
|
|
26
|
-
addFinding(findings, seen, configFile, 'dd-trace/ci/init')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
for (const match of config.matchAll(SCRIPT_LITERAL_PATTERN)) {
|
|
30
|
-
for (const candidate of getSetupFileCandidates(manifest.repository.root, configFile, match[1])) {
|
|
31
|
-
const setup = readSmallFile(candidate)
|
|
32
|
-
if (!setup || !/dd-trace\/ci\/init/.test(setup)) continue
|
|
33
|
-
addFinding(findings, seen, configFile, candidate)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return findings
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getSetupFileCandidates (repositoryRoot, configFile, filename) {
|
|
41
|
-
if (path.isAbsolute(filename)) return [filename]
|
|
42
|
-
return [
|
|
43
|
-
path.resolve(path.dirname(configFile), filename),
|
|
44
|
-
path.resolve(repositoryRoot, filename),
|
|
45
|
-
]
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function readSmallFile (filename) {
|
|
49
|
-
try {
|
|
50
|
-
const stat = fs.statSync(filename)
|
|
51
|
-
if (!stat.isFile() || stat.size > MAX_FILE_BYTES) return
|
|
52
|
-
return fs.readFileSync(filename, 'utf8')
|
|
53
|
-
} catch {}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function addFinding (findings, seen, configFile, setupFile) {
|
|
57
|
-
const key = `${configFile}:${setupFile}`
|
|
58
|
-
if (seen.has(key)) return
|
|
59
|
-
seen.add(key)
|
|
60
|
-
findings.push({ configFile, setupFile })
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
module.exports = { findLateInitialization }
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const path = require('path')
|
|
4
|
-
|
|
5
|
-
const { inheritApprovedExecutable } = require('./executable-approval')
|
|
6
|
-
|
|
7
|
-
const JEST_NO_WATCHMAN_ADJUSTMENT = 'Disable Watchman for local validation to avoid home-directory writes.'
|
|
8
|
-
const INLINE_DATADOG_ENV_PATTERN = /(?:^|[\s;&|()'"])(?:export\s+|set\s+)?["']?(?:\$env:)?DD_[A-Z0-9_]+\s*\+?=/i
|
|
9
|
-
const INIT_PATH = path.resolve(__dirname, '..', 'init.js').replaceAll('\\', '/')
|
|
10
|
-
const REGISTER_PATH = path.resolve(__dirname, '..', '..', 'register.js').replaceAll('\\', '/')
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Applies semantics-preserving local validation adjustments to a command.
|
|
14
|
-
*
|
|
15
|
-
* @param {object} framework manifest framework entry
|
|
16
|
-
* @param {object} command command to adjust
|
|
17
|
-
* @returns {object} adjusted command
|
|
18
|
-
*/
|
|
19
|
-
function getLocalValidationCommand (framework, command) {
|
|
20
|
-
if (framework.framework !== 'jest' || command.usesShell || command.argv?.includes('--no-watchman') ||
|
|
21
|
-
!isJestCommand(command.argv)) {
|
|
22
|
-
return command
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const argv = [...command.argv]
|
|
26
|
-
const executable = path.basename(argv[0]).replace(/\.cmd$/i, '')
|
|
27
|
-
if (executable === 'npm' && !argv.includes('--')) argv.push('--')
|
|
28
|
-
argv.push('--no-watchman')
|
|
29
|
-
|
|
30
|
-
return inheritApprovedExecutable(command, {
|
|
31
|
-
...command,
|
|
32
|
-
argv,
|
|
33
|
-
localAdjustments: [
|
|
34
|
-
...(command.localAdjustments || []),
|
|
35
|
-
JEST_NO_WATCHMAN_ADJUSTMENT,
|
|
36
|
-
],
|
|
37
|
-
})
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Identifies Jest entrypoints and package scripts that can forward Jest options.
|
|
42
|
-
*
|
|
43
|
-
* @param {string[]} argv command arguments
|
|
44
|
-
* @returns {boolean} whether --no-watchman can be appended safely
|
|
45
|
-
*/
|
|
46
|
-
function isJestCommand (argv) {
|
|
47
|
-
if (!Array.isArray(argv) || argv.length === 0) return false
|
|
48
|
-
|
|
49
|
-
const executable = path.basename(argv[0]).replace(/\.cmd$/i, '')
|
|
50
|
-
if (['npm', 'npx', 'pnpm', 'yarn', 'yarnpkg'].includes(executable)) return true
|
|
51
|
-
|
|
52
|
-
return argv.some(argument => /(?:^|[/\\])(?:jest|jest\.js)$/.test(argument))
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Removes Datadog initialization from a command before an uninstrumented preflight.
|
|
57
|
-
*
|
|
58
|
-
* @param {object} command command to sanitize
|
|
59
|
-
* @returns {object} Datadog-clean command
|
|
60
|
-
*/
|
|
61
|
-
function getDatadogCleanCommand (command) {
|
|
62
|
-
const inlineInitialization = getInlineDatadogInitialization(command)
|
|
63
|
-
if (inlineInitialization) {
|
|
64
|
-
throw new Error(
|
|
65
|
-
`Cannot create a Datadog-clean command because it ${inlineInitialization}. ` +
|
|
66
|
-
'Remove inline Datadog initialization from the local validation command.'
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const env = {}
|
|
71
|
-
for (const [name, value] of Object.entries(command.env || {})) {
|
|
72
|
-
if (name.startsWith('DD_') || (name === 'NODE_OPTIONS' && /dd-trace/.test(value))) continue
|
|
73
|
-
env[name] = value
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return inheritApprovedExecutable(command, {
|
|
77
|
-
...command,
|
|
78
|
-
env,
|
|
79
|
-
})
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Finds Datadog initialization embedded in executable arguments or shell source.
|
|
84
|
-
*
|
|
85
|
-
* @param {object} command manifest command
|
|
86
|
-
* @returns {string|undefined} customer-facing inline initialization description
|
|
87
|
-
*/
|
|
88
|
-
function getInlineDatadogInitialization (command) {
|
|
89
|
-
const source = command?.usesShell
|
|
90
|
-
? String(command.shellCommand || '')
|
|
91
|
-
: (command?.argv || []).join(' ')
|
|
92
|
-
const normalized = source.replaceAll('\\', '/')
|
|
93
|
-
|
|
94
|
-
if (normalized.includes('dd-trace/ci/init') || normalized.includes('dd-trace/register.js') ||
|
|
95
|
-
normalized.includes(INIT_PATH) || normalized.includes(REGISTER_PATH)) {
|
|
96
|
-
return 'contains an inline dd-trace preload'
|
|
97
|
-
}
|
|
98
|
-
if (INLINE_DATADOG_ENV_PATTERN.test(source)) return 'contains an inline DD_* assignment'
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Returns the CI wiring command with the replay shell recorded by CI discovery when available.
|
|
103
|
-
*
|
|
104
|
-
* @param {object} framework manifest framework entry
|
|
105
|
-
* @returns {object|undefined} command to run
|
|
106
|
-
*/
|
|
107
|
-
function getCiWiringCommand (framework) {
|
|
108
|
-
const command = framework.ciWiringCommand
|
|
109
|
-
if (!command || !command.usesShell || command.shell || !framework.ciWiring?.shell) return command
|
|
110
|
-
|
|
111
|
-
const replayCommand = getShellReplayCommand(command, framework.ciWiring.shell)
|
|
112
|
-
if (replayCommand) return inheritApprovedExecutable(command, replayCommand)
|
|
113
|
-
|
|
114
|
-
const shell = getReplayShell(framework.ciWiring.shell)
|
|
115
|
-
if (!shell) return command
|
|
116
|
-
|
|
117
|
-
return inheritApprovedExecutable(command, {
|
|
118
|
-
...command,
|
|
119
|
-
shell,
|
|
120
|
-
})
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function getShellReplayCommand (command, shell) {
|
|
124
|
-
const tokens = tokenizeShellTemplate(shell)
|
|
125
|
-
const hasTemplate = tokens.includes('{0}')
|
|
126
|
-
if (tokens.length <= 1 && !hasTemplate) return
|
|
127
|
-
|
|
128
|
-
const argv = hasTemplate ? tokens.filter(token => token !== '{0}') : tokens
|
|
129
|
-
if (!isBourneShell(argv[0])) return
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
...command,
|
|
133
|
-
argv: [...argv, '-c', command.shellCommand],
|
|
134
|
-
shell: undefined,
|
|
135
|
-
shellCommand: undefined,
|
|
136
|
-
usesShell: false,
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function getReplayShell (shell) {
|
|
141
|
-
const value = String(shell || '').trim()
|
|
142
|
-
if (!value) return
|
|
143
|
-
|
|
144
|
-
const firstToken = value.split(/\s+/)[0]
|
|
145
|
-
if (firstToken && value.includes('{0}')) return firstToken
|
|
146
|
-
if (!/\s/.test(value)) return value
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function tokenizeShellTemplate (shell) {
|
|
150
|
-
return String(shell || '').trim().split(/\s+/).filter(Boolean)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function isBourneShell (executable) {
|
|
154
|
-
const basename = path.basename(String(executable || ''))
|
|
155
|
-
return basename === 'bash' || basename === 'sh' || basename === 'zsh'
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
module.exports = {
|
|
159
|
-
getCiWiringCommand,
|
|
160
|
-
getDatadogCleanCommand,
|
|
161
|
-
getInlineDatadogInitialization,
|
|
162
|
-
getLocalValidationCommand,
|
|
163
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const path = require('path')
|
|
4
|
-
|
|
5
|
-
const { getArtifactId } = require('./artifact-id')
|
|
6
|
-
const { runCommand } = require('./command-runner')
|
|
7
|
-
|
|
8
|
-
async function runSetupCommands ({ framework, out, options }) {
|
|
9
|
-
const commands = framework.setup?.commands || []
|
|
10
|
-
const results = []
|
|
11
|
-
const artifacts = []
|
|
12
|
-
|
|
13
|
-
for (let index = 0; index < commands.length; index++) {
|
|
14
|
-
const command = commands[index]
|
|
15
|
-
const outDir = path.join(
|
|
16
|
-
out,
|
|
17
|
-
'setup',
|
|
18
|
-
getArtifactId(framework.id),
|
|
19
|
-
`${index + 1}-${getArtifactId(command.id || 'setup')}`
|
|
20
|
-
)
|
|
21
|
-
// eslint-disable-next-line no-await-in-loop
|
|
22
|
-
const result = await runCommand(command, {
|
|
23
|
-
artifactRoot: out,
|
|
24
|
-
envMode: 'clean',
|
|
25
|
-
outDir,
|
|
26
|
-
label: `${framework.id}:setup:${command.id || index + 1}`,
|
|
27
|
-
repositoryRoot: options.repositoryRoot,
|
|
28
|
-
requireExecutableApproval: options.requireExecutableApproval,
|
|
29
|
-
verbose: options.verbose,
|
|
30
|
-
})
|
|
31
|
-
const summary = summarizeSetupCommand(command, result, outDir)
|
|
32
|
-
results.push(summary)
|
|
33
|
-
artifacts.push(...Object.values(result.artifacts))
|
|
34
|
-
|
|
35
|
-
if (command.required !== false && result.exitCode !== 0) {
|
|
36
|
-
const failure = getSetupFailure(framework, command, result, results)
|
|
37
|
-
failure.artifacts.push(...artifacts)
|
|
38
|
-
return {
|
|
39
|
-
ok: false,
|
|
40
|
-
results,
|
|
41
|
-
artifacts,
|
|
42
|
-
failure,
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return { ok: true, results, artifacts }
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function getSetupFailure (framework, command, result, setupCommands) {
|
|
51
|
-
const setupName = command.description || command.id || result.command
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
frameworkId: framework.id,
|
|
55
|
-
scenario: 'all',
|
|
56
|
-
status: 'blocked',
|
|
57
|
-
diagnosis: `Validation is blocked by required project setup: ${setupName}. ` +
|
|
58
|
-
'No Test Optimization conclusion was reached for this framework.',
|
|
59
|
-
evidence: {
|
|
60
|
-
blockedByProjectSetup: true,
|
|
61
|
-
setupFailed: true,
|
|
62
|
-
setupCommand: {
|
|
63
|
-
id: command.id,
|
|
64
|
-
description: command.description,
|
|
65
|
-
command: result.command,
|
|
66
|
-
cwd: result.cwd,
|
|
67
|
-
exitCode: result.exitCode,
|
|
68
|
-
timedOut: result.timedOut,
|
|
69
|
-
stdoutSummary: tail(result.stdout),
|
|
70
|
-
stderrSummary: tail(result.stderr),
|
|
71
|
-
},
|
|
72
|
-
setupCommands,
|
|
73
|
-
recommendation: 'Run or fix the documented project setup command, then rerun validation for this framework.',
|
|
74
|
-
},
|
|
75
|
-
artifacts: [],
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function summarizeSetupCommand (command, result, outDir) {
|
|
80
|
-
return {
|
|
81
|
-
id: command.id,
|
|
82
|
-
description: command.description,
|
|
83
|
-
required: command.required !== false,
|
|
84
|
-
command: result.command,
|
|
85
|
-
cwd: result.cwd,
|
|
86
|
-
exitCode: result.exitCode,
|
|
87
|
-
timedOut: result.timedOut,
|
|
88
|
-
durationMs: result.durationMs,
|
|
89
|
-
artifactDirectory: outDir,
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function tail (value) {
|
|
94
|
-
return String(value || '').trim().split(/\r?\n/).slice(-20).join('\n')
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
module.exports = { runSetupCommands }
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://github.com/DataDog/dd-trace-js/ci/test-optimization-validation-manifest.schema.json","title":"Datadog Test Optimization validation manifest","type":"object","required":["schemaVersion","repository","environment","frameworks"],"properties":{"schemaVersion":{"type":"string"},"generatedAt":{"type":"string"},"repository":{"$ref":"#/$defs/repository"},"environment":{"$ref":"#/$defs/environment"},"ciDiscovery":{"$ref":"#/$defs/ciDiscovery"},"frameworks":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/framework"}},"omitted":{"type":"array","items":{"type":"string"}},"warnings":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"$defs":{"repository":{"type":"object","required":["root"],"properties":{"root":{"$ref":"#/$defs/absolutePathString"},"gitRemote":{"type":["string","null"]},"gitSha":{"type":["string","null"]},"packageManager":{"enum":["npm","yarn","pnpm","bun","mixed","unknown"]},"workspaceManager":{"enum":["npm","yarn","pnpm","lerna","nx","turbo","rush","none","unknown"]}},"additionalProperties":true},"environment":{"type":"object","properties":{"os":{"enum":["darwin","linux","windows","unknown"]},"shell":{"type":["string","null"],"minLength":1,"pattern":"^[^\\u0000-\\u001F\\u007F-\\u009F\\u00AD\\u0600-\\u0605\\u061C\\u06DD\\u070F\\u0890\\u0891\\u08E2\\u180B-\\u180F\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u206F\\uFE00-\\uFE0F\\uFEFF\\uFFF9-\\uFFFB]*$"},"nodeVersion":{"type":["string","null"]},"requiredEnvVars":{"type":"array","items":{"type":"string"}},"safeEnv":{"type":"object"}},"additionalProperties":true},"ciDiscovery":{"type":"object","properties":{"searched":{"type":"array","items":{"type":"string"}},"found":{"type":"array","items":{"type":"string"}},"staticFound":{"type":"array","items":{"type":"string"}},"method":{"type":"string"},"warnings":{"type":"array","items":{"type":"string"}},"notes":{"type":"array","items":{"type":"string"}},"contradictions":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"framework":{"type":"object","required":["id","framework","status","project"],"properties":{"id":{"type":"string"},"framework":{"enum":["jest","vitest","mocha","cucumber","cypress","playwright","node:test","ava","tap","jasmine","karma","uvu","testcafe","custom","unknown"]},"frameworkVersion":{"type":["string","null"]},"language":{"enum":["javascript","typescript","mixed","unknown"]},"status":{"enum":["runnable","detected_not_runnable","requires_external_service","requires_manual_setup","unsupported_by_validator","unknown"]},"supportLevel":{"enum":["validator_supported","dd_trace_supported_but_validator_missing_adapter","detected_only","unknown"]},"project":{"$ref":"#/$defs/project"},"setup":{"$ref":"#/$defs/setup"},"existingTestCommand":{"$ref":"#/$defs/command"},"ciWiring":{"$ref":"#/$defs/ciWiring"},"ciWiringCommand":{"$ref":"#/$defs/command"},"forcedLocalCommand":false,"preflight":{"$ref":"#/$defs/preflight"},"generatedTestStrategy":{"$ref":"#/$defs/generatedTestStrategy"},"notes":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"allOf":[{"if":{"properties":{"status":{"const":"runnable"}}},"then":{"required":["existingTestCommand","preflight","ciWiring"]}},{"if":{"properties":{"status":{"enum":["detected_not_runnable","requires_external_service","requires_manual_setup","unsupported_by_validator","unknown"]}}},"then":{"required":["notes"],"properties":{"notes":{"minItems":1}}}},{"if":{"required":["ciWiringCommand"]},"then":{"properties":{"ciWiring":{"required":["provider","configFile","job","step","workingDirectory","whySelected"]}}}},{"if":{"required":["ciWiring"],"properties":{"ciWiring":{"required":["replayability"],"properties":{"replayability":{"const":"replayable"}}}}},"then":{"required":["ciWiringCommand"]}},{"if":{"required":["ciWiring"],"properties":{"ciWiring":{"required":["replayability"],"properties":{"replayability":{"const":"not_replayable"}}}}},"then":{"not":{"required":["ciWiringCommand"]}}}]},"project":{"type":"object","required":["root"],"properties":{"name":{"type":["string","null"]},"root":{"$ref":"#/$defs/absolutePathString"},"packageJson":{"anyOf":[{"$ref":"#/$defs/absolutePathString"},{"type":"null"}]},"configFiles":{"type":"array","items":{"$ref":"#/$defs/absolutePathString"}},"evidence":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"setup":{"type":"object","properties":{"commands":{"type":"array","items":{"$ref":"#/$defs/setupCommand"}},"services":{"type":"array","items":{"$ref":"#/$defs/service"}}},"additionalProperties":true},"setupCommand":{"allOf":[{"$ref":"#/$defs/command"},{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"},"required":{"type":"boolean"}}}]},"service":{"type":"object","properties":{"name":{"type":"string"},"required":{"type":"boolean"},"description":{"type":"string"}},"additionalProperties":true},"command":{"type":"object","required":["cwd"],"properties":{"description":{"type":"string"},"id":{"type":"string"},"required":{"type":"boolean"},"cwd":{"$ref":"#/$defs/absolutePathString"},"argv":{"type":"array","items":{"$ref":"#/$defs/executableString"}},"env":{"type":"object","propertyNames":{"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"additionalProperties":{"$ref":"#/$defs/executableString"}},"requiredEnvVars":{"type":"array","items":{"type":"string"}},"outputPaths":{"type":"array","items":{"$ref":"#/$defs/absolutePathString"}},"timeoutMs":{"type":"number","exclusiveMinimum":0,"maximum":1800000},"usesShell":{"type":"boolean"},"shell":{"$ref":"#/$defs/executableString"},"shellCommand":{"anyOf":[{"$ref":"#/$defs/executableString"},{"type":"null"}]},"shellReason":{"type":["string","null"]}},"additionalProperties":false,"allOf":[{"if":{"required":["usesShell"],"properties":{"usesShell":{"const":true}}},"then":{"required":["shellCommand"]}},{"if":{"required":["shell"]},"then":{"required":["usesShell"],"properties":{"usesShell":{"const":true}}}}],"anyOf":[{"required":["argv"]},{"properties":{"usesShell":{"const":true}},"required":["usesShell","shellCommand"]}]},"executableString":{"type":"string","not":{"pattern":"\\$\\{[^}]+\\}"}},"absolutePathString":{"allOf":[{"$ref":"#/$defs/executableString"},{"pattern":"^(?:/|[A-Za-z]:[\\\\/]|\\\\\\\\)"}]},"preflight":{"type":"object","properties":{"status":{"enum":["pending","verified"]},"ran":{"type":"boolean"},"command":{"type":"string"},"exitCode":{"type":["number","null"]},"durationMs":{"type":["number","null"]},"observedTestCount":{"type":["number","null"]},"stdoutSummary":{"type":"string"},"stderrSummary":{"type":"string"},"maxTestCount":{"type":"integer","minimum":1,"maximum":1000}},"additionalProperties":true,"required":["maxTestCount"]},"ciWiring":{"type":"object","required":["status","replayability"],"properties":{"status":{"enum":["pass","fail","skip","unknown"]},"provider":{"type":"string"},"configFile":{"$ref":"#/$defs/absolutePathString"},"workflow":{"type":["string","null"]},"job":{"type":["string","null"]},"step":{"type":["string","null"]},"matrix":{"type":"object"},"runner":{"type":["string","null"]},"shell":{"type":["string","null"],"minLength":1,"pattern":"^[^\\u0000-\\u001F\\u007F-\\u009F\\u00AD\\u0600-\\u0605\\u061C\\u06DD\\u070F\\u0890\\u0891\\u08E2\\u180B-\\u180F\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u206F\\uFE00-\\uFE0F\\uFEFF\\uFFF9-\\uFFFB]*$"},"workingDirectory":{"anyOf":[{"$ref":"#/$defs/absolutePathString"},{"type":"null"}]},"inheritedEnv":{"type":"object"},"requiredSecretEnvVars":{"type":"array","items":{"type":"string"}},"setupCommandIds":{"type":"array","items":{"type":"string"}},"whySelected":{"type":"string"},"workflowEnv":{"type":"object"},"jobEnv":{"type":"object"},"stepEnv":{"type":"object"},"initialization":{"type":"object","required":["status","evidence"],"properties":{"status":{"enum":["configured","not_configured","unknown"]},"evidence":{"type":"array","items":{"type":"string"}}},"allOf":[{"if":{"properties":{"status":{"enum":["configured","not_configured"]}},"required":["status"]},"then":{"properties":{"evidence":{"minItems":1}}}}],"additionalProperties":false},"packageScriptExpansionChain":{"type":"array","items":{"type":"string"}},"runnerToolChain":{"type":"array","items":{"type":"string"}},"unresolved":{"type":"array","items":{"type":"string"}},"diagnosis":{"type":"string"},"reason":{"type":"string"},"replayability":{"enum":["replayable","not_replayable"]},"replayBlocker":{"type":"string","minLength":1},"ciWiringCommand":false},"additionalProperties":true,"allOf":[{"if":{"properties":{"status":{"enum":["skip","unknown"]}}},"then":{"anyOf":[{"required":["diagnosis"]},{"required":["reason"]}]}},{"if":{"properties":{"replayability":{"const":"not_replayable"}},"required":["replayability"]},"then":{"required":["replayBlocker"],"properties":{"status":{"enum":["skip","unknown"]}}}}]},"generatedTestStrategy":{"type":"object","required":["status"],"properties":{"status":{"enum":["planned","verified","proposed","not_possible"]},"reason":{"type":["string","null"]},"adapter":{"enum":["jest","vitest","mocha","cucumber","cypress","playwright","node:test","generic-js","custom","unknown"]},"testDirectory":{"anyOf":[{"$ref":"#/$defs/absolutePathString"},{"type":"null"}]},"moduleSystem":{"enum":["commonjs","esm","typescript","unknown"]},"fileExtension":{"type":"string"},"supportsFocusedSingleFileRun":{"type":"boolean"},"usesMultipleFiles":{"type":"boolean"},"files":{"type":"array","maxItems":8,"items":{"$ref":"#/$defs/generatedFile"}},"scenarios":{"type":"array","items":{"$ref":"#/$defs/scenario"}},"verification":{"$ref":"#/$defs/verification"},"cleanupPaths":{"type":"array","items":{"$ref":"#/$defs/absolutePathString"}},"limitations":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"allOf":[{"if":{"properties":{"status":{"enum":["planned","verified"]}}},"then":{"required":["files","scenarios","cleanupPaths"],"properties":{"scenarios":{"allOf":[{"items":{"required":["testIdentities","expectedWithoutDatadog"],"properties":{"testIdentities":{"minItems":1}}}},{"contains":{"properties":{"id":{"const":"basic-pass"}},"required":["id"]}},{"contains":{"properties":{"id":{"const":"atr-fail-once"}},"required":["id"]}},{"contains":{"properties":{"id":{"const":"test-management-target"}},"required":["id"]}}]}}}}]},"generatedFile":{"type":"object","required":["path","contentLines"],"properties":{"path":{"$ref":"#/$defs/absolutePathString"},"role":{"enum":["test","feature","steps","support","state","config"]},"contentLines":{"type":"array","maxItems":256,"items":{"type":"string","maxLength":4096,"pattern":"^[^\\r\\n\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F\\u061C\\u180B-\\u180F\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u206F\\uFE00-\\uFE0F\\uFEFF]*$"}}},"additionalProperties":true},"scenario":{"type":"object","required":["id","runCommand"],"properties":{"id":{"type":"string"},"purpose":{"type":"string"},"runCommand":{"$ref":"#/$defs/command"},"expectedWithoutDatadog":{"$ref":"#/$defs/expectedWithoutDatadog"},"testIdentities":{"type":"array","items":{"$ref":"#/$defs/testIdentity"}}},"additionalProperties":true,"allOf":[{"if":{"required":["id"],"properties":{"id":{"const":"basic-pass"}}},"then":{"properties":{"expectedWithoutDatadog":{"properties":{"exitCode":{"const":0},"observedTestCount":{"const":1}}}}}},{"if":{"required":["id"],"properties":{"id":{"const":"atr-fail-once"}}},"then":{"properties":{"expectedWithoutDatadog":{"properties":{"exitCode":{"const":1},"observedTestCount":{"const":1}}}}}},{"if":{"required":["id"],"properties":{"id":{"const":"test-management-target"}}},"then":{"properties":{"expectedWithoutDatadog":{"properties":{"exitCode":{"const":0},"observedTestCount":{"const":1}}}}}}]},"expectedWithoutDatadog":{"type":"object","required":["exitCode","observedTestCount"],"properties":{"exitCode":{"type":"number"},"observedTestCount":{"type":"number"}},"additionalProperties":true},"testIdentity":{"type":"object","required":["name"],"properties":{"suite":{"type":["string","null"]},"name":{"type":"string","minLength":1},"file":{"anyOf":[{"$ref":"#/$defs/absolutePathString"},{"type":"null"}]},"parameters":{"type":["object","null"]}},"additionalProperties":true},"verification":{"type":"object","properties":{"createdTemporaryFiles":{"type":"boolean"},"ranScenarioIds":{"type":"array","items":{"type":"string"}},"exitCode":{"type":["number","null"]},"durationMs":{"type":["number","null"]},"observedTestCount":{"type":["number","null"]},"cleanupCompleted":{"type":"boolean"},"stdoutSummary":{"type":"string"},"stderrSummary":{"type":"string"}},"additionalProperties":true}}}
|