mirror of https://github.com/actions/toolkit
Merge pull request #1844 from actions/robherley/artifact-2.1.11
Properly resolve relative symlinkspull/1845/head
commit
201b082ce1
|
@ -1,5 +1,9 @@
|
||||||
# @actions/artifact Releases
|
# @actions/artifact Releases
|
||||||
|
|
||||||
|
### 2.1.11
|
||||||
|
|
||||||
|
- Fixed a bug with relative symlinks resolution [#1844](https://github.com/actions/toolkit/pull/1844)
|
||||||
|
|
||||||
### 2.1.10
|
### 2.1.10
|
||||||
|
|
||||||
- Fixed a regression with symlinks not being automatically resolved [#1830](https://github.com/actions/toolkit/pull/1830)
|
- Fixed a regression with symlinks not being automatically resolved [#1830](https://github.com/actions/toolkit/pull/1830)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {FilesNotFoundError} from '../src/internal/shared/errors'
|
||||||
import {BlockBlobUploadStreamOptions} from '@azure/storage-blob'
|
import {BlockBlobUploadStreamOptions} from '@azure/storage-blob'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
|
import unzip from 'unzip-stream'
|
||||||
|
|
||||||
const uploadStreamMock = jest.fn()
|
const uploadStreamMock = jest.fn()
|
||||||
const blockBlobClientMock = jest.fn().mockImplementation(() => ({
|
const blockBlobClientMock = jest.fn().mockImplementation(() => ({
|
||||||
|
@ -31,9 +32,20 @@ const fixtures = {
|
||||||
{name: 'file2.txt', content: 'test 2 file content'},
|
{name: 'file2.txt', content: 'test 2 file content'},
|
||||||
{name: 'file3.txt', content: 'test 3 file content'},
|
{name: 'file3.txt', content: 'test 3 file content'},
|
||||||
{
|
{
|
||||||
name: 'from_symlink.txt',
|
name: 'real.txt',
|
||||||
|
content: 'from a symlink'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'relative.txt',
|
||||||
content: 'from a symlink',
|
content: 'from a symlink',
|
||||||
symlink: '../symlinked.txt'
|
symlink: 'real.txt',
|
||||||
|
relative: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'absolute.txt',
|
||||||
|
content: 'from a symlink',
|
||||||
|
symlink: 'real.txt',
|
||||||
|
relative: false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
backendIDs: {
|
backendIDs: {
|
||||||
|
@ -55,14 +67,17 @@ const fixtures = {
|
||||||
|
|
||||||
describe('upload-artifact', () => {
|
describe('upload-artifact', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
if (!fs.existsSync(fixtures.uploadDirectory)) {
|
fs.mkdirSync(fixtures.uploadDirectory, {
|
||||||
fs.mkdirSync(fixtures.uploadDirectory, {recursive: true})
|
recursive: true
|
||||||
}
|
})
|
||||||
|
|
||||||
for (const file of fixtures.files) {
|
for (const file of fixtures.files) {
|
||||||
if (file.symlink) {
|
if (file.symlink) {
|
||||||
const symlinkPath = path.join(fixtures.uploadDirectory, file.symlink)
|
let symlinkPath = file.symlink
|
||||||
fs.writeFileSync(symlinkPath, file.content)
|
if (!file.relative) {
|
||||||
|
symlinkPath = path.join(fixtures.uploadDirectory, file.symlink)
|
||||||
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(path.join(fixtures.uploadDirectory, file.name))) {
|
if (!fs.existsSync(path.join(fixtures.uploadDirectory, file.name))) {
|
||||||
fs.symlinkSync(
|
fs.symlinkSync(
|
||||||
symlinkPath,
|
symlinkPath,
|
||||||
|
@ -227,6 +242,12 @@ describe('upload-artifact', () => {
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let loadedBytes = 0
|
||||||
|
const uploadedZip = path.join(
|
||||||
|
fixtures.uploadDirectory,
|
||||||
|
'..',
|
||||||
|
'uploaded.zip'
|
||||||
|
)
|
||||||
uploadStreamMock.mockImplementation(
|
uploadStreamMock.mockImplementation(
|
||||||
async (
|
async (
|
||||||
stream: NodeJS.ReadableStream,
|
stream: NodeJS.ReadableStream,
|
||||||
|
@ -234,19 +255,28 @@ describe('upload-artifact', () => {
|
||||||
maxConcurrency?: number,
|
maxConcurrency?: number,
|
||||||
options?: BlockBlobUploadStreamOptions
|
options?: BlockBlobUploadStreamOptions
|
||||||
) => {
|
) => {
|
||||||
const {onProgress, abortSignal} = options || {}
|
const {onProgress} = options || {}
|
||||||
|
|
||||||
|
if (fs.existsSync(uploadedZip)) {
|
||||||
|
fs.unlinkSync(uploadedZip)
|
||||||
|
}
|
||||||
|
const uploadedZipStream = fs.createWriteStream(uploadedZip)
|
||||||
|
|
||||||
onProgress?.({loadedBytes: 0})
|
onProgress?.({loadedBytes: 0})
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
return new Promise(resolve => {
|
stream.on('data', chunk => {
|
||||||
const timerId = setTimeout(() => {
|
loadedBytes += chunk.length
|
||||||
onProgress?.({loadedBytes: 256})
|
uploadedZipStream.write(chunk)
|
||||||
resolve({})
|
onProgress?.({loadedBytes})
|
||||||
}, 1_000)
|
})
|
||||||
abortSignal?.addEventListener('abort', () => {
|
stream.on('end', () => {
|
||||||
clearTimeout(timerId)
|
onProgress?.({loadedBytes})
|
||||||
|
uploadedZipStream.end()
|
||||||
resolve({})
|
resolve({})
|
||||||
})
|
})
|
||||||
|
stream.on('error', err => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -260,7 +290,34 @@ describe('upload-artifact', () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(id).toBe(1)
|
expect(id).toBe(1)
|
||||||
expect(size).toBe(256)
|
expect(size).toBe(loadedBytes)
|
||||||
|
|
||||||
|
const extractedDirectory = path.join(
|
||||||
|
fixtures.uploadDirectory,
|
||||||
|
'..',
|
||||||
|
'extracted'
|
||||||
|
)
|
||||||
|
if (fs.existsSync(extractedDirectory)) {
|
||||||
|
fs.rmdirSync(extractedDirectory, {recursive: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
const extract = new Promise((resolve, reject) => {
|
||||||
|
fs.createReadStream(uploadedZip)
|
||||||
|
.pipe(unzip.Extract({path: extractedDirectory}))
|
||||||
|
.on('close', () => {
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
.on('error', err => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(extract).resolves.toBe(true)
|
||||||
|
for (const file of fixtures.files) {
|
||||||
|
const filePath = path.join(extractedDirectory, file.name)
|
||||||
|
expect(fs.existsSync(filePath)).toBe(true)
|
||||||
|
expect(fs.readFileSync(filePath, 'utf8')).toBe(file.content)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should throw an error uploading blob chunks get delayed', async () => {
|
it('should throw an error uploading blob chunks get delayed', async () => {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "@actions/artifact",
|
"name": "@actions/artifact",
|
||||||
"version": "2.1.10",
|
"version": "2.1.11",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@actions/artifact",
|
"name": "@actions/artifact",
|
||||||
"version": "2.1.10",
|
"version": "2.1.11",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.0",
|
"@actions/core": "^1.10.0",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@actions/artifact",
|
"name": "@actions/artifact",
|
||||||
"version": "2.1.10",
|
"version": "2.1.11",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"description": "Actions artifact lib",
|
"description": "Actions artifact lib",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as stream from 'stream'
|
import * as stream from 'stream'
|
||||||
import {readlink} from 'fs/promises'
|
import {realpath} 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'
|
||||||
|
@ -46,7 +46,7 @@ export async function createZipUploadStream(
|
||||||
// Check if symlink and resolve the source path
|
// Check if symlink and resolve the source path
|
||||||
let sourcePath = file.sourcePath
|
let sourcePath = file.sourcePath
|
||||||
if (file.stats.isSymbolicLink()) {
|
if (file.stats.isSymbolicLink()) {
|
||||||
sourcePath = await readlink(file.sourcePath)
|
sourcePath = await realpath(file.sourcePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the file to the zip
|
// Add the file to the zip
|
||||||
|
|
Loading…
Reference in New Issue