diff --git a/__tests__/tar.test.ts b/__tests__/tar.test.ts index ca10f34..56acbce 100644 --- a/__tests__/tar.test.ts +++ b/__tests__/tar.test.ts @@ -1,11 +1,12 @@ import * as exec from "@actions/exec"; import * as io from "@actions/io"; -import { promises as fs } from "fs"; import * as path from "path"; import { CacheFilename } from "../src/constants"; import * as tar from "../src/tar"; +import fs = require("fs"); + jest.mock("@actions/exec"); jest.mock("@actions/io"); @@ -27,37 +28,75 @@ afterAll(async () => { await jest.requireActual("@actions/io").rmRF(getTempDir()); }); -test("extract tar", async () => { +test("extract BSD tar", async () => { const mkdirMock = jest.spyOn(io, "mkdirP"); const execMock = jest.spyOn(exec, "exec"); - const archivePath = "cache.tar"; + const IS_WINDOWS = process.platform === "win32"; + const archivePath = IS_WINDOWS + ? `${process.env["windir"]}\\fakepath\\cache.tar` + : "cache.tar"; const workspace = process.env["GITHUB_WORKSPACE"]; await tar.extractTar(archivePath); expect(mkdirMock).toHaveBeenCalledWith(workspace); - const IS_WINDOWS = process.platform === "win32"; const tarPath = IS_WINDOWS ? `${process.env["windir"]}\\System32\\tar.exe` : "tar"; expect(execMock).toHaveBeenCalledTimes(1); expect(execMock).toHaveBeenCalledWith( `"${tarPath}"`, - ["-xz", "-f", archivePath, "-P", "-C", workspace], + [ + "-xz", + "-f", + IS_WINDOWS ? archivePath.replace(/\\/g, "/") : archivePath, + "-P", + "-C", + IS_WINDOWS ? workspace?.replace(/\\/g, "/") : workspace + ], { cwd: undefined } ); }); -test("create tar", async () => { +test("extract GNU tar", async () => { + const IS_WINDOWS = process.platform === "win32"; + if (IS_WINDOWS) { + jest.spyOn(fs, "existsSync").mockReturnValueOnce(false); + jest.spyOn(tar, "isGnuTar").mockReturnValue(Promise.resolve(true)); + + const execMock = jest.spyOn(exec, "exec"); + const archivePath = `${process.env["windir"]}\\fakepath\\cache.tar`; + const workspace = process.env["GITHUB_WORKSPACE"]; + + await tar.extractTar(archivePath); + + expect(execMock).toHaveBeenCalledTimes(2); + expect(execMock).toHaveBeenLastCalledWith( + `"tar"`, + [ + "-xz", + "-f", + archivePath.replace(/\\/g, "/"), + "-P", + "-C", + workspace?.replace(/\\/g, "/"), + "--force-local" + ], + { cwd: undefined } + ); + } +}); + +test("create BSD tar", async () => { const execMock = jest.spyOn(exec, "exec"); const archiveFolder = getTempDir(); const workspace = process.env["GITHUB_WORKSPACE"]; const sourceDirectories = ["~/.npm/cache", `${workspace}/dist`]; - await fs.mkdir(archiveFolder, { recursive: true }); + await fs.promises.mkdir(archiveFolder, { recursive: true }); await tar.createTar(archiveFolder, sourceDirectories); @@ -72,10 +111,10 @@ test("create tar", async () => { [ "-cz", "-f", - CacheFilename, + IS_WINDOWS ? CacheFilename.replace(/\\/g, "/") : CacheFilename, "-P", "-C", - workspace, + IS_WINDOWS ? workspace?.replace(/\\/g, "/") : workspace, "--files-from", "manifest.txt" ], diff --git a/dist/restore/index.js b/dist/restore/index.js index 2e71724..52de098 100644 --- a/dist/restore/index.js +++ b/dist/restore/index.js @@ -4959,12 +4959,30 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", { value: true }); +const core = __importStar(__webpack_require__(470)); const exec_1 = __webpack_require__(986); const io = __importStar(__webpack_require__(1)); const fs_1 = __webpack_require__(747); const path = __importStar(__webpack_require__(622)); const constants_1 = __webpack_require__(694); -function getTarPath() { +function isGnuTar() { + return __awaiter(this, void 0, void 0, function* () { + core.debug("Checking tar --version"); + let versionOutput = ""; + yield exec_1.exec("tar --version", [], { + ignoreReturnCode: true, + silent: true, + listeners: { + stdout: (data) => (versionOutput += data.toString()), + stderr: (data) => (versionOutput += data.toString()) + } + }); + core.debug(versionOutput.trim()); + return versionOutput.toUpperCase().includes("GNU TAR"); + }); +} +exports.isGnuTar = isGnuTar; +function getTarPath(args) { return __awaiter(this, void 0, void 0, function* () { // Explicitly use BSD Tar on Windows const IS_WINDOWS = process.platform === "win32"; @@ -4973,22 +4991,21 @@ function getTarPath() { if (fs_1.existsSync(systemTar)) { return systemTar; } + else if (isGnuTar()) { + args.push("--force-local"); + } } return yield io.which("tar", true); }); } function execTar(args, cwd) { - var _a, _b; + var _a; return __awaiter(this, void 0, void 0, function* () { try { - yield exec_1.exec(`"${yield getTarPath()}"`, args, { cwd: cwd }); + yield exec_1.exec(`"${yield getTarPath(args)}"`, args, { cwd: cwd }); } catch (error) { - const IS_WINDOWS = process.platform === "win32"; - if (IS_WINDOWS) { - throw new Error(`Tar failed with error: ${(_a = error) === null || _a === void 0 ? void 0 : _a.message}. Ensure BSD tar is installed and on the PATH.`); - } - throw new Error(`Tar failed with error: ${(_b = error) === null || _b === void 0 ? void 0 : _b.message}`); + throw new Error(`Tar failed with error: ${(_a = error) === null || _a === void 0 ? void 0 : _a.message}`); } }); } @@ -5001,7 +5018,14 @@ function extractTar(archivePath) { // Create directory to extract tar into const workingDirectory = getWorkingDirectory(); yield io.mkdirP(workingDirectory); - const args = ["-xz", "-f", archivePath, "-P", "-C", workingDirectory]; + const args = [ + "-xz", + "-f", + archivePath.replace(new RegExp("\\" + path.sep, "g"), "/"), + "-P", + "-C", + workingDirectory.replace(new RegExp("\\" + path.sep, "g"), "/") + ]; yield execTar(args); }); } @@ -5015,10 +5039,10 @@ function createTar(archiveFolder, sourceDirectories) { const args = [ "-cz", "-f", - constants_1.CacheFilename, + constants_1.CacheFilename.replace(new RegExp("\\" + path.sep, "g"), "/"), "-P", "-C", - workingDirectory, + workingDirectory.replace(new RegExp("\\" + path.sep, "g"), "/"), "--files-from", manifestFilename ]; diff --git a/dist/save/index.js b/dist/save/index.js index f807389..3f784aa 100644 --- a/dist/save/index.js +++ b/dist/save/index.js @@ -4936,12 +4936,30 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", { value: true }); +const core = __importStar(__webpack_require__(470)); const exec_1 = __webpack_require__(986); const io = __importStar(__webpack_require__(1)); const fs_1 = __webpack_require__(747); const path = __importStar(__webpack_require__(622)); const constants_1 = __webpack_require__(694); -function getTarPath() { +function isGnuTar() { + return __awaiter(this, void 0, void 0, function* () { + core.debug("Checking tar --version"); + let versionOutput = ""; + yield exec_1.exec("tar --version", [], { + ignoreReturnCode: true, + silent: true, + listeners: { + stdout: (data) => (versionOutput += data.toString()), + stderr: (data) => (versionOutput += data.toString()) + } + }); + core.debug(versionOutput.trim()); + return versionOutput.toUpperCase().includes("GNU TAR"); + }); +} +exports.isGnuTar = isGnuTar; +function getTarPath(args) { return __awaiter(this, void 0, void 0, function* () { // Explicitly use BSD Tar on Windows const IS_WINDOWS = process.platform === "win32"; @@ -4950,22 +4968,21 @@ function getTarPath() { if (fs_1.existsSync(systemTar)) { return systemTar; } + else if (isGnuTar()) { + args.push("--force-local"); + } } return yield io.which("tar", true); }); } function execTar(args, cwd) { - var _a, _b; + var _a; return __awaiter(this, void 0, void 0, function* () { try { - yield exec_1.exec(`"${yield getTarPath()}"`, args, { cwd: cwd }); + yield exec_1.exec(`"${yield getTarPath(args)}"`, args, { cwd: cwd }); } catch (error) { - const IS_WINDOWS = process.platform === "win32"; - if (IS_WINDOWS) { - throw new Error(`Tar failed with error: ${(_a = error) === null || _a === void 0 ? void 0 : _a.message}. Ensure BSD tar is installed and on the PATH.`); - } - throw new Error(`Tar failed with error: ${(_b = error) === null || _b === void 0 ? void 0 : _b.message}`); + throw new Error(`Tar failed with error: ${(_a = error) === null || _a === void 0 ? void 0 : _a.message}`); } }); } @@ -4978,7 +4995,14 @@ function extractTar(archivePath) { // Create directory to extract tar into const workingDirectory = getWorkingDirectory(); yield io.mkdirP(workingDirectory); - const args = ["-xz", "-f", archivePath, "-P", "-C", workingDirectory]; + const args = [ + "-xz", + "-f", + archivePath.replace(new RegExp("\\" + path.sep, "g"), "/"), + "-P", + "-C", + workingDirectory.replace(new RegExp("\\" + path.sep, "g"), "/") + ]; yield execTar(args); }); } @@ -4992,10 +5016,10 @@ function createTar(archiveFolder, sourceDirectories) { const args = [ "-cz", "-f", - constants_1.CacheFilename, + constants_1.CacheFilename.replace(new RegExp("\\" + path.sep, "g"), "/"), "-P", "-C", - workingDirectory, + workingDirectory.replace(new RegExp("\\" + path.sep, "g"), "/"), "--files-from", manifestFilename ]; diff --git a/src/tar.ts b/src/tar.ts index 9a1f446..22eec3e 100644 --- a/src/tar.ts +++ b/src/tar.ts @@ -1,3 +1,4 @@ +import * as core from "@actions/core"; import { exec } from "@actions/exec"; import * as io from "@actions/io"; import { existsSync, writeFileSync } from "fs"; @@ -5,13 +6,32 @@ import * as path from "path"; import { CacheFilename } from "./constants"; -async function getTarPath(): Promise { +export async function isGnuTar(): Promise { + core.debug("Checking tar --version"); + let versionOutput = ""; + await exec("tar --version", [], { + ignoreReturnCode: true, + silent: true, + listeners: { + stdout: (data: Buffer): string => + (versionOutput += data.toString()), + stderr: (data: Buffer): string => (versionOutput += data.toString()) + } + }); + + core.debug(versionOutput.trim()); + return versionOutput.toUpperCase().includes("GNU TAR"); +} + +async function getTarPath(args: string[]): Promise { // Explicitly use BSD Tar on Windows const IS_WINDOWS = process.platform === "win32"; if (IS_WINDOWS) { const systemTar = `${process.env["windir"]}\\System32\\tar.exe`; if (existsSync(systemTar)) { return systemTar; + } else if (isGnuTar()) { + args.push("--force-local"); } } return await io.which("tar", true); @@ -19,14 +39,8 @@ async function getTarPath(): Promise { async function execTar(args: string[], cwd?: string): Promise { try { - await exec(`"${await getTarPath()}"`, args, { cwd: cwd }); + await exec(`"${await getTarPath(args)}"`, args, { cwd: cwd }); } catch (error) { - const IS_WINDOWS = process.platform === "win32"; - if (IS_WINDOWS) { - throw new Error( - `Tar failed with error: ${error?.message}. Ensure BSD tar is installed and on the PATH.` - ); - } throw new Error(`Tar failed with error: ${error?.message}`); } } @@ -39,7 +53,14 @@ export async function extractTar(archivePath: string): Promise { // Create directory to extract tar into const workingDirectory = getWorkingDirectory(); await io.mkdirP(workingDirectory); - const args = ["-xz", "-f", archivePath, "-P", "-C", workingDirectory]; + const args = [ + "-xz", + "-f", + archivePath.replace(new RegExp("\\" + path.sep, "g"), "/"), + "-P", + "-C", + workingDirectory.replace(new RegExp("\\" + path.sep, "g"), "/") + ]; await execTar(args); } @@ -58,10 +79,10 @@ export async function createTar( const args = [ "-cz", "-f", - CacheFilename, + CacheFilename.replace(new RegExp("\\" + path.sep, "g"), "/"), "-P", "-C", - workingDirectory, + workingDirectory.replace(new RegExp("\\" + path.sep, "g"), "/"), "--files-from", manifestFilename ];