From 088d3946f1b19aa561be84635f1bcba82ca52336 Mon Sep 17 00:00:00 2001 From: RomanGaignault <158156364+RomanGaignault@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:58:07 +0200 Subject: [PATCH] =?UTF-8?q?Revert=20"=E2=9C=A8[RUM-3798]=20Report=20the=20?= =?UTF-8?q?cpu=20impact=20as=20a=20pr=20comment=20(#2702)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 1ea85f4c73aa4f1bc65533ebf82b6010bd611555. --- .gitlab-ci.yml | 12 +- scripts/deploy/deploy.js | 16 +- scripts/deploy/lib/deployment-utils.js | 5 - scripts/lib/git-utils.js | 38 +--- .../bundle-size/compute-bundle-size.js | 29 --- .../compute-cpu-performance.js | 64 ------- .../performance/fetch-performance-metrics.js | 43 ----- scripts/performance/index.js | 12 -- scripts/performance/report-as-a-pr-comment.js | 170 ---------------- scripts/report-bundle-size/index.js | 30 +++ .../report-as-a-pr-comment.js | 181 ++++++++++++++++++ .../report-to-datadog.js | 8 +- 12 files changed, 221 insertions(+), 387 deletions(-) delete mode 100644 scripts/performance/bundle-size/compute-bundle-size.js delete mode 100644 scripts/performance/cpu-performance/compute-cpu-performance.js delete mode 100644 scripts/performance/fetch-performance-metrics.js delete mode 100644 scripts/performance/index.js delete mode 100644 scripts/performance/report-as-a-pr-comment.js create mode 100644 scripts/report-bundle-size/index.js create mode 100644 scripts/report-bundle-size/report-as-a-pr-comment.js rename scripts/{performance => report-bundle-size}/report-to-datadog.js (90%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 02de78306b..b70b0a9e92 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -140,17 +140,7 @@ build-and-lint: - yarn build - yarn lint - node scripts/check-packages.js - -test-performance: - extends: - - .base-configuration - - .test-allowed-branches - interruptible: true - script: - - yarn - - yarn build:bundle - - node ./scripts/deploy/deploy.js staging pull-request pull-request - - node scripts/performance/index.js + - node scripts/report-bundle-size/index.js build-bundle: extends: diff --git a/scripts/deploy/deploy.js b/scripts/deploy/deploy.js index 5cc43f4d72..0c96d738dc 100644 --- a/scripts/deploy/deploy.js +++ b/scripts/deploy/deploy.js @@ -1,15 +1,12 @@ 'use strict' const { printLog, runMain } = require('../lib/execution-utils') -const { fetchPR, LOCAL_BRANCH } = require('../lib/git-utils') const { command } = require('../lib/command') - const { buildRootUploadPath, buildDatacenterUploadPath, buildBundleFolder, buildBundleFileName, - buildPullRequestUploadPath, packages, } = require('./lib/deployment-utils') @@ -30,23 +27,20 @@ const AWS_CONFIG = { /** * Deploy SDK files to CDN * Usage: - * node deploy.js staging|prod staging|canary|pull-request|vXXX root,pull-request,us1,eu1,... + * node deploy.js staging|prod staging|canary|vXXX root,us1,eu1,... */ const env = process.argv[2] const version = process.argv[3] const uploadPathTypes = process.argv[4].split(',') -runMain(async () => { +runMain(() => { const awsConfig = AWS_CONFIG[env] let cloudfrontPathsToInvalidate = [] for (const { packageName } of packages) { const bundleFolder = buildBundleFolder(packageName) for (const uploadPathType of uploadPathTypes) { let uploadPath - if (uploadPathType === 'pull-request') { - const PR_NUMBER = (await fetchPR(LOCAL_BRANCH)).number - uploadPath = buildPullRequestUploadPath(packageName, PR_NUMBER) - } else if (uploadPathType === 'root') { + if (uploadPathType === 'root') { uploadPath = buildRootUploadPath(packageName, version) } else { uploadPath = buildDatacenterUploadPath(uploadPathType, packageName, version) @@ -64,9 +58,7 @@ function uploadToS3(awsConfig, bundlePath, uploadPath) { const accessToS3 = generateEnvironmentForRole(awsConfig.accountId, 'build-stable-browser-agent-artifacts-s3-write') const browserCache = - version === 'staging' || version === 'canary' || version === 'pull-request' - ? 15 * ONE_MINUTE_IN_SECOND - : 4 * ONE_HOUR_IN_SECOND + version === 'staging' || version === 'canary' ? 15 * ONE_MINUTE_IN_SECOND : 4 * ONE_HOUR_IN_SECOND const cacheControl = `max-age=${browserCache}, s-maxage=60` printLog(`Upload ${bundlePath} to s3://${awsConfig.bucketName}/${uploadPath}`) diff --git a/scripts/deploy/lib/deployment-utils.js b/scripts/deploy/lib/deployment-utils.js index c26acca3f5..5306158b17 100644 --- a/scripts/deploy/lib/deployment-utils.js +++ b/scripts/deploy/lib/deployment-utils.js @@ -14,10 +14,6 @@ const buildDatacenterUploadPath = (datacenter, packageName, version, extension = // ex: datadog-rum.js const buildBundleFileName = (packageName, extension = 'js') => `datadog-${packageName}.${extension}` -// ex: pull-request/2781/datadog-rum.js -function buildPullRequestUploadPath(packageName, version, extension = 'js') { - return `pull-request/${version}/datadog-${packageName}.${extension}` -} // ex: packages/rum/bundle const buildBundleFolder = (packageName) => `packages/${packageName}/bundle` @@ -27,5 +23,4 @@ module.exports = { buildDatacenterUploadPath, buildBundleFileName, buildBundleFolder, - buildPullRequestUploadPath, } diff --git a/scripts/lib/git-utils.js b/scripts/lib/git-utils.js index b4133f3120..0c9090ccd6 100644 --- a/scripts/lib/git-utils.js +++ b/scripts/lib/git-utils.js @@ -2,38 +2,7 @@ const os = require('os') const fs = require('fs') const { command } = require('../lib/command') -const { getGithubDeployKey, getGithubAccessToken } = require('./secrets') -const { fetchHandlingError } = require('./execution-utils') - -const GITHUB_TOKEN = getGithubAccessToken() - -async function fetchPR(localBranch) { - const response = await fetchHandlingError( - `https://api.github.com/repos/DataDog/browser-sdk/pulls?head=DataDog:${localBranch}`, - { - method: 'GET', - headers: { - Authorization: `token ${GITHUB_TOKEN}`, - }, - } - ) - const pr = response.body ? await response.json() : null - if (pr && pr.length > 1) { - throw new Error('Multiple pull requests found for the branch') - } - return pr ? pr[0] : null -} - -function getLastCommonCommit(baseBranch) { - try { - command`git fetch --depth=100 origin ${baseBranch}`.run() - const commandOutput = command`git merge-base origin/${baseBranch} HEAD`.run() - // SHA commit is truncated to 8 characters as bundle sizes commit are exported in short format to logs for convenience and readability. - return commandOutput.trim().substring(0, 8) - } catch (error) { - throw new Error('Failed to get last common commit', { cause: error }) - } -} +const { getGithubDeployKey } = require('./secrets') function initGitConfig(repository) { const homedir = os.homedir() @@ -52,9 +21,4 @@ function initGitConfig(repository) { module.exports = { initGitConfig, - fetchPR, - getLastCommonCommit, - BASE_BRANCH: process.env.MAIN_BRANCH, - LOCAL_BRANCH: process.env.CI_COMMIT_REF_NAME, - GITHUB_TOKEN, } diff --git a/scripts/performance/bundle-size/compute-bundle-size.js b/scripts/performance/bundle-size/compute-bundle-size.js deleted file mode 100644 index 2cc1c722a8..0000000000 --- a/scripts/performance/bundle-size/compute-bundle-size.js +++ /dev/null @@ -1,29 +0,0 @@ -const path = require('path') -const fs = require('fs') - -const rumPath = path.join(__dirname, '../../../packages/rum/bundle/datadog-rum.js') -const logsPath = path.join(__dirname, '../../../packages/logs/bundle/datadog-logs.js') -const rumSlimPath = path.join(__dirname, '../../../packages/rum-slim/bundle/datadog-rum-slim.js') -const workerPath = path.join(__dirname, '../../../packages/worker/bundle/worker.js') - -function getBundleSize(pathBundle) { - try { - const file = fs.statSync(pathBundle) - return file.size - } catch (error) { - throw new Error('Failed to get bundle size', { cause: error }) - } -} - -function calculateBundleSizes() { - return { - rum: getBundleSize(rumPath), - logs: getBundleSize(logsPath), - rum_slim: getBundleSize(rumSlimPath), - worker: getBundleSize(workerPath), - } -} - -module.exports = { - calculateBundleSizes, -} diff --git a/scripts/performance/cpu-performance/compute-cpu-performance.js b/scripts/performance/cpu-performance/compute-cpu-performance.js deleted file mode 100644 index b613375343..0000000000 --- a/scripts/performance/cpu-performance/compute-cpu-performance.js +++ /dev/null @@ -1,64 +0,0 @@ -const { fetchHandlingError } = require('../../lib/execution-utils') -const { getOrg2ApiKey, getOrg2AppKey } = require('../../lib/secrets') -const { timeout } = require('../../lib/execution-utils') -const { fetchPR, LOCAL_BRANCH } = require('../../lib/git-utils') -const { LOCAL_COMMIT_SHA } = require('../report-as-a-pr-comment') -const API_KEY = getOrg2ApiKey() -const APP_KEY = getOrg2AppKey() -const TIMEOUT_IN_MS = 10000 -const TEST_PUBLIC_ID = 'vcg-7rk-5av' -const RETRIES_NUMBER = 6 - -async function computeCpuPerformance() { - const prNumber = (await fetchPR(LOCAL_BRANCH)).number - const resultId = await triggerSyntheticsTest(prNumber, LOCAL_COMMIT_SHA) - await waitForSyntheticsTestToFinish(resultId, RETRIES_NUMBER) -} - -async function triggerSyntheticsTest(prNumber, commitId) { - const body = { - tests: [ - { - public_id: `${TEST_PUBLIC_ID}`, - startUrl: `https://datadoghq.dev/browser-sdk-test-playground/performance/?prNumber=${prNumber}&commitId=${commitId}`, - }, - ], - } - const url = 'https://api.datadoghq.com/api/v1/synthetics/tests/trigger/ci' - const response = await fetchHandlingError(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'DD-API-KEY': API_KEY, - 'DD-APPLICATION-KEY': APP_KEY, - }, - body: JSON.stringify(body), - }) - const data = await response.json() - return data.results[0].result_id -} - -async function waitForSyntheticsTestToFinish(resultId, RETRIES_NUMBER) { - const url = `https://api.datadoghq.com/api/v1/synthetics/tests/${TEST_PUBLIC_ID}/results/${resultId}` - for (let i = 0; i < RETRIES_NUMBER; i++) { - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'DD-API-KEY': API_KEY, - 'DD-APPLICATION-KEY': APP_KEY, - }, - }) - const data = await response.json() - if (data.length !== 0 && data.status === 0) { - await timeout(TIMEOUT_IN_MS) // Wait for logs ingestion - return - } - await timeout(TIMEOUT_IN_MS) - } - throw new Error('Synthetics test did not finish within the specified number of retries') -} - -module.exports = { - computeCpuPerformance, -} diff --git a/scripts/performance/fetch-performance-metrics.js b/scripts/performance/fetch-performance-metrics.js deleted file mode 100644 index 8a59b5d902..0000000000 --- a/scripts/performance/fetch-performance-metrics.js +++ /dev/null @@ -1,43 +0,0 @@ -const { getOrg2ApiKey, getOrg2AppKey } = require('../lib/secrets') -const { fetchHandlingError } = require('../lib/execution-utils') -const ONE_DAY_IN_SECOND = 24 * 60 * 60 - -function fetchPerformanceMetrics(type, names, commitId) { - return Promise.all(names.map((name) => fetchMetric(type, name, commitId))) -} - -async function fetchMetric(type, name, commitId) { - const now = Math.floor(Date.now() / 1000) - const date = now - 30 * ONE_DAY_IN_SECOND - let query = '' - - if (type === 'bundle') { - query = `avg:bundle_sizes.${name}{commit:${commitId}}&from=${date}&to=${now}` - } else if (type === 'cpu') { - query = `avg:cpu.sdk.${name}.performance.average{commitid:${commitId}}&from=${date}&to=${now}` - } - - const response = await fetchHandlingError(`https://api.datadoghq.com/api/v1/query?query=${query}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'DD-API-KEY': getOrg2ApiKey(), - 'DD-APPLICATION-KEY': getOrg2AppKey(), - }, - }) - const data = await response.json() - if (data.series && data.series.length > 0 && data.series[0].pointlist && data.series[0].pointlist.length > 0) { - return { - name, - value: data.series[0].pointlist[0][1], - } - } - return { - name, - value: null, - } -} - -module.exports = { - fetchPerformanceMetrics, -} diff --git a/scripts/performance/index.js b/scripts/performance/index.js deleted file mode 100644 index 8ff5d103b3..0000000000 --- a/scripts/performance/index.js +++ /dev/null @@ -1,12 +0,0 @@ -const { runMain } = require('../lib/execution-utils') -const { reportAsPrComment } = require('./report-as-a-pr-comment') -const { reportToDatadog } = require('./report-to-datadog') -const { calculateBundleSizes } = require('./bundle-size/compute-bundle-size') -const { computeCpuPerformance } = require('./cpu-performance/compute-cpu-performance') - -runMain(async () => { - const localBundleSizes = calculateBundleSizes() - await computeCpuPerformance() - await reportToDatadog(localBundleSizes) - await reportAsPrComment(localBundleSizes) -}) diff --git a/scripts/performance/report-as-a-pr-comment.js b/scripts/performance/report-as-a-pr-comment.js deleted file mode 100644 index 1f66d7e4fe..0000000000 --- a/scripts/performance/report-as-a-pr-comment.js +++ /dev/null @@ -1,170 +0,0 @@ -const { command } = require('../lib/command') -const { fetchHandlingError } = require('../lib/execution-utils') -const { LOCAL_BRANCH, BASE_BRANCH, GITHUB_TOKEN, getLastCommonCommit, fetchPR } = require('../lib/git-utils') -const { fetchPerformanceMetrics } = require('./fetch-performance-metrics') -const PR_COMMENT_HEADER = 'Bundles Sizes Evolution' -const PR_COMMENTER_AUTH_TOKEN = command`authanywhere`.run().split(' ')[2].trim() -// The value is set to 5% as it's around 10 times the average value for small PRs. -const SIZE_INCREASE_THRESHOLD = 5 -const LOCAL_COMMIT_SHA = process.env.CI_COMMIT_SHORT_SHA -const ACTION_NAMES = [ - 'adderror', - 'addaction', - 'logmessage', - 'startview', - 'startstopsessionreplayrecording', - 'addtiming', - 'addglobalcontext', -] - -async function reportAsPrComment(localBundleSizes) { - const lastCommonCommit = getLastCommonCommit(BASE_BRANCH, LOCAL_BRANCH) - const pr = await fetchPR(LOCAL_BRANCH) - if (!pr) { - console.log('No pull requests found for the branch') - return - } - const packageNames = Object.keys(localBundleSizes) - const baseBundleSizes = await fetchPerformanceMetrics('bundle', packageNames, lastCommonCommit) - const cpuBasePerformance = await fetchPerformanceMetrics('cpu', ACTION_NAMES, lastCommonCommit) - const cpuLocalPerformance = await fetchPerformanceMetrics('cpu', ACTION_NAMES, LOCAL_COMMIT_SHA) - const differenceBundle = compare(baseBundleSizes, localBundleSizes) - const differenceCpu = compare(cpuBasePerformance, cpuLocalPerformance) - const commentId = await retrieveExistingCommentId(pr.number) - const message = createMessage( - differenceBundle, - differenceCpu, - baseBundleSizes, - localBundleSizes, - cpuBasePerformance, - cpuLocalPerformance - ) - await updateOrAddComment(message, pr.number, commentId) -} - -function compare(baseResults, localResults) { - return baseResults.map((baseResult) => { - let localResult = null - - if (Array.isArray(localResults)) { - const localResultObj = localResults.find((result) => result.name === baseResult.name) - localResult = localResultObj ? localResultObj.value : null - } else { - localResult = localResults[baseResult.name] - } - - let change = null - let percentageChange = null - - if (baseResult.value && localResult) { - change = localResult - baseResult.value - percentageChange = ((change / baseResult.value) * 100).toFixed(2) - } else if (localResult) { - change = localResult - percentageChange = 'N/A' - } - - return { - name: baseResult.name, - change, - percentageChange, - } - }) -} - -async function retrieveExistingCommentId(prNumber) { - const response = await fetchHandlingError( - `https://api.github.com/repos/DataDog/browser-sdk/issues/${prNumber}/comments`, - { - method: 'GET', - headers: { - Authorization: `token ${GITHUB_TOKEN}`, - }, - } - ) - const comments = await response.json() - const targetComment = comments.find((comment) => comment.body.startsWith(`## ${PR_COMMENT_HEADER}`)) - if (targetComment !== undefined) { - return targetComment.id - } -} -async function updateOrAddComment(message, prNumber, commentId) { - const method = commentId ? 'PATCH' : 'POST' - const payload = { - pr_url: `https://github.com/DataDog/browser-sdk/pull/${prNumber}`, - message, - header: PR_COMMENT_HEADER, - org: 'DataDog', - repo: 'browser-sdk', - } - await fetchHandlingError('https://pr-commenter.us1.ddbuild.io/internal/cit/pr-comment', { - method, - headers: { - Authorization: `Bearer ${PR_COMMENTER_AUTH_TOKEN}`, - }, - body: JSON.stringify(payload), - }) -} - -function createMessage( - differenceBundle, - differenceCpu, - baseBundleSizes, - localBundleSizes, - cpuBasePerformance, - cpuLocalPerformance -) { - let message = - '| šŸ“¦ Bundle Name| Base Size | Local Size | šš« | šš«% | Status |\n| --- | --- | --- | --- | --- | :---: |\n' - let highIncreaseDetected = false - differenceBundle.forEach((diff, index) => { - const baseSize = formatSize(baseBundleSizes[index].value) - const localSize = formatSize(localBundleSizes[diff.name]) - const diffSize = formatSize(diff.change) - const sign = diff.percentageChange > 0 ? '+' : '' - let status = 'āœ…' - if (diff.percentageChange > SIZE_INCREASE_THRESHOLD) { - status = 'āš ļø' - highIncreaseDetected = true - } - message += `| ${formatBundleName(diff.name)} | ${baseSize} | ${localSize} | ${diffSize} | ${sign}${diff.percentageChange}% | ${status} |\n` - }) - - if (highIncreaseDetected) { - message += `\nāš ļø The increase is particularly high and exceeds ${SIZE_INCREASE_THRESHOLD}%. Please check the changes.` - } - message += '\n\n
\nšŸš€ CPU Performance\n\n\n' - message += - '| Action Name | Base Average Cpu Time (ms) | Local Average Cpu Time (ms) | šš« |\n| --- | --- | --- | --- |\n' - cpuBasePerformance.forEach((cpuActionPerformance, index) => { - const localCpuPerf = cpuLocalPerformance[index] - const diffCpuPerf = differenceCpu[index] - const baseCpuTaskValue = cpuActionPerformance.value !== null ? cpuActionPerformance.value.toFixed(3) : 'N/A' - const localCpuTaskValue = localCpuPerf.value !== null ? localCpuPerf.value.toFixed(3) : 'N/A' - const diffCpuTaskValue = diffCpuPerf.change !== null ? diffCpuPerf.change.toFixed(3) : 'N/A' - message += `| ${cpuActionPerformance.name} | ${baseCpuTaskValue} | ${localCpuTaskValue} | ${diffCpuTaskValue} |\n` - }) - message += '\n
\n' - - return message -} - -function formatBundleName(bundleName) { - return bundleName - .split('_') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' ') -} - -function formatSize(bytes) { - if (bytes < 1024) { - return `${bytes} B` - } - - return `${(bytes / 1024).toFixed(2)} KiB` -} - -module.exports = { - LOCAL_COMMIT_SHA, - reportAsPrComment, -} diff --git a/scripts/report-bundle-size/index.js b/scripts/report-bundle-size/index.js new file mode 100644 index 0000000000..dab2160943 --- /dev/null +++ b/scripts/report-bundle-size/index.js @@ -0,0 +1,30 @@ +const path = require('path') +const fs = require('fs') +const { runMain } = require('../lib/execution-utils') +const { reportBundleSizesAsPrComment } = require('./report-as-a-pr-comment') +const { reportBundleSizesToDatadog } = require('./report-to-datadog') + +const rumPath = path.join(__dirname, '../../packages/rum/bundle/datadog-rum.js') +const logsPath = path.join(__dirname, '../../packages/logs/bundle/datadog-logs.js') +const rumSlimPath = path.join(__dirname, '../../packages/rum-slim/bundle/datadog-rum-slim.js') +const workerPath = path.join(__dirname, '../../packages/worker/bundle/worker.js') + +runMain(async () => { + const bundleSizes = { + rum: getBundleSize(rumPath), + logs: getBundleSize(logsPath), + rum_slim: getBundleSize(rumSlimPath), + worker: getBundleSize(workerPath), + } + await reportBundleSizesToDatadog(bundleSizes) + await reportBundleSizesAsPrComment(bundleSizes) +}) + +function getBundleSize(pathBundle) { + try { + const file = fs.statSync(pathBundle) + return file.size + } catch (error) { + throw new Error('Failed to get bundle size', { cause: error }) + } +} diff --git a/scripts/report-bundle-size/report-as-a-pr-comment.js b/scripts/report-bundle-size/report-as-a-pr-comment.js new file mode 100644 index 0000000000..062f0a4968 --- /dev/null +++ b/scripts/report-bundle-size/report-as-a-pr-comment.js @@ -0,0 +1,181 @@ +const { command } = require('../lib/command') +const { fetchHandlingError } = require('../lib/execution-utils') +const { getOrg2ApiKey, getGithubAccessToken, getOrg2AppKey } = require('../lib/secrets') + +const PR_COMMENT_HEADER = 'Bundles Sizes Evolution' +const BASE_BRANCH = process.env.MAIN_BRANCH +const LOCAL_BRANCH = process.env.CI_COMMIT_REF_NAME +const PR_COMMENTER_AUTH_TOKEN = command`authanywhere`.run().split(' ')[2].trim() +const GITHUB_TOKEN = getGithubAccessToken() +const ONE_DAY_IN_SECOND = 24 * 60 * 60 +// The value is set to 5% as it's around 10 times the average value for small PRs. +const SIZE_INCREASE_THRESHOLD = 5 + +async function reportBundleSizesAsPrComment(localBundleSizes) { + const lastCommonCommit = getLastCommonCommit(BASE_BRANCH, LOCAL_BRANCH) + const pr = await fetchPR(LOCAL_BRANCH) + if (!pr) { + console.log('No pull requests found for the branch') + return + } + const packageNames = Object.keys(localBundleSizes) + const mainBranchBundleSizes = await fetchAllPackagesBaseBundleSize(packageNames, lastCommonCommit) + const difference = compare(mainBranchBundleSizes, localBundleSizes) + const commentId = await retrieveExistingCommentId(pr.number) + await updateOrAddComment(difference, mainBranchBundleSizes, localBundleSizes, pr.number, commentId) +} + +function getLastCommonCommit(baseBranch) { + try { + command`git fetch --depth=100 origin ${baseBranch}`.run() + const commandOutput = command`git merge-base origin/${baseBranch} HEAD`.run() + // SHA commit is truncated to 8 characters as bundle sizes commit are exported in short format to logs for convenience and readability. + return commandOutput.trim().substring(0, 8) + } catch (error) { + throw new Error('Failed to get last common commit', { cause: error }) + } +} + +async function fetchPR(localBranch) { + const response = await fetchHandlingError( + `https://api.github.com/repos/DataDog/browser-sdk/pulls?head=DataDog:${localBranch}`, + { + method: 'GET', + headers: { + Authorization: `token ${GITHUB_TOKEN}`, + }, + } + ) + const pr = response.body ? await response.json() : null + if (pr && pr.length > 1) { + throw new Error('Multiple pull requests found for the branch') + } + return pr ? pr[0] : null +} + +function fetchAllPackagesBaseBundleSize(packageNames, commitSha) { + return Promise.all(packageNames.map((packageName) => fetchBundleSizesMetric(packageName, commitSha))) +} + +async function fetchBundleSizesMetric(packageName, commitSha) { + const now = Math.floor(Date.now() / 1000) + const date = now - 30 * ONE_DAY_IN_SECOND + const query = `avg:bundle_sizes.${packageName}{commit:${commitSha}}&from=${date}&to=${now}` + + const response = await fetchHandlingError(`https://api.datadoghq.com/api/v1/query?query=${query}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'DD-API-KEY': getOrg2ApiKey(), + 'DD-APPLICATION-KEY': getOrg2AppKey(), + }, + }) + const data = await response.json() + if (data.series && data.series.length > 0 && data.series[0].pointlist && data.series[0].pointlist.length > 0) { + return { + name: packageName, + size: data.series[0].pointlist[0][1], + } + } + return { + name: packageName, + size: null, + } +} + +function compare(resultsBaseQuery, localBundleSizes) { + return resultsBaseQuery.map((baseResult) => { + const localSize = localBundleSizes[baseResult.name] + let change = null + let percentageChange = null + + if (baseResult.size && localSize) { + change = localSize - baseResult.size + percentageChange = ((change / baseResult.size) * 100).toFixed(2) + } + + return { + name: baseResult.name, + change, + percentageChange, + } + }) +} + +async function retrieveExistingCommentId(prNumber) { + const response = await fetchHandlingError( + `https://api.github.com/repos/DataDog/browser-sdk/issues/${prNumber}/comments`, + { + method: 'GET', + headers: { + Authorization: `token ${GITHUB_TOKEN}`, + }, + } + ) + const comments = await response.json() + const targetComment = comments.find((comment) => comment.body.startsWith(`## ${PR_COMMENT_HEADER}`)) + if (targetComment !== undefined) { + return targetComment.id + } +} +async function updateOrAddComment(difference, resultsBaseQuery, localBundleSizes, prNumber, commentId) { + const message = createMessage(difference, resultsBaseQuery, localBundleSizes) + const method = commentId ? 'PATCH' : 'POST' + const payload = { + pr_url: `https://github.com/DataDog/browser-sdk/pull/${prNumber}`, + message, + header: PR_COMMENT_HEADER, + org: 'DataDog', + repo: 'browser-sdk', + } + await fetchHandlingError('https://pr-commenter.us1.ddbuild.io/internal/cit/pr-comment', { + method, + headers: { + Authorization: `Bearer ${PR_COMMENTER_AUTH_TOKEN}`, + }, + body: JSON.stringify(payload), + }) +} + +function createMessage(difference, resultsBaseQuery, localBundleSizes) { + let message = + '| šŸ“¦ Bundle Name| Base Size | Local Size | šš« | šš«% | Status |\n| --- | --- | --- | --- | --- | :---: |\n' + let highIncreaseDetected = false + difference.forEach((diff, index) => { + const baseSize = formatSize(resultsBaseQuery[index].size) + const localSize = formatSize(localBundleSizes[diff.name]) + const diffSize = formatSize(diff.change) + const sign = diff.percentageChange > 0 ? '+' : '' + let status = 'āœ…' + if (diff.percentageChange > SIZE_INCREASE_THRESHOLD) { + status = 'āš ļø' + highIncreaseDetected = true + } + message += `| ${formatBundleName(diff.name)} | ${baseSize} | ${localSize} | ${diffSize} | ${sign}${diff.percentageChange}% | ${status} |\n` + }) + + if (highIncreaseDetected) { + message += `\nāš ļø The increase is particularly high and exceeds ${SIZE_INCREASE_THRESHOLD}%. Please check the changes.` + } + + return message +} + +function formatBundleName(bundleName) { + return bundleName + .split('_') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' ') +} + +function formatSize(bytes) { + if (bytes < 1024) { + return `${bytes} B` + } + + return `${(bytes / 1024).toFixed(2)} KiB` +} + +module.exports = { + reportBundleSizesAsPrComment, +} diff --git a/scripts/performance/report-to-datadog.js b/scripts/report-bundle-size/report-to-datadog.js similarity index 90% rename from scripts/performance/report-to-datadog.js rename to scripts/report-bundle-size/report-to-datadog.js index 9c99dc3363..0e83b24e12 100644 --- a/scripts/performance/report-to-datadog.js +++ b/scripts/report-bundle-size/report-to-datadog.js @@ -1,13 +1,14 @@ const { fetchHandlingError } = require('../lib/execution-utils') const { getOrg2ApiKey } = require('../lib/secrets') const { browserSdkVersion } = require('../lib/browser-sdk-version') + const LOG_INTAKE_URL = 'https://http-intake.logs.datadoghq.com/api/v2/logs' const LOG_INTAKE_REQUEST_HEADERS = { 'DD-API-KEY': getOrg2ApiKey(), 'Content-Type': 'application/json', } -async function reportToDatadog(bundleSizes) { +async function reportBundleSizesToDatadog(bundleSizes) { const logData = createLogData(bundleSizes, browserSdkVersion) await sendLogToOrg2(logData) } @@ -26,6 +27,7 @@ function createLogData(bundleSizes, browserSdkVersion) { }, ] } + async function sendLogToOrg2(bundleData = {}) { await fetchHandlingError(LOG_INTAKE_URL, { method: 'POST', @@ -34,6 +36,4 @@ async function sendLogToOrg2(bundleData = {}) { }) } -module.exports = { - reportToDatadog, -} +module.exports = { reportBundleSizesToDatadog }