From 9601909430134fe38926a4b6c0a669966ab0a888 Mon Sep 17 00:00:00 2001 From: Anton Ganhammar Date: Sun, 20 Aug 2023 11:56:40 +0200 Subject: [PATCH 1/3] feat: add support for lcov Signed-off-by: Anton Ganhammar --- .../src/service/CoverageUtils.test.ts | 17 ++- .../src/service/CoverageUtils.ts | 5 +- .../lcov-jsoncoverage-files-1.json | 103 ++++++++++++++++++ .../lcov-jsoncoverage-files-2.json | 47 ++++++++ .../__fixtures__/lcov-sourcefiles-1.txt | 2 + .../__fixtures__/lcov-sourcefiles-2.txt | 2 + .../service/__fixtures__/lcov-testdata-1.info | 71 ++++++++++++ .../service/__fixtures__/lcov-testdata-2.info | 50 +++++++++ .../src/service/converter/index.ts | 1 + .../src/service/converter/lcov.test.ts | 53 +++++++++ .../src/service/converter/lcov.ts | 79 ++++++++++++++ .../src/service/router.ts | 6 +- .../src/service/standaloneServer.ts | 4 +- 13 files changed, 431 insertions(+), 9 deletions(-) create mode 100644 plugins/code-coverage-backend/src/service/__fixtures__/lcov-jsoncoverage-files-1.json create mode 100644 plugins/code-coverage-backend/src/service/__fixtures__/lcov-jsoncoverage-files-2.json create mode 100644 plugins/code-coverage-backend/src/service/__fixtures__/lcov-sourcefiles-1.txt create mode 100644 plugins/code-coverage-backend/src/service/__fixtures__/lcov-sourcefiles-2.txt create mode 100644 plugins/code-coverage-backend/src/service/__fixtures__/lcov-testdata-1.info create mode 100644 plugins/code-coverage-backend/src/service/__fixtures__/lcov-testdata-2.info create mode 100644 plugins/code-coverage-backend/src/service/converter/lcov.test.ts create mode 100644 plugins/code-coverage-backend/src/service/converter/lcov.ts diff --git a/plugins/code-coverage-backend/src/service/CoverageUtils.test.ts b/plugins/code-coverage-backend/src/service/CoverageUtils.test.ts index 44b1411cc4..23c7c6cba0 100644 --- a/plugins/code-coverage-backend/src/service/CoverageUtils.test.ts +++ b/plugins/code-coverage-backend/src/service/CoverageUtils.test.ts @@ -224,11 +224,11 @@ describe('CodeCoverageUtils', () => { err = error; } expect(err?.message).toEqual( - 'Content-Type header "application/json" not supported, expected "text/xml" possibly followed by a charset', + 'Content-Type header "application/json" not supported, expected "text/xml" or "text/plain" possibly followed by a charset', ); }); - it('parses the body', () => { + it('parses the xml body', () => { const data: Readable = utils.validateRequestBody({ headers: { 'content-type': 'text/xml', @@ -240,6 +240,19 @@ describe('CodeCoverageUtils', () => { expect(data.read()).toContain(' { + const data: Readable = utils.validateRequestBody({ + headers: { + 'content-type': 'text/plain', + }, + body: Readable.from( + 'TN:\nSF:/src/index.js\nFNF:0\nFNH:0\nLF:1\nLH:1\nBRF:0\nBRH:0\nend_of_record', + ), + } as Request); + + expect(data.read()).toMatch(/^TN:\nSF:\/src\/index.js/); + }); }); describe('processCoveragePayload', () => { diff --git a/plugins/code-coverage-backend/src/service/CoverageUtils.ts b/plugins/code-coverage-backend/src/service/CoverageUtils.ts index a4d3405868..bfe79e869f 100644 --- a/plugins/code-coverage-backend/src/service/CoverageUtils.ts +++ b/plugins/code-coverage-backend/src/service/CoverageUtils.ts @@ -163,9 +163,10 @@ export class CoverageUtils { const contentType = req.headers['content-type']; if (!contentType) { throw new InputError('Content-Type header missing'); - } else if (!contentType.match(/^text\/xml($|;)/)) { + // text/xml or text/plain is allowed + } else if (!contentType.match(/^text\/xml|plain($|;)/)) { throw new InputError( - `Content-Type header "${contentType}" not supported, expected "text/xml" possibly followed by a charset`, + `Content-Type header "${contentType}" not supported, expected "text/xml" or "text/plain" possibly followed by a charset`, ); } const body = req.body; diff --git a/plugins/code-coverage-backend/src/service/__fixtures__/lcov-jsoncoverage-files-1.json b/plugins/code-coverage-backend/src/service/__fixtures__/lcov-jsoncoverage-files-1.json new file mode 100644 index 0000000000..1d0ed702df --- /dev/null +++ b/plugins/code-coverage-backend/src/service/__fixtures__/lcov-jsoncoverage-files-1.json @@ -0,0 +1,103 @@ +[ + { + "filename": "src/index.js", + "lineHits": { + "1": 1, + "2": 1, + "3": 1, + "4": 5, + "5": 5, + "6": 1, + "7": 5, + "8": 0, + "9": 5, + "10": 3, + "11": 5, + "12": 1, + "13": 5, + "14": 5 + }, + "branchHits": { + "3": { + "covered": 1, + "missed": 0, + "available": 1 + }, + "5": { + "covered": 1, + "missed": 0, + "available": 1 + }, + "7": { + "covered": 0, + "missed": 1, + "available": 1 + }, + "9": { + "covered": 1, + "missed": 0, + "available": 1 + }, + "11": { + "covered": 1, + "missed": 0, + "available": 1 + } + } + }, + { + "filename": "src/math.js", + "lineHits": { + "1": 1, + "2": 1, + "3": 1, + "4": 1, + "5": 1, + "6": 0, + "7": 0, + "8": 1, + "9": 1, + "10": 3, + "11": 0, + "12": 3, + "13": 1, + "14": 3, + "15": 1, + "16": 1, + "17": 1, + "18": 1 + }, + "branchHits": { + "2": { + "covered": 1, + "missed": 0, + "available": 1 + }, + "9": { + "covered": 1, + "missed": 0, + "available": 1 + }, + "10": { + "covered": 0, + "missed": 1, + "available": 1 + }, + "12": { + "covered": 2, + "missed": 0, + "available": 2 + }, + "14": { + "covered": 2, + "missed": 0, + "available": 2 + }, + "17": { + "covered": 1, + "missed": 0, + "available": 1 + } + } + } +] diff --git a/plugins/code-coverage-backend/src/service/__fixtures__/lcov-jsoncoverage-files-2.json b/plugins/code-coverage-backend/src/service/__fixtures__/lcov-jsoncoverage-files-2.json new file mode 100644 index 0000000000..7de4e7dcf7 --- /dev/null +++ b/plugins/code-coverage-backend/src/service/__fixtures__/lcov-jsoncoverage-files-2.json @@ -0,0 +1,47 @@ +[ + { + "filename": "src/Calc/Main.cs", + "lineHits": { + "5": 4, + "6": 4, + "7": 1, + "8": 0, + "9": 2, + "10": 1, + "11": 4 + }, + "branchHits": { + "5": { + "covered": 5, + "missed": 1, + "available": 6 + } + } + }, + { + "filename": "src/Calc/Math.cs", + "lineHits": { + "5": 1, + "6": 0, + "7": 2, + "8": 2, + "9": 0, + "11": 3, + "12": 1, + "14": 1, + "15": 1 + }, + "branchHits": { + "8": { + "covered": 1, + "missed": 1, + "available": 2 + }, + "11": { + "covered": 2, + "missed": 0, + "available": 2 + } + } + } +] diff --git a/plugins/code-coverage-backend/src/service/__fixtures__/lcov-sourcefiles-1.txt b/plugins/code-coverage-backend/src/service/__fixtures__/lcov-sourcefiles-1.txt new file mode 100644 index 0000000000..4f4bf2c118 --- /dev/null +++ b/plugins/code-coverage-backend/src/service/__fixtures__/lcov-sourcefiles-1.txt @@ -0,0 +1,2 @@ +src/index.js +src/math.js diff --git a/plugins/code-coverage-backend/src/service/__fixtures__/lcov-sourcefiles-2.txt b/plugins/code-coverage-backend/src/service/__fixtures__/lcov-sourcefiles-2.txt new file mode 100644 index 0000000000..3aa3c36039 --- /dev/null +++ b/plugins/code-coverage-backend/src/service/__fixtures__/lcov-sourcefiles-2.txt @@ -0,0 +1,2 @@ +src/Calc/Main.cs +src/Calc/Math.cs diff --git a/plugins/code-coverage-backend/src/service/__fixtures__/lcov-testdata-1.info b/plugins/code-coverage-backend/src/service/__fixtures__/lcov-testdata-1.info new file mode 100644 index 0000000000..0bf69f6bc7 --- /dev/null +++ b/plugins/code-coverage-backend/src/service/__fixtures__/lcov-testdata-1.info @@ -0,0 +1,71 @@ +TN: +SF:src/index.js +FN:3,__vite_ssr_exports__.default +FNF:1 +FNH:1 +FNDA:5,__vite_ssr_exports__.default +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,5 +DA:5,5 +DA:6,1 +DA:7,5 +DA:8,0 +DA:9,5 +DA:10,3 +DA:11,5 +DA:12,1 +DA:13,5 +DA:14,5 +LF:14 +LH:13 +BRDA:3,0,0,5 +BRDA:5,1,0,1 +BRDA:7,2,0,0 +BRDA:9,3,0,3 +BRDA:11,4,0,1 +BRF:5 +BRH:4 +end_of_record +TN: +SF:src/math.js +FN:2,addition +FN:5,subtraction +FN:9,division +FNF:3 +FNH:2 +FNDA:1,addition +FNDA:0,subtraction +FNDA:3,division +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,0 +DA:7,0 +DA:8,1 +DA:9,1 +DA:10,3 +DA:11,0 +DA:12,3 +DA:13,1 +DA:14,3 +DA:15,1 +DA:16,1 +DA:17,1 +DA:18,1 +LF:18 +LH:15 +BRDA:2,0,0,1 +BRDA:9,1,0,3 +BRDA:10,2,0,0 +BRDA:12,3,0,2 +BRDA:12,4,0,1 +BRDA:14,5,0,2 +BRDA:14,6,0,1 +BRDA:17,7,0,1 +BRF:8 +BRH:7 +end_of_record diff --git a/plugins/code-coverage-backend/src/service/__fixtures__/lcov-testdata-2.info b/plugins/code-coverage-backend/src/service/__fixtures__/lcov-testdata-2.info new file mode 100644 index 0000000000..688bffb521 --- /dev/null +++ b/plugins/code-coverage-backend/src/service/__fixtures__/lcov-testdata-2.info @@ -0,0 +1,50 @@ +SF:/Some/Path/src/Calc/Main.cs +FN:4,System.Int32 Calc.Main::Calc(System.Int32,System.Int32,System.String) +FNDA:4,System.Int32 Calc.Main::Calc(System.Int32,System.Int32,System.String) +DA:5,4 +DA:6,4 +DA:7,1 +DA:8,0 +DA:9,2 +DA:10,1 +DA:11,4 +BRDA:5,15,0,3 +BRDA:5,28,0,3 +BRDA:5,15,1,1 +BRDA:5,28,1,0 +BRDA:5,41,1,2 +BRDA:5,41,0,1 +LF:7 +LH:6 +BRF:6 +BRH:5 +FNF:1 +FNH:1 +end_of_record +SF:/Some/Path/src/Calc/Math.cs +FN:4,System.Int32 Calc.Math::Addition(System.Int32,System.Int32) +FNDA:1,System.Int32 Calc.Math::Addition(System.Int32,System.Int32) +DA:5,1 +FN:5,System.Int32 Calc.Math::Subtraction(System.Int32,System.Int32) +FNDA:0,System.Int32 Calc.Math::Subtraction(System.Int32,System.Int32) +DA:6,0 +FN:6,System.Int32 Calc.Math::Division(System.Int32,System.Int32) +FNDA:2,System.Int32 Calc.Math::Division(System.Int32,System.Int32) +DA:7,2 +DA:8,2 +DA:9,0 +DA:11,3 +DA:12,1 +DA:14,1 +DA:15,1 +BRDA:8,7,0,0 +BRDA:8,7,1,2 +BRDA:11,22,0,1 +BRDA:11,22,1,1 +LF:9 +LH:7 +BRF:4 +BRH:3 +FNF:3 +FNH:2 +end_of_record diff --git a/plugins/code-coverage-backend/src/service/converter/index.ts b/plugins/code-coverage-backend/src/service/converter/index.ts index a85f320486..7e6d95ead5 100644 --- a/plugins/code-coverage-backend/src/service/converter/index.ts +++ b/plugins/code-coverage-backend/src/service/converter/index.ts @@ -17,3 +17,4 @@ export * from './Converter'; export { Cobertura } from './cobertura'; export { Jacoco } from './jacoco'; +export { Lcov } from './lcov'; diff --git a/plugins/code-coverage-backend/src/service/converter/lcov.test.ts b/plugins/code-coverage-backend/src/service/converter/lcov.test.ts new file mode 100644 index 0000000000..e31261ba77 --- /dev/null +++ b/plugins/code-coverage-backend/src/service/converter/lcov.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import fs from 'fs'; +import path from 'path'; +import { Lcov } from './lcov'; +import { getVoidLogger } from '@backstage/backend-common'; + +describe('convert lcov', () => { + const converter = new Lcov(getVoidLogger()); + [1, 2].forEach(idx => { + const lcov = fs + .readFileSync( + path.resolve(`${__dirname}/../__fixtures__/lcov-testdata-${idx}.info`), + ) + .toString(); + const expected = JSON.parse( + fs + .readFileSync( + path.resolve( + `${__dirname}/../__fixtures__/lcov-jsoncoverage-files-${idx}.json`, + ), + ) + .toString(), + ); + const scmFiles = fs + .readFileSync( + path.resolve( + `${__dirname}/../__fixtures__/lcov-sourcefiles-${idx}.txt`, + ), + ) + .toString() + .split('\n'); + + it(`should convert the lcov report with index ${idx} to json`, async () => { + const files = converter.convert(lcov, scmFiles); + + expect(files).toEqual(expected); + }); + }); +}); diff --git a/plugins/code-coverage-backend/src/service/converter/lcov.ts b/plugins/code-coverage-backend/src/service/converter/lcov.ts new file mode 100644 index 0000000000..319377764a --- /dev/null +++ b/plugins/code-coverage-backend/src/service/converter/lcov.ts @@ -0,0 +1,79 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { FileEntry } from '../types'; +import { Logger } from 'winston'; +import { Converter } from './Converter'; + +export class Lcov implements Converter { + constructor(readonly logger: Logger) { + this.logger = logger; + } + + convert(raw: string, scmFiles: string[]): FileEntry[] { + const lines = raw.split(/\r?\n/); + const jscov: Array = []; + let currentFile: FileEntry | null = null; + + lines.forEach(line => { + // If the line starts with SF, it's a new file + if (/^SF:/.test(line)) { + const coverageFilename = line.replace(/^SF:/, ''); + const filename = scmFiles + .map(f => f.trimEnd()) + .find(f => coverageFilename.endsWith(f)); + + this.logger.debug(`matched ${coverageFilename} to ${currentFile}`); + + if (filename) { + currentFile = { + filename, + lineHits: {}, + branchHits: {}, + }; + } + + // If the line starts with DA, it's a line hit + } else if (currentFile && /^DA:/.test(line)) { + const [, lineNumber, hits] = line.split(/[,:]+/); + currentFile.lineHits[Number(lineNumber)] = Number(hits); + + // If the line starts with BRDA, it's a branch + } else if (currentFile && /^BRDA:/.test(line)) { + const [, lineNumber, , , hits] = line.split(/[,:]+/); + const isHit = Number(hits) > 0; + + if (!currentFile.branchHits[Number(lineNumber)]) { + currentFile.branchHits[Number(lineNumber)] = { + covered: isHit ? 1 : 0, + available: 1, + missed: isHit ? 0 : 1, + }; + } else { + currentFile.branchHits[Number(lineNumber)].available += 1; + currentFile.branchHits[Number(lineNumber)].covered += isHit ? 1 : 0; + currentFile.branchHits[Number(lineNumber)].missed += isHit ? 0 : 1; + } + + // If the line starts with end_of_record, it's the end of current file + } else if (currentFile && /^end_of_record/.test(line)) { + jscov.push(currentFile); + currentFile = null; + } + }); + + return jscov; + } +} diff --git a/plugins/code-coverage-backend/src/service/router.ts b/plugins/code-coverage-backend/src/service/router.ts index 4d4fb779b5..c4f98bd2fb 100644 --- a/plugins/code-coverage-backend/src/service/router.ts +++ b/plugins/code-coverage-backend/src/service/router.ts @@ -30,9 +30,7 @@ import { Config } from '@backstage/config'; import { ScmIntegrations } from '@backstage/integration'; import { CodeCoverageDatabase } from './CodeCoverageDatabase'; import { aggregateCoverage, CoverageUtils } from './CoverageUtils'; -import { Cobertura } from './converter/cobertura'; -import { Jacoco } from './converter/jacoco'; -import { Converter } from './converter'; +import { Converter, Jacoco, Cobertura, Lcov } from './converter'; import { getEntitySourceLocation } from '@backstage/catalog-model'; /** @@ -182,6 +180,8 @@ export const makeRouter = async ( converter = new Jacoco(logger); } else if (coverageType === 'cobertura') { converter = new Cobertura(logger); + } else if (coverageType === 'lcov') { + converter = new Lcov(logger); } else { throw new InputError(`Unsupported coverage type '${coverageType}`); } diff --git a/plugins/code-coverage-backend/src/service/standaloneServer.ts b/plugins/code-coverage-backend/src/service/standaloneServer.ts index 41493660fb..23eaf981c1 100644 --- a/plugins/code-coverage-backend/src/service/standaloneServer.ts +++ b/plugins/code-coverage-backend/src/service/standaloneServer.ts @@ -16,8 +16,8 @@ import { createServiceBuilder, + HostDiscovery, loadBackendConfig, - SingleHostDiscovery, UrlReaders, useHotMemoize, } from '@backstage/backend-common'; @@ -71,7 +71,7 @@ export async function startStandaloneServer( const router = await createRouter({ database: { getClient: async () => db }, config, - discovery: SingleHostDiscovery.fromConfig(config), + discovery: HostDiscovery.fromConfig(config), urlReader: UrlReaders.default({ logger, config }), logger, catalogApi, From 499e76c9349d303811b348b1f266302715a04685 Mon Sep 17 00:00:00 2001 From: Anton Ganhammar Date: Sun, 20 Aug 2023 21:29:04 +0200 Subject: [PATCH 2/3] fix: refactor converter and add comments Signed-off-by: Anton Ganhammar --- .../src/service/converter/lcov.ts | 127 ++++++++++++------ 1 file changed, 87 insertions(+), 40 deletions(-) diff --git a/plugins/code-coverage-backend/src/service/converter/lcov.ts b/plugins/code-coverage-backend/src/service/converter/lcov.ts index 319377764a..beaa574075 100644 --- a/plugins/code-coverage-backend/src/service/converter/lcov.ts +++ b/plugins/code-coverage-backend/src/service/converter/lcov.ts @@ -22,58 +22,105 @@ export class Lcov implements Converter { this.logger = logger; } + /** + * convert lcov into shared json coverage format + * + * @param raw - raw lcov report + * @param scmFiles - list of files that are committed to SCM + */ convert(raw: string, scmFiles: string[]): FileEntry[] { const lines = raw.split(/\r?\n/); const jscov: Array = []; let currentFile: FileEntry | null = null; lines.forEach(line => { - // If the line starts with SF, it's a new file - if (/^SF:/.test(line)) { - const coverageFilename = line.replace(/^SF:/, ''); - const filename = scmFiles - .map(f => f.trimEnd()) - .find(f => coverageFilename.endsWith(f)); - - this.logger.debug(`matched ${coverageFilename} to ${currentFile}`); - - if (filename) { - currentFile = { - filename, - lineHits: {}, - branchHits: {}, - }; - } + const [section, value] = line.split(':'); + switch (section) { + // If the line starts with SF, it's a new file + case 'SF': + currentFile = this.processNewFile(value, scmFiles); + break; // If the line starts with DA, it's a line hit - } else if (currentFile && /^DA:/.test(line)) { - const [, lineNumber, hits] = line.split(/[,:]+/); - currentFile.lineHits[Number(lineNumber)] = Number(hits); - - // If the line starts with BRDA, it's a branch - } else if (currentFile && /^BRDA:/.test(line)) { - const [, lineNumber, , , hits] = line.split(/[,:]+/); - const isHit = Number(hits) > 0; - - if (!currentFile.branchHits[Number(lineNumber)]) { - currentFile.branchHits[Number(lineNumber)] = { - covered: isHit ? 1 : 0, - available: 1, - missed: isHit ? 0 : 1, - }; - } else { - currentFile.branchHits[Number(lineNumber)].available += 1; - currentFile.branchHits[Number(lineNumber)].covered += isHit ? 1 : 0; - currentFile.branchHits[Number(lineNumber)].missed += isHit ? 0 : 1; - } - + case 'DA': + this.processLineHit(currentFile, value); + break; + // If the line starts with BRDA, it's a branch line + case 'BRDA': + this.processBranchHit(currentFile, value); + break; // If the line starts with end_of_record, it's the end of current file - } else if (currentFile && /^end_of_record/.test(line)) { - jscov.push(currentFile); - currentFile = null; + case 'end_of_record': + if (currentFile) { + jscov.push(currentFile); + currentFile = null; + } + break; + default: + break; } }); return jscov; } + + /** + * Parses a new file entry + * + * @param file - file name from coverage report + * @param scmFiles - list of files that are committed to SCM + */ + private processNewFile(file: string, scmFiles: string[]): FileEntry | null { + const filename = scmFiles.map(f => f.trimEnd()).find(f => file.endsWith(f)); + + this.logger.debug(`matched ${file} to ${filename}`); + + if (filename) { + return { + filename, + lineHits: {}, + branchHits: {}, + }; + } + + return null; + } + + /** + * Parses line coverage information + * + * @param currentFile - current file entry + * @param value - line coverage information + */ + private processLineHit(currentFile: FileEntry | null, value: string): void { + if (!currentFile) return; + + const [lineNumber, hits] = value.split(','); + currentFile.lineHits[Number(lineNumber)] = Number(hits); + } + + /** + * Parses branch coverage information + * + * @param currentFile - current file entry + * @param value - branch coverage information + */ + private processBranchHit(currentFile: FileEntry | null, value: string): void { + if (!currentFile) return; + + const [lineNumber, , , hits] = value.split(','); + const lineNumberNum = Number(lineNumber); + const isHit = Number(hits) > 0; + const branch = currentFile.branchHits[lineNumberNum] || { + covered: 0, + available: 0, + missed: 0, + }; + + branch.available++; + branch.covered += isHit ? 1 : 0; + branch.missed += isHit ? 0 : 1; + + currentFile.branchHits[lineNumberNum] = branch; + } } From 3f1367b956c605453a660a9463ce595d18ad3b77 Mon Sep 17 00:00:00 2001 From: Anton Ganhammar Date: Sun, 20 Aug 2023 21:36:59 +0200 Subject: [PATCH 3/3] feat: add changeset and update readme Signed-off-by: Anton Ganhammar --- .changeset/dirty-years-travel.md | 5 +++++ plugins/code-coverage-backend/README.md | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 .changeset/dirty-years-travel.md diff --git a/.changeset/dirty-years-travel.md b/.changeset/dirty-years-travel.md new file mode 100644 index 0000000000..e6e5b8a608 --- /dev/null +++ b/.changeset/dirty-years-travel.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-code-coverage-backend': patch +--- + +Added support for LCOV coverage reports diff --git a/plugins/code-coverage-backend/README.md b/plugins/code-coverage-backend/README.md index 802e538125..a60b95ff25 100644 --- a/plugins/code-coverage-backend/README.md +++ b/plugins/code-coverage-backend/README.md @@ -125,6 +125,24 @@ Example: } ``` +### Adding a LCOV report + +POST a LCOV INFO file to `/report` + +Example: + +```json +// curl -X POST -H "Content-Type:text/plain" -d @coverage.info "localhost:7007/api/code-coverage/report?entity=component:default/entity-name&coverageType=lcov" +{ + "links": [ + { + "href": "http://localhost:7007/api/code-coverage/report?entity=component:default/entity-name", + "rel": "coverage" + } + ] +} +``` + ### Reading json coverage GET `/report`