1
0
Fork 0

updating errors

pull/1690/head
Vallie Joseph 2024-04-08 15:40:32 +00:00
parent e7f45861f6
commit 4f54b861cf
2 changed files with 134 additions and 126 deletions

View File

@ -1,119 +1,124 @@
import * as stream from 'stream'
import * as ZipStream from 'zip-stream'
import * as core from '@actions/core' import * as core from '@actions/core'
import { import async from 'async'
UploadArtifactOptions, import {createReadStream} from 'fs'
UploadArtifactResponse import {UploadZipSpecification} from './upload-zip-specification'
} from '../shared/interfaces' import {getUploadChunkSize} from '../shared/config'
import {getExpiration} from './retention'
import {validateArtifactName} from './path-and-artifact-name-validation'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client'
import {
UploadZipSpecification,
getUploadZipSpecification,
validateRootDirectory
} from './upload-zip-specification'
import {getBackendIdsFromToken} from '../shared/util'
import {uploadZipToBlobStorage} from './blob-upload'
import {createZipUploadStream} from './zip'
import {
CreateArtifactRequest,
FinalizeArtifactRequest,
StringValue
} from '../../generated'
import {FilesNotFoundError, InvalidResponseError} from '../shared/errors'
export async function uploadArtifact( export const DEFAULT_COMPRESSION_LEVEL = 6
name: string,
files: string[],
rootDirectory: string,
options?: UploadArtifactOptions | undefined
): Promise<UploadArtifactResponse> {
validateArtifactName(name)
validateRootDirectory(rootDirectory)
const zipSpecification: UploadZipSpecification[] = getUploadZipSpecification( // Custom stream transformer so we can set the highWaterMark property
files, // See https://github.com/nodejs/node/issues/8855
rootDirectory export class ZipUploadStream extends stream.Transform {
) constructor(bufferSize: number) {
if (zipSpecification.length === 0) { super({
throw new FilesNotFoundError( highWaterMark: bufferSize
zipSpecification.flatMap(s => (s.sourcePath ? [s.sourcePath] : []))
)
}
// get the IDs needed for the artifact creation
const backendIds = getBackendIdsFromToken()
// create the artifact client
const artifactClient = internalArtifactTwirpClient()
// create the artifact
const createArtifactReq: CreateArtifactRequest = {
workflowRunBackendId: backendIds.workflowRunBackendId,
workflowJobRunBackendId: backendIds.workflowJobRunBackendId,
name,
version: 4
}
// if there is a retention period, add it to the request
const expiresAt = getExpiration(options?.retentionDays)
if (expiresAt) {
createArtifactReq.expiresAt = expiresAt
}
const createArtifactResp =
await artifactClient.CreateArtifact(createArtifactReq)
if (!createArtifactResp.ok) {
throw new InvalidResponseError(
'CreateArtifact: response from backend was not ok'
)
}
// Create the zipupload stream for use in blob upload
const zipUploadStream = await createZipUploadStream(
zipSpecification,
options?.compressionLevel
).catch(err => {
throw new InvalidResponseError(
`createZipUploadStream: response from backend was not ok: ${err}`
)
})
// Upload zip to blob storage
const uploadResult = await uploadZipToBlobStorage(
createArtifactResp.signedUploadUrl,
zipUploadStream
).catch(err => {
throw new InvalidResponseError(
`uploadZipToBlobStorage: response blob was not ok: ${err}`
)
})
// finalize the artifact
const finalizeArtifactReq: FinalizeArtifactRequest = {
workflowRunBackendId: backendIds.workflowRunBackendId,
workflowJobRunBackendId: backendIds.workflowJobRunBackendId,
name,
size: uploadResult.uploadSize ? uploadResult.uploadSize.toString() : '0'
}
if (uploadResult.sha256Hash) {
finalizeArtifactReq.hash = StringValue.create({
value: `sha256:${uploadResult.sha256Hash}`
}) })
} }
core.info(`Finalizing artifact upload`)
const finalizeArtifactResp =
await artifactClient.FinalizeArtifact(finalizeArtifactReq)
if (!finalizeArtifactResp.ok) {
throw new InvalidResponseError(
'FinalizeArtifact: response from backend was not ok'
)
}
const artifactId = BigInt(finalizeArtifactResp.artifactId) // eslint-disable-next-line @typescript-eslint/no-explicit-any
core.info( _transform(chunk: any, enc: any, cb: any): void {
`Artifact ${name}.zip successfully finalized. Artifact ID ${artifactId}` cb(null, chunk)
)
return {
size: uploadResult.uploadSize,
id: Number(artifactId)
} }
} }
export async function createZipUploadStream(
uploadSpecification: UploadZipSpecification[],
compressionLevel: number = DEFAULT_COMPRESSION_LEVEL
): Promise<ZipUploadStream> {
core.debug(
`Creating Artifact archive with compressionLevel: ${compressionLevel}`
)
const zlibOptions = {
zlib: {
level: compressionLevel,
bufferSize: getUploadChunkSize()
}
}
const zip = new ZipStream.default(zlibOptions)
const bufferSize = getUploadChunkSize()
const zipUploadStream = new ZipUploadStream(bufferSize)
zip.pipe(zipUploadStream)
// register callbacks for various events during the zip lifecycle
zip.on('error', zipErrorCallback)
zip.on('warning', zipWarningCallback)
zip.on('finish', zipFinishCallback)
zip.on('end', zipEndCallback)
const addFileToZip = (
file: UploadZipSpecification,
callback: (error?: Error) => void
) => {
if (file.sourcePath !== null) {
zip.entry(
createReadStream(file.sourcePath),
{name: file.destinationPath},
(error: any) => {
if (error) {
callback(error)
return
}
callback()
}
)
} else {
zip.entry('', {name: file.destinationPath}, (error: any) => {
if (error) {
callback(error)
return
}
callback()
})
}
}
async.eachSeries(uploadSpecification, addFileToZip, (error: any) => {
if (error) {
core.error('Failed to add a file to the zip:')
core.info(error)
return
}
zip.finalize() // Finalize the archive once all files have been added
})
core.debug(
`Zip write high watermark value ${zipUploadStream.writableHighWaterMark}`
)
core.debug(
`Zip read high watermark value ${zipUploadStream.readableHighWaterMark}`
)
return zipUploadStream
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const zipErrorCallback = (error: any): void => {
core.error('An error has occurred while creating the zip file for upload')
core.info(error)
throw new Error('An error has occurred during zip creation for the artifact')
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const zipWarningCallback = (error: any): void => {
if (error.code === 'ENOENT') {
core.warning(
'ENOENT warning during artifact zip creation. No such file or directory'
)
core.info(error)
} else {
core.warning(
`A non-blocking warning has occurred during artifact zip creation: ${error.code}`
)
core.info(error)
}
}
const zipFinishCallback = (): void => {
core.debug('Zip stream for upload has finished.')
}
const zipEndCallback = (): void => {
core.debug('Zip stream for upload has ended.')
}

View File

@ -22,9 +22,7 @@ export class ZipUploadStream extends stream.Transform {
cb(null, chunk) cb(null, chunk)
} }
} }
interface NodeJSError extends Error {
code?: string
}
export async function createZipUploadStream( export async function createZipUploadStream(
uploadSpecification: UploadZipSpecification[], uploadSpecification: UploadZipSpecification[],
compressionLevel: number = DEFAULT_COMPRESSION_LEVEL compressionLevel: number = DEFAULT_COMPRESSION_LEVEL
@ -32,6 +30,7 @@ export async function createZipUploadStream(
core.debug( core.debug(
`Creating Artifact archive with compressionLevel: ${compressionLevel}` `Creating Artifact archive with compressionLevel: ${compressionLevel}`
) )
const zlibOptions = { const zlibOptions = {
zlib: { zlib: {
level: compressionLevel, level: compressionLevel,
@ -39,14 +38,16 @@ export async function createZipUploadStream(
} }
} }
const zip = new ZipStream.default(zlibOptions) const zip = new ZipStream.default(zlibOptions)
const bufferSize = getUploadChunkSize() const bufferSize = getUploadChunkSize()
const zipUploadStream = new ZipUploadStream(bufferSize) const zipUploadStream = new ZipUploadStream(bufferSize)
zip.pipe(zipUploadStream)
// register callbacks for various events during the zip lifecycle // register callbacks for various events during the zip lifecycle
zip.on('error', zipErrorCallback) zip.on('error', zipErrorCallback)
zip.on('warning', zipWarningCallback) zip.on('warning', zipWarningCallback)
zip.on('finish', zipFinishCallback) zip.on('finish', zipFinishCallback)
zip.on('end', zipEndCallback) zip.on('end', zipEndCallback)
const addFileToZip = ( const addFileToZip = (
file: UploadZipSpecification, file: UploadZipSpecification,
callback: (error?: Error) => void callback: (error?: Error) => void
@ -55,18 +56,18 @@ export async function createZipUploadStream(
zip.entry( zip.entry(
createReadStream(file.sourcePath), createReadStream(file.sourcePath),
{name: file.destinationPath}, {name: file.destinationPath},
(error: NodeJSError) => { (error: unknown) => {
if (error) { if (error) {
callback(error) callback(error as Error) // Cast the error object to the Error type
return return
} }
callback() callback()
} }
) )
} else { } else {
zip.entry('', {name: file.destinationPath}, (error: NodeJSError) => { zip.entry('', {name: file.destinationPath}, (error: unknown) => {
if (error) { if (error) {
callback(error) callback(error as Error)
return return
} }
callback() callback()
@ -74,10 +75,10 @@ export async function createZipUploadStream(
} }
} }
async.eachSeries(uploadSpecification, addFileToZip, (error: NodeJSError) => { async.eachSeries(uploadSpecification, addFileToZip, (error: unknown) => {
if (error) { if (error) {
core.error('Failed to add a file to the zip:') core.error('Failed to add a file to the zip:')
core.info(error.toString()) core.info(error.toString()) // Convert error to string
return return
} }
zip.finalize() // Finalize the archive once all files have been added zip.finalize() // Finalize the archive once all files have been added
@ -93,23 +94,25 @@ export async function createZipUploadStream(
return zipUploadStream return zipUploadStream
} }
const zipErrorCallback = (error: Error): void => { // eslint-disable-next-line @typescript-eslint/no-explicit-any
const zipErrorCallback = (error: any): void => {
core.error('An error has occurred while creating the zip file for upload') core.error('An error has occurred while creating the zip file for upload')
core.info(error.message) core.info(error)
throw new Error('An error has occurred during zip creation for the artifact') throw new Error('An error has occurred during zip creation for the artifact')
} }
const zipWarningCallback = (error: NodeJSError): void => { // eslint-disable-next-line @typescript-eslint/no-explicit-any
const zipWarningCallback = (error: any): void => {
if (error.code === 'ENOENT') { if (error.code === 'ENOENT') {
core.warning( core.warning(
'ENOENT warning during artifact zip creation. No such file or directory' 'ENOENT warning during artifact zip creation. No such file or directory'
) )
core.info(error.toString()) // Convert error object to string core.info(error)
} else { } else {
core.warning( core.warning(
`A non-blocking warning has occurred during artifact zip creation: ${error.code}` `A non-blocking warning has occurred during artifact zip creation: ${error.code}`
) )
core.info(error.toString()) // Convert error object to string core.info(error)
} }
} }