Skip to content

Commit

Permalink
✨[RUM-3255] Report the bundle sizes differences as a PR comment (#2629)
Browse files Browse the repository at this point in the history
* first commit

* bumped chrome version

* added fetchwrapper

* Error fetch

* changed difference name

* chore : changed names

* Update Dockerfile

Co-authored-by: Bastien Caudan <[email protected]>

* nitpick / command instead of execSync / KB conversion / filename

* timeout

* fetch origin

* pr header

* prcommentheader

* throw error if more than 1 pr / nitpick and formatSize function

* format size update

* exit instead of throw error

* extracting retries as a const

* deleted getPR function

* revert fetch PR

* Deleted the buildQuery function and set packageName as a constant to use it directly in the query.

* extract query as a constant

* deleted name in create message

* revert query

* extract query as a constant

* call report bundle size from export bundle size. Deleted retries because we don't need to fetch local metrics anymore.

* update name

* update import

* export

* fixed compare

* create message update

* Divided script into 3 files : index.js / report-to-datadog / report-as-a-pr-comment.

* deleted old scripts

* update path import

* added main function in report to datadog, more precise name to report as a pr comment function

---------

Co-authored-by: Bastien Caudan <[email protected]>
  • Loading branch information
RomanGaignault and bcaudan authored Mar 11, 2024
1 parent 824dbce commit b3cc811
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 57 deletions.
6 changes: 3 additions & 3 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
variables:
CURRENT_STAGING: staging-11
APP: 'browser-sdk'
CURRENT_CI_IMAGE: 59
CURRENT_CI_IMAGE: 60
BUILD_STABLE_REGISTRY: '486234852809.dkr.ecr.us-east-1.amazonaws.com'
CI_IMAGE: '$BUILD_STABLE_REGISTRY/ci/$APP:$CURRENT_CI_IMAGE'
GIT_REPOSITORY: '[email protected]:DataDog/browser-sdk.git'
MAIN_BRANCH: 'main'
CHROME_PACKAGE_VERSION: 121.0.6167.85-1
CHROME_PACKAGE_VERSION: 122.0.6261.57-1

cache:
key:
Expand Down Expand Up @@ -140,7 +140,7 @@ build-and-lint:
- yarn build
- yarn lint
- node scripts/check-packages.js
- node scripts/export-bundles-sizes.js
- node scripts/report-bundle-size/index.js

build-bundle:
extends:
Expand Down
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,6 @@ RUN shasum -a 256 -c codecov.SHA256SUM
RUN chmod +x codecov
RUN mv codecov /usr/local/bin
RUN rm codecov.*

# Install authanywhere for pull request commenter token
RUN if [ $(uname -m) = x86_64 ]; then AAA="amd64"; else AAA="arm64"; fi; curl -OL "binaries.ddbuild.io/dd-source/authanywhere/LATEST/authanywhere-linux-${AAA}" && mv "authanywhere-linux-${AAA}" /bin/authanywhere && chmod +x /bin/authanywhere
54 changes: 0 additions & 54 deletions scripts/export-bundles-sizes.js

This file was deleted.

5 changes: 5 additions & 0 deletions scripts/lib/secrets.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ function getOrg2ApiKey() {
return getSecretKey('ci.browser-sdk.datadog_ci_api_key')
}

function getOrg2AppKey() {
return getSecretKey('ci.browser-sdk.datadog_ci_application_key')
}

function getTelemetryOrgApiKey(site) {
const normalizedSite = site.replaceAll('.', '-')
return getSecretKey(`ci.browser-sdk.source-maps.${normalizedSite}.ci_api_key`)
Expand Down Expand Up @@ -49,5 +53,6 @@ module.exports = {
getGithubAccessToken,
getNpmToken,
getOrg2ApiKey,
getOrg2AppKey,
getTelemetryOrgApiKey,
}
30 changes: 30 additions & 0 deletions scripts/report-bundle-size/index.js
Original file line number Diff line number Diff line change
@@ -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 })
}
}
164 changes: 164 additions & 0 deletions scripts/report-bundle-size/report-as-a-pr-comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
const { command } = require('../lib/command')
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

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 origin`.run()
const commandOutput = command`git merge-base origin/${baseBranch} HEAD`.run()
// SHA commit is truncated to 8 caracters 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 fetch(`https://api.github.com/repos/DataDog/browser-sdk/pulls?head=DataDog:${localBranch}`, {
method: 'GET',
headers: {
Authorization: `token ${GITHUB_TOKEN}`,
},
})
if (!response.ok) {
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`)
}
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 fetch(`https://api.datadoghq.com/api/v1/query?query=${query}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'DD-API-KEY': getOrg2ApiKey(),
'DD-APPLICATION-KEY': getOrg2AppKey(),
},
})
if (!response.ok) {
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`)
}
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 percentageChange = null

if (baseResult.size && localSize) {
percentageChange = (((localSize - baseResult.size) / baseResult.size) * 100).toFixed(2)
}
return {
name: baseResult.name,
percentageChange,
}
})
}

async function retrieveExistingCommentId(prNumber) {
const response = await fetch(`https://api.github.com/repos/DataDog/browser-sdk/issues/${prNumber}/comments`, {
method: 'GET',
headers: {
Authorization: `token ${GITHUB_TOKEN}`,
},
})
if (!response.ok) {
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`)
}
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',
}
const response = await fetch('https://pr-commenter.us1.ddbuild.io/internal/cit/pr-comment', {
method,
headers: {
Authorization: `Bearer ${PR_COMMENTER_AUTH_TOKEN}`,
},
body: JSON.stringify(payload),
})
if (!response.ok) {
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`)
}
}

function createMessage(difference, resultsBaseQuery, localBundleSizes) {
let message = '| 📦 Bundle Name| Base Size | Local Size | 𝚫% |\n| --- | --- | --- | --- |\n'
difference.forEach((diff, index) => {
const baseSize = formatSize(resultsBaseQuery[index].size)
const localSize = formatSize(localBundleSizes[diff.name])
const sign = diff.percentageChange > 0 ? '+' : ''
message += `| ${formatBundleName(diff.name)} | ${baseSize} | ${localSize} | ${sign}${diff.percentageChange}% |\n`
})

return message
}

function formatBundleName(bundleName) {
return bundleName
.split('_')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
}

function formatSize(bundleSize) {
return `${(bundleSize / 1024).toFixed(2)} kB`
}

module.exports = {
reportBundleSizesAsPrComment,
}
39 changes: 39 additions & 0 deletions scripts/report-bundle-size/report-to-datadog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const { fetch } = 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 reportBundleSizesToDatadog(bundleSizes) {
const logData = createLogData(bundleSizes, browserSdkVersion)
await sendLogToOrg2(logData)
}

function createLogData(bundleSizes, browserSdkVersion) {
return [
{
message: 'Browser SDK bundles sizes',
service: 'browser-sdk',
ddsource: 'browser-sdk',
env: 'ci',
bundle_sizes: bundleSizes,
version: browserSdkVersion,
commit: process.env.CI_COMMIT_SHORT_SHA,
branch: process.env.CI_COMMIT_REF_NAME,
},
]
}

async function sendLogToOrg2(bundleData = {}) {
await fetch(LOG_INTAKE_URL, {
method: 'POST',
headers: LOG_INTAKE_REQUEST_HEADERS,
body: JSON.stringify(bundleData),
})
}

module.exports = { reportBundleSizesToDatadog }

0 comments on commit b3cc811

Please sign in to comment.