Skip to content

Commit dc81b43

Browse files
committed
Extend selective testing with implicit consumers (#268256)
## Summary Some plugins use registries to inject functionality. Both our strategies for detecting affected modules cannot discover it, because they rely on static dependencies. We need a better way to handle it. While we discuss a general solution, this PR serves as a quick fix that validates potential implicit consumers on top of computed affected packages and extends "affected modules" if code changes match any of these patterns: ``` '**/plugins/**/public/embeddables/**/*.{ts,tsx}', '**/plugins/**/public/embeddable/**/*.{ts,tsx}', '**/plugins/**/public/react_embeddable/**/*.{ts,tsx}', '**/plugins/**/public/apps/embeddables/**/*.{ts,tsx}', '**/plugins/**/public/ui_actions/**/*.{ts,tsx}', '**/plugins/**/public/trigger_actions/**/*.{ts,tsx}', '**/plugins/**/public/**/actions/register*.{ts,tsx}', ``` Example: Changing `x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.tsx` should trigger Scout tests in `dashboard`, `lens`, `canvas` and `embeddable` plugins <img width="1954" height="279" alt="Screenshot 2026-05-07 at 19 10 42" src="https://github.com/user-attachments/assets/6c0b4163-481b-4f34-84b2-290ab3091d96" /> (cherry picked from commit 86477a0)
1 parent 6f776dc commit dc81b43

2 files changed

Lines changed: 97 additions & 5 deletions

File tree

.buildkite/scripts/steps/test/scout/resolve_selective_testing.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,17 @@
99

1010
import fs from 'node:fs';
1111
import path from 'node:path';
12+
import { ToolingLog } from '@kbn/tooling-log';
13+
import { expandWithImplicitConsumers } from './scout_implicit_consumers';
1214
import {
1315
getAffectedPackages,
1416
listChangedFiles,
1517
touchedCriticalFiles,
1618
CRITICAL_FILES_SCOUT,
1719
} from '#pipeline-utils';
1820

21+
const log = new ToolingLog({ level: 'info', writeTo: process.stderr });
22+
1923
const mergeBase = process.env.AFFECTED_MERGE_BASE;
2024
const outPath = process.env.AFFECTED_MODULES_FILE;
2125

@@ -34,11 +38,17 @@ if (!outPath) {
3438
const changedFiles = listChangedFiles({ mergeBase, commit: 'HEAD' });
3539

3640
// Write affected modules JSON (replaces the list_affected binary call).
37-
const affectedPackages = await getAffectedPackages(mergeBase, {
38-
strategy: 'git',
39-
includeDownstream: true,
40-
ignoreUncategorizedChanges: true,
41-
});
41+
// TEMP: overlay implicit runtime-registry consumers — see scout_implicit_consumers.ts.
42+
const affectedPackages = expandWithImplicitConsumers(
43+
await getAffectedPackages(mergeBase, {
44+
strategy: 'git',
45+
includeDownstream: true,
46+
ignoreUncategorizedChanges: true,
47+
}),
48+
changedFiles,
49+
log
50+
);
51+
4252
const resolvedOutPath = path.resolve(outPath);
4353
fs.mkdirSync(path.dirname(resolvedOutPath), { recursive: true });
4454
fs.writeFileSync(resolvedOutPath, JSON.stringify(Array.from(affectedPackages).sort(), null, 2));
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
/**
11+
* TEMPORARY overlay for Scout selective testing.
12+
*
13+
* The static @kbn/ dependency graph (kibana.jsonc + tsconfig kbn_references)
14+
* cannot model runtime registry coupling — e.g. ML registers actions into the
15+
* uiActions registry that Dashboard renders at run time, with no static import
16+
* edge between them. ML-only changes therefore do not mark Dashboard's Scout
17+
* tests as affected and registration races slip through.
18+
*
19+
* This overlay augments the affected-modules set with a small allowlist of
20+
* (patterns -> consumer @kbn/ IDs) entries when the corresponding publisher
21+
* files change.
22+
*/
23+
24+
import minimatch from 'minimatch';
25+
import type { ToolingLog } from '@kbn/tooling-log';
26+
27+
interface ImplicitConsumerRule {
28+
reason: string;
29+
patterns: readonly string[];
30+
consumers: readonly string[];
31+
}
32+
33+
const IMPLICIT_REGISTRY_CONSUMERS: readonly ImplicitConsumerRule[] = [
34+
{
35+
reason: 'Runtime registry coupling not captured by static @kbn/ references.',
36+
patterns: [
37+
'**/plugins/**/public/embeddables/**/*.{ts,tsx}',
38+
'**/plugins/**/public/embeddable/**/*.{ts,tsx}',
39+
'**/plugins/**/public/react_embeddable/**/*.{ts,tsx}',
40+
'**/plugins/**/public/apps/embeddables/**/*.{ts,tsx}',
41+
'**/plugins/**/public/ui_actions/**/*.{ts,tsx}',
42+
'**/plugins/**/public/trigger_actions/**/*.{ts,tsx}',
43+
'**/plugins/**/public/**/actions/register*.{ts,tsx}',
44+
],
45+
consumers: [
46+
'@kbn/dashboard-plugin',
47+
'@kbn/embeddable-plugin',
48+
'@kbn/canvas-plugin',
49+
'@kbn/lens-plugin',
50+
],
51+
},
52+
];
53+
54+
/**
55+
* Augment an affected-modules set with consumer @kbn/ IDs whose registries are
56+
* touched by `changedFiles`. Returns a new Set; never removes entries and
57+
* never disables selective testing.
58+
*/
59+
export function expandWithImplicitConsumers(
60+
affected: ReadonlySet<string>,
61+
changedFiles: readonly string[],
62+
log: ToolingLog
63+
): Set<string> {
64+
const expanded = new Set(affected);
65+
66+
for (const rule of IMPLICIT_REGISTRY_CONSUMERS) {
67+
const trigger = changedFiles.find((file) =>
68+
rule.patterns.some((pattern) => minimatch(file, pattern, { dot: true }))
69+
);
70+
if (!trigger) continue;
71+
72+
const added = rule.consumers.filter((id) => !expanded.has(id));
73+
if (added.length === 0) continue;
74+
75+
for (const id of added) expanded.add(id);
76+
log.info(
77+
`Implicit consumers added: ${added.join(', ')} (triggered by '${trigger}' — ${rule.reason})`
78+
);
79+
}
80+
81+
return expanded;
82+
}

0 commit comments

Comments
 (0)