From a5d9a3b1a607b8cda170b9fef03c951d9074e525 Mon Sep 17 00:00:00 2001
From: Aiqiao Yan <aiqiaoy@MININT-K6RVDNE.europe.corp.microsoft.com>
Date: Thu, 30 Apr 2020 15:28:04 -0400
Subject: [PATCH] Address PR feedback

---
 __tests__/tar.test.ts    | 17 ++++++++++-------
 dist/restore/index.js    | 29 ++++++++++++++++++++---------
 dist/save/index.js       | 29 ++++++++++++++++++++---------
 src/cacheHttpClient.ts   |  8 ++++----
 src/tar.ts               | 19 ++++++++++++++++---
 src/utils/actionUtils.ts |  6 +++---
 6 files changed, 73 insertions(+), 35 deletions(-)

diff --git a/__tests__/tar.test.ts b/__tests__/tar.test.ts
index 40341f0..8345c67 100644
--- a/__tests__/tar.test.ts
+++ b/__tests__/tar.test.ts
@@ -12,6 +12,7 @@ jest.mock("@actions/exec");
 jest.mock("@actions/io");
 
 const IS_WINDOWS = process.platform === "win32";
+const IS_64OS = process.platform != "win32" || process.arch === "x64";
 
 function getTempDir(): string {
     return path.join(__dirname, "_temp", "tar");
@@ -46,12 +47,13 @@ test("zstd extract tar", async () => {
     const tarPath = IS_WINDOWS
         ? `${process.env["windir"]}\\System32\\tar.exe`
         : "tar";
+    const zstdOptions = IS_64OS ? "zstd -d --long=31" : "zstd -d --long=30";
     expect(execMock).toHaveBeenCalledTimes(1);
     expect(execMock).toHaveBeenCalledWith(
-        `${tarPath}`,
+        `"${tarPath}"`,
         [
             "--use-compress-program",
-            "zstd -d",
+            zstdOptions,
             "-xf",
             IS_WINDOWS ? archivePath.replace(/\\/g, "/") : archivePath,
             "-P",
@@ -78,7 +80,7 @@ test("gzip extract tar", async () => {
         : "tar";
     expect(execMock).toHaveBeenCalledTimes(1);
     expect(execMock).toHaveBeenCalledWith(
-        `${tarPath}`,
+        `"${tarPath}"`,
         [
             "-z",
             "-xf",
@@ -107,7 +109,7 @@ test("gzip extract GNU tar on windows", async () => {
         expect(isGnuMock).toHaveBeenCalledTimes(1);
         expect(execMock).toHaveBeenCalledTimes(1);
         expect(execMock).toHaveBeenCalledWith(
-            `tar`,
+            `"tar"`,
             [
                 "-z",
                 "-xf",
@@ -140,13 +142,14 @@ test("zstd create tar", async () => {
     const tarPath = IS_WINDOWS
         ? `${process.env["windir"]}\\System32\\tar.exe`
         : "tar";
+    const zstdOptions = IS_64OS ? "zstd -T0 --long=31" : "zstd -T0 --long=30";
 
     expect(execMock).toHaveBeenCalledTimes(1);
     expect(execMock).toHaveBeenCalledWith(
-        `${tarPath}`,
+        `"${tarPath}"`,
         [
             "--use-compress-program",
-            "zstd -T0",
+            zstdOptions,
             "-cf",
             IS_WINDOWS
                 ? CacheFilename.Zstd.replace(/\\/g, "/")
@@ -184,7 +187,7 @@ test("gzip create tar", async () => {
 
     expect(execMock).toHaveBeenCalledTimes(1);
     expect(execMock).toHaveBeenCalledWith(
-        `${tarPath}`,
+        `"${tarPath}"`,
         [
             "-z",
             "-cf",
diff --git a/dist/restore/index.js b/dist/restore/index.js
index 96f6fa5..8eeaf0a 100644
--- a/dist/restore/index.js
+++ b/dist/restore/index.js
@@ -2237,10 +2237,9 @@ function createHttpClient() {
     return new http_client_1.HttpClient("actions/cache", [bearerCredentialHandler], getRequestOptions());
 }
 function getCacheVersion(compressionMethod) {
+    const components = [core.getInput(constants_1.Inputs.Path, { required: true })].concat(compressionMethod == constants_1.CompressionMethod.Zstd ? [compressionMethod] : []);
     // Add salt to cache version to support breaking changes in cache entry
-    const components = [core.getInput(constants_1.Inputs.Path, { required: true })].concat(compressionMethod == constants_1.CompressionMethod.Zstd
-        ? [compressionMethod, versionSalt]
-        : versionSalt);
+    components.push(versionSalt);
     return crypto
         .createHash("sha256")
         .update(components.join("|"))
@@ -3320,7 +3319,7 @@ function unlinkFile(path) {
     return util.promisify(fs.unlink)(path);
 }
 exports.unlinkFile = unlinkFile;
-function checkVersion(app) {
+function getVersion(app) {
     return __awaiter(this, void 0, void 0, function* () {
         core.debug(`Checking ${app} --version`);
         let versionOutput = "";
@@ -3344,7 +3343,7 @@ function checkVersion(app) {
 }
 function getCompressionMethod() {
     return __awaiter(this, void 0, void 0, function* () {
-        const versionOutput = yield checkVersion("zstd");
+        const versionOutput = yield getVersion("zstd");
         return versionOutput.toLowerCase().includes("zstd command line interface")
             ? constants_1.CompressionMethod.Zstd
             : constants_1.CompressionMethod.Gzip;
@@ -3359,7 +3358,7 @@ function getCacheFileName(compressionMethod) {
 exports.getCacheFileName = getCacheFileName;
 function useGnuTar() {
     return __awaiter(this, void 0, void 0, function* () {
-        const versionOutput = yield checkVersion("tar");
+        const versionOutput = yield getVersion("tar");
         return versionOutput.toLowerCase().includes("gnu tar");
     });
 }
@@ -5068,7 +5067,7 @@ function execTar(args, cwd) {
     var _a;
     return __awaiter(this, void 0, void 0, function* () {
         try {
-            yield exec_1.exec(`${yield getTarPath(args)}`, args, { cwd: cwd });
+            yield exec_1.exec(`"${yield getTarPath(args)}"`, args, { cwd: cwd });
         }
         catch (error) {
             throw new Error(`Tar failed with error: ${(_a = error) === null || _a === void 0 ? void 0 : _a.message}`);
@@ -5079,14 +5078,22 @@ function getWorkingDirectory() {
     var _a;
     return _a = process.env["GITHUB_WORKSPACE"], (_a !== null && _a !== void 0 ? _a : process.cwd());
 }
+function isOS64() {
+    return process.platform != "win32" || process.arch === "x64";
+}
 function extractTar(archivePath, compressionMethod) {
     return __awaiter(this, void 0, void 0, function* () {
         // Create directory to extract tar into
         const workingDirectory = getWorkingDirectory();
         yield io.mkdirP(workingDirectory);
+        // --d: Decompress. 
+        // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit.
         const args = [
             ...(compressionMethod == constants_1.CompressionMethod.Zstd
-                ? ["--use-compress-program", "zstd -d"]
+                ? [
+                    "--use-compress-program",
+                    isOS64() ? "zstd -d --long=31" : "zstd -d --long=30"
+                ]
                 : ["-z"]),
             "-xf",
             archivePath.replace(new RegExp("\\" + path.sep, "g"), "/"),
@@ -5105,10 +5112,14 @@ function createTar(archiveFolder, sourceDirectories, compressionMethod) {
         const cacheFileName = utils.getCacheFileName(compressionMethod);
         fs_1.writeFileSync(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.
+        // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit.
         const workingDirectory = getWorkingDirectory();
         const args = [
             ...(compressionMethod == constants_1.CompressionMethod.Zstd
-                ? ["--use-compress-program", "zstd -T0"]
+                ? [
+                    "--use-compress-program",
+                    isOS64() ? "zstd -T0 --long=31" : "zstd -T0 --long=30"
+                ]
                 : ["-z"]),
             "-cf",
             cacheFileName.replace(new RegExp("\\" + path.sep, "g"), "/"),
diff --git a/dist/save/index.js b/dist/save/index.js
index 0e080cb..2945161 100644
--- a/dist/save/index.js
+++ b/dist/save/index.js
@@ -2237,10 +2237,9 @@ function createHttpClient() {
     return new http_client_1.HttpClient("actions/cache", [bearerCredentialHandler], getRequestOptions());
 }
 function getCacheVersion(compressionMethod) {
+    const components = [core.getInput(constants_1.Inputs.Path, { required: true })].concat(compressionMethod == constants_1.CompressionMethod.Zstd ? [compressionMethod] : []);
     // Add salt to cache version to support breaking changes in cache entry
-    const components = [core.getInput(constants_1.Inputs.Path, { required: true })].concat(compressionMethod == constants_1.CompressionMethod.Zstd
-        ? [compressionMethod, versionSalt]
-        : versionSalt);
+    components.push(versionSalt);
     return crypto
         .createHash("sha256")
         .update(components.join("|"))
@@ -3320,7 +3319,7 @@ function unlinkFile(path) {
     return util.promisify(fs.unlink)(path);
 }
 exports.unlinkFile = unlinkFile;
-function checkVersion(app) {
+function getVersion(app) {
     return __awaiter(this, void 0, void 0, function* () {
         core.debug(`Checking ${app} --version`);
         let versionOutput = "";
@@ -3344,7 +3343,7 @@ function checkVersion(app) {
 }
 function getCompressionMethod() {
     return __awaiter(this, void 0, void 0, function* () {
-        const versionOutput = yield checkVersion("zstd");
+        const versionOutput = yield getVersion("zstd");
         return versionOutput.toLowerCase().includes("zstd command line interface")
             ? constants_1.CompressionMethod.Zstd
             : constants_1.CompressionMethod.Gzip;
@@ -3359,7 +3358,7 @@ function getCacheFileName(compressionMethod) {
 exports.getCacheFileName = getCacheFileName;
 function useGnuTar() {
     return __awaiter(this, void 0, void 0, function* () {
-        const versionOutput = yield checkVersion("tar");
+        const versionOutput = yield getVersion("tar");
         return versionOutput.toLowerCase().includes("gnu tar");
     });
 }
@@ -5045,7 +5044,7 @@ function execTar(args, cwd) {
     var _a;
     return __awaiter(this, void 0, void 0, function* () {
         try {
-            yield exec_1.exec(`${yield getTarPath(args)}`, args, { cwd: cwd });
+            yield exec_1.exec(`"${yield getTarPath(args)}"`, args, { cwd: cwd });
         }
         catch (error) {
             throw new Error(`Tar failed with error: ${(_a = error) === null || _a === void 0 ? void 0 : _a.message}`);
@@ -5056,14 +5055,22 @@ function getWorkingDirectory() {
     var _a;
     return _a = process.env["GITHUB_WORKSPACE"], (_a !== null && _a !== void 0 ? _a : process.cwd());
 }
+function isOS64() {
+    return process.platform != "win32" || process.arch === "x64";
+}
 function extractTar(archivePath, compressionMethod) {
     return __awaiter(this, void 0, void 0, function* () {
         // Create directory to extract tar into
         const workingDirectory = getWorkingDirectory();
         yield io.mkdirP(workingDirectory);
+        // --d: Decompress. 
+        // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit.
         const args = [
             ...(compressionMethod == constants_1.CompressionMethod.Zstd
-                ? ["--use-compress-program", "zstd -d"]
+                ? [
+                    "--use-compress-program",
+                    isOS64() ? "zstd -d --long=31" : "zstd -d --long=30"
+                ]
                 : ["-z"]),
             "-xf",
             archivePath.replace(new RegExp("\\" + path.sep, "g"), "/"),
@@ -5082,10 +5089,14 @@ function createTar(archiveFolder, sourceDirectories, compressionMethod) {
         const cacheFileName = utils.getCacheFileName(compressionMethod);
         fs_1.writeFileSync(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.
+        // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit.
         const workingDirectory = getWorkingDirectory();
         const args = [
             ...(compressionMethod == constants_1.CompressionMethod.Zstd
-                ? ["--use-compress-program", "zstd -T0"]
+                ? [
+                    "--use-compress-program",
+                    isOS64() ? "zstd -T0 --long=31" : "zstd -T0 --long=30"
+                ]
                 : ["-z"]),
             "-cf",
             cacheFileName.replace(new RegExp("\\" + path.sep, "g"), "/"),
diff --git a/src/cacheHttpClient.ts b/src/cacheHttpClient.ts
index 4ffafbd..c000b7f 100644
--- a/src/cacheHttpClient.ts
+++ b/src/cacheHttpClient.ts
@@ -86,13 +86,13 @@ function createHttpClient(): HttpClient {
 }
 
 export function getCacheVersion(compressionMethod?: CompressionMethod): string {
-    // Add salt to cache version to support breaking changes in cache entry
     const components = [core.getInput(Inputs.Path, { required: true })].concat(
-        compressionMethod == CompressionMethod.Zstd
-            ? [compressionMethod, versionSalt]
-            : versionSalt
+        compressionMethod == CompressionMethod.Zstd ? [compressionMethod] : []
     );
 
+    // Add salt to cache version to support breaking changes in cache entry
+    components.push(versionSalt);
+
     return crypto
         .createHash("sha256")
         .update(components.join("|"))
diff --git a/src/tar.ts b/src/tar.ts
index 2a6caff..8aa7b0a 100644
--- a/src/tar.ts
+++ b/src/tar.ts
@@ -22,7 +22,7 @@ async function getTarPath(args: string[]): Promise<string> {
 
 async function execTar(args: string[], cwd?: string): Promise<void> {
     try {
-        await exec(`${await getTarPath(args)}`, args, { cwd: cwd });
+        await exec(`"${await getTarPath(args)}"`, args, { cwd: cwd });
     } catch (error) {
         throw new Error(`Tar failed with error: ${error?.message}`);
     }
@@ -32,6 +32,10 @@ function getWorkingDirectory(): string {
     return process.env["GITHUB_WORKSPACE"] ?? process.cwd();
 }
 
+function isOS64(): boolean {
+    return process.platform != "win32" || process.arch === "x64";
+}
+
 export async function extractTar(
     archivePath: string,
     compressionMethod: CompressionMethod
@@ -39,9 +43,14 @@ export async function extractTar(
     // Create directory to extract tar into
     const workingDirectory = getWorkingDirectory();
     await io.mkdirP(workingDirectory);
+    // --d: Decompress.
+    // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit.
     const args = [
         ...(compressionMethod == CompressionMethod.Zstd
-            ? ["--use-compress-program", "zstd -d"]
+            ? [
+                  "--use-compress-program",
+                  isOS64() ? "zstd -d --long=31" : "zstd -d --long=30"
+              ]
             : ["-z"]),
         "-xf",
         archivePath.replace(new RegExp("\\" + path.sep, "g"), "/"),
@@ -65,10 +74,14 @@ export async function createTar(
         sourceDirectories.join("\n")
     );
     // -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores.
+    // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit.
     const workingDirectory = getWorkingDirectory();
     const args = [
         ...(compressionMethod == CompressionMethod.Zstd
-            ? ["--use-compress-program", "zstd -T0"]
+            ? [
+                  "--use-compress-program",
+                  isOS64() ? "zstd -T0 --long=31" : "zstd -T0 --long=30"
+              ]
             : ["-z"]),
         "-cf",
         cacheFileName.replace(new RegExp("\\" + path.sep, "g"), "/"),
diff --git a/src/utils/actionUtils.ts b/src/utils/actionUtils.ts
index 152aa8b..12c1c36 100644
--- a/src/utils/actionUtils.ts
+++ b/src/utils/actionUtils.ts
@@ -124,7 +124,7 @@ export function unlinkFile(path: fs.PathLike): Promise<void> {
     return util.promisify(fs.unlink)(path);
 }
 
-async function checkVersion(app: string): Promise<string> {
+async function getVersion(app: string): Promise<string> {
     core.debug(`Checking ${app} --version`);
     let versionOutput = "";
     try {
@@ -148,7 +148,7 @@ async function checkVersion(app: string): Promise<string> {
 }
 
 export async function getCompressionMethod(): Promise<CompressionMethod> {
-    const versionOutput = await checkVersion("zstd");
+    const versionOutput = await getVersion("zstd");
     return versionOutput.toLowerCase().includes("zstd command line interface")
         ? CompressionMethod.Zstd
         : CompressionMethod.Gzip;
@@ -161,6 +161,6 @@ export function getCacheFileName(compressionMethod: CompressionMethod): string {
 }
 
 export async function useGnuTar(): Promise<boolean> {
-    const versionOutput = await checkVersion("tar");
+    const versionOutput = await getVersion("tar");
     return versionOutput.toLowerCase().includes("gnu tar");
 }