mirror of https://github.com/actions/toolkit
Include fs stats in zip archive
parent
68f22927e7
commit
307f360e0e
|
@ -8,6 +8,7 @@ import * as blobUpload from '../src/internal/upload/blob-upload'
|
||||||
import {uploadArtifact} from '../src/internal/upload/upload-artifact'
|
import {uploadArtifact} from '../src/internal/upload/upload-artifact'
|
||||||
import {noopLogs} from './common'
|
import {noopLogs} from './common'
|
||||||
import {FilesNotFoundError} from '../src/internal/shared/errors'
|
import {FilesNotFoundError} from '../src/internal/shared/errors'
|
||||||
|
import {Stats} from 'fs'
|
||||||
|
|
||||||
describe('upload-artifact', () => {
|
describe('upload-artifact', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -28,15 +29,18 @@ describe('upload-artifact', () => {
|
||||||
.mockReturnValue([
|
.mockReturnValue([
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/file1.txt',
|
sourcePath: '/home/user/files/plz-upload/file1.txt',
|
||||||
destinationPath: 'file1.txt'
|
destinationPath: 'file1.txt',
|
||||||
|
stats: new Stats()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/file2.txt',
|
sourcePath: '/home/user/files/plz-upload/file2.txt',
|
||||||
destinationPath: 'file2.txt'
|
destinationPath: 'file2.txt',
|
||||||
|
stats: new Stats()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
|
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
|
||||||
destinationPath: 'dir/file3.txt'
|
destinationPath: 'dir/file3.txt',
|
||||||
|
stats: new Stats()
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -136,15 +140,18 @@ describe('upload-artifact', () => {
|
||||||
.mockReturnValue([
|
.mockReturnValue([
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/file1.txt',
|
sourcePath: '/home/user/files/plz-upload/file1.txt',
|
||||||
destinationPath: 'file1.txt'
|
destinationPath: 'file1.txt',
|
||||||
|
stats: new Stats()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/file2.txt',
|
sourcePath: '/home/user/files/plz-upload/file2.txt',
|
||||||
destinationPath: 'file2.txt'
|
destinationPath: 'file2.txt',
|
||||||
|
stats: new Stats()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
|
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
|
||||||
destinationPath: 'dir/file3.txt'
|
destinationPath: 'dir/file3.txt',
|
||||||
|
stats: new Stats()
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -175,15 +182,18 @@ describe('upload-artifact', () => {
|
||||||
.mockReturnValue([
|
.mockReturnValue([
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/file1.txt',
|
sourcePath: '/home/user/files/plz-upload/file1.txt',
|
||||||
destinationPath: 'file1.txt'
|
destinationPath: 'file1.txt',
|
||||||
|
stats: Stats.prototype
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/file2.txt',
|
sourcePath: '/home/user/files/plz-upload/file2.txt',
|
||||||
destinationPath: 'file2.txt'
|
destinationPath: 'file2.txt',
|
||||||
|
stats: Stats.prototype
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
|
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
|
||||||
destinationPath: 'dir/file3.txt'
|
destinationPath: 'dir/file3.txt',
|
||||||
|
stats: Stats.prototype
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -230,15 +240,18 @@ describe('upload-artifact', () => {
|
||||||
.mockReturnValue([
|
.mockReturnValue([
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/file1.txt',
|
sourcePath: '/home/user/files/plz-upload/file1.txt',
|
||||||
destinationPath: 'file1.txt'
|
destinationPath: 'file1.txt',
|
||||||
|
stats: new Stats()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/file2.txt',
|
sourcePath: '/home/user/files/plz-upload/file2.txt',
|
||||||
destinationPath: 'file2.txt'
|
destinationPath: 'file2.txt',
|
||||||
|
stats: new Stats()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
|
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
|
||||||
destinationPath: 'dir/file3.txt'
|
destinationPath: 'dir/file3.txt',
|
||||||
|
stats: new Stats()
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -293,15 +306,18 @@ describe('upload-artifact', () => {
|
||||||
.mockReturnValue([
|
.mockReturnValue([
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/file1.txt',
|
sourcePath: '/home/user/files/plz-upload/file1.txt',
|
||||||
destinationPath: 'file1.txt'
|
destinationPath: 'file1.txt',
|
||||||
|
stats: new Stats()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/file2.txt',
|
sourcePath: '/home/user/files/plz-upload/file2.txt',
|
||||||
destinationPath: 'file2.txt'
|
destinationPath: 'file2.txt',
|
||||||
|
stats: new Stats()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
|
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
|
||||||
destinationPath: 'dir/file3.txt'
|
destinationPath: 'dir/file3.txt',
|
||||||
|
stats: new Stats()
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
|
@ -287,14 +287,14 @@ describe('Search', () => {
|
||||||
expect(specifications.length).toEqual(2)
|
expect(specifications.length).toEqual(2)
|
||||||
const absolutePaths = specifications.map(item => item.sourcePath)
|
const absolutePaths = specifications.map(item => item.sourcePath)
|
||||||
expect(absolutePaths).toContain(goodItem1Path)
|
expect(absolutePaths).toContain(goodItem1Path)
|
||||||
expect(absolutePaths).toContain(null)
|
expect(absolutePaths).toContain(folderEPath)
|
||||||
|
|
||||||
for (const specification of specifications) {
|
for (const specification of specifications) {
|
||||||
if (specification.sourcePath === goodItem1Path) {
|
if (specification.sourcePath === goodItem1Path) {
|
||||||
expect(specification.destinationPath).toEqual(
|
expect(specification.destinationPath).toEqual(
|
||||||
path.join('/folder-a', 'folder-b', 'folder-c', 'good-item1.txt')
|
path.join('/folder-a', 'folder-b', 'folder-c', 'good-item1.txt')
|
||||||
)
|
)
|
||||||
} else if (specification.sourcePath === null) {
|
} else if (specification.sourcePath === folderEPath) {
|
||||||
expect(specification.destinationPath).toEqual(
|
expect(specification.destinationPath).toEqual(
|
||||||
path.join('/folder-a', 'folder-b', 'folder-e')
|
path.join('/folder-a', 'folder-b', 'folder-e')
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,14 +5,19 @@ import {validateFilePath} from './path-and-artifact-name-validation'
|
||||||
|
|
||||||
export interface UploadZipSpecification {
|
export interface UploadZipSpecification {
|
||||||
/**
|
/**
|
||||||
* An absolute source path that points to a file that will be added to a zip. Null if creating a new directory
|
* An absolute source path that points to a file that will be added to a zip.
|
||||||
*/
|
*/
|
||||||
sourcePath: string | null
|
sourcePath: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The destination path in a zip for a file
|
* The destination path in a zip for a file
|
||||||
*/
|
*/
|
||||||
destinationPath: string
|
destinationPath: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about the file on disk
|
||||||
|
*/
|
||||||
|
stats: fs.Stats
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,37 +80,26 @@ export function getUploadZipSpecification(
|
||||||
- file3.txt
|
- file3.txt
|
||||||
*/
|
*/
|
||||||
for (let file of filesToZip) {
|
for (let file of filesToZip) {
|
||||||
if (!fs.existsSync(file)) {
|
const stats = fs.statSync(file, {throwIfNoEntry: false})
|
||||||
throw new Error(`File ${file} does not exist`)
|
if (!stats) throw new Error(`File ${file} does not exist`)
|
||||||
|
// Normalize and resolve, this allows for either absolute or relative paths to be used
|
||||||
|
file = normalize(file)
|
||||||
|
file = resolve(file)
|
||||||
|
if (!file.startsWith(rootDirectory)) {
|
||||||
|
throw new Error(
|
||||||
|
`The rootDirectory: ${rootDirectory} is not a parent directory of the file: ${file}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (!fs.statSync(file).isDirectory()) {
|
|
||||||
// Normalize and resolve, this allows for either absolute or relative paths to be used
|
|
||||||
file = normalize(file)
|
|
||||||
file = resolve(file)
|
|
||||||
if (!file.startsWith(rootDirectory)) {
|
|
||||||
throw new Error(
|
|
||||||
`The rootDirectory: ${rootDirectory} is not a parent directory of the file: ${file}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for forbidden characters in file paths that may cause ambiguous behavior if downloaded on different file systems
|
// Check for forbidden characters in file paths that may cause ambiguous behavior if downloaded on different file systems
|
||||||
const uploadPath = file.replace(rootDirectory, '')
|
const destinationPath = file.replace(rootDirectory, '')
|
||||||
validateFilePath(uploadPath)
|
validateFilePath(destinationPath)
|
||||||
|
|
||||||
specification.push({
|
specification.push({
|
||||||
sourcePath: file,
|
sourcePath: file,
|
||||||
destinationPath: uploadPath
|
destinationPath,
|
||||||
})
|
stats
|
||||||
} else {
|
})
|
||||||
// Empty directory
|
|
||||||
const directoryPath = file.replace(rootDirectory, '')
|
|
||||||
validateFilePath(directoryPath)
|
|
||||||
|
|
||||||
specification.push({
|
|
||||||
sourcePath: null,
|
|
||||||
destinationPath: directoryPath
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return specification
|
return specification
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,14 +42,18 @@ export async function createZipUploadStream(
|
||||||
zip.on('end', zipEndCallback)
|
zip.on('end', zipEndCallback)
|
||||||
|
|
||||||
for (const file of uploadSpecification) {
|
for (const file of uploadSpecification) {
|
||||||
if (file.sourcePath !== null) {
|
if (!file.stats.isDirectory()) {
|
||||||
// Add a normal file to the zip
|
// Add a normal file to the zip
|
||||||
zip.append(createReadStream(file.sourcePath), {
|
zip.append(createReadStream(file.sourcePath), {
|
||||||
name: file.destinationPath
|
name: file.destinationPath,
|
||||||
|
stats: file.stats
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Add a directory to the zip
|
// Add a directory to the zip
|
||||||
zip.append('', {name: file.destinationPath})
|
zip.append('', {
|
||||||
|
name: file.destinationPath,
|
||||||
|
stats: file.stats
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue