diff --git a/CHANGELOG.md b/CHANGELOG.md index 83642a3..f03b8ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Fixed + +- Fix parsing of !reference tag ([#50](https://github.com/studiometa/prettier-formatter-gitlab/issues/50), [#59](https://github.com/studiometa/prettier-formatter-gitlab/pull/59), [a937d55](https://github.com/studiometa/prettier-formatter-gitlab/commit/a937d55)) + ## v2.1.0 - 2024.12.12 ### Added diff --git a/package-lock.json b/package-lock.json index 57ca82c..e6f83fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,8 @@ "dependencies": { "chalk": "^5.3.0", "jest-diff": "^29.7.0", - "js-yaml": "^4.1.0", - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "yaml": "2.7.0" }, "bin": { "prettier-formatter-gitlab": "bin/cli.js" @@ -901,6 +901,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, "license": "Python-2.0" }, "node_modules/babel-code-frame": { @@ -2246,6 +2247,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -3535,6 +3537,18 @@ "node": ">=12" } }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 68e12f3..69d26cf 100644 --- a/package.json +++ b/package.json @@ -48,8 +48,8 @@ "dependencies": { "chalk": "^5.3.0", "jest-diff": "^29.7.0", - "js-yaml": "^4.1.0", - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "yaml": "2.7.0" }, "peerDependencies": { "prettier": "^3.0" diff --git a/src/index.js b/src/index.js index d60d3ca..2ecf0c8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,36 +1,11 @@ #!/usr/bin/env node -import { readFileSync, mkdirSync, writeFileSync } from 'node:fs'; -import { cwd, env } from 'node:process'; -import { join, resolve, dirname } from 'node:path'; -import yaml from 'js-yaml'; +import { mkdirSync, writeFileSync } from 'node:fs'; +import { env } from 'node:process'; +import { dirname } from 'node:path'; import { diff } from './formatters/diff.js'; import { gitlab } from './formatters/gitlab.js'; import { parse } from './utils/parse-prettier-results.js'; - -const { - // Used as a fallback for local testing. - CI_CONFIG_PATH = '.gitlab-ci.yml', - CI_JOB_NAME, - CI_PROJECT_DIR = cwd(), -} = env; - -/** - * Get the output path for the report file. - * @returns {string} - */ -function getOutputPath() { - const jobs = yaml.load(readFileSync(join(CI_PROJECT_DIR, CI_CONFIG_PATH), 'utf-8')); - const { artifacts } = jobs[CI_JOB_NAME]; - const location = artifacts && artifacts.reports && artifacts.reports.codequality; - const msg = `Expected ${CI_JOB_NAME}.artifacts.reports.codequality to be one exact path`; - if (!location) { - throw new Error(`${msg}, but no value was found.`); - } - if (Array.isArray(location)) { - throw new Error(`${msg}, but found an array instead.`); - } - return resolve(CI_PROJECT_DIR, location); -} +import { getOutputPath } from './utils/get-output-path.js'; /** * Format Prettier results for GitLab Code Quality Reports. @@ -38,7 +13,7 @@ function getOutputPath() { * @returns {Promise} */ export async function prettierFormatterGitLab(results) { - const { PRETTIER_CODE_QUALITY_REPORT } = env; + const { CI_JOB_NAME, PRETTIER_CODE_QUALITY_REPORT } = env; if (CI_JOB_NAME || PRETTIER_CODE_QUALITY_REPORT) { const files = parse(results); diff --git a/src/utils/get-output-path.js b/src/utils/get-output-path.js new file mode 100644 index 0000000..58260d7 --- /dev/null +++ b/src/utils/get-output-path.js @@ -0,0 +1,52 @@ +import { readFileSync, existsSync, lstatSync } from 'node:fs'; +import { cwd, env } from 'node:process'; +import { join, resolve } from 'node:path'; +import { parseDocument } from 'yaml'; + +/** @type {yaml.CollectionTag} */ +const referenceTag = { + tag: '!reference', + collection: 'seq', + default: false, + resolve() { + // We only allow the syntax. We don’t actually resolve the reference. + }, +}; + +/** + * Get the output path for the report file. + * @returns {string} + */ +export function getOutputPath() { + const { + // Used as a fallback for local testing. + CI_CONFIG_PATH = '.gitlab-ci.yml', + CI_JOB_NAME, + CI_PROJECT_DIR = cwd(), + } = env; + + const configPath = join(CI_PROJECT_DIR, CI_CONFIG_PATH); + + if (!existsSync(configPath) || !lstatSync(configPath).isFile()) { + throw new Error( + 'Could not resolve .gitlab-ci.yml to automatically detect report artifact path.' + + ' Please manually provide a path via the ESLINT_CODE_QUALITY_REPORT variable.', + ); + } + + const doc = parseDocument(readFileSync(configPath, 'utf-8'), { + version: '1.1', + customTags: [referenceTag], + }); + + const path = [CI_JOB_NAME, 'artifacts', 'reports', 'codequality']; + const location = doc.getIn(path); + + if (typeof location !== 'string' || !location) { + throw new TypeError( + `Expected ${path.join('.')} to be one exact path, got: ${JSON.stringify(location)}`, + ); + } + + return resolve(CI_PROJECT_DIR, location); +} diff --git a/test/__snapshots__/cli.spec.js.snap b/test/__snapshots__/cli.spec.js.snap index 3305d11..3d8a7f4 100644 --- a/test/__snapshots__/cli.spec.js.snap +++ b/test/__snapshots__/cli.spec.js.snap @@ -2,6 +2,34 @@ exports[`prettier-formatter-gitlab cli should create a code quality report file with prettier -l 1`] = ` [ + { + "check_name": "prettier", + "description": "Delete ⏎", + "fingerprint": "994d9053d8e5f73c098f2bcfadec786c", + "location": { + "lines": { + "begin": 11, + "end": 12, + }, + "path": "test/__stubs__/.gitlab-ci.fail.yml", + }, + "severity": "minor", + "type": "issue", + }, + { + "check_name": "prettier", + "description": "Delete ⏎", + "fingerprint": "20c9ca526d0a43e3075a3e0f74135991", + "location": { + "lines": { + "begin": 11, + "end": 12, + }, + "path": "test/__stubs__/.gitlab-ci.yml", + }, + "severity": "minor", + "type": "issue", + }, { "check_name": "prettier", "description": "Replace ·arg·· with arg", @@ -82,6 +110,34 @@ exports[`prettier-formatter-gitlab cli should create a code quality report file exports[`prettier-formatter-gitlab cli should create a code quality report file with prettier -c 1`] = ` [ + { + "check_name": "prettier", + "description": "Delete ⏎", + "fingerprint": "994d9053d8e5f73c098f2bcfadec786c", + "location": { + "lines": { + "begin": 11, + "end": 12, + }, + "path": "test/__stubs__/.gitlab-ci.fail.yml", + }, + "severity": "minor", + "type": "issue", + }, + { + "check_name": "prettier", + "description": "Delete ⏎", + "fingerprint": "20c9ca526d0a43e3075a3e0f74135991", + "location": { + "lines": { + "begin": 11, + "end": 12, + }, + "path": "test/__stubs__/.gitlab-ci.yml", + }, + "severity": "minor", + "type": "issue", + }, { "check_name": "prettier", "description": "Replace ·arg·· with arg", diff --git a/test/__stubs__/.gitlab-ci.fail.yml b/test/__stubs__/.gitlab-ci.fail.yml new file mode 100644 index 0000000..d8dcf4a --- /dev/null +++ b/test/__stubs__/.gitlab-ci.fail.yml @@ -0,0 +1,11 @@ +.ref: + script: + - npm run lint + +prettier: + script: + - !reference [.ref, script] + artifacts: + reports: + codequalit: gl-codequality.json + diff --git a/test/__stubs__/.gitlab-ci.yml b/test/__stubs__/.gitlab-ci.yml new file mode 100644 index 0000000..26691dc --- /dev/null +++ b/test/__stubs__/.gitlab-ci.yml @@ -0,0 +1,11 @@ +.ref: + script: + - npm run lint + +prettier: + script: + - !reference [.ref, script] + artifacts: + reports: + codequality: gl-codequality.json + diff --git a/test/utils/get-output-path.spec.js b/test/utils/get-output-path.spec.js new file mode 100644 index 0000000..a799094 --- /dev/null +++ b/test/utils/get-output-path.spec.js @@ -0,0 +1,36 @@ +import { describe, it, expect, beforeEach, afterEach } from 'bun:test'; +import { resolve, join } from 'node:path'; +import { getOutputPath } from '../../src/utils/get-output-path.js'; + +let env; + +beforeEach(() => { + env = process.env; +}); + +afterEach(() => { + process.env = env; +}); + +describe('The getPrettierFileInfos function', () => { + it('should return the output path defined in a .gitlab-ci.yml file', () => { + process.env.CI_PROJECT_DIR = resolve(join(import.meta.dirname, '../__stubs__/')); + process.env.CI_JOB_NAME = 'prettier'; + console.log(process.env.CI_PROJECT_DIR); + expect(getOutputPath()).toBe( + resolve(join(import.meta.dirname, '../__stubs__/gl-codequality.json')), + ); + }); + + it('should throw an error if it can not find the report file path', () => { + process.env.CI_PROJECT_DIR = resolve(join(import.meta.dirname, '../__stubs__/')); + process.env.CI_JOB_NAME = 'prettier'; + process.env.CI_CONFIG_PATH = '.gitlab-ci.fail.yml'; + expect(getOutputPath).toThrow(); + }); + + it('should throw an error if it can not find a .gitlab-ci.yml file', () => { + process.env.CI_PROJECT_DIR = '/tmp'; + expect(getOutputPath).toThrow(); + }); +});