diff --git a/packages/tool-cache/__tests__/tool-cache.test.ts b/packages/tool-cache/__tests__/tool-cache.test.ts index d316fe43..be176f1b 100644 --- a/packages/tool-cache/__tests__/tool-cache.test.ts +++ b/packages/tool-cache/__tests__/tool-cache.test.ts @@ -47,6 +47,47 @@ describe('@actions/tool-cache', function() { expect(fs.statSync(downPath).size).toBe(35) }) + it('downloads a 35 byte file (dest)', async () => { + const dest = 'test-download-file' + try { + const downPath: string = await tc.downloadTool( + 'http://example.com/bytes/35', + dest + ) + + expect(downPath).toEqual(dest) + expect(fs.existsSync(downPath)).toBeTruthy() + expect(fs.statSync(downPath).size).toBe(35) + } finally { + try { + await fs.promises.unlink(dest) + } catch { + // intentionally empty + } + } + }) + + it('downloads a 35 byte file (dest requires mkdirp)', async () => { + const dest = 'test-download-dir/test-download-file' + try { + const downPath: string = await tc.downloadTool( + 'http://example.com/bytes/35', + dest + ) + + expect(downPath).toEqual(dest) + expect(fs.existsSync(downPath)).toBeTruthy() + expect(fs.statSync(downPath).size).toBe(35) + } finally { + try { + await fs.promises.unlink(dest) + await fs.promises.rmdir(path.dirname(dest)) + } catch { + // intentionally empty + } + } + }) + it('downloads a 35 byte file after a redirect', async () => { nock('http://example.com') .get('/redirect-to') diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index b0b695d7..972eea0b 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -48,9 +48,13 @@ if (!tempDirectory || !cacheRoot) { * Download a tool from an url and stream it into a file * * @param url url of tool to download + * @param dest path to download tool * @returns path to downloaded tool */ -export async function downloadTool(url: string): Promise { +export async function downloadTool( + url: string, + dest?: string +): Promise { // Wrap in a promise so that we can resolve from within stream callbacks return new Promise(async (resolve, reject) => { try { @@ -58,14 +62,13 @@ export async function downloadTool(url: string): Promise { allowRetries: true, maxRetries: 3 }) - const destPath = path.join(tempDirectory, uuidV4()) - - await io.mkdirP(tempDirectory) + dest = dest || path.join(tempDirectory, uuidV4()) + await io.mkdirP(path.dirname(dest)) core.debug(`Downloading ${url}`) - core.debug(`Downloading ${destPath}`) + core.debug(`Downloading ${dest}`) - if (fs.existsSync(destPath)) { - throw new Error(`Destination file path ${destPath} already exists`) + if (fs.existsSync(dest)) { + throw new Error(`Destination file path ${dest} already exists`) } const response: httpm.HttpClientResponse = await http.get(url) @@ -80,13 +83,13 @@ export async function downloadTool(url: string): Promise { throw err } - const file: NodeJS.WritableStream = fs.createWriteStream(destPath) + const file: NodeJS.WritableStream = fs.createWriteStream(dest) file.on('open', async () => { try { const stream = response.message.pipe(file) stream.on('close', () => { core.debug('download complete') - resolve(destPath) + resolve(dest) }) } catch (err) { core.debug(