* Artifact upload: support uploading single un-zipped files * Fix linters * Fix lint again * Fix tests * Check for 0 sized artifact lists * Add some more stream tests and handle an upload failure gracefully * Add CI tests for non-zipped artifacts * Add an html report to test rendering in the browser * Fix linting issue * Artifact: bump the version and add release notes * Fix Windows tests * Fix linting * stream: switch the error details to error type * Refactor the validation logic in `uploadArtifact` a bit * Added more details about how the name parameter is handled
93 lines
2.6 KiB
TypeScript
93 lines
2.6 KiB
TypeScript
import {realpath} from 'fs/promises'
|
|
import archiver from 'archiver'
|
|
import * as core from '@actions/core'
|
|
import {UploadZipSpecification} from './upload-zip-specification.js'
|
|
import {getUploadChunkSize} from '../shared/config.js'
|
|
import {WaterMarkedUploadStream} from './stream.js'
|
|
|
|
export const DEFAULT_COMPRESSION_LEVEL = 6
|
|
|
|
export async function createZipUploadStream(
|
|
uploadSpecification: UploadZipSpecification[],
|
|
compressionLevel: number = DEFAULT_COMPRESSION_LEVEL
|
|
): Promise<WaterMarkedUploadStream> {
|
|
core.debug(
|
|
`Creating Artifact archive with compressionLevel: ${compressionLevel}`
|
|
)
|
|
|
|
const zip = archiver.create('zip', {
|
|
highWaterMark: getUploadChunkSize(),
|
|
zlib: {level: compressionLevel}
|
|
})
|
|
|
|
// 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)
|
|
|
|
for (const file of uploadSpecification) {
|
|
if (file.sourcePath !== null) {
|
|
// Check if symlink and resolve the source path
|
|
let sourcePath = file.sourcePath
|
|
if (file.stats.isSymbolicLink()) {
|
|
sourcePath = await realpath(file.sourcePath)
|
|
}
|
|
|
|
// Add the file to the zip
|
|
zip.file(sourcePath, {
|
|
name: file.destinationPath
|
|
})
|
|
} else {
|
|
// Add a directory to the zip
|
|
zip.append('', {name: file.destinationPath})
|
|
}
|
|
}
|
|
|
|
const bufferSize = getUploadChunkSize()
|
|
const zipUploadStream = new WaterMarkedUploadStream(bufferSize)
|
|
|
|
core.debug(
|
|
`Zip write high watermark value ${zipUploadStream.writableHighWaterMark}`
|
|
)
|
|
core.debug(
|
|
`Zip read high watermark value ${zipUploadStream.readableHighWaterMark}`
|
|
)
|
|
|
|
zip.pipe(zipUploadStream)
|
|
zip.finalize()
|
|
|
|
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.')
|
|
}
|