1
0
Fork 0

Refactor code

pull/1237/head
Sampark Sharma 2022-11-28 10:24:40 +00:00 committed by GitHub
parent 0822441ee0
commit 0fd856d0a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 118 additions and 130 deletions

View File

@ -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

View File

@ -31,3 +31,8 @@ export interface InternalCacheOptions {
compressionMethod?: CompressionMethod compressionMethod?: CompressionMethod
cacheSize?: number cacheSize?: number
} }
export interface ArchiveTool {
path: string
type: string
}

View File

@ -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)
} }