Skip to content

Commit d0a0884

Browse files
authored
feat: implement inherit:variables (#1384)
* feat: implement inherit:variables * chore: add warning message for inherit:default
1 parent e1adc3a commit d0a0884

4 files changed

Lines changed: 109 additions & 6 deletions

File tree

src/job.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@ export class Job {
100100
readonly exists: string[];
101101
readonly pipelineIid: number;
102102
readonly gitData: GitData;
103+
readonly inherit: {
104+
variables?: boolean | string[];
105+
default?: boolean | string[];
106+
};
103107

108+
private readonly _globalVariables: {[key: string]: string} = {};
104109
private readonly _variables: {[key: string]: string} = {};
105110
private _dotenvVariables: {[key: string]: string} = {};
106111
private _prescriptsExitCode: number | null = null;
@@ -126,7 +131,6 @@ export class Job {
126131
const jobData = opt.data;
127132
const gitData = opt.gitData;
128133
const jobVariables = jobData.variables ?? {};
129-
const globalVariables = opt.globalVariables ?? {};
130134
const variablesFromFiles = opt.variablesFromFiles;
131135
const argv = opt.argv;
132136
const cwd = argv.cwd;
@@ -143,6 +147,15 @@ export class Job {
143147
this.jobId = this.generateJobId();
144148
this.jobData = opt.data;
145149
this.pipelineIid = opt.pipelineIid;
150+
this._globalVariables = opt.globalVariables;
151+
152+
this.inherit = {};
153+
this.inherit.variables = this.jobData.inherit?.variables ?? true;
154+
this.inherit.default = this.jobData.inherit?.default ?? true;
155+
156+
if (this.inherit.default === false) {
157+
this.writeStreams.memoStdout(chalk`{black.bgYellowBright WARN } \`.inherit.default\` has not been implemented!\n`);
158+
}
146159

147160
this.when = jobData.when || "on_success";
148161
this.exists = jobData.exists || [];
@@ -153,7 +166,7 @@ export class Job {
153166

154167
const matrixVariables = opt.matrixVariables ?? {};
155168
const fileVariables = Utils.findEnvMatchedVariables(variablesFromFiles, this.fileVariablesDir);
156-
this._variables = {...globalVariables, ...jobVariables, ...matrixVariables, ...predefinedVariables, ...fileVariables, ...argvVariables};
169+
this._variables = {...this.globalVariables, ...jobVariables, ...matrixVariables, ...predefinedVariables, ...fileVariables, ...argvVariables};
157170

158171
let ciProjectDir = `${cwd}`;
159172
if (this.jobData["image"]) {
@@ -209,14 +222,14 @@ export class Job {
209222
const envMatchedVariables = Utils.findEnvMatchedVariables(variablesFromFiles, this.fileVariablesDir, this.environment);
210223

211224
// Merge and expand after finding env matched variables
212-
this._variables = {...globalVariables, ...jobVariables, ...matrixVariables, ...predefinedVariables, ...envMatchedVariables, ...argvVariables};
225+
this._variables = {...this.globalVariables, ...jobVariables, ...matrixVariables, ...predefinedVariables, ...envMatchedVariables, ...argvVariables};
213226

214227
// Set {when, allowFailure} based on rules result
215228
if (this.rules) {
216229
const ruleResult = Utils.getRulesResult({cwd, rules: this.rules, variables: this._variables}, this.gitData, this.when, this.allowFailure);
217230
this.when = ruleResult.when;
218231
this.allowFailure = ruleResult.allowFailure;
219-
this._variables = {...globalVariables, ...jobVariables, ...ruleResult.variables, ...matrixVariables, ...predefinedVariables, ...envMatchedVariables, ...argvVariables};
232+
this._variables = {...this.globalVariables, ...jobVariables, ...ruleResult.variables, ...matrixVariables, ...predefinedVariables, ...envMatchedVariables, ...argvVariables};
220233
}
221234
// Delete variables the user intentionally wants unset
222235
for (const unsetVariable of argv.unsetVariables) {
@@ -450,6 +463,18 @@ export class Job {
450463
return `/tmp/gitlab-ci-local-file-variables-${this._variables["CI_PROJECT_PATH_SLUG"]}-${this.jobId}`;
451464
}
452465

466+
get globalVariables () {
467+
if (this.inherit.variables === false) {
468+
return {};
469+
} else if (Array.isArray(this.inherit.variables)) {
470+
const inheritVariables = this.inherit.variables;
471+
return Object.fromEntries(
472+
Object.entries(this._globalVariables).filter(([k]) => inheritVariables.includes(k))
473+
);
474+
}
475+
return this._globalVariables;
476+
}
477+
453478
async start (): Promise<void> {
454479
this._running = true;
455480

src/write-streams.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
export interface WriteStreams {
22
stdout: (txt: string) => void;
3+
memoStdout: (txt: string) => void;
34
stderr: (txt: string) => void;
45
flush: () => void;
56
}
67

7-
export class WriteStreamsProcess implements WriteStreams {
8+
abstract class AbstractWriteStreams implements WriteStreams {
9+
abstract stdout (txt: string): void;
10+
abstract stderr (txt: string): void;
11+
abstract flush (): void;
12+
13+
memoStdout = (() => {
14+
const cache = new Map();
15+
return (message: string) => {
16+
if (cache.has(message)) return;
17+
cache.set(message, null);
18+
this.stdout(message);
19+
};
20+
})();
21+
}
22+
23+
export class WriteStreamsProcess extends AbstractWriteStreams {
824
stderr (txt: string): void {
925
process.stderr.write(txt);
1026
}
@@ -18,7 +34,7 @@ export class WriteStreamsProcess implements WriteStreams {
1834
}
1935
}
2036

21-
export class WriteStreamsMock implements WriteStreams {
37+
export class WriteStreamsMock extends AbstractWriteStreams {
2238
private currentStderr = "";
2339
private currentStdout = "";
2440

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
default:
3+
image: busybox
4+
5+
variables:
6+
GCL_TESTS_VAR1: "This is variable 1"
7+
GCL_TESTS_VAR2: "This is variable 2"
8+
GCL_TESTS_VAR3: "This is variable 3"
9+
10+
job1:
11+
inherit:
12+
variables: false
13+
script:
14+
- env | grep GCL_TESTS
15+
16+
job2:
17+
inherit:
18+
variables:
19+
- GCL_TESTS_VAR1
20+
- GCL_TESTS_VAR2
21+
script:
22+
- env | grep GCL_TESTS
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {WriteStreamsMock} from "../../../src/write-streams.js";
2+
import {handler} from "../../../src/handler.js";
3+
import {initSpawnSpy} from "../../mocks/utils.mock.js";
4+
import {WhenStatics} from "../../mocks/when-statics.js";
5+
6+
beforeAll(() => {
7+
initSpawnSpy(WhenStatics.all);
8+
});
9+
10+
11+
describe("inherit:variables", () => {
12+
test("false should disable inheritance of all global variables", async () => {
13+
const writeStreams = new WriteStreamsMock();
14+
await handler({
15+
file: ".gitlab-ci-inherit-variables.yml",
16+
cwd: "tests/test-cases/inherit",
17+
noColor: true,
18+
job: ["job1"],
19+
}, writeStreams);
20+
21+
const filteredStdout = writeStreams.stdoutLines.filter(f => f.startsWith("job1 >")).join("\n");
22+
expect(filteredStdout).toEqual("");
23+
});
24+
25+
test("list of specific variables should only inheritance the respective global variables", async () => {
26+
const writeStreams = new WriteStreamsMock();
27+
await handler({
28+
file: ".gitlab-ci-inherit-variables.yml",
29+
cwd: "tests/test-cases/inherit",
30+
noColor: true,
31+
job: ["job2"],
32+
}, writeStreams);
33+
34+
const expected = `job2 > GCL_TESTS_VAR1=This is variable 1
35+
job2 > GCL_TESTS_VAR2=This is variable 2`;
36+
37+
const filteredStdout = writeStreams.stdoutLines.filter(f => f.startsWith("job2 >")).join("\n");
38+
expect(filteredStdout).toEqual(expected);
39+
});
40+
});

0 commit comments

Comments
 (0)