diff --git a/__tests__/tar.test.ts b/__tests__/tar.test.ts index f85a51d..ed4ab42 100644 --- a/__tests__/tar.test.ts +++ b/__tests__/tar.test.ts @@ -84,56 +84,57 @@ test('zstd extract tar', async () => { ) }) -test.each([ - {operation: 'extract', read: tar.extractTar, flag: '-xf'}, - {operation: 'list', read: tar.listTar, flag: '-tf'} -])( - 'zstd $operation tar with windows BSDtar', - async ({operation, read, flag}) => { - if (!IS_WINDOWS) { - return - } +test('zstd extract tar with windows BSDtar', async () => { + if (IS_WINDOWS) { const mkdirMock = jest.spyOn(io, 'mkdirP') const execMock = jest.spyOn(exec, 'exec') const tempDirectory = getTempDir() jest.spyOn(utils, 'createTempDirectory').mockResolvedValue(tempDirectory) - jest.spyOn(utils, 'getGnuTarPathOnWindows').mockResolvedValue('') + jest + .spyOn(utils, 'getGnuTarPathOnWindows') + .mockReturnValue(Promise.resolve('')) const archivePath = `${process.env['windir']}\\fakepath\\cache.tar` const workspace = process.env['GITHUB_WORKSPACE'] - const options = { - cwd: tempDirectory, - env: expect.objectContaining(defaultEnv) - } - const tarArgs = [`"${SystemTarPathOnWindows}"`, flag, TarFilename, '-P'] - if (operation === 'extract') { - tarArgs.push('-C', `"${workspace?.replace(/\\/g, '/')}"`) - } + const tarPath = SystemTarPathOnWindows - await read(archivePath, CompressionMethod.Zstd) + await tar.extractTar(archivePath, CompressionMethod.Zstd) - if (operation === 'extract') { - expect(mkdirMock).toHaveBeenCalledWith(workspace) - } + expect(mkdirMock).toHaveBeenCalledWith(workspace) expect(execMock).toHaveBeenCalledTimes(2) + expect(execMock).toHaveBeenNthCalledWith( 1, [ 'zstd -d --long=30 --force -o', - TarFilename, - `"${archivePath.replace(/\\/g, '/')}"` + TarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + `"${archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/')}"` ].join(' '), undefined, - options + { + cwd: tempDirectory, + env: expect.objectContaining(defaultEnv) + } ) + expect(execMock).toHaveBeenNthCalledWith( 2, - tarArgs.join(' '), + [ + `"${tarPath}"`, + '-xf', + TarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P', + '-C', + `"${workspace?.replace(/\\/g, '/')}"` + ].join(' '), undefined, - options + { + cwd: tempDirectory, + env: expect.objectContaining(defaultEnv) + } ) } -) +}) test('gzip extract tar', async () => { const mkdirMock = jest.spyOn(io, 'mkdirP') @@ -279,7 +280,7 @@ test('zstd create tar with windows BSDtar', async () => { TarFilename.replace(/\\/g, '/'), '-P', '-C', - `"${workspace?.replace(/\\/g, '/')}"`, + workspace?.replace(/\\/g, '/'), '--files-from', ManifestFilename ].join(' '), @@ -379,6 +380,52 @@ test('zstd list tar', async () => { ) }) +test('zstd list tar with windows BSDtar', async () => { + if (IS_WINDOWS) { + const execMock = jest.spyOn(exec, 'exec') + const tempDirectory = getTempDir() + jest.spyOn(utils, 'createTempDirectory').mockResolvedValue(tempDirectory) + jest + .spyOn(utils, 'getGnuTarPathOnWindows') + .mockReturnValue(Promise.resolve('')) + const archivePath = `${process.env['windir']}\\fakepath\\cache.tar` + + await tar.listTar(archivePath, CompressionMethod.Zstd) + + const tarPath = SystemTarPathOnWindows + expect(execMock).toHaveBeenCalledTimes(2) + + expect(execMock).toHaveBeenNthCalledWith( + 1, + [ + 'zstd -d --long=30 --force -o', + TarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + `"${archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/')}"` + ].join(' '), + undefined, + { + cwd: tempDirectory, + env: expect.objectContaining(defaultEnv) + } + ) + + expect(execMock).toHaveBeenNthCalledWith( + 2, + [ + `"${tarPath}"`, + '-tf', + TarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P' + ].join(' '), + undefined, + { + cwd: tempDirectory, + env: expect.objectContaining(defaultEnv) + } + ) + } +}) + test('zstdWithoutLong list tar', async () => { const execMock = jest.spyOn(exec, 'exec') diff --git a/src/internal/tar.ts b/src/internal/tar.ts index 1e80329..e3334df 100644 --- a/src/internal/tar.ts +++ b/src/internal/tar.ts @@ -19,17 +19,6 @@ interface TarCommands { requiresTempDirectory: boolean } -function isBsdTarZstd( - tarPath: ArchiveTool, - compressionMethod: CompressionMethod -): boolean { - return ( - IS_WINDOWS && - tarPath.type === ArchiveToolType.BSD && - compressionMethod !== CompressionMethod.Gzip - ) -} - // Returns tar path and type: BSD or GNU async function getTarPath(): Promise { switch (process.platform) { @@ -74,16 +63,14 @@ async function getTarArgs( archivePath = '' ): Promise { const args = [`"${tarPath.path}"`] - const BSD_TAR_ZSTD = isBsdTarZstd(tarPath, compressionMethod) - const tarFile = BSD_TAR_ZSTD - ? TarFilename - : (type === 'create' - ? utils.getCacheFileName(compressionMethod) - : archivePath - ).replace(new RegExp(`\\${path.sep}`, 'g'), '/') - const workingDirectory = BSD_TAR_ZSTD - ? quoteAbsolutePath(getWorkingDirectory()) - : getWorkingDirectory().replace(new RegExp(`\\${path.sep}`, 'g'), '/') + const cacheFileName = utils.getCacheFileName(compressionMethod) + const tarFile = 'cache.tar' + const workingDirectory = getWorkingDirectory() + // Speficic args for BSD tar on windows for workaround + const BSD_TAR_ZSTD = + tarPath.type === ArchiveToolType.BSD && + compressionMethod !== CompressionMethod.Gzip && + IS_WINDOWS // Method specific args switch (type) { @@ -91,21 +78,41 @@ async function getTarArgs( args.push( '--posix', '-cf', - tarFile, + BSD_TAR_ZSTD + ? tarFile + : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--exclude', - tarFile, + BSD_TAR_ZSTD + ? tarFile + : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', - workingDirectory, + workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--files-from', ManifestFilename ) break case 'extract': - args.push('-xf', tarFile, '-P', '-C', workingDirectory) + args.push( + '-xf', + BSD_TAR_ZSTD + ? tarFile + : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P', + '-C', + BSD_TAR_ZSTD + ? quoteAbsolutePath(workingDirectory) + : workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ) break case 'list': - args.push('-tf', tarFile, '-P') + args.push( + '-tf', + BSD_TAR_ZSTD + ? tarFile + : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P' + ) break } @@ -143,7 +150,10 @@ async function getCommands( type !== 'create' ? await getDecompressionProgram(tarPath, compressionMethod, archivePath) : await getCompressionProgram(tarPath, compressionMethod) - const BSD_TAR_ZSTD = isBsdTarZstd(tarPath, compressionMethod) + const BSD_TAR_ZSTD = + tarPath.type === ArchiveToolType.BSD && + compressionMethod !== CompressionMethod.Gzip && + IS_WINDOWS if (BSD_TAR_ZSTD && type !== 'create') { args = [[...compressionArgs].join(' '), [...tarArgs].join(' ')] @@ -175,7 +185,10 @@ async function getDecompressionProgram( // unzstd is equivalent to 'zstd -d' // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. // Using 30 here because we also support 32-bit self-hosted runners. - const BSD_TAR_ZSTD = isBsdTarZstd(tarPath, compressionMethod) + const BSD_TAR_ZSTD = + tarPath.type === ArchiveToolType.BSD && + compressionMethod !== CompressionMethod.Gzip && + IS_WINDOWS switch (compressionMethod) { case CompressionMethod.Zstd: return BSD_TAR_ZSTD @@ -208,7 +221,10 @@ async function getCompressionProgram( compressionMethod: CompressionMethod ): Promise { const cacheFileName = utils.getCacheFileName(compressionMethod) - const BSD_TAR_ZSTD = isBsdTarZstd(tarPath, compressionMethod) + const BSD_TAR_ZSTD = + tarPath.type === ArchiveToolType.BSD && + compressionMethod !== CompressionMethod.Gzip && + IS_WINDOWS switch (compressionMethod) { case CompressionMethod.Zstd: return BSD_TAR_ZSTD