Skip to content

Commit

Permalink
feat: add --shell-executor option. (#1447).
Browse files Browse the repository at this point in the history
Allows forcing to always use the shell executor even when there are images declared somewhere.
Avoids image downloads and updates when you want a speedy pipeline
  • Loading branch information
ecourreges-orange committed Jan 27, 2025
1 parent f27c97e commit 5337c1b
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/argv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export class Argv {
writeStreams?.stderr(chalk`{black.bgYellowBright WARN } --shell-isolation does not work with --no-shell-executor-no-image\n`);
}

if (!argv.shellExecutor && argv.shellIsolation) {
writeStreams?.stderr(chalk`{black.bgYellowBright WARN } --shell-isolation does not work with --no-shell-executor\n`);
}

if (argv.defaultImageExplicitlySet && argv.shellIsolation) {
writeStreams?.stderr(chalk`{black.bgYellowBright WARN } --default-image does not work with --shell-isolation=true\n`);
}
Expand All @@ -58,6 +62,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.shellExecutor) {
writeStreams?.stderr(chalk`{black.bgYellowBright WARN } --default-image does not work with --shell-executor=true\n`);
}

return argv;
}

Expand Down Expand Up @@ -287,6 +295,10 @@ export class Argv {
return this.map.get("shellExecutorNoImage") ?? true;
}

get shellExecutor(): boolean {

Check failure on line 298 in src/argv.ts

View workflow job for this annotation

GitHub Actions / eslint

Missing space before function parentheses
return this.map.get("shellExecutor") ?? false;
}

get defaultImage (): string {
return this.map.get("defaultImage") ?? "docker.io/ruby:3.1";
}
Expand Down
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ process.on("SIGUSR2", async () => await cleanupJobResources(jobs));
description: "Enable artifact isolation for shell-executor jobs",
requiresArg: false,
})
.option("shell-executor", {
type: "boolean",
description: "Whether to force always using the shell executor",
requiresArg: false,
})
.option("shell-executor-no-image", {
type: "boolean",
description: "Whether to use shell executor when no image is specified.",
Expand Down
4 changes: 4 additions & 0 deletions src/job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ export class Job {
assert(this.scripts || this.trigger, chalk`{blueBright ${this.name}} must have script specified`);

assert(!(this.interactive && !this.argv.shellExecutorNoImage), chalk`${this.formattedJobName} @Interactive decorator cannot be used with --no-shell-executor-no-image`);
assert(!(this.interactive && !this.argv.shellExecutor), chalk`${this.formattedJobName} @Interactive decorator cannot be used with --no-shell-executor`);

if (this.interactive && (this.when !== "manual" || this.imageName(this._variables) !== null)) {
throw new AssertionError({message: `${this.formattedJobName} @Interactive decorator cannot have image: and must be when:manual`});
Expand Down Expand Up @@ -991,6 +992,9 @@ export class Job {
}

private imageName (vars: {[key: string]: string} = {}): string | null {
if (this.argv.shellExecutor) {
return null;
}
const image = this.jobData["image"];
if (!image) {
if (this.argv.shellExecutorNoImage) {
Expand Down
15 changes: 15 additions & 0 deletions tests/test-cases/artifacts-shell/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,18 @@ test.concurrent("artifacts-shell <deploy> --file .gitlab-ci-when-never.yml --she
expect(writeStreams.stderrLines.join("\n")).not.toMatch(/FAIL/);
expect(writeStreams.stderrLines.join("\n")).toContain(expected);
});

test.concurrent("artifacts-shell <deploy> --file .gitlab-ci-when-never.yml --shell-isolation --no-shell-executor", async () => {
const writeStreams = new WriteStreamsMock();
await handler({
cwd: "tests/test-cases/artifacts-shell",
job: ["deploy"],
file: ".gitlab-ci-when-never.yml",
shellIsolation: true,
shellExecutor: false,
}, writeStreams);

const expected = chalk`{black.bgYellowBright WARN } --shell-isolation does not work with --no-shell-executor\n`;
expect(writeStreams.stderrLines.join("\n")).not.toMatch(/FAIL/);
expect(writeStreams.stderrLines.join("\n")).toContain(expected);
});
6 changes: 6 additions & 0 deletions tests/test-cases/shell-executor/.gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
test-job:
image: alpine:latest
stage: test
script:
- echo "Heya from default-image"
45 changes: 45 additions & 0 deletions tests/test-cases/shell-executor/integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { WriteStreamsMock } from "../../../src/write-streams.js";

Check failure on line 1 in tests/test-cases/shell-executor/integration.test.ts

View workflow job for this annotation

GitHub Actions / eslint

There should be no space after '{'

Check failure on line 1 in tests/test-cases/shell-executor/integration.test.ts

View workflow job for this annotation

GitHub Actions / eslint

There should be no space before '}'
import { handler } from "../../../src/handler.js";

Check failure on line 2 in tests/test-cases/shell-executor/integration.test.ts

View workflow job for this annotation

GitHub Actions / eslint

There should be no space after '{'

Check failure on line 2 in tests/test-cases/shell-executor/integration.test.ts

View workflow job for this annotation

GitHub Actions / eslint

There should be no space before '}'

test("shell-executor false default-image alpine", async () => {
const writeStreams = new WriteStreamsMock();
await handler({
pullPolicy: "if-not-present",
cwd: "tests/test-cases/shell-executor/",
shellExecutor: false,
defaultImage: "alpine:latest",
job: ["test-job"],
noColor: true,
}, writeStreams);

expect(writeStreams.stdoutLines.join("\n")).toMatch(/test-job starting alpine:latest \(test\)/);
});

test("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/shell-executor/",
shellExecutor: false,
defaultImage: null,
job: ["test-job"],
noColor: true,
}, writeStreams);

expect(writeStreams.stdoutLines.join("\n")).toMatch(/test-job starting alpine:latest \(test\)/);
});

test("shell-executor true default-image doesnt-matter", async () => {
const writeStreams = new WriteStreamsMock();
await handler({
pullPolicy: "if-not-present",
cwd: "tests/test-cases/shell-executor/",
shellExecutor: 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 --shell-executor=true/);
});

0 comments on commit 5337c1b

Please sign in to comment.