mirror of https://github.com/actions/toolkit
Refactor code
parent
0822441ee0
commit
0fd856d0a0
|
@ -11,6 +11,11 @@ export enum CompressionMethod {
|
||||||
Zstd = 'zstd'
|
Zstd = 'zstd'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ArchiveToolType {
|
||||||
|
GNU = 'gnu',
|
||||||
|
BSD = 'bsd'
|
||||||
|
}
|
||||||
|
|
||||||
// The default number of retry attempts.
|
// The default number of retry attempts.
|
||||||
export const DefaultRetryAttempts = 2
|
export const DefaultRetryAttempts = 2
|
||||||
|
|
||||||
|
|
|
@ -31,3 +31,8 @@ export interface InternalCacheOptions {
|
||||||
compressionMethod?: CompressionMethod
|
compressionMethod?: CompressionMethod
|
||||||
cacheSize?: number
|
cacheSize?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ArchiveTool {
|
||||||
|
path: string
|
||||||
|
type: string
|
||||||
|
}
|
|
@ -3,21 +3,26 @@ import * as io from '@actions/io'
|
||||||
import {existsSync, writeFileSync} from 'fs'
|
import {existsSync, writeFileSync} from 'fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as utils from './cacheUtils'
|
import * as utils from './cacheUtils'
|
||||||
import {CompressionMethod, SystemTarPathOnWindows} from './constants'
|
import {ArchiveTool} from './contracts'
|
||||||
|
import {
|
||||||
|
CompressionMethod,
|
||||||
|
SystemTarPathOnWindows,
|
||||||
|
ArchiveToolType
|
||||||
|
} from './constants'
|
||||||
|
|
||||||
const IS_WINDOWS = process.platform === 'win32'
|
const IS_WINDOWS = process.platform === 'win32'
|
||||||
|
|
||||||
// Function also mutates the args array. For non-mutation call with passing an empty array.
|
// Function also mutates the args array. For non-mutation call with passing an empty array.
|
||||||
async function getTarPath(): Promise<string> {
|
async function getTarPath(): Promise<ArchiveTool> {
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case 'win32': {
|
case 'win32': {
|
||||||
const gnuTar = await utils.getGnuTarPathOnWindows()
|
const gnuTar = await utils.getGnuTarPathOnWindows()
|
||||||
const systemTar = SystemTarPathOnWindows
|
const systemTar = SystemTarPathOnWindows
|
||||||
if (gnuTar) {
|
if (gnuTar) {
|
||||||
// Use GNUtar as default on windows
|
// Use GNUtar as default on windows
|
||||||
return gnuTar
|
return <ArchiveTool>{path: gnuTar, type: ArchiveToolType.GNU}
|
||||||
} else if (existsSync(systemTar)) {
|
} else if (existsSync(systemTar)) {
|
||||||
return systemTar
|
return <ArchiveTool>{path: systemTar, type: ArchiveToolType.BSD}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -25,30 +30,39 @@ async function getTarPath(): Promise<string> {
|
||||||
const gnuTar = await io.which('gtar', false)
|
const gnuTar = await io.which('gtar', false)
|
||||||
if (gnuTar) {
|
if (gnuTar) {
|
||||||
// fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527
|
// fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527
|
||||||
return gnuTar
|
return <ArchiveTool>{path: gnuTar, type: ArchiveToolType.GNU}
|
||||||
|
} else {
|
||||||
|
return <ArchiveTool>{
|
||||||
|
path: await io.which('tar', true),
|
||||||
|
type: ArchiveToolType.BSD
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return await io.which('tar', true)
|
return <ArchiveTool>{
|
||||||
|
path: await io.which('tar', true),
|
||||||
|
type: ArchiveToolType.GNU
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return arguments for tar as per tarPath, compressionMethod, method type and os
|
||||||
async function getTarArgs(
|
async function getTarArgs(
|
||||||
|
tarPath: ArchiveTool,
|
||||||
compressionMethod: CompressionMethod,
|
compressionMethod: CompressionMethod,
|
||||||
type: string,
|
type: string,
|
||||||
archivePath = ''
|
archivePath = ''
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
const args = []
|
const args = [tarPath.path]
|
||||||
const manifestFilename = 'manifest.txt'
|
const manifestFilename = 'manifest.txt'
|
||||||
const cacheFileName = utils.getCacheFileName(compressionMethod)
|
const cacheFileName = utils.getCacheFileName(compressionMethod)
|
||||||
const tarFile = 'cache.tar'
|
const tarFile = 'cache.tar'
|
||||||
const tarPath = await getTarPath()
|
|
||||||
const workingDirectory = getWorkingDirectory()
|
const workingDirectory = getWorkingDirectory()
|
||||||
const BSD_TAR_ZSTD =
|
const BSD_TAR_ZSTD =
|
||||||
tarPath === SystemTarPathOnWindows &&
|
tarPath.type === ArchiveToolType.BSD &&
|
||||||
compressionMethod !== CompressionMethod.Gzip
|
compressionMethod !== CompressionMethod.Gzip &&
|
||||||
|
IS_WINDOWS
|
||||||
|
|
||||||
// Method specific args
|
// Method specific args
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -93,45 +107,44 @@ async function getTarArgs(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Platform specific args
|
// Platform specific args
|
||||||
switch (process.platform) {
|
if (tarPath.type === ArchiveToolType.GNU) {
|
||||||
case 'win32': {
|
switch (process.platform) {
|
||||||
const gnuTar = await utils.getGnuTarPathOnWindows()
|
case 'win32':
|
||||||
if (gnuTar) {
|
|
||||||
// Use GNUtar as default on windows
|
|
||||||
args.push('--force-local')
|
args.push('--force-local')
|
||||||
}
|
break
|
||||||
break
|
case 'darwin':
|
||||||
}
|
|
||||||
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')
|
args.push('--delay-directory-restore')
|
||||||
}
|
break
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
async function execTar(args: string[], cwd?: string): Promise<void> {
|
async function getArgs(
|
||||||
try {
|
compressionMethod: CompressionMethod,
|
||||||
await exec(`"${await getTarPath()}"`, args, {cwd})
|
type: string,
|
||||||
} catch (error) {
|
archivePath = ''
|
||||||
throw new Error(`Tar failed with error: ${error?.message}`)
|
): Promise<string> {
|
||||||
}
|
const tarPath = await getTarPath()
|
||||||
}
|
const tarArgs = await getTarArgs(
|
||||||
|
tarPath,
|
||||||
async function execCommand(
|
compressionMethod,
|
||||||
command: string,
|
type,
|
||||||
args: string[],
|
archivePath
|
||||||
cwd?: string
|
)
|
||||||
): Promise<void> {
|
const compressionArgs =
|
||||||
try {
|
type !== 'create'
|
||||||
await exec(command, args, {cwd})
|
? await getDecompressionProgram(tarPath, compressionMethod, archivePath)
|
||||||
} catch (error) {
|
: await getCompressionProgram(tarPath, compressionMethod)
|
||||||
throw new Error(`Tar failed with error: ${error?.message}`)
|
const BSD_TAR_ZSTD =
|
||||||
|
tarPath.type === ArchiveToolType.BSD &&
|
||||||
|
compressionMethod !== CompressionMethod.Gzip &&
|
||||||
|
IS_WINDOWS
|
||||||
|
if (BSD_TAR_ZSTD && type !== 'create') {
|
||||||
|
return [...compressionArgs, ...tarArgs].join(' ')
|
||||||
|
} else {
|
||||||
|
return [...tarArgs, ...compressionArgs].join(' ')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +153,8 @@ function getWorkingDirectory(): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common function for extractTar and listTar to get the compression method
|
// Common function for extractTar and listTar to get the compression method
|
||||||
async function getCompressionProgram(
|
async function getDecompressionProgram(
|
||||||
|
tarPath: ArchiveTool,
|
||||||
compressionMethod: CompressionMethod,
|
compressionMethod: CompressionMethod,
|
||||||
archivePath: string
|
archivePath: string
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
|
@ -148,11 +162,11 @@ async function getCompressionProgram(
|
||||||
// unzstd is equivalent to 'zstd -d'
|
// 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.
|
// --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.
|
// Using 30 here because we also support 32-bit self-hosted runners.
|
||||||
const tarPath = await getTarPath()
|
|
||||||
const tarFile = 'cache.tar'
|
const tarFile = 'cache.tar'
|
||||||
const BSD_TAR_ZSTD =
|
const BSD_TAR_ZSTD =
|
||||||
tarPath === SystemTarPathOnWindows &&
|
tarPath.type === ArchiveToolType.BSD &&
|
||||||
compressionMethod !== CompressionMethod.Gzip
|
compressionMethod !== CompressionMethod.Gzip &&
|
||||||
|
IS_WINDOWS
|
||||||
switch (compressionMethod) {
|
switch (compressionMethod) {
|
||||||
case CompressionMethod.Zstd:
|
case CompressionMethod.Zstd:
|
||||||
return BSD_TAR_ZSTD
|
return BSD_TAR_ZSTD
|
||||||
|
@ -180,31 +194,54 @@ async function getCompressionProgram(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -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'
|
||||||
|
// --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.
|
||||||
|
// Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd.
|
||||||
|
async function getCompressionProgram(
|
||||||
|
tarPath: ArchiveTool,
|
||||||
|
compressionMethod: CompressionMethod
|
||||||
|
): Promise<string[]> {
|
||||||
|
const cacheFileName = utils.getCacheFileName(compressionMethod)
|
||||||
|
const tarFile = 'cache.tar'
|
||||||
|
const BSD_TAR_ZSTD =
|
||||||
|
tarPath.type === ArchiveToolType.BSD &&
|
||||||
|
compressionMethod !== CompressionMethod.Gzip &&
|
||||||
|
IS_WINDOWS
|
||||||
|
switch (compressionMethod) {
|
||||||
|
case CompressionMethod.Zstd:
|
||||||
|
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 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']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function listTar(
|
export async function listTar(
|
||||||
archivePath: string,
|
archivePath: string,
|
||||||
compressionMethod: CompressionMethod
|
compressionMethod: CompressionMethod
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tarPath = await getTarPath()
|
const args = await getArgs(compressionMethod, 'list', archivePath)
|
||||||
const BSD_TAR_ZSTD =
|
exec(args)
|
||||||
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(
|
export async function extractTar(
|
||||||
|
@ -213,27 +250,9 @@ export async function extractTar(
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Create directory to extract tar into
|
// Create directory to extract tar into
|
||||||
const workingDirectory = getWorkingDirectory()
|
const workingDirectory = getWorkingDirectory()
|
||||||
const tarPath = await getTarPath()
|
|
||||||
const BSD_TAR_ZSTD =
|
|
||||||
tarPath === SystemTarPathOnWindows &&
|
|
||||||
compressionMethod !== CompressionMethod.Gzip
|
|
||||||
await io.mkdirP(workingDirectory)
|
await io.mkdirP(workingDirectory)
|
||||||
const tarArgs = await getTarArgs(compressionMethod, 'extract', archivePath)
|
const args = await getArgs(compressionMethod, 'extract', archivePath)
|
||||||
const compressionArgs = await getCompressionProgram(
|
exec(args)
|
||||||
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(
|
export async function createTar(
|
||||||
|
@ -243,51 +262,10 @@ export async function createTar(
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Write source directories to manifest.txt to avoid command length limits
|
// Write source directories to manifest.txt to avoid command length limits
|
||||||
const manifestFilename = 'manifest.txt'
|
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(
|
writeFileSync(
|
||||||
path.join(archiveFolder, manifestFilename),
|
path.join(archiveFolder, manifestFilename),
|
||||||
sourceDirectories.join('\n')
|
sourceDirectories.join('\n')
|
||||||
)
|
)
|
||||||
|
const args = await getArgs(compressionMethod, 'create')
|
||||||
// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores.
|
await exec(args)
|
||||||
// zstdmt is equivalent to 'zstd -T0'
|
|
||||||
// --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.
|
|
||||||
// Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd.
|
|
||||||
function getCompressionProgram(): string[] {
|
|
||||||
switch (compressionMethod) {
|
|
||||||
case CompressionMethod.Zstd:
|
|
||||||
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 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 tarArgs = await getTarArgs(compressionMethod, 'create')
|
|
||||||
const compressionArgs = getCompressionProgram()
|
|
||||||
const args = tarArgs.concat(compressionArgs)
|
|
||||||
await execTar(args, archiveFolder)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue