diff --git a/CHANGELOG.md b/CHANGELOG.md index a5f484c25..c95986f01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Unreleased +- **fix** improve parsing for custom benchmarks (#323) + # [v1.20.5](https://github.com/benchmark-action/github-action-benchmark/releases/tag/v1.20.5) - 02 Sep 2025 - **feat** allow to parse generic cargo bench/criterion units (#280) diff --git a/package-lock.json b/package-lock.json index b027b8187..f9b9f3acd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "@actions/core": "^1.10.0", "@actions/exec": "^1.1.1", "@actions/github": "^5.1.1", - "@actions/io": "^1.1.2" + "@actions/io": "^1.1.2", + "zod": "^4.1.5" }, "devDependencies": { "@types/acorn": "^4.0.5", @@ -21,7 +22,7 @@ "@types/deep-equal": "^1.0.1", "@types/jest": "^29.5.11", "@types/markdown-it": "^12.2.3", - "@types/node": "^13.9.1", + "@types/node": "^24.3.1", "@types/rimraf": "^2.0.3", "@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/parser": "^5.4.0", @@ -39,7 +40,7 @@ "prettier": "^2.4.1", "rimraf": "^3.0.2", "ts-jest": "^29.1.2", - "typescript": "^4.5.2" + "typescript": "^5.9.2" } }, "node_modules/@actions/core": { @@ -1507,10 +1508,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "13.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.1.tgz", - "integrity": "sha512-E6M6N0blf/jiZx8Q3nb0vNaswQeEyn0XlupO+xN6DtJ6r6IT4nXrTry7zhIfYvFCl3/8Cu6WIysmUBKiqV0bqQ==", - "dev": true + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", + "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", + "dev": true, + "dependencies": { + "undici-types": "~7.10.0" + } }, "node_modules/@types/rimraf": { "version": "2.0.3", @@ -5795,16 +5799,16 @@ } }, "node_modules/typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/uc.micro": { @@ -5813,6 +5817,12 @@ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "dev": true + }, "node_modules/universal-user-agent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", @@ -6034,6 +6044,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.5.tgz", + "integrity": "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } }, "dependencies": { @@ -7247,10 +7265,13 @@ "dev": true }, "@types/node": { - "version": "13.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.1.tgz", - "integrity": "sha512-E6M6N0blf/jiZx8Q3nb0vNaswQeEyn0XlupO+xN6DtJ6r6IT4nXrTry7zhIfYvFCl3/8Cu6WIysmUBKiqV0bqQ==", - "dev": true + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", + "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", + "dev": true, + "requires": { + "undici-types": "~7.10.0" + } }, "@types/rimraf": { "version": "2.0.3", @@ -10326,9 +10347,9 @@ "dev": true }, "typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true }, "uc.micro": { @@ -10337,6 +10358,12 @@ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true }, + "undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "dev": true + }, "universal-user-agent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", @@ -10496,6 +10523,11 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zod": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.5.tgz", + "integrity": "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==" } } } diff --git a/package.json b/package.json index faa2b4aa4..a2c9472cb 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "@actions/core": "^1.10.0", "@actions/exec": "^1.1.1", "@actions/github": "^5.1.1", - "@actions/io": "^1.1.2" + "@actions/io": "^1.1.2", + "zod": "^4.1.5" }, "devDependencies": { "@types/acorn": "^4.0.5", @@ -43,7 +44,7 @@ "@types/deep-equal": "^1.0.1", "@types/jest": "^29.5.11", "@types/markdown-it": "^12.2.3", - "@types/node": "^13.9.1", + "@types/node": "^24.3.1", "@types/rimraf": "^2.0.3", "@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/parser": "^5.4.0", @@ -61,6 +62,6 @@ "prettier": "^2.4.1", "rimraf": "^3.0.2", "ts-jest": "^29.1.2", - "typescript": "^4.5.2" + "typescript": "^5.9.2" } } diff --git a/src/extract.ts b/src/extract.ts index ec7bc35e2..60736e61c 100644 --- a/src/extract.ts +++ b/src/extract.ts @@ -2,14 +2,19 @@ import { promises as fs } from 'fs'; import * as github from '@actions/github'; import { Config, ToolType } from './config'; +import { z } from 'zod'; -export interface BenchmarkResult { - name: string; - value: number; - range?: string; - unit: string; - extra?: string; -} +export const BenchmarkResult = z.object({ + name: z.coerce.string(), + value: z.coerce.number(), + range: z.coerce.string().optional(), + unit: z.coerce.string(), + extra: z.coerce.string().optional(), +}); + +export type BenchmarkResult = z.infer; + +export const BenchmarkResults = z.array(BenchmarkResult); interface GitHubUser { email?: string; @@ -658,13 +663,11 @@ function extractBenchmarkDotnetResult(output: string): BenchmarkResult[] { function extractCustomBenchmarkResult(output: string): BenchmarkResult[] { try { - const json: BenchmarkResult[] = JSON.parse(output); - return json.map(({ name, value, unit, range, extra }) => { - return { name, value, unit, range, extra }; - }); - } catch (err: any) { + return BenchmarkResults.parse(JSON.parse(output)); + } catch (err: unknown) { + const errMessage = err instanceof Error ? err.message : String(err); throw new Error( - `Output file for 'custom-(bigger|smaller)-is-better' must be JSON file containing an array of entries in BenchmarkResult format: ${err.message}`, + `Output file for 'custom-(bigger|smaller)-is-better' must be JSON file containing an array of entries in BenchmarkResult format: ${errMessage}`, ); } } @@ -673,7 +676,6 @@ function extractLuauBenchmarkResult(output: string): BenchmarkResult[] { const lines = output.split(/\n/); const results: BenchmarkResult[] = []; - output; for (const line of lines) { if (!line.startsWith('SUCCESS')) continue; const [_0, name, _2, valueStr, _4, range, _6, extra] = line.split(/\s+/); diff --git a/test/__snapshots__/extract.spec.ts.snap b/test/__snapshots__/extract.spec.ts.snap index 76d1e91a9..720bfa5f5 100644 --- a/test/__snapshots__/extract.spec.ts.snap +++ b/test/__snapshots__/extract.spec.ts.snap @@ -427,9 +427,7 @@ exports[`extractResult() extracts benchmark output from customBiggerIsBetter - c { "benches": [ { - "extra": undefined, "name": "My Custom Bigger Is Better Benchmark - Throughput", - "range": undefined, "unit": "req/s", "value": 70, }, @@ -467,9 +465,7 @@ exports[`extractResult() extracts benchmark output from customSmallerIsBetter - "value": 50, }, { - "extra": undefined, "name": "My Custom Smaller Is Better Benchmark - Memory Used", - "range": undefined, "unit": "Megabytes", "value": 100, }, @@ -487,6 +483,47 @@ exports[`extractResult() extracts benchmark output from customSmallerIsBetter - } `; +exports[`extractResult() extracts benchmark output from customSmallerIsBetter - customSmallerIsBetter_output2.json 1`] = ` +{ + "benches": [ + { + "name": "No instrumentation", + "range": "0.519165", + "unit": "ns", + "value": 90.9439, + }, + { + "name": "Deactivated probe", + "range": "8.21371", + "unit": "ns", + "value": 445.661, + }, + { + "name": "No logging", + "range": "10.2008", + "unit": "ns", + "value": 1847.38, + }, + { + "name": "Binary file", + "range": "87.1657", + "unit": "ns", + "value": 3886.75, + }, + ], + "commit": { + "author": null, + "committer": null, + "id": "123456789abcdef", + "message": "this is dummy", + "timestamp": "dummy timestamp", + "url": "https://github.com/dummy/repo", + }, + "date": 1712131503296, + "tool": "customSmallerIsBetter", +} +`; + exports[`extractResult() extracts benchmark output from go - go_fiber_output.txt 1`] = ` { "benches": [ diff --git a/test/data/extract/customSmallerIsBetter_output2.json b/test/data/extract/customSmallerIsBetter_output2.json new file mode 100644 index 000000000..dad69075f --- /dev/null +++ b/test/data/extract/customSmallerIsBetter_output2.json @@ -0,0 +1,26 @@ +[ + { + "name": "No instrumentation", + "unit": "ns", + "value": 90.9439, + "range": 0.519165 + }, + { + "name": "Deactivated probe", + "unit": "ns", + "value": 445.661, + "range": 8.21371 + }, + { + "name": "No logging", + "unit": "ns", + "value": 1847.38, + "range": 10.2008 + }, + { + "name": "Binary file", + "unit": "ns", + "value": 3886.75, + "range": 87.1657 + } +] diff --git a/test/extract.spec.ts b/test/extract.spec.ts index 48a5c271a..f07a81d53 100644 --- a/test/extract.spec.ts +++ b/test/extract.spec.ts @@ -143,6 +143,10 @@ describe('extractResult()', function () { tool: 'customSmallerIsBetter', file: 'customSmallerIsBetter_output.json', }, + { + tool: 'customSmallerIsBetter', + file: 'customSmallerIsBetter_output2.json', + }, ]; it.each(normalCases)(`extracts benchmark output from $tool - $file`, async function (test) {