| 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 = {} |
| |
| const resp = await this.octokit.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 {} |
| } |
| |
| const artifacts = resp.data.artifacts.filter(a => this.artifactNameMatch(a.name)) |
| if (artifacts.length === 0) { |
| core.warning(`No artifact matches ${this.artifact}`) |
| return {} |
| } |
| |
| for (const art of artifacts) { |
| const fileName = `${art.name}.zip` |
| 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[reportName]) { |
| result[reportName].push(...files) |
| } else { |
| result[reportName] = files |
| } |
| } finally { |
| core.endGroup() |
| } |
| } |
| |
| return result |
| } |
| |
| async listTrackedFiles(): Promise<string[]> { |
| return listFiles(this.octokit, this.sha) |
| } |
| } |