Repository: firecow/gitlab-ci-local
Pull request #1244 (2024-06-02 06:14:27)

fix: include:component should support custom ssh port
- fixes https://github.com/firecow/gitlab-ci-local/pull/1239#discussion_r1621839688 @stiv-pro
- refactor unit test
- refactor to use `Utils.spawn` instead of `Utils.bash`

sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1244) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=firecow_gitlab-ci-local&pullRequest=1244&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=firecow_gitlab-ci-local&pullRequest=1244&resolutions=WONTFIX)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=firecow_gitlab-ci-local&pullRequest=1244&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1244&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1244&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1244)



Patch:
diff --git a/src/parser-includes.ts b/src/parser-includes.ts
index 04fa2a4..de295a3 100644
--- a/src/parser-includes.ts
+++ b/src/parser-includes.ts
@@ -110,7 +110,7 @@ export class ParserIncludes {
                 const files = [`${componentName}.yml`, `${componentName}/template.yml`, null];
                 for (const f of files) {
                     assert(f !== null, `This GitLab CI configuration is invalid: component: \`${value["component"]}\`. One of the file [${files}] must exists in \`${domain}/${projectPath}\``);
-                    if (!(await Utils.remoteFileExist(f, ref, domain, projectPath, gitData.remote.schema))) continue;
+                    if (!(await Utils.remoteFileExist(cwd, f, ref, domain, projectPath, gitData.remote.schema, gitData.remote.port))) continue;
                     const fileDoc = {
                         include: {
                             project: projectPath,
diff --git a/src/utils.ts b/src/utils.ts
index 48f844f..575b4f7 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -283,12 +283,12 @@ export class Utils {
         return Object.getPrototypeOf(v) === Object.prototype;
     }
 
-    static async remoteFileExist (file: string, ref: string, domain: string, projectPath: string, protocol: GitSchema) {
+    static async remoteFileExist (cwd: string, file: string, ref: string, domain: string, projectPath: string, protocol: GitSchema, port: string) {
         switch (protocol) {
             case "ssh":
             case "git":
                 try {
-                    await Utils.bash(`git archive --remote=ssh://git@${domain}/${projectPath}.git ${ref} ${file} > /dev/null`);
+                    await Utils.spawn(`git archive --remote=ssh://git@${domain}:${port}/${projectPath}.git ${ref} ${file}`.split(" "), cwd);
                     return true;
                 } catch (e: any) {
                     if (!e.stderr.includes(`remote: fatal: pathspec '${file}' did not match any files`)) throw new Error(e);


Test patch:
diff --git a/tests/utils.test.ts b/tests/utils.test.ts
index d1ffb30..d38426f 100644
--- a/tests/utils.test.ts
+++ b/tests/utils.test.ts
@@ -1,48 +1,53 @@
 import {GitData} from "../src/git-data";
 import {Utils} from "../src/utils";
-import {initBashSpyReject, initBashSpy} from "./mocks/utils.mock";
+import {initSpawnSpyReject, initSpawnSpy} from "./mocks/utils.mock";
 
 import {isSshDirFound} from "./utils";
 
-test("remoteFileExist protocol: git", async () => {
-    const file = "templates/test.yml";
-    const ref = "0.3.1";
-    const domain = "gitlab.com";
-    const projectPath = "components/go";
+describe("remoteFileExist", () => {
+    describe("protocol: git", () => {
+        const ref = "0.3.1";
+        const domain = "gitlab.com";
+        const projectPath = "components/go";
+        const port = "22";
+        const cwd = ".";
 
-    // NOTE: Only mocks git archive command if `~/.ssh` ssh dir is not found
-    if (!isSshDirFound()) {
-        const spyGitArchive = {
-            cmd: `git archive --remote=ssh://git@${domain}/${projectPath}.git ${ref} ${file} > /dev/null`,
-            returnValue: {output: ""},
-        };
-        initBashSpy([spyGitArchive]);
-    }
+        test("exists", async () => {
+            const file = "templates/test.yml";
 
-    const fileExist = await Utils.remoteFileExist(file, ref, domain, projectPath, "git");
-    expect(fileExist).toBe(true);
-});
+            // NOTE: Only mocks git archive command if `~/.ssh` ssh dir is not found because we have no setup ssh keys in ci env
+            if (!isSshDirFound()) {
+                const spyGitArchive = {
+                    cmdArgs: `git archive --remote=ssh://git@${domain}:${port}/${projectPath}.git ${ref} ${file}`.split(" "),
+                    returnValue: {output: ""},
+                };
+                initSpawnSpy([spyGitArchive]);
+            }
+
+            const fileExist = await Utils.remoteFileExist(cwd, file, ref, domain, projectPath, "git", port);
+            expect(fileExist).toBe(true);
+        });
 
-test("remoteFileDoesNotExist protocol: git", async () => {
-    const file = "templates/potato.yml";
-    const ref = "0.3.1";
-    const domain = "gitlab.com";
-    const projectPath = "components/go";
+        test("don't exists", async () => {
+            const file = "templates/potato.yml";
 
-    // NOTE: Only mocks git archive command if `~/.ssh` ssh dir is not found
-    if (!isSshDirFound()) {
-        const spyGitArchive = {
-            cmd: `git archive --remote=ssh://git@${domain}/${projectPath}.git ${ref} ${file} > /dev/null`,
-            rejection: {
-                stderr: `fatal: sent error to the client: git upload-archive: archiver died with error
+            // NOTE: Only mocks git archive command if `~/.ssh` ssh dir is not found because we have no setup ssh keys in ci env
+            if (!isSshDirFound()) {
+                const spyGitArchive = {
+                    cmdArgs: `git archive --remote=ssh://git@${domain}:${port}/${projectPath}.git ${ref} ${file}`.split(" "),
+                    rejection: {
+                        stderr: `fatal: sent error to the client: git upload-archive: archiver died with error
 remote: fatal: pathspec 'templates/potato.yml' did not match any files
 remote: git upload-archive: archiver died with error`,
-            },
-        };
-        initBashSpyReject([spyGitArchive]);
-    }
-    const fileExist = await Utils.remoteFileExist(file, ref, domain, projectPath, "git");
-    expect(fileExist).toBe(false);
+                    },
+                };
+                initSpawnSpyReject([spyGitArchive]);
+            }
+
+            const fileExist = await Utils.remoteFileExist(cwd, file, ref, domain, projectPath, "git", port);
+            expect(fileExist).toBe(false);
+        });
+    });
 });
 
 describe("evaluateRuleChanges", () => {