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)