Skip to content

Commit

Permalink
tool-cache: Support for extracting xar compatible archives (#207)
Browse files Browse the repository at this point in the history
* 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 a6cbddc.

* 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>
  • Loading branch information
fwal and thboop committed Jul 15, 2020
1 parent 7e1c59c commit 2710592
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ packages/*/node_modules/
packages/*/lib/
packages/*/__tests__/_temp/
.DS_Store
*.xar
packages/*/audit.json
4 changes: 4 additions & 0 deletions packages/tool-cache/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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 {
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');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
file-with-�-character.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
file.txt contents
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
folder/nested-file.txt contents
105 changes: 105 additions & 0 deletions packages/tool-cache/__tests__/tool-cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ process.env['RUNNER_TOOL_CACHE'] = cachePath
import * as tc from '../src/tool-cache'

const IS_WINDOWS = process.platform === 'win32'
const IS_MAC = process.platform === 'darwin'

describe('@actions/tool-cache', function() {
beforeAll(function() {
Expand Down Expand Up @@ -346,6 +347,110 @@ describe('@actions/tool-cache', function() {
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 () => {
Expand Down
46 changes: 46 additions & 0 deletions packages/tool-cache/src/tool-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export class HTTPError extends Error {
}

const IS_WINDOWS = process.platform === 'win32'
const IS_MAC = process.platform === 'darwin'
const userAgent = 'actions/tool-cache'

/**
Expand Down Expand Up @@ -276,6 +277,43 @@ export async function extractTar(
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
*
Expand Down Expand Up @@ -675,3 +713,11 @@ function _getGlobal<T>(key: string, defaultValue: T): T {
/* eslint-enable @typescript-eslint/no-explicit-any */
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))
}

0 comments on commit 2710592

Please sign in to comment.