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,21 +107,13 @@ async function getTarArgs(
} }
// Platform specific args // Platform specific args
if (tarPath.type === ArchiveToolType.GNU) {
switch (process.platform) { switch (process.platform) {
case 'win32': { case 'win32':
const gnuTar = await utils.getGnuTarPathOnWindows()
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
} }
} }
@ -115,23 +121,30 @@ async function getTarArgs(
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,86 +194,21 @@ async function getCompressionProgram(
} }
} }
export async function listTar( // -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores.
archivePath: string, // 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 compressionMethod: CompressionMethod
): Promise<void> { ): Promise<string[]> {
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(
archivePath: string,
compressionMethod: CompressionMethod
): 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 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(
archiveFolder: string,
sourceDirectories: string[],
compressionMethod: CompressionMethod
): Promise<void> {
// Write source directories to manifest.txt to avoid command length limits
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 BSD_TAR_ZSTD = const BSD_TAR_ZSTD =
tarPath === SystemTarPathOnWindows && tarPath.type === ArchiveToolType.BSD &&
compressionMethod !== CompressionMethod.Gzip compressionMethod !== CompressionMethod.Gzip &&
writeFileSync( IS_WINDOWS
path.join(archiveFolder, manifestFilename),
sourceDirectories.join('\n')
)
// -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.
function getCompressionProgram(): string[] {
switch (compressionMethod) { switch (compressionMethod) {
case CompressionMethod.Zstd: case CompressionMethod.Zstd:
return BSD_TAR_ZSTD return BSD_TAR_ZSTD
@ -285,9 +234,38 @@ export async function createTar(
default: default:
return ['-z'] return ['-z']
} }
} }
const tarArgs = await getTarArgs(compressionMethod, 'create')
const compressionArgs = getCompressionProgram() export async function listTar(
const args = tarArgs.concat(compressionArgs) archivePath: string,
await execTar(args, archiveFolder) compressionMethod: CompressionMethod
): Promise<void> {
const args = await getArgs(compressionMethod, 'list', archivePath)
exec(args)
}
export async function extractTar(
archivePath: string,
compressionMethod: CompressionMethod
): Promise<void> {
// Create directory to extract tar into
const workingDirectory = getWorkingDirectory()
await io.mkdirP(workingDirectory)
const args = await getArgs(compressionMethod, 'extract', archivePath)
exec(args)
}
export async function createTar(
archiveFolder: string,
sourceDirectories: string[],
compressionMethod: CompressionMethod
): Promise<void> {
// Write source directories to manifest.txt to avoid command length limits
const manifestFilename = 'manifest.txt'
writeFileSync(
path.join(archiveFolder, manifestFilename),
sourceDirectories.join('\n')
)
const args = await getArgs(compressionMethod, 'create')
await exec(args)
} }