dd-trace 6.8.0 → 6.9.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 +27 -25
- package/ci/init.js +6 -6
- package/ci/runbook.md +114 -20
- package/ci/test-optimization-validation/approval-artifacts.js +36 -3
- package/ci/test-optimization-validation/approval.js +78 -22
- package/ci/test-optimization-validation/blocker-category.js +24 -0
- package/ci/test-optimization-validation/ci-discovery.js +23 -17
- package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
- package/ci/test-optimization-validation/ci-remediation.js +1 -1
- package/ci/test-optimization-validation/cli.js +280 -52
- package/ci/test-optimization-validation/command-blocker.js +159 -20
- package/ci/test-optimization-validation/command-output-policy.js +9 -3
- package/ci/test-optimization-validation/command-runner.js +9 -7
- package/ci/test-optimization-validation/environment.js +4 -2
- package/ci/test-optimization-validation/executable.js +15 -13
- package/ci/test-optimization-validation/execution-lock.js +70 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
- package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
- package/ci/test-optimization-validation/generated-files.js +38 -28
- package/ci/test-optimization-validation/generated-test-contract.js +8 -4
- package/ci/test-optimization-validation/generated-verifier.js +24 -17
- package/ci/test-optimization-validation/literal-glob.js +52 -0
- package/ci/test-optimization-validation/manifest-loader.js +3 -0
- package/ci/test-optimization-validation/manifest-scaffold.js +300 -50
- package/ci/test-optimization-validation/manifest-schema.js +80 -17
- package/ci/test-optimization-validation/offline-fixtures.js +13 -4
- package/ci/test-optimization-validation/package-check.js +94 -0
- package/ci/test-optimization-validation/plan-writer.js +183 -10
- package/ci/test-optimization-validation/preflight-runner.js +110 -31
- package/ci/test-optimization-validation/project-build-artifact.js +31 -0
- package/ci/test-optimization-validation/redaction.js +18 -27
- package/ci/test-optimization-validation/report-writer.js +182 -48
- package/ci/test-optimization-validation/result-semantics.js +20 -3
- package/ci/test-optimization-validation/runner-command.js +40 -20
- package/ci/test-optimization-validation/runner-contract.js +79 -43
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +59 -5
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
- package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
- package/ci/test-optimization-validation/static-diagnosis.js +10 -3
- package/ci/test-optimization-validation/test-output.js +2 -1
- package/ci/test-optimization-validation/validation-blocker.js +21 -0
- package/ci/vitest-no-worker-init-setup.mjs +30 -34
- package/index.d.ts +98 -1
- package/init.js +1 -17
- package/initialize.mjs +11 -0
- package/loader-hook.mjs +6 -4
- package/package.json +5 -3
- package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
- package/packages/datadog-esbuild/src/utils.js +16 -1
- package/packages/datadog-instrumentations/src/ai.js +61 -34
- package/packages/datadog-instrumentations/src/bluebird.js +6 -6
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +81 -5
- package/packages/datadog-instrumentations/src/cucumber.js +124 -47
- package/packages/datadog-instrumentations/src/dns.js +21 -2
- package/packages/datadog-instrumentations/src/electron.js +1 -0
- package/packages/datadog-instrumentations/src/fastify.js +3 -1
- package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +33 -9
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +118 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +37 -6
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +979 -233
- package/packages/datadog-instrumentations/src/mocha/main.js +107 -9
- package/packages/datadog-instrumentations/src/mocha/utils.js +64 -42
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +155 -23
- package/packages/datadog-instrumentations/src/openai.js +14 -0
- package/packages/datadog-instrumentations/src/pg.js +180 -6
- package/packages/datadog-instrumentations/src/playwright.js +173 -79
- package/packages/datadog-instrumentations/src/promise-js.js +4 -4
- package/packages/datadog-instrumentations/src/promise.js +3 -3
- package/packages/datadog-instrumentations/src/router.js +195 -19
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -10
- package/packages/datadog-instrumentations/src/vitest-main.js +360 -56
- package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
- package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
- package/packages/datadog-instrumentations/src/webdriverio.js +194 -18
- package/packages/datadog-instrumentations/src/when.js +3 -3
- package/packages/datadog-instrumentations/src/ws.js +5 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
- package/packages/datadog-plugin-cucumber/src/index.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
- package/packages/datadog-plugin-cypress/src/support.js +7 -4
- package/packages/datadog-plugin-electron/src/net.js +10 -4
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +1 -18
- package/packages/datadog-plugin-jest/src/util.js +2 -0
- package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
- package/packages/datadog-plugin-mocha/src/index.js +318 -1
- package/packages/datadog-plugin-next/src/index.js +57 -9
- package/packages/datadog-plugin-openai/src/tracing.js +4 -0
- package/packages/datadog-plugin-pg/src/index.js +65 -1
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +95 -7
- package/packages/datadog-shimmer/src/shimmer.js +6 -2
- package/packages/dd-trace/src/aiguard/sdk.js +3 -0
- package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
- package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
- package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
- package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
- package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +48 -476
- package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
- package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
- package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +27 -13
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
- package/packages/dd-trace/src/config/helper.js +2 -0
- package/packages/dd-trace/src/config/index.js +16 -5
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +8 -0
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
- package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
- package/packages/dd-trace/src/guardrails/log.js +1 -10
- package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
- package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
- package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
- package/packages/dd-trace/src/llmobs/noop.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +149 -68
- package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
- package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
- package/packages/dd-trace/src/log/channels.js +1 -9
- package/packages/dd-trace/src/log/levels.js +12 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span.js +4 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +6 -0
- package/packages/dd-trace/src/plugins/util/git.js +6 -4
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
- package/packages/dd-trace/src/plugins/util/test.js +319 -75
- package/packages/dd-trace/src/plugins/util/url.js +1 -1
- package/packages/dd-trace/src/plugins/util/web.js +23 -1
- package/packages/dd-trace/src/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
- package/packages/dd-trace/src/proxy.js +1 -0
- package/packages/dd-trace/src/ritm.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
- package/packages/dd-trace/src/sampling_rule.js +4 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
- package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
- package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/version.js +10 -8
- /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
|
@@ -5,17 +5,23 @@
|
|
|
5
5
|
const fs = require('node:fs')
|
|
6
6
|
const path = require('node:path')
|
|
7
7
|
|
|
8
|
-
const { assertApprovalDigest } = require('./approval')
|
|
8
|
+
const { assertApprovalDigest, getApprovalProjectSnapshot } = require('./approval')
|
|
9
9
|
const { loadApprovedPlan } = require('./approval-artifacts')
|
|
10
|
+
const { BLOCKER_CATEGORIES, getBlockerDomain } = require('./blocker-category')
|
|
10
11
|
const { annotateCiDiscovery } = require('./ci-discovery')
|
|
11
12
|
const { cleanupGeneratedFiles } = require('./generated-files')
|
|
12
13
|
const { verifyGeneratedTestStrategy } = require('./generated-verifier')
|
|
14
|
+
const {
|
|
15
|
+
acquireExecutionLock,
|
|
16
|
+
releaseExecutionLock,
|
|
17
|
+
} = require('./execution-lock')
|
|
13
18
|
const { loadManifest } = require('./manifest-loader')
|
|
14
19
|
const { createManifestScaffold } = require('./manifest-scaffold')
|
|
15
20
|
const {
|
|
16
21
|
formatExecutionPlanArtifacts,
|
|
17
22
|
getExecutionPlanPath,
|
|
18
23
|
} = require('./plan-writer')
|
|
24
|
+
const { checkInstalledPackage, getInstalledPackageFailure } = require('./package-check')
|
|
19
25
|
const { runFrameworkPreflight } = require('./preflight-runner')
|
|
20
26
|
const { sanitizeConsoleText } = require('./redaction')
|
|
21
27
|
const {
|
|
@@ -34,6 +40,7 @@ const { runEarlyFlakeDetection } = require('./scenarios/early-flake-detection')
|
|
|
34
40
|
const { runTestManagement } = require('./scenarios/test-management')
|
|
35
41
|
const { ensureSafeDirectory } = require('./safe-files')
|
|
36
42
|
const { getStaticBlocker, runStaticDiagnosis } = require('./static-diagnosis')
|
|
43
|
+
const { getValidationBlockerEvidence } = require('./validation-blocker')
|
|
37
44
|
|
|
38
45
|
const DEFAULT_MANIFEST = './dd-test-optimization-validation-manifest.json'
|
|
39
46
|
const DEFAULT_OUT = './dd-test-optimization-validation-results'
|
|
@@ -111,9 +118,11 @@ function parseArgs (argv) {
|
|
|
111
118
|
* @returns {Promise<void>} completion
|
|
112
119
|
*/
|
|
113
120
|
async function main (argv) {
|
|
121
|
+
let activeApprovedPlanSha256
|
|
114
122
|
let activeManifest
|
|
115
123
|
let activeOut
|
|
116
124
|
let cleanupOutcome = { status: 'not_started' }
|
|
125
|
+
let executionLock
|
|
117
126
|
|
|
118
127
|
try {
|
|
119
128
|
const options = parseArgs(argv)
|
|
@@ -150,8 +159,16 @@ async function main (argv) {
|
|
|
150
159
|
keepTempFiles: options.keepTempFiles,
|
|
151
160
|
verbose: options.verbose,
|
|
152
161
|
})
|
|
162
|
+
activeApprovedPlanSha256 = options.approvedPlanSha256
|
|
163
|
+
const packageCheck = hasLocalScenarios(options.scenarios)
|
|
164
|
+
? checkInstalledPackage()
|
|
165
|
+
: undefined
|
|
153
166
|
options.requireExecutableApproval = true
|
|
154
167
|
ensureSafeDirectory(manifest.repository.root, out, 'validation output directory', { allowRootSymlink: true })
|
|
168
|
+
executionLock = acquireExecutionLock({
|
|
169
|
+
out,
|
|
170
|
+
approvedPlanSha256: options.approvedPlanSha256,
|
|
171
|
+
})
|
|
155
172
|
writePendingReport({ manifest, out })
|
|
156
173
|
|
|
157
174
|
const staticDiagnosis = runStaticDiagnosis({ manifest, out })
|
|
@@ -162,7 +179,7 @@ async function main (argv) {
|
|
|
162
179
|
for (const framework of selectedFrameworks) {
|
|
163
180
|
// Each framework is independent. A setup blocker must not discard useful results from another adapter.
|
|
164
181
|
// eslint-disable-next-line no-await-in-loop
|
|
165
|
-
await validateFramework({ framework, manifest, options, out, results, staticDiagnosis })
|
|
182
|
+
await validateFramework({ framework, manifest, options, out, packageCheck, results, staticDiagnosis })
|
|
166
183
|
}
|
|
167
184
|
} finally {
|
|
168
185
|
try {
|
|
@@ -186,6 +203,7 @@ async function main (argv) {
|
|
|
186
203
|
results: annotatedResults,
|
|
187
204
|
staticDiagnosis,
|
|
188
205
|
runSummary: {
|
|
206
|
+
approvedPlanSha256: activeApprovedPlanSha256,
|
|
189
207
|
checkedScenarios: [...options.scenarios],
|
|
190
208
|
cleanup: cleanupOutcome,
|
|
191
209
|
executionStatus,
|
|
@@ -197,34 +215,69 @@ async function main (argv) {
|
|
|
197
215
|
validatorExitCode,
|
|
198
216
|
},
|
|
199
217
|
})
|
|
218
|
+
releaseExecutionLock(executionLock)
|
|
219
|
+
executionLock = undefined
|
|
200
220
|
console.log(`Validation report: ${path.join(out, 'report.md')}`)
|
|
201
221
|
console.log('Present the report and stop. Any correction or retry requires a fresh plan and approval.')
|
|
202
222
|
process.exitCode = validatorExitCode
|
|
203
223
|
} catch (error) {
|
|
204
|
-
|
|
205
|
-
if (
|
|
224
|
+
let reportError = error
|
|
225
|
+
if (executionLock) {
|
|
226
|
+
await publishFailureReport({
|
|
227
|
+
approvedPlanSha256: activeApprovedPlanSha256,
|
|
228
|
+
cleanup: cleanupOutcome,
|
|
229
|
+
error: reportError,
|
|
230
|
+
manifest: activeManifest,
|
|
231
|
+
out: activeOut,
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
if (executionLock) {
|
|
206
235
|
try {
|
|
207
|
-
|
|
236
|
+
releaseExecutionLock(executionLock)
|
|
237
|
+
executionLock = undefined
|
|
238
|
+
} catch (releaseError) {
|
|
239
|
+
reportError = releaseError
|
|
240
|
+
await publishFailureReport({
|
|
241
|
+
approvedPlanSha256: activeApprovedPlanSha256,
|
|
242
|
+
cleanup: cleanupOutcome,
|
|
243
|
+
error: reportError,
|
|
208
244
|
manifest: activeManifest,
|
|
209
245
|
out: activeOut,
|
|
210
|
-
results: [getOrchestrationFailure(error)],
|
|
211
|
-
runSummary: {
|
|
212
|
-
checkedScenarios: [],
|
|
213
|
-
cleanup: cleanupOutcome,
|
|
214
|
-
executionStatus: 'validator_error',
|
|
215
|
-
omittedScenarios: getSelectableScenarios(),
|
|
216
|
-
runCompleted: true,
|
|
217
|
-
selectedFrameworkIds: [],
|
|
218
|
-
validationCoverage: 'partial',
|
|
219
|
-
validatorExitCode: 3,
|
|
220
|
-
},
|
|
221
246
|
})
|
|
222
|
-
}
|
|
247
|
+
}
|
|
223
248
|
}
|
|
224
|
-
|
|
249
|
+
const validatorExitCode = reportError?.validationExitCode || 3
|
|
250
|
+
process.exitCode = validatorExitCode
|
|
251
|
+
const blockerRecommendation = reportError?.validationBlocker?.recommendation
|
|
252
|
+
const displayedError = reportError?.validationExitCode
|
|
253
|
+
? `${reportError.message}${blockerRecommendation ? ` ${blockerRecommendation}` : ''}`
|
|
254
|
+
: (reportError?.stack || reportError)
|
|
255
|
+
console.error(sanitizeConsoleText(displayedError))
|
|
225
256
|
}
|
|
226
257
|
}
|
|
227
258
|
|
|
259
|
+
async function publishFailureReport ({ approvedPlanSha256, cleanup, error, manifest, out }) {
|
|
260
|
+
if (!manifest || !out || error?.suppressReport) return
|
|
261
|
+
try {
|
|
262
|
+
await writeReport({
|
|
263
|
+
manifest,
|
|
264
|
+
out,
|
|
265
|
+
results: [getOrchestrationFailure(error)],
|
|
266
|
+
runSummary: {
|
|
267
|
+
approvedPlanSha256,
|
|
268
|
+
checkedScenarios: [],
|
|
269
|
+
cleanup,
|
|
270
|
+
executionStatus: error?.validationBlocker ? 'incomplete' : 'validator_error',
|
|
271
|
+
omittedScenarios: getSelectableScenarios(),
|
|
272
|
+
runCompleted: true,
|
|
273
|
+
selectedFrameworkIds: [],
|
|
274
|
+
validationCoverage: 'partial',
|
|
275
|
+
validatorExitCode: error?.validationExitCode || 3,
|
|
276
|
+
},
|
|
277
|
+
})
|
|
278
|
+
} catch {}
|
|
279
|
+
}
|
|
280
|
+
|
|
228
281
|
/**
|
|
229
282
|
* Runs selected checks for one framework.
|
|
230
283
|
*
|
|
@@ -233,11 +286,12 @@ async function main (argv) {
|
|
|
233
286
|
* @param {object} input.manifest validation manifest
|
|
234
287
|
* @param {object} input.options validator options
|
|
235
288
|
* @param {string} input.out output directory
|
|
289
|
+
* @param {object|undefined} input.packageCheck installed package-load result
|
|
236
290
|
* @param {object[]} input.results accumulated results
|
|
237
291
|
* @param {object} input.staticDiagnosis static diagnosis
|
|
238
292
|
* @returns {Promise<void>} completion
|
|
239
293
|
*/
|
|
240
|
-
async function validateFramework ({ framework, manifest, options, out, results, staticDiagnosis }) {
|
|
294
|
+
async function validateFramework ({ framework, manifest, options, out, packageCheck, results, staticDiagnosis }) {
|
|
241
295
|
let ciResult
|
|
242
296
|
if (options.scenarios.has(CI_WIRING)) {
|
|
243
297
|
logPhase(framework, 'CI configuration audit', 'start')
|
|
@@ -256,18 +310,38 @@ async function validateFramework ({ framework, manifest, options, out, results,
|
|
|
256
310
|
return
|
|
257
311
|
}
|
|
258
312
|
|
|
313
|
+
if (packageCheck?.ok === false) {
|
|
314
|
+
const packageFailure = getInstalledPackageFailure(framework, packageCheck)
|
|
315
|
+
results.push(packageFailure)
|
|
316
|
+
if (ciResult) results.push(ciResult)
|
|
317
|
+
addAdvancedNotReached(results, framework, options.scenarios, packageFailure)
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
|
|
259
321
|
const unavailable = getUnavailableExecutable(getBasicCommand(framework))
|
|
260
322
|
if (unavailable) {
|
|
261
|
-
|
|
323
|
+
const unavailableResult = getUnavailableRunnerResult(framework, unavailable)
|
|
324
|
+
results.push(unavailableResult)
|
|
262
325
|
if (ciResult) results.push(ciResult)
|
|
263
|
-
addNotReachedLocalResults(
|
|
326
|
+
addNotReachedLocalResults(
|
|
327
|
+
results,
|
|
328
|
+
framework,
|
|
329
|
+
options.scenarios,
|
|
330
|
+
'runner-unavailable',
|
|
331
|
+
unavailableResult.evidence.blockerCategory
|
|
332
|
+
)
|
|
264
333
|
return
|
|
265
334
|
}
|
|
266
335
|
|
|
267
336
|
const blocker = getStaticBlocker(framework, staticDiagnosis.report)
|
|
268
337
|
if (blocker) {
|
|
269
338
|
const staticFailure = getStaticFailure(framework, blocker, staticDiagnosis.reportPath)
|
|
270
|
-
const basicNotReached = getBasicNotReached(
|
|
339
|
+
const basicNotReached = getBasicNotReached(
|
|
340
|
+
framework,
|
|
341
|
+
blocker.reason,
|
|
342
|
+
'static-project-blocker',
|
|
343
|
+
blocker.blockerCategory
|
|
344
|
+
)
|
|
271
345
|
results.push(staticFailure, basicNotReached)
|
|
272
346
|
if (ciResult) results.push(ciResult)
|
|
273
347
|
addAdvancedNotReached(results, framework, options.scenarios, basicNotReached)
|
|
@@ -321,8 +395,15 @@ function initializeManifest (options) {
|
|
|
321
395
|
if (path.dirname(manifestPath) !== process.cwd()) {
|
|
322
396
|
throw new Error('The generated manifest must be stored directly in the current repository root.')
|
|
323
397
|
}
|
|
398
|
+
if (fs.existsSync(manifestPath)) return reuseExistingManifest(manifestPath)
|
|
399
|
+
|
|
324
400
|
const manifest = createManifestScaffold({ root: process.cwd(), frameworks: options.frameworks })
|
|
325
|
-
|
|
401
|
+
try {
|
|
402
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, { flag: 'wx' })
|
|
403
|
+
} catch (error) {
|
|
404
|
+
if (error.code === 'EEXIST') return reuseExistingManifest(manifestPath)
|
|
405
|
+
throw error
|
|
406
|
+
}
|
|
326
407
|
const targets = manifest.ciDiscovery.reviewTargets
|
|
327
408
|
console.log(sanitizeConsoleText([
|
|
328
409
|
`Created a data-only validation manifest: ${manifestPath}`,
|
|
@@ -347,27 +428,134 @@ function initializeManifest (options) {
|
|
|
347
428
|
function printPlan (manifest, options) {
|
|
348
429
|
const out = validateOutputPath(manifest, options.out)
|
|
349
430
|
const approvalManifest = getApprovalManifest(manifest, options.frameworks)
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
431
|
+
const localScenariosSelected = hasLocalScenarios(options.scenarios)
|
|
432
|
+
if (!localScenariosSelected ||
|
|
433
|
+
!approvalManifest.frameworks.some(framework => framework.status === 'runnable')) {
|
|
434
|
+
return writeStaticOnlyReport(approvalManifest, out, options)
|
|
435
|
+
}
|
|
436
|
+
ensureSafeDirectory(manifest.repository.root, out, 'validation output directory', { allowRootSymlink: true })
|
|
437
|
+
const publicationLock = acquireExecutionLock({ out, approvedPlanSha256: 'plan-publication' })
|
|
438
|
+
try {
|
|
439
|
+
const packageCheck = localScenariosSelected
|
|
440
|
+
? checkInstalledPackage()
|
|
441
|
+
: undefined
|
|
442
|
+
if (packageCheck?.ok === false) throw getInstalledPackageCheckError(packageCheck)
|
|
443
|
+
|
|
444
|
+
const projectSnapshot = getApprovalProjectSnapshot(approvalManifest, {
|
|
445
|
+
includeLocal: options.requestedScenario !== CI_WIRING,
|
|
446
|
+
})
|
|
447
|
+
const ciPreflightResults = options.scenarios.has(CI_WIRING)
|
|
448
|
+
? new Map(approvalManifest.frameworks.map(framework => [
|
|
449
|
+
framework.id,
|
|
450
|
+
runCiWiring({ manifest: approvalManifest, framework, projectFileSources: projectSnapshot.sources }),
|
|
451
|
+
]))
|
|
452
|
+
: new Map()
|
|
453
|
+
const { plan } = formatExecutionPlanArtifacts({
|
|
454
|
+
manifest: approvalManifest,
|
|
455
|
+
out,
|
|
456
|
+
selectedFrameworkIds: options.frameworks.size > 0
|
|
457
|
+
? approvalManifest.frameworks.map(framework => framework.id)
|
|
458
|
+
: [],
|
|
459
|
+
requestedScenario: options.requestedScenario,
|
|
460
|
+
keepTempFiles: options.keepTempFiles,
|
|
461
|
+
packageCheck,
|
|
462
|
+
ciPreflightResults,
|
|
463
|
+
expectedProjectFiles: projectSnapshot.projectFiles,
|
|
464
|
+
verbose: options.verbose,
|
|
465
|
+
})
|
|
466
|
+
console.log(sanitizeConsoleText([
|
|
467
|
+
'===== CUSTOMER APPROVAL PLAN =====',
|
|
468
|
+
plan,
|
|
469
|
+
'===== END CUSTOMER APPROVAL PLAN =====',
|
|
470
|
+
'',
|
|
471
|
+
`Saved execution plan: ${getExecutionPlanPath(out)}`,
|
|
472
|
+
'LIVE VALIDATION HAS NOT RUN.',
|
|
473
|
+
'Present the complete delimited plan and ask exactly: Approve executing exactly the plan above?',
|
|
474
|
+
].join('\n')))
|
|
475
|
+
} finally {
|
|
476
|
+
releaseExecutionLock(publicationLock)
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function writeStaticOnlyReport (manifest, out, options) {
|
|
481
|
+
const results = []
|
|
482
|
+
const localScenariosSelected = hasLocalScenarios(options.scenarios)
|
|
483
|
+
for (const framework of manifest.frameworks) {
|
|
484
|
+
if (localScenariosSelected) {
|
|
485
|
+
results.push(getFrameworkStatusResult(framework))
|
|
486
|
+
addNotReachedLocalResults(results, framework, options.scenarios, 'framework-not-runnable')
|
|
487
|
+
}
|
|
488
|
+
if (options.scenarios.has(CI_WIRING)) results.push(runCiWiring({ manifest, framework }))
|
|
489
|
+
}
|
|
490
|
+
const annotatedResults = annotateResults(results)
|
|
491
|
+
const executionStatus = getExecutionStatus(annotatedResults)
|
|
492
|
+
const validatorExitCode = getValidatorExitCode(annotatedResults, executionStatus)
|
|
493
|
+
ensureSafeDirectory(manifest.repository.root, out, 'validation output directory', { allowRootSymlink: true })
|
|
494
|
+
const publicationLock = acquireExecutionLock({ out, approvedPlanSha256: 'static-only-report' })
|
|
495
|
+
try {
|
|
496
|
+
writeReport({
|
|
497
|
+
manifest,
|
|
498
|
+
out,
|
|
499
|
+
results: annotatedResults,
|
|
500
|
+
runSummary: {
|
|
501
|
+
checkedScenarios: [...options.scenarios],
|
|
502
|
+
cleanup: { directoriesRemoved: 0, filesRemoved: 0, status: 'completed' },
|
|
503
|
+
executionStatus,
|
|
504
|
+
omittedScenarios: getSelectableScenarios().filter(scenario => !options.scenarios.has(scenario)),
|
|
505
|
+
requestedScenario: options.requestedScenario,
|
|
506
|
+
runCompleted: true,
|
|
507
|
+
selectedFrameworkIds: manifest.frameworks.map(framework => framework.id),
|
|
508
|
+
validationCoverage: getValidationCoverage(annotatedResults),
|
|
509
|
+
validatorExitCode,
|
|
510
|
+
},
|
|
511
|
+
})
|
|
512
|
+
} finally {
|
|
513
|
+
releaseExecutionLock(publicationLock)
|
|
514
|
+
}
|
|
515
|
+
const reason = localScenariosSelected
|
|
516
|
+
? 'No selected framework has an eligible local command.'
|
|
517
|
+
: 'The selected CI-only audit does not require local execution.'
|
|
518
|
+
console.log(sanitizeConsoleText(
|
|
519
|
+
`${reason} A final static-only report was written; no new approval artifact or live validation command was ` +
|
|
520
|
+
'created. Present the report and stop.'
|
|
521
|
+
))
|
|
522
|
+
process.exitCode = validatorExitCode
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function reuseExistingManifest (manifestPath) {
|
|
526
|
+
try {
|
|
527
|
+
const manifest = loadManifest(manifestPath)
|
|
528
|
+
if (fs.realpathSync(manifest.repository.root) !== fs.realpathSync(process.cwd())) {
|
|
529
|
+
throw new Error('repository.root does not identify the current physical repository')
|
|
530
|
+
}
|
|
531
|
+
} catch (error) {
|
|
532
|
+
const recovery = new Error(
|
|
533
|
+
`The existing validation manifest cannot be reused: ${error.message}. ` +
|
|
534
|
+
`Inspect and remove only ${manifestPath}, then rerun --init-manifest. The validator did not delete or replace it.`
|
|
535
|
+
)
|
|
536
|
+
recovery.validationExitCode = 2
|
|
537
|
+
throw recovery
|
|
538
|
+
}
|
|
539
|
+
|
|
360
540
|
console.log(sanitizeConsoleText([
|
|
361
|
-
|
|
362
|
-
plan,
|
|
363
|
-
'
|
|
364
|
-
'',
|
|
365
|
-
`Saved execution plan: ${getExecutionPlanPath(out)}`,
|
|
366
|
-
'LIVE VALIDATION HAS NOT RUN.',
|
|
367
|
-
'Present the complete delimited plan and ask exactly: Approve executing exactly the plan above?',
|
|
541
|
+
`Existing validation manifest is valid for this repository: ${manifestPath}`,
|
|
542
|
+
'Reuse it, refresh only its ciWiring evidence if needed, then run --validate-manifest and --print-plan.',
|
|
543
|
+
'The validator did not overwrite the manifest or delete existing approval and report artifacts.',
|
|
368
544
|
].join('\n')))
|
|
369
545
|
}
|
|
370
546
|
|
|
547
|
+
function hasLocalScenarios (scenarios) {
|
|
548
|
+
return [...scenarios].some(scenario => scenario !== CI_WIRING)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function getInstalledPackageCheckError (packageCheck) {
|
|
552
|
+
const error = new Error(
|
|
553
|
+
`INSTALLED DD-TRACE PACKAGE BLOCKER: ${packageCheck.diagnosis} ${packageCheck.recommendation}`
|
|
554
|
+
)
|
|
555
|
+
error.validationExitCode = 2
|
|
556
|
+
return error
|
|
557
|
+
}
|
|
558
|
+
|
|
371
559
|
/**
|
|
372
560
|
* Restores approved execution options from approval.json.
|
|
373
561
|
*
|
|
@@ -442,6 +630,9 @@ function addAdvancedNotReached (results, framework, scenarios, blocker) {
|
|
|
442
630
|
diagnosis: 'Not reached because Basic Reporting did not establish the direct reporting path.',
|
|
443
631
|
evidence: {
|
|
444
632
|
blockedBy: blocker?.scenario || 'basic-reporting',
|
|
633
|
+
...(blocker?.evidence?.blockerCategory
|
|
634
|
+
? { blockerCategory: blocker.evidence.blockerCategory }
|
|
635
|
+
: {}),
|
|
445
636
|
validationIncomplete: true,
|
|
446
637
|
},
|
|
447
638
|
artifacts: [],
|
|
@@ -456,9 +647,15 @@ function addAdvancedNotReached (results, framework, scenarios, blocker) {
|
|
|
456
647
|
* @param {object} framework framework entry
|
|
457
648
|
* @param {Set<string>} scenarios selected scenarios
|
|
458
649
|
* @param {string} reasonCode blocker id
|
|
650
|
+
* @param {string} [blockerCategoryOverride] blocker category supplied by a runtime result
|
|
459
651
|
* @returns {void}
|
|
460
652
|
*/
|
|
461
|
-
function addNotReachedLocalResults (results, framework, scenarios, reasonCode) {
|
|
653
|
+
function addNotReachedLocalResults (results, framework, scenarios, reasonCode, blockerCategoryOverride) {
|
|
654
|
+
const blockerCategory = blockerCategoryOverride || framework.blockerCategory || (
|
|
655
|
+
framework.status === 'requires_manual_setup'
|
|
656
|
+
? BLOCKER_CATEGORIES.PROJECT_SETUP_REQUIRED
|
|
657
|
+
: BLOCKER_CATEGORIES.VALIDATOR_LIMITATION
|
|
658
|
+
)
|
|
462
659
|
for (const scenario of scenarios) {
|
|
463
660
|
if (scenario === CI_WIRING) continue
|
|
464
661
|
results.push({
|
|
@@ -466,7 +663,7 @@ function addNotReachedLocalResults (results, framework, scenarios, reasonCode) {
|
|
|
466
663
|
scenario,
|
|
467
664
|
status: 'skip',
|
|
468
665
|
diagnosis: 'Not reached because this framework has no available direct-runner validation target.',
|
|
469
|
-
evidence: { reasonCode, validationIncomplete: true },
|
|
666
|
+
evidence: { blockerCategory, reasonCode, validationIncomplete: true },
|
|
470
667
|
artifacts: [],
|
|
471
668
|
})
|
|
472
669
|
}
|
|
@@ -534,7 +731,7 @@ function normalizeScenarioSelection (scenario) {
|
|
|
534
731
|
* @returns {string} normalized target
|
|
535
732
|
*/
|
|
536
733
|
function normalizeFrameworkTarget (target) {
|
|
537
|
-
const normalized = String(target).trim().
|
|
734
|
+
const normalized = String(target).trim().replace(/(?<!:):+$/, '')
|
|
538
735
|
if (!normalized) throw new Error('Framework target cannot be empty.')
|
|
539
736
|
return normalized
|
|
540
737
|
}
|
|
@@ -621,21 +818,31 @@ function logPhase (framework, phase, status) {
|
|
|
621
818
|
* @returns {object} result
|
|
622
819
|
*/
|
|
623
820
|
function getFrameworkStatusResult (framework) {
|
|
624
|
-
const
|
|
821
|
+
const blockerCategory = framework.blockerCategory || (
|
|
822
|
+
framework.status === 'requires_manual_setup'
|
|
823
|
+
? BLOCKER_CATEGORIES.PROJECT_SETUP_REQUIRED
|
|
824
|
+
: BLOCKER_CATEGORIES.VALIDATOR_LIMITATION
|
|
825
|
+
)
|
|
826
|
+
const domain = getBlockerDomain(blockerCategory)
|
|
625
827
|
return {
|
|
626
828
|
frameworkId: framework.id,
|
|
627
829
|
scenario: 'all',
|
|
628
830
|
status: 'skip',
|
|
629
831
|
diagnosis: framework.notes?.[0] || `Framework status is ${framework.status}.`,
|
|
630
832
|
evidence: {
|
|
833
|
+
blockerCategory,
|
|
834
|
+
domain,
|
|
631
835
|
frameworkStatus: framework.status,
|
|
632
836
|
validationIncomplete: true,
|
|
633
|
-
...(
|
|
837
|
+
...(blockerCategory === BLOCKER_CATEGORIES.PROJECT_SETUP_REQUIRED
|
|
634
838
|
? {
|
|
635
839
|
blockedByProjectSetup: true,
|
|
636
840
|
...(framework.notes?.[0] ? { recommendation: framework.notes[0] } : {}),
|
|
637
841
|
}
|
|
638
|
-
: {
|
|
842
|
+
: {
|
|
843
|
+
validatorAdapterUnavailable: true,
|
|
844
|
+
...(framework.notes?.[0] ? { recommendation: framework.notes[0] } : {}),
|
|
845
|
+
}),
|
|
639
846
|
},
|
|
640
847
|
artifacts: [],
|
|
641
848
|
}
|
|
@@ -654,7 +861,12 @@ function getUnavailableRunnerResult (framework, unavailable) {
|
|
|
654
861
|
scenario: 'all',
|
|
655
862
|
status: 'skip',
|
|
656
863
|
diagnosis: `The direct runner is unavailable: ${unavailable}. Complete normal project setup and retry.`,
|
|
657
|
-
evidence: {
|
|
864
|
+
evidence: {
|
|
865
|
+
blockerCategory: BLOCKER_CATEGORIES.PROJECT_SETUP_REQUIRED,
|
|
866
|
+
blockedByProjectSetup: true,
|
|
867
|
+
unavailableRunner: unavailable,
|
|
868
|
+
validationIncomplete: true,
|
|
869
|
+
},
|
|
658
870
|
artifacts: [],
|
|
659
871
|
}
|
|
660
872
|
}
|
|
@@ -668,12 +880,22 @@ function getUnavailableRunnerResult (framework, unavailable) {
|
|
|
668
880
|
* @returns {object} result
|
|
669
881
|
*/
|
|
670
882
|
function getStaticFailure (framework, blocker, reportPath) {
|
|
883
|
+
const blockerCategory = blocker.blockerCategory || BLOCKER_CATEGORIES.PROJECT_SETUP_REQUIRED
|
|
671
884
|
return {
|
|
672
885
|
frameworkId: framework.id,
|
|
673
886
|
scenario: 'all',
|
|
674
887
|
status: 'error',
|
|
675
888
|
diagnosis: blocker.reason,
|
|
676
|
-
evidence: {
|
|
889
|
+
evidence: {
|
|
890
|
+
blockerCategory,
|
|
891
|
+
domain: getBlockerDomain(blockerCategory),
|
|
892
|
+
recommendation: blocker.recommendation,
|
|
893
|
+
staticDiagnosis: reportPath,
|
|
894
|
+
validationIncomplete: true,
|
|
895
|
+
...(blockerCategory === BLOCKER_CATEGORIES.PROJECT_SETUP_REQUIRED
|
|
896
|
+
? { blockedByProjectSetup: true }
|
|
897
|
+
: {}),
|
|
898
|
+
},
|
|
677
899
|
artifacts: [reportPath],
|
|
678
900
|
}
|
|
679
901
|
}
|
|
@@ -684,15 +906,20 @@ function getStaticFailure (framework, blocker, reportPath) {
|
|
|
684
906
|
* @param {object} framework framework entry
|
|
685
907
|
* @param {string} diagnosis blocker diagnosis
|
|
686
908
|
* @param {string} reasonCode blocker id
|
|
909
|
+
* @param {string | undefined} blockerCategory blocker category
|
|
687
910
|
* @returns {object} result
|
|
688
911
|
*/
|
|
689
|
-
function getBasicNotReached (framework, diagnosis, reasonCode) {
|
|
912
|
+
function getBasicNotReached (framework, diagnosis, reasonCode, blockerCategory) {
|
|
690
913
|
return {
|
|
691
914
|
frameworkId: framework.id,
|
|
692
915
|
scenario: BASIC_REPORTING,
|
|
693
916
|
status: 'skip',
|
|
694
917
|
diagnosis: `Basic Reporting was not reached: ${diagnosis}`,
|
|
695
|
-
evidence: {
|
|
918
|
+
evidence: {
|
|
919
|
+
...(blockerCategory ? { blockerCategory } : {}),
|
|
920
|
+
reasonCode,
|
|
921
|
+
validationIncomplete: true,
|
|
922
|
+
},
|
|
696
923
|
artifacts: [],
|
|
697
924
|
}
|
|
698
925
|
}
|
|
@@ -725,12 +952,13 @@ function getCleanupFailure (error, cleanup) {
|
|
|
725
952
|
* @returns {object} result
|
|
726
953
|
*/
|
|
727
954
|
function getOrchestrationFailure (error) {
|
|
955
|
+
const blockerEvidence = getValidationBlockerEvidence(error)
|
|
728
956
|
return {
|
|
729
957
|
frameworkId: 'validator',
|
|
730
958
|
scenario: 'all',
|
|
731
|
-
status: 'error',
|
|
959
|
+
status: blockerEvidence ? 'blocked' : 'error',
|
|
732
960
|
diagnosis: error?.message || String(error),
|
|
733
|
-
evidence: { validationIncomplete: true, validationOrchestrationFailed: true },
|
|
961
|
+
evidence: blockerEvidence || { validationIncomplete: true, validationOrchestrationFailed: true },
|
|
734
962
|
artifacts: [],
|
|
735
963
|
}
|
|
736
964
|
}
|