diff --git a/src/argv.ts b/src/argv.ts index 6e0c9b01..94c52ab9 100644 --- a/src/argv.ts +++ b/src/argv.ts @@ -58,6 +58,10 @@ export class Argv { writeStreams?.stderr(chalk`{black.bgYellowBright WARN } --default-image does not work with --shell-executor-no-image=true\n`); } + if (argv.defaultImageExplicitlySet && argv.forceShellExecutor) { + writeStreams?.stderr(chalk`{black.bgYellowBright WARN } --default-image does not work with --force-shell-executor=true\n`); + } + return argv; } @@ -287,6 +291,10 @@ export class Argv { return this.map.get("shellExecutorNoImage") ?? true; } + get forceShellExecutor (): boolean { + return this.map.get("forceShellExecutor") ?? false; + } + get defaultImage (): string { return this.map.get("defaultImage") ?? "docker.io/ruby:3.1"; } diff --git a/src/index.ts b/src/index.ts index d0bd5bc9..0994b7f5 100755 --- a/src/index.ts +++ b/src/index.ts @@ -172,6 +172,11 @@ process.on("SIGUSR2", async () => await cleanupJobResources(jobs)); description: "Enable artifact isolation for shell-executor jobs", requiresArg: false, }) + .option("force-shell-executor", { + type: "boolean", + description: "Forces all jobs to be executed using the shell executor. (Only use this option for trusted job)", + requiresArg: false, + }) .option("shell-executor-no-image", { type: "boolean", description: "Whether to use shell executor when no image is specified.", diff --git a/src/job.ts b/src/job.ts index 9e0301e7..b13bf115 100644 --- a/src/job.ts +++ b/src/job.ts @@ -991,6 +991,9 @@ export class Job { } private imageName (vars: {[key: string]: string} = {}): string | null { + if (this.argv.forceShellExecutor) { + return null; + } const image = this.jobData["image"]; if (!image) { if (this.argv.shellExecutorNoImage) { diff --git a/tests/test-cases/force-shell-executor/.gitlab-ci.yml b/tests/test-cases/force-shell-executor/.gitlab-ci.yml new file mode 100644 index 00000000..615c860c --- /dev/null +++ b/tests/test-cases/force-shell-executor/.gitlab-ci.yml @@ -0,0 +1,6 @@ +--- +test-job: + image: alpine:latest + stage: test + script: + - echo "Heya from default-image" diff --git a/tests/test-cases/force-shell-executor/integration.test.ts b/tests/test-cases/force-shell-executor/integration.test.ts new file mode 100644 index 00000000..604aa9e1 --- /dev/null +++ b/tests/test-cases/force-shell-executor/integration.test.ts @@ -0,0 +1,45 @@ +import {WriteStreamsMock} from "../../../src/write-streams.js"; +import {handler} from "../../../src/handler.js"; + +test("force-shell-executor false default-image alpine", async () => { + const writeStreams = new WriteStreamsMock(); + await handler({ + pullPolicy: "if-not-present", + cwd: "tests/test-cases/force-shell-executor/", + forceShellExecutor: false, + defaultImage: "alpine:latest", + job: ["test-job"], + noColor: true, + }, writeStreams); + + expect(writeStreams.stdoutLines.join("\n")).toMatch(/test-job starting alpine:latest \(test\)/); +}); + +test("force-shell-executor false default-image null but job image alpine", async () => { + const writeStreams = new WriteStreamsMock(); + await handler({ + pullPolicy: "if-not-present", + cwd: "tests/test-cases/force-shell-executor/", + forceShellExecutor: false, + defaultImage: null, + job: ["test-job"], + noColor: true, + }, writeStreams); + + expect(writeStreams.stdoutLines.join("\n")).toMatch(/test-job starting alpine:latest \(test\)/); +}); + +test("force-shell-executor true default-image doesnt-matter", async () => { + const writeStreams = new WriteStreamsMock(); + await handler({ + pullPolicy: "if-not-present", + cwd: "tests/test-cases/force-shell-executor/", + forceShellExecutor: true, + defaultImage: "doesnt-matter", + job: ["test-job"], + noColor: true, + }, writeStreams); + + expect(writeStreams.stdoutLines.join("\n")).toMatch(/test-job starting shell \(test\)/); + expect(writeStreams.stderrLines.join("\n")).toMatch(/WARN\s\s--default-image does not work with --force-shell-executor=true/); +});