1
0
Fork 0

fix regression, auto readlink on symlinks again

pull/1830/head
Rob Herley 2024-09-20 17:23:43 -04:00
parent 6dd369c0e6
commit 7f19a7886a
No known key found for this signature in database
GPG Key ID: D1602042C3543B06
7 changed files with 83 additions and 20 deletions

View File

@ -1,5 +1,9 @@
# @actions/artifact Releases # @actions/artifact Releases
### 2.1.10
- Fixed a bug with symlinks not being automatically resolved.
### 2.1.9 ### 2.1.9
- Fixed artifact upload chunk timeout logic [#1774](https://github.com/actions/toolkit/pull/1774) - Fixed artifact upload chunk timeout logic [#1774](https://github.com/actions/toolkit/pull/1774)

View File

@ -27,9 +27,14 @@ jest.mock('@azure/storage-blob', () => ({
const fixtures = { const fixtures = {
uploadDirectory: path.join(__dirname, '_temp', 'plz-upload'), uploadDirectory: path.join(__dirname, '_temp', 'plz-upload'),
files: [ files: [
['file1.txt', 'test 1 file content'], {name: 'file1.txt', content: 'test 1 file content'},
['file2.txt', 'test 2 file content'], {name: 'file2.txt', content: 'test 2 file content'},
['file3.txt', 'test 3 file content'] {name: 'file3.txt', content: 'test 3 file content'},
{
name: 'from_symlink.txt',
content: 'from a symlink',
symlink: '../symlinked.txt'
}
], ],
backendIDs: { backendIDs: {
workflowRunBackendId: '67dbcc20-e851-4452-a7c3-2cc0d2e0ec67', workflowRunBackendId: '67dbcc20-e851-4452-a7c3-2cc0d2e0ec67',
@ -54,8 +59,23 @@ describe('upload-artifact', () => {
fs.mkdirSync(fixtures.uploadDirectory, {recursive: true}) fs.mkdirSync(fixtures.uploadDirectory, {recursive: true})
} }
for (const [file, content] of fixtures.files) { for (const file of fixtures.files) {
fs.writeFileSync(path.join(fixtures.uploadDirectory, file), content) if (file.symlink) {
const symlinkPath = path.join(fixtures.uploadDirectory, file.symlink)
fs.writeFileSync(symlinkPath, file.content)
if (!fs.existsSync(path.join(fixtures.uploadDirectory, file.name))) {
fs.symlinkSync(
symlinkPath,
path.join(fixtures.uploadDirectory, file.name),
'file'
)
}
} else {
fs.writeFileSync(
path.join(fixtures.uploadDirectory, file.name),
file.content
)
}
} }
}) })
@ -71,8 +91,9 @@ describe('upload-artifact', () => {
.spyOn(uploadZipSpecification, 'getUploadZipSpecification') .spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockReturnValue( .mockReturnValue(
fixtures.files.map(file => ({ fixtures.files.map(file => ({
sourcePath: path.join(fixtures.uploadDirectory, file[0]), sourcePath: path.join(fixtures.uploadDirectory, file.name),
destinationPath: file[0] destinationPath: file.name,
stats: new fs.Stats()
})) }))
) )
jest.spyOn(config, 'getRuntimeToken').mockReturnValue(fixtures.runtimeToken) jest.spyOn(config, 'getRuntimeToken').mockReturnValue(fixtures.runtimeToken)
@ -185,6 +206,10 @@ describe('upload-artifact', () => {
}) })
it('should successfully upload an artifact', async () => { it('should successfully upload an artifact', async () => {
jest
.spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockRestore()
jest jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact') .spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue( .mockReturnValue(
@ -228,8 +253,10 @@ describe('upload-artifact', () => {
const {id, size} = await uploadArtifact( const {id, size} = await uploadArtifact(
fixtures.inputs.artifactName, fixtures.inputs.artifactName,
fixtures.inputs.files, fixtures.files.map(file =>
fixtures.inputs.rootDirectory path.join(fixtures.uploadDirectory, file.name)
),
fixtures.uploadDirectory
) )
expect(id).toBe(1) expect(id).toBe(1)

View File

@ -305,4 +305,20 @@ describe('Search', () => {
} }
} }
}) })
it('Upload Specification - Includes symlinks', async () => {
const targetPath = path.join(root, 'link-dir', 'symlink-me.txt')
await fs.mkdir(path.dirname(targetPath), {recursive: true})
await fs.writeFile(targetPath, 'symlink file content')
const uploadPath = path.join(root, 'upload-dir', 'symlink.txt')
await fs.mkdir(path.dirname(uploadPath), {recursive: true})
await fs.symlink(targetPath, uploadPath, 'file')
const specifications = getUploadZipSpecification([uploadPath], root)
expect(specifications.length).toEqual(1)
expect(specifications[0].sourcePath).toEqual(uploadPath)
expect(specifications[0].destinationPath).toEqual('/upload-dir/symlink.txt')
expect(specifications[0].stats.isSymbolicLink()).toBe(true)
})
}) })

View File

@ -1,6 +1,6 @@
{ {
"name": "@actions/artifact", "name": "@actions/artifact",
"version": "2.1.9", "version": "2.1.10",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
@ -1315,9 +1315,9 @@
} }
}, },
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "6.2.1", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
"integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="
}, },
"node_modules/prettier": { "node_modules/prettier": {
"version": "2.8.8", "version": "2.8.8",

View File

@ -1,6 +1,6 @@
{ {
"name": "@actions/artifact", "name": "@actions/artifact",
"version": "2.1.9", "version": "2.1.10",
"preview": true, "preview": true,
"description": "Actions artifact lib", "description": "Actions artifact lib",
"keywords": [ "keywords": [

View File

@ -13,6 +13,12 @@ export interface UploadZipSpecification {
* 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
* https://nodejs.org/api/fs.html#class-fsstats
*/
stats: fs.Stats
} }
/** /**
@ -75,10 +81,11 @@ export function getUploadZipSpecification(
- file3.txt - file3.txt
*/ */
for (let file of filesToZip) { for (let file of filesToZip) {
if (!fs.existsSync(file)) { const stats = fs.lstatSync(file, {throwIfNoEntry: false})
if (!stats) {
throw new Error(`File ${file} does not exist`) throw new Error(`File ${file} does not exist`)
} }
if (!fs.statSync(file).isDirectory()) { if (!stats.isDirectory()) {
// Normalize and resolve, this allows for either absolute or relative paths to be used // Normalize and resolve, this allows for either absolute or relative paths to be used
file = normalize(file) file = normalize(file)
file = resolve(file) file = resolve(file)
@ -94,7 +101,8 @@ export function getUploadZipSpecification(
specification.push({ specification.push({
sourcePath: file, sourcePath: file,
destinationPath: uploadPath destinationPath: uploadPath,
stats
}) })
} else { } else {
// Empty directory // Empty directory
@ -103,7 +111,8 @@ export function getUploadZipSpecification(
specification.push({ specification.push({
sourcePath: null, sourcePath: null,
destinationPath: directoryPath destinationPath: directoryPath,
stats
}) })
} }
} }

View File

@ -1,4 +1,5 @@
import * as stream from 'stream' import * as stream from 'stream'
import {readlink} from 'fs/promises'
import * as archiver from 'archiver' import * as archiver from 'archiver'
import * as core from '@actions/core' import * as core from '@actions/core'
import {UploadZipSpecification} from './upload-zip-specification' import {UploadZipSpecification} from './upload-zip-specification'
@ -42,8 +43,14 @@ export async function createZipUploadStream(
for (const file of uploadSpecification) { for (const file of uploadSpecification) {
if (file.sourcePath !== null) { if (file.sourcePath !== null) {
// Add a normal file to the zip // Check if symlink and resolve the source path
zip.file(file.sourcePath, { let sourcePath = file.sourcePath
if (file.stats.isSymbolicLink()) {
sourcePath = await readlink(file.sourcePath)
}
// Add the file to the zip
zip.file(sourcePath, {
name: file.destinationPath name: file.destinationPath
}) })
} else { } else {