diff --git a/README.md b/README.md index 3240f5b1e..5052cbc79 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Get rid of all those dev specific shell scripts and make files. * [Decorators](#decorators) * [Includes](#includes) * [Artifacts](#artifacts) + * [Self Hosted Custom Ports](#self-hosted-custom-ports) * [Development](#development) * [Scripts](#scripts) * [Package binaries](#package-binaries) @@ -358,6 +359,18 @@ handling for shell jobs. Docker executor copies artifacts to and from .gitlab-ci-local/artifacts +### Self Hosted Custom Ports + +If your self-hosted GitLab instance uses custom ports, it is recommended to manually define the `CI_SERVER_PORT` and/or `CI_SERVER_SHELL_SSH_PORT` variables accordingly. + +```yaml +--- +# $CWD/.gitlab-ci-local-variables.yml + +CI_SERVER_PORT: 8443 +CI_SERVER_SHELL_SSH_PORT: 8022 +``` + ## Development You need nodejs 18+ diff --git a/src/job.ts b/src/job.ts index 92ee221ec..39ea7a3d8 100644 --- a/src/job.ts +++ b/src/job.ts @@ -74,6 +74,9 @@ export type JobRule = { }; export class Job { + private generateJobId (): number { + return Math.floor(Math.random() * 1000000); + } static readonly illegalJobNames = new Set([ "include", "local_configuration", "image", "services", @@ -136,7 +139,7 @@ export class Job { this.gitData = opt.gitData; this.name = opt.name; this.baseName = opt.baseName; - this.jobId = Math.floor(Math.random() * 1000000); + this.jobId = this.generateJobId(); this.jobData = opt.data; this.pipelineIid = opt.pipelineIid; @@ -170,8 +173,8 @@ export class Job { predefinedVariables["CI_JOB_NAME_SLUG"] = `${this.name.replace(/[^a-z\d]+/ig, "-").replace(/^-/, "").slice(0, 63).replace(/-$/, "").toLowerCase()}`; predefinedVariables["CI_JOB_STAGE"] = `${this.stage}`; predefinedVariables["CI_PROJECT_DIR"] = ciProjectDir; - predefinedVariables["CI_JOB_URL"] = `https://${gitData.remote.host}/${gitData.remote.group}/${gitData.remote.project}/-/jobs/${this.jobId}`; // Changes on rerun. - predefinedVariables["CI_PIPELINE_URL"] = `https://${gitData.remote.host}/${gitData.remote.group}/${gitData.remote.project}/pipelines/${this.pipelineIid}`; + predefinedVariables["CI_JOB_URL"] = `${this._variables["CI_SERVER_URL"]}/${gitData.remote.group}/${gitData.remote.project}/-/jobs/${this.jobId}`; // Changes on rerun. + predefinedVariables["CI_PIPELINE_URL"] = `${this._variables["CI_SERVER_URL"]}/${gitData.remote.group}/${gitData.remote.project}/pipelines/${this.pipelineIid}`; predefinedVariables["CI_ENVIRONMENT_NAME"] = this.environment?.name ?? ""; predefinedVariables["CI_ENVIRONMENT_SLUG"] = this.environment?.name?.replace(/[^a-z\d]+/ig, "-").replace(/^-/, "").slice(0, 23).replace(/-$/, "").toLowerCase() ?? ""; predefinedVariables["CI_ENVIRONMENT_URL"] = this.environment?.url ?? ""; diff --git a/src/parser-includes.ts b/src/parser-includes.ts index 37e2ac360..1f7cc2904 100644 --- a/src/parser-includes.ts +++ b/src/parser-includes.ts @@ -105,12 +105,13 @@ export class ParserIncludes { includeDatas = includeDatas.concat(await this.init(fileDoc, opts)); } } else if (value["component"]) { - const {domain, projectPath, componentName, ref} = this.parseIncludeComponent(value["component"]); + const {domain, port, projectPath, componentName, ref} = this.parseIncludeComponent(value["component"]); // converts component to project 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}\``); + assert(f !== null, `This GitLab CI configuration is invalid: component: \`${value["component"]}\`. One of the files [${files}] must exist in \`${domain}` + + (port ? `:${port}` : "") + `/${projectPath}\``); const isLocalComponent = projectPath === `${gitData.remote.group}/${gitData.remote.project}` && ref === gitData.commit.SHA; if (isLocalComponent) { @@ -199,15 +200,16 @@ export class ParserIncludes { }; } - static parseIncludeComponent (component: string): {domain: string; projectPath: string; componentName: string; ref: string} { + static parseIncludeComponent (component: string): {domain: string; port: string; projectPath: string; componentName: string; ref: string} { assert(!component.includes("://"), `This GitLab CI configuration is invalid: component: \`${component}\` should not contain protocol`); // eslint-disable-next-line no-useless-escape - const pattern = /(?[^/\s]+)\/(?.+)\/(?[^@]+)@(?.+)/; // regexr.com/7v7hm + const pattern = /(?[^/:\s]+)(:(?\d+))?\/(?.+)\/(?[^@]+)@(?.+)/; // https://regexr.com/7v7hm const gitRemoteMatch = pattern.exec(component); if (gitRemoteMatch?.groups == null) throw new Error(`This is a bug, please create a github issue if this is something you're expecting to work. input: ${component}`); return { domain: gitRemoteMatch.groups["domain"], + port: gitRemoteMatch.groups["port"], projectPath: gitRemoteMatch.groups["projectPath"], componentName: `templates/${gitRemoteMatch.groups["componentName"]}`, ref: gitRemoteMatch.groups["ref"], @@ -239,7 +241,7 @@ export class ParserIncludes { await Utils.bash(` cd ${cwd}/${stateDir} \\ && git clone --branch "${ref}" -n --depth=1 --filter=tree:0 \\ - ${remote.schema}://${remote.host}/${project}.git \\ + ${remote.schema}://${remote.host}:${remote.port}/${project}.git \\ ${cwd}/${target}.${ext} \\ && cd ${cwd}/${target}.${ext} \\ && git sparse-checkout set --no-cone ${normalizedFile} \\ diff --git a/src/parser.ts b/src/parser.ts index ca95e4290..896336599 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -98,8 +98,8 @@ export class Parser { const fetchIncludes = argv.fetchIncludes; const gitData = await GitData.init(cwd, writeStreams); const variablesFromFiles = await VariablesFromFiles.init(argv, writeStreams, gitData); - const predefinedVariables = initPredefinedVariables({gitData, argv}); const envMatchedVariables = Utils.findEnvMatchedVariables(variablesFromFiles); + const predefinedVariables = initPredefinedVariables({gitData, argv, envMatchedVariables}); const variables = {...predefinedVariables, ...envMatchedVariables, ...argv.variable}; const expanded = Utils.expandVariables(variables); diff --git a/src/predefined-variables.ts b/src/predefined-variables.ts index 94e0e6a86..235fe80b8 100644 --- a/src/predefined-variables.ts +++ b/src/predefined-variables.ts @@ -3,10 +3,45 @@ import {GitData} from "./git-data.js"; type PredefinedVariablesOpts = { gitData: GitData; - argv: {unsetVariables: string[]}; + argv: { + unsetVariables: string[]; + variable: {[key: string]: string}; + }; + envMatchedVariables: {[key: string]: string}; }; -export function init ({gitData, argv}: PredefinedVariablesOpts): {[name: string]: string} { +export function init ({gitData, argv, envMatchedVariables}: PredefinedVariablesOpts): {[name: string]: string} { + + const _variables = {...envMatchedVariables, ...argv.variable}; + + // precedence: + // 1. cli option + // 2. gitlab variables files + // 3. values derieved implicitly from `git remote -v` + // 4. default value + const CI_SERVER_PROTOCOL = _variables["CI_SERVER_PROTOCOL"] + ?? ((gitData.remote.schema === "http" || gitData.remote.schema === "https") + ? gitData.remote.schema + : "https"); + const CI_SERVER_PORT = _variables["CI_SERVER_PORT"] + ?? ((gitData.remote.schema === "http" || gitData.remote.schema === "https") + ? gitData.remote.port + : "443"); + const CI_SERVER_SHELL_SSH_PORT = _variables["CI_SERVER_SHELL_SSH_PORT"] + ?? ((gitData.remote.schema === "ssh") + ? gitData.remote.port + : "22"); + const CI_SERVER_HOST = _variables["CI_SERVER_HOST"] + ?? `${gitData.remote.host}`; + const CI_SERVER_FQDN = _variables["CI_SERVER_FQDN"] + ?? (CI_SERVER_PORT == "443" + ? gitData.remote.host + : `${gitData.remote.host}:${CI_SERVER_PORT}`); + const CI_SERVER_URL = _variables["CI_SERVER_URL"] + ?? `${CI_SERVER_PROTOCOL}://${CI_SERVER_FQDN}`; + const CI_PROJECT_ROOT_NAMESPACE = gitData.remote.group.split("/")[0]; + const CI_PROJECT_NAMESPACE = gitData.remote.group; + const predefinedVariables: {[key: string]: string} = { CI: "true", GITLAB_USER_LOGIN: gitData.user["GITLAB_USER_LOGIN"], @@ -19,7 +54,8 @@ export function init ({gitData, argv}: PredefinedVariablesOpts): {[name: string] CI_PROJECT_TITLE: `${camelCase(gitData.remote.project)}`, CI_PROJECT_PATH: `${gitData.remote.group}/${gitData.remote.project}`, CI_PROJECT_PATH_SLUG: `${gitData.remote.group.replace(/\//g, "-")}-${gitData.remote.project}`.toLowerCase(), - CI_PROJECT_NAMESPACE: `${gitData.remote.group}`, + CI_PROJECT_ROOT_NAMESPACE: CI_PROJECT_ROOT_NAMESPACE, + CI_PROJECT_NAMESPACE: CI_PROJECT_NAMESPACE, CI_PROJECT_VISIBILITY: "internal", CI_PROJECT_ID: "1217", CI_COMMIT_REF_PROTECTED: "false", @@ -28,19 +64,19 @@ export function init ({gitData, argv}: PredefinedVariablesOpts): {[name: string] CI_COMMIT_REF_SLUG: gitData.commit.REF_NAME.replace(/[^a-z\d]+/ig, "-").replace(/^-/, "").slice(0, 63).replace(/-$/, "").toLowerCase(), CI_COMMIT_TIMESTAMP: gitData.commit.TIMESTAMP, CI_PIPELINE_CREATED_AT: new Date().toISOString().split(".")[0] + "Z", - CI_JOB_STARTED_AT: new Date().toISOString().split(".")[0] + "Z", CI_COMMIT_TITLE: "Commit Title", // First line of commit message. CI_COMMIT_MESSAGE: "Commit Title\nMore commit text", // Full commit message CI_COMMIT_DESCRIPTION: "More commit text", CI_DEFAULT_BRANCH: gitData.branches.default, CI_PIPELINE_SOURCE: "push", - CI_SERVER_FQDN: `${gitData.remote.host}`, - CI_SERVER_HOST: `${gitData.remote.host}`, - CI_SERVER_PORT: `${gitData.remote.port}`, - CI_SERVER_URL: `https://${gitData.remote.host}:443`, - CI_SERVER_PROTOCOL: "https", - CI_API_V4_URL: `https://${gitData.remote.host}/api/v4`, - CI_PROJECT_URL: `https://${gitData.remote.host}/${gitData.remote.group}/${gitData.remote.project}`, + CI_SERVER_FQDN: CI_SERVER_FQDN, + CI_SERVER_HOST: CI_SERVER_HOST, + CI_SERVER_PORT: CI_SERVER_PORT, + CI_SERVER_SHELL_SSH_PORT: CI_SERVER_SHELL_SSH_PORT, + CI_SERVER_URL: CI_SERVER_URL, + CI_SERVER_PROTOCOL: CI_SERVER_PROTOCOL, + CI_API_V4_URL: `${CI_SERVER_URL}/api/v4`, + CI_PROJECT_URL: `${CI_SERVER_URL}/${gitData.remote.group}/${gitData.remote.project}`, CI_TEMPLATE_REGISTRY_HOST: "registry.gitlab.com", GITLAB_CI: "false", }; diff --git a/src/utils.ts b/src/utils.ts index d86800874..f596863a4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -345,7 +345,7 @@ ${evalStr} case "http": case "https": { try { - const {status} = await axios.get(`${protocol}://${domain}/${projectPath}/-/raw/${ref}/${file}`); + const {status} = await axios.get(`${protocol}://${domain}:${port}/${projectPath}/-/raw/${ref}/${file}`); return (status === 200); } catch (e) { return false; diff --git a/tests/test-cases/include-component/integration.include-component.test.ts b/tests/test-cases/include-component/integration.include-component.test.ts index d9099daab..9c0bcf164 100644 --- a/tests/test-cases/include-component/integration.include-component.test.ts +++ b/tests/test-cases/include-component/integration.include-component.test.ts @@ -20,7 +20,7 @@ test.concurrent("include-component no component template file (protocol: https)" expect(true).toBe(false); } catch (e: any) { assert(e instanceof AssertionError, `Unexpected error thrown:\n ${e}`); - expect(e.message).toBe("This GitLab CI configuration is invalid: component: `gitlab.com/components/go/potato@0.3.1`. One of the file [templates/potato.yml,templates/potato/template.yml,] must exists in `gitlab.com/components/go`"); + expect(e.message).toBe("This GitLab CI configuration is invalid: component: `gitlab.com/components/go/potato@0.3.1`. One of the files [templates/potato.yml,templates/potato/template.yml,] must exist in `gitlab.com/components/go`"); } }); diff --git a/tests/test-cases/predefined-variables/.gitlab-ci.yml b/tests/test-cases/predefined-variables/.gitlab-ci.yml index be9d69f80..804e3eb11 100644 --- a/tests/test-cases/predefined-variables/.gitlab-ci.yml +++ b/tests/test-cases/predefined-variables/.gitlab-ci.yml @@ -1,16 +1,9 @@ --- test-job: + image: busybox script: - - echo ${CI_PROJECT_NAME} - - echo ${CI_PROJECT_PATH} - - echo ${CI_PROJECT_PATH_SLUG} - - echo ${CI_PROJECT_NAMESPACE} - - echo ${CI_PROJECT_DIR} - - echo ${CI_DEFAULT_BRANCH} - - echo ${CI_REGISTRY_IMAGE} - - echo ${CI_NODE_INDEX}/${CI_NODE_TOTAL} + - env | sort | grep -Ev "^PATH|^HOSTNAME|^HOME=|^More commit text$|^SHLVL" -test-job-commit-short-length: +shell-isolation: script: - - echo ${CI_COMMIT_SHORT_SHA} - - "[ ${#CI_COMMIT_SHORT_SHA} -eq 8 ] || exit 1" + - echo ${CI_PROJECT_DIR} diff --git a/tests/test-cases/predefined-variables/integration.predefined-variables.test.ts b/tests/test-cases/predefined-variables/integration.predefined-variables.test.ts index b31aeae1b..16042bd1f 100644 --- a/tests/test-cases/predefined-variables/integration.predefined-variables.test.ts +++ b/tests/test-cases/predefined-variables/integration.predefined-variables.test.ts @@ -3,56 +3,207 @@ import {handler} from "../../../src/handler.js"; import chalk from "chalk"; import {initSpawnSpy} from "../../mocks/utils.mock.js"; import {WhenStatics} from "../../mocks/when-statics.js"; +import {stripAnsi} from "../../utils.js"; +import {Job} from "../../../src/job.js"; +import path from "path"; +import fs from "fs-extra"; -test("predefined-variables ", async () => { - const writeStreams = new WriteStreamsMock(); +const jest = import.meta.jest; +let jobIdSpy: jest.SpyInstance; +let dateSpy: jest.SpyInstance; + +const mockJobId = 123; +const mockDate = "2020-01-05T00:00:00Z"; + +const envVars: {[key: string]: string} = { + CI: "true", + CI_API_V4_URL: "https://gitlab.com/api/v4", + CI_COMMIT_BRANCH: "master", + CI_COMMIT_DESCRIPTION: "More commit text", + CI_COMMIT_MESSAGE: "Commit Title", + CI_COMMIT_REF_NAME: "master", + CI_COMMIT_REF_PROTECTED: "false", + CI_COMMIT_REF_SLUG: "master", + CI_COMMIT_SHA: "02618988a1864b3d06cfee3bd79f8baa2dd21407", + CI_COMMIT_SHORT_SHA: "02618988", + CI_COMMIT_TIMESTAMP: mockDate, + CI_COMMIT_TITLE: "Commit Title", + CI_DEFAULT_BRANCH: "main", + CI_ENVIRONMENT_ACTION: "", + CI_ENVIRONMENT_NAME: "", + CI_ENVIRONMENT_SLUG: "", + CI_ENVIRONMENT_TIER: "", + CI_ENVIRONMENT_URL: "", + CI_JOB_ID: `${mockJobId}`, + CI_JOB_NAME: "test-job", + CI_JOB_NAME_SLUG: "test-job", + CI_JOB_STAGE: "test", + CI_JOB_STARTED_AT: mockDate, + CI_JOB_STATUS: "running", + CI_JOB_URL: `https://gitlab.com/GCL/predefined-variables/-/jobs/${mockJobId}`, + CI_NODE_TOTAL: "1", + CI_PIPELINE_CREATED_AT: mockDate, + CI_PIPELINE_ID: "1000", + CI_PIPELINE_IID: "0", + CI_PIPELINE_SOURCE: "push", + CI_PIPELINE_URL: "https://gitlab.com/GCL/predefined-variables/pipelines/0", + CI_PROJECT_DIR: "/gcl-builds", + CI_PROJECT_ID: "1217", + CI_PROJECT_NAME: "predefined-variables", + CI_PROJECT_NAMESPACE: "GCL", + CI_PROJECT_PATH: "GCL/predefined-variables", + CI_PROJECT_PATH_SLUG: "gcl-predefined-variables", + CI_PROJECT_ROOT_NAMESPACE: "GCL", + CI_PROJECT_TITLE: "predefinedVariables", + CI_PROJECT_URL: "https://gitlab.com/GCL/predefined-variables", + CI_PROJECT_VISIBILITY: "internal", + CI_REGISTRY: "local-registry.gitlab.com", + CI_REGISTRY_IMAGE: "local-registry.gitlab.com/gcl/predefined-variables", + CI_SERVER_FQDN: "gitlab.com", + CI_SERVER_HOST: "gitlab.com", + CI_SERVER_PORT: "443", + CI_SERVER_PROTOCOL: "https", + CI_SERVER_SHELL_SSH_PORT: "22", + CI_SERVER_URL: "https://gitlab.com", + CI_TEMPLATE_REGISTRY_HOST: "registry.gitlab.com", + FF_DISABLE_UMASK_FOR_DOCKER_EXECUTOR: "false", + GITLAB_CI: "false", + GITLAB_USER_EMAIL: "test@test.com", + GITLAB_USER_ID: "990", + GITLAB_USER_LOGIN: "test", + GITLAB_USER_NAME: "Testersen", + OLDPWD: "/gcl-builds", + PWD: "/gcl-builds", +}; + + +const cwd = "tests/test-cases/predefined-variables"; +const fileVariable = path.join(cwd, ".gitlab-ci-local-variables.yml"); + +beforeAll(() => { const spyGitRemote = { cmdArgs: ["git", "remote", "get-url", "origin"], returnValue: {stdout: "git@gitlab.com:GCL/predefined-variables.git"}, }; initSpawnSpy([...WhenStatics.all, spyGitRemote]); - await handler({ - cwd: "tests/test-cases/predefined-variables", - job: ["test-job"], - }, writeStreams); + jobIdSpy = jest.spyOn( + Job.prototype as any, + "generateJobId" + ); + jobIdSpy.mockReturnValue(mockJobId); - const expected = [ - chalk`{blueBright test-job} {greenBright >} predefined-variables`, - chalk`{blueBright test-job} {greenBright >} GCL/predefined-variables`, - chalk`{blueBright test-job} {greenBright >} gcl-predefined-variables`, - chalk`{blueBright test-job} {greenBright >} GCL`, - chalk`{blueBright test-job} {greenBright >} ${process.cwd()}/tests/test-cases/predefined-variables`, - chalk`{blueBright test-job} {greenBright >} main`, - chalk`{blueBright test-job} {greenBright >} local-registry.gitlab.com/gcl/predefined-variables`, - chalk`{blueBright test-job} {greenBright >} /1`, - ]; - expect(writeStreams.stdoutLines).toEqual(expect.arrayContaining(expected)); + const _mockDate = new Date(mockDate); + dateSpy = jest.spyOn(global, "Date").mockImplementation(() => _mockDate); }); -test("predefined-variables --shell-isolation", async () => { - const writeStreams = new WriteStreamsMock(); - await handler({ - cwd: "tests/test-cases/predefined-variables", - job: ["test-job"], - shellIsolation: true, - }, writeStreams); +describe("predefined-variables", () => { + beforeEach(() => { + fs.createFileSync(fileVariable); + }); + afterEach(() => { + fs.removeSync(fileVariable); + jest.clearAllMocks(); + }); - const expected = [ - chalk`{blueBright test-job} {greenBright >} ${process.cwd()}/tests/test-cases/predefined-variables/.gitlab-ci-local/builds/test-job`, - ]; - expect(writeStreams.stdoutLines).toEqual(expect.arrayContaining(expected)); + test("normal", async () => { + const writeStreams = new WriteStreamsMock(); + + await handler({ + cwd: cwd, + job: ["test-job"], + shellIsolation: true, + }, writeStreams); + + let expected = ""; + Object.keys(envVars).forEach(key => { + expected += `test-job > ${key}=${envVars[key]}\n`; + }); + + const filteredStdout = stripAnsi(writeStreams.stdoutLines.filter(f => stripAnsi(f).startsWith("test-job > ")).join("\n")); + expect(filteredStdout).toEqual(expected.trim()); + expect(jobIdSpy).toHaveBeenCalledTimes(2); + expect(dateSpy).toHaveBeenCalledTimes(3); + }); + + test("custom ports (via variable-file)", async () => { + const writeStreams = new WriteStreamsMock(); + fs.writeFileSync(fileVariable, ` +CI_SERVER_PORT: 8443 +CI_SERVER_SHELL_SSH_PORT: 8022 +`); + + await handler({ + cwd: cwd, + job: ["test-job"], + }, writeStreams); + + envVars["CI_API_V4_URL"] = "https://gitlab.com:8443/api/v4"; + envVars["CI_JOB_URL"] = `https://gitlab.com:8443/GCL/predefined-variables/-/jobs/${mockJobId}`; + envVars["CI_PIPELINE_URL"] = "https://gitlab.com:8443/GCL/predefined-variables/pipelines/0"; + envVars["CI_PROJECT_URL"] = "https://gitlab.com:8443/GCL/predefined-variables"; + envVars["CI_SERVER_FQDN"] = "gitlab.com:8443"; + envVars["CI_SERVER_PORT"] = "8443"; + envVars["CI_SERVER_SHELL_SSH_PORT"] = "8022"; + envVars["CI_SERVER_URL"] = "https://gitlab.com:8443"; + + let expected = ""; + Object.keys(envVars).forEach(key => { + expected += `test-job > ${key}=${envVars[key]}\n`; + }); + const filteredStdout = stripAnsi(writeStreams.stdoutLines.filter(f => stripAnsi(f).startsWith("test-job > ")).join("\n")); + expect(filteredStdout).toEqual(expected.trim()); + expect(jobIdSpy).toHaveBeenCalledTimes(2); + expect(dateSpy).toHaveBeenCalledTimes(3); + }); + + test("custom ports (via --variable)", async () => { + const writeStreams = new WriteStreamsMock(); + + fs.writeFileSync(fileVariable, ` +CI_SERVER_PORT: 8443 +CI_SERVER_SHELL_SSH_PORT: 8022 +`); + await handler({ + cwd: cwd, + job: ["test-job"], + variable: [ + "CI_SERVER_PORT=9443", + "CI_SERVER_SHELL_SSH_PORT=9022", + ], + }, writeStreams); + + envVars["CI_API_V4_URL"] = "https://gitlab.com:9443/api/v4"; + envVars["CI_JOB_URL"] = `https://gitlab.com:9443/GCL/predefined-variables/-/jobs/${mockJobId}`; + envVars["CI_PIPELINE_URL"] = "https://gitlab.com:9443/GCL/predefined-variables/pipelines/0"; + envVars["CI_PROJECT_URL"] = "https://gitlab.com:9443/GCL/predefined-variables"; + envVars["CI_SERVER_FQDN"] = "gitlab.com:9443"; + envVars["CI_SERVER_PORT"] = "9443"; + envVars["CI_SERVER_SHELL_SSH_PORT"] = "9022"; + envVars["CI_SERVER_URL"] = "https://gitlab.com:9443"; + + let expected = ""; + Object.keys(envVars).forEach(key => { + expected += `test-job > ${key}=${envVars[key]}\n`; + }); + + const filteredStdout = stripAnsi(writeStreams.stdoutLines.filter(f => stripAnsi(f).startsWith("test-job > ")).join("\n")); + expect(filteredStdout).toEqual(expected.trim()); + expect(jobIdSpy).toHaveBeenCalledTimes(2); + expect(dateSpy).toHaveBeenCalledTimes(3); + }); }); -test("predefined-variables CI_COMMIT_SHORT_SHA length", async () => { +test("predefined-variables --shell-isolation", async () => { const writeStreams = new WriteStreamsMock(); await handler({ cwd: "tests/test-cases/predefined-variables", - job: ["test-job-commit-short-length"], + job: ["shell-isolation"], + shellIsolation: true, + shellExecutorNoImage: false, }, writeStreams); const expected = [ - chalk`{black.bgGreenBright PASS } {blueBright test-job-commit-short-length}`, + chalk`{blueBright shell-isolation} {greenBright >} ${process.cwd()}/tests/test-cases/predefined-variables/.gitlab-ci-local/builds/shell-isolation`, ]; - expect(writeStreams.stdoutLines).toEqual(expect.arrayContaining(expected)); });