Compare commits

...

19 Commits

Author SHA1 Message Date
Bishal Prasad 44cd5d150b args map 2022-11-24 08:50:57 +00:00
Sampark Sharma 0822441ee0
Fix lint test 2022-11-23 11:51:46 +00:00
Sampark Sharma 187781e273
Fix tests 2022-11-23 11:40:11 +00:00
Sampark Sharma 1f3371766a
Add new tests 2022-11-23 11:20:24 +00:00
Sampark Sharma 39b7a86b23
Fix old tests 2022-11-23 07:58:46 +00:00
Sampark Sharma 2f73afa843
Separate args 2022-11-23 07:39:15 +00:00
Sampark Sharma ffde3e4bd5
Fix test 2022-11-22 07:33:01 +00:00
Sampark Sharma 32f538163d
Fix windows test 2022-11-21 12:51:07 +00:00
Sampark Sharma 32b95825ba
Add windows bsdtar test 2022-11-21 12:19:44 +00:00
Sampark Sharma 54eb9b8055
Address some comments and correct compression commands 2022-11-21 12:05:03 +00:00
Lovepreet Singh f9dfb05bd2 Temporarily remove thhe condition that prevents zstd usage on windows unless with GNUtar 2022-11-17 12:13:49 +00:00
Lovepreet Singh b3bd482c0f Fix windows gnutar test case 2022-11-17 09:46:01 +00:00
Lovepreet Singh 4fa5b7d133 Fix lint issues 2022-11-17 09:12:53 +00:00
Lovepreet Singh ea9856079f Fix tar tests 2022-11-17 08:53:46 +00:00
Lovepreet Singh 5f89653f1b Fix order of args for tar 2022-11-17 06:22:13 +00:00
Lovepreet Singh e9e146bbf8 Add -v option for testing 2022-11-16 21:04:25 +00:00
Lovepreet Singh 27bf8304ea Fix tar operations 2022-11-16 20:50:04 +00:00
Lovepreet Singh db3517fe3b bsd + zstd fallback implementation 2022-11-16 20:21:40 +00:00
Lovepreet Singh 6349c3ca3a bsd + zstd fallback implementation 2022-11-16 20:18:31 +00:00
4 changed files with 453 additions and 92 deletions

View File

@ -4,7 +4,8 @@ import * as path from 'path'
import {
CacheFilename,
CompressionMethod,
GnuTarPathOnWindows
GnuTarPathOnWindows,
SystemTarPathOnWindows
} from '../src/internal/constants'
import * as tar from '../src/internal/tar'
import * as utils from '../src/internal/cacheUtils'
@ -17,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')
@ -58,8 +59,6 @@ test('zstd extract tar', async () => {
expect(execMock).toHaveBeenCalledWith(
`"${tarPath}"`,
[
'--use-compress-program',
IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30',
'-xf',
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
'-P',
@ -67,11 +66,50 @@ test('zstd extract tar', async () => {
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace
]
.concat(IS_WINDOWS ? ['--force-local'] : [])
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
.concat([
'--use-compress-program',
IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30'
]),
{cwd: undefined}
)
})
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')
@ -88,7 +126,6 @@ test('gzip extract tar', async () => {
expect(execMock).toHaveBeenCalledWith(
`"${tarPath}"`,
[
'-z',
'-xf',
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
'-P',
@ -96,7 +133,8 @@ test('gzip extract tar', async () => {
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace
]
.concat(IS_WINDOWS ? ['--force-local'] : [])
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
.concat(['-z']),
{cwd: undefined}
)
})
@ -104,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')
@ -113,18 +151,17 @@ test('gzip extract GNU tar on windows with GNUtar in path', async () => {
await tar.extractTar(archivePath, CompressionMethod.Gzip)
expect(isGnuMock).toHaveBeenCalledTimes(1)
expect(execMock).toHaveBeenCalledTimes(1)
expect(execMock).toHaveBeenCalledWith(
`"tar"`,
[
'-z',
'-xf',
archivePath.replace(/\\/g, '/'),
'-P',
'-C',
workspace?.replace(/\\/g, '/'),
'--force-local'
'--force-local',
'-z'
],
{cwd: undefined}
)
@ -149,8 +186,6 @@ test('zstd create tar', async () => {
`"${tarPath}"`,
[
'--posix',
'--use-compress-program',
IS_WINDOWS ? 'zstd -T0 --long=30' : 'zstdmt --long=30',
'-cf',
IS_WINDOWS ? CacheFilename.Zstd.replace(/\\/g, '/') : CacheFilename.Zstd,
'--exclude',
@ -162,13 +197,65 @@ test('zstd create tar', async () => {
'manifest.txt'
]
.concat(IS_WINDOWS ? ['--force-local'] : [])
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
.concat([
'--use-compress-program',
IS_WINDOWS ? 'zstd -T0 --long=30' : 'zstdmt --long=30'
]),
{
cwd: archiveFolder
}
)
})
test('zstd create tar with windows BSDtar', async () => {
if (IS_WINDOWS) {
const execMock = jest.spyOn(exec, 'exec')
jest
.spyOn(utils, 'getGnuTarPathOnWindows')
.mockReturnValue(Promise.resolve(''))
const archiveFolder = getTempDir()
const workspace = process.env['GITHUB_WORKSPACE']
const sourceDirectories = ['~/.npm/cache', `${workspace}/dist`]
const tarFilename = 'cache.tar'
await fs.promises.mkdir(archiveFolder, {recursive: true})
await tar.createTar(
archiveFolder,
sourceDirectories,
CompressionMethod.Zstd
)
const tarPath = SystemTarPathOnWindows
expect(execMock).toHaveBeenCalledTimes(1)
expect(execMock).toHaveBeenCalledWith(
`"${tarPath}"`,
[
'--posix',
'-cf',
tarFilename.replace(/\\/g, '/'),
'--exclude',
tarFilename.replace(/\\/g, '/'),
'-P',
'-C',
workspace?.replace(/\\/g, '/'),
'--files-from',
'manifest.txt',
'&&',
'zstd -T0 --long=30 -o',
CacheFilename.Zstd.replace(/\\/g, '/'),
tarFilename.replace(/\\/g, '/')
],
{
cwd: archiveFolder
}
)
}
})
test('gzip create tar', async () => {
const execMock = jest.spyOn(exec, 'exec')
@ -187,7 +274,6 @@ test('gzip create tar', async () => {
`"${tarPath}"`,
[
'--posix',
'-z',
'-cf',
IS_WINDOWS ? CacheFilename.Gzip.replace(/\\/g, '/') : CacheFilename.Gzip,
'--exclude',
@ -199,7 +285,8 @@ test('gzip create tar', async () => {
'manifest.txt'
]
.concat(IS_WINDOWS ? ['--force-local'] : [])
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
.concat(['-z']),
{
cwd: archiveFolder
}
@ -219,19 +306,46 @@ test('zstd list tar', async () => {
expect(execMock).toHaveBeenCalledTimes(1)
expect(execMock).toHaveBeenCalledWith(
`"${tarPath}"`,
[
'--use-compress-program',
IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30',
'-tf',
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
'-P'
]
['-tf', IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, '-P']
.concat(IS_WINDOWS ? ['--force-local'] : [])
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
.concat([
'--use-compress-program',
IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30'
]),
{cwd: undefined}
)
})
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')
@ -245,15 +359,10 @@ test('zstdWithoutLong list tar', async () => {
expect(execMock).toHaveBeenCalledTimes(1)
expect(execMock).toHaveBeenCalledWith(
`"${tarPath}"`,
[
'--use-compress-program',
IS_WINDOWS ? 'zstd -d' : 'unzstd',
'-tf',
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
'-P'
]
['-tf', IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, '-P']
.concat(IS_WINDOWS ? ['--force-local'] : [])
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
.concat(['--use-compress-program', IS_WINDOWS ? 'zstd -d' : 'unzstd']),
{cwd: undefined}
)
})
@ -270,14 +379,10 @@ test('gzip list tar', async () => {
expect(execMock).toHaveBeenCalledTimes(1)
expect(execMock).toHaveBeenCalledWith(
`"${tarPath}"`,
[
'-z',
'-tf',
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
'-P'
]
['-tf', IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, '-P']
.concat(IS_WINDOWS ? ['--force-local'] : [])
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
.concat(['-z']),
{cwd: undefined}
)
})

View File

@ -71,7 +71,7 @@ export async function unlinkFile(filePath: fs.PathLike): Promise<void> {
return util.promisify(fs.unlink)(filePath)
}
async function getVersion(app: string): Promise<string> {
export async function getVersion(app: string): Promise<string> {
core.debug(`Checking ${app} --version`)
let versionOutput = ''
try {
@ -94,11 +94,6 @@ async function getVersion(app: string): Promise<string> {
// Use zstandard if possible to maximize cache performance
export async function getCompressionMethod(): Promise<CompressionMethod> {
if (process.platform === 'win32' && !(await getGnuTarPathOnWindows())) {
// Disable zstd due to bug https://github.com/actions/cache/issues/301
return CompressionMethod.Gzip
}
const versionOutput = await getVersion('zstd')
const version = semver.clean(versionOutput)

View File

@ -24,3 +24,5 @@ export const SocketTimeout = 5000
// The default path of GNUtar on hosted Windows runners
export const GnuTarPathOnWindows = `${process.env['PROGRAMFILES']}\\Git\\usr\\bin\\tar.exe`
export const SystemTarPathOnWindows = `${process.env['SYSTEMDRIVE']}\\Windows\\System32\\tar.exe`

View File

@ -3,18 +3,18 @@ import * as io from '@actions/io'
import {existsSync, writeFileSync} from 'fs'
import * as path from 'path'
import * as utils from './cacheUtils'
import {CompressionMethod} from './constants'
import {CompressionMethod, SystemTarPathOnWindows} from './constants'
const IS_WINDOWS = process.platform === 'win32'
async function getTarPath(args: string[]): Promise<string> {
// Function also mutates the args array. For non-mutation call with passing an empty array.
async function getTarPath(): Promise<string> {
switch (process.platform) {
case 'win32': {
const gnuTar = await utils.getGnuTarPathOnWindows()
const systemTar = `${process.env['windir']}\\System32\\tar.exe`
const systemTar = SystemTarPathOnWindows
if (gnuTar) {
// Use GNUtar as default on windows
args.push('--force-local')
return gnuTar
} else if (existsSync(systemTar)) {
return systemTar
@ -25,7 +25,6 @@ async function getTarPath(args: string[]): Promise<string> {
const gnuTar = await io.which('gtar', false)
if (gnuTar) {
// fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527
args.push('--delay-directory-restore')
return gnuTar
}
break
@ -36,9 +35,101 @@ async function getTarPath(args: string[]): Promise<string> {
return await io.which('tar', true)
}
async function getTarArgs(
compressionMethod: CompressionMethod,
type: string,
archivePath = ''
): Promise<string[]> {
const args = []
const manifestFilename = 'manifest.txt'
const cacheFileName = utils.getCacheFileName(compressionMethod)
const tarFile = 'cache.tar'
const tarPath = await getTarPath()
const workingDirectory = getWorkingDirectory()
const BSD_TAR_ZSTD =
tarPath === SystemTarPathOnWindows &&
compressionMethod !== CompressionMethod.Gzip
// Method specific args
switch (type) {
case 'create':
args.push(
'--posix',
'-cf',
BSD_TAR_ZSTD
? tarFile
: cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'--exclude',
BSD_TAR_ZSTD
? tarFile
: cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'-P',
'-C',
workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'--files-from',
manifestFilename
)
break
case 'extract':
args.push(
'-xf',
BSD_TAR_ZSTD
? tarFile
: archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'-P',
'-C',
workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
)
break
case 'list':
args.push(
'-tf',
BSD_TAR_ZSTD
? tarFile
: archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'-P'
)
break
}
// Platform specific args
switch (process.platform) {
case 'win32': {
const gnuTar = await utils.getGnuTarPathOnWindows()
if (gnuTar) {
// Use GNUtar as default on windows
args.push('--force-local')
}
break
}
case 'darwin': {
const gnuTar = await io.which('gtar', false)
if (gnuTar) {
// fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527
args.push('--delay-directory-restore')
}
break
}
}
return args
}
async function execTar(args: string[], cwd?: string): Promise<void> {
try {
await exec(`"${await getTarPath(args)}"`, args, {cwd})
await exec(`"${await getTarPath()}"`, args, {cwd})
} catch (error) {
throw new Error(`Tar failed with error: ${error?.message}`)
}
}
async function execCommand(
command: string,
args: string[],
cwd?: string
): Promise<void> {
try {
await exec(command, args, {cwd})
} catch (error) {
throw new Error(`Tar failed with error: ${error?.message}`)
}
@ -49,19 +140,41 @@ function getWorkingDirectory(): string {
}
// Common function for extractTar and listTar to get the compression method
function getCompressionProgram(compressionMethod: CompressionMethod): string[] {
async function getCompressionProgram(
compressionMethod: CompressionMethod,
archivePath: string
): Promise<string[]> {
// -d: Decompress.
// 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 tarPath = await getTarPath()
const tarFile = 'cache.tar'
const BSD_TAR_ZSTD =
tarPath === SystemTarPathOnWindows &&
compressionMethod !== CompressionMethod.Gzip
switch (compressionMethod) {
case CompressionMethod.Zstd:
return [
'--use-compress-program',
IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30'
]
return BSD_TAR_ZSTD
? [
'zstd -d --long=30 -o',
tarFile,
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'&&'
]
: [
'--use-compress-program',
IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30'
]
case CompressionMethod.ZstdWithoutLong:
return ['--use-compress-program', IS_WINDOWS ? 'zstd -d' : 'unzstd']
return BSD_TAR_ZSTD
? [
'zstd -d -o',
tarFile,
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'&&'
]
: ['--use-compress-program', IS_WINDOWS ? 'zstd -d' : 'unzstd']
default:
return ['-z']
}
@ -71,13 +184,27 @@ export async function listTar(
archivePath: string,
compressionMethod: CompressionMethod
): Promise<void> {
const args = [
...getCompressionProgram(compressionMethod),
'-tf',
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'-P'
]
await execTar(args)
const tarPath = await getTarPath()
const BSD_TAR_ZSTD =
tarPath === SystemTarPathOnWindows &&
compressionMethod !== CompressionMethod.Gzip
const compressionArgs = await getCompressionProgram(
compressionMethod,
archivePath
)
const tarArgs = await getTarArgs(compressionMethod, 'list', archivePath)
// TODO: Add a test for BSD tar on windows
if (BSD_TAR_ZSTD) {
const command = compressionArgs[0]
const args = compressionArgs
.slice(1)
.concat([tarPath])
.concat(tarArgs)
await execCommand(command, args)
} else {
const args = tarArgs.concat(compressionArgs)
await execTar(args)
}
}
export async function extractTar(
@ -86,16 +213,27 @@ export async function extractTar(
): Promise<void> {
// Create directory to extract tar into
const workingDirectory = getWorkingDirectory()
const tarPath = await getTarPath()
const BSD_TAR_ZSTD =
tarPath === SystemTarPathOnWindows &&
compressionMethod !== CompressionMethod.Gzip
await io.mkdirP(workingDirectory)
const args = [
...getCompressionProgram(compressionMethod),
'-xf',
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'-P',
'-C',
workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
]
await execTar(args)
const tarArgs = await getTarArgs(compressionMethod, 'extract', archivePath)
const compressionArgs = await getCompressionProgram(
compressionMethod,
archivePath
)
if (BSD_TAR_ZSTD) {
const command = compressionArgs[0]
const args = compressionArgs
.slice(1)
.concat([tarPath])
.concat(tarArgs)
await execCommand(command, args)
} else {
const args = tarArgs.concat(compressionArgs)
await execTar(args)
}
}
export async function createTar(
@ -106,11 +244,15 @@ export async function createTar(
// Write source directories to manifest.txt to avoid command length limits
const manifestFilename = 'manifest.txt'
const cacheFileName = utils.getCacheFileName(compressionMethod)
const tarFile = 'cache.tar'
const tarPath = await getTarPath()
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'
@ -120,28 +262,145 @@ export async function createTar(
function getCompressionProgram(): string[] {
switch (compressionMethod) {
case CompressionMethod.Zstd:
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:
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']
}
}
const args = [
'--posix',
...getCompressionProgram(),
'-cf',
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'--exclude',
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'-P',
'-C',
workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'--files-from',
manifestFilename
]
const tarArgs = await getTarArgs(compressionMethod, 'create')
const compressionArgs = getCompressionProgram()
const args = tarArgs.concat(compressionArgs)
await execTar(args, archiveFolder)
}
const ARCHIVE_TOOL_GNU: string = 'gnu'
const ARCHIVE_TOOL_BSD: string = 'bsd'
const manifestFilename = 'manifest.txt'
export async function createTar2(
archiveFolder: string,
sourceDirectories: string[]
): Promise<void> {
// 1. decide the compression algo - zstd otherwise gzip
// 2. decide tar - gnutar otherwise bsdtar or systemtar
// 3. deide archiving/unarchiving args depending on tar
// 4. decide compression/decompression args depending on tar and compression
// 5. decide the exec command
let tar = getTarPath2()
let compression = getCompressionMethod2()
let args = argsMap.get({tar: tar, compression: compression, os: process.platform, "create"})
}
// return archive tool name and path
async function getTarPath2(): Promise<ArchiveTool> {
switch (process.platform) {
case 'win32': {
const gnuTar = await utils.getGnuTarPathOnWindows()
const systemTar = SystemTarPathOnWindows
if (gnuTar) {
// Use GNUtar as default on windows
return <ArchiveTool>{ name: ARCHIVE_TOOL_GNU, path: gnuTar }
} else if (existsSync(systemTar)) {
return <ArchiveTool>{name: ARCHIVE_TOOL_BSD, path: systemTar}
}
break
}
case 'darwin': {
const gnuTar = await io.which('gtar', false)
if (gnuTar) {
// fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527
return <ArchiveTool>{ name: ARCHIVE_TOOL_GNU, path: gnuTar }
} else {
const path = await io.which('tar', true)
return <ArchiveTool>{name: ARCHIVE_TOOL_BSD, path: path}
}
}
default:
break
}
const path = await io.which('tar', true)
return <ArchiveTool>{name: ARCHIVE_TOOL_GNU, path: path}
}
// Use zstandard if possible to maximize cache performance
export async function getCompressionMethod2(): Promise<CompressionMethod> {
const versionOutput = await utils.getVersion('zstd')
const version = semver.clean(versionOutput)
if (!versionOutput.toLowerCase().includes('zstd command line interface')) {
// zstd is not installed
return CompressionMethod.Gzip
} else if (!version || semver.lt(version, 'v1.3.2')) {
// zstd is installed but using a version earlier than v1.3.2
// v1.3.2 is required to use the `--long` options in zstd
return CompressionMethod.ZstdWithoutLong
} else {
return CompressionMethod.Zstd
}
}
interface ArchiveTool {
name: string
path: string
}
// tar, compression, os, operation -> tar args + compression args
const argsMap: Map<ArgsLookupKey, string[][]> = new Map()
argsMap.set({tar: ARCHIVE_TOOL_GNU, compression: CompressionMethod.Zstd, os: "windows", operation: "create"},
[[
'--posix',
'-cf',
'%%cacheFileName%%',
'--exclude',
'%%cacheFileName%%',
'-P',
'-C',
'%%workingDirectory%%',
'--files-from',
manifestFilename],
['--use-compress-program',
'zstd -T0 --long=30']
])
argsMap.set({tar: ARCHIVE_TOOL_GNU, compression: CompressionMethod.Zstd, os: "linux", operation: "create"},
[
'--use-compress-program',
'zstdmt --long=30'
])
interface ArgsLookupKey {
tar: string
compression: string
os: string
operation: string
}