diff --git a/src/parser.ts b/src/parser.ts index 8abfab4af..302f2aa89 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -443,7 +443,14 @@ function validateInput (ctx: any) { const regex = inputsSpecification.spec.inputs[interpolationKey]?.regex; if (regex) { - ctx.writeStreams?.stderr(chalk`{black.bgYellowBright WARN } spec:inputs:regex is currently not supported via gitlab-ci-local. This will just be a no-op.\n`); + let re: RegExp; + try { + re = new RegExp(regex); + } catch { + assert(false, chalk`This GitLab CI configuration is invalid: \`{blueBright ${configFilePath}}\`: \`{blueBright ${interpolationKey}}\` input: regex \`{blueBright ${regex}}\` is not a valid regular expression.`); + } + assert(re.test(String(inputValue)), + chalk`This GitLab CI configuration is invalid: \`{blueBright ${configFilePath}}\`: \`{blueBright ${interpolationKey}}\` input: \`{blueBright ${inputValue}}\` does not match required regex: {blueBright ${regex}}.`); } } diff --git a/tests/test-cases/include-inputs/input-templates/regex-validation-invalid-pattern/.gitlab-ci-input-template.yml b/tests/test-cases/include-inputs/input-templates/regex-validation-invalid-pattern/.gitlab-ci-input-template.yml new file mode 100644 index 000000000..20d1cd156 --- /dev/null +++ b/tests/test-cases/include-inputs/input-templates/regex-validation-invalid-pattern/.gitlab-ci-input-template.yml @@ -0,0 +1,9 @@ +--- +spec: + inputs: + version: + regex: ^[unclosed +--- +deploy: + script: + - echo $[[ inputs.version ]] diff --git a/tests/test-cases/include-inputs/input-templates/regex-validation-invalid-pattern/.gitlab-ci.yml b/tests/test-cases/include-inputs/input-templates/regex-validation-invalid-pattern/.gitlab-ci.yml new file mode 100644 index 000000000..74492808e --- /dev/null +++ b/tests/test-cases/include-inputs/input-templates/regex-validation-invalid-pattern/.gitlab-ci.yml @@ -0,0 +1,7 @@ +--- +include: + - local: '/.gitlab-ci-input-template.yml' + inputs: + version: "v1.2.3" +stages: + - test diff --git a/tests/test-cases/include-inputs/input-templates/regex-validation-pass/.gitlab-ci-input-template.yml b/tests/test-cases/include-inputs/input-templates/regex-validation-pass/.gitlab-ci-input-template.yml new file mode 100644 index 000000000..668afe097 --- /dev/null +++ b/tests/test-cases/include-inputs/input-templates/regex-validation-pass/.gitlab-ci-input-template.yml @@ -0,0 +1,9 @@ +--- +spec: + inputs: + version: + regex: ^v\d+\.\d+\.\d+$ +--- +deploy: + script: + - echo $[[ inputs.version ]] diff --git a/tests/test-cases/include-inputs/input-templates/regex-validation-pass/.gitlab-ci.yml b/tests/test-cases/include-inputs/input-templates/regex-validation-pass/.gitlab-ci.yml new file mode 100644 index 000000000..74492808e --- /dev/null +++ b/tests/test-cases/include-inputs/input-templates/regex-validation-pass/.gitlab-ci.yml @@ -0,0 +1,7 @@ +--- +include: + - local: '/.gitlab-ci-input-template.yml' + inputs: + version: "v1.2.3" +stages: + - test diff --git a/tests/test-cases/include-inputs/input-templates/regex-validation/.gitlab-ci-input-template.yml b/tests/test-cases/include-inputs/input-templates/regex-validation/.gitlab-ci-input-template.yml new file mode 100644 index 000000000..668afe097 --- /dev/null +++ b/tests/test-cases/include-inputs/input-templates/regex-validation/.gitlab-ci-input-template.yml @@ -0,0 +1,9 @@ +--- +spec: + inputs: + version: + regex: ^v\d+\.\d+\.\d+$ +--- +deploy: + script: + - echo $[[ inputs.version ]] diff --git a/tests/test-cases/include-inputs/input-templates/regex-validation/.gitlab-ci.yml b/tests/test-cases/include-inputs/input-templates/regex-validation/.gitlab-ci.yml new file mode 100644 index 000000000..d513876a0 --- /dev/null +++ b/tests/test-cases/include-inputs/input-templates/regex-validation/.gitlab-ci.yml @@ -0,0 +1,7 @@ +--- +include: + - local: '/.gitlab-ci-input-template.yml' + inputs: + version: "invalid-version" +stages: + - test diff --git a/tests/test-cases/include-inputs/integration.test.ts b/tests/test-cases/include-inputs/integration.test.ts index eabd4d6e2..79bb8f5bf 100644 --- a/tests/test-cases/include-inputs/integration.test.ts +++ b/tests/test-cases/include-inputs/integration.test.ts @@ -358,3 +358,60 @@ scan-website: expect(writeStreams.stdoutLines[0]).toEqual(expected); }); + +test.concurrent("include-inputs regex validation (invalid)", async () => { + try { + const writeStreams = new WriteStreamsMock(); + await handler({ + cwd: "tests/test-cases/include-inputs/input-templates/regex-validation", + preview: true, + }, writeStreams); + } catch (e: any) { + assert(e instanceof AssertionError, "e is not instanceof AssertionError"); + expect(e.message).toContain("This GitLab CI configuration is invalid:"); + expect(e.message).toContain( + chalk`\`{blueBright version}\` input: \`{blueBright invalid-version}\` does not match required regex: {blueBright ^v\\d+\\.\\d+\\.\\d+$}.`, + ); + return; + } + + throw new Error("Error is expected but not thrown/caught"); +}); + +test.concurrent("include-inputs regex validation (valid)", async () => { + const writeStreams = new WriteStreamsMock(); + await handler({ + cwd: "tests/test-cases/include-inputs/input-templates/regex-validation-pass", + preview: true, + }, writeStreams); + + const expected = `--- +stages: + - .pre + - test + - .post +deploy: + script: + - echo v1.2.3`; + + expect(writeStreams.stdoutLines[0]).toEqual(expected); +}); + +test.concurrent("include-inputs regex validation (invalid pattern)", async () => { + try { + const writeStreams = new WriteStreamsMock(); + await handler({ + cwd: "tests/test-cases/include-inputs/input-templates/regex-validation-invalid-pattern", + preview: true, + }, writeStreams); + } catch (e: any) { + assert(e instanceof AssertionError, "e is not instanceof AssertionError"); + expect(e.message).toContain("This GitLab CI configuration is invalid:"); + expect(e.message).toContain( + chalk`\`{blueBright version}\` input: regex \`{blueBright ^[unclosed}\` is not a valid regular expression.`, + ); + return; + } + + throw new Error("Error is expected but not thrown/caught"); +});