diff --git a/packages/cache/__tests__/tar.test.ts b/packages/cache/__tests__/tar.test.ts index f1792217..36c0a38d 100644 --- a/packages/cache/__tests__/tar.test.ts +++ b/packages/cache/__tests__/tar.test.ts @@ -18,7 +18,7 @@ jest.mock('@actions/io') const IS_WINDOWS = process.platform === 'win32' const IS_MAC = process.platform === 'darwin' -const defaultTarPath = process.platform === 'darwin' ? 'gtar' : 'tar' +const defaultTarPath = IS_MAC ? 'gtar' : 'tar' function getTempDir(): string { return path.join(__dirname, '_temp', 'tar') @@ -75,6 +75,41 @@ test('zstd extract tar', async () => { ) }) +test('zstd extract tar with windows BSDtar', async () => { + if (IS_WINDOWS) { + const mkdirMock = jest.spyOn(io, 'mkdirP') + const execMock = jest.spyOn(exec, 'exec') + jest + .spyOn(utils, 'getGnuTarPathOnWindows') + .mockReturnValue(Promise.resolve('')) + + const archivePath = `${process.env['windir']}\\fakepath\\cache.tar` + const workspace = process.env['GITHUB_WORKSPACE'] + const tarPath = SystemTarPathOnWindows + const tarFilename = 'cache.tar' + + await tar.extractTar(archivePath, CompressionMethod.Zstd) + + expect(mkdirMock).toHaveBeenCalledWith(workspace) + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + 'zstd -d --long=30 -o', + [ + tarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '&&', + `"${tarPath}"`, + '-xf', + tarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P', + '-C', + workspace?.replace(/\\/g, '/') + ], + {cwd: undefined} + ) + } +}) + test('gzip extract tar', async () => { const mkdirMock = jest.spyOn(io, 'mkdirP') const execMock = jest.spyOn(exec, 'exec') @@ -107,7 +142,7 @@ test('gzip extract tar', async () => { test('gzip extract GNU tar on windows with GNUtar in path', async () => { if (IS_WINDOWS) { // GNU tar present in path but not at default location - const isGnuMock = jest + jest .spyOn(utils, 'getGnuTarPathOnWindows') .mockReturnValue(Promise.resolve('tar')) const execMock = jest.spyOn(exec, 'exec') @@ -116,7 +151,6 @@ test('gzip extract GNU tar on windows with GNUtar in path', async () => { await tar.extractTar(archivePath, CompressionMethod.Gzip) - expect(isGnuMock).toHaveBeenCalledTimes(2) expect(execMock).toHaveBeenCalledTimes(1) expect(execMock).toHaveBeenCalledWith( `"tar"`, @@ -177,7 +211,7 @@ test('zstd create tar', async () => { test('zstd create tar with windows BSDtar', async () => { if (IS_WINDOWS) { const execMock = jest.spyOn(exec, 'exec') - const isGnuMock = jest + jest .spyOn(utils, 'getGnuTarPathOnWindows') .mockReturnValue(Promise.resolve('')) @@ -196,7 +230,6 @@ test('zstd create tar with windows BSDtar', async () => { const tarPath = SystemTarPathOnWindows - expect(isGnuMock).toHaveBeenCalledTimes(2) expect(execMock).toHaveBeenCalledTimes(1) expect(execMock).toHaveBeenCalledWith( `"${tarPath}"`, @@ -284,6 +317,35 @@ test('zstd list tar', async () => { ) }) +test('zstd list tar with windows BSDtar', async () => { + if (IS_WINDOWS) { + const execMock = jest.spyOn(exec, 'exec') + jest + .spyOn(utils, 'getGnuTarPathOnWindows') + .mockReturnValue(Promise.resolve('')) + const archivePath = `${process.env['windir']}\\fakepath\\cache.tar` + + await tar.listTar(archivePath, CompressionMethod.Zstd) + + const tarFilename = 'cache.tar' + const tarPath = SystemTarPathOnWindows + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + 'zstd -d --long=30 -o', + [ + tarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '&&', + `"${tarPath}"`, + '-tf', + tarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P' + ], + {cwd: undefined} + ) + } +}) + test('zstdWithoutLong list tar', async () => { const execMock = jest.spyOn(exec, 'exec') diff --git a/packages/cache/src/internal/tar.ts b/packages/cache/src/internal/tar.ts index 8647fdc0..27fcf1ca 100644 --- a/packages/cache/src/internal/tar.ts +++ b/packages/cache/src/internal/tar.ts @@ -46,7 +46,9 @@ async function getTarArgs( const tarFile = 'cache.tar' const tarPath = await getTarPath() const workingDirectory = getWorkingDirectory() - const BSD_TAR_WINDOWS = IS_WINDOWS && tarPath === SystemTarPathOnWindows + const BSD_TAR_ZSTD = + tarPath === SystemTarPathOnWindows && + compressionMethod !== CompressionMethod.Gzip // Method specific args switch (type) { @@ -54,11 +56,11 @@ async function getTarArgs( args.push( '--posix', '-cf', - BSD_TAR_WINDOWS + BSD_TAR_ZSTD ? tarFile : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--exclude', - BSD_TAR_WINDOWS + BSD_TAR_ZSTD ? tarFile : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', @@ -71,7 +73,7 @@ async function getTarArgs( case 'extract': args.push( '-xf', - BSD_TAR_WINDOWS + BSD_TAR_ZSTD ? tarFile : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', @@ -79,11 +81,10 @@ async function getTarArgs( workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/') ) break - // TODO: Correct the below code especially archivePath for BSD_TAR_WINDOWS case 'list': args.push( '-tf', - BSD_TAR_WINDOWS + BSD_TAR_ZSTD ? tarFile : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P' @@ -149,31 +150,31 @@ async function getCompressionProgram( const tarPath = await getTarPath() const cacheFileName = utils.getCacheFileName(compressionMethod) const tarFile = 'cache.tar' - const BSD_TAR_WINDOWS = IS_WINDOWS && tarPath === SystemTarPathOnWindows + const BSD_TAR_ZSTD = + tarPath === SystemTarPathOnWindows && + compressionMethod !== CompressionMethod.Gzip switch (compressionMethod) { case CompressionMethod.Zstd: - if (BSD_TAR_WINDOWS) { - return [ - 'zstd -d --long=30 -o', - tarFile, - cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), - '&&' - ] - } - return [ - '--use-compress-program', - IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30' - ] + return BSD_TAR_ZSTD + ? [ + 'zstd -d --long=30 -o', + tarFile, + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '&&' + ] + : [ + '--use-compress-program', + IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30' + ] case CompressionMethod.ZstdWithoutLong: - if (BSD_TAR_WINDOWS) { - return [ - 'zstd -d -o', - tarFile, - cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), - '&&' - ] - } - return ['--use-compress-program', IS_WINDOWS ? 'zstd -d' : 'unzstd'] + return BSD_TAR_ZSTD + ? [ + 'zstd -d -o', + tarFile, + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '&&' + ] + : ['--use-compress-program', IS_WINDOWS ? 'zstd -d' : 'unzstd'] default: return ['-z'] } @@ -184,13 +185,15 @@ export async function listTar( compressionMethod: CompressionMethod ): Promise { const tarPath = await getTarPath() - const BSD_TAR_WINDOWS = IS_WINDOWS && tarPath === SystemTarPathOnWindows + const BSD_TAR_ZSTD = + tarPath === SystemTarPathOnWindows && + compressionMethod !== CompressionMethod.Gzip const compressionArgs = await getCompressionProgram(compressionMethod) const tarArgs = await getTarArgs(compressionMethod, 'list', archivePath) // TODO: Add a test for BSD tar on windows - if (BSD_TAR_WINDOWS) { + if (BSD_TAR_ZSTD) { const command = compressionArgs[0] - const args = compressionArgs.slice(1).concat(tarArgs) + const args = compressionArgs.slice(1).concat([tarPath]).concat(tarArgs) await execCommand(command, args) } else { const args = tarArgs.concat(compressionArgs) @@ -205,13 +208,15 @@ export async function extractTar( // Create directory to extract tar into const workingDirectory = getWorkingDirectory() const tarPath = await getTarPath() - const BSD_TAR_WINDOWS = IS_WINDOWS && tarPath === SystemTarPathOnWindows + const BSD_TAR_ZSTD = + tarPath === SystemTarPathOnWindows && + compressionMethod !== CompressionMethod.Gzip await io.mkdirP(workingDirectory) const tarArgs = await getTarArgs(compressionMethod, 'extract', archivePath) const compressionArgs = await getCompressionProgram(compressionMethod) - if (BSD_TAR_WINDOWS) { + if (BSD_TAR_ZSTD) { const command = compressionArgs[0] - const args = compressionArgs.slice(1).concat(tarArgs) + const args = compressionArgs.slice(1).concat([tarPath]).concat(tarArgs) await execCommand(command, args) } else { const args = tarArgs.concat(compressionArgs) @@ -229,12 +234,13 @@ export async function createTar( const cacheFileName = utils.getCacheFileName(compressionMethod) const tarFile = 'cache.tar' const tarPath = await getTarPath() - const BSD_TAR_WINDOWS = IS_WINDOWS && tarPath === SystemTarPathOnWindows + const BSD_TAR_ZSTD = + tarPath === SystemTarPathOnWindows && + compressionMethod !== CompressionMethod.Gzip writeFileSync( path.join(archiveFolder, manifestFilename), sourceDirectories.join('\n') ) - const workingDirectory = getWorkingDirectory() // -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores. // zstdmt is equivalent to 'zstd -T0' @@ -244,28 +250,26 @@ export async function createTar( function getCompressionProgram(): string[] { switch (compressionMethod) { case CompressionMethod.Zstd: - if (BSD_TAR_WINDOWS) { - return [ - '&&', - 'zstd -T0 --long=30 -o', - cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), - tarFile - ] - } - return [ - '--use-compress-program', - IS_WINDOWS ? 'zstd -T0 --long=30' : 'zstdmt --long=30' - ] + return BSD_TAR_ZSTD + ? [ + '&&', + 'zstd -T0 --long=30 -o', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + tarFile + ] + : [ + '--use-compress-program', + IS_WINDOWS ? 'zstd -T0 --long=30' : 'zstdmt --long=30' + ] case CompressionMethod.ZstdWithoutLong: - if (BSD_TAR_WINDOWS) { - return [ - '&&', - 'zstd -T0 -o', - cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), - tarFile - ] - } - return ['--use-compress-program', IS_WINDOWS ? 'zstd -T0' : 'zstdmt'] + return BSD_TAR_ZSTD + ? [ + '&&', + 'zstd -T0 -o', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + tarFile + ] + : ['--use-compress-program', IS_WINDOWS ? 'zstd -T0' : 'zstdmt'] default: return ['-z'] }