1
0
Fork 0

tool-cache: Support for extracting xar compatible archives (#207)

* Test xar extraction

* Support for extracting xar compatible archives

* Only allow extractXar on mac

* Create xar during test instead of using prebuilt

* Update lockfiles

* Add verbose flag if debug

* Add extractXar example to readme

* Revert "Update lockfiles"

This reverts commit a6cbddccf6.

* Use node pkg in example

* Remove and ignore prebuilt xar

* Tests for non-existing dir and without flags

* Better arguments handling

* Make sure that target directory exists

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>
pull/519/head
Frederik Wallner 2020-07-15 20:49:23 +02:00 committed by GitHub
parent 7e1c59c51e
commit 2710592b73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 159 additions and 0 deletions

1
.gitignore vendored
View File

@ -3,4 +3,5 @@ packages/*/node_modules/
packages/*/lib/ packages/*/lib/
packages/*/__tests__/_temp/ packages/*/__tests__/_temp/
.DS_Store .DS_Store
*.xar
packages/*/audit.json packages/*/audit.json

View File

@ -29,6 +29,10 @@ if (process.platform === 'win32') {
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.7z'); const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.7z');
const node12ExtractedFolder = await tc.extract7z(node12Path, 'path/to/extract/to'); const node12ExtractedFolder = await tc.extract7z(node12Path, 'path/to/extract/to');
} }
else if (process.platform === 'darwin') {
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0.pkg');
const node12ExtractedFolder = await tc.extractXar(node12Path, 'path/to/extract/to');
}
else { else {
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz'); const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz');
const node12ExtractedFolder = await tc.extractTar(node12Path, 'path/to/extract/to'); const node12ExtractedFolder = await tc.extractTar(node12Path, 'path/to/extract/to');

View File

@ -0,0 +1 @@
file-with-ç-character.txt

View File

@ -0,0 +1 @@
file.txt contents

View File

@ -0,0 +1 @@
folder/nested-file.txt contents

View File

@ -15,6 +15,7 @@ process.env['RUNNER_TOOL_CACHE'] = cachePath
import * as tc from '../src/tool-cache' import * as tc from '../src/tool-cache'
const IS_WINDOWS = process.platform === 'win32' const IS_WINDOWS = process.platform === 'win32'
const IS_MAC = process.platform === 'darwin'
describe('@actions/tool-cache', function() { describe('@actions/tool-cache', function() {
beforeAll(function() { beforeAll(function() {
@ -346,6 +347,110 @@ describe('@actions/tool-cache', function() {
await io.rmRF(tempDir) await io.rmRF(tempDir)
} }
}) })
} else if (IS_MAC) {
it('extract .xar', async () => {
const tempDir = path.join(tempPath, 'test-install.xar')
const sourcePath = path.join(__dirname, 'data', 'archive-content')
const targetPath = path.join(tempDir, 'test.xar')
await io.mkdirP(tempDir)
// Create test archive
const xarPath = await io.which('xar', true)
await exec.exec(`${xarPath}`, ['-cf', targetPath, '.'], {
cwd: sourcePath
})
// extract/cache
const extPath: string = await tc.extractXar(targetPath, undefined, '-x')
await tc.cacheDir(extPath, 'my-xar-contents', '1.1.0')
const toolPath: string = tc.find('my-xar-contents', '1.1.0')
expect(fs.existsSync(toolPath)).toBeTruthy()
expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy()
expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt'))
).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt'))
).toBeTruthy()
expect(
fs.readFileSync(
path.join(toolPath, 'folder', 'nested-file.txt'),
'utf8'
)
).toBe('folder/nested-file.txt contents')
})
it('extract .xar to a directory that does not exist', async () => {
const tempDir = path.join(tempPath, 'test-install.xar')
const sourcePath = path.join(__dirname, 'data', 'archive-content')
const targetPath = path.join(tempDir, 'test.xar')
await io.mkdirP(tempDir)
const destDir = path.join(tempDir, 'not-exist')
// Create test archive
const xarPath = await io.which('xar', true)
await exec.exec(`${xarPath}`, ['-cf', targetPath, '.'], {
cwd: sourcePath
})
// extract/cache
const extPath: string = await tc.extractXar(targetPath, destDir, '-x')
await tc.cacheDir(extPath, 'my-xar-contents', '1.1.0')
const toolPath: string = tc.find('my-xar-contents', '1.1.0')
expect(fs.existsSync(toolPath)).toBeTruthy()
expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy()
expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt'))
).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt'))
).toBeTruthy()
expect(
fs.readFileSync(
path.join(toolPath, 'folder', 'nested-file.txt'),
'utf8'
)
).toBe('folder/nested-file.txt contents')
})
it('extract .xar without flags', async () => {
const tempDir = path.join(tempPath, 'test-install.xar')
const sourcePath = path.join(__dirname, 'data', 'archive-content')
const targetPath = path.join(tempDir, 'test.xar')
await io.mkdirP(tempDir)
// Create test archive
const xarPath = await io.which('xar', true)
await exec.exec(`${xarPath}`, ['-cf', targetPath, '.'], {
cwd: sourcePath
})
// extract/cache
const extPath: string = await tc.extractXar(targetPath, undefined)
await tc.cacheDir(extPath, 'my-xar-contents', '1.1.0')
const toolPath: string = tc.find('my-xar-contents', '1.1.0')
expect(fs.existsSync(toolPath)).toBeTruthy()
expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy()
expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt'))
).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt'))
).toBeTruthy()
expect(
fs.readFileSync(
path.join(toolPath, 'folder', 'nested-file.txt'),
'utf8'
)
).toBe('folder/nested-file.txt contents')
})
} }
it('extract .tar.gz', async () => { it('extract .tar.gz', async () => {

View File

@ -23,6 +23,7 @@ export class HTTPError extends Error {
} }
const IS_WINDOWS = process.platform === 'win32' const IS_WINDOWS = process.platform === 'win32'
const IS_MAC = process.platform === 'darwin'
const userAgent = 'actions/tool-cache' const userAgent = 'actions/tool-cache'
/** /**
@ -276,6 +277,43 @@ export async function extractTar(
return dest return dest
} }
/**
* Extract a xar compatible archive
*
* @param file path to the archive
* @param dest destination directory. Optional.
* @param flags flags for the xar. Optional.
* @returns path to the destination directory
*/
export async function extractXar(
file: string,
dest?: string,
flags: string | string[] = []
): Promise<string> {
ok(IS_MAC, 'extractXar() not supported on current OS')
ok(file, 'parameter "file" is required')
dest = await _createExtractFolder(dest)
let args: string[]
if (flags instanceof Array) {
args = flags
} else {
args = [flags]
}
args.push('-x', '-C', dest, '-f', file)
if (core.isDebug()) {
args.push('-v')
}
const xarPath: string = await io.which('xar', true)
await exec(`"${xarPath}"`, _unique(args))
return dest
}
/** /**
* Extract a zip * Extract a zip
* *
@ -675,3 +713,11 @@ function _getGlobal<T>(key: string, defaultValue: T): T {
/* eslint-enable @typescript-eslint/no-explicit-any */ /* eslint-enable @typescript-eslint/no-explicit-any */
return value !== undefined ? value : defaultValue return value !== undefined ? value : defaultValue
} }
/**
* Returns an array of unique values.
* @param values Values to make unique.
*/
function _unique<T>(values: T[]): T[] {
return Array.from(new Set(values))
}