This guide explains how to integrate ESLint results into your Bitbucket Pull Requests using Bitbucket Pipelines and creating a Report with Annotations.
Generate ESLint Report as JSON
First, you'll need to run ESLint and output the results in JSON format. This file will later be used to create a report and annotations.
Add -f
and -o
args to your eslint
command. E.g:
eslint . --ext .ts -f json -o eslint-report.json
Post ESLint Report and Annotations to Bitbucket
To display ESLint findings directly in your Pull Requests, you'll use Bitbucket's Report API and Annotations API.
- Read the ESLint JSON report.
- Generate a report with the total number of errors and warnings.
- Post inline annotations based on ESLint messages.
const fs = require('fs')
const path = require('path')
const util = require('util')
// Must be unique per report on a commit
const REPORT_ID = 'com.yorcompany.reports.eslint'
const BB_USER = 'YOUR_USER'
const BB_REPO = 'YOUR_REPO'
const BB_URL = 'https://api.bitbucket.org/2.0'
// This is available by default in the pipeline.
const COMMIT = process.env.BITBUCKET_COMMIT
// For this to be availble you need to create an access token with read access to the repo
// and set it an environment variable in the pipeline.
const TOKEN = process.env.BITBUCKET_TOKEN
// Map ESLint severities to Bitbucket report severities
const severities = {
0: 'LOW',
1: 'MEDIUM',
2: 'HIGH'
}
if (!fs.existsSync(path.join(process.cwd(), 'eslint-report.json'))) {
console.log('eslint-report.json file does not exist.');
process.exit();
}
doIt().catch(e => {
console.error('Error posting insights:', e.response ? e.response.data : e.message)
})
async function doIt() {
const data = await util.promisify(fs.readFile)(path.join(process.cwd(), 'eslint-report.json'), 'utf8')
.catch(err => {
console.error('Error reading eslint-report.json:', err)
throw err
})
const eslintOutput = JSON.parse(data)
let totalErrorCount = 0
let totalWarningCount = 0
const annotations = []
let i = 1
eslintOutput.forEach(file => {
totalErrorCount += file.errorCount
totalWarningCount += file.warningCount
const relativePath = path.relative(process.cwd(), file.filePath)
file.messages.forEach(message => {
annotations.push({
external_id: `${REPORT_ID}.${COMMIT}.${i++}`,
path: relativePath,
annotation_type: 'CODE_SMELL',
summary: message.message,
line: message.line,
severity: severities[message.severity]
})
})
})
const report = {
title: 'ESLint report',
details: 'ESLint report',
report_type: 'TEST',
logoUrl: 'https://eslint.org/img/logo.svg',
data: [
{
title: 'Error Count',
type: 'NUMBER',
value: totalErrorCount
},
{
title: 'Warning Count',
type: 'NUMBER',
value: totalWarningCount
}
]
}
await postReport(report)
await postAnnotations(annotations)
}
async function postReport(report) {
// https://developer.atlassian.com/cloud/bitbucket/rest/api-group-reports/#api-repositories-workspace-repo-slug-commit-commit-reports-reportid-put
const reportUrl = `${BB_URL}/repositories/${BB_USER}/${BB_REPO}/commit/${COMMIT}/reports/${REPORT_ID}`
const response = await fetch(reportUrl, {
method: 'PUT',
body: JSON.stringify(report),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': `Bearer ${TOKEN}`
}
})
if (!response.ok) {
console.error(await response.text())
throw new Error(`Error posting report: ${response.statusText}`)
}
console.log('Report posted successfully!')
console.log(await response.json())
}
async function postAnnotations(annotations) {
if (annotations.length > 0) {
// https://developer.atlassian.com/cloud/bitbucket/rest/api-group-reports/#api-repositories-workspace-repo-slug-commit-commit-reports-reportid-annotations-post
const annotationsUrl = `${BB_URL}/repositories/${BB_USER}/${BB_REPO}/commit/${COMMIT}/reports/${REPORT_ID}/annotations`
const response = await fetch(annotationsUrl, {
method: 'POST',
body: JSON.stringify(annotations),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': `Bearer ${TOKEN}`
}
})
if (!response.ok) {
console.error(await response.text())
throw new Error(`Error posting annotations: ${response.statusText}`)
}
console.log('Annotations posted successfully!')
console.log(await response.json())
}
}
Configure Bitbucket Pipeline
To automate this process as part of your CI/CD workflow, you can set up a Bitbucket pipeline to run ESLint, generate the JSON report, and post the results. Below is a sample bitbucket-pipelines.yml
file to get you started:
image: node:18.13.0
pipelines:
default:
- step:
name: ESLint
caches:
- node
script:
- npm install
- npx eslint . --ext .ts -f json -o eslint-report.json # Run ESLint and save the report
after-script:
- node post-eslint-results.js # Post results to Bitbucket
artifacts:
- eslint-report.json
Note
Report is posted to Bitbucket in after-script
because subsequent script
s will not be called if eslint returns non 0 exit code (if ESLint has errors).
Top comments (0)