mirror of
https://github.com/dorny/test-reporter.git
synced 2025-12-17 14:57:09 +01:00
.
This commit is contained in:
parent
4da860cd59
commit
5473a04c5a
13 changed files with 22679 additions and 8953 deletions
|
|
@ -1,123 +0,0 @@
|
|||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import {GitHub} from '@actions/github/lib/utils'
|
||||
|
||||
import Zip from 'adm-zip'
|
||||
import picomatch from 'picomatch'
|
||||
|
||||
import {FileContent, InputProvider, ReportInput} from './input-provider'
|
||||
import {downloadArtifact, listFiles} from '../utils/github-utils'
|
||||
|
||||
export class ArtifactProvider implements InputProvider {
|
||||
private readonly artifactNameMatch: (name: string) => boolean
|
||||
private readonly fileNameMatch: (name: string) => boolean
|
||||
private readonly getReportName: (name: string) => string
|
||||
|
||||
constructor(
|
||||
readonly octokit: InstanceType<typeof GitHub>,
|
||||
readonly artifact: string,
|
||||
readonly name: string,
|
||||
readonly pattern: string[],
|
||||
readonly sha: string,
|
||||
readonly runId: number,
|
||||
readonly token: string
|
||||
) {
|
||||
if (this.artifact.startsWith('/')) {
|
||||
const endIndex = this.artifact.lastIndexOf('/')
|
||||
const rePattern = this.artifact.substring(1, endIndex)
|
||||
const reOpts = this.artifact.substring(endIndex + 1)
|
||||
const re = new RegExp(rePattern, reOpts)
|
||||
this.artifactNameMatch = (str: string) => re.test(str)
|
||||
this.getReportName = (str: string) => {
|
||||
const match = str.match(re)
|
||||
if (match === null) {
|
||||
throw new Error(`Artifact name '${str}' does not match regex ${this.artifact}`)
|
||||
}
|
||||
let reportName = this.name
|
||||
for (let i = 1; i < match.length; i++) {
|
||||
reportName = reportName.replace(new RegExp(`\\$${i}`, 'g'), match[i])
|
||||
}
|
||||
return reportName
|
||||
}
|
||||
} else {
|
||||
this.artifactNameMatch = (str: string) => str === this.artifact
|
||||
this.getReportName = () => this.name
|
||||
}
|
||||
|
||||
this.fileNameMatch = picomatch(pattern)
|
||||
}
|
||||
|
||||
async load(): Promise<ReportInput> {
|
||||
const result: ReportInput = {
|
||||
artifactFilePaths: [],
|
||||
reports: {}
|
||||
}
|
||||
|
||||
const resp = await this.octokit.rest.actions.listWorkflowRunArtifacts({
|
||||
...github.context.repo,
|
||||
run_id: this.runId
|
||||
})
|
||||
|
||||
if (resp.data.artifacts.length === 0) {
|
||||
core.warning(`No artifacts found in run ${this.runId}`)
|
||||
return result
|
||||
}
|
||||
|
||||
const artifacts = resp.data.artifacts.filter(a => this.artifactNameMatch(a.name))
|
||||
if (artifacts.length === 0) {
|
||||
core.warning(`No artifact matches ${this.artifact}`)
|
||||
return result
|
||||
}
|
||||
|
||||
const versionArtifact = resp.data.artifacts.find(a => a.name === 'version.txt')
|
||||
|
||||
if (versionArtifact) {
|
||||
await downloadArtifact(this.octokit, versionArtifact.id, 'version.txt', this.token)
|
||||
result.versionArtifactPath = 'version.txt'
|
||||
} else {
|
||||
core.warning(
|
||||
`Could not find version.txt artifact among these artifacts: ${resp.data.artifacts.map(a => a.name).join(', ')}`
|
||||
)
|
||||
}
|
||||
|
||||
for (const art of artifacts) {
|
||||
const fileName = `${art.name}.zip`
|
||||
result.artifactFilePaths.push(fileName)
|
||||
await downloadArtifact(this.octokit, art.id, fileName, this.token)
|
||||
core.startGroup(`Reading archive ${fileName}`)
|
||||
try {
|
||||
const reportName = this.getReportName(art.name)
|
||||
core.info(`Report name: ${reportName}`)
|
||||
const files: FileContent[] = []
|
||||
const zip = new Zip(fileName)
|
||||
for (const entry of zip.getEntries()) {
|
||||
const file = entry.entryName
|
||||
if (entry.isDirectory) {
|
||||
core.info(`Skipping ${file}: entry is a directory`)
|
||||
continue
|
||||
}
|
||||
if (!this.fileNameMatch(file)) {
|
||||
core.info(`Skipping ${file}: filename does not match pattern`)
|
||||
continue
|
||||
}
|
||||
const content = zip.readAsText(entry)
|
||||
files.push({file, content})
|
||||
core.info(`Read ${file}: ${content.length} chars`)
|
||||
}
|
||||
if (result.reports[reportName]) {
|
||||
result.reports[reportName].push(...files)
|
||||
} else {
|
||||
result.reports[reportName] = files
|
||||
}
|
||||
} finally {
|
||||
core.endGroup()
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async listTrackedFiles(): Promise<string[]> {
|
||||
return listFiles(this.octokit, this.sha)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import Zip from 'adm-zip'
|
||||
|
||||
export interface ReportInput {
|
||||
artifactFilePaths: string[]
|
||||
versionArtifactPath?: string
|
||||
trxZip: Zip
|
||||
reports: {
|
||||
[reportName: string]: FileContent[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import * as fs from 'fs'
|
|||
import glob from 'fast-glob'
|
||||
import {FileContent, InputProvider, ReportInput} from './input-provider.js'
|
||||
import {listFiles} from '../utils/git.js'
|
||||
import Zip from 'adm-zip'
|
||||
import path from 'path'
|
||||
|
||||
export class LocalFileProvider implements InputProvider {
|
||||
constructor(
|
||||
|
|
@ -11,16 +13,19 @@ export class LocalFileProvider implements InputProvider {
|
|||
|
||||
async load(): Promise<ReportInput> {
|
||||
const result: FileContent[] = []
|
||||
const zip = new Zip()
|
||||
for (const pat of this.pattern) {
|
||||
const paths = await glob(pat, {dot: true})
|
||||
for (const file of paths) {
|
||||
const dir = path.dirname(file)
|
||||
zip.addLocalFile(file, dir)
|
||||
const content = await fs.promises.readFile(file, {encoding: 'utf8'})
|
||||
result.push({file, content})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
artifactFilePaths: [],
|
||||
trxZip: zip,
|
||||
reports: {
|
||||
[this.name]: result
|
||||
}
|
||||
|
|
|
|||
37
src/main.ts
37
src/main.ts
|
|
@ -2,7 +2,6 @@ import * as core from '@actions/core'
|
|||
import * as github from '@actions/github'
|
||||
import {GitHub} from '@actions/github/lib/utils.js'
|
||||
|
||||
import {ArtifactProvider} from './input-providers/artifact-provider.js'
|
||||
import {LocalFileProvider} from './input-providers/local-file-provider.js'
|
||||
import {FileContent} from './input-providers/input-provider.js'
|
||||
import {ParseOptions, TestParser} from './test-parser.js'
|
||||
|
|
@ -10,19 +9,14 @@ import {TestRunResult} from './test-results.js'
|
|||
import {getAnnotations} from './report/get-annotations.js'
|
||||
import {getReport} from './report/get-report.js'
|
||||
|
||||
import {DartJsonParser} from './parsers/dart-json/dart-json-parser.js'
|
||||
import {DotnetTrxParser} from './parsers/dotnet-trx/dotnet-trx-parser.js'
|
||||
import {JavaJunitParser} from './parsers/java-junit/java-junit-parser.js'
|
||||
import {JestJunitParser} from './parsers/jest-junit/jest-junit-parser.js'
|
||||
import {MochaJsonParser} from './parsers/mocha-json/mocha-json-parser.js'
|
||||
import {SwiftXunitParser} from './parsers/swift-xunit/swift-xunit-parser.js'
|
||||
|
||||
import {normalizeDirPath, normalizeFilePath} from './utils/path-utils.js'
|
||||
import {getCheckRunContext} from './utils/github-utils.js'
|
||||
import {IncomingWebhook} from '@slack/webhook'
|
||||
import fs from 'fs'
|
||||
import bent from 'bent'
|
||||
import { cwd } from 'process';
|
||||
import {cwd} from 'process'
|
||||
|
||||
async function main(): Promise<void> {
|
||||
try {
|
||||
|
|
@ -86,17 +80,7 @@ class TestReporter {
|
|||
const pathsList = this.path.split(',')
|
||||
const pattern = this.pathReplaceBackslashes ? pathsList.map(normalizeFilePath) : pathsList
|
||||
|
||||
const inputProvider = this.artifact
|
||||
? new ArtifactProvider(
|
||||
this.octokit,
|
||||
this.artifact,
|
||||
this.name,
|
||||
pattern,
|
||||
this.context.sha,
|
||||
this.context.runId,
|
||||
this.token
|
||||
)
|
||||
: new LocalFileProvider(this.name, pattern)
|
||||
const inputProvider = new LocalFileProvider(this.name, pattern)
|
||||
|
||||
const parseErrors = this.maxAnnotations > 0
|
||||
const trackedFiles = parseErrors ? await inputProvider.listTrackedFiles() : []
|
||||
|
|
@ -116,7 +100,6 @@ class TestReporter {
|
|||
const results: TestRunResult[] = []
|
||||
const input = await inputProvider.load()
|
||||
|
||||
|
||||
try {
|
||||
const readStream = input.trxZip.toBuffer()
|
||||
const version = fs.existsSync('test/EVA.TestSuite.Core/bin/Release/version.txt')
|
||||
|
|
@ -126,7 +109,9 @@ class TestReporter {
|
|||
? fs.readFileSync('test/EVA.TestSuite.Core/bin/Release/commit.txt').toString()
|
||||
: null
|
||||
|
||||
core.info(`Using EVA version ${version}, commit ${commitID}, branch ${this.context.branch}, current directory: ${cwd()}`)
|
||||
core.info(
|
||||
`Using EVA version ${version}, commit ${commitID}, branch ${this.context.branch}, current directory: ${cwd()}`
|
||||
)
|
||||
|
||||
const post = bent(this.resultsEndpoint, 'POST', {}, 200)
|
||||
await post(
|
||||
|
|
@ -278,20 +263,8 @@ class TestReporter {
|
|||
|
||||
getParser(reporter: string, options: ParseOptions): TestParser {
|
||||
switch (reporter) {
|
||||
case 'dart-json':
|
||||
return new DartJsonParser(options, 'dart')
|
||||
case 'dotnet-trx':
|
||||
return new DotnetTrxParser(options)
|
||||
case 'flutter-json':
|
||||
return new DartJsonParser(options, 'flutter')
|
||||
case 'java-junit':
|
||||
return new JavaJunitParser(options)
|
||||
case 'jest-junit':
|
||||
return new JestJunitParser(options)
|
||||
case 'mocha-json':
|
||||
return new MochaJsonParser(options)
|
||||
case 'swift-xunit':
|
||||
return new SwiftXunitParser(options)
|
||||
default:
|
||||
throw new Error(`Input variable 'reporter' is set to invalid value '${reporter}'`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import * as core from '@actions/core'
|
||||
import { getExecOutput } from '@actions/exec'
|
||||
import {getExecOutput} from '@actions/exec'
|
||||
|
||||
export async function listFiles(): Promise<string[]> {
|
||||
core.startGroup('Listing all files tracked by git')
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export function getCheckRunContext(): {sha: string; runId: number; branch: strin
|
|||
const runId = github.context.runId
|
||||
if (github.context.payload.pull_request) {
|
||||
core.info(`Action was triggered by ${github.context.eventName}: using SHA from head of source branch`)
|
||||
const pr = github.context.payload.pull_request;
|
||||
const pr = github.context.payload.pull_request
|
||||
return {sha: pr.head.sha, runId, branch}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue