Fix remaining SonarCloud findings (#1823)

This commit is contained in:
Mads Jon Nielsen
2026-04-03 13:10:18 +02:00
committed by GitHub
parent 67976a9786
commit e590d11550
15 changed files with 70 additions and 66 deletions
+4 -4
View File
@@ -2,14 +2,14 @@ import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import stylistic from "@stylistic/eslint-plugin";
export default tseslint.config(
export default [
eslint.configs.recommended,
tseslint.configs.recommended,
...tseslint.configs.recommended,
{
ignores: [
"**/*.js",
"**/*.cjs",
".gitlab-ci-local",
"**/.gitlab-ci-local/**",
],
},
{
@@ -50,4 +50,4 @@ export default tseslint.config(
"key-spacing": "error",
},
},
);
];
+2 -2
View File
@@ -1,7 +1,7 @@
import assert from "assert";
import assert from "node:assert";
import fs from "fs-extra";
import * as dotenv from "dotenv";
import * as path from "path";
import * as path from "node:path";
import camelCase from "camelcase";
import {Utils} from "./utils.js";
import {WriteStreams} from "./write-streams.js";
+1 -1
View File
@@ -6,7 +6,7 @@ import {WriteStreams} from "./write-streams.js";
import {Executor} from "./executor.js";
import fs from "fs-extra";
import {Argv} from "./argv.js";
import {AssertionError} from "assert";
import {AssertionError} from "node:assert";
export class Commander {
+2 -2
View File
@@ -1,6 +1,6 @@
import chalk from "chalk-template";
import deepExtend from "deep-extend";
import assert, {AssertionError} from "assert";
import assert, {AssertionError} from "node:assert";
import {Job, Need} from "./job.js";
import {traverse} from "object-traversal";
import {Utils} from "./utils.js";
@@ -150,7 +150,7 @@ export function cacheEach (jobName: string, gitlabData: any) {
jobData.cache = Array.isArray(cache) ? cache : [cache];
for (const [i, c] of Object.entries<any>(jobData.cache)) {
if (c.key?.files instanceof Array) {
if (Array.isArray(c.key?.files)) {
assert(c.key.files.length === 1 || c.key.files.length === 2, `cache:key:files should be an array of one or two file paths. Got ${c.key.files.length}`);
}
jobData.cache[i] = cacheComplex(c);
+1 -1
View File
@@ -1,6 +1,6 @@
import chalk from "chalk-template";
import {Job} from "./job.js";
import assert, {AssertionError} from "assert";
import assert, {AssertionError} from "node:assert";
import {Argv} from "./argv.js";
import pMap from "p-map";
+7 -7
View File
@@ -1,5 +1,5 @@
import {Utils} from "./utils.js";
import assert, {AssertionError} from "assert";
import assert, {AssertionError} from "node:assert";
import {WriteStreams} from "./write-streams.js";
import chalk from "chalk-template";
@@ -35,12 +35,12 @@ export class GitData {
static async init (cwd: string, writeStreams: WriteStreams): Promise<GitData> {
const gitData = new GitData();
const promises = [];
promises.push(gitData.initCommitData(cwd, writeStreams));
promises.push(gitData.initRemoteData(cwd, writeStreams));
promises.push(gitData.initUserData(cwd, writeStreams));
promises.push(gitData.initBranchData(cwd, writeStreams));
await Promise.all(promises);
await Promise.all([
gitData.initCommitData(cwd, writeStreams),
gitData.initRemoteData(cwd, writeStreams),
gitData.initUserData(cwd, writeStreams),
gitData.initBranchData(cwd, writeStreams),
]);
return gitData;
}
+2 -2
View File
@@ -1,6 +1,6 @@
import * as yaml from "js-yaml";
import chalk from "chalk-template";
import path from "path";
import path from "node:path";
import fs from "fs-extra";
import yargs from "yargs";
import {Commander} from "./commander.js";
@@ -11,7 +11,7 @@ import {WriteStreams} from "./write-streams.js";
import {cleanupJobResources, Job} from "./job.js";
import {Utils} from "./utils.js";
import {Argv} from "./argv.js";
import assert from "assert";
import assert from "node:assert";
const generateGitIgnore = (cwd: string, stateDir: string) => {
const gitIgnoreFilePath = `${cwd}/${stateDir}/.gitignore`;
+28 -23
View File
@@ -6,7 +6,7 @@ import split2 from "split2";
import {Utils} from "./utils.js";
import {WriteStreams} from "./write-streams.js";
import {GitData} from "./git-data.js";
import assert, {AssertionError} from "assert";
import assert, {AssertionError} from "node:assert";
import {Mutex} from "./mutex.js";
import {Argv} from "./argv.js";
import execa from "execa";
@@ -418,10 +418,10 @@ If you know what you're doing and would like to suppress this warning, use one o
}
break;
case "object":
if (! Array.isArray(allowFailure.exit_codes)) {
allowedExitCodes = [allowFailure.exit_codes];
} else {
if (Array.isArray(allowFailure.exit_codes)) {
allowedExitCodes = allowFailure.exit_codes;
} else {
allowedExitCodes = [allowFailure.exit_codes];
}
break;
default:
@@ -685,12 +685,11 @@ If you know what you're doing and would like to suppress this warning, use one o
const tmpVolumeName = this.tmpVolumeName;
const fileVariablesDir = this.fileVariablesDir;
const volumePromises = [];
volumePromises.push(Utils.spawn([this.argv.containerExecutable, "volume", "create", `${buildVolumeName}`], argv.cwd));
volumePromises.push(Utils.spawn([this.argv.containerExecutable, "volume", "create", `${tmpVolumeName}`], argv.cwd));
this._containerVolumeNames.push(buildVolumeName);
this._containerVolumeNames.push(tmpVolumeName);
await Promise.all(volumePromises);
this._containerVolumeNames.push(buildVolumeName, tmpVolumeName);
await Promise.all([
Utils.spawn([this.argv.containerExecutable, "volume", "create", `${buildVolumeName}`], argv.cwd),
Utils.spawn([this.argv.containerExecutable, "volume", "create", `${tmpVolumeName}`], argv.cwd),
]);
const time = process.hrtime();
this.refreshLongRunningSilentTimeout(writeStreams);
@@ -762,7 +761,7 @@ If you know what you're doing and would like to suppress this warning, use one o
}
await this.execPreScripts(expanded);
if (this._prescriptsExitCode == null) throw Error("this._prescriptsExitCode must be defined!");
if (this._prescriptsExitCode == null) throw new Error("this._prescriptsExitCode must be defined!");
await this.execAfterScripts(expanded);
@@ -844,7 +843,7 @@ If you know what you're doing and would like to suppress this warning, use one o
scripts.forEach((script) => {
const split = script.split(/\r?\n/);
const multilineText = split.length > 1 ? " # collapsed multi-line command" : "";
const text = split[0]?.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/[$]/g, "\\$");
const text = split[0]?.replaceAll("\\", String.raw`\\`).replaceAll("\"", String.raw`\"`).replaceAll("$", String.raw`\$`);
if (this.interactive) {
cmd += chalk`echo "{green $ ${text}${multilineText}}"\n`;
} else {
@@ -1035,7 +1034,8 @@ If you know what you're doing and would like to suppress this warning, use one o
for (const [key, val] of Object.entries(expanded)) {
// Replacing `'` with `'\''` to correctly handle single quotes(if `val` contains `'`) in shell commands
dockerCmd += ` -e '${key}=${val.toString().replaceAll("'", String.raw`'\''`)}' \\\n`;
const escaped = val.toString().replaceAll("'", String.raw`'\''`);
dockerCmd += ` -e '${key}=${escaped}' \\\n`;
}
if (this.imageEntrypoint) {
@@ -1594,7 +1594,8 @@ If you know what you're doing and would like to suppress this warning, use one o
for (const [key, val] of Object.entries(expanded)) {
// Replacing `'` with `'\''` to correctly handle single quotes(if `val` contains `'`) in shell commands
dockerCmd += ` -e '${key}=${val.toString().replaceAll("'", String.raw`'\''`)}' \\\n`;
const escaped = val.toString().replaceAll("'", String.raw`'\''`);
dockerCmd += ` -e '${key}=${escaped}' \\\n`;
}
const serviceEntrypoint = service.entrypoint;
@@ -1621,7 +1622,10 @@ If you know what you're doing and would like to suppress this warning, use one o
dockerCmd += `${Utils.safeBashString(e)} `;
});
}
(service.command ?? []).forEach((e) => dockerCmd += `"${e.replace(/\$/g, "\\$")}" `);
for (const e of service.command ?? []) {
const escaped = e.replaceAll("$", String.raw`\$`);
dockerCmd += `"${escaped}" `;
}
const time = process.hrtime();
@@ -1656,14 +1660,15 @@ If you know what you're doing and would like to suppress this warning, use one o
const time = process.hrtime();
try {
// Iterate over each port defined in the image, and try to connect to the alias
await Promise.any(Object.keys(imageInspect[0].Config.ExposedPorts).map((port) => {
if (!port.endsWith("/tcp")) return;
const portNum = parseInt(port.replace("/tcp", ""));
const containerName = `gcl-wait-for-it-${this.jobId}-${serviceIndex}-${portNum}`;
const spawnCmd = [this.argv.containerExecutable, "run", "--rm", `--name=${containerName}`, "--network", `${this._serviceNetworkId}`, `${waitImageName}`, `${serviceAlias}:${portNum}`, "-t", `${waitForServicesTimeout}`];
this._containersToClean.push(containerName);
return Utils.spawn(spawnCmd);
}));
await Promise.any(Object.keys(imageInspect[0].Config.ExposedPorts)
.filter((port) => port.endsWith("/tcp"))
.map((port) => {
const portNum = Number.parseInt(port.replace("/tcp", ""));
const containerName = `gcl-wait-for-it-${this.jobId}-${serviceIndex}-${portNum}`;
const spawnCmd = [this.argv.containerExecutable, "run", "--rm", `--name=${containerName}`, "--network", `${this._serviceNetworkId}`, `${waitImageName}`, `${serviceAlias}:${portNum}`, "-t", `${waitForServicesTimeout}`];
this._containersToClean.push(containerName);
return Utils.spawn(spawnCmd);
}));
const endTime = process.hrtime(time);
writeStreams.stdout(chalk`${this.formattedJobName} {greenBright service image: ${serviceName} healthcheck passed in {green ${prettyHrtime(endTime)}}}\n`);
} catch (e: any) {
+1 -1
View File
@@ -1,4 +1,4 @@
import assert from "assert";
import assert from "node:assert";
import deepExtend from "deep-extend";
export function isPlainParallel (jobData: any) {
+4 -4
View File
@@ -3,11 +3,11 @@ import {Utils} from "./utils.js";
import fs from "fs-extra";
import {WriteStreams} from "./write-streams.js";
import {GitData} from "./git-data.js";
import assert, {AssertionError} from "assert";
import assert, {AssertionError} from "node:assert";
import chalk from "chalk-template";
import {Parser} from "./parser.js";
import axios from "axios";
import path from "path";
import path from "node:path";
import prettyHrtime from "pretty-hrtime";
import semver from "semver";
import {RE2JS} from "re2js";
@@ -433,11 +433,11 @@ export async function resolveIncludeLocal (pattern: string, cwd: string) {
// `**` matches anything
const anything = ".*?";
pattern = pattern.replaceAll(/\\\*\\\*/g, anything);
pattern = pattern.replaceAll(String.raw`\*\*`, anything);
// `*` matches anything except for `/`
const anything_but_not_slash = "([^/])*?";
pattern = pattern.replaceAll(/\\\*/g, anything_but_not_slash);
pattern = pattern.replaceAll(String.raw`\*`, anything_but_not_slash);
const re2js = RE2JS.compile(`^${pattern}`);
return repoFiles.filter((f: any) => re2js.matches(f));
+4 -4
View File
@@ -1,5 +1,5 @@
import chalk from "chalk-template";
import path from "path";
import path from "node:path";
import deepExtend from "deep-extend";
import fs from "fs-extra";
import * as yaml from "js-yaml";
@@ -7,7 +7,7 @@ import prettyHrtime from "pretty-hrtime";
import {Job} from "./job.js";
import * as DataExpander from "./data-expander.js";
import {Utils} from "./utils.js";
import assert from "assert";
import assert from "node:assert";
import {Validator} from "./validator.js";
import * as parallel from "./parallel.js";
import {GitData} from "./git-data.js";
@@ -199,7 +199,7 @@ export class Parser {
if (this.argv.maxJobNamePadding !== null && this.argv.maxJobNamePadding <= 0) {
this._jobNamePad = 0;
} else {
const jobs = this.argv.job.length !== 0 ? this.argv.job : this.jobs;
const jobs = this.argv.job.length === 0 ? this.jobs : this.argv.job;
jobs.forEach((job) => {
let jobNeedsLength: number[] = [];
@@ -304,7 +304,7 @@ export class Parser {
const uninterpolatedConfigurations: any = fileData[1];
const interpolatedConfigurations = JSON.stringify(uninterpolatedConfigurations)
.replace(
.replaceAll(
/(?<firstChar>.)?(?<secondChar>.)?\$\[\[\s*inputs.(?<interpolationKey>[\w-]+)\s*\|?\s*(?<interpolationFunctions>.*?)\s*\]\](?<lastChar>[^$])?/g // https://regexr.com/81c16
, (_: string, firstChar: string, secondChar: string, interpolationKey: string, interpolationFunctions: string, lastChar: string) => {
const configFilePath = path.relative(process.cwd(), filePath);
+2 -2
View File
@@ -38,7 +38,7 @@ export function init ({gitData, argv, envMatchedVariables}: PredefinedVariablesO
CI_PROJECT_NAME: gitData.remote.project,
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_PATH_SLUG: `${gitData.remote.group.replaceAll("/", "-")}-${gitData.remote.project}`.toLowerCase(),
CI_PROJECT_ROOT_NAMESPACE: CI_PROJECT_ROOT_NAMESPACE,
CI_PROJECT_NAMESPACE: CI_PROJECT_NAMESPACE,
CI_PROJECT_VISIBILITY: "internal",
@@ -46,7 +46,7 @@ export function init ({gitData, argv, envMatchedVariables}: PredefinedVariablesO
CI_COMMIT_REF_PROTECTED: "false",
CI_COMMIT_BRANCH: gitData.commit.REF_NAME, // Not available in merge request or tag pipelines
CI_COMMIT_REF_NAME: gitData.commit.REF_NAME, // Tag or branch name
CI_COMMIT_REF_SLUG: gitData.commit.REF_NAME.replace(/[^a-z\d]+/ig, "-").replace(/^-/, "").slice(0, 63).replace(/-$/, "").toLowerCase(),
CI_COMMIT_REF_SLUG: gitData.commit.REF_NAME.replaceAll(/[^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_COMMIT_TITLE: "Commit Title", // First line of commit message.
+7 -8
View File
@@ -7,7 +7,7 @@ import fs from "fs-extra";
import checksum from "checksum";
import base64url from "base64url";
import execa, {ExecaError} from "execa";
import assert from "assert";
import assert from "node:assert";
import {CICDVariable} from "./variables-from-files.js";
import {GitData} from "./git-data.js";
import {globbySync} from "globby";
@@ -92,7 +92,7 @@ export class Utils {
const matches = Array.from(content.matchAllRE2JS(regex));
if (matches.length === 0) return "0";
const lastMatch = matches[matches.length - 1];
const lastMatch = matches.at(-1)!;
const digits = /\d+(?:\.\d+)?/.exec(lastMatch[1] ?? lastMatch[0] ?? "");
if (!digits) return "0";
return digits[0] ?? "0";
@@ -111,16 +111,15 @@ export class Utils {
return text;
}
return text.replace(
return text.replaceAll(
/(\$\$)|\$\{([a-zA-Z_]\w*)}|\$([a-zA-Z_]\w*)/g, // https://regexr.com/7s4ka
(_match, escape, var1, var2) => {
if (typeof escape !== "undefined") {
if (escape !== undefined) {
return expandWith.unescape;
} else {
const name = var1 || var2;
assert(name, "unexpected unset capture group");
return `${expandWith.variable(name)}`;
}
const name = var1 || var2;
assert(name, "unexpected unset capture group");
return `${expandWith.variable(name)}`;
},
);
}
+1 -1
View File
@@ -1,6 +1,6 @@
import Ajv from "ajv";
import {Job} from "./job.js";
import assert from "assert";
import assert from "node:assert";
import chalk from "chalk-template";
import schema from "./schema.js";
import {betterAjvErrors} from "./schema-error.js";
+4 -4
View File
@@ -5,7 +5,7 @@ import * as yaml from "js-yaml";
import chalk from "chalk-template";
import prettyHrtime from "pretty-hrtime";
import {Argv} from "./argv.js";
import assert from "assert";
import assert from "node:assert";
import {Utils} from "./utils.js";
import dotenv from "dotenv";
import deepExtend from "deep-extend";
@@ -72,12 +72,12 @@ export class VariablesFromFiles {
for (const [matcher, content] of Object.entries(values)) {
assert(typeof content == "string", `${key}.${matcher} content must be text or multiline text`);
if (isDotEnv || type === "variable" || (type === null && !/^[/~]/.exec(content))) {
const regexp = matcher === "*" ? /.*/g : new RegExp(`^${matcher.replace(/\*/g, ".*")}$`, "g");
const regexp = matcher === "*" ? /.*/g : new RegExp(`^${matcher.replaceAll("*", ".*")}$`, "g");
variables[key] = variables[key] ?? {type: "variable", environments: []};
variables[key].environments.push({content, regexp, regexpPriority: matcher.length, scopePriority});
} else if (type === null && /^[/~]/.exec(content)) {
const fileSource = content.replace(/^~\/(.*)/, `${homeDir}/$1`);
const regexp = matcher === "*" ? /.*/g : new RegExp(`^${matcher.replace(/\*/g, ".*")}$`, "g");
const regexp = matcher === "*" ? /.*/g : new RegExp(`^${matcher.replaceAll("*", ".*")}$`, "g");
variables[key] = variables[key] ?? {type: "file", environments: []};
if (fs.existsSync(fileSource)) {
variables[key].environments.push({content, regexp, regexpPriority: matcher.length, scopePriority, fileSource});
@@ -85,7 +85,7 @@ export class VariablesFromFiles {
variables[key].environments.push({content: `warn: ${key} is pointing to invalid path\n`, regexp, regexpPriority: matcher.length, scopePriority});
}
} else if (type === "file") {
const regexp = matcher === "*" ? /.*/g : new RegExp(`^${matcher.replace(/\*/g, ".*")}$`, "g");
const regexp = matcher === "*" ? /.*/g : new RegExp(`^${matcher.replaceAll("*", ".*")}$`, "g");
variables[key] = variables[key] ?? {type: "file", environments: []};
variables[key].environments.push({content, regexp, regexpPriority: matcher.length, scopePriority});
} else {