import * as core from "@actions/core" import { Artifact } from "./Artifact" import { Releases } from "./Releases" export interface ArtifactUploader { uploadArtifacts(artifacts: Artifact[], releaseId: number, uploadUrl: string): Promise } export class GithubArtifactUploader implements ArtifactUploader { constructor( private releases: Releases, private replacesExistingArtifacts: boolean = true, private throwsUploadErrors: boolean = false ) {} async uploadArtifacts(artifacts: Artifact[], releaseId: number, uploadUrl: string): Promise { if (this.replacesExistingArtifacts) { await this.deleteUpdatedArtifacts(artifacts, releaseId) } for (const artifact of artifacts) { await this.uploadArtifact(artifact, releaseId, uploadUrl) } } private async uploadArtifact(artifact: Artifact, releaseId: number, uploadUrl: string, retry = 3) { try { core.debug(`Uploading artifact ${artifact.name}...`) await this.releases.uploadArtifact( uploadUrl, artifact.contentLength, artifact.contentType, artifact.readFile(), artifact.name, releaseId ) } catch (error: any) { if (error.status >= 500 && retry > 0) { core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}. Retrying...`) await this.uploadArtifact(artifact, releaseId, uploadUrl, retry - 1) } else { if (this.throwsUploadErrors) { throw Error(`Failed to upload artifact ${artifact.name}. ${error.message}.`) } else { core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}.`) } } } } private async deleteUpdatedArtifacts(artifacts: Artifact[], releaseId: number): Promise { const releaseAssets = await this.releases.listArtifactsForRelease(releaseId) const assetByName: Record = {} releaseAssets.forEach((asset) => { assetByName[asset.name] = asset }) for (const artifact of artifacts) { const asset = assetByName[artifact.name] if (asset) { core.debug(`Deleting existing artifact ${artifact.name}...`) await this.releases.deleteArtifact(asset.id) } } } }