Skip to content

Commit de836ed

Browse files
authored
Run telemetry automated checks conditionally, use octokit (#240799)
## Summary Closes #240390 This PR brings 2 important enhancements: * Use octokit to fetch telemetry JSON schemas from GH (at the moment we were performing anonymous requests and we have already been rate-limited). * Only run "PR automated checks" task if the pull request has modified the telemetry schemas.
1 parent 7234ca2 commit de836ed

File tree

6 files changed

+148
-430
lines changed

6 files changed

+148
-430
lines changed

.buildkite/scripts/steps/checks/telemetry.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ echo --- Check Telemetry Schema
99
if is_pr && ! is_auto_commit_disabled; then
1010
node scripts/telemetry_check --baseline "${GITHUB_PR_MERGE_BASE:-}" --fix
1111
check_for_changed_files "node scripts/telemetry_check" true
12-
else
12+
elif is_pr; then
1313
node scripts/telemetry_check --baseline "${GITHUB_PR_MERGE_BASE:-}"
14+
else
15+
# assume on-merge pipeline
16+
node scripts/telemetry_check --baseline "${BUILDKITE_BRANCH:-main}"
1417
fi

src/platform/packages/private/kbn-telemetry-tools/src/cli/run_telemetry_check.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
import type { ListrTask } from 'listr2';
1110
import { Listr } from 'listr2';
1211
import chalk from 'chalk';
1312
import { createFailError } from '@kbn/dev-cli-errors';
1413
import { run } from '@kbn/dev-cli-runner';
1514

16-
import { prAutomatedChecks } from '../tools/tasks/pr_automated_checks';
15+
import { validateSchemaChanges } from '../tools/tasks/validate_schema_changes';
1716
import type { TaskContext } from '../tools/tasks';
1817
import {
1918
createTaskContext,
@@ -119,15 +118,13 @@ export function runTelemetryCheck() {
119118
title: 'Updating telemetry mapping files',
120119
task: (context, task) => task.newListr(writeToFileTask(context), { exitOnError: true }),
121120
},
122-
...(baselineSha && false // temporarily disable (see https://github.com/elastic/kibana/issues/240390)
123-
? [
124-
{
125-
title: 'Automated PR review checks',
126-
task: (context, task) =>
127-
task.newListr(prAutomatedChecks(context), { exitOnError: true }),
128-
} as ListrTask<TaskContext>,
129-
]
130-
: []),
121+
{
122+
title: 'Validating changes in telemetry schemas',
123+
task: (context, task) =>
124+
task.newListr(validateSchemaChanges(context), { exitOnError: true }),
125+
// only run if on a PR branch
126+
enabled: (_) => Boolean(baselineSha),
127+
},
131128
],
132129
{
133130
renderer: process.env.CI ? 'verbose' : ('default' as any),
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
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+
import { promisify } from 'util';
11+
import { exec } from 'child_process';
12+
import { Octokit } from '@octokit/rest';
13+
import type { TelemetrySchemaObject } from '../../schema_ftr_validations/schema_to_config_schema';
14+
15+
const execAsync = promisify(exec);
16+
let octokit: null | Octokit;
17+
let changedFilesCache: null | string[];
18+
19+
function getOctokit() {
20+
if (!process.env.GITHUB_TOKEN) {
21+
throw new Error('Missing environment variable: GITHUB_TOKEN');
22+
}
23+
octokit =
24+
octokit ??
25+
new Octokit({
26+
auth: process.env.GITHUB_TOKEN,
27+
});
28+
29+
return octokit;
30+
}
31+
32+
async function fetchPrChangedFiles(
33+
owner = process.env.GITHUB_PR_BASE_OWNER,
34+
repo = process.env.GITHUB_PR_BASE_REPO,
35+
prNumber: undefined | string | number = process.env.GITHUB_PR_NUMBER
36+
): Promise<string[]> {
37+
if (!owner || !repo || !prNumber) {
38+
throw Error(
39+
"Couldn't retrieve Github PR info from environment variables in order to retrieve PR changes"
40+
);
41+
}
42+
43+
const github = getOctokit();
44+
const files = await github.paginate(github.pulls.listFiles, {
45+
owner,
46+
repo,
47+
pull_number: typeof prNumber === 'number' ? prNumber : parseInt(prNumber, 10),
48+
per_page: 100,
49+
});
50+
51+
return files.map(({ filename }) => filename);
52+
}
53+
54+
async function getPrChangedFiles(): Promise<string[]> {
55+
changedFilesCache = changedFilesCache || (await fetchPrChangedFiles());
56+
return changedFilesCache;
57+
}
58+
59+
async function getUncommitedChanges(): Promise<string[]> {
60+
const { stdout } = await execAsync(
61+
`git status --porcelain -- . ':!:config/node.options' ':!config/kibana.yml'`,
62+
{ maxBuffer: 1024 * 1024 * 128 }
63+
);
64+
65+
return stdout
66+
.split('\n')
67+
.map((line) =>
68+
line
69+
.replace('?? ', '')
70+
.replace('A ', '')
71+
.replace('M ', '')
72+
.replace('D ', '')
73+
.replace('R ', '')
74+
.trim()
75+
);
76+
}
77+
78+
export async function isTelemetrySchemaModified({ path }: { path: string }): Promise<boolean> {
79+
const modifiedFiles = await getUncommitedChanges();
80+
if (modifiedFiles.includes(path)) {
81+
return true;
82+
}
83+
84+
const prChanges = await getPrChangedFiles();
85+
return prChanges.length >= 3000 || prChanges.includes(path);
86+
}
87+
88+
export async function fetchTelemetrySchemaAtRevision({
89+
path,
90+
ref,
91+
}: {
92+
path: string;
93+
ref: string;
94+
}): Promise<TelemetrySchemaObject> {
95+
const github = getOctokit();
96+
const res = await github.rest.repos.getContent({
97+
mediaType: {
98+
format: 'application/vnd.github.VERSION.raw',
99+
},
100+
owner: 'elastic',
101+
repo: 'kibana',
102+
path,
103+
ref,
104+
});
105+
106+
if (!Array.isArray(res.data) && res.data.type === 'file') {
107+
return JSON.parse(Buffer.from(res.data.content!, 'base64').toString()) as TelemetrySchemaObject;
108+
} else {
109+
throw new Error(`Error retrieving contents of ${path}, unexpected response data.`);
110+
}
111+
}

0 commit comments

Comments
 (0)