Skip to content

Commit 09fc774

Browse files
bcouetilCopilot
andauthored
feat: improve --list-csv and --list output (#1810)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent ac02922 commit 09fc774

5 files changed

Lines changed: 103 additions & 33 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@
99
/.gitlab-ci-local/
1010
.DS_Store
1111
.vscode
12+
.history
13+
/tmp/
1214
/.gitlab-ci.yml

README.md

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -180,46 +180,58 @@ The command `gitlab-ci-local --list` will return pretty output and will also fil
180180
to `when: never`.
181181

182182
```text
183-
name description stage when allow_failure needs
184-
test-job Run Tests test on_success false
185-
build-job build on_success true [test-job]
183+
name description stage when allow_failure needs
184+
test-job Run Tests test on_success false
185+
build-job build on_success true [test-job]
186+
exit-codes-job build on_success [42,137] []
187+
deploy-job deploy on_success [1]
186188
```
187189

190+
- **description**: always shown, empty when not set
191+
- **allow_failure**: `true`, `false`, or `[exit_code1,exit_code2]` when specific exit codes are allowed to fail
192+
- **needs**: omitted when not specified (job follows stage ordering), `[]` when explicitly set to no dependencies (job starts immediately)
193+
188194
#### --list-all
189195

190196
Same as `--list` but will also print out jobs which are set to `when: never` (directly and implicit e.g. via rules).
191197

192198
```text
193-
name description stage when allow_failure needs
194-
test-job Run Tests test on_success false
195-
build-job build on_success true [test-job]
196-
deploy-job deploy never false [build-job]
199+
name description stage when allow_failure needs
200+
test-job Run Tests test on_success false
201+
build-job build on_success true [test-job]
202+
exit-codes-job build on_success [42,137] []
203+
deploy-job deploy on_success [1]
204+
never-job test never false
197205
```
198206

199207
#### --list-csv
200208

201-
The command `gitlab-ci-local --list-csv` will output the pipeline jobs as csv formatted list and will also filter all
202-
jobs which are set
203-
to `when: never`.
204-
The description will always be wrapped in quotes (even if there is none) to prevent semicolons in the description
205-
disturb the csv structure.
209+
The command `gitlab-ci-local --list-csv` will output the pipeline jobs as a CSV-formatted list and will also filter all
210+
jobs which are set to `when: never`.
206211

207212
```text
208-
name;description;stage;when;allow_failure;needs
209-
test-job;"Run Tests";test;on_success;false;[]
210-
build-job;"";build;on_success;true;[test-job]
213+
name;stage;when;allowFailure;needs
214+
test-job;test;on_success;false;
215+
build-job;build;on_success;true;[test-job]
216+
exit-codes-job;build;on_success;[42,137];[]
217+
deploy-job;deploy;on_success;[1];
211218
```
212219

220+
- **allowFailure**: `true`, `false`, or `[exit_code1,exit_code2]` when specific exit codes are allowed to fail
221+
- **needs**: empty when not specified (job follows stage ordering), `[]` when explicitly set to no dependencies (job starts immediately)
222+
213223
#### --list-csv-all
214224

215-
Same as `--list-csv-all` but will also print out jobs which are set to `when: never` (directly and implicit e.g. via
225+
Same as `--list-csv` but will also print out jobs which are set to `when: never` (directly and implicit e.g. via
216226
rules).
217227

218228
```text
219-
name;description;stage;when;allow_failure;needs
220-
test-job;"Run Tests";test;on_success;false;[]
221-
build-job;"";build;on_success;true;[test-job]
222-
deploy-job;"";deploy;never;false;[build-job]
229+
name;stage;when;allowFailure;needs
230+
test-job;test;on_success;false;
231+
build-job;build;on_success;true;[test-job]
232+
exit-codes-job;build;on_success;[42,137];[]
233+
deploy-job;deploy;on_success;[1];
234+
never-job;test;never;false;
223235
```
224236

225237
## Quirks

src/commander.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,11 @@ export class Commander {
224224
writeStreams.stdout(chalk`{grey allow_failure needs}\n`);
225225

226226
const renderLine = (job: Job) => {
227-
const needs = job.needs?.filter(n => !n.project && !n.pipeline).map(n => n.job);
228-
const allowFailure = job.allowFailure ? "true " : "false ";
227+
const needs = Commander.formatNeeds(job);
228+
const allowFailure = Commander.formatAllowFailure(job.allowFailure);
229229
let jobLine = chalk`{blueBright ${job.name.padEnd(jobNamePad)}} ${job.description.padEnd(descriptionPadEnd)} `;
230230
jobLine += chalk`{yellow ${job.stage.padEnd(stagePadEnd)}} ${job.when.padEnd(whenPadEnd)} ${allowFailure.padEnd(11)}`;
231-
if (needs) {
231+
if (needs !== null) {
232232
jobLine += chalk` [{blueBright ${needs}}]`;
233233
}
234234
writeStreams.stdout(`${jobLine}\n`);
@@ -267,10 +267,14 @@ export class Commander {
267267
jobs = jobs.filter(j => j.when !== "never");
268268
}
269269

270-
writeStreams.stdout("name;description;stage;when;allowFailure;needs\n");
270+
writeStreams.stdout("name;stage;when;allowFailure;needs\n");
271+
271272
jobs.forEach((job) => {
272-
const needs = job.needs?.filter(n => !n.project && !n.pipeline).map(n => n.job).join(",") ?? [];
273-
writeStreams.stdout(`${job.name};"${job.description}";${job.stage};${job.when};${job.allowFailure ? "true" : "false"};[${needs}]\n`);
273+
const needs = Commander.formatNeeds(job);
274+
const needsStr = needs === null ? "" : `[${needs.join(",")}]`;
275+
const allowFailure = Commander.formatAllowFailure(job.allowFailure);
276+
const row = [job.name, job.stage, job.when, allowFailure, needsStr].join(";");
277+
writeStreams.stdout(`${row}\n`);
274278
});
275279
}
276280

@@ -294,4 +298,19 @@ export class Commander {
294298
}
295299
}
296300
}
301+
302+
/** Returns `[code1,code2]` for exit_codes, `true`/`false` otherwise. */
303+
private static formatAllowFailure (allowFailure: Job["allowFailure"]): string {
304+
if (typeof allowFailure === "object") {
305+
const codes = Array.isArray(allowFailure.exit_codes) ? allowFailure.exit_codes : [allowFailure.exit_codes];
306+
return `[${codes.join(",")}]`;
307+
}
308+
return allowFailure ? "true" : "false";
309+
}
310+
311+
/** Returns `[job1, job2]` when needs lists jobs, `[]` when explicitly set to no dependencies, or nothing when unset (job follows stage ordering). */
312+
private static formatNeeds (job: Job): string[] | null {
313+
if (job.needs === null) return null;
314+
return job.needs.filter(n => !n.project && !n.pipeline).map(n => n.job);
315+
}
297316
}

tests/test-cases/list-csv-case/.gitlab-ci.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
stages: [test, build]
2+
stages: [test, build, deploy]
33

44
# @Description Run Tests
55
test-job:
@@ -14,3 +14,18 @@ build-job:
1414
allow_failure: true
1515
script:
1616
- 'echo "Build something"'
17+
18+
exit-codes-job:
19+
stage: build
20+
needs: []
21+
allow_failure:
22+
exit_codes: [42, 137]
23+
script:
24+
- 'echo "Exit codes"'
25+
26+
deploy-job:
27+
stage: deploy
28+
allow_failure:
29+
exit_codes: 1
30+
script:
31+
- 'echo "Deploy"'

tests/test-cases/list-csv-case/integration.test.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {WriteStreamsMock} from "../../../src/write-streams.js";
22
import {handler} from "../../../src/handler.js";
3+
import chalk from "chalk-template";
34
import {initSpawnSpy} from "../../mocks/utils.mock.js";
45
import {WhenStatics} from "../../mocks/when-statics.js";
56

@@ -16,9 +17,11 @@ test.concurrent("list-csv-case --list-csv", async () => {
1617
}, writeStreams);
1718

1819
const expected = [
19-
"name;description;stage;when;allowFailure;needs",
20-
"test-job;\"Run Tests\";test;on_success;false;[]",
21-
"build-job;\"\";build;on_success;true;[test-job]",
20+
"name;stage;when;allowFailure;needs",
21+
"test-job;test;on_success;false;",
22+
"build-job;build;on_success;true;[test-job]",
23+
"exit-codes-job;build;on_success;[42,137];[]",
24+
"deploy-job;deploy;on_success;[1];",
2225
];
2326
expect(writeStreams.stdoutLines).toEqual(expect.arrayContaining(expected));
2427
});
@@ -34,10 +37,29 @@ test.concurrent("list-csv-case --list-csv colon should add process descriptors w
3437
}, writeStreams);
3538

3639
const expected = [
37-
"name;description;stage;when;allowFailure;needs",
38-
"test-job;\"Run;Tests\";test;on_success;false;[]",
39-
"build-job;\"\";build;on_success;true;[test-job]",
40+
"name;stage;when;allowFailure;needs",
41+
"test-job;test;on_success;false;",
42+
"build-job;build;on_success;true;[test-job]",
4043
];
4144
expect(writeStreams.stdoutLines).toEqual(expect.arrayContaining(expected));
4245
});
4346

47+
48+
test.concurrent("list-csv-case --list", async () => {
49+
const writeStreams = new WriteStreamsMock();
50+
await handler({
51+
cwd: "tests/test-cases/list-csv-case/",
52+
list: true,
53+
stateDir: ".gitlab-ci-local-list-csv-case-list",
54+
}, writeStreams);
55+
56+
// jobNamePad=14 (exit-codes-job), descriptionPadEnd=11, stagePadEnd=6 (deploy), whenPadEnd=10
57+
const expected = [
58+
chalk`{grey ${"name".padEnd(14)} ${"description".padEnd(11)}} {grey ${"stage".padEnd(6)} ${"when".padEnd(10)}} {grey allow_failure needs}`,
59+
chalk`{blueBright ${"test-job".padEnd(14)}} ${"Run Tests".padEnd(11)} {yellow ${"test".padEnd(6)}} ${"on_success".padEnd(10)} ${"false".padEnd(11)}`,
60+
chalk`{blueBright ${"build-job".padEnd(14)}} ${"".padEnd(11)} {yellow ${"build".padEnd(6)}} ${"on_success".padEnd(10)} ${"true".padEnd(11)} [{blueBright test-job}]`,
61+
chalk`{blueBright ${"exit-codes-job".padEnd(14)}} ${"".padEnd(11)} {yellow ${"build".padEnd(6)}} ${"on_success".padEnd(10)} ${"[42,137]".padEnd(11)} [{blueBright }]`,
62+
chalk`{blueBright ${"deploy-job".padEnd(14)}} ${"".padEnd(11)} {yellow ${"deploy".padEnd(6)}} ${"on_success".padEnd(10)} ${"[1]".padEnd(11)}`,
63+
];
64+
expect(writeStreams.stdoutLines).toEqual(expect.arrayContaining(expected));
65+
});

0 commit comments

Comments
 (0)